aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Tuomi <jan@jantuomi.fi>2024-12-30 00:33:53 +0200
committerJan Tuomi <jan@jantuomi.fi>2024-12-30 00:33:53 +0200
commit2ded47f27f5f358972a613e2ac9e564c0efbc630 (patch)
tree854cb8d3061bec902aac853bb61a0c93c38df7a8
parentf1096f78f89e10b867b6ff7689d0a8777855b451 (diff)
Add SRAM memory model
-rw-r--r--atk16_fpga/sram.v60
-rw-r--r--atk16_fpga/sram_tb.gtkw56
-rw-r--r--atk16_fpga/sram_tb.v94
3 files changed, 210 insertions, 0 deletions
diff --git a/atk16_fpga/sram.v b/atk16_fpga/sram.v
new file mode 100644
index 0000000..c1ccb86
--- /dev/null
+++ b/atk16_fpga/sram.v
@@ -0,0 +1,60 @@
+`default_nettype none
+`timescale 1 ns / 100 ps
+
+// This is a memory model of the SRAM chip on the iCE40HX8K-EVB.
+// Relevant specs:
+// - 256Kx16 capacity
+// - 18-bit address bus, 16-bit data bus
+// - 10 ns read or write cycle time
+// - 3 ns output hold from address change
+module sram(
+ input wire cs_n,
+ input wire wr_n,
+ input wire rd_n,
+ input wire [17:0] addr,
+ input wire [15:0] data_in,
+ output reg [15:0] data_out
+);
+
+ reg [15:0] mem [0:262143]; // 256K x 16 memory
+ reg [17:0] last_addr = -1;
+ reg [15:0] last_data_in = -1;
+ reg last_wr_n, last_rd_n, last_cs_n = 0;
+ reg trigger = 0;
+
+ always @(posedge trigger) begin
+ if (cs_n == 1'b0) begin
+ if (wr_n == 1'b0) begin
+ mem[addr] = 16'hx;
+ //$display("wrote %04h at time %0d ns", mem[addr], $time);
+ #7 mem[addr] = data_in;
+ //$display("wrote %04h at time %0d ns", mem[addr], $time);
+ end
+ if (rd_n == 1'b0) begin
+ data_out = 16'hx;
+ //$display("read %04h at time %0d ns", data_out, $time);
+ #7 data_out = mem[addr];
+ //$display("read %04h at time %0d ns", data_out, $time);
+ end
+ end
+ end
+
+ always @(*) begin
+ if (addr != last_addr ||
+ data_in != last_data_in ||
+ wr_n != last_wr_n ||
+ rd_n != last_rd_n ||
+ cs_n != last_cs_n
+ ) begin
+ //$display("SRAM: addr=%h data_in=%h", addr, data_in);
+ last_addr = addr;
+ last_data_in = data_in;
+ last_wr_n = wr_n;
+ last_rd_n = rd_n;
+ last_cs_n = cs_n;
+ trigger = 0;
+ #3 trigger = 1;
+ end
+ end
+
+endmodule
diff --git a/atk16_fpga/sram_tb.gtkw b/atk16_fpga/sram_tb.gtkw
new file mode 100644
index 0000000..2b61ccd
--- /dev/null
+++ b/atk16_fpga/sram_tb.gtkw
@@ -0,0 +1,56 @@
+[*]
+[*] GTKWave Analyzer v3.4.0 (w)1999-2022 BSI
+[*] Sun Dec 29 21:48:27 2024
+[*]
+[dumpfile] "/Users/jantuomi/Projects/Personal/ATK16/atk16_fpga/sram_tb.vcd"
+[dumpfile_mtime] "Sun Dec 29 21:48:18 2024"
+[dumpfile_size] 1502
+[savefile] "/Users/jantuomi/Projects/Personal/ATK16/atk16_fpga/sram_tb.gtkw"
+[timestart] 0
+[size] 1280 600
+[pos] -1 -1
+*-15.091954 14300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+[markername] AA
+[markername] BB
+[markername] CC
+[markername] DD
+[markername] EE
+[markername] FF
+[markername] GG
+[markername] HH
+[markername] II
+[markername] JJ
+[markername] KK
+[markername] LL
+[markername] MM
+[markername] NN
+[markername] OO
+[markername] PP
+[markername] QQ
+[markername] RR
+[markername] SS
+[markername] TT
+[markername] UU
+[markername] VV
+[markername] WW
+[markername] XX
+[markername] YY
+[markername] ZZ
+[treeopen] sram_tb.
+[sst_width] 240
+[signals_width] 177
+[sst_expanded] 1
+[sst_vpaned_height] 159
+@28
+sram_tb.dut.cs_n
+sram_tb.dut.rd_n
+sram_tb.dut.wr_n
+@22
+sram_tb.dut.last_addr[17:0]
+sram_tb.dut.addr[17:0]
+sram_tb.dut.data_in[15:0]
+sram_tb.dut.data_out[15:0]
+@23
+sram_tb.debug_mem100[15:0]
+[pattern_trace] 1
+[pattern_trace] 0
diff --git a/atk16_fpga/sram_tb.v b/atk16_fpga/sram_tb.v
new file mode 100644
index 0000000..c734395
--- /dev/null
+++ b/atk16_fpga/sram_tb.v
@@ -0,0 +1,94 @@
+`default_nettype none
+`define DUMPSTR(x) `"x.vcd`"
+`timescale 1 ns / 100 ps
+
+module sram_tb();
+
+ reg clk, cs_n, wr_n, rd_n;
+ reg [17:0] addr;
+ reg [15:0] data_in;
+ wire [15:0] data_out;
+
+ sram dut(
+ .cs_n(cs_n),
+ .wr_n(wr_n),
+ .rd_n(rd_n),
+ .addr(addr),
+ .data_in(data_in),
+ .data_out(data_out)
+ );
+
+ wire [15:0] debug_mem100;
+ assign debug_mem100 = dut.mem[100];
+
+ reg failed;
+ initial begin
+ failed = 0;
+ $dumpfile(`DUMPSTR(`VCD_OUTPUT));
+ $dumpvars(0, sram_tb);
+
+ cs_n = 1;
+ wr_n = 1;
+ rd_n = 1;
+ addr = 18'd0;
+ data_in = 16'h0;
+ #10
+
+ // test write
+ cs_n = 0;
+ wr_n = 0;
+ addr = 18'd100;
+ data_in = 16'h1234;
+
+ #5
+ if (debug_mem100 === 16'hx) begin
+ $display("\033[0;32m[PASS]\033[0m Write: data is invalid at time: %0d ns", $time);
+ end else begin
+ $display("\033[0;31m[FAIL]\033[0m Write: data is valid too early, got %04h at time %0d ns", debug_mem100, $time);
+ failed = 1;
+ end
+
+ #5
+ if (debug_mem100 == 16'h1234) begin
+ $display("\033[0;32m[PASS]\033[0m Write: data is written as expected");
+ end else begin
+ $display("\033[0;31m[FAIL]\033[0m Write: data is %04h (expected 1234)", debug_mem100);
+ failed = 1;
+ end
+
+ cs_n = 1;
+ wr_n = 1;
+ #100;
+
+ // test read
+ cs_n = 0;
+ rd_n = 0;
+ addr = 18'd100;
+
+ #5
+ if (data_out === 16'hx) begin
+ $display("\033[0;32m[PASS]\033[0m Read: data is invalid at time: %0d ns", $time);
+ end else begin
+ $display("\033[0;31m[FAIL]\033[0m Read: data is valid too early, got %04h at time %0d ns", data_out, $time);
+ failed = 1;
+ end
+
+ #5
+ if (data_out == 16'h1234) begin
+ $display("\033[0;32m[PASS]\033[0m Read: data is available as expected");
+ end else begin
+ $display("\033[0;31m[FAIL]\033[0m Read: data is %04h (expected 1234)", data_out);
+ failed = 1;
+ end
+
+ #100
+
+ if (!failed) begin
+ $display("\033[0;32m[PASS]\033[0m All tests passed");
+ end else begin
+ $display("\033[0;31m[FAIL]\033[0m Some tests failed");
+ end
+ $finish;
+ end
+
+endmodule