feat(hw): add banked CAM pipeline with grouped read/write noise

- Add cam_core_banked.sv with 8-lane banked CAM core
- Add cam_write_noise.sv and cam_read_noise.sv for grouped noise injection
- Add noise_mask_grouped.sv generating grouped flip masks from 128-bit PRNG
- Add match_engine_pipeline.sv with multi-stage pipelined top-1 selection
- Add popcount_pipeline.sv for pipelined popcount operations
- Refactor test_cam_basic.py with parametrized DUT introspection helpers
- Add Python ref_model match_top1_with_read_noise() for read noise verification
- Update Makefile with separate WRITE_NOISE_* and READ_NOISE_* parameter groups
- Add new testbenches: test_cam_core_banked, test_cam_read_noise,
  test_cam_write_noise, test_match_engine_pipeline, test_ref_model_noise
  
breaking change hint: NUM_ROWS default changed from 512→4096, LANES from 16→8
This commit is contained in:
2026-05-13 16:21:27 +08:00
parent c41e64d1c6
commit 8f59a287c4
17 changed files with 1331 additions and 384 deletions

View File

@@ -0,0 +1,72 @@
`timescale 1ns / 1ps
module popcount_pipeline #(
parameter int WIDTH = 512,
parameter int ROW_BITS = 12,
parameter int OUT_WIDTH = $clog2(WIDTH + 1)
) (
input logic clk,
input logic rst_n,
input logic valid_i,
input logic [ROW_BITS-1:0] row_i,
input logic [WIDTH-1:0] bits_i,
output logic valid_o,
output logic [ROW_BITS-1:0] row_o,
output logic [OUT_WIDTH-1:0] count_o
);
localparam int GROUP = 8;
localparam int NUM_GROUPS = WIDTH / GROUP;
localparam int GROUP_COUNT_WIDTH = $clog2(GROUP + 1);
localparam int STAGE1_GROUPS = NUM_GROUPS / 4;
logic [GROUP_COUNT_WIDTH-1:0] group_counts [0:NUM_GROUPS-1];
logic [OUT_WIDTH-1:0] partial_q [0:3];
logic [OUT_WIDTH-1:0] sum_q;
logic [ROW_BITS-1:0] row_s1_q, row_s2_q;
logic valid_s1_q, valid_s2_q;
logic [OUT_WIDTH-1:0] partial_comb [0:3];
always_comb begin
for (int g = 0; g < NUM_GROUPS; g++) begin
group_counts[g] = '0;
for (int b = 0; b < GROUP; b++) begin
group_counts[g] = group_counts[g] + bits_i[g*GROUP + b];
end
end
for (int p = 0; p < 4; p++) begin
partial_comb[p] = '0;
for (int g = 0; g < STAGE1_GROUPS; g++) begin
partial_comb[p] = partial_comb[p] + group_counts[p*STAGE1_GROUPS + g];
end
end
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
valid_s1_q <= 1'b0;
valid_s2_q <= 1'b0;
valid_o <= 1'b0;
row_s1_q <= '0;
row_s2_q <= '0;
row_o <= '0;
sum_q <= '0;
count_o <= '0;
for (int p = 0; p < 4; p++) partial_q[p] <= '0;
end else begin
valid_s1_q <= valid_i;
row_s1_q <= row_i;
for (int p = 0; p < 4; p++) begin
partial_q[p] <= partial_comb[p];
end
valid_s2_q <= valid_s1_q;
row_s2_q <= row_s1_q;
sum_q <= partial_q[0] + partial_q[1] + partial_q[2] + partial_q[3];
valid_o <= valid_s2_q;
row_o <= row_s2_q;
count_o <= sum_q;
end
end
endmodule