hsdaoh-fpga/common/async_fifo/rptr_empty.v
Steve Markgraf 040bdb24a4 Optimize async FIFO critical path and add CLI build system
Replace chained adder in rptr_empty/wptr_full with parallel
pre-computation (rbin+1, rbin+2) and mux selection. This reduces
the critical path from ~9 to ~5-6 logic levels, improving clk_pixel
Fmax from 120.8 to 166.7 MHz (+38%).

Add build.sh/build.tcl for headless CLI builds via gw_sh with
timing-driven PnR and increased placement/routing effort.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 23:37:22 +01:00

86 lines
2.7 KiB
Verilog

// https://github.com/dpretet/async_fifo
// distributed under the mit license
// https://opensource.org/licenses/mit-license.php
// Optimized: replaced chained adder (rbin+inc then +1) with parallel
// pre-computation of rbin+1 and rbin+2, selected via mux. This reduces
// the arempty critical path from ~9 to ~5-6 logic levels.
`timescale 1 ns / 1 ps
`default_nettype none
module rptr_empty
#(
parameter ADDRSIZE = 4
)(
input wire rclk,
input wire rrst_n,
input wire rinc,
input wire [ADDRSIZE :0] rq2_wptr,
output reg rempty,
output reg arempty,
output wire [ADDRSIZE-1:0] raddr,
output reg [ADDRSIZE :0] rptr
);
reg [ADDRSIZE:0] rbin;
wire [ADDRSIZE:0] rgraynext, rbinnext;
wire arempty_val, rempty_val;
// Pre-compute incremented values from registered rbin (parallel, not chained)
wire [ADDRSIZE:0] rbin_p1 = rbin + 1'b1;
wire [ADDRSIZE:0] rbin_p2 = rbin + 2'd2;
wire do_read = rinc & ~rempty;
// Gray code conversions from pre-computed values
wire [ADDRSIZE:0] rgray_p1 = (rbin_p1 >> 1) ^ rbin_p1;
wire [ADDRSIZE:0] rgray_p2 = (rbin_p2 >> 1) ^ rbin_p2;
// Select based on whether a read is happening this cycle
// When do_read: rbinnext = rbin+1, rgraynext = gray(rbin+1)
// When !do_read: rbinnext = rbin, rgraynext = gray(rbin) = rptr (already registered)
assign rbinnext = do_read ? rbin_p1 : rbin;
assign rgraynext = do_read ? rgray_p1 : rptr;
// "Almost empty" = will be empty after one more read from projected next position
// rgraynextm1 = gray(rbinnext + 1) = gray(do_read ? rbin+2 : rbin+1)
wire [ADDRSIZE:0] rgraynextm1 = do_read ? rgray_p2 : rgray_p1;
//-------------------
// GRAYSTYLE2 pointer
//-------------------
always @(posedge rclk or negedge rrst_n) begin
if (!rrst_n)
{rbin, rptr} <= 0;
else
{rbin, rptr} <= {rbinnext, rgraynext};
end
// Memory read-address pointer (okay to use binary to address memory)
assign raddr = rbin[ADDRSIZE-1:0];
//---------------------------------------------------------------
// FIFO empty when the next rptr == synchronized wptr or on reset
//---------------------------------------------------------------
assign rempty_val = (rgraynext == rq2_wptr);
assign arempty_val = (rgraynextm1 == rq2_wptr);
always @ (posedge rclk or negedge rrst_n) begin
if (!rrst_n) begin
arempty <= 1'b0;
rempty <= 1'b1;
end
else begin
arempty <= arempty_val;
rempty <= rempty_val;
end
end
endmodule
`resetall