mirror of
https://github.com/steve-m/hsdaoh-fpga.git
synced 2026-03-14 21:09:46 +01:00
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>
86 lines
2.7 KiB
Verilog
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
|