Browse Source

sdram: return start to the right subunit; add ABC-bus latency counter

Return "start" to the right subunit, which is the requestor selected
by the state machine in the *previous* cycle. Otherwise we can get
some really weird results.

Add a latency counter for ABC-bus memory accesses. They still seem to
be unreasonable (110 SDRAM cycles = 655 ns), however, we still seem to
get the correct data at least more or less all the time?
H. Peter Anvin 3 years ago
parent
commit
0271d1979a
12 changed files with 1321 additions and 1227 deletions
  1. 28 2
      fpga/abcbus.sv
  2. 3 3
      fpga/max80.qpf
  3. BIN
      fpga/output/v1.jic
  4. BIN
      fpga/output/v1.sof
  5. BIN
      fpga/output/v2.jic
  6. BIN
      fpga/output/v2.sof
  7. 40 10
      fpga/sdram.sv
  8. 41 16
      fpga/usb/usb_serial/src_v/usb_cdc_core.sv
  9. 1196 1196
      rv32/boot.mif
  10. 1 0
      rv32/ioregs.h
  11. 12 0
      rv32/main.c
  12. BIN
      rv32/roms/ufddos80.rom

+ 28 - 2
fpga/abcbus.sv

@@ -403,10 +403,14 @@ module abcbus (
      else
        begin
 	  abc_d_oe <= 1'b0;
-	  abc_do   <= 8'bx;
+	  abc_do   <= sdram_rd;
 
-	  if (abc_do_memrd & sdram_ready)
+	  if (abc_do_memrd)
 	    begin
+	       // Drive the output bus even if sdram_rd doesn't yet have
+	       // valid data (i.e. sdram_ready = 0).
+	       // The propagation delay for OE#/DIR for 74HC245 is about
+	       // twice what it is for data.
 	       abc_d_oe <= 1'b1;
 	       abc_do   <= sdram_rd;
 	    end
@@ -422,6 +426,27 @@ module abcbus (
 	    end
        end // else: !if(~rst_n)
 
+   // Memory read latency counter
+   reg [15:0] memrd_latency_ctr = 'b0;
+   reg [15:0] memrd_latency_max = 'b0;
+
+   wire [15:0] memrd_latency_ctr_next = memrd_latency_ctr + 1'b1;
+
+   always @(posedge sdram_clk)
+     begin
+	if (abc_do_memrd)
+	  begin
+	     memrd_latency_ctr <= memrd_latency_ctr_next;
+	     if (memrd_latency_max == memrd_latency_ctr)
+	       memrd_latency_max <= memrd_latency_ctr_next;
+	  end // else: !if(~abc_do_memrd)
+	else
+	  begin
+	     memrd_latency_ctr <= 'b0;
+	  end
+     end // always @ (posedge sdram_clk)
+
+
    // Bus status
    reg  [3:0] abc_status[0:1];
 
@@ -513,6 +538,7 @@ module abcbus (
        5'b00011: cpu_rdata = { 28'b0, abc_resin, abc_nmi, abc_int, abc_wait };
        5'b00100: cpu_rdata = { 21'b0, reg_out_addr, reg_out_data };
        5'b00101: cpu_rdata = { 14'b0, inp_en, reg_inp_data[1], reg_inp_data[0] };
+       5'b00111: cpu_rdata = { 16'b0, memrd_latency_max };
        default:  cpu_rdata = 32'bx;
      endcase // casez (cpu_addr[5:2])
 

+ 3 - 3
fpga/max80.qpf

@@ -19,14 +19,14 @@
 #
 # Quartus Prime
 # Version 21.1.0 Build 842 10/21/2021 SJ Lite Edition
-# Date created = 11:50:23  January 11, 2022
+# Date created = 12:32:07  January 12, 2022
 #
 # -------------------------------------------------------------------------- #
 
 QUARTUS_VERSION = "21.1"
-DATE = "11:50:23  January 11, 2022"
+DATE = "12:32:07  January 12, 2022"
 
 # Revisions
 
