mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1579 lines
		
	
	
	
		
			50 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1579 lines
		
	
	
	
		
			50 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# TODO:
 | 
						|
 | 
						|
# - priority logic
 | 
						|
# - swizzles for weird width progressions
 | 
						|
 | 
						|
 | 
						|
class Test:
 | 
						|
    def __init__(self, name, src, libs, defs, cells):
 | 
						|
        self.name = name
 | 
						|
        self.src = src
 | 
						|
        self.libs = libs
 | 
						|
        self.defs = defs
 | 
						|
        self.cells = cells
 | 
						|
 | 
						|
TESTS = []
 | 
						|
 | 
						|
### basic sanity tests
 | 
						|
# Asynchronous-read RAM
 | 
						|
 | 
						|
ASYNC = """
 | 
						|
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];
 | 
						|
 | 
						|
always @(posedge clk)
 | 
						|
    if (we)
 | 
						|
        mem[wa] <= wd;
 | 
						|
 | 
						|
assign rd = mem[ra];
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
ASYNC_SMALL = ASYNC.format(abits=6, dbits=6)
 | 
						|
ASYNC_BIG = ASYNC.format(abits=11, dbits=10)
 | 
						|
 | 
						|
TESTS += [
 | 
						|
    Test("async_big", ASYNC_BIG, ["lut", "block_tdp"], [], {"RAM_LUT": 384}),
 | 
						|
    Test("async_big_block", ASYNC_BIG, ["block_tdp"], [], {"RAM_BLOCK_TDP": 0}),
 | 
						|
    Test("async_small", ASYNC_SMALL, ["lut", "block_tdp"], [], {"RAM_LUT": 8}),
 | 
						|
    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);
 | 
						|
 | 
						|
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;
 | 
						|
 | 
						|
{attr}
 | 
						|
reg [DBITS-1:0] mem [0:2**ABITS-1];
 | 
						|
 | 
						|
always @(posedge clk)
 | 
						|
    if (we)
 | 
						|
        mem[wa] <= wd;
 | 
						|
 | 
						|
always @(posedge clk)
 | 
						|
    rd <= mem[ra];
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
SYNC_SMALL = SYNC.format(abits=6, dbits=6, attr="")
 | 
						|
SYNC_SMALL_BLOCK = SYNC.format(abits=6, dbits=6, attr='(* ram_style="block" *)')
 | 
						|
SYNC_BIG = SYNC.format(abits=11, dbits=10, attr="")
 | 
						|
SYNC_MID = SYNC.format(abits=6, dbits=16, attr="")
 | 
						|
 | 
						|
TESTS += [
 | 
						|
    Test("sync_big", SYNC_BIG, ["lut", "block_tdp"], [], {"RAM_BLOCK_TDP": 20}),
 | 
						|
    Test("sync_big_sdp", SYNC_BIG, ["lut", "block_sdp"], [], {"RAM_BLOCK_SDP": 20}),
 | 
						|
    Test("sync_big_lut", SYNC_BIG, ["lut"], [], {"RAM_LUT": 384}),
 | 
						|
    Test("sync_small", SYNC_SMALL, ["lut", "block_tdp"], [], {"RAM_LUT": 8}),
 | 
						|
    Test("sync_small_block", SYNC_SMALL, ["block_tdp"], [], {"RAM_BLOCK_TDP": 1}),
 | 
						|
    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 = """
 | 
						|
module top(clka, clkb, addra, addrb, rda, rdb, wda, wdb, wea, web);
 | 
						|
 | 
						|
localparam ABITS = 6;
 | 
						|
localparam DBITS = 6;
 | 
						|
 | 
						|
input wire clka, clkb;
 | 
						|
input wire wea, web;
 | 
						|
input wire [ABITS-1:0] addra, addrb;
 | 
						|
input wire [DBITS-1:0] wda, wdb;
 | 
						|
output reg [DBITS-1:0] rda, rdb;
 | 
						|
 | 
						|
reg [DBITS-1:0] mem [0:2**ABITS-1];
 | 
						|
 | 
						|
always @(posedge clka)
 | 
						|
    if (wea)
 | 
						|
        mem[addra] <= wda;
 | 
						|
    else
 | 
						|
        rda <= mem[addra];
 | 
						|
 | 
						|
always @(posedge clkb)
 | 
						|
    if (web)
 | 
						|
        mem[addrb] <= wdb;
 | 
						|
    else
 | 
						|
        rdb <= mem[addrb];
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
TESTS += [
 | 
						|
    Test("tdp", TDP, ["block_tdp", "block_sdp"], [], {"RAM_BLOCK_TDP": 1}),
 | 
						|
]
 | 
						|
 | 
						|
# shared clock
 | 
						|
# Synchronous SDP with clock domain crossing
 | 
						|
SYNC_2CLK = """
 | 
						|
module top(rclk, wclk, ra, wa, rd, wd, we);
 | 
						|
 | 
						|
localparam ABITS = 6;
 | 
						|
localparam DBITS = 16;
 | 
						|
 | 
						|
input wire rclk, wclk;
 | 
						|
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 wclk)
 | 
						|
    if (we)
 | 
						|
        mem[wa] <= wd;
 | 
						|
 | 
						|
always @(posedge rclk)
 | 
						|
    rd <= mem[ra];
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
TESTS += [
 | 
						|
        Test("sync_2clk", SYNC_2CLK, ["block_sdp"], [], {"RAM_BLOCK_SDP": 1}),
 | 
						|
        Test("sync_shared", SYNC_MID, ["block_sdp_1clk"], [], {"RAM_BLOCK_SDP_1CLK": 1}),
 | 
						|
        Test("sync_2clk_shared", SYNC_2CLK, ["block_sdp_1clk"], [], {"RAM_BLOCK_SDP_1CLK": 0}),
 | 
						|
]
 | 
						|
 | 
						|
# inter-port transparency
 | 
						|
# Synchronous SDP with write-first behaviour
 | 
						|
SYNC_TRANS = """
 | 
						|
module top(clk, ra, wa, rd, wd, we);
 | 
						|
 | 
						|
