Wire a per-frame MCU→FPGA "beam pattern ready" handshake so the chirp scheduler can stall between 48-chirp frames until the MCU finishes writing the next ADAR1000 pattern. The legacy unused stm32_new_chirp input on PD8 is repurposed as stm32_beam_ready; chirp_scheduler.v gets a new S_BEAM_WAIT state entered after each frame_pulse and an 80 ms watchdog so a missed MCU toggle degrades to wall-clock cadence with a sticky telemetry bit rather than stalling the radar. Cold-reset defaults the handshake off (host_handshake_enable=0, new opcode 0x1A); the GUI opts in once the MCU PD8 wiring is verified on the bench. Both the FT601 and FT2232H status word 4 paths get the new beam_handshake_watchdog_fired sticky at bit [1] (reclaimed from the range_mode retirement in commit 1). RTL: - chirp_scheduler.v: 2-FF ASYNC_REG sync on beam_ready_async; 1-cycle edge detect (any transition, MCU side uses HAL_GPIO_TogglePin); new S_BEAM_WAIT state entered at frame_pulse when host_handshake_enable=1; 23-bit beam_watchdog counter with BEAM_WATCHDOG_MAX = 8_000_000 (~80 ms at 100 MHz, ~10 nominal frames); beam_handshake_watchdog_fired output sticky across mixers_enable cycles, cleared only by reset_n; mid-wait disable releases the FSM so dropping the opcode never strands the radar between frames. - radar_receiver_final.v: thread stm32_beam_ready_async + host_handshake_enable + beam_handshake_watchdog_fired through the scheduler instance. - radar_system_top.v: rename input port stm32_new_chirp → stm32_beam_ready; add host_handshake_enable register (cold-reset = 1'b0); opcode 0x1A dispatch (value[0]); add rx_beam_handshake_watchdog wire; pack into status_words[4][1] in both USB paths. - radar_system_top_50t.v: rename wrapper port + sub-instance wiring. - usb_data_interface.v + usb_data_interface_ft2232h.v: add status_beam_handshake_watchdog input + 2-FF level CDC (same convention as F-6.4 / F-1.2 stickies); refresh word-4 layout doc comment; pack beam_handshake_wd_sync_1 into status_words[4][1]. XDC: - xc7a50t_ftg256.xdc + xc7a200t_fbg484.xdc: rename stm32_new_chirp port references to stm32_beam_ready (same PD8 pin, F13 on 50T / L18 on 200T). MCU: - main.h: add FPGA_BEAM_READY_Pin = GPIO_PIN_8 + FPGA_BEAM_READY_GPIO_Port = GPIOD alongside the existing FPGA_FRAME_PULSE alias. - main.cpp:runRadarPulseSequence: insert HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_8) after each setCustomBeamPattern16(RX) — once after the per-azimuth broadside (vector_0), once after matrix1, once after matrix2 — between the SPI burst completion and waitForFramePulse. GUI: - radar_protocol.py: Opcode.HANDSHAKE_ENABLE = 0x1A; StatusResponse.beam_handshake_watchdog = 0 default; parse word 4 bit [1] in parse_status_packet; update word-4 layout comment. - test_GUI_V65_Tk.py: add beam_handshake_watchdog kwarg to _make_status_packet (sets bit [1] of word 4); refresh test_parse_status_word4_layout_co_spec to cover the new bit (used+9=32); add test_parse_status_beam_handshake_watchdog round-trip; test_handshake_enable_opcode pins 0x1A; defaults / chirps_mismatch / agc-coexist tests gain a watchdog==0 assertion; bump test_all_rtl_opcodes_present expected set to include 0x17/0x18/0x19/0x1A. TB: - new tb_chirp_scheduler_handshake.v (16 checks): legacy open-loop, edge exit (rising + falling), 200-cycle idle hold, watchdog auto-advance via force on dut.beam_watchdog, sticky-survives-mixers_disable, mid-wait disable release, reset_n clears sticky. - run_regression.sh: register the new TB in PHASE 1. - tb_radar_receiver_final.v: tie the 3 new receiver ports off (beam_ready_async=0, handshake_enable=0, watchdog unconnected). - tb_system_mechanics.v / tb_system_opcodes.v: explicit .stm32_beam_ready(1'b0) connection (the cold-reset host_handshake_enable=0 keeps the FSM out of S_BEAM_WAIT). - tb_usb_data_interface.v / tb_usb_protocol_v2.v / tb_e2e_dsp_to_host.v / tb_ft2232h_frame_drop.v: tie .status_beam_handshake_watchdog(1'b0). Ride-along ruff sweep (14 → 0 across the repo): - tb/cosim/compare_independent.py: RUF003 — '5×' → 'at least 5x'. - tb/cosim/gen_e2e_expected.py: noqa: E402 on the post-sys.path import; drop unused EXPECTED_RANGE_BIN + EXPECTED_DOPPLER_BIN_PER_SF imports; fold the detect-class slot if/else into a ternary (SIM108). - tb/cosim/gen_e2e_stimulus.py: drop int() wrapping round() at four call sites (RUF046 — round() already returns int in Python 3); rewrite the range-bin derivation comment block from code-like `# range_bin = ...` to prose (ERA001); strip stray f from placeholder-free error string (F541). - tb/cosim/tb_e2e_dsp_to_host_parse.py: open(path, 'r') → open(path) (UP015). - v7/dashboard.py: '3×' → '3x' (RUF003); drop quotes from 'StatusResponse | None' annotation (UP037, file already has `from __future__ import annotations`). CI summary (all suites green pre-commit): - ruff: All checks passed! - FPGA regression (iverilog): 43 / 0 / 0 (incl. new handshake TB 16/16). - MCU tests: 51 / 0 + 34 / 0 + 13 / 13 ADAR1000_AGC. - GUI Tk (test_GUI_V65_Tk): 120 / 0. - GUI v7 (test_v7): 152 / 0. Production rollout note: bitstream cold-resets with host_handshake_enable=0 so existing flashes keep their open-loop cadence until the GUI sends opcode 0x1A=1. Once enabled, the per-pattern dwell tracks both the chirp ladder (PD14 frame_pulse from commit-3 work) and the MCU pattern-write completion (PD8 toggle from this commit), eliminating drift from the SPI burst timing.
542 lines
30 KiB
Tcl
542 lines
30 KiB
Tcl
# ============================================================================
|
||
# RADAR SYSTEM FPGA CONSTRAINTS
|
||
# ============================================================================
|
||
# Device: XC7A50T-2FTG256I (FTG256 package)
|
||
# Board: AERIS-10 Phased Array Radar — Main Board
|
||
# Source: Pin assignments extracted from Eagle schematic (RADAR_Main_Board.sch)
|
||
# FPGA = U42
|
||
#
|
||
# NOTE: The README and prior version of this file incorrectly referenced
|
||
# XC7A100TCSG324-1. The physical board uses XC7A50T in a 256-ball BGA
|
||
# (FTG256). All PACKAGE_PIN values below are FTG256 ball locations.
|
||
#
|
||
# I/O Bank Voltage Summary:
|
||
# Bank 0: VCCO = 3.3V (JTAG, flash CS)
|
||
# Bank 14: VCCO = 2.5V (ADC LVDS_25 data — placer-enforced; adc_pwdn as LVCMOS25)
|
||
# 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 (FT2232H USB 2.0 FIFO — 15 signals)
|
||
#
|
||
# 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 / Place 30-372: Bank 14 must have a single VCCO. LVDS_25 forces
|
||
# VCCO=2.5V, so adc_pwdn was changed from LVCMOS33 to LVCMOS25 to match.
|
||
# IBUFDS input buffers are VCCO-independent. BIVC-1 also waived via
|
||
# set_property SEVERITY in the build script as an additional safety net.
|
||
# in the build script. adc_pwdn (LVCMOS25) coexists in the same bank.
|
||
# - UCIO/NSTD: Unconstrained ports (FT601 ports inactive with USB_MODE=1,
|
||
# status/debug outputs have no physical pins). Handled with SEVERITY
|
||
# demotion + default IOSTANDARD.
|
||
# - PLIO-9: FT2232H CLKOUT routed to C4 (IO_L12N_T1_MRCC_35, N-type).
|
||
# Clock inputs normally use P-type MRCC pins, but IBUFG works correctly
|
||
# on N-type. Demote PLIO-9 to warning in build script.
|
||
# ============================================================================
|
||
|
||
# ============================================================================
|
||
# CONFIGURATION
|
||
# ============================================================================
|
||
set_property CFGBVS VCCO [current_design]
|
||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||
|
||
# ============================================================================
|
||
# DRC WAIVERS — Hardware-Level Known Issues (applied in build script)
|
||
# ============================================================================
|
||
# BIVC-1: Bank 14 VCCO=3.3V with LVDS_25 inputs + LVCMOS33 adc_pwdn.
|
||
# IBUFDS input buffers are VCCO-independent on 7-series — they use an
|
||
# internal differential amplifier that works correctly at any VCCO.
|
||
# The BIVC-1 DRC check is intended for OBUFDS *outputs* where VCCO
|
||
# directly affects the output swing. Since Bank 14 has only LVDS inputs
|
||
# and one LVCMOS33 output, this is safe to demote to a warning.
|
||
# → Applied in build_50t_test.tcl: set_property SEVERITY {Warning} [get_drc_checks BIVC-1]
|
||
#
|
||
# NSTD-1 / UCIO-1: Unconstrained ports — FT601 USB ports (inactive with
|
||
# USB_MODE=1 generate block), dac_clk (DAC clock comes from AD9523, not FPGA),
|
||
# and all status/debug outputs (no physical pins available). These ports are
|
||
# present in the shared RTL but have no connections on the 50T board.
|
||
# → Applied in build_50t_test.tcl: set_property SEVERITY {Warning} [get_drc_checks {NSTD-1 UCIO-1}]
|
||
|
||
# ============================================================================
|
||
# CLOCK CONSTRAINTS
|
||
# ============================================================================
|
||
|
||
# 100MHz System Clock (AD9523 OUT6 → FPGA_SYS_CLOCK → Bank 15 MRCC pin E12)
|
||
set_property PACKAGE_PIN E12 [get_ports {clk_100m}]
|
||
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 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 RTL output that assigns clk_120m directly. It has no
|
||
# physical pin on the 50T board and is left unconnected here. The port
|
||
# CANNOT be removed from the RTL because the 200T board uses it with
|
||
# ODDR clock forwarding (pin H17, see xc7a200t_fbg484.xdc).
|
||
# 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
|
||
|
||
# ADC DCO Clock (400MHz LVDS — AD9523 OUT5 → AD9484 → FPGA, Bank 14 MRCC)
|
||
# NOTE: LVDS_25 is the only valid differential input standard on 7-series HR
|
||
# banks. IBUFDS input buffers are VCCO-independent — they work correctly even
|
||
# with VCCO=3.3V. The BIVC-1 DRC (voltage conflict with LVCMOS33 adc_pwdn)
|
||
# is waived in the build script since this bank has only LVDS *inputs*.
|
||
set_property PACKAGE_PIN N14 [get_ports {adc_dco_p}]
|
||
set_property PACKAGE_PIN P14 [get_ports {adc_dco_n}]
|
||
set_property IOSTANDARD LVDS_25 [get_ports {adc_dco_p}]
|
||
set_property IOSTANDARD LVDS_25 [get_ports {adc_dco_n}]
|
||
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
|
||
|
||
# --------------------------------------------------------------------------
|
||
# FT2232H 60 MHz CLKOUT (Bank 35, MRCC pin C4)
|
||
# --------------------------------------------------------------------------
|
||
# The FT2232H provides a 60 MHz clock in 245 Synchronous FIFO mode.
|
||
# Pin C4 is IO_L12N_T1_MRCC_35 (N-type of MRCC pair). Vivado requires
|
||
# CLOCK_DEDICATED_ROUTE FALSE for clock inputs on N-type MRCC pins
|
||
# (Place 30-876). The schematic routes CLKOUT to C4; this cannot be
|
||
# changed without a board respin. The clock still uses an IBUFG and
|
||
# reaches the clock network — the constraint only disables the DRC check.
|
||
set_property PACKAGE_PIN C4 [get_ports {ft_clkout}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {ft_clkout}]
|
||
create_clock -name ft_clkout -period 16.667 [get_ports {ft_clkout}]
|
||
set_input_jitter [get_clocks ft_clkout] 0.2
|
||
# N-type MRCC pin requires dedicated route override (Place 30-876).
|
||
# Audit F-0.4: the literal net name `ft_clkout_IBUF` exists post-synth but
|
||
# the XDC scan happens before synthesis, when the IBUF net does not yet
|
||
# exist — Vivado reported `No nets matched 'ft_clkout_IBUF'` + CRITICAL
|
||
# WARNING. Use -hierarchical -filter + -quiet so the constraint matches
|
||
# post-synth without warning during pre-synth XDC scan. The TCL duplicate
|
||
# at scripts/50t/build_50t.tcl:119 remains as belt-and-suspenders.
|
||
set_property -quiet CLOCK_DEDICATED_ROUTE FALSE \
|
||
[get_nets -quiet -hierarchical -filter {NAME =~ *ft_clkout_IBUF}]
|
||
|
||
# ============================================================================
|
||
# RESET (Active-Low)
|
||
# ============================================================================
|
||
# DIG_4 (STM32 PD12 → FPGA Bank 15 pin E15)
|
||
# STM32 firmware uses HAL_GPIO_WritePin to assert/deassert FPGA reset.
|
||
|
||
set_property PACKAGE_PIN E15 [get_ports {reset_n}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {reset_n}]
|
||
set_property PULLUP true [get_ports {reset_n}]
|
||
|
||
# ============================================================================
|
||
# TRANSMITTER INTERFACE (DAC — Bank 15, VCCO=3.3V)
|
||
# ============================================================================
|
||
|
||
# DAC Data Bus (8-bit) — AD9708 data inputs via schematic nets DAC_0..DAC_7
|
||
set_property PACKAGE_PIN A14 [get_ports {dac_data[0]}]
|
||
set_property PACKAGE_PIN A13 [get_ports {dac_data[1]}]
|
||
set_property PACKAGE_PIN A12 [get_ports {dac_data[2]}]
|
||
set_property PACKAGE_PIN B11 [get_ports {dac_data[3]}]
|
||
set_property PACKAGE_PIN B10 [get_ports {dac_data[4]}]
|
||
set_property PACKAGE_PIN A10 [get_ports {dac_data[5]}]
|
||
set_property PACKAGE_PIN A9 [get_ports {dac_data[6]}]
|
||
set_property PACKAGE_PIN A8 [get_ports {dac_data[7]}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {dac_data[*]}]
|
||
set_property SLEW FAST [get_ports {dac_data[*]}]
|
||
set_property DRIVE 8 [get_ports {dac_data[*]}]
|
||
|
||
# DAC Clock Output — NOT DIRECTLY WIRED TO DAC IN SCHEMATIC
|
||
# The DAC chip (U3) receives its clock from AD9523 via a separate net
|
||
# (DAC_CLOCK), not from the FPGA. The RTL `dac_clk` output has no
|
||
# physical pin. Comment out or remove from RTL.
|
||
# set_property PACKAGE_PIN ??? [get_ports {dac_clk}]
|
||
# set_property IOSTANDARD LVCMOS33 [get_ports {dac_clk}]
|
||
# set_property SLEW FAST [get_ports {dac_clk}]
|
||
|
||
# DAC Sleep Control — DAC_SLEEP net
|
||
set_property PACKAGE_PIN A15 [get_ports {dac_sleep}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {dac_sleep}]
|
||
|
||
# RF Switch Control — M3S_VCTRL net
|
||
set_property PACKAGE_PIN G15 [get_ports {fpga_rf_switch}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {fpga_rf_switch}]
|
||
|
||
# Mixer Enables — MIX_RX_EN, MIX_TX_EN nets
|
||
set_property PACKAGE_PIN D11 [get_ports {rx_mixer_en}]
|
||
set_property PACKAGE_PIN C11 [get_ports {tx_mixer_en}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {rx_mixer_en}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {tx_mixer_en}]
|
||
|
||
# ============================================================================
|
||
# ADAR1000 BEAMFORMER CONTROL (Bank 34, VCCO=1.8V)
|
||
# ============================================================================
|
||
|
||
# ADAR1000 TX Load Pins (via level shifters, active-high pulse)
|
||
set_property PACKAGE_PIN P3 [get_ports {adar_tx_load_1}]
|
||
set_property PACKAGE_PIN T4 [get_ports {adar_tx_load_2}]
|
||
set_property PACKAGE_PIN R3 [get_ports {adar_tx_load_3}]
|
||
set_property PACKAGE_PIN R2 [get_ports {adar_tx_load_4}]
|
||
|
||
# ADAR1000 RX Load Pins
|
||
set_property PACKAGE_PIN M5 [get_ports {adar_rx_load_1}]
|
||
set_property PACKAGE_PIN T2 [get_ports {adar_rx_load_2}]
|
||
set_property PACKAGE_PIN R1 [get_ports {adar_rx_load_3}]
|
||
set_property PACKAGE_PIN N4 [get_ports {adar_rx_load_4}]
|
||
|
||
# Bank 34 VCCO = 1.8V → must use LVCMOS18 (not LVCMOS33)
|
||
set_property IOSTANDARD LVCMOS18 [get_ports {adar_*_load_*}]
|
||
|
||
# ADAR1000 TR (Transmit/Receive) Pins
|
||
set_property PACKAGE_PIN N2 [get_ports {adar_tr_1}]
|
||
set_property PACKAGE_PIN N1 [get_ports {adar_tr_2}]
|
||
set_property PACKAGE_PIN P1 [get_ports {adar_tr_3}]
|
||
set_property PACKAGE_PIN P4 [get_ports {adar_tr_4}]
|
||
set_property IOSTANDARD LVCMOS18 [get_ports {adar_tr_*}]
|
||
|
||
# ============================================================================
|
||
# LEVEL SHIFTER SPI INTERFACE (STM32 ↔ ADAR1000)
|
||
# ============================================================================
|
||
|
||
# 3.3V Side (from STM32, Bank 15, VCCO=3.3V)
|
||
set_property PACKAGE_PIN J16 [get_ports {stm32_sclk_3v3}]
|
||
set_property PACKAGE_PIN H13 [get_ports {stm32_mosi_3v3}]
|
||
set_property PACKAGE_PIN G14 [get_ports {stm32_miso_3v3}]
|
||
set_property PACKAGE_PIN F14 [get_ports {stm32_cs_adar1_3v3}]
|
||
set_property PACKAGE_PIN H16 [get_ports {stm32_cs_adar2_3v3}]
|
||
set_property PACKAGE_PIN G16 [get_ports {stm32_cs_adar3_3v3}]
|
||
set_property PACKAGE_PIN J15 [get_ports {stm32_cs_adar4_3v3}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {stm32_*_3v3}]
|
||
|
||
# 1.8V Side (to ADAR1000, Bank 34, VCCO=1.8V)
|
||
set_property PACKAGE_PIN P5 [get_ports {stm32_sclk_1v8}]
|
||
set_property PACKAGE_PIN M1 [get_ports {stm32_mosi_1v8}]
|
||
set_property PACKAGE_PIN N3 [get_ports {stm32_miso_1v8}]
|
||
set_property PACKAGE_PIN L5 [get_ports {stm32_cs_adar1_1v8}]
|
||
set_property PACKAGE_PIN L4 [get_ports {stm32_cs_adar2_1v8}]
|
||
set_property PACKAGE_PIN M4 [get_ports {stm32_cs_adar3_1v8}]
|
||
set_property PACKAGE_PIN M2 [get_ports {stm32_cs_adar4_1v8}]
|
||
set_property IOSTANDARD LVCMOS18 [get_ports {stm32_*_1v8}]
|
||
|
||
# ============================================================================
|
||
# STM32 CONTROL INTERFACE (DIG bus, Bank 15, VCCO=3.3V)
|
||
# ============================================================================
|
||
# DIG_0..DIG_4 are STM32 outputs (PD8-PD12) → FPGA inputs (control)
|
||
# DIG_5..DIG_7 are STM32 inputs (PD13-PD15) ← FPGA outputs (status / sync)
|
||
|
||
set_property PACKAGE_PIN F13 [get_ports {stm32_beam_ready}] ;# DIG_0 (PD8)
|
||
# DIG_1 (PD9) + DIG_2 (PD10) retired in PR-AB.b expanded — formerly
|
||
# stm32_new_elevation / stm32_new_azimuth. MCU side init is also stripped
|
||
# (see commit 3); the pins default to high-Z inputs after MCU reset.
|
||
set_property PACKAGE_PIN F15 [get_ports {stm32_mixers_enable}] ;# DIG_3 (PD11)
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {stm32_beam_ready}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {stm32_mixers_enable}]
|
||
# reset_n is DIG_4 (PD12) — constrained above in the RESET section
|
||
|
||
# DIG_5 = H11, DIG_6 = G12, DIG_7 = H12 — FPGA→STM32 status / sync outputs
|
||
# DIG_5 (PD13): signal-chain saturation flag — drives MCU outer-loop AGC.
|
||
# DIG_6 (PD14): stretched chirp_scheduler frame_pulse (~100 ns) — PR-AB.b
|
||
# STM32 EXTI rising edge for drift-free dwell sync.
|
||
# DIG_7 (PD15): control-chain fault OR — F-6.4 range_decim_watchdog
|
||
# | F-1.2 CIC→FIR CDC overrun. MCU PD15 stuck-high sampler
|
||
# triggers attemptErrorRecovery(ERROR_FPGA_DSP_STALL).
|
||
set_property PACKAGE_PIN H11 [get_ports {gpio_dig5}]
|
||
set_property PACKAGE_PIN G12 [get_ports {gpio_dig6}]
|
||
set_property PACKAGE_PIN H12 [get_ports {gpio_dig7}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {gpio_dig*}]
|
||
set_property DRIVE 8 [get_ports {gpio_dig*}]
|
||
set_property SLEW SLOW [get_ports {gpio_dig*}]
|
||
|
||
# ============================================================================
|
||
# ADC INTERFACE (LVDS — Bank 14, VCCO=3.3V)
|
||
# ============================================================================
|
||
|
||
# ADC Data (8-bit LVDS pairs from AD9484)
|
||
set_property PACKAGE_PIN P15 [get_ports {adc_d_p[0]}]
|
||
set_property PACKAGE_PIN P16 [get_ports {adc_d_n[0]}]
|
||
set_property PACKAGE_PIN R15 [get_ports {adc_d_p[1]}]
|
||
set_property PACKAGE_PIN R16 [get_ports {adc_d_n[1]}]
|
||
set_property PACKAGE_PIN T14 [get_ports {adc_d_p[2]}]
|
||
set_property PACKAGE_PIN T15 [get_ports {adc_d_n[2]}]
|
||
set_property PACKAGE_PIN R13 [get_ports {adc_d_p[3]}]
|
||
set_property PACKAGE_PIN T13 [get_ports {adc_d_n[3]}]
|
||
set_property PACKAGE_PIN R10 [get_ports {adc_d_p[4]}]
|
||
set_property PACKAGE_PIN R11 [get_ports {adc_d_n[4]}]
|
||
set_property PACKAGE_PIN T9 [get_ports {adc_d_p[5]}]
|
||
set_property PACKAGE_PIN T10 [get_ports {adc_d_n[5]}]
|
||
set_property PACKAGE_PIN T7 [get_ports {adc_d_p[6]}]
|
||
set_property PACKAGE_PIN T8 [get_ports {adc_d_n[6]}]
|
||
set_property PACKAGE_PIN R6 [get_ports {adc_d_p[7]}]
|
||
set_property PACKAGE_PIN R7 [get_ports {adc_d_n[7]}]
|
||
|
||
# ADC DCO Clock (LVDS) — already constrained above in CLOCK section
|
||
|
||
# ADC Power Down — ADC_PWRD net (single-ended, Bank 14)
|
||
# Uses LVCMOS25 to be voltage-compatible with LVDS_25 in the same bank.
|
||
# The placer enforces a single VCCO per bank; LVDS_25 demands VCCO=2.5V.
|
||
# LVCMOS25 output drives the AD9484 PWDN pin (CMOS threshold ~0.8V) safely.
|
||
# On the physical board (VCCO=3.3V), output swings follow actual VCCO.
|
||
set_property PACKAGE_PIN T5 [get_ports {adc_pwdn}]
|
||
set_property IOSTANDARD LVCMOS25 [get_ports {adc_pwdn}]
|
||
|
||
# LVDS I/O Standard — LVDS_25 is the only valid differential input standard
|
||
# on 7-series HR banks. IBUFDS inputs work correctly regardless of VCCO.
|
||
set_property IOSTANDARD LVDS_25 [get_ports {adc_d_p[*]}]
|
||
set_property IOSTANDARD LVDS_25 [get_ports {adc_d_n[*]}]
|
||
|
||
# Differential termination — DIFF_TERM uses a ~100-ohm on-die termination
|
||
# inside the IBUFDS. This is VCCO-independent for 7-series input buffers.
|
||
# RTL IBUFDS uses DIFF_TERM("FALSE") so this XDC property takes precedence.
|
||
set_property DIFF_TERM TRUE [get_ports {adc_d_p[*]}]
|
||
|
||
# Input delay for ADC data relative to DCO (adjust based on PCB trace length)
|
||
# DDR interface: constrain both rising and falling clock edges
|
||
set_input_delay -clock [get_clocks adc_dco_p] -max 1.0 [get_ports {adc_d_p[*]}]
|
||
set_input_delay -clock [get_clocks adc_dco_p] -min 0.2 [get_ports {adc_d_p[*]}]
|
||
set_input_delay -clock [get_clocks adc_dco_p] -max 1.0 -clock_fall [get_ports {adc_d_p[*]}] -add_delay
|
||
set_input_delay -clock [get_clocks adc_dco_p] -min 0.2 -clock_fall [get_ports {adc_d_p[*]}] -add_delay
|
||
|
||
# --------------------------------------------------------------------------
|
||
# Audit F-0.1: AD9484 OR (overrange) LVDS pair (Bank 14)
|
||
# Schematic RADAR_Main_Board.sch: ADC_OR_P → U42 IO_L19P_T3_A10_D26_14 (M6)
|
||
# ADC_OR_N → U42 IO_L19N_T3_A09_D25_VREF_14 (N6)
|
||
# DDR-sourced by adc_dco_p, same timing class as adc_d_p[*].
|
||
# --------------------------------------------------------------------------
|
||
set_property PACKAGE_PIN M6 [get_ports {adc_or_p}]
|
||
set_property PACKAGE_PIN N6 [get_ports {adc_or_n}]
|
||
set_property IOSTANDARD LVDS_25 [get_ports {adc_or_p}]
|
||
set_property IOSTANDARD LVDS_25 [get_ports {adc_or_n}]
|
||
set_property DIFF_TERM TRUE [get_ports {adc_or_p}]
|
||
set_input_delay -clock [get_clocks adc_dco_p] -max 1.0 [get_ports {adc_or_p}]
|
||
set_input_delay -clock [get_clocks adc_dco_p] -min 0.2 [get_ports {adc_or_p}]
|
||
set_input_delay -clock [get_clocks adc_dco_p] -max 1.0 -clock_fall [get_ports {adc_or_p}] -add_delay
|
||
set_input_delay -clock [get_clocks adc_dco_p] -min 0.2 -clock_fall [get_ports {adc_or_p}] -add_delay
|
||
|
||
# ============================================================================
|
||
# FT2232H USB 2.0 INTERFACE (Bank 35, VCCO=3.3V)
|
||
# ============================================================================
|
||
# FT2232H (U6) Channel A in 245 Synchronous FIFO mode.
|
||
# All signals are direct connections to FPGA Bank 35 (LVCMOS33).
|
||
# Pin mapping extracted from Eagle schematic (RADAR_Main_Board.sch).
|
||
#
|
||
# The FT2232H replaces the previously-unwired FT601 on the 50T production
|
||
# board. The 200T dev board retains FT601 USB 3.0 (32-bit).
|
||
# ============================================================================
|
||
|
||
# 8-bit bidirectional data bus (ADBUS0–ADBUS7)
|
||
set_property PACKAGE_PIN K1 [get_ports {ft_data[0]}] ;# ADBUS0 → IO_L22P_T3_35
|
||
set_property PACKAGE_PIN J3 [get_ports {ft_data[1]}] ;# ADBUS1 → IO_L21P_T3_DQS_35
|
||
set_property PACKAGE_PIN H3 [get_ports {ft_data[2]}] ;# ADBUS2 → IO_L21N_T3_DQS_35
|
||
set_property PACKAGE_PIN G4 [get_ports {ft_data[3]}] ;# ADBUS3 → IO_L16N_T2_35
|
||
set_property PACKAGE_PIN F2 [get_ports {ft_data[4]}] ;# ADBUS4 → IO_L15P_T2_DQS_35
|
||
set_property PACKAGE_PIN D1 [get_ports {ft_data[5]}] ;# ADBUS5 → IO_L10N_T1_AD15N_35
|
||
set_property PACKAGE_PIN C3 [get_ports {ft_data[6]}] ;# ADBUS6 → IO_L7P_T1_AD6P_35
|
||
set_property PACKAGE_PIN C1 [get_ports {ft_data[7]}] ;# ADBUS7 → IO_L9P_T1_DQS_AD7P_35
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {ft_data[*]}]
|
||
|
||
# Control signals (active low where noted)
|
||
set_property PACKAGE_PIN A2 [get_ports {ft_rxf_n}] ;# ACBUS0 → IO_L8N_T1_AD14N_35
|
||
set_property PACKAGE_PIN B2 [get_ports {ft_txe_n}] ;# ACBUS1 → IO_L8P_T1_AD14P_35
|
||
set_property PACKAGE_PIN A3 [get_ports {ft_rd_n}] ;# ACBUS2 → IO_L4N_T0_35
|
||
set_property PACKAGE_PIN A4 [get_ports {ft_wr_n}] ;# ACBUS3 → IO_L3N_T0_DQS_AD5N_35
|
||
set_property PACKAGE_PIN A5 [get_ports {ft_siwu}] ;# ACBUS4 → IO_L3P_T0_DQS_AD5P_35
|
||
set_property PACKAGE_PIN B7 [get_ports {ft_oe_n}] ;# ACBUS6 → IO_L1P_T0_AD4P_35
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {ft_rxf_n}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {ft_txe_n}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {ft_rd_n}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {ft_wr_n}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {ft_siwu}]
|
||
set_property IOSTANDARD LVCMOS33 [get_ports {ft_oe_n}]
|
||
|
||
# Output timing: SLEW FAST + DRIVE 8 for FT2232H signals
|
||
set_property SLEW FAST [get_ports {ft_rd_n}]
|
||
set_property SLEW FAST [get_ports {ft_wr_n}]
|
||
set_property SLEW FAST [get_ports {ft_oe_n}]
|
||
set_property SLEW FAST [get_ports {ft_siwu}]
|
||
set_property SLEW FAST [get_ports {ft_data[*]}]
|
||
set_property DRIVE 8 [get_ports {ft_rd_n}]
|
||
set_property DRIVE 8 [get_ports {ft_wr_n}]
|
||
set_property DRIVE 8 [get_ports {ft_oe_n}]
|
||
set_property DRIVE 8 [get_ports {ft_siwu}]
|
||
set_property DRIVE 8 [get_ports {ft_data[*]}]
|
||
|
||
# ft_clkout constrained above in CLOCK CONSTRAINTS section (C4, 60 MHz)
|
||
|
||
# --------------------------------------------------------------------------
|
||
# FT2232H Source-Synchronous Timing Constraints
|
||
# --------------------------------------------------------------------------
|
||
# FT2232H 245 Synchronous FIFO mode timing (60 MHz, period = 16.667 ns).
|
||
# Values per FTDI TN_167 "FT2232H Synchronous FIFO Bus Bridge" — verify
|
||
# against the exact app-note revision before shipping.
|
||
#
|
||
# FPGA Read Path (FT2232H drives data/RXF#/TXE#, FPGA samples on CLKOUT↑):
|
||
# - t_co (CLKOUT↑ → data valid) max = 10.0 ns
|
||
# - t_coh (CLKOUT↑ → data hold) min = 0.5 ns
|
||
# - set_input_delay -max = t_co, -min = t_coh
|
||
#
|
||
# FPGA Write Path (FPGA drives data/WR#/RD#/OE#, FT2232H samples on CLKOUT↑):
|
||
# - t_su (data setup before CLKOUT↑) min = 3.5 ns (NOT 5 ns — prior
|
||
# constraint used a synthetic period-based back-calculation)
|
||
# - t_h (data hold after CLKOUT↑) min = 1.0 ns (NOT 0 — a 0 ns hold
|
||
# constraint produced no hold check at all)
|
||
# - set_output_delay -max = t_su, -min = -t_h (Vivado convention)
|
||
#
|
||
# Audit F-2026-04-20 Option B: the previous output_delay = 11.667 ns
|
||
# (= period − 5) over-constrained launch by ~8 ns vs the actual datasheet
|
||
# figure. Relaxing to 3.5 ns matches the chip's real setup requirement.
|
||
# --------------------------------------------------------------------------
|
||
|
||
# Input delays: FT2232H → FPGA (data bus and status signals)
|
||
set_input_delay -clock [get_clocks ft_clkout] -max 10.0 [get_ports {ft_data[*]}]
|
||
set_input_delay -clock [get_clocks ft_clkout] -min 0.5 [get_ports {ft_data[*]}]
|
||
set_input_delay -clock [get_clocks ft_clkout] -max 10.0 [get_ports {ft_rxf_n}]
|
||
set_input_delay -clock [get_clocks ft_clkout] -min 0.5 [get_ports {ft_rxf_n}]
|
||
set_input_delay -clock [get_clocks ft_clkout] -max 10.0 [get_ports {ft_txe_n}]
|
||
set_input_delay -clock [get_clocks ft_clkout] -min 0.5 [get_ports {ft_txe_n}]
|
||
|
||
# Output delays: FPGA → FT2232H (control strobes and data bus when writing)
|
||
set_output_delay -clock [get_clocks ft_clkout] -max 3.5 [get_ports {ft_data[*]}]
|
||
set_output_delay -clock [get_clocks ft_clkout] -min -1.0 [get_ports {ft_data[*]}]
|
||
set_output_delay -clock [get_clocks ft_clkout] -max 3.5 [get_ports {ft_rd_n}]
|
||
set_output_delay -clock [get_clocks ft_clkout] -min -1.0 [get_ports {ft_rd_n}]
|
||
set_output_delay -clock [get_clocks ft_clkout] -max 3.5 [get_ports {ft_wr_n}]
|
||
set_output_delay -clock [get_clocks ft_clkout] -min -1.0 [get_ports {ft_wr_n}]
|
||
set_output_delay -clock [get_clocks ft_clkout] -max 3.5 [get_ports {ft_oe_n}]
|
||
set_output_delay -clock [get_clocks ft_clkout] -min -1.0 [get_ports {ft_oe_n}]
|
||
set_output_delay -clock [get_clocks ft_clkout] -max 3.5 [get_ports {ft_siwu}]
|
||
set_output_delay -clock [get_clocks ft_clkout] -min -1.0 [get_ports {ft_siwu}]
|
||
|
||
# ============================================================================
|
||
# STATUS / DEBUG OUTPUTS — NO PHYSICAL CONNECTIONS
|
||
# ============================================================================
|
||
# The following RTL output ports have no corresponding FPGA pins in the
|
||
# schematic. The only FPGA→STM32 outputs available are DIG_5 (H11),
|
||
# DIG_6 (G12), and DIG_7 (H12) — only 3 pins for potentially 60+ signals.
|
||
#
|
||
# These constraints are commented out. If status readback is needed, either:
|
||
# (a) Multiplex selected status bits onto DIG_5/6/7, or
|
||
# (b) Send status data over the SPI interface, or
|
||
# (c) Route through USB once FT601 is wired.
|
||
#
|
||
# Ports affected:
|
||
# current_elevation[5:0], current_azimuth[5:0], current_chirp[5:0],
|
||
# new_chirp_frame, dbg_doppler_data[31:0], dbg_doppler_valid,
|
||
# dbg_doppler_bin[4:0], dbg_range_bin[5:0], system_status[3:0]
|
||
# ============================================================================
|
||
|
||
# ============================================================================
|
||
# TIMING EXCEPTIONS
|
||
# ============================================================================
|
||
|
||
# False paths for asynchronous STM32 control signals (active-edge toggle interface)
|
||
set_false_path -from [get_ports {stm32_new_*}]
|
||
set_false_path -from [get_ports {stm32_mixers_enable}]
|
||
|
||
# --------------------------------------------------------------------------
|
||
# Async reset recovery/removal false paths
|
||
#
|
||
# The async reset (reset_n) is held asserted for multiple clock cycles during
|
||
# power-on and system reset. The recovery/removal timing checks on CLR pins
|
||
# are over-constrained for this use case:
|
||
# - reset_sync_reg[1] fans out to 1000+ registers across the FPGA
|
||
# - Route delay alone exceeds the clock period (18+ ns for 10ns period)
|
||
# - Reset deassertion order is not functionally critical — all registers
|
||
# come out of reset within a few cycles of each other
|
||
# --------------------------------------------------------------------------
|
||
# Audit F-0.5: the literal cell name `reset_sync_reg[*]` does not match any
|
||
# cell in the post-synth netlist. The actual sync regs are
|
||
# `u_core/reset_sync_reg[0..1]`, `u_core/rx_inst/ddc/reset_sync_400m_reg[*]`,
|
||
# `u_core/gen_ft2232h.usb_inst/ft_reset_sync_reg[*]`, and peers under
|
||
# `u_core/reset_sync_120m_reg[*]`, `u_core/reset_sync_ft601_reg[*]`,
|
||
# `u_core/rx_inst/adc/reset_sync_400m_reg[*]`. The waiver below covers all
|
||
# of them by matching any register whose name contains `reset_sync`.
|
||
# Without this, STA runs recovery/removal on the fanout of each sync-chain
|
||
# output register (up to ~1000 loads pre-PR#113 replication).
|
||
set_false_path -from [get_cells -hierarchical -filter {NAME =~ *reset_sync*_reg*}] \
|
||
-to [get_pins -hierarchical -filter {REF_PIN_NAME == CLR || REF_PIN_NAME == PRE}]
|
||
|
||
# --------------------------------------------------------------------------
|
||
# Clock Domain Crossing false paths
|
||
# --------------------------------------------------------------------------
|
||
|
||
# clk_100m ↔ adc_dco_p (400 MHz): DDC has internal CDC synchronizers
|
||
set_false_path -from [get_clocks clk_100m] -to [get_clocks adc_dco_p]
|
||
set_false_path -from [get_clocks adc_dco_p] -to [get_clocks clk_100m]
|
||
|
||
# --------------------------------------------------------------------------
|
||
# CIC comb stages — multicycle path (4-cycle setup / 3-cycle hold)
|
||
# --------------------------------------------------------------------------
|
||
# Comb registers (cic_*/comb_reg[*], cic_*/comb_delay_reg[*][*],
|
||
# cic_*/comb_0_c_reg, cic_*/comb_0_ab_reg, cic_*/comb_0_p_reg) are clocked at
|
||
# adc_dco_p (400 MHz) but their CE pins are driven by data_valid_comb_pipe /
|
||
# data_valid_comb_0_out, which fire once every 4 cycles after the 4× decimator.
|
||
# Effective throughput is 100 MHz, so STA can budget 4·2.5 ns = 10 ns of setup
|
||
# slack instead of 2.5 ns. This frees the DSP48E1s these stages previously
|
||
# occupied (5 per channel × 2 channels = 10 DSPs) and lets fabric carry-chain
|
||
# subtracts close timing comfortably. See cic_decimator_4x_enhanced.v header
|
||
# comment on the comb array declaration.
|
||
set_multicycle_path 4 -setup \
|
||
-from [get_cells -hierarchical -filter {NAME =~ *cic_*/comb_*reg*}] \
|
||
-to [get_cells -hierarchical -filter {NAME =~ *cic_*/comb_*reg*}]
|
||
set_multicycle_path 3 -hold \
|
||
-from [get_cells -hierarchical -filter {NAME =~ *cic_*/comb_*reg*}] \
|
||
-to [get_cells -hierarchical -filter {NAME =~ *cic_*/comb_*reg*}]
|
||
# Also relax the launch path from integrator_sampled_comb (fed by integrator_4
|
||
# DSP48E1 at decimated rate) into comb_0_c_reg.
|
||
set_multicycle_path 4 -setup \
|
||
-from [get_cells -hierarchical -filter {NAME =~ *cic_*/integrator_sampled_comb_reg*}] \
|
||
-to [get_cells -hierarchical -filter {NAME =~ *cic_*/comb_*reg*}]
|
||
set_multicycle_path 3 -hold \
|
||
-from [get_cells -hierarchical -filter {NAME =~ *cic_*/integrator_sampled_comb_reg*}] \
|
||
-to [get_cells -hierarchical -filter {NAME =~ *cic_*/comb_*reg*}]
|
||
|
||
# clk_100m ↔ clk_120m_dac: CDC via synchronizers in radar_system_top
|
||
set_false_path -from [get_clocks clk_100m] -to [get_clocks clk_120m_dac]
|
||
set_false_path -from [get_clocks clk_120m_dac] -to [get_clocks clk_100m]
|
||
|
||
# FT2232H CDC: clk_100m ↔ ft_clkout (60 MHz), toggle CDC in RTL
|
||
set_false_path -from [get_clocks clk_100m] -to [get_clocks ft_clkout]
|
||
set_false_path -from [get_clocks ft_clkout] -to [get_clocks clk_100m]
|
||
|
||
# FT2232H CDC: clk_120m_dac ↔ ft_clkout (no direct crossing, but belt-and-suspenders)
|
||
set_false_path -from [get_clocks clk_120m_dac] -to [get_clocks ft_clkout]
|
||
set_false_path -from [get_clocks ft_clkout] -to [get_clocks clk_120m_dac]
|
||
|
||
# MMCM 400 MHz domain ↔ FT2232H USB clock (see adc_clk_mmcm.xdc for rationale)
|
||
set_false_path -from [get_clocks clk_mmcm_out0] -to [get_clocks ft_clkout]
|
||
set_false_path -from [get_clocks ft_clkout] -to [get_clocks clk_mmcm_out0]
|
||
|
||
# ============================================================================
|
||
# PHYSICAL CONSTRAINTS
|
||
# ============================================================================
|
||
|
||
# Pull up unused pins to prevent floating inputs
|
||
set_property BITSTREAM.CONFIG.UNUSEDPIN Pullup [current_design]
|
||
|
||
# ============================================================================
|
||
# ADDITIONAL NOTES
|
||
# ============================================================================
|
||
#
|
||
# 1. ADC Sampling Clock: FPGA_ADC_CLOCK_P/N (N11/N12) is the 400 MHz LVDS
|
||
# clock from AD9523 OUT5 that drives the AD9484. It connects to FPGA MRCC
|
||
# pins but is not used as an FPGA clock input — the ADC returns data with
|
||
# its own DCO (adc_dco_p/n on N14/P14).
|
||
#
|
||
# 2. Clock Test: FPGA_CLOCK_TEST (H14) is a 20 MHz LVCMOS output from the
|
||
# FPGA, configured by AD9523 OUT7. Not currently used in RTL.
|
||
#
|
||
# 3. SPI Flash: FPGA_FLASH_CS (E8), FPGA_FLASH_CLK (J13),
|
||
# FPGA_FLASH_D0 (J14), D1 (K15), D2 (K16), D3 (L12), unnamed (L13).
|
||
# These are typically handled by Vivado bitstream configuration and do
|
||
# not need explicit XDC constraints for user logic.
|
||
#
|
||
# 4. JTAG: FPGA_TCK (L7), FPGA_TDI (N7), FPGA_TDO (N8), FPGA_TMS (M7).
|
||
# Dedicated pins — no XDC constraints needed.
|
||
#
|
||
# 5. dac_clk port: Not connected on the 50T board (DAC clocked directly from
|
||
# AD9523). The RTL port exists for 200T board compatibility, where the FPGA
|
||
# forwards the DAC clock via ODDR to pin H17 with generated clock and
|
||
# timing constraints (see xc7a200t_fbg484.xdc). Do NOT remove from RTL.
|
||
#
|
||
# ============================================================================
|
||
# END OF CONSTRAINTS
|
||
# ============================================================================
|