/ 9_Firmware / 9_2_FPGA / scripts / utils / insert_ila_probes.tcl
insert_ila_probes.tcl
  1  ################################################################################
  2  # insert_ila_probes.tcl
  3  #
  4  # AERIS-10 Radar FPGA — Post-Synthesis ILA Debug Core Insertion
  5  # Target: XC7A200T-2FBG484I
  6  # Design: radar_system_top (Build 16 frozen netlist)
  7  #
  8  # Usage:
  9  #   vivado -mode batch -source insert_ila_probes.tcl
 10  #
 11  # This script:
 12  #   1. Opens the post-synth DCP from Build 16
 13  #   2. Inserts 4 ILA debug cores across 2 clock domains
 14  #   3. Runs full implementation with Build 16 directives
 15  #   4. Generates bitstream, reports, and .ltx probe file
 16  #
 17  # ILA 0: ADC Capture          — 400 MHz (rx_inst/adc/clk_400m) — up to 9 bits
 18  # ILA 1: DDC Output           — 100 MHz                        — up to 37 bits
 19  # ILA 2: Matched Filter Ctrl  — 100 MHz                        — 4 signals
 20  # ILA 3: Doppler Output       — 100 MHz                        — up to 45 bits
 21  #
 22  # APPROACH: Uses get_nets with -hierarchical wildcards and get_nets -of
 23  # [get_pins ...] to resolve post-synthesis net names. All probe connections
 24  # are fault-tolerant — if a net cannot be found it is logged and skipped,
 25  # rather than aborting the build.
 26  #
 27  # Author: auto-generated for Jason Stone
 28  # Date:   2026-03-18
 29  ################################################################################
 30  
 31  # ==============================================================================
 32  # 0. Configuration — all paths and parameters in one place
 33  # ==============================================================================
 34  
 35  set script_dir     [file dirname [file normalize [info script]]]
 36  set project_root   [file normalize [file join $script_dir "../.."]]
 37  set project_base   [file join $project_root "build"]
 38  set synth_dcp      "${project_base}/aeris10_radar.runs/synth_1/radar_system_top.dcp"
 39  set synth_xdc      [file join $project_root "constraints" "xc7a200t_fbg484.xdc"]
 40  set output_dir     "${project_base}/aeris10_radar.runs/impl_ila"
 41  set top_module     "radar_system_top"
 42  set part           "xc7a200tfbg484-2"
 43  
 44  # Timestamp for output file naming
 45  set timestamp      [clock format [clock seconds] -format {%Y%m%d_%H%M%S}]
 46  set run_tag        "build16_ila_${timestamp}"
 47  
 48  # ILA parameters
 49  set ila_depth      4096
 50  set trigger_pos    512     ;# 512 pre-trigger samples
 51  
 52  # Global counter: total probes actually connected (for final summary)
 53  set total_probes_connected 0
 54  
 55  # ==============================================================================
 56  # 1. Helper procedures — fault-tolerant net resolution
 57  # ==============================================================================
 58  
 59  # Try a sequence of strategies to find a single net. Returns the net object
 60  # or empty string "" if nothing was found. Never errors out.
 61  #
 62  # Each element in $strategies is itself a list:
 63  #   { method arg }
 64  # where method is one of:
 65  #   "net"   — try exact path first (get_nets -quiet $arg), then hierarchical
 66  #   "pin"   — call  get_nets -quiet -of [get_pins -quiet $arg]
 67  #
 68  # Example:
 69  #   find_net { {net rx_inst/adc/adc_valid} {net *adc_valid*} }
 70  #
 71  proc find_net {strategies} {
 72      foreach strategy $strategies {
 73          set method [lindex $strategy 0]
 74          set arg    [lindex $strategy 1]
 75          switch $method {
 76              "net" {
 77                  # Try exact path first (works for fully-qualified hierarchical names)
 78                  set result [get_nets -quiet $arg]
 79                  # Fall back to hierarchical search (works for leaf names and wildcards)
 80                  if {[llength $result] == 0} {
 81                      set result [get_nets -quiet -hierarchical $arg]
 82                  }
 83              }
 84              "pin" {
 85                  set pins [get_pins -quiet $arg]
 86                  if {[llength $pins] == 0} {
 87                      # Also try hierarchical pin search
 88                      set pins [get_pins -quiet -hierarchical $arg]
 89                  }
 90                  if {[llength $pins] > 0} {
 91                      set result [get_nets -quiet -of $pins]
 92                  } else {
 93                      set result {}
 94                  }
 95              }
 96              default {
 97                  set result {}
 98              }
 99          }
100          if {[llength $result] > 0} {
101              # Return the first matching net
102              set chosen [lindex $result 0]
103              puts "  INFO: Resolved '$arg' ($method) -> $chosen"
104              return $chosen
105          }
106      }
107      return ""
108  }
109  
110  # Try a sequence of strategies to find a bus (vector) of nets.
111  # Returns a Tcl list of net objects. The list may be shorter than requested
112  # if some bits were optimised away.
113  #
114  # Each element in $strategies is { method pattern } where pattern may contain
115  # a literal '*' or a specific glob. The procedure evaluates ALL strategies
116  # as a batch and picks the first one that returns >= 1 net.
117  #
118  proc find_bus {strategies} {
119      foreach strategy $strategies {
120          set method [lindex $strategy 0]
121          set arg    [lindex $strategy 1]
122          switch $method {
123              "net" {
124                  # Try exact path first (works for fully-qualified hierarchical paths)
125                  set result [get_nets -quiet $arg]
126                  # Fall back to hierarchical search (leaf names, wildcards)
127                  if {[llength $result] == 0} {
128                      set result [get_nets -quiet -hierarchical $arg]
129                  }
130              }
131              "pin" {
132                  set pins [get_pins -quiet $arg]
133                  if {[llength $pins] == 0} {
134                      # Also try hierarchical pin search
135                      set pins [get_pins -quiet -hierarchical $arg]
136                  }
137                  if {[llength $pins] > 0} {
138                      set result [get_nets -quiet -of $pins]
139                  } else {
140                      set result {}
141                  }
142              }
143              default {
144                  set result {}
145              }
146          }
147          if {[llength $result] > 0} {
148              puts "  INFO: Bus resolved via '$arg' ($method) -> [llength $result] nets"
149              return $result
150          }
151      }
152      return {}
153  }
154  
155  # Connect a list of nets to the next available probe port on an ILA core.
156  # If the net list is empty, logs a warning and returns the same probe index
157  # (no probe port is consumed).
158  #
159  # ila_name:    e.g. u_ila_0
160  # probe_index: current probe port index (0 for PROBE0, etc.)
161  # net_list:    Tcl list of net objects to connect
162  # label:       human-readable description for log messages
163  #
164  # Returns the next available probe index.
165  #
166  proc connect_probe {ila_name probe_index net_list label} {
167      global total_probes_connected
168  
169      set width [llength $net_list]
170      if {$width == 0} {
171          puts "  WARNING: No nets found for '$label' — skipping probe${probe_index} on $ila_name"
172          return $probe_index
173      }
174  
175      puts "  INFO: Connecting $width nets to ${ila_name}/probe${probe_index} ($label)"
176  
177      if {$probe_index > 0} {
178          create_debug_port $ila_name probe
179      }
180  
181      set_property port_width $width [get_debug_ports ${ila_name}/probe${probe_index}]
182      connect_debug_port ${ila_name}/probe${probe_index} $net_list
183  
184      incr total_probes_connected $width
185      return [expr {$probe_index + 1}]
186  }
187  
188  # Deferred ILA creation — create the debug core, set properties, connect clock,
189  # and wire up all resolved probes in one shot. If no probes resolved, the ILA
190  # is NOT created at all (avoids dangling probe0 error).
191  #
192  # ila_name:     e.g. u_ila_0
193  # clk_net:      clock net object
194  # probe_list:   list of {label net_list} pairs (pre-resolved)
195  # depth:        ILA sample depth
196  #
197  # Returns the number of probe ports actually connected.
198  #
199  proc create_ila_deferred {ila_name clk_net probe_list depth} {
200      global total_probes_connected
201  
202      # Filter to only probes that have at least 1 net
203      set valid_probes {}
204      foreach probe_entry $probe_list {
205          set label    [lindex $probe_entry 0]
206          set net_list [lindex $probe_entry 1]
207          if {[llength $net_list] > 0} {
208              lappend valid_probes [list $label $net_list]
209          } else {
210              puts "  WARNING: No nets found for '$label' on $ila_name — skipping"
211          }
212      }
213  
214      if {[llength $valid_probes] == 0} {
215          puts "  WARNING: ALL probes failed for $ila_name — ILA core NOT created (avoiding dangling probe0)"
216          return 0
217      }
218  
219      # Now create the debug core — we know we have at least 1 probe
220      puts "  INFO: Creating $ila_name with [llength $valid_probes] probe(s)"
221      create_debug_core $ila_name ila
222      set_property ALL_PROBE_SAME_MU    true  [get_debug_cores $ila_name]
223      set_property ALL_PROBE_SAME_MU_CNT 2    [get_debug_cores $ila_name]
224      set_property C_ADV_TRIGGER        false [get_debug_cores $ila_name]
225      set_property C_DATA_DEPTH         $depth [get_debug_cores $ila_name]
226      set_property C_EN_STRG_QUAL       true  [get_debug_cores $ila_name]
227      set_property C_INPUT_PIPE_STAGES  0     [get_debug_cores $ila_name]
228      set_property C_TRIGIN_EN          false [get_debug_cores $ila_name]
229      set_property C_TRIGOUT_EN         false [get_debug_cores $ila_name]
230  
231      # Connect the clock
232      set_property port_width 1 [get_debug_ports ${ila_name}/clk]
233      connect_debug_port ${ila_name}/clk [get_nets $clk_net]
234  
235      # Connect each resolved probe
236      set probe_idx 0
237      foreach probe_entry $valid_probes {
238          set label    [lindex $probe_entry 0]
239          set net_list [lindex $probe_entry 1]
240          set probe_idx [connect_probe $ila_name $probe_idx $net_list $label]
241      }
242  
243      return $probe_idx
244  }
245  
246  # ==============================================================================
247  # 2. Open the synthesized checkpoint
248  # ==============================================================================
249  
250  puts "======================================================================"
251  puts " AERIS-10 ILA Insertion — Starting at [clock format [clock seconds]]"
252  puts "======================================================================"
253  
254  # Create output directory
255  file mkdir $output_dir
256  
257  # Open the frozen Build 13 post-synth DCP
258  puts "\nINFO: Opening post-synth DCP: $synth_dcp"
259  open_checkpoint $synth_dcp
260  
261  # Verify the part
262  set loaded_part [get_property PART [current_design]]
263  puts "INFO: Design part = $loaded_part"
264  if {$loaded_part ne $part} {
265      puts "WARNING: Expected part '$part', got '$loaded_part'. Continuing anyway."
266  }
267  
268  # Read the synthesis-only constraints (pin assignments, clocks, etc.)
269  puts "INFO: Reading XDC: $synth_xdc"
270  read_xdc $synth_xdc
271  
272  # ==============================================================================
273  # 3. Resolve clock nets
274  # ==============================================================================
275  
276  puts "\n--- Resolving clock nets ---"
277  
278  # 400 MHz clock — inside ADC interface (confirmed resolved to rx_inst/clk_400m)
279  set clk_400m_net [find_net {
280      {net  rx_inst/clk_400m}
281      {net  rx_inst/adc/clk_400m}
282      {net  *adc*/clk_400m}
283      {net  *clk_400m*}
284  }]
285  if {$clk_400m_net eq ""} {
286      error "FATAL: Cannot find 400 MHz clock net. Cannot insert ILA 0."
287  }
288  puts "INFO: 400 MHz clock net = $clk_400m_net"
289  
290  # 100 MHz system clock
291  set clk_100m_net [find_net {
292      {net  clk_100m_IBUF_BUFG}
293      {net  clk_100m_buf}
294      {net  clk_100m_BUFG}
295      {net  *clk_100m*}
296  }]
297  if {$clk_100m_net eq ""} {
298      error "FATAL: Cannot find 100 MHz clock net. Cannot insert ILA 1/2/3."
299  }
300  puts "INFO: 100 MHz clock net = $clk_100m_net"
301  
302  # ==============================================================================
303  # 4. ILA 0 — ADC Capture (400 MHz domain)
304  #
305  # Monitors raw ADC data at the CMOS interface output.
306  # Probes: ADC data [7:0] + ADC valid = up to 9 bits.
307  # 4096 samples at 400 MHz => ~10.24 us capture window.
308  #
309  # Uses DEFERRED creation: probes are resolved first, ILA is only created
310  # if at least one probe has nets.  This avoids dangling probe0 errors.
311  # ==============================================================================
312  
313  puts "\n====== ILA 0: ADC Capture (400 MHz) ======"
314  
315  # Probe 0: ADC data [7:0]
316  # Post-synth register name is adc_data_400m_reg_reg (double "reg" from synthesis).
317  # Bit 7 is inverted: adc_data_400m_reg_reg[7]_inv.
318  # Use pin-based discovery which catches both normal and _inv variants.
319  set adc_data_nets [find_bus {
320      {pin  rx_inst/adc/adc_data_400m_reg_reg[*]/Q}
321      {net  rx_inst/adc/adc_data_400m_reg_reg[*]}
322      {pin  rx_inst/adc/adc_data_400m_reg[*]/Q}
323      {net  rx_inst/adc/A[*]}
324      {pin  rx_inst/adc/adc_data_cmos_reg[*]/Q}
325      {net  rx_inst/adc/adc_data_400m[*]}
326      {net  rx_inst/adc/adc_data_cmos[*]}
327  }]
328  
329  # Probe 1: ADC valid
330  # Net confirmed as rx_inst/adc/adc_valid
331  # Pin confirmed as rx_inst/adc/adc_data_valid_400m_reg_reg/Q (double "reg")
332  set adc_valid_net [find_net {
333      {net  rx_inst/adc/adc_valid}
334      {pin  rx_inst/adc/adc_data_valid_400m_reg_reg/Q}
335      {pin  rx_inst/adc/adc_valid_reg/Q}
336      {net  *adc/adc_valid*}
337  }]
338  if {$adc_valid_net ne ""} {
339      set adc_valid_list [list $adc_valid_net]
340  } else {
341      set adc_valid_list {}
342  }
343  
344  # Deferred creation: only create ILA if at least 1 probe resolves
345  set ila0_probes [list \
346      [list "ADC data"  $adc_data_nets] \
347      [list "ADC valid" $adc_valid_list] \
348  ]
349  set ila0_count [create_ila_deferred u_ila_0 $clk_400m_net $ila0_probes $ila_depth]
350  puts "INFO: ILA 0 — $ila0_count probe ports on 400 MHz clock"
351  
352  # ==============================================================================
353  # 5. ILA 1 — DDC Output (100 MHz domain)
354  #
355  # Monitors the digital down-converter output after CIC+FIR decimation.
356  # Probes: DDC I [17:1] + DDC Q [17:1] + DDC valid = up to 35 bits.
357  # Bit 0 is optimized away in synthesis.
358  #
359  # Uses DEFERRED creation to avoid dangling probe0 errors.
360  # ==============================================================================
361  
362  puts "\n====== ILA 1: DDC Output (100 MHz) ======"
363  
364  # Probe 0: ddc_out_i — DDC I-channel baseband output
365  # Nets confirmed as rx_inst/ddc/ddc_out_i[1] through [17] (bit 0 optimized away)
366  # Use exact path WITHOUT -hierarchical, then fall back to pin-based and hierarchical
367  set ddc_i_nets [find_bus {
368      {net  rx_inst/ddc/ddc_out_i[*]}
369      {pin  rx_inst/ddc/ddc_out_i_reg[*]/Q}
370      {net  *ddc/ddc_out_i[*]}
371  }]
372  
373  # Probe 1: ddc_out_q — DDC Q-channel baseband output
374  # Nets confirmed as rx_inst/ddc/ddc_out_q[1] through [17] (bit 0 optimized away)
375  set ddc_q_nets [find_bus {
376      {net  rx_inst/ddc/ddc_out_q[*]}
377      {pin  rx_inst/ddc/ddc_out_q_reg[*]/Q}
378      {net  *ddc/ddc_out_q[*]}
379  }]
380  
381  # Probe 2: DDC output valid
382  # Confirmed nets: rx_inst/ddc_valid_q, rx_inst/ddc/baseband_valid_q
383  set ddc_valid_net [find_net {
384      {net  rx_inst/ddc_valid_q}
385      {net  rx_inst/ddc/baseband_valid_q}
386      {net  rx_inst/ddc/fir_valid}
387      {pin  rx_inst/ddc/baseband_valid_q_reg/Q}
388      {net  *ddc*valid*}
389  }]
390  if {$ddc_valid_net ne ""} {
391      set ddc_valid_list [list $ddc_valid_net]
392  } else {
393      set ddc_valid_list {}
394  }
395  
396  # Deferred creation: only create ILA if at least 1 probe resolves
397  set ila1_probes [list \
398      [list "DDC I"     $ddc_i_nets] \
399      [list "DDC Q"     $ddc_q_nets] \
400      [list "DDC valid" $ddc_valid_list] \
401  ]
402  set ila1_count [create_ila_deferred u_ila_1 $clk_100m_net $ila1_probes $ila_depth]
403  puts "INFO: ILA 1 — $ila1_count probe ports on 100 MHz clock"
404  
405  # ==============================================================================
406  # 6. ILA 2 — Matched Filter Control (100 MHz domain)
407  #
408  # Reduced probe set: only control/status signals that are confirmed to exist
409  # in the post-synthesis netlist. Data nets (pc_i_w, pc_q_w) do NOT exist
410  # post-synth due to hierarchy flattening.
411  #
412  # Probes: range_profile_valid + mf_valid_out + segment_request[1:0] = 4 bits.
413  #
414  # Uses DEFERRED creation to avoid dangling probe0 errors.
415  # ==============================================================================
416  
417  puts "\n====== ILA 2: Matched Filter Control (100 MHz) ======"
418  
419  # Probe 0: range_profile_valid
420  # Confirmed nets: rx_inst/mf_dual/range_profile_valid,
421  #                 rx_inst/mf_dual/m_f_p_c/range_profile_valid,
422  #                 rx_inst/range_decim/range_profile_valid
423  set rpv_net [find_net {
424      {net  rx_inst/mf_dual/range_profile_valid}
425      {net  rx_inst/mf_dual/m_f_p_c/range_profile_valid}
426      {net  rx_inst/range_decim/range_profile_valid}
427      {pin  rx_inst/mf_dual/range_profile_valid_reg/Q}
428      {net  *mf_dual/range_profile_valid*}
429  }]
430  if {$rpv_net ne ""} {
431      set rpv_list [list $rpv_net]
432  } else {
433      set rpv_list {}
434  }
435  
436  # Probe 1: mf_valid_out (internal MF output valid)
437  # Confirmed nets: rx_inst/mf_dual/m_f_p_c/mf_inst/mf_valid_out,
438  #                 rx_inst/mf_dual/m_f_p_c/mf_valid_in
439  set mfv_net [find_net {
440      {net  rx_inst/mf_dual/m_f_p_c/mf_inst/mf_valid_out}
441      {net  rx_inst/mf_dual/m_f_p_c/mf_valid_in}
442      {pin  rx_inst/mf_dual/m_f_p_c/mf_inst/mf_valid_out_reg/Q}
443      {net  *mf_inst/mf_valid_out*}
444  }]
445  if {$mfv_net ne ""} {
446      set mfv_list [list $mfv_net]
447  } else {
448      set mfv_list {}
449  }
450  
451  # Probe 2: segment_request[1:0] (confirmed in net dump)
452  set seg_nets [find_bus {
453      {pin  rx_inst/mf_dual/segment_request_reg[*]/Q}
454      {net  rx_inst/mf_dual/segment_request[*]}
455      {net  *mf_dual/segment_request[*]}
456  }]
457  
458  # Deferred creation: only create ILA if at least 1 probe resolves
459  set ila2_probes [list \
460      [list "MF range_profile_valid" $rpv_list] \
461      [list "MF mf_valid_out"        $mfv_list] \
462      [list "MF segment_request"     $seg_nets] \
463  ]
464  set ila2_count [create_ila_deferred u_ila_2 $clk_100m_net $ila2_probes $ila_depth]
465  puts "INFO: ILA 2 — $ila2_count probe ports on 100 MHz clock (control signals only)"
466  
467  # ==============================================================================
468  # 7. ILA 3 — Doppler Output (100 MHz domain)
469  #
470  # Monitors the Doppler processor output (post-FFT).
471  # Probes: doppler_data OBUF [31:0] + doppler_valid + doppler_bin [4:0]
472  #         + range_bin [5:0] + new_frame_pulse = up to 45 bits.
473  # Uses _OBUF net variants which are guaranteed to exist at top-level I/O.
474  #
475  # Uses DEFERRED creation to avoid dangling probe0 errors.
476  # ==============================================================================
477  
478  puts "\n====== ILA 3: Doppler Output (100 MHz) ======"
479  
480  # Probe 0: Doppler output data [31:0]
481  # Use _OBUF variants (top-level output buffer nets) which are guaranteed
482  # to exist. Fall back to register Q pins if OBUFs are not present.
483  set dop_data_nets [find_bus {
484      {net  dbg_doppler_data_OBUF[*]}
485      {pin  rx_inst/doppler_proc/doppler_output_reg[*]/Q}
486      {net  *doppler_data_OBUF[*]}
487      {net  *doppler_output[*]}
488  }]
489  
490  # Probe 1: Doppler valid
491  set dop_valid_net [find_net {
492      {net  dbg_doppler_valid_OBUF}
493      {net  rx_inst/doppler_proc/dbg_doppler_valid_OBUF}
494      {pin  rx_inst/doppler_proc/doppler_valid_reg/Q}
495      {net  *doppler_valid*}
496  }]
497  if {$dop_valid_net ne ""} {
498      set dop_valid_list [list $dop_valid_net]
499  } else {
500      set dop_valid_list {}
501  }
502  
503  # Probe 2: Doppler bin [4:0]
504  set dop_bin_nets [find_bus {
505      {pin  rx_inst/doppler_proc/doppler_bin_reg[*]/Q}
506      {net  rx_inst/doppler_bin_reg[*]}
507      {net  *doppler_bin_OBUF[*]}
508      {net  *doppler_bin[*]}
509  }]
510  
511  # Probe 3: Range bin [5:0]
512  set rng_bin_nets [find_bus {
513      {pin  rx_inst/doppler_proc/range_bin_reg[*]/Q}
514      {net  rx_inst/range_bin_reg[*]}
515      {net  *range_bin_OBUF[*]}
516      {net  *range_bin[*]}
517  }]
518  
519  # Probe 4: new_frame_pulse — frame synchronization
520  set frame_net [find_net {
521      {net  rx_inst/new_frame_pulse}
522      {net  *new_frame_pulse*}
523      {pin  rx_inst/new_frame_pulse_reg/Q}
524      {net  *frame_pulse*}
525  }]
526  if {$frame_net ne ""} {
527      set frame_list [list $frame_net]
528  } else {
529      set frame_list {}
530  }
531  
532  # Deferred creation: only create ILA if at least 1 probe resolves
533  set ila3_probes [list \
534      [list "Doppler data"      $dop_data_nets] \
535      [list "Doppler valid"     $dop_valid_list] \
536      [list "Doppler bin"       $dop_bin_nets] \
537      [list "Range bin"         $rng_bin_nets] \
538      [list "Frame sync pulse"  $frame_list] \
539  ]
540  set ila3_count [create_ila_deferred u_ila_3 $clk_100m_net $ila3_probes $ila_depth]
541  puts "INFO: ILA 3 — $ila3_count probe ports on 100 MHz clock"
542  
543  # ==============================================================================
544  # 8. Pre-implementation validation
545  # ==============================================================================
546  
547  puts "\n--- Pre-implementation ILA summary ---"
548  puts "INFO: Total probe bits connected across all ILAs: $total_probes_connected"
549  
550  # Sanity check: make sure we connected SOMETHING
551  if {$total_probes_connected == 0} {
552      error "FATAL: No probe nets were connected to any ILA. Check net names against the post-synth netlist."
553  }
554  
555  # List all debug cores for the log
556  set created_cores [get_debug_cores -quiet]
557  if {[llength $created_cores] > 0} {
558      foreach core $created_cores {
559          puts "  DEBUG CORE: $core"
560      }
561  } else {
562      puts "  WARNING: No debug cores found (this should not happen if total_probes_connected > 0)"
563  }
564  
565  # ==============================================================================
566  # 9. Implement the modified design (Build 13 directives)
567  # ==============================================================================
568  
569  puts "\n======================================================================"
570  puts " Implementation — matching Build 13 directives"
571  puts "======================================================================"
572  
573  # Save the post-ILA-insertion checkpoint for reference
574  set ila_dcp "${output_dir}/${top_module}_ila_inserted.dcp"
575  write_checkpoint -force $ila_dcp
576  puts "INFO: Saved ILA-inserted checkpoint: $ila_dcp"
577  
578  # --- opt_design (Explore) ---
579  puts "\n--- opt_design -directive Explore ---"
580  opt_design -directive Explore
581  
582  write_checkpoint -force "${output_dir}/${top_module}_opt.dcp"
583  
584  # --- place_design (ExtraTimingOpt) ---
585  puts "\n--- place_design -directive ExtraTimingOpt ---"
586  place_design -directive ExtraTimingOpt
587  
588  write_checkpoint -force "${output_dir}/${top_module}_placed.dcp"
589  
590  # Post-place timing estimate
591  report_timing_summary -file "${output_dir}/timing_post_place.rpt" -max_paths 20
592  
593  # --- phys_opt_design (AggressiveExplore) — post-place ---
594  puts "\n--- phys_opt_design -directive AggressiveExplore (post-place) ---"
595  phys_opt_design -directive AggressiveExplore
596  
597  write_checkpoint -force "${output_dir}/${top_module}_physopt.dcp"
598  
599  # --- route_design (AggressiveExplore) ---
600  puts "\n--- route_design -directive AggressiveExplore ---"
601  route_design -directive AggressiveExplore
602  
603  write_checkpoint -force "${output_dir}/${top_module}_routed.dcp"
604  
605  # Post-route timing check
606  report_timing_summary -file "${output_dir}/timing_post_route.rpt" -max_paths 50
607  
608  # --- post-route phys_opt_design (AggressiveExplore) ---
609  puts "\n--- phys_opt_design -directive AggressiveExplore (post-route) ---"
610  phys_opt_design -directive AggressiveExplore
611  
612  # Final routed + physopt checkpoint
613  set final_dcp "${output_dir}/${top_module}_postroute_physopt.dcp"
614  write_checkpoint -force $final_dcp
615  puts "INFO: Final checkpoint: $final_dcp"
616  
617  # ==============================================================================
618  # 10. Generate reports for comparison with Build 13
619  # ==============================================================================
620  
621  puts "\n======================================================================"
622  puts " Reports"
623  puts "======================================================================"
624  
625  # Timing summary (compare WNS/TNS/WHS/THS against Build 13)
626  report_timing_summary \
627      -file "${output_dir}/timing_summary_final.rpt" \
628      -max_paths 100 \
629      -report_unconstrained
630  
631  # Per-clock-domain timing (critical for multi-clock radar design)
632  report_timing \
633      -file "${output_dir}/timing_per_clock.rpt" \
634      -max_paths 20 \
635      -sort_by group
636  
637  # Utilization (expect ~2-4% increase from ILA cores on XC7A200T)
638  report_utilization \
639      -file "${output_dir}/utilization.rpt"
640  
641  report_utilization \
642      -file "${output_dir}/utilization_hierarchical.rpt" \
643      -hierarchical
644  
645  # DRC
646  report_drc \
647      -file "${output_dir}/drc.rpt"
648  
649  # Clock interaction / CDC (important with 400 MHz <-> 100 MHz crossing)
650  report_clock_interaction \
651      -file "${output_dir}/clock_interaction.rpt" \
652      -delay_type min_max
653  
654  # Clock networks (verify BUFG usage)
655  report_clock_networks \
656      -file "${output_dir}/clock_networks.rpt"
657  
658  # Power estimate
659  report_power \
660      -file "${output_dir}/power.rpt"
661  
662  # ILA core summary
663  report_debug_core \
664      -file "${output_dir}/debug_core_summary.rpt"
665  
666  puts "INFO: All reports written to $output_dir"
667  
668  # ==============================================================================
669  # 11. Write debug probes file (.ltx) for Vivado Hardware Manager
670  # ==============================================================================
671  
672  puts "\n--- Writing debug probes .ltx file ---"
673  
674  set ltx_file "${output_dir}/${top_module}.ltx"
675  write_debug_probes -force $ltx_file
676  puts "INFO: Debug probes file: $ltx_file"
677  
678  # Also copy the .ltx next to the bitstream for convenience
679  file copy -force $ltx_file "${output_dir}/debug_nets.ltx"
680  
681  # ==============================================================================
682  # 12. Generate bitstream
683  # ==============================================================================
684  
685  puts "\n======================================================================"
686  puts " Bitstream Generation"
687  puts "======================================================================"
688  
689  set bitstream_file "${output_dir}/${top_module}.bit"
690  
691  write_bitstream -force $bitstream_file
692  
693  puts "INFO: Bitstream written: $bitstream_file"
694  
695  # Also generate a .bin file for SPI flash programming if needed
696  write_cfgmem -force \
697      -format BIN \
698      -size 32 \
699      -interface SPIx4 \
700      -loadbit "up 0x0 $bitstream_file" \
701      "${output_dir}/${top_module}.bin"
702  
703  puts "INFO: SPI flash image: ${output_dir}/${top_module}.bin"
704  
705  # ==============================================================================
706  # 13. Final summary
707  # ==============================================================================
708  
709  puts "\n======================================================================"
710  puts " AERIS-10 ILA Insertion Complete"
711  puts "======================================================================"
712  puts ""
713  puts " Output directory:  $output_dir"
714  puts " Final DCP:         $final_dcp"
715  puts " Bitstream:         $bitstream_file"
716  puts " Debug probes:      $ltx_file"
717  puts " Run tag:           $run_tag"
718  puts ""
719  puts " ILA Cores Inserted (only cores with resolved probes):"
720  if {$ila0_count > 0} {
721      puts "   u_ila_0 : ADC Capture       (400 MHz, depth=$ila_depth, ${ila0_count} probes)"
722  } else {
723      puts "   u_ila_0 : ADC Capture       — SKIPPED (no probes resolved)"
724  }
725  if {$ila1_count > 0} {
726      puts "   u_ila_1 : DDC Output        (100 MHz, depth=$ila_depth, ${ila1_count} probes)"
727  } else {
728      puts "   u_ila_1 : DDC Output        — SKIPPED (no probes resolved)"
729  }
730  if {$ila2_count > 0} {
731      puts "   u_ila_2 : MF Control        (100 MHz, depth=$ila_depth, ${ila2_count} probes)"
732  } else {
733      puts "   u_ila_2 : MF Control        — SKIPPED (no probes resolved)"
734  }
735  if {$ila3_count > 0} {
736      puts "   u_ila_3 : Doppler Output    (100 MHz, depth=$ila_depth, ${ila3_count} probes)"
737  } else {
738      puts "   u_ila_3 : Doppler Output    — SKIPPED (no probes resolved)"
739  }
740  puts "   Total probe bits connected: $total_probes_connected"
741  puts ""
742  puts " Compare these reports against Build 13 baseline:"
743  puts "   - timing_summary_final.rpt  (WNS/TNS/WHS/THS)"
744  puts "   - utilization.rpt           (BRAM/LUT/FF overhead)"
745  puts "   - clock_interaction.rpt     (CDC paths)"
746  puts ""
747  puts " To load in Hardware Manager:"
748  puts "   1. Program bitstream: $bitstream_file"
749  puts "   2. Load probes file:  $ltx_file"
750  puts "   3. Set trigger position to $trigger_pos for pre/post capture"
751  puts ""
752  puts " Finished at [clock format [clock seconds]]"
753  puts "======================================================================"
754  
755  close_design