fix(fpga): F-0.1 wire AD9484 OR overrange pin into diagnostics

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.
This commit is contained in:
Jason
2026-04-20 15:32:23 +05:45
parent eb8189a7f1
commit 951390f678
5 changed files with 130 additions and 5 deletions
+61 -3
View File
@@ -4,15 +4,23 @@ module ad9484_interface_400m (
input wire [7:0] adc_d_n, // ADC Data N input wire [7:0] adc_d_n, // ADC Data N
input wire adc_dco_p, // Data Clock Output P (400MHz) input wire adc_dco_p, // Data Clock Output P (400MHz)
input wire adc_dco_n, // Data Clock Output N (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 // System Interface
input wire sys_clk, // 100MHz system clock (for control only) input wire sys_clk, // 100MHz system clock (for control only)
input wire reset_n, input wire reset_n,
// Output at 400MHz domain // Output at 400MHz domain
output wire [7:0] adc_data_400m, // ADC data at 400MHz output wire [7:0] adc_data_400m, // ADC data at 400MHz
output wire adc_data_valid_400m, // Valid 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 // LVDS to single-ended conversion
@@ -166,4 +174,54 @@ end
assign adc_data_400m = adc_data_400m_reg; assign adc_data_400m = adc_data_400m_reg;
assign adc_data_valid_400m = adc_data_valid_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 endmodule
@@ -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 IOSTANDARD LVDS_25 [get_ports {adc_d_n[*]}]
set_property DIFF_TERM TRUE [get_ports {adc_d_p[*]}] 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) # ADC Power Down — single-ended, Bank 14 (LVCMOS25 matches bank VCCO)
# Pin: P20 = IO_0_14 # Pin: P20 = IO_0_14
set_property PACKAGE_PIN P20 [get_ports {adc_pwdn}] set_property PACKAGE_PIN P20 [get_ports {adc_pwdn}]
@@ -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] -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 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 USB 2.0 INTERFACE (Bank 35, VCCO=3.3V)
# ============================================================================ # ============================================================================
+32 -2
View File
@@ -9,6 +9,9 @@ module radar_receiver_final (
input wire [7:0] adc_d_n, // ADC Data N (LVDS) 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_p, // Data Clock Output P (400MHz LVDS)
input wire adc_dco_n, // Data Clock Output N (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, output wire adc_pwdn,
// Chirp counter from transmitter (for matched filter indexing) // 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) // ADC power-down control (directly tie low = ADC always on)
assign adc_pwdn = 1'b0; assign adc_pwdn = 1'b0;
wire adc_overrange_400m;
ad9484_interface_400m adc ( ad9484_interface_400m adc (
.adc_d_p(adc_d_p), .adc_d_p(adc_d_p),
.adc_d_n(adc_d_n), .adc_d_n(adc_d_n),
.adc_dco_p(adc_dco_p), .adc_dco_p(adc_dco_p),
.adc_dco_n(adc_dco_n), .adc_dco_n(adc_dco_n),
.adc_or_p(adc_or_p),
.adc_or_n(adc_or_n),
.sys_clk(clk), .sys_clk(clk),
.reset_n(reset_n), .reset_n(reset_n),
.adc_data_400m(adc_data_cmos), .adc_data_400m(adc_data_cmos),
.adc_data_valid_400m(adc_valid), .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, lowhigh-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 // 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 // (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 // 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) .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]; assign ddc_saturation_count = ddc_diagnostics_w[7:5];
ddc_input_interface ddc_if ( ddc_input_interface ddc_if (
+5
View File
@@ -67,6 +67,9 @@ module radar_system_top (
input wire [7:0] adc_d_n, // ADC Data N (LVDS) 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_p, // Data Clock Output P (400MHz LVDS)
input wire adc_dco_n, // Data Clock Output N (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 output wire adc_pwdn, // ADC Power Down
// ========== STM32 CONTROL INTERFACES ========== // ========== STM32 CONTROL INTERFACES ==========
@@ -526,6 +529,8 @@ radar_receiver_final rx_inst (
.adc_d_n(adc_d_n), .adc_d_n(adc_d_n),
.adc_dco_p(adc_dco_p), .adc_dco_p(adc_dco_p),
.adc_dco_n(adc_dco_n), .adc_dco_n(adc_dco_n),
.adc_or_p(adc_or_p),
.adc_or_n(adc_or_n),
.adc_pwdn(adc_pwdn), .adc_pwdn(adc_pwdn),
// Doppler Outputs // Doppler Outputs