# 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))