localparam ABITS = 6;
 | 
						|
localparam DBITS = 16;
 | 
						|
 | 
						|
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 @(negedge clk)
 | 
						|
    if (we)
 | 
						|
        mem[wa] <= wd;
 | 
						|
 | 
						|
always @(negedge clk) begin
 | 
						|
    rd <= mem[ra];
 | 
						|
    if (we && ra == wa)
 | 
						|
        rd <= wd;
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
TESTS += [
 | 
						|
        Test("sync_trans_old_old", SYNC_MID, ["block_sdp_1clk"], ["TRANS_OLD"], {"RAM_BLOCK_SDP_1CLK": (1, {"OPTION_TRANS": 0})}),
 | 
						|
        Test("sync_trans_old_new", SYNC_MID, ["block_sdp_1clk"], ["TRANS_NEW"], {"RAM_BLOCK_SDP_1CLK": 1}),
 | 
						|
        Test("sync_trans_old_none", SYNC_MID, ["block_sdp_1clk"], [], {"RAM_BLOCK_SDP_1CLK": 1}),
 | 
						|
        Test("sync_trans_new_old", SYNC_TRANS, ["block_sdp_1clk"], ["TRANS_OLD"], {"RAM_BLOCK_SDP_1CLK": 1}),
 | 
						|
        Test("sync_trans_new_new", SYNC_TRANS, ["block_sdp_1clk"], ["TRANS_NEW"], {"RAM_BLOCK_SDP_1CLK": (1, {"OPTION_TRANS": 1})}),
 | 
						|
        Test("sync_trans_new_none", SYNC_TRANS, ["block_sdp_1clk"], [], {"RAM_BLOCK_SDP_1CLK": 1}),
 | 
						|
]
 | 
						|
 | 
						|
# rdwr checks
 | 
						|
# Synchronous single-port RAM with mutually exclusive read/write
 | 
						|
SP_NO_CHANGE = """
 | 
						|
module top(clk, addr, rd, wd, we);
 | 
						|
 | 
						|
input wire clk;
 | 
						|
input wire we;
 | 
						|
input wire [3:0] addr;
 | 
						|
input wire [15:0] wd;
 | 
						|
output reg [15:0] rd;
 | 
						|
 | 
						|
reg [15:0] mem [0:15];
 | 
						|
 | 
						|
always @(negedge clk) begin
 | 
						|
    if (we)
 | 
						|
        mem[addr] <= wd;
 | 
						|
    else
 | 
						|
        rd <= mem[addr];
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
SP_NO_CHANGE_BE = """
 | 
						|
module top(clk, addr, rd, wd, we);
 | 
						|
 | 
						|
input wire clk;
 | 
						|
input wire [1:0] we;
 | 
						|
input wire [3:0] addr;
 | 
						|
input wire [15:0] wd;
 | 
						|
output reg [15:0] rd;
 | 
						|
 | 
						|
reg [15:0] mem [0:15];
 | 
						|
 | 
						|
always @(negedge clk) begin
 | 
						|
    if (we) begin
 | 
						|
        if (we[0])
 | 
						|
            mem[addr][7:0] <= wd[7:0];
 | 
						|
        if (we[1])
 | 
						|
            mem[addr][15:8] <= wd[15:8];
 | 
						|
    end else
 | 
						|
        rd <= mem[addr];
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
# Synchronous single-port RAM with write-first behaviour
 | 
						|
SP_NEW = """
 | 
						|
module top(clk, addr, rd, wd, we);
 | 
						|
 | 
						|
input wire clk;
 | 
						|
input wire we;
 | 
						|
input wire [3:0] addr;
 | 
						|
input wire [15:0] wd;
 | 
						|
output reg [15:0] rd;
 | 
						|
 | 
						|
reg [15:0] mem [0:15];
 | 
						|
 | 
						|
always @(negedge clk) begin
 | 
						|
    if (we) begin
 | 
						|
        mem[addr] <= wd;
 | 
						|
        rd <= wd;
 | 
						|
    end else
 | 
						|
        rd <= mem[addr];
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
SP_NEW_BE = """
 | 
						|
module top(clk, addr, rd, wd, we);
 | 
						|
 | 
						|
input wire clk;
 | 
						|
input wire [1:0] we;
 | 
						|
input wire [3:0] addr;
 | 
						|
input wire [15:0] wd;
 | 
						|
output reg [15:0] rd;
 | 
						|
 | 
						|
reg [15:0] mem [0:15];
 | 
						|
 | 
						|
always @(negedge clk) begin
 | 
						|
    rd <= mem[addr];
 | 
						|
    if (we[0]) begin
 | 
						|
        mem[addr][7:0] <= wd[7:0];
 | 
						|
        rd[7:0] <= wd[7:0];
 | 
						|
    end
 | 
						|
    if (we[1]) begin
 | 
						|
        mem[addr][15:8] <= wd[15:8];
 | 
						|
        rd[15:8] <= wd[15:8];
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
# Synchronous single-port RAM with read-first behaviour
 | 
						|
SP_OLD = """
 | 
						|
module top(clk, addr, rd, wd, we);
 | 
						|
 | 
						|
input wire clk;
 | 
						|
input wire we;
 | 
						|
input wire [3:0] addr;
 | 
						|
input wire [15:0] wd;
 | 
						|
output reg [15:0] rd;
 | 
						|
 | 
						|
reg [15:0] mem [0:15];
 | 
						|
 | 
						|
always @(negedge clk) begin
 | 
						|
    if (we)
 | 
						|
        mem[addr] <= wd;
 | 
						|
    rd <= mem[addr];
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
SP_OLD_BE = """
 | 
						|
module top(clk, addr, rd, wd, we);
 | 
						|
 | 
						|
input wire clk;
 | 
						|
input wire [1:0] we;
 | 
						|
input wire [3:0] addr;
 | 
						|
input wire [15:0] wd;
 | 
						|
output reg [15:0] rd;
 | 
						|
 | 
						|
reg [15:0] mem [0:15];
 | 
						|
 | 
						|