-PROJECT_REVISION = "v2"
 PROJECT_REVISION = "v1"
+PROJECT_REVISION = "v2"

BIN
fpga/output/v1.jic


BIN
fpga/output/v1.sof


BIN
fpga/output/v2.jic


BIN
fpga/output/v2.sof


+ 40 - 10
fpga/sdram.sv

@@ -40,16 +40,16 @@
 // is "start". All other signals are broadcast.
 //
 interface dram_bus;
-   logic [1:0]	prio;		// Priority vs refresh
+   logic  [1:0]	prio;		// Priority vs refresh
    logic        rst_n;
    logic        clk;
-   tri   [24:0] addr;
+   logic [24:0] addr;
    logic	addr0;		// addr[0] latched at transaction start
    logic [15:0] rd;
    logic	req;
    logic  [1:0]	rstrb;		// Data read strobe
-   tri   [31:0] wd;
-   tri    [3:0] wstrb;
+   logic [31:0] wd;
+   logic  [3:0] wstrb;
    logic	start;		// Transaction start
    logic	wrack;		// Transaction is a write
 
@@ -173,9 +173,20 @@ module dram_arbiter
     output logic do_rfsh
     );
 
+   logic [31:0]  u_wd[1:port_count];
+   logic  [3:0]  u_wstrb[1:port_count];
+   logic [24:0]  u_addr[1:port_count];
+
    logic [port_count:0] grant;
    assign grant[0] = 1'b0;	// Dummy to make the below logic simpler
 
+   reg [port_count:0]	grant_q;
+   always @(negedge dstr.rst_n or posedge dstr.clk)
+     if (~dstr.rst_n)
+       grant_q <= 'b0;
+     else
+       grant_q <= grant;
+
    generate
       genvar i;
       for (i = 1; i <= port_count; i++)
@@ -190,15 +201,34 @@ module dram_arbiter
 	   assign grant[i] = ~|grant[i-1:0] & ustr[i].req &
 			     (ustr[i].prio >= rfsh_prio);
 
-	   assign ustr[i].start = grant[i] & dstr.start;
-	   assign dstr.addr     = grant[i] ? ustr[i].addr  : 'bz;
-	   assign dstr.wd       = grant[i] ? ustr[i].wd    : 'bz;
-	   assign dstr.wstrb    = grant[i] ? ustr[i].wstrb : 'bz;
+	   assign u_addr[i]     = ustr[i].addr;
+	   assign u_wd[i]       = ustr[i].wd;
+	   assign u_wstrb[i]    = ustr[i].wstrb;
+
+	   // Note: start indicates that the requestor from the *previous*
+	   // cycle was started.
+	   assign ustr[i].start = grant_q[i] & dstr.start;
 	end // block: u
    endgenerate
 
-   assign dstr.req =  |grant;
-   assign do_rfsh  = ~|grant & |rfsh_prio;
+   always_comb
+     begin
+	dstr.addr  = 'bx;
+	dstr.wd    = 'bx;
+	dstr.wstrb = 4'b0;
+	dstr.req   = 1'b0;
+	do_rfsh    = |rfsh_prio;
+
+	for (int j = 1; j <= port_count; j++)
+	  if (grant[j])
+	       begin
+		  dstr.addr  = u_addr[j];
+		  dstr.wd    = u_wd[j];
+		  dstr.wstrb = u_wstrb[j];
+		  dstr.req   = 1'b1;
+		  do_rfsh    = 1'b0;
+	       end
+     end // always_comb
 endmodule // dram_arbiter
 
 module sdram

+ 41 - 16
fpga/usb/usb_serial/src_v/usb_cdc_core.sv

@@ -45,11 +45,11 @@
 //        11:9 - receive FIFO low watermark   (resets to 1/4)
 //       15:13 - receive FIFO high watermark  (resets to 3/4)
 // 2 - RO - status register (some bits W1C)
