mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-12 20:15:31 +08:00
- 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
127 lines
5.4 KiB
Systemverilog
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
|