Browse Source

Implement FPGA <-> ESP32 communication path

ESP32 can do direct DMA to FPGA SDRAM via DIO SPI. Reorganize DRAM
slightly to make sure that there is a well-known address to start
looking at memory.
H. Peter Anvin 2 years ago
parent
commit
aac953ed19

+ 0 - 1
esp32/max80/fpga.c

@@ -1,5 +1,4 @@
 #define MODULE "fpga"
-#define DEBUG 1
 
 #include "common.h"
 #include "jtag.h"

BIN
esp32/output/max80.ino.bin


+ 177 - 113
fpga/esp.sv

@@ -1,136 +1,200 @@
 //
-// Communication with ESP32
+// Communication interface with ESP32-S2
 //
-// Components are:
-// a. serial interface
-// b. SPI interface (not yet implemented)
-// c. common open drain IRQ line
-// d. ESP32 EN and IO0 lines
+// This is a DIO (2-bit, including command) SPI slave interface which
+// allows direct access to content in SDRAM. Additionally, each
+// direction has three interrupt flags (3-1); the FPGA CPU additionally
+// has a fourth interrupt condition (0) which indicates DRAM timing
+// overrun/underrun.
+//
+// The SPI command byte is:
+// Bit [7:5]  - reserved, must be 0
+// Bit    4   - read/write#
+// Bit [3:2]  - clear upstream (FPGA->ESP) interrupt flag if nonzero
+// Bit [1:0]  - set downstream (ESP->FPGA) interrupt flag if nonzero
+//
+// CPU downstream interrupts are set after the transaction completes
+// (CS# goes high.)
+//
+// A 32-bit address follows, and for a read, 32 dummy bits (16 cycles)
+// All data is processed as 32-bit words only.
 //
-
 module esp (
-	    input 	      rst_n,
-	    input 	      clk,
-
-	    input 	      cpu_valid,
-	    input [4:0]       cpu_addr,
-	    input [3:0]       cpu_wstrb,
-	    input [31:0]      cpu_wdata,
-	    output reg [31:0] cpu_rdata,
-	    output reg	      irq,
-
-	    input 	      tty_rx,
-	    output 	      tty_tx,
-
-	    output 	      esp_en, // ESP reset#
-	    inout 	      esp_int,
-	    inout 	      esp_io0,
-
-	    inout 	      spi_clk,
-	    inout 	      spi_miso,
-	    inout 	      spi_mosi,
-	    inout 	      spi_cs_esp_n,
-	    inout 	      spi_cs_flash_n
+	    input 	  rst_n,
+	    input 	  sys_clk,
+	    input 	  sdram_clk,
+
+	    input 	  cpu_valid,
+	    input [4:0]   cpu_addr,
+	    input [3:0]   cpu_wstrb,
+	    input [31:0]  cpu_wdata,
+	    output [31:0] cpu_rdata,
+	    output reg 	  irq,
+
+	    dram_bus.dstr dram,
+ 
+	    output reg 	  esp_int,
+	    input 	  spi_clk,
+	    inout [1:0]   spi_io,
+	    input 	  spi_cs_n
 	    );
 
