From d6e1b9d8ba83949a0359616c2b94a774dad1a742 Mon Sep 17 00:00:00 2001 From: SikongJueluo Date: Wed, 27 May 2026 13:53:36 +0800 Subject: [PATCH] refactor(cam): rename read-noise path to read-pass-through and reorganize test module structure - Rename `read_noise` scenarios and noise_mode to `read_pass_through` across run_cam_correctness.py, test_run_cam_correctness.py, and Makefiles - Update RTL comment in match_engine_pipeline.sv to reflect pass-through behavior - Move unit-level cocotb tests from `tests.test_*` flat namespace to `tests.modules.*` and `tests.top.*` subdirectory layout, matching actual Makefile paths (hw/sim/tests/modules/..., hw/sim/tests/top/...) - Remove redundant dual-noise subtarget from read_noise/Makefile - Update help text and docs to reflect read-path pass-through semantics - Add .codegraph to .gitignore --- .gitignore | 1 + .justfile | 4 +- docs/experiments.md | 2 +- hw/rtl/core/match_engine_pipeline.sv | 4 +- hw/sim/Makefile | 6 +- .../cam_read_noise/test_cam_read_noise.py | 2 +- hw/sim/tests/top/read_noise/Makefile | 9 +- scripts/run_cam_correctness.py | 32 +++--- tests/test_run_cam_correctness.py | 97 +++++++++---------- 9 files changed, 72 insertions(+), 85 deletions(-) diff --git a/.gitignore b/.gitignore index 636c795..0cff4fc 100644 --- a/.gitignore +++ b/.gitignore @@ -218,6 +218,7 @@ outputs/ .sisyphus .claude/settings.local.json openspec/changes/ +.codegraph/ .logs/ docs/superpowers diff --git a/.justfile b/.justfile index 958bb31..b3dbcd1 100644 --- a/.justfile +++ b/.justfile @@ -79,7 +79,7 @@ cam-test-retrieval-no-noise: just remote "make -C hw/sim clean && make -C hw/sim test-benchmark-retrieval TOPK_K=5 WRITE_NOISE_EN=0" # Run CAM retrieval benchmark with write noise enabled (Phase 2: read noise removed) -cam-test-retrieval-read-noise: +cam-test-retrieval-write-noise: just remote "make -C hw/sim clean && make -C hw/sim test-benchmark-retrieval TOPK_K=5 WRITE_NOISE_EN=1 WRITE_NOISE_RATE_NUM=1 WRITE_NOISE_RATE_DEN=100" # Prepare CIFAR10 hash artifact for CAM retrieval smoke benchmark @@ -95,5 +95,5 @@ cam-test-retrieval-artifact DATASET_PATH NUM_ROWS="4096": just remote "make -C hw/sim clean && make -C hw/sim test-benchmark-retrieval TOPK_K=5 NUM_ROWS={{NUM_ROWS}} WRITE_NOISE_EN=0 CAM_RETRIEVAL_DATASET={{ DATASET_PATH }}" # Run CAM retrieval benchmark on a prepared artifact with write noise enabled (Phase 2: read noise removed) -cam-test-retrieval-artifact-read-noise DATASET_PATH NUM_ROWS="4096": +cam-test-retrieval-artifact-write-noise DATASET_PATH NUM_ROWS="4096": just remote "make -C hw/sim clean && make -C hw/sim test-benchmark-retrieval TOPK_K=5 NUM_ROWS={{NUM_ROWS}} WRITE_NOISE_EN=1 WRITE_NOISE_RATE_NUM=1 WRITE_NOISE_RATE_DEN=100 CAM_RETRIEVAL_DATASET={{ DATASET_PATH }}" diff --git a/docs/experiments.md b/docs/experiments.md index 0fcef38..3125e97 100644 --- a/docs/experiments.md +++ b/docs/experiments.md @@ -157,7 +157,7 @@ - `HASH_BITS` - `LANES` - `WRITE_NOISE_*` - - `READ_NOISE_*` + - 读取路径 pass-through 验证(读阶段不再注入噪声) - `.justfile` 已封装远程 Verilator/Cocotb 执行命令。 ### 还需补齐 diff --git a/hw/rtl/core/match_engine_pipeline.sv b/hw/rtl/core/match_engine_pipeline.sv index f7d31d2..b390598 100644 --- a/hw/rtl/core/match_engine_pipeline.sv +++ b/hw/rtl/core/match_engine_pipeline.sv @@ -375,8 +375,8 @@ module match_engine_pipeline ( S_ISSUE_READ: begin // Emit rd_valid_o for exactly one cycle (combinational), then - // transition to S_WAIT_READ_RESP. This ensures the read-noise - // PRNG in cam_read_noise only advances once per batch issue. + // transition to S_WAIT_READ_RESP. This keeps one logical read + // request per batch issue before waiting for the response path. state_q <= S_WAIT_READ_RESP; end diff --git a/hw/sim/Makefile b/hw/sim/Makefile index 107fa52..bd2e7ed 100644 --- a/hw/sim/Makefile +++ b/hw/sim/Makefile @@ -7,11 +7,11 @@ TOP_CONFIGS := no_noise write_noise read_noise help: @echo "Available hw/sim targets:" @echo " make test-model" - @echo " make test-top # 默认运行所有顶层噪声配置" - @echo " make test-top-all # 运行所有顶层噪声配置" + @echo " make test-top # 默认运行所有顶层配置" + @echo " make test-top-all # 运行所有顶层配置" @echo " make test-top-no_noise # 无噪声集成测试" @echo " make test-top-write_noise # 写入噪声集成测试" - @echo " make test-top-read_noise # 读取噪声集成测试" + @echo " make test-top-read_noise # 读取路径 pass-through 集成测试" @echo " make test-module MODULE=cam_core_banked" @echo " make test-modules" @echo " make test-perf" diff --git a/hw/sim/tests/modules/cam_read_noise/test_cam_read_noise.py b/hw/sim/tests/modules/cam_read_noise/test_cam_read_noise.py index 8826f54..c65666e 100644 --- a/hw/sim/tests/modules/cam_read_noise/test_cam_read_noise.py +++ b/hw/sim/tests/modules/cam_read_noise/test_cam_read_noise.py @@ -52,7 +52,7 @@ async def read_noise_disabled_forwards_hashes_after_one_stage(dut): @cocotb.test() async def read_noise_enabled_still_forwards_hashes_unmodified(dut): - """With READ_NOISE_EN=1, the pass-through still forwards hashes unmodified.""" + """The read path pass-through forwards hashes unmodified.""" cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start()) await reset_read_noise(dut) diff --git a/hw/sim/tests/top/read_noise/Makefile b/hw/sim/tests/top/read_noise/Makefile index c35c628..c443918 100644 --- a/hw/sim/tests/top/read_noise/Makefile +++ b/hw/sim/tests/top/read_noise/Makefile @@ -6,17 +6,10 @@ TOPLEVEL := cam_top COCOTB_TEST_MODULES := tests.top.read_noise.test_read_noise VERILOG_SOURCES := $(RTL_CAM_TOP) -# 读取噪声开启(Phase 2 后为 pass-through),写入噪声默认关闭 +# 读取路径 pass-through 配置,写入噪声默认关闭 WRITE_NOISE_EN := 0 include $(SIM_ROOT)/mk/cocotb-common.mk -# ── 写入+读取双重噪声子目标 ───────────────────────────────────────── -.PHONY: test-with-write-noise - -test-with-write-noise: - $(MAKE) -B -f Makefile results.xml WRITE_NOISE_EN=1 \ - COCOTB_TEST_FILTER=read_noise_model_match - clean:: rm -rf sim_build diff --git a/scripts/run_cam_correctness.py b/scripts/run_cam_correctness.py index 4e12a8b..9fa1388 100644 --- a/scripts/run_cam_correctness.py +++ b/scripts/run_cam_correctness.py @@ -80,18 +80,18 @@ default_scenarios: list[Scenario] = [ expected="bit_flip_injected", ), Scenario( - name="cam_top_read_noise", + name="cam_top_read_path", kind="cocotb", module="tests.top.read_noise.test_read_noise", testcase=None, params={}, - noise_mode="read_noise", - expected="read_perturbed", + noise_mode="read_pass_through", + expected="pure_matching", ), Scenario( name="cam_core_banked", kind="cocotb", - module="tests.test_cam_core_banked", + module="tests.modules.cam_core_banked.test_cam_core_banked", testcase=None, params={}, noise_mode="none", @@ -100,7 +100,7 @@ default_scenarios: list[Scenario] = [ Scenario( name="match_engine_pipeline", kind="cocotb", - module="tests.test_match_engine_pipeline", + module="tests.modules.match_engine_pipeline.test_match_engine_pipeline", testcase=None, params={}, noise_mode="none", @@ -109,20 +109,20 @@ default_scenarios: list[Scenario] = [ Scenario( name="cam_write_noise", kind="cocotb", - module="tests.test_cam_write_noise", + module="tests.modules.cam_write_noise.test_cam_write_noise", testcase=None, params={}, noise_mode="write_noise", expected="bit_flip_injected", ), Scenario( - name="cam_read_noise", + name="cam_read_path", kind="cocotb", - module="tests.test_cam_read_noise", + module="tests.modules.cam_read_noise.test_cam_read_noise", testcase=None, params={}, - noise_mode="read_noise", - expected="read_perturbed", + noise_mode="read_pass_through", + expected="pass_through", ), Scenario( name="ref_model_noise", @@ -145,11 +145,11 @@ default_scenarios: list[Scenario] = [ _MAKE_DIR: dict[str, str] = { "cam_top_basic": "top/no_noise", "cam_top_write_noise": "top/write_noise", - "cam_top_read_noise": "top/read_noise", + "cam_top_read_path": "top/read_noise", "cam_core_banked": "modules/cam_core_banked", "match_engine_pipeline": "modules/match_engine_pipeline", "cam_write_noise": "modules/cam_write_noise", - "cam_read_noise": "modules/cam_read_noise", + "cam_read_path": "modules/cam_read_noise", } @@ -493,8 +493,8 @@ def write_summary_md( lines.append("") lines.append( "- **cam_top**: 通过 `cam_top_basic`、`cam_top_write_noise`、" - "`cam_top_read_noise` 三个场景覆盖顶层 CAM 模块的基本功能、" - "写入噪声注入和读取噪声注入路径" + "`cam_top_read_path` 三个场景覆盖顶层 CAM 模块的基本功能、" + "写入噪声注入和读取路径 pass-through" ) lines.append( "- **cam_core_banked**: 通过 `cam_core_banked` 场景覆盖" @@ -513,8 +513,8 @@ def write_summary_md( "写入噪声注入路径" ) lines.append( - "- **cam_read_noise**: 通过 `cam_read_noise` 场景独立覆盖" - "读取噪声注入路径" + "- **cam_read_noise**: 通过 `cam_read_path` 场景独立覆盖" + "读取路径 pass-through" ) lines.append("") lines.append("## 论文可引用结论草稿") diff --git a/tests/test_run_cam_correctness.py b/tests/test_run_cam_correctness.py index d0a030a..2f6d367 100644 --- a/tests/test_run_cam_correctness.py +++ b/tests/test_run_cam_correctness.py @@ -92,11 +92,11 @@ class TestRunnerContract: assert names == [ "cam_top_basic", "cam_top_write_noise", - "cam_top_read_noise", + "cam_top_read_path", "cam_core_banked", "match_engine_pipeline", "cam_write_noise", - "cam_read_noise", + "cam_read_path", "ref_model_noise", ] @@ -106,7 +106,7 @@ class TestRunnerContract: s = sc["cam_top_basic"] assert s.name == "cam_top_basic" assert s.kind == "cocotb" - assert s.module == "tests.test_cam_basic" + assert s.module == "tests.top.no_noise.test_no_noise" assert s.testcase is None assert s.params == {} assert s.noise_mode == "none" @@ -116,37 +116,27 @@ class TestRunnerContract: s = sc["cam_top_write_noise"] assert s.name == "cam_top_write_noise" assert s.kind == "cocotb" - assert s.module == "tests.test_cam_basic" + assert s.module == "tests.top.write_noise.test_write_noise" assert s.testcase is None - assert s.params == { - "WRITE_NOISE_EN": "1", - "WRITE_NOISE_RATE_NUM": "1", - "WRITE_NOISE_RATE_DEN": "100", - "READ_NOISE_EN": "0", - } + assert s.params == {} assert s.noise_mode == "write_noise" assert s.expected == "bit_flip_injected" - # --- cam_top_read_noise --- - s = sc["cam_top_read_noise"] - assert s.name == "cam_top_read_noise" + # --- cam_top_read_path --- + s = sc["cam_top_read_path"] + assert s.name == "cam_top_read_path" assert s.kind == "cocotb" - assert s.module == "tests.test_cam_basic" + assert s.module == "tests.top.read_noise.test_read_noise" assert s.testcase is None - assert s.params == { - "WRITE_NOISE_EN": "0", - "READ_NOISE_EN": "1", - "READ_NOISE_RATE_NUM": "1", - "READ_NOISE_RATE_DEN": "100", - } - assert s.noise_mode == "read_noise" - assert s.expected == "read_perturbed" + assert s.params == {} + assert s.noise_mode == "read_pass_through" + assert s.expected == "pure_matching" # --- cam_core_banked --- s = sc["cam_core_banked"] assert s.name == "cam_core_banked" assert s.kind == "cocotb" - assert s.module == "tests.test_cam_core_banked" + assert s.module == "tests.modules.cam_core_banked.test_cam_core_banked" assert s.testcase is None assert s.params == {} assert s.noise_mode == "none" @@ -156,7 +146,7 @@ class TestRunnerContract: s = sc["match_engine_pipeline"] assert s.name == "match_engine_pipeline" assert s.kind == "cocotb" - assert s.module == "tests.test_match_engine_pipeline" + assert s.module == "tests.modules.match_engine_pipeline.test_match_engine_pipeline" assert s.testcase is None assert s.params == {} assert s.noise_mode == "none" @@ -166,21 +156,21 @@ class TestRunnerContract: s = sc["cam_write_noise"] assert s.name == "cam_write_noise" assert s.kind == "cocotb" - assert s.module == "tests.test_cam_write_noise" + assert s.module == "tests.modules.cam_write_noise.test_cam_write_noise" assert s.testcase is None assert s.params == {} assert s.noise_mode == "write_noise" assert s.expected == "bit_flip_injected" - # --- cam_read_noise --- - s = sc["cam_read_noise"] - assert s.name == "cam_read_noise" + # --- cam_read_path --- + s = sc["cam_read_path"] + assert s.name == "cam_read_path" assert s.kind == "cocotb" - assert s.module == "tests.test_cam_read_noise" + assert s.module == "tests.modules.cam_read_noise.test_cam_read_noise" assert s.testcase is None assert s.params == {} - assert s.noise_mode == "read_noise" - assert s.expected == "read_perturbed" + assert s.noise_mode == "read_pass_through" + assert s.expected == "pass_through" # --- ref_model_noise --- s = sc["ref_model_noise"] @@ -207,7 +197,7 @@ class TestRunnerContract: assert cmd == [ "make", "-C", - "hw/sim", + "hw/sim/tests/top/write_noise", "NUM_ROWS=512", "HASH_BITS=512", "LANES=8", @@ -217,32 +207,35 @@ class TestRunnerContract: "WRITE_NOISE_RATE_NUM=1", ] - def test_build_cocotb_command_includes_toplevel_for_unit_scenarios(self): - """Unit-level cocotb scenarios get explicit TOPLEVEL override matching scenario name.""" + def test_build_cocotb_command_uses_correct_make_directory(self): + """Unit-level cocotb scenarios build in hw/sim/tests/modules/..., + top-level scenarios in hw/sim/tests/top/...""" runner = load_runner() sc = {s.name: s for s in runner.default_scenarios} - # Unit scenarios should get TOPLEVEL override matching their scenario name - unit_names = [ - "cam_core_banked", - "match_engine_pipeline", - "cam_write_noise", - "cam_read_noise", - ] - for name in unit_names: + # Unit scenarios — make -C hw/sim/tests/modules/ + for name, subpath in [ + ("cam_core_banked", "modules/cam_core_banked"), + ("match_engine_pipeline", "modules/match_engine_pipeline"), + ("cam_write_noise", "modules/cam_write_noise"), + ("cam_read_path", "modules/cam_read_noise"), + ]: cmd = runner.build_scenario_command(sc[name]) - toplevel_kvp = f"TOPLEVEL={name}" - assert toplevel_kvp in cmd, ( - f"Expected {toplevel_kvp!r} in command for {name!r}, got {cmd}" + c_flag = cmd[cmd.index("-C") + 1] + assert c_flag == f"hw/sim/tests/{subpath}", ( + f"Expected -C hw/sim/tests/{subpath} for {name!r}, got -C {c_flag}" ) - # cam_top_* scenarios should NOT get explicit TOPLEVEL (use Makefile default) - top_names = ["cam_top_basic", "cam_top_write_noise", "cam_top_read_noise"] - for name in top_names: + # Top-level scenarios — make -C hw/sim/tests/top/ + for name, subpath in [ + ("cam_top_basic", "top/no_noise"), + ("cam_top_write_noise", "top/write_noise"), + ("cam_top_read_path", "top/read_noise"), + ]: cmd = runner.build_scenario_command(sc[name]) - toplevel_flags = [arg for arg in cmd if arg.startswith("TOPLEVEL=")] - assert len(toplevel_flags) == 0, ( - f"Unexpected TOPLEVEL override in command for {name!r}: {cmd}" + c_flag = cmd[cmd.index("-C") + 1] + assert c_flag == f"hw/sim/tests/{subpath}", ( + f"Expected -C hw/sim/tests/{subpath} for {name!r}, got -C {c_flag}" ) def test_build_pytest_command_uses_reference_model_test(self): @@ -261,7 +254,7 @@ class TestRunnerContract: "uv", "run", "pytest", - "hw/sim/tests/test_ref_model_noise.py", + "hw/sim/tests/model/test_ref_model_noise.py", "-q", ]