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
125 lines
5.0 KiB
Systemverilog
125 lines
5.0 KiB
Systemverilog
`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
|