-//           0 - transmit FIFO empty
+//           0 - transmit FIFO empty (write 1 to flush Tx data)
 //           1 - transmit FIFO <= low watermark
 //           2 - transmit FIFO >= high watermark
 //           3 - transmit FIFO full
-//           4 - receive FIFO empty
+//           4 - receive FIFO empty (write 1 to flush Rx data)
 //           5 - receive FIFO <= low watermark
 //           6 - receive FIFO >= high watermark
 //           7 - receive FIFO full
@@ -98,11 +98,16 @@ module usb_cdc_channel
    input	 start_of_frame_s
    );
 
-   localparam fifo_size   = 1024;
-   localparam fifo_bits   = $clog2(fifo_size);
+   localparam fifo_bits   = 10;
+   localparam fifo_size   = 1 << fifo_bits;
    localparam packet_bits = 6;
    localparam packet_size = 1 << packet_bits;
 
+   // Control line status in the USB domain
+   reg [1:0]   control_lines_q;
+   wire        inport_dtr_w = control_lines_q[0];
+   wire        inport_rts_w = control_lines_q[1];
+
    // Up to four bits per watermark, but all are not necessarily
    // implemented
    localparam water_bits  = 3;
@@ -123,8 +128,11 @@ module usb_cdc_channel
    wire [water_bits:0]	txused_msb = txused[fifo_bits-1:fifo_bits-water_bits];
    wire		        inport_empty_w;
    reg			inport_valid_q;
+   reg			flush_tx_data;
 
    cdc_fifo txfifo (
+		    .aclr    ( flush_tx_data ),
+
 		    .wrclk   ( sys_clk ),
 		    .data    ( cpu_wdata[7:0] ),
 		    .wrreq   ( fifo_access & ~fifo_access_q & cpu_wstrb[0] ),
@@ -142,10 +150,13 @@ module usb_cdc_channel
 		    .rdusedw ( )
 		    );
 
+   // If RTS is not asserted, suspend data transmit
+   wire inport_has_data = ~inport_empty_w & inport_rts_w;
+
    always @(posedge clk_i or posedge rst_i)
      if (rst_i)
        inport_valid_q <= 1'b0;
-     else if ( ~inport_empty_w )
+     else if ( inport_has_data )
        inport_valid_q <= 1'b1;
      else if ( data_ep.u.tx_data_accept )
        inport_valid_q <= 1'b0;
@@ -156,7 +167,7 @@ module usb_cdc_channel
 
    // Must terminate a transfer at the max packet size
    reg [packet_bits-1:0] inport_cnt_q;
-   wire			 inport_last_w = inport_empty_w | &inport_cnt_q;
+   wire			 inport_last_w = ~inport_has_data | &inport_cnt_q;
 
    always @(posedge clk_i or posedge rst_i)
      if (rst_i)
@@ -171,15 +182,21 @@ module usb_cdc_channel
    wire [7:0]	        rdata_fifo;
    wire		        rxempty;
    wire		        rxfull;
+   reg			flush_rx_data;
    wire [fifo_bits-1:0] rxused;
    wire [water_bits:0]	rxused_msb = rxused[fifo_bits-1:fifo_bits-water_bits];
    wire [fifo_bits-1:0] outport_used_w;
+   wire			outport_full_w;
 
    wire			outport_valid_w = data_ep.u.rx_valid & data_ep.uc.rx_strb;
 
-   assign data_ep.d.rx_space = outport_used_w <= fifo_size - packet_size;
+   // Should be space for a max-sized packet before allowing input
+   assign data_ep.d.rx_space = ~outport_full_w &
+			       (outport_used_w < fifo_size - packet_size);
 
    cdc_fifo rxfifo (
+		    .aclr    ( flush_rx_data ),
+
 		    .rdclk   ( sys_clk ),
 		    .q       ( rdata_fifo ),
 		    .rdreq   ( fifo_access & ~fifo_access_q & ~|cpu_wstrb ),
@@ -191,7 +208,7 @@ module usb_cdc_channel
 		    .data    ( data_ep.uc.rx_data ),
 		    .wrreq   ( outport_valid_w ),
 		    .wrempty ( ),
-		    .wrfull  ( ),
+		    .wrfull  ( outport_full_w ),
 		    .wrusedw ( outport_used_w )
 		    );
 
