Add memory_libmap tests.
authorMarcelina Kościelnicka <mwk@0x04.net>
Fri, 6 May 2022 14:30:56 +0000 (16:30 +0200)
committerMarcelina Kościelnicka <mwk@0x04.net>
Wed, 18 May 2022 15:32:56 +0000 (17:32 +0200)
22 files changed:
Makefile
tests/memlib/.gitignore [new file with mode: 0644]
tests/memlib/generate.py [new file with mode: 0644]
tests/memlib/memlib_block_sdp.txt [new file with mode: 0644]
tests/memlib/memlib_block_sdp.v [new file with mode: 0644]
tests/memlib/memlib_block_sdp_1clk.txt [new file with mode: 0644]
tests/memlib/memlib_block_sdp_1clk.v [new file with mode: 0644]
tests/memlib/memlib_block_sp.txt [new file with mode: 0644]
tests/memlib/memlib_block_sp.v [new file with mode: 0644]
tests/memlib/memlib_block_tdp.txt [new file with mode: 0644]
tests/memlib/memlib_block_tdp.v [new file with mode: 0644]
tests/memlib/memlib_lut.txt [new file with mode: 0644]
tests/memlib/memlib_lut.v [new file with mode: 0644]
tests/memlib/memlib_wide_read.txt [new file with mode: 0644]
tests/memlib/memlib_wide_read.v [new file with mode: 0644]
tests/memlib/memlib_wide_sdp.txt [new file with mode: 0644]
tests/memlib/memlib_wide_sdp.v [new file with mode: 0644]
tests/memlib/memlib_wide_sp.txt [new file with mode: 0644]
tests/memlib/memlib_wide_sp.v [new file with mode: 0644]
tests/memlib/memlib_wide_write.txt [new file with mode: 0644]
tests/memlib/memlib_wide_write.v [new file with mode: 0644]
tests/memlib/run-test.sh [new file with mode: 0755]

index 0c542aae0f9a2adc693fdc1f288e946c0385763a..7c3cc380dffaef92ab3863dc90ed0347c18141f0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -808,6 +808,7 @@ test: $(TARGETS) $(EXTRA_TARGETS)
        +cd tests/fsm && bash run-test.sh $(SEEDOPT)
        +cd tests/techmap && bash run-test.sh
        +cd tests/memories && bash run-test.sh $(ABCOPT) $(SEEDOPT)
+       +cd tests/memlib && bash run-test.sh $(SEEDOPT)
        +cd tests/bram && bash run-test.sh $(SEEDOPT)
        +cd tests/various && bash run-test.sh
        +cd tests/select && bash run-test.sh
