mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 13:29:12 +00:00 
			
		
		
		
	Right now neither `sat` nor `sim` have support for the `$check` cell. For formal verification it is a good idea to always run either async2sync or clk2fflogic which will (in a future commit) lower `$check` to `$assert`, etc. While `sim` should eventually support `$check` directly, using `async2sync` is ok for the current tests that use `sim`, so this commit also runs `async2sync` before running sim on designs containing assertions.
		
			
				
	
	
		
			463 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			463 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from __future__ import annotations
 | 
						|
 | 
						|
from dataclasses import dataclass
 | 
						|
 | 
						|
 | 
						|
blockram_template = """# ======================================
 | 
						|
log ** GENERATING TEST {top} WITH PARAMS{param_str}
 | 
						|
design -reset; read_verilog -defer ../../common/blockram.v
 | 
						|
chparam{param_str} {top}
 | 
						|
hierarchy -top {top}
 | 
						|
synth_quicklogic -family qlf_k6n10f -top {top}
 | 
						|
"""
 | 
						|
blockram_tests: "list[tuple[list[tuple[str, int]], str, list[str]]]" = [
 | 
						|
    # TDP36K = 1024x36bit RAM, 2048x18bit or 4096x9bit also work
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 36)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 18)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 12), ("DATA_WIDTH",  9)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    # larger sizes need an extra ram
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 48)], "sync_ram_*dp", ["-assert-count 2 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 36)], "sync_ram_*dp", ["-assert-count 2 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 18)], "sync_ram_*dp", ["-assert-count 2 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 10)], "sync_ram_*dp", ["-assert-count 2 t:TDP36K"]),
 | 
						|
    # 4096x20bit *can* fit in 3, albeit somewhat awkwardly
 | 
						|
    ([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 20)], "sync_ram_*dp", ["-assert-min 3 t:TDP36K",
 | 
						|
                                                                   "-assert-max 4 t:TDP36K"]),
 | 
						|
 | 
						|
    # smaller sizes can still fit in one, and assign the correct width (1, 2, 4, 8, 18 or 36)
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 32)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=36 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 24)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=36 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 18)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=18 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH",  9)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=9 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 16)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=18 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 11), ("DATA_WIDTH",  9)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=9 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 12), ("DATA_WIDTH",  8)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=9 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 13), ("DATA_WIDTH",  4)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=4 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 14), ("DATA_WIDTH",  2)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=2 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 15), ("DATA_WIDTH",  1)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=1 %i"]),
 | 
						|
 | 
						|
    # 2x asymmetric (1024x36bit write / 2048x18bit read or vice versa = 1TDP36K)
 | 
						|
    ([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 18), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 16), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 12), ("DATA_WIDTH",  9), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 12), ("DATA_WIDTH",  8), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
 | 
						|
 | 
						|
    # 4x asymmetric (1024x36bit write / 4096x9bit read or vice versa = 1TDP36K)
 | 
						|
    ([("ADDRESS_WIDTH", 11), ("DATA_WIDTH",  4), ("SHIFT_VAL", 2)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 12), ("DATA_WIDTH",  9), ("SHIFT_VAL", 2)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 12), ("DATA_WIDTH",  8), ("SHIFT_VAL", 2)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
 | 
						|
 | 
						|
    # can also use an extra TDP36K for higher width
 | 
						|
    ([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 16), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 12), ("DATA_WIDTH",  8), ("SHIFT_VAL", 2)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 36), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 2 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 36), ("SHIFT_VAL", 2)], "sync_ram_sdp_w*r", ["-assert-count 4 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH",  9), ("DATA_WIDTH", 36), ("SHIFT_VAL", 2)], "sync_ram_sdp_w*r", ["-assert-count 4 t:TDP36K"]),
 | 
						|
 | 
						|
    # # SHIFT=0 should be identical to sync_ram_sdp
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 36), ("SHIFT_VAL", 0)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 18), ("SHIFT_VAL", 0)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
 | 
						|
 | 
						|
    # asymmetric memories assign different port widths on a and b ports
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 18), ("SHIFT_VAL", 1)], "sync_ram_sdp_wwr", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=36 %i a:port_b_width=18 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH",  9), ("SHIFT_VAL", 1)], "sync_ram_sdp_wwr", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=18 %i a:port_b_width=9  %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH",  9), ("SHIFT_VAL", 2)], "sync_ram_sdp_wwr", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=36 %i a:port_b_width=9  %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 18), ("SHIFT_VAL", 1)], "sync_ram_sdp_wrr", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=18 %i a:port_b_width=36 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH",  9), ("SHIFT_VAL", 1)], "sync_ram_sdp_wrr", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=9  %i a:port_b_width=18 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH",  9), ("SHIFT_VAL", 2)], "sync_ram_sdp_wrr", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=9  %i a:port_b_width=36 %i"]),
 | 
						|
 | 
						|
    # two disjoint 18K memories can share a single TDP36K
 | 
						|
    ([("ADDRESS_WIDTH_A", 10), ("DATA_WIDTH_A", 18),
 | 
						|
      ("ADDRESS_WIDTH_B", 10), ("DATA_WIDTH_B", 18)], "double_sync_ram_sdp", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH_A", 10), ("DATA_WIDTH_A", 16),
 | 
						|
      ("ADDRESS_WIDTH_B", 11), ("DATA_WIDTH_B",  8)], "double_sync_ram_sdp", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH_A", 14), ("DATA_WIDTH_A",  1),
 | 
						|
      ("ADDRESS_WIDTH_B", 11), ("DATA_WIDTH_B",  8)], "double_sync_ram_sdp", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    # but only if data width is <= 18
 | 
						|
    ([("ADDRESS_WIDTH_A",  9), ("DATA_WIDTH_A", 36),
 | 
						|
      ("ADDRESS_WIDTH_B", 11), ("DATA_WIDTH_B",  9)], "double_sync_ram_sdp", ["-assert-count 2 t:TDP36K"]),
 | 
						|
 | 
						|
    # also for tdp
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 18)], "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 16)], "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 11), ("DATA_WIDTH",  8)], "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 12), ("DATA_WIDTH",  4)], "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 13), ("DATA_WIDTH",  2)], "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 14), ("DATA_WIDTH",  1)], "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K"]),
 | 
						|
    # still only if data width is <= 18
 | 
						|
    ([("ADDRESS_WIDTH",  9), ("DATA_WIDTH", 36)], "double_sync_ram_tdp", ["-assert-count 2 t:TDP36K"]),
 | 
						|
 | 
						|
    # sharing a TDP36K sets is_split=1
 | 
						|
    ([("ADDRESS_WIDTH_A", 10), ("DATA_WIDTH_A", 18),
 | 
						|
      ("ADDRESS_WIDTH_B", 10), ("DATA_WIDTH_B", 18)], "double_sync_ram_sdp", ["-assert-count 1 t:TDP36K a:is_split=1 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 18)],     "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K a:is_split=1 %i"]),
 | 
						|
    # an unshared TDP36K sets is_split=0
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 36)],     "sync_ram_*dp",        ["-assert-count 1 t:TDP36K a:is_split=0 %i"]),
 | 
						|
    ([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 18)],     "sync_ram_sdp_w*r",    ["-assert-count 1 t:TDP36K a:is_split=0 %i"]),
 | 
						|
 | 
						|
    # sharing a TDP36K sets correct port widths
 | 
						|
    ([("ADDRESS_WIDTH_A", 10), ("DATA_WIDTH_A", 18), ("DATA_WIDTH_B", 18), ("ADDRESS_WIDTH_B", 10)], "double_sync_ram_sdp",
 | 
						|
     ["-assert-count 1 t:TDP36K a:port_a1_width=18 %i a:port_a2_width=18 %i",
 | 
						|
      "-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 18)],                                                    "double_sync_ram_tdp",
 | 
						|
     ["-assert-count 1 t:TDP36K a:port_a1_width=18 %i a:port_a2_width=18 %i",
 | 
						|
      "-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH_A", 10), ("DATA_WIDTH_A", 16), ("DATA_WIDTH_B", 8),  ("ADDRESS_WIDTH_B", 11)], "double_sync_ram_sdp",
 | 
						|
     ["-assert-count 1 t:TDP36K a:port_a1_width=18 %i a:port_a2_width=9  %i "
 | 
						|
                    + "t:TDP36K a:port_a2_width=18 %i a:port_a1_width=9  %i %u",
 | 
						|
      "-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH_A", 12), ("DATA_WIDTH_A", 4),  ("DATA_WIDTH_B", 12), ("ADDRESS_WIDTH_B", 10)], "double_sync_ram_sdp",
 | 
						|
     ["-assert-count 1 t:TDP36K a:port_a1_width=4  %i a:port_a2_width=18 %i "
 | 
						|
                    + "t:TDP36K a:port_a2_width=4  %i a:port_a1_width=18 %i %u",
 | 
						|
      "-assert-count 1 t:TDP36K"]),
 | 
						|
    ([("ADDRESS_WIDTH_A", 13), ("DATA_WIDTH_A", 2),  ("DATA_WIDTH_B", 1),  ("ADDRESS_WIDTH_B", 14)], "double_sync_ram_sdp",
 | 
						|
     ["-assert-count 1 t:TDP36K a:port_a1_width=2  %i a:port_a2_width=1  %i "
 | 
						|
                    + "t:TDP36K a:port_a2_width=2  %i a:port_a1_width=1  %i %u",
 | 
						|
      "-assert-count 1 t:TDP36K"]),
 | 
						|
]
 | 
						|
 | 
						|
