Files
Mini-Nav/hw/rtl/core/topk_tracker.sv
SikongJueluo e4cbb5e30d 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
2026-05-19 22:43:21 +08:00

127 lines
5.4 KiB
Systemverilog

`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