`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