mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-12 20:15:31 +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/result_serializer.sv
Normal file
126
hw/rtl/core/result_serializer.sv
Normal file
@@ -0,0 +1,126 @@
|
||||
`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
|
||||
Reference in New Issue
Block a user