sim_template = """\
 | 
						|
design -stash synthesized
 | 
						|
design -copy-from synthesized -as {top}_synth {top}
 | 
						|
design -delete synthesized
 | 
						|
read_verilog +/quicklogic/common/cells_sim.v +/quicklogic/qlf_k6n10f/cells_sim.v +/quicklogic/qlf_k6n10f/brams_sim.v +/quicklogic/qlf_k6n10f/sram1024x18_mem.v +/quicklogic/qlf_k6n10f/ufifo_ctl.v +/quicklogic/qlf_k6n10f/TDP18K_FIFO.v
 | 
						|
read_verilog <<EOF
 | 
						|
`define MEM_TEST_VECTOR {mem_test_vector}
 | 
						|
 | 
						|
`define UUT_SUBMODULE \\
 | 
						|
{uut_submodule}
 | 
						|
EOF
 | 
						|
read_verilog -defer -formal mem_tb.v
 | 
						|
chparam{param_str} -set VECTORLEN {vectorlen} TB
 | 
						|
hierarchy -top TB -check
 | 
						|
prep
 | 
						|
async2sync
 | 
						|
log ** CHECKING SIMULATION FOR TEST {top} WITH PARAMS{param_str}
 | 
						|
sim -clock clk -n {vectorlen} -assert
 | 
						|
"""
 | 
						|
 | 
						|
