Reset must not leave one domain “running” with pointers at zero while the other still thinks it is full. Almost-full is occupancy in the write domain from a Gray-converted synced read pointer.
Assert asynchronously if you want (POR). Deassert synchronously in each domain with a 2FF reset synchronizer on that domain’s clock. Both pointer engines and both 2FF chains return to 0. After both domains release reset, empty=1, full=0.
POR may assert both resets at once. Deassert is synchronized separately so neither pointer engine starts while the other is still in reset with a stale Gray value in the 2FF.
Do not clear only the write pointer. The read domain would still hold an old rgray; synced into write domain it can look full forever or compare nonsense.
In the write domain, convert wq2_rgray back to binary, subtract from wbin (mod 2^PTR_W), get occupancy as seen with CDC delay, compare to a watermark. Because the pointer is stale, almost-full is still conservative if you treat occupancy as an upper bound.
wire [PTR_W-1:0] rbin_sync = gray2bin(wq2_rgray); wire [PTR_W-1:0] occ_wr = wbin - rbin_sync; // WIDTH PTR_W, wraps correctly assign almost_full = (occ_wr >= ALMOST_FULL_LEVEL);
Do not use almost-full as a substitute for full. Hard full still gates wr_fire.
Gray incrementers assume a binary counter that visits 0..2^n−1. A 10-entry FIFO cannot use this pointer coding as-is. Options: implement 16 deep and ignore 6 locations; use a vendor hard FIFO; use a style with Gray of two n-bit counters and more compare logic (error-prone — not this course’s default).
After POR, RAM is X in sim and random in silicon. Empty means those X’s are unreachable if pointers start at 0. Scan reset must not leave one 2FF chain at 1s while pointers are 0 — you would decode full. Tie DFT so both domains see a coordinated reset release, or hold wr_en/rd_en low until a software “FIFO ready” bit that waits for both reset-sync done flags.
Set almost-full high enough that the producer’s pipeline (2–4 cycles of in-flight beats) can drain into the RAM before hard full. Because of 2FF lag, the true free space is at least as large as the writer thinks — good. Almost-empty should be low enough that the consumer’s pipeline does not starve; remember empty can stay 1 while data is already in RAM, so almost-empty from converted wgray in the read domain is a lower bound on occupancy — also conservative for “do I have a beat?” if you treat it as “maybe not yet.”
Assert can be asynchronous (POR). Deassert each domain’s reset through a 2FF synchronizer on that domain’s clock. Clear both pointer engines and both 2FF chains so empty=1 and full=0 after both sides are out of reset.
Because each pointer stays 0 until its domain is released. The other domain sees Gray 0 through its 2FF. Do not release one engine while the other still holds a stale non-zero Gray.
gray2bin of the synced read Gray, subtract from wbin, compare to a watermark. Still gate writes with hard full. Treat occupancy as an upper bound because the read pointer is stale.
Not without wasting locations (use 16) or changing coding. Classic unit-distance wrap needs a power-of-two number of pointer states.