From 7f033d3c1f4604d303da237fbc7a38ee503416ad Mon Sep 17 00:00:00 2001 From: KrystalDelusion Date: Thu, 7 Jul 2022 11:10:33 +1200 Subject: [PATCH] More tests in memlib/generate.py Covers most of the todo list, at least functionally. Some minor issues with not always using hardware features. --- tests/memlib/generate.py | 677 +++++++++++++++++++++++++- tests/memlib/memlib_9b1B.txt | 31 ++ tests/memlib/memlib_9b1B.v | 68 +++ tests/memlib/memlib_block_sp_full.txt | 61 +++ tests/memlib/memlib_block_sp_full.v | 82 ++++ tests/memlib/memlib_clock_sdp.txt | 76 +++ tests/memlib/memlib_clock_sdp.v | 36 ++ tests/memlib/memlib_lut.txt | 18 +- tests/memlib/memlib_lut.v | 9 +- tests/memlib/memlib_multilut.txt | 19 + tests/memlib/memlib_multilut.v | 45 ++ tests/memlib/memlib_wren.txt | 37 ++ tests/memlib/memlib_wren.v | 33 ++ 13 files changed, 1180 insertions(+), 12 deletions(-) create mode 100644 tests/memlib/memlib_9b1B.txt create mode 100644 tests/memlib/memlib_9b1B.v create mode 100644 tests/memlib/memlib_block_sp_full.txt create mode 100644 tests/memlib/memlib_block_sp_full.v create mode 100644 tests/memlib/memlib_clock_sdp.txt create mode 100644 tests/memlib/memlib_clock_sdp.v create mode 100644 tests/memlib/memlib_multilut.txt create mode 100644 tests/memlib/memlib_multilut.v create mode 100644 tests/memlib/memlib_wren.txt create mode 100644 tests/memlib/memlib_wren.v diff --git a/tests/memlib/generate.py b/tests/memlib/generate.py index 341486584..f40210501 100644 --- a/tests/memlib/generate.py +++ b/tests/memlib/generate.py @@ -1,13 +1,6 @@ # TODO: -# - memory initialization -# - clock polarity combinations -# - CE/srst/rdwr/be interactions # - priority logic -# - byte enables, wrbe_separate -# - duplication for read ports -# - abits/dbits determination -# - mixed width # - swizzles for weird width progressions @@ -22,6 +15,7 @@ class Test: TESTS = [] ### basic sanity tests +# Asynchronous-read RAM ASYNC = """ module top(clk, ra, wa, rd, wd, we); @@ -56,6 +50,7 @@ TESTS += [ Test("async_small_block", ASYNC_SMALL, ["block_tdp"], [], {"RAM_BLOCK_TDP": 0}), ] +# Synchronous SDP read first SYNC = """ module top(clk, ra, wa, rd, wd, we); @@ -95,6 +90,261 @@ TESTS += [ Test("sync_small_block_attr", SYNC_SMALL_BLOCK, ["lut", "block_tdp"], [], {"RAM_BLOCK_TDP": 1}), ] +### initialization values testing +LUT_INIT = """ +module top(clk, ra, wa, rd, wd, we); + +localparam ABITS = {abits}; +localparam DBITS = {dbits}; + +input wire clk; +input wire we; +input wire [ABITS-1:0] ra, wa; +input wire [DBITS-1:0] wd; +output wire [DBITS-1:0] rd; + +reg [DBITS-1:0] mem [0:2**ABITS-1]; + +integer i; +initial + for (i = 0; i < 2**ABITS-1; i = i + 1) + mem[i] = {ival}; + +always @(posedge clk) + if (we) + mem[wa] <= wd; + +assign rd = mem[ra]; + +endmodule +""" + +INIT_LUT_ZEROS = LUT_INIT.format(abits=4, dbits=4, ival=0); +INIT_LUT_VAL = LUT_INIT.format(abits=4, dbits=4, ival=5); +INIT_LUT_VAL2 = LUT_INIT.format(abits=6, dbits=6, ival="6'h12"); +INIT_LUT_X = LUT_INIT.format(abits=4, dbits=4, ival="4'hx") + +TESTS += [ + Test("init_lut_zeros_zero", INIT_LUT_ZEROS, ["lut"], ["INIT_ZERO"], {"RAM_LUT":1}), + Test("init_lut_zeros_any", INIT_LUT_ZEROS, ["lut"], ["INIT_ANY"], {"RAM_LUT":1}), + Test("init_lut_val_zero", INIT_LUT_VAL, ["lut"], ["INIT_ZERO"], {"RAM_LUT":0}), #CHECK: no emulation? + Test("init_lut_val_any", INIT_LUT_VAL, ["lut"], ["INIT_ANY"], {"RAM_LUT":1}), + Test("init_lut_val_no_undef", INIT_LUT_VAL, ["lut"], ["INIT_NO_UNDEF"], {"RAM_LUT":1}), + Test("init_lut_val2_any", INIT_LUT_VAL2, ["lut"], ["INIT_ANY"], {"RAM_LUT":8}), + Test("init_lut_val2_no_undef", INIT_LUT_VAL2, ["lut"], ["INIT_NO_UNDEF"], {"RAM_LUT":8}), + Test("init_lut_x_none", INIT_LUT_X, ["lut"], ["INIT_NONE"], {"RAM_LUT":1}), + Test("init_lut_x_zero", INIT_LUT_X, ["lut"], ["INIT_ZERO"], {"RAM_LUT":1}), + Test("init_lut_x_any", INIT_LUT_X, ["lut"], ["INIT_ANY"], {"RAM_LUT":1}), + Test("init_lut_x_no_undef", INIT_LUT_X, ["lut"], ["INIT_NO_UNDEF"], {"RAM_LUT":1}), +] + +### width testing 9-bit-per-byte +RAM_9b1B = """ +module top(clk, ra, wa, rd, wd, we); + +localparam ABITS = {abits}; +localparam DBITS = {dbits}; + +input wire clk; +input wire we; +input wire [ABITS-1:0] ra, wa; +input wire [DBITS-1:0] wd; +output reg [DBITS-1:0] rd; + +reg [DBITS-1:0] mem [0:2**ABITS-1]; + +always @(posedge clk) + if (we) + mem[wa] <= wd; + +always @(posedge clk) + rd <= mem[ra]; + +endmodule +""" + +RAM_18b2B = RAM_9b1B.format(abits=3, dbits=18); +RAM_9b1B = RAM_9b1B.format(abits=4, dbits=9); +RAM_4b1B = RAM_9b1B.format(abits=5, dbits=4); +RAM_2b1B = RAM_9b1B.format(abits=6, dbits=2); +RAM_1b1B = RAM_9b1B.format(abits=7, dbits=1); + +TESTS += [ + Test("ram_18b2B", RAM_18b2B, ["9b1B"], [], {"RAM_9b1B":1}), + Test("ram_9b1B", RAM_9b1B, ["9b1B"], [], {"RAM_9b1B":1}), + Test("ram_4b1B", RAM_4b1B, ["9b1B"], [], {"RAM_9b1B":1}), + Test("ram_2b1B", RAM_2b1B, ["9b1B"], [], {"RAM_9b1B":1}), + Test("ram_1b1B", RAM_1b1B, ["9b1B"], [], {"RAM_9b1B":1}), +] + +### initializing 9-bits-per-byte +RAM_9b1B_init = """ +module top(clk, ra, wa, rd, wd, we); + +localparam ABITS = {abits}; +localparam DBITS = {dbits}; + +input wire clk; +input wire we; +input wire [ABITS-1:0] ra, wa; +input wire [DBITS-1:0] wd; +output reg [DBITS-1:0] rd; + +reg [DBITS-1:0] mem [0:2**ABITS-1]; + +integer i; +initial + for (i = 0; i < 2**ABITS-1; i = i + 1) + mem[i] = {ival}; + +always @(posedge clk) + if (we) + mem[wa] <= wd; + +always @(posedge clk) + rd <= mem[ra]; + +endmodule +""" + +INIT_9b1B_ZEROS = RAM_9b1B_init.format(abits=4, dbits=9, ival=0); +INIT_9b1B_VAL = RAM_9b1B_init.format(abits=4, dbits=9, ival=275); +INIT_13b2B_VAL = RAM_9b1B_init.format(abits=3, dbits=13, ival="13'h01f3") +INIT_18b2B_VAL = RAM_9b1B_init.format(abits=4, dbits=18, ival="18'h1f39a"); +INIT_4b1B_X = RAM_9b1B_init.format(abits=5, dbits=4, ival="4'hx") + +TESTS += [ + Test("init_9b1B_zeros_zero", INIT_9b1B_ZEROS, ["9b1B"], ["INIT_ZERO"], {"RAM_9b1B":1}), + Test("init_9b1B_zeros_any", INIT_9b1B_ZEROS, ["9b1B"], ["INIT_ANY"], {"RAM_9b1B":1}), + Test("init_9b1B_val_zero", INIT_9b1B_VAL, ["9b1B"], ["INIT_ZERO"], {"RAM_9b1B":0}), #CHECK: no emulation? + Test("init_9b1B_val_any", INIT_9b1B_VAL, ["9b1B"], ["INIT_ANY"], {"RAM_9b1B":1}), + Test("init_9b1B_val_no_undef", INIT_9b1B_VAL, ["9b1B"], ["INIT_NO_UNDEF"], {"RAM_9b1B":1}), + Test("init_13b2B_val_any", INIT_13b2B_VAL, ["9b1B"], ["INIT_ANY"], {"RAM_9b1B":1}), + Test("init_18b2B_val_any", INIT_18b2B_VAL, ["9b1B"], ["INIT_ANY"], {"RAM_9b1B":2}), + Test("init_18b2B_val_no_undef", INIT_18b2B_VAL, ["9b1B"], ["INIT_NO_UNDEF"], {"RAM_9b1B":2}), + Test("init_4b1B_x_none", INIT_4b1B_X, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B":1}), + Test("init_4b1B_x_zero", INIT_4b1B_X, ["9b1B"], ["INIT_ZERO"], {"RAM_9b1B":1}), + Test("init_4b1B_x_any", INIT_4b1B_X, ["9b1B"], ["INIT_ANY"], {"RAM_9b1B":1}), + Test("init_4b1B_x_no_undef", INIT_4b1B_X, ["9b1B"], ["INIT_NO_UNDEF"], {"RAM_9b1B":1}), +] + +### Clock polarity combinations +# I'm not entirely convinced auto-test is correctly testing clock edging +# but they do at least all gen/synth +SYNCCLOCK = """ +module top(clk, ra, wa, rd, wd, we); + +localparam ABITS = {abits}; +localparam DBITS = 8; + +input wire clk; +input wire we; +input wire [ABITS-1:0] ra, wa; +input wire [DBITS-1:0] wd; +output reg [DBITS-1:0] rd; + +reg [DBITS-1:0] mem [0:2**ABITS-1]; + +always @(posedge clk) + if (we) + mem[wa] <= wd; + +always @(posedge clk) + rd <= mem[ra]; + +endmodule +""" +for (abits, cnt, wclk, rclk, shared) in [ + (4, 1, "ANY","ANY", False), + (4, 1, "ANY","NEG", False), + (4, 1, "ANY","POS", False), + (4, 1, "NEG","ANY", False), + (4, 1, "NEG","POS", False), + (4, 1, "NEG","NEG", False), + (4, 1, "POS","ANY", False), + (4, 1, "POS","NEG", False), + (4, 1, "POS","POS", False), + (4, 1, "ANY","ANY", True), + (4, 0, "NEG","POS", True), # FF mapping + (4, 1, "NEG","NEG", True), + (4, 0, "POS","NEG", True), # FF mapping + (4, 1, "POS","POS", True), + # cannot combine "ANY" with "POS|NEG" when using shared clock +]: + name = f"clock_a{abits}_w{wclk}r{rclk}s{shared}" + defs = ["WCLK_" + wclk, "RCLK_" + rclk] + if (shared): + defs.append("SHARED_CLK") + TESTS.append(Test( + name, SYNCCLOCK.format(abits=abits), + ["clock_sdp"], defs, {"RAM_CLOCK_SDP": cnt} + )) + +### mixed width testing +# Wide write port +MIXED_WRITE = """ +module top(clk, ra, wa, rd, wd, we); + +localparam WABITS = {wabits}; +localparam WDBITS = {wdbits}; + +localparam RABITS = {rabits}; +localparam RDBITS = {rdbits}; + +input wire clk; +input wire we; +input wire [WABITS-1:0] wa; +input wire [WDBITS-1:0] wd; +input wire [RABITS-1:0] ra; +output reg [RDBITS-1:0] rd; + +localparam DEPTH = (2**WABITS); + +localparam OFFSET = RABITS-WABITS; + +(* syn_ramstyle = "block_ram" *) +reg [WDBITS-1:0] mem [0:DEPTH-1]; + +always @(posedge clk) + if (we) + mem[wa] <= wd; + +if (OFFSET > 0) begin + reg [WDBITS-1:0] mem_read; + reg [OFFSET-1:0] subaddr_r; + always @(posedge clk) begin + mem_read <= mem[ra[RABITS-1:OFFSET]]; + subaddr_r <= ra[OFFSET-1:0]; + end + + always @(mem_read, subaddr_r) + rd <= mem_read[subaddr_r*RDBITS+:RDBITS]; +end +else +begin + always @(posedge clk) + case (OFFSET) + 0: rd <= mem[ra]; + -1: rd <= {{ mem[ra], mem[ra+1] }}; + endcase +end +endmodule +""" + +UNMIXED = MIXED_WRITE.format(wabits=4, wdbits=9, rabits=4, rdbits=9) +MIXED_9_18 = MIXED_WRITE.format(wabits=5, wdbits=9, rabits=4, rdbits=18) +MIXED_18_9 = MIXED_WRITE.format(wabits=3, wdbits=18, rabits=4, rdbits=9) +MIXED_36_9 = MIXED_WRITE.format(wabits=3, wdbits=36, rabits=5, rdbits=9) +MIXED_4_2 = MIXED_WRITE.format(wabits=5, wdbits=4, rabits=6, rdbits=2); + +TESTS += [ + Test("unmixed", UNMIXED, ["9b1B"], [], {"RAM_9b1B":1}), + Test("mixed_9_18", MIXED_9_18, ["9b1B"], [], {"RAM_9b1B":4}), #CHECK: only using half the memory + Test("mixed_18_9", MIXED_18_9, ["9b1B"], [], {"RAM_9b1B":1}), + Test("mixed_36_9", MIXED_36_9, ["9b1B"], [], {"RAM_9b1B":2}), + Test("mixed_4_2", MIXED_4_2, ["9b1B"], [], {"RAM_9b1B":1}), +] + ### basic TDP test TDP = """ @@ -131,7 +381,7 @@ TESTS += [ ] # shared clock - +# Synchronous SDP with clock domain crossing SYNC_2CLK = """ module top(rclk, wclk, ra, wa, rd, wd, we); @@ -163,7 +413,7 @@ TESTS += [ ] # inter-port transparency - +# Synchronous SDP with write-first behaviour SYNC_TRANS = """ module top(clk, ra, wa, rd, wd, we); @@ -201,7 +451,7 @@ TESTS += [ ] # rdwr checks - +# Synchronous single-port RAM with mutually exclusive read/write SP_NO_CHANGE = """ module top(clk, addr, rd, wd, we); @@ -247,6 +497,7 @@ end endmodule """ +# Synchronous single-port RAM with write-first behaviour SP_NEW = """ module top(clk, addr, rd, wd, we); @@ -295,6 +546,7 @@ end endmodule """ +# Synchronous single-port RAM with read-first behaviour SP_OLD = """ module top(clk, addr, rd, wd, we); @@ -373,6 +625,7 @@ TESTS += [ Test("sp_old_auto_be", SP_OLD_BE, ["block_sp"], ["RDWR_NO_CHANGE", "RDWR_OLD", "RDWR_NEW"], {"RAM_BLOCK_SP": (1, {"OPTION_RDWR": "OLD"})}), ] +# Synchronous read port with initial value SP_INIT = """ module top(clk, addr, rd, wd, we, re); @@ -418,6 +671,7 @@ TESTS += [ Test("sp_init_v_any_re", SP_INIT_V, ["block_sp"], ["RDINIT_ANY", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}), ] +# Synchronous read port with asynchronous reset SP_ARST = """ module top(clk, addr, rd, wd, we, re, ar); @@ -488,6 +742,7 @@ TESTS += [ Test("sp_arst_n_init_re", SP_ARST_N, ["block_sp"], ["RDINIT_ANY", "RDARST_INIT", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}), ] +# Synchronous read port with synchronous reset (reset priority over enable) SP_SRST = """ module top(clk, addr, rd, wd, we, re, sr); @@ -515,6 +770,7 @@ end endmodule """ +# Synchronous read port with synchronous reet (enable priority over reset) SP_SRST_G = """ module top(clk, addr, rd, wd, we, re, sr); @@ -602,6 +858,180 @@ TESTS += [ Test("sp_srst_gv_init_re", SP_SRST_GV, ["block_sp"], ["RDINIT_ANY", "RDSRST_INIT", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}), ] +# Byte enables, wrbe_separate +SYNC_ENABLE = """ +module top(clk, rwa, rd, wd, we); + +localparam ABITS = {abits}; +localparam DBITS = {dbits}; + +input wire clk; +input wire we; +input wire [ABITS-1:0] rwa; +input wire [DBITS-1:0] wd; +output reg [DBITS-1:0] rd; + +reg [DBITS-1:0] mem [0:2**ABITS-1]; + +always @(posedge clk) begin + if (we) + mem[rwa] <= wd; + else + rd <= mem[rwa]; +end + +endmodule +""" + +for (abits, dbits, sep, defs, cells) in [ + (4, 4, False, ["NO_BYTE"], {"RAM_WREN": 1}), + (5, 4, False, ["NO_BYTE"], {"RAM_WREN": 2}), + (6, 4, False, ["NO_BYTE"], {"RAM_WREN": 4}), + # (4, 4, True, ["NO_BYTE"], {"RAM_WREN": 1}), # should throw an error + (3, 8, False, ["NO_BYTE"], {"RAM_WREN": 2}), # needs two write ports + (4, 8, False, ["NO_BYTE"], {"RAM_WREN": 2}), + (4, 4, False, ["W4_B4"], {"RAM_WREN": 1}), + (4, 8, True, ["W4_B4"], {"RAM_WREN": 2}), + (4, 8, False, ["W8_B4"], {"RAM_WREN": 1}), + (4, 8, True, ["W8_B4"], {"RAM_WREN": 1}), + (4, 8, False, ["W8_B8"], {"RAM_WREN": 1}), + (4, 8, True, ["W8_B8"], {"RAM_WREN": 1}), + +]: + name = f"wren_a{abits}d{dbits}_{defs[0]}" + if (sep): + defs.append("WRBE_SEPARATE") + name += "_separate" + + TESTS.append(Test( + name, SYNC_ENABLE.format(abits=abits, dbits=dbits), + ["wren"], defs, cells + )) + +# Write port with byte enables +ENABLES = """ +module top(clk, we, be, rwa, wd, rd); + +localparam ABITS = {abits}; +localparam WBITS = {wbits}; +localparam WORDS = {words}; + +input wire clk; +input wire we; +input wire [WORDS-1:0] be; +input wire [ABITS-1:0] rwa; +input wire [(WBITS*WORDS)-1:0] wd; +output reg [(WBITS*WORDS)-1:0] rd; + +reg [(WBITS*WORDS)-1:0] mem [0:2**ABITS-1]; + +integer i; +always @(posedge clk) + for (i=0; i