sync_ram_sdp_submodule = """\
 | 
						|
sync_ram_sdp_synth uut (\\
 | 
						|
	.clk(clk),\\
 | 
						|
	.address_in_r(ra_a),\\
 | 
						|
	.data_out(rq_a),\\
 | 
						|
	.write_enable(wce_a),\\
 | 
						|
	.address_in_w(wa_a),\\
 | 
						|
	.data_in(wd_a)\\
 | 
						|
);\
 | 
						|
"""
 | 
						|
 | 
						|
sync_ram_tdp_submodule = """\
 | 
						|
sync_ram_tdp_synth uut (\\
 | 
						|
	.clk_a(clk),\\
 | 
						|
	.clk_b(clk),\\
 | 
						|
	.write_enable_a(wce_a),\\
 | 
						|
	.write_enable_b(wce_b),\\
 | 
						|
	.read_enable_a(rce_a),\\
 | 
						|
	.read_enable_b(rce_b),\\
 | 
						|
	.addr_a(ra_a),\\
 | 
						|
	.addr_b(ra_b),\\
 | 
						|
	.read_data_a(rq_a),\\
 | 
						|
	.read_data_b(rq_b),\\
 | 
						|
	.write_data_a(wd_a),\\
 | 
						|
	.write_data_b(wd_b)\\
 | 
						|
);\
 | 
						|
"""
 | 
						|
 | 
						|
sync_ram_sdp_wwr_submodule = """\
 | 
						|
sync_ram_sdp_wwr_synth uut (\\
 | 
						|
	.clk_w(clk),\\
 | 
						|
	.clk_r(clk),\\
 | 
						|
	.write_enable(wce_a),\\
 | 
						|
	.data_in(wd_a),\\
 | 
						|
	.address_in_w(wa_a),\\
 | 
						|
	.address_in_r(ra_a),\\
 | 
						|
	.data_out(rq_a)\\
 | 
						|
);\
 | 
						|
"""
 | 
						|
 | 
						|
