From dd75d78491d4f35284e3da08dfd330cc6063e96c Mon Sep 17 00:00:00 2001 From: thejayman77 Date: Mon, 20 Jul 2026 20:03:42 -0400 Subject: [PATCH] Fix signoff timing: remove dead bilinear clamp (comb loop) + relax gray skew 2.0->2.5ns Two fixes for the failing STA (Codex-reviewed): 1. gs_texture_unit lerp8: remove the dead 0/255 clamp. For 8-bit taps and f in 0..15, a+floor((b-a)*f/16) is always in [0,255], so the clamp never fired - but its comparators formed a 161-node combinational loop that failed design-clock setup by 42 ps (tap[0][11] -> tex_cache_sel_q). Functionally identical (tb_gs_texture_bilinear + texture TBs bit-identical PASS). 2. SDC: relax the u_zc_emit gray-code CDC max_skew 2.0 -> 2.5 ns. Post-fog placement pushed actual skew to 2.010 ns (fails 2.0 by ~10 ps). Real ceiling is the 3.225 ns EMIF period (one gray bit in flight); 2.5 clears the miss and stays well under it. Documented relax of a conservative round number. Acceptance (owner fit): no comb-loop warning, no tap->tex_cache_sel_q path, all setup + max_skew slacks >= 0. Co-Authored-By: Claude Opus 4.8 (1M context) --- rtl/gif_gs/gs_texture_unit.sv | 10 ++++++---- .../de25_nano_psmct32_raster_demo_top.sdc | 13 +++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/rtl/gif_gs/gs_texture_unit.sv b/rtl/gif_gs/gs_texture_unit.sv index f261c71..dc7210f 100644 --- a/rtl/gif_gs/gs_texture_unit.sv +++ b/rtl/gif_gs/gs_texture_unit.sv @@ -671,10 +671,12 @@ module gs_texture_unit #( prod = diff * $signed({1'b0, f}); shifted = prod >>> 4; res = $signed({14'd0, a}) + shifted; - // defensive clamp 0..255 (in-range inputs keep res in range) - if (res < 0) lerp8 = 8'd0; - else if (res > 22'sd255) lerp8 = 8'd255; - else lerp8 = res[7:0]; + // For 8-bit taps a,b and f in 0..15, res = a + floor((b-a)*f/16) always lands + // in [min(a,b),max(a,b)] subset [0,255] (worst cases a=255,b=0,f=15 -> 15; + // a=1,b=0,f=15 -> 0), so the old 0/255 clamp NEVER fired. Its comparators formed + // a 161-node combinational loop (Codex STA) that failed design-clock setup by 42 ps. + // Return the computed byte directly: functionally identical, breaks the loop. + lerp8 = res[7:0]; end endfunction diff --git a/synth/de25_nano/top_psmct32_raster_demo/de25_nano_psmct32_raster_demo_top.sdc b/synth/de25_nano/top_psmct32_raster_demo/de25_nano_psmct32_raster_demo_top.sdc index 2846b62..2494964 100644 --- a/synth/de25_nano/top_psmct32_raster_demo/de25_nano_psmct32_raster_demo_top.sdc +++ b/synth/de25_nano/top_psmct32_raster_demo/de25_nano_psmct32_raster_demo_top.sdc @@ -178,18 +178,23 @@ post_message -type info "SDC: explicit toggle CDC cuts applied to CLUT=$clut_com # the 2-FF synchronizers handle metastability normally. (1.0 ns << both clock periods.) # Ch357 (Codex) — PER-INSTANCE gray-skew. Keep the global 1 ns for every gs_async_fifo EXCEPT the two Z-path FIFOs # (u_zc_emit request FIFO + its color-writer FIFO): those route into the fast 310 MHz EMIF domain and route+placement -# put them just over 1 ns (worst -0.346). Give THEM a justified 2 ns bound — still < the 3.225 ns fast-source period, so +# put them just over 1 ns (worst -0.346). Give THEM a justified 2.5 ns bound — still < the 3.225 ns fast-source period, so # at most one gray bit is ever in flight and the 2-FF synchronizers handle metastability normally. Overlapping # set_max_skew take the TIGHTEST, so the Z FIFOs are CARVED OUT of the global 1 ns set (remove_from_collection) — the -# broad rule must not keep overriding the 2 ns exception. +# broad rule must not keep overriding the 2.5 ns exception. +# Ch415 (Codex-reviewed): bumped 2.0 -> 2.5. Post-fog placement pushed the worst zc_emit gray skew to 2.010 ns, failing +# the round-number 2.0 bound by ~10 ps. The bound is conservative — the real correctness ceiling is the 3.225 ns fast-EMIF +# period (skew < period => at most one gray bit in flight, gray-code single-bit-change guarantee holds). 2.5 ns clears the +# 2.010 ns actual with margin AND stays well under 3.225 ns, so the CDC guarantee is intact. This is a documented relax of +# a conservative round number, not a waiver of a real requirement. set z_wgray [get_keepers -nowarn {*u_zc_emit|*wgray[*]}] set z_rgray [get_keepers -nowarn {*u_zc_emit|*rgray[*]}] set other_wgray [remove_from_collection [get_keepers -nowarn {*wgray[*]}] $z_wgray] set other_rgray [remove_from_collection [get_keepers -nowarn {*rgray[*]}] $z_rgray] set_max_skew -from $other_wgray -to {*wgray_s1[*]} 1.0 set_max_skew -from $other_rgray -to {*rgray_s1[*]} 1.0 -set_max_skew -from {*u_zc_emit|*wgray[*]} -to {*u_zc_emit|*wgray_s1[*]} 2.0 -set_max_skew -from {*u_zc_emit|*rgray[*]} -to {*u_zc_emit|*rgray_s1[*]} 2.0 +set_max_skew -from {*u_zc_emit|*wgray[*]} -to {*u_zc_emit|*wgray_s1[*]} 2.5 +set_max_skew -from {*u_zc_emit|*rgray[*]} -to {*u_zc_emit|*rgray_s1[*]} 2.5 set z_wgray_n [get_collection_size $z_wgray] set z_rgray_n [get_collection_size $z_rgray] post_message -type info "Ch357 SDC: gray-skew 1ns global (Z FIFOs carved out), 2ns for $z_wgray_n u_zc_emit wgray + $z_rgray_n rgray bits"