/ 9_Firmware / 9_2_FPGA / scripts / utils / program_fpga.tcl
program_fpga.tcl
  1  # program_fpga.tcl
  2  # AERIS-10 Radar FPGA Bitstream Programming Flow
  3  # Target FPGA: XC7A200T-2FBG484I (Artix-7)
  4  #
  5  # Programs the radar_system_top bitstream onto the target device via
  6  # Vivado Hardware Manager and optionally loads ILA debug probes.
  7  #
  8  # Usage:
  9  #   Interactive: source program_fpga.tcl
 10  #   Batch:       vivado -mode batch -source program_fpga.tcl
 11  #   With args:   vivado -mode batch -source program_fpga.tcl -tclargs \
 12  #                    -server 192.168.1.50 -port 3121 -no_probes
 13  #
 14  # Arguments:
 15  #   -server <hostname>   Hardware server hostname (default: localhost)
 16  #   -port   <port>       Hardware server port     (default: 3121)
 17  #   -bit    <path>       Bitstream file path      (overrides default)
 18  #   -ltx    <path>       Debug probes file path   (overrides default)
 19  #   -no_probes           Skip loading debug probes even if .ltx exists
 20  #   -force               Program even if device ID doesn't match expected
 21  
 22  # ============================================================================
 23  # DEFAULTS
 24  # ============================================================================
 25  
 26  set default_server   "localhost"
 27  set default_port     3121
 28  set script_dir       [file dirname [file normalize [info script]]]
 29  set project_root     [file normalize [file join $script_dir "../.."]]
 30  set default_bit      [file join $project_root "build" "bitstream" "radar_system_top_build21.bit"]
 31  set default_ltx      [file join $project_root "build" "aeris10_radar.runs" "impl_ila" "radar_system_top.ltx"]
 32  set expected_part    "xc7a200t"
 33  set expected_pkg     "fbg484"
 34  
 35  # ============================================================================
 36  # ARGUMENT PARSING
 37  # ============================================================================
 38  
 39  proc parse_args {} {
 40      global argc argv
 41      global default_server default_port default_bit default_ltx
 42      global hw_server_host hw_server_port bitstream_path probes_path
 43      global skip_probes force_program
 44  
 45      set hw_server_host $default_server
 46      set hw_server_port $default_port
 47      set bitstream_path $default_bit
 48      set probes_path    $default_ltx
 49      set skip_probes    0
 50      set force_program  0
 51  
 52      # In batch mode, argv comes from -tclargs; in interactive it may be empty
 53      if {[info exists argv]} {
 54          set args $argv
 55      } else {
 56          set args {}
 57      }
 58  
 59      set i 0
 60      while {$i < [llength $args]} {
 61          set arg [lindex $args $i]
 62          switch -exact -- $arg {
 63              "-server" {
 64                  incr i
 65                  set hw_server_host [lindex $args $i]
 66              }
 67              "-port" {
 68                  incr i
 69                  set hw_server_port [lindex $args $i]
 70              }
 71              "-bit" {
 72                  incr i
 73                  set bitstream_path [lindex $args $i]
 74              }
 75              "-ltx" {
 76                  incr i
 77                  set probes_path [lindex $args $i]
 78              }
 79              "-no_probes" {
 80                  set skip_probes 1
 81              }
 82              "-force" {
 83                  set force_program 1
 84              }
 85              default {
 86                  puts "WARNING: Unknown argument '$arg' — ignoring."
 87              }
 88          }
 89          incr i
 90      }
 91  }
 92  
 93  # ============================================================================
 94  # UTILITY PROCEDURES
 95  # ============================================================================
 96  
 97  proc log_info {msg} {
 98      puts "INFO:  \[AERIS-10\] $msg"
 99  }
