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 }