mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-13 04:25:32 +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.1 KiB
Systemverilog
127 lines
5.1 KiB
Systemverilog
`timescale 1ns / 1ps
|
|
`include "cam_params.svh"
|
|
|
|
//==============================================================================
|
|
// Result Serializer
|
|
//==============================================================================
|
|
// Serializes the packed Top-K array (rows and scores) into a rank-ordered
|
|
// handshake interface, one result per fire.
|
|
//
|
|
// Start is edge-triggered (sampled on the cycle start_i is high while not
|
|
// already active). Once started, the module presents outputs for the
|
|
// current rank (starting at 0) and advances on each valid-ready fire.
|
|
// After the last rank (K-1) fires, done_o pulses for one cycle and the
|
|
// module returns to idle with rank reset to 0.
|
|
//
|
|
// Back-pressure: holds current output while result_ready_i is low.
|
|
//
|
|
// Supports K=1 cleanly (last rank = rank 0, done on first fire).
|
|
//==============================================================================
|
|
|
|
module result_serializer #(
|
|
parameter int K = `TOPK_K,
|
|
parameter int ROW_BITS = `ROW_BITS,
|
|
parameter int SCORE_BITS = `SCORE_BITS,
|
|
parameter int RANK_BITS = (K <= 1) ? 1 : $clog2(K)
|
|
) (
|
|
input logic clk,
|
|
input logic rst_n,
|
|
|
|
input logic start_i,
|
|
output logic busy_o,
|
|
output logic done_o,
|
|
|
|
input logic [K*ROW_BITS-1:0] topk_rows_i,
|
|
input logic [K*SCORE_BITS-1:0] topk_scores_i,
|
|
|
|
output logic result_valid_o,
|
|
input logic result_ready_i,
|
|
output logic [RANK_BITS-1:0] result_rank_o,
|
|
output logic [ROW_BITS-1:0] result_row_o,
|
|
output logic [SCORE_BITS-1:0] result_score_o,
|
|
output logic result_last_o
|
|
);
|
|
|
|
// ── State encoding ─────────────────────────────────────────────────────
|
|
typedef enum logic {
|
|
IDLE,
|
|
ACTIVE
|
|
} state_t;
|
|
|
|
// ── Localparam for last-rank comparison (width-matched to avoid warnings) ─
|
|
localparam [RANK_BITS-1:0] LAST_RANK = RANK_BITS'(K - 1);
|
|
|
|
state_t state_q, state_next;
|
|
|
|
logic [RANK_BITS-1:0] rank_q, rank_next;
|
|
logic done_q, done_next;
|
|
|
|
// Snapshot of packed inputs captured at start (holds during back-pressure)
|
|
logic [K*ROW_BITS-1:0] rows_snapshot_q, rows_snapshot_next;
|
|
logic [K*SCORE_BITS-1:0] scores_snapshot_q, scores_snapshot_next;
|
|
|
|
// ── Sequential: state, rank, done, and snapshot registers ─────────────
|
|
always_ff @(posedge clk or negedge rst_n) begin
|
|
if (!rst_n) begin
|
|
state_q <= IDLE;
|
|
rank_q <= '0;
|
|
done_q <= 1'b0;
|
|
rows_snapshot_q <= '0;
|
|
scores_snapshot_q <= '0;
|
|
end else begin
|
|
state_q <= state_next;
|
|
rank_q <= rank_next;
|
|
done_q <= done_next;
|
|
rows_snapshot_q <= rows_snapshot_next;
|
|
scores_snapshot_q <= scores_snapshot_next;
|
|
end
|
|
end
|
|
|
|
// ── Combinational: next-state, snapshot, and output logic ──────────────
|
|
always_comb begin
|
|
// Defaults: stay in current state, retain rank, no done pulse,
|
|
// hold snapshot (outputs stable under back-pressure)
|
|
state_next = state_q;
|
|
rank_next = rank_q;
|
|
done_next = 1'b0;
|
|
rows_snapshot_next = rows_snapshot_q;
|
|
scores_snapshot_next = scores_snapshot_q;
|
|
|
|
if (state_q == IDLE) begin
|
|
// Edge-triggered start — capture packed inputs into snapshot
|
|
if (start_i) begin
|
|
state_next = ACTIVE;
|
|
rank_next = '0; // start from rank 0
|
|
rows_snapshot_next = topk_rows_i;
|
|
scores_snapshot_next = topk_scores_i;
|
|
end
|
|
end else begin
|
|
// ACTIVE: advance on fire
|
|
if (result_valid_o && result_ready_i) begin
|
|
// Is this the last rank?
|
|
if (rank_q == LAST_RANK) begin
|
|
state_next = IDLE;
|
|
rank_next = '0;
|
|
done_next = 1'b1;
|
|
end else begin
|
|
rank_next = rank_q + 1'b1;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
// ── Output assignments ─────────────────────────────────────────────────
|
|
assign busy_o = (state_q == ACTIVE);
|
|
assign done_o = done_q; // registered — pulses after deactivation
|
|
assign result_valid_o = (state_q == ACTIVE);
|
|
|
|
// Slice the snapshot registers at the current rank
|
|
assign result_row_o = rows_snapshot_q[rank_q * ROW_BITS +: ROW_BITS];
|
|
assign result_score_o = scores_snapshot_q[rank_q * SCORE_BITS +: SCORE_BITS];
|
|
assign result_rank_o = rank_q;
|
|
|
|
// result_last_o: high only when active and on the last rank
|
|
assign result_last_o = (state_q == ACTIVE) && (rank_q == LAST_RANK);
|
|
|
|
endmodule
|