mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-13 04:25:32 +08:00
- Make cam_read_noise a pass-through module, removing all noise injection logic - Switch write noise to use noise_mask_bernoulli instead of noise_mask_grouped - Add state machine to cam_write_noise for mask generation timing - Remove noise_mask_grouped.sv (no longer needed) - Remove read noise parameters from cam_noisy and cam_top - Update simulation and benchmark code to reflect read noise removal - Sync documentation to reflect Phase 2 architecture
103 lines
3.8 KiB
Python
103 lines
3.8 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
参考模型(ref_model)的纯 Python 单元测试 — Phase 2 cleaned.
|
||
|
||
本文件不涉及任何 RTL / Verilator 仿真,仅验证 Python 参考模型的正确性。
|
||
Phase 2 后只保留 pure matching 函数;所有 grouped/read-noise helpers 已删除。
|
||
|
||
测试覆盖:
|
||
1. XNOR 评分语义 — 确认是「匹配位数」而非「汉明距离」
|
||
2. Top-1 matching — 纯匹配,正确选出最高分索引
|
||
3. Top-K matching — 返回排序后的行索引列表
|
||
4. Top-K 排序规则 — 分数降序、平局行号升序
|
||
5. Top-K k 超过行数时 clamp
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from model.ref_model import (
|
||
match_top1,
|
||
match_topk,
|
||
match_topk_from_scores,
|
||
xnor_popcount_score,
|
||
)
|
||
import numpy as np
|
||
|
||
|
||
# ==============================================================================
|
||
# 测试 1:评分函数语义 — 确认是「XNOR 匹配位数」而非「汉明距离」
|
||
# ==============================================================================
|
||
|
||
|
||
def test_score_is_bit_match_popcount_not_hamming_distance():
|
||
"""
|
||
关键语义验证:xnor_popcount_score 计算的是匹配位的数量,不是汉明距离。
|
||
|
||
示例:
|
||
query = 0b1010
|
||
stored = 0b1000
|
||
XNOR = 0b1101 → popcount = 3(有 3 个位匹配)
|
||
汉明距离 = 1 → 只有一个位不同
|
||
|
||
为什么这个区分很重要:
|
||
- 如果 RTL 或模型错误地使用了汉明距离作为分数,则:
|
||
完全匹配的分数会是 0 而非 hash_bits(512)
|
||
Top-K 排序会颠倒(分数低的反而排前面)
|
||
- 这会导致整个 CAM 检索系统返回错误结果
|
||
"""
|
||
query = 0b1010
|
||
stored = 0b1000
|
||
assert xnor_popcount_score(query, stored, width=4) == 3
|
||
|
||
|
||
# ==============================================================================
|
||
# 测试 2:Top-1 matching
|
||
# ==============================================================================
|
||
|
||
|
||
def test_match_top1_selects_highest_xnor_score_with_row_index_tiebreak():
|
||
"""Top-1 应选出 XNOR 分最高的行;平局时选最小行号。"""
|
||
rows = [0b0000, 0b1111, 0b0011, 0b0101]
|
||
query = 0b0000
|
||
result = match_top1(query, rows, width=4)
|
||
assert result.top1_index == 0
|
||
assert result.top1_score == 4
|
||
assert result.scores.tolist() == [4, 0, 2, 2]
|
||
|
||
|
||
# ==============================================================================
|
||
# 测试 3:Top-K matching
|
||
# ==============================================================================
|
||
|
||
|
||
def test_match_topk_scores_rows_by_xnor_popcount():
|
||
"""match_topk 通过 xnor_popcount 计算分数,返回排序后的行索引和分数数组。"""
|
||
rows = [0b0000, 0b1111, 0b0011, 0b0101]
|
||
query = 0b0000
|
||
indices, scores = match_topk(query, rows, width=4, k=3)
|
||
assert scores.tolist() == [4, 0, 2, 2]
|
||
assert indices == [0, 2, 3]
|
||
|
||
|
||
# ==============================================================================
|
||
# 测试 4:Top-K 排序 — 分数降序、平局行号升序
|
||
# ==============================================================================
|
||
|
||
|
||
def test_match_topk_from_scores_uses_score_desc_then_row_asc():
|
||
"""Top-K 排序规则:分数越大越优先;分数相同时行号越小越优先。"""
|
||
scores = np.array([7, 9, 9, 2, 7], dtype=np.int32)
|
||
assert match_topk_from_scores(scores, 4) == [1, 2, 0, 4]
|
||
|
||
|
||
# ==============================================================================
|
||
# 测试 5:Top-K k 超过行数时 clamp
|
||
# ==============================================================================
|
||
|
||
|
||
def test_match_topk_clamps_k_to_row_count():
|
||
"""当 k 超过实际行数时,返回所有行(按排序)。"""
|
||
indices, scores = match_topk(0, [0, 1], width=1, k=5)
|
||
assert scores.tolist() == [1, 0]
|
||
assert indices == [0, 1]
|