@@ -223,7 +240,6 @@ module usb_cdc_channel
       .q     ( recv_break_s )
       );
 
-   reg [1:0]   control_lines_q;
    wire [1:0]  control_lines_s;
    always @(posedge rst_i or posedge clk_i)
      if (rst_i)
@@ -244,15 +260,19 @@ module usb_cdc_channel
    always @(negedge rst_n or posedge sys_clk)
      if (~rst_n)
        begin
-	  water_ctl   <= 16'hc3c3 & water_ctl_mask;
-	  irq_mask     <= 16'b0;
-	  irq_pol      <= 16'b0;
-	  recv_break_q <= 1'b0;
+	  water_ctl     <= 16'hc3c3 & water_ctl_mask;
+	  irq_mask      <= 16'b0;
+	  irq_pol       <= 16'b0;
+	  recv_break_q  <= 1'b0;
+	  flush_rx_data <= 1'b0;
+	  flush_tx_data <= 1'b0;
        end
      else
        begin
-	  if (recv_break_s)
-	    recv_break_q <= 1'b1;
+	  flush_rx_data <= flush_rx_data & ~rxempty;
+	  flush_tx_data <= flush_tx_data & ~txempty;
+
+	  recv_break_q  <= recv_break_q | recv_break_s;
 
 	  if (cpu_valid)
 	    case (cpu_addr)
@@ -263,8 +283,13 @@ module usb_cdc_channel
 		     water_ctl[15:8] <= cpu_wdata[15:8] & water_ctl_mask[15:8];
 		end
 	      2'b10: begin
+		 if (cpu_wstrb[0] & cpu_wdata[0])
+		   flush_tx_data <= ~txempty;
+		 if (cpu_wstrb[0] & cpu_wdata[4])
+		   flush_rx_data <= ~rxempty;
+
 		 if (cpu_wstrb[1] & cpu_wdata[9])
-		   recv_break_q <= 1'b0;
+		   recv_break_q <= recv_break_s;
 	      end
 	      2'b11: begin
 		 if (cpu_wstrb[0])

File diff suppressed because it is too large
+ 1196 - 1196
rv32/boot.mif


+ 1 - 0
rv32/ioregs.h

@@ -128,6 +128,7 @@
 #define ABC_INP0_DATA		IODEVB0(ABC,5)
 #define ABC_INP1_DATA		IODEVB1(ABC,5)
 #define ABC_INP_ENABLE		IODEVB2(ABC,5)
+#define ABC_LATENCY		IODEVRL(ABC,7)
 
 /* n = 0 ... 511 */
 #define ABCMEMMAP_PAGE(n)	IODEVL(ABCMEMMAP,n)

+ 12 - 0
rv32/main.c

@@ -8,6 +8,9 @@ volatile bool dont_gc = false;	/* Keep things from linker gc */
 
 void __hot main(void)
 {
+    unsigned int max_abc_latency = 0;
+    unsigned int abc_latency;
+
     init();
 
     if (dont_gc)
@@ -15,8 +18,17 @@ void __hot main(void)
 
     while (1) {
 	wait_for_irq();
+
 	if (unlikely(do_write_rtc))
 	    write_rtc();
+
 	abcdisk_io_poll();
+
+	abc_latency = ABC_LATENCY;
+	if (abc_latency > max_abc_latency) {
+	    max_abc_latency = abc_latency;
+	    con_printf("\nWorst ABC-bus latency: %u SDRAM cycles\n",
+		       abc_latency);
+	}
     }
 }

BIN
rv32/roms/ufddos80.rom


Some files were not shown because too many files changed in this diff