Files
Mini-Nav/hw/rtl/core/candidate_fifo.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

125 lines
5.0 KiB
Systemverilog
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
`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 — dualport 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