diff --git a/9_Firmware/9_2_FPGA/constraints/xc7a50t_ftg256.xdc b/9_Firmware/9_2_FPGA/constraints/xc7a50t_ftg256.xdc index cb16122..b39c513 100644 --- a/9_Firmware/9_2_FPGA/constraints/xc7a50t_ftg256.xdc +++ b/9_Firmware/9_2_FPGA/constraints/xc7a50t_ftg256.xdc @@ -12,10 +12,17 @@ # # I/O Bank Voltage Summary: # Bank 0: VCCO = 3.3V (JTAG, flash CS) -# Bank 14: VCCO = 3.3V (ADC LVDS data, SPI flash) +# Bank 14: VCCO = 3.3V (ADC LVDS data, SPI flash, adc_pwdn) # Bank 15: VCCO = 3.3V (DAC, clocks, STM32 SPI 3.3V side, DIG bus, mixer) # Bank 34: VCCO = 1.8V (ADAR1000 beamformer control, SPI 1.8V side) # Bank 35: VCCO = 3.3V (unused — no signal connections) +# +# DRC Fix History: +# - PLIO-9: Moved clk_120m_dac from C13 (N-type) to D13 (P-type MRCC). +# Clock inputs must use the P-type pin of a Multi-Region Clock-Capable pair. +# - BIVC-1: Disabled DIFF_TERM on Bank 14 LVDS pairs (adc_dco, adc_d) to +# resolve VCCO conflict with single-ended adc_pwdn (LVCMOS33) on T5. +# External 100-ohm differential termination required on board. # ============================================================================ # ============================================================================ @@ -28,14 +35,16 @@ set_property IOSTANDARD LVCMOS33 [get_ports {clk_100m}] create_clock -name clk_100m -period 10.0 [get_ports {clk_100m}] set_input_jitter [get_clocks clk_100m] 0.1 -# 120MHz DAC Clock (AD9523 OUT11 → FPGA_DAC_CLOCK → Bank 15 MRCC pin C13) +# 120MHz DAC Clock (AD9523 OUT11 → FPGA_DAC_CLOCK → Bank 15 MRCC pin D13) # NOTE: The physical DAC (U3, AD9708) receives its clock directly from the # AD9523 via a separate net (DAC_CLOCK), NOT from the FPGA. The FPGA # uses this clock input for internal DAC data timing only. The RTL port # `dac_clk` is an output that assigns clk_120m directly — it has no # separate physical pin on this board and should be removed from the # RTL or left unconnected. -set_property PACKAGE_PIN C13 [get_ports {clk_120m_dac}] +# FIX: Moved from C13 (IO_L12N = N-type) to D13 (IO_L12P = P-type MRCC). +# Clock inputs must use the P-type pin of an MRCC pair (PLIO-9 DRC). +set_property PACKAGE_PIN D13 [get_ports {clk_120m_dac}] set_property IOSTANDARD LVCMOS33 [get_ports {clk_120m_dac}] create_clock -name clk_120m_dac -period 8.333 [get_ports {clk_120m_dac}] set_input_jitter [get_clocks clk_120m_dac] 0.1 @@ -45,7 +54,11 @@ set_property PACKAGE_PIN N14 [get_ports {adc_dco_p}] set_property PACKAGE_PIN P14 [get_ports {adc_dco_n}] set_property IOSTANDARD LVDS_33 [get_ports {adc_dco_p}] set_property IOSTANDARD LVDS_33 [get_ports {adc_dco_n}] -set_property DIFF_TERM TRUE [get_ports {adc_dco_p}] +# NOTE: DIFF_TERM disabled to avoid BIVC-1 DRC conflict with single-ended +# adc_pwdn (LVCMOS33) on T5 in the same bank. The board should have external +# 100-ohm differential termination on the ADC LVDS pairs. If signal integrity +# issues arise, the board may need adc_pwdn relocated to a non-Bank-14 pin. +# set_property DIFF_TERM TRUE [get_ports {adc_dco_p}] create_clock -name adc_dco_p -period 2.5 [get_ports {adc_dco_p}] set_input_jitter [get_clocks adc_dco_p] 0.05 @@ -204,8 +217,10 @@ set_property IOSTANDARD LVCMOS33 [get_ports {adc_pwdn}] set_property IOSTANDARD LVDS_33 [get_ports {adc_d_p[*]}] set_property IOSTANDARD LVDS_33 [get_ports {adc_d_n[*]}] -# Differential termination -set_property DIFF_TERM TRUE [get_ports {adc_d_p[*]}] +# Differential termination — disabled to avoid BIVC-1 DRC conflict with +# single-ended adc_pwdn (LVCMOS33) on T5 in the same bank. Requires external +# 100-ohm differential termination on the board for proper LVDS signal integrity. +# set_property DIFF_TERM TRUE [get_ports {adc_d_p[*]}] # Input delay for ADC data relative to DCO (adjust based on PCB trace length) set_input_delay -clock [get_clocks adc_dco_p] -max 1.0 [get_ports {adc_d_p[*]}] diff --git a/9_Firmware/9_2_FPGA/scripts/build17_production.tcl b/9_Firmware/9_2_FPGA/scripts/build17_production.tcl index 8d4fcbc..91d4ef5 100644 --- a/9_Firmware/9_2_FPGA/scripts/build17_production.tcl +++ b/9_Firmware/9_2_FPGA/scripts/build17_production.tcl @@ -321,13 +321,14 @@ puts $summary_fh "Impl Time: ${impl_elapsed}s" puts $summary_fh "Bitstream Time: ${bit_elapsed}s" puts $summary_fh "" -# Extract key timing numbers +# Extract key timing numbers — use catch to handle empty STATS properties +# (Vivado 2025.2 may return empty strings after write_bitstream auto-launch) puts $summary_fh "--- Timing ---" -set wns [get_property STATS.WNS [current_design]] -set tns [get_property STATS.TNS [current_design]] -set whs [get_property STATS.WHS [current_design]] -set ths [get_property STATS.THS [current_design]] -set fail_ep [get_property STATS.TPWS [current_design]] +if {[catch {set wns [get_property STATS.WNS [current_design]]}] || $wns eq ""} { set wns "N/A" } +if {[catch {set tns [get_property STATS.TNS [current_design]]}] || $tns eq ""} { set tns "N/A" } +if {[catch {set whs [get_property STATS.WHS [current_design]]}] || $whs eq ""} { set whs "N/A" } +if {[catch {set ths [get_property STATS.THS [current_design]]}] || $ths eq ""} { set ths "N/A" } +if {[catch {set fail_ep [get_property STATS.TPWS [current_design]]}] || $fail_ep eq ""} { set fail_ep "N/A" } puts $summary_fh " WNS: $wns ns" puts $summary_fh " TNS: $tns ns" puts $summary_fh " WHS: $whs ns" @@ -366,19 +367,25 @@ puts $summary_fh "" # Signoff puts $summary_fh "--- Final Signoff ---" set signoff_pass 1 -if {$wns < 0} { +if {![string is double -strict $wns]} { + puts $summary_fh " WARN: WNS = N/A (timing stats unavailable — check reports)" +} elseif {$wns < 0} { puts $summary_fh " FAIL: WNS = $wns (negative slack)" set signoff_pass 0 } else { puts $summary_fh " PASS: WNS = $wns ns (no setup violations)" } -if {$whs < 0} { +if {![string is double -strict $whs]} { + puts $summary_fh " WARN: WHS = N/A (timing stats unavailable — check reports)" +} elseif {$whs < 0} { puts $summary_fh " FAIL: WHS = $whs (hold violation)" set signoff_pass 0 } else { puts $summary_fh " PASS: WHS = $whs ns (no hold violations)" } -if {$tns != 0} { +if {![string is double -strict $tns]} { + puts $summary_fh " WARN: TNS = N/A (timing stats unavailable — check reports)" +} elseif {$tns != 0} { puts $summary_fh " FAIL: TNS = $tns (total negative slack)" set signoff_pass 0 } else { diff --git a/9_Firmware/9_2_FPGA/scripts/build18_production.tcl b/9_Firmware/9_2_FPGA/scripts/build18_production.tcl index 0feef68..18b0486 100644 --- a/9_Firmware/9_2_FPGA/scripts/build18_production.tcl +++ b/9_Firmware/9_2_FPGA/scripts/build18_production.tcl @@ -331,13 +331,14 @@ puts $summary_fh "Impl Time: ${impl_elapsed}s" puts $summary_fh "Bitstream Time: ${bit_elapsed}s" puts $summary_fh "" -# Extract key timing numbers +# Extract key timing numbers — use catch to handle empty STATS properties +# (Vivado 2025.2 may return empty strings after write_bitstream auto-launch) puts $summary_fh "--- Timing ---" -set wns [get_property STATS.WNS [current_design]] -set tns [get_property STATS.TNS [current_design]] -set whs [get_property STATS.WHS [current_design]] -set ths [get_property STATS.THS [current_design]] -set fail_ep [get_property STATS.TPWS [current_design]] +if {[catch {set wns [get_property STATS.WNS [current_design]]}] || $wns eq ""} { set wns "N/A" } +if {[catch {set tns [get_property STATS.TNS [current_design]]}] || $tns eq ""} { set tns "N/A" } +if {[catch {set whs [get_property STATS.WHS [current_design]]}] || $whs eq ""} { set whs "N/A" } +if {[catch {set ths [get_property STATS.THS [current_design]]}] || $ths eq ""} { set ths "N/A" } +if {[catch {set fail_ep [get_property STATS.TPWS [current_design]]}] || $fail_ep eq ""} { set fail_ep "N/A" } puts $summary_fh " WNS: $wns ns" puts $summary_fh " TNS: $tns ns" puts $summary_fh " WHS: $whs ns" @@ -376,19 +377,25 @@ puts $summary_fh "" # Signoff puts $summary_fh "--- Final Signoff ---" set signoff_pass 1 -if {$wns < 0} { +if {![string is double -strict $wns]} { + puts $summary_fh " WARN: WNS = N/A (timing stats unavailable — check reports)" +} elseif {$wns < 0} { puts $summary_fh " FAIL: WNS = $wns (negative slack)" set signoff_pass 0 } else { puts $summary_fh " PASS: WNS = $wns ns (no setup violations)" } -if {$whs < 0} { +if {![string is double -strict $whs]} { + puts $summary_fh " WARN: WHS = N/A (timing stats unavailable — check reports)" +} elseif {$whs < 0} { puts $summary_fh " FAIL: WHS = $whs (hold violation)" set signoff_pass 0 } else { puts $summary_fh " PASS: WHS = $whs ns (no hold violations)" } -if {$tns != 0} { +if {![string is double -strict $tns]} { + puts $summary_fh " WARN: TNS = N/A (timing stats unavailable — check reports)" +} elseif {$tns != 0} { puts $summary_fh " FAIL: TNS = $tns (total negative slack)" set signoff_pass 0 } else { diff --git a/9_Firmware/9_2_FPGA/scripts/build19_mmcm.tcl b/9_Firmware/9_2_FPGA/scripts/build19_mmcm.tcl index c20f2bb..e23350b 100644 --- a/9_Firmware/9_2_FPGA/scripts/build19_mmcm.tcl +++ b/9_Firmware/9_2_FPGA/scripts/build19_mmcm.tcl @@ -322,21 +322,30 @@ puts $summary_fh "Impl Time: ${impl_elapsed}s" puts $summary_fh "Bitstream Time: ${bit_elapsed}s" puts $summary_fh "" -# Extract key timing numbers +# Extract key timing numbers — use catch to handle empty STATS properties +# (Vivado 2025.2 may return empty strings after write_bitstream auto-launch) puts $summary_fh "--- Timing ---" -set wns [get_property STATS.WNS [current_design]] -set tns [get_property STATS.TNS [current_design]] -set whs [get_property STATS.WHS [current_design]] -set ths [get_property STATS.THS [current_design]] -set fail_ep [get_property STATS.TPWS [current_design]] +if {[catch {set wns [get_property STATS.WNS [current_design]]}] || $wns eq ""} { set wns "N/A" } +if {[catch {set tns [get_property STATS.TNS [current_design]]}] || $tns eq ""} { set tns "N/A" } +if {[catch {set whs [get_property STATS.WHS [current_design]]}] || $whs eq ""} { set whs "N/A" } +if {[catch {set ths [get_property STATS.THS [current_design]]}] || $ths eq ""} { set ths "N/A" } +if {[catch {set fail_ep [get_property STATS.TPWS [current_design]]}] || $fail_ep eq ""} { set fail_ep "N/A" } puts $summary_fh " WNS: $wns ns" puts $summary_fh " TNS: $tns ns" puts $summary_fh " WHS: $whs ns" puts $summary_fh " THS: $ths ns" puts $summary_fh "" puts $summary_fh " Build 18 Baseline: WNS = +0.062 ns, WHS = +0.059 ns" -puts $summary_fh " Delta WNS: [expr {$wns - 0.062}] ns" -puts $summary_fh " Delta WHS: [expr {$whs - 0.059}] ns" +if {[string is double -strict $wns]} { + puts $summary_fh " Delta WNS: [expr {$wns - 0.062}] ns" +} else { + puts $summary_fh " Delta WNS: N/A (timing stats unavailable)" +} +if {[string is double -strict $whs]} { + puts $summary_fh " Delta WHS: [expr {$whs - 0.059}] ns" +} else { + puts $summary_fh " Delta WHS: N/A (timing stats unavailable)" +} puts $summary_fh "" # Extract utilization @@ -380,19 +389,25 @@ puts $summary_fh "" # Signoff puts $summary_fh "--- Final Signoff ---" set signoff_pass 1 -if {$wns < 0} { +if {![string is double -strict $wns]} { + puts $summary_fh " WARN: WNS = N/A (timing stats unavailable — check reports)" +} elseif {$wns < 0} { puts $summary_fh " FAIL: WNS = $wns (negative slack)" set signoff_pass 0 } else { puts $summary_fh " PASS: WNS = $wns ns (no setup violations)" } -if {$whs < 0} { +if {![string is double -strict $whs]} { + puts $summary_fh " WARN: WHS = N/A (timing stats unavailable — check reports)" +} elseif {$whs < 0} { puts $summary_fh " FAIL: WHS = $whs (hold violation)" set signoff_pass 0 } else { puts $summary_fh " PASS: WHS = $whs ns (no hold violations)" } -if {$tns != 0} { +if {![string is double -strict $tns]} { + puts $summary_fh " WARN: TNS = N/A (timing stats unavailable — check reports)" +} elseif {$tns != 0} { puts $summary_fh " FAIL: TNS = $tns (total negative slack)" set signoff_pass 0 } else { @@ -413,11 +428,11 @@ if {[file exists $bit_src]} { puts $summary_fh "" # Timing regression check vs Build 18 -if {$wns < 0.062} { +if {[string is double -strict $wns] && $wns < 0.062} { puts $summary_fh " *** WARNING: WNS REGRESSED vs Build 18 (was +0.062 ns, now $wns ns) ***" puts $summary_fh " *** Consider reverting MMCM changes per revert-safety policy ***" } -if {$whs < 0.059} { +if {[string is double -strict $whs] && $whs < 0.059} { puts $summary_fh " *** WARNING: WHS REGRESSED vs Build 18 (was +0.059 ns, now $whs ns) ***" } diff --git a/9_Firmware/9_2_FPGA/scripts/build20_mmcm_creg.tcl b/9_Firmware/9_2_FPGA/scripts/build20_mmcm_creg.tcl index 607c014..8b34da7 100644 --- a/9_Firmware/9_2_FPGA/scripts/build20_mmcm_creg.tcl +++ b/9_Firmware/9_2_FPGA/scripts/build20_mmcm_creg.tcl @@ -326,13 +326,14 @@ puts $summary_fh "Impl Time: ${impl_elapsed}s" puts $summary_fh "Bitstream Time: ${bit_elapsed}s" puts $summary_fh "" -# Extract key timing numbers +# Extract key timing numbers — use catch to handle empty STATS properties +# (Vivado 2025.2 may return empty strings after write_bitstream auto-launch) puts $summary_fh "--- Timing ---" -set wns [get_property STATS.WNS [current_design]] -set tns [get_property STATS.TNS [current_design]] -set whs [get_property STATS.WHS [current_design]] -set ths [get_property STATS.THS [current_design]] -set fail_ep [get_property STATS.TPWS [current_design]] +if {[catch {set wns [get_property STATS.WNS [current_design]]}] || $wns eq ""} { set wns "N/A" } +if {[catch {set tns [get_property STATS.TNS [current_design]]}] || $tns eq ""} { set tns "N/A" } +if {[catch {set whs [get_property STATS.WHS [current_design]]}] || $whs eq ""} { set whs "N/A" } +if {[catch {set ths [get_property STATS.THS [current_design]]}] || $ths eq ""} { set ths "N/A" } +if {[catch {set fail_ep [get_property STATS.TPWS [current_design]]}] || $fail_ep eq ""} { set fail_ep "N/A" } puts $summary_fh " WNS: $wns ns" puts $summary_fh " TNS: $tns ns" puts $summary_fh " WHS: $whs ns" @@ -340,8 +341,16 @@ puts $summary_fh " THS: $ths ns" puts $summary_fh "" puts $summary_fh " Build 18 Baseline: WNS = +0.062 ns, WHS = +0.059 ns" puts $summary_fh " Build 19 (FAILED): WNS = -0.011 ns, WHS = +0.055 ns" -puts $summary_fh " Delta WNS vs B18: [expr {$wns - 0.062}] ns" -puts $summary_fh " Delta WHS vs B18: [expr {$whs - 0.059}] ns" +if {[string is double -strict $wns]} { + puts $summary_fh " Delta WNS vs B18: [expr {$wns - 0.062}] ns" +} else { + puts $summary_fh " Delta WNS vs B18: N/A (timing stats unavailable)" +} +if {[string is double -strict $whs]} { + puts $summary_fh " Delta WHS vs B18: [expr {$whs - 0.059}] ns" +} else { + puts $summary_fh " Delta WHS vs B18: N/A (timing stats unavailable)" +} puts $summary_fh "" # Extract utilization @@ -387,19 +396,25 @@ puts $summary_fh "" # Signoff puts $summary_fh "--- Final Signoff ---" set signoff_pass 1 -if {$wns < 0} { +if {![string is double -strict $wns]} { + puts $summary_fh " WARN: WNS = N/A (timing stats unavailable — check reports)" +} elseif {$wns < 0} { puts $summary_fh " FAIL: WNS = $wns (negative slack)" set signoff_pass 0 } else { puts $summary_fh " PASS: WNS = $wns ns (no setup violations)" } -if {$whs < 0} { +if {![string is double -strict $whs]} { + puts $summary_fh " WARN: WHS = N/A (timing stats unavailable — check reports)" +} elseif {$whs < 0} { puts $summary_fh " FAIL: WHS = $whs (hold violation)" set signoff_pass 0 } else { puts $summary_fh " PASS: WHS = $whs ns (no hold violations)" } -if {$tns != 0} { +if {![string is double -strict $tns]} { + puts $summary_fh " WARN: TNS = N/A (timing stats unavailable — check reports)" +} elseif {$tns != 0} { puts $summary_fh " FAIL: TNS = $tns (total negative slack)" set signoff_pass 0 } else { @@ -420,11 +435,11 @@ if {[file exists $bit_src]} { puts $summary_fh "" # Timing regression check vs Build 18 -if {$wns < 0.062} { +if {[string is double -strict $wns] && $wns < 0.062} { puts $summary_fh " *** WARNING: WNS REGRESSED vs Build 18 (was +0.062 ns, now $wns ns) ***" puts $summary_fh " *** Review critical paths — CREG fix may not have helped ***" } -if {$whs < 0.059} { +if {[string is double -strict $whs] && $whs < 0.059} { puts $summary_fh " *** WARNING: WHS REGRESSED vs Build 18 (was +0.059 ns, now $whs ns) ***" } diff --git a/9_Firmware/9_2_FPGA/scripts/build21_fft_e2e.tcl b/9_Firmware/9_2_FPGA/scripts/build21_fft_e2e.tcl index 2733bbd..f73ae11 100644 --- a/9_Firmware/9_2_FPGA/scripts/build21_fft_e2e.tcl +++ b/9_Firmware/9_2_FPGA/scripts/build21_fft_e2e.tcl @@ -335,13 +335,14 @@ puts $summary_fh "Impl Time: ${impl_elapsed}s" puts $summary_fh "Bitstream Time: ${bit_elapsed}s" puts $summary_fh "" -# Extract key timing numbers +# Extract key timing numbers — use catch to handle empty STATS properties +# (Vivado 2025.2 may return empty strings after write_bitstream auto-launch) puts $summary_fh "--- Timing ---" -set wns [get_property STATS.WNS [current_design]] -set tns [get_property STATS.TNS [current_design]] -set whs [get_property STATS.WHS [current_design]] -set ths [get_property STATS.THS [current_design]] -set fail_ep [get_property STATS.TPWS [current_design]] +if {[catch {set wns [get_property STATS.WNS [current_design]]}] || $wns eq ""} { set wns "N/A" } +if {[catch {set tns [get_property STATS.TNS [current_design]]}] || $tns eq ""} { set tns "N/A" } +if {[catch {set whs [get_property STATS.WHS [current_design]]}] || $whs eq ""} { set whs "N/A" } +if {[catch {set ths [get_property STATS.THS [current_design]]}] || $ths eq ""} { set ths "N/A" } +if {[catch {set fail_ep [get_property STATS.TPWS [current_design]]}] || $fail_ep eq ""} { set fail_ep "N/A" } puts $summary_fh " WNS: $wns ns" puts $summary_fh " TNS: $tns ns" puts $summary_fh " WHS: $whs ns" @@ -349,8 +350,16 @@ puts $summary_fh " THS: $ths ns" puts $summary_fh "" puts $summary_fh " Build 20 Baseline: WNS = +0.426 ns, WHS = +0.058 ns" puts $summary_fh " Gap 2 Build (ref): WNS = +0.078 ns, WHS = +0.054 ns" -puts $summary_fh " Delta WNS vs B20: [expr {$wns - 0.426}] ns" -puts $summary_fh " Delta WHS vs B20: [expr {$whs - 0.058}] ns" +if {[string is double -strict $wns]} { + puts $summary_fh " Delta WNS vs B20: [expr {$wns - 0.426}] ns" +} else { + puts $summary_fh " Delta WNS vs B20: N/A (timing stats unavailable)" +} +if {[string is double -strict $whs]} { + puts $summary_fh " Delta WHS vs B20: [expr {$whs - 0.058}] ns" +} else { + puts $summary_fh " Delta WHS vs B20: N/A (timing stats unavailable)" +} puts $summary_fh "" # Extract utilization @@ -396,19 +405,25 @@ puts $summary_fh "" # Signoff puts $summary_fh "--- Final Signoff ---" set signoff_pass 1 -if {$wns < 0} { +if {![string is double -strict $wns]} { + puts $summary_fh " WARN: WNS = N/A (timing stats unavailable — check reports)" +} elseif {$wns < 0} { puts $summary_fh " FAIL: WNS = $wns (negative slack)" set signoff_pass 0 } else { puts $summary_fh " PASS: WNS = $wns ns (no setup violations)" } -if {$whs < 0} { +if {![string is double -strict $whs]} { + puts $summary_fh " WARN: WHS = N/A (timing stats unavailable — check reports)" +} elseif {$whs < 0} { puts $summary_fh " FAIL: WHS = $whs (hold violation)" set signoff_pass 0 } else { puts $summary_fh " PASS: WHS = $whs ns (no hold violations)" } -if {$tns != 0} { +if {![string is double -strict $tns]} { + puts $summary_fh " WARN: TNS = N/A (timing stats unavailable — check reports)" +} elseif {$tns != 0} { puts $summary_fh " FAIL: TNS = $tns (total negative slack)" set signoff_pass 0 } else { @@ -429,11 +444,11 @@ if {[file exists $bit_src]} { puts $summary_fh "" # Timing regression check vs Build 20 -if {$wns < 0.078} { +if {[string is double -strict $wns] && $wns < 0.078} { puts $summary_fh " *** WARNING: WNS REGRESSED below Gap 2 build (was +0.078 ns, now $wns ns) ***" puts $summary_fh " *** Review critical paths — FFT opts or RTL fixes may have introduced new timing pressure ***" } -if {$whs < 0.054} { +if {[string is double -strict $whs] && $whs < 0.054} { puts $summary_fh " *** WARNING: WHS REGRESSED below Gap 2 build (was +0.054 ns, now $whs ns) ***" }