Hog v10.13.0
post-bitstream.tcl
Go to the documentation of this file.
1 #!/usr/bin/env tclsh
2 # Copyright 2018-2026 The University of Birmingham
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 # @file
17 # The post bitstream script copies binary files, reports and other files to the bin directory in your repository.
18 # This script is automatically integrated into the Vivado/Quartus workflow by the Create Project script.
19 
20 set old_path [pwd]
21 set tcl_path [file normalize "[file dirname [info script]]/.."]
22 source $tcl_path/hog.tcl
23 set repo_path [file normalize "$tcl_path/../../"]
24 
25 # Import tcllib
26 if {[IsLibero] || [IsDiamond]} {
27  if {[info exists env(HOG_TCLLIB_PATH)]} {
28  lappend auto_path $env(HOG_TCLLIB_PATH)
29  } else {
30  puts "ERROR: To run Hog with Microsemi Libero SoC, you need to define the HOG_TCLLIB_PATH variable."
31  return
32  }
33 }
34 
35 if {[info exists env(HOG_EXTERNAL_PATH)]} {
36  set ext_path $env(HOG_EXTERNAL_PATH)
37  Msg Info "Found environment variable HOG_EXTERNAL_PATH, setting path for external files to $ext_path..."
38 } else {
39  set ext_path ""
40 }
41 
42 set bin_dir [file normalize "$repo_path/bin"]
43 
44 if {[IsXilinx]} {
45  # Binary files are called .bit for ISE and for Vivado unless the chip is a Versal
46  set fw_file_ext "bit"
47 
48  # Vivado + PlanAhead
49  if {[IsISE]} {
50  # planAhead
51  set work_path [get_property DIRECTORY [get_runs impl_1]]
52  } else {
53  # Vivado
54  set work_path $old_path
55  if {[IsVersal [get_property PART [current_design]]]} {
56  #In Vivado if a Versal chip is used, the main binary file is called .pdi
57  set fw_file_ext "pdi"
58  }
59  }
60 
61  set main_files [lsort [glob -nocomplain "$work_path/*.$fw_file_ext"]]
62  if {[llength $main_files] > 1} {
63  #In case of segmented configuration on Versal there are 2 .pdi files: <top>_boot.pdi and <top>_pld.pdi
64  # Main file is the pld, so not the first
65  set main_file [file normalize [lindex $main_files 1]]
66  # Secondary file is the boot, so the first!
67  set secondary_file [file normalize [lindex $main_files 0]]
68  set main_file_suffix "_pld"
69  set secondary_file_suffix "_boot"
70  Msg Info "Found main and secondary binary file main: [file tail $main_file], secondary: [file tail $secondary_file]..."
71  # remove _pld suffix only at the end
72  set top_name [regsub $main_file_suffix\$ [file rootname [file tail $main_file]] ""]
73  if {[llength $main_files] > 2} {
74  Msg Warning "Multiple (more than 2) binary files found: $main_files."
75  }
76  } else {
77  set main_file [file normalize [lindex $main_files 0]]
78  set main_file_suffix ""
79  set secondary_file ""
80  set secondary_file_suffix ""
81  set top_name [file rootname [file tail $main_file]]
82  }
83 
84 
85  set proj_name [file tail [file normalize $work_path/../../]]
86  set proj_dir [file normalize "$work_path/../.."]
87 
88 
89  set additional_ext ".bin .ltx .bif .mmi"
90 
91  set xml_dir [file normalize "$work_path/../xml"]
92  set run_dir [file normalize "$work_path/.."]
93 } elseif {[IsQuartus]} {
94  # Quartus
95  ##nagelfar ignore Unknown variable
96  set proj_name [lindex $quartus(args) 1]
97  set proj_dir [pwd]
98  set xml_dir [file normalize "$repo_path/xml"]
99  set run_dir [file normalize "$proj_dir"]
100  set name [file rootname [file tail [file normalize [pwd]]]]
101  # programming object file
102  set pof_file [file normalize "$proj_dir/output_files/$proj_name.pof"]
103  # SRAM Object File
104  set sof_file [file normalize "$proj_dir/output_files/$proj_name.sof"]
105  # raw binary file
106  set rbf_file [file normalize "$proj_dir/output_files/$proj_name.rbf"]
107  #raw programming file
108  set rpd_file [file normalize "$proj_dir/output_files/$proj_name.rpd"]
109  # signal tap file
110  set stp_file [file normalize "$proj_dir/output_files/$proj_name.stp"]
111  #source and probes file
112  set spf_file [file normalize "$proj_dir/output_files/$proj_name.spf"]
113 } elseif {[IsLibero]} {
114  # Libero
115  ##nagelfar ignore Unknown variable
116  set proj_name $project
117  ##nagelfar ignore Unknown variable
118  set proj_dir $main_folder
119  set map_file [file normalize [lindex [glob -nocomplain "$proj_dir/synthesis/*.map"] 0]]
120  set sap_file [file normalize [lindex [glob -nocomplain "$proj_dir/synthesis/*.sap"] 0]]
121  set srd_file [file normalize [lindex [glob -nocomplain "$proj_dir/synthesis/*.srd"] 0]]
122  set srm_file [file normalize [lindex [glob -nocomplain "$proj_dir/synthesis/*.srm"] 0]]
123  set srs_file [file normalize [lindex [glob -nocomplain "$proj_dir/synthesis/*.srs"] 0]]
124  set srr_file [file normalize [lindex [glob -nocomplain "$proj_dir/synthesis/*.srr"] 0]]
125  set top_name [file rootname [file tail $srr_file]]
126  set stxt_files [glob -nocomplain "$proj_dir/synthesis/*.txt"]
127  set scsv_files [glob -nocomplain "$proj_dir/synthesis/*.csv"]
128  set slog_files [glob -nocomplain "$proj_dir/synthesis/*.log"]
129  set srpt_files [glob -nocomplain "$proj_dir/synthesis/*.rpt"]
130  set dtxt_files [glob -nocomplain "$proj_dir/designer/$top_name/*.txt"]
131  set dcsv_files [glob -nocomplain "$proj_dir/designer/$top_name/*.csv"]
132  set dlog_files [glob -nocomplain "$proj_dir/designer/$top_name/*.log"]
133  set drpt_files [glob -nocomplain "$proj_dir/designer/$top_name/*.rpt"]
134  set xml_dir [file normalize "$repo_path/xml"]
135 } elseif {[IsDiamond]} {
136  set proj_dir [file normalize "[pwd]/.."]
137  set proj_name [file tail $proj_dir]
138  set project $proj_name
139  set xml_dir [file normalize "$repo_path/xml"]
140  set main_file [file normalize "$proj_dir/Implementation0/${proj_name}_Implementation0"]
141 } else {
142  #tcl shell
143  set work_path $old_path
144  set fw_file [file normalize [lindex [glob -nocomplain "$work_path/*.bit"] 0]]
145  set proj_name [file tail [file normalize $work_path/../../]]
146  set proj_dir [file normalize "$work_path/../.."]
147 
148  set top_name [file rootname [file tail $fw_file]]
149 
150  set main_file [file normalize "$work_path/$top_name.bit"]
151  set bin_file [file normalize "$work_path/$top_name.bin"]
152  set ltx_file [file normalize "$work_path/$top_name.ltx"]
153 
154  set xml_dir [file normalize "$work_path/../xml"]
155  set run_dir [file normalize "$work_path/.."]
156 }
157 
158 set group_name [GetGroupName $proj_dir "$tcl_path/../.."]
159 # Go to repository path
160 cd $repo_path
161 
162 Msg Info "Evaluating Hog describe for $proj_name..."
163 set describe [GetHogDescribe [file normalize ./Top/$group_name/$proj_name] $repo_path]
164 Msg Info "Hog describe set to: $describe"
165 
166 set dst_dir [file normalize "$bin_dir/$group_name/$proj_name\-$describe"]
167 set dst_xml [file normalize "$dst_dir/xml"]
168 
169 Msg Info "Creating $dst_dir..."
170 file mkdir $dst_dir
171 
172 set ts [clock format [clock seconds] -format {%Y-%m-%d-%H-%M}]
173 
174 
175 # Vivado
176 if {[IsXilinx] && [file exists $main_file]} {
177  set dst_main [file normalize "$dst_dir/$proj_name$main_file_suffix\-$describe.$fw_file_ext"]
178  Msg Info "Copying main binary file $main_file into $dst_main..."
179  file copy -force $main_file $dst_main
180  if {$secondary_file != ""} {
181  set dst_secondary [file normalize "$dst_dir/$proj_name$secondary_file_suffix\-$describe.$fw_file_ext"]
182  Msg Info "Copying secondary binary file $secondary_file into $dst_secondary..."
183  file copy -force $secondary_file $dst_secondary
184  }
185 
186 
187  # Additional files
188  # In case of Segmented Configuration, there are 2 files per extension.
189 
190  set ltx_files {}
191  lappend ltx_files "$top_name.ltx"
192 
193  if {$main_file_suffix != ""} {
194  foreach e $additional_ext {
195  lappend new_ext $e
196  lappend new_ext $main_file_suffix$e
197  lappend new_ext $secondary_file_suffix$e
198  lappend ltx_files "$top_name$main_file_suffix.ltx"
199  lappend ltx_files "$top_name$secondary_file_suffix.ltx"
200  }
201  set additional_ext $new_ext
202  }
203 
204 
205  # LTX file for ILA, needs a special treatment...
206  foreach l $ltx_files {
207  set ltx_file "$work_path/$l"
208  Msg Info "Writing debug probes for $ltx_file..."
209  write_debug_probes -quiet $ltx_file
210  }
211 
212  foreach e $additional_ext {
213  set orig [file normalize "$work_path/$top_name$e"]
214  set dst [file normalize "$dst_dir/$proj_name\-$describe$e"]
215  if {[file exists $work_path/$top_name$e] == 1} {
216  Msg Info "Copying $orig file into $dst..."
217  file copy -force $orig $dst
218  } else {
219  Msg Debug "File: $orig not found."
220  }
221  }
222 } elseif {[IsQuartus]} {
223  set dst_pof [file normalize "$dst_dir/$name\-$describe.pof"]
224  set dst_sof [file normalize "$dst_dir/$name\-$describe.sof"]
225  set dst_rbf [file normalize "$dst_dir/$name\-$describe.rbf"]
226  set dst_rpd [file normalize "$dst_dir/$name\-$describe.rpd"]
227  set dst_stp [file normalize "$dst_dir/$name\-$describe.stp"]
228  set dst_spf [file normalize "$dst_dir/$name\-$describe.spf"]
229  set dst_xml [file normalize "$dst_dir/xml"]
230 
231  Msg Info "Evaluating differences with last commit..."
232  set found_uncommitted 0
233  set diff [Git diff]
234  if {$diff != ""} {
235  set found_uncommitted 1
236  Msg Warning "Found non committed changes:"
237  Msg Status "$diff"
238  set fp [open "$dst_dir/diff_postbistream.txt" w+]
239  puts $fp "$diff"
240  close $fp
241  }
242 
243  if {$found_uncommitted == 0} {
244  Msg Info "No uncommitted changes found."
245  }
246 
247  #pof file
248  if {[file exists $pof_file]} {
249  Msg Info "Copying pof file $pof_file into $dst_pof..."
250  file copy -force $pof_file $dst_pof
251  } else {
252  Msg Info "No pof file found: $pof_file, that is not a problem"
253  }
254 
255  #Reports
256  file mkdir $dst_dir/reports
257  set reps [glob -nocomplain "$proj_dir/output_files/*.rpt"]
258 
259  if {[file exists [lindex $reps 0]]} {
260  file copy -force {*}$reps $dst_dir/reports
261  } else {
262  Msg Warning "No reports found in $proj_dir/output_files subfolders"
263  }
264 
265  # sof File
266  if {[file exists $sof_file]} {
267  Msg Info "Copying sof file $sof_file into $dst_sof..."
268  file copy -force $sof_file $dst_sof
269  } else {
270  Msg Info "No sof file found: $sof_file, that is not a problem"
271  }
272 
273 
274  #rbf rpd
275  if {[file exists $rbf_file] || [file exists $rpd_file]} {
276  if {[file exists $rbf_file]} {
277  file copy -force $rbf_file $dst_rbf
278  }
279  if {[file exists $rpd_file]} {
280  file copy -force $rpd_file $dst_rpd
281  }
282  } else {
283  Msg Info "No rbf or rpd file found: this is not a problem"
284  }
285 
286  # stp and spf File
287  if {[file exists $stp_file] || [file exists $spf_file]} {
288  if {[file exists $stp_file]} {
289  file copy -force $stp_file $dst_stp
290  }
291  if {[file exists $spf_file]} {
292  file copy -force $spf_file $dst_spf
293  }
294  } else {
295  Msg Info "No stp or spf file found: that is not a problem"
296  }
297 } elseif {[IsLibero]} {
298  set dst_map [file normalize "$dst_dir/$project\-$describe.map"]
299  set dst_sap [file normalize "$dst_dir/$project\-$describe.sap"]
300  set dst_srd [file normalize "$dst_dir/$project\-$describe.srd"]
301  set dst_srm [file normalize "$dst_dir/$project\-$describe.srm"]
302  set dst_srs [file normalize "$dst_dir/$project\-$describe.srs"]
303  set dst_srr [file normalize "$dst_dir/$project\-$describe.srr"]
304  set dst_rpt [file normalize "$dst_dir/reports"]
305  set dst_xml [file normalize "$dst_dir/xml"]
306 
307  file mkdir $dst_dir/reports
308 
309  if {[file exists $map_file]} {
310  Msg Info "Copying map file $map_file into $dst_map..."
311  file copy -force $map_file $dst_map
312  }
313 
314  if {[file exists $sap_file]} {
315  Msg Info "Copying sap file $sap_file into $dst_sap..."
316  file copy -force $sap_file $dst_sap
317  }
318 
319  if {[file exists $srd_file]} {
320  Msg Info "Copying srd file $srd_file into $dst_srd..."
321  file copy -force $srd_file $dst_srd
322  }
323 
324  if {[file exists $srm_file]} {
325  Msg Info "Copying srm file $srm_file into $dst_srm..."
326  file copy -force $srm_file $dst_map
327  }
328 
329  if {[file exists $srs_file]} {
330  Msg Info "Copying srs file $srs_file into $dst_srs..."
331  file copy -force $srs_file $dst_srs
332  }
333 
334  if {[file exists $srr_file]} {
335  Msg Info "Copying srr file $srr_file into $dst_srr..."
336  file copy -force $srr_file $dst_srr
337  }
338 
339 
340  Msg Info "Copying synth txt files $stxt_files into $dst_rpt..."
341  file copy -force {*}$stxt_files $dst_rpt
342  Msg Info "Copying synth csv files $scsv_files into $dst_rpt..."
343  file copy -force {*}$scsv_files $dst_rpt
344  Msg Info "Copying synth log files $slog_files into $dst_rpt..."
345  file copy -force {*}$slog_files $dst_rpt
346  Msg Info "Copying synth rpt files $srpt_files into $dst_rpt..."
347  file copy -force {*}$srpt_files $dst_rpt
348 
349  Msg Info "Copying impl txt files $dtxt_files into $dst_rpt..."
350  file copy -force {*}$dtxt_files $dst_rpt
351  Msg Info "Copying impl csv files $dcsv_files into $dst_rpt..."
352  file copy -force {*}$dcsv_files $dst_rpt
353  Msg Info "Copying impl log files $dlog_files into $dst_rpt..."
354  file copy -force {*}$dlog_files $dst_rpt
355  Msg Info "Copying impl rpt files $drpt_files into $dst_rpt..."
356  file copy -force {*}$drpt_files $dst_rpt
357 } elseif {[IsDiamond]} {
358  set dst_main [file normalize "$dst_dir/$proj_name\-$describe.bit"]
359  Msg Info "Copying main binary file $main_file.bit into $dst_main..."
360  file copy -force $main_file.bit $dst_main
361  Msg Info "Copying binary generation log $main_file.bgn into $dst_dir/reports..."
362  file copy -force $main_file.bgn $dst_dir/reports
363 } else {
364  Msg CriticalWarning "Firmware binary file not found."
365 }
366 
367 
368 # IPbus XML
369 if {[file exists $xml_dir]} {
370  Msg Info "XML directory found, copying xml files from $xml_dir to $dst_xml..."
371  if {[file exists $dst_xml]} {
372  Msg Info "Directory $dst_xml exists, deleting it..."
373  file delete -force $dst_xml
374  }
375  file copy -force $xml_dir $dst_xml
376 }
377 
378 # Zynq XSA Export
379 if {[IsXilinx]} {
380  # Vivado
381  # automatically export for zynqs (checking via regex)
382  set export_xsa "NONE"
383  set part [get_property part [current_project]]
384 
385  # check for explicit EXPORT_XSA flag in hog.conf
386  set properties [ReadConf [lindex [GetConfFiles $repo_path/Top/$group_name/$proj_name] 0]]
387  if {[dict exists $properties "hog"]} {
388  set propDict [dict get $properties "hog"]
389  if {[dict exists $propDict "EXPORT_XSA"]} {
390  set export_xsa [dict get $propDict "EXPORT_XSA"]
391  }
392  }
393 
394  if {$export_xsa == "NONE"} {
395  set export_xsa false
396  if {([IsZynq $part] || [IsVersal $part])} {
397  Msg Info "SoC FPGA detected (Zynq or Versal), automatically enabling XSA file creation. \
398  To disable it, add 'EXPORT_XSA = false' in the \[hog\] section of hog.conf."
399  set export_xsa true
400  }
401  }
402 
403  if {[string compare [string tolower $export_xsa] "true"] == 0} {
404  set wrote_xsa 0
405 
406  # There is a bug in Vivado 2020.1, check for that version and warn
407  # that we can't export XSAs
408  regexp -- {Vivado v([0-9]{4}\.[0-9,A-z,_,\.]*) } [version] -> VIVADO_VERSION
409  if {[string compare "2020.1" $VIVADO_VERSION] == 0} {
410  Msg Warning "Vivado 2020.1, a patch must be applied to Vivado to export XSA Files, c.f. https://www.xilinx.com/support/answers/75210.html"
411  } else {
412  set dst_xsa [file normalize "$dst_dir/${proj_name}\-$describe.xsa"]
413  Msg Info "Generating XSA File at $dst_xsa"
414 
415  if {[IsVersal $part]} {
416  # Run user pre-platform file
417  set user_pre_platform_file "./Top/$group_name/$proj_name/pre-platform.tcl"
418  if {[file exists $user_pre_platform_file]} {
419  Msg Info "Sourcing user pre-platform file $user_pre_platform_file"
420  source $user_pre_platform_file
421  }
422 
423  # We do not want to touch the full_pdi_file property if there is a _boot .pdi file
424  if {$secondary_file == ""} {
425  set pdi_post_imp [file normalize "$work_path/$top_name.pdi"]
426  set_property platform.full_pdi_file $pdi_post_imp [current_project]
427  Msg Info "XSA file will be generated for Versal with this PDI: $pdi_post_imp"
428  write_hw_platform -fixed -force -file "$dst_xsa"
429  set wrote_xsa 1
430  }
431  Msg Warning "No XSA will be produced in post-bitream for segmented configuration mode. \
432  If you're running with the GUI, please type the following on the Tcl console: write_hw_platform -fixed -force -file $dst_xsa."
433  } else {
434  # We leave include bit also for Versal
435  write_hw_platform -include_bit -fixed -force -file "$dst_xsa"
436  set wrote_xsa 1
437  }
438  }
439 
440  if {$wrote_xsa == 1} {
441  # Determine project type from configuration file
442  set conf_file [lindex [GetConfFiles $repo_path/Top/$group_name/$proj_name] 0]
443  set is_vitis_classic 0
444  set is_vitis_unified 0
445 
446  if {[file exists $conf_file]} {
447  set ide_name_and_ver [GetIDEFromConf $conf_file]
448  set ide_name [string tolower [lindex $ide_name_and_ver 0]]
449  if {[string match "*vitis_classic*" $ide_name] || [string match "*vivado_vitis_classic*" $ide_name]} {
450  set is_vitis_classic 1
451  } elseif {[string match "*vitis_unified*" $ide_name] || [string match "*vivado_vitis_unified*" $ide_name]} {
452  set is_vitis_unified 1
453  }
454  }
455 
456  if {$is_vitis_classic || $is_vitis_unified} {
457  Msg Info "XSA file written to $dst_xsa"
458 
459  set full_proj_name [file join $group_name $proj_name]
460 
461  # Determine command based on project type
462  if {$is_vitis_classic} {
463  set vitis_cmd "xsct $tcl_path/launch.tcl CW -xsa $dst_xsa -vitis_only $full_proj_name"
464  set vitis_type "Vitis Classic"
465  set error_prefix "xsct (vitis classic)"
466  } elseif {$is_vitis_unified} {
467  set vitis_cmd "$tcl_path/launch.tcl CW -xsa $dst_xsa -vitis_only $full_proj_name"
468  set vitis_type "Vitis Unified"
469  set error_prefix "vivado (for vitis unified)"
470  } else {
471  Msg Error "No Vitis project type found."
472  return
473  }
474 
475  Msg Info "Running $vitis_type to create elf file with cmd: $vitis_cmd"
476  set ret [catch {exec -ignorestderr {*}$vitis_cmd >@ stdout} result]
477  if {$ret != 0} {
478  Msg Error "$error_prefix returned an error state."
479  }
480 
481  # Copy ELF files from Vitis build output into bin directory
482  # Extract app names preserving original case from hog.conf,
483  # since Vitis creates directories matching the original case
484  set app_names [list]
485  foreach prop_key [dict keys $properties] {
486  if {[regexp {^app:(.+)$} $prop_key -> raw_app_name]} {
487  lappend app_names [string trim $raw_app_name]
488  }
489  }
490  foreach app_name $app_names {
491  if {$is_vitis_unified} {
492  set elf_src [file normalize "$repo_path/Projects/$full_proj_name/vitis_unified/$app_name/build/$app_name.elf"]
493  } else {
494  set elf_src [file normalize "$repo_path/Projects/$full_proj_name/vitis_classic/$app_name/Release/$app_name.elf"]
495  }
496  set elf_dst [file normalize "$dst_dir/${proj_name}\-${app_name}\-$describe.elf"]
497  if {[file exists $elf_src]} {
498  Msg Info "Copying ELF $elf_src into $elf_dst..."
499  file copy -force $elf_src $elf_dst
500  } else {
501  Msg Warning "ELF file not found: $elf_src"
502  }
503  }
504 
505  # Process ELF files and update bitstream with memory content
506  set mmi_file [file normalize "$dst_dir/${proj_name}\-$describe.mmi"]
507  GenerateBootArtifacts $properties $repo_path $proj_dir $dst_dir $proj_name $describe $dst_main $mmi_file
508  }
509  }
510 
511  }
512 }
513 
514 
515 # Run user post-bitstream file
516 set user_post_bitstream_file "./Top/$group_name/$proj_name/post-bitstream.tcl"
517 if {[file exists $user_post_bitstream_file]} {
518  Msg Info "Sourcing user post-bitstream file $user_post_bitstream_file"
519  source $user_post_bitstream_file
520 }
521 
522 cd $old_path
523 Msg Info "All done."