commit fe0d0a7e943c0a82de682ec89cfff40e72c6a217
parent b1ffb705c7ab63c9f1edbe9e11c1535beab5301c
Author: Brian Swetland <swetland@frotz.net>
Date: Tue, 4 Feb 2020 04:02:43 -0800
sdram: even more work
- correctly set MODE bits
- generate mode bits and read delays from T_RCD parameter
- correct external clock to align with data
- various testbench changes
- adjust sim sdram to behave more like real hardware
Diffstat:
6 files changed, 222 insertions(+), 55 deletions(-)
diff --git a/hdl/sdram/sdram.sv b/hdl/sdram/sdram.sv
@@ -74,6 +74,14 @@ localparam XWIDTH = (ROWBITS + BANKBITS + COLBITS);
localparam BANKCOUNT = (1 << BANKBITS);
+// validate T_RCD and adjust MODE bits and read latency configuration
+if ((T_RCD != 2) && (T_RCD != 3))
+ $error("T_RCD must be 2 or 3");
+
+localparam PMSB = T_RCD;
+localparam [1:0]SDRAM_CL = T_RCD;
+localparam [9:0]SDRAM_MODE = { 4'b000, SDRAM_CL, 4'b0000 };
+
integer i; // used by various for loops
// split input address into bank, row, col
@@ -156,12 +164,10 @@ localparam CMD_READ = 3'b101; // BA*=bankno, A10=AP, COLADDR
localparam CMD_STOP = 3'b110;
localparam CMD_NOP = 3'b111;
-// TODO CL2 vs CL3 configurability here and elsewhere
-
-reg [3:0]rd_pipe_rdy = 0;
-reg [3:0]rd_pipe_bsy = 0;
-reg [3:0]rd_pipe_rdy_next;
-reg [3:0]rd_pipe_bsy_next;
+reg [PMSB:0]rd_pipe_rdy = 0;
+reg [PMSB:0]rd_pipe_bsy = 0;
+reg [PMSB:0]rd_pipe_rdy_next;
+reg [PMSB:0]rd_pipe_bsy_next;
reg [3:0]burst = 0;
reg [3:0]burst_next;
@@ -215,8 +221,8 @@ always_comb begin
// read pipe regs track inbound read data (rdy)
// and hold off writes (bsy) to avoid bus conflict
- rd_pipe_rdy_next = { 1'b0, rd_pipe_rdy[3:1] };
- rd_pipe_bsy_next = { 1'b0, rd_pipe_bsy[3:1] };
+ rd_pipe_rdy_next = { 1'b0, rd_pipe_rdy[PMSB:1] };
+ rd_pipe_bsy_next = { 1'b0, rd_pipe_bsy[PMSB:1] };
if (rd_pipe_rdy[0]) begin
rd_rdy_next = 1;
@@ -230,7 +236,6 @@ always_comb begin
state_next = INIT0;
end
IDLE: begin
- data_oe_next = 0;
if (refresh_done) begin
// refresh counter has expired, precharge all and refresh
state_next = REFRESH;
@@ -239,7 +244,8 @@ always_comb begin
io_sel_a10_next = 1; // ALL BANKS
//count_next = T_RP - 2;
count_next[T_RP-2] = 1; count_done_next = 0;
- end else if (rd_req) begin
+ end else
+ if (rd_req) begin
io_do_rd_next = 1;
io_addr_next = rd_addr;
burst_next = rd_len;
@@ -266,8 +272,8 @@ always_comb begin
cmd_next = CMD_READ;
io_sel_row_next = 0; // column addr
io_sel_a10_next = 0; // no auto precharge
- rd_pipe_rdy_next = { 1'b1, rd_pipe_rdy[3:1] };
- rd_pipe_bsy_next = 4'b1111;
+ rd_pipe_rdy_next = { 1'b1, rd_pipe_rdy[PMSB:1] };
+ rd_pipe_bsy_next = { PMSB + 1 { 1'b1 } };
state_next = (burst != 4'd0) ? READ : IDLE;
end
end
@@ -285,7 +291,12 @@ always_comb begin
io_sel_row_next = 0; // column addr
io_sel_a10_next = 0; // no auto precharge
data_oe_next = 1;
- state_next = (burst != 4'd0) ? WRITE : IDLE;
+ if (burst != 4'd0) begin
+ state_next = WRITE;
+ end else begin
+ state_next = IDLE;
+ count_next[2] = 1; count_done_next = 0; // WR?
+ end
end
end
ACTIVE: begin
@@ -306,18 +317,20 @@ always_comb begin
// column addressing pre-selected from initial read
io_addr_next[COLBITS-1:0] = io_col_add1;
cmd_next = CMD_READ;
- rd_pipe_rdy_next = { 1'b1, rd_pipe_rdy[3:1] };
- rd_pipe_bsy_next = 4'b1111;
+ rd_pipe_rdy_next = { 1'b1, rd_pipe_rdy[PMSB:1] };
+ rd_pipe_bsy_next = { PMSB + 1 { 1'b1 } };
end
WRITE: begin
if (burst_done) begin
state_next = IDLE;
+ count_next[2] = 1; count_done_next = 0; // WR?
end else begin
burst_next = burst_sub1;
end
// column addressing pre-selected from initial write
io_addr_next[COLBITS-1:0] = io_col_add1;
cmd_next = CMD_WRITE;
+ data_o_next = wr_data; // handshake?
data_oe_next = 1;
end
INIT0: if (refresh_done) begin
@@ -333,8 +346,8 @@ always_comb begin
cmd_next = CMD_SET_MODE;
//count_next = T_MRD - 2;
count_next[T_MRD-2] = 1; count_done_next = 0;
- // r/w burst off, cas lat 3, sequential addr
- io_addr_next[XWIDTH-1:COLBITS] = { {(AWIDTH - 10){1'b0}}, 10'b0000110000};
+ io_addr_next[XWIDTH-1:COLBITS] =
+ { {ROWBITS-10{1'b0}}, SDRAM_MODE, {BANKBITS{1'b0}} };
io_sel_row_next = 1; // row addressing
end
INIT2: begin
@@ -364,6 +377,7 @@ always_ff @(posedge clk) begin
state <= reset ? START : state_next;
count <= count_next;
count_done <= count_done_next;
+ burst <= burst_next;
refresh <= refresh_next;
cmd <= cmd_next;
io_do_rd <= io_do_rd_next;
diff --git a/hdl/sdram/sdram_glue_ecp5.sv b/hdl/sdram/sdram_glue_ecp5.sv
@@ -32,8 +32,8 @@ ODDRX1F clock_ddr (
.Q(pin_clk),
.SCLK(clk),
.RST(0),
- .D0(0),
- .D1(1)
+ .D0(1),
+ .D1(0)
);
genvar n;
diff --git a/hdl/sdram/test.asm b/hdl/sdram/test.asm
@@ -11,24 +11,51 @@ rdc 1234
addr 20
rdc aa55
+#trigger
auto+
-addr 0
-wrp .15
+addr 80
+wrp 100
show 42
p1rst
-addr 0
-rdp .15
+addr 80
+rdp 100
show 43
show e0
p1rst
-addr 0
+
+#trigger
+addr f0
+wri 8888
+
+addr 80
show e1
-rdf .15
+rdf 100
show e2
wait .30
-verify .15
+verify 100
show e3
show ff
+
+show 00
+show 00
+#dump .250
+
+p1rst
+addr 80
+show bb
+rdb .15
+wait .30
+verify .15
+show b0
+
+addr 1000
+p0rst
+#wrp ffff
+
+addr 1000
+p1rst
+#rdp ffff
+show a7
diff --git a/hdl/sdram/testbench.sv b/hdl/sdram/testbench.sv
@@ -35,7 +35,7 @@ wire rd_ack;
wire [15:0]rd_data;
wire rd_rdy;
-reg [15:0]wr_len = 0;
+reg [3:0]wr_len = 0;
reg wr_req = 0;
wire wr_ack;
@@ -59,19 +59,69 @@ reg [31:0]scratch[0:511];
reg [8:0]swraddr = 0;
reg [8:0]srdaddr = 0;
reg [31:0]srddata = 0;
+reg [8:0]swraddr_next;
+reg [8:0]srdaddr_next;
-reg sreset;
-reg srd;
-always @(posedge clk) begin
- swraddr <= sreset ? 0 : (rd_rdy ? swraddr + 9'd1 : swraddr);
- srdaddr <= sreset ? 0 : (srd ? srdaddr + 9'd1 : srdaddr);
+reg sreset = 0;
+reg srd = 0;
+
+reg sreset_next;
+reg srd_next;
+
+always_comb begin
+ swraddr_next = swraddr;
+ srdaddr_next = srdaddr;
+
+ if (sreset) begin
+ swraddr_next = 0;
+ srdaddr_next = 0;
+ end else begin
+ if (rd_rdy)
+ swraddr_next = swraddr + 9'd1;
+ if (srd)
+ srdaddr_next = srdaddr + 9'd1;
+ end
+end
+
+always_ff @(posedge clk) begin
+ swraddr <= swraddr_next;
+ srdaddr <= srdaddr_next;
+end
+always_ff @(posedge clk) begin
if (rd_rdy)
scratch[swraddr] <= { 16'h0, rd_data };
- if (srd)
+ if (srd_next)
srddata <= scratch[srdaddr];
end
+
+wire [15:0]xdata;
+
+`ifdef WITH_SCOPE
+reg [31:0]scope[0:511];
+reg [31:0]scdata = 0;
+reg [8:0]scwp = 0;
+reg [8:0]scrp = 0;
+reg scgo = 0;
+reg scgo_next;
+reg scrd;
+
+wire [31:0]probe = {
+ 1'b0, sdram_ras_n, sdram_cas_n, sdram_we_n, sdram_addr, xdata
+};
+
+always @(posedge clk) begin
+ scwp <= (scgo && (scwp != 511)) ? scwp + 9'd1 : scwp;
+ scrp <= scrd ? scrp + 9'd1 : scrp;
+
+ if (scgo)
+ scope[scwp] <= probe;
+ if (scrd)
+ scdata <= scope[scrp];
+end
+`endif
+
localparam OP_MISC = 4'h0; // all 0 is NOP, see MISC_ bits
localparam OP_WR_IMM = 4'h1; // value to write
localparam OP_WR_PAT = 4'h2; // count to write pattern0
@@ -79,15 +129,18 @@ localparam OP_RD_CHK = 4'h3; // value to check against
localparam OP_RD_PAT = 4'h4; // count to read and check vs pattern1
localparam OP_VERIFY = 4'h5; // count read data to verify vs pattern
localparam OP_RD_FAST = 4'h6; // count fast read
+localparam OP_RD_BURST= 4'h7; // count burst read
localparam OP_ADDR = 4'hA;
localparam OP_DISPLAY = 4'hD; // write arg to vram
localparam OP_WAIT = 4'hF;
localparam MISC_RESET_PAT0 = 0;
localparam MISC_RESET_PAT1 = 1;
-localparam MISC_HALT = 31;
localparam MISC_SET_AUTO = 2;
localparam MISC_CLR_AUTO = 3;
+localparam MISC_TRIGGER = 29;
+localparam MISC_DUMP = 30;
+localparam MISC_HALT = 31;
localparam START = 4'd0;
localparam EXEC = 4'd1;
@@ -99,8 +152,15 @@ localparam SHOW2 = 4'd6;
localparam WAIT = 4'd7;
localparam HALT = 4'd8;
localparam READFAST = 4'd9;
+localparam READBURST = 4'd12;
localparam VERIFY = 4'd10;
localparam VERIFY2 = 4'd11;
+`ifdef WITH_SCOPE
+localparam DUMP0 = 4'd12;
+localparam DUMP1 = 4'd13;
+localparam DUMP2 = 4'd14;
+localparam DUMP3 = 4'd15;
+`endif
reg auto_inc = 0;
reg auto_inc_next;
@@ -116,6 +176,7 @@ reg pattern1_reset = 0;
reg pattern1_reset_next;
reg pattern1_step = 0;
reg pattern1_step_next;
+wire p1_match_srddata = (pattern1[15:0] == srddata[15:0]);
reg done_next;
reg error_next;
@@ -134,6 +195,8 @@ localparam COUNTONE = 16'd1;
localparam COUNTZERO = 16'd0;
reg [COUNTMSB:0]count = 0;
reg [COUNTMSB:0]count_next;
+wire count_is_zero = (count == COUNTZERO);
+wire [COUNTMSB:0]count_minus_one = (count - COUNTONE);
always_comb begin
state_next = state;
@@ -141,6 +204,8 @@ always_comb begin
data_next = data;
rd_req_next = rd_req;
wr_req_next = wr_req;
+ rd_len_next = rd_len;
+ wr_len_next = wr_len;
count_next = count;
pattern0_reset_next = 0;
pattern1_reset_next = 0;
@@ -151,8 +216,12 @@ always_comb begin
info_e_next = 0;
match_next = match;
capture_next = capture;
- srd = 0;
- sreset = 0;
+ srd_next = 0;
+ sreset_next = 0;
+`ifdef WITH_SCOPE
+ scgo_next = scgo;
+ scrd = 0;
+`endif
case (state)
START: begin
@@ -166,6 +235,14 @@ always_comb begin
if (ip[MISC_SET_AUTO]) auto_inc_next = 1;
if (ip[MISC_CLR_AUTO]) auto_inc_next = 1;
if (ip[MISC_HALT]) state_next = HALT;
+`ifdef WITH_SCOPE
+ if (ip[MISC_TRIGGER]) scgo_next = 1;
+ if (ip[MISC_DUMP]) begin
+ state_next = DUMP0;
+ count_next = ip[COUNTMSB:0];
+ scrd = 1;
+ end
+`endif
end
OP_WAIT: begin
state_next = WAIT;
@@ -205,9 +282,15 @@ always_comb begin
OP_RD_FAST: begin
state_next = READFAST;
rd_req_next = 1;
- sreset = 1;
+ sreset_next = 1;
count_next = ip[COUNTMSB:0];
end
+ OP_RD_BURST: begin
+ state_next = READBURST;
+ rd_req_next = 1;
+ rd_len_next = ip[3:0];
+ sreset_next = 1;
+ end
OP_DISPLAY: begin
info_next = ip[15:0];
info_e_next = 1;
@@ -215,16 +298,17 @@ always_comb begin
OP_VERIFY: begin
state_next = VERIFY;
count_next = ip[COUNTMSB:0];
+ srd_next = 1;
end
default: ;
endcase
end
WRITE: if (wr_ack) begin
- if (count == COUNTZERO) begin
+ if (count_is_zero) begin
state_next = EXEC;
wr_req_next = 0;
end else begin
- count_next = count - COUNTONE;
+ count_next = count_minus_one;
data_next = pattern0;
pattern0_step_next = 1;
end
@@ -242,27 +326,32 @@ always_comb begin
end
READFAST: if (rd_ack) begin
if (auto_inc) addr_next = addr + 32'd1;
- if (count == COUNTZERO) begin
+ if (count_is_zero) begin
state_next = EXEC;
rd_req_next = 0;
end else begin
- count_next = count - COUNTONE;
+ count_next = count_minus_one;
end
end
+ READBURST: if (rd_ack) begin
+ state_next = EXEC;
+ rd_req_next = 0;
+ rd_len_next = 0;
+ end
SHOW: begin
state_next = SHOW2;
info_next = { match ? 8'h20 : 8'h40, capture[15:8] };
info_e_next = 1;
end
SHOW2: begin
- if (count == COUNTZERO) begin
+ if (count_is_zero) begin
state_next = EXEC;
end else begin
state_next = READ;
rd_req_next = 1;
data_next = pattern1;
pattern1_step_next = 1;
- count_next = count - COUNTONE;
+ count_next = count_minus_one;
end
info_next = { match ? 8'h20 : 8'h40, capture[7:0] };
info_e_next = 1;
@@ -273,21 +362,49 @@ always_comb begin
info_e_next = 1;
end
VERIFY2: begin
- if (count == COUNTZERO) begin
+ if (count_is_zero) begin
state_next = EXEC;
end else begin
state_next = VERIFY;
+ count_next = count_minus_one;
pattern1_step_next = 1;
- count_next = count - COUNTONE;
- srd = 1;
+ srd_next = 1;
end
info_next = { (srddata[15:0] == pattern1[15:0]) ? 8'h20 : 8'h40, srddata[7:0] };
info_e_next = 1;
end
- WAIT: if (count == 0) begin
+`ifdef WITH_SCOPE
+ DUMP0: begin
+ info_next = { 8'h07, scdata[31:24] };
+ info_e_next = 1;
+ state_next = DUMP1;
+ end
+ DUMP1: begin
+ info_next = { 8'h70, scdata[23:16] };
+ info_e_next = 1;
+ state_next = DUMP2;
+ end
+ DUMP2: begin
+ info_next = { 8'h30, scdata[15:8] };
+ info_e_next = 1;
+ state_next = DUMP3;
+ end
+ DUMP3: begin
+ info_next = { 8'h30, scdata[7:0] };
+ info_e_next = 1;
+ if (count_is_zero) begin
+ state_next = EXEC;
+ end else begin
+ state_next = DUMP0;
+ count_next = count_minus_one;
+ scrd = 1;
+ end
+ end
+`endif
+ WAIT: if (count_is_zero) begin
state_next = EXEC;
end else begin
- count_next = count - COUNTONE;
+ count_next = count_minus_one;
end
HALT: begin
state_next = HALT;
@@ -322,22 +439,28 @@ always_ff @(posedge clk) begin
capture <= capture_next;
wr_req <= wr_req_next;
rd_req <= rd_req_next;
+ wr_len <= wr_len_next;
+ rd_len <= rd_len_next;
+ srd <= srd_next;
+ sreset <= sreset_next;
+`ifdef WITH_SCOPE
+ scgo <= scgo_next;
+`endif
end
xorshift32 xs0(
.clk(clk),
- .next(pattern0_step),
+ .next(pattern0_step_next),
.reset(pattern0_reset),
.data(pattern0)
);
xorshift32 xs1(
.clk(clk),
- .next(pattern1_step),
+ .next(pattern1_step_next),
.reset(pattern1_reset),
.data(pattern1)
);
-
sdram #(
.T_PWR_UP(T_PWR_UP),
.T_RI(T_RI)
@@ -365,7 +488,7 @@ sdram #(
.wr_addr(addr[19:0]),
.wr_data(data[15:0]),
- .wr_len(0),
+ .wr_len(wr_len),
.wr_req(wr_req),
.wr_ack(wr_ack)
);
diff --git a/src/astl.c b/src/astl.c
@@ -19,9 +19,12 @@ struct {
{ 0x400000000, "rdp", "w", "rdp # read + check vs pattern1 #+1 times" },
{ 0x500000000, "verify","w", "rdp check count readbuffer vs pattern1" },
{ 0x600000000, "rdf", "w", "rdp read count into readbuffer" },
+ { 0x700000000, "rdb", "w", "rdp read burst count into readbuffer" },
{ 0xA00000000, "addr", "w", "addr # set address" },
{ 0xF00000000, "wait", "w", "wait # wait # cycles" },
{ 0x080000000, "halt", "", "halt stop processing" },
+ { 0x040000000, "dump", "w", "dump display # scope traces" },
+ { 0x020000000, "trigger","", "trigger trigger scope capture" },
{ 0x000000001, "p0rst", "", "p0rst reset pattern0" },
{ 0x000000002, "p1rst", "", "p1rst reset pattern1" },
{ 0x000000004, "auto+", "", "auto+ enable addr auto inc" },
diff --git a/src/sim-sdram.cpp b/src/sim-sdram.cpp
@@ -148,7 +148,7 @@ int sim_sdram(unsigned ctl, unsigned addr, unsigned din, unsigned* dout) {
unsigned a_col = addr & COLMASK;
unsigned a_a10 = (addr >> 10) & 1;
- printf("(%-4s) %06x ", cname(ctl), addr);
+ printf("(%-4s) %06x %04x ", cname(ctl), addr, din);
for (unsigned n = 0; n < 3; n++) {
if (sdram.pipe_data_e[n]) {
printf("<%04x", sdram.pipe_data_o[n]);
@@ -293,8 +293,8 @@ int sim_sdram(unsigned ctl, unsigned addr, unsigned din, unsigned* dout) {
if (sdram.state == BANK_WRITE) {
memory[sdram.addr] = din;
} else {
- sdram.pipe_data_o[1] = memory[sdram.addr];
- sdram.pipe_data_e[1] = 1;
+ sdram.pipe_data_o[0] = memory[sdram.addr];
+ sdram.pipe_data_e[0] = 1;
}
sdram.count--;
if (sdram.count == 0) {