From 70067c6121143f06cc1c61fe8faf412590396d4c Mon Sep 17 00:00:00 2001 From: Jason <83615043+JJassonn69@users.noreply.github.com> Date: Mon, 20 Apr 2026 15:32:23 +0545 Subject: [PATCH] fix(fpga): F-0.1 wire AD9484 OR overrange pin into diagnostics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AD9484 OR (overrange) LVDS pair is routed on the 50T main board to xc7a50t-ftg256 bank-14 pins M6/N6 but was previously left unconnected at the top level. Plumb it through the full stack so saturation at the raw ADC boundary shows up in the existing overflow aggregation: - ad9484_interface_400m: add adc_or_p/n inputs, IBUFDS + IDDR capture of both phases in the BUFIO domain, re-register into the clk_400m BUFG domain, OR rise|fall into adc_overrange_400m output. - radar_receiver_final: stickify adc_overrange_400m in clk_400m, CDC to clk_100m via a 2FF ASYNC_REG chain (same reasoning as F-1.2's cdc_cic_fir_overrun — single-bit, latched low→high, GPIO-class diagnostic), OR into the existing ddc_overflow_any aggregation. - radar_system_top: expose adc_or_p/n top-level ports and pass through. - xc7a50t_ftg256.xdc: anchor M6/N6 as LVDS_25 DIFF_TERM, with the same DCO-relative input-delay constraints as adc_d_p[*]. - xc7a200t_fbg484.xdc: IOSTANDARD/DIFF_TERM set; PACKAGE_PIN left as a documented TODO — the 200T dev-board schematic has not been checked and the 200T build will need the anchor filled in before place/route. --- 9_Firmware/9_2_FPGA/ad9484_interface_400m.v | 64 ++++++++++++++++++- .../9_2_FPGA/constraints/xc7a200t_fbg484.xdc | 16 +++++ .../9_2_FPGA/constraints/xc7a50t_ftg256.xdc | 16 +++++ 9_Firmware/9_2_FPGA/radar_receiver_final.v | 34 +++++++++- 9_Firmware/9_2_FPGA/radar_system_top.v | 5 ++ 5 files changed, 130 insertions(+), 5 deletions(-) diff --git a/9_Firmware/9_2_FPGA/ad9484_interface_400m.v b/9_Firmware/9_2_FPGA/ad9484_interface_400m.v index 557ce12..796cca5 100644 --- a/9_Firmware/9_2_FPGA/ad9484_interface_400m.v +++ b/9_Firmware/9_2_FPGA/ad9484_interface_400m.v @@ -4,15 +4,23 @@ module ad9484_interface_400m ( input wire [7:0] adc_d_n, // ADC Data N input wire adc_dco_p, // Data Clock Output P (400MHz) input wire adc_dco_n, // Data Clock Output N (400MHz) - + // Audit F-0.1: AD9484 OR (overrange) LVDS pair, DDR like data. + // Routed on the 50T main board to bank 14 pins M6/N6. Asserts for any + // sample whose absolute value exceeds full-scale. + input wire adc_or_p, + input wire adc_or_n, + // System Interface input wire sys_clk, // 100MHz system clock (for control only) input wire reset_n, - + // Output at 400MHz domain output wire [7:0] adc_data_400m, // ADC data at 400MHz output wire adc_data_valid_400m, // Valid at 400MHz - output wire adc_dco_bufg // Buffered 400MHz DCO clock for downstream use + output wire adc_dco_bufg, // Buffered 400MHz DCO clock for downstream use + // Audit F-0.1: OR flag, clk_400m domain. High on any sample in the + // current 400 MHz cycle where the ADC reports overrange. + output wire adc_overrange_400m ); // LVDS to single-ended conversion @@ -166,4 +174,54 @@ end assign adc_data_400m = adc_data_400m_reg; assign adc_data_valid_400m = adc_data_valid_400m_reg; +// ============================================================================ +// Audit F-0.1: AD9484 OR (overrange) capture +// OR is a DDR LVDS pair (same as data). Buffer it, capture both edges with an +// IDDR in the BUFIO domain, then OR the two phases into a single clk_400m +// flag. Register once for stability. No latching — downstream is expected to +// stickify in its own domain. +// ============================================================================ +wire adc_or_raw; +IBUFDS #( + .DIFF_TERM("FALSE"), + .IOSTANDARD("DEFAULT") +) ibufds_or ( + .O(adc_or_raw), + .I(adc_or_p), + .IB(adc_or_n) +); + +wire adc_or_rise; +wire adc_or_fall; +IDDR #( + .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), + .INIT_Q1(1'b0), + .INIT_Q2(1'b0), + .SRTYPE("SYNC") +) iddr_or ( + .Q1(adc_or_rise), + .Q2(adc_or_fall), + .C(adc_dco_bufio), + .CE(1'b1), + .D(adc_or_raw), + .R(1'b0), + .S(1'b0) +); + +reg adc_or_rise_bufg; +reg adc_or_fall_bufg; +always @(posedge adc_dco_buffered) begin + adc_or_rise_bufg <= adc_or_rise; + adc_or_fall_bufg <= adc_or_fall; +end + +reg adc_overrange_r; +always @(posedge adc_dco_buffered or negedge reset_n_400m) begin + if (!reset_n_400m) + adc_overrange_r <= 1'b0; + else + adc_overrange_r <= adc_or_rise_bufg | adc_or_fall_bufg; +end +assign adc_overrange_400m = adc_overrange_r; + endmodule \ No newline at end of file diff --git a/9_Firmware/9_2_FPGA/constraints/xc7a200t_fbg484.xdc b/9_Firmware/9_2_FPGA/constraints/xc7a200t_fbg484.xdc index 1185c2e..6290737 100644 --- a/9_Firmware/9_2_FPGA/constraints/xc7a200t_fbg484.xdc +++ b/9_Firmware/9_2_FPGA/constraints/xc7a200t_fbg484.xdc @@ -134,6 +134,22 @@ set_property IOSTANDARD LVDS_25 [get_ports {adc_d_p[*]}] set_property IOSTANDARD LVDS_25 [get_ports {adc_d_n[*]}] set_property DIFF_TERM TRUE [get_ports {adc_d_p[*]}] +# -------------------------------------------------------------------------- +# Audit F-0.1: AD9484 OR (overrange) LVDS pair +# The 50T main board schematic routes ADC_OR_P/N to bank-14 pins M6/N6 on +# xc7a50t-ftg256. The 200T dev-board schematic has NOT been checked yet; +# adc_or_p/n are declared as top-level ports so the 50T build anchors them +# cleanly, but the 200T anchor below is a TODO placeholder — synth/impl will +# error on unplaced IO until the 200T schematic is verified and the PACKAGE_PIN +# values are set. IOSTANDARD/DIFF_TERM properties stay as-is (same class as +# adc_d_p). +# -------------------------------------------------------------------------- +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}] +# TODO(F-0.1): set_property PACKAGE_PIN [get_ports {adc_or_p}] after 200T schematic audit +# TODO(F-0.1): set_property PACKAGE_PIN [get_ports {adc_or_n}] after 200T schematic audit + # ADC Power Down — single-ended, Bank 14 (LVCMOS25 matches bank VCCO) # Pin: P20 = IO_0_14 set_property PACKAGE_PIN P20 [get_ports {adc_pwdn}] diff --git a/9_Firmware/9_2_FPGA/constraints/xc7a50t_ftg256.xdc b/9_Firmware/9_2_FPGA/constraints/xc7a50t_ftg256.xdc index c63e995..4696edd 100644 --- a/9_Firmware/9_2_FPGA/constraints/xc7a50t_ftg256.xdc +++ b/9_Firmware/9_2_FPGA/constraints/xc7a50t_ftg256.xdc @@ -290,6 +290,22 @@ 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) # ============================================================================ diff --git a/9_Firmware/9_2_FPGA/radar_receiver_final.v b/9_Firmware/9_2_FPGA/radar_receiver_final.v index 24eed03..f292c1c 100644 --- a/9_Firmware/9_2_FPGA/radar_receiver_final.v +++ b/9_Firmware/9_2_FPGA/radar_receiver_final.v @@ -9,6 +9,9 @@ module radar_receiver_final ( input wire [7:0] adc_d_n, // ADC Data N (LVDS) input wire adc_dco_p, // Data Clock Output P (400MHz LVDS) input wire adc_dco_n, // Data Clock Output N (400MHz LVDS) + // Audit F-0.1: AD9484 OR (overrange) LVDS pair + input wire adc_or_p, + input wire adc_or_n, output wire adc_pwdn, // Chirp counter from transmitter (for matched filter indexing) @@ -206,18 +209,43 @@ wire adc_valid; // Data valid signal // ADC power-down control (directly tie low = ADC always on) assign adc_pwdn = 1'b0; +wire adc_overrange_400m; ad9484_interface_400m adc ( .adc_d_p(adc_d_p), .adc_d_n(adc_d_n), .adc_dco_p(adc_dco_p), .adc_dco_n(adc_dco_n), + .adc_or_p(adc_or_p), + .adc_or_n(adc_or_n), .sys_clk(clk), .reset_n(reset_n), .adc_data_400m(adc_data_cmos), .adc_data_valid_400m(adc_valid), - .adc_dco_bufg(clk_400m) + .adc_dco_bufg(clk_400m), + .adc_overrange_400m(adc_overrange_400m) ); +// Audit F-0.1: stickify the 400 MHz OR pulse, then CDC to clk_100m via 2FF. +// Same reasoning as ddc_cic_fir_overrun: single-bit, low→high-only once +// latched, so a 2FF sync is sufficient for a GPIO-class diagnostic. Cleared +// only by global reset_n. +reg adc_overrange_sticky_400m; +always @(posedge clk_400m or negedge reset_n) begin + if (!reset_n) + adc_overrange_sticky_400m <= 1'b0; + else if (adc_overrange_400m) + adc_overrange_sticky_400m <= 1'b1; +end + +(* ASYNC_REG = "TRUE" *) reg [1:0] adc_overrange_sync_100m; +always @(posedge clk or negedge reset_n) begin + if (!reset_n) + adc_overrange_sync_100m <= 2'b00; + else + adc_overrange_sync_100m <= {adc_overrange_sync_100m[0], adc_overrange_sticky_400m}; +end +wire adc_overrange_100m = adc_overrange_sync_100m[1]; + // NOTE: The cdc_adc_to_processing instance that was here used src_clk=dst_clk=clk_400m // (same clock domain — no crossing). Gray-code CDC on same-clock with fast-changing // ADC data corrupts samples because Gray coding only guarantees safe transfer of @@ -270,7 +298,9 @@ ddc_400m_enhanced ddc( .cdc_cic_fir_overrun(ddc_cic_fir_overrun) ); -assign ddc_overflow_any = ddc_mixer_saturation | ddc_filter_overflow; +// Audit F-0.1: AD9484 overrange aggregated here so a single gpio_dig bit +// covers DDC-internal saturation, FIR overflow, AND raw ADC clipping. +assign ddc_overflow_any = ddc_mixer_saturation | ddc_filter_overflow | adc_overrange_100m; assign ddc_saturation_count = ddc_diagnostics_w[7:5]; ddc_input_interface ddc_if ( diff --git a/9_Firmware/9_2_FPGA/radar_system_top.v b/9_Firmware/9_2_FPGA/radar_system_top.v index fa80791..461af0f 100644 --- a/9_Firmware/9_2_FPGA/radar_system_top.v +++ b/9_Firmware/9_2_FPGA/radar_system_top.v @@ -67,6 +67,9 @@ module radar_system_top ( input wire [7:0] adc_d_n, // ADC Data N (LVDS) input wire adc_dco_p, // Data Clock Output P (400MHz LVDS) input wire adc_dco_n, // Data Clock Output N (400MHz LVDS) + // Audit F-0.1: AD9484 OR (overrange) LVDS pair + input wire adc_or_p, + input wire adc_or_n, output wire adc_pwdn, // ADC Power Down // ========== STM32 CONTROL INTERFACES ========== @@ -526,6 +529,8 @@ radar_receiver_final rx_inst ( .adc_d_n(adc_d_n), .adc_dco_p(adc_dco_p), .adc_dco_n(adc_dco_n), + .adc_or_p(adc_or_p), + .adc_or_n(adc_or_n), .adc_pwdn(adc_pwdn), // Doppler Outputs