sync_ram_sdp_wrr_submodule = """\
 | 
						|
sync_ram_sdp_wrr_synth uut (\\
 | 
						|
	.clk_w(clk),\\
 | 
						|
	.clk_r(clk),\\
 | 
						|
	.write_enable(wce_a),\\
 | 
						|
	.data_in(wd_a),\\
 | 
						|
	.address_in_w(wa_a),\\
 | 
						|
	.address_in_r(ra_a),\\
 | 
						|
	.data_out(rq_a)\\
 | 
						|
);\
 | 
						|
"""
 | 
						|
 | 
						|
double_sync_ram_sdp_submodule = """\
 | 
						|
double_sync_ram_sdp_synth uut (\\
 | 
						|
	.clk_a(clk),\\
 | 
						|
	.write_enable_a(wce_a),\\
 | 
						|
	.address_in_w_a(wa_a),\\
 | 
						|
	.address_in_r_a(ra_a),\\
 | 
						|
	.data_in_a(wd_a),\\
 | 
						|
	.data_out_a(rq_a),\\
 | 
						|
	.clk_b(clk),\\
 | 
						|
	.write_enable_b(wce_b),\\
 | 
						|
	.address_in_w_b(wa_b),\\
 | 
						|
	.address_in_r_b(ra_b),\\
 | 
						|
	.data_in_b(wd_b),\\
 | 
						|
	.data_out_b(rq_b)\\
 | 
						|
);\
 | 
						|
"""
 | 
						|
 | 
						|
@dataclass
 | 
						|
class TestClass:
 | 
						|
    params: dict[str, int]
 | 
						|
    top: str
 | 
						|
    assertions: list[str]
 | 
						|
    test_steps: None | list[dict[str, int]]
 | 
						|
 | 
						|
test_val_map = {
 | 
						|
    "rce_a": "rce_a_testvector",
 | 
						|
    "ra_a":  "ra_a_testvector",
 | 
						|
    "rq_a":  "rq_a_expected",
 | 
						|
    "wce_a": "wce_a_testvector",
 | 
						|
    "wa_a":  "wa_a_testvector",
 | 
						|
    "wd_a":  "wd_a_testvector",
 | 
						|
    "rce_b": "rce_b_testvector",
 | 
						|
    "ra_b":  "ra_b_testvector",
 | 
						|
    "rq_b":  "rq_b_expected",
 | 
						|
    "wce_b": "wce_b_testvector",
 | 
						|
    "wa_b":  "wa_b_testvector",
 | 
						|
    "wd_b":  "wd_b_testvector",
 | 
						|
}
 | 
						|
 | 
						|