diff --git a/tests/memlib/.gitignore b/tests/memlib/.gitignore
new file mode 100644 (file)
index 0000000..03dbe82
--- /dev/null
@@ -0,0 +1,5 @@
+t_*.log
+t_*.out
+t_*.v
+t_*.ys
+run-test.mk
diff --git a/tests/memlib/generate.py b/tests/memlib/generate.py
new file mode 100644 (file)
index 0000000..3414865
--- /dev/null
@@ -0,0 +1,900 @@
+# 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
+
+
+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
+
+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}),
+]
+
+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}),
+]
+
+### 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
+
+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
+
+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
+
+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
+"""
+
+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
+"""
+
+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"})}),
+]
+
+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}),
+]
+
+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}),
+]
+
+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
+"""
+
+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}),
+]
+
+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}
+    ))
+
+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))
diff --git a/tests/memlib/memlib_block_sdp.txt b/tests/memlib/memlib_block_sdp.txt
new file mode 100644 (file)
index 0000000..6c34c5a
--- /dev/null
@@ -0,0 +1,12 @@
+ram block \RAM_BLOCK_SDP {
+       cost 64;
+       abits 10;
+       widths 1 2 4 8 16 per_port;
+       init any;
+       port sw "W" {
+               clock anyedge;
+       }
+       port sr "R" {
+               clock anyedge;
+       }
+}
diff --git a/tests/memlib/memlib_block_sdp.v b/tests/memlib/memlib_block_sdp.v
new file mode 100644 (file)
index 0000000..d8dac68
--- /dev/null
@@ -0,0 +1,26 @@
+module RAM_BLOCK_SDP(
+       input PORT_R_CLK,
+       input [9:0] PORT_R_ADDR,
+       output reg [15:0] PORT_R_RD_DATA,
+       input PORT_W_CLK,
+       input PORT_W_WR_EN,
+       input [9:0] PORT_W_ADDR,
+       input [15:0] PORT_W_WR_DATA
+);
+
+parameter INIT = 0;
+parameter PORT_R_WIDTH = 1;
+parameter PORT_W_WIDTH = 1;
+parameter PORT_R_CLK_POL = 0;
+parameter PORT_W_CLK_POL = 0;
+
+reg [2**10-1:0] mem = INIT;
+
+always @(negedge (PORT_R_CLK ^ PORT_R_CLK_POL))
+       PORT_R_RD_DATA <= mem[PORT_R_ADDR+:PORT_R_WIDTH];
+
+always @(negedge (PORT_W_CLK ^ PORT_W_CLK_POL))
+       if (PORT_W_WR_EN)
+               mem[PORT_W_ADDR+:PORT_W_WIDTH] <= PORT_W_WR_DATA;
+
+endmodule
diff --git a/tests/memlib/memlib_block_sdp_1clk.txt b/tests/memlib/memlib_block_sdp_1clk.txt
new file mode 100644 (file)
index 0000000..07c76c2
--- /dev/null
@@ -0,0 +1,22 @@
+ram block \RAM_BLOCK_SDP_1CLK {
+       cost 64;
+       abits 10;
+       widths 1 2 4 8 16 per_port;
+       init any;
+       port sw "W" {
+               clock anyedge "C";
+               ifdef TRANS_OLD {
+                       option "TRANS" 0 {
+                               wrtrans "R" old;
+                       }
+               }
+               ifdef TRANS_NEW {
+                       option "TRANS" 1 {
+                               wrtrans "R" new;
+                       }
+               }
+       }
+       port sr "R" {
+               clock anyedge "C";
+       }
+}
diff --git a/tests/memlib/memlib_block_sdp_1clk.v b/tests/memlib/memlib_block_sdp_1clk.v
new file mode 100644 (file)
index 0000000..5e8159f
--- /dev/null
@@ -0,0 +1,36 @@
+module RAM_BLOCK_SDP_1CLK(
+       input CLK_C,
+       input PORT_R_CLK,
+       input [9:0] PORT_R_ADDR,
+       output reg [15:0] PORT_R_RD_DATA,
+       input PORT_W_CLK,
+       input PORT_W_WR_EN,
+       input [9:0] PORT_W_ADDR,
+       input [15:0] PORT_W_WR_DATA
+);
+
+parameter PORT_R_CLK_POL = 0;
+parameter PORT_W_CLK_POL = 0;
+parameter CLK_C_POL = 0;
+parameter INIT = 0;
+parameter OPTION_TRANS = 2;
+parameter PORT_R_WIDTH = 1;
+parameter PORT_W_WIDTH = 1;
+
+reg [2**10-1:0] mem = INIT;
+
+always @(negedge (CLK_C ^ CLK_C_POL)) begin
+       if (OPTION_TRANS == 0)
+               PORT_R_RD_DATA <= mem[PORT_R_ADDR+:PORT_R_WIDTH];
+       if (PORT_W_WR_EN)
+               mem[PORT_W_ADDR+:PORT_W_WIDTH] = 16'hx;
+       if (OPTION_TRANS == 2)
+               PORT_R_RD_DATA <= mem[PORT_R_ADDR+:PORT_R_WIDTH];
+       if (PORT_W_WR_EN)
+               mem[PORT_W_ADDR+:PORT_W_WIDTH] = PORT_W_WR_DATA;
+       if (OPTION_TRANS == 1)
+               PORT_R_RD_DATA <= mem[PORT_R_ADDR+:PORT_R_WIDTH];
+end
+
+
+endmodule
diff --git a/tests/memlib/memlib_block_sp.txt b/tests/memlib/memlib_block_sp.txt
new file mode 100644 (file)
index 0000000..f99320d
--- /dev/null
@@ -0,0 +1,95 @@
+ram block \RAM_BLOCK_SP {
+       cost 2;
+       abits 4;
+       width 16;
+       byte 8;
+       port srsw "A" {
+               clock posedge;
+               ifdef CLKEN {
+                       clken;
+               }
+               ifdef RDEN {
+                       rden;
+               }
+               ifdef RDWR_NO_CHANGE {
+                       option "RDWR" "NO_CHANGE" {
+                               rdwr no_change;
+                       }
+               }
+               ifdef RDWR_OLD {
+                       option "RDWR" "OLD" {
+                               rdwr old;
+                       }
+               }
+               ifdef RDWR_NEW {
+                       option "RDWR" "NEW" {
+                               rdwr new;
+                       }
+               }
+               ifdef RDWR_NEW_ONLY {
+                       option "RDWR" "NEW_ONLY" {
+                               rdwr new_only;
+                       }
+               }
+               ifdef RDINIT_0 {
+                       option "RDINIT" "ZERO" {
+                               rdinit zero;
+                       }
+               }
+               ifdef RDINIT_ANY {
+                       option "RDINIT" "ANY" {
+                               rdinit any;
+                       }
+               }
+               ifdef RDARST_0 {
+                       option "RDARST" "ZERO" {
+                               rdarst zero;
+                       }
+               }
+               ifdef RDARST_ANY {
+                       option "RDARST" "ANY" {
+                               rdarst any;
+                       }
+               }
+               ifdef RDARST_INIT {
+                       option "RDARST" "INIT" {
+                               rdarst init;
+                       }
+               }
+               ifdef RDSRST_0 {
+                       option "SRST_GATE" 0 {
+                               option "RDSRST" "ZERO" {
+                                       rdsrst zero ungated;
+                               }
+                       }
+               }
+               ifdef RDSRST_ANY {
+                       option "SRST_GATE" 0 {
+                               option "RDSRST" "ANY" {
+                                       rdsrst any ungated;
+                               }
+                       }
+               }
+               ifdef RDSRST_INIT {
+                       option "SRST_GATE" 0 {
+                               option "RDSRST" "INIT" {
+                                       rdsrst init ungated;
+                               }
+                       }
+               }
+               ifdef RDSRST_ANY_CE {
+                       option "SRST_GATE" 1 {
+                               option "RDSRST" "ANY" {
+                                       rdsrst any gated_clken;
+                               }
+                       }
+               }
+               ifdef RDSRST_ANY_RE {
+                       option "SRST_GATE" 2 {
+                               option "RDSRST" "ANY" {
+                                       rdsrst any gated_rden;
+                               }
+                       }
+               }
+       }
+}
diff --git a/tests/memlib/memlib_block_sp.v b/tests/memlib/memlib_block_sp.v
new file mode 100644 (file)
index 0000000..1f78301
--- /dev/null
@@ -0,0 +1,81 @@
+module RAM_BLOCK_SP(
+       input PORT_A_CLK,
+       input PORT_A_CLK_EN,
+       input PORT_A_RD_EN,
+       input PORT_A_RD_ARST,
+       input PORT_A_RD_SRST,
+       input [1:0] PORT_A_WR_EN,
+       input [3:0] PORT_A_ADDR,
+       output reg [15:0] PORT_A_RD_DATA,
+       input [15:0] PORT_A_WR_DATA
+);
+
+parameter OPTION_RDWR = "UNDEFINED";
+parameter OPTION_RDINIT = "UNDEFINED";
+parameter OPTION_RDARST = "UNDEFINED";
+parameter OPTION_RDSRST = "UNDEFINED";
+parameter OPTION_SRST_GATE = 0;
+parameter PORT_A_RD_INIT_VALUE = 16'hxxxx;
+parameter PORT_A_RD_ARST_VALUE = 16'hxxxx;
+parameter PORT_A_RD_SRST_VALUE = 16'hxxxx;
+
+reg [15:0] mem [0:15];
+
+initial
+       if (OPTION_RDINIT == "ZERO")
+               PORT_A_RD_DATA = 0;
+       else if (OPTION_RDINIT == "ANY")
+               PORT_A_RD_DATA = PORT_A_RD_INIT_VALUE;
+
+localparam ARST_VALUE = 
+       (OPTION_RDARST == "ZERO") ? 16'h0000 :
+       (OPTION_RDARST == "INIT") ? PORT_A_RD_INIT_VALUE :
+       (OPTION_RDARST == "ANY") ? PORT_A_RD_ARST_VALUE :
+       16'hxxxx;
+
+localparam SRST_VALUE = 
+       (OPTION_RDSRST == "ZERO") ? 16'h0000 :
+       (OPTION_RDSRST == "INIT") ? PORT_A_RD_INIT_VALUE :
+       (OPTION_RDSRST == "ANY") ? PORT_A_RD_SRST_VALUE :
+       16'hxxxx;
+
+pullup (PORT_A_CLK_EN);
+pullup (PORT_A_RD_EN);
+pulldown (PORT_A_RD_ARST);
+pulldown (PORT_A_RD_SRST);
+
+always @(posedge PORT_A_CLK) begin
+       if (PORT_A_CLK_EN) begin
+               if (PORT_A_WR_EN[0])
+                       mem[PORT_A_ADDR][7:0] <= PORT_A_WR_DATA[7:0];
+               if (PORT_A_WR_EN[1])
+                       mem[PORT_A_ADDR][15:8] <= PORT_A_WR_DATA[15:8];
+               if (PORT_A_RD_EN && (!PORT_A_WR_EN || OPTION_RDWR != "NO_CHANGE")) begin
+                       PORT_A_RD_DATA <= mem[PORT_A_ADDR];
+                       if (PORT_A_WR_EN && OPTION_RDWR == "NEW_ONLY")
+                               PORT_A_RD_DATA <= 16'hx;
+                       if (PORT_A_WR_EN[0])
+                               case (OPTION_RDWR)
+                               "NEW": PORT_A_RD_DATA[7:0] <= PORT_A_WR_DATA[7:0];
+                               "NEW_ONLY": PORT_A_RD_DATA[7:0] <= PORT_A_WR_DATA[7:0];
+                               "UNDEFINED": PORT_A_RD_DATA[7:0] <= 8'hx;
+                               endcase
+                       if (PORT_A_WR_EN[1])
+                               case (OPTION_RDWR)
+                               "NEW": PORT_A_RD_DATA[15:8] <= PORT_A_WR_DATA[15:8];
+                               "NEW_ONLY": PORT_A_RD_DATA[15:8] <= PORT_A_WR_DATA[15:8];
+                               "UNDEFINED": PORT_A_RD_DATA[15:8] <= 8'hx;
+                               endcase
+               end
+       end
+       if (PORT_A_RD_SRST && (!OPTION_SRST_GATE || (OPTION_SRST_GATE == 2 && PORT_A_RD_EN) || (OPTION_SRST_GATE == 1 && PORT_A_CLK_EN)))
+               PORT_A_RD_DATA <= SRST_VALUE;
+end
+
+always @(PORT_A_RD_ARST)
+       if (PORT_A_RD_ARST)
+               force PORT_A_RD_DATA = ARST_VALUE;
+       else
+               release PORT_A_RD_DATA;
+
+endmodule
diff --git a/tests/memlib/memlib_block_tdp.txt b/tests/memlib/memlib_block_tdp.txt
new file mode 100644 (file)
index 0000000..80cc7e5
--- /dev/null
@@ -0,0 +1,10 @@
+ram block \RAM_BLOCK_TDP {
+       cost 64;
+       abits 10;
+       widths 1 2 4 8 16 per_port;
+       init any;
+       port srsw "A" "B" {
+               clock anyedge;
+               rdwr no_change;
+       }
+}
diff --git a/tests/memlib/memlib_block_tdp.v b/tests/memlib/memlib_block_tdp.v
new file mode 100644 (file)
index 0000000..c6b8763
--- /dev/null
@@ -0,0 +1,38 @@
+module RAM_BLOCK_TDP(
+       input PORT_A_CLK,
+       input PORT_A_WR_EN,
+       input [9:0] PORT_A_ADDR,
+       input [15:0] PORT_A_WR_DATA,
+       output reg [15:0] PORT_A_RD_DATA,
+       input PORT_B_CLK,
+       input PORT_B_WR_EN,
+       input [9:0] PORT_B_ADDR,
+       input [15:0] PORT_B_WR_DATA,
+       output reg [15:0] PORT_B_RD_DATA
+);
+
+parameter INIT = 0;
+parameter PORT_A_WIDTH = 1;
+parameter PORT_B_WIDTH = 1;
+parameter PORT_A_CLK_POL = 0;
+parameter PORT_B_CLK_POL = 0;
+
+reg [2**10-1:0] mem = INIT;
+
+always @(negedge (PORT_A_CLK ^ PORT_A_CLK_POL)) begin
+       if (PORT_A_WR_EN) begin
+               mem[PORT_A_ADDR+:PORT_A_WIDTH] <= PORT_A_WR_DATA;
+       end else begin
+               PORT_A_RD_DATA <= mem[PORT_A_ADDR+:PORT_A_WIDTH];
+       end
+end
+
+always @(negedge (PORT_B_CLK ^ PORT_B_CLK_POL)) begin
+       if (PORT_B_WR_EN) begin
+               mem[PORT_B_ADDR+:PORT_B_WIDTH] <= PORT_B_WR_DATA;
+       end else begin
+               PORT_B_RD_DATA <= mem[PORT_B_ADDR+:PORT_B_WIDTH];
+       end
+end
+
+endmodule
diff --git a/tests/memlib/memlib_lut.txt b/tests/memlib/memlib_lut.txt
new file mode 100644 (file)
index 0000000..0cc8fda
--- /dev/null
@@ -0,0 +1,12 @@
+ram distributed \RAM_LUT {
+       abits 4;
+       width 4;
+       init any;
+       cost 4;
+       port ar "R" {
+       }
+       port arsw "RW" {
+               clock anyedge;
+       }
+}
+
diff --git a/tests/memlib/memlib_lut.v b/tests/memlib/memlib_lut.v
new file mode 100644 (file)
index 0000000..1f20a11
--- /dev/null
@@ -0,0 +1,30 @@
+module RAM_LUT(
+       input [3:0] PORT_R_ADDR,
+       input [3:0] PORT_RW_ADDR,
+       input PORT_RW_CLK,
+       input PORT_RW_WR_EN,
+       input [3:0] PORT_RW_WR_DATA,
+       output [3:0] PORT_R_RD_DATA,
+       output [3:0] PORT_RW_RD_DATA
+);
+
+parameter INIT = 0;
+parameter PORT_RW_CLK_POL = 1;
+
+reg [3:0] mem [0:15];
+
+integer i;
+initial
+       for (i = 0; i < 16; i += 1)
+               mem[i] = INIT[i*4+:4];
+
+assign PORT_R_RD_DATA = mem[PORT_R_ADDR];
+assign PORT_RW_RD_DATA = mem[PORT_RW_ADDR];
+
+wire CLK = PORT_RW_CLK ~^ PORT_RW_CLK_POL;
+
+always @(posedge CLK)
+       if (PORT_RW_WR_EN)
+               mem[PORT_RW_ADDR] <= PORT_RW_WR_DATA;
+
+endmodule
diff --git a/tests/memlib/memlib_wide_read.txt b/tests/memlib/memlib_wide_read.txt
new file mode 100644 (file)
index 0000000..c110210
--- /dev/null
@@ -0,0 +1,12 @@
+ram block \RAM_WIDE_READ {
+       cost 2;
+       abits 6;
+       widths 1 2 4 8 per_port;
+       init any;
+       port srsw "A" {
+               width rd 8 wr 2;
+               clock posedge;
+               rden;
+               rdwr old;
+       }
+}
diff --git a/tests/memlib/memlib_wide_read.v b/tests/memlib/memlib_wide_read.v
new file mode 100644 (file)
index 0000000..e45f643
--- /dev/null
@@ -0,0 +1,25 @@
+module RAM_WIDE_READ #(
+       parameter [63:0] INIT = 64'hx,
+       parameter PORT_A_RD_WIDTH = 8,
+       parameter PORT_A_WR_WIDTH = 2
+) (
+       input PORT_A_CLK,
+       input PORT_A_RD_EN,
+       input [5:0] PORT_A_ADDR,
+       output reg [7:0] PORT_A_RD_DATA,
+       input PORT_A_WR_EN,
+       input [1:0] PORT_A_WR_DATA
+);
+
+reg [63:0] mem;
+
+initial mem = INIT;
+
+always @(posedge PORT_A_CLK) begin
+       if (PORT_A_RD_EN)
+               PORT_A_RD_DATA <= mem[{PORT_A_ADDR[5:3], 3'b000}+:8];
+       if (PORT_A_WR_EN)
+               mem[{PORT_A_ADDR[5:1],1'b0}+:2] <= PORT_A_WR_DATA;
+end
+
+endmodule
diff --git a/tests/memlib/memlib_wide_sdp.txt b/tests/memlib/memlib_wide_sdp.txt
new file mode 100644 (file)
index 0000000..ec90c45
--- /dev/null
@@ -0,0 +1,17 @@
+ram block \RAM_WIDE_SDP {
+       cost 2;
+       abits 6;
+       widths 1 2 5 10 20 per_port;
+       byte 5;
+       init any;
+       port sr "R" {
+               clock posedge;
+               rden;
+               rdsrst any ungated;
+       }
+       port sw "W" {
+               clock posedge;
+               wrtrans "R" old;
+               wrbe_separate;
+       }
+}
diff --git a/tests/memlib/memlib_wide_sdp.v b/tests/memlib/memlib_wide_sdp.v
new file mode 100644 (file)
index 0000000..4568531
--- /dev/null
@@ -0,0 +1,45 @@
+module RAM_WIDE_SDP #(
+       parameter [79:0] INIT = 80'hx,
+       parameter PORT_R_WIDTH = 1,
+       parameter PORT_W_WIDTH = 1,
+       parameter PORT_W_WR_BE_WIDTH = 1,
+       parameter PORT_R_RD_SRST_VALUE = 16'hx
+) (
+       input PORT_R_CLK,
+       input PORT_R_RD_EN,
+       input PORT_R_RD_SRST,
+       input [5:0] PORT_R_ADDR,
+       output reg [PORT_R_WIDTH-1:0] PORT_R_RD_DATA,
+       input PORT_W_CLK,
+       input PORT_W_WR_EN,
+       input [PORT_W_WR_BE_WIDTH-1:0] PORT_W_WR_BE,
+       input [5:0] PORT_W_ADDR,
+       input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA
+);
+
+reg [79:0] mem;
+
+initial mem = INIT;
+
+always @(posedge PORT_R_CLK)
+       if (PORT_R_RD_SRST)
+               PORT_R_RD_DATA <= PORT_R_RD_SRST_VALUE;
+       else if (PORT_R_RD_EN)
+               PORT_R_RD_DATA <= mem[PORT_R_ADDR[5:2] * 5 + PORT_R_ADDR[1:0]+:PORT_R_WIDTH];
+
+generate
+       if (PORT_W_WIDTH < 5) begin
+               always @(posedge PORT_W_CLK)
+                       if (PORT_W_WR_EN && PORT_W_WR_BE[0])
+                               mem[PORT_W_ADDR[5:2] * 5 + PORT_W_ADDR[1:0]+:PORT_W_WIDTH] <= PORT_W_WR_DATA;
+       end else begin
+               integer i;
+               always @(posedge PORT_W_CLK)
+                       if (PORT_W_WR_EN)
+                               for (i = 0; i < PORT_W_WR_BE_WIDTH; i = i + 1)
+                                       if (PORT_W_WR_BE[i])
+                                               mem[(PORT_W_ADDR[5:2] + i) * 5+:5] <= PORT_W_WR_DATA[i * 5+:5];
+       end
+endgenerate
+
+endmodule
diff --git a/tests/memlib/memlib_wide_sp.txt b/tests/memlib/memlib_wide_sp.txt
new file mode 100644 (file)
index 0000000..7780e4f
--- /dev/null
@@ -0,0 +1,22 @@
+ram block \RAM_WIDE_SP {
+       cost 2;
+       abits 6;
+       widths 1 2 5 10 20 per_port;
+       byte 5;
+       init any;
+       port srsw "A" {
+               ifdef WIDTH_MIX {
+                       option "WIDTH_MIX" 1 {
+                               width mix;
+                       }
+               } else {
+                       option "WIDTH_MIX" 0 {
+                               width tied;
+                       }
+               }
+               clock posedge;
+               rden;
+               rdwr old;
+               rdsrst any ungated;
+       }
+}
diff --git a/tests/memlib/memlib_wide_sp.v b/tests/memlib/memlib_wide_sp.v
new file mode 100644 (file)
index 0000000..920a333
--- /dev/null
@@ -0,0 +1,54 @@
+module RAM_WIDE_SP #(
+       parameter [79:0] INIT = 80'hx,
+       parameter PORT_A_RD_WIDTH = 1,
+       parameter PORT_A_WR_WIDTH = 1,
+       parameter PORT_A_WIDTH = 1,
+       parameter OPTION_WIDTH_MIX = 0,
+       parameter PORT_A_WR_EN_WIDTH = 1,
+       parameter PORT_A_RD_SRST_VALUE = 16'hx,
+       parameter RD_WIDTH = OPTION_WIDTH_MIX ? PORT_A_RD_WIDTH : PORT_A_WIDTH,
+       parameter WR_WIDTH = OPTION_WIDTH_MIX ? PORT_A_WR_WIDTH : PORT_A_WIDTH
+) (
+       input PORT_A_CLK,
+       input PORT_A_RD_EN,
+       input PORT_A_RD_SRST,
+       input [5:0] PORT_A_ADDR,
+       output reg [RD_WIDTH-1:0] PORT_A_RD_DATA,
+       input [PORT_A_WR_EN_WIDTH-1:0] PORT_A_WR_EN,
+       input [WR_WIDTH-1:0] PORT_A_WR_DATA
+);
+
+reg [79:0] mem;
+
+initial mem = INIT;
+
+always @(posedge PORT_A_CLK)
+       if (PORT_A_RD_SRST)
+               PORT_A_RD_DATA <= PORT_A_RD_SRST_VALUE;
+       else if (PORT_A_RD_EN)
+               case (RD_WIDTH)
+               1: PORT_A_RD_DATA <= mem[PORT_A_ADDR[5:2] * 5 + PORT_A_ADDR[1:0]+:1];
+               2: PORT_A_RD_DATA <= mem[PORT_A_ADDR[5:2] * 5 + PORT_A_ADDR[1] * 2+:2];
+               5: PORT_A_RD_DATA <= mem[PORT_A_ADDR[5:2] * 5+:5];
+               10: PORT_A_RD_DATA <= mem[PORT_A_ADDR[5:3] * 10+:10];
+               20: PORT_A_RD_DATA <= mem[PORT_A_ADDR[5:4] * 20+:20];
+               endcase
+
+always @(posedge PORT_A_CLK)
+       case (WR_WIDTH)
+       1: if (PORT_A_WR_EN) mem[PORT_A_ADDR[5:2] * 5 + PORT_A_ADDR[1:0]+:1] <= PORT_A_WR_DATA;
+       2: if (PORT_A_WR_EN) mem[PORT_A_ADDR[5:2] * 5 + PORT_A_ADDR[1] * 2+:2] <= PORT_A_WR_DATA;
+       5: if (PORT_A_WR_EN) mem[PORT_A_ADDR[5:2] * 5+:5] <= PORT_A_WR_DATA;
+       10: begin
+               if (PORT_A_WR_EN[0]) mem[PORT_A_ADDR[5:3] * 10+:5] <= PORT_A_WR_DATA[4:0];
+               if (PORT_A_WR_EN[1]) mem[PORT_A_ADDR[5:3] * 10 + 5+:5] <= PORT_A_WR_DATA[9:5];
+       end
+       20: begin
+               if (PORT_A_WR_EN[0]) mem[PORT_A_ADDR[5:4] * 20+:5] <= PORT_A_WR_DATA[4:0];
+               if (PORT_A_WR_EN[1]) mem[PORT_A_ADDR[5:4] * 20 + 5+:5] <= PORT_A_WR_DATA[9:5];
+               if (PORT_A_WR_EN[2]) mem[PORT_A_ADDR[5:4] * 20 + 10+:5] <= PORT_A_WR_DATA[14:10];
+               if (PORT_A_WR_EN[3]) mem[PORT_A_ADDR[5:4] * 20 + 15+:5] <= PORT_A_WR_DATA[19:15];
+       end
+       endcase
+
+endmodule
diff --git a/tests/memlib/memlib_wide_write.txt b/tests/memlib/memlib_wide_write.txt
new file mode 100644 (file)
index 0000000..59222b7
--- /dev/null
@@ -0,0 +1,13 @@
+ram block \RAM_WIDE_WRITE {
+       cost 2;
+       abits 6;
+       widths 1 2 4 8 per_port;
+       byte 4;
+       init any;
+       port srsw "A" {
+               width rd 2 wr 8;
+               clock posedge;
+               rden;
+               rdwr old;
+       }
+}
diff --git a/tests/memlib/memlib_wide_write.v b/tests/memlib/memlib_wide_write.v
new file mode 100644 (file)
index 0000000..afed6d0
--- /dev/null
@@ -0,0 +1,29 @@
+module RAM_WIDE_WRITE #(
+       parameter [63:0] INIT = 64'hx,
+       parameter PORT_A_RD_WIDTH = 2,
+       parameter PORT_A_WR_WIDTH = 8,
+       parameter PORT_A_WR_EN_WIDTH = 2
+) (
+       input PORT_A_CLK,
+       input PORT_A_RD_EN,
+       input [5:0] PORT_A_ADDR,
+       output reg [1:0] PORT_A_RD_DATA,
+       input [1:0] PORT_A_WR_EN,
+       input [7:0] PORT_A_WR_DATA
+);
+
+reg [63:0] mem;
+
+initial mem = INIT;
+
+always @(posedge PORT_A_CLK) begin
+       if (PORT_A_RD_EN)
+               PORT_A_RD_DATA <= mem[{PORT_A_ADDR[5:1],1'b0}+:2];
+       if (PORT_A_WR_EN[0])
+               mem[{PORT_A_ADDR[5:3],3'b000}+:4] <= PORT_A_WR_DATA[3:0];
+       if (PORT_A_WR_EN[1])
+               mem[{PORT_A_ADDR[5:3],3'b100}+:4] <= PORT_A_WR_DATA[7:4];
+end
+
+endmodule
+
diff --git a/tests/memlib/run-test.sh b/tests/memlib/run-test.sh
new file mode 100755 (executable)
index 0000000..abe88a6
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/bash
+set -eu
+
+OPTIND=1
+seed=""    # default to no seed specified
+while getopts "S:" opt
+do
+    case "$opt" in
+       S) seed="$OPTARG" ;;
+    esac
+done
+shift "$((OPTIND-1))"
+
+python3 generate.py
+exec ${MAKE:-make} -f run-test.mk SEED="$seed"