always @(negedge clk) begin
 | 
						|
    if (we[0])
 | 
						|
        mem[addr][7:0] <= wd[7:0];
 | 
						|
    if (we[1])
 | 
						|
        mem[addr][15:8] <= wd[15:8];
 | 
						|
    rd <= mem[addr];
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
TESTS += [
 | 
						|
        Test("sp_nc_none", SP_NO_CHANGE, ["block_sp"], [], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_new_none", SP_NEW, ["block_sp"], [], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_old_none", SP_OLD, ["block_sp"], [], {"RAM_BLOCK_SP": 0}),
 | 
						|
        Test("sp_nc_nc", SP_NO_CHANGE, ["block_sp"], ["RDWR_NO_CHANGE"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_new_nc", SP_NEW, ["block_sp"], ["RDWR_NO_CHANGE"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_old_nc", SP_OLD, ["block_sp"], ["RDWR_NO_CHANGE"], {"RAM_BLOCK_SP": 0}),
 | 
						|
        Test("sp_nc_new", SP_NO_CHANGE, ["block_sp"], ["RDWR_NEW"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_new_new", SP_NEW, ["block_sp"], ["RDWR_NEW"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_old_new", SP_OLD, ["block_sp"], ["RDWR_NEW"], {"RAM_BLOCK_SP": 0}),
 | 
						|
        Test("sp_nc_old", SP_NO_CHANGE, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_new_old", SP_NEW, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_old_old", SP_OLD, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_nc_new_only", SP_NO_CHANGE, ["block_sp"], ["RDWR_NEW_ONLY"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_new_new_only", SP_NEW, ["block_sp"], ["RDWR_NEW_ONLY"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_old_new_only", SP_OLD, ["block_sp"], ["RDWR_NEW_ONLY"], {"RAM_BLOCK_SP": 0}),
 | 
						|
        Test("sp_nc_new_only_be", SP_NO_CHANGE_BE, ["block_sp"], ["RDWR_NEW_ONLY"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_new_new_only_be", SP_NEW_BE, ["block_sp"], ["RDWR_NEW_ONLY"], {"RAM_BLOCK_SP": 2}),
 | 
						|
        Test("sp_old_new_only_be", SP_OLD_BE, ["block_sp"], ["RDWR_NEW_ONLY"], {"RAM_BLOCK_SP": 0}),
 | 
						|
        Test("sp_nc_new_be", SP_NO_CHANGE_BE, ["block_sp"], ["RDWR_NEW"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_new_new_be", SP_NEW_BE, ["block_sp"], ["RDWR_NEW"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_old_new_be", SP_OLD_BE, ["block_sp"], ["RDWR_NEW"], {"RAM_BLOCK_SP": 0}),
 | 
						|
        Test("sp_nc_old_be", SP_NO_CHANGE_BE, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_new_old_be", SP_NEW_BE, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_old_old_be", SP_OLD_BE, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_nc_nc_be", SP_NO_CHANGE_BE, ["block_sp"], ["RDWR_NO_CHANGE"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_new_nc_be", SP_NEW_BE, ["block_sp"], ["RDWR_NO_CHANGE"], {"RAM_BLOCK_SP": 2}),
 | 
						|
        Test("sp_old_nc_be", SP_OLD_BE, ["block_sp"], ["RDWR_NO_CHANGE"], {"RAM_BLOCK_SP": 0}),
 | 
						|
        Test("sp_nc_auto", SP_NO_CHANGE, ["block_sp"], ["RDWR_NO_CHANGE", "RDWR_OLD", "RDWR_NEW"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_new_auto", SP_NEW, ["block_sp"], ["RDWR_NO_CHANGE", "RDWR_OLD", "RDWR_NEW"], {"RAM_BLOCK_SP": (1, {"OPTION_RDWR": "NEW"})}),
 | 
						|
        Test("sp_old_auto", SP_OLD, ["block_sp"], ["RDWR_NO_CHANGE", "RDWR_OLD", "RDWR_NEW"], {"RAM_BLOCK_SP": (1, {"OPTION_RDWR": "OLD"})}),
 | 
						|
        Test("sp_nc_auto_be", SP_NO_CHANGE_BE, ["block_sp"], ["RDWR_NO_CHANGE", "RDWR_OLD", "RDWR_NEW"], {"RAM_BLOCK_SP": 1}),
 | 
						|
        Test("sp_new_auto_be", SP_NEW_BE, ["block_sp"], ["RDWR_NO_CHANGE", "RDWR_OLD", "RDWR_NEW"], {"RAM_BLOCK_SP": (1, {"OPTION_RDWR": "NEW"})}),
 | 
						|
        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);
 | 
						|
 | 
						|
input wire clk;
 | 
						|
input wire we, re;
 | 
						|
input wire [3:0] addr;
 | 
						|
input wire [15:0] wd;
 | 
						|
output reg [15:0] rd;
 | 
						|
 | 
						|
reg [15:0] mem [0:15];
 | 
						|
 | 
						|
initial rd = {ival};
 | 
						|
 | 
						|
always @(posedge clk) begin
 | 
						|
    if (we)
 | 
						|
        mem[addr] <= wd;
 | 
						|
    if (re)
 | 
						|
        rd <= mem[addr];
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
SP_INIT_X = SP_INIT.format(ival="16'hxxxx")
 | 
						|
SP_INIT_0 = SP_INIT.format(ival="16'h0000")
 | 
						|
SP_INIT_V = SP_INIT.format(ival="16'h55aa")
 | 
						|
 | 
						|
TESTS += [
 | 
						|
    Test("sp_init_x_x", SP_INIT_X, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_init_x_x_re", SP_INIT_X, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_init_x_x_ce", SP_INIT_X, ["block_sp"], ["CLKEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_init_0_x", SP_INIT_0, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_init_0_x_re", SP_INIT_0, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_init_0_0", SP_INIT_0, ["block_sp"], ["RDINIT_0", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_init_0_0_re", SP_INIT_0, ["block_sp"], ["RDINIT_0", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_init_0_any", SP_INIT_0, ["block_sp"], ["RDINIT_ANY", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_init_0_any_re", SP_INIT_0, ["block_sp"], ["RDINIT_ANY", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_init_v_x", SP_INIT_V, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_init_v_x_re", SP_INIT_V, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_init_v_0", SP_INIT_V, ["block_sp"], ["RDINIT_0", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_init_v_0_re", SP_INIT_V, ["block_sp"], ["RDINIT_0", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_init_v_any", SP_INIT_V, ["block_sp"], ["RDINIT_ANY", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    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);
 | 
						|
 | 
						|
input wire clk;
 | 
						|
input wire we, re, ar;
 | 
						|
input wire [3:0] addr;
 | 
						|
input wire [15:0] wd;
 | 
						|
output reg [15:0] rd;
 | 
						|
 | 
						|
reg [15:0] mem [0:15];
 | 
						|
 | 
						|
initial rd = {ival};
 | 
						|
 | 
						|
always @(posedge clk) begin
 | 
						|
    if (we)
 | 
						|
        mem[addr] <= wd;
 | 
						|
end
 | 
						|
always @(posedge clk, posedge ar) begin
 | 
						|
    if (ar)
 | 
						|
        rd <= {aval};
 | 
						|
    else if (re)
 | 
						|
        rd <= mem[addr];
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
SP_ARST_X = SP_ARST.format(ival="16'hxxxx", aval="16'hxxxx")
 | 
						|
SP_ARST_0 = SP_ARST.format(ival="16'hxxxx", aval="16'h0000")
 | 
						|
SP_ARST_V = SP_ARST.format(ival="16'hxxxx", aval="16'h55aa")
 | 
						|
SP_ARST_E = SP_ARST.format(ival="16'h55aa", aval="16'h55aa")
 | 
						|
SP_ARST_N = SP_ARST.format(ival="16'h1234", aval="16'h55aa")
 | 
						|
 | 
						|
TESTS += [
 | 
						|
    Test("sp_arst_x_x", SP_ARST_X, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_x_x_re", SP_ARST_X, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_0_x", SP_ARST_0, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_0_x_re", SP_ARST_0, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_0_0", SP_ARST_0, ["block_sp"], ["RDINIT_ANY", "RDARST_0", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_0_0_re", SP_ARST_0, ["block_sp"], ["RDINIT_ANY", "RDARST_0", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_0_any", SP_ARST_0, ["block_sp"], ["RDINIT_ANY", "RDARST_ANY", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_0_any_re", SP_ARST_0, ["block_sp"], ["RDINIT_ANY", "RDARST_ANY", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_0_init", SP_ARST_0, ["block_sp"], ["RDINIT_ANY", "RDARST_INIT", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_0_init_re", SP_ARST_0, ["block_sp"], ["RDINIT_ANY", "RDARST_INIT", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_v_x", SP_ARST_V, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_v_x_re", SP_ARST_V, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_v_0", SP_ARST_V, ["block_sp"], ["RDINIT_ANY", "RDARST_0", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_v_0_re", SP_ARST_V, ["block_sp"], ["RDINIT_ANY", "RDARST_0", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_v_any", SP_ARST_V, ["block_sp"], ["RDINIT_ANY", "RDARST_ANY", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_v_any_re", SP_ARST_V, ["block_sp"], ["RDINIT_ANY", "RDARST_ANY", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_v_init", SP_ARST_V, ["block_sp"], ["RDINIT_ANY", "RDARST_INIT", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_v_init_re", SP_ARST_V, ["block_sp"], ["RDINIT_ANY", "RDARST_INIT", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_e_x", SP_ARST_E, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_e_x_re", SP_ARST_E, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_e_0", SP_ARST_E, ["block_sp"], ["RDINIT_ANY", "RDARST_0", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_e_0_re", SP_ARST_E, ["block_sp"], ["RDINIT_ANY", "RDARST_0", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_e_any", SP_ARST_E, ["block_sp"], ["RDINIT_ANY", "RDARST_ANY", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_e_any_re", SP_ARST_E, ["block_sp"], ["RDINIT_ANY", "RDARST_ANY", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_e_init", SP_ARST_E, ["block_sp"], ["RDINIT_ANY", "RDARST_INIT", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_e_init_re", SP_ARST_E, ["block_sp"], ["RDINIT_ANY", "RDARST_INIT", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_n_x", SP_ARST_N, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_n_x_re", SP_ARST_N, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_n_0", SP_ARST_N, ["block_sp"], ["RDINIT_ANY", "RDARST_0", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_n_0_re", SP_ARST_N, ["block_sp"], ["RDINIT_ANY", "RDARST_0", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_n_any", SP_ARST_N, ["block_sp"], ["RDINIT_ANY", "RDARST_ANY", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_n_any_re", SP_ARST_N, ["block_sp"], ["RDINIT_ANY", "RDARST_ANY", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_arst_n_init", SP_ARST_N, ["block_sp"], ["RDINIT_ANY", "RDARST_INIT", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    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);
 | 
						|
 | 
						|
input wire clk;
 | 
						|
input wire we, re, sr;
 | 
						|
input wire [3:0] addr;
 | 
						|
input wire [15:0] wd;
 | 
						|
output reg [15:0] rd;
 | 
						|
 | 
						|
reg [15:0] mem [0:15];
 | 
						|
 | 
						|
initial rd = {ival};
 | 
						|
 | 
						|
always @(posedge clk) begin
 | 
						|
    if (we)
 | 
						|
        mem[addr] <= wd;
 | 
						|
end
 | 
						|
always @(posedge clk) begin
 | 
						|
    if (sr)
 | 
						|
        rd <= {sval};
 | 
						|
    else if (re)
 | 
						|
        rd <= mem[addr];
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
# Synchronous read port with synchronous reet (enable priority over reset)
 | 
						|
SP_SRST_G = """
 | 
						|
module top(clk, addr, rd, wd, we, re, sr);
 | 
						|
 | 
						|
input wire clk;
 | 
						|
input wire we, re, sr;
 | 
						|
input wire [3:0] addr;
 | 
						|
input wire [15:0] wd;
 | 
						|
output reg [15:0] rd;
 | 
						|
 | 
						|
reg [15:0] mem [0:15];
 | 
						|
 | 
						|
initial rd = {ival};
 | 
						|
 | 
						|
always @(posedge clk) begin
 | 
						|
    if (we)
 | 
						|
        mem[addr] <= wd;
 | 
						|
end
 | 
						|
always @(posedge clk) begin
 | 
						|
    if (re) begin
 | 
						|
        if (sr)
 | 
						|
            rd <= {sval};
 | 
						|
        else
 | 
						|
            rd <= mem[addr];
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
SP_SRST_X = SP_SRST.format(ival="16'hxxxx", sval="16'hxxxx")
 | 
						|
SP_SRST_0 = SP_SRST.format(ival="16'hxxxx", sval="16'h0000")
 | 
						|
SP_SRST_V = SP_SRST.format(ival="16'hxxxx", sval="16'h55aa")
 | 
						|
SP_SRST_E = SP_SRST.format(ival="16'h55aa", sval="16'h55aa")
 | 
						|
SP_SRST_N = SP_SRST.format(ival="16'h1234", sval="16'h55aa")
 | 
						|
SP_SRST_GV = SP_SRST_G.format(ival="16'hxxxx", sval="16'h55aa")
 | 
						|
 | 
						|
TESTS += [
 | 
						|
    Test("sp_srst_x_x", SP_SRST_X, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_x_x_re", SP_SRST_X, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_0_x", SP_SRST_0, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_0_x_re", SP_SRST_0, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_0_0", SP_SRST_0, ["block_sp"], ["RDINIT_ANY", "RDSRST_0", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_0_0_re", SP_SRST_0, ["block_sp"], ["RDINIT_ANY", "RDSRST_0", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_0_any", SP_SRST_0, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_0_any_re", SP_SRST_0, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_0_init", SP_SRST_0, ["block_sp"], ["RDINIT_ANY", "RDSRST_INIT", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_0_init_re", SP_SRST_0, ["block_sp"], ["RDINIT_ANY", "RDSRST_INIT", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_v_x", SP_SRST_V, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_v_x_re", SP_SRST_V, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_v_0", SP_SRST_V, ["block_sp"], ["RDINIT_ANY", "RDSRST_0", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_v_0_re", SP_SRST_V, ["block_sp"], ["RDINIT_ANY", "RDSRST_0", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_v_any", SP_SRST_V, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_v_any_re", SP_SRST_V, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_v_any_re_gated", SP_SRST_V, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY_RE", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_v_any_ce", SP_SRST_V, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY", "CLKEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_v_any_ce_gated", SP_SRST_V, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY_CE", "CLKEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_v_init", SP_SRST_V, ["block_sp"], ["RDINIT_ANY", "RDSRST_INIT", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_v_init_re", SP_SRST_V, ["block_sp"], ["RDINIT_ANY", "RDSRST_INIT", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_e_x", SP_SRST_E, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_e_x_re", SP_SRST_E, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_e_0", SP_SRST_E, ["block_sp"], ["RDINIT_ANY", "RDSRST_0", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_e_0_re", SP_SRST_E, ["block_sp"], ["RDINIT_ANY", "RDSRST_0", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_e_any", SP_SRST_E, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_e_any_re", SP_SRST_E, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_e_init", SP_SRST_E, ["block_sp"], ["RDINIT_ANY", "RDSRST_INIT", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_e_init_re", SP_SRST_E, ["block_sp"], ["RDINIT_ANY", "RDSRST_INIT", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_n_x", SP_SRST_N, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_n_x_re", SP_SRST_N, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_n_0", SP_SRST_N, ["block_sp"], ["RDINIT_ANY", "RDSRST_0", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_n_0_re", SP_SRST_N, ["block_sp"], ["RDINIT_ANY", "RDSRST_0", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_n_any", SP_SRST_N, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_n_any_re", SP_SRST_N, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_n_init", SP_SRST_N, ["block_sp"], ["RDINIT_ANY", "RDSRST_INIT", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_n_init_re", SP_SRST_N, ["block_sp"], ["RDINIT_ANY", "RDSRST_INIT", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_gv_x", SP_SRST_GV, ["block_sp"], ["RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_gv_x_re", SP_SRST_GV, ["block_sp"], ["RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_gv_0", SP_SRST_GV, ["block_sp"], ["RDINIT_ANY", "RDSRST_0", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_gv_0_re", SP_SRST_GV, ["block_sp"], ["RDINIT_ANY", "RDSRST_0", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_gv_any", SP_SRST_GV, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_gv_any_re", SP_SRST_GV, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_gv_any_re_gated", SP_SRST_GV, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY_RE", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_gv_any_ce", SP_SRST_GV, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY", "CLKEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_gv_any_ce_gated", SP_SRST_GV, ["block_sp"], ["RDINIT_ANY", "RDSRST_ANY_CE", "CLKEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    Test("sp_srst_gv_init", SP_SRST_GV, ["block_sp"], ["RDINIT_ANY", "RDSRST_INIT", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
 | 
						|
    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);
 | 
						|
 | 
						|
input wire rclk, wclk, re, rr;
 | 
						|
input wire [2**({ww}-{bw})-1:0] we;
 | 
						|
input wire [{aw}-{rw}+{xw}-1:0] ra;
 | 
						|
input wire [{aw}-{ww}+{xw}-1:0] wa;
 | 
						|
input wire [2**{ww}-1:0] wd;
 | 
						|
output reg [2**{rw}-1:0] rd;
 | 
						|
 | 
						|
reg mem [0:2**{aw}-1];
 | 
						|
 | 
						|
initial mem[3] = 0;
 | 
						|
initial mem[17] = 1;
 | 
						|
initial mem[23] = 0;
 | 
						|
initial mem[24] = 1;
 | 
						|
 | 
						|
integer i, j;
 | 
						|
always @(posedge wclk)
 | 
						|
    for (i = 0; i < 2**{ww}; i = i + 2**{bw})
 | 
						|
        if (we[i >> {bw}])
 | 
						|
            for (j = 0; j < 2**{bw}; j = j + 1)
 | 
						|
                mem[wa << {ww} | i | j] <= wd[i | j];
 | 
						|
 | 
						|
always @(posedge rclk)
 | 
						|
    if (rr)
 | 
						|
        rd <= {sval};
 | 
						|
    else if (re)
 | 
						|
        for (i = 0; i < 2**{rw}; i = i + 1)
 | 
						|
            rd[i] <= mem[ra << {rw} | i];
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
for (aw, rw, ww, bw, xw, sval, cnt) in [
 | 
						|
    (6, 1, 1, 1, 1, "2'h1", 1),
 | 
						|
    (7, 1, 1, 1, 1, "2'h2", 2),
 | 
						|
    (8, 1, 1, 1, 1, "2'h3", 4),
 | 
						|
    (6, 0, 0, 0, 0, "2'h0", 1),
 | 
						|
    (6, 1, 0, 0, 0, "2'h0", 1),
 | 
						|
    (6, 2, 0, 0, 0, "2'h0", 1),
 | 
						|
    (6, 3, 0, 0, 0, "2'h0", 1),
 | 
						|
    (6, 4, 0, 0, 0, "2'h0", 1),
 | 
						|
    (6, 5, 0, 0, 0, "2'h0", 2),
 | 
						|
    (6, 0, 1, 0, 0, "2'h0", 2),
 | 
						|
    (6, 0, 1, 1, 0, "2'h0", 1),
 | 
						|
    (6, 0, 2, 0, 0, "2'h0", 4),
 | 
						|
    (6, 0, 2, 2, 0, "2'h0", 1),
 | 
						|
    (6, 0, 3, 2, 0, "2'h0", 1),
 | 
						|
    (6, 0, 4, 2, 0, "2'h0", 1),
 | 
						|
    (6, 0, 5, 2, 0, "2'h0", 2),
 | 
						|
    (7, 0, 0, 0, 0, "2'h0", 2),
 | 
						|
    (7, 1, 0, 0, 0, "2'h0", 2),
 | 
						|
    (7, 2, 0, 0, 0, "2'h0", 2),
 | 
						|
    (7, 3, 0, 0, 0, "2'h0", 2),
 | 
						|
    (7, 4, 0, 0, 0, "2'h0", 2),
 | 
						|
    (7, 5, 0, 0, 0, "2'h0", 2),
 | 
						|
    (7, 0, 1, 0, 0, "2'h0", 2),
 | 
						|
    (7, 0, 1, 1, 0, "2'h0", 2),
 | 
						|
    (7, 0, 2, 0, 0, "2'h0", 4),
 | 
						|
    (7, 0, 2, 2, 0, "2'h0", 2),
 | 
						|
    (7, 0, 3, 2, 0, "2'h0", 2),
 | 
						|
    (7, 0, 4, 2, 0, "2'h0", 2),
 | 
						|
    (7, 0, 5, 2, 0, "2'h0", 2),
 | 
						|
]:
 | 
						|
    TESTS.append(Test(
 | 
						|
        f"wide_sdp_a{aw}r{rw}w{ww}b{bw}x{xw}",
 | 
						|
        WIDE_SDP.format(aw=aw, rw=rw, ww=ww, bw=bw, xw=xw, sval=sval),
 | 
						|
        ["wide_sdp"], [],
 | 
						|
        {"RAM_WIDE_SDP": cnt}
 | 
						|
    ))
 | 
						|
 | 
						|
WIDE_SP = """
 | 
						|
module top(clk, a, rd, re, rr, wd, we);
 | 
						|
 | 
						|
input wire clk, re, rr;
 | 
						|
input wire [2**({ww}-{bw})-1:0] we;
 | 
						|
input wire [{aw}-1:0] a;
 | 
						|
input wire [2**{ww}-1:0] wd;
 | 
						|
output reg [2**{rw}-1:0] rd;
 | 
						|
 | 
						|
reg mem [0:2**{aw}-1];
 | 
						|
 | 
						|
initial mem[3] = 0;
 | 
						|
initial mem[17] = 1;
 | 
						|
initial mem[23] = 0;
 | 
						|
initial mem[24] = 1;
 | 
						|
 | 
						|
integer i, j;
 | 
						|
always @(posedge clk) begin
 | 
						|
    for (i = 0; i < 2**{ww}; i = i + 2**{bw})
 | 
						|
        if (we[i >> {bw}])
 | 
						|
            for (j = 0; j < 2**{bw}; j = j + 1)
 | 
						|
                mem[a & ~((1 << {ww}) - 1) | i | j] <= wd[i | j];
 | 
						|
    if (rr)
 | 
						|
        rd <= {sval};
 | 
						|
    else if (re)
 | 
						|
        for (i = 0; i < 2**{rw}; i = i + 1)
 | 
						|
            rd[i] <= mem[a & ~((1 << {rw}) - 1) | i];
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
for (aw, rw, ww, bw, sval, cnt) in [
 | 
						|
    (6, 1, 1, 1, "2'h1", 1),
 | 
						|
    (7, 1, 1, 1, "2'h2", 2),
 | 
						|
    (8, 1, 1, 1, "2'h3", 4),
 | 
						|
    (6, 0, 0, 0, "2'h0", 1),
 | 
						|
    (6, 1, 0, 0, "2'h0", 1),
 | 
						|
    (6, 2, 0, 0, "2'h0", 1),
 | 
						|
    (6, 3, 0, 0, "2'h0", 1),
 | 
						|
    (6, 4, 0, 0, "2'h0", 1),
 | 
						|
    (6, 5, 0, 0, "2'h0", 2),
 | 
						|
    (6, 0, 1, 0, "2'h0", 2),
 | 
						|
    (6, 0, 1, 1, "2'h0", 1),
 | 
						|
    (6, 0, 2, 0, "2'h0", 4),
 | 
						|
    (6, 0, 2, 2, "2'h0", 1),
 | 
						|
    (6, 0, 3, 2, "2'h0", 1),
 | 
						|
    (6, 0, 4, 2, "2'h0", 1),
 | 
						|
    (6, 0, 5, 2, "2'h0", 2),
 | 
						|
    (7, 0, 0, 0, "2'h0", 2),
 | 
						|
    (7, 1, 0, 0, "2'h0", 2),
 | 
						|
    (7, 2, 0, 0, "2'h0", 2),
 | 
						|
    (7, 3, 0, 0, "2'h0", 2),
 | 
						|
    (7, 4, 0, 0, "2'h0", 2),
 | 
						|
    (7, 5, 0, 0, "2'h0", 2),
 | 
						|
    (7, 0, 1, 0, "2'h0", 2),
 | 
						|
    (7, 0, 1, 1, "2'h0", 2),
 | 
						|
    (7, 0, 2, 0, "2'h0", 4),
 | 
						|
    (7, 0, 2, 2, "2'h0", 2),
 | 
						|
    (7, 0, 3, 2, "2'h0", 2),
 | 
						|
    (7, 0, 4, 2, "2'h0", 2),
 | 
						|
    (7, 0, 5, 2, "2'h0", 2),
 | 
						|
]:
 | 
						|
    TESTS.append(Test(
 | 
						|
        f"wide_sp_mix_a{aw}r{rw}w{ww}b{bw}",
 | 
						|
        WIDE_SP.format(aw=aw, rw=rw, ww=ww, bw=bw, sval=sval),
 | 
						|
        ["wide_sp"], ["WIDTH_MIX"],
 | 
						|
        {"RAM_WIDE_SP": cnt}
 | 
						|
    ))
 | 
						|
 | 
						|
for (aw, rw, ww, bw, sval, cnt) in [
 | 
						|
    (6, 1, 1, 1, "2'h1", 1),
 | 
						|
    (7, 1, 1, 1, "2'h2", 2),
 | 
						|
    (8, 1, 1, 1, "2'h3", 4),
 | 
						|
    (6, 0, 0, 0, "2'h0", 1),
 | 
						|
    (6, 1, 0, 0, "2'h0", 2),
 | 
						|
    (6, 2, 0, 0, "2'h0", 4),
 | 
						|
    (6, 3, 0, 0, "2'h0", 4),
 | 
						|
    (6, 4, 0, 0, "2'h0", 4),
 | 
						|
    (6, 5, 0, 0, "2'h0", 8),
 | 
						|
    (6, 0, 1, 0, "2'h0", 2),
 | 
						|
    (6, 0, 1, 1, "2'h0", 1),
 | 
						|
    (6, 0, 2, 0, "2'h0", 4),
 | 
						|
    (6, 0, 2, 2, "2'h0", 1),
 | 
						|
    (6, 0, 3, 2, "2'h0", 1),
 | 
						|
    (6, 0, 4, 2, "2'h0", 1),
 | 
						|
    (6, 0, 5, 2, "2'h0", 2),
 | 
						|
    (7, 0, 0, 0, "2'h0", 2),
 | 
						|
    (7, 1, 0, 0, "2'h0", 2),
 | 
						|
    (7, 2, 0, 0, "2'h0", 4),
 | 
						|
    (7, 3, 0, 0, "2'h0", 8),
 | 
						|
    (7, 4, 0, 0, "2'h0", 8),
 | 
						|
    (7, 5, 0, 0, "2'h0", 8),
 | 
						|
    (7, 0, 1, 0, "2'h0", 2),
 | 
						|
    (7, 0, 1, 1, "2'h0", 2),
 | 
						|
    (7, 0, 2, 0, "2'h0", 4),
 | 
						|
    (7, 0, 2, 2, "2'h0", 2),
 | 
						|
    (7, 0, 3, 2, "2'h0", 2),
 | 
						|
    (7, 0, 4, 2, "2'h0", 2),
 | 
						|
    (7, 0, 5, 2, "2'h0", 2),
 | 
						|
]:
 | 
						|
    TESTS.append(Test(
 | 
						|
        f"wide_sp_tied_a{aw}r{rw}w{ww}b{bw}",
 | 
						|
        WIDE_SP.format(aw=aw, rw=rw, ww=ww, bw=bw, sval=sval),
 | 
						|
        ["wide_sp"], [],
 | 
						|
        {"RAM_WIDE_SP": cnt}
 | 
						|
    ))
 | 
						|
 | 
						|
WIDE_RW = """
 | 
						|
module top(clk, a, rd, re, wd, we);
 | 
						|
 | 
						|
input wire clk, re;
 | 
						|
input wire [2**({ww}-{bw})-1:0] we;
 | 
						|
input wire [{aw}-1:0] a;
 | 
						|
input wire [2**{ww}-1:0] wd;
 | 
						|
output reg [2**{rw}-1:0] rd;
 | 
						|
 | 
						|
(* ram_block *)
 | 
						|
reg mem [0:2**{aw}-1];
 | 
						|
 | 
						|
initial mem[3] = 0;
 | 
						|
initial mem[17] = 1;
 | 
						|
initial mem[23] = 0;
 | 
						|
initial mem[24] = 1;
 | 
						|
 | 
						|
integer i, j;
 | 
						|
always @(posedge clk) begin
 | 
						|
    for (i = 0; i < 2**{ww}; i = i + 2**{bw})
 | 
						|
        if (we[i >> {bw}])
 | 
						|
            for (j = 0; j < 2**{bw}; j = j + 1)
 | 
						|
                mem[a & ~((1 << {ww}) - 1) | i | j] <= wd[i | j];
 | 
						|
    if (re)
 | 
						|
        for (i = 0; i < 2**{rw}; i = i + 1)
 | 
						|
            rd[i] <= mem[a & ~((1 << {rw}) - 1) | i];
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
for (aw, rw, ww, bw, cntww, cntwr) in [
 | 
						|
    (6, 1, 1, 1, 2, 1),
 | 
						|
    (7, 1, 1, 1, 4, 2),
 | 
						|
    (8, 1, 1, 1, 8, 4),
 | 
						|
    (6, 0, 0, 0, 4, 2),
 | 
						|
    (6, 1, 0, 0, 4, 2),
 | 
						|
    (6, 2, 0, 0, 4, 2),
 | 
						|
    (6, 3, 0, 0, 8, 2),
 | 
						|
    (6, 4, 0, 0, 16, 4),
 | 
						|
    (6, 5, 0, 0, 32, 8),
 | 
						|
    (6, 0, 1, 0, 4, 2),
 | 
						|
    (6, 0, 1, 1, 2, 1),
 | 
						|
    (6, 0, 2, 0, 4, 4),
 | 
						|
    (6, 0, 2, 2, 1, 2),
 | 
						|
    (6, 0, 3, 2, 1, 4),
 | 
						|
    (6, 0, 4, 2, 2, 8),
 | 
						|
    (6, 0, 5, 2, 4, 16),
 | 
						|
    (7, 0, 0, 0, 8, 4),
 | 
						|
    (7, 1, 0, 0, 8, 4),
 | 
						|
    (7, 2, 0, 0, 8, 4),
 | 
						|
    (7, 3, 0, 0, 8, 4),
 | 
						|
    (7, 4, 0, 0, 16, 4),
 | 
						|
    (7, 5, 0, 0, 32, 8),
 | 
						|
    (7, 0, 1, 0, 8, 4),
 | 
						|
    (7, 0, 1, 1, 4, 2),
 | 
						|
    (7, 0, 2, 0, 8, 4),
 | 
						|
    (7, 0, 2, 2, 2, 2),
 | 
						|
    (7, 0, 3, 2, 2, 4),
 | 
						|
    (7, 0, 4, 2, 2, 8),
 | 
						|
    (7, 0, 5, 2, 4, 16),
 | 
						|
]:
 | 
						|
    TESTS.append(Test(
 | 
						|
        f"wide_read_a{aw}r{rw}w{ww}b{bw}",
 | 
						|
        WIDE_RW.format(aw=aw, rw=rw, ww=ww, bw=bw),
 | 
						|
        ["wide_read"], [],
 | 
						|
        {"RAM_WIDE_READ": cntwr}
 | 
						|
    ))
 | 
						|
    TESTS.append(Test(
 | 
						|
        f"wide_write_a{aw}r{rw}w{ww}b{bw}",
 | 
						|
        WIDE_RW.format(aw=aw, rw=rw, ww=ww, bw=bw),
 | 
						|
        ["wide_write"], [],
 | 
						|
        {"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}
 | 
						|
	))
 | 
						|
	
 | 
						|
ROM_CASE = """
 | 
						|
module rom(input clk, input [2:0] addr, {attr}output reg [7:0] data);
 | 
						|
 | 
						|
always @(posedge clk) begin
 | 
						|
	case (addr)
 | 
						|
		3'b000: data <= 8'h12;
 | 
						|
		3'b001: data <= 8'hAB;
 | 
						|
		3'b010: data <= 8'h42;
 | 
						|
		3'b011: data <= 8'h23;
 | 
						|
		3'b100: data <= 8'h66;
 | 
						|
		3'b101: data <= 8'hC0;
 | 
						|
		3'b110: data <= 8'h3F;
 | 
						|
		3'b111: data <= 8'h95;
 | 
						|
	endcase
 | 
						|
end
 | 
						|
 | 
						|
endmodule
 | 
						|
"""
 | 
						|
 | 
						|
TESTS.append(Test("rom_case", ROM_CASE.format(attr=""), ["block_sdp"], [], {"RAM_BLOCK_SDP" : 0}))
 | 
						|
TESTS.append(Test("rom_case_block", ROM_CASE.format(attr="(* rom_style = \"block\" *) "), ["block_sdp"], [], {"RAM_BLOCK_SDP" : 1}))
 | 
						|
 | 
						|
with open("run-test.mk", "w") as mf:
 | 
						|
    mf.write("ifneq ($(strip $(SEED)),)\n")
 | 
						|
    mf.write("SEEDOPT=-S$(SEED)\n")
 | 
						|
    mf.write("endif\n")
 | 
						|
    mf.write("all:")
 | 
						|
    for t in TESTS:
 | 
						|
        mf.write(" " + t.name)
 | 
						|
    mf.write("\n")
 | 
						|
    mf.write(".PHONY: all\n")
 | 
						|
 | 
						|
 | 
						|
    for t in TESTS:
 | 
						|
        with open("t_{}.v".format(t.name), "w") as tf:
 | 
						|
            tf.write(t.src)
 | 
						|
        with open("t_{}.ys".format(t.name), "w") as sf:
 | 
						|
            sf.write("proc\n")
 | 
						|
            sf.write("opt\n")
 | 
						|
            sf.write("opt -full\n")
 | 
						|
            sf.write("memory -nomap\n")
 | 
						|
            sf.write("dump\n")
 | 
						|
            sf.write("memory_libmap")
 | 
						|
            for lib in t.libs:
 | 
						|
                sf.write(" -lib ../memlib_{}.txt".format(lib))
 | 
						|
            for d in t.defs:
 | 
						|
                sf.write(" -D {}".format(d))
 | 
						|
            sf.write("\n")
 | 
						|
            sf.write("memory_map\n")
 | 
						|
            for k, v in t.cells.items():
 | 
						|
                if isinstance(v, tuple):
 | 
						|
                    (cc, ca) = v
 | 
						|
                    sf.write("select -assert-count {} t:{}\n".format(cc, k))
 | 
						|
                    for kk, vv in ca.items():
 | 
						|
                        sf.write("select -assert-count {} t:{} r:{}={} %i\n".format(cc, k, kk, vv))
 | 
						|
                else:
 | 
						|
                    sf.write("select -assert-count {} t:{}\n".format(v, k))
 | 
						|
        mf.write("{}:\n".format(t.name))
 | 
						|
        mf.write("\t@../tools/autotest.sh -G -j $(SEEDOPT) $(EXTRA_FLAGS) -p 'script ../t_{}.ys'".format(t.name))
 | 
						|
        for lib in t.libs:
 | 
						|
            mf.write(" -l memlib_{}.v".format(lib))
 | 
						|
        mf.write(" t_{}.v || (cat t_{}.err; exit 1)\n".format(t.name, t.name))
 | 
						|
        mf.write(".PHONY: {}\n".format(t.name))
 |