18 if {![
info exists tcl_path]} {
19 set tcl_path [
file normalize "[
file dirname [
info script]]"]
22 set hog_path [
file normalize "[
file dirname [
info script]]"]
24 source "$hog_path/utils/Logger.tcl"
28 set CI_STAGES {"generate_project" "simulate_project"}
29 set CI_PROPS {"-synth_only"}
38 proc AddFile {file fileset} {
40 add_files -norecurse -fileset $fileset $file
50 proc AddHogFiles {libraries properties filesets} {
51 Msg Info "Adding source files to project..."
52 Msg Debug "Filesets: $filesets"
53 Msg Debug "Libraries: $libraries"
54 Msg Debug "Properties: $properties"
57 set synth_conf_command "organize_tool_files -tool {SYNTHESIZE} -input_type {constraint}"
59 set timing_conf_command "organize_tool_files -tool {VERIFYTIMING} -input_type {constraint}"
61 set place_conf_command "organize_tool_files -tool {PLACEROUTE} -input_type {constraint}"
70 if {![
IsVivado] || ([
info exists globalSettings::vitis_only_pass] && $globalSettings::vitis_only_pass == 1)} {
74 set ws_apps [
GetVitisApps "$globalSettings::build_dir/vitis_unified" \
75 "$globalSettings::repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"]
79 foreach fileset [dict keys $filesets] {
80 Msg Debug "Fileset: $fileset"
83 if {[
string equal [get_filesets -quiet $fileset] ""]} {
85 create_fileset -simset $fileset
88 current_fileset -simset [get_filesets $fileset]
89 set simulation [get_filesets $fileset]
91 set_property -name {$simulator.compile.vhdl_syntax} -value {2008} -objects $simulation
93 set_property SOURCE_SET sources_1 $simulation
97 set libs_in_fileset [
DictGet $filesets $fileset]
98 if {[
IsInList "ips.src" $libs_in_fileset]} {
103 if {$ws_apps ne ""} {
104 dict for {app_name app_config} $ws_apps {
105 set app_lib [string tolower "app_$app_name\.src"]
106 if {![IsInList $app_lib $libs_in_fileset 0 1]} {
107 Msg Warning "App '$app_name' exists in workspace but no corresponding sourcefile '$app_lib' found. \
108 Make sure you have a list file with the correct naming convention: \[app_<app_name>\.src\]"
116 set app_files_dict [dict create]
120 set reapply_targets [dict create]
121 foreach lib $libs_in_fileset {
122 Msg Debug "lib: $lib \n"
123 set lib_files [
DictGet $libraries $lib]
124 Msg Debug "Files in $lib: $lib_files"
125 set rootlib [
file rootname [
file tail $lib]]
126 set ext [
file extension $lib]
127 Msg Debug "lib: $lib ext: $ext fileset: $fileset"
129 if {[
IsXilinx] && !([
info exists globalSettings::vitis_only_pass] && $globalSettings::vitis_only_pass == 1)} {
131 if {[
string match "app_*" [
string tolower $lib]]} {
134 Msg Debug "Adding $lib to $fileset"
135 add_files -norecurse -fileset $fileset $lib_files
137 foreach f $lib_files {
138 set file_obj [get_files -of_objects [get_filesets $fileset] [list "*$f"]]
140 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
141 set_property -name "library" -value $rootlib -objects $file_obj
145 set props [
DictGet $properties $f]
146 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
148 if {[lsearch -inline -regexp $props "93"] < 0} {
151 if {[lsearch -inline -regexp $props "2008"] >= 0} {
152 set vhdl_year "VHDL 2008"
153 }
elseif {[lsearch -inline -regexp $props "2019"] >= 0} {
155 set vhdl_year "VHDL 2019"
157 Msg CriticalWarning "VHDL 2019 is not supported\
158 in Vivado version older than 2023.2.\
159 Using VHDL 2008, but this might not work."
160 set vhdl_year "VHDL 2008"
164 set vhdl_year "VHDL 2008"
166 Msg Debug "File type for $f is $vhdl_year"
167 set_property -name "file_type" -value $vhdl_year -objects $file_obj
170 Msg Debug "Filetype is VHDL 93 for $f"
175 if {[lsearch -inline -regexp $props "SystemVerilog"] > 0} {
178 set_property -name "file_type" -value "SystemVerilog" -objects $file_obj
179 Msg Debug "Filetype is SystemVerilog for $f"
181 Msg Warning "Xilinx PlanAhead/ISE does not support SystemVerilog.\
182 Property not set for $f"
187 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
189 Msg Info "Setting $top as top module for file set $fileset..."
190 set globalSettings::synth_top_module $top
194 if {[lsearch -inline -regexp $props "verilog_header"] >= 0} {
195 Msg Debug "Setting verilog header type for $f..."
196 set_property file_type {Verilog Header} [get_files $f]
197 }
elseif {[lsearch -inline -regexp $props "verilog_template"] >= 0} {
199 Msg Debug "Setting verilog template type for $f..."
200 set_property file_type {Verilog Template} [get_files $f]
201 }
elseif {[lsearch -inline -regexp $props "verilog"] >= 0} {
203 Msg Debug "Setting verilog type for $f..."
204 set_property file_type {Verilog} [get_files $f]
208 if {[lsearch -inline -regexp $props "nosynth"] >= 0} {
209 Msg Debug "Setting not used in synthesis for $f..."
210 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
214 if {[lsearch -inline -regexp $props "noimpl"] >= 0} {
215 Msg Debug "Setting not used in implementation for $f..."
216 set_property -name "used_in_implementation" -value "false" -objects $file_obj
220 if {[lsearch -inline -regexp $props "nosim"] >= 0} {
221 Msg Debug "Setting not used in simulation for $f..."
222 set_property -name "used_in_simulation" -value "false" -objects $file_obj
227 set top_sim [
lindex [regexp -inline {\ytopsim\s*=\s*(.+?)\y.*} $props] 1]
228 if {$top_sim != ""} {
229 Msg Warning "Setting the simulation top module with the topsim property is deprecated.\
230 Please set this property in the \[properties\] section of your .sim list file,
231 or in the \[$fileset\] section of your sim.conf,\
232 by adding the following line.\ntop=$top_sim"
236 set sim_runtime [
lindex [regexp -inline {\yruntime\s*=\s*(.+?)\y.*} $props] 1]
237 if {$sim_runtime != ""} {
238 Msg Warning "Setting the simulation runtime using the runtime= property is deprecated.\
239 Please set this property in the \[properties\] section of your .sim list file,\
240 or in the \[$fileset\] section of your sim.conf,\
241 by adding the following line.\n<simulator_name>.simulate.runtime=$sim_runtime"
245 if {[lsearch -inline -regexp $props "wavefile"] >= 0} {
246 Msg Warning "Setting a wave do file using the wavefile property is deprecated.\
247 Set this property in the sim.conf file under the \[$fileset\] section,\
248 or in the \[properties\] section of the .sim list file,\
249 by adding the following line .\n<simulator_name>.simulate.custom_wave_do=[
file tail $f]"
253 if {[lsearch -inline -regexp $props "dofile"] >= 0} {
254 Msg Warning "Setting a wave do file using the dofile property is deprecated.\
255 Set this property in the sim.conf file under the \[$fileset\] section,\
256 or in the \[properties\] section of the .sim list file,\
257 by adding the following line .\n<simulator_name>.simulate.custom_do=[
file tail $f]"
261 if {[lsearch -inline -regexp $props "locked"] >= 0 && $ext == ".ip"} {
262 Msg Info "Locking IP $f..."
263 set_property IS_MANAGED 0 [get_files $f]
267 if {[
file extension $f] == ".bd"} {
268 Msg Info "Generating Target for [
file tail $f],\
269 please remember to commit the (possible) changed file."
270 generate_target all [get_files $f]
273 dict for {fo pd} $reapply_targets {
274 set ref [dict get $pd ref]
275 set cell [dict get $pd cell]
276 Msg Debug "Reapplying ref: $ref and cell:$cell to $fo"
277 set_property SCOPED_TO_REF $ref $fo
278 set_property SCOPED_TO_CELLS [split $cell ","] $fo
283 if {[
file extension $f] == ".tcl" && $ext != ".con"} {
284 if {[lsearch -inline -regexp $props "source"] >= 0} {
285 Msg Info "Sourcing Tcl script $f,\
286 and setting it not used in synthesis, implementation and simulation..."
288 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
289 set_property -name "used_in_implementation" -value "false" -objects $file_obj
290 set_property -name "used_in_simulation" -value "false" -objects $file_obj
295 set ref [
lindex [regexp -inline {\yscoped_to_ref\s*=\s*([^ ]+)} $props] 1]
296 set cell [
lindex [regexp -inline {\yscoped_to_cells\s*=\s*([^ ]+)} $props] 1]
297 if {[
file extension $f] == ".elf" || (([
file extension $f] == ".tcl" || [
file extension $f] == ".xdc") && $ext == ".con")} {
298 if {[
file extension $f] == ".elf"} {dict set reapply_targets $file_obj [dict create ref $ref cell $cell]}
300 set_property SCOPED_TO_REF $ref $file_obj
303 set_property SCOPED_TO_CELLS [
split $cell ","] $file_obj
307 Msg Info "[
llength $lib_files] file/s added to library $rootlib..."
310 if {$ext == ".sim"} {
311 Msg Warning "Simulation files not supported in Quartus Prime mode... Skipping $lib"
313 if {![is_project_open]} {
314 Msg Error "Project is closed"
316 foreach cur_file $lib_files {
320 set props [
DictGet $properties $cur_file]
323 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
325 Msg Info "Setting $top as top module for file set $fileset..."
326 set globalSettings::synth_top_module $top
329 if {[
string first "VHDL" $file_type] != -1} {
330 if {[
string first "1987" $props] != -1} {
331 set hdl_version "VHDL_1987"
332 }
elseif {[
string first "1993" $props] != -1} {
333 set hdl_version "VHDL_1993"
334 }
elseif {[
string first "2008" $props] != -1} {
335 set hdl_version "VHDL_2008"
337 set hdl_version "default"
339 if {$hdl_version == "default"} {
340 set_global_assignment -name $file_type $cur_file -library $rootlib
342 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version -library $rootlib
344 }
elseif {[
string first "SYSTEMVERILOG" $file_type] != -1} {
346 if {[
string first "2005" $props] != -1} {
347 set hdl_version "systemverilog_2005"
348 }
elseif {[
string first "2009" $props] != -1} {
349 set hdl_version "systemverilog_2009"
351 set hdl_version "default"
353 if {$hdl_version == "default"} {
354 set_global_assignment -name $file_type $cur_file
356 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
358 }
elseif {[
string first "VERILOG" $file_type] != -1} {
360 if {[
string first "1995" $props] != -1} {
361 set hdl_version "verilog_1995"
362 }
elseif {[
string first "2001" $props] != -1} {
363 set hdl_version "verilog_2001"
365 set hdl_version "default"
367 if {$hdl_version == "default"} {
368 set_global_assignment -name $file_type $cur_file
370 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
372 }
elseif {[
string first "SOURCE" $file_type] != -1 || [
string first "COMMAND_MACRO" $file_type] != -1} {
373 set_global_assignment -name $file_type $cur_file
374 if {$ext == ".con"} {
376 }
elseif {$ext == ".src"} {
378 if {[
string first "qsys" $props] != -1} {
381 regsub -all {\{||qsys||\}} $props $emptyString props
383 set qsysPath [
file dirname $cur_file]
384 set qsysName "[
file rootname [
file tail $cur_file]].qsys"
385 set qsysFile "$qsysPath/$qsysName"
386 set qsysLogFile "$qsysPath/[
file rootname [
file tail $cur_file]].qsys-script.log"
389 if {![
info exists ::env(QSYS_ROOTDIR)]} {
390 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
391 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
392 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
394 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
397 set qsys_rootdir $::env(QSYS_ROOTDIR)
400 set cmd "$qsys_rootdir/qsys-script"
401 set cmd_options " --script=$cur_file"
402 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
403 Msg Info "Executing: $cmd $cmd_options"
404 Msg Info "Saving logfile in: $qsysLogFile"
405 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
406 set makeRet [
lindex [dict get $opt -errorcode] end]
407 Msg CriticalWarning "$cmd returned with $makeRet"
410 Msg Error " Could not execute command $cmd"
414 if {[
file exists $qsysName] != 0} {
415 file rename -force $qsysName $qsysFile
417 set qsysMd5Sum [
Md5Sum $qsysFile]
419 set fileDir [
file normalize "./hogTmp"]
420 set fileName "$fileDir/.hogQsys.md5"
421 if {![
file exists $fileDir]} {
424 set hogQsysFile [open $fileName "a"]
425 set fileEntry "$qsysFile\t$qsysMd5Sum"
426 puts $hogQsysFile $fileEntry
429 Msg ERROR "Error while moving the generated qsys file to final location: $qsysName not found!"
431 if {[
file exists $qsysFile] != 0} {
432 if {[
string first "noadd" $props] == -1} {
434 set_global_assignment -name $qsysFileType $qsysFile
436 regsub -all {noadd} $props $emptyString props
438 if {[
string first "nogenerate" $props] == -1} {
442 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
446 }
elseif {[
string first "QSYS" $file_type] != -1} {
448 regsub -all {\{||\}} $props $emptyString props
449 if {[
string first "noadd" $props] == -1} {
450 set_global_assignment -name $file_type $cur_file
452 regsub -all {noadd} $props $emptyString props
456 if {[
string first "nogenerate" $props] == -1} {
460 set_global_assignment -name $file_type $cur_file -library $rootlib
465 if {$ext == ".con"} {
466 set vld_exts {.sdc .pin .dcf .gcf .pdc .ndc .fdc .crt .vcd }
467 foreach con_file $lib_files {
469 set con_ext [
file extension $con_file]
470 if {[
IsInList [
file extension $con_file] $vld_exts]} {
471 set option [
string map {. -} $con_ext]
472 set option [
string map {fdc net_fdc} $option]
473 set option [
string map {pdc io_pdc} $option]
474 create_links -convert_EDN_to_HDL 0 -library {work} $option $con_file
476 set props [
DictGet $properties $con_file]
478 if {$con_ext == ".sdc"} {
479 if {[lsearch $props "notiming"] >= 0} {
480 Msg Info "Excluding $con_file from timing verification..."
482 Msg Info "Adding $con_file to time verification"
483 append timing_conf_command " -file $con_file"
487 if {[lsearch $props "nosynth"] >= 0} {
488 Msg Info "Excluding $con_file from synthesis..."
490 Msg Info "Adding $con_file to synthesis"
491 append synth_conf_command " -file $con_file"
496 if {$con_ext == ".pdc" || $con_ext == ".sdc"} {
497 if {[lsearch $props "noplace"] >= 0} {
498 Msg Info "Excluding $con_file from place and route..."
500 Msg Info "Adding $con_file to place and route"
501 append place_conf_command " -file $con_file"
506 Msg CriticalWarning "Constraint file $con_file does not have a valid extension. Allowed extensions are: \n $vld_exts"
509 }
elseif {$ext == ".src"} {
510 foreach f $lib_files {
511 Msg Debug "Adding source $f to library $rootlib..."
512 create_links -library $rootlib -hdl_source $f
514 }
elseif {$ext == ".sim"} {
515 Msg Debug "Adding stimulus file $f to library..."
516 create_links -library $rootlib -stimulus $f
518 build_design_hierarchy
519 foreach cur_file $lib_files {
523 set props [
DictGet $properties $cur_file]
526 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
528 Msg Info "Setting $top as top module for file set $rootlib..."
529 set globalSettings::synth_top_module "${top}::$rootlib"
534 if {$ext == ".src" || $ext == ".con" || $ext == ".ext"} {
535 foreach f $lib_files {
536 Msg Debug "Diamond: adding source file $f to library $rootlib..."
537 prj_src add -work $rootlib $f
538 set props [
DictGet $properties $f]
540 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
542 Msg Info "Setting $top as top module for the project..."
543 set globalSettings::synth_top_module $top
547 if {[lsearch -inline -regexp $props "enable"] >= 0} {
548 Msg Debug "Setting $f as active Logic Preference file"
552 }
elseif {$ext == ".sim"} {
553 foreach f $lib_files {
554 Msg Debug "Diamond Adding simulation file $f to library $rootlib..."
555 prj_src add -work $rootlib -simulate_only $f
561 foreach app_name [dict keys $ws_apps] {
562 foreach f $lib_files {
563 if {[
string tolower $rootlib] != [
string tolower "app_$app_name"]} {
567 Msg Info "Adding source file $f from lib: $lib to vitis app \[$app_name\]..."
568 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
569 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
570 Msg Debug "Project_f_path is $proj_f_path"
572 importsources -name $app_name -soft-link -path $f -target $proj_f_path
576 Msg Debug "Vitis Unified: Collecting files for apps from library $rootlib..."
580 if {[dict size $ws_apps] == 0} {
581 Msg Debug "No apps found in workspace, skipping file collection"
583 Msg Debug "Found [dict size $ws_apps] app(s) in workspace"
584 Msg Debug "Processing library: $rootlib with [
llength $lib_files] file(s)"
585 foreach app_name [dict keys $ws_apps] {
586 set expected_lib [
string tolower "app_$app_name"]
587 Msg Debug "Checking files for app: $app_name (looking for lib: $expected_lib, current lib: [
string tolower $rootlib])"
588 foreach f $lib_files {
589 if {[
string tolower $rootlib] != $expected_lib} {
592 Msg Debug "File $f matches app $app_name"
593 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
594 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
595 set proj_f_path [
string trimleft $proj_f_path "/"]
597 if {![dict exists $app_files_dict $app_name]} {
598 dict set app_files_dict $app_name files [list]
599 dict set app_files_dict $app_name target $proj_f_path
600 Msg Debug "Initialized app_files_dict for $app_name with target: $proj_f_path"
603 set current_files [dict get $app_files_dict $app_name files]
604 lappend current_files $f
605 dict set app_files_dict $app_name files $current_files
606 set current_count [
llength [dict get $app_files_dict $app_name files]]
607 Msg Debug "Added file $f to app_files_dict for $app_name, current count: $current_count"
617 set python_script "$globalSettings::repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"
618 set vitis_workspace "$globalSettings::build_dir/vitis_unified"
622 set ::env(HOG_VITIS_VER) $vitis_version
623 Msg Debug "Vitis version: $vitis_version (set in HOG_VITIS_VER environment variable)"
625 dict for {app_name app_data} $app_files_dict {
626 set files_list [dict get $app_data files]
627 set target_path [dict get $app_data target]
629 if {[llength $files_list] > 0} {
630 Msg Info "Adding [llength $files_list] source file(s) to vitis app \[$app_name\]..."
631 Msg Debug "Files: $files_list"
632 Msg Debug "Target path: $target_path"
634 # Convert Tcl list to JSON array for Python
637 foreach f $files_list {
639 append files_json ", "
641 # Escape backslashes and quotes in file paths
642 set escaped_f [string map {\\ \\\\ \" \\\"} $f]
643 append files_json "\"$escaped_f\""
646 append files_json "\]"
648 Msg Debug "JSON string: $files_json"
650 # The file list contains spaces: pass it in the environment, as a command
651 # line argument it would be split by the Windows shell that runs vitis.bat
652 set ::env(HOG_VITIS_APP_FILES) $files_json
653 set error_msg "Failed to add files to app $app_name"
655 ![ExecuteVitisUnifiedCommand $python_script "add_app_files" \
656 [list $app_name $vitis_workspace $target_path] \
659 Msg Error "Failed to add files to Vitis Unified app '$app_name'"
663 Msg Warning "No files to add for app '$app_name'"
670 if {[
DictGet $filesets "sim_1"] == ""} {
671 delete_fileset -quiet [get_filesets -quiet "sim_1"]
677 if {$synth_conf == 1} {
678 Msg Info $synth_conf_command
679 eval $synth_conf_command
681 if {$timing_conf == 1} {
682 Msg Info $timing_conf_command
683 eval $timing_conf_command
685 if {$place_conf == 1} {
686 Msg Info $place_conf_command
687 eval $place_conf_command
693 proc ALLOWED_PROPS {} {
694 return [dict create ".vhd" [list "93" "nosynth" "noimpl" "nosim" "1987" "1993" "2008" "2019"] \
695 ".vhdl" [list "93" "nosynth" "noimpl" "nosim" "1987" "1993" "2008" "2019"] \
696 ".bd" [list "nosim"] \
697 ".v" [list "SystemVerilog" "verilog_header" "nosynth" "noimpl" "nosim" "1995" "2001"] \
698 ".sv" [list "verilog" "verilog_header" "nosynth" "noimpl" "nosim" "2005" "2009"] \
699 ".svp" [list "verilog" "verilog_header" "nosynth" "noimpl" "nosim" "2005" "2009"] \
700 ".do" [list "nosim"] \
701 ".udo" [list "nosim"] \
702 ".xci" [list "nosynth" "noimpl" "nosim" "locked"] \
703 ".xdc" [list "nosynth" "noimpl" "scoped_to_ref" "scoped_to_cells"] \
704 ".tcl" [list "nosynth" "noimpl" "nosim" "scoped_to_ref" "scoped_to_cells" "source" "qsys" "noadd" \
705 "--block-symbol-file" "--clear-output-directory" "--example-design" \
706 "--export-qsys-script" "--family" "--greybox" "--ipxact" \
707 "--jvm-max-heap-size" "--parallel" "--part" "--search-path" \
708 "--simulation" "--synthesis" "--testbench" "--testbench-simulation" \
709 "--upgrade-ip-cores" "--upgrade-variation-file"] \
710 ".qsys" [list "nogenerate" "noadd" "--block-symbol-file" "--clear-output-directory" "--example-design" \
711 "--export-qsys-script" "--family" "--greybox" "--ipxact" "--jvm-max-heap-size" "--parallel" \
712 "--part" "--search-path" "--simulation" "--synthesis" "--testbench" "--testbench-simulation" \
713 "--upgrade-ip-cores" "--upgrade-variation-file"] \
714 ".sdc" [list "notiming" "nosynth" "noplace"] \
715 ".elf" [list "scoped_to_ref" "scoped_to_cells" "nosim" "noimpl"] \
716 ".pdc" [list "nosynth" "noplace"] \
717 ".lpf" [list "enable"]]
728 proc BinaryStepName {part} {
730 return "WRITE_DEVICE_IMAGE"
734 return "WRITE_BITSTREAM"
741 set essential_vars [dict create \
742 "HOG_USER" "NOT defined. This variable is essential for git to work properly. \
743 It should be set to the username for your service account (a valid git account)." \
744 "HOG_EMAIL" "NOT defined. This variable is essential for git to work properly. It should be set to your service's account email." \
745 "HOG_PUSH_TOKEN" "NOT defined. This variable is essential for git to work properly. \
746 It should be set to a Gitlab/GitHub API token for your service account."]
749 dict for {var msg} $essential_vars {
750 if {![info exists env($var)]} {
751 Msg CriticalWarning "Essential environment variable $var is $msg"
754 Msg Info "Found environment variable $var."
758 set additional_vars [dict create \
759 "HOG_CHECK_YAMLREF" "NOT defined. Set this variable to '1' to make CI fail if there is not coherence between the ref and the Hog." \
760 "HOG_TARGET_BRANCH" "NOT defined. Default branch for merge is \"master\"" \
761 "HOG_CREATE_OFFICIAL_RELEASE" "NOT defined. \
762 Set this variable to '1' to make Hog create an official release in GitHub/Gitlab with the binaries generated in the CI." \
763 "HOG_USE_DOXYGEN" "NOT defined. \
764 Set this variable to 1 to make Hog-CI run Doxygen and copy the official documentation over when you merge to the official branch."]
767 ([
info exists env(HOG_OFFICIAL_BIN_EOS_PATH)] && $env(HOG_OFFICIAL_BIN_EOS_PATH) ne "") ||
768 ([
info exists env(HOG_OFFICIAL_BIN_PATH)] && [
string match "/eos/*" $env(HOG_OFFICIAL_BIN_PATH)])
770 Msg Info "Official binary path points to EOS. Checking EOS environment variables for uploads..."
771 if {[
info exists env(HOG_OFFICIAL_BIN_PATH)]} {
772 Msg CriticalWarning "Variable HOG_OFFICIAL_BIN_EOS_PATH is defined. \
773 From Hog2026.2 this variable will be deprecated. Please, use HOG_OFFICIAL_BIN_PATH instead."
775 if {![
info exists env(EOS_PASSWORD)]} {
776 if {![
info exists env(HOG_PASSWORD)]} {
777 Msg Warning "Neither EOS_PASSWORD nor HOG_PASSWORD environment variable is defined. \
778 This variable is essential for Hog to be able to upload files to EOS. Please set one of them to the password for your EOS account."
780 Msg Info "HOG_PASSWORD environment variable is defined and will be used as password for EOS uploads. \
781 If you want to use a different password for EOS uploads, please set the EOS_PASSWORD environment variable."
784 Msg Info "EOS_PASSWORD environment variable is defined and will be used as password for EOS uploads."
787 if {![
info exists env(EOS_USER)]} {
788 Msg Info "EOS_USER environment variable is not defined. Assuming EOS username is the same as HOG_USER."
790 Msg Info "EOS_USER environment variable is defined and will be used as username for EOS uploads."
793 if {![
info exists env(EOS_MGM_URL)]} {
794 Msg Info "EOS_MGM_URL environment variable is not defined. Assuming default value of root://eosuser.cern.ch."
796 Msg Info "EOS_MGM_URL environment variable is defined and will be used as MGM URL for EOS uploads."
798 }
elseif {[
info exists env(HOG_OFFICIAL_BIN_PATH)]} {
799 Msg Info "Variable HOG_OFFICIAL_BIN_PATH is defined. Hog will copy the official binary files to the path defined in this variable. \
800 Please make sure this path is correct and has enough space to store the binaries."
802 Msg Info "No official binary path defined. Hog will not be able to upload binaries."
807 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
815 proc CheckEnv {project_name ide} {
818 set essential_commands [dict create "git" "--version" "$ide" "-version"]
819 set additional_commands [dict create \
823 "rclone" "--version"]
825 set additional_vars [dict create \
826 "HOG_PATH" "NOT defined. Hog might work as long as all the necessary executable are in the PATH variable." \
827 "HOG_XIL_LICENSE" "NOT defined. If this variable is not set to the license servers separated by comas, \
828 you need some alternative way of getting your Xilinx license (for example a license file on the machine)." \
829 "LM_LICENSE_FILE" "NOT defined. This variable should be set the Quartus/Libero license servers separated by semicolon. \
830 If not, you need an alternative way of getting your Quartus/Libero license." \
831 "HOG_LD_LIBRARY_PATH" "NOT defined. Hog might work as long as all the necessary library are found." \
832 "HOG_SIMULATION_LIB_PATH" "NOT defined. Hog-CI will not be able to run simulations using third-party simulators." \
833 "HOG_CHECK_PROJVER" "NOT defined. Hog will NOT check the CI project version. \
834 Set this variable to '1' if you want Hog to check the CI project version before creating the HDL project in Create_Project stage. \
835 If the project has not been changed with respect to the target branch, the CI will skip this project" \
836 "HOG_CHECK_SYNTAX" "NOT defined. Hog will NOT check the syntax. \
837 Set this variable to '1' if you want Hog to check the syntax after creating the HDL project in Create_Project stage." \
838 "HOG_NO_BITSTREAM" "NOT defined. Hog-CI will run the implementation up to the write_bitstream stage and create bit files." \
839 "HOG_NO_RESET_BD" "NOT defined or not equal to 1. Hog will reset .bd files (if any) before starting synthesis." \
840 "HOG_IP_PATH" "NOT defined. Hog-CI will NOT use an EOS/LOCAL IP repository to speed up the IP synthesis." \
841 "HOG_RESET_FILES" "NOT defined. Hog-CI will NOT reset any files." \
842 "HOG_NJOBS" "NOT defined. Hog-CI will build IPs with default number of jobs (4)." \
843 "HOG_SAVE_DCP" "NOT defined. Set this variable to 1, 2 or 3 to make Hog-CI save the run checkpoint DCP files (Vivado only) in the artifacts.\nCheck the official documentation for more details. https://cern.ch/hog"]
844 Msg Info "Checking environment to run Hog-CI for project $project_name with IDE $ide..."
846 Msg Info "Checking essential commands..."
847 dict for {cmd ver} $essential_commands {
848 if {[catch {exec which $cmd}]} {
849 Msg CriticalWarning "$cmd executable not found. Hog-CI cannot run!"
852 Msg Info "Found executable $cmd."
853 if {$cmd == "ghdl"} {
854 Msg Info [exec $cmd --version]
855 } elseif {$cmd != "diamond"} {
856 Msg Info [exec $cmd $ver]
861 Msg Info "Checking additional commands..."
862 dict for {cmd ver} $additional_commands {
863 if {[catch {exec which $cmd}]} {
864 Msg Warning "$cmd executable not found."
866 Msg Info "Found executable $cmd."
868 Msg Info [exec $cmd $ver]
873 if {$ide == "libero"} {
874 Msg Info "Checking essential environment variables..."
876 if {![
info exists env(HOG_TCLLIB_PATH)]} {
877 Msg Error "Environmnental variable HOG_TCLLIB_PATH is NOT defined. This variable is essential to run Hog with Tcllib and Libero. Please, refer to https://hog.readthedocs.io/en/latest/02-User-Manual/01-Hog-local/13-Libero.html."
880 Msg Info "HOG_TCLLIB_PATH is set. Hog-CI can run with Libero."
884 Msg Info "Checking additional environment variables..."
885 dict for {var msg} $additional_vars {
886 if {![info exists env($var)]} {
887 Msg Info "Environment variable $var is $msg"
889 Msg Info "Found environment variable $var."
894 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
907 proc CheckRemoteIpPath {repo_path ip_path} {
915 if {[regexp {^[^/]+:} $ip_path]} {
919 lassign [
ExecuteRet rclone --version] rclone_ret rclone_ver
920 if {$rclone_ret != 0} {
921 Msg Warning "Rclone path specified but rclone not found or failed: $rclone_ver"
925 Msg Info "IP remote directory path, on Rclone, is set to: $ip_path"
927 if {[
info exists env(HOG_RCLONE_CONFIG)]} {
928 Msg Info "Using rclone config from environment variable HOG_RCLONE_CONFIG: $env(HOG_RCLONE_CONFIG)"
929 set config_path $env(HOG_RCLONE_CONFIG)
931 set config_path "/dev/null"
932 Msg Info "Environment variable HOG_RCLONE_CONFIG not set, using rclone environmental variables..."
935 set remote_name "[
lindex [
split $ip_path ":"] 0]:"
936 lassign [
ExecuteRet rclone listremotes --config $config_path] rclone_list_ret remotes
937 if {$rclone_list_ret != 0} {
938 Msg Warning "Could not list rclone remotes: $remotes"
942 if {![
IsInList $remote_name $remotes]} {
943 Msg Warning "Rclone remote $remote_name not found among available remotes: $remotes"
949 }
elseif {[
string first "/eos/" $ip_path] == 0} {
952 if {!([
info exists ::env(ENABLE_EOS)] && $::env(ENABLE_EOS) == 1)} {
953 Msg Warning "IP remote directory path is on EOS but kinit was not successfull or not done. I will not copy IPs from/to EOS."
958 if {[
file isdirectory $ip_path]} {
959 Msg Info "Eos is mounted in the current machine. Treating it as a normal directory..."
962 lassign [
eos "ls $ip_path"] ret result
964 Msg Warning "Could not run ls for for EOS path: $ip_path (error: $result). \
965 Either the drectory does not exist or there are (temporary) problem with EOS."
969 Msg Info "IP remote directory path, on EOS, is set to: $ip_path"
977 return [list 1 $on_eos $on_rclone $config_path]
980 proc CheckProjVer {repo_path project {sim 0} {ext_path ""}} {
984 Msg Info "Will check also the version of the simulation files..."
988 if {[
info exists env(HOG_PUSH_TOKEN)] && [
info exist env(CI_PROJECT_ID)] && [
info exist env(CI_API_V4_URL)]} {
989 set token $env(HOG_PUSH_TOKEN)
990 set api_url $env(CI_API_V4_URL)
991 set project_id $env(CI_PROJECT_ID)
993 set curl_cmd [
GetCurl $api_url]
994 if {[
info exist env(CI_JOB_NAME)] && $env(CI_JOB_NAME) == "check_branch_state"} {
1003 set project_dir $repo_path/Top/$project
1006 Msg Info "$project was modified, continuing with the CI..."
1007 if {$ci_run == 1 && $curl_cmd != 0 && ![
IsQuartus] && ![
IsISE]} {
1008 Msg Info "Checking if the project has been already built in a previous CI run..."
1010 if {$sha == [
GetSHA $repo_path]} {
1011 Msg Info "Project was modified in the current commit, Hog will proceed with the build workflow."
1014 Msg Info "Checking if project $project has been built in a previous CI run with sha $sha..."
1015 set result [
catch {
package require json} JsonFound]
1016 if {"$result" != "0"} {
1017 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
1020 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/$project_id/pipelines"] ret content
1021 set pipeline_dict [json::json2dict $content]
1022 if {[
llength $pipeline_dict] > 0} {
1023 foreach pip $pipeline_dict {
1024 set pip_sha [
DictGet $pip sha]
1025 set source [
DictGet $pip source]
1026 if {$source == "merge_request_event" && [
string first $sha $pip_sha] != -1} {
1027 Msg Info "Found pipeline with sha $pip_sha for project $project"
1028 set pipeline_id [
DictGet $pip id]
1030 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/${project_id}/pipelines/${pipeline_id}/jobs?pagination=keyset&per_page=100"] ret2 content2
1031 set jobs_dict [json::json2dict $content2]
1032 if {[
llength $jobs_dict] > 0} {
1033 foreach job $jobs_dict {
1034 set job_name [
DictGet $job name]
1036 set artifacts [
DictGet $job artifacts_file]
1037 set status [
DictGet $job status]
1038 set current_job_name $env(CI_JOB_NAME)
1039 if {$current_job_name == $job_name && $status == "success"} {
1041 lassign [
ExecuteRet {*}$curl_cmd --location --output artifacts.zip --header "PRIVATE-TOKEN: $token" --url "$api_url/projects/$project_id/jobs/$job_id/artifacts"] ret3 content3
1043 Msg CriticalWarning "Cannot download artifacts for job $job_name with id $job_id"
1046 lassign [
ExecuteRet unzip -o $repo_path/artifacts.zip] ret_zip
1047 if {$ret_zip != 0} {
1050 Msg Info "Artifacts for job $job_name with id $job_id downloaded and unzipped."
1051 file mkdir $repo_path/Projects/$project
1052 set fp [open "$repo_path/Projects/$project/skip.me" w+]
1064 }
elseif {$ver != -1} {
1065 Msg Info "$project was not modified since version: $ver."
1066 file mkdir $repo_path/Projects/$project
1067 set fp [open "$repo_path/Projects/$project/skip.me" w+]
1071 Msg Error "Impossible to check the project version. Most likely the repository is not clean. Please, commit your changes before running this command."
1082 proc CheckSyntax {project_name repo_path {project_file ""}} {
1084 update_compile_order
1085 set syntax [check_syntax -return_string]
1086 if {[
string first "CRITICAL" $syntax] != -1} {
1091 lassign [
GetHogFiles -list_files "*.src" "$repo_path/Top/$project_name/list/" $repo_path] src_files dummy
1092 dict for {lib files} $src_files {
1094 set file_extension [file extension $f]
1095 if {$file_extension == ".vhd" || $file_extension == ".vhdl" || $file_extension == ".v" || $file_extension == ".sv"} {
1096 if {[catch {execute_module -tool map -args "--analyze_file=$f"} result]} {
1097 Msg Error "\nResult: $result\n"
1098 Msg Error "Check syntax failed.\n"
1100 if {$result == ""} {
1101 Msg Info "Check syntax was successful for $f.\n"
1103 Msg Warning "Found syntax error in file $f:\n $result\n"
1110 lassign [
GetProjectFiles $project_file] prjLibraries prjProperties prjSimLibraries prjConstraints prjSrcSets prjSimSets prjConSets
1111 dict for {lib sources} $prjLibraries {
1112 if {[file extension $lib] == ".src"} {
1113 foreach f $sources {
1114 Msg Info "Checking Syntax of $f"
1120 Msg Info "The Checking Syntax is not supported by this IDE. Skipping..."
1125 proc CloseProject {} {
1147 proc CompareVersions {ver1 ver2} {
1156 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver1 - x y z]} {
1157 set ver1 [list $x $y $z]
1159 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver2 - x y z]} {
1160 set ver2 [list $x $y $z]
1164 set v1 [
join $ver1 ""]
1166 set v2 [
join $ver2 ""]
1169 if {[
string is integer $v1] && [
string is integer $v2]} {
1170 set ver1 [
expr {[scan [lindex $ver1 0] %d] * 1000000 + [scan [lindex $ver1 1] %d] * 1000 + [scan [lindex $ver1 2] %d]}]
1171 set ver2 [
expr {[scan [lindex $ver2 0] %d] * 1000000 + [scan [lindex $ver2 1] %d] * 1000 + [scan [lindex $ver2 2] %d]}]
1173 if {$ver1 > $ver2} {
1175 }
elseif {$ver1 == $ver2} {
1181 Msg Warning "Version is not numeric: $ver1, $ver2"
1184 return [
expr {$ret}]
1191 proc CheckExtraFiles {libraries constraints simlibraries} {
1194 lassign [
GetProjectFiles] prjLibraries prjProperties prjSimLibraries prjConstraints
1195 set prj_dir [get_property DIRECTORY [current_project]]
1196 file mkdir "$prj_dir/.hog"
1197 set extra_file_name "$prj_dir/.hog/extra.files"
1198 set new_extra_file [open $extra_file_name "w"]
1200 dict for {prjLib prjFiles} $prjLibraries {
1201 foreach prjFile $prjFiles {
1202 if {[file extension $prjFile] == ".xcix"} {
1203 Msg Warning "IP $prjFile is packed in a .xcix core container. \
1204 This files are not suitable for version control systems. We recommend to use .xci files instead."
1207 if {[file extension $prjFile] == ".xci" && [get_property CORE_CONTAINER [get_files $prjFile]] != ""} {
1208 Msg Info "$prjFile is a virtual IP file in a core container. Ignoring it..."
1212 if {[IsInList $prjFile [DictGet $libraries $prjLib]] == 0} {
1213 if {[file extension $prjFile] == ".bd"} {
1214 # Generating BD products to save md5sum of already modified BD
1215 Msg Info "Generating targets of $prjFile..."
1216 generate_target all [get_files $prjFile]
1218 puts $new_extra_file "$prjFile [Md5Sum $prjFile]"
1219 Msg Info "$prjFile (lib: $prjLib) has been generated by an external script. Adding to $extra_file_name..."
1223 close $new_extra_file
1224 set extra_sim_file "$prj_dir/.hog/extrasim.files"
1225 set new_extra_file [open $extra_sim_file "w"]
1227 dict for {prjSimLib prjSimFiles} $prjSimLibraries {
1228 foreach prjSimFile $prjSimFiles {
1229 if {[IsInList $prjSimFile [DictGet $simlibraries $prjSimLib]] == 0} {
1230 puts $new_extra_file "$prjSimFile [Md5Sum $prjSimFile]"
1231 Msg Info "$prjSimFile (lib: $prjSimLib) has been generated by an external script. Adding to $extra_sim_file..."
1235 close $new_extra_file
1236 set extra_con_file "$prj_dir/.hog/extracon.files"
1237 set new_extra_file [open $extra_con_file "w"]
1239 dict for {prjConLib prjConFiles} $prjConstraints {
1240 foreach prjConFile $prjConFiles {
1241 if {[IsInList $prjConFile [DictGet $constraints $prjConLib]] == 0} {
1242 puts $new_extra_file "$prjConFile [Md5Sum $prjConFile]"
1243 Msg Info "$prjConFile has been generated by an external script. Adding to $extra_con_file..."
1247 close $new_extra_file
1254 proc CheckLatestHogRelease {{repo_path .}} {
1255 if {[
info exists ::env(CHECK_FOR_HOG_UPDATES)] && $::env(CHECK_FOR_HOG_UPDATES) eq "0"} {
1260 if {[
catch {
package require inifile 0.2.3} ERROR] == 0} {
1261 set repo_conf $repo_path/Top/repo.conf
1262 if {[
file exists $repo_conf]} {
1263 set PROPERTIES [
ReadConf $repo_conf]
1264 if {[dict exists $PROPERTIES main]} {
1265 set mainDict [dict get $PROPERTIES main]
1266 if {[dict exists $mainDict CHECK_FOR_HOG_UPDATES]} {
1267 if {[dict get $mainDict CHECK_FOR_HOG_UPDATES] == 0} {
1277 set current_ver [
Git {describe --always}]
1278 Msg Debug "Current version: $current_ver"
1279 set current_sha [
Git "log $current_ver -1 --format=format:%H"]
1280 Msg Debug "Current SHA: $current_sha"
1283 if {[
OS] == "windows"} {
1284 Msg Info "On windows we cannot set a timeout on 'git fetch', hopefully nothing will go wrong..."
1287 Msg Info "Checking for latest Hog release, can take up to 5 seconds..."
1290 set master_ver [
Git "describe origin/master"]
1291 Msg Debug "Master version: $master_ver"
1292 set master_sha [
Git "log $master_ver -1 --format=format:%H"]
1293 Msg Debug "Master SHA: $master_sha"
1294 lassign [
GitRet "merge-base $current_sha $master_sha"] mb_ret merge_base
1296 Msg Warning "Could not run git merge-base (shallow clone?). Skipping Hog update check."
1300 Msg Debug "merge base: $merge_base"
1303 if {$merge_base != $master_sha} {
1305 Msg Info "Version $master_ver has been released (https://gitlab.com/hog-cern/Hog/-/releases/$master_ver)"
1306 Msg Status "You should consider updating Hog submodule with the following instructions:"
1308 Msg Status "cd Hog && git checkout master && git pull"
1310 Msg Status "Also update the ref: in your .gitlab-ci.yml to $master_ver"
1314 Msg Info "Latest official version is $master_ver, nothing to do."
1326 proc CheckYmlRef {repo_path allow_failure} {
1327 if {$allow_failure} {
1328 set MSG_TYPE CriticalWarning
1333 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
1334 Msg CriticalWarning "Cannot find package YAML, skipping consistency check of \"ref\" in gilab-ci.yaml file.\n Error message: $YAMLPACKAGE
1335 You can fix this by installing package \"tcllib\""
1343 if {[
file exists .gitlab-ci.yml]} {
1347 if {[
file exists .gitlab-ci.yml]} {
1348 set fp [open ".gitlab-ci.yml" r]
1349 set file_data [read $fp]
1352 Msg $MSG_TYPE "Cannot open file .gitlab-ci.yml"
1356 set file_data "\n$file_data\n\n"
1358 if {[
catch {::yaml::yaml2dict -stream $file_data} yamlDict]} {
1359 Msg $MSG_TYPE "Parsing $repo_path/.gitlab-ci.yml failed. To fix this, check that yaml syntax is respected, remember not to use tabs."
1363 dict for {dictKey dictValue} $yamlDict {
1364 #looking for Hog include in .gitlab-ci.yml
1366 "$dictKey" == "include" && (
1367 [lsearch [split $dictValue " {}"] "/hog.yml"] != "-1" ||
1368 [lsearch [split $dictValue " {}"] "/hog-dynamic.yml"] != "-1"
1371 set YML_REF [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "ref"] + 1}]]
1372 set YML_NAME [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "file"] + 1}]]
1376 if {$YML_REF == ""} {
1377 Msg Warning "Hog version not specified in the .gitlab-ci.yml. Assuming that master branch is used."
1379 set YML_REF_F [
Git {name-rev --tags --name-only origin/master}]
1382 set YML_REF_F [regsub -all "'" $YML_REF ""]
1385 if {$YML_NAME == ""} {
1386 Msg $MSG_TYPE "Hog included yml file not specified, assuming hog.yml"
1387 set YML_NAME_F hog.yml
1389 set YML_NAME_F [regsub -all "^/" $YML_NAME ""]
1392 lappend YML_FILES $YML_NAME_F
1398 if {[
catch {::yaml::yaml2dict -file $YML_NAME_F} yamlDict]} {
1399 Msg $MSG_TYPE "Parsing $YML_NAME_F failed."
1403 dict for {dictKey dictValue} $yamlDict {
1404 #looking for included files
1405 if {"$dictKey" == "include"} {
1406 foreach v $dictValue {
1407 lappend YML_FILES [lindex [split $v " "] [expr {[lsearch -dictionary [split $v " "] "local"] + 1}]]
1413 Msg Info "Found the following yml files: $YML_FILES"
1415 set HOGYML_SHA [
GetSHA $YML_FILES]
1416 lassign [
GitRet "log --format=%h -1 --abbrev=7 $YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1418 lassign [
GitRet "log --format=%h -1 --abbrev=7 origin/$YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1420 Msg $MSG_TYPE "Error in project .gitlab-ci.yml. ref: $YML_REF not found"
1421 set EXPECTEDYML_SHA ""
1424 if {!($EXPECTEDYML_SHA eq "")} {
1425 if {$HOGYML_SHA == $EXPECTEDYML_SHA} {
1426 Msg Info "Hog included file $YML_FILES matches with $YML_REF in .gitlab-ci.yml."
1428 Msg $MSG_TYPE "HOG $YML_FILES SHA mismatch.
1429 From Hog submodule: $HOGYML_SHA
1430 From ref in .gitlab-ci.yml: $EXPECTEDYML_SHA
1431 You can fix this in 2 ways: by changing the ref in your repository or by changing the Hog submodule commit"
1434 Msg $MSG_TYPE "One or more of the following files could not be found $YML_FILES in Hog at $YML_REF"
1437 Msg Info ".gitlab-ci.yml not found in $repo_path. Skipping this step"
1458 proc CompareLibDicts {proj_libs list_libs proj_sets list_sets proj_props list_props {severity "CriticalWarning"} {outFile ""} {extraFiles ""}} {
1459 set extra_files $extraFiles
1461 set out_prjlibs $proj_libs
1462 set out_prjprops $proj_props
1464 dict for {prjSet prjLibraries} $proj_sets {
1465 # Check if sets is also in list files
1466 if {[IsInList $prjSet $list_sets]} {
1467 set listLibraries [DictGet $list_sets $prjSet]
1468 # Loop over libraries in fileset
1469 foreach prjLib $prjLibraries {
1470 set prjFiles [DictGet $proj_libs $prjLib]
1471 # Check if library exists in list files
1472 if {[IsInList $prjLib $listLibraries]} {
1473 # Loop over files in library
1474 set listFiles [DictGet $list_libs $prjLib]
1475 foreach prjFile $prjFiles {
1476 set idx [lsearch -exact $listFiles $prjFile]
1477 set listFiles [lreplace $listFiles $idx $idx]
1479 # File is in project but not in list libraries, check if it was generated at creation time...
1480 if {[dict exists $extra_files $prjFile]} {
1481 # File was generated at creation time, checking the md5sum
1482 # Removing the file from the prjFiles list
1483 set idx2 [lsearch -exact $prjFiles $prjFile]
1484 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1485 set new_md5sum [Md5Sum $prjFile]
1486 set old_md5sum [DictGet $extra_files $prjFile]
1487 if {$new_md5sum != $old_md5sum} {
1488 # tclint-disable-next-line line-length
1489 MsgAndLog "$prjFile in project has been modified from creation time. \Please update the script you used to create the file and regenerate the project, or save the file outside the Projects/ directory and add it to a project list file" $severity $outFile
1492 set extra_files [dict remove $extra_files $prjFile]
1494 # File is neither in list files nor in extra_files
1495 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1499 # File is both in list files and project, checking properties...
1500 set prjProps [DictGet $proj_props $prjFile]
1501 set listProps [DictGet $list_props $prjFile]
1502 # Check if it is a potential sourced file
1503 if {[IsInList "nosynth" $prjProps] && [IsInList "noimpl" $prjProps] && [IsInList "nosim" $prjProps]} {
1504 # Check if it is sourced
1505 set idx_source [lsearch -exact $listProps "source"]
1506 if {$idx_source >= 0} {
1507 # It is sourced, let's replace the individual properties with source
1508 set idx [lsearch -exact $prjProps "noimpl"]
1509 set prjProps [lreplace $prjProps $idx $idx]
1510 set idx [lsearch -exact $prjProps "nosynth"]
1511 set prjProps [lreplace $prjProps $idx $idx]
1512 set idx [lsearch -exact $prjProps "nosim"]
1513 set prjProps [lreplace $prjProps $idx $idx]
1514 lappend prjProps "source"
1518 foreach prjProp $prjProps {
1519 set idx [lsearch -exact $listProps $prjProp]
1520 set listProps [lreplace $listProps $idx $idx]
1522 MsgAndLog "Property $prjProp of $prjFile was set in project but not in list files" $severity $outFile
1527 foreach listProp $listProps {
1528 if {[string first $listProp "topsim="] == -1 && [string first $listProp "enable"] == -1} {
1529 MsgAndLog "Property $listProp of $prjFile was found in list files but not set in project." $severity $outFile
1534 # Update project prjProps
1535 dict set out_prjprops $prjFile $prjProps
1538 # Loop over remaining files in list libraries
1539 foreach listFile $listFiles {
1540 MsgAndLog "$listFile was found in list files but not in project." $severity $outFile
1544 # Check extra files again...
1545 foreach prjFile $prjFiles {
1546 if {[dict exists $extra_files $prjFile]} {
1547 # File was generated at creation time, checking the md5sum
1548 # Removing the file from the prjFiles list
1549 set idx2 [lsearch -exact $prjFiles $prjFile]
1550 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1551 set new_md5sum [Md5Sum $prjFile]
1552 set old_md5sum [DictGet $extra_files $prjFile]
1553 if {$new_md5sum != $old_md5sum} {
1554 # tclint-disable-next-line line-length
1555 MsgAndLog "$prjFile in project has been modified from creation time. Please update the script you used to create the file and regenerate the project, or save the file outside the Projects/ directory and add it to a project list file" $severity $outFile
1558 set extra_files [dict remove $extra_files $prjFile]
1560 # File is neither in list files nor in extra_files
1561 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1566 # Update prjLibraries
1567 dict set out_prjlibs $prjLib $prjFiles
1570 MsgAndLog "Fileset $prjSet found in project but not in list files" $severity $outFile
1575 return [list $n_diffs $extra_files $out_prjlibs $out_prjprops]
1585 proc CompareVHDL {file1 file2} {
1586 set a [open $file1 r]
1587 set b [open $file2 r]
1589 while {[
gets $a line] != -1} {
1590 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1591 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1597 while {[
gets $b line] != -1} {
1598 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1599 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1608 foreach x $f1 y $f2 {
1610 lappend diff "> $x\n< $y\n\n"
1623 proc Copy {i_dirs o_dir} {
1624 foreach i_dir $i_dirs {
1625 if {[
file isdirectory $i_dir] && [
file isdirectory $o_dir]} {
1626 if {([
file tail $i_dir] == [
file tail $o_dir]) || ([
file exists $o_dir/[
file tail $i_dir]] && [
file isdirectory $o_dir/[
file tail $i_dir]])} {
1627 file delete -force $o_dir/[
file tail $i_dir]
1631 file copy -force $i_dir $o_dir
1646 proc CopyIPbusXMLs {proj_dir path dst {xml_version "0.0.0"} {xml_sha "00000000"} {use_ipbus_sw 0} {generate 0}} {
1647 if {$use_ipbus_sw == 1} {
1648 lassign [
ExecuteRet python3 -c "from __future__ import print_function; from sys import path;print(':'.join(path\[1:\]))"] ret msg
1650 set ::env(PYTHONPATH) $msg
1651 lassign [
ExecuteRet gen_ipbus_addr_decode -h] ret msg
1658 Msg CriticalWarning "Problem while trying to run python: $msg"
1661 set dst [
file normalize $dst]
1663 if {$can_generate == 0} {
1664 if {$generate == 1} {
1665 Msg Error "Cannot generate IPbus address files, IPbus executable gen_ipbus_addr_decode not found or not working: $msg"
1668 Msg Warning "IPbus executable gen_ipbus_addr_decode not found or not working, will not verify IPbus address tables."
1675 set ipb_files [glob -nocomplain $proj_dir/list/*.ipb]
1676 set n_ipb_files [
llength $ipb_files]
1677 if {$n_ipb_files == 0} {
1678 Msg CriticalWarning "No files with .ipb extension found in $proj_dir/list."
1681 set libraries [dict create]
1682 set vhdl_dict [dict create]
1684 foreach ipb_file $ipb_files {
1690 set xmlfiles [dict get $libraries "xml.ipb"]
1692 set xml_list_error 0
1693 foreach xmlfile $xmlfiles {
1694 if {[
file isdirectory $xmlfile]} {
1695 Msg CriticalWarning "Directory $xmlfile listed in xml list file $list_file. Directories are not supported!"
1696 set xml_list_error 1
1699 if {[
file exists $xmlfile]} {
1700 if {[dict exists $vhdl_dict $xmlfile]} {
1701 set vhdl_file [
file normalize [dict get $vhdl_dict $xmlfile]]
1705 lappend vhdls $vhdl_file
1706 set xmlfile [
file normalize $xmlfile]
1707 Msg Info "Copying $xmlfile to $dst and replacing place holders..."
1708 set in [open $xmlfile r]
1710 if {[regexp {/xml/+(.*)$} $xmlfile XXX out_with_dir]} {
1711 set out_file $dst/$out_with_dir
1712 lappend xmls $out_with_dir
1713 Msg Debug "xml file $xmlfile is contained in a directory called 'xml', so file will be copied to $out_file"
1714 set out_dir [
file dir $out_file]
1715 if {![
file exists $out_dir]} {
1719 set out_file $dst/[
file tail $xmlfile]
1720 lappend xmls [
file tail $xmlfile]
1723 set out [open $out_file w]
1724 while {[
gets $in line] != -1} {
1725 set new_line [regsub {(.*)__VERSION__(.*)} $line "\\1$xml_version\\2"]
1726 set new_line2 [regsub {(.*)__GIT_SHA__(.*)} $new_line "\\1$xml_sha\\2"]
1727 puts $out $new_line2
1732 Msg Warning "XML file $xmlfile not found"
1735 if {${xml_list_error}} {
1736 Msg Error "Invalid files added to $list_file!"
1739 set cnt [
llength $xmls]
1740 Msg Info "$cnt xml file/s copied"
1743 if {$can_generate == 1} {
1746 file mkdir "address_decode"
1748 foreach x $xmls v $vhdls {
1750 set x [
file normalize ../$x]
1751 if {[
file exists $x]} {
1752 lassign [
ExecuteRet gen_ipbus_addr_decode --no-timestamp $x 2>&1] status log
1754 set generated_vhdl ./ipbus_decode_[
file rootname [
file tail $x]].vhd
1755 if {$generate == 1} {
1756 Msg Info "Copying generated VHDL file $generated_vhdl into $v (replacing if necessary)"
1757 file copy -force -- $generated_vhdl $v
1759 if {[
file exists $v]} {
1761 set n [
llength $diff]
1763 Msg CriticalWarning "$v does not correspond to its XML $x, [
expr {$n / 3}] line/s differ:"
1764 Msg Status [
join $diff "\n"]
1765 set diff_file [open ../diff_[
file rootname [
file tail $x]].txt w]
1766 puts $diff_file $diff
1769 Msg Info "[
file tail $x] and $v match."
1772 Msg Warning "VHDL address map file $v not found."
1776 Msg Warning "Address map generation failed for [
file tail $x]: $log"
1779 Msg Warning "Copied XML file $x not found."
1782 Msg Info "Skipped verification of [
file tail $x] as no VHDL file was specified."
1786 file delete -force address_decode
1803 proc ChebyGenFlag {gen} {
1805 "hdl" {
return "--gen-hdl"}
1806 "consts" {
return "--gen-consts"}
1807 "c" {
return "--gen-c"}
1808 "c_check" {
return "--gen-c-check-layout"}
1809 "doc" {
return "--gen-doc"}
1810 "memmap" {
return "--print-memmap"}
1811 "memmap_v" {
return "--print-memmap-verbose"}
1812 "pretty" {
return "--print-pretty"}
1813 "devicetree" {
return "--gen-devicetree"}
1814 "edge" {
return "--gen-edge"}
1815 "silecs" {
return "--gen-silecs"}
1816 "wbgen" {
return "--gen-wbgen-hdl"}
1817 "custom" {
return "--gen-custom"}
1818 "install" {
return "--gen-install-script"}
1819 "gena_memmap" {
return "--gen-gena-memmap"}
1820 "gena_regctrl" {
return "--gen-gena-regctrl"}
1821 default {
return ""}
1836 proc ParseChebyListFile {chb_file} {
1837 set fp [open $chb_file r]
1840 set lines [
split $data "\n"]
1843 foreach line $lines {
1845 set trimmed [
string trim $line]
1846 if {$trimmed eq "" || [
string index $trimmed 0] eq "#"} {
1849 set tokens [regexp -all -inline {\S+} $line]
1850 if {[
llength $tokens] < 1} {
1853 set input [
lindex $tokens 0]
1854 set rest [
lrange $tokens 1 end]
1856 set props [dict create]
1858 set eq_pos [
string first "=" $tok]
1859 if {$eq_pos == -1} {
1860 lappend outputs $tok
1862 set key [
string range $tok 0 [
expr {$eq_pos - 1}]]
1863 set val [
string range $tok [
expr {$eq_pos + 1}] end]
1864 dict set props $key $val
1867 lappend result [dict create input $input outputs $outputs props $props \
1868 line $line_num file $chb_file]
1901 proc BuildChebyCmdArgs {props outputs} {
1903 if {[dict exists $props "gen"]} {
1904 set gen_csv [dict get $props "gen"]
1905 set gens [
split $gen_csv ","]
1907 set ngens [
llength $gens]
1909 if {[
llength $outputs] == 1} {
1910 set ext [
string tolower [
file extension [
lindex $outputs 0]]]
1915 ".sv" {
set gens [list "hdl"]}
1917 ".hpp" {
set gens [list "c"]}
1921 ".tex" {
set gens [list "doc"]}
1923 return [list 1 "Cannot infer generator from output extension '$ext'.\
1924 Please add a `gen=...` property to the line." [list]]
1929 return [list 1 "Cannot map [
llength $outputs] outputs without a `gen=...`\
1930 property listing the generators." [list]]
1933 if {[
llength $outputs] != $ngens} {
1934 return [list 1 "Number of declared outputs ([
llength $outputs]) does not match\
1935 number of generators ($ngens, gen=[
join $gens ,])." [list]]
1938 foreach g $gens o $outputs {
1939 lappend gen_map $g $o
1942 set option_map [dict create \
1944 "header" "--header" \
1945 "c_style" "--c-style" \
1946 "consts_style" "--consts-style" \
1948 "address_space" "--address-space" \
1949 "out_prefix" "--out-prefix" \
1950 "ff_reset" "--ff-reset" \
1951 "word_endian" "--word-endian" \
1952 "wb_lib_name" "--wb-lib-name" \
1953 "axil_lib_name" "--axil-lib-name" \
1954 "rest_headers" "--rest-headers" \
1957 set bool_map [dict create \
1958 "hdl_preload" "--hdl-preload" \
1959 "doc_hide_comments" "--doc-hide-comments" \
1960 "doc_no_reg_drawing" "--doc-no-reg-drawing" \
1961 "doc_include_js_dep" "--doc-include-js-dep" \
1962 "gena_common_visual" "--gena-common-visual" \
1963 "gen_c_bit_struct" "--gen-c-bit-struct" \
1964 "gen_gena_dsp" "--gen-gena-dsp"
1967 set opt_flags [list]
1968 dict for {k v} $props {
1969 if {$k eq "gen" || $k eq "profile"} {
1972 if {[dict exists $option_map $k]} {
1973 lappend opt_flags [dict get $option_map $k] $v
1974 } elseif {[dict exists $bool_map $k]} {
1975 if {$v eq "1" || [string tolower $v] eq "true" || [string tolower $v] eq "yes"} {
1976 lappend opt_flags [dict get $bool_map $k]
1978 } elseif {[string range $k 0 1] eq "--"} {
1979 lappend opt_flags $k $v
1981 Msg Warning "Unknown cheby property '$k=$v', ignoring."
1984 return [list 0 $gen_map $opt_flags]
1997 proc ChebyRelPath {base abs} {
1998 set base [
file normalize $base]
1999 set abs [
file normalize $abs]
2000 if {$base eq [
file dirname $abs]} {
2001 return [
file tail $abs]
2021 proc StripChebyGeneratedHeader {lines} {
2022 set idx [lsearch -regexp $lines {Do not edit\.\s+Generated by cheby}]
2032 set ts_line [
lindex $lines [
expr {$idx + 2}]]
2033 if {$ts_line ne "" && [
string first "Generated on " $ts_line] >= 0} {
2036 return [
lreplace $lines $idx $last]
2049 proc FilesAreSemanticallyEqual {file1 file2 {skip_cheby_header 0}} {
2050 if {![
file exists $file1] || ![
file exists $file2]} {
2053 set fa [open $file1 r]
2054 set fb [open $file2 r]
2057 while {[
gets $fa line] != -1} {
2058 set line [
string trimright $line]
2059 if {[
string trim $line] eq ""} { continue}
2062 while {[
gets $fb line] != -1} {
2063 set line [
string trimright $line]
2064 if {[
string trim $line] eq ""} { continue}
2069 if {$skip_cheby_header} {
2073 if {[
llength $la] != [
llength $lb]} {
2076 foreach x $la y $lb {
2077 if {$x ne $y} {
return 0}
2113 proc CopyChebyFiles {proj_dir path dst {cheby_version "0.0.0"} {cheby_sha "00000000"} \
2114 {generate 0} {profile "all"} {tool "cheby"} {strict 0} {verbose 0}} {
2115 set chb_files [glob -nocomplain $proj_dir/list/*.chb]
2116 if {[
llength $chb_files] == 0} {
2117 Msg CriticalWarning "No files with .chb extension found in $proj_dir/list."
2123 if {$generate == 1} {
2124 Msg Error "Cannot run cheby: '$tool' not found or not working: $tool_msg"
2127 Msg Warning "Cheby executable '$tool' not found or not working ($tool_msg).\
2128 Will not verify cheby outputs."
2132 Msg Info "Using cheby tool: $tool ([
string trim $tool_msg])"
2133 if {$cheby_version ne "" && $cheby_sha ne ""} {
2134 Msg Info "Cheby list set version: $cheby_version, SHA: $cheby_sha"
2137 set dst [
file normalize $dst]
2139 set tmp_dir [
file join $dst _check]
2141 set inputs_dir [
file join $dst inputs]
2142 file mkdir $inputs_dir
2148 set severity [
expr {$strict ? "Error" : "CriticalWarning"}]
2150 foreach chb_file $chb_files {
2151 Msg Info "Processing cheby list file: $chb_file"
2153 foreach entry $entries {
2155 set input_rel [dict get $entry input]
2156 set outputs [dict get $entry outputs]
2157 set props [dict get $entry props]
2158 set line_num [dict get $entry line]
2159 set chb_tail [
file tail $chb_file]
2161 set line_profile [
DictGet $props "profile" "all"]
2162 if {$profile ne "all" && $line_profile ne "all" && $line_profile ne $profile} {
2164 Msg Info " Skipping $chb_tail:$line_num (profile='$line_profile')"
2169 set abs_input [
file normalize "$path/$input_rel"]
2170 if {![
file exists $abs_input]} {
2171 Msg $severity "Cheby input not found ($chb_tail:$line_num): $abs_input"
2178 Msg $severity "$chb_tail:$line_num: $gen_map"
2185 set cheby_cwd [
file dirname $abs_input]
2186 set cmd [list $tool -i [
file tail $abs_input]]
2187 foreach f $opt_flags {
lappend cmd $f}
2189 set out_targets [list]
2191 foreach {gen out_rel} $gen_map {
2193 if {$gen_flag eq ""} {
2194 Msg $severity "$chb_tail:$line_num: unknown generator '$gen'"
2199 set abs_target [
file normalize "$path/$out_rel"]
2200 if {$generate == 1} {
2201 set out_path $abs_target
2203 set out_path [
file join $tmp_dir \
2204 [
file rootname [
file tail $abs_target]]_${n_lines}_${gen}[
file extension $abs_target]]
2206 lappend cmd $gen_flag [
ChebyRelPath $cheby_cwd $out_path]
2207 lappend out_targets [list $out_rel $abs_target $out_path $gen]
2209 if {$bad_gen} { continue}
2211 foreach t $out_targets {
2212 file mkdir [
file dirname [
lindex $t 1]]
2213 file mkdir [
file dirname [
lindex $t 2]]
2217 Msg Info " Running (cwd=$cheby_cwd): [
join $cmd { }]"
2225 Msg $severity "Cheby failed for $input_rel ($chb_tail:$line_num): $cmsg"
2231 if {$generate == 1} {
2232 foreach t $out_targets {
2233 Msg Info " Generated: [
lindex $t 0]"
2237 foreach t $out_targets {
2238 set out_rel [
lindex $t 0]
2239 set abs_target [
lindex $t 1]
2240 set out_path [
lindex $t 2]
2241 if {![
file exists $abs_target]} {
2242 Msg $severity "Expected cheby output missing: $out_rel ($chb_tail:$line_num)"
2247 Msg $severity "Cheby output mismatch: $out_rel does not match what cheby\
2248 would generate from $input_rel ($chb_tail:$line_num).\
2249 Run `Hog/Do cheby <project> -generate` to refresh."
2250 catch {
file copy -force $out_path "$dst/diff_[
file tail $abs_target]"}
2254 Msg Info " OK: $out_rel matches generator output."
2259 catch {
file copy -force $abs_input [
file join $inputs_dir [
file tail $abs_input]]}
2260 foreach t $out_targets {
2261 set abs_target [
lindex $t 1]
2262 if {[
file exists $abs_target]} {
2263 catch {
file copy -force $abs_target [
file join $dst [
file tail $abs_target]]}
2269 catch {
file delete -force $tmp_dir}
2271 set summary "Cheby summary: parsed $n_lines line(s), ran $n_processed cheby invocation(s),\
2272 $n_changed change(s), $n_errors error(s)."
2273 if {$n_errors > 0} {
2274 Msg CriticalWarning $summary
2288 proc DescriptionFromConf {conf_file} {
2289 set f [open $conf_file "r"]
2290 set lines [
split [read $f] "\n"]
2292 set second_line [
lindex $lines 1]
2295 if {![regexp {\#+ *(.+)} $second_line - description]} {
2299 if {[regexp -all {test|Test|TEST} $description]} {
2300 set description "test"
2313 proc DictGet {dictName keyName {default ""}} {
2314 if {[dict exists $dictName $keyName]} {
2315 return [dict get $dictName $keyName]
2326 proc DictSort {dict args} {
2328 foreach key [lsort {*}$args [dict keys $dict]] {
2329 dict set res $key [dict get $dict $key]
2339 proc DoxygenVersion {target_version} {
2340 set ver [
split $target_version "."]
2341 set v [
Execute doxygen --version]
2342 Msg Info "Found Doxygen version: $v"
2343 set current_ver [
split $v ". "]
2344 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
2345 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
2347 return [
expr {$target <= $current}]
2358 proc eos {command {attempt 1}} {
2360 if {![
info exists env(EOS_MGM_URL)]} {
2361 Msg Warning "Environment variable EOS_MGM_URL not set, setting it to default value root://eosuser.cern.ch"
2362 set ::env(EOS_MGM_URL) "root://eosuser.cern.ch"
2365 Msg Warning "The value of attempt should be 1 or more, not $attempt, setting it to 1 as default"
2368 for {
set i 0} {$i < $attempt} {
incr i} {
2369 set ret [
catch {
exec -ignorestderr eos {*}$command} result]
2374 set wait [
expr {1 + int(rand() * 29)}]
2375 Msg Warning "Command $command failed ($i/$attempt): $result, trying again in $wait seconds..."
2376 after [
expr {$wait * 1000}]
2380 return [list $ret $result]
2384 proc ShellSingleQuote {s} {
2385 return "'[
string map {' '\\''} $s]'"
2397 proc ExecuteRetUserEnv {args} {
2404 set tool [
lindex $args 0]
2405 if {[regexp {\.py$} $tool]} {
2406 if {[
info exists env(HOG_PYTHON)] && $env(HOG_PYTHON) ne ""} {
2407 set py $env(HOG_PYTHON)
2409 set py /usr/bin/python3
2411 set run_args [
linsert $args 0 $py]
2415 foreach a $run_args {
2418 set inner [
join $quoted { }]
2420 if {[
OS] eq "windows"} {
2426 set cmd "env -u PYTHONHOME -u PYTHONPATH -u PYTHON \
2427 PATH=/usr/local/bin:/usr/bin:/bin:\$PATH $inner"
2428 return [
ExecuteRet {*}[list /bin/bash -c $cmd]]
2438 proc Execute {args} {
2442 Msg Error "Command [
join $args] returned error code: $ret"
2456 proc ExecuteRet {args} {
2458 if {[
llength $args] == 0} {
2459 Msg CriticalWarning "No argument given"
2463 set ret [
catch {
exec -ignorestderr {*}$args} result]
2466 return [list $ret $result]
2473 proc ExtractFilesSection {file_data} {
2474 set in_files_section 0
2477 foreach line $file_data {
2478 if {[regexp {^ *\[ *files *\]} $line]} {
2479 set in_files_section 1
2482 if {$in_files_section} {
2483 if {[regexp {^ *\[.*\]} $line]} {
2486 lappend result $line
2491 if {!$in_files_section} {
2505 proc ExtractVersionFromTag {tag} {
2506 if {[regexp {^(?:b(\d+))?v0*(\d+)\.0*(\d+).0*(\d+)(?:-\d+)?$} $tag -> mr M m p]} {
2511 Msg Warning "Repository tag $tag is not in a Hog-compatible format."
2517 return [list $M $m $p $mr]
2527 proc FileCommitted {File} {
2529 set currentDir [
pwd]
2530 cd [
file dirname [
file normalize $File]]
2531 set GitLog [
Git ls-files [
file tail $File]]
2532 if {$GitLog == ""} {
2533 Msg CriticalWarning "File [
file normalize $File] is not in the git repository. Please add it with:\n git add [
file normalize $File]\n"
2544 proc FindCommonGitChild {SHA1 SHA2} {
2546 set commits [
Git {log --oneline --merges}]
2549 foreach line [
split $commits "\n"] {
2550 set commit [
lindex [
split $line] 0]
2554 set ancestor $commit
2567 proc findFiles {basedir pattern} {
2570 set basedir [
string trimright [
file join [
file normalize $basedir] { }]]
2576 foreach fileName [glob -nocomplain -type {f r} -path $basedir $pattern] {
2577 lappend fileList $fileName
2581 foreach dirName [glob -nocomplain -type {d r} -path $basedir *] {
2584 set subDirList [
findFiles $dirName $pattern]
2585 if {[
llength $subDirList] > 0} {
2586 foreach subDirFile $subDirList {
2587 lappend fileList $subDirFile
2598 proc FindFileType {file_name} {
2599 set extension [
file extension $file_name]
2602 set file_extension "USE_SIGNALTAP_FILE"
2605 set file_extension "VHDL_FILE"
2608 set file_extension "VHDL_FILE"
2611 set file_extension "VERILOG_FILE"
2614 set file_extension "SYSTEMVERILOG_FILE"
2617 set file_extension "SDC_FILE"
2620 set file_extension "PDC_FILE"
2623 set file_extension "NDC_FILE"
2626 set file_extension "FDC_FILE"
2629 set file_extension "SOURCE_FILE"
2632 set file_extension "IP_FILE"
2635 set file_extension "QSYS_FILE"
2638 set file_extension "QIP_FILE"
2641 set file_extension "SIP_FILE"
2644 set file_extension "BSF_FILE"
2647 set file_extension "BDF_FILE"
2650 set file_extension "COMMAND_MACRO_FILE"
2653 set file_extension "VQM_FILE"
2656 set file_extension "ERROR"
2657 Msg Error "Unknown file extension $extension"
2660 return $file_extension
2667 proc FindNewestVersion {versions} {
2668 set new_ver 00000000
2669 foreach ver $versions {
2671 if {[
expr 0x$ver > 0x$new_ver]} {
2681 proc FindRepoRoot {start_dir} {
2682 if {[
file pathtype $start_dir] eq "relative"} {
2683 set dir [
file normalize [
file join [
pwd] $start_dir]]
2688 if {[
file exists [
file join $dir Top]] && [
file exists [
file join $dir Projects]]} {
2691 set parent [
file dirname $dir]
2692 if {$parent eq $dir} {
2704 proc FindVhdlVersion {file_name} {
2705 set extension [
file extension $file_name]
2708 set vhdl_version "-hdl_version VHDL_2008"
2711 set vhdl_version "-hdl_version VHDL_2008"
2718 return $vhdl_version
2725 proc FormatGeneric {generic} {
2726 if {[
string is integer "0x$generic"]} {
2727 return [
format "32'h%08X" "0x$generic"]
2730 return [
format "32'h%08X" 0]
2740 proc GenerateBitstream {{run_folder ""} {repo_path .} {njobs 1}} {
2741 Msg Info "Starting write bitstream flow..."
2743 set revision [get_current_revision]
2744 if {[
catch {execute_module -tool asm} result]} {
2745 Msg Error "Result: $result\n"
2746 Msg Error "Generate bitstream failed. See the report file.\n"
2748 Msg Info "Generate bitstream was successful for revision $revision.\n"
2751 Msg Info "Run GENERATEPROGRAMMINGDATA ..."
2752 if {[
catch {run_tool -name {GENERATEPROGRAMMINGDATA}}]} {
2753 Msg Error "GENERATEPROGRAMMINGDATA FAILED!"
2755 Msg Info "GENERATEPROGRAMMINGDATA PASSED."
2757 Msg Info "Sourcing Hog/Tcl/integrated/post-bitstream.tcl"
2758 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
2760 prj_run Export -impl Implementation0 -task Bitgen
2770 proc GenerateQsysSystem {qsysFile commandOpts} {
2772 if {[
file exists $qsysFile] != 0} {
2773 set qsysPath [
file dirname $qsysFile]
2774 set qsysName [
file rootname [
file tail $qsysFile]]
2775 set qsysIPDir "$qsysPath/$qsysName"
2776 set qsysLogFile "$qsysPath/$qsysName.qsys-generate.log"
2779 if {![
info exists ::env(QSYS_ROOTDIR)]} {
2780 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
2781 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
2782 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
2784 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
2787 set qsys_rootdir $::env(QSYS_ROOTDIR)
2790 set cmd "$qsys_rootdir/qsys-generate"
2791 set cmd_options "$qsysFile --output-directory=$qsysIPDir $commandOpts"
2792 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
2793 Msg Info "Executing: $cmd $cmd_options"
2794 Msg Info "Saving logfile in: $qsysLogFile"
2795 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
2796 set makeRet [
lindex [dict get $opt -errorcode] end]
2797 Msg CriticalWarning "$cmd returned with $makeRet"
2800 Msg Error " Could not execute command $cmd"
2804 set qsysIPFileList [
concat \
2805 [glob -nocomplain -directory $qsysIPDir -types f *.ip *.qip] \
2806 [glob -nocomplain -directory "$qsysIPDir/synthesis" -types f *.ip *.qip *.vhd *.vhdl]]
2807 foreach qsysIPFile $qsysIPFileList {
2808 if {[
file exists $qsysIPFile] != 0} {
2810 set_global_assignment -name $qsysIPFileType $qsysIPFile
2812 set IpMd5Sum [
Md5Sum $qsysIPFile]
2814 set fileDir [
file normalize "./hogTmp"]
2815 set fileName "$fileDir/.hogQsys.md5"
2816 if {![
file exists $fileDir]} {
2819 set hogQsysFile [open $fileName "a"]
2820 set fileEntry "$qsysIPFile\t$IpMd5Sum"
2821 puts $hogQsysFile $fileEntry
2826 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
2836 proc GenericToSimulatorString {prop_dict target} {
2838 dict for {theKey theValue} $prop_dict {
2839 set theValue [string trim $theValue]
2848 regexp {([0-9]*)('h)([0-9a-fA-F]*)} $theValue valueHexFull valueNumBits valueHexFlag valueHex
2849 regexp {^([0-9]*)$} $theValue valueIntFull ValueInt
2850 regexp {(?!^\d+$)^.+$} $theValue valueStrFull ValueStr
2851 if {[string tolower $target] == "vivado" || [string tolower $target] == "xsim"} {
2852 if {[string tolower $theValue] == "true" || [string tolower $theValue] == "false"} {
2853 set prj_generics "$prj_generics $theKey=[string tolower $theValue]"
2854 } elseif {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2855 set prj_generics "$prj_generics $theKey=$valueHexFull"
2856 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2857 set prj_generics "$prj_generics $theKey=$ValueInt"
2858 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2859 set prj_generics "$prj_generics $theKey=\"$ValueStr\""
2861 set prj_generics "$prj_generics $theKey=\"$theValue\""
2863 } elseif {[lsearch -exact [GetSimulators] [string tolower $target]] >= 0} {
2864 if {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2866 scan $valueNumBits %d numBits
2868 scan $valueHex %x numHex
2869 binary scan [binary format "I" $numHex] "B*" binval
2870 set numBits [expr {$numBits - 1}]
2871 set numBin [string range $binval end-$numBits end]
2872 set prj_generics "$prj_generics $theKey=\"$numBin\""
2873 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2874 set prj_generics "$prj_generics $theKey=$ValueInt"
2875 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2876 set prj_generics "$prj_generics {$theKey=\"$ValueStr\"}"
2878 set prj_generics "$prj_generics {$theKey=\"$theValue\"}"
2881 Msg Warning "Target : $target not implemented"
2884 return $prj_generics
2894 proc GetConfFiles {proj_dir} {
2895 Msg Debug "GetConfFiles called with proj_dir=$proj_dir"
2896 if {![
file isdirectory $proj_dir]} {
2897 Msg Error "$proj_dir is supposed to be the top project directory"
2900 set conf_file [
file normalize $proj_dir/hog.conf]
2901 set sim_file [
file normalize $proj_dir/sim.conf]
2902 set pre_tcl [
file normalize $proj_dir/pre-creation.tcl]
2903 set post_tcl [
file normalize $proj_dir/post-creation.tcl]
2904 set pre_rtl [
file normalize $proj_dir/pre-rtl.tcl]
2905 set post_rtl [
file normalize $proj_dir/post-rtl.tcl]
2908 return [list $conf_file $sim_file $pre_tcl $post_tcl $pre_rtl $post_rtl \
2909 [
file normalize $proj_dir/pre-*.tcl] \
2910 [
file normalize $proj_dir/post-*.tcl]]
2919 proc GetCustomCommands {parameters {directory .}} {
2920 set commands_dict [dict create]
2921 set commands_files [glob -nocomplain $directory/*.tcl]
2923 if {[
llength $commands_files] == 0} {
2927 foreach file $commands_files {
2931 if {$custom_cmd eq ""} {
2936 set custom_cmd_name [dict get $custom_cmd NAME]
2938 Msg Debug "Loaded custom command '$custom_cmd_name' from $file"
2941 if {[dict exists $commands_dict $custom_cmd_name]} {
2942 Msg Error "Custom command '$custom_cmd_name' in $file already defined as: \[dict get $commands_dict $custom_cmd_name\]. Skipping."
2947 set custom_cmd_name [
string toupper $custom_cmd_name]
2948 dict set commands_dict $custom_cmd_name $custom_cmd
2951 return $commands_dict
2954 proc SanitizeCustomCommand {cmdDict file parameters} {
2959 foreach k [dict keys $cmdDict] {
2960 set K [
string toupper $k]
2961 dict set normalized $K [dict get $cmdDict $k]
2964 set cmdDict $normalized
2965 if {![dict exists $cmdDict NAME]} {
2966 Msg Error "Custom command in $file missing required key NAME. Skipping."
2969 if {![dict exists $cmdDict SCRIPT]} {
2970 Msg Error "Custom command '$[dict get $cmdDict NAME]' in $file missing SCRIPT. Skipping."
2975 set allowed {NAME DESCRIPTION OPTIONS CUSTOM_OPTIONS SCRIPT IDE NO_EXIT}
2976 foreach k [dict keys $cmdDict] {
2977 if {[lsearch -exact $allowed $k] < 0} {
2978 Msg Warning "Custom command '[dict get $cmdDict NAME]' in $file: unknown key '$k'. Allowed: $allowed. Skipping."
2983 set name [
string trim [dict get $cmdDict NAME]]
2985 Msg Error "Custom command in $file has empty NAME. Skipping."
2989 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]+$} $name]} {
2990 Msg Error "Custom command NAME '$name' (file $file) contains invalid characters."
2994 if {![dict exists $cmdDict DESCRIPTION]} {
2995 dict set cmdDict DESCRIPTION "No description provided."
2999 set hog_parameters {}
3000 foreach p $parameters {
3001 lappend hog_parameters [
lindex $p 0]
3006 if {[dict exists $cmdDict OPTIONS]} {
3007 set raw_opts [dict get $cmdDict OPTIONS]
3008 if {![
llength $raw_opts]} {
3012 foreach item $raw_opts {
3014 foreach p $parameters {
3015 set hog_parameter [
lindex $p 0]
3016 if {$item eq $hog_parameter} {
3017 lappend hog_options $p
3023 Msg Warning "Custom command '$name' in $file: option '$item' not found in Hog parameters. Skipping."
3026 dict set cmdDict OPTIONS $hog_options
3028 dict set cmdDict OPTIONS {}
3034 if {[dict exists $cmdDict CUSTOM_OPTIONS]} {
3035 set raw_opts [dict get $cmdDict CUSTOM_OPTIONS]
3036 if {![
llength $raw_opts]} {
3039 foreach item $raw_opts {
3040 if {[
llength $item] != 2 && [
llength $item] != 3} {
3041 Msg Error "Bad custom option: \[$item\]. Custom command '$name' in $file: \
3042 each CUSTOM_OPTIONS entry must be {option \"help\"} for flags \
3043 and {option \"default_value\" \"help\"} for options with arguments. Skipping command."
3047 if {[
llength $item] == 2} {
3048 lassign $item opt help
3051 lassign $item opt def help
3054 if {[
IsInList $opt $hog_parameters] == 1} {
3055 Msg Warning "Custom command '$name' in $file: option '$opt' already defined in Hog parameters. Skipping."
3061 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]*(\.arg)?$} $opt]} {
3062 Msg Error "Custom command '$name' in $file: invalid option name '$opt'."
3067 Msg Warning "Custom command '$name' option '$opt' has empty help text."
3071 dict set cmdDict CUSTOM_OPTIONS {}
3075 if {[dict exists $cmdDict NO_EXIT]} {
3076 set no_exit [dict get $cmdDict NO_EXIT]
3077 set no_exit [
string tolower [
string trim $no_exit]]
3079 if {$no_exit eq "1" || $no_exit eq "true"} {
3085 dict set cmdDict NO_EXIT $no_exit
3087 dict set cmdDict NO_EXIT 0
3093 proc LoadCustomCommandFile {file parameters} {
3095 set dir [
file dirname $file]
3097 unset -nocomplain ::hog_command
3098 set rc [
catch {source $file} err]
3101 Msg Error "Error sourcing custom command file $file: $err"
3104 if {![
info exists ::hog_command]} {
3105 Msg Warning "File $file did not define ::hog_command. Skipping."
3108 set cmdDict $::hog_command
3110 if {[
catch {dict size $cmdDict}]} {
3111 Msg Error "In $file ::hog_command is not a valid dict. Skipping."
3121 proc GetDateAndTime {commit} {
3122 set clock_seconds [
clock seconds]
3125 set date [
Git "log -1 --format=%cd --date=format:%d%m%Y $commit"]
3126 set timee [
Git "log -1 --format=%cd --date=format:00%H%M%S $commit"]
3128 Msg Warning "Found Git version older than 2.9.3. Using current date and time instead of commit time."
3129 set date [
clock format $clock_seconds -format {%d%m%Y}]
3130 set timee [
clock format $clock_seconds -format {00%H%M%S}]
3132 return [list $date $timee]
3144 proc GetFile {file fileset} {
3147 set Files [get_files -all $file -of_object [get_filesets $fileset]]
3148 set f [
lindex $Files 0]
3156 puts "***DEBUG Hog:GetFile $file"
3165 proc GetFileGenerics {filename {entity ""}} {
3167 if {[
string equal $file_type "VERILOG_FILE"] || [
string equal $file_type "SYSTEMVERILOG_FILE"]} {
3169 }
elseif {[
string equal $file_type "VHDL_FILE"]} {
3172 Msg CriticalWarning "Could not determine extension of top level file."
3181 proc GetGenericsFromConf {proj_dir} {
3182 set generics_dict [dict create]
3183 set top_dir "Top/$proj_dir"
3184 set conf_file "$top_dir/hog.conf"
3186 Msg Debug "GetGenericsFromConf called with proj_dir=$proj_dir, top_dir=$top_dir"
3188 if {[
file exists $conf_file]} {
3190 if {[dict exists $properties generics]} {
3191 set generics_dict [dict get $properties generics]
3194 Msg Warning "File $conf_file not found."
3196 return $generics_dict
3204 proc GetDefinesFromConf {proj_dir} {
3205 set defines_dict [dict create]
3206 set top_dir "Top/$proj_dir"
3207 set conf_file "$top_dir/hog.conf"
3209 Msg Debug "GetDefinesFromConf called with proj_dir=$proj_dir, top_dir=$top_dir"
3211 if {[
file exists $conf_file]} {
3213 if {[dict exists $properties defines]} {
3214 set defines_dict [dict get $properties defines]
3217 Msg Warning "File $conf_file not found."
3219 return $defines_dict
3232 proc GetSimSets {project_name repo_path {simsets ""} {ghdl 0} {no_conf 0}} {
3233 set simsets_dict [dict create]
3234 set list_dir "$repo_path/Top/$project_name/list"
3236 if {$simsets != ""} {
3237 foreach s $simsets {
3238 set list_file "$list_dir/$s.sim"
3239 if {[
file exists $list_file]} {
3240 lappend list_files $list_file
3241 }
elseif {$s != "sim_1"} {
3242 Msg CriticalWarning "Simulation set list file $list_file not found."
3247 set list_files [glob -nocomplain -directory $list_dir "*.sim"]
3251 set proj_dir [
file normalize $repo_path/Top/$project_name]
3252 set sim_file [
file normalize $proj_dir/sim.conf]
3254 foreach list_file $list_files {
3255 set file_name [
file tail $list_file]
3256 set simset_name [
file rootname $file_name]
3257 set fp [open $list_file r]
3258 set file_data [read $fp]
3260 set data [
split $file_data "\n"]
3262 set firstline [
lindex $data 0]
3264 if {[regexp {^ *\# *Simulator} $firstline]} {
3265 set simulator_prop [regexp -all -inline {\S+} $firstline]
3266 set simulator [
string tolower [
lindex $simulator_prop end]]
3268 Msg Warning "Simulator not set in $simset_name.sim. \
3269 The first line of $simset_name.sim should be #Simulator <SIMULATOR_NAME>,\
3270 where <SIMULATOR_NAME> can be xsim, questa, modelsim, ghdl, riviera, activehdl,\
3271 ies, or vcs, e.g. #Simulator questa.\
3272 Setting simulator by default to xsim."
3273 set simulator "xsim"
3275 if {$simulator eq "skip_simulation"} {
3276 Msg Info "Skipping simulation for $simset_name"
3279 if {($ghdl == 1 && $simulator != "ghdl") || ($ghdl == 0 && $simulator == "ghdl")} {
3283 set SIM_PROPERTIES ""
3284 if {[
file exists $sim_file] && $no_conf == 0} {
3285 set SIM_PROPERTIES [
ReadConf $sim_file]
3288 set global_sim_props [dict create]
3289 dict set global_sim_props "properties" [
DictGet $SIM_PROPERTIES "sim"]
3290 dict set global_sim_props "generics" [
DictGet $SIM_PROPERTIES "generics"]
3291 dict set global_sim_props "hog" [
DictGet $SIM_PROPERTIES "hog"]
3294 set sim_dict [dict create]
3295 dict set sim_dict "simulator" $simulator
3296 if {[dict exists $SIM_PROPERTIES $simset_name]} {
3297 dict set sim_dict "properties" [
DictGet $SIM_PROPERTIES $simset_name]
3298 dict set sim_dict "generics" [
DictGet $SIM_PROPERTIES "$simset_name:generics"]
3299 dict set sim_dict "hog" [
DictGet $SIM_PROPERTIES "$simset_name:hog"]
3300 }
elseif {$no_conf == 0} {
3302 set conf_dict [
ReadConf $list_file]
3303 set sim_dict [
MergeDict $sim_dict $conf_dict 0]
3305 set sim_dict [
MergeDict $sim_dict $global_sim_props 0]
3306 dict set simsets_dict $simset_name $sim_dict
3308 return $simsets_dict
3316 proc GetSimsetGenericsFromConf {proj_dir} {
3317 set simsets_generics_dict [dict create]
3318 set top_dir "Top/$proj_dir"
3319 set conf_file "$top_dir/sim.conf"
3322 if {[
file exists $conf_file]} {
3325 set simsets_generics_dict [dict filter $properties key *:generics]
3327 Msg Warning "File $conf_file not found."
3329 return $simsets_generics_dict
3340 proc GetGroupName {proj_dir repo_dir} {
3341 if {[regexp {^(.*)/(Top|Projects)/+(.*?)/*$} $proj_dir dummy possible_repo_dir proj_or_top dir]} {
3343 if {[
file normalize $repo_dir] eq [
file normalize $possible_repo_dir]} {
3344 set group [
file dir $dir]
3345 if {$group == "."} {
3350 Msg Warning "Project directory $proj_dir seems to be in $possible_repo_dir which is not a the main Git repository $repo_dir."
3353 Msg Warning "Could not parse project directory $proj_dir"
3366 proc GetHogDescribe {proj_dir {repo_path .}} {
3367 lassign [
GetRepoVersions $proj_dir $repo_path] global_commit global_version
3368 if {$global_commit == 0} {
3370 set new_sha "[
string toupper [
GetSHA]]"
3373 set new_sha [
string toupper $global_commit]
3393 proc GetHogFiles {args} {
3396 if {[
catch {
package require cmdline} ERROR]} {
3397 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
3404 {list_files.arg "" "The file wildcard, if not specified all Hog list files will be looked for."}
3405 {sha_mode "Forwarded to ReadListFile, see there for info."}
3406 {ext_path.arg "" "Path for the external libraries forwarded to ReadListFile."}
3407 {print_log "Forwarded to ReadListFile, see there for info."}
3409 set usage "USAGE: GetHogFiles \[options\] <list path> <repository path>"
3410 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
3414 set list_path [
lindex $args 0]
3415 set repo_path [
lindex $args 1]
3417 set list_files $options(list_files)
3418 set sha_mode $options(sha_mode)
3419 set ext_path $options(ext_path)
3420 set print_log $options(print_log)
3422 if {$sha_mode == 1} {
3423 set sha_mode_opt "-sha_mode"
3428 if {$print_log == 1} {
3429 set print_log_opt "-print_log"
3431 set print_log_opt ""
3435 if {$list_files == ""} {
3436 set list_files {.src,.con,.sim,.ext}
3438 set libraries [dict create]
3439 set properties [dict create]
3440 set list_files [glob -nocomplain -directory $list_path "*{$list_files}"]
3441 set filesets [dict create]
3443 foreach f $list_files {
3444 set ext [
file extension $f]
3445 if {$ext == ".ext"} {
3446 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $ext_path"] l p fs
3448 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $repo_path"] l p fs
3451 set properties [
MergeDict $p $properties]
3452 Msg Debug "list file $f, filesets: $fs"
3454 Msg Debug "Merged filesets $filesets"
3462 set proj_conf [
file normalize [
file join [
file dirname $list_path] hog.conf]]
3463 if {[
file exists $proj_conf]} {
3465 set already_tracked [dict create]
3466 dict for {libname libfiles} $libraries {
3467 foreach lf $libfiles {dict set already_tracked [file normalize $lf] 1}
3469 dict for {comp_name cfg_abs} $hls_configs {
3470 if {[dict exists $already_tracked $cfg_abs]} {
3471 Msg Debug "HLS component '$comp_name': cfg $cfg_abs already tracked via a .src, skipping conf-based GetHogFiles discovery."
3474 set lib_name "${comp_name}.src"
3475 dict lappend libraries $lib_name $cfg_abs
3476 dict set already_tracked $cfg_abs 1
3477 set hls_extras [ExpandHlsConfigFiles $cfg_abs]
3478 Msg Info "HLS component '$comp_name' (from hog.conf): tracking [expr {1 + [llength $hls_extras]}] file(s) via $cfg_abs"
3479 if {$print_log == 1} {
3480 Msg Status "\nhog.conf \[hls:$comp_name\] (auto-discovered)"
3481 set rel_cfg [Relative $repo_path $cfg_abs 1]
3482 if {$rel_cfg eq ""} {set rel_cfg $cfg_abs}
3483 if {[llength $hls_extras] == 0} {
3484 Msg Status "└── $rel_cfg"
3486 Msg Status "├── $rel_cfg"
3487 Msg Status " Inside [file tail $cfg_abs] (HLS auto-expand):"
3490 set n_extras [llength $hls_extras]
3492 foreach hls_extra $hls_extras {
3494 if {[dict exists $already_tracked $hls_extra]} {continue}
3495 dict lappend libraries $lib_name $hls_extra
3496 dict set already_tracked $hls_extra 1
3497 if {$print_log == 1} {
3498 if {$i == $n_extras} {set pad "└──"} else {set pad "├──"}
3499 set rel_extra [Relative [file dirname $cfg_abs] $hls_extra 1]
3500 if {$rel_extra eq ""} {set rel_extra $hls_extra}
3501 Msg Status " $pad $rel_extra"
3507 return [list $libraries $properties $filesets]
3514 proc GetIDECommand {proj_conf {custom_ver ""}} {
3515 if {$custom_ver ne ""} {
3516 set ide_name_and_ver [
string tolower "$custom_ver"]
3517 }
elseif {[
file exists $proj_conf]} {
3518 set ide_name_and_ver [
string tolower [
GetIDEFromConf $proj_conf]]
3520 Msg Error "Configuration file $proj_conf not found."
3523 set ide_name [
lindex [regexp -all -inline {\S+} $ide_name_and_ver] 0]
3525 if {$ide_name eq "vivado" || $ide_name eq "vivado_vitis_classic" || $ide_name eq "vivado_vitis_unified" || $ide_name eq "vitis_unified"} {
3526 set command "vivado"
3528 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
3529 set after_tcl_script " -tclargs "
3531 }
elseif {$ide_name eq "planahead"} {
3532 set command "planAhead"
3534 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
3535 set after_tcl_script " -tclargs "
3537 }
elseif {$ide_name eq "quartus"} {
3538 set command "quartus_sh"
3540 set before_tcl_script " -t "
3541 set after_tcl_script " "
3543 }
elseif {$ide_name eq "libero"} {
3546 set command "libero"
3547 set before_tcl_script "SCRIPT:"
3548 set after_tcl_script " SCRIPT_ARGS:\""
3550 }
elseif {$ide_name eq "diamond"} {
3551 set command "diamondc"
3552 set before_tcl_script " "
3553 set after_tcl_script " "
3555 }
elseif {$ide_name eq "vitis_classic"} {
3558 set before_tcl_script ""
3559 set after_tcl_script " "
3561 }
elseif {$ide_name eq "ghdl"} {
3563 set before_tcl_script " "
3564 set after_tcl_script " "
3567 Msg Error "IDE: $ide_name not known."
3570 return [list $command $before_tcl_script $after_tcl_script $end_marker]
3576 proc GetIDEFromConf {conf_file} {
3577 set f [open $conf_file "r"]
3580 if {[regexp -all {^\# *(\w*) *(vitis_(?:classic|unified))? *(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)?(_.*)? *$} $line dummy ide vitisflag version patch]} {
3581 if {[
info exists vitisflag] && $vitisflag != ""} {
3582 set ide "${ide}_${vitisflag}"
3585 if {[
info exists version] && $version != ""} {
3591 set ret [list $ide $ver]
3593 Msg CriticalWarning "The first line of hog.conf should be \#<IDE name> <version>, \
3594 where <IDE name>. is quartus, vivado, planahead, libero, diamond or ghdl, \
3595 and <version> the tool version, e.g. \#vivado 2020.2. Will assume vivado."
3596 set ret [list "vivado" "0.0.0"]
3603 proc GetIDEName {} {
3605 return "ISE/PlanAhead"
3623 proc GetIDEVersion {} {
3626 regexp {\d+\.\d+(\.\d+)?} [version -short] ver
3631 regexp {[\.0-9]+} $quartus(version) ver
3634 set ver [get_libero_version]
3637 regexp {\d+\.\d+(\.\d+)?} [sys_install version] ver
3640 regexp {\d+\.\d+(\.\d+)?} [version] ver
3643 set vitis_output [
exec vitis --version 2>@1]
3644 regexp {[Vv]itis\s+v?(\d+\.\d+(?:\.\d+)?)} $vitis_output -> ver
3658 proc GetLinkedFile {link_file} {
3659 if {[
file type $link_file] eq "link"} {
3660 if {[
OS] == "windows"} {
3662 lassign [
ExecuteRet realpath $link_file] ret msg
3663 lassign [
ExecuteRet cygpath -m $msg] ret2 msg2
3664 if {$ret == 0 && $ret2 == 0} {
3666 Msg Debug "Found link file $link_file on Windows, the linked file is: $real_file"
3668 Msg CriticalWarning "[
file normalize $link_file] is a soft link. \
3669 Soft link are not supported on Windows and readlink.exe or cygpath.exe did not work: readlink=$ret: $msg, cygpath=$ret2: $msg2."
3670 set real_file $link_file
3674 set linked_file [
file link $link_file]
3675 set real_file [
file normalize [
file dirname $link_file]/$linked_file]
3678 if {![
file exists $real_file]} {
3679 Msg Warning "$link_file is a broken link, because the linked file: $real_file does not exist."
3682 Msg Warning "$link file is not a soft link"
3683 set real_file $link_file
3696 proc GetMaxThreads {proj_dir} {
3698 if {[
file exists $proj_dir/hog.conf]} {
3700 if {[dict exists $properties parameters]} {
3701 set propDict [dict get $properties parameters]
3702 if {[dict exists $propDict MAX_THREADS]} {
3703 set maxThreads [dict get $propDict MAX_THREADS]
3707 Msg Warning "File $proj_dir/hog.conf not found. Max threads will be set to default value 1"
3720 proc GetModifiedFiles {{repo_path "."} {pattern "."}} {
3723 set ret [
Git "ls-files --modified $pattern"]
3732 proc GetOptions {argv parameters} {
3735 set param_list [list]
3736 set option_list [list]
3738 foreach p $parameters {
3739 lappend param_list [
lindex $p 0]
3743 while {$index < [
llength $argv]} {
3744 set arg [
lindex $argv $index]
3745 if {[
string first - $arg] == 0} {
3746 set option [
string trimleft $arg "-"]
3748 lappend option_list $arg
3749 if {[lsearch -regex $param_list "$option\[.arg]?"] >= 0} {
3750 if {[lsearch -regex $param_list "$option\[.arg]"] >= 0} {
3751 lappend option_list [
lindex $argv $index]
3756 lappend arg_list $arg
3760 Msg Debug "Argv: $argv"
3761 Msg Debug "Options: $option_list"
3762 Msg Debug "Arguments: $arg_list"
3763 return [list $option_list $arg_list]
3781 proc GetProjectFiles {{project_file ""}} {
3782 set libraries [dict create]
3783 set simlibraries [dict create]
3784 set constraints [dict create]
3785 set properties [dict create]
3786 set consets [dict create]
3787 set srcsets [dict create]
3788 set simsets [dict create]
3791 set all_filesets [get_filesets]
3792 set simulator [get_property target_simulator [current_project]]
3793 set top [get_property "top" [current_fileset]]
3795 dict lappend properties $topfile "top=$top"
3797 foreach fs $all_filesets {
3798 if {$fs == "utils_1"} {
3803 set all_files [get_files -quiet -of_objects [get_filesets $fs]]
3804 set fs_type [get_property FILESET_TYPE [get_filesets $fs]]
3806 if {$fs_type == "BlockSrcs"} {
3808 set dict_fs "sources_1"
3812 foreach f $all_files {
3819 if {[
lindex [get_property IS_GENERATED [
GetFile $f $fs]] 0] != 0} {
3824 if {[get_property FILE_TYPE [
GetFile $f $fs]] == "Configuration Files"} {
3830 if {[get_property CORE_CONTAINER [
GetFile $f $fs]] != ""} {
3831 if {[
file extension $f] == ".xcix"} {
3832 set f [get_property CORE_CONTAINER [
GetFile $f $fs]]
3840 if {[get_property SCOPED_TO_REF [
GetFile $f $fs]] != ""} {
3841 dict lappend properties $f "scoped_to_ref=[get_property SCOPED_TO_REF [
GetFile $f $fs]]"
3846 if {[get_property SCOPED_TO_CELLS [
GetFile $f $fs]] != ""} {
3847 dict lappend properties $f "scoped_to_cells=[regsub " " [get_property SCOPED_TO_CELLS [
GetFile $f $fs]] ","]"
3851 if {[
IsInList "PARENT_COMPOSITE_FILE" [list_property [
GetFile $f $fs]]]} {
3856 if {[
file tail $f] == "nocattrs.dat"} {
3861 if {[
file extension $f] != ".coe"} {
3862 set f [
file normalize $f]
3865 set type [get_property FILE_TYPE [
GetFile $f $fs]]
3867 set lib [get_property -quiet LIBRARY [
GetFile $f $fs]]
3870 Msg Debug "File $f Extension [
file extension $f] Type [
lindex $type 0]"
3872 if {[
string equal [
lindex $type 0] "VHDL"] && [
llength $type] == 1} {
3874 }
elseif {[
string equal [
lindex $type 0] "VHDL"] && [
string equal [
lindex $type 1] "2019"]} {
3880 }
elseif {[
string equal [
lindex $type 0] "Block"] && [
string equal [
lindex $type 1] "Designs"]} {
3883 }
elseif {[
string equal $type "SystemVerilog"] && [
file extension $f] != ".sv" && [
file extension $f] != ".svp"} {
3884 set prop "SystemVerilog"
3885 }
elseif {[
string equal [
lindex $type 0] "XDC"] && [
file extension $f] != ".xdc"} {
3887 }
elseif {[
string equal $type "Verilog Header"] && [
file extension $f] != ".vh" && [
file extension $f] != ".svh"} {
3888 set prop "verilog_header"
3889 }
elseif {[
string equal $type "Verilog Template"] && [
file extension $f] == ".v" && [
file extension $f] != ".sv"} {
3890 set prop "verilog_template"
3892 set type [
lindex $type 0]
3896 if {![
string equal $prop ""]} {
3897 dict lappend properties $f $prop
3900 if {[
string equal $fs_type "SimulationSrcs"]} {
3902 if {[
string equal $type "VHDL"]} {
3903 set library "${lib}.sim"
3905 set library "others.sim"
3909 dict lappend simsets $dict_fs $library
3912 dict lappend simlibraries $library $f
3913 }
elseif {[
string equal $type "VHDL"]} {
3916 dict lappend srcsets $dict_fs "${lib}.src"
3918 dict lappend libraries "${lib}.src" $f
3919 }
elseif {[
string first "IP" $type] != -1} {
3922 dict lappend srcsets $dict_fs "ips.src"
3924 dict lappend libraries "ips.src" $f
3925 Msg Debug "Appending $f to ips.src"
3926 }
elseif {[
string equal $fs_type "Constrs"]} {
3929 dict lappend consets $dict_fs "sources.con"
3931 dict lappend constraints "sources.con" $f
3935 dict lappend srcsets $dict_fs "others.src"
3937 dict lappend libraries "others.src" $f
3938 Msg Debug "Appending $f to others.src"
3941 if {[
lindex [get_property -quiet used_in_synthesis [
GetFile $f $fs]] 0] == 0} {
3942 dict lappend properties $f "nosynth"
3944 if {[
lindex [get_property -quiet used_in_implementation [
GetFile $f $fs]] 0] == 0} {
3945 dict lappend properties $f "noimpl"
3947 if {[
lindex [get_property -quiet used_in_simulation [
GetFile $f $fs]] 0] == 0} {
3948 dict lappend properties $f "nosim"
3950 if {[
lindex [get_property -quiet IS_MANAGED [
GetFile $f $fs]] 0] == 0 && [
file extension $f] != ".xcix"} {
3951 dict lappend properties $f "locked"
3957 dict lappend properties "Simulator" [get_property target_simulator [current_project]]
3960 set file [open $project_file r]
3961 set in_file_manager 0
3963 while {[
gets $file line] >= 0} {
3965 if {[regexp {^KEY ActiveRoot \"([^\"]+)\"} $line -> value]} {
3966 set top [
string range $value 0 [
expr {[string first "::" $value] - 1}]]
3970 if {[regexp {^LIST FileManager} $line]} {
3971 set in_file_manager 1
3976 if {$in_file_manager && [regexp {^ENDLIST} $line]} {
3981 if {$in_file_manager && [regexp {^VALUE \"([^\"]+)} $line -> value]} {
3984 lassign [
split $value ,] file_path file_type
3987 set library "others"
3988 while {[
gets $file line] >= 0} {
3989 if {$line == "ENDFILE"} {
3992 regexp {^LIBRARY=\"([^\"]+)} $line -> library
3993 regexp {^PARENT=\"([^\"]+)} $line -> parent_file
3995 Msg Debug "Found file ${file_path} in project.."
3996 if {$parent_file == ""} {
3997 if {$file_type == "hdl"} {
3999 if {[
IsInList "${library}.src" [
DictGet $srcsets "sources_1"]] == 0} {
4000 dict lappend srcsets "sources_1" "${library}.src"
4002 dict lappend libraries "${library}.src" $file_path
4006 if {[
GetModuleName $file_path] == [
string tolower $top] && $top != ""} {
4007 Msg Debug "Found top module $top in $file_path"
4008 dict lappend properties $file_path "top=$top"
4010 }
elseif {$file_type == "tb_hdl"} {
4012 dict lappend simsets "sim_1" "${library}.sim"
4014 dict lappend simlibraries "${library}.sim" $file_path
4015 }
elseif {$file_type == "io_pdc" || $file_type == "sdc"} {
4017 dict lappend consets "constrs_1" "sources.con"
4019 dict lappend constraints "sources.con" $file_path
4026 set fileData [read [open $project_file]]
4028 set project_path [
file dirname $project_file]
4031 regsub {<\?xml.*\?>} $fileData "" fileData
4034 regexp {<Implementation.*?>(.*)</Implementation>} $fileData -> implementationContent
4038 set sourceRegex {<Source name="([^"]*?)" type="([^"]*?)" type_short="([^"]*?)".*?>(.*?)</Source>}
4040 set optionsRegex {<Options(.*?)\/>}
4041 regexp $optionsRegex $implementationContent -> prj_options
4042 foreach option $prj_options {
4043 if {[regexp {^top=\"([^\"]+)\"} $option match result]} {
4048 while {[regexp $sourceRegex $implementationContent match name type type_short optionsContent]} {
4049 Msg Debug "Found file ${name} in project..."
4050 set file_path [
file normalize $project_path/$name]
4052 set optionsRegex {<Options(.*?)\/>}
4053 regexp $optionsRegex $optionsContent -> options
4054 set library "others"
4056 foreach option $options {
4057 if {[
string first "System Verilog" $option]} {
4060 if {[regexp {^lib=\"([^\"]+)\"} $option match1 result]} {
4065 if {[regexp {syn_sim="([^"]*?)"} $match match_sim simonly]} {
4070 if {$type_short == "VHDL" || $type_short == "Verilog" || $type_short == "IPX"} {
4071 if {$ext == ".src"} {
4072 if {[
IsInList "${library}${ext}" [
DictGet $srcsets "sources_1"]] == 0} {
4073 dict lappend srcsets "sources_1" "${library}${ext}"
4075 dict lappend libraries "${library}${ext}" $file_path
4076 }
elseif {$ext == ".sim"} {
4078 dict lappend simsets "sim_1" "${library}.sim"
4080 dict lappend simlibraries "${library}.sim" $file_path
4086 Msg Debug "Found top module $top in $file_path"
4087 dict lappend properties $file_path "top=$top"
4089 }
elseif {$type_short == "SDC"} {
4091 dict lappend consets "constrs_1" "sources.con"
4093 dict lappend constraints "sources.con" $file_path
4097 regsub -- $match $implementationContent "" implementationContent
4100 return [list $libraries $properties $simlibraries $constraints $srcsets $simsets $consets]
4107 proc GetProjectFlavour {proj_name} {
4109 set flavour [
string map {. ""} [
file extension $proj_name]]
4110 if {$flavour != ""} {
4111 if {[
string is integer $flavour]} {
4112 Msg Info "Project $proj_name has flavour = $flavour, the generic variable FLAVOUR will be set to $flavour"
4114 Msg Warning "Project name has a unexpected non numeric extension, flavour will be set to -1"
4131 proc GetProjectVersion {proj_dir repo_path {ext_path ""} {sim 0}} {
4132 if {![
file exists $proj_dir]} {
4133 Msg CriticalWarning "$proj_dir not found"
4140 lassign [
GitRet {describe --tags --abbrev=0 --match "v*"}] ret result
4142 Msg CriticalWarning "No Hog versioning tags (v*) in repo"
4149 Msg Warning "Repository is not clean"
4157 Msg Debug "Project version $v_proj, latest tag $v_last"
4159 Msg Debug "The specified project was modified since official version."
4166 Msg Info "The specified project was modified in the latest official version $ret"
4167 }
elseif {$comp == -1} {
4168 Msg Info "The specified project was modified in a past official version $ret"
4184 proc GetRepoVersions {proj_dir repo_path {ext_path ""} {sim 0}} {
4185 global NOT_CLEAN_WARNED
4186 if {[
catch {
package require cmdline} ERROR]} {
4187 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
4202 lappend SHAs [
GetSHA {Hog}]
4206 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
4207 Msg Debug "Hog submodule [
pwd] clean."
4208 lassign [
GetVer ./] hog_ver hog_hash
4210 Msg CriticalWarning "Hog submodule [
pwd] not clean, commit hash will be set to 0."
4211 set hog_hash "0000000"
4212 set hog_ver "00000000"
4218 foreach conf_file $conf_files {
4219 if {[
string match "$repo_path/*" $conf_file]} {
4220 set conf_file [
string replace $conf_file 0 [
string length $repo_path]]
4222 lappend project_files $conf_file
4225 lappend project_files Hog
4228 lassign [
GetVer [
join $conf_files]] top_ver top_hash
4229 lappend SHAs $top_hash
4230 lappend versions $top_ver
4237 lassign [
GetHogFiles -list_files "*.src" -sha_mode "$proj_dir/list/" $repo_path] src_files dummy
4238 dict for {f files} $src_files {
4239 # library names have a .src extension in values returned by GetHogFiles
4240 set name [file rootname [file tail $f]]
4241 if {[file ext $f] == ".oth"} {
4244 lassign [GetVer $files] ver hash
4245 # Msg Info "Found source list file $f, version: $ver commit SHA: $hash"
4247 lappend versions $ver
4249 lappend hashes $hash
4251 set relative_files ""
4252 foreach fil $files {
4253 if {[string match "$repo_path/*" $fil]} {
4254 set fil [string replace $fil 0 [string length $repo_path]]
4256 lappend relative_files $fil
4259 lappend project_files $f {*}$relative_files
4265 lassign [
GetHogFiles -list_files "*.con" -sha_mode "$proj_dir/list/" $repo_path] cons_files dummy
4266 dict for {f files} $cons_files {
4267 #library names have a .con extension in values returned by GetHogFiles
4268 set name [file rootname [file tail $f]]
4269 lassign [GetVer $files] ver hash
4270 #Msg Info "Found constraint list file $f, version: $ver commit SHA: $hash"
4272 Msg CriticalWarning "Constraints file $f not found in Git."
4274 lappend cons_hashes $hash
4276 lappend versions $ver
4277 set relative_files ""
4278 foreach fil $files {
4279 if {[string match "$repo_path/*" $fil]} {
4280 set fil [string replace $fil 0 [string length $repo_path]]
4282 lappend relative_files $fil
4284 lappend project_files $f {*}$relative_files
4291 lassign [
GetHogFiles -list_files "*.sim" -sha_mode "$proj_dir/list/" $repo_path] sim_files dummy
4292 dict for {f files} $sim_files {
4293 #library names have a .sim extension in values returned by GetHogFiles
4294 set name [file rootname [file tail $f]]
4295 lassign [GetVer $files] ver hash
4296 #Msg Info "Found simulation list file $f, version: $ver commit SHA: $hash"
4297 lappend sim_hashes $hash
4299 lappend versions $ver
4300 set relative_files ""
4301 foreach fil $files {
4302 if {[string match "$repo_path/*" $fil]} {
4303 set fil [string replace $fil 0 [string length $repo_path]]
4305 lappend relative_files $fil
4307 lappend project_files $f {*}$relative_files
4315 Msg CriticalWarning "No hashes found for constraints files (not in git)"
4318 set cons_hash [
string tolower [
Git "log --format=%h -1 $cons_hashes"]]
4325 set ext_files [glob -nocomplain "$proj_dir/list/*.ext"]
4328 foreach f $ext_files {
4329 set name [
file rootname [
file tail $f]]
4332 lappend ext_names $name
4333 lappend ext_hashes $hash
4336 lappend versions $ext_ver
4337 lappend project_files $f
4340 set file_data [read $fp]
4342 set data [
split $file_data "\n"]
4344 foreach line $data {
4345 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
4347 set file_and_prop [regexp -all -inline {\S+} $line]
4348 set hdlfile [
lindex $file_and_prop 0]
4349 set hdlfile $ext_path/$hdlfile
4350 if {[
file exists $hdlfile]} {
4351 set hash [
lindex $file_and_prop 1]
4352 set current_hash [
Md5Sum $hdlfile]
4353 if {[
string first $hash $current_hash] == -1} {
4354 Msg CriticalWarning "File $hdlfile has a wrong hash. Current checksum: $current_hash, expected: $hash"
4362 if {[
llength [glob -nocomplain $proj_dir/list/*.ipb]] > 0} {
4364 lassign [
GetHogFiles -list_files "*.ipb" -sha_mode "$proj_dir/list/" $repo_path] xml_files dummy
4365 set xml_source_files [dict get $xml_files "xml.ipb"]
4366 lassign [
GetVer $xml_source_files] xml_ver xml_hash
4367 lappend SHAs $xml_hash
4368 lappend versions $xml_ver
4369 set relative_files ""
4370 foreach fil $xml_source_files {
4371 if {[
string match "$repo_path/*" $fil]} {
4372 set fil [
string replace $fil 0 [
string length $repo_path]]
4374 lappend relative_files $fil
4376 lappend project_files {*}[glob $proj_dir/list/*.ipb] {*}$relative_files
4379 Msg Debug "This project does not use IPbus XMLs"
4385 if {[
llength [glob -nocomplain $proj_dir/list/*.chb]] > 0} {
4386 lassign [
GetHogFiles -list_files "*.chb" -sha_mode "$proj_dir/list/" $repo_path] cheby_files dummy
4387 if {[dict exists $cheby_files "cheby.chb"]} {
4388 set cheby_source_files [dict get $cheby_files "cheby.chb"]
4389 lassign [
GetVer $cheby_source_files] cheby_ver cheby_hash
4390 lappend SHAs $cheby_hash
4391 lappend versions $cheby_ver
4392 lappend project_files {*}[glob $proj_dir/list/*.chb] {*}$cheby_source_files
4398 Msg Info "This project does not use Cheby register maps"
4403 set user_ip_repos ""
4404 set user_ip_repo_hashes ""
4405 set user_ip_repo_vers ""
4407 if {[
file exists [
lindex $conf_files 0]]} {
4408 set PROPERTIES [
ReadConf [
lindex $conf_files 0]]
4409 if {[dict exists $PROPERTIES main]} {
4410 set main [dict get $PROPERTIES main]
4411 dict for {p v} $main {
4412 if {[string tolower $p] == "ip_repo_paths"} {
4414 if {[file isdirectory "$repo_path/$repo"]} {
4415 set repo_file_list [glob -nocomplain "$repo_path/$repo/*"]
4416 if {[llength $repo_file_list] == 0} {
4417 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
4419 lappend user_ip_repos "$repo_path/$repo"
4428 foreach repo $user_ip_repos {
4429 if {[
file isdirectory $repo]} {
4430 set repo_file_list [glob -nocomplain "$repo/*"]
4431 if {[
llength $repo_file_list] != 0} {
4432 lassign [
GetVer $repo] ver sha
4433 lappend user_ip_repo_hashes $sha
4434 lappend user_ip_repo_vers $ver
4435 lappend versions $ver
4436 lappend project_files $repo
4438 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
4441 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory does not exist."
4456 append dirty [
Git "status --untracked-files=no --porcelain" $project_files]
4460 Msg Debug "Project-relevant files are clean."
4463 if {![
info exists NOT_CLEAN_WARNED]} {
4464 Msg CriticalWarning "Project-relevant files not clean, commit hash and version will be set to 0."
4465 set NOT_CLEAN_WARNED 1
4473 while {$found == 0} {
4474 set global_commit [
Git "log --topo-order --format=%h -1 --abbrev=7 $SHAs"]
4479 if {$common_child == 0} {
4480 Msg CriticalWarning "The commit $sha is not an ancestor of the global commit $global_commit, which is OK. \
4481 But $sha and $global_commit do not have any common child, which is NOT OK. \
4482 This is probably do to a REBASE that is forbidden in Hog methodology as it changes git history. \
4483 Hog cannot guarantee the accuracy of the SHAs. \
4484 A way to fix this is to make a commit that touches all the projects in the repositories (e.g. change the Hog version), \
4485 but please do not rebase in the official branches in the future."
4487 Msg Info "The commit $sha is not an ancestor of the global commit $global_commit, adding the first common child $common_child instead..."
4488 lappend SHAs $common_child
4498 set global_commit "0000000"
4499 set global_version "00000000"
4504 set top_hash [
format %+07s $top_hash]
4505 set cons_hash [
format %+07s $cons_hash]
4506 return [list $global_commit $global_version \
4507 $hog_hash $hog_ver $top_hash $top_ver \
4508 $libs $hashes $vers $cons_ver $cons_hash \
4509 $ext_names $ext_hashes $xml_hash $xml_ver \
4510 $user_ip_repos $user_ip_repo_hashes $user_ip_repo_vers \
4511 $cheby_hash $cheby_ver]
4520 proc GetSHA {{path ""}} {
4523 lassign [
GitRet {log --format=%h --abbrev=7 -1}] status result
4525 return [
string tolower $result]
4527 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
4533 set repo_path [
lindex [
Git {rev-parse --show-toplevel}] 0]
4538 set file_in_module 0
4539 if {[
file exists .gitmodules]} {
4540 lassign [
GitRet "config --file .gitmodules --get-regexp path"] status result
4542 set submodules [
split $result "\n"]
4545 Msg Warning "Something went wrong while trying to find submodules: $result"
4548 foreach mod $submodules {
4549 set module [
lindex $mod 1]
4550 if {[
string first "$repo_path/$module" $f] == 0} {
4552 set file_in_module 1
4553 lappend paths "$module"
4558 if {$file_in_module == 0} {
4564 lassign [
GitRet {log --format=%h --abbrev=7 -1} $paths] status result
4566 return [
string tolower $result]
4568 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
4572 return [
string tolower $result]
4576 proc GetSimulators {} {
4577 set SIMULATORS [list "modelsim" "questa" "riviera" "activehdl" "ies" "vcs"]
4582 proc GetTopFile {} {
4584 set compile_order_prop [get_property source_mgmt_mode [current_project]]
4585 if {$compile_order_prop ne "All"} {
4586 Msg CriticalWarning "Compile order is not set to automatic, setting it now..."
4587 set_property source_mgmt_mode All [current_project]
4588 update_compile_order -fileset sources_1
4590 return [
lindex [get_files -quiet -compile_order sources -used_in synthesis -filter {FILE_TYPE =~ "VHDL*" || FILE_TYPE =~ "*Verilog*" }] end]
4591 }
elseif {[
IsISE]} {
4592 debug::design_graph_mgr -create [current_fileset]
4593 debug::design_graph -add_fileset [current_fileset]
4594 debug::design_graph -update_all
4595 return [
lindex [debug::design_graph -get_compile_order] end]
4597 Msg Error "GetTopFile not yet implemented for this IDE"
4602 proc GetTopModule {} {
4604 return [get_property top [current_fileset]]
4606 Msg Error "GetTopModule not yet implemented for this IDE"
4616 proc GetVer {path {force_develop 0}} {
4620 Msg CriticalWarning "Empty SHA found for ${path}. Commit to Git to resolve this warning."
4623 set p [
lindex $path 0]
4624 if {[
file isdirectory $p]} {
4627 cd [
file dirname $p]
4629 set repo_path [
Git {rev-parse --show-toplevel}]
4632 return [list [
GetVerFromSHA $SHA $repo_path $force_develop] $SHA]
4643 proc GetVerFromSHA {SHA repo_path {force_develop 0}} {
4644 global NO_HOG_VER_TAG_WARNED
4646 Msg CriticalWarning "Empty SHA found"
4649 lassign [
GitRet "tag --sort=creatordate --contain $SHA -l v*.*.* -l b*v*.*.*"] status result
4652 if {[regexp {^ *$} $result]} {
4654 lassign [
GitRet "log --oneline --pretty=\"%d\""] status2 tag_list
4657 set pattern {tag: v\d+\.\d+\.\d+}
4658 set real_tag_list {}
4659 foreach x $tag_list {
4660 set x_untrimmed [regexp -all -inline $pattern $x]
4661 regsub "tag: " $x_untrimmed "" x_trimmed
4662 set tt [
lindex $x_trimmed 0]
4663 if {![
string equal $tt ""]} {
4664 lappend real_tag_list $tt
4668 Msg Debug "Cleaned up list: $real_tag_list."
4670 set sorted_tags [lsort -decreasing -command CompareVersions $real_tag_list]
4672 Msg Debug "Sorted Tag list: $sorted_tags"
4674 set tag [
lindex $sorted_tags 0]
4677 set pattern {v\d+\.\d+\.\d+}
4678 if {![regexp $pattern $tag]} {
4679 if {![
info exists NO_HOG_VER_TAG_WARNED]} {
4680 Msg CriticalWarning "No Hog version tags found in this repository."
4681 set NO_HOG_VER_TAG_WARNED 1
4687 set repo_conf $repo_path/Top/repo.conf
4691 set hotfix_prefix "hotfix/"
4692 set minor_prefix "minor_version/"
4693 set major_prefix "major_version/"
4695 set enable_develop_branch $force_develop
4697 set branch_name [
Git {rev-parse --abbrev-ref HEAD}]
4699 if {[
file exists $repo_conf]} {
4700 set PROPERTIES [
ReadConf $repo_conf]
4702 if {[dict exists $PROPERTIES main]} {
4703 set mainDict [dict get $PROPERTIES main]
4706 if {[dict exists $mainDict ENABLE_DEVELOP_BRANCH]} {
4707 set enable_develop_branch [dict get $mainDict ENABLE_DEVELOP_BRANCH]
4713 if {[dict exists $PROPERTIES prefixes]} {
4714 set prefixDict [dict get $PROPERTIES prefixes]
4716 if {[dict exists $prefixDict HOTFIX]} {
4717 set hotfix_prefix [dict get $prefixDict HOTFIX]
4719 if {[dict exists $prefixDict MINOR_VERSION]} {
4720 set minor_prefix [dict get $prefixDict MINOR_VERSION]
4722 if {[dict exists $prefixDict MAJOR_VERSION]} {
4723 set major_prefix [dict get $prefixDict MAJOR_VERSION]
4729 if {[
string match "HEAD" $branch_name]} {
4730 Msg Debug "Detached HEAD detected - attempting to find branch name"
4735 set log_refs [
Git {show -s --pretty=%D HEAD}]
4736 set branch_list [
split $log_refs ","]
4737 Msg Debug "list of possible branch refs $log_refs"
4743 set match_prefixes [list $hotfix_prefix $minor_prefix $major_prefix]
4744 set prev_branch_name $branch_name
4746 foreach br $branch_list {
4747 foreach pr $match_prefixes {
4748 if {[
string match "$pr*" [
string trim $br]]} {
4749 set branch_name [
string trim $br]
4755 if {!$match_count == 1} {
4756 set branch_name $prev_branch_name
4757 Msg Debug "Branch name not found. Using $branch_name"
4759 Msg Debug "Branch name found: $branch_name"
4763 if {$enable_develop_branch == 1} {
4764 if {[
string match "$hotfix_prefix*" $branch_name]} {
4769 if {[
string match "$major_prefix*" $branch_name]} {
4771 set version_level major
4772 }
elseif {[
string match "$minor_prefix*" $branch_name] || ($enable_develop_branch == 1 && $is_hotfix == 0)} {
4774 set version_level minor
4777 set version_level patch
4781 Msg CriticalWarning "Tag $tag does not contain a Hog compatible version in this repository."
4784 }
elseif {$mr == 0} {
4785 switch $version_level {
4800 Msg Info "No tag contains $SHA, will use most recent tag $tag. As this is a candidate tag, the patch level will be kept at $p."
4806 set vers [
split $result "\n"]
4807 set ver [
lindex $vers 0]
4809 if {[regexp {^v.*$} $v]} {
4817 Msg CriticalWarning "Error while trying to find tag for $SHA"
4825 set M [
format %02X $M]
4826 set m [
format %02X $m]
4827 set c [
format %04X $c]
4828 }
elseif {$M > -1} {
4830 set M [
format %02X $M]
4831 set m [
format %02X $m]
4832 set c [
format %04X $c]
4834 Msg Warning "Tag does not contain a properly formatted version: $ver"
4835 set M [
format %02X 0]
4836 set m [
format %02X 0]
4837 set c [
format %04X 0]
4850 proc Git {command {files ""}} {
4851 lassign [
GitRet $command $files] ret result
4853 Msg Error "Code $ret returned by git running: $command -- $files"
4864 proc GetModuleName {filename} {
4866 if {![
file exists $filename]} {
4867 Msg CriticalWarning "Error: File $filename does not exist."
4872 set fileId [open $filename r]
4875 set file_content [read $fileId]
4881 if {[
file extension $filename] == ".vhd" || [
file extension $filename] == ".vhdl"} {
4883 set file_content [
string tolower $file_content]
4885 set pattern {(?m)^\s*entity\s+(\S+)\s+is}
4886 }
elseif {[
file extension $filename] == ".v" || [
file extension $filename] == ".sv"} {
4888 set pattern {\n\s*module\s*(\w+)(\s*|\(|\n)}
4890 Msg Debug "File is neither VHDL nor Verilog... Returning empty string..."
4895 if {[regexp $pattern $file_content match module_name]} {
4898 Msg Debug "No module was found in $filename. Returning an empty string..."
4906 proc GetVerilogGenerics {file} {
4907 set fp [open $file r]
4913 foreach line [
split $data "\n"] {
4914 regsub "^\\s*\/\/.*" $line "" line
4915 regsub "(.*)\/\/.*" $line {\1} line
4916 if {![
string equal $line ""]} {
4917 append lines $line " "
4922 regsub -all {/\*.*\*/} $lines "" lines
4925 set punctuation [list]
4926 foreach char [list "(" ")" ";" "," " " "!" "<=" ":=" "=" "\[" "\]"] {
4927 lappend punctuation $char "\000$char\000"
4931 set tokens [
split [
string map $punctuation $lines] \000]
4933 set parameters [dict create]
4942 foreach token $tokens {
4943 set token [
string trim $token]
4944 if {![
string equal "" $token]} {
4945 if {[
string equal [
string tolower $token] "parameter"]} {
4946 set state $PARAM_NAME
4947 }
elseif {[
string equal $token ")"] || [
string equal $token ";"]} {
4949 }
elseif {$state == $PARAM_WIDTH} {
4950 if {[
string equal $token "\]"]} {
4951 set state $PARAM_NAME
4953 }
elseif {$state == $PARAM_VALUE} {
4954 if {[
string equal $token ","]} {
4955 set state $PARAM_NAME
4956 }
elseif {[
string equal $token ";"]} {
4961 }
elseif {$state == $PARAM_NAME} {
4962 if {[
string equal $token "="]} {
4963 set state $PARAM_VALUE
4964 }
elseif {[
string equal $token "\["]} {
4965 set state $PARAM_WIDTH
4966 }
elseif {[
string equal $token ","]} {
4967 set state $PARAM_NAME
4968 }
elseif {[
string equal $token ";"]} {
4970 }
elseif {[
string equal $token ")"]} {
4973 dict set parameters $token "integer"
4986 proc GetVhdlGenerics {file {entity ""}} {
4987 set fp [open $file r]
4993 foreach line [
split $data "\n"] {
4994 regsub "^\\s*--.*" $line "" line
4995 regsub "(.*)--.*" $line {\1} line
4996 if {![
string equal $line ""]} {
4997 append lines $line " "
5002 set generic_block ""
5003 set generics [dict create]
5005 if {1 == [
string equal $entity ""]} {
5006 regexp {(?i).*entity\s+([^\s]+)\s+is} $lines _ entity
5009 set generics_regexp "(?i).*entity\\s+$entity\\s+is\\s+generic\\s*\\((.*)\\)\\s*;\\s*port.*end.*$entity"
5011 if {[regexp $generics_regexp $lines _ generic_block]} {
5013 foreach line [
split $generic_block ";"] {
5015 regexp {(.*):\s*([A-Za-z0-9_]+).*} $line _ generic type
5018 set splits [
split $generic ","]
5019 foreach split $splits {
5020 dict set generics [
string trim $split] [
string trim $type]
5028 proc GHDL {command logfile} {
5029 set ret [
catch {
exec -ignorestderr ghdl {*}$command >>& $logfile} result options]
5034 return [list $ret $result]
5046 proc GitRet {command {files ""}} {
5049 set ret [
catch {
exec -ignorestderr git {*}$command} result]
5051 set ret [
catch {
exec -ignorestderr git {*}$command -- {*}$files} result]
5053 return [list $ret $result]
5061 proc GitVersion {target_version} {
5062 set ver [
split $target_version "."]
5063 set v [
Git --version]
5065 set current_ver [
split [
lindex $v 2] "."]
5066 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
5067 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
5068 return [
expr {$target <= $current}]
5081 proc RcloneFileExists {remote_file config_path} {
5082 if {[
catch {
exec rclone ls $remote_file --config $config_path 2>@1} result opt] == 0} {
5086 set rclone_ret [
lindex [dict get $opt -errorcode] end]
5087 if {$rclone_ret ne "3" && $rclone_ret ne "4"} {
5088 Msg CriticalWarning "Could not check if $remote_file is in the Rclone repository: $result"
5090 Msg Debug "$remote_file not found in the Rclone repository (rclone exit code $rclone_ret)."
5107 proc HandleIP {what_to_do xci_file ip_path repo_path on_eos on_rclone rclone_config_path {gen_dir "."} {force 0}} {
5109 if {!($what_to_do eq "push") && !($what_to_do eq "pull")} {
5110 Msg Error "You must specify push or pull as first argument."
5113 if {[
catch {
package require tar} TARPACKAGE]} {
5114 Msg CriticalWarning "Cannot find package tar. You can fix this by installing package \"tcllib\""
5121 if {!([
file exists $xci_file])} {
5122 Msg CriticalWarning "Could not find $xci_file."
5128 set xci_path [
file dirname $xci_file]
5129 set xci_name [
file tail $xci_file]
5130 set xci_ip_name [
file rootname [
file tail $xci_file]]
5131 set xci_dir_name [
file tail $xci_path]
5132 set gen_path $gen_dir
5134 set hash [
Md5Sum $xci_file]
5135 set file_name $xci_name\_$hash
5139 set scope [get_property -quiet SCOPE [get_ips $xci_ip_name]]
5141 Msg Debug "IP $xci_name is a subcore of a BD file, will not pull/push it from/to the remote directory."
5145 Msg Info "Preparing to $what_to_do IP: $xci_name ($file_name)."
5147 if {$what_to_do eq "push"} {
5150 if {$on_rclone == 1} {
5156 Msg Info "IP already in the Rclone repository, will not copy..."
5158 Msg Info "IP already in the Rclone repository, will forcefully replace..."
5163 }
elseif {$on_eos == 1} {
5164 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
5169 Msg Info "IP already in the EOS repository, will not copy..."
5171 Msg Info "IP already in the EOS repository, will forcefully replace..."
5177 if {[
file exists "$ip_path/$file_name.tar"]} {
5179 Msg Info "IP already in the local repository, will not copy..."
5181 Msg Info "IP already in the local repository, will forcefully replace..."
5190 if {$will_copy == 1} {
5192 Msg Info "Looking for generated files in $gen_path..."
5193 set ip_gen_files [glob -nocomplain $gen_path/*]
5197 if {[
llength $ip_gen_files] > 0} {
5198 Msg Info "Found some IP synthesised files matching $xci_ip_name"
5199 if {$will_remove == 1} {
5200 Msg Info "Removing old synthesised directory $ip_path/$file_name.tar..."
5201 if {$on_rclone == 1} {
5202 lassign [
ExecuteRet rclone delete $ip_path/$file_name.tar --config $rclone_config_path] ret result
5204 Msg CriticalWarning "Could not delete file from Rclone: $result"
5206 }
elseif {$on_eos == 1} {
5207 eos "rm -rf $ip_path/$file_name.tar" 5
5209 file delete -force "$ip_path/$file_name.tar"
5213 Msg Info "Creating local archive with IP generated files..."
5216 set at_least_one_long 0
5217 foreach f $ip_gen_files {
5218 set new_f "[
Relative [
file normalize $repo_path] $f]"
5219 set len [
string length $new_f]
5221 Msg Warning "One file in $xci_ip_name is too long ($len chars): $new_f"
5222 set at_least_one_long 1
5224 lappend tar_files $new_f
5227 Msg Debug "Tar files: $tar_files"
5228 if {$at_least_one_long == 1} {
5229 Msg Warning "Using regular tar, please cross your fingers..."
5231 lassign [
ExecuteRet tar -cf $file_name.tar {*}$tar_files] ret_tar result_tar
5232 if {$ret_tar != 0} {
5233 Msg CriticalWarning "Something went wrong when using regular tar. Error message: $result_tar"
5236 if {[
catch { ::tar::create $file_name.tar $tar_files} err]} {
5237 Msg Warning "Tcl built-in tar failed: $err, will try regular tar..."
5239 lassign [
ExecuteRet tar -cf $file_name.tar {*}$tar_files] ret_tar result_tar
5240 if {$ret_tar != 0} {
5241 Msg CriticalWarning "Something went wrong when using regular tar. Error message: $result_tar"
5246 Msg Info "Copying IP generated files for $xci_name..."
5247 if {$on_rclone == 1} {
5248 lassign [
ExecuteRet rclone copyto $file_name.tar $ip_path/$file_name.tar --config $rclone_config_path] ret result
5250 Msg CriticalWarning "Something went wrong when copying the IP files to Rclone. Error message: $result"
5252 }
elseif {$on_eos == 1} {
5253 lassign [
ExecuteRet xrdcp -f -s $file_name.tar $::env(EOS_MGM_URL)//$ip_path/] ret msg
5255 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
5258 Copy "$file_name.tar" "$ip_path/"
5260 Msg Info "Removing local archive"
5261 file delete $file_name.tar
5263 Msg Warning "Could not find synthesized files matching $gen_path/$file_name*"
5266 }
elseif {$what_to_do eq "pull"} {
5267 if {$on_rclone == 1} {
5270 Msg Info "Nothing for $xci_name was found in the Rclone repository, cannot pull."
5274 Msg Info "IP $xci_name found in the Rclone repository $ip_path, copying it locally to $repo_path..."
5275 lassign [
ExecuteRet rclone copyto $ip_path/$file_name.tar $file_name.tar --config $rclone_config_path] ret_copy result_copy
5276 if {$ret_copy != 0} {
5277 Msg CriticalWarning "Something went wrong when copying the IP files from Rclone. Error message: $result_copy"
5280 }
elseif {$on_eos == 1} {
5281 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
5283 Msg Info "Nothing for $xci_name was found in the EOS repository, cannot pull."
5287 set remote_tar "$::env(EOS_MGM_URL)//$ip_path/$file_name.tar"
5288 Msg Info "IP $xci_name found in the repository $remote_tar, copying it locally to $repo_path..."
5290 lassign [
ExecuteRet xrdcp -f -r -s $remote_tar $repo_path] ret msg
5292 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
5296 if {[
file exists "$ip_path/$file_name.tar"]} {
5297 Msg Info "IP $xci_name found in local repository $ip_path/$file_name.tar, copying it locally to $repo_path..."
5298 Copy $ip_path/$file_name.tar $repo_path
5300 Msg Info "Nothing for $xci_name was found in the local IP repository, cannot pull."
5306 if {[
file exists $file_name.tar]} {
5307 remove_files $xci_file
5308 Msg Info "Extracting IP files from archive to $repo_path..."
5309 ::tar::untar $file_name.tar -dir $repo_path -noperms
5310 Msg Info "Removing local archive"
5311 file delete $file_name.tar
5312 add_files -norecurse -fileset sources_1 $xci_file
5325 proc HexVersionToString {version} {
5326 scan [
string range $version 0 1] %x M
5327 scan [
string range $version 2 3] %x m
5328 scan [
string range $version 4 7] %x c
5333 proc ImportTclLib {} {
5336 if {[
info exists env(HOG_TCLLIB_PATH)]} {
5337 lappend auto_path $env(HOG_TCLLIB_PATH)
5340 puts "ERROR: To run Hog with Microsemi Libero SoC or Lattice Diamond, you need to define the HOG_TCLLIB_PATH variable."
5355 proc InitLauncher {script tcl_path parameters commands argv {custom_commands ""}} {
5356 set repo_path [
file normalize "$tcl_path/../.."]
5358 set bin_path [
file normalize "$tcl_path/../../bin"]
5359 set top_path [
file normalize "$tcl_path/../../Top"]
5361 set cmd_lines [
split $commands "\n"]
5363 set command_options [dict create]
5364 set directive_descriptions [dict create]
5365 set directive_names [dict create]
5366 set common_directive_names [dict create]
5367 set directives_with_optional_projects ""
5368 set custom_command ""
5369 set custom_command_options ""
5371 foreach l $cmd_lines {
5373 if {[regexp {\\(.*) \{\#} $l minc d]} {
5374 lappend directives_with_projects $d
5375 set current_directive $d
5378 if {[regexp {^[^#]* allow_empty_proj} $l minc dd]} {
5379 lappend directives_with_optional_projects $current_directive
5383 if {[regexp {\\(.*) \{} $l minc regular_expression]} {
5384 lappend directive_regex $regular_expression
5388 if {[regexp {\#\s*NAME(\*)?:\s*(.*)\s*} $l minc star name]} {
5389 dict set directive_names $name $regular_expression
5391 dict set common_directive_names $name $regular_expression
5394 set directive_names [
DictSort $directive_names]
5395 set common_directive_names [
DictSort $common_directive_names]
5398 if {[regexp {\#\s*DESCRIPTION:\s*(.*)\s*} $l minc x]} {
5399 dict set directive_descriptions $regular_expression $x
5403 if {[regexp {\#\s*OPTIONS:\s*(.*)\s*} $l minc x]} {
5404 dict set command_options $regular_expression [
split [regsub -all {[ \t\n]+} $x {}] ","]
5408 set short_usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nMost common directives (case insensitive):"
5410 dict for {key value} $common_directive_names {
5411 set short_usage "$short_usage\n - $key: [dict get $directive_descriptions $value]"
5414 if {[
string length $custom_commands] > 0} {
5415 Msg Debug "Found custom commands to add to short short_usage."
5416 set short_usage "$short_usage\n\nCustom commands:"
5417 dict for {key command} $custom_commands {
5418 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
5419 set short_usage "$short_usage\n - $key: [dict get $command DESCRIPTION]"
5424 set short_usage "$short_usage\n\n\
5425 To see all the available directives, run:\n./Hog/Do HELP\n\n\
5426 To list available options for the chosen directive run:\n\
5427 ./Hog/Do <directive> HELP\n
5430 set usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nDirectives (case insensitive):"
5432 dict for {key value} $directive_names {
5433 set usage "$usage\n - $key: [dict get $directive_descriptions $value]"
5437 if {[
string length $custom_commands] > 0} {
5438 Msg Debug "Found custom commands to add to short usage."
5439 set usage "$usage\n\nCustom commands:"
5440 dict for {key command} $custom_commands {
5441 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
5442 set usage "$usage\n - $key: [dict get $command DESCRIPTION]"
5447 set usage "$usage\n\nTo list available options for the chosen directive run:\n./Hog/Do <directive> HELP"
5456 Msg Debug "HogEnv.conf found"
5463 if {[
catch {
package require cmdline} ERROR]} {
5464 Msg Debug "The cmdline Tcl package was not found, sourcing it from Hog..."
5465 source $tcl_path/utils/cmdline.tcl
5468 set argv [regsub -all {(?i) HELP\y} $argv " -help"]
5473 set custom_parameters [list]
5474 dict for {key command} $custom_commands {
5475 set custom_parameters [concat $custom_parameters [dict get $command CUSTOM_OPTIONS]]
5478 lassign [
GetOptions $argv [
concat $custom_parameters $parameters]] option_list arg_list
5480 if {[
IsInList "-all" $option_list]} {
5489 set directive [
string toupper [
lindex $arg_list 0]]
5492 set argument_is_no_project 1
5494 set NO_DIRECTIVE_FOUND 0
5495 switch -regexp -- $directive "$commands"
5497 if {$NO_DIRECTIVE_FOUND == 1} {
5498 if {[
string length $custom_commands] > 0 && [dict exists $custom_commands $directive]} {
5499 set custom_command $directive
5500 set custom_command_dict [
DictGet $custom_commands $directive]
5501 set custom_command_hog_parameters [
DictGet $custom_command_dict OPTIONS]
5502 set custom_command_options [
DictGet $custom_command_dict CUSTOM_OPTIONS]
5503 set custom_command_options [
concat $custom_command_hog_parameters $custom_command_options]
5505 Msg Status "ERROR: Unknown directive $directive.\n\n"
5511 if {[
IsInList $directive $directives_with_projects 1]} {
5512 set argument_is_no_project 0
5516 if {$directive != ""} {
5517 if {[
IsInList $directive $directives_with_projects 1]} {
5518 puts "usage: ./Hog/Do \[OPTIONS\] $directive <project>\n"
5519 }
elseif {[regexp "^COMPSIM(LIB)?$" $directive]} {
5520 puts "usage: ./Hog/Do \[OPTIONS\] $directive <simulator>\n"
5522 puts "usage: ./Hog/Do \[OPTIONS\] $directive \n"
5525 dict for {dir desc} $directive_descriptions {
5526 if {[regexp $dir $directive]} {
5533 if {$custom_command ne ""} {
5534 if {[
llength $custom_command_options] > 0} {
5535 puts "Available options:"
5537 foreach custom_option $custom_command_options {
5538 set n [
llength $custom_option]
5540 lassign $custom_option opt help
5543 }
elseif {$n == 3} {
5544 lassign $custom_option opt def help
5545 puts " -$opt <argument>"
5547 puts " $help (default: $def)"
5552 Msg Warning "Custom option spec has invalid arity (expected 2 or 3): $custom_option"
5557 dict for {dir opts} $command_options {
5558 if {[regexp $dir $directive]} {
5559 puts "Available options:"
5561 foreach par $parameters {
5562 if {$opt == [lindex $par 0]} {
5563 if {[regexp {\.arg$} $opt]} {
5564 set opt_name [regsub {\.arg$} $opt ""]
5565 puts " -$opt_name <argument>"
5569 puts " [lindex $par [llength $par]-1]"
5584 set directive_options ""
5585 set directive_found 0
5586 if {$custom_command ne ""} {
5587 set directive_options $custom_command_options
5588 set directive_found 1
5590 dict for {dir opts} $command_options {
5591 if {[regexp $dir $directive]} {
5592 set directive_options $opts
5593 set directive_found 1
5600 set directive_option_names {}
5601 foreach opt $directive_options {
5602 lappend directive_option_names [regsub {\.arg$} [
lindex $opt 0] ""]
5605 if {$custom_command ne ""} {
5606 set parameters [
concat $parameters $custom_command_options]
5609 if {[
catch {
array set options [
cmdline::getoptions option_list $parameters $usage]} err]} {
5610 Msg Status "\nERROR: Syntax error, probably unknown option.\n\n USAGE: $err"
5615 if {$directive_found == 1} {
5616 set default_option_list {}
5618 foreach {key value} [
array get options] {
5619 if {[
IsInList $key $directive_option_names] || ![
info exists default_options($key)]} {
5622 if {$value ne $default_options($key)} {
5623 Msg Warning "Option -$key is not supported by directive $directive, ignoring it."
5624 set options($key) $default_options($key)
5629 if {[
llength $arg_list] <= $min_n_of_args || [
llength $arg_list] > $max_n_of_args} {
5630 Msg Status "\nERROR: Wrong number of arguments: [
llength $argv]"
5635 set project [
lindex $arg_list 1]
5636 set optional_project [
IsInList $directive $directives_with_optional_projects 1]
5638 if {$argument_is_no_project == 0} {
5640 regsub "^(\./)?Top/" $project "" project
5642 regsub "/? *\$" $project "" project
5644 if {$project eq "" && $optional_project == 1} {
5653 Msg Debug "Option list:"
5655 foreach {key value} [
array get options] {
5656 Msg Debug "$key => $value"
5663 if {$proj_conf != 0} {
5666 lassign [
GetIDECommand $proj_conf] cmd before_tcl_script after_tcl_script end_marker
5667 Msg Info "Project $project uses $cmd IDE"
5670 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
5672 if {$custom_command ne ""} {
5673 if {[dict exists $custom_commands $directive IDE]} {
5674 lassign [
GetIDECommand "" [dict get $custom_commands $directive IDE]] cmd before_tcl_script after_tcl_script end_marker
5675 Msg Info "Custom command: $custom_command uses $cmd IDE"
5676 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
5678 set command "custom_tcl"
5680 }
elseif {$argument_is_no_project == 1} {
5682 Msg Debug "$project will be used as first argument"
5683 }
elseif {$project != ""} {
5686 }
elseif {$min_n_of_args < 0} {
5691 if {$optional_project == 0} {
5703 set project_group [
file dirname $project]
5704 set project_name $project
5705 set project [
file tail $project]
5706 Msg Debug "InitLauncher: project_group=$project_group, project_name=$project_name, project=$project"
5708 return [list $directive $project $project_name $project_group $repo_path $old_path\
5709 $bin_path $top_path $usage $short_usage $command $cmd [
array get options]\
5717 proc IsCommitAncestor {ancestor commit} {
5718 lassign [
GitRet "merge-base --is-ancestor $ancestor $commit"] status result
5727 return [
expr {[info commands sys_install] != ""}]
5732 return [
expr {[info commands get_libero_version] != ""}]
5741 proc IsInList {element list {regex 0} {nocase 0}} {
5745 if {[regexp -nocase $x $element]} {
5749 if {[regexp $x $element]} {
5753 }
elseif {$regex == 0} {
5755 if {[
string tolower $x] eq [
string tolower $element]} {
5759 if {$x eq $element} {
5772 return [
expr {[string first PlanAhead [version]] == 0}]
5780 if {[
catch {
package require ::quartus::flow} result]} {
5793 proc IsRelativePath {path} {
5794 if {[
string index $path 0] == "~"} {
5797 return [
expr {[file pathtype $path] eq "relative"}]
5801 proc IsSynplify {} {
5802 return [
expr {[info commands program_version] != ""}]
5807 return [
expr {![IsQuartus] && ![IsXilinx] && ![IsVitisClassic] && ![IsVitisUnified] && ![IsLibero] && ![IsSynplify] && ![IsDiamond]}]
5815 proc IsVerilog {file} {
5816 if {[
file extension $file] == ".v" || [
file extension $file] == ".sv"} {
5828 proc IsVersal {part} {
5829 if {[get_property ARCHITECTURE [get_parts $part]] eq "versal"} {
5839 return [
expr {[string first Vivado [version]] == 0}]
5847 if {[
info commands version] != ""} {
5848 set current_version [version]
5849 if {[
string first PlanAhead $current_version] == 0 || [
string first Vivado $current_version] == 0} {
5851 }
elseif {[
string first xsct $current_version] == 0} {
5854 Msg Warning "This IDE has the version command but it is not PlanAhead or Vivado: $current_version"
5863 proc IsVitisClassic {} {
5864 if {[
info exists globalSettings::vitis_classic]} {
5865 return $globalSettings::vitis_classic
5867 return [
expr {[info commands platform] != ""}]
5871 proc IsVitisUnified {} {
5872 if {[
info exists globalSettings::vitis_unified]} {
5873 return $globalSettings::vitis_unified
5887 proc ExecuteVitisUnifiedCommand {python_script command args {error_prefix "Failed to execute command"} {output_var ""}} {
5888 set cmdlist [list vitis -s $python_script $command]
5890 lappend cmdlist $arg
5892 lappend cmdlist 2>@1
5894 Msg Debug "Executing: vitis -s $python_script $command $args"
5897 set ::env(PYTHONUNBUFFERED) "1"
5900 if {[
catch {
set pipe [open "|$cmdlist" "r"]} err]} {
5901 Msg Error "$error_prefix: Failed to open pipe: $err"
5905 fconfigure $pipe -buffering line
5906 set script_output ""
5907 set vitis_version ""
5910 set vitis_banner_patterns {
5911 "*Vitis Development Environment*"
5914 "*Copyright*Xilinx*"
5915 "*Copyright*Advanced Micro Devices*"
5916 "*All Rights Reserved*"
5920 while {[
gets $pipe line] >= 0} {
5922 if {[
string match "*Vitis v*" $line]} {
5923 if {[regexp {Vitis\s+v([0-9]+)\.([0-9]+)(?:\.[0-9]+)?} $line -> major minor]} {
5924 set year_last_two [
string range $major end-1 end]
5925 set vitis_version "$year_last_two.$minor"
5931 foreach pattern $vitis_banner_patterns {
5932 if {[
string match $pattern $line]} {
5938 if {![
string match "INFO:*" $line] && ![
string match "WARNING:*" $line] && ![
string match "ERROR:*" $line] && ![
string match "DEBUG:*" $line]} {
5939 if {$vitis_version ne ""} {
5940 set line "INFO: \[Vitis_v$vitis_version\] $line"
5942 set line "INFO: $line"
5946 append script_output "$line\n"
5953 if {[
catch {
close $pipe} err]} {
5954 if {[regexp {exit (\d+)} $err -> exit_code]} {
5955 if {$exit_code != 0} {
5956 Msg Error "$error_prefix (exit code: $exit_code)"
5957 if {$output_var ne ""} {
5958 upvar $output_var output
5959 set output $script_output
5964 Msg Error "$error_prefix: $err"
5965 if {$output_var ne ""} {
5966 upvar $output_var output
5967 set output $script_output
5974 if {$output_var ne ""} {
5975 upvar $output_var output
5976 set output $script_output
5992 proc GetVitisApps {{vitis_workspace ""} {python_script ""}} {
5995 if {[
catch {
set ws_apps [app list -dict]}]} {
6008 "Failed to get app list from Vitis Unified workspace $vitis_workspace" json_output]
6013 if {[
catch {
package require json}]} {
6014 Msg Warning "JSON package not available for parsing Vitis Unified app list"
6018 set json_output_filtered ""
6019 if {[regexp -lineanchor {\{.*\}} $json_output json_output_filtered]} {
6020 return [json::json2dict $json_output_filtered]
6022 return [json::json2dict $json_output]
6030 proc IsZynq {part} {
6031 if {[regexp {^(xc7z|xczu).*} $part]} {
6038 proc ImportGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
6039 set list_path "$repo_path/Top/$project_name/list"
6040 lassign [
GetHogFiles -list_files {.src,.ext,.sim} -ext_path $ext_path $list_path $repo_path] src_files properties filesets
6045 set properties [
DictGet $simset_dict "properties"]
6046 set options [
DictGet $properties "options"]
6049 set workdir Projects/$project_name/ghdl
6050 file delete -force $workdir
6052 set import_log "$workdir/ghdl-import-${simset_name}.log"
6053 dict for {lib sources} $src_files {
6054 set libname [file rootname $lib]
6055 foreach f $sources {
6056 if {[file extension $f] != ".vhd" && [file extension $f] != ".vhdl"} {
6057 Msg Info "File $f is not a VHDL file, copying it in workfolder..."
6058 file copy -force $f $workdir
6060 set file_path [Relative $repo_path $f]
6061 set import_log_file [open $import_log "a"]
6062 puts "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
6063 puts $import_log_file "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
6064 close $import_log_file
6065 lassign [GHDL "-i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path" $import_log] ret result
6067 Msg CriticalWarning "GHDL import failed for file $f: $result"
6075 proc LaunchGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
6078 set sim_props [
DictGet $simset_dict "properties"]
6079 set options [
DictGet $sim_props "options"]
6080 set runopts [
DictGet $sim_props "run_options"]
6082 dict for {prop_name prop_val} $sim_props {
6083 set prop_name [string toupper $prop_name]
6084 if {$prop_name == "TOP"} {
6085 set top_sim $prop_val
6088 set workdir $repo_path/Projects/$project_name/ghdl
6089 set make_log "$workdir/ghdl-make-${simset_name}.log"
6090 set run_log "$workdir/ghdl-run-${simset_name}.log"
6093 set make_log_file [open $make_log "w"]
6095 puts "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
6096 puts $make_log_file "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
6097 close $make_log_file
6099 lassign [
GHDL "-m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim" $make_log] ret result
6102 Msg Error "GHDL make failed for $top_sim: $result"
6106 set run_log_file [open $run_log "w"]
6107 puts "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
6108 puts $run_log_file "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
6111 lassign [
GHDL "-r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts" $run_log] ret result
6115 Msg Error "GHDL run failed for $top_sim: $result"
6130 proc LaunchImplementation {reset do_create run_folder project_name {repo_path .} {njobs 4} {do_bitstream 0}} {
6131 Msg Info "Starting implementation flow..."
6134 Msg Info "Resetting run before launching implementation..."
6139 if {[
file exist "$run_folder/timing_ok.txt"]} {
6140 file delete "$run_folder/timing_ok.txt"
6142 if {[
file exist "$run_folder/timing_error.txt"]} {
6143 file delete "$run_folder/timing_error.txt"
6147 source $repo_path/Hog/Tcl/integrated/pre-implementation.tcl
6150 if {$do_bitstream == 1} {
6151 launch_runs impl_1 -to_step [
BinaryStepName [get_property PART [current_project]]] -jobs $njobs -dir $run_folder
6153 launch_runs impl_1 -jobs $njobs -dir $run_folder
6158 Msg Info "running post-implementation"
6159 source $repo_path/Hog/Tcl/integrated/post-implementation.tcl
6160 if {$do_bitstream == 1} {
6161 Msg Info "running pre-bitstream"
6162 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
6163 Msg Info "running post-bitstream"
6164 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
6168 set prog [get_property PROGRESS [get_runs impl_1]]
6169 set status [get_property STATUS [get_runs impl_1]]
6170 Msg Info "Run: impl_1 progress: $prog, status : $status"
6174 set status_file [open "$run_folder/timing.txt" "w"]
6175 puts $status_file "## $project_name Timing summary"
6177 set f [open [
lindex [glob "$run_folder/impl_1/*.twr" 0]]]
6179 while {[
gets $f line] >= 0} {
6180 if {[
string match "Timing summary:" $line]} {
6181 while {[
gets $f line] >= 0} {
6182 if {[
string match "Timing errors:*" $line]} {
6183 set errs [regexp -inline -- {[0-9]+} $line]
6185 if {[
string match "*Footnotes*" $line]} {
6188 puts $status_file "$line"
6197 Msg Info "Time requirements are met"
6198 file rename -force "$run_folder/timing.txt" "$run_folder/timing_ok.txt"
6201 Msg CriticalWarning "Time requirements are NOT met"
6202 file rename -force "$run_folder/timing.txt" "$run_folder/timing_error.txt"
6208 set wns [get_property STATS.WNS [get_runs [current_run]]]
6209 set tns [get_property STATS.TNS [get_runs [current_run]]]
6210 set whs [get_property STATS.WHS [get_runs [current_run]]]
6211 set ths [get_property STATS.THS [get_runs [current_run]]]
6212 set tpws [get_property STATS.TPWS [get_runs [current_run]]]
6214 if {$wns >= 0 && $whs >= 0 && $tpws >= 0} {
6215 Msg Info "Time requirements are met"
6216 set status_file [open "$run_folder/timing_ok.txt" "w"]
6219 Msg CriticalWarning "Time requirements are NOT met"
6220 set status_file [open "$run_folder/timing_error.txt" "w"]
6224 Msg Status "*** Timing summary ***"
6225 Msg Status "WNS: $wns"
6226 Msg Status "TNS: $tns"
6227 Msg Status "WHS: $whs"
6228 Msg Status "THS: $ths"
6229 Msg Status "TPWS: $tpws"
6235 puts $status_file "## $project_name Timing summary"
6237 m add row "| **Parameter** | \"**value (ns)**\" |"
6238 m add row "| --- | --- |"
6239 m add row "| WNS: | $wns |"
6240 m add row "| TNS: | $tns |"
6241 m add row "| WHS: | $whs |"
6242 m add row "| THS: | $ths |"
6243 m add row "| TPWS: | $tpws |"
6245 puts $status_file [m format 2string]
6246 puts $status_file "\n"
6247 if {$timing_ok == 1} {
6248 puts $status_file " Time requirements are met."
6250 puts $status_file "Time requirements are **NOT** met."
6252 puts $status_file "\n\n"
6256 if {$prog ne "100%"} {
6257 Msg Error "Implementation error"
6262 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6263 Msg Info "Git describe set to $describe"
6265 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
6270 if {[
file exists $run_folder/versions.txt]} {
6271 file copy -force $run_folder/versions.txt $dst_dir
6273 Msg Warning "No versions file found in $run_folder/versions.txt"
6276 set timing_files [glob -nocomplain "$run_folder/timing_*.txt"]
6277 set timing_file [
file normalize [
lindex $timing_files 0]]
6279 if {[
file exists $timing_file]} {
6280 file copy -force $timing_file $dst_dir/
6282 Msg Warning "No timing file found, not a problem if running locally"
6286 if {[
IsVersal [get_property part [current_project]]]} {
6287 if {[get_property segmented_configuration [current_project]] == 1} {
6288 Msg Info "Versal Segmented configuration detected: exporting XSA file..."
6289 set xsa_name "$dst_dir/[
file tail $project_name]\-$describe.xsa"
6290 write_hw_platform -fixed -force -file $xsa_name
6294 set revision [get_current_revision]
6296 set ic [get_global_assignment -name INCREMENTAL_COMPILATION]
6297 if {$ic ne "" && [
string toupper $ic] ne "OFF"} {
6298 Msg Info "Incremental compilation enabled ($ic), running Partition Merge..."
6299 if {[
catch {execute_module -tool cdb -args "--merge=on"} result]} {
6300 Msg Error "Result: $result\nPartition Merge failed. See the report file.\n"
6304 if {[
catch {execute_module -tool fit} result]} {
6305 Msg Error "Result: $result\n"
6306 Msg Error "Place & Route failed. See the report file.\n"
6308 Msg Info "\nINFO: Place & Route was successful for revision $revision.\n"
6311 if {[
catch {execute_module -tool sta -args "--do_report_timing"} result]} {
6312 Msg Error "Result: $result\n"
6313 Msg Error "Time Quest failed. See the report file.\n"
6315 Msg Info "Time Quest was successfully run for revision $revision.\n"
6318 set panel "Timing Analyzer||Timing Analyzer Summary"
6319 set device [get_report_panel_data -name $panel -col 1 -row_name "Device Name"]
6320 set timing_model [get_report_panel_data -name $panel -col 1 -row_name "Timing Models"]
6321 set delay_model [get_report_panel_data -name $panel -col 1 -row_name "Delay Model"]
6323 Msg Info "*******************************************************************"
6324 Msg Info "Device: $device"
6325 Msg Info "Timing Models: $timing_model"
6326 Msg Info "Delay Model: $delay_model"
6329 Msg Info "*******************************************************************"
6332 Msg Info "Starting implementation flow..."
6333 if {[
catch {run_tool -name {PLACEROUTE}}]} {
6334 Msg Error "PLACEROUTE FAILED!"
6336 Msg Info "PLACEROUTE PASSED."
6340 Msg Info "Run VERIFYTIMING ..."
6341 if {[
catch {run_tool -name {VERIFYTIMING} -script {Hog/Tcl/integrated/libero_timing.tcl}}]} {
6342 Msg CriticalWarning "VERIFYTIMING FAILED!"
6344 Msg Info "VERIFYTIMING PASSED \n"
6350 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6351 Msg Info "Git describe set to $describe"
6353 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
6354 file mkdir $dst_dir/reports
6357 if {[
file exists $run_folder/versions.txt]} {
6358 file copy -force $run_folder/versions.txt $dst_dir
6360 Msg Warning "No versions file found in $run_folder/versions.txt"
6363 set timing_file_path [
file normalize "$repo_path/Projects/timing_libero.txt"]
6364 if {[
file exists $timing_file_path]} {
6365 file copy -force $timing_file_path $dst_dir/reports/Timing.txt
6366 set timing_file [open $timing_file_path "r"]
6367 set status_file [open "$dst_dir/timing.txt" "w"]
6368 puts $status_file "## $project_name Timing summary\n\n"
6369 puts $status_file "| | |"
6370 puts $status_file "| --- | --- |"
6371 while {[
gets $timing_file line] >= 0} {
6372 if {[
string match "SUMMARY" $line]} {
6373 while {[
gets $timing_file line] >= 0} {
6374 if {[
string match "END SUMMARY" $line]} {
6377 if {[
string first ":" $line] == -1} {
6380 set out_string "| [
string map {: | } $line] |"
6381 puts $status_file "$out_string"
6386 Msg Warning "No timing file found, not a problem if running locally"
6391 set force_rst "-forceOne"
6393 prj_run Map $force_rst
6394 prj_run PAR $force_rst
6406 proc GenerateBitstreamOnly {project_name {repo_path .}} {
6410 set project_file [
file normalize "$repo_path/Projects/$project_name/$project_name.xpr"]
6411 if {![
file exists $project_file]} {
6412 Msg Error "Project file not found: $project_file. Please create the project first."
6418 set impl_runs [get_runs -quiet impl_1]
6419 if {[
llength $impl_runs] == 0} {
6420 Msg Error "Implementation run 'impl_1' does not exist. Please run implementation first."
6426 set run_dir [get_property DIRECTORY [get_runs impl_1]]
6427 if {![
file exists $run_dir]} {
6428 Msg Error "Implementation run directory not found: $run_dir. Please check your implementation run."
6433 Msg Info "Running pre-bitstream..."
6434 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
6436 Msg Info "Writing bitstream for $project_name..."
6439 write_bitstream -force [
file normalize "$run_dir/$top_name.bit"]
6441 Msg Info "Running post-bitstream..."
6442 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
6451 proc LaunchSimulation {project_name lib_path simsets {repo_path .} {scripts_only 0} {compile_only 0}} {
6454 set project [
file tail $project_name]
6455 set main_sim_folder [
file normalize "$repo_path/Projects/$project_name/$project.sim/"]
6457 if {$simsets != ""} {
6458 dict for {simset sim_dict} $simsets {
6459 lappend simsets_todo $simset
6461 Msg Info "Will run only the following simulation's sets (if they exist): $simsets_todo"
6464 if {$scripts_only == 1} {
6465 Msg Info "Only generating simulation scripts, not running simulations..."
6468 if {$compile_only == 1} {
6469 Msg Info "Only compiling simulation libraries, not running simulations..."
6474 set sim_dic [dict create]
6476 Msg Info "Retrieving list of simulation sets..."
6477 foreach s [get_filesets] {
6479 set use_simpass_str 0
6482 set type [get_property FILESET_TYPE $s]
6483 if {$type eq "SimulationSrcs"} {
6484 if {$simsets_todo != "" && $s ni $simsets_todo} {
6485 Msg Info "Skipping $s as it was not specified with the -simset option..."
6488 set sim_dict [
DictGet $simsets $s]
6489 set simulator [
DictGet $sim_dict "simulator"]
6490 set_property "target_simulator" $simulator [current_project]
6491 set hog_sim_props [
DictGet $sim_dict "hog"]
6492 dict for {prop_name prop_val} $hog_sim_props {
6493 # If HOG_SIMPASS_STR is set, use the HOG_SIMPASS_STR string to search for in logs, after simulation is done
6494 if {[string toupper $prop_name] == "HOG_SIMPASS_STR" && $prop_val != ""} {
6495 Msg Info "Setting simulation pass string as '$prop_val'"
6496 set use_simpass_str 1
6497 set simpass_str $prop_val
6499 if {[string toupper $prop_name] == "HOG_SILENT_SIM" && $prop_val == 1} {
6500 set quiet_sim " -quiet"
6506 Msg Info "Creating simulation scripts for $s..."
6507 if {[
file exists $repo_path/Top/$project_name/pre-simulation.tcl]} {
6508 Msg Info "Running $repo_path/Top/$project_name/pre-simulation.tcl"
6509 source $repo_path/Top/$project_name/pre-simulation.tcl
6511 if {[
file exists $repo_path/Top/$project_name/pre-$s-simulation.tcl]} {
6512 Msg Info "Running $repo_path/Top/$project_name/pre-$s-simulation.tcl"
6513 source $repo_path/Top/$project_name/pre-$s-simulation.tcl
6515 current_fileset -simset $s
6516 set sim_dir $main_sim_folder/$s/behav
6517 set sim_output_logfile $sim_dir/xsim/simulate.log
6518 if {([
string tolower $simulator] eq "xsim")} {
6519 set sim_name "xsim:$s"
6521 set simulation_command "launch_simulation $quiet_sim -simset [get_filesets $s]"
6522 if {[
catch $simulation_command log]} {
6525 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
6526 lappend failed $sim_name
6531 if {$use_simpass_str == 1} {
6534 set file_desc [open $sim_output_logfile r]
6535 set log [read $file_desc]
6538 Msg Info "Searching for simulation pass string: '$simpass_str'"
6539 if {[
string first $simpass_str $log] == -1} {
6540 Msg CriticalWarning "Simulation failed for $s, error info: '$simpass_str' NOT found!"
6541 lappend failed $sim_name
6544 lappend success $sim_name
6548 lappend success $sim_name
6552 Msg Info "Simulation library path is set to $lib_path."
6554 if {!([
file exists $lib_path])} {
6555 Msg Warning "Could not find simulation library path: $lib_path, $simulator simulation will not work."
6559 if {$simlib_ok == 1} {
6560 set_property "compxlib.${simulator}_compiled_library_dir" [
file normalize $lib_path] [current_project]
6561 launch_simulation -scripts_only -simset [get_filesets $s]
6562 set top_name [get_property TOP $s]
6563 set sim_script [
file normalize $sim_dir/$simulator/]
6564 Msg Info "Adding simulation script location $sim_script for $s..."
6565 lappend sim_scripts $sim_script
6566 dict append sim_dic $sim_script $s
6568 Msg Error "Cannot run $simulator simulations without a valid library path"
6575 if {[
info exists sim_scripts] && $scripts_only == 0} {
6577 Msg Info "Generating IP simulation targets, if any..."
6579 foreach ip [get_ips] {
6580 generate_target simulation -quiet $ip
6585 Msg Info "====== Starting simulations runs ======"
6588 foreach s $sim_scripts {
6590 set cmd ./compile.sh
6591 Msg Info " ************* Compiling: $s ************* "
6593 set sim_name "comp:[dict get $sim_dic $s]"
6595 Msg CriticalWarning "Compilation failed for $s, error info: $::errorInfo"
6596 lappend failed $sim_name
6598 lappend success $sim_name
6600 Msg Info "###################### Compilation log starts ######################"
6601 Msg Info "\n\n$log\n\n"
6602 Msg Info "###################### Compilation log ends ######################"
6604 if {$compile_only == 1} {
6607 if {[
file exists "./elaborate.sh"]} {
6608 set cmd ./elaborate.sh
6609 Msg Info " ************* Elaborating: $s ************* "
6611 set sim_name "elab:[dict get $sim_dic $s]"
6613 Msg CriticalWarning "Elaboration failed for $s, error info: $::errorInfo"
6614 lappend failed $sim_name
6616 lappend success $sim_name
6618 Msg Info "###################### Elaboration log starts ######################"
6619 Msg Info "\n\n$log\n\n"
6620 Msg Info "###################### Elaboration log ends ######################"
6622 set cmd ./simulate.sh
6623 Msg Info " ************* Simulating: $s ************* "
6628 if {$use_simpass_str == 1} {
6629 if {[
string first $simpass_str $log] == -1} {
6633 Msg Debug "Simulation pass string not set, relying on simulator exit code."
6637 set sim_name "sim:[dict get $sim_dic $s]"
6639 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
6640 lappend failed $sim_name
6642 lappend success $sim_name
6644 Msg Info "###################### Simulation log starts ######################"
6645 Msg Info "\n\n$log\n\n"
6646 Msg Info "###################### Simulation log ends ######################"
6651 if {[
llength $success] > 0} {
6652 set successes [
join $success "\n"]
6653 Msg Info "The following simulation sets were successful:\n\n$successes\n\n"
6656 if {[
llength $failed] > 0} {
6657 set failures [
join $failed "\n"]
6658 Msg Error "The following simulation sets have failed:\n\n$failures\n\n"
6660 }
elseif {[
llength $success] > 0} {
6661 Msg Info "All the [
llength $success] compilations, elaborations and simulations were successful."
6664 Msg Info "Simulation done."
6666 Msg Warning "Simulation is not yet supported for [
GetIDEName]."
6673 proc LaunchRTLAnalysis {repo_path {pre_rtl_file ""} {post_rtl_file ""}} {
6675 if {[
file exists $pre_rtl_file]} {
6676 Msg Info "Found pre-rtl Tcl script $pre_rtl_file, executing it..."
6677 source $pre_rtl_file
6679 Msg Info "Starting RTL Analysis..."
6681 synth_design -rtl -name rtl_1
6682 if {[
file exists $post_rtl_file]} {
6683 Msg Info "Found post-rtl Tcl script $post_rtl_file, executing it..."
6684 source $post_rtl_file
6687 Msg Warning "RTL Analysis is not yet supported for [
GetIDEName]."
6700 proc LaunchSynthesis {reset do_create run_folder project_name {repo_path .} {ext_path ""} {njobs 4}} {
6703 Msg Info "Resetting run before launching synthesis..."
6707 source $repo_path/Hog/Tcl/integrated/pre-synthesis.tcl
6709 launch_runs synth_1 -jobs $njobs -dir $run_folder
6711 set prog [get_property PROGRESS [get_runs synth_1]]
6712 set status [get_property STATUS [get_runs synth_1]]
6713 Msg Info "Run: synth_1 progress: $prog, status : $status"
6720 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6721 Msg Info "Git describe set to $describe"
6724 set xci_file [get_property IP_FILE $ip]
6726 set xci_path [
file dirname $xci_file]
6727 set xci_ip_name [
file rootname [
file tail $xci_file]]
6728 foreach rptfile [glob -nocomplain -directory $xci_path *.rpt] {
6729 file copy $rptfile $repo_path/bin/$project_name-$describe/reports
6733 if {$prog ne "100%"} {
6734 Msg Error "Synthesis error, status is: $status"
6738 set project [
file tail [
file rootname $project_name]]
6740 Msg Info "Number of jobs set to $njobs."
6741 set_global_assignment -name NUM_PARALLEL_PROCESSORS $njobs
6745 set describe [
GetHogDescribe [
file normalize $repo_path/Top/$project_name] $repo_path]
6747 set revision [get_current_revision]
6750 set tool_and_command [
split [get_global_assignment -name PRE_FLOW_SCRIPT_FILE] ":"]
6751 set tool [
lindex $tool_and_command 0]
6752 set pre_flow_script [
lindex $tool_and_command 1]
6753 set cmd "$tool -t $pre_flow_script quartus_map $project $revision"
6759 Msg Warning "Can not execute command $cmd"
6760 Msg Warning "LOG: $log"
6762 Msg Info "Pre flow script executed!"
6763 Msg Info "Pre flow script log: \n$log"
6767 if {![is_project_open]} {
6768 Msg Info "Re-opening project file $project_name..."
6769 project_open $project -current_revision
6773 if {[
catch {execute_module -tool ipg -args "--clean"} result]} {
6774 Msg Error "Result: $result\n"
6775 Msg Error "IP Generation failed. See the report file.\n"
6777 Msg Info "IP Generation was successful for revision $revision.\n"
6781 if {[
catch {execute_module -tool map -args "--parallel"} result]} {
6782 Msg Error "Result: $result\n"
6783 Msg Error "Analysis & Synthesis failed. See the report file.\n"
6785 Msg Info "Analysis & Synthesis was successful for revision $revision.\n"
6789 defvar_set -name RWNETLIST_32_64_MIXED_FLOW -value 0
6791 Msg Info "Run SYNTHESIS..."
6792 if {[
catch {run_tool -name {SYNTHESIZE}}]} {
6793 Msg Error "SYNTHESIZE FAILED!"
6795 Msg Info "SYNTHESIZE PASSED!"
6801 set force_rst "-forceOne"
6803 prj_run Synthesis $force_rst
6804 if {[prj_syn] == "synplify"} {
6805 prj_run Translate $force_rst
6808 Msg Error "Impossible condition. You need to run this in an IDE."
6819 proc LaunchVitisBuild {project_name {repo_path .} {stage "presynth"}} {
6820 set proj_name $project_name
6821 set bin_dir [
file normalize "$repo_path/bin"]
6827 set vitis_workspace [
file normalize "$repo_path/Projects/$project_name/vitis_unified"]
6828 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"]
6829 set ws_apps [
GetVitisApps $vitis_workspace $python_script]
6833 Msg Error "Impossible condition. You need to run this in a Vitis Unified or Vitis Classic IDE."
6838 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$proj_name] $repo_path] commit version hog_hash hog_ver top_hash top_ver \
6839 libs hashes vers cons_ver cons_hash ext_names ext_hashes xml_hash xml_ver user_ip_repos user_ip_hashes user_ip_vers \
6840 cheby_hash cheby_ver
6842 if {$commit == 0} {
set commit $this_commit}
6849 foreach app_name [dict keys $ws_apps] {
6850 app config -name $app_name -set build-config Release
6854 WriteGenerics "vitisbuild" $repo_path $proj_name $date $timee $commit $version $top_hash $top_ver $hog_hash $hog_ver $cons_ver $cons_hash $libs \
6855 $vers $hashes $ext_names $ext_hashes $user_ip_repos $user_ip_vers $user_ip_hashes $flavour $xml_ver $xml_hash \
6856 $cheby_ver $cheby_hash
6859 foreach app_name [dict keys $ws_apps] {
6862 if {![
ExecuteVitisUnifiedCommand $python_script "build_app" [list $app_name $vitis_workspace] "Failed to build app $app_name"]} {
6863 Msg Error "Failed to build app $app_name"
6867 app build -name $app_name
6871 if {$stage == "presynth"} {
6872 Msg Info "Done building apps for $project_name..."
6875 if {[
info exists ::globalSettings::vitis_only_pass] && $::globalSettings::vitis_only_pass == 1} {
6876 Msg Info "Skipping bin directory creation in vitis_only mode (post-bitstream.tcl handles artifacts)."
6880 Msg Info "Evaluating Hog describe for $project_name..."
6881 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6882 Msg Info "Hog describe set to: $describe"
6883 set dst_dir [
file normalize "$bin_dir/$proj_name\-$describe"]
6884 if {![
file exists $dst_dir]} {
6885 Msg Info "Creating $dst_dir..."
6889 foreach app_name [dict keys $ws_apps] {
6891 set main_file "$repo_path/Projects/$project_name/vitis_unified/$app_name/build/$app_name.elf"
6893 set main_file "$repo_path/Projects/$project_name/vitis_classic/$app_name/Release/$app_name.elf"
6895 set dst_main [
file normalize "$dst_dir/[
file tail $proj_name]\-$app_name\-$describe.elf"]
6897 if {![
file exists $main_file]} {
6898 Msg Error "No Vitis .elf file found. Perhaps there was an issue building it?"
6902 Msg Info "Copying main binary file $main_file into $dst_main..."
6903 file copy -force $main_file $dst_main
6918 proc LaunchHlsBuild {project_name {repo_path .}} {
6919 set proj_name $project_name
6920 set bin_dir [
file normalize "$repo_path/bin"]
6924 set conf_file [
file normalize "$repo_path/Top/$project_name/hog.conf"]
6925 if {![
file exists $conf_file]} {
6926 Msg Error "Configuration file not found: $conf_file"
6929 set properties [
ReadConf $conf_file]
6930 set hls_components [dict filter $properties key {hls:*}]
6932 if {[dict size $hls_components] == 0} {
6933 Msg Info "No HLS components found for project $project_name"
6937 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/HlsCommands.py"]
6945 set ide_name [
string tolower [
lindex $ide_info 0]]
6946 if {$ide_name eq "vivado_vitis_unified"} {
6947 set is_mixed_project 1
6949 set is_mixed_project 0
6952 dict for {hls_key hls_props} $hls_components {
6953 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {
6956 set component_name [string trim $component_name]
6957 Msg Info "Building HLS component: $component_name"
6959 # Read HLS_CONFIG path from hog.conf (mandatory)
6961 if {[dict exists $hls_props hls_config]} {
6962 set hls_cfg_rel [dict get $hls_props hls_config]
6963 } elseif {[dict exists $hls_props HLS_CONFIG]} {
6964 set hls_cfg_rel [dict get $hls_props HLS_CONFIG]
6966 if {$hls_cfg_rel eq ""} {
6967 Msg Error "HLS component '$component_name' missing HLS_CONFIG in hog.conf"
6971 set cfg_file [file normalize "$repo_path/$hls_cfg_rel"]
6972 if {![file exists $cfg_file]} {
6973 Msg Error "HLS config file not found: $cfg_file (from HLS_CONFIG=$hls_cfg_rel)"
6977 set hls_work_dir [file normalize "$repo_path/Projects/$project_name/vitis_unified/$component_name"]
6978 file mkdir $hls_work_dir
6981 Msg Info "Running C synthesis for HLS component '$component_name'..."
6983 ![ExecuteVitisUnifiedCommand $python_script "synthesis" \
6984 [list $component_name $cfg_file $hls_work_dir] \
6985 "Failed to run C synthesis for $component_name"]
6987 Msg Error "C synthesis failed for HLS component '$component_name'"
6992 Msg Info "Running implementation for HLS component '$component_name'..."
6994 ![ExecuteVitisUnifiedCommand $python_script "impl" \
6995 [list $component_name $cfg_file $hls_work_dir] \
6996 "Failed to run implementation for $component_name"]
6998 Msg Error "Implementation failed for HLS component '$component_name'"
7002 # Export VHDL to source tree if VHDL_OUTPUT is set
7004 if {[dict exists $hls_props vhdl_output]} {
7005 set vhdl_output [dict get $hls_props vhdl_output]
7006 } elseif {[dict exists $hls_props VHDL_OUTPUT]} {
7007 set vhdl_output [dict get $hls_props VHDL_OUTPUT]
7009 if {$vhdl_output ne ""} {
7010 set vhdl_output_dir [file normalize "$repo_path/$vhdl_output"]
7011 Msg Info "Exporting VHDL for '$component_name' to $vhdl_output_dir..."
7013 ![ExecuteVitisUnifiedCommand $python_script "export_rtl" \
7014 [list $component_name $hls_work_dir $vhdl_output_dir vhdl] \
7015 "Failed to export VHDL for $component_name"]
7017 Msg Warning "Could not export VHDL for HLS component '$component_name'"
7021 # Export Verilog to source tree if VERILOG_OUTPUT is set
7022 set verilog_output ""
7023 if {[dict exists $hls_props verilog_output]} {
7024 set verilog_output [dict get $hls_props verilog_output]
7025 } elseif {[dict exists $hls_props VERILOG_OUTPUT]} {
7026 set verilog_output [dict get $hls_props VERILOG_OUTPUT]
7028 if {$verilog_output ne ""} {
7029 set verilog_output_dir [file normalize "$repo_path/$verilog_output"]
7030 Msg Info "Exporting Verilog for '$component_name' to $verilog_output_dir..."
7032 ![ExecuteVitisUnifiedCommand $python_script "export_rtl" \
7033 [list $component_name $hls_work_dir $verilog_output_dir verilog] \
7034 "Failed to export Verilog for $component_name"]
7036 Msg Warning "Could not export Verilog for HLS component '$component_name'"
7040 # Export IP catalog ZIP to source tree if IP_OUTPUT is set
7042 if {[dict exists $hls_props ip_output]} {
7043 set ip_output [dict get $hls_props ip_output]
7044 } elseif {[dict exists $hls_props IP_OUTPUT]} {
7045 set ip_output [dict get $hls_props IP_OUTPUT]
7047 if {$ip_output ne ""} {
7048 set ip_output_dir [file normalize "$repo_path/$ip_output"]
7049 Msg Info "Exporting IP catalog for '$component_name' to $ip_output_dir..."
7051 ![ExecuteVitisUnifiedCommand $python_script "export_ip" \
7052 [list $component_name $hls_work_dir $ip_output_dir] \
7053 "Failed to export IP for $component_name"]
7055 Msg Error "Could not export IP for HLS component '$component_name'. Make sure package.output.format=ip_catalog is set in hls_config.cfg."
7059 # Collect reports into bin/ for CI and release notes
7060 Msg Info "Evaluating Hog describe for $project_name..."
7061 set describe [GetHogDescribe [file normalize ./Top/$project_name] $repo_path]
7062 Msg Info "Hog describe set to: $describe"
7063 set dst_dir [file normalize "$bin_dir/$proj_name\-$describe"]
7064 if {![file exists $dst_dir]} {
7065 Msg Info "Creating $dst_dir..."
7069 # Mixed projects group HLS components under bin/<proj>/vitis_hls/; pure
7070 # vitis_unified projects place them directly under bin/<proj>/
7071 if {$is_mixed_project} {
7072 set hls_out_dir [file normalize "$dst_dir/vitis_hls"]
7074 set hls_out_dir $dst_dir
7077 Msg Info "Collecting HLS reports for component '$component_name'..."
7079 ![ExecuteVitisUnifiedCommand $python_script "collect_reports" \
7080 [list $component_name $hls_work_dir $hls_out_dir] \
7081 "Failed to collect HLS reports for $component_name"]
7083 Msg Warning "No reports found for HLS component '$component_name'"
7086 # Generate markdown summary files (utilization.txt, timing_ok.txt /
7087 # timing_error.txt) under <hls_out_dir>/<component>/. File names mirror the
7088 # Vivado convention so the existing CI logic (release notes assembly and
7089 # timing-failure detection) works for HLS components too
7090 Msg Info "Generating HLS release-notes summary for component '$component_name'..."
7092 ![ExecuteVitisUnifiedCommand $python_script "release_notes" \
7093 [list $component_name $hls_work_dir $hls_out_dir] \
7094 "Failed to generate HLS summary for $component_name"]
7096 Msg Warning "Could not generate HLS summary for component '$component_name'"
7099 Msg Info "HLS component '$component_name' built successfully"
7104 if {!$is_mixed_project && [
info exists dst_dir] && [
file isdirectory $dst_dir]} {
7105 set versions_file [
file normalize "$dst_dir/versions.txt"]
7106 Msg Info "Generating top-level versions.txt for pure-HLS project at $versions_file"
7109 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$project_name] $repo_path] \
7110 commit version hog_hash hog_ver top_hash top_ver \
7111 libs hashes vers cons_ver cons_hash \
7112 ext_names ext_hashes xml_hash xml_ver \
7113 user_ip_repos user_ip_hashes user_ip_vers \
7114 cheby_hash cheby_ver
7115 if {$commit == 0} {
set commit [
GetSHA]}
7119 set fh [open $versions_file "w"]
7120 puts $fh "## $proj_name Version Table\n"
7121 puts $fh "| **File set** | **Commit SHA** | **Version** |"
7122 puts $fh "| --- | --- | --- |"
7123 puts $fh "| Global | $commit | $version_str |"
7124 puts $fh "| Top Directory | $top_hash | $top_ver_str |"
7125 puts $fh "| Hog | $hog_hash | $hog_ver_str |"
7126 foreach l $libs v $vers h $hashes {
7128 puts $fh "| **Lib:** $l | $h | $v_str |"
7134 Msg Warning "Could not generate top-level versions.txt for pure-HLS project: $err"
7138 Msg Info "Done building HLS components for $project_name"
7154 proc LaunchHlsSimulation {project_name {repo_path .} {hls_simsets {}}} {
7157 set conf_file [
file normalize "$repo_path/Top/$project_name/hog.conf"]
7158 if {![
file exists $conf_file]} {
7159 Msg Error "Configuration file not found: $conf_file"
7162 set properties [
ReadConf $conf_file]
7163 set hls_components [dict filter $properties key {hls:*}]
7165 if {[dict size $hls_components] == 0} {
7166 Msg Info "No HLS components found for project $project_name"
7172 set targeted_mode [
expr {[llength $hls_simsets] > 0}]
7173 set targets [dict create]
7174 foreach entry $hls_simsets {
7175 if {[regexp {^(csim|cosim):(.+)$} $entry -> sim_type comp_name]} {
7176 dict lappend targets $comp_name $sim_type
7178 Msg Error "Invalid HLS simset format '$entry'. Expected 'csim:<component>' or 'cosim:<component>'."
7179 Msg Info "Available HLS components in hog.conf:"
7180 dict for {hls_key hls_props} $hls_components {
7181 if {[regexp {^hls:(.+)$} $hls_key -> cname]} {
7182 Msg Info " - csim:[string trim $cname]"
7183 Msg Info " - cosim:[string trim $cname]"
7190 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/HlsCommands.py"]
7192 dict for {hls_key hls_props} $hls_components {
7193 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {
7196 set component_name [string trim $component_name]
7198 # In targeted mode, skip components not in the request
7199 if {$targeted_mode && ![dict exists $targets $component_name]} {
7203 # Read HLS_CONFIG path from hog.conf (mandatory)
7205 if {[dict exists $hls_props hls_config]} {
7206 set hls_cfg_rel [dict get $hls_props hls_config]
7207 } elseif {[dict exists $hls_props HLS_CONFIG]} {
7208 set hls_cfg_rel [dict get $hls_props HLS_CONFIG]
7210 if {$hls_cfg_rel eq ""} {
7211 Msg Error "HLS component '$component_name' missing HLS_CONFIG in hog.conf"
7215 set cfg_file [file normalize "$repo_path/$hls_cfg_rel"]
7216 if {![file exists $cfg_file]} {
7217 Msg Error "HLS config file not found: $cfg_file (from HLS_CONFIG=$hls_cfg_rel)"
7221 set hls_work_dir [file normalize "$repo_path/Projects/$project_name/vitis_unified/$component_name"]
7222 file mkdir $hls_work_dir
7224 # Determine which simulations to run
7225 if {$targeted_mode} {
7226 # User explicitly requested these sim types via -simset
7227 set sim_types [dict get $targets $component_name]
7228 set run_csim [expr {"csim" in $sim_types}]
7229 set run_cosim [expr {"cosim" in $sim_types}]
7231 # Validate: check if the requested sim is enabled in hog.conf
7232 foreach st $sim_types {
7234 if {[dict exists $hls_props $st]} {
7235 set flag_val [dict get $hls_props $st]
7236 } elseif {[dict exists $hls_props [string toupper $st]]} {
7237 set flag_val [dict get $hls_props [string toupper $st]]
7239 set is_enabled [expr {[string tolower $flag_val] eq "true" || $flag_val eq "1"}]
7241 set upper_st [string toupper $st]
7242 Msg Warning "HLS simulation '$st' requested for '$component_name' but\
7243 $upper_st is not enabled in \[hls:$component_name\] section of\
7244 hog.conf. Running it anyway."
7245 Msg Info "To enable it permanently, add '$upper_st=true' under\
7246 \[hls:$component_name\] in Top/$project_name/hog.conf"
7250 # Default mode: read flags from hog.conf
7253 foreach {key_lower key_upper} {csim CSIM cosim COSIM} {
7255 if {[dict exists $hls_props $key_lower]} {
7256 set val [dict get $hls_props $key_lower]
7257 } elseif {[dict exists $hls_props $key_upper]} {
7258 set val [dict get $hls_props $key_upper]
7260 if {[string tolower $val] eq "true" || $val eq "1"} {
7261 set run_$key_lower 1
7265 if {!$run_csim && !$run_cosim} {
7266 Msg Info "No HLS simulations enabled for '$component_name' (set CSIM=true and/or COSIM=true in \[hls:$component_name\] in hog.conf)"
7271 Msg Info "Running HLS simulations for component: $component_name"
7275 Msg Info "Running C simulation for HLS component '$component_name'..."
7277 ![ExecuteVitisUnifiedCommand $python_script "csim" \
7278 [list $component_name $cfg_file $hls_work_dir] \
7279 "Failed to run C simulation for $component_name"]
7281 Msg Error "C simulation failed for HLS component '$component_name'"
7286 # COSIM (auto-run synthesis if needed)
7288 set syn_done_marker [file normalize "$hls_work_dir/hls/syn"]
7289 if {![file exists $syn_done_marker]} {
7290 Msg Info "C synthesis output not found for '$component_name', running synthesis automatically before co-simulation..."
7292 ![ExecuteVitisUnifiedCommand $python_script "synthesis" \
7293 [list $component_name $cfg_file $hls_work_dir] \
7294 "Failed to run C synthesis for $component_name"]
7296 Msg Error "C synthesis failed for HLS component '$component_name', cannot proceed with co-simulation"
7301 Msg Info "Running C/RTL co-simulation for HLS component '$component_name'..."
7303 ![ExecuteVitisUnifiedCommand $python_script "cosim" \
7304 [list $component_name $cfg_file $hls_work_dir] \
7305 "Failed to run co-simulation for $component_name"]
7307 Msg Error "C/RTL co-simulation failed for HLS component '$component_name'"
7312 Msg Info "HLS simulations completed for '$component_name'"
7315 Msg Info "Done with HLS simulations for $project_name"
7323 proc GetProcFromProps {repo_path props platform} {
7324 if {[dict exists $props "platform:$platform" "BIF"]} {
7325 set bif_file [dict get $props "platform:$platform" "BIF"]
7327 set bif_file "$repo_path/$bif_file"
7331 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
7341 proc GetBifFromProps {repo_path props platform} {
7342 if {[dict exists $props "platform:$platform" "BIF"]} {
7343 set bif_file [dict get $props "platform:$platform" "BIF"]
7345 set bif_file "$repo_path/$bif_file"
7349 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
7358 proc GetPartFromProps {props} {
7359 if {[dict exists $props "main" "PART"]} {
7360 return [
string tolower [dict get $props "main" "PART"]]
7362 Msg Error "Part number not found in properties"
7371 proc GetArchFromPart {part} {
7372 set part [
string tolower $part]
7374 if {[
string match "xczu*" $part] || [
string match "xqzu*" $part]} {
7376 }
elseif {[
string match "xc7z*" $part] || [
string match "xq7z*" $part]} {
7379 [
string match "xcve*" $part] || [
string match "xcvc*" $part] ||
7380 [
string match "xcvp*" $part] || [
string match "xcvm*" $part] ||
7381 [
string match "xqve*" $part] || [
string match "xck26*" $part]
7384 }
elseif {[
string match "xc*" $part] || [
string match "xq*" $part]} {
7388 Msg CriticalWarning "Unknown part number: $part"
7397 proc GetAppsFromProps {props {list_names 0}} {
7398 set prop_apps [dict filter $props key {app:*}]
7399 set apps [dict create]
7400 set app_names [list]
7402 dict for {app_key app_value} $prop_apps {
7403 if {[regexp {^app:(.+)$} $app_key -> app_name]} {
7404 set app_name [string trim [string tolower $app_name]]
7405 # Convert only the keys of the inner dictionary to lowercase
7406 set app_value_lower [dict create]
7407 dict for {key value} $app_value {
7408 dict set app_value_lower [string tolower $key] $value
7410 dict set apps $app_name $app_value_lower
7411 lappend app_names $app_name
7414 if {$list_names eq 1} {
7425 proc GetPlatformsFromProps {props {list_names 0} {lower_case 0}} {
7426 set platforms [dict create]
7427 set platform_names [list]
7428 set prop_platforms [dict filter $props key {platform:*}]
7430 dict for {platform_key platform_value} $prop_platforms {
7431 if {[regexp {^platform:(.+)$} $platform_key -> platform_name]} {
7432 if {$lower_case == 1} {
7433 set platform_name [string trim [string tolower $platform_name]]
7435 set platform_name [string trim $platform_name]
7437 dict set platforms $platform_name $platform_value
7438 lappend platform_names $platform_name
7441 if {$list_names eq 1} {
7442 return $platform_names
7457 proc GenerateBootArtifacts {properties repo_path proj_dir bin_dir proj_name describe bitfile mmi_file} {
7458 set elf_list [glob -nocomplain "$bin_dir/*.elf"]
7462 if {[
llength $elf_list] == 0} {
7463 Msg Warning "No ELF files found in $bin_dir, skipping generation of boot artifacts"
7467 if {![
file exists $bitfile]} {
7468 Msg Warning "Bitfile $bitfile does not exist, skipping generation of boot artifacts"
7472 Msg Info "Generating boot artifacts for $proj_name..."
7473 Msg Info "Found apps: $apps"
7474 Msg Info "Found platforms: $platforms"
7478 foreach elf_file $elf_list {
7479 set elf_name [
file rootname [
file tail $elf_file]]
7480 Msg Info "Found elf name: $elf_name"
7481 Msg Info "Removing $describe from elf"
7484 if {[regexp "^(.+)-(.+)-$describe\$" $elf_name -> project_name elf_app]} {
7485 set elf_app [
string trim [
string tolower $elf_app]]
7486 Msg Info "Found elf_app: $elf_app"
7488 Msg Error "Could not extract app name from elf file: $elf_name"
7491 Msg Info "Removed project name ($project_name) and $describe from elf"
7493 set app_conf [dict get $apps $elf_app]
7494 set plat [dict get $app_conf "platform"]
7495 set app_proc [dict get $app_conf "proc"]
7498 if {[regexp -nocase {microblaze|risc} $app_proc]} {
7499 Msg Info "Detected soft processor ($app_proc) for $elf_app, updating bitstream memory with ELF file..."
7505 if {$proc_cell eq ""} {
7506 set proc_map_file [
file normalize "$proj_dir/vitis_classic/${plat}.PROC_MAP"]
7508 if {[dict exists $proc_map $app_proc]} {
7509 set proc_cell [dict get $proc_map $app_proc]
7510 Msg Info "Processor $app_proc not found in $mmi_file, using $proc_map_file instead."
7512 Msg Error "Could not determine the instance path of processor '$app_proc' for $elf_app, \
7513 neither from $mmi_file nor from $proc_map_file. \
7514 Check that proc= in the \[app:$elf_app\] section of hog.conf matches the processor instance name."
7518 Msg Info "Updating memory at processor cell: $proc_cell"
7520 set update_mem_cmd "updatemem -force -meminfo $mmi_file -data $elf_file -bit $bitfile -proc $proc_cell -out $bitfile"
7521 set ret [
catch {
exec -ignorestderr {*}$update_mem_cmd >@ stdout} result]
7523 Msg Error "Error updating memory for $elf_app: $result"
7525 Msg Info "Done updating memory for $elf_app"
7527 Msg Info "Detected hard processor ($app_proc) for $elf_app. Make sure the .elf file is defined in the platform ($plat)\
7528 .bif file to be included in the bootable binary image (.bin) generation."
7534 foreach plat $platforms {
7536 if {$bif_file != ""} {
7537 Msg Info "Generating bootable binary image (.bin) for $plat"
7539 Msg Info "Architecture: $arch"
7540 Msg Info "BIF file: $bif_file"
7541 if {$arch eq "unknown"} {
7542 Msg CriticalWarning "Skipping bootable image (.bin) generation for $plat: \
7543 could not determine bootgen architecture from the FPGA part."
7546 set bootgen_cmd "bootgen -arch $arch -image $bif_file -o i $bin_dir/$proj_name-$plat-$describe.bin -w on"
7547 set ret [
catch {
exec -ignorestderr {*}$bootgen_cmd >@ stdout} result]
7549 Msg Error "Error generating bootable binary image (.bin) for $plat: $result"
7551 Msg Info "Done generating bootable binary image (.bin) for $plat"
7563 proc GenerateStandaloneXciTargets {} {
7568 foreach xci [get_files -quiet *.xci] {
7569 set xci_file [get_files -quiet $xci]
7570 if {$xci_file eq ""} {
7575 catch {
set parent [get_property -quiet PARENT_COMPOSITE_FILE $xci_file]}
7576 if {$parent ne ""} {
7577 Msg Debug "Skipping [
file tail $xci], it is generated by its parent [
file tail $parent]"
7581 Msg Info "Generating targets for standalone IP [
file tail $xci]..."
7582 if {[
catch {generate_target all $xci_file} err]} {
7583 Msg CriticalWarning "Failed to generate targets for $xci: $err"
7597 proc GetProcInstPathFromMmi {mmi_file app_proc} {
7598 if {![
file exists $mmi_file]} {
7599 Msg Debug "MMI file not found: $mmi_file"
7603 set f [open $mmi_file "r"]
7604 set mmi_content [read $f]
7607 set inst_paths [list]
7608 foreach {match inst_path} [regexp -all -inline {<Processor[^>]*InstPath\s*=\s*"([^"]+)"} $mmi_content] {
7609 if {[lsearch -exact $inst_paths $inst_path] < 0} {
7610 lappend inst_paths $inst_path
7613 Msg Debug "Processor instance paths found in $mmi_file: $inst_paths"
7615 foreach inst_path $inst_paths {
7616 if {[
string equal -nocase [
file tail $inst_path] $app_proc]} {
7622 if {[
llength $inst_paths] == 1} {
7623 set inst_path [
lindex $inst_paths 0]
7624 Msg Info "Processor $app_proc does not match [
file tail $inst_path], \
7625 using the only processor found in the MMI file: $inst_path"
7636 proc ReadProcMap {proc_map_file} {
7637 set proc_map [dict create]
7638 if {[
file exists $proc_map_file]} {
7639 set f [open $proc_map_file "r"]
7640 while {[
gets $f line] >= 0} {
7641 Msg Debug "Line: $line"
7642 if {[regexp {^(\S+)\s+(.+)$} $line -> key value]} {
7643 Msg Debug "Found key: $key, value: $value in proc map file"
7644 dict set proc_map $key $value
7658 proc ListProjects {{repo_path .} {print 1} {ret_conf 0} {silent 0}} {
7659 set top_path [
file normalize $repo_path/Top]
7660 set confs [
findFiles [
file normalize $top_path] hog.conf]
7662 set confs [lsort $confs]
7666 set p [
Relative $top_path [
file dirname $c]]
7669 if {$description eq "test"} {
7670 set description " - Test project"
7671 }
elseif {$description ne ""} {
7672 set description " - $description"
7675 if {$print == 1 || $description ne " - Test project"} {
7677 set g [
file dirname $p]
7680 if {$silent == 0} {
Msg Status ""}
7688 if {$ret_conf == 0} {
7700 proc Md5Sum {file_name} {
7701 if {!([
file exists $file_name])} {
7702 Msg Warning "Could not find $file_name."
7705 if {[
catch {
package require md5 2.0.7} result]} {
7706 Msg Warning "Tcl package md5 version 2.0.7 not found ($result), will use command line..."
7707 set hash [
lindex [
Execute md5sum $file_name] 0]
7709 set file_hash [
string tolower [md5::md5 -hex -file $file_name]]
7723 proc MergeDict {dict0 dict1 {remove_duplicates 1}} {
7724 set outdict [dict merge $dict1 $dict0]
7725 foreach key [dict keys $dict1] {
7726 if {[dict exists $dict0 $key]} {
7727 set temp_list [dict get $dict1 $key]
7728 foreach item $temp_list {
7730 if {[
IsInList $item [
DictGet $outdict $key]] == 0 || $remove_duplicates == 0} {
7732 dict lappend outdict $key $item
7744 proc MoveElementToEnd {inputList element} {
7745 set index [lsearch $inputList $element]
7747 set inputList [
lreplace $inputList $index $index]
7748 lappend inputList $element
7757 proc OpenProject {project_file repo_path} {
7759 open_project $project_file
7761 set project_folder [
file dirname $project_file]
7762 set project [
file tail [
file rootname $project_file]]
7763 if {[
file exists $project_folder]} {
7765 if {![is_project_open]} {
7766 Msg Info "Opening existing project file $project_file..."
7767 project_open $project -current_revision
7770 Msg Error "Project directory not found for $project_file."
7774 Msg Info "Opening existing project file $project_file..."
7776 open_project -file $project_file -do_backup_on_convert 1 -backup_file {./Projects/$project_file.zip}
7778 Msg Info "Opening existing project file $project_file..."
7779 prj_project open $project_file
7781 Msg Error "This IDE is currently not supported by Hog. Exiting!"
7788 return $tcl_platform(platform)
7798 proc ParseJSON {JSON_FILE JSON_KEY} {
7799 set result [
catch {
package require Tcl 8.4} TclFound]
7800 if {"$result" != "0"} {
7801 Msg CriticalWarning "Cannot find Tcl package version equal or higher than 8.4.\n $TclFound\n Exiting"
7805 set result [
catch {
package require json} JsonFound]
7806 if {"$result" != "0"} {
7807 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
7810 set JsonDict [json::json2dict $JSON_FILE]
7811 set result [
catch {dict get $JsonDict $JSON_KEY} RETURNVALUE]
7812 if {"$result" != "0"} {
7813 Msg CriticalWarning "Cannot find $JSON_KEY in $JSON_FILE\n Exiting"
7826 proc ProjectExists {project {repo_path .}} {
7827 set index [lsearch -exact [
ListProjects $repo_path 0] $project]
7833 Msg Warning "Project not found. Available projects in $repo_path are"
7836 Msg Error "Project $project not found in repository $repo_path"
7847 proc ReadConf {file_name} {
7848 if {[
catch {
package require inifile 0.2.3} ERROR]} {
7849 Msg Error "Could not find inifile package version 0.2.3 or higher.\n
7850 To use ghdl, libero or diamond with Hog, you need to install the tcllib package\n
7851 You can install it with 'sudo apt install tcllib' on Debian/Ubuntu or 'sudo dnf install tcllib' on Fedora/RedHat/CentOs."
7855 ::ini::commentchar "#"
7856 set f [::ini::open $file_name]
7857 set properties [dict create]
7858 foreach sec [::ini::sections $f] {
7860 if {$new_sec == "files"} {
7863 set key_pairs [::ini::get $f $sec]
7865 regsub -all {\{\"} $key_pairs "\{" key_pairs
7866 regsub -all {\"\}} $key_pairs "\}" key_pairs
7868 dict set properties $new_sec [dict create {*}$key_pairs]
7881 proc ReadExtraFileList {extra_file_name} {
7882 set extra_file_dict [dict create]
7883 if {[
file exists $extra_file_name]} {
7884 set file [open $extra_file_name "r"]
7885 set file_data [read $file]
7888 set data [
split $file_data "\n"]
7889 foreach line $data {
7890 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
7891 set ip_and_md5 [regexp -all -inline {\S+} $line]
7892 dict lappend extra_file_dict "[
lindex $ip_and_md5 0]" "[
lindex $ip_and_md5 1]"
7896 return $extra_file_dict
7907 proc ExpandHlsConfigFiles {cfg_file} {
7909 if {![
file exists $cfg_file] || ![
file isfile $cfg_file]} {
7912 set cfg_dir [
file normalize [
file dirname $cfg_file]]
7914 if {[
catch {
set fp [open $cfg_file r]} err]} {
7915 Msg Warning "ExpandHlsConfigFiles: cannot open $cfg_file: $err"
7918 set lines [
split [read $fp] "\n"]
7921 foreach line $lines {
7922 if {[regexp {^[\t\s]*$} $line]} {continue}
7923 if {[regexp {^[\t\s]*\#} $line]} {continue}
7924 if {[regexp {^\s*\[.*\]\s*$} $line]} {continue}
7925 if {![regexp {^\s*[^=\s]+\s*=\s*(.+?)\s*$} $line -> value]} {continue}
7927 foreach tok [regexp -all -inline {\S+} $value] {
7928 if {[
file pathtype $tok] eq "absolute"} {
7929 set candidate [
file normalize $tok]
7931 set candidate [
file normalize [
file join $cfg_dir $tok]]
7933 if {[
file isfile $candidate]} {
7934 lappend out $candidate
7935 }
elseif {[
file isdirectory $candidate]} {
7936 set stack [list $candidate]
7937 while {[
llength $stack] > 0} {
7938 set d [
lindex $stack 0]
7939 set stack [
lrange $stack 1 end]
7940 foreach f [glob -nocomplain -directory $d -types f *] {
7941 lappend out [
file normalize $f]
7943 foreach sub [glob -nocomplain -directory $d -types d *] {
7950 return [lsort -unique $out]
7960 proc GetHlsConfigsFromProjConf {conf_file repo_path} {
7961 set out [dict create]
7962 if {![
file exists $conf_file]} {
return $out}
7963 if {[
catch {
set properties [
ReadConf $conf_file]} err]} {
7964 Msg Debug "GetHlsConfigsFromProjConf: cannot parse $conf_file: $err"
7967 set hls_components [dict filter $properties key {hls:*}]
7968 dict for {hls_key hls_props} $hls_components {
7969 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {continue}
7970 set component_name [string trim $component_name]
7972 if {[dict exists $hls_props hls_config]} {
7973 set cfg_rel [dict get $hls_props hls_config]
7974 } elseif {[dict exists $hls_props HLS_CONFIG]} {
7975 set cfg_rel [dict get $hls_props HLS_CONFIG]
7977 if {$cfg_rel eq ""} {continue}
7978 set cfg_abs [file normalize "$repo_path/$cfg_rel"]
7979 if {![file exists $cfg_abs]} {
7980 Msg CriticalWarning "HLS component '$component_name': HLS_CONFIG=$cfg_rel does not exist on disk."
7983 dict set out $component_name $cfg_abs
8005 proc ReadListFile {args} {
8008 if {[
catch {
package require cmdline} ERROR]} {
8009 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
8015 {lib.arg "" "The name of the library files will be added to, if not given will be extracted from the file name."}
8016 {fileset.arg "" "The name of the library, from the main list file"}
8017 {sha_mode "If set, the list files will be added as well and the IPs will be added to the file rather than to the special IP library. The SHA mode should be used when you use the lists to calculate the git SHA, rather than to add the files to the project."}
8018 {print_log "If set, will use PrintFileTree for the VIEW directive"}
8019 {indent.arg "" "Used to indent files with the VIEW directive"}
8022 set usage "USAGE: ReadListFile \[options\] <list file> <path>"
8023 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
8029 set list_file [
lindex $args 0]
8030 set path [
lindex $args 1]
8031 set sha_mode $options(sha_mode)
8032 set lib $options(lib)
8033 set fileset $options(fileset)
8034 set print_log $options(print_log)
8035 set indent $options(indent)
8037 if {$sha_mode == 1} {
8038 set sha_mode_opt "-sha_mode"
8043 if {$print_log == 1} {
8044 set print_log_opt "-print_log"
8046 set print_log_opt ""
8051 set lib [
file rootname [
file tail $list_file]]
8053 set fp [open $list_file r]
8054 set file_data [read $fp]
8056 set list_file_ext [
file extension $list_file]
8057 switch $list_file_ext {
8059 if {$fileset eq ""} {
8065 set fileset "constrs_1"
8068 set fileset "sources_1"
8072 set libraries [dict create]
8073 set filesets [dict create]
8074 set properties [dict create]
8076 set data [
split $file_data "\n"]
8078 set n [
llength $data]
8080 if {$print_log == 1} {
8081 if {$indent eq ""} {
8082 set list_file_rel [
file tail $list_file]
8083 Msg Status "\n$list_file_rel"
8087 Msg Debug "$n lines read from $list_file."
8090 foreach line $data {
8092 if {![regexp {^[\t\s]*$} $line] & ![regexp {^[\t\s]*\#} $line]} {
8093 set file_and_prop [regexp -all -inline {\S+} $line]
8094 set srcfile [
lindex $file_and_prop 0]
8095 set srcfile "$path/$srcfile"
8097 set srcfiles [glob -nocomplain $srcfile]
8100 if {$srcfiles != $srcfile && ![
string equal $srcfiles ""]} {
8101 Msg Debug "Wildcard source expanded from $srcfile to $srcfiles"
8103 if {![
file exists $srcfile]} {
8104 if {$print_log == 0} {
8105 Msg CriticalWarning "File: $srcfile (from list file: $list_file) does not exist."
8111 foreach vhdlfile $srcfiles {
8112 if {[
file exists $vhdlfile]} {
8113 set vhdlfile [
file normalize $vhdlfile]
8114 set extension [
file extension $vhdlfile]
8116 set prop [
lrange $file_and_prop 1 end]
8119 set library [
lindex [regexp -inline {\ylib\s*=\s*(.+?)\y.*} $prop] 1]
8120 if {$library == ""} {
8124 if {$extension == $list_file_ext} {
8127 set ref_path [
lindex [regexp -inline {\ypath\s*=\s*(\S+).*} $prop] 1]
8128 if {$ref_path eq ""} {
8131 set ref_path [
file normalize $path/$ref_path]
8133 Msg Debug "List file $vhdlfile found in list file, recursively opening it using path \"$ref_path\"..."
8134 if {$print_log == 1} {
8135 if {[
file normalize $last_printed] ne [
file normalize $vhdlfile]} {
8136 Msg Status "$indent Inside [
file tail $vhdlfile]:"
8140 lassign [
ReadListFile {*}"-indent \" $indent\" -lib $library -fileset $fileset $sha_mode_opt $print_log_opt $vhdlfile $ref_path"] l p fs
8142 set properties [
MergeDict $p $properties]
8144 }
elseif {[lsearch {.src .sim .con ReadExtraFileList} $extension] >= 0} {
8146 Msg Error "$vhdlfile cannot be included into $list_file, $extension files must be included into $extension files."
8149 regsub -all " *= *" $prop "=" prop
8153 if {[
string first "lib=" $p] == -1} {
8155 set pos [
string first "=" $p]
8159 set prop_name [
string range $p 0 [
expr {$pos - 1}]]
8162 || [
string first "top" $p] == 0
8163 || $list_file_ext eq ".ipb"
8164 || $list_file_ext eq ".chb"} {
8165 if {$list_file_ext eq ".ipb"} {
8166 dict lappend properties $vhdlfile $path/$p
8168 dict lappend properties $vhdlfile $p
8170 Msg Debug "Adding property $p to $vhdlfile..."
8171 }
elseif {$list_file_ext != ".ipb" && $list_file_ext != ".chb"} {
8172 Msg Warning "Setting Property $p is not supported for file $vhdlfile or it is already its default. \
8177 if {[lsearch {.xci .ip .bd .xcix} $extension] >= 0} {
8179 set lib_name "ips.src"
8180 }
elseif {[
IsInList $extension {.vhd .vhdl}] || $list_file_ext == ".sim"} {
8182 if {![
IsInList $extension {.vhd .vhdl}]} {
8183 set lib_name "others.sim"
8185 set lib_name "$library$list_file_ext"
8187 }
elseif {$list_file_ext == ".con"} {
8188 set lib_name "sources.con"
8189 }
elseif {$list_file_ext == ".ipb"} {
8190 set lib_name "xml.ipb"
8191 }
elseif {$list_file_ext == ".chb"} {
8192 set lib_name "cheby.chb"
8193 }
elseif {[
IsInList $list_file_ext {.src}] && [
IsInList $extension {.c .cpp .h .hpp}]} {
8195 set lib_name "$library$list_file_ext"
8198 set lib_name "others.src"
8201 Msg Debug "Appending $vhdlfile to $lib_name list..."
8202 dict lappend libraries $lib_name $vhdlfile
8203 if {$sha_mode != 0 && [
file type $vhdlfile] eq "link"} {
8206 dict lappend libraries $lib_name $real_file
8207 Msg Debug "File $vhdlfile is a soft link, also adding the real file: $real_file"
8215 if {$list_file_ext eq ".src" && $extension eq ".cfg"} {
8217 if {$print_log == 1 && [
llength $hls_extras] > 0} {
8218 Msg Status "$indent Inside [
file tail $vhdlfile] (HLS auto-expand):"
8220 set n_extras [
llength $hls_extras]
8222 foreach hls_extra $hls_extras {
8224 if {$hls_extra eq $vhdlfile} {continue}
8225 Msg Debug "HLS cfg expansion: adding $hls_extra (from $vhdlfile) to $lib_name"
8226 if {$print_log == 1} {
8227 if {$i == $n_extras} {
set pad "└──"}
else {
set pad "├──"}
8228 set rel [
Relative [
file dirname $vhdlfile] $hls_extra]
8229 Msg Status "$indent $pad $rel"
8231 dict lappend libraries $lib_name $hls_extra
8232 if {$sha_mode != 0 && [
file type $hls_extra] eq "link"} {
8234 dict lappend libraries $lib_name $real_extra
8235 Msg Debug "HLS expanded file $hls_extra is a soft link, also adding $real_extra"
8242 if {[dict exists $filesets $fileset] == 0} {
8244 Msg Debug "Adding $fileset to the fileset dictionary..."
8245 Msg Debug "Adding library $lib_name to fileset $fileset..."
8246 dict set filesets $fileset $lib_name
8250 Msg Debug "Adding library $lib_name to fileset $fileset..."
8251 dict lappend filesets $fileset $lib_name
8257 Msg CriticalWarning "File $vhdlfile not found."
8263 if {$sha_mode != 0} {
8265 if {$list_file_ext eq ".ipb"} {
8266 set sha_lib "xml.ipb"
8267 }
elseif {$list_file_ext eq ".chb"} {
8268 set sha_lib "cheby.chb"
8270 set sha_lib $lib$list_file_ext
8272 dict lappend libraries $sha_lib [
file normalize $list_file]
8273 if {[
file type $list_file] eq "link"} {
8276 dict lappend libraries $lib$list_file_ext $real_file
8277 Msg Debug "List file $list_file is a soft link, also adding the real file: $real_file"
8280 return [list $libraries $properties $filesets]
8289 proc Relative {base dst {quiet 0}} {
8290 if {![
string equal [
file pathtype $base] [
file pathtype $dst]]} {
8292 Msg CriticalWarning "Unable to compute relation for paths of different path types: [
file pathtype $base] vs. [
file pathtype $dst], ($base vs. $dst)"
8297 set base [
file normalize [
file join [
pwd] $base]]
8298 set dst [
file normalize [
file join [
pwd] $dst]]
8301 set base [
file split $base]
8302 set dst [
file split $dst]
8304 while {[
string equal [
lindex $dst 0] [
lindex $base 0]]} {
8305 set dst [
lrange $dst 1 end]
8306 set base [
lrange $base 1 end]
8307 if {![
llength $dst]} {break}
8310 set dstlen [
llength $dst]
8311 set baselen [
llength $base]
8313 if {($dstlen == 0) && ($baselen == 0)} {
8316 while {$baselen > 0} {
8317 set dst [
linsert $dst 0 ..]
8320 set dst [
eval [
linsert $dst 0 file join]]
8331 proc RelativeLocal {pathName filePath} {
8332 if {[
string first [
file normalize $pathName] [
file normalize $filePath]] != -1} {
8333 return [
Relative $pathName $filePath]
8344 proc RemoveDuplicates {mydict} {
8345 set new_dict [dict create]
8346 foreach key [dict keys $mydict] {
8347 set values [
DictGet $mydict $key]
8348 foreach value $values {
8349 set idxs [
lreverse [
lreplace [lsearch -exact -all $values $value] 0 0]]
8351 set values [
lreplace $values $idx $idx]
8354 dict set new_dict $key $values
8365 proc ResetRepoFiles {reset_file} {
8366 if {[
file exists $reset_file]} {
8367 Msg Info "Found $reset_file, opening it..."
8368 set fp [open $reset_file r]
8369 set wild_cards [lsearch -all -inline -not -regexp [
split [read $fp] "\n"] "^ *$"]
8371 Msg Info "Found the following files/wild cards to restore if modified: $wild_cards..."
8372 foreach w $wild_cards {
8374 if {[
llength $mod_files] > 0} {
8375 Msg Info "Found modified $w files: $mod_files, will restore them..."
8378 Msg Info "No modified $w files found."
8389 proc RestoreModifiedFiles {{repo_path "."} {pattern "."}} {
8392 set ret [
Git checkout $pattern]
8403 proc SearchHogProjects {dir} {
8404 set projects_list {}
8405 if {[
file exists $dir]} {
8406 if {[
file isdirectory $dir]} {
8407 foreach proj_dir [glob -nocomplain -types d $dir/*] {
8408 if {![regexp {^.*Top/+(.*)$} $proj_dir dummy proj_name]} {
8409 Msg Warning "Could not parse Top directory $dir"
8412 if {[
file exists "$proj_dir/hog.conf"]} {
8413 lappend projects_list $proj_name
8416 lappend projects_list $p
8421 Msg Error "Input $dir is not a directory!"
8424 Msg Error "Directory $dir doesn't exist!"
8426 return $projects_list
8435 proc SetGenericsSimulation {repo_path proj_dir target} {
8436 set top_dir "$repo_path/Top/$proj_dir"
8437 set simsets [get_filesets]
8438 if {$simsets != ""} {
8439 foreach simset $simsets {
8441 if {[get_property FILESET_TYPE $simset] != "SimulationSrcs"} {
8445 set merged_generics_dict [dict create]
8449 set simset_generics [
DictGet $simset_dict "generics"]
8450 set merged_generics_dict [
MergeDict $merged_generics_dict $simset_generics 0]
8453 Msg Debug "TOP = [get_property top [get_filesets sources_1]]"
8454 Msg Debug "GENERICS = [get_property generic [get_filesets sources_1]]"
8456 set_property generic $generic_str [get_filesets $simset]
8457 Msg Info "Setting generics $generic_str for simulator $target\
8458 and simulation file-set $simset..."
8470 proc SetTopProperty {top_module fileset} {
8471 Msg Info "Setting TOP property to $top_module module"
8474 set_property "top" $top_module [get_filesets $fileset]
8477 set_global_assignment -name TOP_LEVEL_ENTITY $top_module
8479 set_root -module $top_module
8481 prj_impl option top $top_module
8486 proc VIVADO_PATH_PROPERTIES {} {
8487 return {"\.*\.TCL\.PRE$" "^.*\.TCL\.POST$" "^RQS_FILES$" "^INCREMENTAL\_CHECKPOINT$" "NOC\_SOLUTION\_FILE"}
8491 proc VITIS_PATH_PROPERTIES {} {
8492 return {"^HW$" "^XPFM$" "^LINKER-SCRIPT$" "^LIBRARIES$" "^LIBRARY-SEARCH-PATH$"}
8502 proc WriteConf {file_name config {comment ""}} {
8503 if {[
catch {
package require inifile 0.2.3} ERROR]} {
8504 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
8508 ::ini::commentchar "#"
8509 set f [::ini::open $file_name w]
8511 foreach sec [dict keys $config] {
8512 set section [dict get $config $sec]
8513 dict for {p v} $section {
8514 if {[string trim $v] == ""} {
8515 Msg Warning "Property $p has empty value. Skipping..."
8518 ::ini::set $f $sec $p $v
8523 if {![
string equal "$comment" ""]} {
8524 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $comment
8525 set hog_header "Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
8526 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $hog_header
8537 proc WriteDefines {design} {
8539 if {[dict size $prj_defines] > 0} {
8542 set_property verilog_define $define_string [current_fileset]
8543 Msg Info "Setting Verilog defines from the \[defines\] section of hog.conf..."
8544 Msg Debug "Detailed defines: $define_string"
8547 Msg Error "Project $design has a \[defines\] section in hog.conf, but Verilog defines are not supported for $ide_name projects."
8560 proc WriteGenerics {
8561 mode repo_path design date timee
8562 commit version top_hash top_ver hog_hash hog_ver
8563 cons_ver cons_hash libs vers hashes ext_names ext_hashes
8564 user_ip_repos user_ip_vers user_ip_hashes flavour {xml_ver ""} {xml_hash ""}
8565 {cheby_ver ""} {cheby_hash ""}
8567 Msg Info "Passing parameters/generics to project's top module..."
8570 set generic_string [
concat \
8582 if {$xml_hash != "" && $xml_ver != ""} {
8583 lappend generic_string \
8588 if {$cheby_hash != "" && $cheby_ver != ""} {
8589 lappend generic_string \
8594 foreach l $libs v $vers h $hashes {
8600 set ver [regsub -all {[\.-]} $ver {_}]
8601 set hash [regsub -all {[\.-]} $hash {_}]
8602 lappend generic_string "$ver" "$hash"
8605 foreach e $ext_names h $ext_hashes {
8607 lappend generic_string "$hash"
8610 foreach repo $user_ip_repos v $user_ip_vers h $user_ip_hashes {
8611 set repo_name [
file tail $repo]
8612 set ver "[
string toupper $repo_name]_VER=[
FormatGeneric $v]"
8613 set hash "[
string toupper $repo_name]_SHA=[
FormatGeneric $h]"
8614 set ver [regsub -all {[\.-]} $ver {_}]
8615 set hash [regsub -all {[\.-]} $hash {_}]
8616 lappend generic_string "$ver" "$hash"
8619 if {$flavour != -1} {
8620 lappend generic_string "FLAVOUR=$flavour"
8626 set generic_string "$prj_generics $generic_string"
8635 if {$mode == "create" || [
IsISE]} {
8638 if {[
file exists $top_file]} {
8641 Msg Debug "Found top level generics $generics in $top_file"
8643 set filtered_generic_string ""
8645 foreach generic_to_set [
split [
string trim $generic_string]] {
8646 set key [
lindex [
split $generic_to_set "="] 0]
8647 if {[dict exists $generics $key]} {
8648 Msg Debug "Hog generic $key found in $top_name"
8649 lappend filtered_generic_string "$generic_to_set"
8651 Msg Warning "Generic $key is passed by Hog but is NOT present in $top_name."
8657 set generic_string $filtered_generic_string
8662 set_property generic $generic_string [current_fileset]
8663 Msg Info "Setting parameters/generics..."
8664 Msg Debug "Detailed parameters/generics: $generic_string"
8669 set simulator [get_property target_simulator [current_project]]
8670 if {$mode == "create"} {
8677 Msg Info "Setting Synplify parameters/generics one by one..."
8678 foreach generic $generic_string {
8679 Msg Debug "Setting Synplify generic: $generic"
8680 set_option -hdl_param -set "$generic"
8683 Msg Info "Setting Diamond parameters/generics one by one..."
8684 prj_impl option -impl Implementation0 HDL_PARAM "$generic_string"
8686 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
8688 foreach app_name [dict keys $ws_apps] {
8689 set defined_symbols [app config -name $app_name -get define-compiler-symbols]
8690 foreach generic_to_set [
split [
string trim $generic_string]] {
8691 set key [
lindex [
split $generic_to_set "="] 0]
8692 set value [
lindex [
split $generic_to_set "="] 1]
8693 if {[
string match "32'h*" $value]} {
8694 set value [
string map {"32'h" "0x"} $value]
8697 foreach symbol [
split $defined_symbols ";"] {
8698 if {[
string match "$key=*" $symbol]} {
8699 Msg Debug "Generic $key found in $app_name, removing it..."
8700 app config -name $app_name -remove define-compiler-symbols "$symbol"
8704 Msg Info "Setting Vitis parameters/generics for app $app_name: $key=$value"
8705 app config -name $app_name define-compiler-symbols "$key=$value"
8717 proc WriteGenericsToBdIPs {mode repo_path proj generic_string} {
8718 Msg Debug "Parameters/generics passed to WriteGenericsToIP: $generic_string"
8720 set bd_ip_generics false
8722 if {[dict exists $properties "hog"]} {
8723 set propDict [dict get $properties "hog"]
8724 if {[dict exists $propDict "PASS_GENERICS_TO_BD_IPS"]} {
8725 set bd_ip_generics [dict get $propDict "PASS_GENERICS_TO_BD_IPS"]
8729 if {[
string compare [
string tolower $bd_ip_generics] "false"] == 0} {
8733 if {$mode == "synth"} {
8734 Msg Info "Attempting to apply generics pre-synthesis..."
8735 set PARENT_PRJ [get_property "PARENT.PROJECT_PATH" [current_project]]
8736 set workaround [open "$repo_path/Projects/$proj/.hog/presynth_workaround.tcl" "w"]
8737 puts $workaround "source \[lindex \$argv 0\];"
8738 puts $workaround "open_project \[lindex \$argv 1\];"
8739 puts $workaround "WriteGenericsToBdIPs \[lindex \$argv 2\] \[lindex \$argv 3\] \[lindex \$argv 4\] \[lindex \$argv 5\];"
8740 puts $workaround "close_project"
8744 exec vivado -mode batch -source $repo_path/Projects/$proj/.hog/presynth_workaround.tcl \
8745 -tclargs $repo_path/Hog/Tcl/hog.tcl $PARENT_PRJ \
8746 "childprocess" $repo_path $proj $generic_string
8749 Msg Error "Encountered an error while attempting workaround: $errMsg"
8751 file delete $repo_path/Projects/$proj/.hog/presynth_workaround.tcl
8753 Msg Info "Done applying generics pre-synthesis."
8757 Msg Info "Looking for IPs to add generics to..."
8758 set ips_generic_string ""
8759 foreach generic_to_set [
split [
string trim $generic_string]] {
8760 set key [
lindex [
split $generic_to_set "="] 0]
8761 set value [
lindex [
split $generic_to_set "="] 1]
8762 append ips_generic_string "CONFIG.$key $value "
8766 if {[
string compare [
string tolower $bd_ip_generics] "true"] == 0} {
8769 set ip_regex $bd_ip_generics
8772 set ip_list [get_ips -regex $ip_regex]
8773 Msg Debug "IPs found with regex \{$ip_regex\}: $ip_list"
8775 set regen_targets {}
8777 foreach {ip} $ip_list {
8778 set WARN_ABOUT_IP false
8779 set ip_props [list_property [get_ips $ip]]
8782 if {[lsearch -exact $ip_props "IS_BD_CONTEXT"] == -1} {
8786 if {[get_property "IS_BD_CONTEXT" [get_ips $ip]] eq "1"} {
8787 foreach {ip_prop} $ip_props {
8788 if {[dict exists $ips_generic_string $ip_prop]} {
8789 if {$WARN_ABOUT_IP == false} {
8790 lappend regen_targets [get_property SCOPE [get_ips $ip]]
8791 Msg Warning "The ip \{$ip\} contains generics that are set by Hog.\
8792 If this is IP is apart of a block design, the .bd file may contain stale, unused, values.\
8793 Hog will always apply the most up-to-date values to the IP during synthesis,\
8794 however these values may or may not be reflected in the .bd file."
8795 set WARN_ABOUT_IP true
8800 set xci_path [get_property IP_FILE [get_ips $ip]]
8802 if {[
string equal $generic_format "ERROR"]} {
8803 Msg Warning "Could not find format for generic $ip_prop in IP $ip. Skipping..."
8807 set value_to_set [dict get $ips_generic_string $ip_prop]
8808 switch -exact $generic_format {
8810 if {[
string match "32'h*" $value_to_set]} {
8811 scan [
string map {"32'h" ""} $value_to_set] "%x" value_to_set
8815 set value_to_set [
expr {$value_to_set ? "true" : "false"}]
8818 if {[
string match "32'h*" $value_to_set]} {
8819 binary scan [
binary format H* [
string map {"32'h" ""} $value_to_set]] d value_to_set
8823 if {[
string match "32'h*" $value_to_set]} {
8824 set value_to_set [
string map {"32'h" "0x"} $value_to_set]
8828 set value_to_set [
format "%s" $value_to_set]
8831 Msg Warning "Unknown generic format $generic_format for IP $ip. Will attempt to pass as string..."
8836 Msg Info "The IP \{$ip\} contains: $ip_prop ($generic_format), setting it to $value_to_set."
8837 if {[
catch {set_property -name $ip_prop -value $value_to_set -objects [get_ips $ip]} prop_error]} {
8838 Msg CriticalWarning "Failed to set property $ip_prop to $value_to_set for IP \{$ip\}: $prop_error"
8845 foreach {regen_target} [lsort -unique $regen_targets] {
8846 Msg Info "Regenerating target: $regen_target"
8847 if {[
catch {generate_target -force all [get_files $regen_target]} prop_error]} {
8848 Msg CriticalWarning "Failed to regen targets: $prop_error"
8856 proc GetGenericFormatFromXciXML {generic_name xml_file} {
8857 if {![
file exists $xml_file]} {
8858 Msg Error "Could not find XML file: $xml_file"
8862 set fp [open $xml_file r]
8863 set xci_data [read $fp]
8866 set paramType "string"
8867 set modelparam_regex [
format {^.*\y%s\y.*$} [
string map {"CONFIG." "MODELPARAM_VALUE."} $generic_name]]
8868 set format_regex {format="([^"]+)"}
8870 set line [
lindex [regexp -inline -line $modelparam_regex $xci_data] 0]
8871 Msg Debug "line: $line"
8873 if {[regexp $format_regex $line match format_value]} {
8874 Msg Debug "Extracted: $format_value format from xml"
8875 set paramType $format_value
8877 Msg Debug "No format found, using string"
8886 proc GetGenericFormatFromXci {generic_name xci_file} {
8887 if {![
file exists $xci_file]} {
8888 Msg Error "Could not find XCI file: $xci_file"
8892 set fp [open $xci_file r]
8893 set xci_data [read $fp]
8896 set paramType "string"
8897 if {[
string first "xilinx.com:schema:json_instance:1.0" $xci_data] == -1} {
8898 Msg Debug "XCI format is not JSON, trying XML..."
8899 set xml_file "[
file rootname $xci_file].xml"
8903 set generic_name [
string map {"CONFIG." ""} $generic_name]
8904 set ip_inst [
ParseJSON $xci_data "ip_inst"]
8905 set parameters [dict get $ip_inst parameters]
8906 set component_parameters [dict get $parameters component_parameters]
8907 if {[dict exists $component_parameters $generic_name]} {
8908 set generic_info [dict get $component_parameters $generic_name]
8909 if {[dict exists [
lindex $generic_info 0] format]} {
8910 set paramType [dict get [
lindex $generic_info 0] format]
8911 Msg Debug "Extracted: $paramType format from xci"
8914 Msg Debug "No format found, using string"
8927 proc WriteGitLabCIYAML {proj_name {ci_conf ""}} {
8928 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
8929 Msg CriticalWarning "Cannot find package YAML.\n Error message: $YAMLPACKAGE. \
8930 If you are running on tclsh, you can fix this by installing package \"tcllib\""
8935 if {$ci_conf != ""} {
8937 foreach sec [dict keys $ci_confs] {
8938 if {[
string first : $sec] == -1} {
8939 lappend job_list $sec
8943 set job_list {"generate_project" "simulate_project"}
8947 set out_yaml [huddle create]
8948 foreach job $job_list {
8950 set huddle_tags [huddle list]
8952 set sec_dict [dict create]
8954 if {$ci_confs != ""} {
8955 foreach var [dict keys [dict get $ci_confs $job]] {
8956 if {$var == "tags"} {
8957 set tag_section "tags"
8958 set tags [dict get [dict get $ci_confs $job] $var]
8959 set tags [
split $tags ","]
8961 set tag_list [huddle list $tag]
8962 set huddle_tags [huddle combine $huddle_tags $tag_list]
8965 dict set sec_dict $var [dict get [dict get $ci_confs $job] $var]
8971 set huddle_variables [huddle create "PROJECT_NAME" $proj_name "extends" ".vars"]
8972 if {[dict exists $ci_confs "$job:variables"]} {
8973 set var_dict [dict get $ci_confs $job:variables]
8974 foreach var [dict keys $var_dict] {
8976 set value [dict get $var_dict "$var"]
8977 set var_inner [huddle create "$var" "$value"]
8978 set huddle_variables [huddle combine $huddle_variables $var_inner]
8983 set middle [huddle create "extends" ".$job" "variables" $huddle_variables]
8984 foreach sec [dict keys $sec_dict] {
8985 set value [dict get $sec_dict $sec]
8986 set var_inner [huddle create "$sec" "$value"]
8987 set middle [huddle combine $middle $var_inner]
8989 if {$tag_section != ""} {
8990 set middle2 [huddle create "$tag_section" $huddle_tags]
8991 set middle [huddle combine $middle $middle2]
8994 set outer [huddle create "$job:$proj_name" $middle]
8995 set out_yaml [huddle combine $out_yaml $outer]
8998 return [
string trimleft [yaml::huddle2yaml $out_yaml] "-"]
9008 proc WriteListFiles {libs props list_path repo_path {ext_path ""}} {
9010 foreach lib [dict keys $libs] {
9011 if {[
llength [
DictGet $libs $lib]] > 0} {
9012 set list_file_name $list_path$lib
9013 set list_file [open $list_file_name w]
9014 Msg Info "Writing $list_file_name..."
9015 puts $list_file "#Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
9016 foreach file [
DictGet $libs $lib] {
9018 set prop [
DictGet $props $file]
9022 puts $list_file "$file_path $prop"
9025 set ext_list_file [open "[
file rootname $list_file].ext" a]
9026 puts $ext_list_file "$file_path $prop"
9027 close $ext_list_file
9030 Msg Warning "The path of file $file is not relative to your repository. Please check!"
9046 proc WriteSimListFile {simset libs props simsets list_path repo_path {force 0}} {
9048 set list_file_name $list_path/${simset}.sim
9049 if {$force == 0 && [
file exists $list_file_name]} {
9050 Msg Info "List file $list_file_name already exists, skipping..."
9054 set list_file [open $list_file_name a+]
9057 puts $list_file "\[files\]"
9058 Msg Info "Writing $list_file_name..."
9059 foreach lib [
DictGet $simsets $simset] {
9060 foreach file [
DictGet $libs $lib] {
9062 set prop [
DictGet $props $file]
9066 set lib_name [
file rootname $lib]
9067 if {$lib_name != $simset && [
file extension $file] == ".vhd" && [
file extension $file] == ""} {
9068 lappend prop "lib=$lib_name"
9070 puts $list_file "$file_path $prop"
9073 Msg Warning "The path of file $file is not relative to your repository. Please check!"
9085 proc WriteToFile {File msg} {
9086 set f [open $File a+]
9097 proc WriteUtilizationSummary {input output project_name run} {
9098 set f [open $input "r"]
9099 set o [open $output "a"]
9100 puts $o "## $project_name $run Utilization report\n\n"
9101 struct::matrix util_m
9102 util_m add columns 14
9105 util_m add row "| **Site Type** | **Used** | **Fixed** | **Prohibited** | **Available** | **Util%** |"
9106 util_m add row "| --- | --- | --- | --- | --- | --- |"
9108 util_m add row "| **Site Type** | **Used** | **Fixed** | **Available** | **Util%** |"
9109 util_m add row "| --- | --- | --- | --- | --- |"
9119 while {[
gets $f line] >= 0} {
9120 if {([
string first "| CLB LUTs" $line] >= 0 || [
string first "| Slice LUTs" $line] >= 0) && $luts == 0} {
9121 util_m add row $line
9124 if {([
string first "| CLB Registers" $line] >= 0 || [
string first "| Slice Registers" $line] >= 0) && $regs == 0} {
9125 util_m add row $line
9128 if {[
string first "| Block RAM Tile" $line] >= 0 && $bram == 0} {
9129 util_m add row $line
9132 if {[
string first "URAM " $line] >= 0 && $uram == 0} {
9133 util_m add row $line
9136 if {[
string first "DSPs" $line] >= 0 && $dsps == 0} {
9137 util_m add row $line
9140 if {[
string first "Bonded IOB" $line] >= 0 && $ios == 0} {
9141 util_m add row $line
9148 puts $o [util_m format 2string]
9154 Msg Error "Found Git version older than 2.7.2. Hog will not work as expected, exiting now."
9163 proc GetCurl {{gitlab_url "https://gitlab.com"}} {
9164 if {[
auto_execok curl] == ""} {
9165 Msg Warning "Cannot find a working curl invocation"
9170 set cmd [list curl --silent --show-error]
9172 set cmd [list env -u LD_LIBRARY_PATH curl --silent --show-error]
9175 if {![
catch {
exec {*}$cmd -I $gitlab_url}]} {
9178 Msg Warning "Impossible to contact $gitlab_url"