3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-06-18 20:03:39 +00:00

More tests in memlib/generate.py

Covers most of the todo list, at least functionally.  Some minor issues with not always using hardware features.
This commit is contained in:
KrystalDelusion 2022-07-07 11:10:33 +12:00
parent af1b9c9e07
commit 7f033d3c1f
13 changed files with 1180 additions and 12 deletions

View file

@ -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<WORDS; i=i+1)
if (we && be[i])
mem[rwa][i*WBITS+:WBITS] <= wd[i*WBITS+:WBITS];
always @(posedge clk)
if (!we)
rd <= mem[rwa];
endmodule
"""
for (abits, wbits, words, defs, cells) in [
(4, 2, 8, ["W16_B4"], {"RAM_WREN": 2}),
(4, 4, 4, ["W16_B4"], {"RAM_WREN": 1}),
(5, 4, 2, ["W16_B4"], {"RAM_WREN": 1}),
(5, 4, 4, ["W16_B4"], {"RAM_WREN": 2}),
(4, 8, 2, ["W16_B4"], {"RAM_WREN": 1}),
(5, 8, 1, ["W16_B4"], {"RAM_WREN": 1}),
(5, 8, 2, ["W16_B4"], {"RAM_WREN": 2}),
(4,16, 1, ["W16_B4"], {"RAM_WREN": 1}),
(4, 4, 2, ["W8_B8"], {"RAM_WREN": 2}),
(4, 4, 1, ["W8_B8"], {"RAM_WREN": 1}),
(4, 8, 2, ["W8_B8"], {"RAM_WREN": 2}),
(3, 8, 2, ["W8_B8"], {"RAM_WREN": 2}),
(4, 4, 2, ["W8_B4"], {"RAM_WREN": 1}),
(4, 2, 4, ["W8_B4"], {"RAM_WREN": 2}),
(4, 4, 4, ["W8_B4"], {"RAM_WREN": 2}),
(4, 4, 4, ["W4_B4"], {"RAM_WREN": 4}),
(4, 4, 5, ["W4_B4"], {"RAM_WREN": 5}),
]:
name = f"wren_a{abits}d{wbits}w{words}_{defs[0]}"
TESTS.append(Test(
name, ENABLES.format(abits=abits, wbits=wbits, words=words),
["wren"], defs, cells
))
defs.append("WRBE_SEPARATE")
name += "_separate"
TESTS.append(Test(
name, ENABLES.format(abits=abits, wbits=wbits, words=words),
["wren"], defs, cells
))
# abits/dbits determination (aka general geometry picking)
GEOMETRIC = """
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;
(* ram_style="block" *)
reg [DBITS-1:0] mem [0:2**ABITS-1];
always @(posedge clk)
if (we)
mem[rwa] <= wd;
else
rd <= mem[rwa];
endmodule
"""
for (abits,dbits, libs, defs, cells) in [
# W64_B8 gives 16 * 64 = 1024 bits of memory with up to 8x8b rw ports
( 4, 64, ["wren"], ["W64_B8"], {"RAM_WREN": 1}),
( 5, 32, ["wren"], ["W64_B8"], {"RAM_WREN": 1}),
( 5, 64, ["wren"], ["W64_B8"], {"RAM_WREN": 2}),
( 6, 16, ["wren"], ["W64_B8"], {"RAM_WREN": 1}),
( 6, 30, ["wren"], ["W64_B8"], {"RAM_WREN": 2}),
( 6, 64, ["wren"], ["W64_B8"], {"RAM_WREN": 4}),
( 7, 4, ["wren"], ["W64_B8"], {"RAM_WREN": 1}),
( 7, 6, ["wren"], ["W64_B8"], {"RAM_WREN": 1}),
( 7, 8, ["wren"], ["W64_B8"], {"RAM_WREN": 1}),
( 7, 17, ["wren"], ["W64_B8"], {"RAM_WREN": 3}),
( 8, 4, ["wren"], ["W64_B8"], {"RAM_WREN": 2}),
( 8, 6, ["wren"], ["W64_B8"], {"RAM_WREN": 2}),
( 9, 4, ["wren"], ["W64_B8"], {"RAM_WREN": 4}),
( 9, 8, ["wren"], ["W64_B8"], {"RAM_WREN": 4}),
( 9, 5, ["wren"], ["W64_B8"], {"RAM_WREN": 4}),
( 9, 6, ["wren"], ["W64_B8"], {"RAM_WREN": 4}),
# 9b1B gives 128 bits of memory with 1 2 or 4 bit read and write ports
# or 144 bits with 9 or 18 bit read and write ports
( 3, 18, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 1}),
( 4, 4, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 1}),
( 4, 18, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 2}),
( 5, 32, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 8}),
( 6, 4, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 2}),
( 7, 11, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 11}),
( 7, 18, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 16}),
( 11, 1, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 16}),
]:
name = f"geom_a{abits}d{dbits}_{libs[0]}"
TESTS.append(Test(
name, GEOMETRIC.format(abits=abits, dbits=dbits),
libs, defs, cells
))
# Mixed width testing
WIDE_SDP = """
module top(rclk, ra, rd, re, rr, wclk, wa, wd, we);
@ -857,6 +1287,233 @@ for (aw, rw, ww, bw, cntww, cntwr) in [
{"RAM_WIDE_WRITE": cntww}
))
# Multiple read ports
# 1rw port plus 3 (or 7) r ports
QUAD_PORT = """
module top(clk, rwa, r0a, r1a, r2a, rd, r0d, r1d, r2d, wd, we);
localparam ABITS = {abits};
localparam DBITS = {dbits};
input wire clk;
input wire we;
input wire [ABITS-1:0] rwa;
input wire [ABITS-1:0] r0a;
input wire [ABITS-1:0] r1a;
input wire [ABITS-1:0] r2a;
input wire [DBITS-1:0] wd;
output wire [DBITS-1:0] rd;
output wire [DBITS-1:0] r0d;
output wire [DBITS-1:0] r1d;
output wire [DBITS-1:0] r2d;
reg [DBITS-1:0] mem [0:2**ABITS-1];
always @(posedge clk)
if (we)
mem[rwa] <= wd;
assign rd = mem[rwa];
assign r0d = mem[r0a];
assign r1d = mem[r1a];
assign r2d = mem[r2a];
endmodule
"""
for (abits, dbits, cnt) in [
(2, 2, 1),
(4, 2, 1),
(5, 2, 2),
(4, 4, 2),
(6, 2, 4),
(4, 8, 4),
]:
TESTS.append(Test(
f"quad_port_a{abits}d{dbits}",
QUAD_PORT.format(abits=abits, dbits=dbits),
["multilut"], ["PORTS_QUAD"],
{"LUT_MULTI": cnt}
))
# Wide asynchronous read port
WIDE_READ = """
module top(clk, we, rwa, wd, rd);
localparam ABITS = {abits};
localparam WBITS = {wbits};
localparam RWORDS = {rwords};
input wire clk;
input wire we;
input wire [ABITS-1:0] rwa;
input wire [WBITS-1:0] wd;
output wire [(WBITS*RWORDS)-1:0] rd;
reg [WBITS-1:0] mem [0:2**ABITS-1];
always @(posedge clk)
if (we)
mem[rwa] <= wd;
genvar i;
generate
for (i = 0; i < RWORDS; i = i + 1)
assign rd[i*WBITS+:WBITS] = mem[rwa + i];
endgenerate
endmodule
"""
for (abits, wbits, rwords, cntquad, cntoct) in [
(4, 2, 1, 1, 1),
(4, 2, 2, 1, 1),
(4, 2, 3, 1, 1),
(4, 2, 4, 1, 1),
(4, 2, 5, 2, 1),
(4, 2, 6, 2, 1),
(4, 2, 7, 2, 1), # Write port needs to be duplicated, so only 3 extra read
(4, 2, 8, 3, 1), # ports per quad port LUT (i.e. 7 ports in 2, 8 ports in 3)
(4, 2, 9, 3, 2),
(4, 4, 1, 2, 2),
(4, 4, 4, 2, 2),
(4, 4, 6, 4, 2),
(4, 4, 9, 6, 4),
(5, 2, 1, 2, 2),
(5, 2, 4, 2, 2),
(5, 2, 9, 6, 4),
]:
TESTS.append(Test(
f"wide_quad_a{abits}w{wbits}r{rwords}",
WIDE_READ.format(abits=abits, wbits=wbits, rwords=rwords),
["multilut"], ["PORTS_QUAD"],
{"LUT_MULTI": cntquad}
))
TESTS.append(Test(
f"wide_oct_a{abits}w{wbits}r{rwords}",
WIDE_READ.format(abits=abits, wbits=wbits, rwords=rwords),
["multilut"], ["PORTS_OCT"],
{"LUT_MULTI": cntoct}
))
# signal priorities & pattern testing
PRIORITY = """
module top(clk, clken, wren, wben, rden, rst, addr, wdata, rdata);
localparam ABITS = {abits};
localparam WBITS = {wbits};
localparam WORDS = {words};
localparam BITS = WBITS * WORDS;
input wire clk, clken;
input wire wren, rden, rst;
input wire [WORDS-1:0] wben;
input wire [ABITS-1:0] addr;
input wire [BITS-1:0] wdata;
output reg [BITS-1:0] rdata;
reg [BITS-1:0] mem [0:2**ABITS-1];
integer i;
always @(posedge clk) begin
{code}
end
endmodule
"""
#reset_gate in ["ungated", "rst", "rden && rst"]
#clk_en in [True, False]
#rdwr in ["nc", "old", "new", "undef", "newdef"]
for (testname, reset_gate, rdwr, clk_en, add_logic) in [
("no_reset", "", "old", False, 0),
("gclken", "rst", "old", False, 0),
("ungated", "ungated", "old", False, 1), # muxes wren with rst
("gclken_ce", "rst", "old", True, 3), # AND to simulate CLK_EN
("grden", "rden && rst", "old", False, 1), # selects _clken, simulates _rden
("grden_ce", "rden && rst", "old", True, 4), # both of the above
("exclwr", "", "nc", False, 2), # selects new_only and simulates
("excl_rst", "rst", "nc", False, 3), # as above, extra gate for rst
("transwr", "", "new", False, 0),
("trans_rst", "rst", "new", False, 0),
]:
write = "if (wren) \n\t\tmem[addr] <= wdata;"
if rdwr == "new":
read = """if (rden)
if (wren)
rdata <= wdata;
else
rdata <= mem[addr];"""
else:
read = "if (rden) \n\t\trdata <= mem[addr];"
if "rst" in reset_gate:
read = f"if ({reset_gate})\n\t\trdata <= 0; \n\telse {read}"
if reset_gate == "ungated":
outer = "if (rst)\n\trdata <= 0;\nelse "
else:
outer = ""
if clk_en:
outer = f"{outer}if (clken) "
code = f"""{outer}begin
{write}
{"else " if rdwr == "nc" else ""}{read}
end"""
TESTS.append(Test(
testname, PRIORITY.format(code=code, abits=4, wbits=8, words=2),
["block_sp_full"], ["USE_SRST"],
{"RAM_BLOCK_SP": 1, "$*": add_logic}
))
for (testname, reset_gate, defs, rdwr, add_logic) in [
("wr_byte", "", ["USE_SRST_BLOCKING"], "old", 0),
("trans_byte", "", ["USE_SRST_BLOCKING"], "new", 0),
("wr_rst_byte", "rst", ["USE_SRST"], "old", 2), # expected mux to emulate blocking
("rst_wr_byte", "rst", ["USE_SRST_BLOCKING"], "old", 2), # should use hardware blocking, doesn't
("rdenrst_wr_byte", "rden && rst", ["USE_SRST"], "old", 3),
]:
wordsloop = "for (i=0; i<WORDS; i=i+1)"
if rdwr == "old":
read_write = f"""if (rden)
rdata = mem[addr];
{wordsloop}
if (wben[i])
mem[addr][i] <= wdata[i];"""
else:
read_write = f"""{wordsloop}
if (wben[i]) begin
mem[addr][i] <= wdata[i];
rdata[i] <= wdata[i];
end else
rdata[i] <= mem[addr][i];"""
if "rst" in reset_gate:
reset_rw = f"""if ({reset_gate})
rdata <= 0;
else begin
{read_write}
end"""
else:
reset_rw = read_write
if reset_gate == "ungated":
outer = "if (rst)\n\trdata <= 0;\nelse "
else:
outer = ""
code = f"{outer}\n{reset_rw}"
TESTS.append(Test(
testname, PRIORITY.format(code=code, abits=4, wbits=1, words=2),
["block_sp_full"], defs,
{"RAM_BLOCK_SP": 1, "$*": add_logic}
))
with open("run-test.mk", "w") as mf:
mf.write("ifneq ($(strip $(SEED)),)\n")
mf.write("SEEDOPT=-S$(SEED)\n")