-   wire [31:0] 	  cpu_reg  = cpu_valid << cpu_addr;
-   wire 	  cpu_read = ~|cpu_wstrb;
+   reg  [24:2] 		  mem_addr;
+   reg 			  mem_valid;
+   reg  [31:0] 		  mem_wdata;
+   wire			  mem_write;
+   wire 		  mem_ready;
+   wire [31:0] 		  mem_rdata;
+   
+   dram_port #(32) mem
+     (
+      .bus   ( dram ),
+      .prio  ( 2'd2 ),
+      .addr  ( {mem_addr, 2'b00} ),
+      .valid ( mem_valid ),
+      .wd    ( mem_wdata ),
+      .wstrb ( {4{mem_wrq}} ),
+      .ready ( mem_ready ),
+      .rd    ( mem_rdata )
+      );
    
-   reg 		  tx_break_q;
-   wire 	  tx_full;
-   wire 	  tx_empty;
-   wire 	  rx_full;
-   wire 	  rx_empty;
-   wire 	  rx_break;
-   reg		  tx_flush;
-   reg 		  rx_flush;
-   wire [7:0] 	  tty_rdata;
-   wire [31:0] 	  tty_divisor;
+   reg [1:0]		  spi_clk_q;
+   reg 			  spi_cs_n_q;
+   reg [1:0] 		  spi_io_q;
+
+   always @(posedge sdram_clk)
+     begin
+	spi_clk_q  <= { spi_clk_q[0], spi_clk };
+	spi_cs_n_q <= spi_cs_n;
+	spi_io_q   <= spi_io;
+     end
+
+   typedef enum logic [1:0] {
+	st_cmd,			// Reading command
+	st_addr,		// Reading address
+	st_io			// I/O (including read dummy bits)
+   } state_t;
    
-   serial #(
-	    .BAUDRATE_SETTABLE ( 1'b1 )
-	    )
-   esptty (
-	   .rst_n  ( rst_n ),
-	   .clk    ( clk ),
-	   .tty_tx ( tty_tx ),
-	   .tty_rx ( tty_rx ),
-		  
-	   .tx_wstrb ( cpu_wstrb[0] & cpu_reg[0] ),
-	   .tx_data  ( cpu_wdata[7:0] ),
-	   .tx_break ( tx_break_q ),
-	   .tx_full  ( tx_full ),
-	   .tx_empty ( tx_empty ),
-	   .tx_flush ( tx_flush ),
-
-	   .rx_rstrb ( cpu_read & cpu_reg[0] ),
-	   .rx_data  ( tty_rdata ),
-	   .rx_break ( rx_break ),
-	   .rx_full  ( rx_full ),
-	   .rx_empty ( rx_empty ),
-	   .rx_flush ( rx_flush ),
-
-	   .divisor_wdata ( cpu_wdata ),
-	   .divisor_wstrb ( cpu_wstrb[0] & cpu_reg[1] ),
-	   .divisor  ( tty_divisor )
-	   );
-
-   // Control/clear status bits
-   reg 		  rx_break_q;
-   reg 		  esp_rst_q;
-   reg 		  esp_dl_q;	// Force download boot
-   reg 		  esp_int_q;
-
-   assign esp_en  = esp_rst_q ? 1'b0 : 1'bz;
-   assign esp_io0 = esp_dl_q  ? 1'b0 : 1'bz;
-   assign esp_int = esp_int_q ? 1'b0 : 1'bz;
-
-   always @(negedge rst_n or posedge clk)
+   state_t    spi_state;
+
+   reg [ 4:0] spi_cmd;
+   reg [31:0] spi_shr;
+   reg [15:0] spi_ctr;
+   reg [ 3:0] cpu_irq;
+   reg [ 3:1] spi_irq;
+   reg [ 1:0] spi_out;
+   reg 	      spi_oe;
+
+   assign spi_io = spi_oe ? spi_out : 2'bzz;
+
+   assign mem_write = ~spi_cmd[4];
+
+   wire [31:0] spi_indata = { spi_shr[29:0], spi_io_q };
+
+   reg 	       cpu_valid_q;
+
+   always @(negedge rst_n or posedge sdram_clk)
      if (~rst_n)
        begin
-	  rx_break_q <= 1'b0;
-	  tx_break_q <= 1'b0;
-	  esp_rst_q  <= 1'b0;
-	  esp_dl_q   <= 1'b0;
-	  esp_int_q  <= 1'b0;
+	  spi_state <= st_cmd;
+	  spi_cmd   <= 5'b0;
+	  spi_ctr   <= 4'd3;	// 8 bits needed for this state
+	  cpu_irq   <= 3'b000;
+	  spi_irq   <= 3'b000;
+	  spi_oe    <= 1'b0;
        end
      else
        begin
-	  rx_break_q <= rx_break | rx_break_q;
+	  esp_int <= ~|spi_irq;
 
-	  if (cpu_wstrb[0] & cpu_reg[2])
+	  if (spi_cs_n_q)
 	    begin
-	       if (cpu_wdata[2])
-		 rx_break_q <= rx_break;
-
-	       tx_break_q <= cpu_wdata[6];
+	       spi_state  <= st_cmd;
+	       spi_ctr    <= 4'd3;
+	       spi_oe     <= 1'b0;
+	       spi_cmd    <= 5'b0;
+	       for (int i = 1; i < 4; i++)
+		 if (spi_cmd[1:0] == i)
+		   cpu_irq[i] <= 1'b1;
+	    end
+	  else if (spi_clk_q == 2'b01)
+	    begin
+	       spi_ctr <= spi_ctr - 1'b1;
+	       
+	       if (|spi_ctr)
+		 begin
+		    spi_shr   <= spi_indata;
+		 end
+	       else
+		 begin
+		    spi_shr   <= mem_rdata;
+		    mem_wdata <= spi_indata;
+
+		    if (mem_valid)
+		      cpu_irq[0] <= 1'b1; // Overrun/underrun
+		    
+		    case (spi_state)
+		      st_cmd: begin
+			 spi_cmd   <= spi_indata[5:0];
+			 spi_state <= st_addr;
+			 for (int i = 1; i < 4; i++)
+			   if (spi_indata[3:2] == i)
+			     spi_irq[i] <= 1'b0;
+		      end
+		      st_addr: begin
+			 mem_addr  <= spi_indata[25:2];
+			 spi_state <= st_io;
+			 mem_valid <= ~mem_write;
+		      end
+		      st_io: begin
+			 mem_valid <= 1'b1;
+		      end
+		    endcase
+		 end
+	    end
+	  else if (spi_clk_q == 2'b10)
+	    begin
+	       spi_out <= spi_shr[31:30];
+	       spi_oe  <= (spi_state == st_io) & ~mem_write;
 	    end
 
-	  if (cpu_wstrb[1] & cpu_reg[2])
-	    esp_int_q <= cpu_wdata[8];
-	  
-	  if (cpu_wstrb[2] & cpu_reg[2])
+	  if (mem_valid & mem_ready)
 	    begin
-	       esp_rst_q <= cpu_wdata[16];
-	       esp_dl_q  <= cpu_wdata[17];
+	       mem_valid <= 1'b0;
+	       mem_addr  <= mem_addr + 1'b1;
 	    end
-       end // else: !if(~rst_n)
 
-   // Not used yet
-   assign irq = 1'b0;
+	  cpu_valid_q <= cpu_valid;
+	  if (cpu_valid & ~cpu_valid_q & cpu_wstrb[0])
+	    case (cpu_addr[1:0])
+	      2'b00:
+		cpu_irq <= cpu_wdata[3:0];
+	      2'b01:
+		for (int i = 0; i < 4; i++)
+		  if (cpu_wdata[i])
+		    cpu_irq[i] <= 1'b0;
+	      2'b10:
+		spi_irq <= cpu_wdata[3:1];
+	      2'b11:
+		for (int i = 1; i < 4; i++)
+		  if (cpu_wdata[i])
+		    spi_irq[i] <= 1'b1;
+	    endcase // case (cpu_addr[1:0])
+       end // else: !if(~rst_n)
 
-   // Output data MUX
+   always @(posedge sys_clk)
+     irq <= |cpu_irq;
+   
    always @(*)
-     case (cpu_addr)
-       5'd0: cpu_rdata = { 24'b0, tty_rdata };
-       5'd1: cpu_rdata = tty_divisor;
-       5'd2: cpu_rdata = {
-			  8'b0,
-			  6'b0, esp_dl_q, esp_rst_q,
-			  7'b0, ~esp_int,
-			  1'b0, tx_break_q, tx_full, ~tx_empty,
-			  1'b0, rx_break_q, rx_full, ~rx_empty
-			  };
-       default: cpu_rdata = 32'bx;
-     endcase // case (cpu_addr)
+     casez (cpu_addr[1:0])
+       2'b0?:
+	 cpu_rdata = { 28'b0, cpu_irq };
+       2'b1?:
+	 cpu_rdata = { 28'b0, spi_irq, 1'b0 };
+     endcase // casez (cpu_addr[1:0])
+
 endmodule // esp
+
+		      
+	
+   

+ 4 - 4
fpga/max80.qpf

@@ -19,15 +19,15 @@
 #
 # Quartus Prime
 # Version 21.1.0 Build 842 10/21/2021 SJ Lite Edition
-# Date created = 01:33:58  April 12, 2022
+# Date created = 02:50:13  May 01, 2022
 #
 # -------------------------------------------------------------------------- #
 
 QUARTUS_VERSION = "21.1"
-DATE = "01:33:58  April 12, 2022"
+DATE = "02:50:13  May 01, 2022"
 
 # Revisions
 
-PROJECT_REVISION = "bypass"
-PROJECT_REVISION = "v2"
 PROJECT_REVISION = "v1"
+PROJECT_REVISION = "v2"
+PROJECT_REVISION = "bypass"

+ 1 - 0
fpga/max80.qsf

@@ -234,6 +234,7 @@ set_global_assignment -name SYSTEMVERILOG_FILE transpose.sv
 set_global_assignment -name SYSTEMVERILOG_FILE synchro.sv
 set_global_assignment -name SYSTEMVERILOG_FILE tmdsenc.sv
 set_global_assignment -name SYSTEMVERILOG_FILE video.sv
+set_global_assignment -name SYSTEMVERILOG_FILE esp.sv
 set_global_assignment -name SDC_FILE max80.sdc
 set_global_assignment -name SYSTEMVERILOG_FILE max80.sv
 set_global_assignment -name SOURCE_TCL_SCRIPT_FILE scripts/pins.tcl

+ 91 - 97
fpga/max80.sv

@@ -13,124 +13,123 @@ module max80
     parameter logic [7:0] fpga_ver)
    (
     // Clock oscillator
-    input	  master_clk,	// 336 MHz from PLL2
-    input	  slow_clk,	// ~12 MHz clock from PLL2
-    input	  master_pll_locked, // PLL2 is locked, master_clk is good
-    output	  reset_plls,	// Reset all PLLs including PLL2
+    input 	  master_clk, // 336 MHz from PLL2
+    input 	  slow_clk, // ~12 MHz clock from PLL2
+    input 	  master_pll_locked, // PLL2 is locked, master_clk is good
+    output 	  reset_plls, // Reset all PLLs including PLL2
 
-    input	  board_id, // This better match the firmware
+    input 	  board_id, // This better match the firmware
 
     // ABC-bus
-    inout	  abc_clk, // ABC-bus 3 MHz clock
+    inout 	  abc_clk, // ABC-bus 3 MHz clock
     inout [15:0]  abc_a, // ABC address bus
     inout [7:0]   abc_d, // ABC data bus
-    output	  abc_d_oe, // Data bus output enable
-    inout	  abc_rst_n, // ABC bus reset strobe
-    inout	  abc_cs_n, // ABC card select strobe
+    output 	  abc_d_oe, // Data bus output enable
+    inout 	  abc_rst_n, // ABC bus reset strobe
+    inout 	  abc_cs_n, // ABC card select strobe
     inout [4:0]   abc_out_n, // OUT, C1-C4 strobe
     inout [1:0]   abc_inp_n, // INP, STATUS strobe
-    inout	  abc_xmemfl_n, // Memory read strobe
-    inout	  abc_xmemw800_n, // Memory write strobe (ABC800)
-    inout	  abc_xmemw80_n, // Memory write strobe (ABC80)
-    inout	  abc_xinpstb_n, // I/O read strobe (ABC800)
-    inout	  abc_xoutpstb_n, // I/O write strobe (ABC80)
+    inout 	  abc_xmemfl_n, // Memory read strobe
+    inout 	  abc_xmemw800_n, // Memory write strobe (ABC800)
+    inout 	  abc_xmemw80_n, // Memory write strobe (ABC80)
+    inout 	  abc_xinpstb_n, // I/O read strobe (ABC800)
+    inout 	  abc_xoutpstb_n, // I/O write strobe (ABC80)
     // The following are inverted versus the bus IF
     // the corresponding MOSFETs are installed
-    inout	  abc_rdy_x, // RDY = WAIT#
-    inout	  abc_resin_x, // System reset request
-    inout	  abc_int80_x, // System INT request (ABC80)
-    inout	  abc_int800_x, // System INT request (ABC800)
-    inout	  abc_nmi_x, // System NMI request (ABC800)
-    inout	  abc_xm_x, // System memory override (ABC800)
+    inout 	  abc_rdy_x, // RDY = WAIT#
+    inout 	  abc_resin_x, // System reset request
+    inout 	  abc_int80_x, // System INT request (ABC80)
+    inout 	  abc_int800_x, // System INT request (ABC800)
+    inout 	  abc_nmi_x, // System NMI request (ABC800)
+    inout 	  abc_xm_x, // System memory override (ABC800)
     // Host/device control
-    output	  abc_host, // 1 = host, 0 = target
+    output 	  abc_host, // 1 = host, 0 = target
 
     // ABC-bus extension header
     // (Note: cannot use an array here because HC and HH are
     // input only.)
-    inout	  exth_ha,
-    inout	  exth_hb,
-    input	  exth_hc,
-    inout	  exth_hd,
-    inout	  exth_he,
-    inout	  exth_hf,
-    inout	  exth_hg,
-    input	  exth_hh,
+    inout 	  exth_ha,
+    inout 	  exth_hb,
+    input 	  exth_hc,
+    inout 	  exth_hd,
+    inout 	  exth_he,
+    inout 	  exth_hf,
+    inout 	  exth_hg,
+    input 	  exth_hh,
 
     // SDRAM bus
-    output	  sr_clk,
+    output 	  sr_clk,
     output [1:0]  sr_ba, // Bank address
     output [12:0] sr_a, // Address within bank
     inout [15:0]  sr_dq, // Also known as D or IO
     output [1:0]  sr_dqm, // DQML and DQMH
-    output	  sr_cs_n,
-    output	  sr_we_n,
-    output	  sr_cas_n,
-    output	  sr_ras_n,
+    output 	  sr_cs_n,
+    output 	  sr_we_n,
+    output 	  sr_cas_n,
+    output 	  sr_ras_n,
 
     // SD card
-    input	  sd_cd_n,
-    output	  sd_cs_n,
-    output	  sd_clk,
-    output	  sd_di,
-    input	  sd_do,
+    input 	  sd_cd_n,
+    output 	  sd_cs_n,
+    output 	  sd_clk,
+    output 	  sd_di,
+    input 	  sd_do,
 
     // Serial console (naming is FPGA as DCE)
-    input	  tty_txd,
-    output	  tty_rxd,
-    input	  tty_rts,
-    output	  tty_cts,
-    input	  tty_dtr,
+    input 	  tty_txd,
+    output 	  tty_rxd,
+    input 	  tty_rts,
+    output 	  tty_cts,
+    input 	  tty_dtr,
 
     // SPI flash memory (also configuration)
-    output	  flash_cs_n,
-    output	  flash_sck,
+    output 	  flash_cs_n,
+    output 	  flash_sck,
     inout [1:0]   flash_io,
 
     // SPI bus (connected to ESP32 so can be bidirectional)
-    inout	  spi_clk,
-    inout	  spi_miso,
-    inout	  spi_mosi,
-    inout	  spi_cs_esp_n, // ESP32 IO10
-    inout	  spi_cs_flash_n, // ESP32 IO01
+    inout 	  spi_clk,	  // ESP32 IO12
+    inout [1:0]   spi_io,	  // ESP32 IO13,IO11
+    inout 	  spi_cs_esp_n,   // ESP32 IO10
+    inout 	  spi_cs_flash_n, // ESP32 IO01
 
     // Other ESP32 connections
-    inout	  esp_io0, // ESP32 IO00
-    inout	  esp_int, // ESP32 IO09
+    inout 	  esp_io0,        // ESP32 IO00
+    inout 	  esp_int,        // ESP32 IO09
 
     // I2C bus (RTC and external)
-    inout	  i2c_scl,
-    inout	  i2c_sda,
-    input	  rtc_32khz,
-    input	  rtc_int_n,
+    inout 	  i2c_scl,
+    inout 	  i2c_sda,
+    input 	  rtc_32khz,
+    input 	  rtc_int_n,
 
     // LEDs
     output [2:0]  led,
 
     // USB
-    inout	  usb_dp,
-    inout	  usb_dn,
-    output	  usb_pu,
-    input	  usb_rx,
-    input	  usb_rx_ok,
+    inout 	  usb_dp,
+    inout 	  usb_dn,
+    output 	  usb_pu,
+    input 	  usb_rx,
+    input 	  usb_rx_ok,
 
     // HDMI
     output [2:0]  hdmi_d,
-    output	  hdmi_clk,
-    inout	  hdmi_scl,
-    inout	  hdmi_sda,
-    inout	  hdmi_hpd,
+    output 	  hdmi_clk,
+    inout 	  hdmi_scl,
+    inout 	  hdmi_sda,
+    inout 	  hdmi_hpd,
 
     // Unconnected pins with pullups, used for randomness
     inout [2:0]   rngio,
 
     // Various clocks available to the top level as well as internally
-    output	  sdram_clk, // 168 MHz SDRAM clock
-    output	  sys_clk, //  84 MHz System clock
-    output	  flash_clk, // 134 MHz Serial flash ROM clock
-    output	  usb_clk, //  48 MHz USB clock
-    output	  vid_clk, //  56 MHz Video pixel clock
-    output	  vid_hdmiclk	// 280 MHz HDMI serializer clock = vid_clk x 5
+    output 	  sdram_clk, // 168 MHz SDRAM clock
+    output 	  sys_clk, //  84 MHz System clock
+    output 	  flash_clk, // 134 MHz Serial flash ROM clock
+    output 	  usb_clk, //  48 MHz USB clock
+    output 	  vid_clk, //  56 MHz Video pixel clock
+    output 	  vid_hdmiclk	// 280 MHz HDMI serializer clock = vid_clk x 5
     );
 
    // -----------------------------------------------------------------------
@@ -321,7 +320,7 @@ module max80
    //
    // SDRAM
    //
-   localparam dram_port_count = 3;
+   localparam dram_port_count = 4;
    dram_bus sr_bus[1:dram_port_count] ( );
 
    // ABC interface
@@ -367,7 +366,7 @@ module max80
 
    dram_port #(32)
    cpu_dram_port (
-		  .bus   ( sr_bus[3] ),
+		  .bus   ( sr_bus[4] ),
 		  .prio  ( 2'd1 ),
 		  .addr  ( cpu_mem_addr[24:0] ),
 		  .rd    ( sdram_mem_rdata ),
@@ -862,28 +861,23 @@ module max80
    assign spi_cs_flash_n = 1'bz;
 
    esp esp (
-	    .rst_n    ( rst_n ),
-	    .clk      ( sys_clk ),
-
-	    .cpu_valid ( iodev_valid_esp ),
-	    .cpu_addr  ( cpu_mem_addr[6:2] ),
-	    .cpu_wstrb ( cpu_mem_wstrb ),
-	    .cpu_wdata ( cpu_mem_wdata ),
-	    .cpu_rdata ( iodev_rdata_esp ),
-	    .irq       ( iodev_irq_esp ),
-
-	    .tty_rx   ( ),
-	    .tty_tx   ( ),
-
-	    .esp_en   ( ),
-	    .esp_int  ( esp_int ),
-	    .esp_io0  ( esp_io0 ),
-
-	    .spi_clk  ( spi_clk ),
-	    .spi_miso ( spi_miso ),
-	    .spi_mosi ( spi_mosi ),
-	    .spi_cs_esp_n ( spi_cs_esp_n ),
-	    .spi_cs_flash_n ( spi_cs_flash_n )
+	    .rst_n      ( rst_n ),
+	    .sys_clk    ( sys_clk ),
+	    .sdram_clk  ( sdram_clk ),
+
+	    .cpu_valid  ( iodev_valid_esp ),
+	    .cpu_addr   ( cpu_mem_addr[6:2] ),
+	    .cpu_wstrb  ( cpu_mem_wstrb ),
+	    .cpu_wdata  ( cpu_mem_wdata ),
+	    .cpu_rdata  ( iodev_rdata_esp ),
+	    .irq        ( iodev_irq_esp ),
+
+	    .esp_int    ( esp_int ),
+	    .spi_clk    ( spi_clk ),
+	    .spi_io     ( spi_io ),
+	    .spi_cs_n   ( spi_cs_esp_n ),
+
+	    .dram       ( sr_bus[2].dstr )
 	    );
 
    //
@@ -915,7 +909,7 @@ module max80
 	  .sys_clk      ( sys_clk ),
 	  .reset_cmd    ( vjtag_reset_cmd ),
 
-	  .sdram	( sr_bus[2].dstr ),
+	  .sdram	( sr_bus[3].dstr ),
 
 	  .cpu_valid    ( iodev_valid_vjtag ),
 	  .cpu_addr     ( cpu_mem_addr[6:2] ),

BIN
fpga/output/bypass.jic


BIN
fpga/output/v1.fw


BIN
fpga/output/v1.jic


+ 3 - 3
fpga/output/v1.pin

@@ -251,8 +251,8 @@ VCCIO2                       : M3        : power  :                   : 3.3V
 GND                          : M4        : gnd    :                   :         :           :                
 GNDA1                        : M5        : gnd    :                   :         :           :                
 abc_d[1]                     : M6        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
-spi_miso                     : M7        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
-spi_mosi                     : M8        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
+spi_io[1]                    : M7        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
+spi_clk                      : M8        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
 RESERVED_INPUT_WITH_WEAK_PULLUP : M9        :        :                   :         : 4         :                
 sd_dat[1]                    : M10       : bidir  : 3.3-V LVTTL       :         : 4         : Y              
 hdmi_scl                     : M11       : bidir  : 3.3-V LVTTL       :         : 4         : Y              
@@ -282,7 +282,7 @@ abc_rst_n                    : P2        : input  : 3.3-V LVTTL       :
 abc_d[0]                     : P3        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
 VCCIO3                       : P4        : power  :                   : 3.3V    : 3         :                
 GND                          : P5        : gnd    :                   :         :           :                
-spi_clk                      : P6        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
+spi_io[0]                    : P6        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
 VCCIO3                       : P7        : power  :                   : 3.3V    : 3         :                
 esp_int                      : P8        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
 gpio[1]                      : P9        : bidir  : 3.3-V LVTTL       :         : 4         : Y              

BIN
fpga/output/v1.rbf.gz


BIN
fpga/output/v1.rpd.gz


BIN
fpga/output/v1.sof


BIN
fpga/output/v1.svf.gz


BIN
fpga/output/v1.xsvf.gz


BIN
fpga/output/v2.fw


BIN
fpga/output/v2.jic


+ 3 - 3
fpga/output/v2.pin

@@ -251,8 +251,8 @@ VCCIO2                       : M3        : power  :                   : 3.3V
 GND                          : M4        : gnd    :                   :         :           :                
 GNDA1                        : M5        : gnd    :                   :         :           :                
 abc_d[1]                     : M6        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
-spi_miso                     : M7        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
-spi_mosi                     : M8        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
+spi_io[1]                    : M7        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
+spi_clk                      : M8        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
 abc_inp_n[1]                 : M9        : bidir  : 3.3-V LVTTL       :         : 4         : Y              
 abc_a[6]                     : M10       : bidir  : 3.3-V LVTTL       :         : 4         : Y              
 hdmi_scl                     : M11       : bidir  : 3.3-V LVTTL       :         : 4         : Y              
@@ -282,7 +282,7 @@ abc_rst_n                    : P2        : bidir  : 3.3-V LVTTL       :
 abc_d[0]                     : P3        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
 VCCIO3                       : P4        : power  :                   : 3.3V    : 3         :                
 GND                          : P5        : gnd    :                   :         :           :                
-spi_clk                      : P6        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
+spi_io[0]                    : P6        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
 VCCIO3                       : P7        : power  :                   : 3.3V    : 3         :                
 esp_int                      : P8        : bidir  : 3.3-V LVTTL       :         : 3         : Y              
 gpio[1]                      : P9        : bidir  : 3.3-V LVTTL       :         : 4         : Y              

BIN
fpga/output/v2.rbf.gz


BIN
fpga/output/v2.rpd.gz


BIN
fpga/output/v2.sof


BIN
fpga/output/v2.svf.gz


BIN
fpga/output/v2.xsvf.gz


+ 3 - 3
fpga/v1.pins

@@ -43,8 +43,8 @@ l4	abc_a[10]
 
 # Bank 3
 m6	abc_d[1]
-p6	spi_clk
-m7	spi_miso
+p6	spi_io[0]
+m7	spi_io[1]
 t5	abc_d_ce_n
 r5	abc_d_oe
 r6	abc_resin_x
@@ -53,7 +53,7 @@ l7	gpio[0]
 r7	gpio[5]
 t7	gpio[4]
 l8	esp_io0
-m8	spi_mosi
+m8	spi_clk
 n8	spi_cs_esp_n
 p8	esp_int
 r8	exth_hh

+ 6 - 8
fpga/v1.sv

@@ -81,15 +81,14 @@ module v1
     inout [1:0]   flash_io,
 
     // SPI bus (connected to ESP32 so can be bidirectional)
-    inout 	  spi_clk,
-    inout 	  spi_miso,
-    inout 	  spi_mosi,
-    inout 	  spi_cs_esp_n, // ESP32 IO10
+    inout 	  spi_clk,	  // ESP32 IO12
+    inout [1:0]   spi_io,	  // ESP32 IO13,IO11
+    inout 	  spi_cs_esp_n,   // ESP32 IO10
     inout 	  spi_cs_flash_n, // ESP32 IO01
 
     // Other ESP32 connections
-    inout 	  esp_io0, // ESP32 IO00
-    inout 	  esp_int, // ESP32 IO09
+    inout 	  esp_io0,        // ESP32 IO00
+    inout 	  esp_int,        // ESP32 IO09
 
     // I2C bus (RTC and external)
     inout 	  i2c_scl,
@@ -202,8 +201,7 @@ module v1
 	  .flash_sck              ( flash_sck ),
 	  .flash_io               ( flash_io ),
 	  .spi_clk                ( spi_clk ),
-	  .spi_miso               ( spi_miso ),
-	  .spi_mosi               ( spi_mosi ),
+	  .spi_io                 ( spi_io ),
 	  .spi_cs_esp_n           ( spi_cs_esp_n ),
 	  .spi_cs_flash_n         ( spi_cs_flash_n ),
 	  .esp_io0                ( esp_io0 ),

+ 3 - 3
fpga/v2.pins

@@ -43,8 +43,8 @@ l4	abc_a[10]
 
 # Bank 3
 m6	abc_d[1]
-p6	spi_clk
-m7	spi_miso
+p6	spi_io[0]
+m7	spi_io[1]
 r5	abc_d_oe
 t5	abc_clk
 r6	abc_resin_x
@@ -53,7 +53,7 @@ l7	gpio[0]
 r7	gpio[5]
 t7	gpio[4]
 l8	esp_io0
-m8	spi_mosi
+m8	spi_clk
 n8	spi_cs_esp_n
 p8	esp_int
 r8	exth_hh

+ 9 - 11
fpga/v2.vh

@@ -67,20 +67,19 @@ module `TOP
     input	  sd_do,
 
     // SPI flash memory (also configuration)
-    output	  flash_cs_n,
-    output	  flash_sck,
+    output 	  flash_cs_n,
+    output 	  flash_sck,
     inout [1:0]   flash_io,
 
     // SPI bus (connected to ESP32 so can be bidirectional)
-    inout	  spi_clk,
-    inout	  spi_miso,
-    inout	  spi_mosi,
-    inout	  spi_cs_esp_n, // ESP32 IO10
-    inout	  spi_cs_flash_n, // ESP32 IO01
+    inout 	  spi_clk,	  // ESP32 IO12
+    inout [1:0]   spi_io,	  // ESP32 IO13,IO11
+    inout 	  spi_cs_esp_n,   // ESP32 IO10
+    inout 	  spi_cs_flash_n, // ESP32 IO01
 
     // Other ESP32 connections
-    inout	  esp_io0, // ESP32 IO00
-    inout	  esp_int, // ESP32 IO09
+    inout 	  esp_io0,        // ESP32 IO00
+    inout 	  esp_int,        // ESP32 IO09
 
     // I2C bus (RTC and external)
     inout	  i2c_scl,
@@ -192,8 +191,7 @@ module `TOP
 	  .flash_sck              ( flash_sck ),
 	  .flash_io               ( flash_io ),
 	  .spi_clk                ( spi_clk ),
-	  .spi_miso               ( spi_miso ),
-	  .spi_mosi               ( spi_mosi ),
+	  .spi_io                 ( spi_io ),
 	  .spi_cs_esp_n           ( spi_cs_esp_n ),
 	  .spi_cs_flash_n         ( spi_cs_flash_n ),
 	  .esp_io0                ( esp_io0 ),

+ 1 - 1
rv32/checksum.h

@@ -1,4 +1,4 @@
 #ifndef CHECKSUM_H
 #define CHECKSUM_H
-#define SDRAM_SUM 0x874aa99a
+#define SDRAM_SUM 0xd671717c
 #endif

+ 2 - 0
rv32/compiler.h

@@ -96,6 +96,8 @@ typedef union xcptr {
 #define __string_hot	___section(".rodata.hot.str","aMS")
 #define __sbss		___section(".sbss.hot","aw",@nobits)
 #define __bss_hot	___section(".bss.hot","aw",@nobits)
+#define __dram_io_head	___section(".dram.io.head","a",@nobits)
+#define __dram_io	___section(".dram.io","a",@nobits)
 #define __dram_text	___section(".dram.text","ax")
 #define __dram_rodata	___section(".dram.rodata","a")
 #define __dram_string	___section(".dram.rodata.str","aMS")

+ 5 - 0
rv32/fw.h

@@ -26,9 +26,14 @@ extern const size_t __rom_offset;
 extern const uint32_t __dram_checksum;
 extern const char __datestamp[];
 
+extern char __dram_io_start[],   __dram_io_end[];
 extern char __dram_init_start[], __dram_init_end[];
 extern char __dram_bss_start[],  __dram_bss_end[];
 
+struct dram_io_head;
+extern struct dram_io_head dram_io_head;
+extern uint32_t dram_io[];
+
 extern no_return _die(void);
 extern no_return exit(int);
 extern no_return _exit(int);

+ 4 - 0
rv32/head.S

@@ -59,6 +59,10 @@ __start:
 #endif
 	addqxi gp,gp,0		// Set gp for interrupt code too
 
+	// Clear dram_io_head.magic as quickly as possible
+	la a0,__dram_io_start
+	sw zero,(a0)
+	
 	// Clear bss
 	la a0,__BSS_START__
 	la a1,__BSS_END__

+ 11 - 0
rv32/jtagupd.ld

@@ -257,10 +257,21 @@ SECTIONS
 	/* Sections in SDRAM */
 	. = SDRAM_ADDR;
 
+	/* This really should be pointed by .dram.io.head ... */
 	.jtag_flash_buffer (NOLOAD) : {
 		  *(.jtag_flash_buffer)
 	} >DRAM
 
+	/* Always first in DRAM for remote DMA to find fixed addresses */
+	. = ALIGN(4096);
+	.dram.io (NOLOAD) : ALIGN(4096) {
+		__dram_io_start = .;
+		KEEP(*(SORT_NONE(.dram.io.head)));
+		*(.dram.io .dram.io.*);
+		. = ALIGN(16);
+		__dram_io_end = .;
+	} >DRAM
+
 	/* There is no dram init ... there can't be! */
 	. = ALIGN(8);
 	__dram_init_start = .;

+ 13 - 6
rv32/max80.ld

@@ -213,7 +213,8 @@ SECTIONS
 	/* Are these necessary/supportable? */
 	  .jcr          : { KEEP (*(.jcr)) }
 	.data.rel.ro	: {
-			*(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*)
+			*(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*)
+			*(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*)
 	}
 
 .data           : {
@@ -238,17 +239,23 @@ SECTIONS
 	/* Sections in SDRAM */
 	. = SDRAM_ADDR;
 	__dram_start = .;
+
+	/* Always first in DRAM for remote DMA to find fixed addresses */
+	.dram.io (NOLOAD) : ALIGN(4096) {
+		__dram_io_start = .;
+		KEEP(*(SORT_NONE(.dram.io.head)));
+		*(.dram.io .dram.io.*);
+		. = ALIGN(16);
+		__dram_io_end = .;
+	} >DRAM
+
+	. = ALIGN(4096);	/* Align to a flash page */
 	__dram_init_start = .;
 
 	.dram.abcrom : AT(SRAM_SIZE) ALIGN(4) {
 		__abcrom_start = .;
 		KEEP(*(SORT_NONE(.dram.abcrom*)))
 		__abcrom_end = .;
-		/* Make sure this section is not empty */
-		LONG(0xffffffff)
-		LONG(0xffffffff)
-		LONG(0xffffffff)
-		LONG(0xffffffff)
 	} >DRAM
 
 	.dram.text : ALIGN(4) {

+ 9 - 1
rv32/romcopy.c

@@ -123,6 +123,12 @@ IRQHANDLER(romcopy,0)
 
     switch (romcopy_state++) {
     case 0:
+	/* Clear dram_io as quickly as possible */
+	len = __dram_io_end - __dram_io_start;
+	romcopy_bzero(__dram_io_start, len);
+	break;
+      
+    case 1:
 	/* Condition flash ROM */
 	romcopy_config_flash();
 
@@ -136,11 +142,13 @@ IRQHANDLER(romcopy,0)
 	/* Convert serial number and export to USB */
 	rom_mangle_serial();
 	break;
-    case 1:
+
+    case 2:
 	/* Zero .dram.bss */
 	len = __dram_bss_end - __dram_bss_start;
 	romcopy_bzero(__dram_bss_start, len);
 	break;
+
     default:
 	mask_irq(ROMCOPY_IRQ);
 	break;

+ 20 - 1
rv32/system.c

@@ -9,6 +9,17 @@
 #define MINITESTS 1
 #define DELAY     0
 
+#define DRAM_IO_MAGIC 0x3648dec4
+struct dram_io_head {
+    uint32_t magic;
+    size_t   hlen;
+    void    *dptr;
+    size_t   dlen;
+};
+struct dram_io_head __dram_io_head dram_io_head;
+
+uint32_t __dram_io dram_io[(65536 - sizeof(struct dram_io_head)) >> 2];
+
 void __hot con_print_hex(unsigned int n)
 {
     for (int i = 0; i < 8; i++) {
@@ -268,9 +279,17 @@ static void __noinline late_init(void)
 
     set_leds(1);
 
-
     abc_init();
 
+    /* Ready for communications */
+    dram_io_head.hlen  = sizeof dram_io_head;
+    dram_io_head.dptr  = dram_io;
+    dram_io_head.dlen  = sizeof dram_io;
+    dram_io_head.magic = DRAM_IO_MAGIC;
+
+    static const char dram_io_test[] = "Hej tomtebuggar slå i glasen!";
+    memcpy(dram_io_head.dptr, dram_io_test, sizeof dram_io_test);
+ 
     /* Release WAIT# if asserted */
     ABC_BUSCTL = 0;