mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-12 20:15:31 +08:00
feat(retrieval): add CAM retrieval benchmark with topk scoring and read noise support
- Add cocotb benchmark infrastructure under hw/sim/benchmarks/retrieval/ with Makefile - Implement test_retrieval_benchmark.py supporting configurable topk-k, read/write noise - Add cluster-based synthetic dataset generator with configurable bit-flip rates - Add reference model functions: match_topk, match_topk_from_scores, score_rows_with_read_noise - Add .justfile shortcuts: cam-test-retrieval-no-noise, cam-test-retrieval-read-noise - Add TOPK_K to Verilator EXTRA_ARGS via cocotb-common.mk - Add unit tests for topk sorting logic and stateful read-noise scoring
This commit is contained in:
@@ -53,6 +53,30 @@ def match_top1(
|
||||
)
|
||||
|
||||
|
||||
def match_topk_from_scores(scores: Sequence[int], k: int) -> list[int]:
|
||||
"""Return row indices sorted by score desc, row index asc (HW tie-break)."""
|
||||
if k <= 0:
|
||||
raise ValueError("k must be greater than 0")
|
||||
return sorted(range(len(scores)), key=lambda idx: (-int(scores[idx]), idx))[: min(k, len(scores))]
|
||||
|
||||
|
||||
def match_topk(
|
||||
query: int,
|
||||
rows: Sequence[int],
|
||||
*,
|
||||
width: int = 512,
|
||||
k: int = 5,
|
||||
) -> tuple[list[int], np.ndarray]:
|
||||
"""Pure Top-K matching — noise is already baked into rows if needed.
|
||||
|
||||
Returns (list of row indices in rank order, NumPy score array).
|
||||
"""
|
||||
scores = np.zeros(len(rows), dtype=np.int32)
|
||||
for idx, row in enumerate(rows):
|
||||
scores[idx] = xnor_popcount_score(int(query), int(row), width)
|
||||
return match_topk_from_scores(scores, k), scores
|
||||
|
||||
|
||||
def xorshift128(state: int) -> int:
|
||||
"""128-bit xorshift PRNG, single step. Matches random128.sv."""
|
||||
mask32 = (1 << 32) - 1
|
||||
@@ -182,6 +206,49 @@ def generate_read_lane_masks(
|
||||
return masks, next_states
|
||||
|
||||
|
||||
def score_rows_with_read_noise(
|
||||
query: int,
|
||||
rows: Sequence[int],
|
||||
*,
|
||||
lane_states: Sequence[int],
|
||||
width: int = 512,
|
||||
lanes: int = 8,
|
||||
noise_bits: int = 8,
|
||||
rate_num: int = 1,
|
||||
rate_den: int = 100,
|
||||
) -> tuple[np.ndarray, list[int]]:
|
||||
"""Score one query with read noise and return updated lane PRNG states.
|
||||
|
||||
Unlike match_top1_with_read_noise(), this helper is stateful across calls:
|
||||
callers pass current lane states in and receive the next states back.
|
||||
This matches a DUT that is reset once, then serves multiple queries.
|
||||
"""
|
||||
assert lanes > 0
|
||||
assert len(rows) % lanes == 0
|
||||
assert len(lane_states) == lanes
|
||||
|
||||
scores = np.zeros(len(rows), dtype=np.int32)
|
||||
next_lane_states = [int(state) for state in lane_states]
|
||||
|
||||
for base in range(0, len(rows), lanes):
|
||||
lane_valid = [True] * lanes
|
||||
masks, next_lane_states = generate_read_lane_masks(
|
||||
next_lane_states,
|
||||
hash_bits=width,
|
||||
noise_bits=noise_bits,
|
||||
rate_num=rate_num,
|
||||
rate_den=rate_den,
|
||||
lane_valid=lane_valid,
|
||||
)
|
||||
|
||||
for lane in range(lanes):
|
||||
row_idx = base + lane
|
||||
noisy_row = int(rows[row_idx]) ^ int(masks[lane])
|
||||
scores[row_idx] = xnor_popcount_score(int(query), noisy_row, width)
|
||||
|
||||
return scores, next_lane_states
|
||||
|
||||
|
||||
def match_top1_with_read_noise(
|
||||
query: int,
|
||||
rows: Sequence[int],
|
||||
|
||||
Reference in New Issue
Block a user