sim_tests: list[TestClass] = [
 | 
						|
    TestClass( # basic SDP test
 | 
						|
        # note that the common SDP model reads every cycle, but the testbench
 | 
						|
        # still uses the rce signal to check read assertions
 | 
						|
        params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 36},
 | 
						|
        top="sync_ram_sdp",
 | 
						|
        assertions=[],
 | 
						|
        test_steps=[
 | 
						|
            {"wce_a": 1, "wa_a": 0x0A, "wd_a": 0xdeadbeef},
 | 
						|
            {"wce_a": 1, "wa_a": 0xBA, "wd_a": 0x5a5a5a5a},
 | 
						|
            {"wce_a": 1, "wa_a": 0xFF, "wd_a": 0},
 | 
						|
            {"rce_a": 1, "ra_a": 0x0A},
 | 
						|
            {"rq_a": 0xdeadbeef},
 | 
						|
            {"rce_a": 1, "ra_a": 0xFF},
 | 
						|
            {"rq_a": 0},
 | 
						|
        ]
 | 
						|
    ),
 | 
						|
    TestClass( # SDP read before write
 | 
						|
        params={"ADDRESS_WIDTH": 4, "DATA_WIDTH": 16},
 | 
						|
        top="sync_ram_sdp",
 | 
						|
        assertions=[],
 | 
						|
        test_steps=[
 | 
						|
            {"wce_a": 1, "wa_a": 0xA, "wd_a": 0x1234},
 | 
						|
            {"wce_a": 1, "wa_a": 0xA, "wd_a": 0x5678, "rce_a": 1, "ra_a": 0xA},
 | 
						|
            {"rq_a": 0x1234, "rce_a": 1, "ra_a": 0xA},
 | 
						|
            {"rq_a": 0x5678},
 | 
						|
        ]
 | 
						|
    ),
 | 
						|
    TestClass( # basic TDP test
 | 
						|
        # note that the testbench uses ra and wa, while the common TDP model
 | 
						|
        # uses a shared address
 | 
						|
        params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 36},
 | 
						|
        top="sync_ram_tdp",
 | 
						|
        assertions=[],
 | 
						|
        test_steps=[
 | 
						|
            {"wce_a": 1, "ra_a": 0x0A,      "wce_b": 1, "ra_b": 0xBA,
 | 
						|
             "wd_a": 0xdeadbeef,            "wd_b": 0x5a5a5a5a},
 | 
						|
            {"wce_a": 1, "ra_a": 0xFF,
 | 
						|
             "wd_a": 0},
 | 
						|
            {"rce_a": 1, "ra_a": 0x0A,      "rce_b": 1, "ra_b": 0x0A},
 | 
						|
            {"rq_a": 0xdeadbeef,            "rq_b": 0xdeadbeef},
 | 
						|
            {"rce_a": 1, "ra_a": 0xFF,      "rce_b": 1, "ra_b": 0xBA},
 | 
						|
            {"rq_a": 0,                     "rq_b": 0x5a5a5a5a},
 | 
						|
        ]
 | 
						|
    ),
 | 
						|
    TestClass( # TDP with truncation
 | 
						|
        params={"ADDRESS_WIDTH": 4, "DATA_WIDTH": 16},
 | 
						|
        top="sync_ram_tdp",
 | 
						|
        assertions=[],
 | 
						|
        test_steps=[
 | 
						|
            {"wce_a": 1, "ra_a": 0x0F,      "wce_b": 1, "ra_b": 0xBA,
 | 
						|
             "wd_a": 0xdeadbeef,            "wd_b": 0x5a5a5a5a},
 | 
						|
            {"wce_a": 1, "ra_a": 0xFF,
 | 
						|
             "wd_a": 0},
 | 
						|
            {"rce_a": 1, "ra_a": 0x0F,      "rce_b": 1, "ra_b": 0x0A},
 | 
						|
            {"rq_a": 0,                     "rq_b": 0x00005a5a},
 | 
						|
        ]
 | 
						|
    ),
 | 
						|
    TestClass( # TDP read before write
 | 
						|
        # note that the testbench uses rce and wce, while the common TDP model
 | 
						|
        # uses a single enable for write, with reads on no write
 | 
						|
        params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 36},
 | 
						|
        top="sync_ram_tdp",
 | 
						|
        assertions=[],
 | 
						|
        test_steps=[
 | 
						|
            {"wce_a": 1, "ra_a": 0x0A,      "wce_b": 1, "ra_b": 0xBA,
 | 
						|
             "wd_a": 0xdeadbeef,            "wd_b": 0x5a5a5a5a},
 | 
						|
            {"wce_a": 1, "ra_a": 0xBA,      "rce_b": 1, "ra_b": 0xBA,
 | 
						|
             "wd_a": 0xa5a5a5a5},
 | 
						|
            {                               "rq_b": 0x5a5a5a5a},
 | 
						|
            {"rce_a": 1, "ra_a": 0x0A,      "rce_b": 1, "ra_b": 0x0A},
 | 
						|
            {"rq_a": 0xdeadbeef,            "rq_b": 0xdeadbeef},
 | 
						|
            {                               "rce_b": 1, "ra_b": 0xBA},
 | 
						|
            {                               "rq_b": 0xa5a5a5a5},
 | 
						|
        ]
 | 
						|
    ),
 | 
						|
    TestClass( # 2x wide write
 | 
						|
        params={"ADDRESS_WIDTH": 11, "DATA_WIDTH": 18, "SHIFT_VAL": 1},
 | 
						|
        top="sync_ram_sdp_wwr",
 | 
						|
        assertions=[],
 | 
						|
        test_steps=[
 | 
						|
            {"wce_a": 1, "wa_a": 0b0000000001, "wd_a": 0xdeadbeef},
 | 
						|
            {"rce_a": 0, "ra_a": 0b00000000010},
 | 
						|
            {"rq_a": 0xdead},
 | 
						|
            {"rce_a": 0, "ra_a": 0b00000000011},
 | 
						|
            {"rq_a": 0xbeef},
 | 
						|
        ]
 | 
						|
    ),
 | 
						|
    TestClass( # 4x wide write
 | 
						|
        params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 8, "SHIFT_VAL": 2},
 | 
						|
        top="sync_ram_sdp_wwr",
 | 
						|
        assertions=[],
 | 
						|
        test_steps=[
 | 
						|
            {"wce_a": 1, "wa_a": 0b000100001, "wd_a": 0xdeadbeef},
 | 
						|
            {"rce_a": 0, "ra_a": 0b00010000100},
 | 
						|
            {"rq_a": 0xde},
 | 
						|
            {"rce_a": 0, "ra_a": 0b00010000101},
 | 
						|
            {"rq_a": 0xad},
 | 
						|
            {"rce_a": 0, "ra_a": 0b00010000110},
 | 
						|
            {"rq_a": 0xbe},
 | 
						|
            {"rce_a": 0, "ra_a": 0b00010000111},
 | 
						|
            {"rq_a": 0xef},
 | 
						|
        ]
 | 
						|
    ),
 | 
						|
    TestClass( # 2x wide read
 | 
						|
        params={"ADDRESS_WIDTH": 11, "DATA_WIDTH": 18, "SHIFT_VAL": 1},
 | 
						|
        top="sync_ram_sdp_wrr",
 | 
						|
        assertions=[],
 | 
						|
        test_steps=[
 | 
						|
            {"wce_a": 1, "wa_a": 0b00000000010, "wd_a": 0xdead},
 | 
						|
            {"wce_a": 1, "wa_a": 0b00000000011, "wd_a": 0xbeef},
 | 
						|
            {"rce_a": 0, "ra_a": 0b0000000001},
 | 
						|
            {"rq_a": 0xdeadbeef},
 | 
						|
        ]
 | 
						|
    ),
 | 
						|
    TestClass( # 4x wide read
 | 
						|
        params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 8, "SHIFT_VAL": 2},
 | 
						|
        top="sync_ram_sdp_wrr",
 | 
						|
        assertions=[],
 | 
						|
        test_steps=[
 | 
						|
            {"wce_a": 1, "wa_a": 0b00010000100, "wd_a": 0xde},
 | 
						|
            {"wce_a": 1, "wa_a": 0b00010000101, "wd_a": 0xad},
 | 
						|
            {"wce_a": 1, "wa_a": 0b00010000110, "wd_a": 0xbe},
 | 
						|
            {"wce_a": 1, "wa_a": 0b00010000111, "wd_a": 0xef},
 | 
						|
            {"rce_a": 0, "ra_a": 0b000100001},
 | 
						|
            {"rq_a": 0xdeadbeef},
 | 
						|
        ]
 | 
						|
    ),
 | 
						|
    TestClass( # basic split SDP test
 | 
						|
        params={"ADDRESS_WIDTH_A": 10, "DATA_WIDTH_A": 16,
 | 
						|
                "ADDRESS_WIDTH_B": 10, "DATA_WIDTH_B": 16},
 | 
						|
        top="double_sync_ram_sdp",
 | 
						|
        assertions=[],
 | 
						|
        test_steps=[
 | 
						|
            {"wce_a": 1, "wa_a": 0x0A,      "wce_b": 1, "wa_b": 0xBA,
 | 
						|
            "wd_a": 0x1234,                 "wd_b": 0x4567},
 | 
						|
            {"wce_a": 1, "wa_a": 0xFF,      "wce_b": 1, "wa_b": 0x0A,
 | 
						|
            "wd_a": 0,                      "wd_b": 0xbeef},
 | 
						|
            {"rce_a": 1, "ra_a": 0x0A,      "rce_b": 1, "ra_b": 0x0A},
 | 
						|
            {"rq_a": 0x1234,                "rq_b": 0xbeef},
 | 
						|
        ]
 | 
						|
    ),
 | 
						|
]
 | 
						|
 | 
						|
