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:
124
hw/rtl/core/candidate_fifo.sv
Normal file
124
hw/rtl/core/candidate_fifo.sv
Normal file
@@ -0,0 +1,124 @@
|
||||
`timescale 1ns / 1ps
|
||||
`include "cam_params.svh"
|
||||
|
||||
//==============================================================================
|
||||
// candidate_fifo — Synchronous ready/valid FIFO for (row, score) candidates
|
||||
//
|
||||
// Implements a simple circular-buffer FIFO that accepts writes only when
|
||||
// wr_valid_i && wr_ready_o and reads only when rd_valid_o && rd_ready_i.
|
||||
// Provides empty/full flags. Default depth is `FIFO_DEPTH (16).
|
||||
//
|
||||
// Quality:
|
||||
// - Pointers always wrap at DEPTH-1 (safe for non-power-of-two depths).
|
||||
// - wr_ready_o goes high when full if a read is accepted in the same cycle,
|
||||
// allowing a simultaneous write (throughput-friendly backpressure).
|
||||
// - PTR_BITS is at least 1 for DEPTH=1.
|
||||
//==============================================================================
|
||||
|
||||
module candidate_fifo #(
|
||||
parameter int DEPTH = `FIFO_DEPTH,
|
||||
parameter int ROW_BITS = `ROW_BITS,
|
||||
parameter int SCORE_BITS = `SCORE_BITS,
|
||||
// Ensure at least 1 bit even when DEPTH=1.
|
||||
parameter int PTR_BITS = (DEPTH <= 1) ? 1 : $clog2(DEPTH)
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
|
||||
// Write interface (producer side)
|
||||
input logic wr_valid_i,
|
||||
output logic wr_ready_o,
|
||||
input logic [ROW_BITS -1:0] wr_row_i,
|
||||
input logic [SCORE_BITS -1:0] wr_score_i,
|
||||
|
||||
// Read interface (consumer side)
|
||||
output logic rd_valid_o,
|
||||
input logic rd_ready_i,
|
||||
output logic [ROW_BITS -1:0] rd_row_o,
|
||||
output logic [SCORE_BITS -1:0] rd_score_o,
|
||||
|
||||
// Status flags
|
||||
output logic empty_o,
|
||||
output logic full_o
|
||||
);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Storage — dual‑port array.
|
||||
//--------------------------------------------------------------------------
|
||||
logic [ROW_BITS -1:0] mem_row [0:DEPTH-1];
|
||||
logic [SCORE_BITS -1:0] mem_score [0:DEPTH-1];
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Pointers and fill counter
|
||||
//--------------------------------------------------------------------------
|
||||
logic [PTR_BITS -1:0] wr_ptr, rd_ptr;
|
||||
logic [PTR_BITS :0] count; // extra bit for full detect
|
||||
|
||||
// Wrap-at value (DEPTH-1), sized to match pointer width.
|
||||
// Part-select DEPTH and use a 1'b1 literal so the RHS width equals PTR_BITS.
|
||||
localparam logic [PTR_BITS-1:0] WRAP_AT = DEPTH[PTR_BITS-1:0] - 1'b1;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Status flag outputs (combinational)
|
||||
//--------------------------------------------------------------------------
|
||||
assign empty_o = (count == 0);
|
||||
assign full_o = (count == DEPTH[PTR_BITS:0]);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handshake outputs
|
||||
//
|
||||
// wr_ready_o is asserted when not full, OR when a read is accepted in
|
||||
// the same cycle (full+pop → slot freed → write can proceed).
|
||||
//--------------------------------------------------------------------------
|
||||
assign wr_ready_o = ~full_o | (rd_valid_o & rd_ready_i);
|
||||
assign rd_valid_o = ~empty_o;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Read data (combinational — value at current rd_ptr)
|
||||
//--------------------------------------------------------------------------
|
||||
assign rd_row_o = mem_row [rd_ptr];
|
||||
assign rd_score_o = mem_score[rd_ptr];
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Push / Pop decode
|
||||
//--------------------------------------------------------------------------
|
||||
logic push, pop;
|
||||
assign push = wr_valid_i & wr_ready_o;
|
||||
assign pop = rd_valid_o & rd_ready_i;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Sequential — pointer and count updates
|
||||
//
|
||||
// Pointers wrap at DEPTH-1, safe for any DEPTH (not just powers of two).
|
||||
// Simultaneous push+pop leaves count unchanged.
|
||||
//--------------------------------------------------------------------------
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
wr_ptr <= 0;
|
||||
rd_ptr <= 0;
|
||||
count <= 0;
|
||||
end else begin
|
||||
if (push) begin
|
||||
mem_row [wr_ptr] <= wr_row_i;
|
||||
mem_score[wr_ptr] <= wr_score_i;
|
||||
if (wr_ptr == WRAP_AT)
|
||||
wr_ptr <= 0;
|
||||
else
|
||||
wr_ptr <= wr_ptr + 1;
|
||||
end
|
||||
|
||||
if (pop) begin
|
||||
if (rd_ptr == WRAP_AT)
|
||||
rd_ptr <= 0;
|
||||
else
|
||||
rd_ptr <= rd_ptr + 1;
|
||||
end
|
||||
|
||||
// Update fill count
|
||||
if (push & ~pop) count <= count + 1;
|
||||
else if (~push & pop) count <= count - 1;
|
||||
// else: no change (neither, or both)
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
Reference in New Issue
Block a user