Gap 7 MMCM jitter cleaner + CIC comb CREG pipeline + XDC clock-name fix

MMCM (Gap 7):
- Add adc_clk_mmcm.v: MMCME2_ADV wrapper (VCO=800MHz, CLKOUT0=400MHz)
- Modify ad9484_interface_400m.v: replace BUFG with MMCM path, gate reset on mmcm_locked
- Add adc_clk_mmcm.xdc: CDC false paths for clk_mmcm_out0 <-> clk_100m

XDC Fix (Build 19 WNS=-0.011 root cause):
- Remove conflicting create_generated_clock -name clk_400m_mmcm
- Replace all clk_400m_mmcm references with Vivado auto-generated clk_mmcm_out0
- CDC false paths now correctly apply to actual timing paths

CIC CREG Pipeline (Build 18 critical path fix):
- Explicit DSP48E1 for comb[0] with CREG=1/AREG=1/BREG=1/PREG=1
- Absorbs integrator_sampled_comb fabric FDRE into DSP48 C-port register
- Eliminates 0.643ns fabric->DSP routing delay (Build 18 tightest path)
- +1 cycle comb latency via data_valid_comb_0_out pipeline
- Move shared register declarations above ifndef SIMULATION (iverilog fix)
- Update golden data for +1 cycle CIC pipeline shift

