mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-12 20:15:31 +08:00
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:
109
hw/sim/scripts/sweep_noise.py
Normal file
109
hw/sim/scripts/sweep_noise.py
Normal file
@@ -0,0 +1,109 @@
|
||||
"""Sweep write-noise rates and measure top-1 stability.
|
||||
|
||||
Applies write-noise flip masks to stored rows (simulating noisy writes),
|
||||
then queries the noisy rows and compares top-1 results against clean rows.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SIM_ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(SIM_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(SIM_ROOT))
|
||||
|
||||
import numpy as np
|
||||
from model.ref_model import (
|
||||
generate_write_flip_mask,
|
||||
match_top1,
|
||||
random_hashes,
|
||||
)
|
||||
|
||||
|
||||
def apply_write_noise(
|
||||
rows: list[int],
|
||||
*,
|
||||
width: int,
|
||||
rate_num: int,
|
||||
rate_den: int,
|
||||
noise_bits: int = 8,
|
||||
seed: int = 0,
|
||||
) -> list[int]:
|
||||
"""Apply write-noise flip masks to every row, returning noisy copies.
|
||||
|
||||
*seed* is a 64-bit value (RTL NOISE_SEED). It is duplicated to form
|
||||
the 128-bit xorshift initial state: {seed, seed}.
|
||||
"""
|
||||
noisy: list[int] = []
|
||||
state = (seed << 64) | seed
|
||||
mask_w = (1 << width) - 1
|
||||
for row in rows:
|
||||
flip, state = generate_write_flip_mask(
|
||||
state, width, noise_bits, rate_num, rate_den
|
||||
)
|
||||
noisy.append((row ^ flip) & mask_w)
|
||||
return noisy
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--rows", type=int, default=512)
|
||||
parser.add_argument("--queries", type=int, default=128)
|
||||
parser.add_argument("--width", type=int, default=512)
|
||||
parser.add_argument("--seed", type=int, default=1234)
|
||||
parser.add_argument("--noise-bits", type=int, default=8)
|
||||
parser.add_argument(
|
||||
"--rates",
|
||||
type=float,
|
||||
nargs="+",
|
||||
default=[0.0, 0.001, 0.005, 0.01, 0.02, 0.05],
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
rng = np.random.default_rng(args.seed)
|
||||
rows = random_hashes(rng, args.rows, width=args.width)
|
||||
|
||||
# Construct simple positive queries by selecting existing rows.
|
||||
query_indices = rng.integers(0, args.rows, size=args.queries)
|
||||
queries = [rows[int(i)] for i in query_indices]
|
||||
|
||||
clean_results = [match_top1(q, rows, width=args.width) for q in queries]
|
||||
|
||||
# Use a fixed denominator matching the 8-bit sample space (2^8 = 256).
|
||||
# Note: floor() is used, matching RTL threshold = (rate_num * 256) // rate_den.
|
||||
# Rates below 1/256 (≈0.39%) collapse to zero under this scheme.
|
||||
rate_den = 256
|
||||
|
||||
print("rate,rate_num,effective_prob,top1_stability,avg_clean_margin")
|
||||
for rate in args.rates:
|
||||
rate_num = int(rate * rate_den)
|
||||
effective = rate_num / rate_den if rate_den > 0 else 0.0
|
||||
stable = 0
|
||||
margins = []
|
||||
|
||||
noisy_rows = apply_write_noise(
|
||||
rows,
|
||||
width=args.width,
|
||||
rate_num=rate_num,
|
||||
rate_den=rate_den,
|
||||
noise_bits=args.noise_bits,
|
||||
seed=args.seed,
|
||||
)
|
||||
|
||||
for q, clean in zip(queries, clean_results):
|
||||
noisy = match_top1(q, noisy_rows, width=args.width)
|
||||
|
||||
if noisy.top1_index == clean.top1_index:
|
||||
stable += 1
|
||||
|
||||
sorted_scores = np.sort(clean.scores)
|
||||
margin = int(sorted_scores[-1] - sorted_scores[-2])
|
||||
margins.append(margin)
|
||||
|
||||
print(f"{rate},{rate_num},{effective:.6f},{stable / args.queries:.6f},{np.mean(margins):.3f}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user