131 lines
4.3 KiB
Systemverilog
131 lines
4.3 KiB
Systemverilog
// ============================================================================
|
|
// snix_sync_fifo.sv
|
|
//
|
|
// Synchronous FIFO with first-word-fall-through (FWFT) bypass.
|
|
//
|
|
// Architecture:
|
|
// - Dual-pointer circular buffer with MSB wrap-bit for full detection
|
|
// - Write-through bypass path: when the FIFO is empty and a write arrives,
|
|
// data is forwarded directly to the output without a one-cycle read delay
|
|
// - Block-RAM inference hint for FPGA targets
|
|
// ============================================================================
|
|
module snix_sync_fifo #(
|
|
parameter int DATA_WIDTH = 32,
|
|
parameter int FIFO_DEPTH = 16
|
|
) (
|
|
input logic clk,
|
|
input logic rst_n,
|
|
|
|
// Write port
|
|
input logic [DATA_WIDTH-1:0] data_i,
|
|
input logic wr_en,
|
|
|
|
// Read port
|
|
input logic rd_en,
|
|
output logic [DATA_WIDTH-1:0] data_o,
|
|
|
|
// Status
|
|
output logic [$clog2(FIFO_DEPTH):0] fill_cnt,
|
|
output logic fifo_full,
|
|
output logic fifo_empty
|
|
);
|
|
|
|
localparam int AWIDTH = $clog2(FIFO_DEPTH);
|
|
|
|
// Storage
|
|
(* ram_style="block" *)
|
|
logic [DATA_WIDTH-1:0] mem [0:FIFO_DEPTH-1];
|
|
|
|
// Pointers — extra MSB for wrap-around detection
|
|
logic [AWIDTH:0] wptr, rptr;
|
|
|
|
// Bypass (FWFT) path
|
|
logic [DATA_WIDTH-1:0] fwd_data;
|
|
logic fwd_valid;
|
|
|
|
// Qualified strobes
|
|
wire do_wr = wr_en & ~fifo_full;
|
|
wire do_rd = rd_en & ~fifo_empty;
|
|
|
|
// -----------------------------------------------------------------
|
|
// Write pointer
|
|
// -----------------------------------------------------------------
|
|
always_ff @(posedge clk or negedge rst_n)
|
|
if (!rst_n)
|
|
wptr <= '0;
|
|
else if (do_wr)
|
|
wptr <= wptr + 1'b1;
|
|
|
|
// -----------------------------------------------------------------
|
|
// Read pointer
|
|
// -----------------------------------------------------------------
|
|
always_ff @(posedge clk or negedge rst_n)
|
|
if (!rst_n)
|
|
rptr <= '0;
|
|
else if (do_rd)
|
|
rptr <= rptr + 1'b1;
|
|
|
|
// -----------------------------------------------------------------
|
|
// Memory write
|
|
// -----------------------------------------------------------------
|
|
always_ff @(posedge clk)
|
|
if (do_wr)
|
|
mem[wptr[AWIDTH-1:0]] <= data_i;
|
|
|
|
// -----------------------------------------------------------------
|
|
// Memory read — pre-fetch next location for FWFT
|
|
// -----------------------------------------------------------------
|
|
logic [DATA_WIDTH-1:0] mem_rd;
|
|
|
|
always_ff @(posedge clk)
|
|
if (do_rd)
|
|
mem_rd <= mem[rptr[AWIDTH-1:0] + 1'b1];
|
|
|
|
// -----------------------------------------------------------------
|
|
// Fill counter and empty flag
|
|
// -----------------------------------------------------------------
|
|
always_ff @(posedge clk or negedge rst_n)
|
|
if (!rst_n) begin
|
|
fifo_empty <= 1'b1;
|
|
fill_cnt <= '0;
|
|
end else begin
|
|
case ({do_wr, do_rd})
|
|
2'b10: begin
|
|
fill_cnt <= fill_cnt + 1'b1;
|
|
fifo_empty <= 1'b0;
|
|
end
|
|
2'b01: begin
|
|
fill_cnt <= fill_cnt - 1'b1;
|
|
fifo_empty <= (fill_cnt <= 1);
|
|
end
|
|
default: ;
|
|
endcase
|
|
end
|
|
|
|
// Full flag — MSB of fill counter
|
|
assign fifo_full = fill_cnt[AWIDTH];
|
|
|
|
// -----------------------------------------------------------------
|
|
// FWFT bypass — forward write data directly when FIFO is empty
|
|
// -----------------------------------------------------------------
|
|
always_ff @(posedge clk or negedge rst_n)
|
|
if (!rst_n)
|
|
fwd_valid <= 1'b0;
|
|
else if (fifo_empty || rd_en) begin
|
|
if (!wr_en)
|
|
fwd_valid <= 1'b0;
|
|
else if (fifo_empty || (rd_en && fill_cnt == 1))
|
|
fwd_valid <= 1'b1;
|
|
else
|
|
fwd_valid <= 1'b0;
|
|
end
|
|
|
|
always_ff @(posedge clk)
|
|
if (fifo_empty || rd_en)
|
|
fwd_data <= data_i;
|
|
|
|
// Output mux — bypass path has priority
|
|
assign data_o = fwd_valid ? fwd_data : mem_rd;
|
|
|
|
endmodule : snix_sync_fifo
|