100  
101  proc log_warn {msg} {
102      puts "WARN:  \[AERIS-10\] $msg"
103  }
104  
105  proc log_error {msg} {
106      puts "ERROR: \[AERIS-10\] $msg"
107  }
108  
109  proc log_sep {} {
110      puts [string repeat "=" 72]
111  }
112  
113  # Print a key-value pair aligned for the summary table
114  proc log_kv {key value} {
115      puts [format "  %-28s : %s" $key $value]
116  }
117  
118  # ============================================================================
119  # PROGRAMMING FLOW
120  # ============================================================================
121  
122  proc program_fpga {} {
123      global hw_server_host hw_server_port bitstream_path probes_path
124      global skip_probes force_program expected_part expected_pkg
125  
126      set result "FAIL"
127      set probes_loaded "N/A"
128      set device_name "unknown"
129  
130      log_sep
131      log_info "AERIS-10 Radar FPGA Programming Flow"
132      log_info "Timestamp: [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S}]"
133      log_sep
134  
135      # ------------------------------------------------------------------
136      # Step 1: Validate bitstream file exists
137      # ------------------------------------------------------------------
138      log_info "Step 1/7: Validating bitstream file..."
139  
140      if {![file exists $bitstream_path]} {
141          log_error "Bitstream not found: $bitstream_path"
142          log_error "Ensure the build completed successfully and the file is accessible."
143          return -code error "BITSTREAM_NOT_FOUND"
144      }
145      set bit_size [file size $bitstream_path]
146      log_info "Bitstream: $bitstream_path ([expr {$bit_size / 1024}] KB)"
147  
148      # ------------------------------------------------------------------
149      # Step 2: Open Hardware Manager
150      # ------------------------------------------------------------------
151      log_info "Step 2/7: Opening Vivado Hardware Manager..."
152  
153      if {[catch {open_hw_manager} err]} {
154          # Hardware manager may already be open in interactive mode
155          log_warn "open_hw_manager returned: $err (may already be open)"
156      }
157  
158      # ------------------------------------------------------------------
159      # Step 3: Connect to hardware server
160      # ------------------------------------------------------------------
161      log_info "Step 3/7: Connecting to hw_server at ${hw_server_host}:${hw_server_port}..."
162  
163      if {[catch {
164          connect_hw_server -url ${hw_server_host}:${hw_server_port} -allow_non_jtag
165      } err]} {
166          log_error "Failed to connect to hardware server: $err"
167          log_error "Troubleshooting:"
168          log_error "  1. Ensure hw_server is running: hw_server -d"
169          log_error "  2. Check that the JTAG cable is connected and powered"
170          log_error "  3. Verify firewall allows port $hw_server_port"
171          log_error "  4. For remote: vivado -mode batch -source program_fpga.tcl -tclargs -server <ip>"
172          return -code error "HW_SERVER_CONNECT_FAILED"
173      }
174      log_info "Connected to hw_server."
175  
176      # ------------------------------------------------------------------
177      # Step 4: Open JTAG target and auto-detect device
178      # ------------------------------------------------------------------
179      log_info "Step 4/7: Scanning JTAG chain for target device..."
180  
181      if {[catch {
182          open_hw_target
183      } err]} {
184          log_error "Failed to open hardware target: $err"
185          log_error "No JTAG targets found. Check cable and board power."
186          catch {disconnect_hw_server}
187          return -code error "NO_HW_TARGET"
188      }
189  
190      # Enumerate devices on the chain
191      set hw_devices [get_hw_devices]
192      if {[llength $hw_devices] == 0} {
193          log_error "No devices detected on JTAG chain."
194          catch {close_hw_target}
195          catch {disconnect_hw_server}
196          return -code error "NO_DEVICES"
197      }
198  
199      log_info "Devices on JTAG chain: $hw_devices"
200  
201      # ------------------------------------------------------------------
202      # Step 5: Identify and verify the target XC7A200T
203      # ------------------------------------------------------------------
204      log_info "Step 5/7: Verifying target device is $expected_part..."
205  
206      set target_device ""
207      foreach dev $hw_devices {
208          set part_name [string tolower [get_property PART $dev]]
209          log_info "  Found device: $dev (part: $part_name)"
210  
211          if {[string match "${expected_part}*" $part_name]} {
212              set target_device $dev
213              set device_name $part_name
214              break
215          }
216      }
217  
218      if {$target_device eq ""} {
219          if {$force_program} {
220              log_warn "Expected $expected_part not found. -force specified, using first device."
221              set target_device [lindex $hw_devices 0]
222              set device_name [get_property PART $target_device]
223          } else {
224              log_error "Target device $expected_part not found on JTAG chain."
225              log_error "Found devices: $hw_devices"
226              log_error "Use -force to program a different device."
227              catch {close_hw_target}
228              catch {disconnect_hw_server}
229              return -code error "DEVICE_MISMATCH"
230          }
231      }
232  
233      # Make this the current device
234      current_hw_device $target_device
235      log_info "Target device selected: $target_device ($device_name)"
236  
237      # ------------------------------------------------------------------
238      # Step 6: Program the bitstream
239      # ------------------------------------------------------------------
240      log_info "Step 6/7: Programming bitstream..."
241  
242      # Set the programming file
243      set_property PROGRAM.FILE $bitstream_path $target_device
244  
245      # If probes file exists and not skipped, associate it now so ILA cores
246      # are recognized immediately after programming
247      if {!$skip_probes && [file exists $probes_path]} {
248          log_info "Associating debug probes: $probes_path"
249          set_property PROBES.FILE $probes_path $target_device
250      }
251  
252      # Execute programming
253      if {[catch {
254          program_hw_devices $target_device
255      } err]} {
256          log_error "Bitstream programming FAILED: $err"
257          log_error "Possible causes:"
258          log_error "  - Bitstream built for a different part/package"
259          log_error "  - JTAG communication error (check cable)"
260          log_error "  - Board power supply issue"
261          log_error "  - Bitstream file corruption"
262          catch {close_hw_target}
263          catch {disconnect_hw_server}
264          return -code error "PROGRAMMING_FAILED"
265      }
266  
267      # ------------------------------------------------------------------
268      # Step 7: Verify DONE pin
269      # ------------------------------------------------------------------
270      log_info "Step 7/7: Verifying DONE pin status..."
271  
272      # Refresh device status registers
273      refresh_hw_device $target_device
274  
275      set done_status [get_property REGISTER.CONFIG_STATUS.DONE $target_device]
276      set init_status [get_property REGISTER.CONFIG_STATUS.INIT_COMPLETE $target_device]
277  
278      if {$done_status == 1} {
279          log_info "DONE pin is HIGH — device configured successfully."
280          set result "PASS"
281      } else {
282          log_error "DONE pin is LOW — configuration may have failed."
283          log_error "CONFIG_STATUS.INIT_COMPLETE: $init_status"
284          set result "FAIL"
285      }
286  
287      # ------------------------------------------------------------------
288      # Optional: Load debug probes (ILA)
289      # ------------------------------------------------------------------
290      if {!$skip_probes && [file exists $probes_path]} {
291          log_info "Loading ILA debug probes..."
292  
293          if {[catch {
294              # Probes were already associated before programming.
295              # Refresh to enumerate ILA cores.
296              refresh_hw_device $target_device
297  
298              set ila_cores [get_hw_ilas -quiet]
299              if {[llength $ila_cores] > 0} {
300                  log_info "ILA cores detected: [llength $ila_cores]"
301                  foreach ila $ila_cores {
302                      set ila_name [get_property DESCRIPTION $ila]
303                      set ila_depth [get_property CONTROL.DATA_DEPTH $ila]
304                      log_info "  $ila : depth=$ila_depth"
305                  }
306                  set probes_loaded "YES ([llength $ila_cores] ILAs)"
307              } else {
308                  log_warn "No ILA cores found in the design. Probes file may not match bitstream."
309                  set probes_loaded "NO (no ILA cores detected)"
310              }
311          } err]} {
312              log_warn "Debug probe loading encountered an issue: $err"
313              set probes_loaded "ERROR"
314          }
315      } elseif {$skip_probes} {
316          set probes_loaded "SKIPPED (-no_probes)"
317      } elseif {![file exists $probes_path]} {
318          log_info "No .ltx probes file found at: $probes_path"
319          set probes_loaded "NO (.ltx not found)"
320      }
321  
322      # ------------------------------------------------------------------
323      # Summary
324      # ------------------------------------------------------------------
325      log_sep
326      log_info "PROGRAMMING SUMMARY"
327      log_sep
328      log_kv "Result"            $result
329      log_kv "Target Device"     $device_name
330      log_kv "Bitstream"         [file tail $bitstream_path]
331      log_kv "Bitstream Size"    "[expr {[file size $bitstream_path] / 1024}] KB"
332      log_kv "DONE Pin"          [expr {$done_status == 1 ? "HIGH (OK)" : "LOW (FAIL)"}]
333      log_kv "INIT_COMPLETE"     $init_status
334      log_kv "Debug Probes"      $probes_loaded
335      log_kv "HW Server"         "${hw_server_host}:${hw_server_port}"
336      log_kv "Timestamp"         [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S}]
337      log_sep
338  
339      if {$result eq "FAIL"} {
340          return -code error "PROGRAMMING_VERIFICATION_FAILED"
341      }
342  
343      return $result
344  }
345  
346  # ============================================================================
347  # MAIN ENTRY POINT
348  # ============================================================================
349  
350  parse_args
351  
352  log_info "Configuration:"
353  log_kv "HW Server"   "${hw_server_host}:${hw_server_port}"
354  log_kv "Bitstream"   $bitstream_path
355  log_kv "Probes"      [expr {$skip_probes ? "DISABLED" : $probes_path}]
356  log_kv "Force Mode"  [expr {$force_program ? "YES" : "NO"}]
357  
358  if {[catch {program_fpga} err]} {
359      log_error "Programming flow terminated with error: $err"
360      # In batch mode, exit with non-zero status
361      if {[string match "batch" [get_property MODE [current_hw_server -quiet]]]} {
362          exit 1
363      }
364  } else {
365      log_info "Programming flow completed successfully."
366  }