refactor(hw/sim): extract common cocotb make infrastructure into shared mk/ directory

- Split monolithic hw/sim/Makefile into modular include files (mk/cocotb-common.mk, mk/rtl-sources.mk)
- Add per-module test Makefiles (cam_core_banked, cam_read_noise, cam_write_noise, match_engine_pipeline, perf, top)
- Add missing simulation tools (yosys, graphviz, xdot) to devenv.nix
- Fix path handling in sweep_noise.py and test modules to be HASH_BITS-aware
This commit is contained in:
2026-05-16 19:23:49 +08:00
parent 2e0e36eea5
commit ca167e79c6
27 changed files with 241 additions and 97 deletions

View File

@@ -0,0 +1,9 @@
SIM_ROOT := $(abspath ../../..)
RTL_ROOT := $(abspath $(SIM_ROOT)/../rtl)
include $(SIM_ROOT)/mk/rtl-sources.mk
TOPLEVEL := match_engine_pipeline
COCOTB_TEST_MODULES := tests.modules.match_engine_pipeline.test_match_engine_pipeline
VERILOG_SOURCES := $(RTL_MATCH_ENGINE)
include $(SIM_ROOT)/mk/cocotb-common.mk

View File

@@ -0,0 +1,66 @@
from __future__ import annotations
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge
async def reset_match(dut):
dut.rst_n.value = 0
dut.query_valid.value = 0
dut.query_hash.value = 0
dut.result_ready.value = 1
dut.rd_valid_i.value = 0
dut.rd_row_ids_i.value = 0
dut.rd_hashes_i.value = 0
dut.rd_lane_valid_i.value = 0
for _ in range(3):
await RisingEdge(dut.clk)
dut.rst_n.value = 1
for _ in range(2):
await RisingEdge(dut.clk)
@cocotb.test()
async def match_engine_returns_top1_after_pipeline_drain(dut):
cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
await reset_match(dut)
LANES = len(dut.rd_lane_valid_i)
ROW_BITS = len(dut.rd_row_ids_i) // LANES
HASH_BITS = len(dut.rd_hashes_i) // LANES
NUM_ROWS = 1 << len(dut.rd_base_row_o)
TARGET_ROW = 9
query = (1 << HASH_BITS) - 1
dut.query_hash.value = query
dut.query_valid.value = 1
await RisingEdge(dut.clk)
dut.query_valid.value = 0
for base in range(0, NUM_ROWS, LANES):
while int(dut.rd_valid_o.value) == 0:
await RisingEdge(dut.clk)
assert int(dut.rd_base_row_o.value) == base
rows = 0
hashes = 0
for lane in range(LANES):
row_id = base + lane
rows |= row_id << (lane * ROW_BITS)
row_hash = query if row_id == TARGET_ROW else 0
hashes |= row_hash << (lane * HASH_BITS)
dut.rd_row_ids_i.value = rows
dut.rd_hashes_i.value = hashes
dut.rd_lane_valid_i.value = (1 << LANES) - 1
dut.rd_valid_i.value = 1
await RisingEdge(dut.clk)
dut.rd_valid_i.value = 0
for _ in range(20):
if int(dut.result_valid.value):
break
await RisingEdge(dut.clk)
assert int(dut.result_valid.value) == 1
assert int(dut.result_row.value) == TARGET_ROW
assert int(dut.result_score.value) == HASH_BITS