for (params, top, assertions) in blockram_tests:
 | 
						|
    sim_test = TestClass(
 | 
						|
        params=dict(params),
 | 
						|
        top=top,
 | 
						|
        assertions=assertions,
 | 
						|
        test_steps=None
 | 
						|
    )
 | 
						|
    sim_tests.append(sim_test)
 | 
						|
 | 
						|
i = 0
 | 
						|
j = 0
 | 
						|
max_j = 16
 | 
						|
f = None
 | 
						|
for sim_test in sim_tests:
 | 
						|
    # format params
 | 
						|
    param_str = ""
 | 
						|
    for (key, val) in sim_test.params.items():
 | 
						|
        param_str += f" -set {key} {val}"
 | 
						|
 | 
						|
    # resolve top module wildcards
 | 
						|
    top_list = [sim_test.top]
 | 
						|
    if "*dp" in sim_test.top:
 | 
						|
        top_list += [
 | 
						|
            sim_test.top.replace("*dp", dp_sub) for dp_sub in ["sdp", "tdp"]
 | 
						|
        ]
 | 
						|
    if "w*r" in sim_test.top:
 | 
						|
        top_list += [
 | 
						|
            sim_test.top.replace("w*r", wr_sub) for wr_sub in ["wwr", "wrr"]
 | 
						|
        ]
 | 
						|
    if len(top_list) > 1:
 | 
						|
        top_list.pop(0)
 | 
						|
 | 
						|
    # iterate over string substitutions
 | 
						|
    for top in top_list:
 | 
						|
        # limit number of tests per file to allow parallel make
 | 
						|
        if not f:
 | 
						|
            fn = f"t_mem{i}.ys"
 | 
						|
            f = open(fn, mode="w")
 | 
						|
            j = 0
 | 
						|
 | 
						|
        # output yosys script test file
 | 
						|
        print(
 | 
						|
            blockram_template.format(param_str=param_str, top=top),
 | 
						|
            file=f
 | 
						|
        )
 | 
						|
        for assertion in sim_test.assertions:
 | 
						|
            print("log ** CHECKING CELL COUNTS FOR TEST {top} WITH PARAMS{param_str}".format(param_str=param_str, top=top), file=f)
 | 
						|
            print("select {}".format(assertion), file=f)
 | 
						|
        print("", file=f)
 | 
						|
 | 
						|
        # prepare simulation tests
 | 
						|
        test_steps = sim_test.test_steps
 | 
						|
        if test_steps:
 | 
						|
            if top == "sync_ram_sdp":
 | 
						|
                uut_submodule = sync_ram_sdp_submodule
 | 
						|
            elif top == "sync_ram_tdp":
 | 
						|
                uut_submodule = sync_ram_tdp_submodule
 | 
						|
            elif top == "sync_ram_sdp_wwr":
 | 
						|
                uut_submodule = sync_ram_sdp_wwr_submodule
 | 
						|
            elif top == "sync_ram_sdp_wrr":
 | 
						|
                uut_submodule = sync_ram_sdp_wrr_submodule
 | 
						|
            elif top == "double_sync_ram_sdp":
 | 
						|
                uut_submodule = double_sync_ram_sdp_submodule
 | 
						|
            else:
 | 
						|
                raise NotImplementedError(f"missing submodule header for {top}")
 | 
						|
            mem_test_vector = ""
 | 
						|
            for step, test in enumerate(test_steps):
 | 
						|
                for key, val in test.items():
 | 
						|
                    key = test_val_map[key]
 | 
						|
                    mem_test_vector += f"\\\n{key}[{step}] = 'h{val:x};"
 | 
						|
            print(
 | 
						|
                sim_template.format(
 | 
						|
                    top=top,
 | 
						|
                    mem_test_vector=mem_test_vector,
 | 
						|
                    uut_submodule=uut_submodule,
 | 
						|
                    param_str=param_str,
 | 
						|
                    vectorlen=len(test_steps) + 2
 | 
						|
                ), file=f
 | 
						|
            )
 | 
						|
            # simulation counts for 2 tests
 | 
						|
            j += 1
 | 
						|
 | 
						|
        # increment test counter
 | 
						|
        j += 1
 | 
						|
        if j >= max_j:
 | 
						|
            f = f.close()
 | 
						|
            i += 1
 | 
						|
 | 
						|
if f:
 | 
						|
    f.close()
 |