commit 142b2fc861452602ea3a669cfc9e30f3b9c3e02c
parent fcba5a8307bc7e05e1814a3f024abf0925c0a28c
Author: Brian Swetland <swetland@frotz.net>
Date: Sat, 1 Feb 2020 10:25:57 -0800
sdram: fancier testbench
- weird little domain specific processor to exercise this stuff
- "sdram test language" (stl) assembler for that
Diffstat:
6 files changed, 417 insertions(+), 109 deletions(-)
diff --git a/Makefile b/Makefile
@@ -32,6 +32,13 @@ list-all-targets::
#### Tools ####
+out/astl: src/astl.c
+ @mkdir -p out
+ gcc -g -Wall -O1 -o out/astl src/astl.c
+
+hdl/sdram/test.hex: hdl/sdram/test.asm out/astl
+ ./out/astl < hdl/sdram/test.asm > hdl/sdram/test.hex
+
out/a16: src/a16v5.c src/d16v5.c
@mkdir -p out
gcc -g -Wall -O1 -o out/a16 src/a16v5.c src/d16v5.c
diff --git a/hdl/sdram/test.asm b/hdl/sdram/test.asm
@@ -0,0 +1,34 @@
+show aa
+show bb
+wait .25000
+show cc
+addr 10
+wri 1234
+addr 20
+wri aa55
+addr 10
+rdc 1234
+addr 20
+rdc aa55
+
+auto+
+addr 0
+wrp .15
+show 42
+
+p1rst
+addr 0
+rdp .15
+show 43
+
+show e0
+p1rst
+addr 0
+show e1
+rdf .15
+show e2
+wait .30
+verify .15
+show e3
+
+show ff
diff --git a/hdl/sdram/testbench.sv b/hdl/sdram/testbench.sv
@@ -29,157 +29,315 @@ module testbench #(
reg [15:0]info_next;
reg info_e_next;
-reg [19:0]rd_addr = 0;
-wire [15:0]rd_data;
-reg rd_req = 0;
reg [3:0]rd_len = 0;
+reg rd_req = 0;
wire rd_ack;
+wire [15:0]rd_data;
wire rd_rdy;
-reg [19:0]wr_addr = 0;
-reg [15:0]wr_data = 0;
+reg [15:0]wr_len = 0;
reg wr_req = 0;
wire wr_ack;
reg rd_req_next;
reg wr_req_next;
reg [3:0]rd_len_next;
-reg [19:0]wr_addr_next;
-reg [19:0]rd_addr_next;
-reg [15:0]wr_data_next;
+reg [3:0]wr_len_next;
-reg done_next;
-reg error_next;
+reg [31:0]addr = 0;
+reg [31:0]data = 0;
+reg [31:0]addr_next;
+reg [31:0]data_next;
-reg [15:0]count = T_PWR_UP + 32;
-reg [15:0]count_next;
-wire [15:0]count_sub1;
-wire count_done;
+reg [31:0]capture = 0;
+reg [31:0]capture_next;
+reg match = 0;
+reg match_next;
-assign { count_done, count_sub1 } = { 1'b0, count } - 16'd1;
+// scratch memory to capture back-to-back and burst read results
+reg [31:0]scratch[0:511];
+reg [8:0]swraddr = 0;
+reg [8:0]srdaddr = 0;
+reg [31:0]srddata = 0;
-localparam INIT = 4'd0;
-localparam WRITES = 4'd1;
-localparam READS = 4'd2;
-localparam STOP = 4'd3;
+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 [3:0]state = INIT;
-reg [3:0]state_next;
+ if (rd_rdy)
+ scratch[swraddr] <= { 16'h0, rd_data };
+ if (srd)
+ srddata <= scratch[srdaddr];
+end
-reg number_next;
-reg number_reset;
-wire [31:0]number;
+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
+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_ADDR = 4'hA;
+localparam OP_DISPLAY = 4'hD; // write arg to vram
+localparam OP_WAIT = 4'hF;
-xorshift32 xs(
- .clk(clk),
- .next(number_next),
- .reset(number_reset),
- .data(number)
-);
+localparam MISC_RESET_PAT0 = 0;
+localparam MISC_RESET_PAT1 = 1;
+localparam MISC_HALT = 31;
+localparam MISC_SET_AUTO = 2;
+localparam MISC_CLR_AUTO = 3;
-reg [15:0]cycles = 0;
-reg [15:0]cycles_next;
+localparam START = 4'd0;
+localparam EXEC = 4'd1;
+localparam WRITE = 4'd2;
+localparam READ = 4'd3;
+localparam READ2 = 4'd4;
+localparam SHOW = 4'd5;
+localparam SHOW2 = 4'd6;
+localparam WAIT = 4'd7;
+localparam HALT = 4'd8;
+localparam READFAST = 4'd9;
+localparam VERIFY = 4'd10;
+localparam VERIFY2 = 4'd11;
-reg reset = 1;
+reg auto_inc = 0;
+reg auto_inc_next;
+
+wire [31:0]pattern0;
+reg pattern0_reset = 0;
+reg pattern0_reset_next;
+reg pattern0_step = 0;
+reg pattern0_step_next;
+
+wire [31:0]pattern1;
+reg pattern1_reset = 0;
+reg pattern1_reset_next;
+reg pattern1_step = 0;
+reg pattern1_step_next;
+
+reg done_next;
+reg error_next;
+
+reg [35:0]insram[0:1023];
+reg [35:0]ip = 0;
+reg [9:0]pc = 0;
+
+initial $readmemh("hdl/sdram/test.hex", insram);
+
+reg [3:0]state = START;
+reg [3:0]state_next;
+
+localparam COUNTMSB = 15;
+localparam COUNTONE = 16'd1;
+localparam COUNTZERO = 16'd0;
+reg [COUNTMSB:0]count = 0;
+reg [COUNTMSB:0]count_next;
always_comb begin
- number_reset = 0;
- number_next = 0;
state_next = state;
- count_next = count;
+ addr_next = addr;
+ data_next = data;
rd_req_next = rd_req;
wr_req_next = wr_req;
- wr_addr_next = wr_addr;
- rd_addr_next = rd_addr;
- rd_len_next = rd_len;
- wr_data_next = wr_data;
+ count_next = count;
+ pattern0_reset_next = 0;
+ pattern1_reset_next = 0;
+ pattern0_step_next = 0;
+ pattern1_step_next = 0;
+ auto_inc_next = auto_inc;
info_next = info;
info_e_next = 0;
- done_next = 0;
- error_next = 0;
- cycles_next = cycles + 16'd1;
-
- if (cycles == 16'd5000)
- error_next = 1;
+ match_next = match;
+ capture_next = capture;
+ srd = 0;
+ sreset = 0;
case (state)
- INIT: if (count_done) begin
- state_next = WRITES;
- count_next = 1000; //32;
- wr_addr_next = 20'hF0;
- //wr_data_next = 0;
- wr_data_next= number[15:0];
- number_next = 1;
- wr_req_next = 1;
- info_next = 16'h10FF;
- info_e_next = 1;
- end else begin
- count_next = count_sub1;
+ START: begin
+ state_next = EXEC;
end
- WRITES: if (count_done) begin
- state_next = READS;
- number_reset = 1;
- count_next = 1000; //32;
- rd_addr_next = 20'hF0;
- rd_req_next = 1;
- wr_req_next = 0;
- info_next = 16'h20EE;
- info_e_next = 1;
- end else begin
- if (wr_ack) begin
- //wr_data_next = wr_data + 1;
- wr_data_next = number[15:0];
- number_next = 1;
- wr_addr_next = wr_addr + 1;
- count_next = count_sub1;
+ EXEC: begin
+ case (ip[35:32])
+ OP_MISC: begin
+ if (ip[MISC_RESET_PAT0]) pattern0_reset_next = 1;
+ if (ip[MISC_RESET_PAT1]) pattern1_reset_next = 1;
+ 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;
+ end
+ OP_WAIT: begin
+ state_next = WAIT;
+`ifdef verilator
+ count_next = 30;
+`else
+ count_next = ip[COUNTMSB:0];
+`endif
+ end
+ OP_ADDR: begin
+ addr_next = ip[31:0];
+ end
+ OP_WR_IMM: begin
+ state_next = WRITE;
+ data_next = ip[31:0];
+ wr_req_next = 1;
+ end
+ OP_WR_PAT: begin
+ state_next = WRITE;
+ data_next = pattern0;
+ count_next = ip[COUNTMSB:0];
+ pattern0_step_next = 1;
+ wr_req_next = 1;
+ end
+ OP_RD_CHK: begin
+ state_next = READ;
+ data_next = ip[31:0];
+ rd_req_next = 1;
+ end
+ OP_RD_PAT: begin
+ state_next = READ;
+ data_next = pattern1;
+ pattern1_step_next = 1;
+ rd_req_next = 1;
+ count_next = ip[COUNTMSB:0];
+ end
+ OP_RD_FAST: begin
+ state_next = READFAST;
+ rd_req_next = 1;
+ sreset = 1;
+ count_next = ip[COUNTMSB:0];
end
+ OP_DISPLAY: begin
+ info_next = ip[15:0];
+ info_e_next = 1;
+ end
+ OP_VERIFY: begin
+ state_next = VERIFY;
+ count_next = ip[COUNTMSB:0];
+ end
+ default: ;
+ endcase
end
- READS: if (count_done) begin
- state_next = STOP;
- done_next = 1;
- info_next = 16'h20DD;
- info_e_next = 1;
+ WRITE: if (wr_ack) begin
+ if (count == COUNTZERO) begin
+ state_next = EXEC;
+ wr_req_next = 0;
+ end else begin
+ count_next = count - COUNTONE;
+ data_next = pattern0;
+ pattern0_step_next = 1;
+ end
+ if (auto_inc) addr_next = addr + 32'd1;
+ end
+ READ: if (rd_ack) begin
+ state_next = READ2;
rd_req_next = 0;
- end else begin
- if (rd_ack) begin
+ if (auto_inc) addr_next = addr + 32'd1;
+ end
+ READ2: if (rd_rdy) begin
+ state_next = SHOW;
+ match_next = (data[15:0] == rd_data);
+ capture_next = { 16'h0, rd_data };
+ end
+ READFAST: if (rd_ack) begin
+ if (auto_inc) addr_next = addr + 32'd1;
+ if (count == COUNTZERO) begin
+ state_next = EXEC;
rd_req_next = 0;
+ end else begin
+ count_next = count - COUNTONE;
end
- if (rd_rdy) begin
+ 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
+ state_next = EXEC;
+ end else begin
+ state_next = READ;
rd_req_next = 1;
- rd_addr_next = rd_addr + 1;
- count_next = count_sub1;
- if (rd_data == number[15:0])
- info_next = { 16'h7011 };
- else
- info_next = { 16'h40FF };
- //info_next = { 8'h40, rd_data[7:0] };
- number_next = 1;
- info_e_next = 1;
+ data_next = pattern1;
+ pattern1_step_next = 1;
+ count_next = count - COUNTONE;
+ end
+ info_next = { match ? 8'h20 : 8'h40, capture[7:0] };
+ info_e_next = 1;
+ end
+ VERIFY: begin
+ state_next = VERIFY2;
+ info_next = { (srddata[15:0] == pattern1[15:0]) ? 8'h20 : 8'h40, srddata[15:8] };
+ info_e_next = 1;
+ end
+ VERIFY2: begin
+ if (count == COUNTZERO) begin
+ state_next = EXEC;
+ end else begin
+ state_next = VERIFY;
+ pattern1_step_next = 1;
+ count_next = count - COUNTONE;
+ srd = 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
+ state_next = EXEC;
+ end else begin
+ count_next = count - COUNTONE;
end
- STOP: state_next = STOP;
- default: state_next = INIT;
+ HALT: begin
+ state_next = HALT;
+`ifdef verilator
+ $finish();
+`endif
+ end
+ default: state_next = HALT;
endcase
end
+reg reset = 1;
+
always_ff @(posedge clk) begin
+ reset <= 0;
state <= state_next;
- rd_req <= rd_req_next;
- wr_req <= wr_req_next;
- rd_addr <= rd_addr_next;
- wr_addr <= wr_addr_next;
- wr_data <= wr_data_next;
- rd_len <= rd_len_next;
count <= count_next;
+ if (state_next == EXEC) begin
+ ip <= insram[pc];
+ pc <= pc + 10'd1;
+ end
+ addr <= addr_next;
+ data <= data_next;
+ pattern0_reset <= pattern0_reset_next;
+ pattern1_reset <= pattern1_reset_next;
+ pattern0_step <= pattern0_step_next;
+ pattern1_step <= pattern1_step_next;
+ auto_inc <= auto_inc_next;
info <= info_next;
info_e <= info_e_next;
-
- cycles <= cycles_next;
- done <= done_next;
- error <= error_next;
- reset <= 0;
+ match <= match_next;
+ capture <= capture_next;
+ wr_req <= wr_req_next;
+ rd_req <= rd_req_next;
end
+xorshift32 xs0(
+ .clk(clk),
+ .next(pattern0_step),
+ .reset(pattern0_reset),
+ .data(pattern0)
+);
+xorshift32 xs1(
+ .clk(clk),
+ .next(pattern1_step),
+ .reset(pattern1_reset),
+ .data(pattern1)
+);
+
+
sdram #(
.T_PWR_UP(T_PWR_UP),
.T_RI(T_RI)
@@ -198,15 +356,15 @@ sdram #(
`else
.pin_data(sdram_data),
`endif
- .rd_addr(rd_addr),
+ .rd_addr(addr[19:0]),
.rd_len(rd_len),
.rd_req(rd_req),
.rd_ack(rd_ack),
.rd_data(rd_data),
.rd_rdy(rd_rdy),
- .wr_addr(wr_addr),
- .wr_data(wr_data),
+ .wr_addr(addr[19:0]),
+ .wr_data(data[15:0]),
.wr_len(0),
.wr_req(wr_req),
.wr_ack(wr_ack)
diff --git a/project/colorlight-sdram.def b/project/colorlight-sdram.def
@@ -3,7 +3,7 @@ PROJECT_TYPE := nextpnr-ecp5
PROJECT_SRCS := hdl/colorlight-sdram.sv hdl/colorlight.lpf
PROJECT_SRCS += hdl/lattice/ecp5_pll_25_125_250.v
PROJECT_SRCS += hdl/display/display.sv hdl/display/display_timing.sv
-PROJECT_SRCS += hdl/sdram/testbench.sv
+PROJECT_SRCS += hdl/sdram/testbench.sv hdl/sdram/test.hex
PROJECT_SRCS += hdl/sdram/sdram.sv hdl/sdram/sdram_glue_ecp5.sv
PROJECT_SRCS += hdl/xorshift.sv
diff --git a/project/test-sdram.def b/project/test-sdram.def
@@ -1,7 +1,7 @@
PROJECT_TYPE := verilator-sim
-PROJECT_SRCS := hdl/sdram/testbench.sv
+PROJECT_SRCS := hdl/sdram/testbench.sv hdl/sdram/test.hex
PROJECT_SRCS += hdl/sdram/sdram.sv hdl/xorshift.sv
PROJECT_VOPTS := -CFLAGS -DSDRAM
diff --git a/src/astl.c b/src/astl.c
@@ -0,0 +1,109 @@
+// Copyright 2015, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0.
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+struct {
+ uint64_t enc;
+ const char* name;
+ const char* args;
+ const char* desc;
+} ops[] = {
+ { 0x100000000, "wri", "w", "wri # write immediate" },
+ { 0x200000000, "wrp", "w", "wrp # write pattern0 #+1 times" },
+ { 0x300000000, "rdc", "w", "rdi # read + check vs immediate" },
+ { 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" },
+ { 0xA00000000, "addr", "w", "addr # set address" },
+ { 0xF00000000, "wait", "w", "wait # wait # cycles" },
+ { 0x080000000, "halt", "", "halt stop processing" },
+ { 0x000000001, "p0rst", "", "p0rst reset pattern0" },
+ { 0x000000002, "p1rst", "", "p1rst reset pattern1" },
+ { 0x000000004, "auto+", "", "auto+ enable addr auto inc" },
+ { 0x000000008, "auto-", "", "auto- disble addr auto inc" },
+ { 0xD00007100, "show", "b", "show # show status byte" },
+ { 0x000000000, "", "", "" },
+};
+
+unsigned lineno = 1;
+
+const char* token(void) {
+ static char buf[64];
+ int n = 0;
+ for (;;) {
+ int c = getchar();
+ if (c == '#') { // comment to EOL
+ for (;;) {
+ c = getchar();
+ if (c == EOF) break;
+ if (c == '\n') break;
+ }
+ }
+ if (c == '\n') lineno++;
+ if (c == EOF) break;
+ if (isspace(c)) {
+ if (n) break;
+ } else {
+ buf[n++] = c;
+ if (n == sizeof(buf)) {
+ fprintf(stderr, "error: %u: token too large\n", lineno);
+ exit(-1);
+ }
+ }
+ }
+ buf[n] = 0;
+ return buf;
+}
+
+uint32_t arg_word(const char* tok) {
+ if (tok[0] == 0) {
+ fprintf(stderr, "error: %u: missing argument\n", lineno);
+ exit(-1);
+ }
+ if (tok[0] == '.') return strtoul(tok+1, 0, 10);
+ else return strtoul(tok, 0, 16);
+}
+uint32_t arg_byte(const char* tok) {
+ uint32_t n = arg_word(tok);
+ if (n > 255) {
+ fprintf(stderr, "error: %u: byte argument too large\n", lineno);
+ exit(-1);
+ }
+ return n;
+}
+
+int main(int argc, char **argv) {
+ if (argc != 1) {
+ for (unsigned n = 0; ops[n].enc; n++) {
+ fprintf(stderr, "%s\n", ops[n].desc);
+ }
+ return 0;
+ }
+ for (;;) {
+ const char* tok = token();
+ if (tok[0] == 0) break;
+ for (unsigned n = 0; ops[n].enc; n++) {
+ if (!strcasecmp(ops[n].name, tok)) {
+ uint64_t op = ops[n].enc;
+ const char* args = ops[n].args;
+ while (*args) {
+ if (*args == 'w') op |= arg_word(token());
+ if (*args == 'b') op |= arg_byte(token());
+ args++;
+ }
+ printf("%09lx\n", op);
+ goto next;
+ }
+ }
+ fprintf(stderr, "error: %u: unknown opcode '%s'\n", lineno, tok);
+ exit(-1);
+next: ;
+ }
+ printf("080000000\n");
+ return 0;
+}