浏览代码

esplink: handle arbitrary byte ranges; allow ESP to query status

Handle arbitrary byte ranges in esplink (possibly requiring additional
dummy cycles for a read, but that's ok.)

Use the "dummy" phase to transmit link status information for ESP.
H. Peter Anvin 2 年之前
父节点
当前提交
d8e0e2d6ac
共有 22 个文件被更改,包括 241 次插入108 次删除
  1. 12 2
      esp32/max80/esplink.h
  2. 71 26
      esp32/max80/fpgasvc.c
  3. 二进制
      esp32/output/max80.ino.bin
  4. 154 76
      fpga/esp.sv
  5. 2 2
      fpga/max80.qpf
  6. 二进制
      fpga/output/bypass.jic
  7. 二进制
      fpga/output/v1.fw
  8. 二进制
      fpga/output/v1.jic
  9. 二进制
      fpga/output/v1.rbf.gz
  10. 二进制
      fpga/output/v1.rpd.gz
  11. 二进制
      fpga/output/v1.sof
  12. 二进制
      fpga/output/v1.svf.gz
  13. 二进制
      fpga/output/v1.xsvf.gz
  14. 二进制
      fpga/output/v2.fw
  15. 二进制
      fpga/output/v2.jic
  16. 二进制
      fpga/output/v2.rbf.gz
  17. 二进制
      fpga/output/v2.rpd.gz
  18. 二进制
      fpga/output/v2.sof
  19. 二进制
      fpga/output/v2.svf.gz
  20. 二进制
      fpga/output/v2.xsvf.gz
  21. 1 1
      rv32/checksum.h
  22. 1 1
      rv32/esp.c

+ 12 - 2
esp32/max80/esplink.h

@@ -15,10 +15,20 @@ struct dram_io_head {
     size_t      hlen;
     void       *dptr;
     size_t      dlen;
-    uint32_t    board;
+    struct {
+	union {
+	    uint32_t    cfg;
+	    struct {
+		uint8_t fixes;
+		uint8_t minor;
+		uint8_t major;
+		uint8_t fpga;
+	    };
+	};
+    } board;
     const char *signature;
     size_t      signature_len;
-    uint32_t    resv[1];
+    uint32_t    resv[9];
 };
 
 #define RV_IRQ_UNDERRUN	0

+ 71 - 26
esp32/max80/fpgasvc.c

@@ -147,15 +147,41 @@ void fpga_service_stop(void)
 
 #define FPGA_HDR_ADDR	0x40000000
 
-static esp_err_t fpga_io(uint8_t cmd, uint32_t addr, void *data, size_t len)
+static esp_err_t fpga_io_write(uint8_t cmd, uint32_t addr,
+			       const void *data, size_t len)
 {
     spi_transaction_ext_t trans;
     esp_err_t err;
 
-    if ((len|addr) & 3) {
-	printf("[FPGA] ERROR: I/O must be aligned dwords\n");
-	return ESP_FAIL;
-    }
+    if (!len)
+	return ESP_OK;
+
+    memset(&trans, 0, sizeof trans);
+    trans.base.flags   =
+	SPI_TRANS_MODE_DIO |
+	SPI_TRANS_MULTILINE_CMD |
+	SPI_TRANS_MULTILINE_ADDR;
+
+    trans.base.cmd       = cmd | ~FPGA_CMD_RD;
+    trans.base.addr      = addr;
+    trans.base.tx_buffer = data;
+    trans.base.length    = len << 3;
+
+    xSemaphoreTake(spi_mutex, portMAX_DELAY);
+    err = spi_device_transmit(spi_handle, (spi_transaction_t *)&trans);
+    xSemaphoreGive(spi_mutex);
+
+    return err;
+}
+
+static esp_err_t fpga_io_read(uint8_t cmd, uint32_t addr,
+			      void *data, size_t len)
+{
+    spi_transaction_ext_t trans;
+    esp_err_t err;
+
+    if (!len)
+	return ESP_OK;
 
     memset(&trans, 0, sizeof trans);
     trans.base.flags   =
@@ -163,19 +189,13 @@ static esp_err_t fpga_io(uint8_t cmd, uint32_t addr, void *data, size_t len)
 	SPI_TRANS_VARIABLE_DUMMY |
 	SPI_TRANS_MULTILINE_CMD |
 	SPI_TRANS_MULTILINE_ADDR;
-    trans.command_bits =  8;
-    trans.address_bits = 32;
-
-    trans.base.cmd    = cmd;
-    trans.base.addr   = addr;
-    if (cmd & FPGA_CMD_RD) {
-	trans.dummy_bits     = 16; /* 16 cycles = 32 bits */
-	trans.base.rx_buffer = data;
-	trans.base.rxlength  = len << 3;
-    } else {
-	trans.base.tx_buffer = data;
-	trans.base.length    = len << 3;
-    }
+
+    trans.base.cmd       = cmd  | FPGA_CMD_RD;
+    trans.base.addr      = addr;
+    trans.base.rx_buffer = data;
+    /* Emulate partial word read by adding dummy bits for offset */
+    trans.dummy_bits     = 16 + ((addr & 3) << 2);
+    trans.base.rxlength  = len << 3;
 
     xSemaphoreTake(spi_mutex, portMAX_DELAY);
     err = spi_device_transmit(spi_handle, (spi_transaction_t *)&trans);
@@ -184,6 +204,27 @@ static esp_err_t fpga_io(uint8_t cmd, uint32_t addr, void *data, size_t len)
     return err;
 }
 
+static uint32_t fpga_io_status(void)
+{
+    spi_transaction_ext_t trans;
+    esp_err_t err;
+
+    memset(&trans, 0, sizeof trans);
+    trans.base.flags   =
+	SPI_TRANS_MODE_DIO |
+	SPI_TRANS_MULTILINE_CMD |
+	SPI_TRANS_MULTILINE_ADDR |
+	SPI_TRANS_USE_RXDATA;
+
+    trans.base.cmd       = FPGA_CMD_RD;
+    trans.base.rxlength  = 32;
+
+    xSemaphoreTake(spi_mutex, portMAX_DELAY);
+    err = spi_device_transmit(spi_handle, (spi_transaction_t *)&trans);
+    xSemaphoreGive(spi_mutex);
+
+    return err ? 0 : ntohl(*(const uint32_t *)&trans.base.rx_data);
+}
 
 static void fpga_service_task(void *dummy)
 {
@@ -191,23 +232,27 @@ static void fpga_service_task(void *dummy)
     struct dram_io_head head;
 
     /* If the FPGA is already up, need to issue our own active handshake */
-    fpga_io(FPGA_CMD_WR|FPGA_CMD_IRQ(RV_IRQ_HELLO), 0, NULL, 0);
+    fpga_io_write(FPGA_CMD_IRQ(RV_IRQ_HELLO), 0, NULL, 0);
 
     while (1) {
 	while (!digitalRead(PIN_FPGA_INT)) {
 	    printf("[FPGA] FPGA signals ready\n");
 
-	    fpga_io(FPGA_CMD_RD|FPGA_CMD_ACK(ESP_IRQ_READY), FPGA_HDR_ADDR,
-		    &head, sizeof head);
+	    uint32_t status = fpga_io_status();
+	    printf("[FPGA] Link status bits = 0x%08x\n", status);
+	    
+	    fpga_io_read(FPGA_CMD_ACK(ESP_IRQ_READY), FPGA_HDR_ADDR,
+			 &head, sizeof head);
 
 	    if (head.magic == DRAM_IO_MAGIC && head.hlen >= sizeof head) {
-		printf("[FPGA] Ready, board = %u.%u\n",
-		       (uint8_t)(head.board >> 24), (uint8_t)(head.board >> 16));
+		printf("[FPGA] Ready, board = %u.%u fixes %02x fpga %u\n",
+		       head.board.major, head.board.minor,
+		       head.board.fixes, head.board.fpga);
 	    }
 
-	    char signature_string[head.signature_len + 3];
-	    fpga_io(FPGA_CMD_RD, (size_t)head.signature,
-		    signature_string, (head.signature_len + 3) & ~3);
+	    char signature_string[head.signature_len+1];
+	    fpga_io_read(0, (size_t)head.signature,
+			 signature_string, head.signature_len);
 	    signature_string[head.signature_len] = '\0';
 
 	    printf("[FPGA] \"%s\"\n", signature_string);

二进制
esp32/output/max80.ino.bin


+ 154 - 76
fpga/esp.sv

@@ -16,44 +16,60 @@
 // 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.
+// A 32-bit address follows; for a read, the following 16 cycles
+// contains dummy/status data:
 //
-module esp (
-	    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,
+// Bit [31:16] = adjusted memory address
+// Bit [15:14] = 2'b10
+// Bit [13: 8] = 0 reserved
+// Bit [ 7: 5] = upstream interrupt status
+// Bit      4  = 0 reserved
+// Bit [ 3: 1] = downstream interrupt status
+// Bit      0  = underrun error
+//
+module esp #(
+	     parameter        dram_bits = 25,
+	     parameter [31:0] dram_base = 32'h40000000
+	     ) (
+		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
-	    );
-
-   reg  [24:2] 		  mem_addr;
-   reg 			  mem_valid;
-   reg  [31:0] 		  mem_wdata;
-   wire			  mem_write;
-   wire 		  mem_ready;
-   wire [31:0] 		  mem_rdata;
-   
+		output reg    esp_int,
+		input 	      spi_clk,
+		inout [1:0]   spi_io,
+		input 	      spi_cs_n
+		);
+
+   reg  [31:0] 		      mem_addr = 'b0;
+   wire [31:0] 		      mem_addr_mask = (1'b1 << dram_bits) - 3'd4;
+   wire [31:0] 		      mem_addr_out = (mem_addr & mem_addr_mask)
+			      | dram_base;
+
+   reg 			      mem_valid;
+   reg [31:0] 		      mem_wdata;
+   wire 		      mem_write;
+   reg [ 3:0] 		      mem_wstrb;
+   wire 		      mem_ready;
+   wire [31:0] 		      mem_rdata;
+
    dram_port #(32) mem
      (
       .bus   ( dram ),
       .prio  ( 2'd2 ),
-      .addr  ( {mem_addr, 2'b00} ),
+      .addr  ( mem_addr[dram_bits-1:0] ),
       .valid ( mem_valid ),
       .wd    ( mem_wdata ),
-      .wstrb ( {4{mem_wrq}} ),
+      .wstrb ( mem_wstrb ),
       .ready ( mem_ready ),
       .rd    ( mem_rdata )
       );
@@ -82,8 +98,11 @@ module esp (
    reg [ 3:0] spi_ctr;
    reg [ 3:0] cpu_irq;
    reg [ 3:1] spi_irq;
+   reg [ 3:1] latched_spi_irq;
    reg [ 1:0] spi_out;
    reg 	      spi_oe;
+   reg [ 2:0] spi_wbe;		// Partial word write byte enables
+   reg [23:0] spi_wdata;	// Partial word write data
 
    assign spi_io = spi_oe ? spi_out : 2'bzz;
 
@@ -102,6 +121,10 @@ module esp (
 	  cpu_irq   <= 'b0;
 	  spi_irq   <= 'b0;
 	  spi_oe    <= 1'b0;
+	  spi_wbe   <= 3'b0;
+	  mem_addr  <= 'b0;
+	  mem_wstrb <= 4'b0;
+	  mem_valid <= 1'b0;
        end
      else
        begin
@@ -112,55 +135,100 @@ module esp (
 	       spi_state  <= st_cmd;
 	       spi_ctr    <= 4'd3;
 	       spi_oe     <= 1'b0;
-	       spi_cmd    <= 'b0;
-	       for (int i = 1; i < 4; i++)
-		 if (spi_cmd[1:0] == i)
-		   cpu_irq[i] <= 1'b1;
+	       if (~mem_valid)
+		 begin
+		    spi_cmd <= 'b0;
+		    for (int i = 1; i < 4; i++)
+		      if (spi_cmd[1:0] == i)
+			cpu_irq[i] <= 1'b1;
+		 end
 	    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
-		    // Transfer data to/from memory controller, but
-		    // we have to shuffle endianness...
-		    spi_shr[31:24]   <= mem_rdata[ 7: 0];
-		    spi_shr[23:16]   <= mem_rdata[15: 8];
-		    spi_shr[15: 8]   <= mem_rdata[23:16];
-		    spi_shr[ 7: 0]   <= mem_rdata[31:24];
-
-		    mem_wdata[31:24] <= spi_indata[ 7: 0];
-		    mem_wdata[23:16] <= spi_indata[15: 8];
-		    mem_wdata[15: 8] <= spi_indata[23:16];
-		    mem_wdata[ 7: 0] <= spi_indata[31:24];
-
-		    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
+	       spi_shr <= spi_indata;
+
+	       case (spi_ctr)
+		 4'b1100:
+		   if (spi_state == st_io && mem_write)
+		     begin
+			spi_wbe[0]      <= 1'b1;
+			spi_wdata[7:0]  <= spi_indata[7:0];
+		     end
+		 4'b1000:
+		   if (spi_state == st_io && mem_write)
+		     begin
+			spi_wbe[1]      <= 1'b1;
+			spi_wdata[15:8] <= spi_indata[7:0];
+		     end
+		 4'b0100:
+		   if (spi_state == st_io && mem_write)
+		     begin
+			spi_wbe[2]       <= 1'b1;
+			spi_wdata[23:16] <= spi_indata[7:0];
+		     end
+		 4'b0000: begin
+		       // Transfer data to/from memory controller, but
+		       // we have to shuffle endianness...
+		       if (spi_state == st_io)
+			 begin
+			    // Memory output
+			    spi_shr[31:24]  <= mem_rdata[ 7: 0];
+			    spi_shr[23:16]  <= mem_rdata[15: 8];
+			    spi_shr[15: 8]  <= mem_rdata[23:16];
+			    spi_shr[ 7: 0]  <= mem_rdata[31:24];
+			 end
+		       else
+			 begin
+			    // Status output
+			    spi_shr[31:16]  <= mem_addr_out[31:16];
+			    spi_shr[15: 8]  <= 8'h80;
+			    spi_shr[ 7: 4]  <= { latched_spi_irq, 1'b0 };
+			    spi_shr[ 3: 0]  <= cpu_irq;
+			 end // else: !if(spi_state == st_io)
+
+		       if (mem_valid && spi_state != st_cmd)
+			 cpu_irq[0] <= 1'b1; // Overrun/underrun
+
+		       case (spi_state)
+			 st_cmd: begin
+			    spi_cmd         <= spi_indata[5:0];
+			    spi_state       <= st_addr;
+			    latched_spi_irq <= spi_irq;
+			    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 & mem_addr_mask;
+			    spi_state <= st_io;
+			    mem_valid <= ~mem_write;
+			    mem_wstrb <= 4'b0;
+			    spi_wbe   <= 3'b000;
+			    // If the first word is partial, skip ahead
+			    if (mem_write)
+			      spi_ctr[3:2] <= ~spi_indata[1:0];
+			 end
+			 st_io: begin
+			    if (mem_write)
+			      begin
+				 mem_wdata[23: 0] <= spi_wdata[23:0];
+				 mem_wdata[31:24] <= spi_indata[7:0];
+				 mem_wstrb <= { 1'b1, spi_wbe };
+			      end
+			    else
+			      begin
+				 mem_wstrb <= 4'b0000;
+			      end // else: !if(mem_write)
+			    mem_valid <= 1'b1;
+			    spi_wbe   <= 3'b000;
+			 end
+		       endcase
+		    end // case: 4'b0000
+		 default:
+		   ;		// Nothing
+	       endcase // case (spi_ctr)
+	    end // if (spi_clk_q == 2'b01)
 	  else if (spi_clk_q == 2'b10)
 	    begin
 	       spi_out <= spi_shr[31:30];
@@ -169,8 +237,18 @@ module esp (
 
 	  if (mem_valid & mem_ready)
 	    begin
+	       mem_addr  <= mem_addr + 3'd4;
 	       mem_valid <= 1'b0;
-	       mem_addr  <= mem_addr + 1'b1;
+	    end
+
+	  if (spi_state != st_io & ~mem_valid & |spi_wbe)
+	    begin
+	       // Complete a partial write terminated by CS#
+	       mem_valid        <= 1'b1;
+	       mem_wstrb        <= { 1'b0, spi_wbe };
+	       mem_wdata[23:0]  <= spi_wdata[23:0];
+	       mem_wdata[31:24] <= 8'hxx;
+	       spi_wbe          <= 3'b000;
 	    end
 
 	  cpu_valid_q <= cpu_valid;

+ 2 - 2
fpga/max80.qpf

@@ -19,12 +19,12 @@
 #
 # Quartus Prime
 # Version 21.1.0 Build 842 10/21/2021 SJ Lite Edition
-# Date created = 13:46:46  May 02, 2022
+# Date created = 17:39:37  May 02, 2022
 #
 # -------------------------------------------------------------------------- #
 
 QUARTUS_VERSION = "21.1"
-DATE = "13:46:46  May 02, 2022"
+DATE = "17:39:37  May 02, 2022"
 
 # Revisions
 

二进制
fpga/output/bypass.jic


二进制
fpga/output/v1.fw


二进制
fpga/output/v1.jic


二进制
fpga/output/v1.rbf.gz


二进制
fpga/output/v1.rpd.gz


二进制
fpga/output/v1.sof


二进制
fpga/output/v1.svf.gz


二进制
fpga/output/v1.xsvf.gz


二进制
fpga/output/v2.fw


二进制
fpga/output/v2.jic


二进制
fpga/output/v2.rbf.gz


二进制
fpga/output/v2.rpd.gz


二进制
fpga/output/v2.sof


二进制
fpga/output/v2.svf.gz


二进制
fpga/output/v2.xsvf.gz


+ 1 - 1
rv32/checksum.h

@@ -1,4 +1,4 @@
 #ifndef CHECKSUM_H
 #define CHECKSUM_H
-#define SDRAM_SUM 0x1ec13164
+#define SDRAM_SUM 0x8381a3d3
 #endif

+ 1 - 1
rv32/esp.c

@@ -32,7 +32,7 @@ void esp_init(void)
     dram_io_head.hlen          = sizeof dram_io_head;
     dram_io_head.dptr          = dram_io;
     dram_io_head.dlen          = sizeof dram_io;
-    dram_io_head.board         = SYS_BOARDCFG;
+    dram_io_head.board.cfg     = SYS_BOARDCFG;
     dram_io_head.signature     = esp_signature;
     dram_io_head.signature_len = sizeof esp_signature - 1;
     memset(&dram_io_head.resv, 0, sizeof dram_io_head.resv);