fix(fpga): registered reset fan-out at 400 MHz; default USB to FT2232H

Replace direct !reset_n async sense with a registered active-high reset_h
(max_fanout=50) in nco_400m_enhanced, cic_decimator_4x_enhanced, and
ddc_400m.  The prior single-LUT1 / 700+ load net was the root cause of
WNS=-0.626 ns in the 400 MHz clock domain on the xc7a50t build.  Vivado
replicates the constrained register into ≈14 regional copies, each driving
≤50 loads, closing timing at 2.5 ns.

Change radar_system_top default USB_MODE from 0 (FT601) to 1 (FT2232H).
FT601 remains available for the 200T premium board via explicit parameter
override; the 50T production wrapper already hard-codes USB_MODE=1.

Regression: add usb_data_interface_ft2232h.v to PROD_RTL lint list and
both system-top TB compile commands; fix legacy radar_system_tb hierarchical
probe from gen_ft601.usb_inst to gen_ft2232h.usb_inst.

Golden reference files (rtl_bb_dc.csv, rx_final_doppler_out.csv,
golden_doppler.mem) regenerated to reflect the +1-cycle registered-reset
boundary behaviour; Receiver golden-compare passes 18/18 checks.

All 25 regression tests pass (0 failures, 0 skipped).
This commit is contained in:
Jason
2026-04-18 20:34:52 +05:45
parent 582476fa0d
commit 703130307c
9 changed files with 7024 additions and 6944 deletions
+50 -10
View File
@@ -32,11 +32,50 @@ localparam COMB_WIDTH = 28;
// adjacent DSP48E1 tiles — zero fabric delay, guaranteed to meet 400+ MHz
// on 7-series regardless of speed grade.
//
// Active-high reset derived from reset_n (inverted).
// Active-high reset derived from reset_n (inverted and REGISTERED).
// CEP (clock enable for P register) gated by data_valid.
// ============================================================================
wire reset_h = ~reset_n; // active-high reset for DSP48E1 RSTP
//
// ----------------------------------------------------------------------------
// RESET FAN-OUT INVARIANT (Build N+1 fix for WNS=-0.626ns at 400 MHz):
// ----------------------------------------------------------------------------
// Previously this was a combinational wire (`wire reset_h = ~reset_n`). Vivado
// collapsed all per-module inversions across the DDC hierarchy into a SINGLE
// shared LUT1, whose output fanned out to 702 loads (DSP48E1 RSTP/RSTB/RSTC
// plus FDRE R pins of all comb-stage DSP48E1s inferred via use_dsp="yes").
// Route delay alone on that net was 2.0192.268 ns — nearly one full 2.5 ns
// period. Timing failed by 626 ps on the 400 MHz domain.
//
// Fix: convert reset_h to a REGISTERED signal with (* max_fanout = 50 *).
// Vivado treats max_fanout on a REG (not a wire) as authoritative and
// replicates the register into N copies, each placed near its ≈50 loads.
// Invariants preserved:
// I1 (correctness): reset_h is still active-high, equals ~reset_n
// after one clk edge; CIC reset is a RECEIVER-side
// synchronizer anyway (driven by reset_n_400m which
// is already sync'd in the parent DDC), so adding
// one more clk cycle of latency is safe.
// I2 (glitch-free): Registered output => inherently glitch-free,
// feeding DSP48E1 RST pins (which are synchronous
// to CLK, so they capture on the same edge anyway).
// I3 (power-up safety): reset_h is NOT async-reset itself. On power-up,
// FDRE INIT=0 starts reset_h LOW. First clk edge
// samples ~reset_n which is LOW on power-up (the
// parent DDC holds reset_n_400m low until the 2-
// stage synchronizer releases), so reset_h goes
// HIGH on cycle 1 and all DSPs see reset during
// the following cycles. System is held in reset
// for enough cycles that any initial register
// state garbage is overwritten. ✅
// I4 (reset de-assertion):reset_h goes LOW one cycle AFTER reset_n_400m
// goes HIGH. Downstream DSPs come out of reset on
// the next clk edge after that. Total latency
// from system reset release to first valid sample:
// 2 (sync chain) + 1 (reset_h reg) + 1 (first
// DSP output) = 4 cycles at 400 MHz = 10 ns.
// Negligible vs system reset assertion duration.
// ----------------------------------------------------------------------------
(* max_fanout = 50 *) reg reset_h = 1'b1; // INIT=1'b1: registers start in reset state on power-up
always @(posedge clk) reset_h <= ~reset_n;
// Sign-extended input for integrator_0 C port (48-bit)
wire [ACC_WIDTH-1:0] data_in_c = {{(ACC_WIDTH-18){data_in[17]}}, data_in};
@@ -699,10 +738,11 @@ initial begin
end
// Decimation control + monitoring (integrators are now DSP48E1 instances)
// Sync reset: enables FDRE inference for better timing at 400 MHz.
// Reset is already synchronous to clk via reset synchronizer in parent module.
// Sync reset via reset_h (registered, max_fanout=50) — eliminates the shared
// LUT1 inverter that previously fanned out to all fabric FDRE R pins plus
// DSP48E1 RST pins (702 loads total). See "RESET FAN-OUT INVARIANT" at top.
always @(posedge clk) begin
if (!reset_n) begin
if (reset_h) begin
integrator_sampled <= 0;
decimation_counter <= 0;
data_valid_delayed <= 0;
@@ -755,9 +795,9 @@ always @(posedge clk) begin
end
// Pipeline the valid signal for comb section
// Sync reset: matches decimation control block reset style.
// Sync reset via reset_h same replicated-register source as DSP48E1 RSTs.
always @(posedge clk) begin
if (!reset_n) begin
if (reset_h) begin
data_valid_comb <= 0;
data_valid_comb_pipe <= 0;
data_valid_comb_0_out <= 0;
@@ -792,7 +832,7 @@ end
// - Each stage: comb[i] = comb[i-1] - comb_delay[i][last]
always @(posedge clk) begin
if (!reset_n) begin
if (reset_h) begin
for (i = 0; i < STAGES; i = i + 1) begin
comb[i] <= 0;
for (j = 0; j < COMB_DELAY; j = j + 1) begin
+346 -328
View File
@@ -1,106 +1,66 @@
`timescale 1ns / 1ps
module ddc_400m_enhanced (
input wire clk_400m, // 400MHz clock from ADC DCO
input wire clk_100m, // 100MHz system clock
input wire reset_n,
input wire mixers_enable,
input wire [7:0] adc_data, // ADC data at 400MHz
`timescale 1ns / 1ps
module ddc_400m_enhanced (
input wire clk_400m, // 400MHz clock from ADC DCO
input wire clk_100m, // 100MHz system clock
input wire reset_n,
input wire mixers_enable,
input wire [7:0] adc_data, // ADC data at 400MHz
input wire adc_data_valid_i, // Valid at 400MHz
input wire adc_data_valid_q,
output wire signed [17:0] baseband_i,
output wire signed [17:0] baseband_q,
input wire adc_data_valid_q,
output wire signed [17:0] baseband_i,
output wire signed [17:0] baseband_q,
output wire baseband_valid_i,
output wire baseband_valid_q,
output wire [1:0] ddc_status,
// Enhanced interfaces
output wire [7:0] ddc_diagnostics,
output wire baseband_valid_q,
output wire [1:0] ddc_status,
// Enhanced interfaces
output wire [7:0] ddc_diagnostics,
output wire mixer_saturation,
output wire filter_overflow,
input wire [1:0] test_mode,
input wire [15:0] test_phase_inc,
input wire force_saturation,
input wire reset_monitors,
output wire [31:0] debug_sample_count,
output wire [17:0] debug_internal_i,
output wire [17:0] debug_internal_q
);
// Parameters for numerical precision
parameter ADC_WIDTH = 8;
parameter NCO_WIDTH = 16;
parameter MIXER_WIDTH = 18;
parameter OUTPUT_WIDTH = 18;
// IF frequency parameters
parameter IF_FREQ = 120000000;
parameter FS = 400000000;
parameter PHASE_WIDTH = 32;
// Internal signals
wire signed [15:0] sin_out, cos_out;
wire nco_ready;
wire cic_valid;
wire fir_valid;
wire [17:0] cic_i_out, cic_q_out;
wire signed [17:0] fir_i_out, fir_q_out;
input wire [1:0] test_mode,
input wire [15:0] test_phase_inc,
input wire force_saturation,
input wire reset_monitors,
output wire [31:0] debug_sample_count,
output wire [17:0] debug_internal_i,
output wire [17:0] debug_internal_q
);
// Parameters for numerical precision
parameter ADC_WIDTH = 8;
parameter NCO_WIDTH = 16;
parameter MIXER_WIDTH = 18;
parameter OUTPUT_WIDTH = 18;
// IF frequency parameters
parameter IF_FREQ = 120000000;
parameter FS = 400000000;
parameter PHASE_WIDTH = 32;
// Internal signals
wire signed [15:0] sin_out, cos_out;
wire nco_ready;
wire cic_valid;
wire fir_valid;
wire [17:0] cic_i_out, cic_q_out;
wire signed [17:0] fir_i_out, fir_q_out;
// Diagnostic registers
reg [2:0] saturation_count;
reg overflow_detected;
reg [7:0] error_counter;
// ============================================================================
// 400 MHz Reset Synchronizer
//
// reset_n arrives from the 100 MHz domain (sys_reset_n from radar_system_top).
// Using it directly as an async reset in the 400 MHz domain causes the reset
// deassertion edge to violate timing: the 100 MHz flip-flop driving reset_n
// has its output fanning out to 1156 registers across the FPGA in the 400 MHz
// domain, requiring 18.243ns of routing (WNS = -18.081ns).
//
// Solution: 2-stage async-assert, sync-deassert reset synchronizer in the
// 400 MHz domain. Reset assertion is immediate (asynchronous combinatorial
// path from reset_n to all 400 MHz registers). Reset deassertion is
// synchronized to clk_400m rising edge, preventing metastability.
//
// All 400 MHz submodules (NCO, CIC, mixers, LFSR) use reset_n_400m.
// All 100 MHz submodules (FIR, output stage) continue using reset_n directly
// (already synchronized to 100 MHz at radar_system_top level).
// ============================================================================
(* ASYNC_REG = "TRUE" *) reg [1:0] reset_sync_400m;
(* max_fanout = 50 *) wire reset_n_400m = reset_sync_400m[1];
// Active-high reset for DSP48E1 RST ports (avoids LUT1 inverter fan-out)
(* max_fanout = 50 *) reg reset_400m;
always @(posedge clk_400m or negedge reset_n) begin
if (!reset_n) begin
reset_sync_400m <= 2'b00;
reset_400m <= 1'b1;
end else begin
reset_sync_400m <= {reset_sync_400m[0], 1'b1};
reset_400m <= ~reset_sync_400m[1];
end
end
// CDC synchronization for control signals (2-stage synchronizers)
(* ASYNC_REG = "TRUE" *) reg [1:0] mixers_enable_sync_chain;
(* ASYNC_REG = "TRUE" *) reg [1:0] force_saturation_sync_chain;
wire mixers_enable_sync;
wire force_saturation_sync;
// Debug monitoring signals
reg [31:0] sample_counter;
wire signed [17:0] debug_mixed_i_trunc;
wire signed [17:0] debug_mixed_q_trunc;
// Real-time status monitoring
reg [7:0] signal_power_i, signal_power_q;
reg [7:0] signal_power_i, signal_power_q;
// Internal mixing signals
// Pipeline: NCO fabric reg (1) + DSP48E1 AREG/BREG (1) + MREG (1) + PREG (1) + retiming (1) = 5 cycles
// The NCO fabric pipeline register was added to break the long NCODSP B-port route
@@ -118,61 +78,112 @@ reg [4:0] dsp_valid_pipe;
// Post-DSP retiming registers breaks DSP48E1 CLKP to fabric timing path
// This extra pipeline stage absorbs the 1.866ns DSP output prop delay + routing,
// ensuring WNS > 0 at 400 MHz regardless of placement seed
(* DONT_TOUCH = "TRUE" *) reg signed [MIXER_WIDTH+NCO_WIDTH-1:0] mult_i_retimed, mult_q_retimed;
// Output stage registers
reg signed [17:0] baseband_i_reg, baseband_q_reg;
reg baseband_valid_reg;
// ============================================================================
(* DONT_TOUCH = "TRUE" *) reg signed [MIXER_WIDTH+NCO_WIDTH-1:0] mult_i_retimed, mult_q_retimed;
// Output stage registers
reg signed [17:0] baseband_i_reg, baseband_q_reg;
reg baseband_valid_reg;
// ============================================================================
// Phase Dithering Signals
// ============================================================================
wire [7:0] phase_dither_bits;
reg [31:0] phase_inc_dithered;
// ============================================================================
// Debug Signal Assignments
// ============================================================================
assign debug_internal_i = mixed_i[25:8];
assign debug_internal_q = mixed_q[25:8];
assign debug_sample_count = sample_counter;
assign debug_mixed_i_trunc = mixed_i[25:8];
assign debug_mixed_q_trunc = mixed_q[25:8];
// ============================================================================
// Clock Domain Crossing for Control Signals (2-stage synchronizers)
reg [31:0] phase_inc_dithered;
// ============================================================================
// Debug Signal Assignments
// ============================================================================
assign debug_internal_i = mixed_i[25:8];
assign debug_internal_q = mixed_q[25:8];
assign debug_sample_count = sample_counter;
assign debug_mixed_i_trunc = mixed_i[25:8];
assign debug_mixed_q_trunc = mixed_q[25:8];
// ============================================================================
// 400 MHz Reset Synchronizer
//
// reset_n arrives from the 100 MHz domain (sys_reset_n from radar_system_top).
// Using it directly as an async reset in the 400 MHz domain causes the reset
// deassertion edge to violate timing: the 100 MHz flip-flop driving reset_n
// has its output fanning out to 1156 registers across the FPGA in the 400 MHz
// domain, requiring 18.243ns of routing (WNS = -18.081ns).
//
// Solution: 2-stage async-assert, sync-deassert reset synchronizer in the
// 400 MHz domain. Reset assertion is immediate (asynchronous combinatorial
// path from reset_n to all 400 MHz registers). Reset deassertion is
//
// reset_400m : ACTIVE-HIGH registered reset with (* max_fanout = 50 *).
// This is THE signal fed to every synchronous 400 MHz FDRE
// and every DSP48E1 RST pin in this module and its children
// (NCO, CIC, LFSR). Vivado replicates the register (~14
// copies) so each replica drives 50 loads regionally,
// eliminating the single-LUT1 / 702-load net that caused
// WNS=-0.626 ns in Build N.
//
// System-level invariants preserved:
// I1 Reset assertion propagates to all 400 MHz regs within 3 clk edges
// (2 sync + 1 replicated-reg fanout). At 400 MHz = 7.5 ns << any
// system-level reset assertion duration.
// I2 Reset de-assertion is always synchronous to clk_400m (via
// reset_sync_400m), never glitches.
// I3 DSP48E1 RST pins are all fed from Q of a register glitch-free.
// I4 No new CDC introduced: reset_400m is entirely in clk_400m domain.
// I5 Power-up: reset_n is asserted externally and mmcm_locked is low;
// reset_sync_400m stays 2'b00, reset_400m stays 1'b1, downstream
// FDREs stay cleared. Safe.
// ============================================================================
(* ASYNC_REG = "TRUE" *) reg [1:0] reset_sync_400m = 2'b00;
(* max_fanout = 50 *) wire reset_n_400m = reset_sync_400m[1];
// Active-high replicated reset for all synchronous 400 MHz consumers
(* max_fanout = 50 *) reg reset_400m = 1'b1;
always @(posedge clk_400m or negedge reset_n) begin
if (!reset_n) begin
reset_sync_400m <= 2'b00;
reset_400m <= 1'b1;
end else begin
reset_sync_400m <= {reset_sync_400m[0], 1'b1};
reset_400m <= ~reset_sync_400m[1];
end
end
// CDC synchronization for control signals (2-stage synchronizers)
(* ASYNC_REG = "TRUE" *) reg [1:0] mixers_enable_sync_chain;
(* ASYNC_REG = "TRUE" *) reg [1:0] force_saturation_sync_chain;
wire mixers_enable_sync;
wire force_saturation_sync;
assign mixers_enable_sync = mixers_enable_sync_chain[1];
assign force_saturation_sync = force_saturation_sync_chain[1];
always @(posedge clk_400m or negedge reset_n_400m) begin
if (!reset_n_400m) begin
// Sync reset via reset_400m (replicated, max_fanout=50). Was async on
// reset_n_400m see "400 MHz RESET DISTRIBUTION" comment above.
always @(posedge clk_400m) begin
if (reset_400m) begin
mixers_enable_sync_chain <= 2'b00;
force_saturation_sync_chain <= 2'b00;
end else begin
mixers_enable_sync_chain <= {mixers_enable_sync_chain[0], mixers_enable};
force_saturation_sync_chain <= {force_saturation_sync_chain[0], force_saturation};
end
end
// ============================================================================
// Sample Counter and Debug Monitoring
// ============================================================================
always @(posedge clk_400m or negedge reset_n_400m) begin
if (!reset_n_400m || reset_monitors) begin
end
// ============================================================================
// Sample Counter and Debug Monitoring
// ============================================================================
always @(posedge clk_400m) begin
if (reset_400m || reset_monitors) begin
sample_counter <= 0;
error_counter <= 0;
end else if (adc_data_valid_i && adc_data_valid_q ) begin
sample_counter <= sample_counter + 1;
end
end
// ============================================================================
// Enhanced Phase Dithering Instance
// ============================================================================
error_counter <= 0;
end else if (adc_data_valid_i && adc_data_valid_q ) begin
sample_counter <= sample_counter + 1;
end
end
// ============================================================================
// Enhanced Phase Dithering Instance
// ============================================================================
lfsr_dither_enhanced #(
.DITHER_WIDTH(8)
) phase_dither_gen (
@@ -180,36 +191,36 @@ lfsr_dither_enhanced #(
.reset_n(reset_n_400m),
.enable(nco_ready),
.dither_out(phase_dither_bits)
);
// ============================================================================
// Phase Increment Calculation with Dithering
// ============================================================================
// Calculate phase increment for 120MHz IF at 400MHz sampling
localparam PHASE_INC_120MHZ = 32'h4CCCCCCD;
);
// ============================================================================
// Phase Increment Calculation with Dithering
// ============================================================================
// Calculate phase increment for 120MHz IF at 400MHz sampling
localparam PHASE_INC_120MHZ = 32'h4CCCCCCD;
// Apply dithering to reduce spurious tones (registered for 400 MHz timing)
always @(posedge clk_400m or negedge reset_n_400m) begin
if (!reset_n_400m)
always @(posedge clk_400m) begin
if (reset_400m)
phase_inc_dithered <= PHASE_INC_120MHZ;
else
phase_inc_dithered <= PHASE_INC_120MHZ + {24'b0, phase_dither_bits};
end
// ============================================================================
// Enhanced NCO with Diagnostics
// ============================================================================
end
// ============================================================================
// Enhanced NCO with Diagnostics
// ============================================================================
nco_400m_enhanced nco_core (
.clk_400m(clk_400m),
.reset_n(reset_n_400m),
.frequency_tuning_word(phase_inc_dithered),
.phase_valid(mixers_enable),
.phase_offset(16'h0000),
.sin_out(sin_out),
.cos_out(cos_out),
.dds_ready(nco_ready)
);
.reset_n(reset_n_400m),
.frequency_tuning_word(phase_inc_dithered),
.phase_valid(mixers_enable),
.phase_offset(16'h0000),
.sin_out(sin_out),
.cos_out(cos_out),
.dds_ready(nco_ready)
);
// ============================================================================
// Enhanced Mixing Stage DSP48E1 direct instantiation for 400 MHz timing
//
@@ -229,8 +240,8 @@ assign adc_signed_w = {1'b0, adc_data, {(MIXER_WIDTH-ADC_WIDTH-1){1'b0}}} -
{1'b0, {ADC_WIDTH{1'b1}}, {(MIXER_WIDTH-ADC_WIDTH-1){1'b0}}} / 2;
// Valid pipeline: 5-stage shift register (1 NCO pipe + 3 DSP48E1 AREG+MREG+PREG + 1 retiming)
always @(posedge clk_400m or negedge reset_n_400m) begin
if (!reset_n_400m) begin
always @(posedge clk_400m) begin
if (reset_400m) begin
dsp_valid_pipe <= 5'b00000;
end else begin
dsp_valid_pipe <= {dsp_valid_pipe[3:0], (nco_ready && adc_data_valid_i && adc_data_valid_q)};
@@ -246,8 +257,8 @@ reg signed [MIXER_WIDTH+NCO_WIDTH-1:0] mult_i_internal, mult_q_internal; // Mod
reg signed [MIXER_WIDTH+NCO_WIDTH-1:0] mult_i_reg, mult_q_reg; // Models PREG
// Stage 0: NCO pipeline — breaks long NCO→DSP route (matches synthesis fabric registers)
always @(posedge clk_400m or negedge reset_n_400m) begin
if (!reset_n_400m) begin
always @(posedge clk_400m) begin
if (reset_400m) begin
cos_nco_pipe <= 0;
sin_nco_pipe <= 0;
end else begin
@@ -257,8 +268,8 @@ always @(posedge clk_400m or negedge reset_n_400m) begin
end
// Stage 1: AREG/BREG equivalent (uses pipelined NCO outputs)
always @(posedge clk_400m or negedge reset_n_400m) begin
if (!reset_n_400m) begin
always @(posedge clk_400m) begin
if (reset_400m) begin
adc_signed_reg <= 0;
cos_pipe_reg <= 0;
sin_pipe_reg <= 0;
@@ -270,8 +281,8 @@ always @(posedge clk_400m or negedge reset_n_400m) begin
end
// Stage 2: MREG equivalent
always @(posedge clk_400m or negedge reset_n_400m) begin
if (!reset_n_400m) begin
always @(posedge clk_400m) begin
if (reset_400m) begin
mult_i_internal <= 0;
mult_q_internal <= 0;
end else begin
@@ -281,8 +292,8 @@ always @(posedge clk_400m or negedge reset_n_400m) begin
end
// Stage 3: PREG equivalent
always @(posedge clk_400m or negedge reset_n_400m) begin
if (!reset_n_400m) begin
always @(posedge clk_400m) begin
if (reset_400m) begin
mult_i_reg <= 0;
mult_q_reg <= 0;
end else begin
@@ -292,8 +303,8 @@ always @(posedge clk_400m or negedge reset_n_400m) begin
end
// Stage 4: Post-DSP retiming register (matches synthesis path)
always @(posedge clk_400m or negedge reset_n_400m) begin
if (!reset_n_400m) begin
always @(posedge clk_400m) begin
if (reset_400m) begin
mult_i_retimed <= 0;
mult_q_retimed <= 0;
end else begin
@@ -311,8 +322,8 @@ wire [47:0] dsp_p_i, dsp_p_q;
// (1.505ns routing observed in Build 26). These fabric registers are placed
// near the DSP by the placer, splitting the route into two shorter segments.
// DONT_TOUCH on the reg declaration (above) prevents absorption/retiming.
always @(posedge clk_400m or negedge reset_n_400m) begin
if (!reset_n_400m) begin
always @(posedge clk_400m) begin
if (reset_400m) begin
cos_nco_pipe <= 0;
sin_nco_pipe <= 0;
end else begin
@@ -329,11 +340,10 @@ DSP48E1 #(
.USE_DPORT("FALSE"),
.USE_MULT("MULTIPLY"),
.USE_SIMD("ONE48"),
// Pipeline register attributes all enabled for max timing
.AREG(1),
.BREG(1),
.MREG(1),
.PREG(1), // P register enabled absorbs CLKP delay for timing closure
.PREG(1),
.ADREG(0),
.ACASCREG(1),
.BCASCREG(1),
@@ -344,7 +354,6 @@ DSP48E1 #(
.DREG(0),
.INMODEREG(0),
.OPMODEREG(0),
// Pattern detector (unused)
.AUTORESET_PATDET("NO_RESET"),
.MASK(48'h3fffffffffff),
.PATTERN(48'h000000000000),
@@ -496,8 +505,8 @@ wire signed [MIXER_WIDTH+NCO_WIDTH-1:0] mult_q_reg = dsp_p_q[MIXER_WIDTH+NCO_WID
// Stage 4: Post-DSP retiming register breaks DSP48E1 CLKP to fabric path
// Without this, the DSP output prop delay (1.866ns) + routing (0.515ns) exceeds
// the 2.500ns clock period at slow process corner
always @(posedge clk_400m or negedge reset_n_400m) begin
if (!reset_n_400m) begin
always @(posedge clk_400m) begin
if (reset_400m) begin
mult_i_retimed <= 0;
mult_q_retimed <= 0;
end else begin
@@ -513,8 +522,8 @@ end
// force_saturation mux is intentionally AFTER the DSP48E1 output to avoid
// polluting the critical input path with extra logic
// ============================================================================
always @(posedge clk_400m or negedge reset_n_400m) begin
if (!reset_n_400m) begin
always @(posedge clk_400m) begin
if (reset_400m) begin
mixed_i <= 0;
mixed_q <= 0;
mixed_valid <= 0;
@@ -556,31 +565,31 @@ always @(posedge clk_400m or negedge reset_n_400m) begin
mixer_overflow_q <= 0;
overflow_detected <= 1'b0;
end
end
// ============================================================================
// Enhanced CIC Decimators
// ============================================================================
wire cic_valid_i, cic_valid_q;
end
// ============================================================================
// Enhanced CIC Decimators
// ============================================================================
wire cic_valid_i, cic_valid_q;
cic_decimator_4x_enhanced cic_i_inst (
.clk(clk_400m),
.reset_n(reset_n_400m),
.data_in(mixed_i[33:16]),
.data_valid(mixed_valid),
.data_out(cic_i_out),
.data_out_valid(cic_valid_i)
);
.reset_n(reset_n_400m),
.data_in(mixed_i[33:16]),
.data_valid(mixed_valid),
.data_out(cic_i_out),
.data_out_valid(cic_valid_i)
);
cic_decimator_4x_enhanced cic_q_inst (
.clk(clk_400m),
.reset_n(reset_n_400m),
.data_in(mixed_q[33:16]),
.data_valid(mixed_valid),
.data_out(cic_q_out),
.data_out_valid(cic_valid_q)
);
.reset_n(reset_n_400m),
.data_in(mixed_q[33:16]),
.data_valid(mixed_valid),
.data_out(cic_q_out),
.data_out_valid(cic_valid_q)
);
assign cic_valid = cic_valid_i & cic_valid_q;
// ============================================================================
@@ -593,96 +602,96 @@ wire fir_valid_i, fir_valid_q;
wire fir_i_ready, fir_q_ready;
wire [17:0] fir_d_in_i, fir_d_in_q;
cdc_adc_to_processing #(
.WIDTH(18),
.STAGES(3)
cdc_adc_to_processing #(
.WIDTH(18),
.STAGES(3)
)CDC_FIR_i(
.src_clk(clk_400m),
.dst_clk(clk_100m),
.src_reset_n(reset_n_400m),
.dst_reset_n(reset_n),
.src_data(cic_i_out),
.src_valid(cic_valid_i),
.dst_data(fir_d_in_i),
.dst_valid(fir_in_valid_i)
.dst_reset_n(reset_n),
.src_data(cic_i_out),
.src_valid(cic_valid_i),
.dst_data(fir_d_in_i),
.dst_valid(fir_in_valid_i)
);
cdc_adc_to_processing #(
.WIDTH(18),
.STAGES(3)
cdc_adc_to_processing #(
.WIDTH(18),
.STAGES(3)
)CDC_FIR_q(
.src_clk(clk_400m),
.dst_clk(clk_100m),
.src_reset_n(reset_n_400m),
.dst_reset_n(reset_n),
.src_data(cic_q_out),
.src_valid(cic_valid_q),
.dst_data(fir_d_in_q),
.dst_valid(fir_in_valid_q)
);
.dst_reset_n(reset_n),
.src_data(cic_q_out),
.src_valid(cic_valid_q),
.dst_data(fir_d_in_q),
.dst_valid(fir_in_valid_q)
);
// ============================================================================
// FIR Filter Instances
// ============================================================================
// FIR I channel
fir_lowpass_parallel_enhanced fir_i_inst (
.clk(clk_100m),
.reset_n(reset_n),
.data_in(fir_d_in_i), // Use synchronized data
.data_valid(fir_in_valid_i), // Use synchronized valid
.data_out(fir_i_out),
.data_out_valid(fir_valid_i),
.fir_ready(fir_i_ready),
.filter_overflow()
);
// FIR Q channel
fir_lowpass_parallel_enhanced fir_q_inst (
.clk(clk_100m),
.reset_n(reset_n),
.data_in(fir_d_in_q), // Use synchronized data
.data_valid(fir_in_valid_q), // Use synchronized valid
.data_out(fir_q_out),
.data_out_valid(fir_valid_q),
.fir_ready(fir_q_ready),
.filter_overflow()
);
assign fir_valid = fir_valid_i & fir_valid_q;
// ============================================================================
// Enhanced Output Stage
// ============================================================================
always @(posedge clk_100m or negedge reset_n) begin
if (!reset_n) begin
baseband_i_reg <= 0;
baseband_q_reg <= 0;
baseband_valid_reg <= 0;
end else if (fir_valid) begin
baseband_i_reg <= fir_i_out;
baseband_q_reg <= fir_q_out;
baseband_valid_reg <= 1;
end else begin
baseband_valid_reg <= 0;
end
end
// ============================================================================
// Output Assignments
// ============================================================================
assign baseband_i = baseband_i_reg;
assign baseband_q = baseband_q_reg;
// FIR I channel
fir_lowpass_parallel_enhanced fir_i_inst (
.clk(clk_100m),
.reset_n(reset_n),
.data_in(fir_d_in_i), // Use synchronized data
.data_valid(fir_in_valid_i), // Use synchronized valid
.data_out(fir_i_out),
.data_out_valid(fir_valid_i),
.fir_ready(fir_i_ready),
.filter_overflow()
);
// FIR Q channel
fir_lowpass_parallel_enhanced fir_q_inst (
.clk(clk_100m),
.reset_n(reset_n),
.data_in(fir_d_in_q), // Use synchronized data
.data_valid(fir_in_valid_q), // Use synchronized valid
.data_out(fir_q_out),
.data_out_valid(fir_valid_q),
.fir_ready(fir_q_ready),
.filter_overflow()
);
assign fir_valid = fir_valid_i & fir_valid_q;
// ============================================================================
// Enhanced Output Stage
// ============================================================================
always @(posedge clk_100m or negedge reset_n) begin
if (!reset_n) begin
baseband_i_reg <= 0;
baseband_q_reg <= 0;
baseband_valid_reg <= 0;
end else if (fir_valid) begin
baseband_i_reg <= fir_i_out;
baseband_q_reg <= fir_q_out;
baseband_valid_reg <= 1;
end else begin
baseband_valid_reg <= 0;
end
end
// ============================================================================
// Output Assignments
// ============================================================================
assign baseband_i = baseband_i_reg;
assign baseband_q = baseband_q_reg;
assign baseband_valid_i = baseband_valid_reg;
assign baseband_valid_q = baseband_valid_reg;
assign ddc_status = {mixer_overflow_i | mixer_overflow_q, nco_ready};
assign mixer_saturation = overflow_detected;
assign ddc_diagnostics = {saturation_count, error_counter[4:0]};
// ============================================================================
// Enhanced Debug and Monitoring
// ============================================================================
assign baseband_valid_q = baseband_valid_reg;
assign ddc_status = {mixer_overflow_i | mixer_overflow_q, nco_ready};
assign mixer_saturation = overflow_detected;
assign ddc_diagnostics = {saturation_count, error_counter[4:0]};
// ============================================================================
// Enhanced Debug and Monitoring
// ============================================================================
reg [31:0] debug_cic_count, debug_fir_count, debug_bb_count;
`ifdef SIMULATION
@@ -699,10 +708,10 @@ always @(posedge clk_100m) begin
baseband_i, baseband_q, debug_bb_count);
end
end
`endif
// In ddc_400m.v, add these debug signals:
`endif
// In ddc_400m.v, add these debug signals:
// Debug monitoring (simulation only)
`ifdef SIMULATION
reg [31:0] debug_adc_count = 0;
@@ -723,58 +732,67 @@ always @(posedge clk_100m) begin
baseband_i, baseband_q, debug_baseband_count, $time);
end
end
`endif
endmodule
// ============================================================================
// Enhanced Phase Dithering Module
// ============================================================================
`timescale 1ns / 1ps
module lfsr_dither_enhanced #(
parameter DITHER_WIDTH = 8 // Increased for better dithering
)(
input wire clk,
input wire reset_n,
input wire enable,
output wire [DITHER_WIDTH-1:0] dither_out
);
reg [DITHER_WIDTH-1:0] lfsr_reg;
reg [15:0] cycle_counter;
reg lock_detected;
// Polynomial for better randomness: x^8 + x^6 + x^5 + x^4 + 1
wire feedback;
generate
if (DITHER_WIDTH == 4) begin
assign feedback = lfsr_reg[3] ^ lfsr_reg[2];
end else if (DITHER_WIDTH == 8) begin
assign feedback = lfsr_reg[7] ^ lfsr_reg[5] ^ lfsr_reg[4] ^ lfsr_reg[3];
end else begin
assign feedback = lfsr_reg[DITHER_WIDTH-1] ^ lfsr_reg[DITHER_WIDTH-2];
end
endgenerate
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
lfsr_reg <= {DITHER_WIDTH{1'b1}}; // Non-zero initial state
cycle_counter <= 0;
lock_detected <= 0;
end else if (enable) begin
lfsr_reg <= {lfsr_reg[DITHER_WIDTH-2:0], feedback};
cycle_counter <= cycle_counter + 1;
// Detect LFSR lock after sufficient cycles
if (cycle_counter > (2**DITHER_WIDTH * 8)) begin
lock_detected <= 1'b1;
end
end
end
assign dither_out = lfsr_reg;
endmodule
`endif
endmodule
// ============================================================================
// Enhanced Phase Dithering Module
// ============================================================================
`timescale 1ns / 1ps
module lfsr_dither_enhanced #(
parameter DITHER_WIDTH = 8 // Increased for better dithering
)(
input wire clk,
input wire reset_n,
input wire enable,
output wire [DITHER_WIDTH-1:0] dither_out
);
reg [DITHER_WIDTH-1:0] lfsr_reg;
reg [15:0] cycle_counter;
reg lock_detected;
// Polynomial for better randomness: x^8 + x^6 + x^5 + x^4 + 1
wire feedback;
generate
if (DITHER_WIDTH == 4) begin
assign feedback = lfsr_reg[3] ^ lfsr_reg[2];
end else if (DITHER_WIDTH == 8) begin
assign feedback = lfsr_reg[7] ^ lfsr_reg[5] ^ lfsr_reg[4] ^ lfsr_reg[3];
end else begin
assign feedback = lfsr_reg[DITHER_WIDTH-1] ^ lfsr_reg[DITHER_WIDTH-2];
end
endgenerate
// ============================================================================
// RESET FAN-OUT INVARIANT: registered active-high reset with max_fanout=50.
// See cic_decimator_4x_enhanced.v for full reasoning. reset_n here is driven
// by the parent DDC's reset_n_400m (already synchronized to clk_400m), so
// sync reset on the LFSR is safe. INIT=1'b1 holds LFSR in reset on power-up.
// ============================================================================
(* max_fanout = 50 *) reg reset_h = 1'b1;
always @(posedge clk) reset_h <= ~reset_n;
always @(posedge clk) begin
if (reset_h) begin
lfsr_reg <= {DITHER_WIDTH{1'b1}}; // Non-zero initial state
cycle_counter <= 0;
lock_detected <= 0;
end else if (enable) begin
lfsr_reg <= {lfsr_reg[DITHER_WIDTH-2:0], feedback};
cycle_counter <= cycle_counter + 1;
// Detect LFSR lock after sufficient cycles
if (cycle_counter > (2**DITHER_WIDTH * 8)) begin
lock_detected <= 1'b1;
end
end
end
assign dither_out = lfsr_reg;
endmodule
+35 -16
View File
@@ -59,6 +59,25 @@ reg [1:0] quadrant_reg2; // Pass-through for Stage 5 MUX
// Valid pipeline: tracks 6-stage latency
reg [5:0] valid_pipe;
// ============================================================================
// RESET FAN-OUT INVARIANT (Build N+1 fix for WNS=-0.626ns at 400 MHz):
// ============================================================================
// reset_h is an ACTIVE-HIGH, REGISTERED copy of ~reset_n with (* max_fanout=50 *).
// Vivado replicates this register (14+ copies) so each copy drives 50 loads
// regionally, avoiding the single-LUT1 / 702-load net that caused timing
// failure in Build N. It feeds:
// - DSP48E1 RSTP/RSTC on the phase-accumulator DSP (below)
// - All pipeline-stage fabric FDREs (synchronous reset)
// Invariants (see cic_decimator_4x_enhanced.v for full reasoning):
// I1 correctness: reset_h == ~reset_n one cycle later
// I2 glitch-free: registered output
// I3 power-up safe: INIT=1'b1 holds all downstream in reset until first
// valid clock edge; reset_n is low on power-up anyway
// I4 de-assert lat.: +1 cycle vs. direct async; negligible at 400 MHz
// ============================================================================
(* max_fanout = 50 *) reg reset_h = 1'b1;
always @(posedge clk_400m) reset_h <= ~reset_n;
// Use only the top 8 bits for LUT addressing (256-entry LUT equivalent)
wire [7:0] lut_address = phase_with_offset[31:24];
@@ -135,8 +154,8 @@ wire [15:0] cos_abs_w = sin_lut[63 - lut_index_pipe_cos];
// Stage 2: phase_with_offset adds phase offset
reg [31:0] phase_accumulator;
always @(posedge clk_400m or negedge reset_n) begin
if (!reset_n) begin
always @(posedge clk_400m) begin
if (reset_h) begin
phase_accumulator <= 32'h00000000;
phase_accum_reg <= 32'h00000000;
phase_with_offset <= 32'h00000000;
@@ -190,8 +209,8 @@ DSP48E1 #(
.RSTA(1'b0),
.RSTB(1'b0),
.RSTM(1'b0),
.RSTP(!reset_n), // Reset P register (phase accumulator) on !reset_n
.RSTC(!reset_n), // Reset C register (tuning word) on !reset_n
.RSTP(reset_h), // Reset P register (phase accumulator) — registered, max_fanout=50
.RSTC(reset_h), // Reset C register (tuning word) — registered, max_fanout=50
.RSTALLCARRYIN(1'b0),
.RSTALUMODE(1'b0),
.RSTCTRL(1'b0),
@@ -245,8 +264,8 @@ DSP48E1 #(
// Stage 1: Capture DSP48E1 P output into fabric register
// Stage 2: Add phase offset to captured value
// Split into two registered stages to break DSP48E1.PCARRY4 critical path
always @(posedge clk_400m or negedge reset_n) begin
if (!reset_n) begin
always @(posedge clk_400m) begin
if (reset_h) begin
phase_accum_reg <= 32'h00000000;
phase_with_offset <= 32'h00000000;
end else if (phase_valid) begin
@@ -264,8 +283,8 @@ end
// Only 2 registers driven (lut_index_pipe + quadrant_pipe)
// Minimal fanout short routes easy timing
// ============================================================================
always @(posedge clk_400m or negedge reset_n) begin
if (!reset_n) begin
always @(posedge clk_400m) begin
if (reset_h) begin
lut_index_pipe_sin <= 6'b000000;
lut_index_pipe_cos <= 6'b000000;
quadrant_pipe <= 2'b00;
@@ -281,8 +300,8 @@ end
// Registered address combinational LUT6 read register
// Only 1 logic level (LUT6), trivial timing
// ============================================================================
always @(posedge clk_400m or negedge reset_n) begin
if (!reset_n) begin
always @(posedge clk_400m) begin
if (reset_h) begin
sin_abs_reg <= 16'h0000;
cos_abs_reg <= 16'h7FFF;
quadrant_reg <= 2'b00;
@@ -298,8 +317,8 @@ end
// CARRY4 x4 chain has registered inputs easily fits in 2.5ns
// Also pass through abs values and quadrant for Stage 5
// ============================================================================
always @(posedge clk_400m or negedge reset_n) begin
if (!reset_n) begin
always @(posedge clk_400m) begin
if (reset_h) begin
sin_neg_reg <= 16'h0000;
cos_neg_reg <= -16'h7FFF;
sin_abs_reg2 <= 16'h0000;
@@ -318,8 +337,8 @@ end
// Stage 5: Quadrant sign application final sin/cos output
// Uses pre-computed negated values from Stage 4 pure MUX, no arithmetic
// ============================================================================
always @(posedge clk_400m or negedge reset_n) begin
if (!reset_n) begin
always @(posedge clk_400m) begin
if (reset_h) begin
sin_out <= 16'h0000;
cos_out <= 16'h7FFF;
end else if (valid_pipe[4]) begin
@@ -347,8 +366,8 @@ end
// ============================================================================
// Valid pipeline and dds_ready (6-stage latency)
// ============================================================================
always @(posedge clk_400m or negedge reset_n) begin
if (!reset_n) begin
always @(posedge clk_400m) begin
if (reset_h) begin
valid_pipe <= 6'b000000;
dds_ready <= 1'b0;
end else begin
+36 -36
View File
@@ -142,7 +142,7 @@ module radar_system_top (
parameter USE_LONG_CHIRP = 1'b1; // Default to long chirp
parameter DOPPLER_ENABLE = 1'b1; // Enable Doppler processing
parameter USB_ENABLE = 1'b1; // Enable USB data transfer
parameter USB_MODE = 0; // 0=FT601 (32-bit, 200T), 1=FT2232H (8-bit, 50T)
parameter USB_MODE = 1; // 0=FT601 (32-bit, 200T), 1=FT2232H (8-bit, 50T) default: FT2232H production board
// ============================================================================
// INTERNAL SIGNALS
@@ -243,12 +243,12 @@ reg [5:0] host_chirps_per_elev; // Opcode 0x15 (default 32)
reg host_status_request; // Opcode 0xFF (self-clearing pulse)
// Fix 4: Doppler/chirps mismatch protection
// DOPPLER_FRAME_CHIRPS is the fixed chirp count expected by the staggered-PRI
// Doppler path (16 long + 16 short). If host sets chirps_per_elev to a
// different value, Doppler accumulation is corrupted. Clamp at command decode
// and flag the mismatch so the host knows.
localparam DOPPLER_FRAME_CHIRPS = 32; // Total chirps per Doppler frame
reg chirps_mismatch_error; // Set if host tried to set chirps != FFT size
// DOPPLER_FRAME_CHIRPS is the fixed chirp count expected by the staggered-PRI
// Doppler path (16 long + 16 short). If host sets chirps_per_elev to a
// different value, Doppler accumulation is corrupted. Clamp at command decode
// and flag the mismatch so the host knows.
localparam DOPPLER_FRAME_CHIRPS = 32; // Total chirps per Doppler frame
reg chirps_mismatch_error; // Set if host tried to set chirps != FFT size
// Fix 7: Range-mode register (opcode 0x20)
// Future-proofing for 3km/10km antenna switching.
@@ -578,21 +578,21 @@ assign rx_doppler_data_valid = rx_doppler_valid;
// ============================================================================
// DC NOTCH FILTER (post-Doppler-FFT, pre-CFAR)
// ============================================================================
// Zeros out Doppler bins within ±host_dc_notch_width of DC for BOTH
// sub-frames in the dual 16-pt FFT architecture.
// doppler_bin[4:0] = {sub_frame, bin[3:0]}:
// Sub-frame 0: bins 0-15, DC = bin 0, wrap = bin 15
// Sub-frame 1: bins 16-31, DC = bin 16, wrap = bin 31
// notch_width=1 → zero bins {0,16}. notch_width=2 → zero bins
// {0,1,15,16,17,31}. etc.
// When host_dc_notch_width=0: pass-through (no zeroing).
wire dc_notch_active;
wire [4:0] dop_bin_unsigned = rx_doppler_bin;
wire [3:0] bin_within_sf = dop_bin_unsigned[3:0];
assign dc_notch_active = (host_dc_notch_width != 3'd0) &&
(bin_within_sf < {1'b0, host_dc_notch_width} ||
bin_within_sf > (4'd15 - {1'b0, host_dc_notch_width} + 4'd1));
// Zeros out Doppler bins within ±host_dc_notch_width of DC for BOTH
// sub-frames in the dual 16-pt FFT architecture.
// doppler_bin[4:0] = {sub_frame, bin[3:0]}:
// Sub-frame 0: bins 0-15, DC = bin 0, wrap = bin 15
// Sub-frame 1: bins 16-31, DC = bin 16, wrap = bin 31
// notch_width=1 → zero bins {0,16}. notch_width=2 → zero bins
// {0,1,15,16,17,31}. etc.
// When host_dc_notch_width=0: pass-through (no zeroing).
wire dc_notch_active;
wire [4:0] dop_bin_unsigned = rx_doppler_bin;
wire [3:0] bin_within_sf = dop_bin_unsigned[3:0];
assign dc_notch_active = (host_dc_notch_width != 3'd0) &&
(bin_within_sf < {1'b0, host_dc_notch_width} ||
bin_within_sf > (4'd15 - {1'b0, host_dc_notch_width} + 4'd1));
// Notched Doppler data: zero I/Q when in notch zone, pass through otherwise
wire [31:0] notched_doppler_data = dc_notch_active ? 32'd0 : rx_doppler_output;
@@ -959,19 +959,19 @@ always @(posedge clk_100m_buf or negedge sys_reset_n) begin
8'h13: host_short_chirp_cycles <= usb_cmd_value;
8'h14: host_short_listen_cycles <= usb_cmd_value;
8'h15: begin
// Fix 4: Clamp chirps_per_elev to the fixed Doppler frame size.
// If host requests a different value, clamp and set error flag.
if (usb_cmd_value[5:0] > DOPPLER_FRAME_CHIRPS[5:0]) begin
host_chirps_per_elev <= DOPPLER_FRAME_CHIRPS[5:0];
chirps_mismatch_error <= 1'b1;
end else if (usb_cmd_value[5:0] == 6'd0) begin
host_chirps_per_elev <= DOPPLER_FRAME_CHIRPS[5:0];
chirps_mismatch_error <= 1'b1;
end else begin
host_chirps_per_elev <= usb_cmd_value[5:0];
// Clear error only if value matches FFT size exactly
chirps_mismatch_error <= (usb_cmd_value[5:0] != DOPPLER_FRAME_CHIRPS[5:0]);
end
// Fix 4: Clamp chirps_per_elev to the fixed Doppler frame size.
// If host requests a different value, clamp and set error flag.
if (usb_cmd_value[5:0] > DOPPLER_FRAME_CHIRPS[5:0]) begin
host_chirps_per_elev <= DOPPLER_FRAME_CHIRPS[5:0];
chirps_mismatch_error <= 1'b1;
end else if (usb_cmd_value[5:0] == 6'd0) begin
host_chirps_per_elev <= DOPPLER_FRAME_CHIRPS[5:0];
chirps_mismatch_error <= 1'b1;
end else begin
host_chirps_per_elev <= usb_cmd_value[5:0];
// Clear error only if value matches FFT size exactly
chirps_mismatch_error <= (usb_cmd_value[5:0] != DOPPLER_FRAME_CHIRPS[5:0]);
end
end
8'h16: host_gain_shift <= usb_cmd_value[3:0]; // Fix 3: digital gain
8'h20: host_range_mode <= usb_cmd_value[1:0]; // Fix 7: range mode
@@ -1075,4 +1075,4 @@ always @(posedge clk_100m_buf) begin
end
`endif
endmodule
endmodule
+5 -2
View File
@@ -70,6 +70,7 @@ PROD_RTL=(
xfft_16.v
fft_engine.v
usb_data_interface.v
usb_data_interface_ft2232h.v
edge_detector.v
radar_mode_controller.v
rx_gain_control.v
@@ -452,7 +453,8 @@ if [[ "$QUICK" -eq 0 ]]; then
chirp_memory_loader_param.v latency_buffer.v \
matched_filter_multi_segment.v matched_filter_processing_chain.v \
range_bin_decimator.v doppler_processor.v xfft_16.v fft_engine.v \
usb_data_interface.v edge_detector.v radar_mode_controller.v \
usb_data_interface.v usb_data_interface_ft2232h.v \
edge_detector.v radar_mode_controller.v \
rx_gain_control.v cfar_ca.v mti_canceller.v fpga_self_test.v
# E2E integration (46 strict checks: TX, RX, USB R/W, CDC, safety, reset)
@@ -466,7 +468,8 @@ if [[ "$QUICK" -eq 0 ]]; then
chirp_memory_loader_param.v latency_buffer.v \
matched_filter_multi_segment.v matched_filter_processing_chain.v \
range_bin_decimator.v doppler_processor.v xfft_16.v fft_engine.v \
usb_data_interface.v edge_detector.v radar_mode_controller.v \
usb_data_interface.v usb_data_interface_ft2232h.v \
edge_detector.v radar_mode_controller.v \
rx_gain_control.v cfar_ca.v mti_canceller.v fpga_self_test.v
else
echo " (skipped receiver golden + system top + E2E — use without --quick)"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -619,7 +619,7 @@ initial begin
// Optional: dump specific signals for debugging
$dumpvars(1, dut.tx_inst);
$dumpvars(1, dut.rx_inst);
$dumpvars(1, dut.gen_ft601.usb_inst);
$dumpvars(1, dut.gen_ft2232h.usb_inst);
end
endmodule