diff --git a/docs/M3_2_SDRAM_TIMING_IMPLEMENTATION.md b/docs/M3_2_SDRAM_TIMING_IMPLEMENTATION.md new file mode 100644 index 0000000..453caa1 --- /dev/null +++ b/docs/M3_2_SDRAM_TIMING_IMPLEMENTATION.md @@ -0,0 +1,20 @@ +# M3.2 controller timing implementation + +The native controller now encodes the Rev-A **IS42S16320B-6TLI** 100 MHz / CL2 timing basis directly in RTL. + +Default guards: + +| Parameter | Default | 100 MHz interpretation | +|---|---:|---| +| POWERUP_CYCLES | 20000 | 200 us | +| TRP | 2 | 20 ns | +| TRCD | 2 | 20 ns | +| TRFC | 7 | 70 ns | +| TMRD | 2 | 2 clocks | +| TWR | 2 | 20 ns | +| CAS_LATENCY | 2 | CL2 | +| REFRESH_INTERVAL | 780 | 7.80 us | + +The refresh interval is deliberately below the 7.8125 us average implied by 8192 refreshes / 64 ms. + +The controller now has explicit post-MRS and write-recovery states. These defaults are qualification parameters for the selected part and clock; board-level timing and post-route timing remain separate gates. diff --git a/fpga/retrocore/platform/sdram_controller.v b/fpga/retrocore/platform/sdram_controller.v index 140a635..eafab33 100644 --- a/fpga/retrocore/platform/sdram_controller.v +++ b/fpga/retrocore/platform/sdram_controller.v @@ -5,9 +5,14 @@ module retrocore_sdram_controller #( parameter ADDR_WIDTH=25, parameter POWERUP_CYCLES=20000, - parameter TRP=2, parameter TRCD=2, parameter TRFC=7, + // IS42S16320B-6TLI conservative 100 MHz / CL2 timing basis. + parameter TRP=2, // tRP >= 18 ns -> 2 cycles + parameter TRCD=2, // tRCD >= 18 ns -> 2 cycles + parameter TRFC=7, // tRFC >= 60 ns; 7 cycles keeps margin + parameter TMRD=2, // mode-register-set to next command + parameter TWR=2, // write recovery before PRECHARGE parameter CAS_LATENCY=2, - parameter REFRESH_INTERVAL=780 + parameter REFRESH_INTERVAL=780 // 64 ms / 8192 = 7.8125 us -> <=781 cycles )( input wire clk,input wire reset, input wire req,input wire we,input wire [1:0] be, @@ -21,7 +26,7 @@ module retrocore_sdram_controller #( ); localparam ST_POWER=0,ST_PRE=1,ST_TRP=2,ST_REF1=3,ST_RFC1=4,ST_REF2=5,ST_RFC2=6, ST_MRS=7,ST_IDLE=8,ST_ACT=9,ST_TRCD=10,ST_READ=11,ST_CAS=12,ST_WRITE=13, - ST_PRECHARGE=14,ST_WAITRP=15,ST_REFRESH=16,ST_WAITRFC=17; + ST_PRECHARGE=14,ST_WAITRP=15,ST_REFRESH=16,ST_WAITRFC=17,ST_MRD=18,ST_WR=19; reg [4:0] state=ST_POWER; reg [31:0] timer=0,refresh=0; reg [ADDR_WIDTH-1:0] la=0; reg lwe=0; reg [1:0] lbe=0; reg [15:0] lwd=0; task nop; begin sdram_cs_n<=0;sdram_ras_n<=1;sdram_cas_n<=1;sdram_we_n<=1; end endtask @@ -36,7 +41,8 @@ always @(posedge clk) begin ST_RFC1: if(timer>=TRFC-1) begin timer<=0;state<=ST_REF2;end else timer<=timer+1; ST_REF2: begin sdram_ras_n<=0;sdram_cas_n<=0;timer<=0;state<=ST_RFC2;end ST_RFC2: if(timer>=TRFC-1) begin timer<=0;state<=ST_MRS;end else timer<=timer+1; - ST_MRS: begin sdram_ras_n<=0;sdram_cas_n<=0;sdram_we_n<=0;sdram_a<=13'b0000000100000;refresh<=0;state<=ST_IDLE;end + ST_MRS: begin sdram_ras_n<=0;sdram_cas_n<=0;sdram_we_n<=0;sdram_a<=13'b0000000100000;refresh<=0;timer<=0;state<=ST_MRD;end + ST_MRD: if(timer>=TMRD-1) begin timer<=0;state<=ST_IDLE;end else timer<=timer+1; ST_IDLE: begin if(refresh>=REFRESH_INTERVAL) begin refresh<=0;state<=ST_REFRESH;end else begin refresh<=refresh+1;if(req) begin la<=addr;lwe<=we;lbe<=be;lwd<=wdata;state<=ST_ACT;end end @@ -45,7 +51,8 @@ always @(posedge clk) begin ST_TRCD: if(timer>=TRCD-1) begin timer<=0;state<=lwe?ST_WRITE:ST_READ;end else timer<=timer+1; ST_READ: begin sdram_cas_n<=0;sdram_ba<=la[24:23];sdram_a<={3'b000,la[9:0]};sdram_dqm<=~lbe;timer<=0;state<=ST_CAS;end ST_CAS: if(timer>=CAS_LATENCY-1) begin rdata<=dq_in;timer<=0;state<=ST_PRECHARGE;end else timer<=timer+1; - ST_WRITE: begin sdram_cas_n<=0;sdram_we_n<=0;sdram_ba<=la[24:23];sdram_a<={3'b000,la[9:0]};sdram_dqm<=~lbe;dq_out<=lwd;dq_oe<=1;state<=ST_PRECHARGE;end + ST_WRITE: begin sdram_cas_n<=0;sdram_we_n<=0;sdram_ba<=la[24:23];sdram_a<={3'b000,la[9:0]};sdram_dqm<=~lbe;dq_out<=lwd;dq_oe<=1;timer<=0;state<=ST_WR;end + ST_WR: if(timer>=TWR-1) begin timer<=0;state<=ST_PRECHARGE;end else timer<=timer+1; ST_PRECHARGE: begin sdram_ras_n<=0;sdram_we_n<=0;sdram_a[10]<=1;timer<=0;state<=ST_WAITRP;end ST_WAITRP: if(timer>=TRP-1) begin ready<=1;timer<=0;state<=ST_IDLE;end else timer<=timer+1; ST_REFRESH: begin sdram_ras_n<=0;sdram_cas_n<=0;timer<=0;state<=ST_WAITRFC;end