Build scripts: build19_mmcm.tcl, build20_mmcm_creg.tcl
Regression: 18/18 FPGA pass, 20/20 MCU pass
Build 20 launched on remote Vivado (pending results)
This commit is contained in:
Jason
2026-03-19 22:59:46 +02:00
parent f3bbf77ca1
commit c6103b37de
10 changed files with 9038 additions and 7438 deletions
+17 -6
View File
@@ -62,9 +62,16 @@ BUFIO bufio_dco (
.O(adc_dco_bufio) .O(adc_dco_bufio)
); );
BUFG bufg_dco ( // MMCME2 jitter-cleaning wrapper replaces the direct BUFG.
.I(adc_dco), // The PLL feedback loop attenuates input jitter from ~50 ps to ~20-30 ps,
.O(adc_dco_buffered) // reducing clock uncertainty and improving WNS on the 400 MHz CIC path.
wire mmcm_locked;
adc_clk_mmcm mmcm_inst (
.clk_in (adc_dco), // 400 MHz from IBUFDS output
.reset_n (reset_n),
.clk_400m_out (adc_dco_buffered), // Jitter-cleaned 400 MHz on BUFG
.mmcm_locked (mmcm_locked)
); );
assign adc_dco_bufg = adc_dco_buffered; assign adc_dco_bufg = adc_dco_buffered;
@@ -117,12 +124,16 @@ reg dco_phase;
// is asynchronous and safe the FFs enter reset instantly. De-assertion // is asynchronous and safe the FFs enter reset instantly. De-assertion
// (going high) must be synchronised to adc_dco_buffered to avoid // (going high) must be synchronised to adc_dco_buffered to avoid
// metastability. This is the classic "async assert, sync de-assert" pattern. // metastability. This is the classic "async assert, sync de-assert" pattern.
//
// mmcm_locked gates de-assertion: the 400 MHz domain stays in reset until
// the MMCM PLL has locked and the jitter-cleaned clock is stable.
(* ASYNC_REG = "TRUE" *) reg [1:0] reset_sync_400m; (* ASYNC_REG = "TRUE" *) reg [1:0] reset_sync_400m;
wire reset_n_400m; wire reset_n_400m;
wire reset_n_gated = reset_n & mmcm_locked;
always @(posedge adc_dco_buffered or negedge reset_n) begin always @(posedge adc_dco_buffered or negedge reset_n_gated) begin
if (!reset_n) if (!reset_n_gated)
reset_sync_400m <= 2'b00; // async assert reset_sync_400m <= 2'b00; // async assert (or MMCM not locked)
else else
reset_sync_400m <= {reset_sync_400m[0], 1'b1}; // sync de-assert reset_sync_400m <= {reset_sync_400m[0], 1'b1}; // sync de-assert
end end
+224
View File
@@ -0,0 +1,224 @@
`timescale 1ns / 1ps
// ============================================================================
// adc_clk_mmcm.v MMCME2 Jitter-Cleaning Wrapper for AD9484 400 MHz DCO
//
// PURPOSE:
// Replaces the direct BUFG on the ADC data clock output (adc_dco) with an
// MMCME2_ADV configured for 1:1 frequency (400 MHz in 400 MHz out) with
// jitter attenuation via the PLL feedback loop.
//
// CURRENT ARCHITECTURE (ad9484_interface_400m.v):
// adc_dco_p/n IBUFDS BUFIO (drives IDDR only, near-zero delay)
// BUFG (drives all fabric 400 MHz logic)
//
// NEW ARCHITECTURE (this module replaces the BUFG path):
// adc_dco_p/n IBUFDS BUFIO (unchanged drives IDDR only)
// MMCME2 CLKIN1 CLKOUT0 BUFG (fabric 400 MHz)
//
// BENEFITS:
// 1. Jitter attenuation: MMCM PLL loop filters input jitter from ~50 ps
// to ~20-30 ps output jitter, reducing clock uncertainty by ~20 ps.
// 2. Phase control: CLKOUT0_PHASE can fine-tune phase offset if needed.
// 3. Locked indicator: mmcm_locked output enables proper reset sequencing.
// 4. Expected WNS improvement: +20-40 ps on the 400 MHz CIC critical path.
//
// MMCM CONFIGURATION (Artix-7 XC7A200T-2):
// CLKIN1 = 400 MHz (from IBUFDS output)
// DIVCLK_DIVIDE = 1
// CLKFBOUT_MULT_F = 2.0 VCO = 400 * 2 = 800 MHz (range: 600-1200 MHz)
// CLKOUT0_DIVIDE_F = 2.0 CLKOUT0 = 800 / 2 = 400 MHz
// CLKFBOUT BUFG CLKFBIN (internal feedback for best jitter performance)
//
// INTEGRATION:
// This module is a DROP-IN replacement for the BUFG in ad9484_interface_400m.v.
// See adc_clk_mmcm_integration.md for step-by-step instructions.
//
// SIMULATION:
// Under `ifdef SIMULATION, this module passes the clock through a simple
// BUFG (no MMCM primitive), matching the current behavior for iverilog.
//
// TARGET: XC7A200T-2FBG484I (Artix-7, speed grade -2, industrial temp)
// ============================================================================
module adc_clk_mmcm (
// Input: single-ended clock from IBUFDS output
input wire clk_in, // 400 MHz from IBUFDS (adc_dco after IBUFDS)
// System reset (active-low, from 100 MHz domain)
input wire reset_n,
// Outputs
output wire clk_400m_out, // Jitter-cleaned 400 MHz on BUFG (fabric logic)
output wire mmcm_locked // 1 = MMCM PLL is locked and clock is stable
);
`ifdef SIMULATION
// ============================================================================
// SIMULATION PATH simple passthrough (no Xilinx primitives)
// ============================================================================
// iverilog and other simulators don't have MMCME2_ADV. Pass clock through
// with a locked signal that asserts after a brief delay matching real MMCM
// lock time (~10 us at 400 MHz = ~4000 cycles).
reg locked_sim;
reg [12:0] lock_counter;
initial begin
locked_sim = 1'b0;
lock_counter = 13'd0;
end
always @(posedge clk_in or negedge reset_n) begin
if (!reset_n) begin
locked_sim <= 1'b0;
lock_counter <= 13'd0;
end else begin
if (lock_counter < 13'd4096) begin
lock_counter <= lock_counter + 1;
end else begin
locked_sim <= 1'b1;
end
end
end
`ifdef SIMULATION_HAS_BUFG
// If the simulator supports BUFG (e.g., Vivado xsim)
BUFG bufg_sim (
.I(clk_in),
.O(clk_400m_out)
);
`else
// Pure behavioral iverilog
assign clk_400m_out = clk_in;
`endif
assign mmcm_locked = locked_sim;
`else
// ============================================================================
// SYNTHESIS PATH MMCME2_ADV with jitter-cleaning feedback loop
// ============================================================================
wire clk_mmcm_out0; // MMCM CLKOUT0 (unbuffered)
wire clk_mmcm_fb_out; // MMCM CLKFBOUT (unbuffered)
wire clk_mmcm_fb_bufg; // CLKFBOUT after BUFG (feedback)
wire mmcm_locked_int;
// ---- MMCME2_ADV Instance ----
// Configuration for 400 MHz 1:1 with jitter cleaning:
// VCO = CLKIN1 * CLKFBOUT_MULT_F / DIVCLK_DIVIDE = 400 * 2.0 / 1 = 800 MHz
// CLKOUT0 = VCO / CLKOUT0_DIVIDE_F = 800 / 2.0 = 400 MHz
// Bandwidth = "HIGH" for maximum jitter attenuation
MMCME2_ADV #(
// Input clock
.CLKIN1_PERIOD (2.500), // 400 MHz = 2.500 ns period
.CLKIN2_PERIOD (0.000), // Unused
.REF_JITTER1 (0.020), // 20 ps reference jitter (conservative)
.REF_JITTER2 (0.000), // Unused
// VCO configuration
.DIVCLK_DIVIDE (1), // Input divider = 1 (no division)
.CLKFBOUT_MULT_F (2.0), // Feedback multiplier VCO = 800 MHz
.CLKFBOUT_PHASE (0.0), // No feedback phase shift
// Output 0: 400 MHz fabric clock
.CLKOUT0_DIVIDE_F (2.0), // 800 / 2.0 = 400 MHz
.CLKOUT0_PHASE (0.0), // Phase-aligned with input
.CLKOUT0_DUTY_CYCLE (0.5), // 50% duty cycle
// Unused outputs disabled
.CLKOUT1_DIVIDE (1),
.CLKOUT1_PHASE (0.0),
.CLKOUT1_DUTY_CYCLE (0.5),
.CLKOUT2_DIVIDE (1),
.CLKOUT2_PHASE (0.0),
.CLKOUT2_DUTY_CYCLE (0.5),
.CLKOUT3_DIVIDE (1),
.CLKOUT3_PHASE (0.0),
.CLKOUT3_DUTY_CYCLE (0.5),
.CLKOUT4_DIVIDE (1),
.CLKOUT4_PHASE (0.0),
.CLKOUT4_DUTY_CYCLE (0.5),
.CLKOUT5_DIVIDE (1),
.CLKOUT5_PHASE (0.0),
.CLKOUT5_DUTY_CYCLE (0.5),
.CLKOUT6_DIVIDE (1),
.CLKOUT6_PHASE (0.0),
.CLKOUT6_DUTY_CYCLE (0.5),
// PLL filter bandwidth HIGH for maximum jitter attenuation
.BANDWIDTH ("HIGH"),
// Compensation mode BUFG on feedback path
.COMPENSATION ("BUF_IN"),
// Startup wait for configuration clock
.STARTUP_WAIT ("FALSE")
) mmcm_adc_400m (
// Clock inputs
.CLKIN1 (clk_in), // 400 MHz from IBUFDS
.CLKIN2 (1'b0), // Unused second input
.CLKINSEL (1'b1), // Select CLKIN1
// Feedback
.CLKFBOUT (clk_mmcm_fb_out), // Feedback output (unbuffered)
.CLKFBIN (clk_mmcm_fb_bufg), // Feedback input (from BUFG)
// Clock outputs
.CLKOUT0 (clk_mmcm_out0), // 400 MHz output (unbuffered)
.CLKOUT0B (), // Unused inverted
.CLKOUT1 (),
.CLKOUT1B (),
.CLKOUT2 (),
.CLKOUT2B (),
.CLKOUT3 (),
.CLKOUT3B (),
.CLKOUT4 (),
.CLKOUT5 (),
.CLKOUT6 (),
.CLKFBOUTB (), // Unused inverted feedback
// Control
.RST (~reset_n), // Active-high reset
.PWRDWN (1'b0), // Never power down
// Status
.LOCKED (mmcm_locked_int),
// Dynamic reconfiguration (unused tie off)
.DADDR (7'd0),
.DCLK (1'b0),
.DEN (1'b0),
.DI (16'd0),
.DWE (1'b0),
.DO (),
.DRDY (),
// Phase shift (unused tie off)
.PSCLK (1'b0),
.PSEN (1'b0),
.PSINCDEC (1'b0),
.PSDONE ()
);
// ---- Feedback BUFG ----
// Routes CLKFBOUT through a BUFG back to CLKFBIN.
// This is the standard "internal feedback" topology for best jitter performance.
// Vivado's clock network insertion delay is compensated by the MMCM feedback loop.
BUFG bufg_feedback (
.I(clk_mmcm_fb_out),
.O(clk_mmcm_fb_bufg)
);
// ---- Output BUFG ----
// Routes the jitter-cleaned 400 MHz CLKOUT0 onto a global clock network.
BUFG bufg_clk400m (
.I(clk_mmcm_out0),
.O(clk_400m_out)
);
assign mmcm_locked = mmcm_locked_int;
`endif
endmodule
@@ -0,0 +1,164 @@
# ADC Clock MMCM Integration Guide
## Overview
`adc_clk_mmcm.v` is a drop-in MMCME2_ADV wrapper that replaces the direct BUFG
on the 400 MHz ADC data clock output with a jitter-cleaning PLL loop.
### Current clock path (Build 18)
```
adc_dco_p/n → IBUFDS → BUFIO (drives IDDR only — near-zero delay)
→ BUFG (drives all fabric 400 MHz logic)
```
### New clock path (with MMCM)
```
adc_dco_p/n → IBUFDS → BUFIO (unchanged — drives IDDR only)
→ MMCME2 CLKIN1 → CLKOUT0 → BUFG (fabric 400 MHz)
→ CLKFBOUT → BUFG → CLKFBIN (feedback)
```
## Expected Timing Improvement
| Parameter | Before (Build 18) | After (estimated) |
|-----------|-------------------|-------------------|
| Input jitter | 50 ps | 50 ps (unchanged) |
| Output jitter (MMCM) | N/A | ~20-30 ps |
| Clock uncertainty | 43 ps | ~25 ps |
| WNS (setup, 400 MHz) | +0.062 ns | ~+0.08 to +0.10 ns |
| WHS (hold, 100 MHz) | +0.059 ns | unchanged |
The improvement comes from reduced clock uncertainty in the Vivado timing
analysis. The MMCM's PLL loop attenuates the input jitter, so Vivado deducts
less uncertainty from the timing budget.
## MMCM Configuration
```
CLKIN1 = 400 MHz (2.500 ns)
DIVCLK_DIVIDE = 1
CLKFBOUT_MULT_F = 2.0 → VCO = 800 MHz
CLKOUT0_DIVIDE_F = 2.0 → Output = 400 MHz
BANDWIDTH = HIGH (maximum jitter filtering)
```
VCO at 800 MHz is well within the Artix-7 -2 speed grade range (6001200 MHz).
## Resource Cost
| Resource | Count | Notes |
|----------|-------|-------|
| MMCME2_ADV | 1 | Was 0/10, now 1/10 |
| BUFG | +1 (feedback) | Was 4/32, now 5/32 |
| FFs | 0 | No additional fabric registers |
## Integration Steps
### Step 1: Modify `ad9484_interface_400m.v`
Replace the BUFG instantiation with `adc_clk_mmcm`:
```verilog
// REMOVE these lines (65-69):
// BUFG bufg_dco (
// .I(adc_dco),
// .O(adc_dco_buffered)
// );
// assign adc_dco_bufg = adc_dco_buffered;
// ADD this instead:
wire mmcm_locked;
wire adc_dco_mmcm;
adc_clk_mmcm mmcm_inst (
.clk_in (adc_dco), // From IBUFDS output
.reset_n (reset_n),
.clk_400m_out (adc_dco_mmcm), // Jitter-cleaned 400 MHz
.mmcm_locked (mmcm_locked)
);
// Use MMCM output for all fabric logic
wire adc_dco_buffered = adc_dco_mmcm;
assign adc_dco_bufg = adc_dco_buffered;
```
### Step 2: Gate reset on MMCM lock (recommended)
In `ad9484_interface_400m.v`, modify the reset synchronizer to require MMCM lock:
```verilog
// Change the reset synchronizer input from:
// always @(posedge adc_dco_buffered or negedge reset_n) begin
// if (!reset_n)
// reset_sync_400m <= 2'b00;
// else
// reset_sync_400m <= {reset_sync_400m[0], 1'b1};
// end
// To:
wire reset_n_gated = reset_n & mmcm_locked;
always @(posedge adc_dco_buffered or negedge reset_n_gated) begin
if (!reset_n_gated)
reset_sync_400m <= 2'b00;
else
reset_sync_400m <= {reset_sync_400m[0], 1'b1};
end
```
This ensures the 400 MHz domain stays in reset until the MMCM has locked and
the clock is stable. Without this, the first ~10 µs after power-up (before
MMCM lock) could produce glitchy clock edges.
### Step 3: Add constraint file
Add `constraints/adc_clk_mmcm.xdc` to the Vivado project. Uncomment the
constraints and adjust hierarchy paths based on your actual instantiation.
Key constraints to uncomment:
1. `create_generated_clock` (or verify Vivado auto-creates it)
2. `set_max_delay` between `adc_dco_p` and `clk_400m_mmcm`
3. `set_false_path` between `clk_400m_mmcm` and other clock domains
4. `set_false_path` on `LOCKED` output
### Step 4: Add to build script
In the Tcl build script, add the new source file:
```tcl
read_verilog adc_clk_mmcm.v
read_xdc constraints/adc_clk_mmcm.xdc
```
### Step 5: Verify
After building:
1. Check `report_clocks` — should show the new MMCM-derived clock
2. Check `report_clock_interaction` — verify no unexpected crossings
3. Check WNS on the `adc_dco_p` / MMCM clock group — should improve
4. Check MMCM locked in ILA during bring-up
## BUFIO Compatibility Note
The BUFIO path for IDDR capture is **not affected** by this change. BUFIO
drives only IOB primitives (IDDR) and cannot go through an MMCM. The BUFIO
continues to use the raw IBUFDS output with near-zero insertion delay, which
is correct for source-synchronous DDR capture.
The re-registration from BUFIO domain to BUFG domain (lines 105-108 of
`ad9484_interface_400m.v`) now crosses from the raw `adc_dco_p` clock to the
MMCM-derived clock. Since both are frequency-matched and the MMCM is locked
to the input, this is a safe single-register transfer. The `set_max_delay`
constraint in the XDC ensures Vivado verifies this.
## Simulation
Under `SIMULATION` define (iverilog), the module passes the clock straight
through with a simulated lock delay of ~4096 cycles. This matches the
current testbench behavior — no changes to any testbenches needed.
## Rollback
To revert: simply restore the original BUFG in `ad9484_interface_400m.v` and
remove `adc_clk_mmcm.v` + `constraints/adc_clk_mmcm.xdc` from the project.
No other files are affected.
+235 -62
View File
@@ -45,6 +45,61 @@ wire [ACC_WIDTH-1:0] data_in_c = {{(ACC_WIDTH-18){data_in[17]}}, data_in};
wire [47:0] pcout_0, pcout_1, pcout_2, pcout_3; wire [47:0] pcout_0, pcout_1, pcout_2, pcout_3;
wire [47:0] p_out_0, p_out_1, p_out_2, p_out_3, p_out_4; wire [47:0] p_out_0, p_out_1, p_out_2, p_out_3, p_out_4;
// Comb stage 0 DSP48E1 output wire (CREG+AREG+BREG pipelined subtract)
wire [47:0] comb_0_p_out;
// ============================================================================
// SHARED REGISTER DECLARATIONS
// ============================================================================
// These registers are referenced by both the synthesis DSP48E1 instances
// (inside `ifndef SIMULATION) and the behavioral simulation model (inside
// `else), as well as the shared fabric logic (after `endif).
// Icarus Verilog 13.0 requires registers to be declared before their first
// use within any `ifdef branch, so we declare them here — before the
// `ifndef SIMULATION block — rather than in the post-`endif shared section.
// ============================================================================
(* keep = "true", dont_touch = "true" *) reg signed [COMB_WIDTH-1:0] integrator_sampled;
(* keep = "true", dont_touch = "true", max_fanout = 1 *) reg signed [COMB_WIDTH-1:0] integrator_sampled_comb;
(* use_dsp = "yes" *) reg signed [COMB_WIDTH-1:0] comb [0:STAGES-1];
reg signed [COMB_WIDTH-1:0] comb_delay [0:STAGES-1][0:COMB_DELAY-1];
// Pipeline valid for comb stages 1-4: delayed by 1 cycle vs comb_pipe to
// account for CREG+AREG+BREG pipeline inside comb_0_dsp (explicit DSP48E1).
// Comb[0] result appears 1 cycle after data_valid_comb_pipe.
(* keep = "true", max_fanout = 4 *) reg data_valid_comb_0_out;
// Enhanced control and monitoring
reg [1:0] decimation_counter;
(* keep = "true", max_fanout = 4 *) reg data_valid_delayed;
(* keep = "true", max_fanout = 4 *) reg data_valid_comb;
(* keep = "true", max_fanout = 4 *) reg data_valid_comb_pipe;
reg [7:0] output_counter;
reg [ACC_WIDTH-1:0] max_integrator_value;
reg overflow_detected;
reg overflow_latched;
// Diagnostic registers
reg [7:0] saturation_event_count;
reg [31:0] sample_count;
// Comb-stage saturation flags
reg comb_overflow_latched;
reg comb_saturation_detected;
reg [7:0] comb_saturation_event_count;
// Temporary signals for calculations
reg signed [ACC_WIDTH-1:0] abs_integrator_value;
reg signed [COMB_WIDTH-1:0] temp_scaled_output;
reg signed [17:0] temp_output;
// Pipeline stage for saturation comparison
reg sat_pos;
reg sat_neg;
reg signed [17:0] temp_output_pipe;
reg data_out_valid_pipe;
integer i, j;
`ifndef SIMULATION `ifndef SIMULATION
// ============================================================================ // ============================================================================
// SYNTHESIS: Explicit DSP48E1 instances with PCOUTPCIN cascade // SYNTHESIS: Explicit DSP48E1 instances with PCOUTPCIN cascade
@@ -426,6 +481,111 @@ DSP48E1 #(
.UNDERFLOW () .UNDERFLOW ()
); );
// ============================================================================
// COMB STAGE 0 Explicit DSP48E1 with CREG=1 for Critical Path Fix
// ============================================================================
// Build 18 critical path: integrator_sampled_comb_reg comb_reg[0]/C[38]
// WNS = +0.062 ns, data path = 1.022 ns (0.379 logic + 0.643 route)
//
// By enabling CREG=1 (+ AREG=1, BREG=1), the fabric register
// integrator_sampled_comb is absorbed into the DSP48's internal C pipeline
// register, eliminating the 0.643 ns fabricDSP routing delay entirely.
// The DSP48 performs: P = C_reg - {A_reg, B_reg} (i.e., subtract)
//
// Latency: +1 cycle vs. the old inferred comb[0]. This is accounted for
// by the data_valid_comb_0_out signal, which delays the valid for stages 1-4.
//
// C-port = sign-extended integrator_sampled_comb (2848 bits)
// A:B = sign-extended comb_delay[0][0] (2848 bits)
// OPMODE = 7'b0110011: Z=C(011), Y=0(00), X=A:B(11)
// ALUMODE= 4'b0011: Z - (X + Y + CIN) = C - A:B
//
// The comb_delay[0][0] register stays in fabric (captures
// integrator_sampled_comb at the same time as the C register, unchanged).
// Comb stages 1-4 remain inferred with (* use_dsp = "yes" *).
// Sign-extended inputs for comb_0 DSP48E1
wire [47:0] comb_0_c_in = {{(48-COMB_WIDTH){integrator_sampled_comb[COMB_WIDTH-1]}},
integrator_sampled_comb};
wire [47:0] comb_0_ab_in = {{(48-COMB_WIDTH){comb_delay[0][COMB_DELAY-1][COMB_WIDTH-1]}},
comb_delay[0][COMB_DELAY-1]};
DSP48E1 #(
.A_INPUT ("DIRECT"),
.B_INPUT ("DIRECT"),
.USE_DPORT ("FALSE"),
.USE_MULT ("NONE"),
.AUTORESET_PATDET ("NO_RESET"),
.MASK (48'h3FFFFFFFFFFF),
.PATTERN (48'h000000000000),
.SEL_MASK ("MASK"),
.SEL_PATTERN ("PATTERN"),
.USE_PATTERN_DETECT ("NO_PATDET"),
.ACASCREG (1), // A cascade register matches AREG
.ADREG (0),
.ALUMODEREG (0),
.AREG (1), // A-port registered eliminates fabric routing
.BCASCREG (1), // B cascade register matches BREG
.BREG (1), // B-port registered eliminates fabric routing
.CARRYINREG (0),
.CARRYINSELREG (0),
.CREG (1), // *** KEY: C-port registered inside DSP48 ***
// Absorbs integrator_sampled_comb FDRE, eliminates
// 0.643 ns fabricDSP C-port routing delay.
.DREG (0),
.INMODEREG (0),
.MREG (0),
.OPMODEREG (0),
.PREG (1) // P register enabled (output pipeline)
) comb_0_dsp (
.CLK (clk),
// A:B = sign-extended comb_delay[0][last] (subtrahend)
.A (comb_0_ab_in[47:18]), // Upper 30 bits
.B (comb_0_ab_in[17:0]), // Lower 18 bits
.C (comb_0_c_in), // integrator_sampled_comb (minuend)
.D (25'd0),
.CARRYIN (1'b0),
.CARRYINSEL (3'b000),
.OPMODE (7'b0110011), // Z=C, Y=0, X=A:B ALU input = C, A:B
.ALUMODE (4'b0011), // Z - (X+Y+CIN) = C - A:B
.INMODE (5'b00000),
.CEA1 (1'b0),
.CEA2 (data_valid_comb_pipe), // Load A register when valid
.CEB1 (1'b0),
.CEB2 (data_valid_comb_pipe), // Load B register when valid
.CEC (data_valid_comb_pipe), // Load C register when valid
.CED (1'b0),
.CEM (1'b0),
.CEP (1'b1), // Always propagate P updates 1 cycle after
// input registers are loaded
.CEAD (1'b0),
.CEALUMODE (1'b0),
.CECTRL (1'b0),
.CECARRYIN (1'b0),
.CEINMODE (1'b0),
.RSTP (reset_h),
.RSTA (reset_h),
.RSTB (reset_h),
.RSTC (reset_h),
.RSTD (1'b0),
.RSTM (1'b0),
.RSTALLCARRYIN (1'b0),
.RSTALUMODE (1'b0),
.RSTCTRL (1'b0),
.RSTINMODE (1'b0),
.P (comb_0_p_out),
.PCOUT (),
.ACOUT (),
.BCOUT (),
.CARRYCASCOUT (),
.CARRYOUT (),
.MULTSIGNOUT (),
.OVERFLOW (),
.PATTERNBDETECT (),
.PATTERNDETECT (),
.UNDERFLOW ()
);
`else `else
// ============================================================================ // ============================================================================
// SIMULATION: Behavioral model (Icarus Verilog compatible) // SIMULATION: Behavioral model (Icarus Verilog compatible)
@@ -441,6 +601,14 @@ DSP48E1 #(
reg signed [ACC_WIDTH-1:0] sim_int_0, sim_int_1, sim_int_2, sim_int_3, sim_int_4; reg signed [ACC_WIDTH-1:0] sim_int_0, sim_int_1, sim_int_2, sim_int_3, sim_int_4;
reg signed [ACC_WIDTH-1:0] data_in_c_delayed; // Models CREG=1 on integrator_0 reg signed [ACC_WIDTH-1:0] data_in_c_delayed; // Models CREG=1 on integrator_0
// Comb_0 DSP48E1 behavioral model (models CREG+AREG+BREG+PREG pipeline)
// In simulation there is no DSP48E1 primitive, so we model the 4-stage pipe:
// Stage 1 (CREG/AREG/BREG): capture C and A:B inputs (on data_valid_comb_pipe)
// Stage 2 (PREG): P = C_reg - AB_reg (always, like CEP=1 in synthesis)
reg signed [COMB_WIDTH-1:0] sim_comb_0_c_reg; // Models CREG
reg signed [COMB_WIDTH-1:0] sim_comb_0_ab_reg; // Models AREG+BREG (combined)
reg signed [47:0] sim_comb_0_p_reg; // Models PREG
always @(posedge clk) begin always @(posedge clk) begin
if (reset_h) begin if (reset_h) begin
sim_int_0 <= 0; sim_int_0 <= 0;
@@ -449,17 +617,33 @@ always @(posedge clk) begin
sim_int_3 <= 0; sim_int_3 <= 0;
sim_int_4 <= 0; sim_int_4 <= 0;
data_in_c_delayed <= 0; data_in_c_delayed <= 0;
end else if (data_valid) begin sim_comb_0_c_reg <= 0;
// CREG pipeline: capture current data, use previous sim_comb_0_ab_reg <= 0;
data_in_c_delayed <= $signed(data_in_c); sim_comb_0_p_reg <= 0;
sim_int_0 <= sim_int_0 + data_in_c_delayed; end else begin
sim_int_1 <= sim_int_1 + sim_int_0; if (data_valid) begin
sim_int_2 <= sim_int_2 + sim_int_1; // CREG pipeline: capture current data, use previous
sim_int_3 <= sim_int_3 + sim_int_2; data_in_c_delayed <= $signed(data_in_c);
sim_int_4 <= sim_int_4 + sim_int_3; sim_int_0 <= sim_int_0 + data_in_c_delayed;
sim_int_1 <= sim_int_1 + sim_int_0;
sim_int_2 <= sim_int_2 + sim_int_1;
sim_int_3 <= sim_int_3 + sim_int_2;
sim_int_4 <= sim_int_4 + sim_int_3;
end
// Comb_0 DSP48 behavioral model:
// CREG/AREG/BREG load on data_valid_comb_pipe (like CEC/CEA2/CEB2)
if (data_valid_comb_pipe) begin
sim_comb_0_c_reg <= integrator_sampled_comb;
sim_comb_0_ab_reg <= comb_delay[0][COMB_DELAY-1];
end
// PREG always updates (CEP=1): P = C_reg - AB_reg
sim_comb_0_p_reg <= {{(48-COMB_WIDTH){sim_comb_0_c_reg[COMB_WIDTH-1]}}, sim_comb_0_c_reg}
- {{(48-COMB_WIDTH){sim_comb_0_ab_reg[COMB_WIDTH-1]}}, sim_comb_0_ab_reg};
end end
end end
assign comb_0_p_out = sim_comb_0_p_reg;
assign p_out_0 = sim_int_0; assign p_out_0 = sim_int_0;
assign p_out_1 = sim_int_1; assign p_out_1 = sim_int_1;
assign p_out_2 = sim_int_2; assign p_out_2 = sim_int_2;
@@ -475,42 +659,8 @@ assign pcout_3 = sim_int_3;
// ============================================================================ // ============================================================================
// CONTROL AND MONITORING (fabric logic) // CONTROL AND MONITORING (fabric logic)
// ============================================================================ // ============================================================================
(* keep = "true", dont_touch = "true" *) reg signed [COMB_WIDTH-1:0] integrator_sampled; // (Register declarations moved above `ifndef SIMULATION for Icarus Verilog
(* keep = "true", dont_touch = "true", max_fanout = 1 *) reg signed [COMB_WIDTH-1:0] integrator_sampled_comb; // forward-reference compatibility — see "SHARED REGISTER DECLARATIONS".)
(* use_dsp = "yes" *) reg signed [COMB_WIDTH-1:0] comb [0:STAGES-1];
reg signed [COMB_WIDTH-1:0] comb_delay [0:STAGES-1][0:COMB_DELAY-1];
// Enhanced control and monitoring
reg [1:0] decimation_counter;
(* keep = "true", max_fanout = 4 *) reg data_valid_delayed;
(* keep = "true", max_fanout = 4 *) reg data_valid_comb;
(* keep = "true", max_fanout = 4 *) reg data_valid_comb_pipe;
reg [7:0] output_counter;
reg [ACC_WIDTH-1:0] max_integrator_value;
reg overflow_detected;
reg overflow_latched;
// Diagnostic registers
reg [7:0] saturation_event_count;
reg [31:0] sample_count;
// Comb-stage saturation flags
reg comb_overflow_latched;
reg comb_saturation_detected;
reg [7:0] comb_saturation_event_count;
// Temporary signals for calculations
reg signed [ACC_WIDTH-1:0] abs_integrator_value;
reg signed [COMB_WIDTH-1:0] temp_scaled_output;
reg signed [17:0] temp_output;
// Pipeline stage for saturation comparison
reg sat_pos;
reg sat_neg;
reg signed [17:0] temp_output_pipe;
reg data_out_valid_pipe;
integer i, j;
// Initialize // Initialize
initial begin initial begin
@@ -525,6 +675,7 @@ initial begin
data_valid_delayed = 0; data_valid_delayed = 0;
data_valid_comb = 0; data_valid_comb = 0;
data_valid_comb_pipe = 0; data_valid_comb_pipe = 0;
data_valid_comb_0_out = 0;
output_counter = 0; output_counter = 0;
max_integrator_value = 0; max_integrator_value = 0;
overflow_detected = 0; overflow_detected = 0;
@@ -609,10 +760,16 @@ always @(posedge clk) begin
if (!reset_n) begin if (!reset_n) begin
data_valid_comb <= 0; data_valid_comb <= 0;
data_valid_comb_pipe <= 0; data_valid_comb_pipe <= 0;
data_valid_comb_0_out <= 0;
integrator_sampled_comb <= 0; integrator_sampled_comb <= 0;
end else begin end else begin
data_valid_comb <= data_valid_delayed; data_valid_comb <= data_valid_delayed;
data_valid_comb_pipe <= data_valid_comb; data_valid_comb_pipe <= data_valid_comb;
// data_valid_comb_0_out is delayed 1 cycle from data_valid_comb_pipe
// to account for CREG+AREG+BREG pipeline in comb_0_dsp.
// When data_valid_comb_0_out fires, comb_0_p_out (DSP48 PREG)
// contains the valid comb[0] result.
data_valid_comb_0_out <= data_valid_comb_pipe;
integrator_sampled_comb <= integrator_sampled; integrator_sampled_comb <= integrator_sampled;
end end
end end
@@ -625,11 +782,14 @@ end
// WNS = +0.128ns). DSP48E1 ALU performs 48-bit add/subtract in a single // WNS = +0.128ns). DSP48E1 ALU performs 48-bit add/subtract in a single
// cycle with zero fabric logic, targeting WNS > +1.0ns. // cycle with zero fabric logic, targeting WNS > +1.0ns.
// //
// The (* use_dsp = "yes" *) attribute on comb[] tells Vivado synthesis to // COMB STAGE 0: Explicit DSP48E1 with CREG=1 (comb_0_dsp instance above).
// map the subtract into DSP48E1 P = C - A:B (ALUMODE=4'b0011). Each comb // - comb[0] is driven by comb_0_p_out[COMB_WIDTH-1:0] (DSP48 P register)
// stage becomes one DSP48E1 with PREG=1, completely eliminating the CARRY4 // - comb_delay[0][0] still captures integrator_sampled_comb in fabric
// chain from fabric. With 5 stages × 2 channels (I/Q) = 10 additional // - Valid signal for stages 1-4 is data_valid_comb_0_out (delayed by 1 cycle
// DSP48E1s, total DSP usage rises from 130 to ~140 out of 740 (18.9%). // from data_valid_comb_pipe to match CREG+AREG+BREG pipeline latency)
//
// COMB STAGES 1-4: Inferred DSP48E1 via (* use_dsp = "yes" *) attribute.
// - Each stage: comb[i] = comb[i-1] - comb_delay[i][last]
always @(posedge clk) begin always @(posedge clk) begin
if (!reset_n) begin if (!reset_n) begin
@@ -657,21 +817,34 @@ always @(posedge clk) begin
comb_saturation_event_count <= 0; comb_saturation_event_count <= 0;
end end
// ---- Comb Stage 0: delay line update + DSP48 output capture ----
// comb_delay[0][0] captures integrator_sampled_comb on the SAME cycle
// as the DSP48 input registers (CREG/AREG/BREG), so they see the
// same value. The DSP48 PREG output appears 1 cycle later.
if (data_valid_comb_pipe) begin if (data_valid_comb_pipe) begin
for (i = 0; i < STAGES; i = i + 1) begin for (j = COMB_DELAY-1; j > 0; j = j - 1) begin
if (i == 0) begin comb_delay[0][j] <= comb_delay[0][j-1];
comb[0] <= integrator_sampled_comb - comb_delay[0][COMB_DELAY-1]; end
for (j = COMB_DELAY-1; j > 0; j = j - 1) begin comb_delay[0][0] <= integrator_sampled_comb;
comb_delay[0][j] <= comb_delay[0][j-1]; end
end
comb_delay[0][0] <= integrator_sampled_comb; // ---- Comb Stage 0 result: from explicit DSP48E1 ----
end else begin // comb_0_dsp PREG output is valid 1 cycle after data_valid_comb_pipe.
comb[i] <= comb[i-1] - comb_delay[i][COMB_DELAY-1]; // We capture it into comb[0] on data_valid_comb_0_out so comb stages
for (j = COMB_DELAY-1; j > 0; j = j - 1) begin // 1-4 can read it.
comb_delay[i][j] <= comb_delay[i][j-1]; if (data_valid_comb_0_out) begin
end comb[0] <= comb_0_p_out[COMB_WIDTH-1:0];
comb_delay[i][0] <= comb[i-1]; end
// ---- Comb Stages 1-4: inferred DSP48E1 subtracts ----
// These fire on data_valid_comb_0_out (when comb[0] is valid).
if (data_valid_comb_0_out) begin
for (i = 1; i < STAGES; i = i + 1) begin
comb[i] <= comb[i-1] - comb_delay[i][COMB_DELAY-1];
for (j = COMB_DELAY-1; j > 0; j = j - 1) begin
comb_delay[i][j] <= comb_delay[i][j-1];
end end
comb_delay[i][0] <= comb[i-1];
end end
// Gain = (4^5) = 1024 = 2^10, scale by 2^10 to normalize // Gain = (4^5) = 1024 = 2^10, scale by 2^10 to normalize
@@ -0,0 +1,70 @@
# ============================================================================
# adc_clk_mmcm.xdc — Supplementary constraints for MMCM ADC clock path
#
# These constraints augment the existing adc_dco_p clock definitions when the
# adc_clk_mmcm module is integrated into ad9484_interface_400m.v.
#
# USAGE:
# Add this file to the Vivado project AFTER the main production XDC.
# The main XDC still defines create_clock on adc_dco_p (the physical input).
# Vivado automatically creates a generated clock on the MMCM output;
# these constraints handle CDC paths for the new clock topology.
#
# HIERARCHY: rx_inst/adc/mmcm_inst/...
# ============================================================================
# --------------------------------------------------------------------------
# MMCM Output Clock — use Vivado's auto-generated clock name
# --------------------------------------------------------------------------
# Vivado auto-creates a generated clock named 'clk_mmcm_out0' on the MMCM
# CLKOUT0 net. We do NOT create_generated_clock here (that would create a
# second clock on the same net, causing the CDC false paths below to bind
# to the wrong clock and leave clk_mmcm_out0 uncovered — exactly the bug
# that caused Build 19's -0.011 ns WNS on the CDC_FIR gray-code path).
# All constraints below reference 'clk_mmcm_out0' directly.
# --------------------------------------------------------------------------
# CDC: BUFIO domain (adc_dco_p) ↔ MMCM output domain (clk_mmcm_out0)
# --------------------------------------------------------------------------
# The IDDR outputs are captured by BUFIO (adc_dco_p clock), then re-registered
# into the MMCM BUFG domain in ad9484_interface_400m.v.
# These clocks are frequency-matched and phase-related (MMCM is locked to
# adc_dco_p), so the single register transfer is safe. We use max_delay
# (one period) to ensure the tools verify the transfer fits within one cycle
# without over-constraining with full inter-clock setup/hold analysis.
set_max_delay -datapath_only -from [get_clocks adc_dco_p] \
-to [get_clocks clk_mmcm_out0] 2.500
set_max_delay -datapath_only -from [get_clocks clk_mmcm_out0] \
-to [get_clocks adc_dco_p] 2.500
# --------------------------------------------------------------------------
# CDC: MMCM output domain ↔ other clock domains
# --------------------------------------------------------------------------
# The existing false paths in the production XDC reference adc_dco_p, which
# now only covers the BUFIO/IDDR domain. The MMCM output clock (which drives
# all fabric 400 MHz logic) needs its own false path declarations.
set_false_path -from [get_clocks clk_100m] -to [get_clocks clk_mmcm_out0]
set_false_path -from [get_clocks clk_mmcm_out0] -to [get_clocks clk_100m]
set_false_path -from [get_clocks clk_mmcm_out0] -to [get_clocks ft601_clk_in]
set_false_path -from [get_clocks ft601_clk_in] -to [get_clocks clk_mmcm_out0]
set_false_path -from [get_clocks clk_mmcm_out0] -to [get_clocks clk_120m_dac]
set_false_path -from [get_clocks clk_120m_dac] -to [get_clocks clk_mmcm_out0]
# --------------------------------------------------------------------------
# MMCM Locked — asynchronous status signal, no timing paths needed
# --------------------------------------------------------------------------
set_false_path -from [get_pins rx_inst/adc/mmcm_inst/mmcm_adc_400m/LOCKED]
# --------------------------------------------------------------------------
# Hold waiver for BUFIO→MMCM domain transfer (if Vivado flags hold violations)
# --------------------------------------------------------------------------
# The existing hold waiver for BUFIO source-synchronous capture stays:
# set_false_path -hold -from [get_ports {adc_d_p[*]}] -to [get_clocks adc_dco_p]
#
# The MMCM BUFG re-registration of IDDR outputs: since BUFIO and MMCM output
# are derived from the same IBUFDS source, hold is inherently met (MMCM adds
# insertion delay). If Vivado flags hold violations on this transfer, uncomment:
# set_false_path -hold -from [get_clocks adc_dco_p] -to [get_clocks clk_mmcm_out0]
@@ -0,0 +1,475 @@
################################################################################
# build19_mmcm.tcl
#
# AERIS-10 Build 19: MMCM Jitter-Cleaning on ADC 400 MHz Clock (Gap 7)
# Target: XC7A200T-2FBG484I
# Design: radar_system_top
# Tag: v0.1.2-build18 + adc_clk_mmcm jitter cleaning wrapper
#
# Changes vs Build 18:
# - NEW MODULE: adc_clk_mmcm.v — MMCME2_ADV jitter-cleaning wrapper
# - MODIFIED: ad9484_interface_400m.v — BUFG replaced with MMCM path,
# reset gated on mmcm_locked
# - NEW XDC: adc_clk_mmcm.xdc — generated clock rename, CDC false paths
#
# Expected impact:
# - WNS improvement: +20-40 ps (reduced clock uncertainty from jitter cleaning)
# - MMCME2 usage: 0 → 1 (of 10 available)
# - BUFG usage: 4 → 5 (of 32 available; feedback BUFG inside MMCM wrapper)
#
# Generates ALL reports required for the 15-point Vivado TCL Build Report.
#
# Usage:
# vivado -mode batch -source build19_mmcm.tcl \
# -log ~/PLFM_RADAR_work/vivado_project/build19.log \
# -journal ~/PLFM_RADAR_work/vivado_project/build19.jou
#
# Author: auto-generated for Jason Stone
# Date: 2026-03-19
################################################################################
# ==============================================================================
# 0. Configuration
# ==============================================================================
set project_name "aeris10_radar"
set project_dir "/home/jason-stone/PLFM_RADAR_work/vivado_project"
set rtl_dir "/home/jason-stone/PLFM_RADAR_work/PLFM_RADAR/9_Firmware/9_2_FPGA"
set top_module "radar_system_top"
set fpga_part "xc7a200tfbg484-2"
set report_dir "${project_dir}/reports_build19"
set sim_dir "${project_dir}/sim"
set bitstream_dir "${project_dir}/bitstream"
set build_tag "build19"
file mkdir $report_dir
file mkdir $sim_dir
file mkdir $bitstream_dir
# Record start time
set build_start [clock seconds]
set build_timestamp [clock format $build_start -format {%Y-%m-%d %H:%M:%S}]
puts "================================================================"
puts " AERIS-10 Build 19: MMCM Jitter-Cleaning (Gap 7)"
puts " Target: $fpga_part"
puts " Top: $top_module"
puts " Reports: $report_dir"
puts " Started: $build_timestamp"
puts "================================================================"
# ==============================================================================
# 1. Project Creation + Source Files
# ==============================================================================
create_project $project_name $project_dir -part $fpga_part -force
set_property target_language Verilog [current_project]
# --- Add RTL sources ---
# NOTE: adc_clk_mmcm.v is NEW for Build 19 (Gap 7 MMCM wrapper)
set rtl_files [list \
"${rtl_dir}/adc_clk_mmcm.v" \
"${rtl_dir}/ad9484_interface_400m.v" \
"${rtl_dir}/cdc_modules.v" \
"${rtl_dir}/chirp_lut_init.v" \
"${rtl_dir}/chirp_memory_loader_param.v" \
"${rtl_dir}/cic_decimator_4x_enhanced.v" \
"${rtl_dir}/dac_interface_single.v" \
"${rtl_dir}/ddc_400m.v" \
"${rtl_dir}/ddc_input_interface.v" \
"${rtl_dir}/doppler_processor.v" \
"${rtl_dir}/edge_detector.v" \
"${rtl_dir}/fft_1024_forward.v" \
"${rtl_dir}/fft_1024_inverse.v" \
"${rtl_dir}/fir_lowpass.v" \
"${rtl_dir}/frequency_matched_filter.v" \
"${rtl_dir}/latency_buffer.v" \
"${rtl_dir}/level_shifter_interface.v" \
"${rtl_dir}/lvds_to_cmos_400m.v" \
"${rtl_dir}/matched_filter_multi_segment.v" \
"${rtl_dir}/matched_filter_processing_chain.v" \
"${rtl_dir}/nco_400m_enhanced.v" \
"${rtl_dir}/plfm_chirp_controller.v" \
"${rtl_dir}/radar_mode_controller.v" \
"${rtl_dir}/radar_receiver_final.v" \
"${rtl_dir}/radar_system_top.v" \
"${rtl_dir}/radar_transmitter.v" \
"${rtl_dir}/range_bin_decimator.v" \
"${rtl_dir}/usb_data_interface.v" \
"${rtl_dir}/usb_packet_analyzer.v" \
"${rtl_dir}/xfft_32.v" \
"${rtl_dir}/fft_engine.v" \
]
set file_count 0
foreach f $rtl_files {
if {[file exists $f]} {
add_files -norecurse $f
incr file_count
} else {
puts " WARNING: RTL file not found: $f"
}
}
puts " Added $file_count RTL files"
# Add .mem files for BRAM initialization
set mem_files [glob -nocomplain "${rtl_dir}/*.mem"]
foreach f $mem_files {
add_files -norecurse $f
puts " Added MEM: [file tail $f]"
}
# Add constraints — main production XDC + MMCM supplementary XDC
add_files -fileset constrs_1 -norecurse "${project_dir}/synth_only.xdc"
add_files -fileset constrs_1 -norecurse "${rtl_dir}/constraints/adc_clk_mmcm.xdc"
set_property top $top_module [current_fileset]
set_property verilog_define {FFT_XPM_BRAM} [current_fileset]
# ==============================================================================
# 2. Synthesis
# ==============================================================================
puts ""
puts "================================================================"
puts " Phase 1/5: Synthesis"
puts "================================================================"
set_property STEPS.SYNTH_DESIGN.ARGS.FLATTEN_HIERARCHY rebuilt [get_runs synth_1]
set_property STEPS.SYNTH_DESIGN.ARGS.KEEP_EQUIVALENT_REGISTERS true [get_runs synth_1]
set synth_start [clock seconds]
launch_runs synth_1 -jobs 8
wait_on_run synth_1
set synth_elapsed [expr {[clock seconds] - $synth_start}]
set synth_status [get_property STATUS [get_runs synth_1]]
puts " Synthesis status: $synth_status"
puts " Synthesis time: ${synth_elapsed}s ([expr {$synth_elapsed/60}]m [expr {$synth_elapsed%60}]s)"
if {[string match "*ERROR*" $synth_status] || [string match "*FAILED*" $synth_status]} {
puts "CRITICAL: SYNTHESIS FAILED aborting build"
close_project
exit 1
}
# Post-synth timing (for comparison)
open_run synth_1 -name synth_1
report_timing_summary -delay_type min_max -max_paths 10 -file "${report_dir}/01_timing_post_synth.rpt"
report_utilization -file "${report_dir}/01_utilization_post_synth.rpt"
close_design
# ==============================================================================
# 3. Implementation (opt → place → phys_opt → route → post_route_phys_opt)
# ==============================================================================
puts ""
puts "================================================================"
puts " Phase 2/5: Implementation"
puts "================================================================"
# Aggressive directives for best timing
set_property STEPS.OPT_DESIGN.ARGS.DIRECTIVE Explore [get_runs impl_1]
set_property STEPS.PLACE_DESIGN.ARGS.DIRECTIVE ExtraTimingOpt [get_runs impl_1]
set_property STEPS.PHYS_OPT_DESIGN.ARGS.DIRECTIVE AggressiveExplore [get_runs impl_1]
set_property STEPS.PHYS_OPT_DESIGN.IS_ENABLED true [get_runs impl_1]
set_property STEPS.ROUTE_DESIGN.ARGS.DIRECTIVE AggressiveExplore [get_runs impl_1]
set_property STEPS.POST_ROUTE_PHYS_OPT_DESIGN.IS_ENABLED true [get_runs impl_1]
set_property STEPS.POST_ROUTE_PHYS_OPT_DESIGN.ARGS.DIRECTIVE AggressiveExplore [get_runs impl_1]
set impl_start [clock seconds]
launch_runs impl_1 -jobs 8
wait_on_run impl_1
set impl_elapsed [expr {[clock seconds] - $impl_start}]
set impl_status [get_property STATUS [get_runs impl_1]]
puts " Implementation status: $impl_status"
puts " Implementation time: ${impl_elapsed}s ([expr {$impl_elapsed/60}]m [expr {$impl_elapsed%60}]s)"
if {![string match "*Complete*" $impl_status]} {
puts "CRITICAL: IMPLEMENTATION FAILED: $impl_status"
close_project
exit 1
}
# ==============================================================================
# 4. Bitstream Generation
# ==============================================================================
puts ""
puts "================================================================"
puts " Phase 3/5: Bitstream Generation"
puts "================================================================"
set bit_start [clock seconds]
launch_runs impl_1 -to_step write_bitstream -jobs 8
wait_on_run impl_1
set bit_elapsed [expr {[clock seconds] - $bit_start}]
puts " Bitstream time: ${bit_elapsed}s"
# Copy bitstream to known location
set bit_src "${project_dir}/aeris10_radar.runs/impl_1/${top_module}.bit"
if {[file exists $bit_src]} {
file copy -force $bit_src "${bitstream_dir}/${top_module}_${build_tag}.bit"
puts " Bitstream: ${bitstream_dir}/${top_module}_${build_tag}.bit"
puts " Size: [file size $bit_src] bytes"
} else {
puts " WARNING: Bitstream file not found at $bit_src"
}
# ==============================================================================
# 5. Comprehensive Report Generation
# ==============================================================================
puts ""
puts "================================================================"
puts " Phase 4/5: Report Generation (15-point checklist)"
puts "================================================================"
# Open the routed design for reporting
open_run impl_1 -name impl_1
# --- Checklist Item 2: Timing Summary ---
puts " [2/15] Timing Summary..."
report_timing_summary -delay_type min_max -max_paths 100 \
-report_unconstrained \
-file "${report_dir}/02_timing_summary.rpt"
# --- Checklist Item 3: Clock Analysis ---
puts " [3/15] Clock Analysis..."
report_clocks -file "${report_dir}/03_clocks.rpt"
report_clock_interaction -delay_type min_max \
-file "${report_dir}/03_clock_interaction.rpt"
report_clock_networks -file "${report_dir}/03_clock_networks.rpt"
# --- Checklist Item 4: Utilization ---
puts " [4/15] Utilization..."
report_utilization -file "${report_dir}/04_utilization.rpt"
report_utilization -hierarchical -file "${report_dir}/04_utilization_hierarchical.rpt"
# --- Checklist Item 5: Power ---
puts " [5/15] Power Report..."
report_power -file "${report_dir}/05_power.rpt"
# --- Checklist Item 6: DRC ---
puts " [6/15] DRC..."
report_drc -file "${report_dir}/06_drc.rpt"
# --- Checklist Item 7: IO and Constraints ---
puts " [7/15] IO Report..."
report_io -file "${report_dir}/07_io.rpt"
report_timing -from [all_inputs] -to [all_outputs] -max_paths 20 \
-file "${report_dir}/07_io_timing.rpt"
# --- Checklist Item 8: Congestion Analysis ---
puts " [8/15] Congestion Analysis..."
report_design_analysis -congestion -file "${report_dir}/08_congestion.rpt"
# --- Checklist Item 9: Route Status ---
puts " [9/15] Route Status..."
report_route_status -file "${report_dir}/09_route_status.rpt"
# --- Checklist Item 10: Critical Paths ---
puts " [10/15] Critical Paths..."
report_timing -max_paths 20 -sort_by slack -nworst 5 \
-file "${report_dir}/10_critical_paths_setup.rpt"
report_timing -delay_type min -max_paths 20 -sort_by slack -nworst 5 \
-file "${report_dir}/10_critical_paths_hold.rpt"
report_high_fanout_nets -timing -load_type -max_nets 20 \
-file "${report_dir}/10_high_fanout_nets.rpt"
# --- Checklist Item 11: QoR Summary ---
puts " [11/15] QoR Summary..."
report_design_analysis -timing -file "${report_dir}/11_design_analysis_timing.rpt"
report_design_analysis -logic_level_distribution -file "${report_dir}/11_logic_level_dist.rpt"
report_methodology -file "${report_dir}/11_methodology.rpt"
# --- Checklist Item 12: CDC Analysis ---
puts " [12/15] CDC Analysis..."
report_cdc -details -file "${report_dir}/12_cdc.rpt"
# --- Checklist Item 13: Log Scan (captured separately in build log) ---
puts " [13/15] Log scan see build19.log"
# --- Additional reports ---
puts " [extra] Generating additional diagnostic reports..."
# report_exceptions can fail in Vivado 2025.2 — wrap in catch
if {[catch {report_exceptions -file "${report_dir}/13_exceptions.rpt"} err]} {
puts " WARNING: report_exceptions failed: $err"
puts " (Known Vivado 2025.2 issue non-critical)"
}
check_timing -verbose -file "${report_dir}/13_check_timing.rpt"
# Compile configuration summary into a single text file
set summary_fh [open "${report_dir}/00_build19_summary.txt" w]
puts $summary_fh "================================================================"
puts $summary_fh " AERIS-10 Build 19 MMCM Jitter-Cleaning (Gap 7) Summary"
puts $summary_fh "================================================================"
puts $summary_fh ""
puts $summary_fh "Build Tag: $build_tag"
puts $summary_fh "Build Timestamp: $build_timestamp"
puts $summary_fh "FPGA Part: $fpga_part"
puts $summary_fh "Top Module: $top_module"
puts $summary_fh "RTL Files: $file_count"
puts $summary_fh "Synth Status: $synth_status"
puts $summary_fh "Synth Time: ${synth_elapsed}s"
puts $summary_fh "Impl Status: $impl_status"
puts $summary_fh "Impl Time: ${impl_elapsed}s"
puts $summary_fh "Bitstream Time: ${bit_elapsed}s"
puts $summary_fh ""
# Extract key timing numbers
puts $summary_fh "--- Timing ---"
set wns [get_property STATS.WNS [current_design]]
set tns [get_property STATS.TNS [current_design]]
set whs [get_property STATS.WHS [current_design]]
set ths [get_property STATS.THS [current_design]]
set fail_ep [get_property STATS.TPWS [current_design]]
puts $summary_fh " WNS: $wns ns"
puts $summary_fh " TNS: $tns ns"
puts $summary_fh " WHS: $whs ns"
puts $summary_fh " THS: $ths ns"
puts $summary_fh ""
puts $summary_fh " Build 18 Baseline: WNS = +0.062 ns, WHS = +0.059 ns"
puts $summary_fh " Delta WNS: [expr {$wns - 0.062}] ns"
puts $summary_fh " Delta WHS: [expr {$whs - 0.059}] ns"
puts $summary_fh ""
# Extract utilization
puts $summary_fh "--- Utilization ---"
set lut_used [llength [get_cells -hierarchical -filter {PRIMITIVE_TYPE =~ CLB.LUT.*}]]
set ff_used [llength [get_cells -hierarchical -filter {PRIMITIVE_TYPE =~ CLB.FF.*}]]
set bram_used [llength [get_cells -hierarchical -filter {PRIMITIVE_TYPE =~ BMEM.*}]]
set dsp_used [llength [get_cells -hierarchical -filter {PRIMITIVE_TYPE =~ MULT.DSP.*}]]
puts $summary_fh " LUTs: $lut_used / 134600"
puts $summary_fh " FFs: $ff_used / 269200"
puts $summary_fh " BRAM: $bram_used cells"
puts $summary_fh " DSP: $dsp_used cells"
puts $summary_fh ""
puts $summary_fh " Build 18 Baseline: LUTs=6088, FFs=8946, BRAM=16, DSP=140"
puts $summary_fh ""
# Route status
set unrouted [llength [get_nets -hierarchical -filter {ROUTE_STATUS == UNROUTED}]]
puts $summary_fh "--- Route ---"
puts $summary_fh " Unrouted nets: $unrouted"
puts $summary_fh ""
# MMCM usage (new for Build 19)
puts $summary_fh "--- MMCM Usage (Gap 7) ---"
set mmcm_count [llength [get_cells -hierarchical -filter {PRIMITIVE_TYPE =~ CLOCK.MMCM.*}]]
puts $summary_fh " MMCME2 used: $mmcm_count / 10"
puts $summary_fh " Expected: 1 (adc_clk_mmcm jitter cleaner)"
puts $summary_fh ""
# Bitstream
if {[file exists $bit_src]} {
puts $summary_fh "--- Bitstream ---"
puts $summary_fh " File: ${top_module}_${build_tag}.bit"
puts $summary_fh " Size: [file size $bit_src] bytes"
} else {
puts $summary_fh "--- Bitstream ---"
puts $summary_fh " WARNING: NOT GENERATED"
}
puts $summary_fh ""
# Signoff
puts $summary_fh "--- Final Signoff ---"
set signoff_pass 1
if {$wns < 0} {
puts $summary_fh " FAIL: WNS = $wns (negative slack)"
set signoff_pass 0
} else {
puts $summary_fh " PASS: WNS = $wns ns (no setup violations)"
}
if {$whs < 0} {
puts $summary_fh " FAIL: WHS = $whs (hold violation)"
set signoff_pass 0
} else {
puts $summary_fh " PASS: WHS = $whs ns (no hold violations)"
}
if {$tns != 0} {
puts $summary_fh " FAIL: TNS = $tns (total negative slack)"
set signoff_pass 0
} else {
puts $summary_fh " PASS: TNS = 0 ns"
}
if {$unrouted > 0} {
puts $summary_fh " FAIL: $unrouted unrouted nets"
set signoff_pass 0
} else {
puts $summary_fh " PASS: All nets routed"
}
if {[file exists $bit_src]} {
puts $summary_fh " PASS: Bitstream generated"
} else {
puts $summary_fh " FAIL: No bitstream"
set signoff_pass 0
}
puts $summary_fh ""
# Timing regression check vs Build 18
if {$wns < 0.062} {
puts $summary_fh " *** WARNING: WNS REGRESSED vs Build 18 (was +0.062 ns, now $wns ns) ***"
puts $summary_fh " *** Consider reverting MMCM changes per revert-safety policy ***"
}
if {$whs < 0.059} {
puts $summary_fh " *** WARNING: WHS REGRESSED vs Build 18 (was +0.059 ns, now $whs ns) ***"
}
if {$signoff_pass} {
puts $summary_fh " *** SIGNOFF: PASS ***"
} else {
puts $summary_fh " *** SIGNOFF: FAIL ***"
}
close $summary_fh
puts " Summary written to: ${report_dir}/00_build19_summary.txt"
# ==============================================================================
# 6. SDF + Timing Netlist (for post-route simulation)
# ==============================================================================
puts ""
puts "================================================================"
puts " Phase 5/5: SDF + Timing Netlist"
puts "================================================================"
write_verilog -force -mode timesim "${sim_dir}/post_impl_timesim.v"
write_sdf -force "${sim_dir}/post_impl_timesim.sdf"
close_design
open_run synth_1 -name synth_1
write_verilog -force -mode funcsim "${sim_dir}/post_synth_funcsim.v"
# ==============================================================================
# Done
# ==============================================================================
set build_total [expr {[clock seconds] - $build_start}]
set build_end [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S}]
puts ""
puts "================================================================"
puts " BUILD 19 COMPLETE"
puts "================================================================"
puts " Started: $build_timestamp"
puts " Finished: $build_end"
puts " Total time: ${build_total}s ([expr {$build_total/60}]m [expr {$build_total%60}]s)"
puts " Synth: ${synth_elapsed}s"
puts " Impl: ${impl_elapsed}s"
puts " Bitstream: ${bit_elapsed}s"
puts " Reports: $report_dir"
puts " Bitstream: ${bitstream_dir}/${top_module}_${build_tag}.bit"
puts " WNS: $wns ns | WHS: $whs ns | TNS: $tns ns"
puts " Build 18 baseline: WNS +0.062 | WHS +0.059"
if {$signoff_pass} {
puts " SIGNOFF: PASS"
} else {
puts " SIGNOFF: FAIL"
}
puts "================================================================"
close_project
puts "Done."
@@ -0,0 +1,483 @@
################################################################################
# build20_mmcm_creg.tcl
#
# AERIS-10 Build 20: MMCM XDC Clock-Name Fix + CIC Comb CREG Pipeline
# Target: XC7A200T-2FBG484I
# Design: radar_system_top
# Tag: v0.1.2-build18 + MMCM (Gap 7) + XDC fix + CIC CREG
#
# Changes vs Build 19:
# - FIX: adc_clk_mmcm.xdc — removed conflicting create_generated_clock
# (clk_400m_mmcm), replaced all references with Vivado auto-generated
# clk_mmcm_out0. This fixes the CDC false path that wasn't applying
# to the actual clk_mmcm_out0→clk_100m crossing (Build 19 WNS -0.011).
# - NEW: cic_decimator_4x_enhanced.v — explicit DSP48E1 for comb[0] with
# CREG=1/AREG=1/BREG=1/PREG=1. Absorbs the integrator_sampled_comb
# fabric register into DSP48 C-port pipeline, eliminating 0.643 ns
# fabric→DSP route delay (Build 18 tightest path, WNS +0.062).
#
# Expected impact:
# - WNS: should be >> +0.062 ns (CREG eliminates Build 18 critical path,
# XDC fix properly applies CDC false path)
# - DSP48E1: 140 → 142 (+2: one per CIC I/Q channel for comb_0_dsp)
# - LUT/FF: ~same (CREG replaces fabric FDREs with DSP internal registers)
#
# Generates ALL reports required for the 15-point Vivado TCL Build Report.
#
# Usage:
# vivado -mode batch -source build20_mmcm_creg.tcl \
# -log ~/PLFM_RADAR_work/vivado_project/build20.log \
# -journal ~/PLFM_RADAR_work/vivado_project/build20.jou
#
# Author: auto-generated for Jason Stone
# Date: 2026-03-19
################################################################################
# ==============================================================================
# 0. Configuration
# ==============================================================================
set project_name "aeris10_radar"
set project_dir "/home/jason-stone/PLFM_RADAR_work/vivado_project"
set rtl_dir "/home/jason-stone/PLFM_RADAR_work/PLFM_RADAR/9_Firmware/9_2_FPGA"
set top_module "radar_system_top"
set fpga_part "xc7a200tfbg484-2"
set report_dir "${project_dir}/reports_build20"
set sim_dir "${project_dir}/sim"
set bitstream_dir "${project_dir}/bitstream"
set build_tag "build20"
file mkdir $report_dir
file mkdir $sim_dir
file mkdir $bitstream_dir
# Record start time
set build_start [clock seconds]
set build_timestamp [clock format $build_start -format {%Y-%m-%d %H:%M:%S}]
puts "================================================================"
puts " AERIS-10 Build 20: MMCM XDC Fix + CIC CREG Pipeline"
puts " Target: $fpga_part"
puts " Top: $top_module"
puts " Reports: $report_dir"
puts " Started: $build_timestamp"
puts "================================================================"
# ==============================================================================
# 1. Project Creation + Source Files
# ==============================================================================
create_project $project_name $project_dir -part $fpga_part -force
set_property target_language Verilog [current_project]
# --- Add RTL sources ---
set rtl_files [list \
"${rtl_dir}/adc_clk_mmcm.v" \
"${rtl_dir}/ad9484_interface_400m.v" \
"${rtl_dir}/cdc_modules.v" \
"${rtl_dir}/chirp_lut_init.v" \
"${rtl_dir}/chirp_memory_loader_param.v" \
"${rtl_dir}/cic_decimator_4x_enhanced.v" \
"${rtl_dir}/dac_interface_single.v" \
"${rtl_dir}/ddc_400m.v" \
"${rtl_dir}/ddc_input_interface.v" \
"${rtl_dir}/doppler_processor.v" \
"${rtl_dir}/edge_detector.v" \
"${rtl_dir}/fft_1024_forward.v" \
"${rtl_dir}/fft_1024_inverse.v" \
"${rtl_dir}/fir_lowpass.v" \
"${rtl_dir}/frequency_matched_filter.v" \
"${rtl_dir}/latency_buffer.v" \
"${rtl_dir}/level_shifter_interface.v" \
"${rtl_dir}/lvds_to_cmos_400m.v" \
"${rtl_dir}/matched_filter_multi_segment.v" \
"${rtl_dir}/matched_filter_processing_chain.v" \
"${rtl_dir}/nco_400m_enhanced.v" \
"${rtl_dir}/plfm_chirp_controller.v" \
"${rtl_dir}/radar_mode_controller.v" \
"${rtl_dir}/radar_receiver_final.v" \
"${rtl_dir}/radar_system_top.v" \
"${rtl_dir}/radar_transmitter.v" \
"${rtl_dir}/range_bin_decimator.v" \
"${rtl_dir}/usb_data_interface.v" \
"${rtl_dir}/usb_packet_analyzer.v" \
"${rtl_dir}/xfft_32.v" \
"${rtl_dir}/fft_engine.v" \
]
set file_count 0
foreach f $rtl_files {
if {[file exists $f]} {
add_files -norecurse $f
incr file_count
} else {
puts " WARNING: RTL file not found: $f"
}
}
puts " Added $file_count RTL files"
# Add .mem files for BRAM initialization
set mem_files [glob -nocomplain "${rtl_dir}/*.mem"]
foreach f $mem_files {
add_files -norecurse $f
puts " Added MEM: [file tail $f]"
}
# Add constraints — main production XDC + MMCM supplementary XDC (FIXED)
add_files -fileset constrs_1 -norecurse "${project_dir}/synth_only.xdc"
add_files -fileset constrs_1 -norecurse "${rtl_dir}/constraints/adc_clk_mmcm.xdc"
set_property top $top_module [current_fileset]
set_property verilog_define {FFT_XPM_BRAM} [current_fileset]
# ==============================================================================
# 2. Synthesis
# ==============================================================================
puts ""
puts "================================================================"
puts " Phase 1/5: Synthesis"
puts "================================================================"
set_property STEPS.SYNTH_DESIGN.ARGS.FLATTEN_HIERARCHY rebuilt [get_runs synth_1]
set_property STEPS.SYNTH_DESIGN.ARGS.KEEP_EQUIVALENT_REGISTERS true [get_runs synth_1]
set synth_start [clock seconds]
launch_runs synth_1 -jobs 8
wait_on_run synth_1
set synth_elapsed [expr {[clock seconds] - $synth_start}]
set synth_status [get_property STATUS [get_runs synth_1]]
puts " Synthesis status: $synth_status"
puts " Synthesis time: ${synth_elapsed}s ([expr {$synth_elapsed/60}]m [expr {$synth_elapsed%60}]s)"
if {[string match "*ERROR*" $synth_status] || [string match "*FAILED*" $synth_status]} {
puts "CRITICAL: SYNTHESIS FAILED aborting build"
close_project
exit 1
}
# Post-synth timing (for comparison)
open_run synth_1 -name synth_1
report_timing_summary -delay_type min_max -max_paths 10 -file "${report_dir}/01_timing_post_synth.rpt"
report_utilization -file "${report_dir}/01_utilization_post_synth.rpt"
close_design
# ==============================================================================
# 3. Implementation (opt → place → phys_opt → route → post_route_phys_opt)
# ==============================================================================
puts ""
puts "================================================================"
puts " Phase 2/5: Implementation"
puts "================================================================"
# Aggressive directives for best timing
set_property STEPS.OPT_DESIGN.ARGS.DIRECTIVE Explore [get_runs impl_1]
set_property STEPS.PLACE_DESIGN.ARGS.DIRECTIVE ExtraTimingOpt [get_runs impl_1]
set_property STEPS.PHYS_OPT_DESIGN.ARGS.DIRECTIVE AggressiveExplore [get_runs impl_1]
set_property STEPS.PHYS_OPT_DESIGN.IS_ENABLED true [get_runs impl_1]
set_property STEPS.ROUTE_DESIGN.ARGS.DIRECTIVE AggressiveExplore [get_runs impl_1]
set_property STEPS.POST_ROUTE_PHYS_OPT_DESIGN.IS_ENABLED true [get_runs impl_1]
set_property STEPS.POST_ROUTE_PHYS_OPT_DESIGN.ARGS.DIRECTIVE AggressiveExplore [get_runs impl_1]
set impl_start [clock seconds]
launch_runs impl_1 -jobs 8
wait_on_run impl_1
set impl_elapsed [expr {[clock seconds] - $impl_start}]
set impl_status [get_property STATUS [get_runs impl_1]]
puts " Implementation status: $impl_status"
puts " Implementation time: ${impl_elapsed}s ([expr {$impl_elapsed/60}]m [expr {$impl_elapsed%60}]s)"
if {![string match "*Complete*" $impl_status]} {
puts "CRITICAL: IMPLEMENTATION FAILED: $impl_status"
close_project
exit 1
}
# ==============================================================================
# 4. Bitstream Generation
# ==============================================================================
puts ""
puts "================================================================"
puts " Phase 3/5: Bitstream Generation"
puts "================================================================"
set bit_start [clock seconds]
launch_runs impl_1 -to_step write_bitstream -jobs 8
wait_on_run impl_1
set bit_elapsed [expr {[clock seconds] - $bit_start}]
puts " Bitstream time: ${bit_elapsed}s"
# Copy bitstream to known location
set bit_src "${project_dir}/aeris10_radar.runs/impl_1/${top_module}.bit"
if {[file exists $bit_src]} {
file copy -force $bit_src "${bitstream_dir}/${top_module}_${build_tag}.bit"
puts " Bitstream: ${bitstream_dir}/${top_module}_${build_tag}.bit"
puts " Size: [file size $bit_src] bytes"
} else {
puts " WARNING: Bitstream file not found at $bit_src"
}
# ==============================================================================
# 5. Comprehensive Report Generation
# ==============================================================================
puts ""
puts "================================================================"
puts " Phase 4/5: Report Generation (15-point checklist)"
puts "================================================================"
# Open the routed design for reporting
open_run impl_1 -name impl_1
# --- Checklist Item 2: Timing Summary ---
puts " [2/15] Timing Summary..."
report_timing_summary -delay_type min_max -max_paths 100 \
-report_unconstrained \
-file "${report_dir}/02_timing_summary.rpt"
# --- Checklist Item 3: Clock Analysis ---
puts " [3/15] Clock Analysis..."
report_clocks -file "${report_dir}/03_clocks.rpt"
report_clock_interaction -delay_type min_max \
-file "${report_dir}/03_clock_interaction.rpt"
report_clock_networks -file "${report_dir}/03_clock_networks.rpt"
# --- Checklist Item 4: Utilization ---
puts " [4/15] Utilization..."
report_utilization -file "${report_dir}/04_utilization.rpt"
report_utilization -hierarchical -file "${report_dir}/04_utilization_hierarchical.rpt"
# --- Checklist Item 5: Power ---
puts " [5/15] Power Report..."
report_power -file "${report_dir}/05_power.rpt"
# --- Checklist Item 6: DRC ---
puts " [6/15] DRC..."
report_drc -file "${report_dir}/06_drc.rpt"
# --- Checklist Item 7: IO and Constraints ---
puts " [7/15] IO Report..."
report_io -file "${report_dir}/07_io.rpt"
report_timing -from [all_inputs] -to [all_outputs] -max_paths 20 \
-file "${report_dir}/07_io_timing.rpt"
# --- Checklist Item 8: Congestion Analysis ---
puts " [8/15] Congestion Analysis..."
report_design_analysis -congestion -file "${report_dir}/08_congestion.rpt"
# --- Checklist Item 9: Route Status ---
puts " [9/15] Route Status..."
report_route_status -file "${report_dir}/09_route_status.rpt"
# --- Checklist Item 10: Critical Paths ---
puts " [10/15] Critical Paths..."
report_timing -max_paths 20 -sort_by slack -nworst 5 \
-file "${report_dir}/10_critical_paths_setup.rpt"
report_timing -delay_type min -max_paths 20 -sort_by slack -nworst 5 \
-file "${report_dir}/10_critical_paths_hold.rpt"
report_high_fanout_nets -timing -load_type -max_nets 20 \
-file "${report_dir}/10_high_fanout_nets.rpt"
# --- Checklist Item 11: QoR Summary ---
puts " [11/15] QoR Summary..."
report_design_analysis -timing -file "${report_dir}/11_design_analysis_timing.rpt"
report_design_analysis -logic_level_distribution -file "${report_dir}/11_logic_level_dist.rpt"
report_methodology -file "${report_dir}/11_methodology.rpt"
# --- Checklist Item 12: CDC Analysis ---
puts " [12/15] CDC Analysis..."
report_cdc -details -file "${report_dir}/12_cdc.rpt"
# --- Checklist Item 13: Log Scan (captured separately in build log) ---
puts " [13/15] Log scan see build20.log"
# --- Additional reports ---
puts " [extra] Generating additional diagnostic reports..."
# report_exceptions can fail in Vivado 2025.2 — wrap in catch
if {[catch {report_exceptions -file "${report_dir}/13_exceptions.rpt"} err]} {
puts " WARNING: report_exceptions failed: $err"
puts " (Known Vivado 2025.2 issue non-critical)"
}
check_timing -verbose -file "${report_dir}/13_check_timing.rpt"
# Compile configuration summary into a single text file
set summary_fh [open "${report_dir}/00_build20_summary.txt" w]
puts $summary_fh "================================================================"
puts $summary_fh " AERIS-10 Build 20 MMCM XDC Fix + CIC CREG Pipeline"
puts $summary_fh "================================================================"
puts $summary_fh ""
puts $summary_fh "Build Tag: $build_tag"
puts $summary_fh "Build Timestamp: $build_timestamp"
puts $summary_fh "FPGA Part: $fpga_part"
puts $summary_fh "Top Module: $top_module"
puts $summary_fh "RTL Files: $file_count"
puts $summary_fh "Synth Status: $synth_status"
puts $summary_fh "Synth Time: ${synth_elapsed}s"
puts $summary_fh "Impl Status: $impl_status"
puts $summary_fh "Impl Time: ${impl_elapsed}s"
puts $summary_fh "Bitstream Time: ${bit_elapsed}s"
puts $summary_fh ""
# Extract key timing numbers
puts $summary_fh "--- Timing ---"
set wns [get_property STATS.WNS [current_design]]
set tns [get_property STATS.TNS [current_design]]
set whs [get_property STATS.WHS [current_design]]
set ths [get_property STATS.THS [current_design]]
set fail_ep [get_property STATS.TPWS [current_design]]
puts $summary_fh " WNS: $wns ns"
puts $summary_fh " TNS: $tns ns"
puts $summary_fh " WHS: $whs ns"
puts $summary_fh " THS: $ths ns"
puts $summary_fh ""
puts $summary_fh " Build 18 Baseline: WNS = +0.062 ns, WHS = +0.059 ns"
puts $summary_fh " Build 19 (FAILED): WNS = -0.011 ns, WHS = +0.055 ns"
puts $summary_fh " Delta WNS vs B18: [expr {$wns - 0.062}] ns"
puts $summary_fh " Delta WHS vs B18: [expr {$whs - 0.059}] ns"
puts $summary_fh ""
# Extract utilization
puts $summary_fh "--- Utilization ---"
set lut_used [llength [get_cells -hierarchical -filter {PRIMITIVE_TYPE =~ CLB.LUT.*}]]
set ff_used [llength [get_cells -hierarchical -filter {PRIMITIVE_TYPE =~ CLB.FF.*}]]
set bram_used [llength [get_cells -hierarchical -filter {PRIMITIVE_TYPE =~ BMEM.*}]]
set dsp_used [llength [get_cells -hierarchical -filter {PRIMITIVE_TYPE =~ MULT.DSP.*}]]
puts $summary_fh " LUTs: $lut_used / 134600"
puts $summary_fh " FFs: $ff_used / 269200"
puts $summary_fh " BRAM: $bram_used cells"
puts $summary_fh " DSP: $dsp_used cells"
puts $summary_fh ""
puts $summary_fh " Build 18 Baseline: LUTs=6088, FFs=8946, BRAM=16, DSP=140"
puts $summary_fh " Build 19: LUTs=6093, FFs=8949, BRAM=16, DSP=140"
puts $summary_fh " Expected Build 20: DSP=142 (+2 for comb_0_dsp I/Q)"
puts $summary_fh ""
# Route status
set unrouted [llength [get_nets -hierarchical -filter {ROUTE_STATUS == UNROUTED}]]
puts $summary_fh "--- Route ---"
puts $summary_fh " Unrouted nets: $unrouted"
puts $summary_fh ""
# MMCM usage
puts $summary_fh "--- MMCM Usage (Gap 7) ---"
set mmcm_count [llength [get_cells -hierarchical -filter {PRIMITIVE_TYPE =~ CLOCK.MMCM.*}]]
puts $summary_fh " MMCME2 used: $mmcm_count / 10"
puts $summary_fh " Expected: 1 (adc_clk_mmcm jitter cleaner)"
puts $summary_fh ""
# Bitstream
if {[file exists $bit_src]} {
puts $summary_fh "--- Bitstream ---"
puts $summary_fh " File: ${top_module}_${build_tag}.bit"
puts $summary_fh " Size: [file size $bit_src] bytes"
} else {
puts $summary_fh "--- Bitstream ---"
puts $summary_fh " WARNING: NOT GENERATED"
}
puts $summary_fh ""
# Signoff
puts $summary_fh "--- Final Signoff ---"
set signoff_pass 1
if {$wns < 0} {
puts $summary_fh " FAIL: WNS = $wns (negative slack)"
set signoff_pass 0
} else {
puts $summary_fh " PASS: WNS = $wns ns (no setup violations)"
}
if {$whs < 0} {
puts $summary_fh " FAIL: WHS = $whs (hold violation)"
set signoff_pass 0
} else {
puts $summary_fh " PASS: WHS = $whs ns (no hold violations)"
}
if {$tns != 0} {
puts $summary_fh " FAIL: TNS = $tns (total negative slack)"
set signoff_pass 0
} else {
puts $summary_fh " PASS: TNS = 0 ns"
}
if {$unrouted > 0} {
puts $summary_fh " FAIL: $unrouted unrouted nets"
set signoff_pass 0
} else {
puts $summary_fh " PASS: All nets routed"
}
if {[file exists $bit_src]} {
puts $summary_fh " PASS: Bitstream generated"
} else {
puts $summary_fh " FAIL: No bitstream"
set signoff_pass 0
}
puts $summary_fh ""
# Timing regression check vs Build 18
if {$wns < 0.062} {
puts $summary_fh " *** WARNING: WNS REGRESSED vs Build 18 (was +0.062 ns, now $wns ns) ***"
puts $summary_fh " *** Review critical paths CREG fix may not have helped ***"
}
if {$whs < 0.059} {
puts $summary_fh " *** WARNING: WHS REGRESSED vs Build 18 (was +0.059 ns, now $whs ns) ***"
}
if {$signoff_pass} {
puts $summary_fh " *** SIGNOFF: PASS ***"
} else {
puts $summary_fh " *** SIGNOFF: FAIL ***"
}
close $summary_fh
puts " Summary written to: ${report_dir}/00_build20_summary.txt"
# ==============================================================================
# 6. SDF + Timing Netlist (for post-route simulation)
# ==============================================================================
puts ""
puts "================================================================"
puts " Phase 5/5: SDF + Timing Netlist"
puts "================================================================"
write_verilog -force -mode timesim "${sim_dir}/post_impl_timesim.v"
write_sdf -force "${sim_dir}/post_impl_timesim.sdf"
close_design
open_run synth_1 -name synth_1
write_verilog -force -mode funcsim "${sim_dir}/post_synth_funcsim.v"
# ==============================================================================
# Done
# ==============================================================================
set build_total [expr {[clock seconds] - $build_start}]
set build_end [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S}]
puts ""
puts "================================================================"
puts " BUILD 20 COMPLETE"
puts "================================================================"
puts " Started: $build_timestamp"
puts " Finished: $build_end"
puts " Total time: ${build_total}s ([expr {$build_total/60}]m [expr {$build_total%60}]s)"
puts " Synth: ${synth_elapsed}s"
puts " Impl: ${impl_elapsed}s"
puts " Bitstream: ${bit_elapsed}s"
puts " Reports: $report_dir"
puts " Bitstream: ${bitstream_dir}/${top_module}_${build_tag}.bit"
puts " WNS: $wns ns | WHS: $whs ns | TNS: $tns ns"
puts " Build 18 baseline: WNS +0.062 | WHS +0.059"
puts " Build 19 (failed): WNS -0.011 | WHS +0.055"
if {$signoff_pass} {
puts " SIGNOFF: PASS"
} else {
puts " SIGNOFF: FAIL"
}
puts "================================================================"
close_project
puts "Done."
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