mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-13 04:25:32 +08:00
feat(hw/rtl): implement full Top-K CAM search pipeline with serial result output
- add TOPK_K, FIFO_DEPTH, RESULT_SERIAL parameters to cam_params - add candidate_fifo: synchronous ready/valid FIFO for (row, score) candidates - add topk_tracker: tracks top-K candidates with clear/ready handshake - add result_serializer: serializes packed Top-K array into rank-ordered stream - refactor match_engine_pipeline from 4-state to 8-state Top-K pipeline - extend cam_top with serial Top-K interface (result_rank/row/score/last) - add backward-compatible top1_index/top1_score aliases from rank-0 beat - add comprehensive tests for all new modules
This commit is contained in:
126
hw/rtl/core/topk_tracker.sv
Normal file
126
hw/rtl/core/topk_tracker.sv
Normal file
@@ -0,0 +1,126 @@
|
||||
`timescale 1ns / 1ps
|
||||
`include "cam_params.svh"
|
||||
|
||||
//==============================================================================
|
||||
// Top-K Tracker
|
||||
//==============================================================================
|
||||
// Maintains a sorted Top-K array of (row, score) candidates.
|
||||
// Insertion sort on every candidate handshake:
|
||||
// - Higher score wins.
|
||||
// - If equal score, lower row wins (tie-break).
|
||||
// - Entries that are worse than all K current slots are dropped.
|
||||
//
|
||||
// candidate_ready_o is always 1 — the tracker accepts a candidate every cycle.
|
||||
// update_pending_o is always 0 — insertion is fully combinational (one cycle).
|
||||
//
|
||||
// Packed outputs:
|
||||
// topk_rows_o [i*ROW_BITS +: ROW_BITS] = rank i row
|
||||
// topk_scores_o [i*SCORE_BITS +: SCORE_BITS] = rank i score
|
||||
//==============================================================================
|
||||
|
||||
module topk_tracker #(
|
||||
parameter int K = `TOPK_K,
|
||||
parameter int ROW_BITS = `ROW_BITS,
|
||||
parameter int SCORE_BITS = `SCORE_BITS
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic clear_i,
|
||||
|
||||
input logic candidate_valid_i,
|
||||
output logic candidate_ready_o,
|
||||
input logic [ROW_BITS-1:0] candidate_row_i,
|
||||
input logic [SCORE_BITS-1:0] candidate_score_i,
|
||||
|
||||
output logic [K*ROW_BITS-1:0] topk_rows_o,
|
||||
output logic [K*SCORE_BITS-1:0] topk_scores_o,
|
||||
output logic update_pending_o
|
||||
);
|
||||
|
||||
// ── Sequential state: K-entry sorted arrays ────────────────────────────
|
||||
logic [ROW_BITS-1:0] rows_q [0:K-1];
|
||||
logic [SCORE_BITS-1:0] scores_q [0:K-1];
|
||||
|
||||
// ── Next-state signals ─────────────────────────────────────────────────
|
||||
logic [ROW_BITS-1:0] rows_next [0:K-1];
|
||||
logic [SCORE_BITS-1:0] scores_next [0:K-1];
|
||||
|
||||
// ── Static output assignments ──────────────────────────────────────────
|
||||
assign candidate_ready_o = 1'b1;
|
||||
assign update_pending_o = 1'b0;
|
||||
|
||||
// ── Combinational: insertion logic ─────────────────────────────────────
|
||||
always_comb begin
|
||||
int insert_idx;
|
||||
insert_idx = K; // always assigned — default: no insertion
|
||||
|
||||
// Default: retain current state
|
||||
for (int i = 0; i < K; i++) begin
|
||||
rows_next[i] = rows_q[i];
|
||||
scores_next[i] = scores_q[i];
|
||||
end
|
||||
|
||||
// Only evaluate insertion when a valid candidate is presented
|
||||
if (candidate_valid_i) begin
|
||||
// Find the first slot where candidate is strictly better than the
|
||||
// current occupant. "Better" means higher score, or equal score
|
||||
// with lower row (tie-break).
|
||||
// Note: guard on insert_idx == K avoids loop-local break which
|
||||
// Yosys rejects; the effect is the same — only the first match
|
||||
// is captured because subsequent iterations see insert_idx != K.
|
||||
for (int i = 0; i < K; i++) begin
|
||||
if (insert_idx == K &&
|
||||
(candidate_score_i > scores_q[i] ||
|
||||
(candidate_score_i == scores_q[i] && candidate_row_i < rows_q[i]))) begin
|
||||
insert_idx = i;
|
||||
end
|
||||
end
|
||||
|
||||
// Shift slots down from the insertion point and insert.
|
||||
// Static loop bounds with guard for Yosys compatibility.
|
||||
if (insert_idx < K) begin
|
||||
for (int i = 0; i < K; i++) begin
|
||||
if (i == insert_idx) begin
|
||||
rows_next[i] = candidate_row_i;
|
||||
scores_next[i] = candidate_score_i;
|
||||
end else if (i > insert_idx) begin
|
||||
rows_next[i] = rows_q[i - 1];
|
||||
scores_next[i] = scores_q[i - 1];
|
||||
end
|
||||
// i < insert_idx: keep default from copy loop above
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ── Sequential: state update ───────────────────────────────────────────
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
for (int i = 0; i < K; i++) begin
|
||||
rows_q[i] <= {ROW_BITS{1'b1}}; // all-ones sentinel
|
||||
scores_q[i] <= '0;
|
||||
end
|
||||
end else if (clear_i) begin
|
||||
for (int i = 0; i < K; i++) begin
|
||||
rows_q[i] <= {ROW_BITS{1'b1}};
|
||||
scores_q[i] <= '0;
|
||||
end
|
||||
end else if (candidate_valid_i) begin
|
||||
for (int i = 0; i < K; i++) begin
|
||||
rows_q[i] <= rows_next[i];
|
||||
scores_q[i] <= scores_next[i];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ── Combinational: pack arrays into flat outputs ───────────────────────
|
||||
always_comb begin
|
||||
topk_rows_o = '0;
|
||||
topk_scores_o = '0;
|
||||
for (int i = 0; i < K; i++) begin
|
||||
topk_rows_o[i*ROW_BITS +: ROW_BITS] = rows_q[i];
|
||||
topk_scores_o[i*SCORE_BITS +: SCORE_BITS] = scores_q[i];
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
Reference in New Issue
Block a user