17 if {![
info exists tcl_path]} {
18 set tcl_path [
file normalize "[
file dirname [
info script]]"]
21 set hog_path [
file normalize "[
file dirname [
info script]]"]
23 source "$hog_path/utils/Logger.tcl"
27 set CI_STAGES {"generate_project" "simulate_project"}
28 set CI_PROPS {"-synth_only"}
37 proc AddFile {file fileset} {
39 add_files -norecurse -fileset $fileset $file
49 proc AddHogFiles {libraries properties filesets} {
50 Msg Info "Adding source files to project..."
51 Msg Debug "Filesets: $filesets"
52 Msg Debug "Libraries: $libraries"
53 Msg Debug "Properties: $properties"
56 set synth_conf_command "organize_tool_files -tool {SYNTHESIZE} -input_type {constraint}"
58 set timing_conf_command "organize_tool_files -tool {VERIFYTIMING} -input_type {constraint}"
60 set place_conf_command "organize_tool_files -tool {PLACEROUTE} -input_type {constraint}"
64 foreach fileset [dict keys $filesets] {
65 Msg Debug "Fileset: $fileset"
68 if {[
string equal [get_filesets -quiet $fileset] ""]} {
70 create_fileset -simset $fileset
73 current_fileset -simset [get_filesets $fileset]
74 set simulation [get_filesets $fileset]
76 set_property -name {$simulator.compile.vhdl_syntax} -value {2008} -objects $simulation
78 set_property SOURCE_SET sources_1 $simulation
82 set libs_in_fileset [
DictGet $filesets $fileset]
83 if {[
IsInList "ips.src" $libs_in_fileset]} {
92 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
95 set vitis_workspace "$globalSettings::build_dir/vitis_unified"
96 set python_script "$globalSettings::repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"
98 if {![
ExecuteVitisUnifiedCommand $python_script "app_list" [list $vitis_workspace] "Failed to get app list from Vitis Unified" json_output]} {
99 Msg Warning "Failed to get app list from Vitis Unified"
102 if {[
catch {
package require json}]} {
103 Msg Warning "JSON package not available for parsing Vitis Unified app list"
106 set json_output_filtered ""
107 if {[regexp -lineanchor {\{.*\}} $json_output json_output_filtered]} {
108 set ws_apps [json::json2dict $json_output_filtered]
110 set ws_apps [json::json2dict $json_output]
117 if {$ws_apps ne ""} {
118 dict for {app_name app_config} $ws_apps {
119 set app_lib [string tolower "app_$app_name\.src"]
120 if {![IsInList $app_lib $libs_in_fileset 0 1]} {
121 Msg Warning "App '$app_name' exists in workspace but no corresponding sourcefile '$app_lib' found. \
122 Make sure you have a list file with the correct naming convention: \[app_<app_name>\.src\]"
131 set app_files_dict [dict create]
135 foreach lib $libs_in_fileset {
136 Msg Debug "lib: $lib \n"
137 set lib_files [
DictGet $libraries $lib]
138 Msg Debug "Files in $lib: $lib_files"
139 set rootlib [
file rootname [
file tail $lib]]
140 set ext [
file extension $lib]
141 Msg Debug "lib: $lib ext: $ext fileset: $fileset"
143 if {[
IsXilinx] && !([
info exists globalSettings::vitis_only_pass] && $globalSettings::vitis_only_pass == 1)} {
145 if {[
string match "app_*" [
string tolower $lib]]} {
148 Msg Debug "Adding $lib to $fileset"
149 add_files -norecurse -fileset $fileset $lib_files
151 foreach f $lib_files {
152 set file_obj [get_files -of_objects [get_filesets $fileset] [list "*$f"]]
154 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
155 set_property -name "library" -value $rootlib -objects $file_obj
159 set props [
DictGet $properties $f]
160 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
162 if {[lsearch -inline -regexp $props "93"] < 0} {
165 if {[lsearch -inline -regexp $props "2008"] >= 0} {
166 set vhdl_year "VHDL 2008"
167 }
elseif {[lsearch -inline -regexp $props "2019"] >= 0} {
169 set vhdl_year "VHDL 2019"
171 Msg CriticalWarning "VHDL 2019 is not supported\
172 in Vivado version older than 2023.2.\
173 Using VHDL 2008, but this might not work."
174 set vhdl_year "VHDL 2008"
178 set vhdl_year "VHDL 2008"
180 Msg Debug "File type for $f is $vhdl_year"
181 set_property -name "file_type" -value $vhdl_year -objects $file_obj
184 Msg Debug "Filetype is VHDL 93 for $f"
189 if {[lsearch -inline -regexp $props "SystemVerilog"] > 0} {
192 set_property -name "file_type" -value "SystemVerilog" -objects $file_obj
193 Msg Debug "Filetype is SystemVerilog for $f"
195 Msg Warning "Xilinx PlanAhead/ISE does not support SystemVerilog.\
196 Property not set for $f"
201 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
203 Msg Info "Setting $top as top module for file set $fileset..."
204 set globalSettings::synth_top_module $top
208 if {[lsearch -inline -regexp $props "verilog_header"] >= 0} {
209 Msg Debug "Setting verilog header type for $f..."
210 set_property file_type {Verilog Header} [get_files $f]
211 }
elseif {[lsearch -inline -regexp $props "verilog_template"] >= 0} {
213 Msg Debug "Setting verilog template type for $f..."
214 set_property file_type {Verilog Template} [get_files $f]
215 }
elseif {[lsearch -inline -regexp $props "verilog"] >= 0} {
217 Msg Debug "Setting verilog type for $f..."
218 set_property file_type {Verilog} [get_files $f]
222 if {[lsearch -inline -regexp $props "nosynth"] >= 0} {
223 Msg Debug "Setting not used in synthesis for $f..."
224 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
228 if {[lsearch -inline -regexp $props "noimpl"] >= 0} {
229 Msg Debug "Setting not used in implementation for $f..."
230 set_property -name "used_in_implementation" -value "false" -objects $file_obj
234 if {[lsearch -inline -regexp $props "nosim"] >= 0} {
235 Msg Debug "Setting not used in simulation for $f..."
236 set_property -name "used_in_simulation" -value "false" -objects $file_obj
241 set top_sim [
lindex [regexp -inline {\ytopsim\s*=\s*(.+?)\y.*} $props] 1]
242 if {$top_sim != ""} {
243 Msg Warning "Setting the simulation top module with the topsim property is deprecated.\
244 Please set this property in the \[properties\] section of your .sim list file,
245 or in the \[$fileset\] section of your sim.conf,\
246 by adding the following line.\ntop=$top_sim"
250 set sim_runtime [
lindex [regexp -inline {\yruntime\s*=\s*(.+?)\y.*} $props] 1]
251 if {$sim_runtime != ""} {
252 Msg Warning "Setting the simulation runtime using the runtime= property is deprecated.\
253 Please set this property in the \[properties\] section of your .sim list file,\
254 or in the \[$fileset\] section of your sim.conf,\
255 by adding the following line.\n<simulator_name>.simulate.runtime=$sim_runtime"
259 if {[lsearch -inline -regexp $props "wavefile"] >= 0} {
260 Msg Warning "Setting a wave do file using the wavefile property is deprecated.\
261 Set this property in the sim.conf file under the \[$fileset\] section,\
262 or in the \[properties\] section of the .sim list file,\
263 by adding the following line .\n<simulator_name>.simulate.custom_wave_do=[
file tail $f]"
267 if {[lsearch -inline -regexp $props "dofile"] >= 0} {
268 Msg Warning "Setting a wave do file using the dofile property is deprecated.\
269 Set this property in the sim.conf file under the \[$fileset\] section,\
270 or in the \[properties\] section of the .sim list file,\
271 by adding the following line .\n<simulator_name>.simulate.custom_do=[
file tail $f]"
275 if {[lsearch -inline -regexp $props "locked"] >= 0 && $ext == ".ip"} {
276 Msg Info "Locking IP $f..."
277 set_property IS_MANAGED 0 [get_files $f]
281 if {[
file extension $f] == ".bd"} {
282 Msg Info "Generating Target for [
file tail $f],\
283 please remember to commit the (possible) changed file."
284 generate_target all [get_files $f]
288 if {[
file extension $f] == ".tcl" && $ext != ".con"} {
289 if {[lsearch -inline -regexp $props "source"] >= 0} {
290 Msg Info "Sourcing Tcl script $f,\
291 and setting it not used in synthesis, implementation and simulation..."
293 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
294 set_property -name "used_in_implementation" -value "false" -objects $file_obj
295 set_property -name "used_in_simulation" -value "false" -objects $file_obj
300 set ref [
lindex [regexp -inline {\yscoped_to_ref\s*=\s*([^ ]+)} $props] 1]
301 set cell [
lindex [regexp -inline {\yscoped_to_cells\s*=\s*([^ ]+)} $props] 1]
302 if {[
file extension $f] == ".elf" || (([
file extension $f] == ".tcl" || [
file extension $f] == ".xdc") && $ext == ".con")} {
304 set_property SCOPED_TO_REF $ref $file_obj
307 set_property SCOPED_TO_CELLS [
split $cell ","] $file_obj
311 Msg Info "[
llength $lib_files] file/s added to library $rootlib..."
314 if {$ext == ".sim"} {
315 Msg Warning "Simulation files not supported in Quartus Prime mode... Skipping $lib"
317 if {![is_project_open]} {
318 Msg Error "Project is closed"
320 foreach cur_file $lib_files {
324 set props [
DictGet $properties $cur_file]
327 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
329 Msg Info "Setting $top as top module for file set $fileset..."
330 set globalSettings::synth_top_module $top
333 if {[
string first "VHDL" $file_type] != -1} {
334 if {[
string first "1987" $props] != -1} {
335 set hdl_version "VHDL_1987"
336 }
elseif {[
string first "1993" $props] != -1} {
337 set hdl_version "VHDL_1993"
338 }
elseif {[
string first "2008" $props] != -1} {
339 set hdl_version "VHDL_2008"
341 set hdl_version "default"
343 if {$hdl_version == "default"} {
344 set_global_assignment -name $file_type $cur_file -library $rootlib
346 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version -library $rootlib
348 }
elseif {[
string first "SYSTEMVERILOG" $file_type] != -1} {
350 if {[
string first "2005" $props] != -1} {
351 set hdl_version "systemverilog_2005"
352 }
elseif {[
string first "2009" $props] != -1} {
353 set hdl_version "systemverilog_2009"
355 set hdl_version "default"
357 if {$hdl_version == "default"} {
358 set_global_assignment -name $file_type $cur_file
360 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
362 }
elseif {[
string first "VERILOG" $file_type] != -1} {
364 if {[
string first "1995" $props] != -1} {
365 set hdl_version "verilog_1995"
366 }
elseif {[
string first "2001" $props] != -1} {
367 set hdl_version "verilog_2001"
369 set hdl_version "default"
371 if {$hdl_version == "default"} {
372 set_global_assignment -name $file_type $cur_file
374 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
376 }
elseif {[
string first "SOURCE" $file_type] != -1 || [
string first "COMMAND_MACRO" $file_type] != -1} {
377 set_global_assignment -name $file_type $cur_file
378 if {$ext == ".con"} {
380 }
elseif {$ext == ".src"} {
382 if {[
string first "qsys" $props] != -1} {
385 regsub -all {\{||qsys||\}} $props $emptyString props
387 set qsysPath [
file dirname $cur_file]
388 set qsysName "[
file rootname [
file tail $cur_file]].qsys"
389 set qsysFile "$qsysPath/$qsysName"
390 set qsysLogFile "$qsysPath/[
file rootname [
file tail $cur_file]].qsys-script.log"
393 if {![
info exists ::env(QSYS_ROOTDIR)]} {
394 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
395 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
396 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
398 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
401 set qsys_rootdir $::env(QSYS_ROOTDIR)
404 set cmd "$qsys_rootdir/qsys-script"
405 set cmd_options " --script=$cur_file"
406 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
407 Msg Info "Executing: $cmd $cmd_options"
408 Msg Info "Saving logfile in: $qsysLogFile"
409 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
410 set makeRet [
lindex [dict get $opt -errorcode] end]
411 Msg CriticalWarning "$cmd returned with $makeRet"
414 Msg Error " Could not execute command $cmd"
418 if {[
file exists $qsysName] != 0} {
419 file rename -force $qsysName $qsysFile
421 set qsysMd5Sum [
Md5Sum $qsysFile]
423 set fileDir [
file normalize "./hogTmp"]
424 set fileName "$fileDir/.hogQsys.md5"
425 if {![
file exists $fileDir]} {
428 set hogQsysFile [open $fileName "a"]
429 set fileEntry "$qsysFile\t$qsysMd5Sum"
430 puts $hogQsysFile $fileEntry
433 Msg ERROR "Error while moving the generated qsys file to final location: $qsysName not found!"
435 if {[
file exists $qsysFile] != 0} {
436 if {[
string first "noadd" $props] == -1} {
438 set_global_assignment -name $qsysFileType $qsysFile
440 regsub -all {noadd} $props $emptyString props
442 if {[
string first "nogenerate" $props] == -1} {
446 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
450 }
elseif {[
string first "QSYS" $file_type] != -1} {
452 regsub -all {\{||\}} $props $emptyString props
453 if {[
string first "noadd" $props] == -1} {
454 set_global_assignment -name $file_type $cur_file
456 regsub -all {noadd} $props $emptyString props
460 if {[
string first "nogenerate" $props] == -1} {
464 set_global_assignment -name $file_type $cur_file -library $rootlib
469 if {$ext == ".con"} {
470 set vld_exts {.sdc .pin .dcf .gcf .pdc .ndc .fdc .crt .vcd }
471 foreach con_file $lib_files {
473 set con_ext [
file extension $con_file]
474 if {[
IsInList [
file extension $con_file] $vld_exts]} {
475 set option [
string map {. -} $con_ext]
476 set option [
string map {fdc net_fdc} $option]
477 set option [
string map {pdc io_pdc} $option]
478 create_links -convert_EDN_to_HDL 0 -library {work} $option $con_file
480 set props [
DictGet $properties $con_file]
482 if {$con_ext == ".sdc"} {
483 if {[lsearch $props "notiming"] >= 0} {
484 Msg Info "Excluding $con_file from timing verification..."
486 Msg Info "Adding $con_file to time verification"
487 append timing_conf_command " -file $con_file"
491 if {[lsearch $props "nosynth"] >= 0} {
492 Msg Info "Excluding $con_file from synthesis..."
494 Msg Info "Adding $con_file to synthesis"
495 append synth_conf_command " -file $con_file"
500 if {$con_ext == ".pdc" || $con_ext == ".sdc"} {
501 if {[lsearch $props "noplace"] >= 0} {
502 Msg Info "Excluding $con_file from place and route..."
504 Msg Info "Adding $con_file to place and route"
505 append place_conf_command " -file $con_file"
510 Msg CriticalWarning "Constraint file $con_file does not have a valid extension. Allowed extensions are: \n $vld_exts"
513 }
elseif {$ext == ".src"} {
514 foreach f $lib_files {
515 Msg Debug "Adding source $f to library $rootlib..."
516 create_links -library $rootlib -hdl_source $f
518 }
elseif {$ext == ".sim"} {
519 Msg Debug "Adding stimulus file $f to library..."
520 create_links -library $rootlib -stimulus $f
522 build_design_hierarchy
523 foreach cur_file $lib_files {
527 set props [
DictGet $properties $cur_file]
530 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
532 Msg Info "Setting $top as top module for file set $rootlib..."
533 set globalSettings::synth_top_module "${top}::$rootlib"
538 if {$ext == ".src" || $ext == ".con" || $ext == ".ext"} {
539 foreach f $lib_files {
540 Msg Debug "Diamond: adding source file $f to library $rootlib..."
541 prj_src add -work $rootlib $f
542 set props [
DictGet $properties $f]
544 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
546 Msg Info "Setting $top as top module for the project..."
547 set globalSettings::synth_top_module $top
551 if {[lsearch -inline -regexp $props "enable"] >= 0} {
552 Msg Debug "Setting $f as active Logic Preference file"
556 }
elseif {$ext == ".sim"} {
557 foreach f $lib_files {
558 Msg Debug "Diamond Adding simulation file $f to library $rootlib..."
559 prj_src add -work $rootlib -simulate_only $f
566 foreach app_name [dict keys $ws_apps] {
567 foreach f $lib_files {
568 if {[
string tolower $rootlib] != [
string tolower "app_$app_name"]} {
572 Msg Info "Adding source file $f from lib: $lib to vitis app \[$app_name\]..."
573 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
574 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
575 Msg Debug "Project_f_path is $proj_f_path"
577 importsources -name $app_name -soft-link -path $f -target $proj_f_path
581 Msg Debug "Vitis Unified: Collecting files for apps from library $rootlib..."
585 if {[dict size $ws_apps] == 0} {
586 Msg Debug "No apps found in workspace, skipping file collection"
588 Msg Debug "Found [dict size $ws_apps] app(s) in workspace"
589 Msg Debug "Processing library: $rootlib with [
llength $lib_files] file(s)"
590 foreach app_name [dict keys $ws_apps] {
591 set expected_lib [
string tolower "app_$app_name"]
592 Msg Debug "Checking files for app: $app_name (looking for lib: $expected_lib, current lib: [
string tolower $rootlib])"
593 foreach f $lib_files {
594 if {[
string tolower $rootlib] != $expected_lib} {
597 Msg Debug "File $f matches app $app_name"
598 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
599 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
600 set proj_f_path [
string trimleft $proj_f_path "/"]
602 if {![dict exists $app_files_dict $app_name]} {
603 dict set app_files_dict $app_name files [list]
604 dict set app_files_dict $app_name target $proj_f_path
605 Msg Debug "Initialized app_files_dict for $app_name with target: $proj_f_path"
608 set current_files [dict get $app_files_dict $app_name files]
609 lappend current_files $f
610 dict set app_files_dict $app_name files $current_files
611 set current_count [
llength [dict get $app_files_dict $app_name files]]
612 Msg Debug "Added file $f to app_files_dict for $app_name, current count: $current_count"
622 set python_script "$globalSettings::repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"
623 set vitis_workspace "$globalSettings::build_dir/vitis_unified"
627 set env(HOG_VITIS_VER) $vitis_version
628 Msg Debug "Vitis version: $vitis_version (set in HOG_VITIS_VER environment variable)"
630 dict for {app_name app_data} $app_files_dict {
631 set files_list [dict get $app_data files]
632 set target_path [dict get $app_data target]
634 if {[llength $files_list] > 0} {
635 Msg Info "Adding [llength $files_list] source file(s) to vitis app \[$app_name\]..."
636 Msg Debug "Files: $files_list"
637 Msg Debug "Target path: $target_path"
639 # Convert Tcl list to JSON array for Python
642 foreach f $files_list {
644 append files_json ", "
646 # Escape backslashes and quotes in file paths
647 set escaped_f [string map {\\ \\\\ \" \\\"} $f]
648 append files_json "\"$escaped_f\""
651 append files_json "\]"
653 Msg Debug "JSON string: $files_json"
655 set error_msg "Failed to add files to app $app_name"
656 if {![ExecuteVitisUnifiedCommand $python_script "add_app_files" \
657 [list $app_name $files_json $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"
711 ".qsys" [list "nogenerate" "noadd" "--block-symbol-file" "--clear-output-directory" "--example-design"\
712 "--export-qsys-script" "--family" "--greybox" "--ipxact" "--jvm-max-heap-size" "--parallel"\
713 "--part" "--search-path" "--simulation" "--synthesis" "--testbench" "--testbench-simulation"\
714 "--upgrade-ip-cores" "--upgrade-variation-file"
716 ".sdc" [list "notiming" "nosynth" "noplace"] \
717 ".elf" [list "scoped_to_ref" "scoped_to_cells" "nosim" "noimpl"] \
718 ".pdc" [list "nosynth" "noplace"] \
719 ".lpf" [list "enable"]]
730 proc BinaryStepName {part} {
732 return "WRITE_DEVICE_IMAGE"
736 return "WRITE_BITSTREAM"
743 set essential_vars [dict create \
744 "HOG_USER" "NOT defined. This variable is essential for git to work properly. \
745 It should be set to the username for your service account (a valid git account)." \
746 "HOG_EMAIL" "NOT defined. This variable is essential for git to work properly. It should be set to your service's account email."\
747 "HOG_PUSH_TOKEN" "NOT defined. This variable is essential for git to work properly. It should be set to a Gitlab/GitHub API token for your service account."
751 dict for {var msg} $essential_vars {
752 if {![info exists env($var)]} {
753 Msg CriticalWarning "Essential environment variable $var is $msg"
756 Msg Info "Found environment variable $var."
760 set additional_vars [dict create \
761 "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." \
762 "HOG_TARGET_BRANCH" "NOT defined. Default branch for merge is \"master\""\
763 "HOG_CREATE_OFFICIAL_RELEASE" "NOT defined. \
764 Set this variable to '1' to make Hog create an official release in GitHub/Gitlab with the binaries generated in the CI."\
765 "HOG_USE_DOXYGEN" "NOT defined. \
766 Set this variable to 1 to make Hog-CI run Doxygen and copy the official documentation over when you merge to the official branch."
769 if {([
info exists env(HOG_OFFICIAL_BIN_EOS_PATH)] && $env(HOG_OFFICIAL_BIN_EOS_PATH) ne "") || \
770 ([
info exists env(HOG_OFFICIAL_BIN_PATH)] && [
string match "/eos/*" $env(HOG_OFFICIAL_BIN_PATH)])} {
771 Msg Info "Official binary path points to EOS. Checking EOS environment variables for uploads..."
772 if {[
info exists env(HOG_OFFICIAL_BIN_PATH)]} {
773 Msg CriticalWarning "Variable HOG_OFFICIAL_BIN_EOS_PATH is defined. \
774 From Hog2026.2 this variable will be deprecated. Please, use HOG_OFFICIAL_BIN_PATH instead."
776 if {![
info exists env(EOS_PASSWORD)]} {
777 if {![
info exists env(HOG_PASSWORD)]} {
778 Msg Warning "Neither EOS_PASSWORD nor HOG_PASSWORD environment variable is defined. \
779 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."
781 Msg Info "HOG_PASSWORD environment variable is defined and will be used as password for EOS uploads. \
782 If you want to use a different password for EOS uploads, please set the EOS_PASSWORD environment variable."
785 Msg Info "EOS_PASSWORD environment variable is defined and will be used as password for EOS uploads."
788 if {![
info exists env(EOS_USER)]} {
789 Msg Info "EOS_USER environment variable is not defined. Assuming EOS username is the same as HOG_USER."
791 Msg Info "EOS_USER environment variable is defined and will be used as username for EOS uploads."
794 if {![
info exists env(EOS_MGM_URL)]} {
795 Msg Info "EOS_MGM_URL environment variable is not defined. Assuming default value of root://eosuser.cern.ch."
797 Msg Info "EOS_MGM_URL environment variable is defined and will be used as MGM URL for EOS uploads."
799 }
elseif {[
info exists env(HOG_OFFICIAL_BIN_PATH)] } {
800 Msg Info "Variable HOG_OFFICIAL_BIN_PATH is defined. Hog will copy the official binary files to the path defined in this variable. \
801 Please make sure this path is correct and has enough space to store the binaries."
803 Msg Info "No official binary path defined. Hog will not be able to upload binaries."
809 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
817 proc CheckEnv {project_name ide} {
820 set essential_commands [dict create "git" "--version" "$ide" "-version"]
821 set additional_commands [dict create \
828 set additional_vars [dict create \
829 "HOG_PATH" "NOT defined. Hog might work as long as all the necessary executable are in the PATH variable."\
830 "HOG_XIL_LICENSE" "NOT defined. If this variable is not set to the license servers separated by comas, \
831 you need some alternative way of getting your Xilinx license (for example a license file on the machine)."\
832 "LM_LICENSE_FILE" "NOT defined. This variable should be set the Quartus/Libero license servers separated by semicolon. \
833 If not, you need an alternative way of getting your Quartus/Libero license."\
834 "HOG_LD_LIBRARY_PATH" "NOT defined. Hog might work as long as all the necessary library are found."\
835 "HOG_SIMULATION_LIB_PATH" "NOT defined. Hog-CI will not be able to run simulations using third-party simulators."\
836 "HOG_CHECK_PROJVER" "NOT defined. Hog will NOT check the CI project version. \
837 Set this variable to '1' if you want Hog to check the CI project version before creating the HDL project in Create_Project stage. \
838 If the project has not been changed with respect to the target branch, the CI will skip this project" \
839 "HOG_CHECK_SYNTAX" "NOT defined. Hog will NOT check the syntax. \
840 Set this variable to '1' if you want Hog to check the syntax after creating the HDL project in Create_Project stage." \
841 "HOG_NO_BITSTREAM" "NOT defined. Hog-CI will run the implementation up to the write_bitstream stage and create bit files." \
842 "HOG_NO_RESET_BD" "NOT defined or not equal to 1. Hog will reset .bd files (if any) before starting synthesis."\
843 "HOG_IP_PATH" "NOT defined. Hog-CI will NOT use an EOS/LOCAL IP repository to speed up the IP synthesis." \
844 "HOG_RESET_FILES" "NOT defined. Hog-CI will NOT reset any files."\
845 "HOG_NJOBS" "NOT defined. Hog-CI will build IPs with default number of jobs (4)."\
846 "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"
848 Msg Info "Checking environment to run Hog-CI for project $project_name with IDE $ide..."
850 Msg Info "Checking essential commands..."
851 dict for {cmd ver} $essential_commands {
852 if {[catch {exec which $cmd}]} {
853 Msg CriticalWarning "$cmd executable not found. Hog-CI cannot run!"
856 Msg Info "Found executable $cmd."
857 if {$cmd == "ghdl"} {
858 Msg Info [exec $cmd --version]
859 } elseif {$cmd != "diamond"} {
860 Msg Info [exec $cmd $ver]
865 Msg Info "Checking additional commands..."
866 dict for {cmd ver} $additional_commands {
867 if {[catch {exec which $cmd}]} {
868 Msg Warning "$cmd executable not found."
870 Msg Info "Found executable $cmd."
872 Msg Info [exec $cmd $ver]
877 if {$ide == "libero"} {
878 Msg Info "Checking essential environment variables..."
880 if {![
info exists env(HOG_TCLLIB_PATH)]} {
881 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."
884 Msg Info "HOG_TCLLIB_PATH is set. Hog-CI can run with Libero."
888 Msg Info "Checking additional environment variables..."
889 dict for {var msg} $additional_vars {
890 if {![info exists env($var)]} {
891 Msg Info "Environment variable $var is $msg"
893 Msg Info "Found environment variable $var."
898 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
906 proc CheckProjVer {repo_path project {sim 0} {ext_path ""}} {
911 Msg Info "Will check also the version of the simulation files..."
915 if {[
info exists env(HOG_PUSH_TOKEN)] && [
info exist env(CI_PROJECT_ID)] && [
info exist env(CI_API_V4_URL)] } {
916 set token $env(HOG_PUSH_TOKEN)
917 set api_url $env(CI_API_V4_URL)
918 set project_id $env(CI_PROJECT_ID)
923 set project_dir $repo_path/Top/$project
926 Msg Info "$project was modified, continuing with the CI..."
928 Msg Info "Checking if the project has been already built in a previous CI run..."
930 if {$sha == [
GetSHA $repo_path]} {
931 Msg Info "Project was modified in the current commit, Hog will proceed with the build workflow."
934 Msg Info "Checking if project $project has been built in a previous CI run with sha $sha..."
935 set result [
catch {
package require json} JsonFound]
936 if {"$result" != "0"} {
937 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
940 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/$project_id/pipelines"] ret content
941 set pipeline_dict [json::json2dict $content]
942 if {[
llength $pipeline_dict] > 0} {
943 foreach pip $pipeline_dict {
945 set source [
DictGet $pip source]
946 if {$source == "merge_request_event" && [
string first $sha $pip_sha] != -1} {
947 Msg Info "Found pipeline with sha $pip_sha for project $project"
948 set pipeline_id [
DictGet $pip id]
950 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/${project_id}/pipelines/${pipeline_id}/jobs?pagination=keyset&per_page=100"] ret2 content2
951 set jobs_dict [json::json2dict $content2]
952 if {[
llength $jobs_dict] > 0} {
953 foreach job $jobs_dict {
954 set job_name [
DictGet $job name]
956 set artifacts [
DictGet $job artifacts_file]
957 set status [
DictGet $job status]
958 set current_job_name $env(CI_JOB_NAME)
959 if {$current_job_name == $job_name && $status == "success"} {
961 lassign [
ExecuteRet {*}$curl_cmd --location --output artifacts.zip --header "PRIVATE-TOKEN: $token" --url "$api_url/projects/$project_id/jobs/$job_id/artifacts"] ret3 content3
963 Msg CriticalWarning "Cannot download artifacts for job $job_name with id $job_id"
966 lassign [
ExecuteRet unzip -o $repo_path/artifacts.zip] ret_zip
970 Msg Info "Artifacts for job $job_name with id $job_id downloaded and unzipped."
971 file mkdir $repo_path/Projects/$project
972 set fp [open "$repo_path/Projects/$project/skip.me" w+]
984 }
elseif {$ver != -1} {
985 Msg Info "$project was not modified since version: $ver, disabling the CI..."
986 file mkdir $repo_path/Projects/$project
987 set fp [open "$repo_path/Projects/$project/skip.me" w+]
991 Msg Error "Impossible to check the project version. Most likely the repository is not clean. Please, commit your changes before running this command."
1001 proc CheckSyntax {project_name repo_path {project_file ""}} {
1003 update_compile_order
1004 set syntax [check_syntax -return_string]
1005 if {[
string first "CRITICAL" $syntax] != -1} {
1010 lassign [
GetHogFiles -list_files "*.src" "$repo_path/Top/$project_name/list/" $repo_path] src_files dummy
1011 dict for {lib files} $src_files {
1013 set file_extension [file extension $f]
1014 if {$file_extension == ".vhd" || $file_extension == ".vhdl" || $file_extension == ".v" || $file_extension == ".sv"} {
1015 if {[catch {execute_module -tool map -args "--analyze_file=$f"} result]} {
1016 Msg Error "\nResult: $result\n"
1017 Msg Error "Check syntax failed.\n"
1019 if {$result == ""} {
1020 Msg Info "Check syntax was successful for $f.\n"
1022 Msg Warning "Found syntax error in file $f:\n $result\n"
1029 lassign [
GetProjectFiles $project_file] prjLibraries prjProperties prjSimLibraries prjConstraints prjSrcSets prjSimSets prjConSets
1030 dict for {lib sources} $prjLibraries {
1031 if {[file extension $lib] == ".src"} {
1032 foreach f $sources {
1033 Msg Info "Checking Syntax of $f"
1039 Msg Info "The Checking Syntax is not supported by this IDE. Skipping..."
1044 proc CloseProject {} {
1066 proc CompareVersions {ver1 ver2} {
1075 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver1 - x y z]} {
1076 set ver1 [list $x $y $z]
1078 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver2 - x y z]} {
1079 set ver2 [list $x $y $z]
1083 set v1 [
join $ver1 ""]
1085 set v2 [
join $ver2 ""]
1088 if {[
string is integer $v1] && [
string is integer $v2]} {
1089 set ver1 [
expr {[scan [lindex $ver1 0] %d] * 1000000 + [scan [lindex $ver1 1] %d] * 1000 + [scan [lindex $ver1 2] %d]}]
1090 set ver2 [
expr {[scan [lindex $ver2 0] %d] * 1000000 + [scan [lindex $ver2 1] %d] * 1000 + [scan [lindex $ver2 2] %d]}]
1092 if {$ver1 > $ver2} {
1094 }
elseif {$ver1 == $ver2} {
1100 Msg Warning "Version is not numeric: $ver1, $ver2"
1103 return [
expr {$ret}]
1110 proc CheckExtraFiles {libraries constraints simlibraries} {
1113 lassign [
GetProjectFiles] prjLibraries prjProperties prjSimLibraries prjConstraints
1114 set prj_dir [get_property DIRECTORY [current_project]]
1115 file mkdir "$prj_dir/.hog"
1116 set extra_file_name "$prj_dir/.hog/extra.files"
1117 set new_extra_file [open $extra_file_name "w"]
1119 dict for {prjLib prjFiles} $prjLibraries {
1120 foreach prjFile $prjFiles {
1121 if {[file extension $prjFile] == ".xcix"} {
1122 Msg Warning "IP $prjFile is packed in a .xcix core container. \
1123 This files are not suitable for version control systems. We recommend to use .xci files instead."
1126 if {[file extension $prjFile] == ".xci" && [get_property CORE_CONTAINER [get_files $prjFile]] != ""} {
1127 Msg Info "$prjFile is a virtual IP file in a core container. Ignoring it..."
1131 if {[IsInList $prjFile [DictGet $libraries $prjLib]] == 0} {
1132 if {[file extension $prjFile] == ".bd"} {
1133 # Generating BD products to save md5sum of already modified BD
1134 Msg Info "Generating targets of $prjFile..."
1135 generate_target all [get_files $prjFile]
1137 puts $new_extra_file "$prjFile [Md5Sum $prjFile]"
1138 Msg Info "$prjFile (lib: $prjLib) has been generated by an external script. Adding to $extra_file_name..."
1142 close $new_extra_file
1143 set extra_sim_file "$prj_dir/.hog/extrasim.files"
1144 set new_extra_file [open $extra_sim_file "w"]
1146 dict for {prjSimLib prjSimFiles} $prjSimLibraries {
1147 foreach prjSimFile $prjSimFiles {
1148 if {[IsInList $prjSimFile [DictGet $simlibraries $prjSimLib]] == 0} {
1149 puts $new_extra_file "$prjSimFile [Md5Sum $prjSimFile]"
1150 Msg Info "$prjSimFile (lib: $prjSimLib) has been generated by an external script. Adding to $extra_sim_file..."
1154 close $new_extra_file
1155 set extra_con_file "$prj_dir/.hog/extracon.files"
1156 set new_extra_file [open $extra_con_file "w"]
1158 dict for {prjConLib prjConFiles} $prjConstraints {
1159 foreach prjConFile $prjConFiles {
1160 if {[IsInList $prjConFile [DictGet $constraints $prjConLib]] == 0} {
1161 puts $new_extra_file "$prjConFile [Md5Sum $prjConFile]"
1162 Msg Info "$prjConFile has been generated by an external script. Adding to $extra_con_file..."
1166 close $new_extra_file
1173 proc CheckLatestHogRelease {{repo_path .}} {
1176 set current_ver [
Git {describe --always}]
1177 Msg Debug "Current version: $current_ver"
1178 set current_sha [
Git "log $current_ver -1 --format=format:%H"]
1179 Msg Debug "Current SHA: $current_sha"
1182 if {[
OS] == "windows"} {
1183 Msg Info "On windows we cannot set a timeout on 'git fetch', hopefully nothing will go wrong..."
1186 Msg Info "Checking for latest Hog release, can take up to 5 seconds..."
1189 set master_ver [
Git "describe origin/master"]
1190 Msg Debug "Master version: $master_ver"
1191 set master_sha [
Git "log $master_ver -1 --format=format:%H"]
1192 Msg Debug "Master SHA: $master_sha"
1193 set merge_base [
Git "merge-base $current_sha $master_sha"]
1194 Msg Debug "merge base: $merge_base"
1197 if {$merge_base != $master_sha} {
1199 Msg Info "Version $master_ver has been released (https://gitlab.com/hog-cern/Hog/-/releases/$master_ver)"
1200 Msg Status "You should consider updating Hog submodule with the following instructions:"
1202 Msg Status "cd Hog && git checkout master && git pull"
1204 Msg Status "Also update the ref: in your .gitlab-ci.yml to $master_ver"
1208 Msg Info "Latest official version is $master_ver, nothing to do."
1220 proc CheckYmlRef {repo_path allow_failure} {
1221 if {$allow_failure} {
1222 set MSG_TYPE CriticalWarning
1227 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
1228 Msg CriticalWarning "Cannot find package YAML, skipping consistency check of \"ref\" in gilab-ci.yaml file.\n Error message: $YAMLPACKAGE
1229 You can fix this by installing package \"tcllib\""
1237 if {[
file exists .gitlab-ci.yml]} {
1241 if {[
file exists .gitlab-ci.yml]} {
1242 set fp [open ".gitlab-ci.yml" r]
1243 set file_data [read $fp]
1246 Msg $MSG_TYPE "Cannot open file .gitlab-ci.yml"
1250 set file_data "\n$file_data\n\n"
1252 if {[
catch {::yaml::yaml2dict -stream $file_data} yamlDict]} {
1253 Msg $MSG_TYPE "Parsing $repo_path/.gitlab-ci.yml failed. To fix this, check that yaml syntax is respected, remember not to use tabs."
1257 dict for {dictKey dictValue} $yamlDict {
1258 #looking for Hog include in .gitlab-ci.yml
1259 if {"$dictKey" == "include" && (
1260 [lsearch [split $dictValue " {}"] "/hog.yml"] != "-1" ||
1261 [lsearch [split $dictValue " {}"] "/hog-dynamic.yml"] != "-1"
1263 set YML_REF [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "ref"] + 1}]]
1264 set YML_NAME [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "file"] + 1}]]
1268 if {$YML_REF == ""} {
1269 Msg Warning "Hog version not specified in the .gitlab-ci.yml. Assuming that master branch is used."
1271 set YML_REF_F [
Git {name-rev --tags --name-only origin/master}]
1274 set YML_REF_F [regsub -all "'" $YML_REF ""]
1277 if {$YML_NAME == ""} {
1278 Msg $MSG_TYPE "Hog included yml file not specified, assuming hog.yml"
1279 set YML_NAME_F hog.yml
1281 set YML_NAME_F [regsub -all "^/" $YML_NAME ""]
1284 lappend YML_FILES $YML_NAME_F
1290 if {[
catch {::yaml::yaml2dict -file $YML_NAME_F} yamlDict]} {
1291 Msg $MSG_TYPE "Parsing $YML_NAME_F failed."
1295 dict for {dictKey dictValue} $yamlDict {
1296 #looking for included files
1297 if {"$dictKey" == "include"} {
1298 foreach v $dictValue {
1299 lappend YML_FILES [lindex [split $v " "] [expr {[lsearch -dictionary [split $v " "] "local"] + 1}]]
1305 Msg Info "Found the following yml files: $YML_FILES"
1307 set HOGYML_SHA [
GetSHA $YML_FILES]
1308 lassign [
GitRet "log --format=%h -1 --abbrev=7 $YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1310 lassign [
GitRet "log --format=%h -1 --abbrev=7 origin/$YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1312 Msg $MSG_TYPE "Error in project .gitlab-ci.yml. ref: $YML_REF not found"
1313 set EXPECTEDYML_SHA ""
1316 if {!($EXPECTEDYML_SHA eq "")} {
1317 if {$HOGYML_SHA == $EXPECTEDYML_SHA} {
1318 Msg Info "Hog included file $YML_FILES matches with $YML_REF in .gitlab-ci.yml."
1320 Msg $MSG_TYPE "HOG $YML_FILES SHA mismatch.
1321 From Hog submodule: $HOGYML_SHA
1322 From ref in .gitlab-ci.yml: $EXPECTEDYML_SHA
1323 You can fix this in 2 ways: by changing the ref in your repository or by changing the Hog submodule commit"
1326 Msg $MSG_TYPE "One or more of the following files could not be found $YML_FILES in Hog at $YML_REF"
1329 Msg Info ".gitlab-ci.yml not found in $repo_path. Skipping this step"
1350 proc CompareLibDicts {proj_libs list_libs proj_sets list_sets proj_props list_props {severity "CriticalWarning"} {outFile ""} {extraFiles ""}} {
1351 set extra_files $extraFiles
1353 set out_prjlibs $proj_libs
1354 set out_prjprops $proj_props
1356 dict for {prjSet prjLibraries} $proj_sets {
1357 # Check if sets is also in list files
1358 if {[IsInList $prjSet $list_sets]} {
1359 set listLibraries [DictGet $list_sets $prjSet]
1360 # Loop over libraries in fileset
1361 foreach prjLib $prjLibraries {
1362 set prjFiles [DictGet $proj_libs $prjLib]
1363 # Check if library exists in list files
1364 if {[IsInList $prjLib $listLibraries]} {
1365 # Loop over files in library
1366 set listFiles [DictGet $list_libs $prjLib]
1367 foreach prjFile $prjFiles {
1368 set idx [lsearch -exact $listFiles $prjFile]
1369 set listFiles [lreplace $listFiles $idx $idx]
1371 # File is in project but not in list libraries, check if it was generated at creation time...
1372 if {[dict exists $extra_files $prjFile]} {
1373 # File was generated at creation time, checking the md5sum
1374 # Removing the file from the prjFiles list
1375 set idx2 [lsearch -exact $prjFiles $prjFile]
1376 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1377 set new_md5sum [Md5Sum $prjFile]
1378 set old_md5sum [DictGet $extra_files $prjFile]
1379 if {$new_md5sum != $old_md5sum} {
1380 # tclint-disable-next-line line-length
1381 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
1384 set extra_files [dict remove $extra_files $prjFile]
1386 # File is neither in list files nor in extra_files
1387 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1391 # File is both in list files and project, checking properties...
1392 set prjProps [DictGet $proj_props $prjFile]
1393 set listProps [DictGet $list_props $prjFile]
1394 # Check if it is a potential sourced file
1395 if {[IsInList "nosynth" $prjProps] && [IsInList "noimpl" $prjProps] && [IsInList "nosim" $prjProps]} {
1396 # Check if it is sourced
1397 set idx_source [lsearch -exact $listProps "source"]
1398 if {$idx_source >= 0} {
1399 # It is sourced, let's replace the individual properties with source
1400 set idx [lsearch -exact $prjProps "noimpl"]
1401 set prjProps [lreplace $prjProps $idx $idx]
1402 set idx [lsearch -exact $prjProps "nosynth"]
1403 set prjProps [lreplace $prjProps $idx $idx]
1404 set idx [lsearch -exact $prjProps "nosim"]
1405 set prjProps [lreplace $prjProps $idx $idx]
1406 lappend prjProps "source"
1410 foreach prjProp $prjProps {
1411 set idx [lsearch -exact $listProps $prjProp]
1412 set listProps [lreplace $listProps $idx $idx]
1414 MsgAndLog "Property $prjProp of $prjFile was set in project but not in list files" $severity $outFile
1419 foreach listProp $listProps {
1420 if {[string first $listProp "topsim="] == -1 && [string first $listProp "enable"] == -1} {
1421 MsgAndLog "Property $listProp of $prjFile was found in list files but not set in project." $severity $outFile
1426 # Update project prjProps
1427 dict set out_prjprops $prjFile $prjProps
1430 # Loop over remaining files in list libraries
1431 foreach listFile $listFiles {
1432 MsgAndLog "$listFile was found in list files but not in project." $severity $outFile
1436 # Check extra files again...
1437 foreach prjFile $prjFiles {
1438 if {[dict exists $extra_files $prjFile]} {
1439 # File was generated at creation time, checking the md5sum
1440 # Removing the file from the prjFiles list
1441 set idx2 [lsearch -exact $prjFiles $prjFile]
1442 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1443 set new_md5sum [Md5Sum $prjFile]
1444 set old_md5sum [DictGet $extra_files $prjFile]
1445 if {$new_md5sum != $old_md5sum} {
1446 # tclint-disable-next-line line-length
1447 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
1450 set extra_files [dict remove $extra_files $prjFile]
1452 # File is neither in list files nor in extra_files
1453 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1458 # Update prjLibraries
1459 dict set out_prjlibs $prjLib $prjFiles
1462 MsgAndLog "Fileset $prjSet found in project but not in list files" $severity $outFile
1467 return [list $n_diffs $extra_files $out_prjlibs $out_prjprops]
1477 proc CompareVHDL {file1 file2} {
1478 set a [open $file1 r]
1479 set b [open $file2 r]
1481 while {[
gets $a line] != -1} {
1482 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1483 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1489 while {[
gets $b line] != -1} {
1490 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1491 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1500 foreach x $f1 y $f2 {
1502 lappend diff "> $x\n< $y\n\n"
1515 proc Copy {i_dirs o_dir} {
1516 foreach i_dir $i_dirs {
1517 if {[
file isdirectory $i_dir] && [
file isdirectory $o_dir]} {
1518 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]])} {
1519 file delete -force $o_dir/[
file tail $i_dir]
1523 file copy -force $i_dir $o_dir
1538 proc CopyIPbusXMLs {proj_dir path dst {xml_version "0.0.0"} {xml_sha "00000000"} {use_ipbus_sw 0} {generate 0}} {
1539 if {$use_ipbus_sw == 1} {
1540 lassign [
ExecuteRet python3 -c "from __future__ import print_function; from sys import path;print(':'.join(path\[1:\]))"] ret msg
1542 set ::env(PYTHONPATH) $msg
1543 lassign [
ExecuteRet gen_ipbus_addr_decode -h] ret msg
1550 Msg CriticalWarning "Problem while trying to run python: $msg"
1553 set dst [
file normalize $dst]
1555 if {$can_generate == 0} {
1556 if {$generate == 1} {
1557 Msg Error "Cannot generate IPbus address files, IPbus executable gen_ipbus_addr_decode not found or not working: $msg"
1560 Msg Warning "IPbus executable gen_ipbus_addr_decode not found or not working, will not verify IPbus address tables."
1567 set ipb_files [glob -nocomplain $proj_dir/list/*.ipb]
1568 set n_ipb_files [
llength $ipb_files]
1569 if {$n_ipb_files == 0} {
1570 Msg CriticalWarning "No files with .ipb extension found in $proj_dir/list."
1573 set libraries [dict create]
1574 set vhdl_dict [dict create]
1576 foreach ipb_file $ipb_files {
1582 set xmlfiles [dict get $libraries "xml.ipb"]
1584 set xml_list_error 0
1585 foreach xmlfile $xmlfiles {
1586 if {[
file isdirectory $xmlfile]} {
1587 Msg CriticalWarning "Directory $xmlfile listed in xml list file $list_file. Directories are not supported!"
1588 set xml_list_error 1
1591 if {[
file exists $xmlfile]} {
1592 if {[dict exists $vhdl_dict $xmlfile]} {
1593 set vhdl_file [
file normalize $path/[dict get $vhdl_dict $xmlfile]]
1597 lappend vhdls $vhdl_file
1598 set xmlfile [
file normalize $xmlfile]
1599 Msg Info "Copying $xmlfile to $dst and replacing place holders..."
1600 set in [open $xmlfile r]
1601 set out [open $dst/[
file tail $xmlfile] w]
1603 while {[
gets $in line] != -1} {
1604 set new_line [regsub {(.*)__VERSION__(.*)} $line "\\1$xml_version\\2"]
1605 set new_line2 [regsub {(.*)__GIT_SHA__(.*)} $new_line "\\1$xml_sha\\2"]
1606 puts $out $new_line2
1610 lappend xmls [
file tail $xmlfile]
1612 Msg Warning "XML file $xmlfile not found"
1615 if {${xml_list_error}} {
1616 Msg Error "Invalid files added to $list_file!"
1619 set cnt [
llength $xmls]
1620 Msg Info "$cnt xml file/s copied"
1623 if {$can_generate == 1} {
1626 file mkdir "address_decode"
1628 foreach x $xmls v $vhdls {
1630 set x [
file normalize ../$x]
1631 if {[
file exists $x]} {
1632 lassign [
ExecuteRet gen_ipbus_addr_decode --no-timestamp $x 2>&1] status log
1634 set generated_vhdl ./ipbus_decode_[
file rootname [
file tail $x]].vhd
1635 if {$generate == 1} {
1636 Msg Info "Copying generated VHDL file $generated_vhdl into $v (replacing if necessary)"
1637 file copy -force -- $generated_vhdl $v
1639 if {[
file exists $v]} {
1641 set n [
llength $diff]
1643 Msg CriticalWarning "$v does not correspond to its XML $x, [
expr {$n / 3}] line/s differ:"
1644 Msg Status [
join $diff "\n"]
1645 set diff_file [open ../diff_[
file rootname [
file tail $x]].txt w]
1646 puts $diff_file $diff
1649 Msg Info "[
file tail $x] and $v match."
1652 Msg Warning "VHDL address map file $v not found."
1656 Msg Warning "Address map generation failed for [
file tail $x]: $log"
1659 Msg Warning "Copied XML file $x not found."
1662 Msg Info "Skipped verification of [
file tail $x] as no VHDL file was specified."
1666 file delete -force address_decode
1678 proc DescriptionFromConf {conf_file} {
1679 set f [open $conf_file "r"]
1680 set lines [
split [read $f] "\n"]
1682 set second_line [
lindex $lines 1]
1685 if {![regexp {\#+ *(.+)} $second_line - description]} {
1689 if {[regexp -all {test|Test|TEST} $description]} {
1690 set description "test"
1703 proc DictGet {dictName keyName {default ""}} {
1704 if {[dict exists $dictName $keyName]} {
1705 return [dict get $dictName $keyName]
1716 proc DictSort {dict args} {
1718 foreach key [lsort {*}$args [dict keys $dict]] {
1719 dict set res $key [dict get $dict $key]
1729 proc DoxygenVersion {target_version} {
1730 set ver [
split $target_version "."]
1731 set v [
Execute doxygen --version]
1732 Msg Info "Found Doxygen version: $v"
1733 set current_ver [
split $v ". "]
1734 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
1735 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
1737 return [
expr {$target <= $current}]
1748 proc eos {command {attempt 1}} {
1750 if {![
info exists env(EOS_MGM_URL)]} {
1751 Msg Warning "Environment variable EOS_MGM_URL not set, setting it to default value root://eosuser.cern.ch"
1752 set ::env(EOS_MGM_URL) "root://eosuser.cern.ch"
1755 Msg Warning "The value of attempt should be 1 or more, not $attempt, setting it to 1 as default"
1758 for {
set i 0} {$i < $attempt} {
incr i} {
1759 set ret [
catch {
exec -ignorestderr eos {*}$command} result]
1764 set wait [
expr {1 + int(rand() * 29)}]
1765 Msg Warning "Command $command failed ($i/$attempt): $result, trying again in $wait seconds..."
1766 after [
expr {$wait * 1000}]
1770 return [list $ret $result]
1780 proc Execute {args} {
1784 Msg Error "Command [
join $args] returned error code: $ret"
1798 proc ExecuteRet {args} {
1800 if {[
llength $args] == 0} {
1801 Msg CriticalWarning "No argument given"
1805 set ret [
catch {
exec -ignorestderr {*}$args} result]
1808 return [list $ret $result]
1815 proc ExtractFilesSection {file_data} {
1816 set in_files_section 0
1819 foreach line $file_data {
1820 if {[regexp {^ *\[ *files *\]} $line]} {
1821 set in_files_section 1
1824 if {$in_files_section} {
1825 if {[regexp {^ *\[.*\]} $line]} {
1828 lappend result $line
1833 if {!$in_files_section} {
1847 proc ExtractVersionFromTag {tag} {
1848 if {[regexp {^(?:b(\d+))?v(\d+)\.(\d+).(\d+)(?:-\d+)?$} $tag -> mr M m p]} {
1853 Msg Warning "Repository tag $tag is not in a Hog-compatible format."
1859 return [list $M $m $p $mr]
1869 proc FileCommitted {File} {
1871 set currentDir [
pwd]
1872 cd [
file dirname [
file normalize $File]]
1873 set GitLog [
Git ls-files [
file tail $File]]
1874 if {$GitLog == ""} {
1875 Msg CriticalWarning "File [
file normalize $File] is not in the git repository. Please add it with:\n git add [
file normalize $File]\n"
1886 proc FindCommonGitChild {SHA1 SHA2} {
1888 set commits [
Git {log --oneline --merges}]
1891 foreach line [
split $commits "\n"] {
1892 set commit [
lindex [
split $line] 0]
1896 set ancestor $commit
1909 proc findFiles {basedir pattern} {
1912 set basedir [
string trimright [
file join [
file normalize $basedir] { }]]
1918 foreach fileName [glob -nocomplain -type {f r} -path $basedir $pattern] {
1919 lappend fileList $fileName
1923 foreach dirName [glob -nocomplain -type {d r} -path $basedir *] {
1926 set subDirList [
findFiles $dirName $pattern]
1927 if {[
llength $subDirList] > 0} {
1928 foreach subDirFile $subDirList {
1929 lappend fileList $subDirFile
1940 proc FindFileType {file_name} {
1941 set extension [
file extension $file_name]
1944 set file_extension "USE_SIGNALTAP_FILE"
1947 set file_extension "VHDL_FILE"
1950 set file_extension "VHDL_FILE"
1953 set file_extension "VERILOG_FILE"
1956 set file_extension "SYSTEMVERILOG_FILE"
1959 set file_extension "SDC_FILE"
1962 set file_extension "PDC_FILE"
1965 set file_extension "NDC_FILE"
1968 set file_extension "FDC_FILE"
1971 set file_extension "SOURCE_FILE"
1974 set file_extension "IP_FILE"
1977 set file_extension "QSYS_FILE"
1980 set file_extension "QIP_FILE"
1983 set file_extension "SIP_FILE"
1986 set file_extension "BSF_FILE"
1989 set file_extension "BDF_FILE"
1992 set file_extension "COMMAND_MACRO_FILE"
1995 set file_extension "VQM_FILE"
1998 set file_extension "ERROR"
1999 Msg Error "Unknown file extension $extension"
2002 return $file_extension
2009 proc FindNewestVersion {versions} {
2010 set new_ver 00000000
2011 foreach ver $versions {
2013 if {[
expr 0x$ver > 0x$new_ver]} {
2023 proc FindRepoRoot {start_dir} {
2024 if {[
file pathtype $start_dir] eq "relative"} {
2025 set dir [
file normalize [
file join [
pwd] $start_dir]]
2030 if {[
file exists [
file join $dir Top]] && [
file exists [
file join $dir Projects]]} {
2033 set parent [
file dirname $dir]
2034 if {$parent eq $dir} {
2046 proc FindVhdlVersion {file_name} {
2047 set extension [
file extension $file_name]
2050 set vhdl_version "-hdl_version VHDL_2008"
2053 set vhdl_version "-hdl_version VHDL_2008"
2060 return $vhdl_version
2067 proc FormatGeneric {generic} {
2068 if {[
string is integer "0x$generic"]} {
2069 return [
format "32'h%08X" "0x$generic"]
2072 return [
format "32'h%08X" 0]
2082 proc GenerateBitstream {{run_folder ""} {repo_path .} {njobs 1}} {
2083 Msg Info "Starting write bitstream flow..."
2085 set revision [get_current_revision]
2086 if {[
catch {execute_module -tool asm} result]} {
2087 Msg Error "Result: $result\n"
2088 Msg Error "Generate bitstream failed. See the report file.\n"
2090 Msg Info "Generate bitstream was successful for revision $revision.\n"
2093 Msg Info "Run GENERATEPROGRAMMINGDATA ..."
2094 if {[
catch {run_tool -name {GENERATEPROGRAMMINGDATA}}]} {
2095 Msg Error "GENERATEPROGRAMMINGDATA FAILED!"
2097 Msg Info "GENERATEPROGRAMMINGDATA PASSED."
2099 Msg Info "Sourcing Hog/Tcl/integrated/post-bitstream.tcl"
2100 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
2102 prj_run Export -impl Implementation0 -task Bitgen
2112 proc GenerateQsysSystem {qsysFile commandOpts} {
2114 if {[
file exists $qsysFile] != 0} {
2115 set qsysPath [
file dirname $qsysFile]
2116 set qsysName [
file rootname [
file tail $qsysFile]]
2117 set qsysIPDir "$qsysPath/$qsysName"
2118 set qsysLogFile "$qsysPath/$qsysName.qsys-generate.log"
2121 if {![
info exists ::env(QSYS_ROOTDIR)]} {
2122 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
2123 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
2124 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
2126 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
2129 set qsys_rootdir $::env(QSYS_ROOTDIR)
2132 set cmd "$qsys_rootdir/qsys-generate"
2133 set cmd_options "$qsysFile --output-directory=$qsysIPDir $commandOpts"
2134 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
2135 Msg Info "Executing: $cmd $cmd_options"
2136 Msg Info "Saving logfile in: $qsysLogFile"
2137 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
2138 set makeRet [
lindex [dict get $opt -errorcode] end]
2139 Msg CriticalWarning "$cmd returned with $makeRet"
2142 Msg Error " Could not execute command $cmd"
2146 set qsysIPFileList [
concat \
2147 [glob -nocomplain -directory $qsysIPDir -types f *.ip *.qip] \
2148 [glob -nocomplain -directory "$qsysIPDir/synthesis" -types f *.ip *.qip *.vhd *.vhdl]
2150 foreach qsysIPFile $qsysIPFileList {
2151 if {[
file exists $qsysIPFile] != 0} {
2153 set_global_assignment -name $qsysIPFileType $qsysIPFile
2155 set IpMd5Sum [
Md5Sum $qsysIPFile]
2157 set fileDir [
file normalize "./hogTmp"]
2158 set fileName "$fileDir/.hogQsys.md5"
2159 if {![
file exists $fileDir]} {
2162 set hogQsysFile [open $fileName "a"]
2163 set fileEntry "$qsysIPFile\t$IpMd5Sum"
2164 puts $hogQsysFile $fileEntry
2169 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
2179 proc GenericToSimulatorString {prop_dict target} {
2181 dict for {theKey theValue} $prop_dict {
2190 regexp {([0-9]*)('h)([0-9a-fA-F]*)} $theValue valueHexFull valueNumBits valueHexFlag valueHex
2191 regexp {^([0-9]*)$} $theValue valueIntFull ValueInt
2192 regexp {(?!^\d+$)^.+$} $theValue valueStrFull ValueStr
2193 if {[string tolower $target] == "vivado" || [string tolower $target] == "xsim"} {
2194 if {[string tolower $theValue] == "true" || [string tolower $theValue] == "false"} {
2195 set prj_generics "$prj_generics $theKey=$theValue"
2196 } elseif {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2197 set prj_generics "$prj_generics $theKey=$valueHexFull"
2198 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2199 set prj_generics "$prj_generics $theKey=$ValueInt"
2200 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2201 set prj_generics "$prj_generics $theKey=\"$ValueStr\""
2203 set prj_generics "$prj_generics $theKey=\"$theValue\""
2205 } elseif {[lsearch -exact [GetSimulators] [string tolower $target]] >= 0} {
2206 if {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2208 scan $valueNumBits %d numBits
2210 scan $valueHex %x numHex
2211 binary scan [binary format "I" $numHex] "B*" binval
2212 set numBits [expr {$numBits - 1}]
2213 set numBin [string range $binval end-$numBits end]
2214 set prj_generics "$prj_generics $theKey=\"$numBin\""
2215 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2216 set prj_generics "$prj_generics $theKey=$ValueInt"
2217 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2218 set prj_generics "$prj_generics {$theKey=\"$ValueStr\"}"
2220 set prj_generics "$prj_generics {$theKey=\"$theValue\"}"
2223 Msg Warning "Target : $target not implemented"
2226 return $prj_generics
2234 proc GetConfFiles {proj_dir} {
2235 Msg Debug "GetConfFiles called with proj_dir=$proj_dir"
2236 if {![
file isdirectory $proj_dir]} {
2237 Msg Error "$proj_dir is supposed to be the top project directory"
2240 set conf_file [
file normalize $proj_dir/hog.conf]
2241 set sim_file [
file normalize $proj_dir/sim.conf]
2242 set pre_tcl [
file normalize $proj_dir/pre-creation.tcl]
2243 set post_tcl [
file normalize $proj_dir/post-creation.tcl]
2244 set pre_rtl [
file normalize $proj_dir/pre-rtl.tcl]
2245 set post_rtl [
file normalize $proj_dir/post-rtl.tcl]
2247 return [list $conf_file $sim_file $pre_tcl $post_tcl $pre_rtl $post_rtl]
2256 proc GetCustomCommands {parameters {directory .}} {
2257 set commands_dict [dict create]
2258 set commands_files [glob -nocomplain $directory/*.tcl]
2260 if {[
llength $commands_files] == 0} {
2264 foreach file $commands_files {
2269 if {$custom_cmd eq ""} {
2274 set custom_cmd_name [dict get $custom_cmd NAME]
2276 Msg Debug "Loaded custom command '$custom_cmd_name' from $file"
2279 if {[dict exists $commands_dict $custom_cmd_name]} {
2280 Msg Error "Custom command '$custom_cmd_name' in $file already defined as: \[dict get $commands_dict $custom_cmd_name\]. Skipping."
2286 set custom_cmd_name [
string toupper $custom_cmd_name]
2287 dict set commands_dict $custom_cmd_name $custom_cmd
2290 return $commands_dict
2293 proc SanitizeCustomCommand {cmdDict file parameters} {
2296 foreach k [dict keys $cmdDict] {
2297 set K [
string toupper $k]
2298 dict set normalized $K [dict get $cmdDict $k]
2301 set cmdDict $normalized
2302 if {![dict exists $cmdDict NAME]} {
2303 Msg Error "Custom command in $file missing required key NAME. Skipping."
2306 if {![dict exists $cmdDict SCRIPT]} {
2307 Msg Error "Custom command '$[dict get $cmdDict NAME]' in $file missing SCRIPT. Skipping."
2312 set allowed {NAME DESCRIPTION OPTIONS CUSTOM_OPTIONS SCRIPT IDE NO_EXIT}
2313 foreach k [dict keys $cmdDict] {
2314 if {[lsearch -exact $allowed $k] < 0} {
2315 Msg Warning "Custom command '[dict get $cmdDict NAME]' in $file: unknown key '$k'. Allowed: $allowed. Skipping."
2320 set name [
string trim [dict get $cmdDict NAME]]
2322 Msg Error "Custom command in $file has empty NAME. Skipping."
2326 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]+$} $name]} {
2327 Msg Error "Custom command NAME '$name' (file $file) contains invalid characters."
2331 if {![dict exists $cmdDict DESCRIPTION]} {
2332 dict set cmdDict DESCRIPTION "No description provided."
2336 set hog_parameters {}
2337 foreach p $parameters {
2338 lappend hog_parameters [
lindex $p 0]
2343 if {[dict exists $cmdDict OPTIONS]} {
2344 set raw_opts [dict get $cmdDict OPTIONS]
2345 if {![
llength $raw_opts]} {
2349 foreach item $raw_opts {
2351 foreach p $parameters {
2352 set hog_parameter [
lindex $p 0]
2353 if { $item eq $hog_parameter } {
2354 lappend hog_options $p
2360 Msg Warning "Custom command '$name' in $file: option '$item' not found in Hog parameters. Skipping."
2363 dict set cmdDict OPTIONS $hog_options
2365 dict set cmdDict CUSTOM_OPTIONS {}
2372 if {[dict exists $cmdDict CUSTOM_OPTIONS]} {
2373 set raw_opts [dict get $cmdDict CUSTOM_OPTIONS]
2374 if {![
llength $raw_opts]} {
2377 foreach item $raw_opts {
2379 if {[
llength $item] != 2 && [
llength $item] != 3} {
2380 Msg Error "Bad custom option: \[$item\]. Custom command '$name' in $file: \
2381 each CUSTOM_OPTIONS entry must be {option \"help\"} for flags \
2382 and {option \"default_value\" \"help\"} for options with arguments. Skipping command."
2386 if {[
llength $item] == 2} {
2387 lassign $item opt help
2390 lassign $item opt def help
2393 if { [
IsInList $opt $hog_parameters] == 1 } {
2394 Msg Warning "Custom command '$name' in $file: option '$opt' already defined in Hog parameters. Skipping."
2400 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]*(\.arg)?$} $opt]} {
2401 Msg Error "Custom command '$name' in $file: invalid option name '$opt'."
2406 Msg Warning "Custom command '$name' option '$opt' has empty help text."
2410 dict set cmdDict CUSTOM_OPTIONS {}
2414 if {[dict exists $cmdDict NO_EXIT]} {
2415 set no_exit [dict get $cmdDict NO_EXIT]
2416 set no_exit [
string tolower [
string trim $no_exit]]
2418 if {$no_exit eq "1" || $no_exit eq "true"} {
2424 dict set cmdDict NO_EXIT $no_exit
2426 dict set cmdDict NO_EXIT 0
2432 proc LoadCustomCommandFile {file parameters} {
2434 set dir [
file dirname $file]
2436 unset -nocomplain ::hog_command
2437 set rc [
catch {source $file} err]
2440 Msg Error "Error sourcing custom command file $file: $err"
2443 if {![
info exists ::hog_command]} {
2444 Msg Warning "File $file did not define ::hog_command. Skipping."
2447 set cmdDict $::hog_command
2449 if {[
catch {dict size $cmdDict}]} {
2450 Msg Error "In $file ::hog_command is not a valid dict. Skipping."
2460 proc GetDateAndTime {commit} {
2461 set clock_seconds [
clock seconds]
2464 set date [
Git "log -1 --format=%cd --date=format:%d%m%Y $commit"]
2465 set timee [
Git "log -1 --format=%cd --date=format:00%H%M%S $commit"]
2467 Msg Warning "Found Git version older than 2.9.3. Using current date and time instead of commit time."
2468 set date [
clock format $clock_seconds -format {%d%m%Y}]
2469 set timee [
clock format $clock_seconds -format {00%H%M%S}]
2471 return [list $date $timee]
2483 proc GetFile {file fileset} {
2486 set Files [get_files -all $file -of_object [get_filesets $fileset]]
2487 set f [
lindex $Files 0]
2495 puts "***DEBUG Hog:GetFile $file"
2504 proc GetFileGenerics {filename {entity ""}} {
2506 if {[
string equal $file_type "VERILOG_FILE"] || [
string equal $file_type "SYSTEMVERILOG_FILE"]} {
2508 }
elseif {[
string equal $file_type "VHDL_FILE"]} {
2511 Msg CriticalWarning "Could not determine extension of top level file."
2520 proc GetGenericsFromConf {proj_dir} {
2521 set generics_dict [dict create]
2522 set top_dir "Top/$proj_dir"
2523 set conf_file "$top_dir/hog.conf"
2525 Msg Debug "GetGenericsFromConf called with proj_dir=$proj_dir, top_dir=$top_dir"
2527 if {[
file exists $conf_file]} {
2529 if {[dict exists $properties generics]} {
2530 set generics_dict [dict get $properties generics]
2533 Msg Warning "File $conf_file not found."
2535 return $generics_dict
2548 proc GetSimSets {project_name repo_path {simsets ""} {ghdl 0} {no_conf 0}} {
2549 set simsets_dict [dict create]
2550 set list_dir "$repo_path/Top/$project_name/list"
2552 if {$simsets != ""} {
2553 foreach s $simsets {
2554 set list_file "$list_dir/$s.sim"
2555 if {[
file exists $list_file]} {
2556 lappend list_files $list_file
2557 }
elseif {$s != "sim_1"} {
2558 Msg CriticalWarning "Simulation set list file $list_file not found."
2563 set list_files [glob -nocomplain -directory $list_dir "*.sim"]
2567 set proj_dir [
file normalize $repo_path/Top/$project_name]
2568 set sim_file [
file normalize $proj_dir/sim.conf]
2570 foreach list_file $list_files {
2571 set file_name [
file tail $list_file]
2572 set simset_name [
file rootname $file_name]
2573 set fp [open $list_file r]
2574 set file_data [read $fp]
2576 set data [
split $file_data "\n"]
2578 set firstline [
lindex $data 0]
2580 if {[regexp {^ *\# *Simulator} $firstline]} {
2581 set simulator_prop [regexp -all -inline {\S+} $firstline]
2582 set simulator [
string tolower [
lindex $simulator_prop end]]
2584 Msg Warning "Simulator not set in $simset_name.sim. \
2585 The first line of $simset_name.sim should be #Simulator <SIMULATOR_NAME>,\
2586 where <SIMULATOR_NAME> can be xsim, questa, modelsim, ghdl, riviera, activehdl,\
2587 ies, or vcs, e.g. #Simulator questa.\
2588 Setting simulator by default to xsim."
2589 set simulator "xsim"
2591 if {$simulator eq "skip_simulation"} {
2592 Msg Info "Skipping simulation for $simset_name"
2595 if {($ghdl == 1 && $simulator != "ghdl") || ($ghdl == 0 && $simulator == "ghdl")} {
2599 set SIM_PROPERTIES ""
2600 if {[
file exists $sim_file] && $no_conf == 0} {
2601 set SIM_PROPERTIES [
ReadConf $sim_file]
2604 set global_sim_props [dict create]
2605 dict set global_sim_props "properties" [
DictGet $SIM_PROPERTIES "sim"]
2606 dict set global_sim_props "generics" [
DictGet $SIM_PROPERTIES "generics"]
2607 dict set global_sim_props "hog" [
DictGet $SIM_PROPERTIES "hog"]
2610 set sim_dict [dict create]
2611 dict set sim_dict "simulator" $simulator
2612 if {[dict exists $SIM_PROPERTIES $simset_name]} {
2613 dict set sim_dict "properties" [
DictGet $SIM_PROPERTIES $simset_name]
2614 dict set sim_dict "generics" [
DictGet $SIM_PROPERTIES "$simset_name:generics"]
2615 dict set sim_dict "hog" [
DictGet $SIM_PROPERTIES "$simset_name:hog"]
2616 }
elseif {$no_conf == 0} {
2618 set conf_dict [
ReadConf $list_file]
2619 set sim_dict [
MergeDict $sim_dict $conf_dict 0]
2621 set sim_dict [
MergeDict $sim_dict $global_sim_props 0]
2622 dict set simsets_dict $simset_name $sim_dict
2624 return $simsets_dict
2632 proc GetSimsetGenericsFromConf {proj_dir} {
2633 set simsets_generics_dict [dict create]
2634 set top_dir "Top/$proj_dir"
2635 set conf_file "$top_dir/sim.conf"
2638 if {[
file exists $conf_file]} {
2641 set simsets_generics_dict [dict filter $properties key *:generics]
2643 Msg Warning "File $conf_file not found."
2645 return $simsets_generics_dict
2656 proc GetGroupName {proj_dir repo_dir} {
2657 if {[regexp {^(.*)/(Top|Projects)/+(.*?)/*$} $proj_dir dummy possible_repo_dir proj_or_top dir]} {
2659 if {[
file normalize $repo_dir] eq [
file normalize $possible_repo_dir]} {
2660 set group [
file dir $dir]
2661 if {$group == "."} {
2666 Msg Warning "Project directory $proj_dir seems to be in $possible_repo_dir which is not a the main Git repository $repo_dir."
2669 Msg Warning "Could not parse project directory $proj_dir"
2682 proc GetHogDescribe {proj_dir {repo_path .}} {
2683 lassign [
GetRepoVersions $proj_dir $repo_path] global_commit global_version
2684 if {$global_commit == 0} {
2686 set new_sha "[
string toupper [
GetSHA]]"
2689 set new_sha [
string toupper $global_commit]
2709 proc GetHogFiles {args} {
2712 if {[
catch {
package require cmdline} ERROR]} {
2713 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
2720 {list_files.arg "" "The file wildcard, if not specified all Hog list files will be looked for."}
2721 {sha_mode "Forwarded to ReadListFile, see there for info."}
2722 {ext_path.arg "" "Path for the external libraries forwarded to ReadListFile."}
2723 {print_log "Forwarded to ReadListFile, see there for info."}
2725 set usage "USAGE: GetHogFiles \[options\] <list path> <repository path>"
2726 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
2730 set list_path [
lindex $args 0]
2731 set repo_path [
lindex $args 1]
2733 set list_files $options(list_files)
2734 set sha_mode $options(sha_mode)
2735 set ext_path $options(ext_path)
2736 set print_log $options(print_log)
2738 if {$sha_mode == 1} {
2739 set sha_mode_opt "-sha_mode"
2744 if {$print_log == 1} {
2745 set print_log_opt "-print_log"
2747 set print_log_opt ""
2751 if {$list_files == ""} {
2752 set list_files {.src,.con,.sim,.ext}
2754 set libraries [dict create]
2755 set properties [dict create]
2756 set list_files [glob -nocomplain -directory $list_path "*{$list_files}"]
2757 set filesets [dict create]
2759 foreach f $list_files {
2760 set ext [
file extension $f]
2761 if {$ext == ".ext"} {
2762 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $ext_path"] l p fs
2764 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $repo_path"] l p fs
2767 set properties [
MergeDict $p $properties]
2768 Msg Debug "list file $f, filesets: $fs"
2770 Msg Debug "Merged filesets $filesets"
2778 set proj_conf [
file normalize [
file join [
file dirname $list_path] hog.conf]]
2779 if {[
file exists $proj_conf]} {
2781 set already_tracked [dict create]
2782 dict for {libname libfiles} $libraries {
2783 foreach lf $libfiles { dict set already_tracked [file normalize $lf] 1 }
2785 dict for {comp_name cfg_abs} $hls_configs {
2786 if {[dict exists $already_tracked $cfg_abs]} {
2787 Msg Debug "HLS component '$comp_name': cfg $cfg_abs already tracked via a .src, skipping conf-based GetHogFiles discovery."
2790 set lib_name "${comp_name}.src"
2791 dict lappend libraries $lib_name $cfg_abs
2792 dict set already_tracked $cfg_abs 1
2793 set hls_extras [ExpandHlsConfigFiles $cfg_abs]
2794 Msg Info "HLS component '$comp_name' (from hog.conf): tracking [expr {1 + [llength $hls_extras]}] file(s) via $cfg_abs"
2795 if {$print_log == 1} {
2796 Msg Status "\nhog.conf \[hls:$comp_name\] (auto-discovered)"
2797 set rel_cfg [Relative $repo_path $cfg_abs 1]
2798 if {$rel_cfg eq ""} { set rel_cfg $cfg_abs }
2799 if {[llength $hls_extras] == 0} {
2800 Msg Status "└── $rel_cfg"
2802 Msg Status "├── $rel_cfg"
2803 Msg Status " Inside [file tail $cfg_abs] (HLS auto-expand):"
2806 set n_extras [llength $hls_extras]
2808 foreach hls_extra $hls_extras {
2810 if {[dict exists $already_tracked $hls_extra]} { continue }
2811 dict lappend libraries $lib_name $hls_extra
2812 dict set already_tracked $hls_extra 1
2813 if {$print_log == 1} {
2814 if {$i == $n_extras} { set pad "└──" } else { set pad "├──" }
2815 set rel_extra [Relative [file dirname $cfg_abs] $hls_extra 1]
2816 if {$rel_extra eq ""} { set rel_extra $hls_extra }
2817 Msg Status " $pad $rel_extra"
2823 return [list $libraries $properties $filesets]
2830 proc GetIDECommand {proj_conf {custom_ver ""}} {
2831 if {$custom_ver ne ""} {
2832 set ide_name_and_ver [
string tolower "$custom_ver"]
2833 }
elseif {[
file exists $proj_conf]} {
2834 set ide_name_and_ver [
string tolower [
GetIDEFromConf $proj_conf]]
2836 Msg Error "Configuration file $proj_conf not found."
2839 set ide_name [
lindex [regexp -all -inline {\S+} $ide_name_and_ver] 0]
2841 if {$ide_name eq "vivado" || $ide_name eq "vivado_vitis_classic" || $ide_name eq "vivado_vitis_unified" || $ide_name eq "vitis_unified"} {
2842 set command "vivado"
2844 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2845 set after_tcl_script " -tclargs "
2847 }
elseif {$ide_name eq "planahead"} {
2848 set command "planAhead"
2850 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2851 set after_tcl_script " -tclargs "
2853 }
elseif {$ide_name eq "quartus"} {
2854 set command "quartus_sh"
2856 set before_tcl_script " -t "
2857 set after_tcl_script " "
2859 }
elseif {$ide_name eq "libero"} {
2862 set command "libero"
2863 set before_tcl_script "SCRIPT:"
2864 set after_tcl_script " SCRIPT_ARGS:\""
2866 }
elseif {$ide_name eq "diamond"} {
2867 set command "diamondc"
2868 set before_tcl_script " "
2869 set after_tcl_script " "
2871 }
elseif {$ide_name eq "vitis_classic"} {
2874 set before_tcl_script ""
2875 set after_tcl_script " "
2877 }
elseif {$ide_name eq "ghdl"} {
2879 set before_tcl_script " "
2880 set after_tcl_script " "
2883 Msg Error "IDE: $ide_name not known."
2886 return [list $command $before_tcl_script $after_tcl_script $end_marker]
2892 proc GetIDEFromConf {conf_file} {
2893 set f [open $conf_file "r"]
2896 if {[regexp -all {^\# *(\w*) *(vitis_(?:classic|unified))? *(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)?(_.*)? *$} $line dummy ide vitisflag version patch]} {
2897 if {[
info exists vitisflag] && $vitisflag != ""} {
2898 set ide "${ide}_${vitisflag}"
2901 if {[
info exists version] && $version != ""} {
2907 set ret [list $ide $ver]
2909 Msg CriticalWarning "The first line of hog.conf should be \#<IDE name> <version>, \
2910 where <IDE name>. is quartus, vivado, planahead, libero, diamond or ghdl, \
2911 and <version> the tool version, e.g. \#vivado 2020.2. Will assume vivado."
2912 set ret [list "vivado" "0.0.0"]
2919 proc GetIDEName {} {
2921 return "ISE/PlanAhead"
2939 proc GetIDEVersion {} {
2942 regexp {\d+\.\d+(\.\d+)?} [version -short] ver
2947 regexp {[\.0-9]+} $quartus(version) ver
2950 set ver [get_libero_version]
2953 regexp {\d+\.\d+(\.\d+)?} [sys_install version] ver
2956 regexp {\d+\.\d+(\.\d+)?} [version] ver
2959 set vitis_output [
exec vitis --version 2>@1]
2960 regexp {[Vv]itis\s+v?(\d+\.\d+(?:\.\d+)?)} $vitis_output -> ver
2974 proc GetLinkedFile {link_file} {
2975 if {[
file type $link_file] eq "link"} {
2976 if {[
OS] == "windows"} {
2978 lassign [
ExecuteRet realpath $link_file] ret msg
2979 lassign [
ExecuteRet cygpath -m $msg] ret2 msg2
2980 if {$ret == 0 && $ret2 == 0} {
2982 Msg Debug "Found link file $link_file on Windows, the linked file is: $real_file"
2984 Msg CriticalWarning "[
file normalize $link_file] is a soft link. \
2985 Soft link are not supported on Windows and readlink.exe or cygpath.exe did not work: readlink=$ret: $msg, cygpath=$ret2: $msg2."
2986 set real_file $link_file
2990 set linked_file [
file link $link_file]
2991 set real_file [
file normalize [
file dirname $link_file]/$linked_file]
2994 if {![
file exists $real_file]} {
2995 Msg Warning "$link_file is a broken link, because the linked file: $real_file does not exist."
2998 Msg Warning "$link file is not a soft link"
2999 set real_file $link_file
3012 proc GetMaxThreads {proj_dir} {
3014 if {[
file exists $proj_dir/hog.conf]} {
3016 if {[dict exists $properties parameters]} {
3017 set propDict [dict get $properties parameters]
3018 if {[dict exists $propDict MAX_THREADS]} {
3019 set maxThreads [dict get $propDict MAX_THREADS]
3023 Msg Warning "File $proj_dir/hog.conf not found. Max threads will be set to default value 1"
3036 proc GetModifiedFiles {{repo_path "."} {pattern "."}} {
3039 set ret [
Git "ls-files --modified $pattern"]
3048 proc GetOptions {argv parameters} {
3051 set param_list [list]
3052 set option_list [list]
3054 foreach p $parameters {
3055 lappend param_list [
lindex $p 0]
3059 while {$index < [
llength $argv]} {
3060 set arg [
lindex $argv $index]
3061 if {[
string first - $arg] == 0} {
3062 set option [
string trimleft $arg "-"]
3064 lappend option_list $arg
3065 if {[lsearch -regex $param_list "$option\[.arg]?"] >= 0 } {
3066 if {[lsearch -regex $param_list "$option\[.arg]"] >= 0 } {
3067 lappend option_list [
lindex $argv $index]
3072 lappend arg_list $arg
3076 Msg Debug "Argv: $argv"
3077 Msg Debug "Options: $option_list"
3078 Msg Debug "Arguments: $arg_list"
3079 return [list $option_list $arg_list]
3097 proc GetProjectFiles {{project_file ""}} {
3098 set libraries [dict create]
3099 set simlibraries [dict create]
3100 set constraints [dict create]
3101 set properties [dict create]
3102 set consets [dict create]
3103 set srcsets [dict create]
3104 set simsets [dict create]
3107 set all_filesets [get_filesets]
3108 set simulator [get_property target_simulator [current_project]]
3109 set top [get_property "top" [current_fileset]]
3111 dict lappend properties $topfile "top=$top"
3113 foreach fs $all_filesets {
3114 if {$fs == "utils_1"} {
3119 set all_files [get_files -quiet -of_objects [get_filesets $fs]]
3120 set fs_type [get_property FILESET_TYPE [get_filesets $fs]]
3122 if {$fs_type == "BlockSrcs"} {
3124 set dict_fs "sources_1"
3128 foreach f $all_files {
3135 if {[
lindex [get_property IS_GENERATED [
GetFile $f $fs]] 0] != 0} {
3140 if {[get_property FILE_TYPE [
GetFile $f $fs]] == "Configuration Files"} {
3146 if {[get_property CORE_CONTAINER [
GetFile $f $fs]] != ""} {
3147 if {[
file extension $f] == ".xcix"} {
3148 set f [get_property CORE_CONTAINER [
GetFile $f $fs]]
3156 if {[get_property SCOPED_TO_REF [
GetFile $f $fs]] != ""} {
3157 dict lappend properties $f "scoped_to_ref=[get_property SCOPED_TO_REF [
GetFile $f $fs]]"
3162 if {[get_property SCOPED_TO_CELLS [
GetFile $f $fs]] != ""} {
3163 dict lappend properties $f "scoped_to_cells=[regsub " " [get_property SCOPED_TO_CELLS [
GetFile $f $fs]] ","]"
3167 if {[
IsInList "PARENT_COMPOSITE_FILE" [list_property [
GetFile $f $fs]]]} {
3172 if {[
file tail $f] == "nocattrs.dat"} {
3177 if {[
file extension $f] != ".coe"} {
3178 set f [
file normalize $f]
3181 set type [get_property FILE_TYPE [
GetFile $f $fs]]
3183 set lib [get_property -quiet LIBRARY [
GetFile $f $fs]]
3186 Msg Debug "File $f Extension [
file extension $f] Type [
lindex $type 0]"
3188 if {[
string equal [
lindex $type 0] "VHDL"] && [
llength $type] == 1} {
3190 }
elseif {[
string equal [
lindex $type 0] "Block"] && [
string equal [
lindex $type 1] "Designs"]} {
3193 }
elseif {[
string equal $type "SystemVerilog"] && [
file extension $f] != ".sv" && [
file extension $f] != ".svp"} {
3194 set prop "SystemVerilog"
3195 }
elseif {[
string equal [
lindex $type 0] "XDC"] && [
file extension $f] != ".xdc"} {
3197 }
elseif {[
string equal $type "Verilog Header"] && [
file extension $f] != ".vh" && [
file extension $f] != ".svh"} {
3198 set prop "verilog_header"
3199 }
elseif {[
string equal $type "Verilog Template"] && [
file extension $f] == ".v" && [
file extension $f] != ".sv"} {
3200 set prop "verilog_template"
3202 set type [
lindex $type 0]
3206 if {![
string equal $prop ""]} {
3207 dict lappend properties $f $prop
3210 if {[
string equal $fs_type "SimulationSrcs"]} {
3212 if {[
string equal $type "VHDL"]} {
3213 set library "${lib}.sim"
3215 set library "others.sim"
3219 dict lappend simsets $dict_fs $library
3222 dict lappend simlibraries $library $f
3223 }
elseif {[
string equal $type "VHDL"]} {
3226 dict lappend srcsets $dict_fs "${lib}.src"
3228 dict lappend libraries "${lib}.src" $f
3229 }
elseif {[
string first "IP" $type] != -1} {
3232 dict lappend srcsets $dict_fs "ips.src"
3234 dict lappend libraries "ips.src" $f
3235 Msg Debug "Appending $f to ips.src"
3236 }
elseif {[
string equal $fs_type "Constrs"]} {
3239 dict lappend consets $dict_fs "sources.con"
3241 dict lappend constraints "sources.con" $f
3245 dict lappend srcsets $dict_fs "others.src"
3247 dict lappend libraries "others.src" $f
3248 Msg Debug "Appending $f to others.src"
3251 if {[
lindex [get_property -quiet used_in_synthesis [
GetFile $f $fs]] 0] == 0} {
3252 dict lappend properties $f "nosynth"
3254 if {[
lindex [get_property -quiet used_in_implementation [
GetFile $f $fs]] 0] == 0} {
3255 dict lappend properties $f "noimpl"
3257 if {[
lindex [get_property -quiet used_in_simulation [
GetFile $f $fs]] 0] == 0} {
3258 dict lappend properties $f "nosim"
3260 if {[
lindex [get_property -quiet IS_MANAGED [
GetFile $f $fs]] 0] == 0 && [
file extension $f] != ".xcix"} {
3261 dict lappend properties $f "locked"
3267 dict lappend properties "Simulator" [get_property target_simulator [current_project]]
3270 set file [open $project_file r]
3271 set in_file_manager 0
3273 while {[
gets $file line] >= 0} {
3275 if {[regexp {^KEY ActiveRoot \"([^\"]+)\"} $line -> value]} {
3276 set top [
string range $value 0 [
expr {[string first "::" $value] - 1}]]
3280 if {[regexp {^LIST FileManager} $line]} {
3281 set in_file_manager 1
3286 if {$in_file_manager && [regexp {^ENDLIST} $line]} {
3291 if {$in_file_manager && [regexp {^VALUE \"([^\"]+)} $line -> value]} {
3294 lassign [
split $value ,] file_path file_type
3297 set library "others"
3298 while {[
gets $file line] >= 0} {
3299 if {$line == "ENDFILE"} {
3302 regexp {^LIBRARY=\"([^\"]+)} $line -> library
3303 regexp {^PARENT=\"([^\"]+)} $line -> parent_file
3305 Msg Debug "Found file ${file_path} in project.."
3306 if {$parent_file == ""} {
3307 if {$file_type == "hdl"} {
3309 if {[
IsInList "${library}.src" [
DictGet $srcsets "sources_1"]] == 0} {
3310 dict lappend srcsets "sources_1" "${library}.src"
3312 dict lappend libraries "${library}.src" $file_path
3316 if {[
GetModuleName $file_path] == [
string tolower $top] && $top != ""} {
3317 Msg Debug "Found top module $top in $file_path"
3318 dict lappend properties $file_path "top=$top"
3320 }
elseif {$file_type == "tb_hdl"} {
3322 dict lappend simsets "sim_1" "${library}.sim"
3324 dict lappend simlibraries "${library}.sim" $file_path
3325 }
elseif {$file_type == "io_pdc" || $file_type == "sdc"} {
3327 dict lappend consets "constrs_1" "sources.con"
3329 dict lappend constraints "sources.con" $file_path
3336 set fileData [read [open $project_file]]
3338 set project_path [
file dirname $project_file]
3341 regsub {<\?xml.*\?>} $fileData "" fileData
3344 regexp {<Implementation.*?>(.*)</Implementation>} $fileData -> implementationContent
3348 set sourceRegex {<Source name="([^"]*?)" type="([^"]*?)" type_short="([^"]*?)".*?>(.*?)</Source>}
3350 set optionsRegex {<Options(.*?)\/>}
3351 regexp $optionsRegex $implementationContent -> prj_options
3352 foreach option $prj_options {
3353 if {[regexp {^top=\"([^\"]+)\"} $option match result]} {
3358 while {[regexp $sourceRegex $implementationContent match name type type_short optionsContent]} {
3359 Msg Debug "Found file ${name} in project..."
3360 set file_path [
file normalize $project_path/$name]
3362 set optionsRegex {<Options(.*?)\/>}
3363 regexp $optionsRegex $optionsContent -> options
3364 set library "others"
3366 foreach option $options {
3367 if {[
string first "System Verilog" $option]} {
3370 if {[regexp {^lib=\"([^\"]+)\"} $option match1 result]} {
3375 if {[regexp {syn_sim="([^"]*?)"} $match match_sim simonly]} {
3380 if {$type_short == "VHDL" || $type_short == "Verilog" || $type_short == "IPX"} {
3381 if {$ext == ".src"} {
3382 if {[
IsInList "${library}${ext}" [
DictGet $srcsets "sources_1"]] == 0} {
3383 dict lappend srcsets "sources_1" "${library}${ext}"
3385 dict lappend libraries "${library}${ext}" $file_path
3386 }
elseif {$ext == ".sim"} {
3388 dict lappend simsets "sim_1" "${library}.sim"
3390 dict lappend simlibraries "${library}.sim" $file_path
3396 Msg Debug "Found top module $top in $file_path"
3397 dict lappend properties $file_path "top=$top"
3399 }
elseif {$type_short == "SDC"} {
3401 dict lappend consets "constrs_1" "sources.con"
3403 dict lappend constraints "sources.con" $file_path
3407 regsub -- $match $implementationContent "" implementationContent
3410 return [list $libraries $properties $simlibraries $constraints $srcsets $simsets $consets]
3417 proc GetProjectFlavour {proj_name} {
3419 set flavour [
string map {. ""} [
file extension $proj_name]]
3420 if {$flavour != ""} {
3421 if {[
string is integer $flavour]} {
3422 Msg Info "Project $proj_name has flavour = $flavour, the generic variable FLAVOUR will be set to $flavour"
3424 Msg Warning "Project name has a unexpected non numeric extension, flavour will be set to -1"
3441 proc GetProjectVersion {proj_dir repo_path {ext_path ""} {sim 0}} {
3442 if {![
file exists $proj_dir]} {
3443 Msg CriticalWarning "$proj_dir not found"
3453 Msg Warning "Repository is not clean"
3461 Msg Debug "Project version $v_proj, latest tag $v_last"
3463 Msg Info "The specified project was modified since official version."
3470 Msg Info "The specified project was modified in the latest official version $ret"
3471 }
elseif {$comp == -1} {
3472 Msg Info "The specified project was modified in a past official version $ret"
3488 proc GetRepoVersions {proj_dir repo_path {ext_path ""} {sim 0}} {
3489 if {[
catch {
package require cmdline} ERROR]} {
3490 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
3505 lappend SHAs [
GetSHA {Hog}]
3509 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
3510 Msg Info "Hog submodule [
pwd] clean."
3511 lassign [
GetVer ./] hog_ver hog_hash
3513 Msg CriticalWarning "Hog submodule [
pwd] not clean, commit hash will be set to 0."
3514 set hog_hash "0000000"
3515 set hog_ver "00000000"
3521 set project_files $conf_files
3522 lappend project_files $repo_path/Hog
3525 lassign [
GetVer [
join $conf_files]] top_ver top_hash
3526 lappend SHAs $top_hash
3527 lappend versions $top_ver
3534 lassign [
GetHogFiles -list_files "*.src" -sha_mode "./list/" $repo_path] src_files dummy
3535 dict for {f files} $src_files {
3536 # library names have a .src extension in values returned by GetHogFiles
3537 set name [file rootname [file tail $f]]
3538 if {[file ext $f] == ".oth"} {
3541 lassign [GetVer $files] ver hash
3542 # Msg Info "Found source list file $f, version: $ver commit SHA: $hash"
3544 lappend versions $ver
3546 lappend hashes $hash
3548 lappend project_files $f {*}$files
3554 lassign [
GetHogFiles -list_files "*.con" -sha_mode "./list/" $repo_path] cons_files dummy
3555 dict for {f files} $cons_files {
3556 #library names have a .con extension in values returned by GetHogFiles
3557 set name [file rootname [file tail $f]]
3558 lassign [GetVer $files] ver hash
3559 #Msg Info "Found constraint list file $f, version: $ver commit SHA: $hash"
3561 Msg CriticalWarning "Constraints file $f not found in Git."
3563 lappend cons_hashes $hash
3565 lappend versions $ver
3566 lappend project_files $f {*}$files
3573 lassign [
GetHogFiles -list_files "*.sim" -sha_mode "./list/" $repo_path] sim_files dummy
3574 dict for {f files} $sim_files {
3575 #library names have a .sim extension in values returned by GetHogFiles
3576 set name [file rootname [file tail $f]]
3577 lassign [GetVer $files] ver hash
3578 #Msg Info "Found simulation list file $f, version: $ver commit SHA: $hash"
3579 lappend sim_hashes $hash
3581 lappend versions $ver
3582 lappend project_files $f {*}$files
3590 Msg CriticalWarning "No hashes found for constraints files (not in git)"
3593 set cons_hash [
string tolower [
Git "log --format=%h -1 $cons_hashes"]]
3600 set ext_files [glob -nocomplain "./list/*.ext"]
3603 foreach f $ext_files {
3604 set name [
file rootname [
file tail $f]]
3607 lappend ext_names $name
3608 lappend ext_hashes $hash
3611 lappend versions $ext_ver
3612 lappend project_files $f
3615 set file_data [read $fp]
3617 set data [
split $file_data "\n"]
3619 foreach line $data {
3620 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
3622 set file_and_prop [regexp -all -inline {\S+} $line]
3623 set hdlfile [
lindex $file_and_prop 0]
3624 set hdlfile $ext_path/$hdlfile
3625 if {[
file exists $hdlfile]} {
3626 set hash [
lindex $file_and_prop 1]
3627 set current_hash [
Md5Sum $hdlfile]
3628 if {[
string first $hash $current_hash] == -1} {
3629 Msg CriticalWarning "File $hdlfile has a wrong hash. Current checksum: $current_hash, expected: $hash"
3637 if {[
llength [glob -nocomplain ./list/*.ipb]] > 0} {
3639 lassign [
GetHogFiles -list_files "*.ipb" -sha_mode "./list/" $repo_path] xml_files dummy
3640 set xml_source_files [dict get $xml_files "xml.ipb"]
3641 lassign [
GetVer $xml_source_files] xml_ver xml_hash
3642 lappend SHAs $xml_hash
3643 lappend versions $xml_ver
3644 lappend project_files {*}[glob ./list/*.ipb] {*}$xml_source_files
3648 Msg Info "This project does not use IPbus XMLs"
3653 set user_ip_repos ""
3654 set user_ip_repo_hashes ""
3655 set user_ip_repo_vers ""
3657 if {[
file exists [
lindex $conf_files 0]]} {
3658 set PROPERTIES [
ReadConf [
lindex $conf_files 0]]
3659 if {[dict exists $PROPERTIES main]} {
3660 set main [dict get $PROPERTIES main]
3661 dict for {p v} $main {
3662 if {[string tolower $p] == "ip_repo_paths"} {
3664 if {[file isdirectory "$repo_path/$repo"]} {
3665 set repo_file_list [glob -nocomplain "$repo_path/$repo/*"]
3666 if {[llength $repo_file_list] == 0} {
3667 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3669 lappend user_ip_repos "$repo_path/$repo"
3678 foreach repo $user_ip_repos {
3679 if {[
file isdirectory $repo]} {
3680 set repo_file_list [glob -nocomplain "$repo/*"]
3681 if {[
llength $repo_file_list] != 0} {
3682 lassign [
GetVer $repo] ver sha
3683 lappend user_ip_repo_hashes $sha
3684 lappend user_ip_repo_vers $ver
3685 lappend versions $ver
3686 lappend project_files $repo
3688 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3691 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory does not exist."
3698 if {[
Git "status --untracked-files=no --porcelain" $project_files] eq ""} {
3699 Msg Info "Project-relevant files are clean."
3702 Msg CriticalWarning "Project-relevant files not clean, commit hash and version will be set to 0."
3709 while {$found == 0} {
3710 set global_commit [
Git "log --format=%h -1 --abbrev=7 $SHAs"]
3715 if {$common_child == 0} {
3716 Msg CriticalWarning "The commit $sha is not an ancestor of the global commit $global_commit, which is OK. \
3717 But $sha and $global_commit do not have any common child, which is NOT OK. \
3718 This is probably do to a REBASE that is forbidden in Hog methodology as it changes git history. \
3719 Hog cannot guarantee the accuracy of the SHAs. \
3720 A way to fix this is to make a commit that touches all the projects in the repositories (e.g. change the Hog version), \
3721 but please do not rebase in the official branches in the future."
3723 Msg Info "The commit $sha is not an ancestor of the global commit $global_commit, adding the first common child $common_child instead..."
3724 lappend SHAs $common_child
3734 set global_commit "0000000"
3735 set global_version "00000000"
3740 set top_hash [
format %+07s $top_hash]
3741 set cons_hash [
format %+07s $cons_hash]
3742 return [list $global_commit $global_version \
3743 $hog_hash $hog_ver $top_hash $top_ver \
3744 $libs $hashes $vers $cons_ver $cons_hash \
3745 $ext_names $ext_hashes $xml_hash $xml_ver \
3746 $user_ip_repos $user_ip_repo_hashes $user_ip_repo_vers]
3755 proc GetSHA {{path ""}} {
3757 lassign [
GitRet {log --format=%h --abbrev=7 -1}] status result
3759 return [
string tolower $result]
3761 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3767 set repo_path [
lindex [
Git {rev-parse --show-toplevel}] 0]
3771 set file_in_module 0
3772 if {[
file exists $repo_path/.gitmodules]} {
3773 lassign [
GitRet "config --file $repo_path/.gitmodules --get-regexp path"] status result
3775 set submodules [
split $result "\n"]
3778 Msg Warning "Something went wrong while trying to find submodules: $result"
3781 foreach mod $submodules {
3782 set module [
lindex $mod 1]
3783 if {[
string first "$repo_path/$module" $f] == 0} {
3785 set file_in_module 1
3786 lappend paths "$repo_path/$module"
3791 if {$file_in_module == 0} {
3797 lassign [
GitRet {log --format=%h --abbrev=7 -1} $paths] status result
3799 return [
string tolower $result]
3801 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3804 return [
string tolower $result]
3808 proc GetSimulators {} {
3809 set SIMULATORS [list "modelsim" "questa" "riviera" "activehdl" "ies" "vcs"]
3814 proc GetTopFile {} {
3816 set compile_order_prop [get_property source_mgmt_mode [current_project]]
3817 if {$compile_order_prop ne "All"} {
3818 Msg CriticalWarning "Compile order is not set to automatic, setting it now..."
3819 set_property source_mgmt_mode All [current_project]
3820 update_compile_order -fileset sources_1
3822 return [
lindex [get_files -quiet -compile_order sources -used_in synthesis -filter {FILE_TYPE =~ "VHDL*" || FILE_TYPE =~ "*Verilog*" }] end]
3823 }
elseif {[
IsISE]} {
3824 debug::design_graph_mgr -create [current_fileset]
3825 debug::design_graph -add_fileset [current_fileset]
3826 debug::design_graph -update_all
3827 return [
lindex [debug::design_graph -get_compile_order] end]
3829 Msg Error "GetTopFile not yet implemented for this IDE"
3834 proc GetTopModule {} {
3836 return [get_property top [current_fileset]]
3838 Msg Error "GetTopModule not yet implemented for this IDE"
3848 proc GetVer {path {force_develop 0} {verbose 1}} {
3852 Msg CriticalWarning "Empty SHA found for ${path}. Commit to Git to resolve this warning."
3855 set p [
lindex $path 0]
3856 if {[
file isdirectory $p]} {
3859 cd [
file dirname $p]
3861 set repo_path [
Git {rev-parse --show-toplevel}]
3864 return [list [
GetVerFromSHA $SHA $repo_path $force_develop $verbose] $SHA]
3876 proc GetVerFromSHA {SHA repo_path {force_develop 0} {verbose 1}} {
3878 Msg CriticalWarning "Empty SHA found"
3881 lassign [
GitRet "tag --sort=creatordate --contain $SHA -l v*.*.* -l b*v*.*.*"] status result
3884 if {[regexp {^ *$} $result]} {
3886 lassign [
GitRet "log --oneline --pretty=\"%d\""] status2 tag_list
3889 set pattern {tag: v\d+\.\d+\.\d+}
3890 set real_tag_list {}
3891 foreach x $tag_list {
3892 set x_untrimmed [regexp -all -inline $pattern $x]
3893 regsub "tag: " $x_untrimmed "" x_trimmed
3894 set tt [
lindex $x_trimmed 0]
3895 if {![
string equal $tt ""]} {
3896 lappend real_tag_list $tt
3900 Msg Debug "Cleaned up list: $real_tag_list."
3902 set sorted_tags [lsort -decreasing -command CompareVersions $real_tag_list]
3904 Msg Debug "Sorted Tag list: $sorted_tags"
3906 set tag [
lindex $sorted_tags 0]
3909 set pattern {v\d+\.\d+\.\d+}
3910 if {![regexp $pattern $tag]} {
3911 Msg CriticalWarning "No Hog version tags found in this repository."
3916 set repo_conf $repo_path/Top/repo.conf
3920 set hotfix_prefix "hotfix/"
3921 set minor_prefix "minor_version/"
3922 set major_prefix "major_version/"
3924 set enable_develop_branch $force_develop
3926 set branch_name [
Git {rev-parse --abbrev-ref HEAD}]
3928 if {[
file exists $repo_conf]} {
3929 set PROPERTIES [
ReadConf $repo_conf]
3931 if {[dict exists $PROPERTIES main]} {
3932 set mainDict [dict get $PROPERTIES main]
3935 if {[dict exists $mainDict ENABLE_DEVELOP_BRANCH]} {
3936 set enable_develop_branch [dict get $mainDict ENABLE_DEVELOP_BRANCH]
3942 if {[dict exists $PROPERTIES prefixes]} {
3943 set prefixDict [dict get $PROPERTIES prefixes]
3945 if {[dict exists $prefixDict HOTFIX]} {
3946 set hotfix_prefix [dict get $prefixDict HOTFIX]
3948 if {[dict exists $prefixDict MINOR_VERSION]} {
3949 set minor_prefix [dict get $prefixDict MINOR_VERSION]
3951 if {[dict exists $prefixDict MAJOR_VERSION]} {
3952 set major_prefix [dict get $prefixDict MAJOR_VERSION]
3958 if {[
string match "HEAD" $branch_name]} {
3959 if {$verbose == 1} {
3960 Msg Warning "Detached HEAD detected - attempting to find branch name"
3965 set log_refs [
Git {show -s --pretty=%D HEAD}]
3966 set branch_list [
split $log_refs ","]
3967 Msg Debug "list of possible branch refs $log_refs"
3973 set match_prefixes [list $hotfix_prefix $minor_prefix $major_prefix]
3974 set prev_branch_name $branch_name
3976 foreach br $branch_list {
3977 foreach pr $match_prefixes {
3978 if {[
string match "$pr*" [
string trim $br]]} {
3979 set branch_name [
string trim $br]
3985 if {!$match_count == 1} {
3986 set branch_name $prev_branch_name
3987 if {$verbose == 1} {
3988 Msg Warning "Branch name not found. Using $branch_name"
3991 if {$verbose == 1} {
3992 Msg Info "Branch name found: $branch_name"
3997 if {$enable_develop_branch == 1} {
3998 if {[
string match "$hotfix_prefix*" $branch_name]} {
4003 if {[
string match "$major_prefix*" $branch_name]} {
4005 set version_level major
4006 }
elseif {[
string match "$minor_prefix*" $branch_name] || ($enable_develop_branch == 1 && $is_hotfix == 0)} {
4008 set version_level minor
4011 set version_level patch
4015 Msg CriticalWarning "Tag $tag does not contain a Hog compatible version in this repository."
4018 }
elseif {$mr == 0} {
4019 switch $version_level {
4034 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."
4040 set vers [
split $result "\n"]
4041 set ver [
lindex $vers 0]
4043 if {[regexp {^v.*$} $v]} {
4051 Msg CriticalWarning "Error while trying to find tag for $SHA"
4059 set M [
format %02X $M]
4060 set m [
format %02X $m]
4061 set c [
format %04X $c]
4062 }
elseif {$M > -1} {
4064 set M [
format %02X $M]
4065 set m [
format %02X $m]
4066 set c [
format %04X $c]
4068 Msg Warning "Tag does not contain a properly formatted version: $ver"
4069 set M [
format %02X 0]
4070 set m [
format %02X 0]
4071 set c [
format %04X 0]
4084 proc Git {command {files ""}} {
4085 lassign [
GitRet $command $files] ret result
4087 Msg Error "Code $ret returned by git running: $command -- $files"
4098 proc GetModuleName {filename} {
4100 if {![
file exists $filename]} {
4101 Msg CriticalWarning "Error: File $filename does not exist."
4106 set fileId [open $filename r]
4109 set file_content [read $fileId]
4115 if {[
file extension $filename] == ".vhd" || [
file extension $filename] == ".vhdl"} {
4117 set file_content [
string tolower $file_content]
4119 set pattern {(?m)^\s*entity\s+(\S+)\s+is}
4120 }
elseif {[
file extension $filename] == ".v" || [
file extension $filename] == ".sv"} {
4122 set pattern {\n\s*module\s*(\w+)(\s*|\(|\n)}
4124 Msg Debug "File is neither VHDL nor Verilog... Returning empty string..."
4129 if {[regexp $pattern $file_content match module_name]} {
4132 Msg Debug "No module was found in $filename. Returning an empty string..."
4140 proc GetVerilogGenerics {file} {
4141 set fp [open $file r]
4147 foreach line [
split $data "\n"] {
4148 regsub "^\\s*\/\/.*" $line "" line
4149 regsub "(.*)\/\/.*" $line {\1} line
4150 if {![
string equal $line ""]} {
4151 append lines $line " "
4156 regsub -all {/\*.*\*/} $lines "" lines
4159 set punctuation [list]
4160 foreach char [list "(" ")" ";" "," " " "!" "<=" ":=" "=" "\[" "\]"] {
4161 lappend punctuation $char "\000$char\000"
4165 set tokens [
split [
string map $punctuation $lines] \000]
4167 set parameters [dict create]
4176 foreach token $tokens {
4177 set token [
string trim $token]
4178 if {![
string equal "" $token]} {
4179 if {[
string equal [
string tolower $token] "parameter"]} {
4180 set state $PARAM_NAME
4181 }
elseif {[
string equal $token ")"] || [
string equal $token ";"]} {
4183 }
elseif {$state == $PARAM_WIDTH} {
4184 if {[
string equal $token "\]"]} {
4185 set state $PARAM_NAME
4187 }
elseif {$state == $PARAM_VALUE} {
4188 if {[
string equal $token ","]} {
4189 set state $PARAM_NAME
4190 }
elseif {[
string equal $token ";"]} {
4195 }
elseif {$state == $PARAM_NAME} {
4196 if {[
string equal $token "="]} {
4197 set state $PARAM_VALUE
4198 }
elseif {[
string equal $token "\["]} {
4199 set state $PARAM_WIDTH
4200 }
elseif {[
string equal $token ","]} {
4201 set state $PARAM_NAME
4202 }
elseif {[
string equal $token ";"]} {
4204 }
elseif {[
string equal $token ")"]} {
4207 dict set parameters $token "integer"
4220 proc GetVhdlGenerics {file {entity ""}} {
4221 set fp [open $file r]
4227 foreach line [
split $data "\n"] {
4228 regsub "^\\s*--.*" $line "" line
4229 regsub "(.*)--.*" $line {\1} line
4230 if {![
string equal $line ""]} {
4231 append lines $line " "
4236 set generic_block ""
4237 set generics [dict create]
4239 if {1 == [
string equal $entity ""]} {
4240 regexp {(?i).*entity\s+([^\s]+)\s+is} $lines _ entity
4243 set generics_regexp "(?i).*entity\\s+$entity\\s+is\\s+generic\\s*\\((.*)\\)\\s*;\\s*port.*end.*$entity"
4245 if {[regexp $generics_regexp $lines _ generic_block]} {
4247 foreach line [
split $generic_block ";"] {
4249 regexp {(.*):\s*([A-Za-z0-9_]+).*} $line _ generic type
4252 set splits [
split $generic ","]
4253 foreach split $splits {
4254 dict set generics [
string trim $split] [
string trim $type]
4262 proc GHDL {command logfile} {
4263 set ret [
catch {
exec -ignorestderr ghdl {*}$command >>& $logfile} result options]
4268 return [list $ret $result]
4280 proc GitRet {command {files ""}} {
4283 set ret [
catch {
exec -ignorestderr git {*}$command} result]
4285 set ret [
catch {
exec -ignorestderr git {*}$command -- {*}$files} result]
4287 return [list $ret $result]
4295 proc GitVersion {target_version} {
4296 set ver [
split $target_version "."]
4297 set v [
Git --version]
4299 set current_ver [
split [
lindex $v 2] "."]
4300 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
4301 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
4302 return [
expr {$target <= $current}]
4316 proc HandleIP {what_to_do xci_file ip_path repo_path {gen_dir "."} {force 0}} {
4318 if {!($what_to_do eq "push") && !($what_to_do eq "pull")} {
4319 Msg Error "You must specify push or pull as first argument."
4322 if {[
catch {
package require tar} TARPACKAGE]} {
4323 Msg CriticalWarning "Cannot find package tar. You can fix this by installing package \"tcllib\""
4334 if {[regexp {^[^/]+:} $ip_path]} {
4338 lassign [
ExecuteRet rclone --version] rclone_ret rclone_ver
4339 if {$rclone_ret != 0} {
4340 Msg CriticalWarning "Rclone path specified but rclone not found or failed: $rclone_ver"
4344 Msg Info "IP remote directory path, on Rclone, is set to: $ip_path"
4346 if {[
info exists env(HOG_RCLONE_CONFIG)]} {
4347 Msg Info "Using rclone config from environment variable HOG_RCLONE_CONFIG: $env(HOG_RCLONE_CONFIG)"
4348 set config_path $env(HOG_RCLONE_CONFIG)
4350 set config_path "/dev/null"
4351 Msg Info "Environment variable HOG_RCLONE_CONFIG not set, using rclone environmental variables..."
4354 set remote_name "[
lindex [
split $ip_path ":"] 0]:"
4355 lassign [
ExecuteRet rclone listremotes --config $config_path] rclone_list_ret remotes
4356 if {$rclone_list_ret != 0} {
4357 Msg CriticalWarning "Could not list rclone remotes: $remotes"
4361 if {![
IsInList $remote_name $remotes]} {
4362 Msg CriticalWarning "Rclone remote $remote_name not found among available remotes: $remotes"
4368 }
elseif {[
string first "/eos/" $ip_path] == 0} {
4371 lassign [
eos "ls $ip_path"] ret result
4373 Msg CriticalWarning "Could not run ls for for EOS path: $ip_path (error: $result). \
4374 Either the drectory does not exist or there are (temporary) problem with EOS."
4378 Msg Info "IP remote directory path, on EOS, is set to: $ip_path"
4384 if {!([
file exists $xci_file])} {
4385 Msg CriticalWarning "Could not find $xci_file."
4391 set xci_path [
file dirname $xci_file]
4392 set xci_name [
file tail $xci_file]
4393 set xci_ip_name [
file rootname [
file tail $xci_file]]
4394 set xci_dir_name [
file tail $xci_path]
4395 set gen_path $gen_dir
4397 set hash [
Md5Sum $xci_file]
4398 set file_name $xci_name\_$hash
4400 Msg Info "Preparing to $what_to_do IP: $xci_name..."
4402 if {$what_to_do eq "push"} {
4405 if {$on_rclone == 1} {
4406 lassign [
ExecuteRet rclone ls $ip_path/$file_name.tar --config $config_path] ret result
4411 Msg Info "IP already in the Rclone repository, will not copy..."
4413 Msg Info "IP already in the Rclone repository, will forcefully replace..."
4418 }
elseif {$on_eos == 1} {
4419 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4424 Msg Info "IP already in the EOS repository, will not copy..."
4426 Msg Info "IP already in the EOS repository, will forcefully replace..."
4432 if {[
file exists "$ip_path/$file_name.tar"]} {
4434 Msg Info "IP already in the local repository, will not copy..."
4436 Msg Info "IP already in the local repository, will forcefully replace..."
4445 if {$will_copy == 1} {
4447 Msg Info "Looking for generated files in $gen_path..."
4448 set ip_gen_files [glob -nocomplain $gen_path/*]
4452 if {[
llength $ip_gen_files] > 0} {
4453 Msg Info "Found some IP synthesised files matching $xci_ip_name"
4454 if {$will_remove == 1} {
4455 Msg Info "Removing old synthesised directory $ip_path/$file_name.tar..."
4456 if {$on_rclone == 1} {
4457 lassign [
ExecuteRet rclone delete $ip_path/$file_name.tar --config $config_path] ret result
4459 Msg CriticalWarning "Could not delete file from Rclone: $result"
4461 }
elseif {$on_eos == 1} {
4462 eos "rm -rf $ip_path/$file_name.tar" 5
4464 file delete -force "$ip_path/$file_name.tar"
4468 Msg Info "Creating local archive with IP generated files..."
4471 foreach f $ip_gen_files {
4472 lappend tar_files "[
Relative [
file normalize $repo_path] $f]"
4475 ::tar::create $file_name.tar $tar_files
4477 Msg Info "Copying IP generated files for $xci_name..."
4478 if {$on_rclone == 1} {
4479 lassign [
ExecuteRet rclone copyto $file_name.tar $ip_path/$file_name.tar --config $config_path] ret result
4481 Msg CriticalWarning "Something went wrong when copying the IP files to Rclone. Error message: $result"
4483 }
elseif {$on_eos == 1} {
4484 lassign [
ExecuteRet xrdcp -f -s $file_name.tar $::env(EOS_MGM_URL)//$ip_path/] ret msg
4486 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4489 Copy "$file_name.tar" "$ip_path/"
4491 Msg Info "Removing local archive"
4492 file delete $file_name.tar
4494 Msg Warning "Could not find synthesized files matching $gen_path/$file_name*"
4497 }
elseif {$what_to_do eq "pull"} {
4498 if {$on_rclone == 1} {
4499 lassign [
ExecuteRet rclone ls $ip_path/$file_name.tar --config $config_path] ret result
4501 Msg Info "Nothing for $xci_name was found in the Rclone repository, cannot pull."
4505 Msg Info "IP $xci_name found in the Rclone repository $ip_path, copying it locally to $repo_path..."
4506 lassign [
ExecuteRet rclone copyto $ip_path/$file_name.tar $file_name.tar --config $config_path] ret_copy result_copy
4507 if {$ret_copy != 0} {
4508 Msg CriticalWarning "Something went wrong when copying the IP files from Rclone. Error message: $result_copy"
4511 }
elseif {$on_eos == 1} {
4512 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4514 Msg Info "Nothing for $xci_name was found in the EOS repository, cannot pull."
4518 set remote_tar "$::env(EOS_MGM_URL)//$ip_path/$file_name.tar"
4519 Msg Info "IP $xci_name found in the repository $remote_tar, copying it locally to $repo_path..."
4521 lassign [
ExecuteRet xrdcp -f -r -s $remote_tar $repo_path] ret msg
4523 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4527 if {[
file exists "$ip_path/$file_name.tar"]} {
4528 Msg Info "IP $xci_name found in local repository $ip_path/$file_name.tar, copying it locally to $repo_path..."
4529 Copy $ip_path/$file_name.tar $repo_path
4531 Msg Info "Nothing for $xci_name was found in the local IP repository, cannot pull."
4537 if {[
file exists $file_name.tar]} {
4538 remove_files $xci_file
4539 Msg Info "Extracting IP files from archive to $repo_path..."
4540 ::tar::untar $file_name.tar -dir $repo_path -noperms
4541 Msg Info "Removing local archive"
4542 file delete $file_name.tar
4543 add_files -norecurse -fileset sources_1 $xci_file
4556 proc HexVersionToString {version} {
4557 scan [
string range $version 0 1] %x M
4558 scan [
string range $version 2 3] %x m
4559 scan [
string range $version 4 7] %x c
4564 proc ImportTclLib {} {
4567 if {[
info exists env(HOG_TCLLIB_PATH)]} {
4568 lappend auto_path $env(HOG_TCLLIB_PATH)
4571 puts "ERROR: To run Hog with Microsemi Libero SoC or Lattice Diamond, you need to define the HOG_TCLLIB_PATH variable."
4586 proc InitLauncher {script tcl_path parameters commands argv {custom_commands ""}} {
4587 set repo_path [
file normalize "$tcl_path/../.."]
4589 set bin_path [
file normalize "$tcl_path/../../bin"]
4590 set top_path [
file normalize "$tcl_path/../../Top"]
4592 set cmd_lines [
split $commands "\n"]
4594 set command_options [dict create]
4595 set directive_descriptions [dict create]
4596 set directive_names [dict create]
4597 set common_directive_names [dict create]
4598 set custom_command ""
4599 set custom_command_options ""
4601 foreach l $cmd_lines {
4603 if {[regexp {\\(.*) \{\#} $l minc d]} {
4604 lappend directives_with_projects $d
4608 if {[regexp {\\(.*) \{} $l minc regular_expression]} {
4609 lappend directive_regex $regular_expression
4613 if {[regexp {\#\s*NAME(\*)?:\s*(.*)\s*} $l minc star name]} {
4614 dict set directive_names $name $regular_expression
4616 dict set common_directive_names $name $regular_expression
4619 set directive_names [
DictSort $directive_names]
4620 set common_directive_names [
DictSort $common_directive_names]
4623 if {[regexp {\#\s*DESCRIPTION:\s*(.*)\s*} $l minc x]} {
4624 dict set directive_descriptions $regular_expression $x
4628 if {[regexp {\#\s*OPTIONS:\s*(.*)\s*} $l minc x]} {
4629 dict set command_options $regular_expression [
split [regsub -all {[ \t\n]+} $x {}] ","]
4633 set short_usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nMost common directives (case insensitive):"
4635 dict for {key value} $common_directive_names {
4636 set short_usage "$short_usage\n - $key: [dict get $directive_descriptions $value]"
4639 if {[
string length $custom_commands] > 0} {
4640 Msg Debug "Found custom commands to add to short short_usage."
4641 set short_usage "$short_usage\n\nCustom commands:"
4642 dict for {key command} $custom_commands {
4643 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4644 set short_usage "$short_usage\n - $key: [dict get $command DESCRIPTION]"
4650 set short_usage "$short_usage\n\n\
4651 To see all the available directives, run:\n./Hog/Do HELP\n\n\
4652 To list available options for the chosen directive run:\n\
4653 ./Hog/Do <directive> HELP\n
4656 set usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nDirectives (case insensitive):"
4658 dict for {key value} $directive_names {
4659 set usage "$usage\n - $key: [dict get $directive_descriptions $value]"
4663 if {[
string length $custom_commands] > 0} {
4664 Msg Debug "Found custom commands to add to short usage."
4665 set usage "$usage\n\nCustom commands:"
4666 dict for {key command} $custom_commands {
4667 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4668 set usage "$usage\n - $key: [dict get $command DESCRIPTION]"
4673 set usage "$usage\n\nTo list available options for the chosen directive run:\n./Hog/Do <directive> HELP"
4682 Msg Debug "HogEnv.conf found"
4690 if {[
catch {
package require cmdline} ERROR]} {
4691 Msg Debug "The cmdline Tcl package was not found, sourcing it from Hog..."
4692 source $tcl_path/utils/cmdline.tcl
4695 set argv [regsub -all {(?i) HELP\y} $argv " -help"]
4700 set custom_parameters [list]
4701 dict for {key command} $custom_commands {
4702 set custom_parameters [concat $custom_parameters [dict get $command CUSTOM_OPTIONS]]
4705 lassign [
GetOptions $argv [
concat $custom_parameters $parameters]] option_list arg_list
4707 if {[
IsInList "-all" $option_list]} {
4716 set directive [
string toupper [
lindex $arg_list 0]]
4719 set argument_is_no_project 1
4721 set NO_DIRECTIVE_FOUND 0
4722 switch -regexp -- $directive "$commands"
4723 if {$NO_DIRECTIVE_FOUND == 1} {
4724 if {[
string length $custom_commands] > 0 && [dict exists $custom_commands $directive]} {
4725 set custom_command $directive
4726 set custom_command_hog_parameters [dict get $custom_commands $directive OPTIONS]
4727 set custom_command_options [dict get $custom_commands $directive CUSTOM_OPTIONS]
4728 set custom_command_options [
concat $custom_command_hog_parameters $custom_command_options]
4730 Msg Status "ERROR: Unknown directive $directive.\n\n"
4736 if {[
IsInList $directive $directives_with_projects 1]} {
4737 set argument_is_no_project 0
4741 if {$directive != ""} {
4742 if {[
IsInList $directive $directives_with_projects 1]} {
4743 puts "usage: ./Hog/Do \[OPTIONS\] $directive <project>\n"
4744 }
elseif {[regexp "^COMPSIM(LIB)?$" $directive]} {
4745 puts "usage: ./Hog/Do \[OPTIONS\] $directive <simulator>\n"
4747 puts "usage: ./Hog/Do \[OPTIONS\] $directive \n"
4750 dict for {dir desc} $directive_descriptions {
4751 if {[regexp $dir $directive]} {
4759 if {$custom_command ne ""} {
4760 if {[
llength $custom_command_options] > 0} {
4761 puts "Available options:"
4763 foreach custom_option $custom_command_options {
4764 set n [
llength $custom_option]
4766 lassign $custom_option opt help
4769 }
elseif {$n == 3} {
4770 lassign $custom_option opt def help
4771 puts " -$opt <argument>"
4773 puts " $help (default: $def)"
4778 Msg Warning "Custom option spec has invalid arity (expected 2 or 3): $custom_option"
4783 dict for {dir opts} $command_options {
4784 if {[regexp $dir $directive]} {
4785 puts "Available options:"
4787 foreach par $parameters {
4788 if {$opt == [lindex $par 0]} {
4789 if {[regexp {\.arg$} $opt]} {
4790 set opt_name [regsub {\.arg$} $opt ""]
4791 puts " -$opt_name <argument>"
4795 puts " [lindex $par [llength $par]-1]"
4809 if {$custom_command ne ""} {
4810 set parameters [
concat $parameters $custom_command_options]
4813 if {[
catch {
array set options [
cmdline::getoptions option_list $parameters $usage]} err]} {
4814 Msg Status "\nERROR: Syntax error, probably unknown option.\n\n USAGE: $err"
4818 if {[
llength $arg_list] <= $min_n_of_args || [
llength $arg_list] > $max_n_of_args} {
4819 Msg Status "\nERROR: Wrong number of arguments: [
llength $argv]"
4824 set project [
lindex $arg_list 1]
4826 if {$argument_is_no_project == 0} {
4828 regsub "^(\./)?Top/" $project "" project
4830 regsub "/? *\$" $project "" project
4836 Msg Debug "Option list:"
4837 foreach {key value} [
array get options] {
4838 Msg Debug "$key => $value"
4845 if {$proj_conf != 0} {
4848 lassign [
GetIDECommand $proj_conf] cmd before_tcl_script after_tcl_script end_marker
4849 Msg Info "Project $project uses $cmd IDE"
4852 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4854 if {$custom_command ne ""} {
4855 if { [dict exists $custom_commands $directive IDE] } {
4856 lassign [
GetIDECommand "" [dict get $custom_commands $directive IDE]] cmd before_tcl_script after_tcl_script end_marker
4857 Msg Info "Custom command: $custom_command uses $cmd IDE"
4858 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4860 set command "custom_tcl"
4862 }
elseif {$argument_is_no_project == 1} {
4864 Msg Debug "$project will be used as first argument"
4865 }
elseif {$project != ""} {
4868 }
elseif {$min_n_of_args < 0} {
4881 set project_group [
file dirname $project]
4882 set project_name $project
4883 set project [
file tail $project]
4884 Msg Debug "InitLauncher: project_group=$project_group, project_name=$project_name, project=$project"
4886 return [list $directive $project $project_name $project_group $repo_path $old_path $bin_path $top_path $usage $short_usage $command $cmd [
array get options]]
4893 proc IsCommitAncestor {ancestor commit} {
4894 lassign [
GitRet "merge-base --is-ancestor $ancestor $commit"] status result
4903 return [
expr {[info commands sys_install] != ""}]
4908 return [
expr {[info commands get_libero_version] != ""}]
4917 proc IsInList {element list {regex 0} {nocase 0}} {
4921 if {[regexp -nocase $x $element]} {
4925 if {[regexp $x $element]} {
4929 }
elseif {$regex == 0} {
4931 if {[
string tolower $x] eq [
string tolower $element]} {
4935 if {$x eq $element} {
4948 return [
expr {[string first PlanAhead [version]] == 0}]
4956 if {[
catch {
package require ::quartus::flow} result]} {
4969 proc IsRelativePath {path} {
4970 if {[
string index $path 0] == "/" || [
string index $path 0] == "~"} {
4978 proc IsSynplify {} {
4979 return [
expr {[info commands program_version] != ""}]
4984 return [
expr {![IsQuartus] && ![IsXilinx] && ![IsVitisClassic] && ![IsVitisUnified] && ![IsLibero] && ![IsSynplify] && ![IsDiamond]}]
4992 proc IsVerilog {file} {
4993 if {[
file extension $file] == ".v" || [
file extension $file] == ".sv"} {
5005 proc IsVersal {part} {
5006 if {[get_property ARCHITECTURE [get_parts $part]] eq "versal"} {
5016 return [
expr {[string first Vivado [version]] == 0}]
5024 if {[
info commands version] != ""} {
5025 set current_version [version]
5026 if {[
string first PlanAhead $current_version] == 0 || [
string first Vivado $current_version] == 0} {
5028 }
elseif {[
string first xsct $current_version] == 0} {
5031 Msg Warning "This IDE has the version command but it is not PlanAhead or Vivado: $current_version"
5040 proc IsVitisClassic {} {
5041 if {[
info exists globalSettings::vitis_classic]} {
5042 return $globalSettings::vitis_classic
5044 return [
expr {[info commands platform] != ""}]
5048 proc IsVitisUnified {} {
5049 if {[
info exists globalSettings::vitis_unified]} {
5050 return $globalSettings::vitis_unified
5064 proc ExecuteVitisUnifiedCommand {python_script command args {error_prefix "Failed to execute command"} {output_var ""}} {
5065 set cmdlist [list vitis -s $python_script $command]
5067 lappend cmdlist $arg
5069 lappend cmdlist 2>@1
5071 Msg Debug "Executing: vitis -s $python_script $command $args"
5074 set env(PYTHONUNBUFFERED) "1"
5077 if {[
catch {
set pipe [open "|$cmdlist" "r"]} err]} {
5078 Msg Error "$error_prefix: Failed to open pipe: $err"
5082 fconfigure $pipe -buffering line
5083 set script_output ""
5084 set vitis_version ""
5087 set vitis_banner_patterns {
5088 "*Vitis Development Environment*"
5091 "*Copyright*Xilinx*"
5092 "*Copyright*Advanced Micro Devices*"
5093 "*All Rights Reserved*"
5097 while {[
gets $pipe line] >= 0} {
5099 if {[
string match "*Vitis v*" $line]} {
5100 if {[regexp {Vitis\s+v([0-9]+)\.([0-9]+)(?:\.[0-9]+)?} $line -> major minor]} {
5101 set year_last_two [
string range $major end-1 end]
5102 set vitis_version "$year_last_two.$minor"
5108 foreach pattern $vitis_banner_patterns {
5109 if {[
string match $pattern $line]} {
5115 if {![
string match "INFO:*" $line] && ![
string match "WARNING:*" $line] && ![
string match "ERROR:*" $line] && ![
string match "DEBUG:*" $line]} {
5116 if {$vitis_version ne ""} {
5117 set line "INFO: \[Vitis_v$vitis_version\] $line"
5119 set line "INFO: $line"
5123 append script_output "$line\n"
5130 if {[
catch {
close $pipe} err]} {
5131 if {[regexp {exit (\d+)} $err -> exit_code]} {
5132 if {$exit_code != 0} {
5133 Msg Error "$error_prefix (exit code: $exit_code)"
5134 if {$output_var ne ""} {
5135 upvar $output_var output
5136 set output $script_output
5141 Msg Error "$error_prefix: $err"
5142 if {$output_var ne ""} {
5143 upvar $output_var output
5144 set output $script_output
5151 if {$output_var ne ""} {
5152 upvar $output_var output
5153 set output $script_output
5164 proc IsZynq {part} {
5165 if {[regexp {^(xc7z|xczu).*} $part]} {
5172 proc ImportGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
5173 set list_path "$repo_path/Top/$project_name/list"
5174 lassign [
GetHogFiles -list_files {.src,.ext,.sim} -ext_path $ext_path $list_path $repo_path] src_files properties filesets
5179 set properties [
DictGet $simset_dict "properties"]
5180 set options [
DictGet $properties "options"]
5183 set workdir Projects/$project_name/ghdl
5184 file delete -force $workdir
5186 set import_log "$workdir/ghdl-import-${simset_name}.log"
5187 dict for {lib sources} $src_files {
5188 set libname [file rootname $lib]
5189 foreach f $sources {
5190 if {[file extension $f] != ".vhd" && [file extension $f] != ".vhdl"} {
5191 Msg Info "File $f is not a VHDL file, copying it in workfolder..."
5192 file copy -force $f $workdir
5194 set file_path [Relative $repo_path $f]
5195 set import_log_file [open $import_log "a"]
5196 puts "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
5197 puts $import_log_file "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
5198 close $import_log_file
5199 lassign [GHDL "-i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path" $import_log] ret result
5201 Msg CriticalWarning "GHDL import failed for file $f: $result"
5210 proc LaunchGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
5213 set sim_props [
DictGet $simset_dict "properties"]
5214 set options [
DictGet $sim_props "options"]
5215 set runopts [
DictGet $sim_props "run_options"]
5217 dict for {prop_name prop_val} $sim_props {
5218 set prop_name [string toupper $prop_name]
5219 if {$prop_name == "TOP"} {
5220 set top_sim $prop_val
5223 set workdir $repo_path/Projects/$project_name/ghdl
5224 set make_log "$workdir/ghdl-make-${simset_name}.log"
5225 set run_log "$workdir/ghdl-run-${simset_name}.log"
5228 set make_log_file [open $make_log "w"]
5230 puts "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
5231 puts $make_log_file "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
5232 close $make_log_file
5234 lassign [
GHDL "-m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim" $make_log] ret result
5237 Msg Error "GHDL make failed for $top_sim: $result"
5241 set run_log_file [open $run_log "w"]
5242 puts "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
5243 puts $run_log_file "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
5246 lassign [
GHDL "-r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts" $run_log] ret result
5250 Msg Error "GHDL run failed for $top_sim: $result"
5265 proc LaunchImplementation {reset do_create run_folder project_name {repo_path .} {njobs 4} {do_bitstream 0}} {
5266 Msg Info "Starting implementation flow..."
5269 Msg Info "Resetting run before launching implementation..."
5274 source $repo_path/Hog/Tcl/integrated/pre-implementation.tcl
5277 if {$do_bitstream == 1} {
5278 launch_runs impl_1 -to_step [
BinaryStepName [get_property PART [current_project]]] -jobs $njobs -dir $run_folder
5280 launch_runs impl_1 -jobs $njobs -dir $run_folder
5285 Msg Info "running post-implementation"
5286 source $repo_path/Hog/Tcl/integrated/post-implementation.tcl
5287 if {$do_bitstream == 1} {
5288 Msg Info "running pre-bitstream"
5289 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5290 Msg Info "running post-bitstream"
5291 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5295 set prog [get_property PROGRESS [get_runs impl_1]]
5296 set status [get_property STATUS [get_runs impl_1]]
5297 Msg Info "Run: impl_1 progress: $prog, status : $status"
5301 set status_file [open "$run_folder/timing.txt" "w"]
5302 puts $status_file "## $project_name Timing summary"
5304 set f [open [
lindex [glob "$run_folder/impl_1/*.twr" 0]]]
5306 while {[
gets $f line] >= 0} {
5307 if {[
string match "Timing summary:" $line]} {
5308 while {[
gets $f line] >= 0} {
5309 if {[
string match "Timing errors:*" $line]} {
5310 set errs [regexp -inline -- {[0-9]+} $line]
5312 if {[
string match "*Footnotes*" $line]} {
5315 puts $status_file "$line"
5324 Msg Info "Time requirements are met"
5325 file rename -force "$run_folder/timing.txt" "$run_folder/timing_ok.txt"
5328 Msg CriticalWarning "Time requirements are NOT met"
5329 file rename -force "$run_folder/timing.txt" "$run_folder/timing_error.txt"
5335 set wns [get_property STATS.WNS [get_runs [current_run]]]
5336 set tns [get_property STATS.TNS [get_runs [current_run]]]
5337 set whs [get_property STATS.WHS [get_runs [current_run]]]
5338 set ths [get_property STATS.THS [get_runs [current_run]]]
5339 set tpws [get_property STATS.TPWS [get_runs [current_run]]]
5341 if {$wns >= 0 && $whs >= 0 && $tpws >= 0} {
5342 Msg Info "Time requirements are met"
5343 set status_file [open "$run_folder/timing_ok.txt" "w"]
5346 Msg CriticalWarning "Time requirements are NOT met"
5347 set status_file [open "$run_folder/timing_error.txt" "w"]
5351 Msg Status "*** Timing summary ***"
5352 Msg Status "WNS: $wns"
5353 Msg Status "TNS: $tns"
5354 Msg Status "WHS: $whs"
5355 Msg Status "THS: $ths"
5356 Msg Status "TPWS: $tpws"
5362 puts $status_file "## $project_name Timing summary"
5364 m add row "| **Parameter** | \"**value (ns)**\" |"
5365 m add row "| --- | --- |"
5366 m add row "| WNS: | $wns |"
5367 m add row "| TNS: | $tns |"
5368 m add row "| WHS: | $whs |"
5369 m add row "| THS: | $ths |"
5370 m add row "| TPWS: | $tpws |"
5372 puts $status_file [m format 2string]
5373 puts $status_file "\n"
5374 if {$timing_ok == 1} {
5375 puts $status_file " Time requirements are met."
5377 puts $status_file "Time requirements are **NOT** met."
5379 puts $status_file "\n\n"
5383 if {$prog ne "100%"} {
5384 Msg Error "Implementation error"
5389 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5390 Msg Info "Git describe set to $describe"
5392 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5397 if {[
file exists $run_folder/versions.txt]} {
5398 file copy -force $run_folder/versions.txt $dst_dir
5400 Msg Warning "No versions file found in $run_folder/versions.txt"
5403 set timing_files [glob -nocomplain "$run_folder/timing_*.txt"]
5404 set timing_file [
file normalize [
lindex $timing_files 0]]
5406 if {[
file exists $timing_file]} {
5407 file copy -force $timing_file $dst_dir/
5409 Msg Warning "No timing file found, not a problem if running locally"
5413 if {[
IsVersal [get_property part [current_project]]]} {
5414 if {[get_property segmented_configuration [current_project]] == 1} {
5415 Msg Info "Versal Segmented configuration detected: exporting XSA file..."
5416 set xsa_name "$dst_dir/[
file tail $project_name]\-$describe.xsa"
5417 write_hw_platform -fixed -force -file $xsa_name
5421 set revision [get_current_revision]
5423 if {[
catch {execute_module -tool fit} result]} {
5424 Msg Error "Result: $result\n"
5425 Msg Error "Place & Route failed. See the report file.\n"
5427 Msg Info "\nINFO: Place & Route was successful for revision $revision.\n"
5430 if {[
catch {execute_module -tool sta -args "--do_report_timing"} result]} {
5431 Msg Error "Result: $result\n"
5432 Msg Error "Time Quest failed. See the report file.\n"
5434 Msg Info "Time Quest was successfully run for revision $revision.\n"
5437 set panel "Timing Analyzer||Timing Analyzer Summary"
5438 set device [get_report_panel_data -name $panel -col 1 -row_name "Device Name"]
5439 set timing_model [get_report_panel_data -name $panel -col 1 -row_name "Timing Models"]
5440 set delay_model [get_report_panel_data -name $panel -col 1 -row_name "Delay Model"]
5442 Msg Info "*******************************************************************"
5443 Msg Info "Device: $device"
5444 Msg Info "Timing Models: $timing_model"
5445 Msg Info "Delay Model: $delay_model"
5448 Msg Info "*******************************************************************"
5451 Msg Info "Starting implementation flow..."
5452 if {[
catch {run_tool -name {PLACEROUTE}}]} {
5453 Msg Error "PLACEROUTE FAILED!"
5455 Msg Info "PLACEROUTE PASSED."
5459 Msg Info "Run VERIFYTIMING ..."
5460 if {[
catch {run_tool -name {VERIFYTIMING} -script {Hog/Tcl/integrated/libero_timing.tcl}}]} {
5461 Msg CriticalWarning "VERIFYTIMING FAILED!"
5463 Msg Info "VERIFYTIMING PASSED \n"
5469 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5470 Msg Info "Git describe set to $describe"
5472 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5473 file mkdir $dst_dir/reports
5476 if {[
file exists $run_folder/versions.txt]} {
5477 file copy -force $run_folder/versions.txt $dst_dir
5479 Msg Warning "No versions file found in $run_folder/versions.txt"
5482 set timing_file_path [
file normalize "$repo_path/Projects/timing_libero.txt"]
5483 if {[
file exists $timing_file_path]} {
5484 file copy -force $timing_file_path $dst_dir/reports/Timing.txt
5485 set timing_file [open $timing_file_path "r"]
5486 set status_file [open "$dst_dir/timing.txt" "w"]
5487 puts $status_file "## $project_name Timing summary\n\n"
5488 puts $status_file "| | |"
5489 puts $status_file "| --- | --- |"
5490 while {[
gets $timing_file line] >= 0} {
5491 if {[
string match "SUMMARY" $line]} {
5492 while {[
gets $timing_file line] >= 0} {
5493 if {[
string match "END SUMMARY" $line]} {
5496 if {[
string first ":" $line] == -1} {
5499 set out_string "| [
string map {: | } $line] |"
5500 puts $status_file "$out_string"
5505 Msg Warning "No timing file found, not a problem if running locally"
5510 set force_rst "-forceOne"
5512 prj_run Map $force_rst
5513 prj_run PAR $force_rst
5525 proc GenerateBitstreamOnly {project_name {repo_path .}} {
5529 set project_file [
file normalize "$repo_path/Projects/$project_name/$project_name.xpr"]
5530 if {![
file exists $project_file]} {
5531 Msg Error "Project file not found: $project_file. Please create the project first."
5538 set impl_runs [get_runs -quiet impl_1]
5539 if {[
llength $impl_runs] == 0} {
5540 Msg Error "Implementation run 'impl_1' does not exist. Please run implementation first."
5544 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5545 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5547 cd Projects/$project_name/$project_name.runs/impl_1
5548 Msg Info "Running pre-bitstream..."
5549 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5551 Msg Info "Writing bitstream for $project_name..."
5553 write_bitstream -force $dst_dir/$project_name-$describe.bit
5555 Msg Info "Running post-bitstream..."
5556 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5565 proc LaunchSimulation {project_name lib_path simsets {repo_path .} {scripts_only 0} {compile_only 0}} {
5568 set project [
file tail $project_name]
5569 set main_sim_folder [
file normalize "$repo_path/Projects/$project_name/$project.sim/"]
5571 if {$simsets != ""} {
5572 dict for {simset sim_dict} $simsets {
5573 lappend simsets_todo $simset
5575 Msg Info "Will run only the following simulation's sets (if they exist): $simsets_todo"
5578 if {$scripts_only == 1} {
5579 Msg Info "Only generating simulation scripts, not running simulations..."
5582 if {$compile_only == 1} {
5583 Msg Info "Only compiling simulation libraries, not running simulations..."
5588 set sim_dic [dict create]
5590 Msg Info "Retrieving list of simulation sets..."
5591 foreach s [get_filesets] {
5593 set use_simpass_str 0
5596 set type [get_property FILESET_TYPE $s]
5597 if {$type eq "SimulationSrcs"} {
5598 if {$simsets_todo != "" && $s ni $simsets_todo} {
5599 Msg Info "Skipping $s as it was not specified with the -simset option..."
5602 set sim_dict [
DictGet $simsets $s]
5603 set simulator [
DictGet $sim_dict "simulator"]
5604 set_property "target_simulator" $simulator [current_project]
5605 set hog_sim_props [
DictGet $sim_dict "hog"]
5606 dict for {prop_name prop_val} $hog_sim_props {
5607 # If HOG_SIMPASS_STR is set, use the HOG_SIMPASS_STR string to search for in logs, after simulation is done
5608 if {[string toupper $prop_name] == "HOG_SIMPASS_STR" && $prop_val != ""} {
5609 Msg Info "Setting simulation pass string as '$prop_val'"
5610 set use_simpass_str 1
5611 set simpass_str $prop_val
5613 if {[string toupper $prop_name] == "HOG_SILENT_SIM" && $prop_val == 1} {
5614 set quiet_sim " -quiet"
5620 Msg Info "Creating simulation scripts for $s..."
5621 if {[
file exists $repo_path/Top/$project_name/pre-simulation.tcl]} {
5622 Msg Info "Running $repo_path/Top/$project_name/pre-simulation.tcl"
5623 source $repo_path/Top/$project_name/pre-simulation.tcl
5625 if {[
file exists $repo_path/Top/$project_name/pre-$s-simulation.tcl]} {
5626 Msg Info "Running $repo_path/Top/$project_name/pre-$s-simulation.tcl"
5627 source Running $repo_path/Top/$project_name/pre-$s-simulation.tcl
5629 current_fileset -simset $s
5630 set sim_dir $main_sim_folder/$s/behav
5631 set sim_output_logfile $sim_dir/xsim/simulate.log
5632 if {([
string tolower $simulator] eq "xsim")} {
5633 set sim_name "xsim:$s"
5635 set simulation_command "launch_simulation $quiet_sim -simset [get_filesets $s]"
5636 if {[
catch $simulation_command log]} {
5639 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5640 lappend failed $sim_name
5645 if {$use_simpass_str == 1} {
5648 set file_desc [open $sim_output_logfile r]
5649 set log [read $file_desc]
5652 Msg Info "Searching for simulation pass string: '$simpass_str'"
5653 if {[
string first $simpass_str $log] == -1} {
5654 Msg CriticalWarning "Simulation failed for $s, error info: '$simpass_str' NOT found!"
5655 lappend failed $sim_name
5658 lappend success $sim_name
5662 lappend success $sim_name
5666 Msg Info "Simulation library path is set to $lib_path."
5668 if {!([
file exists $lib_path])} {
5669 Msg Warning "Could not find simulation library path: $lib_path, $simulator simulation will not work."
5673 if {$simlib_ok == 1} {
5674 set_property "compxlib.${simulator}_compiled_library_dir" [
file normalize $lib_path] [current_project]
5675 launch_simulation -scripts_only -simset [get_filesets $s]
5676 set top_name [get_property TOP $s]
5677 set sim_script [
file normalize $sim_dir/$simulator/]
5678 Msg Info "Adding simulation script location $sim_script for $s..."
5679 lappend sim_scripts $sim_script
5680 dict append sim_dic $sim_script $s
5682 Msg Error "Cannot run $simulator simulations without a valid library path"
5689 if {[
info exists sim_scripts] && $scripts_only == 0} {
5691 Msg Info "Generating IP simulation targets, if any..."
5693 foreach ip [get_ips] {
5694 generate_target simulation -quiet $ip
5699 Msg Info "====== Starting simulations runs ======"
5702 foreach s $sim_scripts {
5704 set cmd ./compile.sh
5705 Msg Info " ************* Compiling: $s ************* "
5707 set sim_name "comp:[dict get $sim_dic $s]"
5709 Msg CriticalWarning "Compilation failed for $s, error info: $::errorInfo"
5710 lappend failed $sim_name
5712 lappend success $sim_name
5714 Msg Info "###################### Compilation log starts ######################"
5715 Msg Info "\n\n$log\n\n"
5716 Msg Info "###################### Compilation log ends ######################"
5718 if {$compile_only == 1} {
5721 if {[
file exists "./elaborate.sh"] } {
5722 set cmd ./elaborate.sh
5723 Msg Info " ************* Elaborating: $s ************* "
5725 set sim_name "elab:[dict get $sim_dic $s]"
5727 Msg CriticalWarning "Elaboration failed for $s, error info: $::errorInfo"
5728 lappend failed $sim_name
5730 lappend success $sim_name
5732 Msg Info "###################### Elaboration log starts ######################"
5733 Msg Info "\n\n$log\n\n"
5734 Msg Info "###################### Elaboration log ends ######################"
5736 set cmd ./simulate.sh
5737 Msg Info " ************* Simulating: $s ************* "
5742 if {$use_simpass_str == 1} {
5743 if {[
string first $simpass_str $log] == -1} {
5747 Msg Debug "Simulation pass string not set, relying on simulator exit code."
5751 set sim_name "sim:[dict get $sim_dic $s]"
5753 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5754 lappend failed $sim_name
5756 lappend success $sim_name
5758 Msg Info "###################### Simulation log starts ######################"
5759 Msg Info "\n\n$log\n\n"
5760 Msg Info "###################### Simulation log ends ######################"
5765 if {[
llength $success] > 0} {
5766 set successes [
join $success "\n"]
5767 Msg Info "The following simulation sets were successful:\n\n$successes\n\n"
5770 if {[
llength $failed] > 0} {
5771 set failures [
join $failed "\n"]
5772 Msg Error "The following simulation sets have failed:\n\n$failures\n\n"
5774 }
elseif {[
llength $success] > 0} {
5775 Msg Info "All the [
llength $success] compilations, elaborations and simulations were successful."
5778 Msg Info "Simulation done."
5780 Msg Warning "Simulation is not yet supported for [
GetIDEName]."
5787 proc LaunchRTLAnalysis {repo_path {pre_rtl_file ""} {post_rtl_file ""}} {
5789 if {[
file exists $pre_rtl_file]} {
5790 Msg Info "Found pre-rtl Tcl script $pre_rtl_file, executing it..."
5791 source $pre_rtl_file
5793 Msg Info "Starting RTL Analysis..."
5795 synth_design -rtl -name rtl_1
5796 if {[
file exists $post_rtl_file]} {
5797 Msg Info "Found post-rtl Tcl script $post_rtl_file, executing it..."
5798 source $post_rtl_file
5801 Msg Warning "RTL Analysis is not yet supported for [
GetIDEName]."
5814 proc LaunchSynthesis {reset do_create run_folder project_name {repo_path .} {ext_path ""} {njobs 4}} {
5817 Msg Info "Resetting run before launching synthesis..."
5821 source $repo_path/Hog/Tcl/integrated/pre-synthesis.tcl
5823 launch_runs synth_1 -jobs $njobs -dir $run_folder
5825 set prog [get_property PROGRESS [get_runs synth_1]]
5826 set status [get_property STATUS [get_runs synth_1]]
5827 Msg Info "Run: synth_1 progress: $prog, status : $status"
5834 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5835 Msg Info "Git describe set to $describe"
5838 set xci_file [get_property IP_FILE $ip]
5840 set xci_path [
file dirname $xci_file]
5841 set xci_ip_name [
file rootname [
file tail $xci_file]]
5842 foreach rptfile [glob -nocomplain -directory $xci_path *.rpt] {
5843 file copy $rptfile $repo_path/bin/$project_name-$describe/reports
5847 if {$prog ne "100%"} {
5848 Msg Error "Synthesis error, status is: $status"
5852 set project [
file tail [
file rootname $project_name]]
5854 Msg Info "Number of jobs set to $njobs."
5855 set_global_assignment -name NUM_PARALLEL_PROCESSORS $njobs
5859 set describe [
GetHogDescribe [
file normalize $repo_path/Top/$project_name] $repo_path]
5861 set revision [get_current_revision]
5864 set tool_and_command [
split [get_global_assignment -name PRE_FLOW_SCRIPT_FILE] ":"]
5865 set tool [
lindex $tool_and_command 0]
5866 set pre_flow_script [
lindex $tool_and_command 1]
5867 set cmd "$tool -t $pre_flow_script quartus_map $project $revision"
5873 Msg Warning "Can not execute command $cmd"
5874 Msg Warning "LOG: $log"
5876 Msg Info "Pre flow script executed!"
5880 if {![is_project_open]} {
5881 Msg Info "Re-opening project file $project_name..."
5882 project_open $project -current_revision
5886 if {[
catch {execute_module -tool ipg -args "--clean"} result]} {
5887 Msg Error "Result: $result\n"
5888 Msg Error "IP Generation failed. See the report file.\n"
5890 Msg Info "IP Generation was successful for revision $revision.\n"
5894 if {[
catch {execute_module -tool map -args "--parallel"} result]} {
5895 Msg Error "Result: $result\n"
5896 Msg Error "Analysis & Synthesis failed. See the report file.\n"
5898 Msg Info "Analysis & Synthesis was successful for revision $revision.\n"
5902 defvar_set -name RWNETLIST_32_64_MIXED_FLOW -value 0
5904 Msg Info "Run SYNTHESIS..."
5905 if {[
catch {run_tool -name {SYNTHESIZE}}]} {
5906 Msg Error "SYNTHESIZE FAILED!"
5908 Msg Info "SYNTHESIZE PASSED!"
5914 set force_rst "-forceOne"
5916 prj_run Synthesis $force_rst
5917 if {[prj_syn] == "synplify"} {
5918 prj_run Translate $force_rst
5921 Msg Error "Impossible condition. You need to run this in an IDE."
5932 proc LaunchVitisBuild {project_name {repo_path .} {stage "presynth"}} {
5933 set proj_name $project_name
5934 set bin_dir [
file normalize "$repo_path/bin"]
5940 set vitis_workspace [
file normalize "$repo_path/Projects/$project_name/vitis_unified"]
5941 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"]
5943 if {![
ExecuteVitisUnifiedCommand $python_script "app_list" [list $vitis_workspace] "Failed to get app list from Vitis Unified" json_output]} {
5944 Msg Error "Failed to get app list from Vitis Unified"
5947 if {[
catch {
package require json}]} {
5948 Msg Error "JSON package not available for parsing Vitis Unified app list"
5951 set json_output_filtered ""
5952 if {[regexp -lineanchor {\{.*\}} $json_output json_output_filtered]} {
5953 set ws_apps [json::json2dict $json_output_filtered]
5955 set ws_apps [json::json2dict $json_output]
5960 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
5962 Msg Error "Impossible condition. You need to run this in a Vitis Unified or Vitis Classic IDE."
5967 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$proj_name] $repo_path] commit version hog_hash hog_ver top_hash top_ver \
5968 libs hashes vers cons_ver cons_hash ext_names ext_hashes xml_hash xml_ver user_ip_repos user_ip_hashes user_ip_vers
5970 if {$commit == 0 } {
set commit $this_commit}
5977 foreach app_name [dict keys $ws_apps] {
5978 app config -name $app_name -set build-config Release
5982 WriteGenerics "vitisbuild" $repo_path $proj_name $date $timee $commit $version $top_hash $top_ver $hog_hash $hog_ver $cons_ver $cons_hash $libs \
5983 $vers $hashes $ext_names $ext_hashes $user_ip_repos $user_ip_vers $user_ip_hashes $flavour $xml_ver $xml_hash
5986 foreach app_name [dict keys $ws_apps] {
5989 if {![
ExecuteVitisUnifiedCommand $python_script "build_app" [list $app_name $vitis_workspace] "Failed to build app $app_name"]} {
5990 Msg Error "Failed to build app $app_name"
5994 app build -name $app_name
5998 if {$stage == "presynth"} {
5999 Msg Info "Done building apps for $project_name..."
6002 if {[
info exists ::globalSettings::vitis_only_pass] && $::globalSettings::vitis_only_pass == 1} {
6003 Msg Info "Skipping bin directory creation in vitis_only mode (post-bitstream.tcl handles artifacts)."
6007 Msg Info "Evaluating Hog describe for $project_name..."
6008 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6009 Msg Info "Hog describe set to: $describe"
6010 set dst_dir [
file normalize "$bin_dir/$proj_name\-$describe"]
6011 if {![
file exists $dst_dir]} {
6012 Msg Info "Creating $dst_dir..."
6016 foreach app_name [dict keys $ws_apps] {
6018 set main_file "$repo_path/Projects/$project_name/vitis_unified/$app_name/build/$app_name.elf"
6020 set main_file "$repo_path/Projects/$project_name/vitis_classic/$app_name/Release/$app_name.elf"
6022 set dst_main [
file normalize "$dst_dir/[
file tail $proj_name]\-$app_name\-$describe.elf"]
6024 if {![
file exists $main_file]} {
6025 Msg Error "No Vitis .elf file found. Perhaps there was an issue building it?"
6029 Msg Info "Copying main binary file $main_file into $dst_main..."
6030 file copy -force $main_file $dst_main
6045 proc LaunchHlsBuild {project_name {repo_path .}} {
6046 set proj_name $project_name
6047 set bin_dir [
file normalize "$repo_path/bin"]
6051 set conf_file [
file normalize "$repo_path/Top/$project_name/hog.conf"]
6052 if {![
file exists $conf_file]} {
6053 Msg Error "Configuration file not found: $conf_file"
6056 set properties [
ReadConf $conf_file]
6057 set hls_components [dict filter $properties key {hls:*}]
6059 if {[dict size $hls_components] == 0} {
6060 Msg Info "No HLS components found for project $project_name"
6064 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/HlsCommands.py"]
6072 set ide_name [
string tolower [
lindex $ide_info 0]]
6073 if {$ide_name eq "vivado_vitis_unified"} {
6074 set is_mixed_project 1
6076 set is_mixed_project 0
6079 dict for {hls_key hls_props} $hls_components {
6080 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {
6083 set component_name [string trim $component_name]
6084 Msg Info "Building HLS component: $component_name"
6086 # Read HLS_CONFIG path from hog.conf (mandatory)
6088 if {[dict exists $hls_props hls_config]} {
6089 set hls_cfg_rel [dict get $hls_props hls_config]
6090 } elseif {[dict exists $hls_props HLS_CONFIG]} {
6091 set hls_cfg_rel [dict get $hls_props HLS_CONFIG]
6093 if {$hls_cfg_rel eq ""} {
6094 Msg Error "HLS component '$component_name' missing HLS_CONFIG in hog.conf"
6098 set cfg_file [file normalize "$repo_path/$hls_cfg_rel"]
6099 if {![file exists $cfg_file]} {
6100 Msg Error "HLS config file not found: $cfg_file (from HLS_CONFIG=$hls_cfg_rel)"
6104 set hls_work_dir [file normalize "$repo_path/Projects/$project_name/vitis_unified/$component_name"]
6105 file mkdir $hls_work_dir
6108 Msg Info "Running C synthesis for HLS component '$component_name'..."
6109 if {![ExecuteVitisUnifiedCommand $python_script "synthesis" \
6110 [list $component_name $cfg_file $hls_work_dir] \
6111 "Failed to run C synthesis for $component_name"]} {
6112 Msg Error "C synthesis failed for HLS component '$component_name'"
6117 Msg Info "Running implementation for HLS component '$component_name'..."
6118 if {![ExecuteVitisUnifiedCommand $python_script "impl" \
6119 [list $component_name $cfg_file $hls_work_dir] \
6120 "Failed to run implementation for $component_name"]} {
6121 Msg Error "Implementation failed for HLS component '$component_name'"
6125 # Export VHDL to source tree if VHDL_OUTPUT is set
6127 if {[dict exists $hls_props vhdl_output]} {
6128 set vhdl_output [dict get $hls_props vhdl_output]
6129 } elseif {[dict exists $hls_props VHDL_OUTPUT]} {
6130 set vhdl_output [dict get $hls_props VHDL_OUTPUT]
6132 if {$vhdl_output ne ""} {
6133 set vhdl_output_dir [file normalize "$repo_path/$vhdl_output"]
6134 Msg Info "Exporting VHDL for '$component_name' to $vhdl_output_dir..."
6135 if {![ExecuteVitisUnifiedCommand $python_script "export_rtl" \
6136 [list $component_name $hls_work_dir $vhdl_output_dir vhdl] \
6137 "Failed to export VHDL for $component_name"]} {
6138 Msg Warning "Could not export VHDL for HLS component '$component_name'"
6142 # Export Verilog to source tree if VERILOG_OUTPUT is set
6143 set verilog_output ""
6144 if {[dict exists $hls_props verilog_output]} {
6145 set verilog_output [dict get $hls_props verilog_output]
6146 } elseif {[dict exists $hls_props VERILOG_OUTPUT]} {
6147 set verilog_output [dict get $hls_props VERILOG_OUTPUT]
6149 if {$verilog_output ne ""} {
6150 set verilog_output_dir [file normalize "$repo_path/$verilog_output"]
6151 Msg Info "Exporting Verilog for '$component_name' to $verilog_output_dir..."
6152 if {![ExecuteVitisUnifiedCommand $python_script "export_rtl" \
6153 [list $component_name $hls_work_dir $verilog_output_dir verilog] \
6154 "Failed to export Verilog for $component_name"]} {
6155 Msg Warning "Could not export Verilog for HLS component '$component_name'"
6159 # Export IP catalog ZIP to source tree if IP_OUTPUT is set
6161 if {[dict exists $hls_props ip_output]} {
6162 set ip_output [dict get $hls_props ip_output]
6163 } elseif {[dict exists $hls_props IP_OUTPUT]} {
6164 set ip_output [dict get $hls_props IP_OUTPUT]
6166 if {$ip_output ne ""} {
6167 set ip_output_dir [file normalize "$repo_path/$ip_output"]
6168 Msg Info "Exporting IP catalog for '$component_name' to $ip_output_dir..."
6169 if {![ExecuteVitisUnifiedCommand $python_script "export_ip" \
6170 [list $component_name $hls_work_dir $ip_output_dir] \
6171 "Failed to export IP for $component_name"]} {
6172 Msg Error "Could not export IP for HLS component '$component_name'. Make sure package.output.format=ip_catalog is set in hls_config.cfg."
6176 # Collect reports into bin/ for CI and release notes
6177 Msg Info "Evaluating Hog describe for $project_name..."
6178 set describe [GetHogDescribe [file normalize ./Top/$project_name] $repo_path]
6179 Msg Info "Hog describe set to: $describe"
6180 set dst_dir [file normalize "$bin_dir/$proj_name\-$describe"]
6181 if {![file exists $dst_dir]} {
6182 Msg Info "Creating $dst_dir..."
6186 # Mixed projects group HLS components under bin/<proj>/vitis_hls/; pure
6187 # vitis_unified projects place them directly under bin/<proj>/
6188 if {$is_mixed_project} {
6189 set hls_out_dir [file normalize "$dst_dir/vitis_hls"]
6191 set hls_out_dir $dst_dir
6194 Msg Info "Collecting HLS reports for component '$component_name'..."
6195 if {![ExecuteVitisUnifiedCommand $python_script "collect_reports" \
6196 [list $component_name $hls_work_dir $hls_out_dir] \
6197 "Failed to collect HLS reports for $component_name"]} {
6198 Msg Warning "No reports found for HLS component '$component_name'"
6201 # Generate markdown summary files (utilization.txt, timing_ok.txt /
6202 # timing_error.txt) under <hls_out_dir>/<component>/. File names mirror the
6203 # Vivado convention so the existing CI logic (release notes assembly and
6204 # timing-failure detection) works for HLS components too
6205 Msg Info "Generating HLS release-notes summary for component '$component_name'..."
6206 if {![ExecuteVitisUnifiedCommand $python_script "release_notes" \
6207 [list $component_name $hls_work_dir $hls_out_dir] \
6208 "Failed to generate HLS summary for $component_name"]} {
6209 Msg Warning "Could not generate HLS summary for component '$component_name'"
6212 Msg Info "HLS component '$component_name' built successfully"
6217 if {!$is_mixed_project && [
info exists dst_dir] && [
file isdirectory $dst_dir]} {
6218 set versions_file [
file normalize "$dst_dir/versions.txt"]
6219 Msg Info "Generating top-level versions.txt for pure-HLS project at $versions_file"
6221 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$project_name] $repo_path] \
6222 commit version hog_hash hog_ver top_hash top_ver \
6223 libs hashes vers cons_ver cons_hash \
6224 ext_names ext_hashes xml_hash xml_ver \
6225 user_ip_repos user_ip_hashes user_ip_vers
6226 if {$commit == 0} {
set commit [
GetSHA]}
6230 set fh [open $versions_file "w"]
6231 puts $fh "## $proj_name Version Table\n"
6232 puts $fh "| **File set** | **Commit SHA** | **Version** |"
6233 puts $fh "| --- | --- | --- |"
6234 puts $fh "| Global | $commit | $version_str |"
6235 puts $fh "| Top Directory | $top_hash | $top_ver_str |"
6236 puts $fh "| Hog | $hog_hash | $hog_ver_str |"
6237 foreach l $libs v $vers h $hashes {
6239 puts $fh "| **Lib:** $l | $h | $v_str |"
6244 Msg Warning "Could not generate top-level versions.txt for pure-HLS project: $err"
6248 Msg Info "Done building HLS components for $project_name"
6264 proc LaunchHlsSimulation {project_name {repo_path .} {hls_simsets {}}} {
6267 set conf_file [
file normalize "$repo_path/Top/$project_name/hog.conf"]
6268 if {![
file exists $conf_file]} {
6269 Msg Error "Configuration file not found: $conf_file"
6272 set properties [
ReadConf $conf_file]
6273 set hls_components [dict filter $properties key {hls:*}]
6275 if {[dict size $hls_components] == 0} {
6276 Msg Info "No HLS components found for project $project_name"
6282 set targeted_mode [
expr {[llength $hls_simsets] > 0}]
6283 set targets [dict create]
6284 foreach entry $hls_simsets {
6285 if {[regexp {^(csim|cosim):(.+)$} $entry -> sim_type comp_name]} {
6286 dict lappend targets $comp_name $sim_type
6288 Msg Error "Invalid HLS simset format '$entry'. Expected 'csim:<component>' or 'cosim:<component>'."
6289 Msg Info "Available HLS components in hog.conf:"
6290 dict for {hls_key hls_props} $hls_components {
6291 if {[regexp {^hls:(.+)$} $hls_key -> cname]} {
6292 Msg Info " - csim:[string trim $cname]"
6293 Msg Info " - cosim:[string trim $cname]"
6300 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/HlsCommands.py"]
6302 dict for {hls_key hls_props} $hls_components {
6303 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {
6306 set component_name [string trim $component_name]
6308 # In targeted mode, skip components not in the request
6309 if {$targeted_mode && ![dict exists $targets $component_name]} {
6313 # Read HLS_CONFIG path from hog.conf (mandatory)
6315 if {[dict exists $hls_props hls_config]} {
6316 set hls_cfg_rel [dict get $hls_props hls_config]
6317 } elseif {[dict exists $hls_props HLS_CONFIG]} {
6318 set hls_cfg_rel [dict get $hls_props HLS_CONFIG]
6320 if {$hls_cfg_rel eq ""} {
6321 Msg Error "HLS component '$component_name' missing HLS_CONFIG in hog.conf"
6325 set cfg_file [file normalize "$repo_path/$hls_cfg_rel"]
6326 if {![file exists $cfg_file]} {
6327 Msg Error "HLS config file not found: $cfg_file (from HLS_CONFIG=$hls_cfg_rel)"
6331 set hls_work_dir [file normalize "$repo_path/Projects/$project_name/vitis_unified/$component_name"]
6332 file mkdir $hls_work_dir
6334 # Determine which simulations to run
6335 if {$targeted_mode} {
6336 # User explicitly requested these sim types via -simset
6337 set sim_types [dict get $targets $component_name]
6338 set run_csim [expr {"csim" in $sim_types}]
6339 set run_cosim [expr {"cosim" in $sim_types}]
6341 # Validate: check if the requested sim is enabled in hog.conf
6342 foreach st $sim_types {
6344 if {[dict exists $hls_props $st]} {
6345 set flag_val [dict get $hls_props $st]
6346 } elseif {[dict exists $hls_props [string toupper $st]]} {
6347 set flag_val [dict get $hls_props [string toupper $st]]
6349 set is_enabled [expr {[string tolower $flag_val] eq "true" || $flag_val eq "1"}]
6351 set upper_st [string toupper $st]
6352 Msg Warning "HLS simulation '$st' requested for '$component_name' but\
6353 $upper_st is not enabled in \[hls:$component_name\] section of\
6354 hog.conf. Running it anyway."
6355 Msg Info "To enable it permanently, add '$upper_st=true' under\
6356 \[hls:$component_name\] in Top/$project_name/hog.conf"
6360 # Default mode: read flags from hog.conf
6363 foreach {key_lower key_upper} {csim CSIM cosim COSIM} {
6365 if {[dict exists $hls_props $key_lower]} {
6366 set val [dict get $hls_props $key_lower]
6367 } elseif {[dict exists $hls_props $key_upper]} {
6368 set val [dict get $hls_props $key_upper]
6370 if {[string tolower $val] eq "true" || $val eq "1"} {
6371 set run_$key_lower 1
6375 if {!$run_csim && !$run_cosim} {
6376 Msg Info "No HLS simulations enabled for '$component_name' (set CSIM=true and/or COSIM=true in \[hls:$component_name\] in hog.conf)"
6381 Msg Info "Running HLS simulations for component: $component_name"
6385 Msg Info "Running C simulation for HLS component '$component_name'..."
6386 if {![ExecuteVitisUnifiedCommand $python_script "csim" \
6387 [list $component_name $cfg_file $hls_work_dir] \
6388 "Failed to run C simulation for $component_name"]} {
6389 Msg Error "C simulation failed for HLS component '$component_name'"
6394 # COSIM (auto-run synthesis if needed)
6396 set syn_done_marker [file normalize "$hls_work_dir/hls/syn"]
6397 if {![file exists $syn_done_marker]} {
6398 Msg Info "C synthesis output not found for '$component_name', running synthesis automatically before co-simulation..."
6399 if {![ExecuteVitisUnifiedCommand $python_script "synthesis" \
6400 [list $component_name $cfg_file $hls_work_dir] \
6401 "Failed to run C synthesis for $component_name"]} {
6402 Msg Error "C synthesis failed for HLS component '$component_name', cannot proceed with co-simulation"
6407 Msg Info "Running C/RTL co-simulation for HLS component '$component_name'..."
6408 if {![ExecuteVitisUnifiedCommand $python_script "cosim" \
6409 [list $component_name $cfg_file $hls_work_dir] \
6410 "Failed to run co-simulation for $component_name"]} {
6411 Msg Error "C/RTL co-simulation failed for HLS component '$component_name'"
6416 Msg Info "HLS simulations completed for '$component_name'"
6419 Msg Info "Done with HLS simulations for $project_name"
6427 proc GetProcFromProps {repo_path props platform} {
6428 if {[dict exists $props "platform:$platform" "BIF"]} {
6429 set bif_file [dict get $props "platform:$platform" "BIF"]
6431 set bif_file "$repo_path/$bif_file"
6435 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
6445 proc GetBifFromProps {repo_path props platform} {
6446 if {[dict exists $props "platform:$platform" "BIF"]} {
6447 set bif_file [dict get $props "platform:$platform" "BIF"]
6449 set bif_file "$repo_path/$bif_file"
6453 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
6462 proc GetPartFromProps {props} {
6463 if {[dict exists $props "main" "PART"]} {
6464 return [
string tolower [dict get $props "main" "PART"]]
6466 Msg Error "Part number not found in properties"
6475 proc GetArchFromPart {part} {
6477 if {[
string match "xczu*" $part]} {
6479 }
elseif {[
string match "xc7z*" $part]} {
6481 }
elseif {[
string match "xck26*" $part]} {
6484 Msg CriticalWarning "Unknown part number: $part"
6493 proc GetAppsFromProps {props {list_names 0}} {
6494 set prop_apps [dict filter $props key {app:*}]
6495 set apps [dict create]
6496 set app_names [list]
6498 dict for {app_key app_value} $prop_apps {
6499 if {[regexp {^app:(.+)$} $app_key -> app_name]} {
6500 set app_name [string trim [string tolower $app_name]]
6501 # Convert only the keys of the inner dictionary to lowercase
6502 set app_value_lower [dict create]
6503 dict for {key value} $app_value {
6504 dict set app_value_lower [string tolower $key] $value
6506 dict set apps $app_name $app_value_lower
6507 lappend app_names $app_name
6510 if {$list_names eq 1} {
6521 proc GetPlatformsFromProps {props {list_names 0} {lower_case 0}} {
6522 set platforms [dict create]
6523 set platform_names [list]
6524 set prop_platforms [dict filter $props key {platform:*}]
6526 dict for {platform_key platform_value} $prop_platforms {
6527 if {[regexp {^platform:(.+)$} $platform_key -> platform_name]} {
6528 if {$lower_case == 1} {
6529 set platform_name [string trim [string tolower $platform_name]]
6531 set platform_name [string trim $platform_name]
6533 dict set platforms $platform_name $platform_value
6534 lappend platform_names $platform_name
6537 if {$list_names eq 1} {
6538 return $platform_names
6553 proc GenerateBootArtifacts {properties repo_path proj_dir bin_dir proj_name describe bitfile mmi_file} {
6554 set elf_list [glob -nocomplain "$bin_dir/*.elf"]
6558 if {[
llength $elf_list] == 0} {
6559 Msg Warning "No ELF files found in $bin_dir, skipping generation of boot artifacts"
6563 if {![
file exists $bitfile]} {
6564 Msg Warning "Bitfile $bitfile does not exist, skipping generation of boot artifacts"
6568 Msg Info "Generating boot artifacts for $proj_name..."
6569 Msg Info "Found apps: $apps"
6570 Msg Info "Found platforms: $platforms"
6574 foreach elf_file $elf_list {
6575 set elf_name [
file rootname [
file tail $elf_file]]
6576 Msg Info "Found elf name: $elf_name"
6577 Msg Info "Removing $describe from elf"
6580 if {[regexp "^(.+)-(.+)-$describe\$" $elf_name -> project_name elf_app]} {
6581 set elf_app [
string trim [
string tolower $elf_app]]
6582 Msg Info "Found elf_app: $elf_app"
6584 Msg Error "Could not extract app name from elf file: $elf_name"
6587 Msg Info "Removed project name ($project_name) and $describe from elf"
6589 set app_conf [dict get $apps $elf_app]
6590 set plat [dict get $app_conf "platform"]
6591 set app_proc [dict get $app_conf "proc"]
6594 if {[regexp -nocase {microblaze|risc} $app_proc]} {
6595 Msg Info "Detected soft processor ($app_proc) for $elf_app, updating bitstream memory with ELF file..."
6598 if {[dict size $proc_map] == 0} {
6599 Msg Error "Failed to read map from $proc_map_file"
6602 Msg Info "Found processor map: $proc_map"
6604 set proc_cell [
lindex [
split [dict get $proc_map $app_proc] ":"] 1]
6605 Msg Info "Updating memory at processor cell: $proc_cell"
6607 set update_mem_cmd "updatemem -force -meminfo $mmi_file -data $elf_file -bit $bitfile -proc $proc_cell -out $bitfile"
6608 set ret [
catch {
exec -ignorestderr {*}$update_mem_cmd >@ stdout} result]
6610 Msg Error "Error updating memory for $elf_app: $result"
6612 Msg Info "Done updating memory for $elf_app"
6615 Msg Info "Detected hard processor ($app_proc) for $elf_app. Make sure the .elf file is defined in the platform ($plat)\
6616 .bif file to be included in the bootable binary image (.bin) generation."
6622 foreach plat $platforms {
6624 if {$bif_file != ""} {
6625 Msg Info "Generating bootable binary image (.bin) for $plat"
6627 Msg Info "Architecture: $arch"
6628 Msg Info "BIF file: $bif_file"
6629 set bootgen_cmd "bootgen -arch $arch -image $bif_file -o i $bin_dir/$proj_name-$plat-$describe.bin -w on"
6630 set ret [
catch {
exec -ignorestderr {*}$bootgen_cmd >@ stdout} result]
6632 Msg Error "Error generating bootable binary image (.bin) for $elf_app: $result"
6634 Msg Info "Done generating bootable binary image (.bin) for $plat"
6643 proc ReadProcMap {proc_map_file} {
6644 set proc_map [dict create]
6645 if {[
file exists $proc_map_file]} {
6646 set f [open $proc_map_file "r"]
6647 while {[
gets $f line] >= 0} {
6648 Msg Debug "Line: $line"
6649 if {[regexp {^(\S+)\s+(.+)$} $line -> key value]} {
6650 Msg Debug "Found key: $key, value: $value in proc map file"
6651 dict set proc_map $key $value
6665 proc ListProjects {{repo_path .} {print 1} {ret_conf 0}} {
6666 set top_path [
file normalize $repo_path/Top]
6667 set confs [
findFiles [
file normalize $top_path] hog.conf]
6669 set confs [lsort $confs]
6673 set p [
Relative $top_path [
file dirname $c]]
6676 if {$description eq "test"} {
6677 set description " - Test project"
6678 }
elseif {$description ne ""} {
6679 set description " - $description"
6682 if {$print == 1 || $description ne " - Test project"} {
6684 set g [
file dirname $p]
6695 if {$ret_conf == 0} {
6707 proc Md5Sum {file_name} {
6708 if {!([
file exists $file_name])} {
6709 Msg Warning "Could not find $file_name."
6712 if {[
catch {
package require md5 2.0.7} result]} {
6713 Msg Warning "Tcl package md5 version 2.0.7 not found ($result), will use command line..."
6714 set hash [
lindex [
Execute md5sum $file_name] 0]
6716 set file_hash [
string tolower [md5::md5 -hex -file $file_name]]
6730 proc MergeDict {dict0 dict1 {remove_duplicates 1}} {
6731 set outdict [dict merge $dict1 $dict0]
6732 foreach key [dict keys $dict1] {
6733 if {[dict exists $dict0 $key]} {
6734 set temp_list [dict get $dict1 $key]
6735 foreach item $temp_list {
6737 if {[
IsInList $item [
DictGet $outdict $key]] == 0 || $remove_duplicates == 0} {
6739 dict lappend outdict $key $item
6751 proc MoveElementToEnd {inputList element} {
6752 set index [lsearch $inputList $element]
6754 set inputList [
lreplace $inputList $index $index]
6755 lappend inputList $element
6764 proc OpenProject {project_file repo_path} {
6766 open_project $project_file
6768 set project_folder [
file dirname $project_file]
6769 set project [
file tail [
file rootname $project_file]]
6770 if {[
file exists $project_folder]} {
6772 if {![is_project_open]} {
6773 Msg Info "Opening existing project file $project_file..."
6774 project_open $project -current_revision
6777 Msg Error "Project directory not found for $project_file."
6781 Msg Info "Opening existing project file $project_file..."
6783 open_project -file $project_file -do_backup_on_convert 1 -backup_file {./Projects/$project_file.zip}
6785 Msg Info "Opening existing project file $project_file..."
6786 prj_project open $project_file
6788 Msg Error "This IDE is currently not supported by Hog. Exiting!"
6795 return $tcl_platform(platform)
6805 proc ParseJSON {JSON_FILE JSON_KEY} {
6806 set result [
catch {
package require Tcl 8.4} TclFound]
6807 if {"$result" != "0"} {
6808 Msg CriticalWarning "Cannot find Tcl package version equal or higher than 8.4.\n $TclFound\n Exiting"
6812 set result [
catch {
package require json} JsonFound]
6813 if {"$result" != "0"} {
6814 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
6817 set JsonDict [json::json2dict $JSON_FILE]
6818 set result [
catch {dict get $JsonDict $JSON_KEY} RETURNVALUE]
6819 if {"$result" != "0"} {
6820 Msg CriticalWarning "Cannot find $JSON_KEY in $JSON_FILE\n Exiting"
6833 proc ProjectExists {project {repo_path .}} {
6834 set index [lsearch -exact [
ListProjects $repo_path 0] $project]
6840 Msg Error "Project $project not found in repository $repo_path"
6851 proc ReadConf {file_name} {
6852 if {[
catch {
package require inifile 0.2.3} ERROR]} {
6853 Msg Error "Could not find inifile package version 0.2.3 or higher.\n
6854 To use ghdl, libero or diamond with Hog, you need to install the tcllib package\n
6855 You can install it with 'sudo apt install tcllib' on Debian/Ubuntu or 'sudo dnf install tcllib' on Fedora/RedHat/CentOs."
6859 ::ini::commentchar "#"
6860 set f [::ini::open $file_name]
6861 set properties [dict create]
6862 foreach sec [::ini::sections $f] {
6864 if {$new_sec == "files"} {
6867 set key_pairs [::ini::get $f $sec]
6869 regsub -all {\{\"} $key_pairs "\{" key_pairs
6870 regsub -all {\"\}} $key_pairs "\}" key_pairs
6872 dict set properties $new_sec [dict create {*}$key_pairs]
6885 proc ReadExtraFileList {extra_file_name} {
6886 set extra_file_dict [dict create]
6887 if {[
file exists $extra_file_name]} {
6888 set file [open $extra_file_name "r"]
6889 set file_data [read $file]
6892 set data [
split $file_data "\n"]
6893 foreach line $data {
6894 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
6895 set ip_and_md5 [regexp -all -inline {\S+} $line]
6896 dict lappend extra_file_dict "[
lindex $ip_and_md5 0]" "[
lindex $ip_and_md5 1]"
6900 return $extra_file_dict
6911 proc ExpandHlsConfigFiles {cfg_file} {
6913 if {![
file exists $cfg_file] || ![
file isfile $cfg_file]} {
6916 set cfg_dir [
file normalize [
file dirname $cfg_file]]
6918 if {[
catch {
set fp [open $cfg_file r]} err]} {
6919 Msg Warning "ExpandHlsConfigFiles: cannot open $cfg_file: $err"
6922 set lines [
split [read $fp] "\n"]
6925 foreach line $lines {
6926 if {[regexp {^[\t\s]*$} $line]} { continue}
6927 if {[regexp {^[\t\s]*\#} $line]} { continue}
6928 if {[regexp {^\s*\[.*\]\s*$} $line]} { continue}
6929 if {![regexp {^\s*[^=\s]+\s*=\s*(.+?)\s*$} $line -> value]} { continue}
6931 foreach tok [regexp -all -inline {\S+} $value] {
6932 if {[
file pathtype $tok] eq "absolute"} {
6933 set candidate [
file normalize $tok]
6935 set candidate [
file normalize [
file join $cfg_dir $tok]]
6937 if {[
file isfile $candidate]} {
6938 lappend out $candidate
6939 }
elseif {[
file isdirectory $candidate]} {
6940 set stack [list $candidate]
6941 while {[
llength $stack] > 0} {
6942 set d [
lindex $stack 0]
6943 set stack [
lrange $stack 1 end]
6944 foreach f [glob -nocomplain -directory $d -types f *] {
6945 lappend out [
file normalize $f]
6947 foreach sub [glob -nocomplain -directory $d -types d *] {
6954 return [lsort -unique $out]
6964 proc GetHlsConfigsFromProjConf {conf_file repo_path} {
6965 set out [dict create]
6966 if {![
file exists $conf_file]} {
return $out}
6967 if {[
catch {
set properties [
ReadConf $conf_file]} err]} {
6968 Msg Debug "GetHlsConfigsFromProjConf: cannot parse $conf_file: $err"
6971 set hls_components [dict filter $properties key {hls:*}]
6972 dict for {hls_key hls_props} $hls_components {
6973 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} { continue }
6974 set component_name [string trim $component_name]
6976 if {[dict exists $hls_props hls_config]} {
6977 set cfg_rel [dict get $hls_props hls_config]
6978 } elseif {[dict exists $hls_props HLS_CONFIG]} {
6979 set cfg_rel [dict get $hls_props HLS_CONFIG]
6981 if {$cfg_rel eq ""} { continue }
6982 set cfg_abs [file normalize "$repo_path/$cfg_rel"]
6983 if {![file exists $cfg_abs]} {
6984 Msg CriticalWarning "HLS component '$component_name': HLS_CONFIG=$cfg_rel does not exist on disk."
6987 dict set out $component_name $cfg_abs
7009 proc ReadListFile {args} {
7012 if {[
catch {
package require cmdline} ERROR]} {
7013 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
7019 {lib.arg "" "The name of the library files will be added to, if not given will be extracted from the file name."}
7020 {fileset.arg "" "The name of the library, from the main list file"}
7021 {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."}
7022 {print_log "If set, will use PrintFileTree for the VIEW directive"}
7023 {indent.arg "" "Used to indent files with the VIEW directive"}
7026 set usage "USAGE: ReadListFile \[options\] <list file> <path>"
7027 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
7033 set list_file [
lindex $args 0]
7034 set path [
lindex $args 1]
7035 set sha_mode $options(sha_mode)
7036 set lib $options(lib)
7037 set fileset $options(fileset)
7038 set print_log $options(print_log)
7039 set indent $options(indent)
7041 if {$sha_mode == 1} {
7042 set sha_mode_opt "-sha_mode"
7047 if {$print_log == 1} {
7048 set print_log_opt "-print_log"
7050 set print_log_opt ""
7055 set lib [
file rootname [
file tail $list_file]]
7057 set fp [open $list_file r]
7058 set file_data [read $fp]
7060 set list_file_ext [
file extension $list_file]
7061 switch $list_file_ext {
7063 if {$fileset eq ""} {
7069 set fileset "constrs_1"
7072 set fileset "sources_1"
7076 set libraries [dict create]
7077 set filesets [dict create]
7078 set properties [dict create]
7080 set data [
split $file_data "\n"]
7082 set n [
llength $data]
7084 if {$print_log == 1} {
7085 if {$indent eq ""} {
7086 set list_file_rel [
file tail $list_file]
7087 Msg Status "\n$list_file_rel"
7091 Msg Debug "$n lines read from $list_file."
7094 foreach line $data {
7096 if {![regexp {^[\t\s]*$} $line] & ![regexp {^[\t\s]*\#} $line]} {
7097 set file_and_prop [regexp -all -inline {\S+} $line]
7098 set srcfile [
lindex $file_and_prop 0]
7099 set srcfile "$path/$srcfile"
7101 set srcfiles [glob -nocomplain $srcfile]
7104 if {$srcfiles != $srcfile && ![
string equal $srcfiles ""]} {
7105 Msg Debug "Wildcard source expanded from $srcfile to $srcfiles"
7107 if {![
file exists $srcfile]} {
7108 if {$print_log == 0} {
7109 Msg CriticalWarning "File: $srcfile (from list file: $list_file) does not exist."
7115 foreach vhdlfile $srcfiles {
7116 if {[
file exists $vhdlfile]} {
7117 set vhdlfile [
file normalize $vhdlfile]
7118 set extension [
file extension $vhdlfile]
7120 set prop [
lrange $file_and_prop 1 end]
7123 set library [
lindex [regexp -inline {\ylib\s*=\s*(.+?)\y.*} $prop] 1]
7124 if {$library == ""} {
7128 if {$extension == $list_file_ext} {
7131 set ref_path [
lindex [regexp -inline {\ypath\s*=\s*(\S+).*} $prop] 1]
7132 if {$ref_path eq ""} {
7135 set ref_path [
file normalize $path/$ref_path]
7137 Msg Debug "List file $vhdlfile found in list file, recursively opening it using path \"$ref_path\"..."
7138 if {$print_log == 1} {
7139 if {[
file normalize $last_printed] ne [
file normalize $vhdlfile]} {
7140 Msg Status "$indent Inside [
file tail $vhdlfile]:"
7144 lassign [
ReadListFile {*}"-indent \" $indent\" -lib $library -fileset $fileset $sha_mode_opt $print_log_opt $vhdlfile $ref_path"] l p fs
7146 set properties [
MergeDict $p $properties]
7148 }
elseif {[lsearch {.src .sim .con ReadExtraFileList} $extension] >= 0} {
7150 Msg Error "$vhdlfile cannot be included into $list_file, $extension files must be included into $extension files."
7153 regsub -all " *= *" $prop "=" prop
7157 if {[
string first "lib=" $p] == -1} {
7159 set pos [
string first "=" $p]
7163 set prop_name [
string range $p 0 [
expr {$pos - 1}]]
7166 dict lappend properties $vhdlfile $p
7167 Msg Debug "Adding property $p to $vhdlfile..."
7168 }
elseif {$list_file_ext != ".ipb"} {
7169 Msg Warning "Setting Property $p is not supported for file $vhdlfile or it is already its default. \
7174 if {[lsearch {.xci .ip .bd .xcix} $extension] >= 0} {
7176 set lib_name "ips.src"
7177 }
elseif {[
IsInList $extension {.vhd .vhdl}] || $list_file_ext == ".sim"} {
7179 if {![
IsInList $extension {.vhd .vhdl}]} {
7180 set lib_name "others.sim"
7182 set lib_name "$library$list_file_ext"
7184 }
elseif {$list_file_ext == ".con"} {
7185 set lib_name "sources.con"
7186 }
elseif {$list_file_ext == ".ipb"} {
7187 set lib_name "xml.ipb"
7188 }
elseif { [
IsInList $list_file_ext {.src}] && [
IsInList $extension {.c .cpp .h .hpp}] } {
7190 set lib_name "$library$list_file_ext"
7193 set lib_name "others.src"
7196 Msg Debug "Appending $vhdlfile to $lib_name list..."
7197 dict lappend libraries $lib_name $vhdlfile
7198 if {$sha_mode != 0 && [
file type $vhdlfile] eq "link"} {
7201 dict lappend libraries $lib_name $real_file
7202 Msg Debug "File $vhdlfile is a soft link, also adding the real file: $real_file"
7210 if {$list_file_ext eq ".src" && $extension eq ".cfg"} {
7212 if {$print_log == 1 && [
llength $hls_extras] > 0} {
7213 Msg Status "$indent Inside [
file tail $vhdlfile] (HLS auto-expand):"
7215 set n_extras [
llength $hls_extras]
7217 foreach hls_extra $hls_extras {
7219 if {$hls_extra eq $vhdlfile} { continue}
7220 Msg Debug "HLS cfg expansion: adding $hls_extra (from $vhdlfile) to $lib_name"
7221 if {$print_log == 1} {
7222 if {$i == $n_extras} {
set pad "└──"}
else {
set pad "├──"}
7223 set rel [
Relative [
file dirname $vhdlfile] $hls_extra]
7224 Msg Status "$indent $pad $rel"
7226 dict lappend libraries $lib_name $hls_extra
7227 if {$sha_mode != 0 && [
file type $hls_extra] eq "link"} {
7229 dict lappend libraries $lib_name $real_extra
7230 Msg Debug "HLS expanded file $hls_extra is a soft link, also adding $real_extra"
7237 if {[dict exists $filesets $fileset] == 0} {
7239 Msg Debug "Adding $fileset to the fileset dictionary..."
7240 Msg Debug "Adding library $lib_name to fileset $fileset..."
7241 dict set filesets $fileset $lib_name
7245 Msg Debug "Adding library $lib_name to fileset $fileset..."
7246 dict lappend filesets $fileset $lib_name
7252 Msg CriticalWarning "File $vhdlfile not found."
7258 if {$sha_mode != 0} {
7260 if {$list_file_ext eq ".ipb"} {
7261 set sha_lib "xml.ipb"
7263 set sha_lib $lib$list_file_ext
7265 dict lappend libraries $sha_lib [
file normalize $list_file]
7266 if {[
file type $list_file] eq "link"} {
7269 dict lappend libraries $lib$list_file_ext $real_file
7270 Msg Debug "List file $list_file is a soft link, also adding the real file: $real_file"
7273 return [list $libraries $properties $filesets]
7282 proc Relative {base dst {quiet 0}} {
7283 if {![
string equal [
file pathtype $base] [
file pathtype $dst]]} {
7285 Msg CriticalWarning "Unable to compute relation for paths of different path types: [
file pathtype $base] vs. [
file pathtype $dst], ($base vs. $dst)"
7290 set base [
file normalize [
file join [
pwd] $base]]
7291 set dst [
file normalize [
file join [
pwd] $dst]]
7294 set base [
file split $base]
7295 set dst [
file split $dst]
7297 while {[
string equal [
lindex $dst 0] [
lindex $base 0]]} {
7298 set dst [
lrange $dst 1 end]
7299 set base [
lrange $base 1 end]
7300 if {![
llength $dst]} {break}
7303 set dstlen [
llength $dst]
7304 set baselen [
llength $base]
7306 if {($dstlen == 0) && ($baselen == 0)} {
7309 while {$baselen > 0} {
7310 set dst [
linsert $dst 0 ..]
7313 set dst [
eval [
linsert $dst 0 file join]]
7324 proc RelativeLocal {pathName filePath} {
7325 if {[
string first [
file normalize $pathName] [
file normalize $filePath]] != -1} {
7326 return [
Relative $pathName $filePath]
7337 proc RemoveDuplicates {mydict} {
7338 set new_dict [dict create]
7339 foreach key [dict keys $mydict] {
7340 set values [
DictGet $mydict $key]
7341 foreach value $values {
7342 set idxs [
lreverse [
lreplace [lsearch -exact -all $values $value] 0 0]]
7344 set values [
lreplace $values $idx $idx]
7347 dict set new_dict $key $values
7358 proc ResetRepoFiles {reset_file} {
7359 if {[
file exists $reset_file]} {
7360 Msg Info "Found $reset_file, opening it..."
7361 set fp [open $reset_file r]
7362 set wild_cards [lsearch -all -inline -not -regexp [
split [read $fp] "\n"] "^ *$"]
7364 Msg Info "Found the following files/wild cards to restore if modified: $wild_cards..."
7365 foreach w $wild_cards {
7367 if {[
llength $mod_files] > 0} {
7368 Msg Info "Found modified $w files: $mod_files, will restore them..."
7371 Msg Info "No modified $w files found."
7382 proc RestoreModifiedFiles {{repo_path "."} {pattern "."}} {
7385 set ret [
Git checkout $pattern]
7396 proc SearchHogProjects {dir} {
7397 set projects_list {}
7398 if {[
file exists $dir]} {
7399 if {[
file isdirectory $dir]} {
7400 foreach proj_dir [glob -nocomplain -types d $dir/*] {
7401 if {![regexp {^.*Top/+(.*)$} $proj_dir dummy proj_name]} {
7402 Msg Warning "Could not parse Top directory $dir"
7405 if {[
file exists "$proj_dir/hog.conf"]} {
7406 lappend projects_list $proj_name
7409 lappend projects_list $p
7414 Msg Error "Input $dir is not a directory!"
7417 Msg Error "Directory $dir doesn't exist!"
7419 return $projects_list
7428 proc SetGenericsSimulation {repo_path proj_dir target} {
7429 set top_dir "$repo_path/Top/$proj_dir"
7430 set simsets [get_filesets]
7431 if {$simsets != ""} {
7432 foreach simset $simsets {
7434 if {[get_property FILESET_TYPE $simset] != "SimulationSrcs"} {
7438 set merged_generics_dict [dict create]
7442 set simset_generics [
DictGet $simset_dict "generics"]
7443 set merged_generics_dict [
MergeDict $merged_generics_dict $simset_generics 0]
7446 Msg Debug "TOP = [get_property top [get_filesets sources_1]]"
7447 Msg Debug "GENERICS = [get_property generic [get_filesets sources_1]]"
7449 set_property generic $generic_str [get_filesets $simset]
7450 Msg Info "Setting generics $generic_str for simulator $target\
7451 and simulation file-set $simset..."
7463 proc SetTopProperty {top_module fileset} {
7464 Msg Info "Setting TOP property to $top_module module"
7467 set_property "top" $top_module [get_filesets $fileset]
7470 set_global_assignment -name TOP_LEVEL_ENTITY $top_module
7472 set_root -module $top_module
7474 prj_impl option top $top_module
7479 proc VIVADO_PATH_PROPERTIES {} {
7480 return {"\.*\.TCL\.PRE$" "^.*\.TCL\.POST$" "^RQS_FILES$" "^INCREMENTAL\_CHECKPOINT$" "NOC\_SOLUTION\_FILE"}
7484 proc VITIS_PATH_PROPERTIES {} {
7485 return {"^HW$" "^XPFM$" "^LINKER-SCRIPT$" "^LIBRARIES$" "^LIBRARY-SEARCH-PATH$"}
7495 proc WriteConf {file_name config {comment ""}} {
7496 if {[
catch {
package require inifile 0.2.3} ERROR]} {
7497 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
7501 ::ini::commentchar "#"
7502 set f [::ini::open $file_name w]
7504 foreach sec [dict keys $config] {
7505 set section [dict get $config $sec]
7506 dict for {p v} $section {
7507 if {[string trim $v] == ""} {
7508 Msg Warning "Property $p has empty value. Skipping..."
7511 ::ini::set $f $sec $p $v
7516 if {![
string equal "$comment" ""]} {
7517 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $comment
7518 set hog_header "Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
7519 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $hog_header
7534 proc WriteGenerics {mode repo_path design date timee\
7535 commit version top_hash top_ver hog_hash hog_ver \
7536 cons_ver cons_hash libs vers hashes ext_names ext_hashes \
7537 user_ip_repos user_ip_vers user_ip_hashes flavour {xml_ver ""} {xml_hash ""}} {
7538 Msg Info "Passing parameters/generics to project's top module..."
7541 set generic_string [
concat \
7554 if {$xml_hash != "" && $xml_ver != ""} {
7555 lappend generic_string \
7560 foreach l $libs v $vers h $hashes {
7566 set ver [regsub -all {[\.-]} $ver {_}]
7567 set hash [regsub -all {[\.-]} $hash {_}]
7568 lappend generic_string "$ver" "$hash"
7571 foreach e $ext_names h $ext_hashes {
7573 lappend generic_string "$hash"
7576 foreach repo $user_ip_repos v $user_ip_vers h $user_ip_hashes {
7577 set repo_name [
file tail $repo]
7578 set ver "[
string toupper $repo_name]_VER=[
FormatGeneric $v]"
7579 set hash "[
string toupper $repo_name]_SHA=[
FormatGeneric $h]"
7580 set ver [regsub -all {[\.-]} $ver {_}]
7581 set hash [regsub -all {[\.-]} $hash {_}]
7582 lappend generic_string "$ver" "$hash"
7585 if {$flavour != -1} {
7586 lappend generic_string "FLAVOUR=$flavour"
7592 set generic_string "$prj_generics $generic_string"
7598 if {$mode == "create" || [
IsISE]} {
7601 if {[
file exists $top_file]} {
7604 Msg Debug "Found top level generics $generics in $top_file"
7606 set filtered_generic_string ""
7608 foreach generic_to_set [
split [
string trim $generic_string]] {
7609 set key [
lindex [
split $generic_to_set "="] 0]
7610 if {[dict exists $generics $key]} {
7611 Msg Debug "Hog generic $key found in $top_name"
7612 lappend filtered_generic_string "$generic_to_set"
7614 Msg Warning "Generic $key is passed by Hog but is NOT present in $top_name."
7620 set generic_string $filtered_generic_string
7625 set_property generic $generic_string [current_fileset]
7626 Msg Info "Setting parameters/generics..."
7627 Msg Debug "Detailed parameters/generics: $generic_string"
7632 set simulator [get_property target_simulator [current_project]]
7633 if {$mode == "create"} {
7640 Msg Info "Setting Synplify parameters/generics one by one..."
7641 foreach generic $generic_string {
7642 Msg Debug "Setting Synplify generic: $generic"
7643 set_option -hdl_param -set "$generic"
7646 Msg Info "Setting Diamond parameters/generics one by one..."
7647 prj_impl option -impl Implementation0 HDL_PARAM "$generic_string"
7649 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
7651 foreach app_name [dict keys $ws_apps] {
7652 set defined_symbols [app config -name $app_name -get define-compiler-symbols]
7653 foreach generic_to_set [
split [
string trim $generic_string]] {
7654 set key [
lindex [
split $generic_to_set "="] 0]
7655 set value [
lindex [
split $generic_to_set "="] 1]
7656 if {[
string match "32'h*" $value]} {
7657 set value [
string map {"32'h" "0x"} $value]
7660 foreach symbol [
split $defined_symbols ";"] {
7661 if {[
string match "$key=*" $symbol]} {
7662 Msg Debug "Generic $key found in $app_name, removing it..."
7663 app config -name $app_name -remove define-compiler-symbols "$symbol"
7667 Msg Info "Setting Vitis parameters/generics for app $app_name: $key=$value"
7668 app config -name $app_name define-compiler-symbols "$key=$value"
7680 proc WriteGenericsToBdIPs {mode repo_path proj generic_string} {
7681 Msg Debug "Parameters/generics passed to WriteGenericsToIP: $generic_string"
7683 set bd_ip_generics false
7685 if {[dict exists $properties "hog"]} {
7686 set propDict [dict get $properties "hog"]
7687 if {[dict exists $propDict "PASS_GENERICS_TO_BD_IPS"]} {
7688 set bd_ip_generics [dict get $propDict "PASS_GENERICS_TO_BD_IPS"]
7692 if {[
string compare [
string tolower $bd_ip_generics] "false"] == 0} {
7696 if {$mode == "synth"} {
7697 Msg Info "Attempting to apply generics pre-synthesis..."
7698 set PARENT_PRJ [get_property "PARENT.PROJECT_PATH" [current_project]]
7699 set workaround [open "$repo_path/Projects/$proj/.hog/presynth_workaround.tcl" "w"]
7700 puts $workaround "source \[lindex \$argv 0\];"
7701 puts $workaround "open_project \[lindex \$argv 1\];"
7702 puts $workaround "WriteGenericsToBdIPs \[lindex \$argv 2\] \[lindex \$argv 3\] \[lindex \$argv 4\] \[lindex \$argv 5\];"
7703 puts $workaround "close_project"
7707 exec vivado -mode batch -source $repo_path/Projects/$proj/.hog/presynth_workaround.tcl \
7708 -tclargs $repo_path/Hog/Tcl/hog.tcl $PARENT_PRJ \
7709 "childprocess" $repo_path $proj $generic_string
7712 Msg Error "Encountered an error while attempting workaround: $errMsg"
7714 file delete $repo_path/Projects/$proj/.hog/presynth_workaround.tcl
7716 Msg Info "Done applying generics pre-synthesis."
7720 Msg Info "Looking for IPs to add generics to..."
7721 set ips_generic_string ""
7722 foreach generic_to_set [
split [
string trim $generic_string]] {
7723 set key [
lindex [
split $generic_to_set "="] 0]
7724 set value [
lindex [
split $generic_to_set "="] 1]
7725 append ips_generic_string "CONFIG.$key $value "
7729 if {[
string compare [
string tolower $bd_ip_generics] "true"] == 0} {
7732 set ip_regex $bd_ip_generics
7735 set ip_list [get_ips -regex $ip_regex]
7736 Msg Debug "IPs found with regex \{$ip_regex\}: $ip_list"
7738 set regen_targets {}
7740 foreach {ip} $ip_list {
7741 set WARN_ABOUT_IP false
7742 set ip_props [list_property [get_ips $ip]]
7745 if {[lsearch -exact $ip_props "IS_BD_CONTEXT"] == -1} {
7749 if {[get_property "IS_BD_CONTEXT" [get_ips $ip]] eq "1"} {
7750 foreach {ip_prop} $ip_props {
7751 if {[dict exists $ips_generic_string $ip_prop]} {
7752 if {$WARN_ABOUT_IP == false} {
7753 lappend regen_targets [get_property SCOPE [get_ips $ip]]
7754 Msg Warning "The ip \{$ip\} contains generics that are set by Hog.\
7755 If this is IP is apart of a block design, the .bd file may contain stale, unused, values.\
7756 Hog will always apply the most up-to-date values to the IP during synthesis,\
7757 however these values may or may not be reflected in the .bd file."
7758 set WARN_ABOUT_IP true
7763 set xci_path [get_property IP_FILE [get_ips $ip]]
7765 if {[
string equal $generic_format "ERROR"]} {
7766 Msg Warning "Could not find format for generic $ip_prop in IP $ip. Skipping..."
7770 set value_to_set [dict get $ips_generic_string $ip_prop]
7771 switch -exact $generic_format {
7773 if {[
string match "32'h*" $value_to_set]} {
7774 scan [
string map {"32'h" ""} $value_to_set] "%x" value_to_set
7778 set value_to_set [
expr {$value_to_set ? "true" : "false"}]
7781 if {[
string match "32'h*" $value_to_set]} {
7782 binary scan [
binary format H* [
string map {"32'h" ""} $value_to_set]] d value_to_set
7786 if {[
string match "32'h*" $value_to_set]} {
7787 set value_to_set [
string map {"32'h" "0x"} $value_to_set]
7791 set value_to_set [
format "%s" $value_to_set]
7794 Msg Warning "Unknown generic format $generic_format for IP $ip. Will attempt to pass as string..."
7799 Msg Info "The IP \{$ip\} contains: $ip_prop ($generic_format), setting it to $value_to_set."
7800 if {[
catch {set_property -name $ip_prop -value $value_to_set -objects [get_ips $ip]} prop_error]} {
7801 Msg CriticalWarning "Failed to set property $ip_prop to $value_to_set for IP \{$ip\}: $prop_error"
7808 foreach {regen_target} [lsort -unique $regen_targets] {
7809 Msg Info "Regenerating target: $regen_target"
7810 if {[
catch {generate_target -force all [get_files $regen_target]} prop_error]} {
7811 Msg CriticalWarning "Failed to regen targets: $prop_error"
7819 proc GetGenericFormatFromXciXML {generic_name xml_file} {
7820 if {![
file exists $xml_file]} {
7821 Msg Error "Could not find XML file: $xml_file"
7825 set fp [open $xml_file r]
7826 set xci_data [read $fp]
7829 set paramType "string"
7830 set modelparam_regex [
format {^.*\y%s\y.*$} [
string map {"CONFIG." "MODELPARAM_VALUE."} $generic_name]]
7831 set format_regex {format="([^"]+)"}
7833 set line [
lindex [regexp -inline -line $modelparam_regex $xci_data] 0]
7834 Msg Debug "line: $line"
7836 if {[regexp $format_regex $line match format_value]} {
7837 Msg Debug "Extracted: $format_value format from xml"
7838 set paramType $format_value
7840 Msg Debug "No format found, using string"
7849 proc GetGenericFormatFromXci {generic_name xci_file} {
7850 if {![
file exists $xci_file]} {
7851 Msg Error "Could not find XCI file: $xci_file"
7855 set fp [open $xci_file r]
7856 set xci_data [read $fp]
7859 set paramType "string"
7860 if {[
string first "xilinx.com:schema:json_instance:1.0" $xci_data] == -1} {
7861 Msg Debug "XCI format is not JSON, trying XML..."
7862 set xml_file "[
file rootname $xci_file].xml"
7866 set generic_name [
string map {"CONFIG." ""} $generic_name]
7867 set ip_inst [
ParseJSON $xci_data "ip_inst"]
7868 set parameters [dict get $ip_inst parameters]
7869 set component_parameters [dict get $parameters component_parameters]
7870 if {[dict exists $component_parameters $generic_name]} {
7871 set generic_info [dict get $component_parameters $generic_name]
7872 if {[dict exists [
lindex $generic_info 0] format]} {
7873 set paramType [dict get [
lindex $generic_info 0] format]
7874 Msg Debug "Extracted: $paramType format from xci"
7877 Msg Debug "No format found, using string"
7890 proc WriteGitLabCIYAML {proj_name {ci_conf ""}} {
7891 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
7892 Msg CriticalWarning "Cannot find package YAML.\n Error message: $YAMLPACKAGE. \
7893 If you are running on tclsh, you can fix this by installing package \"tcllib\""
7898 if {$ci_conf != ""} {
7900 foreach sec [dict keys $ci_confs] {
7901 if {[
string first : $sec] == -1} {
7902 lappend job_list $sec
7906 set job_list {"generate_project" "simulate_project"}
7910 set out_yaml [huddle create]
7911 foreach job $job_list {
7913 set huddle_tags [huddle list]
7915 set sec_dict [dict create]
7917 if {$ci_confs != ""} {
7918 foreach var [dict keys [dict get $ci_confs $job]] {
7919 if {$var == "tags"} {
7920 set tag_section "tags"
7921 set tags [dict get [dict get $ci_confs $job] $var]
7922 set tags [
split $tags ","]
7924 set tag_list [huddle list $tag]
7925 set huddle_tags [huddle combine $huddle_tags $tag_list]
7928 dict set sec_dict $var [dict get [dict get $ci_confs $job] $var]
7934 set huddle_variables [huddle create "PROJECT_NAME" $proj_name "extends" ".vars"]
7935 if {[dict exists $ci_confs "$job:variables"]} {
7936 set var_dict [dict get $ci_confs $job:variables]
7937 foreach var [dict keys $var_dict] {
7939 set value [dict get $var_dict "$var"]
7940 set var_inner [huddle create "$var" "$value"]
7941 set huddle_variables [huddle combine $huddle_variables $var_inner]
7946 set middle [huddle create "extends" ".$job" "variables" $huddle_variables]
7947 foreach sec [dict keys $sec_dict] {
7948 set value [dict get $sec_dict $sec]
7949 set var_inner [huddle create "$sec" "$value"]
7950 set middle [huddle combine $middle $var_inner]
7952 if {$tag_section != ""} {
7953 set middle2 [huddle create "$tag_section" $huddle_tags]
7954 set middle [huddle combine $middle $middle2]
7957 set outer [huddle create "$job:$proj_name" $middle]
7958 set out_yaml [huddle combine $out_yaml $outer]
7961 return [
string trimleft [yaml::huddle2yaml $out_yaml] "-"]
7971 proc WriteListFiles {libs props list_path repo_path {ext_path ""}} {
7973 foreach lib [dict keys $libs] {
7974 if {[
llength [
DictGet $libs $lib]] > 0} {
7975 set list_file_name $list_path$lib
7976 set list_file [open $list_file_name w]
7977 Msg Info "Writing $list_file_name..."
7978 puts $list_file "#Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
7979 foreach file [
DictGet $libs $lib] {
7981 set prop [
DictGet $props $file]
7985 puts $list_file "$file_path $prop"
7988 set ext_list_file [open "[
file rootname $list_file].ext" a]
7989 puts $ext_list_file "$file_path $prop"
7990 close $ext_list_file
7993 Msg Warning "The path of file $file is not relative to your repository. Please check!"
8009 proc WriteSimListFile {simset libs props simsets list_path repo_path {force 0}} {
8011 set list_file_name $list_path/${simset}.sim
8012 if {$force == 0 && [
file exists $list_file_name]} {
8013 Msg Info "List file $list_file_name already exists, skipping..."
8017 set list_file [open $list_file_name a+]
8020 puts $list_file "\[files\]"
8021 Msg Info "Writing $list_file_name..."
8022 foreach lib [
DictGet $simsets $simset] {
8023 foreach file [
DictGet $libs $lib] {
8025 set prop [
DictGet $props $file]
8029 set lib_name [
file rootname $lib]
8030 if {$lib_name != $simset && [
file extension $file] == ".vhd" && [
file extension $file] == ""} {
8031 lappend prop "lib=$lib_name"
8033 puts $list_file "$file_path $prop"
8036 Msg Warning "The path of file $file is not relative to your repository. Please check!"
8048 proc WriteToFile {File msg} {
8049 set f [open $File a+]
8060 proc WriteUtilizationSummary {input output project_name run} {
8061 set f [open $input "r"]
8062 set o [open $output "a"]
8063 puts $o "## $project_name $run Utilization report\n\n"
8064 struct::matrix util_m
8065 util_m add columns 14
8068 util_m add row "| **Site Type** | **Used** | **Fixed** | **Prohibited** | **Available** | **Util%** |"
8069 util_m add row "| --- | --- | --- | --- | --- | --- |"
8071 util_m add row "| **Site Type** | **Used** | **Fixed** | **Available** | **Util%** |"
8072 util_m add row "| --- | --- | --- | --- | --- |"
8082 while {[
gets $f line] >= 0} {
8083 if {([
string first "| CLB LUTs" $line] >= 0 || [
string first "| Slice LUTs" $line] >= 0) && $luts == 0} {
8084 util_m add row $line
8087 if {([
string first "| CLB Registers" $line] >= 0 || [
string first "| Slice Registers" $line] >= 0) && $regs == 0} {
8088 util_m add row $line
8091 if {[
string first "| Block RAM Tile" $line] >= 0 && $bram == 0} {
8092 util_m add row $line
8095 if {[
string first "URAM " $line] >= 0 && $uram == 0} {
8096 util_m add row $line
8099 if {[
string first "DSPs" $line] >= 0 && $dsps == 0} {
8100 util_m add row $line
8103 if {[
string first "Bonded IOB" $line] >= 0 && $ios == 0} {
8104 util_m add row $line
8111 puts $o [util_m format 2string]
8117 Msg Error "Found Git version older than 2.7.2. Hog will not work as expected, exiting now."
8127 if {![
catch {
exec curl --silent --show-error --version}]} {
8128 if {![
catch {
exec curl --silent --show-error -I https://gitlab.com}]} {
8129 return [list curl --silent --show-error]
8133 if {![
catch {
exec env -u LD_LIBRARY_PATH curl --silent --show-error --version}]} {
8134 if {![
catch {
exec env -u LD_LIBRARY_PATH curl --silent --show-error -I https://gitlab.com}]} {
8135 return [list env -u LD_LIBRARY_PATH curl --silent --show-error]
8139 error "Cannot find a working curl invocation"