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
2892 proc GetConfFiles {proj_dir} {
2893 Msg Debug "GetConfFiles called with proj_dir=$proj_dir"
2894 if {![
file isdirectory $proj_dir]} {
2895 Msg Error "$proj_dir is supposed to be the top project directory"
2898 set conf_file [
file normalize $proj_dir/hog.conf]
2899 set sim_file [
file normalize $proj_dir/sim.conf]
2900 set pre_tcl [
file normalize $proj_dir/pre-creation.tcl]
2901 set post_tcl [
file normalize $proj_dir/post-creation.tcl]
2902 set pre_rtl [
file normalize $proj_dir/pre-rtl.tcl]
2903 set post_rtl [
file normalize $proj_dir/post-rtl.tcl]
2905 return [list $conf_file $sim_file $pre_tcl $post_tcl $pre_rtl $post_rtl]
2914 proc GetCustomCommands {parameters {directory .}} {
2915 set commands_dict [dict create]
2916 set commands_files [glob -nocomplain $directory/*.tcl]
2918 if {[
llength $commands_files] == 0} {
2922 foreach file $commands_files {
2926 if {$custom_cmd eq ""} {
2931 set custom_cmd_name [dict get $custom_cmd NAME]
2933 Msg Debug "Loaded custom command '$custom_cmd_name' from $file"
2936 if {[dict exists $commands_dict $custom_cmd_name]} {
2937 Msg Error "Custom command '$custom_cmd_name' in $file already defined as: \[dict get $commands_dict $custom_cmd_name\]. Skipping."
2942 set custom_cmd_name [
string toupper $custom_cmd_name]
2943 dict set commands_dict $custom_cmd_name $custom_cmd
2946 return $commands_dict
2949 proc SanitizeCustomCommand {cmdDict file parameters} {
2954 foreach k [dict keys $cmdDict] {
2955 set K [
string toupper $k]
2956 dict set normalized $K [dict get $cmdDict $k]
2959 set cmdDict $normalized
2960 if {![dict exists $cmdDict NAME]} {
2961 Msg Error "Custom command in $file missing required key NAME. Skipping."
2964 if {![dict exists $cmdDict SCRIPT]} {
2965 Msg Error "Custom command '$[dict get $cmdDict NAME]' in $file missing SCRIPT. Skipping."
2970 set allowed {NAME DESCRIPTION OPTIONS CUSTOM_OPTIONS SCRIPT IDE NO_EXIT}
2971 foreach k [dict keys $cmdDict] {
2972 if {[lsearch -exact $allowed $k] < 0} {
2973 Msg Warning "Custom command '[dict get $cmdDict NAME]' in $file: unknown key '$k'. Allowed: $allowed. Skipping."
2978 set name [
string trim [dict get $cmdDict NAME]]
2980 Msg Error "Custom command in $file has empty NAME. Skipping."
2984 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]+$} $name]} {
2985 Msg Error "Custom command NAME '$name' (file $file) contains invalid characters."
2989 if {![dict exists $cmdDict DESCRIPTION]} {
2990 dict set cmdDict DESCRIPTION "No description provided."
2994 set hog_parameters {}
2995 foreach p $parameters {
2996 lappend hog_parameters [
lindex $p 0]
3001 if {[dict exists $cmdDict OPTIONS]} {
3002 set raw_opts [dict get $cmdDict OPTIONS]
3003 if {![
llength $raw_opts]} {
3007 foreach item $raw_opts {
3009 foreach p $parameters {
3010 set hog_parameter [
lindex $p 0]
3011 if {$item eq $hog_parameter} {
3012 lappend hog_options $p
3018 Msg Warning "Custom command '$name' in $file: option '$item' not found in Hog parameters. Skipping."
3021 dict set cmdDict OPTIONS $hog_options
3023 dict set cmdDict OPTIONS {}
3029 if {[dict exists $cmdDict CUSTOM_OPTIONS]} {
3030 set raw_opts [dict get $cmdDict CUSTOM_OPTIONS]
3031 if {![
llength $raw_opts]} {
3034 foreach item $raw_opts {
3035 if {[
llength $item] != 2 && [
llength $item] != 3} {
3036 Msg Error "Bad custom option: \[$item\]. Custom command '$name' in $file: \
3037 each CUSTOM_OPTIONS entry must be {option \"help\"} for flags \
3038 and {option \"default_value\" \"help\"} for options with arguments. Skipping command."
3042 if {[
llength $item] == 2} {
3043 lassign $item opt help
3046 lassign $item opt def help
3049 if {[
IsInList $opt $hog_parameters] == 1} {
3050 Msg Warning "Custom command '$name' in $file: option '$opt' already defined in Hog parameters. Skipping."
3056 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]*(\.arg)?$} $opt]} {
3057 Msg Error "Custom command '$name' in $file: invalid option name '$opt'."
3062 Msg Warning "Custom command '$name' option '$opt' has empty help text."
3066 dict set cmdDict CUSTOM_OPTIONS {}
3070 if {[dict exists $cmdDict NO_EXIT]} {
3071 set no_exit [dict get $cmdDict NO_EXIT]
3072 set no_exit [
string tolower [
string trim $no_exit]]
3074 if {$no_exit eq "1" || $no_exit eq "true"} {
3080 dict set cmdDict NO_EXIT $no_exit
3082 dict set cmdDict NO_EXIT 0
3088 proc LoadCustomCommandFile {file parameters} {
3090 set dir [
file dirname $file]
3092 unset -nocomplain ::hog_command
3093 set rc [
catch {source $file} err]
3096 Msg Error "Error sourcing custom command file $file: $err"
3099 if {![
info exists ::hog_command]} {
3100 Msg Warning "File $file did not define ::hog_command. Skipping."
3103 set cmdDict $::hog_command
3105 if {[
catch {dict size $cmdDict}]} {
3106 Msg Error "In $file ::hog_command is not a valid dict. Skipping."
3116 proc GetDateAndTime {commit} {
3117 set clock_seconds [
clock seconds]
3120 set date [
Git "log -1 --format=%cd --date=format:%d%m%Y $commit"]
3121 set timee [
Git "log -1 --format=%cd --date=format:00%H%M%S $commit"]
3123 Msg Warning "Found Git version older than 2.9.3. Using current date and time instead of commit time."
3124 set date [
clock format $clock_seconds -format {%d%m%Y}]
3125 set timee [
clock format $clock_seconds -format {00%H%M%S}]
3127 return [list $date $timee]
3139 proc GetFile {file fileset} {
3142 set Files [get_files -all $file -of_object [get_filesets $fileset]]
3143 set f [
lindex $Files 0]
3151 puts "***DEBUG Hog:GetFile $file"
3160 proc GetFileGenerics {filename {entity ""}} {
3162 if {[
string equal $file_type "VERILOG_FILE"] || [
string equal $file_type "SYSTEMVERILOG_FILE"]} {
3164 }
elseif {[
string equal $file_type "VHDL_FILE"]} {
3167 Msg CriticalWarning "Could not determine extension of top level file."
3176 proc GetGenericsFromConf {proj_dir} {
3177 set generics_dict [dict create]
3178 set top_dir "Top/$proj_dir"
3179 set conf_file "$top_dir/hog.conf"
3181 Msg Debug "GetGenericsFromConf called with proj_dir=$proj_dir, top_dir=$top_dir"
3183 if {[
file exists $conf_file]} {
3185 if {[dict exists $properties generics]} {
3186 set generics_dict [dict get $properties generics]
3189 Msg Warning "File $conf_file not found."
3191 return $generics_dict
3199 proc GetDefinesFromConf {proj_dir} {
3200 set defines_dict [dict create]
3201 set top_dir "Top/$proj_dir"
3202 set conf_file "$top_dir/hog.conf"
3204 Msg Debug "GetDefinesFromConf called with proj_dir=$proj_dir, top_dir=$top_dir"
3206 if {[
file exists $conf_file]} {
3208 if {[dict exists $properties defines]} {
3209 set defines_dict [dict get $properties defines]
3212 Msg Warning "File $conf_file not found."
3214 return $defines_dict
3227 proc GetSimSets {project_name repo_path {simsets ""} {ghdl 0} {no_conf 0}} {
3228 set simsets_dict [dict create]
3229 set list_dir "$repo_path/Top/$project_name/list"
3231 if {$simsets != ""} {
3232 foreach s $simsets {
3233 set list_file "$list_dir/$s.sim"
3234 if {[
file exists $list_file]} {
3235 lappend list_files $list_file
3236 }
elseif {$s != "sim_1"} {
3237 Msg CriticalWarning "Simulation set list file $list_file not found."
3242 set list_files [glob -nocomplain -directory $list_dir "*.sim"]
3246 set proj_dir [
file normalize $repo_path/Top/$project_name]
3247 set sim_file [
file normalize $proj_dir/sim.conf]
3249 foreach list_file $list_files {
3250 set file_name [
file tail $list_file]
3251 set simset_name [
file rootname $file_name]
3252 set fp [open $list_file r]
3253 set file_data [read $fp]
3255 set data [
split $file_data "\n"]
3257 set firstline [
lindex $data 0]
3259 if {[regexp {^ *\# *Simulator} $firstline]} {
3260 set simulator_prop [regexp -all -inline {\S+} $firstline]
3261 set simulator [
string tolower [
lindex $simulator_prop end]]
3263 Msg Warning "Simulator not set in $simset_name.sim. \
3264 The first line of $simset_name.sim should be #Simulator <SIMULATOR_NAME>,\
3265 where <SIMULATOR_NAME> can be xsim, questa, modelsim, ghdl, riviera, activehdl,\
3266 ies, or vcs, e.g. #Simulator questa.\
3267 Setting simulator by default to xsim."
3268 set simulator "xsim"
3270 if {$simulator eq "skip_simulation"} {
3271 Msg Info "Skipping simulation for $simset_name"
3274 if {($ghdl == 1 && $simulator != "ghdl") || ($ghdl == 0 && $simulator == "ghdl")} {
3278 set SIM_PROPERTIES ""
3279 if {[
file exists $sim_file] && $no_conf == 0} {
3280 set SIM_PROPERTIES [
ReadConf $sim_file]
3283 set global_sim_props [dict create]
3284 dict set global_sim_props "properties" [
DictGet $SIM_PROPERTIES "sim"]
3285 dict set global_sim_props "generics" [
DictGet $SIM_PROPERTIES "generics"]
3286 dict set global_sim_props "hog" [
DictGet $SIM_PROPERTIES "hog"]
3289 set sim_dict [dict create]
3290 dict set sim_dict "simulator" $simulator
3291 if {[dict exists $SIM_PROPERTIES $simset_name]} {
3292 dict set sim_dict "properties" [
DictGet $SIM_PROPERTIES $simset_name]
3293 dict set sim_dict "generics" [
DictGet $SIM_PROPERTIES "$simset_name:generics"]
3294 dict set sim_dict "hog" [
DictGet $SIM_PROPERTIES "$simset_name:hog"]
3295 }
elseif {$no_conf == 0} {
3297 set conf_dict [
ReadConf $list_file]
3298 set sim_dict [
MergeDict $sim_dict $conf_dict 0]
3300 set sim_dict [
MergeDict $sim_dict $global_sim_props 0]
3301 dict set simsets_dict $simset_name $sim_dict
3303 return $simsets_dict
3311 proc GetSimsetGenericsFromConf {proj_dir} {
3312 set simsets_generics_dict [dict create]
3313 set top_dir "Top/$proj_dir"
3314 set conf_file "$top_dir/sim.conf"
3317 if {[
file exists $conf_file]} {
3320 set simsets_generics_dict [dict filter $properties key *:generics]
3322 Msg Warning "File $conf_file not found."
3324 return $simsets_generics_dict
3335 proc GetGroupName {proj_dir repo_dir} {
3336 if {[regexp {^(.*)/(Top|Projects)/+(.*?)/*$} $proj_dir dummy possible_repo_dir proj_or_top dir]} {
3338 if {[
file normalize $repo_dir] eq [
file normalize $possible_repo_dir]} {
3339 set group [
file dir $dir]
3340 if {$group == "."} {
3345 Msg Warning "Project directory $proj_dir seems to be in $possible_repo_dir which is not a the main Git repository $repo_dir."
3348 Msg Warning "Could not parse project directory $proj_dir"
3361 proc GetHogDescribe {proj_dir {repo_path .}} {
3362 lassign [
GetRepoVersions $proj_dir $repo_path] global_commit global_version
3363 if {$global_commit == 0} {
3365 set new_sha "[
string toupper [
GetSHA]]"
3368 set new_sha [
string toupper $global_commit]
3388 proc GetHogFiles {args} {
3391 if {[
catch {
package require cmdline} ERROR]} {
3392 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
3399 {list_files.arg "" "The file wildcard, if not specified all Hog list files will be looked for."}
3400 {sha_mode "Forwarded to ReadListFile, see there for info."}
3401 {ext_path.arg "" "Path for the external libraries forwarded to ReadListFile."}
3402 {print_log "Forwarded to ReadListFile, see there for info."}
3404 set usage "USAGE: GetHogFiles \[options\] <list path> <repository path>"
3405 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
3409 set list_path [
lindex $args 0]
3410 set repo_path [
lindex $args 1]
3412 set list_files $options(list_files)
3413 set sha_mode $options(sha_mode)
3414 set ext_path $options(ext_path)
3415 set print_log $options(print_log)
3417 if {$sha_mode == 1} {
3418 set sha_mode_opt "-sha_mode"
3423 if {$print_log == 1} {
3424 set print_log_opt "-print_log"
3426 set print_log_opt ""
3430 if {$list_files == ""} {
3431 set list_files {.src,.con,.sim,.ext}
3433 set libraries [dict create]
3434 set properties [dict create]
3435 set list_files [glob -nocomplain -directory $list_path "*{$list_files}"]
3436 set filesets [dict create]
3438 foreach f $list_files {
3439 set ext [
file extension $f]
3440 if {$ext == ".ext"} {
3441 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $ext_path"] l p fs
3443 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $repo_path"] l p fs
3446 set properties [
MergeDict $p $properties]
3447 Msg Debug "list file $f, filesets: $fs"
3449 Msg Debug "Merged filesets $filesets"
3457 set proj_conf [
file normalize [
file join [
file dirname $list_path] hog.conf]]
3458 if {[
file exists $proj_conf]} {
3460 set already_tracked [dict create]
3461 dict for {libname libfiles} $libraries {
3462 foreach lf $libfiles {dict set already_tracked [file normalize $lf] 1}
3464 dict for {comp_name cfg_abs} $hls_configs {
3465 if {[dict exists $already_tracked $cfg_abs]} {
3466 Msg Debug "HLS component '$comp_name': cfg $cfg_abs already tracked via a .src, skipping conf-based GetHogFiles discovery."
3469 set lib_name "${comp_name}.src"
3470 dict lappend libraries $lib_name $cfg_abs
3471 dict set already_tracked $cfg_abs 1
3472 set hls_extras [ExpandHlsConfigFiles $cfg_abs]
3473 Msg Info "HLS component '$comp_name' (from hog.conf): tracking [expr {1 + [llength $hls_extras]}] file(s) via $cfg_abs"
3474 if {$print_log == 1} {
3475 Msg Status "\nhog.conf \[hls:$comp_name\] (auto-discovered)"
3476 set rel_cfg [Relative $repo_path $cfg_abs 1]
3477 if {$rel_cfg eq ""} {set rel_cfg $cfg_abs}
3478 if {[llength $hls_extras] == 0} {
3479 Msg Status "└── $rel_cfg"
3481 Msg Status "├── $rel_cfg"
3482 Msg Status " Inside [file tail $cfg_abs] (HLS auto-expand):"
3485 set n_extras [llength $hls_extras]
3487 foreach hls_extra $hls_extras {
3489 if {[dict exists $already_tracked $hls_extra]} {continue}
3490 dict lappend libraries $lib_name $hls_extra
3491 dict set already_tracked $hls_extra 1
3492 if {$print_log == 1} {
3493 if {$i == $n_extras} {set pad "└──"} else {set pad "├──"}
3494 set rel_extra [Relative [file dirname $cfg_abs] $hls_extra 1]
3495 if {$rel_extra eq ""} {set rel_extra $hls_extra}
3496 Msg Status " $pad $rel_extra"
3502 return [list $libraries $properties $filesets]
3509 proc GetIDECommand {proj_conf {custom_ver ""}} {
3510 if {$custom_ver ne ""} {
3511 set ide_name_and_ver [
string tolower "$custom_ver"]
3512 }
elseif {[
file exists $proj_conf]} {
3513 set ide_name_and_ver [
string tolower [
GetIDEFromConf $proj_conf]]
3515 Msg Error "Configuration file $proj_conf not found."
3518 set ide_name [
lindex [regexp -all -inline {\S+} $ide_name_and_ver] 0]
3520 if {$ide_name eq "vivado" || $ide_name eq "vivado_vitis_classic" || $ide_name eq "vivado_vitis_unified" || $ide_name eq "vitis_unified"} {
3521 set command "vivado"
3523 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
3524 set after_tcl_script " -tclargs "
3526 }
elseif {$ide_name eq "planahead"} {
3527 set command "planAhead"
3529 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
3530 set after_tcl_script " -tclargs "
3532 }
elseif {$ide_name eq "quartus"} {
3533 set command "quartus_sh"
3535 set before_tcl_script " -t "
3536 set after_tcl_script " "
3538 }
elseif {$ide_name eq "libero"} {
3541 set command "libero"
3542 set before_tcl_script "SCRIPT:"
3543 set after_tcl_script " SCRIPT_ARGS:\""
3545 }
elseif {$ide_name eq "diamond"} {
3546 set command "diamondc"
3547 set before_tcl_script " "
3548 set after_tcl_script " "
3550 }
elseif {$ide_name eq "vitis_classic"} {
3553 set before_tcl_script ""
3554 set after_tcl_script " "
3556 }
elseif {$ide_name eq "ghdl"} {
3558 set before_tcl_script " "
3559 set after_tcl_script " "
3562 Msg Error "IDE: $ide_name not known."
3565 return [list $command $before_tcl_script $after_tcl_script $end_marker]
3571 proc GetIDEFromConf {conf_file} {
3572 set f [open $conf_file "r"]
3575 if {[regexp -all {^\# *(\w*) *(vitis_(?:classic|unified))? *(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)?(_.*)? *$} $line dummy ide vitisflag version patch]} {
3576 if {[
info exists vitisflag] && $vitisflag != ""} {
3577 set ide "${ide}_${vitisflag}"
3580 if {[
info exists version] && $version != ""} {
3586 set ret [list $ide $ver]
3588 Msg CriticalWarning "The first line of hog.conf should be \#<IDE name> <version>, \
3589 where <IDE name>. is quartus, vivado, planahead, libero, diamond or ghdl, \
3590 and <version> the tool version, e.g. \#vivado 2020.2. Will assume vivado."
3591 set ret [list "vivado" "0.0.0"]
3598 proc GetIDEName {} {
3600 return "ISE/PlanAhead"
3618 proc GetIDEVersion {} {
3621 regexp {\d+\.\d+(\.\d+)?} [version -short] ver
3626 regexp {[\.0-9]+} $quartus(version) ver
3629 set ver [get_libero_version]
3632 regexp {\d+\.\d+(\.\d+)?} [sys_install version] ver
3635 regexp {\d+\.\d+(\.\d+)?} [version] ver
3638 set vitis_output [
exec vitis --version 2>@1]
3639 regexp {[Vv]itis\s+v?(\d+\.\d+(?:\.\d+)?)} $vitis_output -> ver
3653 proc GetLinkedFile {link_file} {
3654 if {[
file type $link_file] eq "link"} {
3655 if {[
OS] == "windows"} {
3657 lassign [
ExecuteRet realpath $link_file] ret msg
3658 lassign [
ExecuteRet cygpath -m $msg] ret2 msg2
3659 if {$ret == 0 && $ret2 == 0} {
3661 Msg Debug "Found link file $link_file on Windows, the linked file is: $real_file"
3663 Msg CriticalWarning "[
file normalize $link_file] is a soft link. \
3664 Soft link are not supported on Windows and readlink.exe or cygpath.exe did not work: readlink=$ret: $msg, cygpath=$ret2: $msg2."
3665 set real_file $link_file
3669 set linked_file [
file link $link_file]
3670 set real_file [
file normalize [
file dirname $link_file]/$linked_file]
3673 if {![
file exists $real_file]} {
3674 Msg Warning "$link_file is a broken link, because the linked file: $real_file does not exist."
3677 Msg Warning "$link file is not a soft link"
3678 set real_file $link_file
3691 proc GetMaxThreads {proj_dir} {
3693 if {[
file exists $proj_dir/hog.conf]} {
3695 if {[dict exists $properties parameters]} {
3696 set propDict [dict get $properties parameters]
3697 if {[dict exists $propDict MAX_THREADS]} {
3698 set maxThreads [dict get $propDict MAX_THREADS]
3702 Msg Warning "File $proj_dir/hog.conf not found. Max threads will be set to default value 1"
3715 proc GetModifiedFiles {{repo_path "."} {pattern "."}} {
3718 set ret [
Git "ls-files --modified $pattern"]
3727 proc GetOptions {argv parameters} {
3730 set param_list [list]
3731 set option_list [list]
3733 foreach p $parameters {
3734 lappend param_list [
lindex $p 0]
3738 while {$index < [
llength $argv]} {
3739 set arg [
lindex $argv $index]
3740 if {[
string first - $arg] == 0} {
3741 set option [
string trimleft $arg "-"]
3743 lappend option_list $arg
3744 if {[lsearch -regex $param_list "$option\[.arg]?"] >= 0} {
3745 if {[lsearch -regex $param_list "$option\[.arg]"] >= 0} {
3746 lappend option_list [
lindex $argv $index]
3751 lappend arg_list $arg
3755 Msg Debug "Argv: $argv"
3756 Msg Debug "Options: $option_list"
3757 Msg Debug "Arguments: $arg_list"
3758 return [list $option_list $arg_list]
3776 proc GetProjectFiles {{project_file ""}} {
3777 set libraries [dict create]
3778 set simlibraries [dict create]
3779 set constraints [dict create]
3780 set properties [dict create]
3781 set consets [dict create]
3782 set srcsets [dict create]
3783 set simsets [dict create]
3786 set all_filesets [get_filesets]
3787 set simulator [get_property target_simulator [current_project]]
3788 set top [get_property "top" [current_fileset]]
3790 dict lappend properties $topfile "top=$top"
3792 foreach fs $all_filesets {
3793 if {$fs == "utils_1"} {
3798 set all_files [get_files -quiet -of_objects [get_filesets $fs]]
3799 set fs_type [get_property FILESET_TYPE [get_filesets $fs]]
3801 if {$fs_type == "BlockSrcs"} {
3803 set dict_fs "sources_1"
3807 foreach f $all_files {
3814 if {[
lindex [get_property IS_GENERATED [
GetFile $f $fs]] 0] != 0} {
3819 if {[get_property FILE_TYPE [
GetFile $f $fs]] == "Configuration Files"} {
3825 if {[get_property CORE_CONTAINER [
GetFile $f $fs]] != ""} {
3826 if {[
file extension $f] == ".xcix"} {
3827 set f [get_property CORE_CONTAINER [
GetFile $f $fs]]
3835 if {[get_property SCOPED_TO_REF [
GetFile $f $fs]] != ""} {
3836 dict lappend properties $f "scoped_to_ref=[get_property SCOPED_TO_REF [
GetFile $f $fs]]"
3841 if {[get_property SCOPED_TO_CELLS [
GetFile $f $fs]] != ""} {
3842 dict lappend properties $f "scoped_to_cells=[regsub " " [get_property SCOPED_TO_CELLS [
GetFile $f $fs]] ","]"
3846 if {[
IsInList "PARENT_COMPOSITE_FILE" [list_property [
GetFile $f $fs]]]} {
3851 if {[
file tail $f] == "nocattrs.dat"} {
3856 if {[
file extension $f] != ".coe"} {
3857 set f [
file normalize $f]
3860 set type [get_property FILE_TYPE [
GetFile $f $fs]]
3862 set lib [get_property -quiet LIBRARY [
GetFile $f $fs]]
3865 Msg Debug "File $f Extension [
file extension $f] Type [
lindex $type 0]"
3867 if {[
string equal [
lindex $type 0] "VHDL"] && [
llength $type] == 1} {
3869 }
elseif {[
string equal [
lindex $type 0] "VHDL"] && [
string equal [
lindex $type 1] "2019"]} {
3875 }
elseif {[
string equal [
lindex $type 0] "Block"] && [
string equal [
lindex $type 1] "Designs"]} {
3878 }
elseif {[
string equal $type "SystemVerilog"] && [
file extension $f] != ".sv" && [
file extension $f] != ".svp"} {
3879 set prop "SystemVerilog"
3880 }
elseif {[
string equal [
lindex $type 0] "XDC"] && [
file extension $f] != ".xdc"} {
3882 }
elseif {[
string equal $type "Verilog Header"] && [
file extension $f] != ".vh" && [
file extension $f] != ".svh"} {
3883 set prop "verilog_header"
3884 }
elseif {[
string equal $type "Verilog Template"] && [
file extension $f] == ".v" && [
file extension $f] != ".sv"} {
3885 set prop "verilog_template"
3887 set type [
lindex $type 0]
3891 if {![
string equal $prop ""]} {
3892 dict lappend properties $f $prop
3895 if {[
string equal $fs_type "SimulationSrcs"]} {
3897 if {[
string equal $type "VHDL"]} {
3898 set library "${lib}.sim"
3900 set library "others.sim"
3904 dict lappend simsets $dict_fs $library
3907 dict lappend simlibraries $library $f
3908 }
elseif {[
string equal $type "VHDL"]} {
3911 dict lappend srcsets $dict_fs "${lib}.src"
3913 dict lappend libraries "${lib}.src" $f
3914 }
elseif {[
string first "IP" $type] != -1} {
3917 dict lappend srcsets $dict_fs "ips.src"
3919 dict lappend libraries "ips.src" $f
3920 Msg Debug "Appending $f to ips.src"
3921 }
elseif {[
string equal $fs_type "Constrs"]} {
3924 dict lappend consets $dict_fs "sources.con"
3926 dict lappend constraints "sources.con" $f
3930 dict lappend srcsets $dict_fs "others.src"
3932 dict lappend libraries "others.src" $f
3933 Msg Debug "Appending $f to others.src"
3936 if {[
lindex [get_property -quiet used_in_synthesis [
GetFile $f $fs]] 0] == 0} {
3937 dict lappend properties $f "nosynth"
3939 if {[
lindex [get_property -quiet used_in_implementation [
GetFile $f $fs]] 0] == 0} {
3940 dict lappend properties $f "noimpl"
3942 if {[
lindex [get_property -quiet used_in_simulation [
GetFile $f $fs]] 0] == 0} {
3943 dict lappend properties $f "nosim"
3945 if {[
lindex [get_property -quiet IS_MANAGED [
GetFile $f $fs]] 0] == 0 && [
file extension $f] != ".xcix"} {
3946 dict lappend properties $f "locked"
3952 dict lappend properties "Simulator" [get_property target_simulator [current_project]]
3955 set file [open $project_file r]
3956 set in_file_manager 0
3958 while {[
gets $file line] >= 0} {
3960 if {[regexp {^KEY ActiveRoot \"([^\"]+)\"} $line -> value]} {
3961 set top [
string range $value 0 [
expr {[string first "::" $value] - 1}]]
3965 if {[regexp {^LIST FileManager} $line]} {
3966 set in_file_manager 1
3971 if {$in_file_manager && [regexp {^ENDLIST} $line]} {
3976 if {$in_file_manager && [regexp {^VALUE \"([^\"]+)} $line -> value]} {
3979 lassign [
split $value ,] file_path file_type
3982 set library "others"
3983 while {[
gets $file line] >= 0} {
3984 if {$line == "ENDFILE"} {
3987 regexp {^LIBRARY=\"([^\"]+)} $line -> library
3988 regexp {^PARENT=\"([^\"]+)} $line -> parent_file
3990 Msg Debug "Found file ${file_path} in project.."
3991 if {$parent_file == ""} {
3992 if {$file_type == "hdl"} {
3994 if {[
IsInList "${library}.src" [
DictGet $srcsets "sources_1"]] == 0} {
3995 dict lappend srcsets "sources_1" "${library}.src"
3997 dict lappend libraries "${library}.src" $file_path
4001 if {[
GetModuleName $file_path] == [
string tolower $top] && $top != ""} {
4002 Msg Debug "Found top module $top in $file_path"
4003 dict lappend properties $file_path "top=$top"
4005 }
elseif {$file_type == "tb_hdl"} {
4007 dict lappend simsets "sim_1" "${library}.sim"
4009 dict lappend simlibraries "${library}.sim" $file_path
4010 }
elseif {$file_type == "io_pdc" || $file_type == "sdc"} {
4012 dict lappend consets "constrs_1" "sources.con"
4014 dict lappend constraints "sources.con" $file_path
4021 set fileData [read [open $project_file]]
4023 set project_path [
file dirname $project_file]
4026 regsub {<\?xml.*\?>} $fileData "" fileData
4029 regexp {<Implementation.*?>(.*)</Implementation>} $fileData -> implementationContent
4033 set sourceRegex {<Source name="([^"]*?)" type="([^"]*?)" type_short="([^"]*?)".*?>(.*?)</Source>}
4035 set optionsRegex {<Options(.*?)\/>}
4036 regexp $optionsRegex $implementationContent -> prj_options
4037 foreach option $prj_options {
4038 if {[regexp {^top=\"([^\"]+)\"} $option match result]} {
4043 while {[regexp $sourceRegex $implementationContent match name type type_short optionsContent]} {
4044 Msg Debug "Found file ${name} in project..."
4045 set file_path [
file normalize $project_path/$name]
4047 set optionsRegex {<Options(.*?)\/>}
4048 regexp $optionsRegex $optionsContent -> options
4049 set library "others"
4051 foreach option $options {
4052 if {[
string first "System Verilog" $option]} {
4055 if {[regexp {^lib=\"([^\"]+)\"} $option match1 result]} {
4060 if {[regexp {syn_sim="([^"]*?)"} $match match_sim simonly]} {
4065 if {$type_short == "VHDL" || $type_short == "Verilog" || $type_short == "IPX"} {
4066 if {$ext == ".src"} {
4067 if {[
IsInList "${library}${ext}" [
DictGet $srcsets "sources_1"]] == 0} {
4068 dict lappend srcsets "sources_1" "${library}${ext}"
4070 dict lappend libraries "${library}${ext}" $file_path
4071 }
elseif {$ext == ".sim"} {
4073 dict lappend simsets "sim_1" "${library}.sim"
4075 dict lappend simlibraries "${library}.sim" $file_path
4081 Msg Debug "Found top module $top in $file_path"
4082 dict lappend properties $file_path "top=$top"
4084 }
elseif {$type_short == "SDC"} {
4086 dict lappend consets "constrs_1" "sources.con"
4088 dict lappend constraints "sources.con" $file_path
4092 regsub -- $match $implementationContent "" implementationContent
4095 return [list $libraries $properties $simlibraries $constraints $srcsets $simsets $consets]
4102 proc GetProjectFlavour {proj_name} {
4104 set flavour [
string map {. ""} [
file extension $proj_name]]
4105 if {$flavour != ""} {
4106 if {[
string is integer $flavour]} {
4107 Msg Info "Project $proj_name has flavour = $flavour, the generic variable FLAVOUR will be set to $flavour"
4109 Msg Warning "Project name has a unexpected non numeric extension, flavour will be set to -1"
4126 proc GetProjectVersion {proj_dir repo_path {ext_path ""} {sim 0}} {
4127 if {![
file exists $proj_dir]} {
4128 Msg CriticalWarning "$proj_dir not found"
4135 lassign [
GitRet {describe --tags --abbrev=0 --match "v*"}] ret result
4137 Msg CriticalWarning "No Hog versioning tags (v*) in repo"
4144 Msg Warning "Repository is not clean"
4152 Msg Debug "Project version $v_proj, latest tag $v_last"
4154 Msg Debug "The specified project was modified since official version."
4161 Msg Info "The specified project was modified in the latest official version $ret"
4162 }
elseif {$comp == -1} {
4163 Msg Info "The specified project was modified in a past official version $ret"
4179 proc GetRepoVersions {proj_dir repo_path {ext_path ""} {sim 0}} {
4180 global NOT_CLEAN_WARNED
4181 if {[
catch {
package require cmdline} ERROR]} {
4182 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
4197 lappend SHAs [
GetSHA {Hog}]
4201 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
4202 Msg Debug "Hog submodule [
pwd] clean."
4203 lassign [
GetVer ./] hog_ver hog_hash
4205 Msg CriticalWarning "Hog submodule [
pwd] not clean, commit hash will be set to 0."
4206 set hog_hash "0000000"
4207 set hog_ver "00000000"
4213 foreach conf_file $conf_files {
4214 if {[
string match "$repo_path/*" $conf_file]} {
4215 set conf_file [
string replace $conf_file 0 [
string length $repo_path]]
4217 lappend project_files $conf_file
4220 lappend project_files Hog
4223 lassign [
GetVer [
join $conf_files]] top_ver top_hash
4224 lappend SHAs $top_hash
4225 lappend versions $top_ver
4232 lassign [
GetHogFiles -list_files "*.src" -sha_mode "$proj_dir/list/" $repo_path] src_files dummy
4233 dict for {f files} $src_files {
4234 # library names have a .src extension in values returned by GetHogFiles
4235 set name [file rootname [file tail $f]]
4236 if {[file ext $f] == ".oth"} {
4239 lassign [GetVer $files] ver hash
4240 # Msg Info "Found source list file $f, version: $ver commit SHA: $hash"
4242 lappend versions $ver
4244 lappend hashes $hash
4246 set relative_files ""
4247 foreach fil $files {
4248 if {[string match "$repo_path/*" $fil]} {
4249 set fil [string replace $fil 0 [string length $repo_path]]
4251 lappend relative_files $fil
4254 lappend project_files $f {*}$relative_files
4260 lassign [
GetHogFiles -list_files "*.con" -sha_mode "$proj_dir/list/" $repo_path] cons_files dummy
4261 dict for {f files} $cons_files {
4262 #library names have a .con extension in values returned by GetHogFiles
4263 set name [file rootname [file tail $f]]
4264 lassign [GetVer $files] ver hash
4265 #Msg Info "Found constraint list file $f, version: $ver commit SHA: $hash"
4267 Msg CriticalWarning "Constraints file $f not found in Git."
4269 lappend cons_hashes $hash
4271 lappend versions $ver
4272 set relative_files ""
4273 foreach fil $files {
4274 if {[string match "$repo_path/*" $fil]} {
4275 set fil [string replace $fil 0 [string length $repo_path]]
4277 lappend relative_files $fil
4279 lappend project_files $f {*}$relative_files
4286 lassign [
GetHogFiles -list_files "*.sim" -sha_mode "$proj_dir/list/" $repo_path] sim_files dummy
4287 dict for {f files} $sim_files {
4288 #library names have a .sim extension in values returned by GetHogFiles
4289 set name [file rootname [file tail $f]]
4290 lassign [GetVer $files] ver hash
4291 #Msg Info "Found simulation list file $f, version: $ver commit SHA: $hash"
4292 lappend sim_hashes $hash
4294 lappend versions $ver
4295 set relative_files ""
4296 foreach fil $files {
4297 if {[string match "$repo_path/*" $fil]} {
4298 set fil [string replace $fil 0 [string length $repo_path]]
4300 lappend relative_files $fil
4302 lappend project_files $f {*}$relative_files
4310 Msg CriticalWarning "No hashes found for constraints files (not in git)"
4313 set cons_hash [
string tolower [
Git "log --format=%h -1 $cons_hashes"]]
4320 set ext_files [glob -nocomplain "$proj_dir/list/*.ext"]
4323 foreach f $ext_files {
4324 set name [
file rootname [
file tail $f]]
4327 lappend ext_names $name
4328 lappend ext_hashes $hash
4331 lappend versions $ext_ver
4332 lappend project_files $f
4335 set file_data [read $fp]
4337 set data [
split $file_data "\n"]
4339 foreach line $data {
4340 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
4342 set file_and_prop [regexp -all -inline {\S+} $line]
4343 set hdlfile [
lindex $file_and_prop 0]
4344 set hdlfile $ext_path/$hdlfile
4345 if {[
file exists $hdlfile]} {
4346 set hash [
lindex $file_and_prop 1]
4347 set current_hash [
Md5Sum $hdlfile]
4348 if {[
string first $hash $current_hash] == -1} {
4349 Msg CriticalWarning "File $hdlfile has a wrong hash. Current checksum: $current_hash, expected: $hash"
4357 if {[
llength [glob -nocomplain $proj_dir/list/*.ipb]] > 0} {
4359 lassign [
GetHogFiles -list_files "*.ipb" -sha_mode "$proj_dir/list/" $repo_path] xml_files dummy
4360 set xml_source_files [dict get $xml_files "xml.ipb"]
4361 lassign [
GetVer $xml_source_files] xml_ver xml_hash
4362 lappend SHAs $xml_hash
4363 lappend versions $xml_ver
4364 set relative_files ""
4365 foreach fil $xml_source_files {
4366 if {[
string match "$repo_path/*" $fil]} {
4367 set fil [
string replace $fil 0 [
string length $repo_path]]
4369 lappend relative_files $fil
4371 lappend project_files {*}[glob $proj_dir/list/*.ipb] {*}$relative_files
4374 Msg Debug "This project does not use IPbus XMLs"
4380 if {[
llength [glob -nocomplain $proj_dir/list/*.chb]] > 0} {
4381 lassign [
GetHogFiles -list_files "*.chb" -sha_mode "$proj_dir/list/" $repo_path] cheby_files dummy
4382 if {[dict exists $cheby_files "cheby.chb"]} {
4383 set cheby_source_files [dict get $cheby_files "cheby.chb"]
4384 lassign [
GetVer $cheby_source_files] cheby_ver cheby_hash
4385 lappend SHAs $cheby_hash
4386 lappend versions $cheby_ver
4387 lappend project_files {*}[glob $proj_dir/list/*.chb] {*}$cheby_source_files
4393 Msg Info "This project does not use Cheby register maps"
4398 set user_ip_repos ""
4399 set user_ip_repo_hashes ""
4400 set user_ip_repo_vers ""
4402 if {[
file exists [
lindex $conf_files 0]]} {
4403 set PROPERTIES [
ReadConf [
lindex $conf_files 0]]
4404 if {[dict exists $PROPERTIES main]} {
4405 set main [dict get $PROPERTIES main]
4406 dict for {p v} $main {
4407 if {[string tolower $p] == "ip_repo_paths"} {
4409 if {[file isdirectory "$repo_path/$repo"]} {
4410 set repo_file_list [glob -nocomplain "$repo_path/$repo/*"]
4411 if {[llength $repo_file_list] == 0} {
4412 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
4414 lappend user_ip_repos "$repo_path/$repo"
4423 foreach repo $user_ip_repos {
4424 if {[
file isdirectory $repo]} {
4425 set repo_file_list [glob -nocomplain "$repo/*"]
4426 if {[
llength $repo_file_list] != 0} {
4427 lassign [
GetVer $repo] ver sha
4428 lappend user_ip_repo_hashes $sha
4429 lappend user_ip_repo_vers $ver
4430 lappend versions $ver
4431 lappend project_files $repo
4433 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
4436 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory does not exist."
4451 append dirty [
Git "status --untracked-files=no --porcelain" $project_files]
4455 Msg Debug "Project-relevant files are clean."
4458 if {![
info exists NOT_CLEAN_WARNED]} {
4459 Msg CriticalWarning "Project-relevant files not clean, commit hash and version will be set to 0."
4460 set NOT_CLEAN_WARNED 1
4468 while {$found == 0} {
4469 set global_commit [
Git "log --topo-order --format=%h -1 --abbrev=7 $SHAs"]
4474 if {$common_child == 0} {
4475 Msg CriticalWarning "The commit $sha is not an ancestor of the global commit $global_commit, which is OK. \
4476 But $sha and $global_commit do not have any common child, which is NOT OK. \
4477 This is probably do to a REBASE that is forbidden in Hog methodology as it changes git history. \
4478 Hog cannot guarantee the accuracy of the SHAs. \
4479 A way to fix this is to make a commit that touches all the projects in the repositories (e.g. change the Hog version), \
4480 but please do not rebase in the official branches in the future."
4482 Msg Info "The commit $sha is not an ancestor of the global commit $global_commit, adding the first common child $common_child instead..."
4483 lappend SHAs $common_child
4493 set global_commit "0000000"
4494 set global_version "00000000"
4499 set top_hash [
format %+07s $top_hash]
4500 set cons_hash [
format %+07s $cons_hash]
4501 return [list $global_commit $global_version \
4502 $hog_hash $hog_ver $top_hash $top_ver \
4503 $libs $hashes $vers $cons_ver $cons_hash \
4504 $ext_names $ext_hashes $xml_hash $xml_ver \
4505 $user_ip_repos $user_ip_repo_hashes $user_ip_repo_vers \
4506 $cheby_hash $cheby_ver]
4515 proc GetSHA {{path ""}} {
4518 lassign [
GitRet {log --format=%h --abbrev=7 -1}] status result
4520 return [
string tolower $result]
4522 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
4528 set repo_path [
lindex [
Git {rev-parse --show-toplevel}] 0]
4533 set file_in_module 0
4534 if {[
file exists .gitmodules]} {
4535 lassign [
GitRet "config --file .gitmodules --get-regexp path"] status result
4537 set submodules [
split $result "\n"]
4540 Msg Warning "Something went wrong while trying to find submodules: $result"
4543 foreach mod $submodules {
4544 set module [
lindex $mod 1]
4545 if {[
string first "$repo_path/$module" $f] == 0} {
4547 set file_in_module 1
4548 lappend paths "$module"
4553 if {$file_in_module == 0} {
4559 lassign [
GitRet {log --format=%h --abbrev=7 -1} $paths] status result
4561 return [
string tolower $result]
4563 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
4567 return [
string tolower $result]
4571 proc GetSimulators {} {
4572 set SIMULATORS [list "modelsim" "questa" "riviera" "activehdl" "ies" "vcs"]
4577 proc GetTopFile {} {
4579 set compile_order_prop [get_property source_mgmt_mode [current_project]]
4580 if {$compile_order_prop ne "All"} {
4581 Msg CriticalWarning "Compile order is not set to automatic, setting it now..."
4582 set_property source_mgmt_mode All [current_project]
4583 update_compile_order -fileset sources_1
4585 return [
lindex [get_files -quiet -compile_order sources -used_in synthesis -filter {FILE_TYPE =~ "VHDL*" || FILE_TYPE =~ "*Verilog*" }] end]
4586 }
elseif {[
IsISE]} {
4587 debug::design_graph_mgr -create [current_fileset]
4588 debug::design_graph -add_fileset [current_fileset]
4589 debug::design_graph -update_all
4590 return [
lindex [debug::design_graph -get_compile_order] end]
4592 Msg Error "GetTopFile not yet implemented for this IDE"
4597 proc GetTopModule {} {
4599 return [get_property top [current_fileset]]
4601 Msg Error "GetTopModule not yet implemented for this IDE"
4611 proc GetVer {path {force_develop 0}} {
4615 Msg CriticalWarning "Empty SHA found for ${path}. Commit to Git to resolve this warning."
4618 set p [
lindex $path 0]
4619 if {[
file isdirectory $p]} {
4622 cd [
file dirname $p]
4624 set repo_path [
Git {rev-parse --show-toplevel}]
4627 return [list [
GetVerFromSHA $SHA $repo_path $force_develop] $SHA]
4638 proc GetVerFromSHA {SHA repo_path {force_develop 0}} {
4639 global NO_HOG_VER_TAG_WARNED
4641 Msg CriticalWarning "Empty SHA found"
4644 lassign [
GitRet "tag --sort=creatordate --contain $SHA -l v*.*.* -l b*v*.*.*"] status result
4647 if {[regexp {^ *$} $result]} {
4649 lassign [
GitRet "log --oneline --pretty=\"%d\""] status2 tag_list
4652 set pattern {tag: v\d+\.\d+\.\d+}
4653 set real_tag_list {}
4654 foreach x $tag_list {
4655 set x_untrimmed [regexp -all -inline $pattern $x]
4656 regsub "tag: " $x_untrimmed "" x_trimmed
4657 set tt [
lindex $x_trimmed 0]
4658 if {![
string equal $tt ""]} {
4659 lappend real_tag_list $tt
4663 Msg Debug "Cleaned up list: $real_tag_list."
4665 set sorted_tags [lsort -decreasing -command CompareVersions $real_tag_list]
4667 Msg Debug "Sorted Tag list: $sorted_tags"
4669 set tag [
lindex $sorted_tags 0]
4672 set pattern {v\d+\.\d+\.\d+}
4673 if {![regexp $pattern $tag]} {
4674 if {![
info exists NO_HOG_VER_TAG_WARNED]} {
4675 Msg CriticalWarning "No Hog version tags found in this repository."
4676 set NO_HOG_VER_TAG_WARNED 1
4682 set repo_conf $repo_path/Top/repo.conf
4686 set hotfix_prefix "hotfix/"
4687 set minor_prefix "minor_version/"
4688 set major_prefix "major_version/"
4690 set enable_develop_branch $force_develop
4692 set branch_name [
Git {rev-parse --abbrev-ref HEAD}]
4694 if {[
file exists $repo_conf]} {
4695 set PROPERTIES [
ReadConf $repo_conf]
4697 if {[dict exists $PROPERTIES main]} {
4698 set mainDict [dict get $PROPERTIES main]
4701 if {[dict exists $mainDict ENABLE_DEVELOP_BRANCH]} {
4702 set enable_develop_branch [dict get $mainDict ENABLE_DEVELOP_BRANCH]
4708 if {[dict exists $PROPERTIES prefixes]} {
4709 set prefixDict [dict get $PROPERTIES prefixes]
4711 if {[dict exists $prefixDict HOTFIX]} {
4712 set hotfix_prefix [dict get $prefixDict HOTFIX]
4714 if {[dict exists $prefixDict MINOR_VERSION]} {
4715 set minor_prefix [dict get $prefixDict MINOR_VERSION]
4717 if {[dict exists $prefixDict MAJOR_VERSION]} {
4718 set major_prefix [dict get $prefixDict MAJOR_VERSION]
4724 if {[
string match "HEAD" $branch_name]} {
4725 Msg Debug "Detached HEAD detected - attempting to find branch name"
4730 set log_refs [
Git {show -s --pretty=%D HEAD}]
4731 set branch_list [
split $log_refs ","]
4732 Msg Debug "list of possible branch refs $log_refs"
4738 set match_prefixes [list $hotfix_prefix $minor_prefix $major_prefix]
4739 set prev_branch_name $branch_name
4741 foreach br $branch_list {
4742 foreach pr $match_prefixes {
4743 if {[
string match "$pr*" [
string trim $br]]} {
4744 set branch_name [
string trim $br]
4750 if {!$match_count == 1} {
4751 set branch_name $prev_branch_name
4752 Msg Debug "Branch name not found. Using $branch_name"
4754 Msg Debug "Branch name found: $branch_name"
4758 if {$enable_develop_branch == 1} {
4759 if {[
string match "$hotfix_prefix*" $branch_name]} {
4764 if {[
string match "$major_prefix*" $branch_name]} {
4766 set version_level major
4767 }
elseif {[
string match "$minor_prefix*" $branch_name] || ($enable_develop_branch == 1 && $is_hotfix == 0)} {
4769 set version_level minor
4772 set version_level patch
4776 Msg CriticalWarning "Tag $tag does not contain a Hog compatible version in this repository."
4779 }
elseif {$mr == 0} {
4780 switch $version_level {
4795 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."
4801 set vers [
split $result "\n"]
4802 set ver [
lindex $vers 0]
4804 if {[regexp {^v.*$} $v]} {
4812 Msg CriticalWarning "Error while trying to find tag for $SHA"
4820 set M [
format %02X $M]
4821 set m [
format %02X $m]
4822 set c [
format %04X $c]
4823 }
elseif {$M > -1} {
4825 set M [
format %02X $M]
4826 set m [
format %02X $m]
4827 set c [
format %04X $c]
4829 Msg Warning "Tag does not contain a properly formatted version: $ver"
4830 set M [
format %02X 0]
4831 set m [
format %02X 0]
4832 set c [
format %04X 0]
4845 proc Git {command {files ""}} {
4846 lassign [
GitRet $command $files] ret result
4848 Msg Error "Code $ret returned by git running: $command -- $files"
4859 proc GetModuleName {filename} {
4861 if {![
file exists $filename]} {
4862 Msg CriticalWarning "Error: File $filename does not exist."
4867 set fileId [open $filename r]
4870 set file_content [read $fileId]
4876 if {[
file extension $filename] == ".vhd" || [
file extension $filename] == ".vhdl"} {
4878 set file_content [
string tolower $file_content]
4880 set pattern {(?m)^\s*entity\s+(\S+)\s+is}
4881 }
elseif {[
file extension $filename] == ".v" || [
file extension $filename] == ".sv"} {
4883 set pattern {\n\s*module\s*(\w+)(\s*|\(|\n)}
4885 Msg Debug "File is neither VHDL nor Verilog... Returning empty string..."
4890 if {[regexp $pattern $file_content match module_name]} {
4893 Msg Debug "No module was found in $filename. Returning an empty string..."
4901 proc GetVerilogGenerics {file} {
4902 set fp [open $file r]
4908 foreach line [
split $data "\n"] {
4909 regsub "^\\s*\/\/.*" $line "" line
4910 regsub "(.*)\/\/.*" $line {\1} line
4911 if {![
string equal $line ""]} {
4912 append lines $line " "
4917 regsub -all {/\*.*\*/} $lines "" lines
4920 set punctuation [list]
4921 foreach char [list "(" ")" ";" "," " " "!" "<=" ":=" "=" "\[" "\]"] {
4922 lappend punctuation $char "\000$char\000"
4926 set tokens [
split [
string map $punctuation $lines] \000]
4928 set parameters [dict create]
4937 foreach token $tokens {
4938 set token [
string trim $token]
4939 if {![
string equal "" $token]} {
4940 if {[
string equal [
string tolower $token] "parameter"]} {
4941 set state $PARAM_NAME
4942 }
elseif {[
string equal $token ")"] || [
string equal $token ";"]} {
4944 }
elseif {$state == $PARAM_WIDTH} {
4945 if {[
string equal $token "\]"]} {
4946 set state $PARAM_NAME
4948 }
elseif {$state == $PARAM_VALUE} {
4949 if {[
string equal $token ","]} {
4950 set state $PARAM_NAME
4951 }
elseif {[
string equal $token ";"]} {
4956 }
elseif {$state == $PARAM_NAME} {
4957 if {[
string equal $token "="]} {
4958 set state $PARAM_VALUE
4959 }
elseif {[
string equal $token "\["]} {
4960 set state $PARAM_WIDTH
4961 }
elseif {[
string equal $token ","]} {
4962 set state $PARAM_NAME
4963 }
elseif {[
string equal $token ";"]} {
4965 }
elseif {[
string equal $token ")"]} {
4968 dict set parameters $token "integer"
4981 proc GetVhdlGenerics {file {entity ""}} {
4982 set fp [open $file r]
4988 foreach line [
split $data "\n"] {
4989 regsub "^\\s*--.*" $line "" line
4990 regsub "(.*)--.*" $line {\1} line
4991 if {![
string equal $line ""]} {
4992 append lines $line " "
4997 set generic_block ""
4998 set generics [dict create]
5000 if {1 == [
string equal $entity ""]} {
5001 regexp {(?i).*entity\s+([^\s]+)\s+is} $lines _ entity
5004 set generics_regexp "(?i).*entity\\s+$entity\\s+is\\s+generic\\s*\\((.*)\\)\\s*;\\s*port.*end.*$entity"
5006 if {[regexp $generics_regexp $lines _ generic_block]} {
5008 foreach line [
split $generic_block ";"] {
5010 regexp {(.*):\s*([A-Za-z0-9_]+).*} $line _ generic type
5013 set splits [
split $generic ","]
5014 foreach split $splits {
5015 dict set generics [
string trim $split] [
string trim $type]
5023 proc GHDL {command logfile} {
5024 set ret [
catch {
exec -ignorestderr ghdl {*}$command >>& $logfile} result options]
5029 return [list $ret $result]
5041 proc GitRet {command {files ""}} {
5044 set ret [
catch {
exec -ignorestderr git {*}$command} result]
5046 set ret [
catch {
exec -ignorestderr git {*}$command -- {*}$files} result]
5048 return [list $ret $result]
5056 proc GitVersion {target_version} {
5057 set ver [
split $target_version "."]
5058 set v [
Git --version]
5060 set current_ver [
split [
lindex $v 2] "."]
5061 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
5062 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
5063 return [
expr {$target <= $current}]
5076 proc RcloneFileExists {remote_file config_path} {
5077 if {[
catch {
exec rclone ls $remote_file --config $config_path 2>@1} result opt] == 0} {
5081 set rclone_ret [
lindex [dict get $opt -errorcode] end]
5082 if {$rclone_ret ne "3" && $rclone_ret ne "4"} {
5083 Msg CriticalWarning "Could not check if $remote_file is in the Rclone repository: $result"
5085 Msg Debug "$remote_file not found in the Rclone repository (rclone exit code $rclone_ret)."
5102 proc HandleIP {what_to_do xci_file ip_path repo_path on_eos on_rclone rclone_config_path {gen_dir "."} {force 0}} {
5104 if {!($what_to_do eq "push") && !($what_to_do eq "pull")} {
5105 Msg Error "You must specify push or pull as first argument."
5108 if {[
catch {
package require tar} TARPACKAGE]} {
5109 Msg CriticalWarning "Cannot find package tar. You can fix this by installing package \"tcllib\""
5116 if {!([
file exists $xci_file])} {
5117 Msg CriticalWarning "Could not find $xci_file."
5123 set xci_path [
file dirname $xci_file]
5124 set xci_name [
file tail $xci_file]
5125 set xci_ip_name [
file rootname [
file tail $xci_file]]
5126 set xci_dir_name [
file tail $xci_path]
5127 set gen_path $gen_dir
5129 set hash [
Md5Sum $xci_file]
5130 set file_name $xci_name\_$hash
5134 set scope [get_property -quiet SCOPE [get_ips $xci_ip_name]]
5136 Msg Debug "IP $xci_name is a subcore of a BD file, will not pull/push it from/to the remote directory."
5140 Msg Info "Preparing to $what_to_do IP: $xci_name ($file_name)."
5142 if {$what_to_do eq "push"} {
5145 if {$on_rclone == 1} {
5151 Msg Info "IP already in the Rclone repository, will not copy..."
5153 Msg Info "IP already in the Rclone repository, will forcefully replace..."
5158 }
elseif {$on_eos == 1} {
5159 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
5164 Msg Info "IP already in the EOS repository, will not copy..."
5166 Msg Info "IP already in the EOS repository, will forcefully replace..."
5172 if {[
file exists "$ip_path/$file_name.tar"]} {
5174 Msg Info "IP already in the local repository, will not copy..."
5176 Msg Info "IP already in the local repository, will forcefully replace..."
5185 if {$will_copy == 1} {
5187 Msg Info "Looking for generated files in $gen_path..."
5188 set ip_gen_files [glob -nocomplain $gen_path/*]
5192 if {[
llength $ip_gen_files] > 0} {
5193 Msg Info "Found some IP synthesised files matching $xci_ip_name"
5194 if {$will_remove == 1} {
5195 Msg Info "Removing old synthesised directory $ip_path/$file_name.tar..."
5196 if {$on_rclone == 1} {
5197 lassign [
ExecuteRet rclone delete $ip_path/$file_name.tar --config $rclone_config_path] ret result
5199 Msg CriticalWarning "Could not delete file from Rclone: $result"
5201 }
elseif {$on_eos == 1} {
5202 eos "rm -rf $ip_path/$file_name.tar" 5
5204 file delete -force "$ip_path/$file_name.tar"
5208 Msg Info "Creating local archive with IP generated files..."
5211 set at_least_one_long 0
5212 foreach f $ip_gen_files {
5213 set new_f "[
Relative [
file normalize $repo_path] $f]"
5214 set len [
string length $new_f]
5216 Msg Warning "One file in $xci_ip_name is too long ($len chars): $new_f"
5217 set at_least_one_long 1
5219 lappend tar_files $new_f
5222 Msg Debug "Tar files: $tar_files"
5223 if {$at_least_one_long == 1} {
5224 Msg Warning "Using regular tar, please cross your fingers..."
5226 lassign [
ExecuteRet tar -cf $file_name.tar {*}$tar_files] ret_tar result_tar
5227 if {$ret_tar != 0} {
5228 Msg CriticalWarning "Something went wrong when using regular tar. Error message: $result_tar"
5231 if {[
catch { ::tar::create $file_name.tar $tar_files} err]} {
5232 Msg Warning "Tcl built-in tar failed: $err, will try regular tar..."
5234 lassign [
ExecuteRet tar -cf $file_name.tar {*}$tar_files] ret_tar result_tar
5235 if {$ret_tar != 0} {
5236 Msg CriticalWarning "Something went wrong when using regular tar. Error message: $result_tar"
5241 Msg Info "Copying IP generated files for $xci_name..."
5242 if {$on_rclone == 1} {
5243 lassign [
ExecuteRet rclone copyto $file_name.tar $ip_path/$file_name.tar --config $rclone_config_path] ret result
5245 Msg CriticalWarning "Something went wrong when copying the IP files to Rclone. Error message: $result"
5247 }
elseif {$on_eos == 1} {
5248 lassign [
ExecuteRet xrdcp -f -s $file_name.tar $::env(EOS_MGM_URL)//$ip_path/] ret msg
5250 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
5253 Copy "$file_name.tar" "$ip_path/"
5255 Msg Info "Removing local archive"
5256 file delete $file_name.tar
5258 Msg Warning "Could not find synthesized files matching $gen_path/$file_name*"
5261 }
elseif {$what_to_do eq "pull"} {
5262 if {$on_rclone == 1} {
5265 Msg Info "Nothing for $xci_name was found in the Rclone repository, cannot pull."
5269 Msg Info "IP $xci_name found in the Rclone repository $ip_path, copying it locally to $repo_path..."
5270 lassign [
ExecuteRet rclone copyto $ip_path/$file_name.tar $file_name.tar --config $rclone_config_path] ret_copy result_copy
5271 if {$ret_copy != 0} {
5272 Msg CriticalWarning "Something went wrong when copying the IP files from Rclone. Error message: $result_copy"
5275 }
elseif {$on_eos == 1} {
5276 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
5278 Msg Info "Nothing for $xci_name was found in the EOS repository, cannot pull."
5282 set remote_tar "$::env(EOS_MGM_URL)//$ip_path/$file_name.tar"
5283 Msg Info "IP $xci_name found in the repository $remote_tar, copying it locally to $repo_path..."
5285 lassign [
ExecuteRet xrdcp -f -r -s $remote_tar $repo_path] ret msg
5287 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
5291 if {[
file exists "$ip_path/$file_name.tar"]} {
5292 Msg Info "IP $xci_name found in local repository $ip_path/$file_name.tar, copying it locally to $repo_path..."
5293 Copy $ip_path/$file_name.tar $repo_path
5295 Msg Info "Nothing for $xci_name was found in the local IP repository, cannot pull."
5301 if {[
file exists $file_name.tar]} {
5302 remove_files $xci_file
5303 Msg Info "Extracting IP files from archive to $repo_path..."
5304 ::tar::untar $file_name.tar -dir $repo_path -noperms
5305 Msg Info "Removing local archive"
5306 file delete $file_name.tar
5307 add_files -norecurse -fileset sources_1 $xci_file
5320 proc HexVersionToString {version} {
5321 scan [
string range $version 0 1] %x M
5322 scan [
string range $version 2 3] %x m
5323 scan [
string range $version 4 7] %x c
5328 proc ImportTclLib {} {
5331 if {[
info exists env(HOG_TCLLIB_PATH)]} {
5332 lappend auto_path $env(HOG_TCLLIB_PATH)
5335 puts "ERROR: To run Hog with Microsemi Libero SoC or Lattice Diamond, you need to define the HOG_TCLLIB_PATH variable."
5350 proc InitLauncher {script tcl_path parameters commands argv {custom_commands ""}} {
5351 set repo_path [
file normalize "$tcl_path/../.."]
5353 set bin_path [
file normalize "$tcl_path/../../bin"]
5354 set top_path [
file normalize "$tcl_path/../../Top"]
5356 set cmd_lines [
split $commands "\n"]
5358 set command_options [dict create]
5359 set directive_descriptions [dict create]
5360 set directive_names [dict create]
5361 set common_directive_names [dict create]
5362 set directives_with_optional_projects ""
5363 set custom_command ""
5364 set custom_command_options ""
5366 foreach l $cmd_lines {
5368 if {[regexp {\\(.*) \{\#} $l minc d]} {
5369 lappend directives_with_projects $d
5370 set current_directive $d
5373 if {[regexp {^[^#]* allow_empty_proj} $l minc dd]} {
5374 lappend directives_with_optional_projects $current_directive
5378 if {[regexp {\\(.*) \{} $l minc regular_expression]} {
5379 lappend directive_regex $regular_expression
5383 if {[regexp {\#\s*NAME(\*)?:\s*(.*)\s*} $l minc star name]} {
5384 dict set directive_names $name $regular_expression
5386 dict set common_directive_names $name $regular_expression
5389 set directive_names [
DictSort $directive_names]
5390 set common_directive_names [
DictSort $common_directive_names]
5393 if {[regexp {\#\s*DESCRIPTION:\s*(.*)\s*} $l minc x]} {
5394 dict set directive_descriptions $regular_expression $x
5398 if {[regexp {\#\s*OPTIONS:\s*(.*)\s*} $l minc x]} {
5399 dict set command_options $regular_expression [
split [regsub -all {[ \t\n]+} $x {}] ","]
5403 set short_usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nMost common directives (case insensitive):"
5405 dict for {key value} $common_directive_names {
5406 set short_usage "$short_usage\n - $key: [dict get $directive_descriptions $value]"
5409 if {[
string length $custom_commands] > 0} {
5410 Msg Debug "Found custom commands to add to short short_usage."
5411 set short_usage "$short_usage\n\nCustom commands:"
5412 dict for {key command} $custom_commands {
5413 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
5414 set short_usage "$short_usage\n - $key: [dict get $command DESCRIPTION]"
5419 set short_usage "$short_usage\n\n\
5420 To see all the available directives, run:\n./Hog/Do HELP\n\n\
5421 To list available options for the chosen directive run:\n\
5422 ./Hog/Do <directive> HELP\n
5425 set usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nDirectives (case insensitive):"
5427 dict for {key value} $directive_names {
5428 set usage "$usage\n - $key: [dict get $directive_descriptions $value]"
5432 if {[
string length $custom_commands] > 0} {
5433 Msg Debug "Found custom commands to add to short usage."
5434 set usage "$usage\n\nCustom commands:"
5435 dict for {key command} $custom_commands {
5436 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
5437 set usage "$usage\n - $key: [dict get $command DESCRIPTION]"
5442 set usage "$usage\n\nTo list available options for the chosen directive run:\n./Hog/Do <directive> HELP"
5451 Msg Debug "HogEnv.conf found"
5458 if {[
catch {
package require cmdline} ERROR]} {
5459 Msg Debug "The cmdline Tcl package was not found, sourcing it from Hog..."
5460 source $tcl_path/utils/cmdline.tcl
5463 set argv [regsub -all {(?i) HELP\y} $argv " -help"]
5468 set custom_parameters [list]
5469 dict for {key command} $custom_commands {
5470 set custom_parameters [concat $custom_parameters [dict get $command CUSTOM_OPTIONS]]
5473 lassign [
GetOptions $argv [
concat $custom_parameters $parameters]] option_list arg_list
5475 if {[
IsInList "-all" $option_list]} {
5484 set directive [
string toupper [
lindex $arg_list 0]]
5487 set argument_is_no_project 1
5489 set NO_DIRECTIVE_FOUND 0
5490 switch -regexp -- $directive "$commands"
5492 if {$NO_DIRECTIVE_FOUND == 1} {
5493 if {[
string length $custom_commands] > 0 && [dict exists $custom_commands $directive]} {
5494 set custom_command $directive
5495 set custom_command_dict [
DictGet $custom_commands $directive]
5496 set custom_command_hog_parameters [
DictGet $custom_command_dict OPTIONS]
5497 set custom_command_options [
DictGet $custom_command_dict CUSTOM_OPTIONS]
5498 set custom_command_options [
concat $custom_command_hog_parameters $custom_command_options]
5500 Msg Status "ERROR: Unknown directive $directive.\n\n"
5506 if {[
IsInList $directive $directives_with_projects 1]} {
5507 set argument_is_no_project 0
5511 if {$directive != ""} {
5512 if {[
IsInList $directive $directives_with_projects 1]} {
5513 puts "usage: ./Hog/Do \[OPTIONS\] $directive <project>\n"
5514 }
elseif {[regexp "^COMPSIM(LIB)?$" $directive]} {
5515 puts "usage: ./Hog/Do \[OPTIONS\] $directive <simulator>\n"
5517 puts "usage: ./Hog/Do \[OPTIONS\] $directive \n"
5520 dict for {dir desc} $directive_descriptions {
5521 if {[regexp $dir $directive]} {
5528 if {$custom_command ne ""} {
5529 if {[
llength $custom_command_options] > 0} {
5530 puts "Available options:"
5532 foreach custom_option $custom_command_options {
5533 set n [
llength $custom_option]
5535 lassign $custom_option opt help
5538 }
elseif {$n == 3} {
5539 lassign $custom_option opt def help
5540 puts " -$opt <argument>"
5542 puts " $help (default: $def)"
5547 Msg Warning "Custom option spec has invalid arity (expected 2 or 3): $custom_option"
5552 dict for {dir opts} $command_options {
5553 if {[regexp $dir $directive]} {
5554 puts "Available options:"
5556 foreach par $parameters {
5557 if {$opt == [lindex $par 0]} {
5558 if {[regexp {\.arg$} $opt]} {
5559 set opt_name [regsub {\.arg$} $opt ""]
5560 puts " -$opt_name <argument>"
5564 puts " [lindex $par [llength $par]-1]"
5579 set directive_options ""
5580 set directive_found 0
5581 if {$custom_command ne ""} {
5582 set directive_options $custom_command_options
5583 set directive_found 1
5585 dict for {dir opts} $command_options {
5586 if {[regexp $dir $directive]} {
5587 set directive_options $opts
5588 set directive_found 1
5595 set directive_option_names {}
5596 foreach opt $directive_options {
5597 lappend directive_option_names [regsub {\.arg$} [
lindex $opt 0] ""]
5600 if {$custom_command ne ""} {
5601 set parameters [
concat $parameters $custom_command_options]
5604 if {[
catch {
array set options [
cmdline::getoptions option_list $parameters $usage]} err]} {
5605 Msg Status "\nERROR: Syntax error, probably unknown option.\n\n USAGE: $err"
5610 if {$directive_found == 1} {
5611 set default_option_list {}
5613 foreach {key value} [
array get options] {
5614 if {[
IsInList $key $directive_option_names] || ![
info exists default_options($key)]} {
5617 if {$value ne $default_options($key)} {
5618 Msg Warning "Option -$key is not supported by directive $directive, ignoring it."
5619 set options($key) $default_options($key)
5624 if {[
llength $arg_list] <= $min_n_of_args || [
llength $arg_list] > $max_n_of_args} {
5625 Msg Status "\nERROR: Wrong number of arguments: [
llength $argv]"
5630 set project [
lindex $arg_list 1]
5631 set optional_project [
IsInList $directive $directives_with_optional_projects 1]
5633 if {$argument_is_no_project == 0} {
5635 regsub "^(\./)?Top/" $project "" project
5637 regsub "/? *\$" $project "" project
5639 if {$project eq "" && $optional_project == 1} {
5648 Msg Debug "Option list:"
5650 foreach {key value} [
array get options] {
5651 Msg Debug "$key => $value"
5658 if {$proj_conf != 0} {
5661 lassign [
GetIDECommand $proj_conf] cmd before_tcl_script after_tcl_script end_marker
5662 Msg Info "Project $project uses $cmd IDE"
5665 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
5667 if {$custom_command ne ""} {
5668 if {[dict exists $custom_commands $directive IDE]} {
5669 lassign [
GetIDECommand "" [dict get $custom_commands $directive IDE]] cmd before_tcl_script after_tcl_script end_marker
5670 Msg Info "Custom command: $custom_command uses $cmd IDE"
5671 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
5673 set command "custom_tcl"
5675 }
elseif {$argument_is_no_project == 1} {
5677 Msg Debug "$project will be used as first argument"
5678 }
elseif {$project != ""} {
5681 }
elseif {$min_n_of_args < 0} {
5686 if {$optional_project == 0} {
5698 set project_group [
file dirname $project]
5699 set project_name $project
5700 set project [
file tail $project]
5701 Msg Debug "InitLauncher: project_group=$project_group, project_name=$project_name, project=$project"
5703 return [list $directive $project $project_name $project_group $repo_path $old_path\
5704 $bin_path $top_path $usage $short_usage $command $cmd [
array get options]\
5712 proc IsCommitAncestor {ancestor commit} {
5713 lassign [
GitRet "merge-base --is-ancestor $ancestor $commit"] status result
5722 return [
expr {[info commands sys_install] != ""}]
5727 return [
expr {[info commands get_libero_version] != ""}]
5736 proc IsInList {element list {regex 0} {nocase 0}} {
5740 if {[regexp -nocase $x $element]} {
5744 if {[regexp $x $element]} {
5748 }
elseif {$regex == 0} {
5750 if {[
string tolower $x] eq [
string tolower $element]} {
5754 if {$x eq $element} {
5767 return [
expr {[string first PlanAhead [version]] == 0}]
5775 if {[
catch {
package require ::quartus::flow} result]} {
5788 proc IsRelativePath {path} {
5789 if {[
string index $path 0] == "~"} {
5792 return [
expr {[file pathtype $path] eq "relative"}]
5796 proc IsSynplify {} {
5797 return [
expr {[info commands program_version] != ""}]
5802 return [
expr {![IsQuartus] && ![IsXilinx] && ![IsVitisClassic] && ![IsVitisUnified] && ![IsLibero] && ![IsSynplify] && ![IsDiamond]}]
5810 proc IsVerilog {file} {
5811 if {[
file extension $file] == ".v" || [
file extension $file] == ".sv"} {
5823 proc IsVersal {part} {
5824 if {[get_property ARCHITECTURE [get_parts $part]] eq "versal"} {
5834 return [
expr {[string first Vivado [version]] == 0}]
5842 if {[
info commands version] != ""} {
5843 set current_version [version]
5844 if {[
string first PlanAhead $current_version] == 0 || [
string first Vivado $current_version] == 0} {
5846 }
elseif {[
string first xsct $current_version] == 0} {
5849 Msg Warning "This IDE has the version command but it is not PlanAhead or Vivado: $current_version"
5858 proc IsVitisClassic {} {
5859 if {[
info exists globalSettings::vitis_classic]} {
5860 return $globalSettings::vitis_classic
5862 return [
expr {[info commands platform] != ""}]
5866 proc IsVitisUnified {} {
5867 if {[
info exists globalSettings::vitis_unified]} {
5868 return $globalSettings::vitis_unified
5882 proc ExecuteVitisUnifiedCommand {python_script command args {error_prefix "Failed to execute command"} {output_var ""}} {
5883 set cmdlist [list vitis -s $python_script $command]
5885 lappend cmdlist $arg
5887 lappend cmdlist 2>@1
5889 Msg Debug "Executing: vitis -s $python_script $command $args"
5892 set ::env(PYTHONUNBUFFERED) "1"
5895 if {[
catch {
set pipe [open "|$cmdlist" "r"]} err]} {
5896 Msg Error "$error_prefix: Failed to open pipe: $err"
5900 fconfigure $pipe -buffering line
5901 set script_output ""
5902 set vitis_version ""
5905 set vitis_banner_patterns {
5906 "*Vitis Development Environment*"
5909 "*Copyright*Xilinx*"
5910 "*Copyright*Advanced Micro Devices*"
5911 "*All Rights Reserved*"
5915 while {[
gets $pipe line] >= 0} {
5917 if {[
string match "*Vitis v*" $line]} {
5918 if {[regexp {Vitis\s+v([0-9]+)\.([0-9]+)(?:\.[0-9]+)?} $line -> major minor]} {
5919 set year_last_two [
string range $major end-1 end]
5920 set vitis_version "$year_last_two.$minor"
5926 foreach pattern $vitis_banner_patterns {
5927 if {[
string match $pattern $line]} {
5933 if {![
string match "INFO:*" $line] && ![
string match "WARNING:*" $line] && ![
string match "ERROR:*" $line] && ![
string match "DEBUG:*" $line]} {
5934 if {$vitis_version ne ""} {
5935 set line "INFO: \[Vitis_v$vitis_version\] $line"
5937 set line "INFO: $line"
5941 append script_output "$line\n"
5948 if {[
catch {
close $pipe} err]} {
5949 if {[regexp {exit (\d+)} $err -> exit_code]} {
5950 if {$exit_code != 0} {
5951 Msg Error "$error_prefix (exit code: $exit_code)"
5952 if {$output_var ne ""} {
5953 upvar $output_var output
5954 set output $script_output
5959 Msg Error "$error_prefix: $err"
5960 if {$output_var ne ""} {
5961 upvar $output_var output
5962 set output $script_output
5969 if {$output_var ne ""} {
5970 upvar $output_var output
5971 set output $script_output
5987 proc GetVitisApps {{vitis_workspace ""} {python_script ""}} {
5990 if {[
catch {
set ws_apps [app list -dict]}]} {
6003 "Failed to get app list from Vitis Unified workspace $vitis_workspace" json_output]
6008 if {[
catch {
package require json}]} {
6009 Msg Warning "JSON package not available for parsing Vitis Unified app list"
6013 set json_output_filtered ""
6014 if {[regexp -lineanchor {\{.*\}} $json_output json_output_filtered]} {
6015 return [json::json2dict $json_output_filtered]
6017 return [json::json2dict $json_output]
6025 proc IsZynq {part} {
6026 if {[regexp {^(xc7z|xczu).*} $part]} {
6033 proc ImportGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
6034 set list_path "$repo_path/Top/$project_name/list"
6035 lassign [
GetHogFiles -list_files {.src,.ext,.sim} -ext_path $ext_path $list_path $repo_path] src_files properties filesets
6040 set properties [
DictGet $simset_dict "properties"]
6041 set options [
DictGet $properties "options"]
6044 set workdir Projects/$project_name/ghdl
6045 file delete -force $workdir
6047 set import_log "$workdir/ghdl-import-${simset_name}.log"
6048 dict for {lib sources} $src_files {
6049 set libname [file rootname $lib]
6050 foreach f $sources {
6051 if {[file extension $f] != ".vhd" && [file extension $f] != ".vhdl"} {
6052 Msg Info "File $f is not a VHDL file, copying it in workfolder..."
6053 file copy -force $f $workdir
6055 set file_path [Relative $repo_path $f]
6056 set import_log_file [open $import_log "a"]
6057 puts "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
6058 puts $import_log_file "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
6059 close $import_log_file
6060 lassign [GHDL "-i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path" $import_log] ret result
6062 Msg CriticalWarning "GHDL import failed for file $f: $result"
6070 proc LaunchGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
6073 set sim_props [
DictGet $simset_dict "properties"]
6074 set options [
DictGet $sim_props "options"]
6075 set runopts [
DictGet $sim_props "run_options"]
6077 dict for {prop_name prop_val} $sim_props {
6078 set prop_name [string toupper $prop_name]
6079 if {$prop_name == "TOP"} {
6080 set top_sim $prop_val
6083 set workdir $repo_path/Projects/$project_name/ghdl
6084 set make_log "$workdir/ghdl-make-${simset_name}.log"
6085 set run_log "$workdir/ghdl-run-${simset_name}.log"
6088 set make_log_file [open $make_log "w"]
6090 puts "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
6091 puts $make_log_file "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
6092 close $make_log_file
6094 lassign [
GHDL "-m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim" $make_log] ret result
6097 Msg Error "GHDL make failed for $top_sim: $result"
6101 set run_log_file [open $run_log "w"]
6102 puts "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
6103 puts $run_log_file "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
6106 lassign [
GHDL "-r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts" $run_log] ret result
6110 Msg Error "GHDL run failed for $top_sim: $result"
6125 proc LaunchImplementation {reset do_create run_folder project_name {repo_path .} {njobs 4} {do_bitstream 0}} {
6126 Msg Info "Starting implementation flow..."
6129 Msg Info "Resetting run before launching implementation..."
6134 if {[
file exist "$run_folder/timing_ok.txt"]} {
6135 file delete "$run_folder/timing_ok.txt"
6137 if {[
file exist "$run_folder/timing_error.txt"]} {
6138 file delete "$run_folder/timing_error.txt"
6142 source $repo_path/Hog/Tcl/integrated/pre-implementation.tcl
6145 if {$do_bitstream == 1} {
6146 launch_runs impl_1 -to_step [
BinaryStepName [get_property PART [current_project]]] -jobs $njobs -dir $run_folder
6148 launch_runs impl_1 -jobs $njobs -dir $run_folder
6153 Msg Info "running post-implementation"
6154 source $repo_path/Hog/Tcl/integrated/post-implementation.tcl
6155 if {$do_bitstream == 1} {
6156 Msg Info "running pre-bitstream"
6157 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
6158 Msg Info "running post-bitstream"
6159 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
6163 set prog [get_property PROGRESS [get_runs impl_1]]
6164 set status [get_property STATUS [get_runs impl_1]]
6165 Msg Info "Run: impl_1 progress: $prog, status : $status"
6169 set status_file [open "$run_folder/timing.txt" "w"]
6170 puts $status_file "## $project_name Timing summary"
6172 set f [open [
lindex [glob "$run_folder/impl_1/*.twr" 0]]]
6174 while {[
gets $f line] >= 0} {
6175 if {[
string match "Timing summary:" $line]} {
6176 while {[
gets $f line] >= 0} {
6177 if {[
string match "Timing errors:*" $line]} {
6178 set errs [regexp -inline -- {[0-9]+} $line]
6180 if {[
string match "*Footnotes*" $line]} {
6183 puts $status_file "$line"
6192 Msg Info "Time requirements are met"
6193 file rename -force "$run_folder/timing.txt" "$run_folder/timing_ok.txt"
6196 Msg CriticalWarning "Time requirements are NOT met"
6197 file rename -force "$run_folder/timing.txt" "$run_folder/timing_error.txt"
6203 set wns [get_property STATS.WNS [get_runs [current_run]]]
6204 set tns [get_property STATS.TNS [get_runs [current_run]]]
6205 set whs [get_property STATS.WHS [get_runs [current_run]]]
6206 set ths [get_property STATS.THS [get_runs [current_run]]]
6207 set tpws [get_property STATS.TPWS [get_runs [current_run]]]
6209 if {$wns >= 0 && $whs >= 0 && $tpws >= 0} {
6210 Msg Info "Time requirements are met"
6211 set status_file [open "$run_folder/timing_ok.txt" "w"]
6214 Msg CriticalWarning "Time requirements are NOT met"
6215 set status_file [open "$run_folder/timing_error.txt" "w"]
6219 Msg Status "*** Timing summary ***"
6220 Msg Status "WNS: $wns"
6221 Msg Status "TNS: $tns"
6222 Msg Status "WHS: $whs"
6223 Msg Status "THS: $ths"
6224 Msg Status "TPWS: $tpws"
6230 puts $status_file "## $project_name Timing summary"
6232 m add row "| **Parameter** | \"**value (ns)**\" |"
6233 m add row "| --- | --- |"
6234 m add row "| WNS: | $wns |"
6235 m add row "| TNS: | $tns |"
6236 m add row "| WHS: | $whs |"
6237 m add row "| THS: | $ths |"
6238 m add row "| TPWS: | $tpws |"
6240 puts $status_file [m format 2string]
6241 puts $status_file "\n"
6242 if {$timing_ok == 1} {
6243 puts $status_file " Time requirements are met."
6245 puts $status_file "Time requirements are **NOT** met."
6247 puts $status_file "\n\n"
6251 if {$prog ne "100%"} {
6252 Msg Error "Implementation error"
6257 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6258 Msg Info "Git describe set to $describe"
6260 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
6265 if {[
file exists $run_folder/versions.txt]} {
6266 file copy -force $run_folder/versions.txt $dst_dir
6268 Msg Warning "No versions file found in $run_folder/versions.txt"
6271 set timing_files [glob -nocomplain "$run_folder/timing_*.txt"]
6272 set timing_file [
file normalize [
lindex $timing_files 0]]
6274 if {[
file exists $timing_file]} {
6275 file copy -force $timing_file $dst_dir/
6277 Msg Warning "No timing file found, not a problem if running locally"
6281 if {[
IsVersal [get_property part [current_project]]]} {
6282 if {[get_property segmented_configuration [current_project]] == 1} {
6283 Msg Info "Versal Segmented configuration detected: exporting XSA file..."
6284 set xsa_name "$dst_dir/[
file tail $project_name]\-$describe.xsa"
6285 write_hw_platform -fixed -force -file $xsa_name
6289 set revision [get_current_revision]
6291 set ic [get_global_assignment -name INCREMENTAL_COMPILATION]
6292 if {$ic ne "" && [
string toupper $ic] ne "OFF"} {
6293 Msg Info "Incremental compilation enabled ($ic), running Partition Merge..."
6294 if {[
catch {execute_module -tool cdb -args "--merge=on"} result]} {
6295 Msg Error "Result: $result\nPartition Merge failed. See the report file.\n"
6299 if {[
catch {execute_module -tool fit} result]} {
6300 Msg Error "Result: $result\n"
6301 Msg Error "Place & Route failed. See the report file.\n"
6303 Msg Info "\nINFO: Place & Route was successful for revision $revision.\n"
6306 if {[
catch {execute_module -tool sta -args "--do_report_timing"} result]} {
6307 Msg Error "Result: $result\n"
6308 Msg Error "Time Quest failed. See the report file.\n"
6310 Msg Info "Time Quest was successfully run for revision $revision.\n"
6313 set panel "Timing Analyzer||Timing Analyzer Summary"
6314 set device [get_report_panel_data -name $panel -col 1 -row_name "Device Name"]
6315 set timing_model [get_report_panel_data -name $panel -col 1 -row_name "Timing Models"]
6316 set delay_model [get_report_panel_data -name $panel -col 1 -row_name "Delay Model"]
6318 Msg Info "*******************************************************************"
6319 Msg Info "Device: $device"
6320 Msg Info "Timing Models: $timing_model"
6321 Msg Info "Delay Model: $delay_model"
6324 Msg Info "*******************************************************************"
6327 Msg Info "Starting implementation flow..."
6328 if {[
catch {run_tool -name {PLACEROUTE}}]} {
6329 Msg Error "PLACEROUTE FAILED!"
6331 Msg Info "PLACEROUTE PASSED."
6335 Msg Info "Run VERIFYTIMING ..."
6336 if {[
catch {run_tool -name {VERIFYTIMING} -script {Hog/Tcl/integrated/libero_timing.tcl}}]} {
6337 Msg CriticalWarning "VERIFYTIMING FAILED!"
6339 Msg Info "VERIFYTIMING PASSED \n"
6345 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6346 Msg Info "Git describe set to $describe"
6348 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
6349 file mkdir $dst_dir/reports
6352 if {[
file exists $run_folder/versions.txt]} {
6353 file copy -force $run_folder/versions.txt $dst_dir
6355 Msg Warning "No versions file found in $run_folder/versions.txt"
6358 set timing_file_path [
file normalize "$repo_path/Projects/timing_libero.txt"]
6359 if {[
file exists $timing_file_path]} {
6360 file copy -force $timing_file_path $dst_dir/reports/Timing.txt
6361 set timing_file [open $timing_file_path "r"]
6362 set status_file [open "$dst_dir/timing.txt" "w"]
6363 puts $status_file "## $project_name Timing summary\n\n"
6364 puts $status_file "| | |"
6365 puts $status_file "| --- | --- |"
6366 while {[
gets $timing_file line] >= 0} {
6367 if {[
string match "SUMMARY" $line]} {
6368 while {[
gets $timing_file line] >= 0} {
6369 if {[
string match "END SUMMARY" $line]} {
6372 if {[
string first ":" $line] == -1} {
6375 set out_string "| [
string map {: | } $line] |"
6376 puts $status_file "$out_string"
6381 Msg Warning "No timing file found, not a problem if running locally"
6386 set force_rst "-forceOne"
6388 prj_run Map $force_rst
6389 prj_run PAR $force_rst
6401 proc GenerateBitstreamOnly {project_name {repo_path .}} {
6405 set project_file [
file normalize "$repo_path/Projects/$project_name/$project_name.xpr"]
6406 if {![
file exists $project_file]} {
6407 Msg Error "Project file not found: $project_file. Please create the project first."
6413 set impl_runs [get_runs -quiet impl_1]
6414 if {[
llength $impl_runs] == 0} {
6415 Msg Error "Implementation run 'impl_1' does not exist. Please run implementation first."
6421 set run_dir [get_property DIRECTORY [get_runs impl_1]]
6422 if {![
file exists $run_dir]} {
6423 Msg Error "Implementation run directory not found: $run_dir. Please check your implementation run."
6428 Msg Info "Running pre-bitstream..."
6429 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
6431 Msg Info "Writing bitstream for $project_name..."
6434 write_bitstream -force [
file normalize "$run_dir/$top_name.bit"]
6436 Msg Info "Running post-bitstream..."
6437 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
6446 proc LaunchSimulation {project_name lib_path simsets {repo_path .} {scripts_only 0} {compile_only 0}} {
6449 set project [
file tail $project_name]
6450 set main_sim_folder [
file normalize "$repo_path/Projects/$project_name/$project.sim/"]
6452 if {$simsets != ""} {
6453 dict for {simset sim_dict} $simsets {
6454 lappend simsets_todo $simset
6456 Msg Info "Will run only the following simulation's sets (if they exist): $simsets_todo"
6459 if {$scripts_only == 1} {
6460 Msg Info "Only generating simulation scripts, not running simulations..."
6463 if {$compile_only == 1} {
6464 Msg Info "Only compiling simulation libraries, not running simulations..."
6469 set sim_dic [dict create]
6471 Msg Info "Retrieving list of simulation sets..."
6472 foreach s [get_filesets] {
6474 set use_simpass_str 0
6477 set type [get_property FILESET_TYPE $s]
6478 if {$type eq "SimulationSrcs"} {
6479 if {$simsets_todo != "" && $s ni $simsets_todo} {
6480 Msg Info "Skipping $s as it was not specified with the -simset option..."
6483 set sim_dict [
DictGet $simsets $s]
6484 set simulator [
DictGet $sim_dict "simulator"]
6485 set_property "target_simulator" $simulator [current_project]
6486 set hog_sim_props [
DictGet $sim_dict "hog"]
6487 dict for {prop_name prop_val} $hog_sim_props {
6488 # If HOG_SIMPASS_STR is set, use the HOG_SIMPASS_STR string to search for in logs, after simulation is done
6489 if {[string toupper $prop_name] == "HOG_SIMPASS_STR" && $prop_val != ""} {
6490 Msg Info "Setting simulation pass string as '$prop_val'"
6491 set use_simpass_str 1
6492 set simpass_str $prop_val
6494 if {[string toupper $prop_name] == "HOG_SILENT_SIM" && $prop_val == 1} {
6495 set quiet_sim " -quiet"
6501 Msg Info "Creating simulation scripts for $s..."
6502 if {[
file exists $repo_path/Top/$project_name/pre-simulation.tcl]} {
6503 Msg Info "Running $repo_path/Top/$project_name/pre-simulation.tcl"
6504 source $repo_path/Top/$project_name/pre-simulation.tcl
6506 if {[
file exists $repo_path/Top/$project_name/pre-$s-simulation.tcl]} {
6507 Msg Info "Running $repo_path/Top/$project_name/pre-$s-simulation.tcl"
6508 source $repo_path/Top/$project_name/pre-$s-simulation.tcl
6510 current_fileset -simset $s
6511 set sim_dir $main_sim_folder/$s/behav
6512 set sim_output_logfile $sim_dir/xsim/simulate.log
6513 if {([
string tolower $simulator] eq "xsim")} {
6514 set sim_name "xsim:$s"
6516 set simulation_command "launch_simulation $quiet_sim -simset [get_filesets $s]"
6517 if {[
catch $simulation_command log]} {
6520 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
6521 lappend failed $sim_name
6526 if {$use_simpass_str == 1} {
6529 set file_desc [open $sim_output_logfile r]
6530 set log [read $file_desc]
6533 Msg Info "Searching for simulation pass string: '$simpass_str'"
6534 if {[
string first $simpass_str $log] == -1} {
6535 Msg CriticalWarning "Simulation failed for $s, error info: '$simpass_str' NOT found!"
6536 lappend failed $sim_name
6539 lappend success $sim_name
6543 lappend success $sim_name
6547 Msg Info "Simulation library path is set to $lib_path."
6549 if {!([
file exists $lib_path])} {
6550 Msg Warning "Could not find simulation library path: $lib_path, $simulator simulation will not work."
6554 if {$simlib_ok == 1} {
6555 set_property "compxlib.${simulator}_compiled_library_dir" [
file normalize $lib_path] [current_project]
6556 launch_simulation -scripts_only -simset [get_filesets $s]
6557 set top_name [get_property TOP $s]
6558 set sim_script [
file normalize $sim_dir/$simulator/]
6559 Msg Info "Adding simulation script location $sim_script for $s..."
6560 lappend sim_scripts $sim_script
6561 dict append sim_dic $sim_script $s
6563 Msg Error "Cannot run $simulator simulations without a valid library path"
6570 if {[
info exists sim_scripts] && $scripts_only == 0} {
6572 Msg Info "Generating IP simulation targets, if any..."
6574 foreach ip [get_ips] {
6575 generate_target simulation -quiet $ip
6580 Msg Info "====== Starting simulations runs ======"
6583 foreach s $sim_scripts {
6585 set cmd ./compile.sh
6586 Msg Info " ************* Compiling: $s ************* "
6588 set sim_name "comp:[dict get $sim_dic $s]"
6590 Msg CriticalWarning "Compilation failed for $s, error info: $::errorInfo"
6591 lappend failed $sim_name
6593 lappend success $sim_name
6595 Msg Info "###################### Compilation log starts ######################"
6596 Msg Info "\n\n$log\n\n"
6597 Msg Info "###################### Compilation log ends ######################"
6599 if {$compile_only == 1} {
6602 if {[
file exists "./elaborate.sh"]} {
6603 set cmd ./elaborate.sh
6604 Msg Info " ************* Elaborating: $s ************* "
6606 set sim_name "elab:[dict get $sim_dic $s]"
6608 Msg CriticalWarning "Elaboration failed for $s, error info: $::errorInfo"
6609 lappend failed $sim_name
6611 lappend success $sim_name
6613 Msg Info "###################### Elaboration log starts ######################"
6614 Msg Info "\n\n$log\n\n"
6615 Msg Info "###################### Elaboration log ends ######################"
6617 set cmd ./simulate.sh
6618 Msg Info " ************* Simulating: $s ************* "
6623 if {$use_simpass_str == 1} {
6624 if {[
string first $simpass_str $log] == -1} {
6628 Msg Debug "Simulation pass string not set, relying on simulator exit code."
6632 set sim_name "sim:[dict get $sim_dic $s]"
6634 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
6635 lappend failed $sim_name
6637 lappend success $sim_name
6639 Msg Info "###################### Simulation log starts ######################"
6640 Msg Info "\n\n$log\n\n"
6641 Msg Info "###################### Simulation log ends ######################"
6646 if {[
llength $success] > 0} {
6647 set successes [
join $success "\n"]
6648 Msg Info "The following simulation sets were successful:\n\n$successes\n\n"
6651 if {[
llength $failed] > 0} {
6652 set failures [
join $failed "\n"]
6653 Msg Error "The following simulation sets have failed:\n\n$failures\n\n"
6655 }
elseif {[
llength $success] > 0} {
6656 Msg Info "All the [
llength $success] compilations, elaborations and simulations were successful."
6659 Msg Info "Simulation done."
6661 Msg Warning "Simulation is not yet supported for [
GetIDEName]."
6668 proc LaunchRTLAnalysis {repo_path {pre_rtl_file ""} {post_rtl_file ""}} {
6670 if {[
file exists $pre_rtl_file]} {
6671 Msg Info "Found pre-rtl Tcl script $pre_rtl_file, executing it..."
6672 source $pre_rtl_file
6674 Msg Info "Starting RTL Analysis..."
6676 synth_design -rtl -name rtl_1
6677 if {[
file exists $post_rtl_file]} {
6678 Msg Info "Found post-rtl Tcl script $post_rtl_file, executing it..."
6679 source $post_rtl_file
6682 Msg Warning "RTL Analysis is not yet supported for [
GetIDEName]."
6695 proc LaunchSynthesis {reset do_create run_folder project_name {repo_path .} {ext_path ""} {njobs 4}} {
6698 Msg Info "Resetting run before launching synthesis..."
6702 source $repo_path/Hog/Tcl/integrated/pre-synthesis.tcl
6704 launch_runs synth_1 -jobs $njobs -dir $run_folder
6706 set prog [get_property PROGRESS [get_runs synth_1]]
6707 set status [get_property STATUS [get_runs synth_1]]
6708 Msg Info "Run: synth_1 progress: $prog, status : $status"
6715 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6716 Msg Info "Git describe set to $describe"
6719 set xci_file [get_property IP_FILE $ip]
6721 set xci_path [
file dirname $xci_file]
6722 set xci_ip_name [
file rootname [
file tail $xci_file]]
6723 foreach rptfile [glob -nocomplain -directory $xci_path *.rpt] {
6724 file copy $rptfile $repo_path/bin/$project_name-$describe/reports
6728 if {$prog ne "100%"} {
6729 Msg Error "Synthesis error, status is: $status"
6733 set project [
file tail [
file rootname $project_name]]
6735 Msg Info "Number of jobs set to $njobs."
6736 set_global_assignment -name NUM_PARALLEL_PROCESSORS $njobs
6740 set describe [
GetHogDescribe [
file normalize $repo_path/Top/$project_name] $repo_path]
6742 set revision [get_current_revision]
6745 set tool_and_command [
split [get_global_assignment -name PRE_FLOW_SCRIPT_FILE] ":"]
6746 set tool [
lindex $tool_and_command 0]
6747 set pre_flow_script [
lindex $tool_and_command 1]
6748 set cmd "$tool -t $pre_flow_script quartus_map $project $revision"
6754 Msg Warning "Can not execute command $cmd"
6755 Msg Warning "LOG: $log"
6757 Msg Info "Pre flow script executed!"
6758 Msg Info "Pre flow script log: \n$log"
6762 if {![is_project_open]} {
6763 Msg Info "Re-opening project file $project_name..."
6764 project_open $project -current_revision
6768 if {[
catch {execute_module -tool ipg -args "--clean"} result]} {
6769 Msg Error "Result: $result\n"
6770 Msg Error "IP Generation failed. See the report file.\n"
6772 Msg Info "IP Generation was successful for revision $revision.\n"
6776 if {[
catch {execute_module -tool map -args "--parallel"} result]} {
6777 Msg Error "Result: $result\n"
6778 Msg Error "Analysis & Synthesis failed. See the report file.\n"
6780 Msg Info "Analysis & Synthesis was successful for revision $revision.\n"
6784 defvar_set -name RWNETLIST_32_64_MIXED_FLOW -value 0
6786 Msg Info "Run SYNTHESIS..."
6787 if {[
catch {run_tool -name {SYNTHESIZE}}]} {
6788 Msg Error "SYNTHESIZE FAILED!"
6790 Msg Info "SYNTHESIZE PASSED!"
6796 set force_rst "-forceOne"
6798 prj_run Synthesis $force_rst
6799 if {[prj_syn] == "synplify"} {
6800 prj_run Translate $force_rst
6803 Msg Error "Impossible condition. You need to run this in an IDE."
6814 proc LaunchVitisBuild {project_name {repo_path .} {stage "presynth"}} {
6815 set proj_name $project_name
6816 set bin_dir [
file normalize "$repo_path/bin"]
6822 set vitis_workspace [
file normalize "$repo_path/Projects/$project_name/vitis_unified"]
6823 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"]
6824 set ws_apps [
GetVitisApps $vitis_workspace $python_script]
6828 Msg Error "Impossible condition. You need to run this in a Vitis Unified or Vitis Classic IDE."
6833 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$proj_name] $repo_path] commit version hog_hash hog_ver top_hash top_ver \
6834 libs hashes vers cons_ver cons_hash ext_names ext_hashes xml_hash xml_ver user_ip_repos user_ip_hashes user_ip_vers \
6835 cheby_hash cheby_ver
6837 if {$commit == 0} {
set commit $this_commit}
6844 foreach app_name [dict keys $ws_apps] {
6845 app config -name $app_name -set build-config Release
6849 WriteGenerics "vitisbuild" $repo_path $proj_name $date $timee $commit $version $top_hash $top_ver $hog_hash $hog_ver $cons_ver $cons_hash $libs \
6850 $vers $hashes $ext_names $ext_hashes $user_ip_repos $user_ip_vers $user_ip_hashes $flavour $xml_ver $xml_hash \
6851 $cheby_ver $cheby_hash
6854 foreach app_name [dict keys $ws_apps] {
6857 if {![
ExecuteVitisUnifiedCommand $python_script "build_app" [list $app_name $vitis_workspace] "Failed to build app $app_name"]} {
6858 Msg Error "Failed to build app $app_name"
6862 app build -name $app_name
6866 if {$stage == "presynth"} {
6867 Msg Info "Done building apps for $project_name..."
6870 if {[
info exists ::globalSettings::vitis_only_pass] && $::globalSettings::vitis_only_pass == 1} {
6871 Msg Info "Skipping bin directory creation in vitis_only mode (post-bitstream.tcl handles artifacts)."
6875 Msg Info "Evaluating Hog describe for $project_name..."
6876 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6877 Msg Info "Hog describe set to: $describe"
6878 set dst_dir [
file normalize "$bin_dir/$proj_name\-$describe"]
6879 if {![
file exists $dst_dir]} {
6880 Msg Info "Creating $dst_dir..."
6884 foreach app_name [dict keys $ws_apps] {
6886 set main_file "$repo_path/Projects/$project_name/vitis_unified/$app_name/build/$app_name.elf"
6888 set main_file "$repo_path/Projects/$project_name/vitis_classic/$app_name/Release/$app_name.elf"
6890 set dst_main [
file normalize "$dst_dir/[
file tail $proj_name]\-$app_name\-$describe.elf"]
6892 if {![
file exists $main_file]} {
6893 Msg Error "No Vitis .elf file found. Perhaps there was an issue building it?"
6897 Msg Info "Copying main binary file $main_file into $dst_main..."
6898 file copy -force $main_file $dst_main
6913 proc LaunchHlsBuild {project_name {repo_path .}} {
6914 set proj_name $project_name
6915 set bin_dir [
file normalize "$repo_path/bin"]
6919 set conf_file [
file normalize "$repo_path/Top/$project_name/hog.conf"]
6920 if {![
file exists $conf_file]} {
6921 Msg Error "Configuration file not found: $conf_file"
6924 set properties [
ReadConf $conf_file]
6925 set hls_components [dict filter $properties key {hls:*}]
6927 if {[dict size $hls_components] == 0} {
6928 Msg Info "No HLS components found for project $project_name"
6932 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/HlsCommands.py"]
6940 set ide_name [
string tolower [
lindex $ide_info 0]]
6941 if {$ide_name eq "vivado_vitis_unified"} {
6942 set is_mixed_project 1
6944 set is_mixed_project 0
6947 dict for {hls_key hls_props} $hls_components {
6948 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {
6951 set component_name [string trim $component_name]
6952 Msg Info "Building HLS component: $component_name"
6954 # Read HLS_CONFIG path from hog.conf (mandatory)
6956 if {[dict exists $hls_props hls_config]} {
6957 set hls_cfg_rel [dict get $hls_props hls_config]
6958 } elseif {[dict exists $hls_props HLS_CONFIG]} {
6959 set hls_cfg_rel [dict get $hls_props HLS_CONFIG]
6961 if {$hls_cfg_rel eq ""} {
6962 Msg Error "HLS component '$component_name' missing HLS_CONFIG in hog.conf"
6966 set cfg_file [file normalize "$repo_path/$hls_cfg_rel"]
6967 if {![file exists $cfg_file]} {
6968 Msg Error "HLS config file not found: $cfg_file (from HLS_CONFIG=$hls_cfg_rel)"
6972 set hls_work_dir [file normalize "$repo_path/Projects/$project_name/vitis_unified/$component_name"]
6973 file mkdir $hls_work_dir
6976 Msg Info "Running C synthesis for HLS component '$component_name'..."
6978 ![ExecuteVitisUnifiedCommand $python_script "synthesis" \
6979 [list $component_name $cfg_file $hls_work_dir] \
6980 "Failed to run C synthesis for $component_name"]
6982 Msg Error "C synthesis failed for HLS component '$component_name'"
6987 Msg Info "Running implementation for HLS component '$component_name'..."
6989 ![ExecuteVitisUnifiedCommand $python_script "impl" \
6990 [list $component_name $cfg_file $hls_work_dir] \
6991 "Failed to run implementation for $component_name"]
6993 Msg Error "Implementation failed for HLS component '$component_name'"
6997 # Export VHDL to source tree if VHDL_OUTPUT is set
6999 if {[dict exists $hls_props vhdl_output]} {
7000 set vhdl_output [dict get $hls_props vhdl_output]
7001 } elseif {[dict exists $hls_props VHDL_OUTPUT]} {
7002 set vhdl_output [dict get $hls_props VHDL_OUTPUT]
7004 if {$vhdl_output ne ""} {
7005 set vhdl_output_dir [file normalize "$repo_path/$vhdl_output"]
7006 Msg Info "Exporting VHDL for '$component_name' to $vhdl_output_dir..."
7008 ![ExecuteVitisUnifiedCommand $python_script "export_rtl" \
7009 [list $component_name $hls_work_dir $vhdl_output_dir vhdl] \
7010 "Failed to export VHDL for $component_name"]
7012 Msg Warning "Could not export VHDL for HLS component '$component_name'"
7016 # Export Verilog to source tree if VERILOG_OUTPUT is set
7017 set verilog_output ""
7018 if {[dict exists $hls_props verilog_output]} {
7019 set verilog_output [dict get $hls_props verilog_output]
7020 } elseif {[dict exists $hls_props VERILOG_OUTPUT]} {
7021 set verilog_output [dict get $hls_props VERILOG_OUTPUT]
7023 if {$verilog_output ne ""} {
7024 set verilog_output_dir [file normalize "$repo_path/$verilog_output"]
7025 Msg Info "Exporting Verilog for '$component_name' to $verilog_output_dir..."
7027 ![ExecuteVitisUnifiedCommand $python_script "export_rtl" \
7028 [list $component_name $hls_work_dir $verilog_output_dir verilog] \
7029 "Failed to export Verilog for $component_name"]
7031 Msg Warning "Could not export Verilog for HLS component '$component_name'"
7035 # Export IP catalog ZIP to source tree if IP_OUTPUT is set
7037 if {[dict exists $hls_props ip_output]} {
7038 set ip_output [dict get $hls_props ip_output]
7039 } elseif {[dict exists $hls_props IP_OUTPUT]} {
7040 set ip_output [dict get $hls_props IP_OUTPUT]
7042 if {$ip_output ne ""} {
7043 set ip_output_dir [file normalize "$repo_path/$ip_output"]
7044 Msg Info "Exporting IP catalog for '$component_name' to $ip_output_dir..."
7046 ![ExecuteVitisUnifiedCommand $python_script "export_ip" \
7047 [list $component_name $hls_work_dir $ip_output_dir] \
7048 "Failed to export IP for $component_name"]
7050 Msg Error "Could not export IP for HLS component '$component_name'. Make sure package.output.format=ip_catalog is set in hls_config.cfg."
7054 # Collect reports into bin/ for CI and release notes
7055 Msg Info "Evaluating Hog describe for $project_name..."
7056 set describe [GetHogDescribe [file normalize ./Top/$project_name] $repo_path]
7057 Msg Info "Hog describe set to: $describe"
7058 set dst_dir [file normalize "$bin_dir/$proj_name\-$describe"]
7059 if {![file exists $dst_dir]} {
7060 Msg Info "Creating $dst_dir..."
7064 # Mixed projects group HLS components under bin/<proj>/vitis_hls/; pure
7065 # vitis_unified projects place them directly under bin/<proj>/
7066 if {$is_mixed_project} {
7067 set hls_out_dir [file normalize "$dst_dir/vitis_hls"]
7069 set hls_out_dir $dst_dir
7072 Msg Info "Collecting HLS reports for component '$component_name'..."
7074 ![ExecuteVitisUnifiedCommand $python_script "collect_reports" \
7075 [list $component_name $hls_work_dir $hls_out_dir] \
7076 "Failed to collect HLS reports for $component_name"]
7078 Msg Warning "No reports found for HLS component '$component_name'"
7081 # Generate markdown summary files (utilization.txt, timing_ok.txt /
7082 # timing_error.txt) under <hls_out_dir>/<component>/. File names mirror the
7083 # Vivado convention so the existing CI logic (release notes assembly and
7084 # timing-failure detection) works for HLS components too
7085 Msg Info "Generating HLS release-notes summary for component '$component_name'..."
7087 ![ExecuteVitisUnifiedCommand $python_script "release_notes" \
7088 [list $component_name $hls_work_dir $hls_out_dir] \
7089 "Failed to generate HLS summary for $component_name"]
7091 Msg Warning "Could not generate HLS summary for component '$component_name'"
7094 Msg Info "HLS component '$component_name' built successfully"
7099 if {!$is_mixed_project && [
info exists dst_dir] && [
file isdirectory $dst_dir]} {
7100 set versions_file [
file normalize "$dst_dir/versions.txt"]
7101 Msg Info "Generating top-level versions.txt for pure-HLS project at $versions_file"
7104 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$project_name] $repo_path] \
7105 commit version hog_hash hog_ver top_hash top_ver \
7106 libs hashes vers cons_ver cons_hash \
7107 ext_names ext_hashes xml_hash xml_ver \
7108 user_ip_repos user_ip_hashes user_ip_vers \
7109 cheby_hash cheby_ver
7110 if {$commit == 0} {
set commit [
GetSHA]}
7114 set fh [open $versions_file "w"]
7115 puts $fh "## $proj_name Version Table\n"
7116 puts $fh "| **File set** | **Commit SHA** | **Version** |"
7117 puts $fh "| --- | --- | --- |"
7118 puts $fh "| Global | $commit | $version_str |"
7119 puts $fh "| Top Directory | $top_hash | $top_ver_str |"
7120 puts $fh "| Hog | $hog_hash | $hog_ver_str |"
7121 foreach l $libs v $vers h $hashes {
7123 puts $fh "| **Lib:** $l | $h | $v_str |"
7129 Msg Warning "Could not generate top-level versions.txt for pure-HLS project: $err"
7133 Msg Info "Done building HLS components for $project_name"
7149 proc LaunchHlsSimulation {project_name {repo_path .} {hls_simsets {}}} {
7152 set conf_file [
file normalize "$repo_path/Top/$project_name/hog.conf"]
7153 if {![
file exists $conf_file]} {
7154 Msg Error "Configuration file not found: $conf_file"
7157 set properties [
ReadConf $conf_file]
7158 set hls_components [dict filter $properties key {hls:*}]
7160 if {[dict size $hls_components] == 0} {
7161 Msg Info "No HLS components found for project $project_name"
7167 set targeted_mode [
expr {[llength $hls_simsets] > 0}]
7168 set targets [dict create]
7169 foreach entry $hls_simsets {
7170 if {[regexp {^(csim|cosim):(.+)$} $entry -> sim_type comp_name]} {
7171 dict lappend targets $comp_name $sim_type
7173 Msg Error "Invalid HLS simset format '$entry'. Expected 'csim:<component>' or 'cosim:<component>'."
7174 Msg Info "Available HLS components in hog.conf:"
7175 dict for {hls_key hls_props} $hls_components {
7176 if {[regexp {^hls:(.+)$} $hls_key -> cname]} {
7177 Msg Info " - csim:[string trim $cname]"
7178 Msg Info " - cosim:[string trim $cname]"
7185 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/HlsCommands.py"]
7187 dict for {hls_key hls_props} $hls_components {
7188 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {
7191 set component_name [string trim $component_name]
7193 # In targeted mode, skip components not in the request
7194 if {$targeted_mode && ![dict exists $targets $component_name]} {
7198 # Read HLS_CONFIG path from hog.conf (mandatory)
7200 if {[dict exists $hls_props hls_config]} {
7201 set hls_cfg_rel [dict get $hls_props hls_config]
7202 } elseif {[dict exists $hls_props HLS_CONFIG]} {
7203 set hls_cfg_rel [dict get $hls_props HLS_CONFIG]
7205 if {$hls_cfg_rel eq ""} {
7206 Msg Error "HLS component '$component_name' missing HLS_CONFIG in hog.conf"
7210 set cfg_file [file normalize "$repo_path/$hls_cfg_rel"]
7211 if {![file exists $cfg_file]} {
7212 Msg Error "HLS config file not found: $cfg_file (from HLS_CONFIG=$hls_cfg_rel)"
7216 set hls_work_dir [file normalize "$repo_path/Projects/$project_name/vitis_unified/$component_name"]
7217 file mkdir $hls_work_dir
7219 # Determine which simulations to run
7220 if {$targeted_mode} {
7221 # User explicitly requested these sim types via -simset
7222 set sim_types [dict get $targets $component_name]
7223 set run_csim [expr {"csim" in $sim_types}]
7224 set run_cosim [expr {"cosim" in $sim_types}]
7226 # Validate: check if the requested sim is enabled in hog.conf
7227 foreach st $sim_types {
7229 if {[dict exists $hls_props $st]} {
7230 set flag_val [dict get $hls_props $st]
7231 } elseif {[dict exists $hls_props [string toupper $st]]} {
7232 set flag_val [dict get $hls_props [string toupper $st]]
7234 set is_enabled [expr {[string tolower $flag_val] eq "true" || $flag_val eq "1"}]
7236 set upper_st [string toupper $st]
7237 Msg Warning "HLS simulation '$st' requested for '$component_name' but\
7238 $upper_st is not enabled in \[hls:$component_name\] section of\
7239 hog.conf. Running it anyway."
7240 Msg Info "To enable it permanently, add '$upper_st=true' under\
7241 \[hls:$component_name\] in Top/$project_name/hog.conf"
7245 # Default mode: read flags from hog.conf
7248 foreach {key_lower key_upper} {csim CSIM cosim COSIM} {
7250 if {[dict exists $hls_props $key_lower]} {
7251 set val [dict get $hls_props $key_lower]
7252 } elseif {[dict exists $hls_props $key_upper]} {
7253 set val [dict get $hls_props $key_upper]
7255 if {[string tolower $val] eq "true" || $val eq "1"} {
7256 set run_$key_lower 1
7260 if {!$run_csim && !$run_cosim} {
7261 Msg Info "No HLS simulations enabled for '$component_name' (set CSIM=true and/or COSIM=true in \[hls:$component_name\] in hog.conf)"
7266 Msg Info "Running HLS simulations for component: $component_name"
7270 Msg Info "Running C simulation for HLS component '$component_name'..."
7272 ![ExecuteVitisUnifiedCommand $python_script "csim" \
7273 [list $component_name $cfg_file $hls_work_dir] \
7274 "Failed to run C simulation for $component_name"]
7276 Msg Error "C simulation failed for HLS component '$component_name'"
7281 # COSIM (auto-run synthesis if needed)
7283 set syn_done_marker [file normalize "$hls_work_dir/hls/syn"]
7284 if {![file exists $syn_done_marker]} {
7285 Msg Info "C synthesis output not found for '$component_name', running synthesis automatically before co-simulation..."
7287 ![ExecuteVitisUnifiedCommand $python_script "synthesis" \
7288 [list $component_name $cfg_file $hls_work_dir] \
7289 "Failed to run C synthesis for $component_name"]
7291 Msg Error "C synthesis failed for HLS component '$component_name', cannot proceed with co-simulation"
7296 Msg Info "Running C/RTL co-simulation for HLS component '$component_name'..."
7298 ![ExecuteVitisUnifiedCommand $python_script "cosim" \
7299 [list $component_name $cfg_file $hls_work_dir] \
7300 "Failed to run co-simulation for $component_name"]
7302 Msg Error "C/RTL co-simulation failed for HLS component '$component_name'"
7307 Msg Info "HLS simulations completed for '$component_name'"
7310 Msg Info "Done with HLS simulations for $project_name"
7318 proc GetProcFromProps {repo_path props platform} {
7319 if {[dict exists $props "platform:$platform" "BIF"]} {
7320 set bif_file [dict get $props "platform:$platform" "BIF"]
7322 set bif_file "$repo_path/$bif_file"
7326 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
7336 proc GetBifFromProps {repo_path props platform} {
7337 if {[dict exists $props "platform:$platform" "BIF"]} {
7338 set bif_file [dict get $props "platform:$platform" "BIF"]
7340 set bif_file "$repo_path/$bif_file"
7344 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
7353 proc GetPartFromProps {props} {
7354 if {[dict exists $props "main" "PART"]} {
7355 return [
string tolower [dict get $props "main" "PART"]]
7357 Msg Error "Part number not found in properties"
7366 proc GetArchFromPart {part} {
7367 set part [
string tolower $part]
7369 if {[
string match "xczu*" $part] || [
string match "xqzu*" $part]} {
7371 }
elseif {[
string match "xc7z*" $part] || [
string match "xq7z*" $part]} {
7374 [
string match "xcve*" $part] || [
string match "xcvc*" $part] ||
7375 [
string match "xcvp*" $part] || [
string match "xcvm*" $part] ||
7376 [
string match "xqve*" $part] || [
string match "xck26*" $part]
7379 }
elseif {[
string match "xc*" $part] || [
string match "xq*" $part]} {
7383 Msg CriticalWarning "Unknown part number: $part"
7392 proc GetAppsFromProps {props {list_names 0}} {
7393 set prop_apps [dict filter $props key {app:*}]
7394 set apps [dict create]
7395 set app_names [list]
7397 dict for {app_key app_value} $prop_apps {
7398 if {[regexp {^app:(.+)$} $app_key -> app_name]} {
7399 set app_name [string trim [string tolower $app_name]]
7400 # Convert only the keys of the inner dictionary to lowercase
7401 set app_value_lower [dict create]
7402 dict for {key value} $app_value {
7403 dict set app_value_lower [string tolower $key] $value
7405 dict set apps $app_name $app_value_lower
7406 lappend app_names $app_name
7409 if {$list_names eq 1} {
7420 proc GetPlatformsFromProps {props {list_names 0} {lower_case 0}} {
7421 set platforms [dict create]
7422 set platform_names [list]
7423 set prop_platforms [dict filter $props key {platform:*}]
7425 dict for {platform_key platform_value} $prop_platforms {
7426 if {[regexp {^platform:(.+)$} $platform_key -> platform_name]} {
7427 if {$lower_case == 1} {
7428 set platform_name [string trim [string tolower $platform_name]]
7430 set platform_name [string trim $platform_name]
7432 dict set platforms $platform_name $platform_value
7433 lappend platform_names $platform_name
7436 if {$list_names eq 1} {
7437 return $platform_names
7452 proc GenerateBootArtifacts {properties repo_path proj_dir bin_dir proj_name describe bitfile mmi_file} {
7453 set elf_list [glob -nocomplain "$bin_dir/*.elf"]
7457 if {[
llength $elf_list] == 0} {
7458 Msg Warning "No ELF files found in $bin_dir, skipping generation of boot artifacts"
7462 if {![
file exists $bitfile]} {
7463 Msg Warning "Bitfile $bitfile does not exist, skipping generation of boot artifacts"
7467 Msg Info "Generating boot artifacts for $proj_name..."
7468 Msg Info "Found apps: $apps"
7469 Msg Info "Found platforms: $platforms"
7473 foreach elf_file $elf_list {
7474 set elf_name [
file rootname [
file tail $elf_file]]
7475 Msg Info "Found elf name: $elf_name"
7476 Msg Info "Removing $describe from elf"
7479 if {[regexp "^(.+)-(.+)-$describe\$" $elf_name -> project_name elf_app]} {
7480 set elf_app [
string trim [
string tolower $elf_app]]
7481 Msg Info "Found elf_app: $elf_app"
7483 Msg Error "Could not extract app name from elf file: $elf_name"
7486 Msg Info "Removed project name ($project_name) and $describe from elf"
7488 set app_conf [dict get $apps $elf_app]
7489 set plat [dict get $app_conf "platform"]
7490 set app_proc [dict get $app_conf "proc"]
7493 if {[regexp -nocase {microblaze|risc} $app_proc]} {
7494 Msg Info "Detected soft processor ($app_proc) for $elf_app, updating bitstream memory with ELF file..."
7500 if {$proc_cell eq ""} {
7501 set proc_map_file [
file normalize "$proj_dir/vitis_classic/${plat}.PROC_MAP"]
7503 if {[dict exists $proc_map $app_proc]} {
7504 set proc_cell [dict get $proc_map $app_proc]
7505 Msg Info "Processor $app_proc not found in $mmi_file, using $proc_map_file instead."
7507 Msg Error "Could not determine the instance path of processor '$app_proc' for $elf_app, \
7508 neither from $mmi_file nor from $proc_map_file. \
7509 Check that proc= in the \[app:$elf_app\] section of hog.conf matches the processor instance name."
7513 Msg Info "Updating memory at processor cell: $proc_cell"
7515 set update_mem_cmd "updatemem -force -meminfo $mmi_file -data $elf_file -bit $bitfile -proc $proc_cell -out $bitfile"
7516 set ret [
catch {
exec -ignorestderr {*}$update_mem_cmd >@ stdout} result]
7518 Msg Error "Error updating memory for $elf_app: $result"
7520 Msg Info "Done updating memory for $elf_app"
7522 Msg Info "Detected hard processor ($app_proc) for $elf_app. Make sure the .elf file is defined in the platform ($plat)\
7523 .bif file to be included in the bootable binary image (.bin) generation."
7529 foreach plat $platforms {
7531 if {$bif_file != ""} {
7532 Msg Info "Generating bootable binary image (.bin) for $plat"
7534 Msg Info "Architecture: $arch"
7535 Msg Info "BIF file: $bif_file"
7536 if {$arch eq "unknown"} {
7537 Msg CriticalWarning "Skipping bootable image (.bin) generation for $plat: \
7538 could not determine bootgen architecture from the FPGA part."
7541 set bootgen_cmd "bootgen -arch $arch -image $bif_file -o i $bin_dir/$proj_name-$plat-$describe.bin -w on"
7542 set ret [
catch {
exec -ignorestderr {*}$bootgen_cmd >@ stdout} result]
7544 Msg Error "Error generating bootable binary image (.bin) for $plat: $result"
7546 Msg Info "Done generating bootable binary image (.bin) for $plat"
7558 proc GenerateStandaloneXciTargets {} {
7563 foreach xci [get_files -quiet *.xci] {
7564 set xci_file [get_files -quiet $xci]
7565 if {$xci_file eq ""} {
7570 catch {
set parent [get_property -quiet PARENT_COMPOSITE_FILE $xci_file]}
7571 if {$parent ne ""} {
7572 Msg Debug "Skipping [
file tail $xci], it is generated by its parent [
file tail $parent]"
7576 Msg Info "Generating targets for standalone IP [
file tail $xci]..."
7577 if {[
catch {generate_target all $xci_file} err]} {
7578 Msg CriticalWarning "Failed to generate targets for $xci: $err"
7592 proc GetProcInstPathFromMmi {mmi_file app_proc} {
7593 if {![
file exists $mmi_file]} {
7594 Msg Debug "MMI file not found: $mmi_file"
7598 set f [open $mmi_file "r"]
7599 set mmi_content [read $f]
7602 set inst_paths [list]
7603 foreach {match inst_path} [regexp -all -inline {<Processor[^>]*InstPath\s*=\s*"([^"]+)"} $mmi_content] {
7604 if {[lsearch -exact $inst_paths $inst_path] < 0} {
7605 lappend inst_paths $inst_path
7608 Msg Debug "Processor instance paths found in $mmi_file: $inst_paths"
7610 foreach inst_path $inst_paths {
7611 if {[
string equal -nocase [
file tail $inst_path] $app_proc]} {
7617 if {[
llength $inst_paths] == 1} {
7618 set inst_path [
lindex $inst_paths 0]
7619 Msg Info "Processor $app_proc does not match [
file tail $inst_path], \
7620 using the only processor found in the MMI file: $inst_path"
7631 proc ReadProcMap {proc_map_file} {
7632 set proc_map [dict create]
7633 if {[
file exists $proc_map_file]} {
7634 set f [open $proc_map_file "r"]
7635 while {[
gets $f line] >= 0} {
7636 Msg Debug "Line: $line"
7637 if {[regexp {^(\S+)\s+(.+)$} $line -> key value]} {
7638 Msg Debug "Found key: $key, value: $value in proc map file"
7639 dict set proc_map $key $value
7653 proc ListProjects {{repo_path .} {print 1} {ret_conf 0} {silent 0}} {
7654 set top_path [
file normalize $repo_path/Top]
7655 set confs [
findFiles [
file normalize $top_path] hog.conf]
7657 set confs [lsort $confs]
7661 set p [
Relative $top_path [
file dirname $c]]
7664 if {$description eq "test"} {
7665 set description " - Test project"
7666 }
elseif {$description ne ""} {
7667 set description " - $description"
7670 if {$print == 1 || $description ne " - Test project"} {
7672 set g [
file dirname $p]
7675 if {$silent == 0} {
Msg Status ""}
7683 if {$ret_conf == 0} {
7695 proc Md5Sum {file_name} {
7696 if {!([
file exists $file_name])} {
7697 Msg Warning "Could not find $file_name."
7700 if {[
catch {
package require md5 2.0.7} result]} {
7701 Msg Warning "Tcl package md5 version 2.0.7 not found ($result), will use command line..."
7702 set hash [
lindex [
Execute md5sum $file_name] 0]
7704 set file_hash [
string tolower [md5::md5 -hex -file $file_name]]
7718 proc MergeDict {dict0 dict1 {remove_duplicates 1}} {
7719 set outdict [dict merge $dict1 $dict0]
7720 foreach key [dict keys $dict1] {
7721 if {[dict exists $dict0 $key]} {
7722 set temp_list [dict get $dict1 $key]
7723 foreach item $temp_list {
7725 if {[
IsInList $item [
DictGet $outdict $key]] == 0 || $remove_duplicates == 0} {
7727 dict lappend outdict $key $item
7739 proc MoveElementToEnd {inputList element} {
7740 set index [lsearch $inputList $element]
7742 set inputList [
lreplace $inputList $index $index]
7743 lappend inputList $element
7752 proc OpenProject {project_file repo_path} {
7754 open_project $project_file
7756 set project_folder [
file dirname $project_file]
7757 set project [
file tail [
file rootname $project_file]]
7758 if {[
file exists $project_folder]} {
7760 if {![is_project_open]} {
7761 Msg Info "Opening existing project file $project_file..."
7762 project_open $project -current_revision
7765 Msg Error "Project directory not found for $project_file."
7769 Msg Info "Opening existing project file $project_file..."
7771 open_project -file $project_file -do_backup_on_convert 1 -backup_file {./Projects/$project_file.zip}
7773 Msg Info "Opening existing project file $project_file..."
7774 prj_project open $project_file
7776 Msg Error "This IDE is currently not supported by Hog. Exiting!"
7783 return $tcl_platform(platform)
7793 proc ParseJSON {JSON_FILE JSON_KEY} {
7794 set result [
catch {
package require Tcl 8.4} TclFound]
7795 if {"$result" != "0"} {
7796 Msg CriticalWarning "Cannot find Tcl package version equal or higher than 8.4.\n $TclFound\n Exiting"
7800 set result [
catch {
package require json} JsonFound]
7801 if {"$result" != "0"} {
7802 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
7805 set JsonDict [json::json2dict $JSON_FILE]
7806 set result [
catch {dict get $JsonDict $JSON_KEY} RETURNVALUE]
7807 if {"$result" != "0"} {
7808 Msg CriticalWarning "Cannot find $JSON_KEY in $JSON_FILE\n Exiting"
7821 proc ProjectExists {project {repo_path .}} {
7822 set index [lsearch -exact [
ListProjects $repo_path 0] $project]
7828 Msg Warning "Project not found. Available projects in $repo_path are"
7831 Msg Error "Project $project not found in repository $repo_path"
7842 proc ReadConf {file_name} {
7843 if {[
catch {
package require inifile 0.2.3} ERROR]} {
7844 Msg Error "Could not find inifile package version 0.2.3 or higher.\n
7845 To use ghdl, libero or diamond with Hog, you need to install the tcllib package\n
7846 You can install it with 'sudo apt install tcllib' on Debian/Ubuntu or 'sudo dnf install tcllib' on Fedora/RedHat/CentOs."
7850 ::ini::commentchar "#"
7851 set f [::ini::open $file_name]
7852 set properties [dict create]
7853 foreach sec [::ini::sections $f] {
7855 if {$new_sec == "files"} {
7858 set key_pairs [::ini::get $f $sec]
7860 regsub -all {\{\"} $key_pairs "\{" key_pairs
7861 regsub -all {\"\}} $key_pairs "\}" key_pairs
7863 dict set properties $new_sec [dict create {*}$key_pairs]
7876 proc ReadExtraFileList {extra_file_name} {
7877 set extra_file_dict [dict create]
7878 if {[
file exists $extra_file_name]} {
7879 set file [open $extra_file_name "r"]
7880 set file_data [read $file]
7883 set data [
split $file_data "\n"]
7884 foreach line $data {
7885 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
7886 set ip_and_md5 [regexp -all -inline {\S+} $line]
7887 dict lappend extra_file_dict "[
lindex $ip_and_md5 0]" "[
lindex $ip_and_md5 1]"
7891 return $extra_file_dict
7902 proc ExpandHlsConfigFiles {cfg_file} {
7904 if {![
file exists $cfg_file] || ![
file isfile $cfg_file]} {
7907 set cfg_dir [
file normalize [
file dirname $cfg_file]]
7909 if {[
catch {
set fp [open $cfg_file r]} err]} {
7910 Msg Warning "ExpandHlsConfigFiles: cannot open $cfg_file: $err"
7913 set lines [
split [read $fp] "\n"]
7916 foreach line $lines {
7917 if {[regexp {^[\t\s]*$} $line]} {continue}
7918 if {[regexp {^[\t\s]*\#} $line]} {continue}
7919 if {[regexp {^\s*\[.*\]\s*$} $line]} {continue}
7920 if {![regexp {^\s*[^=\s]+\s*=\s*(.+?)\s*$} $line -> value]} {continue}
7922 foreach tok [regexp -all -inline {\S+} $value] {
7923 if {[
file pathtype $tok] eq "absolute"} {
7924 set candidate [
file normalize $tok]
7926 set candidate [
file normalize [
file join $cfg_dir $tok]]
7928 if {[
file isfile $candidate]} {
7929 lappend out $candidate
7930 }
elseif {[
file isdirectory $candidate]} {
7931 set stack [list $candidate]
7932 while {[
llength $stack] > 0} {
7933 set d [
lindex $stack 0]
7934 set stack [
lrange $stack 1 end]
7935 foreach f [glob -nocomplain -directory $d -types f *] {
7936 lappend out [
file normalize $f]
7938 foreach sub [glob -nocomplain -directory $d -types d *] {
7945 return [lsort -unique $out]
7955 proc GetHlsConfigsFromProjConf {conf_file repo_path} {
7956 set out [dict create]
7957 if {![
file exists $conf_file]} {
return $out}
7958 if {[
catch {
set properties [
ReadConf $conf_file]} err]} {
7959 Msg Debug "GetHlsConfigsFromProjConf: cannot parse $conf_file: $err"
7962 set hls_components [dict filter $properties key {hls:*}]
7963 dict for {hls_key hls_props} $hls_components {
7964 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {continue}
7965 set component_name [string trim $component_name]
7967 if {[dict exists $hls_props hls_config]} {
7968 set cfg_rel [dict get $hls_props hls_config]
7969 } elseif {[dict exists $hls_props HLS_CONFIG]} {
7970 set cfg_rel [dict get $hls_props HLS_CONFIG]
7972 if {$cfg_rel eq ""} {continue}
7973 set cfg_abs [file normalize "$repo_path/$cfg_rel"]
7974 if {![file exists $cfg_abs]} {
7975 Msg CriticalWarning "HLS component '$component_name': HLS_CONFIG=$cfg_rel does not exist on disk."
7978 dict set out $component_name $cfg_abs
8000 proc ReadListFile {args} {
8003 if {[
catch {
package require cmdline} ERROR]} {
8004 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
8010 {lib.arg "" "The name of the library files will be added to, if not given will be extracted from the file name."}
8011 {fileset.arg "" "The name of the library, from the main list file"}
8012 {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."}
8013 {print_log "If set, will use PrintFileTree for the VIEW directive"}
8014 {indent.arg "" "Used to indent files with the VIEW directive"}
8017 set usage "USAGE: ReadListFile \[options\] <list file> <path>"
8018 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
8024 set list_file [
lindex $args 0]
8025 set path [
lindex $args 1]
8026 set sha_mode $options(sha_mode)
8027 set lib $options(lib)
8028 set fileset $options(fileset)
8029 set print_log $options(print_log)
8030 set indent $options(indent)
8032 if {$sha_mode == 1} {
8033 set sha_mode_opt "-sha_mode"
8038 if {$print_log == 1} {
8039 set print_log_opt "-print_log"
8041 set print_log_opt ""
8046 set lib [
file rootname [
file tail $list_file]]
8048 set fp [open $list_file r]
8049 set file_data [read $fp]
8051 set list_file_ext [
file extension $list_file]
8052 switch $list_file_ext {
8054 if {$fileset eq ""} {
8060 set fileset "constrs_1"
8063 set fileset "sources_1"
8067 set libraries [dict create]
8068 set filesets [dict create]
8069 set properties [dict create]
8071 set data [
split $file_data "\n"]
8073 set n [
llength $data]
8075 if {$print_log == 1} {
8076 if {$indent eq ""} {
8077 set list_file_rel [
file tail $list_file]
8078 Msg Status "\n$list_file_rel"
8082 Msg Debug "$n lines read from $list_file."
8085 foreach line $data {
8087 if {![regexp {^[\t\s]*$} $line] & ![regexp {^[\t\s]*\#} $line]} {
8088 set file_and_prop [regexp -all -inline {\S+} $line]
8089 set srcfile [
lindex $file_and_prop 0]
8090 set srcfile "$path/$srcfile"
8092 set srcfiles [glob -nocomplain $srcfile]
8095 if {$srcfiles != $srcfile && ![
string equal $srcfiles ""]} {
8096 Msg Debug "Wildcard source expanded from $srcfile to $srcfiles"
8098 if {![
file exists $srcfile]} {
8099 if {$print_log == 0} {
8100 Msg CriticalWarning "File: $srcfile (from list file: $list_file) does not exist."
8106 foreach vhdlfile $srcfiles {
8107 if {[
file exists $vhdlfile]} {
8108 set vhdlfile [
file normalize $vhdlfile]
8109 set extension [
file extension $vhdlfile]
8111 set prop [
lrange $file_and_prop 1 end]
8114 set library [
lindex [regexp -inline {\ylib\s*=\s*(.+?)\y.*} $prop] 1]
8115 if {$library == ""} {
8119 if {$extension == $list_file_ext} {
8122 set ref_path [
lindex [regexp -inline {\ypath\s*=\s*(\S+).*} $prop] 1]
8123 if {$ref_path eq ""} {
8126 set ref_path [
file normalize $path/$ref_path]
8128 Msg Debug "List file $vhdlfile found in list file, recursively opening it using path \"$ref_path\"..."
8129 if {$print_log == 1} {
8130 if {[
file normalize $last_printed] ne [
file normalize $vhdlfile]} {
8131 Msg Status "$indent Inside [
file tail $vhdlfile]:"
8135 lassign [
ReadListFile {*}"-indent \" $indent\" -lib $library -fileset $fileset $sha_mode_opt $print_log_opt $vhdlfile $ref_path"] l p fs
8137 set properties [
MergeDict $p $properties]
8139 }
elseif {[lsearch {.src .sim .con ReadExtraFileList} $extension] >= 0} {
8141 Msg Error "$vhdlfile cannot be included into $list_file, $extension files must be included into $extension files."
8144 regsub -all " *= *" $prop "=" prop
8148 if {[
string first "lib=" $p] == -1} {
8150 set pos [
string first "=" $p]
8154 set prop_name [
string range $p 0 [
expr {$pos - 1}]]
8157 || [
string first "top" $p] == 0
8158 || $list_file_ext eq ".ipb"
8159 || $list_file_ext eq ".chb"} {
8160 if {$list_file_ext eq ".ipb"} {
8161 dict lappend properties $vhdlfile $path/$p
8163 dict lappend properties $vhdlfile $p
8165 Msg Debug "Adding property $p to $vhdlfile..."
8166 }
elseif {$list_file_ext != ".ipb" && $list_file_ext != ".chb"} {
8167 Msg Warning "Setting Property $p is not supported for file $vhdlfile or it is already its default. \
8172 if {[lsearch {.xci .ip .bd .xcix} $extension] >= 0} {
8174 set lib_name "ips.src"
8175 }
elseif {[
IsInList $extension {.vhd .vhdl}] || $list_file_ext == ".sim"} {
8177 if {![
IsInList $extension {.vhd .vhdl}]} {
8178 set lib_name "others.sim"
8180 set lib_name "$library$list_file_ext"
8182 }
elseif {$list_file_ext == ".con"} {
8183 set lib_name "sources.con"
8184 }
elseif {$list_file_ext == ".ipb"} {
8185 set lib_name "xml.ipb"
8186 }
elseif {$list_file_ext == ".chb"} {
8187 set lib_name "cheby.chb"
8188 }
elseif {[
IsInList $list_file_ext {.src}] && [
IsInList $extension {.c .cpp .h .hpp}]} {
8190 set lib_name "$library$list_file_ext"
8193 set lib_name "others.src"
8196 Msg Debug "Appending $vhdlfile to $lib_name list..."
8197 dict lappend libraries $lib_name $vhdlfile
8198 if {$sha_mode != 0 && [
file type $vhdlfile] eq "link"} {
8201 dict lappend libraries $lib_name $real_file
8202 Msg Debug "File $vhdlfile is a soft link, also adding the real file: $real_file"
8210 if {$list_file_ext eq ".src" && $extension eq ".cfg"} {
8212 if {$print_log == 1 && [
llength $hls_extras] > 0} {
8213 Msg Status "$indent Inside [
file tail $vhdlfile] (HLS auto-expand):"
8215 set n_extras [
llength $hls_extras]
8217 foreach hls_extra $hls_extras {
8219 if {$hls_extra eq $vhdlfile} {continue}
8220 Msg Debug "HLS cfg expansion: adding $hls_extra (from $vhdlfile) to $lib_name"
8221 if {$print_log == 1} {
8222 if {$i == $n_extras} {
set pad "└──"}
else {
set pad "├──"}
8223 set rel [
Relative [
file dirname $vhdlfile] $hls_extra]
8224 Msg Status "$indent $pad $rel"
8226 dict lappend libraries $lib_name $hls_extra
8227 if {$sha_mode != 0 && [
file type $hls_extra] eq "link"} {
8229 dict lappend libraries $lib_name $real_extra
8230 Msg Debug "HLS expanded file $hls_extra is a soft link, also adding $real_extra"
8237 if {[dict exists $filesets $fileset] == 0} {
8239 Msg Debug "Adding $fileset to the fileset dictionary..."
8240 Msg Debug "Adding library $lib_name to fileset $fileset..."
8241 dict set filesets $fileset $lib_name
8245 Msg Debug "Adding library $lib_name to fileset $fileset..."
8246 dict lappend filesets $fileset $lib_name
8252 Msg CriticalWarning "File $vhdlfile not found."
8258 if {$sha_mode != 0} {
8260 if {$list_file_ext eq ".ipb"} {
8261 set sha_lib "xml.ipb"
8262 }
elseif {$list_file_ext eq ".chb"} {
8263 set sha_lib "cheby.chb"
8265 set sha_lib $lib$list_file_ext
8267 dict lappend libraries $sha_lib [
file normalize $list_file]
8268 if {[
file type $list_file] eq "link"} {
8271 dict lappend libraries $lib$list_file_ext $real_file
8272 Msg Debug "List file $list_file is a soft link, also adding the real file: $real_file"
8275 return [list $libraries $properties $filesets]
8284 proc Relative {base dst {quiet 0}} {
8285 if {![
string equal [
file pathtype $base] [
file pathtype $dst]]} {
8287 Msg CriticalWarning "Unable to compute relation for paths of different path types: [
file pathtype $base] vs. [
file pathtype $dst], ($base vs. $dst)"
8292 set base [
file normalize [
file join [
pwd] $base]]
8293 set dst [
file normalize [
file join [
pwd] $dst]]
8296 set base [
file split $base]
8297 set dst [
file split $dst]
8299 while {[
string equal [
lindex $dst 0] [
lindex $base 0]]} {
8300 set dst [
lrange $dst 1 end]
8301 set base [
lrange $base 1 end]
8302 if {![
llength $dst]} {break}
8305 set dstlen [
llength $dst]
8306 set baselen [
llength $base]
8308 if {($dstlen == 0) && ($baselen == 0)} {
8311 while {$baselen > 0} {
8312 set dst [
linsert $dst 0 ..]
8315 set dst [
eval [
linsert $dst 0 file join]]
8326 proc RelativeLocal {pathName filePath} {
8327 if {[
string first [
file normalize $pathName] [
file normalize $filePath]] != -1} {
8328 return [
Relative $pathName $filePath]
8339 proc RemoveDuplicates {mydict} {
8340 set new_dict [dict create]
8341 foreach key [dict keys $mydict] {
8342 set values [
DictGet $mydict $key]
8343 foreach value $values {
8344 set idxs [
lreverse [
lreplace [lsearch -exact -all $values $value] 0 0]]
8346 set values [
lreplace $values $idx $idx]
8349 dict set new_dict $key $values
8360 proc ResetRepoFiles {reset_file} {
8361 if {[
file exists $reset_file]} {
8362 Msg Info "Found $reset_file, opening it..."
8363 set fp [open $reset_file r]
8364 set wild_cards [lsearch -all -inline -not -regexp [
split [read $fp] "\n"] "^ *$"]
8366 Msg Info "Found the following files/wild cards to restore if modified: $wild_cards..."
8367 foreach w $wild_cards {
8369 if {[
llength $mod_files] > 0} {
8370 Msg Info "Found modified $w files: $mod_files, will restore them..."
8373 Msg Info "No modified $w files found."
8384 proc RestoreModifiedFiles {{repo_path "."} {pattern "."}} {
8387 set ret [
Git checkout $pattern]
8398 proc SearchHogProjects {dir} {
8399 set projects_list {}
8400 if {[
file exists $dir]} {
8401 if {[
file isdirectory $dir]} {
8402 foreach proj_dir [glob -nocomplain -types d $dir/*] {
8403 if {![regexp {^.*Top/+(.*)$} $proj_dir dummy proj_name]} {
8404 Msg Warning "Could not parse Top directory $dir"
8407 if {[
file exists "$proj_dir/hog.conf"]} {
8408 lappend projects_list $proj_name
8411 lappend projects_list $p
8416 Msg Error "Input $dir is not a directory!"
8419 Msg Error "Directory $dir doesn't exist!"
8421 return $projects_list
8430 proc SetGenericsSimulation {repo_path proj_dir target} {
8431 set top_dir "$repo_path/Top/$proj_dir"
8432 set simsets [get_filesets]
8433 if {$simsets != ""} {
8434 foreach simset $simsets {
8436 if {[get_property FILESET_TYPE $simset] != "SimulationSrcs"} {
8440 set merged_generics_dict [dict create]
8444 set simset_generics [
DictGet $simset_dict "generics"]
8445 set merged_generics_dict [
MergeDict $merged_generics_dict $simset_generics 0]
8448 Msg Debug "TOP = [get_property top [get_filesets sources_1]]"
8449 Msg Debug "GENERICS = [get_property generic [get_filesets sources_1]]"
8451 set_property generic $generic_str [get_filesets $simset]
8452 Msg Info "Setting generics $generic_str for simulator $target\
8453 and simulation file-set $simset..."
8465 proc SetTopProperty {top_module fileset} {
8466 Msg Info "Setting TOP property to $top_module module"
8469 set_property "top" $top_module [get_filesets $fileset]
8472 set_global_assignment -name TOP_LEVEL_ENTITY $top_module
8474 set_root -module $top_module
8476 prj_impl option top $top_module
8481 proc VIVADO_PATH_PROPERTIES {} {
8482 return {"\.*\.TCL\.PRE$" "^.*\.TCL\.POST$" "^RQS_FILES$" "^INCREMENTAL\_CHECKPOINT$" "NOC\_SOLUTION\_FILE"}
8486 proc VITIS_PATH_PROPERTIES {} {
8487 return {"^HW$" "^XPFM$" "^LINKER-SCRIPT$" "^LIBRARIES$" "^LIBRARY-SEARCH-PATH$"}
8497 proc WriteConf {file_name config {comment ""}} {
8498 if {[
catch {
package require inifile 0.2.3} ERROR]} {
8499 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
8503 ::ini::commentchar "#"
8504 set f [::ini::open $file_name w]
8506 foreach sec [dict keys $config] {
8507 set section [dict get $config $sec]
8508 dict for {p v} $section {
8509 if {[string trim $v] == ""} {
8510 Msg Warning "Property $p has empty value. Skipping..."
8513 ::ini::set $f $sec $p $v
8518 if {![
string equal "$comment" ""]} {
8519 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $comment
8520 set hog_header "Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
8521 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $hog_header
8532 proc WriteDefines {design} {
8534 if {[dict size $prj_defines] > 0} {
8537 set_property verilog_define $define_string [current_fileset]
8538 Msg Info "Setting Verilog defines from the \[defines\] section of hog.conf..."
8539 Msg Debug "Detailed defines: $define_string"
8542 Msg Error "Project $design has a \[defines\] section in hog.conf, but Verilog defines are not supported for $ide_name projects."
8555 proc WriteGenerics {
8556 mode repo_path design date timee
8557 commit version top_hash top_ver hog_hash hog_ver
8558 cons_ver cons_hash libs vers hashes ext_names ext_hashes
8559 user_ip_repos user_ip_vers user_ip_hashes flavour {xml_ver ""} {xml_hash ""}
8560 {cheby_ver ""} {cheby_hash ""}
8562 Msg Info "Passing parameters/generics to project's top module..."
8565 set generic_string [
concat \
8577 if {$xml_hash != "" && $xml_ver != ""} {
8578 lappend generic_string \
8583 if {$cheby_hash != "" && $cheby_ver != ""} {
8584 lappend generic_string \
8589 foreach l $libs v $vers h $hashes {
8595 set ver [regsub -all {[\.-]} $ver {_}]
8596 set hash [regsub -all {[\.-]} $hash {_}]
8597 lappend generic_string "$ver" "$hash"
8600 foreach e $ext_names h $ext_hashes {
8602 lappend generic_string "$hash"
8605 foreach repo $user_ip_repos v $user_ip_vers h $user_ip_hashes {
8606 set repo_name [
file tail $repo]
8607 set ver "[
string toupper $repo_name]_VER=[
FormatGeneric $v]"
8608 set hash "[
string toupper $repo_name]_SHA=[
FormatGeneric $h]"
8609 set ver [regsub -all {[\.-]} $ver {_}]
8610 set hash [regsub -all {[\.-]} $hash {_}]
8611 lappend generic_string "$ver" "$hash"
8614 if {$flavour != -1} {
8615 lappend generic_string "FLAVOUR=$flavour"
8621 set generic_string "$prj_generics $generic_string"
8630 if {$mode == "create" || [
IsISE]} {
8633 if {[
file exists $top_file]} {
8636 Msg Debug "Found top level generics $generics in $top_file"
8638 set filtered_generic_string ""
8640 foreach generic_to_set [
split [
string trim $generic_string]] {
8641 set key [
lindex [
split $generic_to_set "="] 0]
8642 if {[dict exists $generics $key]} {
8643 Msg Debug "Hog generic $key found in $top_name"
8644 lappend filtered_generic_string "$generic_to_set"
8646 Msg Warning "Generic $key is passed by Hog but is NOT present in $top_name."
8652 set generic_string $filtered_generic_string
8657 set_property generic $generic_string [current_fileset]
8658 Msg Info "Setting parameters/generics..."
8659 Msg Debug "Detailed parameters/generics: $generic_string"
8664 set simulator [get_property target_simulator [current_project]]
8665 if {$mode == "create"} {
8672 Msg Info "Setting Synplify parameters/generics one by one..."
8673 foreach generic $generic_string {
8674 Msg Debug "Setting Synplify generic: $generic"
8675 set_option -hdl_param -set "$generic"
8678 Msg Info "Setting Diamond parameters/generics one by one..."
8679 prj_impl option -impl Implementation0 HDL_PARAM "$generic_string"
8681 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
8683 foreach app_name [dict keys $ws_apps] {
8684 set defined_symbols [app config -name $app_name -get define-compiler-symbols]
8685 foreach generic_to_set [
split [
string trim $generic_string]] {
8686 set key [
lindex [
split $generic_to_set "="] 0]
8687 set value [
lindex [
split $generic_to_set "="] 1]
8688 if {[
string match "32'h*" $value]} {
8689 set value [
string map {"32'h" "0x"} $value]
8692 foreach symbol [
split $defined_symbols ";"] {
8693 if {[
string match "$key=*" $symbol]} {
8694 Msg Debug "Generic $key found in $app_name, removing it..."
8695 app config -name $app_name -remove define-compiler-symbols "$symbol"
8699 Msg Info "Setting Vitis parameters/generics for app $app_name: $key=$value"
8700 app config -name $app_name define-compiler-symbols "$key=$value"
8712 proc WriteGenericsToBdIPs {mode repo_path proj generic_string} {
8713 Msg Debug "Parameters/generics passed to WriteGenericsToIP: $generic_string"
8715 set bd_ip_generics false
8717 if {[dict exists $properties "hog"]} {
8718 set propDict [dict get $properties "hog"]
8719 if {[dict exists $propDict "PASS_GENERICS_TO_BD_IPS"]} {
8720 set bd_ip_generics [dict get $propDict "PASS_GENERICS_TO_BD_IPS"]
8724 if {[
string compare [
string tolower $bd_ip_generics] "false"] == 0} {
8728 if {$mode == "synth"} {
8729 Msg Info "Attempting to apply generics pre-synthesis..."
8730 set PARENT_PRJ [get_property "PARENT.PROJECT_PATH" [current_project]]
8731 set workaround [open "$repo_path/Projects/$proj/.hog/presynth_workaround.tcl" "w"]
8732 puts $workaround "source \[lindex \$argv 0\];"
8733 puts $workaround "open_project \[lindex \$argv 1\];"
8734 puts $workaround "WriteGenericsToBdIPs \[lindex \$argv 2\] \[lindex \$argv 3\] \[lindex \$argv 4\] \[lindex \$argv 5\];"
8735 puts $workaround "close_project"
8739 exec vivado -mode batch -source $repo_path/Projects/$proj/.hog/presynth_workaround.tcl \
8740 -tclargs $repo_path/Hog/Tcl/hog.tcl $PARENT_PRJ \
8741 "childprocess" $repo_path $proj $generic_string
8744 Msg Error "Encountered an error while attempting workaround: $errMsg"
8746 file delete $repo_path/Projects/$proj/.hog/presynth_workaround.tcl
8748 Msg Info "Done applying generics pre-synthesis."
8752 Msg Info "Looking for IPs to add generics to..."
8753 set ips_generic_string ""
8754 foreach generic_to_set [
split [
string trim $generic_string]] {
8755 set key [
lindex [
split $generic_to_set "="] 0]
8756 set value [
lindex [
split $generic_to_set "="] 1]
8757 append ips_generic_string "CONFIG.$key $value "
8761 if {[
string compare [
string tolower $bd_ip_generics] "true"] == 0} {
8764 set ip_regex $bd_ip_generics
8767 set ip_list [get_ips -regex $ip_regex]
8768 Msg Debug "IPs found with regex \{$ip_regex\}: $ip_list"
8770 set regen_targets {}
8772 foreach {ip} $ip_list {
8773 set WARN_ABOUT_IP false
8774 set ip_props [list_property [get_ips $ip]]
8777 if {[lsearch -exact $ip_props "IS_BD_CONTEXT"] == -1} {
8781 if {[get_property "IS_BD_CONTEXT" [get_ips $ip]] eq "1"} {
8782 foreach {ip_prop} $ip_props {
8783 if {[dict exists $ips_generic_string $ip_prop]} {
8784 if {$WARN_ABOUT_IP == false} {
8785 lappend regen_targets [get_property SCOPE [get_ips $ip]]
8786 Msg Warning "The ip \{$ip\} contains generics that are set by Hog.\
8787 If this is IP is apart of a block design, the .bd file may contain stale, unused, values.\
8788 Hog will always apply the most up-to-date values to the IP during synthesis,\
8789 however these values may or may not be reflected in the .bd file."
8790 set WARN_ABOUT_IP true
8795 set xci_path [get_property IP_FILE [get_ips $ip]]
8797 if {[
string equal $generic_format "ERROR"]} {
8798 Msg Warning "Could not find format for generic $ip_prop in IP $ip. Skipping..."
8802 set value_to_set [dict get $ips_generic_string $ip_prop]
8803 switch -exact $generic_format {
8805 if {[
string match "32'h*" $value_to_set]} {
8806 scan [
string map {"32'h" ""} $value_to_set] "%x" value_to_set
8810 set value_to_set [
expr {$value_to_set ? "true" : "false"}]
8813 if {[
string match "32'h*" $value_to_set]} {
8814 binary scan [
binary format H* [
string map {"32'h" ""} $value_to_set]] d value_to_set
8818 if {[
string match "32'h*" $value_to_set]} {
8819 set value_to_set [
string map {"32'h" "0x"} $value_to_set]
8823 set value_to_set [
format "%s" $value_to_set]
8826 Msg Warning "Unknown generic format $generic_format for IP $ip. Will attempt to pass as string..."
8831 Msg Info "The IP \{$ip\} contains: $ip_prop ($generic_format), setting it to $value_to_set."
8832 if {[
catch {set_property -name $ip_prop -value $value_to_set -objects [get_ips $ip]} prop_error]} {
8833 Msg CriticalWarning "Failed to set property $ip_prop to $value_to_set for IP \{$ip\}: $prop_error"
8840 foreach {regen_target} [lsort -unique $regen_targets] {
8841 Msg Info "Regenerating target: $regen_target"
8842 if {[
catch {generate_target -force all [get_files $regen_target]} prop_error]} {
8843 Msg CriticalWarning "Failed to regen targets: $prop_error"
8851 proc GetGenericFormatFromXciXML {generic_name xml_file} {
8852 if {![
file exists $xml_file]} {
8853 Msg Error "Could not find XML file: $xml_file"
8857 set fp [open $xml_file r]
8858 set xci_data [read $fp]
8861 set paramType "string"
8862 set modelparam_regex [
format {^.*\y%s\y.*$} [
string map {"CONFIG." "MODELPARAM_VALUE."} $generic_name]]
8863 set format_regex {format="([^"]+)"}
8865 set line [
lindex [regexp -inline -line $modelparam_regex $xci_data] 0]
8866 Msg Debug "line: $line"
8868 if {[regexp $format_regex $line match format_value]} {
8869 Msg Debug "Extracted: $format_value format from xml"
8870 set paramType $format_value
8872 Msg Debug "No format found, using string"
8881 proc GetGenericFormatFromXci {generic_name xci_file} {
8882 if {![
file exists $xci_file]} {
8883 Msg Error "Could not find XCI file: $xci_file"
8887 set fp [open $xci_file r]
8888 set xci_data [read $fp]
8891 set paramType "string"
8892 if {[
string first "xilinx.com:schema:json_instance:1.0" $xci_data] == -1} {
8893 Msg Debug "XCI format is not JSON, trying XML..."
8894 set xml_file "[
file rootname $xci_file].xml"
8898 set generic_name [
string map {"CONFIG." ""} $generic_name]
8899 set ip_inst [
ParseJSON $xci_data "ip_inst"]
8900 set parameters [dict get $ip_inst parameters]
8901 set component_parameters [dict get $parameters component_parameters]
8902 if {[dict exists $component_parameters $generic_name]} {
8903 set generic_info [dict get $component_parameters $generic_name]
8904 if {[dict exists [
lindex $generic_info 0] format]} {
8905 set paramType [dict get [
lindex $generic_info 0] format]
8906 Msg Debug "Extracted: $paramType format from xci"
8909 Msg Debug "No format found, using string"
8922 proc WriteGitLabCIYAML {proj_name {ci_conf ""}} {
8923 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
8924 Msg CriticalWarning "Cannot find package YAML.\n Error message: $YAMLPACKAGE. \
8925 If you are running on tclsh, you can fix this by installing package \"tcllib\""
8930 if {$ci_conf != ""} {
8932 foreach sec [dict keys $ci_confs] {
8933 if {[
string first : $sec] == -1} {
8934 lappend job_list $sec
8938 set job_list {"generate_project" "simulate_project"}
8942 set out_yaml [huddle create]
8943 foreach job $job_list {
8945 set huddle_tags [huddle list]
8947 set sec_dict [dict create]
8949 if {$ci_confs != ""} {
8950 foreach var [dict keys [dict get $ci_confs $job]] {
8951 if {$var == "tags"} {
8952 set tag_section "tags"
8953 set tags [dict get [dict get $ci_confs $job] $var]
8954 set tags [
split $tags ","]
8956 set tag_list [huddle list $tag]
8957 set huddle_tags [huddle combine $huddle_tags $tag_list]
8960 dict set sec_dict $var [dict get [dict get $ci_confs $job] $var]
8966 set huddle_variables [huddle create "PROJECT_NAME" $proj_name "extends" ".vars"]
8967 if {[dict exists $ci_confs "$job:variables"]} {
8968 set var_dict [dict get $ci_confs $job:variables]
8969 foreach var [dict keys $var_dict] {
8971 set value [dict get $var_dict "$var"]
8972 set var_inner [huddle create "$var" "$value"]
8973 set huddle_variables [huddle combine $huddle_variables $var_inner]
8978 set middle [huddle create "extends" ".$job" "variables" $huddle_variables]
8979 foreach sec [dict keys $sec_dict] {
8980 set value [dict get $sec_dict $sec]
8981 set var_inner [huddle create "$sec" "$value"]
8982 set middle [huddle combine $middle $var_inner]
8984 if {$tag_section != ""} {
8985 set middle2 [huddle create "$tag_section" $huddle_tags]
8986 set middle [huddle combine $middle $middle2]
8989 set outer [huddle create "$job:$proj_name" $middle]
8990 set out_yaml [huddle combine $out_yaml $outer]
8993 return [
string trimleft [yaml::huddle2yaml $out_yaml] "-"]
9003 proc WriteListFiles {libs props list_path repo_path {ext_path ""}} {
9005 foreach lib [dict keys $libs] {
9006 if {[
llength [
DictGet $libs $lib]] > 0} {
9007 set list_file_name $list_path$lib
9008 set list_file [open $list_file_name w]
9009 Msg Info "Writing $list_file_name..."
9010 puts $list_file "#Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
9011 foreach file [
DictGet $libs $lib] {
9013 set prop [
DictGet $props $file]
9017 puts $list_file "$file_path $prop"
9020 set ext_list_file [open "[
file rootname $list_file].ext" a]
9021 puts $ext_list_file "$file_path $prop"
9022 close $ext_list_file
9025 Msg Warning "The path of file $file is not relative to your repository. Please check!"
9041 proc WriteSimListFile {simset libs props simsets list_path repo_path {force 0}} {
9043 set list_file_name $list_path/${simset}.sim
9044 if {$force == 0 && [
file exists $list_file_name]} {
9045 Msg Info "List file $list_file_name already exists, skipping..."
9049 set list_file [open $list_file_name a+]
9052 puts $list_file "\[files\]"
9053 Msg Info "Writing $list_file_name..."
9054 foreach lib [
DictGet $simsets $simset] {
9055 foreach file [
DictGet $libs $lib] {
9057 set prop [
DictGet $props $file]
9061 set lib_name [
file rootname $lib]
9062 if {$lib_name != $simset && [
file extension $file] == ".vhd" && [
file extension $file] == ""} {
9063 lappend prop "lib=$lib_name"
9065 puts $list_file "$file_path $prop"
9068 Msg Warning "The path of file $file is not relative to your repository. Please check!"
9080 proc WriteToFile {File msg} {
9081 set f [open $File a+]
9092 proc WriteUtilizationSummary {input output project_name run} {
9093 set f [open $input "r"]
9094 set o [open $output "a"]
9095 puts $o "## $project_name $run Utilization report\n\n"
9096 struct::matrix util_m
9097 util_m add columns 14
9100 util_m add row "| **Site Type** | **Used** | **Fixed** | **Prohibited** | **Available** | **Util%** |"
9101 util_m add row "| --- | --- | --- | --- | --- | --- |"
9103 util_m add row "| **Site Type** | **Used** | **Fixed** | **Available** | **Util%** |"
9104 util_m add row "| --- | --- | --- | --- | --- |"
9114 while {[
gets $f line] >= 0} {
9115 if {([
string first "| CLB LUTs" $line] >= 0 || [
string first "| Slice LUTs" $line] >= 0) && $luts == 0} {
9116 util_m add row $line
9119 if {([
string first "| CLB Registers" $line] >= 0 || [
string first "| Slice Registers" $line] >= 0) && $regs == 0} {
9120 util_m add row $line
9123 if {[
string first "| Block RAM Tile" $line] >= 0 && $bram == 0} {
9124 util_m add row $line
9127 if {[
string first "URAM " $line] >= 0 && $uram == 0} {
9128 util_m add row $line
9131 if {[
string first "DSPs" $line] >= 0 && $dsps == 0} {
9132 util_m add row $line
9135 if {[
string first "Bonded IOB" $line] >= 0 && $ios == 0} {
9136 util_m add row $line
9143 puts $o [util_m format 2string]
9149 Msg Error "Found Git version older than 2.7.2. Hog will not work as expected, exiting now."
9158 proc GetCurl {{gitlab_url "https://gitlab.com"}} {
9159 if {[
auto_execok curl] == ""} {
9160 Msg Warning "Cannot find a working curl invocation"
9165 set cmd [list curl --silent --show-error]
9167 set cmd [list env -u LD_LIBRARY_PATH curl --silent --show-error]
9170 if {![
catch {
exec {*}$cmd -I $gitlab_url}]} {
9173 Msg Warning "Impossible to contact $gitlab_url"