mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-13 04:25:32 +08:00
refactor(cam): extract match engine into separate module and centralize parameters
- Split cam_core into pure memory (cam_core.sv) and match engine (match_engine.sv) - Add cam_params.svh with centralized parameter definitions (NUM_ROWS, HASH_BITS, LANES, etc.) - Update cam_top.sv to use shared parameters and compose match_engine - Update Makefile to include new match_engine module and correct Verilator define syntax
This commit is contained in:
@@ -1,80 +1,17 @@
|
||||
`timescale 1ns / 1ps
|
||||
`include "cam_params.svh"
|
||||
|
||||
module cam_core #(
|
||||
parameter int NUM_ROWS = 512,
|
||||
parameter int HASH_BITS = 512,
|
||||
parameter int LANES = 16,
|
||||
|
||||
parameter int ROW_BITS = $clog2(NUM_ROWS),
|
||||
parameter int SCORE_BITS = $clog2(HASH_BITS + 1)
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
|
||||
// Static load interface. In the first prototype, writes are expected
|
||||
// before online queries begin.
|
||||
input logic [ ROW_BITS-1:0] wr_row,
|
||||
input logic [HASH_BITS-1:0] wr_hash,
|
||||
input logic wr_en,
|
||||
|
||||
// Single-request blocking query interface.
|
||||
input logic query_valid,
|
||||
output logic query_ready,
|
||||
input logic [HASH_BITS-1:0] query_hash,
|
||||
|
||||
output logic result_valid,
|
||||
input logic result_ready,
|
||||
output logic [ ROW_BITS-1:0] top1_index,
|
||||
output logic [SCORE_BITS-1:0] top1_score,
|
||||
|
||||
`ifdef SIM_NOISE
|
||||
// Flattened for easier cocotb/Verilator handling:
|
||||
// lane k mask = noise_mask_lanes_flat[k*HASH_BITS +: HASH_BITS]
|
||||
input logic [LANES*HASH_BITS-1:0] noise_mask_lanes_flat,
|
||||
`endif
|
||||
|
||||
`ifdef SIM_DEBUG
|
||||
// Flattened for easier cocotb/Verilator handling:
|
||||
// score[row] = score_debug_flat[row*SCORE_BITS +: SCORE_BITS]
|
||||
output logic [NUM_ROWS*SCORE_BITS-1:0] score_debug_flat,
|
||||
`endif
|
||||
|
||||
output logic busy
|
||||
module cam_core (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic wr_en,
|
||||
input logic [(`ROW_BITS)-1:0] wr_row,
|
||||
input logic [(`HASH_BITS)-1:0] wr_hash,
|
||||
input logic [(`LANES)*(`ROW_BITS)-1:0] rd_addr_lanes_flat,
|
||||
output logic [(`LANES)*(`HASH_BITS)-1:0] rd_hash_lanes_flat
|
||||
);
|
||||
localparam int NUM_BATCHES = (NUM_ROWS + LANES - 1) / LANES;
|
||||
|
||||
typedef enum logic [1:0] {
|
||||
S_IDLE,
|
||||
S_SCAN,
|
||||
S_DONE
|
||||
} state_t;
|
||||
|
||||
state_t state_q, state_d;
|
||||
|
||||
logic [HASH_BITS-1:0] cam_mem[NUM_ROWS];
|
||||
|
||||
logic [HASH_BITS-1: 0] query_q;
|
||||
logic [ROW_BITS-1 : 0] base_row_q;
|
||||
logic [ROW_BITS-1 : 0] base_row_d;
|
||||
|
||||
logic [ROW_BITS-1:0] best_index_q, best_index_d;
|
||||
logic [SCORE_BITS-1:0] best_score_q, best_score_d;
|
||||
|
||||
logic [ ROW_BITS-1:0] lane_best_index;
|
||||
logic [SCORE_BITS-1:0] lane_best_score;
|
||||
|
||||
logic [ ROW_BITS-1:0] lane_best_index_next[LANES+1];
|
||||
logic [SCORE_BITS-1:0] lane_best_score_next[LANES+1];
|
||||
|
||||
logic [SCORE_BITS-1:0] lane_score [ LANES];
|
||||
logic [ ROW_BITS-1:0] lane_row [ LANES];
|
||||
logic lane_valid [ LANES];
|
||||
|
||||
assign query_ready = (state_q == S_IDLE);
|
||||
assign result_valid = (state_q == S_DONE);
|
||||
assign top1_index = best_index_q;
|
||||
assign top1_score = best_score_q;
|
||||
assign busy = (state_q == S_SCAN);
|
||||
logic [(`HASH_BITS)-1:0] cam_mem[0:(`NUM_ROWS)-1];
|
||||
|
||||
// Memory write path.
|
||||
always_ff @(posedge clk) begin
|
||||
@@ -83,132 +20,13 @@ module cam_core #(
|
||||
end
|
||||
end
|
||||
|
||||
`ifdef SIM_DEBUG
|
||||
// Clear by default. Individual rows are overwritten during scan.
|
||||
initial begin
|
||||
score_debug_flat = '0;
|
||||
end
|
||||
`endif
|
||||
|
||||
genvar lane;
|
||||
// Per-lane combinational read.
|
||||
genvar l;
|
||||
generate
|
||||
for (lane = 0; lane < LANES; lane++) begin : gen_lanes
|
||||
logic [HASH_BITS-1:0] row_hash;
|
||||
logic [HASH_BITS-1:0] effective_hash;
|
||||
logic [HASH_BITS-1:0] match_bits;
|
||||
|
||||
assign lane_row[lane] = base_row_q + lane[ROW_BITS-1:0];
|
||||
assign lane_valid[lane] = (lane_row[lane] < NUM_ROWS[ROW_BITS-1:0]);
|
||||
|
||||
// This read is modeled behaviorally. Later this can be replaced
|
||||
// by banked BRAM/URAM without changing the compare path.
|
||||
assign row_hash = lane_valid[lane] ? cam_mem[lane_row[lane]] : '0;
|
||||
|
||||
`ifdef SIM_NOISE
|
||||
logic [HASH_BITS-1:0] lane_noise_mask;
|
||||
assign lane_noise_mask = noise_mask_lanes_flat[lane*HASH_BITS+:HASH_BITS];
|
||||
assign effective_hash = row_hash ^ lane_noise_mask;
|
||||
`else
|
||||
assign effective_hash = row_hash;
|
||||
`endif
|
||||
|
||||
assign match_bits = ~(query_q ^ effective_hash);
|
||||
|
||||
popcount #(
|
||||
.WIDTH(HASH_BITS),
|
||||
.GROUP(8),
|
||||
.OUT_WIDTH(SCORE_BITS)
|
||||
) u_popcount (
|
||||
.bits_i (match_bits),
|
||||
.count_o(lane_score[lane])
|
||||
);
|
||||
|
||||
argmax_update #(
|
||||
.ROW_BITS (ROW_BITS),
|
||||
.SCORE_BITS(SCORE_BITS)
|
||||
) u_argmax_lane (
|
||||
.best_index_i(lane_best_index_next[lane]),
|
||||
.best_score_i(lane_best_score_next[lane]),
|
||||
.cand_index_i(lane_row[lane]),
|
||||
.cand_score_i(lane_valid[lane] ? lane_score[lane] : '0),
|
||||
.best_index_o(lane_best_index_next[lane+1]),
|
||||
.best_score_o(lane_best_score_next[lane+1])
|
||||
);
|
||||
for (l = 0; l < `LANES; l++) begin : rd_lane
|
||||
assign rd_hash_lanes_flat[l*`HASH_BITS +: `HASH_BITS] =
|
||||
cam_mem[rd_addr_lanes_flat[l*`ROW_BITS +: `ROW_BITS]];
|
||||
end
|
||||
endgenerate
|
||||
|
||||
assign lane_best_index_next[0] = best_index_q;
|
||||
assign lane_best_score_next[0] = best_score_q;
|
||||
assign lane_best_index = lane_best_index_next[LANES];
|
||||
assign lane_best_score = lane_best_score_next[LANES];
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
base_row_d = base_row_q;
|
||||
best_index_d = best_index_q;
|
||||
best_score_d = best_score_q;
|
||||
|
||||
unique case (state_q)
|
||||
S_IDLE: begin
|
||||
if (query_valid) begin
|
||||
state_d = S_SCAN;
|
||||
base_row_d = '0;
|
||||
best_index_d = {ROW_BITS{1'b1}};
|
||||
best_score_d = '0;
|
||||
end
|
||||
end
|
||||
|
||||
S_SCAN: begin
|
||||
best_index_d = lane_best_index;
|
||||
best_score_d = lane_best_score;
|
||||
|
||||
if (base_row_q + LANES >= NUM_ROWS) begin
|
||||
state_d = S_DONE;
|
||||
end else begin
|
||||
base_row_d = base_row_q + LANES[ROW_BITS-1:0];
|
||||
end
|
||||
end
|
||||
|
||||
S_DONE: begin
|
||||
if (result_ready) begin
|
||||
state_d = S_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
state_d = S_IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
state_q <= S_IDLE;
|
||||
query_q <= '0;
|
||||
base_row_q <= '0;
|
||||
best_index_q <= '0;
|
||||
best_score_q <= '0;
|
||||
end else begin
|
||||
state_q <= state_d;
|
||||
base_row_q <= base_row_d;
|
||||
best_index_q <= best_index_d;
|
||||
best_score_q <= best_score_d;
|
||||
|
||||
if ((state_q == S_IDLE) && query_valid) begin
|
||||
query_q <= query_hash;
|
||||
end
|
||||
|
||||
`ifdef SIM_DEBUG
|
||||
if (state_q == S_IDLE && query_valid) begin
|
||||
score_debug_flat <= '0;
|
||||
end else if (state_q == S_SCAN) begin
|
||||
for (int l = 0; l < LANES; l++) begin
|
||||
if (lane_valid[l]) begin
|
||||
score_debug_flat[lane_row[l]*SCORE_BITS+:SCORE_BITS] <= lane_score[l];
|
||||
end
|
||||
end
|
||||
end
|
||||
`endif
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
Reference in New Issue
Block a user