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]} {
90 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
91 dict for {app_name app_config} $ws_apps {
92 set app_lib [string tolower "app_$app_name\.src"]
93 if {![IsInList $app_lib $libs_in_fileset 0 1]} {
94 Msg Warning "App '$app_name' exists in workspace but no corresponding sourcefile '$app_lib' found. \
95 Make sure you have a list file with the correct naming convention: \[app_<app_name>\.src\]"
101 foreach lib $libs_in_fileset {
102 Msg Debug "lib: $lib \n"
103 set lib_files [
DictGet $libraries $lib]
104 Msg Debug "Files in $lib: $lib_files"
105 set rootlib [
file rootname [
file tail $lib]]
106 set ext [
file extension $lib]
107 Msg Debug "lib: $lib ext: $ext fileset: $fileset"
111 if {[
string match "app_*" [
string tolower $lib]]} {
114 Msg Debug "Adding $lib to $fileset"
115 add_files -norecurse -fileset $fileset $lib_files
117 foreach f $lib_files {
118 set file_obj [get_files -of_objects [get_filesets $fileset] [list "*$f"]]
120 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
121 set_property -name "library" -value $rootlib -objects $file_obj
125 set props [
DictGet $properties $f]
126 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
128 if {[lsearch -inline -regexp $props "93"] < 0} {
131 if {[lsearch -inline -regexp $props "2008"] >= 0} {
132 set vhdl_year "VHDL 2008"
133 }
elseif {[lsearch -inline -regexp $props "2019"] >= 0} {
135 set vhdl_year "VHDL 2019"
137 Msg CriticalWarning "VHDL 2019 is not supported\
138 in Vivado version older than 2023.2.\
139 Using VHDL 2008, but this might not work."
140 set vhdl_year "VHDL 2008"
144 set vhdl_year "VHDL 2008"
146 Msg Debug "File type for $f is $vhdl_year"
147 set_property -name "file_type" -value $vhdl_year -objects $file_obj
150 Msg Debug "Filetype is VHDL 93 for $f"
155 if {[lsearch -inline -regexp $props "SystemVerilog"] > 0} {
158 set_property -name "file_type" -value "SystemVerilog" -objects $file_obj
159 Msg Debug "Filetype is SystemVerilog for $f"
161 Msg Warning "Xilinx PlanAhead/ISE does not support SystemVerilog.\
162 Property not set for $f"
167 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
169 Msg Info "Setting $top as top module for file set $fileset..."
170 set globalSettings::synth_top_module $top
175 if {[lsearch -inline -regexp $props "verilog_header"] >= 0} {
176 Msg Debug "Setting verilog header type for $f..."
177 set_property file_type {Verilog Header} [get_files $f]
178 }
elseif {[lsearch -inline -regexp $props "verilog_template"] >= 0} {
180 Msg Debug "Setting verilog template type for $f..."
181 set_property file_type {Verilog Template} [get_files $f]
182 }
elseif {[lsearch -inline -regexp $props "verilog"] >= 0} {
184 Msg Debug "Setting verilog type for $f..."
185 set_property file_type {Verilog} [get_files $f]
189 if {[lsearch -inline -regexp $props "nosynth"] >= 0} {
190 Msg Debug "Setting not used in synthesis for $f..."
191 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
195 if {[lsearch -inline -regexp $props "noimpl"] >= 0} {
196 Msg Debug "Setting not used in implementation for $f..."
197 set_property -name "used_in_implementation" -value "false" -objects $file_obj
201 if {[lsearch -inline -regexp $props "nosim"] >= 0} {
202 Msg Debug "Setting not used in simulation for $f..."
203 set_property -name "used_in_simulation" -value "false" -objects $file_obj
208 set top_sim [
lindex [regexp -inline {\ytopsim\s*=\s*(.+?)\y.*} $props] 1]
209 if {$top_sim != ""} {
210 Msg Warning "Setting the simulation top module with the topsim property is deprecated.\
211 Please set this property in the \[properties\] section of your .sim list file,
212 or in the \[$fileset\] section of your sim.conf,\
213 by adding the following line.\ntop=$top_sim"
217 set sim_runtime [
lindex [regexp -inline {\yruntime\s*=\s*(.+?)\y.*} $props] 1]
218 if {$sim_runtime != ""} {
219 Msg Warning "Setting the simulation runtime using the runtime= property is deprecated.\
220 Please set this property in the \[properties\] section of your .sim list file,\
221 or in the \[$fileset\] section of your sim.conf,\
222 by adding the following line.\n<simulator_name>.simulate.runtime=$sim_runtime"
226 if {[lsearch -inline -regexp $props "wavefile"] >= 0} {
227 Msg Warning "Setting a wave do file using the wavefile property is deprecated.\
228 Set this property in the sim.conf file under the \[$fileset\] section,\
229 or in the \[properties\] section of the .sim list file,\
230 by adding the following line .\n<simulator_name>.simulate.custom_wave_do=[
file tail $f]"
234 if {[lsearch -inline -regexp $props "dofile"] >= 0} {
235 Msg Warning "Setting a wave do file using the dofile property is deprecated.\
236 Set this property in the sim.conf file under the \[$fileset\] section,\
237 or in the \[properties\] section of the .sim list file,\
238 by adding the following line .\n<simulator_name>.simulate.custom_do=[
file tail $f]"
242 if {[lsearch -inline -regexp $props "locked"] >= 0 && $ext == ".ip"} {
243 Msg Info "Locking IP $f..."
244 set_property IS_MANAGED 0 [get_files $f]
248 if {[
file extension $f] == ".bd"} {
249 Msg Info "Generating Target for [
file tail $f],\
250 please remember to commit the (possible) changed file."
251 generate_target all [get_files $f]
256 if {[
file extension $f] == ".tcl" && $ext != ".con"} {
257 if {[lsearch -inline -regexp $props "source"] >= 0} {
258 Msg Info "Sourcing Tcl script $f,\
259 and setting it not used in synthesis, implementation and simulation..."
261 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
262 set_property -name "used_in_implementation" -value "false" -objects $file_obj
263 set_property -name "used_in_simulation" -value "false" -objects $file_obj
268 set ref [
lindex [regexp -inline {\yscoped_to_ref\s*=\s*(.+?)\y.*} $props] 1]
269 set cell [
lindex [regexp -inline {\yscoped_to_cells\s*=\s*(.+?)\y.*} $props] 1]
270 if {([
file extension $f] == ".tcl" || [
file extension $f] == ".xdc") && $ext == ".con"} {
272 set_property SCOPED_TO_REF $ref $file_obj
275 set_property SCOPED_TO_CELLS $cell $file_obj
279 Msg Info "[
llength $lib_files] file/s added to library $rootlib..."
282 if {$ext == ".sim"} {
283 Msg Warning "Simulation files not supported in Quartus Prime mode... Skipping $lib"
285 if {![is_project_open]} {
286 Msg Error "Project is closed"
288 foreach cur_file $lib_files {
292 set props [
DictGet $properties $cur_file]
295 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
297 Msg Info "Setting $top as top module for file set $fileset..."
298 set globalSettings::synth_top_module $top
301 if {[
string first "VHDL" $file_type] != -1} {
302 if {[
string first "1987" $props] != -1} {
303 set hdl_version "VHDL_1987"
304 }
elseif {[
string first "1993" $props] != -1} {
305 set hdl_version "VHDL_1993"
306 }
elseif {[
string first "2008" $props] != -1} {
307 set hdl_version "VHDL_2008"
309 set hdl_version "default"
311 if {$hdl_version == "default"} {
312 set_global_assignment -name $file_type $cur_file -library $rootlib
314 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version -library $rootlib
316 }
elseif {[
string first "SYSTEMVERILOG" $file_type] != -1} {
318 if {[
string first "2005" $props] != -1} {
319 set hdl_version "systemverilog_2005"
320 }
elseif {[
string first "2009" $props] != -1} {
321 set hdl_version "systemverilog_2009"
323 set hdl_version "default"
325 if {$hdl_version == "default"} {
326 set_global_assignment -name $file_type $cur_file
328 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
330 }
elseif {[
string first "VERILOG" $file_type] != -1} {
332 if {[
string first "1995" $props] != -1} {
333 set hdl_version "verilog_1995"
334 }
elseif {[
string first "2001" $props] != -1} {
335 set hdl_version "verilog_2001"
337 set hdl_version "default"
339 if {$hdl_version == "default"} {
340 set_global_assignment -name $file_type $cur_file
342 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
344 }
elseif {[
string first "SOURCE" $file_type] != -1 || [
string first "COMMAND_MACRO" $file_type] != -1} {
345 set_global_assignment -name $file_type $cur_file
346 if {$ext == ".con"} {
348 }
elseif {$ext == ".src"} {
350 if {[
string first "qsys" $props] != -1} {
353 regsub -all {\{||qsys||\}} $props $emptyString props
355 set qsysPath [
file dirname $cur_file]
356 set qsysName "[
file rootname [
file tail $cur_file]].qsys"
357 set qsysFile "$qsysPath/$qsysName"
358 set qsysLogFile "$qsysPath/[
file rootname [
file tail $cur_file]].qsys-script.log"
361 if {![
info exists ::env(QSYS_ROOTDIR)]} {
362 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
363 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
364 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
366 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
369 set qsys_rootdir $::env(QSYS_ROOTDIR)
372 set cmd "$qsys_rootdir/qsys-script"
373 set cmd_options " --script=$cur_file"
374 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
375 Msg Info "Executing: $cmd $cmd_options"
376 Msg Info "Saving logfile in: $qsysLogFile"
377 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
378 set makeRet [
lindex [dict get $opt -errorcode] end]
379 Msg CriticalWarning "$cmd returned with $makeRet"
382 Msg Error " Could not execute command $cmd"
386 if {[
file exists $qsysName] != 0} {
387 file rename -force $qsysName $qsysFile
389 set qsysMd5Sum [
Md5Sum $qsysFile]
391 set fileDir [
file normalize "./hogTmp"]
392 set fileName "$fileDir/.hogQsys.md5"
393 if {![
file exists $fileDir]} {
396 set hogQsysFile [open $fileName "a"]
397 set fileEntry "$qsysFile\t$qsysMd5Sum"
398 puts $hogQsysFile $fileEntry
401 Msg ERROR "Error while moving the generated qsys file to final location: $qsysName not found!"
403 if {[
file exists $qsysFile] != 0} {
404 if {[
string first "noadd" $props] == -1} {
406 set_global_assignment -name $qsysFileType $qsysFile
408 regsub -all {noadd} $props $emptyString props
410 if {[
string first "nogenerate" $props] == -1} {
414 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
418 }
elseif {[
string first "QSYS" $file_type] != -1} {
420 regsub -all {\{||\}} $props $emptyString props
421 if {[
string first "noadd" $props] == -1} {
422 set_global_assignment -name $file_type $cur_file
424 regsub -all {noadd} $props $emptyString props
428 if {[
string first "nogenerate" $props] == -1} {
432 set_global_assignment -name $file_type $cur_file -library $rootlib
437 if {$ext == ".con"} {
438 set vld_exts {.sdc .pin .dcf .gcf .pdc .ndc .fdc .crt .vcd }
439 foreach con_file $lib_files {
441 set con_ext [
file extension $con_file]
442 if {[
IsInList [
file extension $con_file] $vld_exts]} {
443 set option [
string map {. -} $con_ext]
444 set option [
string map {fdc net_fdc} $option]
445 set option [
string map {pdc io_pdc} $option]
446 create_links -convert_EDN_to_HDL 0 -library {work} $option $con_file
448 set props [
DictGet $properties $con_file]
450 if {$con_ext == ".sdc"} {
451 if {[lsearch $props "notiming"] >= 0} {
452 Msg Info "Excluding $con_file from timing verification..."
454 Msg Info "Adding $con_file to time verification"
455 append timing_conf_command " -file $con_file"
459 if {[lsearch $props "nosynth"] >= 0} {
460 Msg Info "Excluding $con_file from synthesis..."
462 Msg Info "Adding $con_file to synthesis"
463 append synth_conf_command " -file $con_file"
468 if {$con_ext == ".pdc" || $con_ext == ".sdc"} {
469 if {[lsearch $props "noplace"] >= 0} {
470 Msg Info "Excluding $con_file from place and route..."
472 Msg Info "Adding $con_file to place and route"
473 append place_conf_command " -file $con_file"
478 Msg CriticalWarning "Constraint file $con_file does not have a valid extension. Allowed extensions are: \n $vld_exts"
481 }
elseif {$ext == ".src"} {
482 foreach f $lib_files {
483 Msg Debug "Adding source $f to library $rootlib..."
484 create_links -library $rootlib -hdl_source $f
486 }
elseif {$ext == ".sim"} {
487 Msg Debug "Adding stimulus file $f to library..."
488 create_links -library $rootlib -stimulus $f
490 build_design_hierarchy
491 foreach cur_file $lib_files {
495 set props [
DictGet $properties $cur_file]
498 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
500 Msg Info "Setting $top as top module for file set $rootlib..."
501 set globalSettings::synth_top_module "${top}::$rootlib"
506 if {$ext == ".src" || $ext == ".con" || $ext == ".ext"} {
507 foreach f $lib_files {
508 Msg Debug "Diamond: adding source file $f to library $rootlib..."
509 prj_src add -work $rootlib $f
510 set props [
DictGet $properties $f]
512 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
514 Msg Info "Setting $top as top module for the project..."
515 set globalSettings::synth_top_module $top
519 if {[lsearch -inline -regexp $props "enable"] >= 0} {
520 Msg Debug "Setting $f as active Logic Preference file"
524 }
elseif {$ext == ".sim"} {
525 foreach f $lib_files {
526 Msg Debug "Diamond Adding simulation file $f to library $rootlib..."
527 prj_src add -work $rootlib -simulate_only $f
535 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
537 foreach app_name [dict keys $ws_apps] {
538 foreach f $lib_files {
539 if {[
string tolower $rootlib] != [
string tolower "app_$app_name"]} {
543 Msg Info "Adding source file $f from lib: $lib to vitis app \[$app_name\]..."
544 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
545 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
546 Msg Debug "Project_f_path is $proj_f_path"
548 importsources -name $app_name -soft-link -path $f -target $proj_f_path
558 if {[
DictGet $filesets "sim_1"] == ""} {
559 delete_fileset -quiet [get_filesets -quiet "sim_1"]
565 if {$synth_conf == 1} {
566 Msg Info $synth_conf_command
567 eval $synth_conf_command
569 if {$timing_conf == 1} {
570 Msg Info $timing_conf_command
571 eval $timing_conf_command
573 if {$place_conf == 1} {
574 Msg Info $place_conf_command
575 eval $place_conf_command
581 proc ALLOWED_PROPS {} {
582 return [dict create ".vhd" [list "93" "nosynth" "noimpl" "nosim" "1987" "1993" "2008" "2019"] \
583 ".vhdl" [list "93" "nosynth" "noimpl" "nosim" "1987" "1993" "2008" "2019"] \
584 ".bd" [list "nosim"] \
585 ".v" [list "SystemVerilog" "verilog_header" "nosynth" "noimpl" "nosim" "1995" "2001"] \
586 ".sv" [list "verilog" "verilog_header" "nosynth" "noimpl" "nosim" "2005" "2009"] \
587 ".do" [list "nosim"] \
588 ".udo" [list "nosim"] \
589 ".xci" [list "nosynth" "noimpl" "nosim" "locked"] \
590 ".xdc" [list "nosynth" "noimpl" "scoped_to_ref" "scoped_to_cells"] \
591 ".tcl" [list "nosynth" "noimpl" "nosim" "scoped_to_ref" "scoped_to_cells" "source" "qsys" "noadd"\
592 "--block-symbol-file" "--clear-output-directory" "--example-design"\
593 "--export-qsys-script" "--family" "--greybox" "--ipxact"\
594 "--jvm-max-heap-size" "--parallel" "--part" "--search-path"\
595 "--simulation" "--synthesis" "--testbench" "--testbench-simulation"\
596 "--upgrade-ip-cores" "--upgrade-variation-file"
598 ".qsys" [list "nogenerate" "noadd" "--block-symbol-file" "--clear-output-directory" "--example-design"\
599 "--export-qsys-script" "--family" "--greybox" "--ipxact" "--jvm-max-heap-size" "--parallel"\
600 "--part" "--search-path" "--simulation" "--synthesis" "--testbench" "--testbench-simulation"\
601 "--upgrade-ip-cores" "--upgrade-variation-file"
603 ".sdc" [list "notiming" "nosynth" "noplace"] \
604 ".elf" [list "scoped_to_ref" "scoped_to_cells" "nosim" "noimpl"] \
605 ".pdc" [list "nosynth" "noplace"] \
606 ".lpf" [list "enable"]]
617 proc BinaryStepName {part} {
619 return "WRITE_DEVICE_IMAGE"
623 return "WRITE_BITSTREAM"
630 set essential_vars [dict create \
631 "HOG_USER" "NOT defined. This variable is essential for git to work properly. \
632 It should be set to the username for your service account (a valid git account)." \
633 "HOG_EMAIL" "NOT defined. This variable is essential for git to work properly. It should be set to your service's account email."\
634 "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."
638 dict for {var msg} $essential_vars {
639 if {![info exists env($var)]} {
640 Msg CriticalWarning "Essential environment variable $var is $msg"
643 Msg Info "Found environment variable $var."
647 set additional_vars [dict create \
648 "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." \
649 "HOG_TARGET_BRANCH" "NOT defined. Default branch for merge is \"master\""\
650 "HOG_CREATE_OFFICIAL_RELEASE" "NOT defined. \
651 Set this variable to '1' to make Hog create an official release in GitHub/Gitlab with the binaries generated in the CI."\
652 "HOG_USE_DOXYGEN" "NOT defined. \
653 Set this variable to 1 to make Hog-CI run Doxygen and copy the official documentation over when you merge to the official branch."
656 if {([
info exists env(HOG_OFFICIAL_BIN_EOS_PATH)] && $env(HOG_OFFICIAL_BIN_EOS_PATH) ne "") || \
657 ([
info exists env(HOG_OFFICIAL_BIN_PATH)] && [
string match "/eos/*" $env(HOG_OFFICIAL_BIN_PATH)])} {
658 Msg Info "Official binary path points to EOS. Checking EOS environment variables for uploads..."
659 if {[
info exists env(HOG_OFFICIAL_BIN_PATH)]} {
660 Msg CriticalWarning "Variable HOG_OFFICIAL_BIN_EOS_PATH is defined. \
661 From Hog2026.2 this variable will be deprecated. Please, use HOG_OFFICIAL_BIN_PATH instead."
663 if {![
info exists env(EOS_PASSWORD)]} {
664 if {![
info exists env(HOG_PASSWORD)]} {
665 Msg Warning "Neither EOS_PASSWORD nor HOG_PASSWORD environment variable is defined. \
666 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."
668 Msg Info "HOG_PASSWORD environment variable is defined and will be used as password for EOS uploads. \
669 If you want to use a different password for EOS uploads, please set the EOS_PASSWORD environment variable."
672 Msg Info "EOS_PASSWORD environment variable is defined and will be used as password for EOS uploads."
675 if {![
info exists env(EOS_USER)]} {
676 Msg Info "EOS_USER environment variable is not defined. Assuming EOS username is the same as HOG_USER."
678 Msg Info "EOS_USER environment variable is defined and will be used as username for EOS uploads."
681 if {![
info exists env(EOS_MGM_URL)]} {
682 Msg Info "EOS_MGM_URL environment variable is not defined. Assuming default value of root://eosuser.cern.ch."
684 Msg Info "EOS_MGM_URL environment variable is defined and will be used as MGM URL for EOS uploads."
686 }
elseif {[
info exists env(HOG_OFFICIAL_BIN_PATH)] } {
687 Msg Info "Variable HOG_OFFICIAL_BIN_PATH is defined. Hog will copy the official binary files to the path defined in this variable. \
688 Please make sure this path is correct and has enough space to store the binaries."
690 Msg Info "No official binary path defined. Hog will not be able to upload binaries."
696 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
704 proc CheckEnv {project_name ide} {
707 set essential_commands [dict create "git" "--version" "$ide" "-version"]
708 set additional_commands [dict create \
715 set additional_vars [dict create \
716 "HOG_PATH" "NOT defined. Hog might work as long as all the necessary executable are in the PATH variable."\
717 "HOG_XIL_LICENSE" "NOT defined. If this variable is not set to the license servers separated by comas, \
718 you need some alternative way of getting your Xilinx license (for example a license file on the machine)."\
719 "LM_LICENSE_FILE" "NOT defined. This variable should be set the Quartus/Libero license servers separated by semicolon. \
720 If not, you need an alternative way of getting your Quartus/Libero license."\
721 "HOG_LD_LIBRARY_PATH" "NOT defined. Hog might work as long as all the necessary library are found."\
722 "HOG_SIMULATION_LIB_PATH" "NOT defined. Hog-CI will not be able to run simulations using third-party simulators."\
723 "HOG_CHECK_PROJVER" "NOT defined. Hog will NOT check the CI project version. \
724 Set this variable to '1' if you want Hog to check the CI project version before creating the HDL project in Create_Project stage. \
725 If the project has not been changed with respect to the target branch, the CI will skip this project" \
726 "HOG_CHECK_SYNTAX" "NOT defined. Hog will NOT check the syntax. \
727 Set this variable to '1' if you want Hog to check the syntax after creating the HDL project in Create_Project stage." \
728 "HOG_NO_BITSTREAM" "NOT defined. Hog-CI will run the implementation up to the write_bitstream stage and create bit files." \
729 "HOG_NO_RESET_BD" "NOT defined or not equal to 1. Hog will reset .bd files (if any) before starting synthesis."\
730 "HOG_IP_PATH" "NOT defined. Hog-CI will NOT use an EOS/LOCAL IP repository to speed up the IP synthesis." \
731 "HOG_RESET_FILES" "NOT defined. Hog-CI will NOT reset any files."\
732 "HOG_NJOBS" "NOT defined. Hog-CI will build IPs with default number of jobs (4)."\
733 "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"
735 Msg Info "Checking environment to run Hog-CI for project $project_name with IDE $ide..."
737 Msg Info "Checking essential commands..."
738 dict for {cmd ver} $essential_commands {
739 if {[catch {exec which $cmd}]} {
740 Msg CriticalWarning "$cmd executable not found. Hog-CI cannot run!"
743 Msg Info "Found executable $cmd."
744 if {$cmd == "ghdl"} {
745 Msg Info [exec $cmd --version]
746 } elseif {$cmd != "diamond"} {
747 Msg Info [exec $cmd $ver]
752 Msg Info "Checking additional commands..."
753 dict for {cmd ver} $additional_commands {
754 if {[catch {exec which $cmd}]} {
755 Msg Warning "$cmd executable not found."
757 Msg Info "Found executable $cmd."
759 Msg Info [exec $cmd $ver]
764 if {$ide == "libero"} {
765 Msg Info "Checking essential environment variables..."
767 if {![
info exists env(HOG_TCLLIB_PATH)]} {
768 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."
771 Msg Info "HOG_TCLLIB_PATH is set. Hog-CI can run with Libero."
775 Msg Info "Checking additional environment variables..."
776 dict for {var msg} $additional_vars {
777 if {![info exists env($var)]} {
778 Msg Info "Environment variable $var is $msg"
780 Msg Info "Found environment variable $var."
785 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
793 proc CheckProjVer {repo_path project {sim 0} {ext_path ""}} {
798 Msg Info "Will check also the version of the simulation files..."
802 if {[
info exists env(HOG_PUSH_TOKEN)] && [
info exist env(CI_PROJECT_ID)] && [
info exist env(CI_API_V4_URL)] } {
803 set token $env(HOG_PUSH_TOKEN)
804 set api_url $env(CI_API_V4_URL)
805 set project_id $env(CI_PROJECT_ID)
810 set project_dir $repo_path/Top/$project
813 Msg Info "$project was modified, continuing with the CI..."
815 Msg Info "Checking if the project has been already built in a previous CI run..."
817 if {$sha == [
GetSHA $repo_path]} {
818 Msg Info "Project was modified in the current commit, Hog will proceed with the build workflow."
821 Msg Info "Checking if project $project has been built in a previous CI run with sha $sha..."
822 set result [
catch {
package require json} JsonFound]
823 if {"$result" != "0"} {
824 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
827 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/$project_id/pipelines"] ret content
828 set pipeline_dict [json::json2dict $content]
829 if {[
llength $pipeline_dict] > 0} {
830 foreach pip $pipeline_dict {
832 set source [
DictGet $pip source]
833 if {$source == "merge_request_event" && [
string first $sha $pip_sha] != -1} {
834 Msg Info "Found pipeline with sha $pip_sha for project $project"
835 set pipeline_id [
DictGet $pip id]
837 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/${project_id}/pipelines/${pipeline_id}/jobs?pagination=keyset&per_page=100"] ret2 content2
838 set jobs_dict [json::json2dict $content2]
839 if {[
llength $jobs_dict] > 0} {
840 foreach job $jobs_dict {
841 set job_name [
DictGet $job name]
843 set artifacts [
DictGet $job artifacts_file]
844 set status [
DictGet $job status]
845 set current_job_name $env(CI_JOB_NAME)
846 if {$current_job_name == $job_name && $status == "success"} {
848 lassign [
ExecuteRet {*}$curl_cmd --location --output artifacts.zip --header "PRIVATE-TOKEN: $token" --url "$api_url/projects/$project_id/jobs/$job_id/artifacts"] ret3 content3
850 Msg CriticalWarning "Cannot download artifacts for job $job_name with id $job_id"
853 lassign [
ExecuteRet unzip -o $repo_path/artifacts.zip] ret_zip
857 Msg Info "Artifacts for job $job_name with id $job_id downloaded and unzipped."
858 file mkdir $repo_path/Projects/$project
859 set fp [open "$repo_path/Projects/$project/skip.me" w+]
871 }
elseif {$ver != -1} {
872 Msg Info "$project was not modified since version: $ver, disabling the CI..."
873 file mkdir $repo_path/Projects/$project
874 set fp [open "$repo_path/Projects/$project/skip.me" w+]
878 Msg Error "Impossible to check the project version. Most likely the repository is not clean. Please, commit your changes before running this command."
888 proc CheckSyntax {project_name repo_path {project_file ""}} {
891 set syntax [check_syntax -return_string]
892 if {[
string first "CRITICAL" $syntax] != -1} {
897 lassign [
GetHogFiles -list_files "*.src" "$repo_path/Top/$project_name/list/" $repo_path] src_files dummy
898 dict for {lib files} $src_files {
900 set file_extension [file extension $f]
901 if {$file_extension == ".vhd" || $file_extension == ".vhdl" || $file_extension == ".v" || $file_extension == ".sv"} {
902 if {[catch {execute_module -tool map -args "--analyze_file=$f"} result]} {
903 Msg Error "\nResult: $result\n"
904 Msg Error "Check syntax failed.\n"
907 Msg Info "Check syntax was successful for $f.\n"
909 Msg Warning "Found syntax error in file $f:\n $result\n"
916 lassign [
GetProjectFiles $project_file] prjLibraries prjProperties prjSimLibraries prjConstraints prjSrcSets prjSimSets prjConSets
917 dict for {lib sources} $prjLibraries {
918 if {[file extension $lib] == ".src"} {
920 Msg Info "Checking Syntax of $f"
926 Msg Info "The Checking Syntax is not supported by this IDE. Skipping..."
931 proc CloseProject {} {
953 proc CompareVersions {ver1 ver2} {
962 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver1 - x y z]} {
963 set ver1 [list $x $y $z]
965 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver2 - x y z]} {
966 set ver2 [list $x $y $z]
970 set v1 [
join $ver1 ""]
972 set v2 [
join $ver2 ""]
975 if {[
string is integer $v1] && [
string is integer $v2]} {
976 set ver1 [
expr {[scan [lindex $ver1 0] %d] * 1000000 + [scan [lindex $ver1 1] %d] * 1000 + [scan [lindex $ver1 2] %d]}]
977 set ver2 [
expr {[scan [lindex $ver2 0] %d] * 1000000 + [scan [lindex $ver2 1] %d] * 1000 + [scan [lindex $ver2 2] %d]}]
981 }
elseif {$ver1 == $ver2} {
987 Msg Warning "Version is not numeric: $ver1, $ver2"
997 proc CheckExtraFiles {libraries constraints simlibraries} {
1000 lassign [
GetProjectFiles] prjLibraries prjProperties prjSimLibraries prjConstraints
1001 set prj_dir [get_property DIRECTORY [current_project]]
1002 file mkdir "$prj_dir/.hog"
1003 set extra_file_name "$prj_dir/.hog/extra.files"
1004 set new_extra_file [open $extra_file_name "w"]
1006 dict for {prjLib prjFiles} $prjLibraries {
1007 foreach prjFile $prjFiles {
1008 if {[file extension $prjFile] == ".xcix"} {
1009 Msg Warning "IP $prjFile is packed in a .xcix core container. \
1010 This files are not suitable for version control systems. We recommend to use .xci files instead."
1013 if {[file extension $prjFile] == ".xci" && [get_property CORE_CONTAINER [get_files $prjFile]] != ""} {
1014 Msg Info "$prjFile is a virtual IP file in a core container. Ignoring it..."
1018 if {[IsInList $prjFile [DictGet $libraries $prjLib]] == 0} {
1019 if {[file extension $prjFile] == ".bd"} {
1020 # Generating BD products to save md5sum of already modified BD
1021 Msg Info "Generating targets of $prjFile..."
1022 generate_target all [get_files $prjFile]
1024 puts $new_extra_file "$prjFile [Md5Sum $prjFile]"
1025 Msg Info "$prjFile (lib: $prjLib) has been generated by an external script. Adding to $extra_file_name..."
1029 close $new_extra_file
1030 set extra_sim_file "$prj_dir/.hog/extrasim.files"
1031 set new_extra_file [open $extra_sim_file "w"]
1033 dict for {prjSimLib prjSimFiles} $prjSimLibraries {
1034 foreach prjSimFile $prjSimFiles {
1035 if {[IsInList $prjSimFile [DictGet $simlibraries $prjSimLib]] == 0} {
1036 puts $new_extra_file "$prjSimFile [Md5Sum $prjSimFile]"
1037 Msg Info "$prjSimFile (lib: $prjSimLib) has been generated by an external script. Adding to $extra_sim_file..."
1041 close $new_extra_file
1042 set extra_con_file "$prj_dir/.hog/extracon.files"
1043 set new_extra_file [open $extra_con_file "w"]
1045 dict for {prjConLib prjConFiles} $prjConstraints {
1046 foreach prjConFile $prjConFiles {
1047 if {[IsInList $prjConFile [DictGet $constraints $prjConLib]] == 0} {
1048 puts $new_extra_file "$prjConFile [Md5Sum $prjConFile]"
1049 Msg Info "$prjConFile has been generated by an external script. Adding to $extra_con_file..."
1053 close $new_extra_file
1060 proc CheckLatestHogRelease {{repo_path .}} {
1063 set current_ver [
Git {describe --always}]
1064 Msg Debug "Current version: $current_ver"
1065 set current_sha [
Git "log $current_ver -1 --format=format:%H"]
1066 Msg Debug "Current SHA: $current_sha"
1069 if {[
OS] == "windows"} {
1070 Msg Info "On windows we cannot set a timeout on 'git fetch', hopefully nothing will go wrong..."
1073 Msg Info "Checking for latest Hog release, can take up to 5 seconds..."
1076 set master_ver [
Git "describe origin/master"]
1077 Msg Debug "Master version: $master_ver"
1078 set master_sha [
Git "log $master_ver -1 --format=format:%H"]
1079 Msg Debug "Master SHA: $master_sha"
1080 set merge_base [
Git "merge-base $current_sha $master_sha"]
1081 Msg Debug "merge base: $merge_base"
1084 if {$merge_base != $master_sha} {
1086 Msg Info "Version $master_ver has been released (https://gitlab.com/hog-cern/Hog/-/releases/$master_ver)"
1087 Msg Status "You should consider updating Hog submodule with the following instructions:"
1089 Msg Status "cd Hog && git checkout master && git pull"
1091 Msg Status "Also update the ref: in your .gitlab-ci.yml to $master_ver"
1095 Msg Info "Latest official version is $master_ver, nothing to do."
1107 proc CheckYmlRef {repo_path allow_failure} {
1108 if {$allow_failure} {
1109 set MSG_TYPE CriticalWarning
1114 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
1115 Msg CriticalWarning "Cannot find package YAML, skipping consistency check of \"ref\" in gilab-ci.yaml file.\n Error message: $YAMLPACKAGE
1116 You can fix this by installing package \"tcllib\""
1124 if {[
file exists .gitlab-ci.yml]} {
1128 if {[
file exists .gitlab-ci.yml]} {
1129 set fp [open ".gitlab-ci.yml" r]
1130 set file_data [read $fp]
1133 Msg $MSG_TYPE "Cannot open file .gitlab-ci.yml"
1137 set file_data "\n$file_data\n\n"
1139 if {[
catch {::yaml::yaml2dict -stream $file_data} yamlDict]} {
1140 Msg $MSG_TYPE "Parsing $repo_path/.gitlab-ci.yml failed. To fix this, check that yaml syntax is respected, remember not to use tabs."
1144 dict for {dictKey dictValue} $yamlDict {
1145 #looking for Hog include in .gitlab-ci.yml
1146 if {"$dictKey" == "include" && (
1147 [lsearch [split $dictValue " {}"] "/hog.yml"] != "-1" ||
1148 [lsearch [split $dictValue " {}"] "/hog-dynamic.yml"] != "-1"
1150 set YML_REF [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "ref"] + 1}]]
1151 set YML_NAME [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "file"] + 1}]]
1155 if {$YML_REF == ""} {
1156 Msg Warning "Hog version not specified in the .gitlab-ci.yml. Assuming that master branch is used."
1158 set YML_REF_F [
Git {name-rev --tags --name-only origin/master}]
1161 set YML_REF_F [regsub -all "'" $YML_REF ""]
1164 if {$YML_NAME == ""} {
1165 Msg $MSG_TYPE "Hog included yml file not specified, assuming hog.yml"
1166 set YML_NAME_F hog.yml
1168 set YML_NAME_F [regsub -all "^/" $YML_NAME ""]
1171 lappend YML_FILES $YML_NAME_F
1177 if {[
catch {::yaml::yaml2dict -file $YML_NAME_F} yamlDict]} {
1178 Msg $MSG_TYPE "Parsing $YML_NAME_F failed."
1182 dict for {dictKey dictValue} $yamlDict {
1183 #looking for included files
1184 if {"$dictKey" == "include"} {
1185 foreach v $dictValue {
1186 lappend YML_FILES [lindex [split $v " "] [expr {[lsearch -dictionary [split $v " "] "local"] + 1}]]
1192 Msg Info "Found the following yml files: $YML_FILES"
1194 set HOGYML_SHA [
GetSHA $YML_FILES]
1195 lassign [
GitRet "log --format=%h -1 --abbrev=7 $YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1197 lassign [
GitRet "log --format=%h -1 --abbrev=7 origin/$YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1199 Msg $MSG_TYPE "Error in project .gitlab-ci.yml. ref: $YML_REF not found"
1200 set EXPECTEDYML_SHA ""
1203 if {!($EXPECTEDYML_SHA eq "")} {
1204 if {$HOGYML_SHA == $EXPECTEDYML_SHA} {
1205 Msg Info "Hog included file $YML_FILES matches with $YML_REF in .gitlab-ci.yml."
1207 Msg $MSG_TYPE "HOG $YML_FILES SHA mismatch.
1208 From Hog submodule: $HOGYML_SHA
1209 From ref in .gitlab-ci.yml: $EXPECTEDYML_SHA
1210 You can fix this in 2 ways: by changing the ref in your repository or by changing the Hog submodule commit"
1213 Msg $MSG_TYPE "One or more of the following files could not be found $YML_FILES in Hog at $YML_REF"
1216 Msg Info ".gitlab-ci.yml not found in $repo_path. Skipping this step"
1237 proc CompareLibDicts {proj_libs list_libs proj_sets list_sets proj_props list_props {severity "CriticalWarning"} {outFile ""} {extraFiles ""}} {
1238 set extra_files $extraFiles
1240 set out_prjlibs $proj_libs
1241 set out_prjprops $proj_props
1243 dict for {prjSet prjLibraries} $proj_sets {
1244 # Check if sets is also in list files
1245 if {[IsInList $prjSet $list_sets]} {
1246 set listLibraries [DictGet $list_sets $prjSet]
1247 # Loop over libraries in fileset
1248 foreach prjLib $prjLibraries {
1249 set prjFiles [DictGet $proj_libs $prjLib]
1250 # Check if library exists in list files
1251 if {[IsInList $prjLib $listLibraries]} {
1252 # Loop over files in library
1253 set listFiles [DictGet $list_libs $prjLib]
1254 foreach prjFile $prjFiles {
1255 set idx [lsearch -exact $listFiles $prjFile]
1256 set listFiles [lreplace $listFiles $idx $idx]
1258 # File is in project but not in list libraries, check if it was generated at creation time...
1259 if {[dict exists $extra_files $prjFile]} {
1260 # File was generated at creation time, checking the md5sum
1261 # Removing the file from the prjFiles list
1262 set idx2 [lsearch -exact $prjFiles $prjFile]
1263 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1264 set new_md5sum [Md5Sum $prjFile]
1265 set old_md5sum [DictGet $extra_files $prjFile]
1266 if {$new_md5sum != $old_md5sum} {
1267 # tclint-disable-next-line line-length
1268 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
1271 set extra_files [dict remove $extra_files $prjFile]
1273 # File is neither in list files nor in extra_files
1274 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1278 # File is both in list files and project, checking properties...
1279 set prjProps [DictGet $proj_props $prjFile]
1280 set listProps [DictGet $list_props $prjFile]
1281 # Check if it is a potential sourced file
1282 if {[IsInList "nosynth" $prjProps] && [IsInList "noimpl" $prjProps] && [IsInList "nosim" $prjProps]} {
1283 # Check if it is sourced
1284 set idx_source [lsearch -exact $listProps "source"]
1285 if {$idx_source >= 0} {
1286 # It is sourced, let's replace the individual properties with source
1287 set idx [lsearch -exact $prjProps "noimpl"]
1288 set prjProps [lreplace $prjProps $idx $idx]
1289 set idx [lsearch -exact $prjProps "nosynth"]
1290 set prjProps [lreplace $prjProps $idx $idx]
1291 set idx [lsearch -exact $prjProps "nosim"]
1292 set prjProps [lreplace $prjProps $idx $idx]
1293 lappend prjProps "source"
1297 foreach prjProp $prjProps {
1298 set idx [lsearch -exact $listProps $prjProp]
1299 set listProps [lreplace $listProps $idx $idx]
1301 MsgAndLog "Property $prjProp of $prjFile was set in project but not in list files" $severity $outFile
1306 foreach listProp $listProps {
1307 if {[string first $listProp "topsim="] == -1 && [string first $listProp "enable"] == -1} {
1308 MsgAndLog "Property $listProp of $prjFile was found in list files but not set in project." $severity $outFile
1313 # Update project prjProps
1314 dict set out_prjprops $prjFile $prjProps
1317 # Loop over remaining files in list libraries
1318 foreach listFile $listFiles {
1319 MsgAndLog "$listFile was found in list files but not in project." $severity $outFile
1323 # Check extra files again...
1324 foreach prjFile $prjFiles {
1325 if {[dict exists $extra_files $prjFile]} {
1326 # File was generated at creation time, checking the md5sum
1327 # Removing the file from the prjFiles list
1328 set idx2 [lsearch -exact $prjFiles $prjFile]
1329 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1330 set new_md5sum [Md5Sum $prjFile]
1331 set old_md5sum [DictGet $extra_files $prjFile]
1332 if {$new_md5sum != $old_md5sum} {
1333 # tclint-disable-next-line line-length
1334 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
1337 set extra_files [dict remove $extra_files $prjFile]
1339 # File is neither in list files nor in extra_files
1340 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1345 # Update prjLibraries
1346 dict set out_prjlibs $prjLib $prjFiles
1349 MsgAndLog "Fileset $prjSet found in project but not in list files" $severity $outFile
1354 return [list $n_diffs $extra_files $out_prjlibs $out_prjprops]
1364 proc CompareVHDL {file1 file2} {
1365 set a [open $file1 r]
1366 set b [open $file2 r]
1368 while {[
gets $a line] != -1} {
1369 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1370 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1376 while {[
gets $b line] != -1} {
1377 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1378 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1387 foreach x $f1 y $f2 {
1389 lappend diff "> $x\n< $y\n\n"
1402 proc Copy {i_dirs o_dir} {
1403 foreach i_dir $i_dirs {
1404 if {[
file isdirectory $i_dir] && [
file isdirectory $o_dir]} {
1405 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]])} {
1406 file delete -force $o_dir/[
file tail $i_dir]
1410 file copy -force $i_dir $o_dir
1425 proc CopyIPbusXMLs {proj_dir path dst {xml_version "0.0.0"} {xml_sha "00000000"} {use_ipbus_sw 0} {generate 0}} {
1426 if {$use_ipbus_sw == 1} {
1427 lassign [
ExecuteRet python3 -c "from __future__ import print_function; from sys import path;print(':'.join(path\[1:\]))"] ret msg
1429 set ::env(PYTHONPATH) $msg
1430 lassign [
ExecuteRet gen_ipbus_addr_decode -h] ret msg
1437 Msg CriticalWarning "Problem while trying to run python: $msg"
1440 set dst [
file normalize $dst]
1442 if {$can_generate == 0} {
1443 if {$generate == 1} {
1444 Msg Error "Cannot generate IPbus address files, IPbus executable gen_ipbus_addr_decode not found or not working: $msg"
1447 Msg Warning "IPbus executable gen_ipbus_addr_decode not found or not working, will not verify IPbus address tables."
1454 set ipb_files [glob -nocomplain $proj_dir/list/*.ipb]
1455 set n_ipb_files [
llength $ipb_files]
1456 if {$n_ipb_files == 0} {
1457 Msg CriticalWarning "No files with .ipb extension found in $proj_dir/list."
1460 set libraries [dict create]
1461 set vhdl_dict [dict create]
1463 foreach ipb_file $ipb_files {
1469 set xmlfiles [dict get $libraries "xml.ipb"]
1471 set xml_list_error 0
1472 foreach xmlfile $xmlfiles {
1473 if {[
file isdirectory $xmlfile]} {
1474 Msg CriticalWarning "Directory $xmlfile listed in xml list file $list_file. Directories are not supported!"
1475 set xml_list_error 1
1478 if {[
file exists $xmlfile]} {
1479 if {[dict exists $vhdl_dict $xmlfile]} {
1480 set vhdl_file [
file normalize $path/[dict get $vhdl_dict $xmlfile]]
1484 lappend vhdls $vhdl_file
1485 set xmlfile [
file normalize $xmlfile]
1486 Msg Info "Copying $xmlfile to $dst and replacing place holders..."
1487 set in [open $xmlfile r]
1488 set out [open $dst/[
file tail $xmlfile] w]
1490 while {[
gets $in line] != -1} {
1491 set new_line [regsub {(.*)__VERSION__(.*)} $line "\\1$xml_version\\2"]
1492 set new_line2 [regsub {(.*)__GIT_SHA__(.*)} $new_line "\\1$xml_sha\\2"]
1493 puts $out $new_line2
1497 lappend xmls [
file tail $xmlfile]
1499 Msg Warning "XML file $xmlfile not found"
1502 if {${xml_list_error}} {
1503 Msg Error "Invalid files added to $list_file!"
1506 set cnt [
llength $xmls]
1507 Msg Info "$cnt xml file/s copied"
1510 if {$can_generate == 1} {
1513 file mkdir "address_decode"
1515 foreach x $xmls v $vhdls {
1517 set x [
file normalize ../$x]
1518 if {[
file exists $x]} {
1519 lassign [
ExecuteRet gen_ipbus_addr_decode --no-timestamp $x 2>&1] status log
1521 set generated_vhdl ./ipbus_decode_[
file rootname [
file tail $x]].vhd
1522 if {$generate == 1} {
1523 Msg Info "Copying generated VHDL file $generated_vhdl into $v (replacing if necessary)"
1524 file copy -force -- $generated_vhdl $v
1526 if {[
file exists $v]} {
1528 set n [
llength $diff]
1530 Msg CriticalWarning "$v does not correspond to its XML $x, [
expr {$n / 3}] line/s differ:"
1531 Msg Status [
join $diff "\n"]
1532 set diff_file [open ../diff_[
file rootname [
file tail $x]].txt w]
1533 puts $diff_file $diff
1536 Msg Info "[
file tail $x] and $v match."
1539 Msg Warning "VHDL address map file $v not found."
1543 Msg Warning "Address map generation failed for [
file tail $x]: $log"
1546 Msg Warning "Copied XML file $x not found."
1549 Msg Info "Skipped verification of [
file tail $x] as no VHDL file was specified."
1553 file delete -force address_decode
1565 proc DescriptionFromConf {conf_file} {
1566 set f [open $conf_file "r"]
1567 set lines [
split [read $f] "\n"]
1569 set second_line [
lindex $lines 1]
1572 if {![regexp {\#+ *(.+)} $second_line - description]} {
1576 if {[regexp -all {test|Test|TEST} $description]} {
1577 set description "test"
1590 proc DictGet {dictName keyName {default ""}} {
1591 if {[dict exists $dictName $keyName]} {
1592 return [dict get $dictName $keyName]
1603 proc DictSort {dict args} {
1605 foreach key [lsort {*}$args [dict keys $dict]] {
1606 dict set res $key [dict get $dict $key]
1616 proc DoxygenVersion {target_version} {
1617 set ver [
split $target_version "."]
1618 set v [
Execute doxygen --version]
1619 Msg Info "Found Doxygen version: $v"
1620 set current_ver [
split $v ". "]
1621 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
1622 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
1624 return [
expr {$target <= $current}]
1635 proc eos {command {attempt 1}} {
1637 if {![
info exists env(EOS_MGM_URL)]} {
1638 Msg Warning "Environment variable EOS_MGM_URL not set, setting it to default value root://eosuser.cern.ch"
1639 set ::env(EOS_MGM_URL) "root://eosuser.cern.ch"
1642 Msg Warning "The value of attempt should be 1 or more, not $attempt, setting it to 1 as default"
1645 for {
set i 0} {$i < $attempt} {
incr i} {
1646 set ret [
catch {
exec -ignorestderr eos {*}$command} result]
1651 set wait [
expr {1 + int(rand() * 29)}]
1652 Msg Warning "Command $command failed ($i/$attempt): $result, trying again in $wait seconds..."
1653 after [
expr {$wait * 1000}]
1657 return [list $ret $result]
1667 proc Execute {args} {
1671 Msg Error "Command [
join $args] returned error code: $ret"
1685 proc ExecuteRet {args} {
1687 if {[
llength $args] == 0} {
1688 Msg CriticalWarning "No argument given"
1692 set ret [
catch {
exec -ignorestderr {*}$args} result]
1695 return [list $ret $result]
1702 proc ExtractFilesSection {file_data} {
1703 set in_files_section 0
1706 foreach line $file_data {
1707 if {[regexp {^ *\[ *files *\]} $line]} {
1708 set in_files_section 1
1711 if {$in_files_section} {
1712 if {[regexp {^ *\[.*\]} $line]} {
1715 lappend result $line
1720 if {!$in_files_section} {
1734 proc ExtractVersionFromTag {tag} {
1735 if {[regexp {^(?:b(\d+))?v(\d+)\.(\d+).(\d+)(?:-\d+)?$} $tag -> mr M m p]} {
1740 Msg Warning "Repository tag $tag is not in a Hog-compatible format."
1746 return [list $M $m $p $mr]
1756 proc FileCommitted {File} {
1758 set currentDir [
pwd]
1759 cd [
file dirname [
file normalize $File]]
1760 set GitLog [
Git ls-files [
file tail $File]]
1761 if {$GitLog == ""} {
1762 Msg CriticalWarning "File [
file normalize $File] is not in the git repository. Please add it with:\n git add [
file normalize $File]\n"
1773 proc FindCommonGitChild {SHA1 SHA2} {
1775 set commits [
Git {log --oneline --merges}]
1778 foreach line [
split $commits "\n"] {
1779 set commit [
lindex [
split $line] 0]
1783 set ancestor $commit
1796 proc findFiles {basedir pattern} {
1799 set basedir [
string trimright [
file join [
file normalize $basedir] { }]]
1805 foreach fileName [glob -nocomplain -type {f r} -path $basedir $pattern] {
1806 lappend fileList $fileName
1810 foreach dirName [glob -nocomplain -type {d r} -path $basedir *] {
1813 set subDirList [
findFiles $dirName $pattern]
1814 if {[
llength $subDirList] > 0} {
1815 foreach subDirFile $subDirList {
1816 lappend fileList $subDirFile
1827 proc FindFileType {file_name} {
1828 set extension [
file extension $file_name]
1831 set file_extension "USE_SIGNALTAP_FILE"
1834 set file_extension "VHDL_FILE"
1837 set file_extension "VHDL_FILE"
1840 set file_extension "VERILOG_FILE"
1843 set file_extension "SYSTEMVERILOG_FILE"
1846 set file_extension "SDC_FILE"
1849 set file_extension "PDC_FILE"
1852 set file_extension "NDC_FILE"
1855 set file_extension "FDC_FILE"
1858 set file_extension "SOURCE_FILE"
1861 set file_extension "IP_FILE"
1864 set file_extension "QSYS_FILE"
1867 set file_extension "QIP_FILE"
1870 set file_extension "SIP_FILE"
1873 set file_extension "BSF_FILE"
1876 set file_extension "BDF_FILE"
1879 set file_extension "COMMAND_MACRO_FILE"
1882 set file_extension "VQM_FILE"
1885 set file_extension "ERROR"
1886 Msg Error "Unknown file extension $extension"
1889 return $file_extension
1896 proc FindNewestVersion {versions} {
1897 set new_ver 00000000
1898 foreach ver $versions {
1900 if {[
expr 0x$ver > 0x$new_ver]} {
1912 proc FindVhdlVersion {file_name} {
1913 set extension [
file extension $file_name]
1916 set vhdl_version "-hdl_version VHDL_2008"
1919 set vhdl_version "-hdl_version VHDL_2008"
1926 return $vhdl_version
1933 proc FormatGeneric {generic} {
1934 if {[
string is integer "0x$generic"]} {
1935 return [
format "32'h%08X" "0x$generic"]
1938 return [
format "32'h%08X" 0]
1948 proc GenerateBitstream {{run_folder ""} {repo_path .} {njobs 1}} {
1949 Msg Info "Starting write bitstream flow..."
1951 set revision [get_current_revision]
1952 if {[
catch {execute_module -tool asm} result]} {
1953 Msg Error "Result: $result\n"
1954 Msg Error "Generate bitstream failed. See the report file.\n"
1956 Msg Info "Generate bitstream was successful for revision $revision.\n"
1959 Msg Info "Run GENERATEPROGRAMMINGDATA ..."
1960 if {[
catch {run_tool -name {GENERATEPROGRAMMINGDATA}}]} {
1961 Msg Error "GENERATEPROGRAMMINGDATA FAILED!"
1963 Msg Info "GENERATEPROGRAMMINGDATA PASSED."
1965 Msg Info "Sourcing Hog/Tcl/integrated/post-bitstream.tcl"
1966 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
1968 prj_run Export -impl Implementation0 -task Bitgen
1978 proc GenerateQsysSystem {qsysFile commandOpts} {
1980 if {[
file exists $qsysFile] != 0} {
1981 set qsysPath [
file dirname $qsysFile]
1982 set qsysName [
file rootname [
file tail $qsysFile]]
1983 set qsysIPDir "$qsysPath/$qsysName"
1984 set qsysLogFile "$qsysPath/$qsysName.qsys-generate.log"
1987 if {![
info exists ::env(QSYS_ROOTDIR)]} {
1988 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
1989 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
1990 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
1992 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
1995 set qsys_rootdir $::env(QSYS_ROOTDIR)
1998 set cmd "$qsys_rootdir/qsys-generate"
1999 set cmd_options "$qsysFile --output-directory=$qsysIPDir $commandOpts"
2000 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
2001 Msg Info "Executing: $cmd $cmd_options"
2002 Msg Info "Saving logfile in: $qsysLogFile"
2003 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
2004 set makeRet [
lindex [dict get $opt -errorcode] end]
2005 Msg CriticalWarning "$cmd returned with $makeRet"
2008 Msg Error " Could not execute command $cmd"
2012 set qsysIPFileList [
concat \
2013 [glob -nocomplain -directory $qsysIPDir -types f *.ip *.qip] \
2014 [glob -nocomplain -directory "$qsysIPDir/synthesis" -types f *.ip *.qip *.vhd *.vhdl]
2016 foreach qsysIPFile $qsysIPFileList {
2017 if {[
file exists $qsysIPFile] != 0} {
2019 set_global_assignment -name $qsysIPFileType $qsysIPFile
2021 set IpMd5Sum [
Md5Sum $qsysIPFile]
2023 set fileDir [
file normalize "./hogTmp"]
2024 set fileName "$fileDir/.hogQsys.md5"
2025 if {![
file exists $fileDir]} {
2028 set hogQsysFile [open $fileName "a"]
2029 set fileEntry "$qsysIPFile\t$IpMd5Sum"
2030 puts $hogQsysFile $fileEntry
2035 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
2045 proc GenericToSimulatorString {prop_dict target} {
2047 dict for {theKey theValue} $prop_dict {
2056 regexp {([0-9]*)('h)([0-9a-fA-F]*)} $theValue valueHexFull valueNumBits valueHexFlag valueHex
2057 regexp {^([0-9]*)$} $theValue valueIntFull ValueInt
2058 regexp {(?!^\d+$)^.+$} $theValue valueStrFull ValueStr
2059 if {[string tolower $target] == "vivado" || [string tolower $target] == "xsim"} {
2060 if {[string tolower $theValue] == "true" || [string tolower $theValue] == "false"} {
2061 set prj_generics "$prj_generics $theKey=$theValue"
2062 } elseif {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2063 set prj_generics "$prj_generics $theKey=$valueHexFull"
2064 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2065 set prj_generics "$prj_generics $theKey=$ValueInt"
2066 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2067 set prj_generics "$prj_generics $theKey=\"$ValueStr\""
2069 set prj_generics "$prj_generics $theKey=\"$theValue\""
2071 } elseif {[lsearch -exact [GetSimulators] [string tolower $target]] >= 0} {
2072 if {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2074 scan $valueNumBits %d numBits
2076 scan $valueHex %x numHex
2077 binary scan [binary format "I" $numHex] "B*" binval
2078 set numBits [expr {$numBits - 1}]
2079 set numBin [string range $binval end-$numBits end]
2080 set prj_generics "$prj_generics $theKey=\"$numBin\""
2081 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2082 set prj_generics "$prj_generics $theKey=$ValueInt"
2083 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2084 set prj_generics "$prj_generics {$theKey=\"$ValueStr\"}"
2086 set prj_generics "$prj_generics {$theKey=\"$theValue\"}"
2089 Msg Warning "Target : $target not implemented"
2092 return $prj_generics
2100 proc GetConfFiles {proj_dir} {
2101 Msg Debug "GetConfFiles called with proj_dir=$proj_dir"
2102 if {![
file isdirectory $proj_dir]} {
2103 Msg Error "$proj_dir is supposed to be the top project directory"
2106 set conf_file [
file normalize $proj_dir/hog.conf]
2107 set sim_file [
file normalize $proj_dir/sim.conf]
2108 set pre_tcl [
file normalize $proj_dir/pre-creation.tcl]
2109 set post_tcl [
file normalize $proj_dir/post-creation.tcl]
2111 return [list $conf_file $sim_file $pre_tcl $post_tcl]
2120 proc GetCustomCommands {parameters {directory .}} {
2121 set commands_dict [dict create]
2122 set commands_files [glob -nocomplain $directory/*.tcl]
2124 if {[
llength $commands_files] == 0} {
2128 foreach file $commands_files {
2133 if {$custom_cmd eq ""} {
2138 set custom_cmd_name [dict get $custom_cmd NAME]
2140 Msg Debug "Loaded custom command '$custom_cmd_name' from $file"
2143 if {[dict exists $commands_dict $custom_cmd_name]} {
2144 Msg Error "Custom command '$custom_cmd_name' in $file already defined as: \[dict get $commands_dict $custom_cmd_name\]. Skipping."
2150 set custom_cmd_name [
string toupper $custom_cmd_name]
2151 dict set commands_dict $custom_cmd_name $custom_cmd
2154 return $commands_dict
2157 proc SanitizeCustomCommand {cmdDict file parameters} {
2160 foreach k [dict keys $cmdDict] {
2161 set K [
string toupper $k]
2162 dict set normalized $K [dict get $cmdDict $k]
2165 set cmdDict $normalized
2166 if {![dict exists $cmdDict NAME]} {
2167 Msg Error "Custom command in $file missing required key NAME. Skipping."
2170 if {![dict exists $cmdDict SCRIPT]} {
2171 Msg Error "Custom command '$[dict get $cmdDict NAME]' in $file missing SCRIPT. Skipping."
2176 set allowed {NAME DESCRIPTION OPTIONS CUSTOM_OPTIONS SCRIPT IDE NO_EXIT}
2177 foreach k [dict keys $cmdDict] {
2178 if {[lsearch -exact $allowed $k] < 0} {
2179 Msg Warning "Custom command '[dict get $cmdDict NAME]' in $file: unknown key '$k'. Allowed: $allowed. Skipping."
2184 set name [
string trim [dict get $cmdDict NAME]]
2186 Msg Error "Custom command in $file has empty NAME. Skipping."
2190 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]+$} $name]} {
2191 Msg Error "Custom command NAME '$name' (file $file) contains invalid characters."
2195 if {![dict exists $cmdDict DESCRIPTION]} {
2196 dict set cmdDict DESCRIPTION "No description provided."
2200 set hog_parameters {}
2201 foreach p $parameters {
2202 lappend hog_parameters [
lindex $p 0]
2207 if {[dict exists $cmdDict OPTIONS]} {
2208 set raw_opts [dict get $cmdDict OPTIONS]
2209 if {![
llength $raw_opts]} {
2213 foreach item $raw_opts {
2215 foreach p $parameters {
2216 set hog_parameter [
lindex $p 0]
2217 if { $item eq $hog_parameter } {
2218 lappend hog_options $p
2224 Msg Warning "Custom command '$name' in $file: option '$item' not found in Hog parameters. Skipping."
2227 dict set cmdDict OPTIONS $hog_options
2229 dict set cmdDict CUSTOM_OPTIONS {}
2236 if {[dict exists $cmdDict CUSTOM_OPTIONS]} {
2237 set raw_opts [dict get $cmdDict CUSTOM_OPTIONS]
2238 if {![
llength $raw_opts]} {
2241 foreach item $raw_opts {
2243 if {[
llength $item] != 2 && [
llength $item] != 3} {
2244 Msg Error "Bad custom option: \[$item\]. Custom command '$name' in $file: \
2245 each CUSTOM_OPTIONS entry must be {option \"help\"} for flags \
2246 and {option \"default_value\" \"help\"} for options with arguments. Skipping command."
2250 if {[
llength $item] == 2} {
2251 lassign $item opt help
2254 lassign $item opt def help
2257 if { [
IsInList $opt $hog_parameters] == 1 } {
2258 Msg Warning "Custom command '$name' in $file: option '$opt' already defined in Hog parameters. Skipping."
2264 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]*(\.arg)?$} $opt]} {
2265 Msg Error "Custom command '$name' in $file: invalid option name '$opt'."
2270 Msg Warning "Custom command '$name' option '$opt' has empty help text."
2274 dict set cmdDict CUSTOM_OPTIONS {}
2278 if {[dict exists $cmdDict NO_EXIT]} {
2279 set no_exit [dict get $cmdDict NO_EXIT]
2280 set no_exit [
string tolower [
string trim $no_exit]]
2282 if {$no_exit eq "1" || $no_exit eq "true"} {
2288 dict set cmdDict NO_EXIT $no_exit
2290 dict set cmdDict NO_EXIT 0
2296 proc LoadCustomCommandFile {file parameters} {
2298 set dir [
file dirname $file]
2300 unset -nocomplain ::hog_command
2301 set rc [
catch {source $file} err]
2304 Msg Error "Error sourcing custom command file $file: $err"
2307 if {![
info exists ::hog_command]} {
2308 Msg Warning "File $file did not define ::hog_command. Skipping."
2311 set cmdDict $::hog_command
2313 if {[
catch {dict size $cmdDict}]} {
2314 Msg Error "In $file ::hog_command is not a valid dict. Skipping."
2324 proc GetDateAndTime {commit} {
2325 set clock_seconds [
clock seconds]
2328 set date [
Git "log -1 --format=%cd --date=format:%d%m%Y $commit"]
2329 set timee [
Git "log -1 --format=%cd --date=format:00%H%M%S $commit"]
2331 Msg Warning "Found Git version older than 2.9.3. Using current date and time instead of commit time."
2332 set date [
clock format $clock_seconds -format {%d%m%Y}]
2333 set timee [
clock format $clock_seconds -format {00%H%M%S}]
2335 return [list $date $timee]
2347 proc GetFile {file fileset} {
2350 set Files [get_files -all $file -of_object [get_filesets $fileset]]
2351 set f [
lindex $Files 0]
2359 puts "***DEBUG Hog:GetFile $file"
2368 proc GetFileGenerics {filename {entity ""}} {
2370 if {[
string equal $file_type "VERILOG_FILE"] || [
string equal $file_type "SYSTEMVERILOG_FILE"]} {
2372 }
elseif {[
string equal $file_type "VHDL_FILE"]} {
2375 Msg CriticalWarning "Could not determine extension of top level file."
2384 proc GetGenericsFromConf {proj_dir} {
2385 set generics_dict [dict create]
2386 set top_dir "Top/$proj_dir"
2387 set conf_file "$top_dir/hog.conf"
2389 Msg Debug "GetGenericsFromConf called with proj_dir=$proj_dir, top_dir=$top_dir"
2391 if {[
file exists $conf_file]} {
2393 if {[dict exists $properties generics]} {
2394 set generics_dict [dict get $properties generics]
2397 Msg Warning "File $conf_file not found."
2399 return $generics_dict
2412 proc GetSimSets {project_name repo_path {simsets ""} {ghdl 0} {no_conf 0}} {
2413 set simsets_dict [dict create]
2414 set list_dir "$repo_path/Top/$project_name/list"
2416 if {$simsets != ""} {
2417 foreach s $simsets {
2418 set list_file "$list_dir/$s.sim"
2419 if {[
file exists $list_file]} {
2420 lappend list_files $list_file
2421 }
elseif {$s != "sim_1"} {
2422 Msg CriticalWarning "Simulation set list file $list_file not found."
2427 set list_files [glob -nocomplain -directory $list_dir "*.sim"]
2431 set proj_dir [
file normalize $repo_path/Top/$project_name]
2432 set sim_file [
file normalize $proj_dir/sim.conf]
2434 foreach list_file $list_files {
2435 set file_name [
file tail $list_file]
2436 set simset_name [
file rootname $file_name]
2437 set fp [open $list_file r]
2438 set file_data [read $fp]
2440 set data [
split $file_data "\n"]
2442 set firstline [
lindex $data 0]
2444 if {[regexp {^ *\# *Simulator} $firstline]} {
2445 set simulator_prop [regexp -all -inline {\S+} $firstline]
2446 set simulator [
string tolower [
lindex $simulator_prop end]]
2448 Msg Warning "Simulator not set in $simset_name.sim. \
2449 The first line of $simset_name.sim should be #Simulator <SIMULATOR_NAME>,\
2450 where <SIMULATOR_NAME> can be xsim, questa, modelsim, ghdl, riviera, activehdl,\
2451 ies, or vcs, e.g. #Simulator questa.\
2452 Setting simulator by default to xsim."
2453 set simulator "xsim"
2455 if {$simulator eq "skip_simulation"} {
2456 Msg Info "Skipping simulation for $simset_name"
2459 if {($ghdl == 1 && $simulator != "ghdl") || ($ghdl == 0 && $simulator == "ghdl")} {
2463 set SIM_PROPERTIES ""
2464 if {[
file exists $sim_file] && $no_conf == 0} {
2465 set SIM_PROPERTIES [
ReadConf $sim_file]
2468 set global_sim_props [dict create]
2469 dict set global_sim_props "properties" [
DictGet $SIM_PROPERTIES "sim"]
2470 dict set global_sim_props "generics" [
DictGet $SIM_PROPERTIES "generics"]
2471 dict set global_sim_props "hog" [
DictGet $SIM_PROPERTIES "hog"]
2474 set sim_dict [dict create]
2475 dict set sim_dict "simulator" $simulator
2476 if {[dict exists $SIM_PROPERTIES $simset_name]} {
2477 dict set sim_dict "properties" [
DictGet $SIM_PROPERTIES $simset_name]
2478 dict set sim_dict "generics" [
DictGet $SIM_PROPERTIES "$simset_name:generics"]
2479 dict set sim_dict "hog" [
DictGet $SIM_PROPERTIES "$simset_name:hog"]
2480 }
elseif {$no_conf == 0} {
2482 set conf_dict [
ReadConf $list_file]
2483 set sim_dict [
MergeDict $sim_dict $conf_dict 0]
2485 set sim_dict [
MergeDict $sim_dict $global_sim_props 0]
2486 dict set simsets_dict $simset_name $sim_dict
2488 return $simsets_dict
2496 proc GetSimsetGenericsFromConf {proj_dir} {
2497 set simsets_generics_dict [dict create]
2498 set top_dir "Top/$proj_dir"
2499 set conf_file "$top_dir/sim.conf"
2502 if {[
file exists $conf_file]} {
2505 set simsets_generics_dict [dict filter $properties key *:generics]
2507 Msg Warning "File $conf_file not found."
2509 return $simsets_generics_dict
2520 proc GetGroupName {proj_dir repo_dir} {
2521 if {[regexp {^(.*)/(Top|Projects)/+(.*?)/*$} $proj_dir dummy possible_repo_dir proj_or_top dir]} {
2523 if {[
file normalize $repo_dir] eq [
file normalize $possible_repo_dir]} {
2524 set group [
file dir $dir]
2525 if {$group == "."} {
2530 Msg Warning "Project directory $proj_dir seems to be in $possible_repo_dir which is not a the main Git repository $repo_dir."
2533 Msg Warning "Could not parse project directory $proj_dir"
2546 proc GetHogDescribe {sha {repo_path .}} {
2549 set new_sha "[
string toupper [
GetSHA]]"
2552 set new_sha [
string toupper $sha]
2572 proc GetHogFiles {args} {
2575 if {[
catch {
package require cmdline} ERROR]} {
2576 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
2583 {list_files.arg "" "The file wildcard, if not specified all Hog list files will be looked for."}
2584 {sha_mode "Forwarded to ReadListFile, see there for info."}
2585 {ext_path.arg "" "Path for the external libraries forwarded to ReadListFile."}
2586 {print_log "Forwarded to ReadListFile, see there for info."}
2588 set usage "USAGE: GetHogFiles \[options\] <list path> <repository path>"
2589 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
2593 set list_path [
lindex $args 0]
2594 set repo_path [
lindex $args 1]
2596 set list_files $options(list_files)
2597 set sha_mode $options(sha_mode)
2598 set ext_path $options(ext_path)
2599 set print_log $options(print_log)
2601 if {$sha_mode == 1} {
2602 set sha_mode_opt "-sha_mode"
2607 if {$print_log == 1} {
2608 set print_log_opt "-print_log"
2610 set print_log_opt ""
2614 if {$list_files == ""} {
2615 set list_files {.src,.con,.sim,.ext}
2617 set libraries [dict create]
2618 set properties [dict create]
2619 set list_files [glob -nocomplain -directory $list_path "*{$list_files}"]
2620 set filesets [dict create]
2622 foreach f $list_files {
2623 set ext [
file extension $f]
2624 if {$ext == ".ext"} {
2625 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $ext_path"] l p fs
2627 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $repo_path"] l p fs
2630 set properties [
MergeDict $p $properties]
2631 Msg Debug "list file $f, filesets: $fs"
2633 Msg Debug "Merged filesets $filesets"
2635 return [list $libraries $properties $filesets]
2642 proc GetIDECommand {proj_conf {custom_ver ""}} {
2643 if {$custom_ver ne ""} {
2644 set ide_name_and_ver [
string tolower "$custom_ver"]
2645 }
elseif {[
file exists $proj_conf]} {
2646 set ide_name_and_ver [
string tolower [
GetIDEFromConf $proj_conf]]
2648 Msg Error "Configuration file $proj_conf not found."
2651 set ide_name [
lindex [regexp -all -inline {\S+} $ide_name_and_ver] 0]
2653 if {$ide_name eq "vivado" || $ide_name eq "vivado_vitis_classic"} {
2654 set command "vivado"
2656 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2657 set after_tcl_script " -tclargs "
2659 }
elseif {$ide_name eq "planahead"} {
2660 set command "planAhead"
2662 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2663 set after_tcl_script " -tclargs "
2665 }
elseif {$ide_name eq "quartus"} {
2666 set command "quartus_sh"
2668 set before_tcl_script " -t "
2669 set after_tcl_script " "
2671 }
elseif {$ide_name eq "libero"} {
2674 set command "libero"
2675 set before_tcl_script "SCRIPT:"
2676 set after_tcl_script " SCRIPT_ARGS:\""
2678 }
elseif {$ide_name eq "diamond"} {
2679 set command "diamondc"
2680 set before_tcl_script " "
2681 set after_tcl_script " "
2683 }
elseif {$ide_name eq "vitis_classic"} {
2686 set before_tcl_script ""
2687 set after_tcl_script " "
2689 }
elseif {$ide_name eq "ghdl"} {
2691 set before_tcl_script " "
2692 set after_tcl_script " "
2695 Msg Error "IDE: $ide_name not known."
2698 return [list $command $before_tcl_script $after_tcl_script $end_marker]
2704 proc GetIDEFromConf {conf_file} {
2705 set f [open $conf_file "r"]
2708 if {[regexp -all {^\# *(\w*) *(vitis_classic)? *(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)?(_.*)? *$} $line dummy ide vitisflag version patch]} {
2709 if {[
info exists vitisflag] && $vitisflag != ""} {
2710 set ide "${ide}_${vitisflag}"
2713 if {[
info exists version] && $version != ""} {
2719 set ret [list $ide $ver]
2721 Msg CriticalWarning "The first line of hog.conf should be \#<IDE name> <version>, \
2722 where <IDE name>. is quartus, vivado, planahead, libero, diamond or ghdl, \
2723 and <version> the tool version, e.g. \#vivado 2020.2. Will assume vivado."
2724 set ret [list "vivado" "0.0.0"]
2731 proc GetIDEName {} {
2733 return "ISE/PlanAhead"
2751 proc GetIDEVersion {} {
2754 regexp {\d+\.\d+(\.\d+)?} [version -short] ver
2759 regexp {[\.0-9]+} $quartus(version) ver
2762 set ver [get_libero_version]
2764 regexp {\d+\.\d+(\.\d+)?} [sys_install version] ver
2766 regexp {\d+\.\d+(\.\d+)?} [version] ver
2780 proc GetLinkedFile {link_file} {
2781 if {[
file type $link_file] eq "link"} {
2782 if {[
OS] == "windows"} {
2784 lassign [
ExecuteRet realpath $link_file] ret msg
2785 lassign [
ExecuteRet cygpath -m $msg] ret2 msg2
2786 if {$ret == 0 && $ret2 == 0} {
2788 Msg Debug "Found link file $link_file on Windows, the linked file is: $real_file"
2790 Msg CriticalWarning "[
file normalize $link_file] is a soft link. \
2791 Soft link are not supported on Windows and readlink.exe or cygpath.exe did not work: readlink=$ret: $msg, cygpath=$ret2: $msg2."
2792 set real_file $link_file
2796 set linked_file [
file link $link_file]
2797 set real_file [
file normalize [
file dirname $link_file]/$linked_file]
2800 if {![
file exists $real_file]} {
2801 Msg Warning "$link_file is a broken link, because the linked file: $real_file does not exist."
2804 Msg Warning "$link file is not a soft link"
2805 set real_file $link_file
2818 proc GetMaxThreads {proj_dir} {
2820 if {[
file exists $proj_dir/hog.conf]} {
2822 if {[dict exists $properties parameters]} {
2823 set propDict [dict get $properties parameters]
2824 if {[dict exists $propDict MAX_THREADS]} {
2825 set maxThreads [dict get $propDict MAX_THREADS]
2829 Msg Warning "File $proj_dir/hog.conf not found. Max threads will be set to default value 1"
2842 proc GetModifiedFiles {{repo_path "."} {pattern "."}} {
2845 set ret [
Git "ls-files --modified $pattern"]
2854 proc GetOptions {argv parameters} {
2857 set param_list [list]
2858 set option_list [list]
2860 foreach p $parameters {
2861 lappend param_list [
lindex $p 0]
2865 while {$index < [
llength $argv]} {
2866 set arg [
lindex $argv $index]
2867 if {[
string first - $arg] == 0} {
2868 set option [
string trimleft $arg "-"]
2870 lappend option_list $arg
2871 if {[lsearch -regex $param_list "$option\[.arg]?"] >= 0 } {
2872 if {[lsearch -regex $param_list "$option\[.arg]"] >= 0 } {
2873 lappend option_list [
lindex $argv $index]
2878 lappend arg_list $arg
2882 Msg Debug "Argv: $argv"
2883 Msg Debug "Options: $option_list"
2884 Msg Debug "Arguments: $arg_list"
2885 return [list $option_list $arg_list]
2903 proc GetProjectFiles {{project_file ""}} {
2904 set libraries [dict create]
2905 set simlibraries [dict create]
2906 set constraints [dict create]
2907 set properties [dict create]
2908 set consets [dict create]
2909 set srcsets [dict create]
2910 set simsets [dict create]
2913 set all_filesets [get_filesets]
2914 set simulator [get_property target_simulator [current_project]]
2915 set top [get_property "top" [current_fileset]]
2917 dict lappend properties $topfile "top=$top"
2919 foreach fs $all_filesets {
2920 if {$fs == "utils_1"} {
2925 set all_files [get_files -quiet -of_objects [get_filesets $fs]]
2926 set fs_type [get_property FILESET_TYPE [get_filesets $fs]]
2928 if {$fs_type == "BlockSrcs"} {
2930 set dict_fs "sources_1"
2934 foreach f $all_files {
2941 if {[
lindex [get_property IS_GENERATED [
GetFile $f $fs]] 0] != 0} {
2946 if {[get_property FILE_TYPE [
GetFile $f $fs]] == "Configuration Files"} {
2952 if {[get_property CORE_CONTAINER [
GetFile $f $fs]] != ""} {
2953 if {[
file extension $f] == ".xcix"} {
2954 set f [get_property CORE_CONTAINER [
GetFile $f $fs]]
2962 if {[get_property SCOPED_TO_REF [
GetFile $f $fs]] != ""} {
2963 dict lappend properties $f "scoped_to_ref=[get_property SCOPED_TO_REF [
GetFile $f $fs]]"
2968 if {[get_property SCOPED_TO_CELLS [
GetFile $f $fs]] != ""} {
2969 dict lappend properties $f "scoped_to_cells=[get_property SCOPED_TO_CELLS [
GetFile $f $fs]]"
2973 if {[
IsInList "PARENT_COMPOSITE_FILE" [list_property [
GetFile $f $fs]]]} {
2978 if {[
file tail $f] == "nocattrs.dat"} {
2983 if {[
file extension $f] != ".coe"} {
2984 set f [
file normalize $f]
2987 set type [get_property FILE_TYPE [
GetFile $f $fs]]
2989 set lib [get_property -quiet LIBRARY [
GetFile $f $fs]]
2992 Msg Debug "File $f Extension [
file extension $f] Type [
lindex $type 0]"
2994 if {[
string equal [
lindex $type 0] "VHDL"] && [
llength $type] == 1} {
2996 }
elseif {[
string equal [
lindex $type 0] "Block"] && [
string equal [
lindex $type 1] "Designs"]} {
2999 }
elseif {[
string equal $type "SystemVerilog"] && [
file extension $f] != ".sv"} {
3000 set prop "SystemVerilog"
3001 }
elseif {[
string equal [
lindex $type 0] "XDC"] && [
file extension $f] != ".xdc"} {
3003 }
elseif {[
string equal $type "Verilog Header"] && [
file extension $f] != ".vh" && [
file extension $f] != ".svh"} {
3004 set prop "verilog_header"
3005 }
elseif {[
string equal $type "Verilog Template"] && [
file extension $f] == ".v" && [
file extension $f] != ".sv"} {
3006 set prop "verilog_template"
3008 set type [
lindex $type 0]
3012 if {![
string equal $prop ""]} {
3013 dict lappend properties $f $prop
3016 if {[
string equal $fs_type "SimulationSrcs"]} {
3018 if {[
string equal $type "VHDL"]} {
3019 set library "${lib}.sim"
3021 set library "others.sim"
3025 dict lappend simsets $dict_fs $library
3028 dict lappend simlibraries $library $f
3029 }
elseif {[
string equal $type "VHDL"]} {
3032 dict lappend srcsets $dict_fs "${lib}.src"
3034 dict lappend libraries "${lib}.src" $f
3035 }
elseif {[
string first "IP" $type] != -1} {
3038 dict lappend srcsets $dict_fs "ips.src"
3040 dict lappend libraries "ips.src" $f
3041 Msg Debug "Appending $f to ips.src"
3042 }
elseif {[
string equal $fs_type "Constrs"]} {
3045 dict lappend consets $dict_fs "sources.con"
3047 dict lappend constraints "sources.con" $f
3051 dict lappend srcsets $dict_fs "others.src"
3053 dict lappend libraries "others.src" $f
3054 Msg Debug "Appending $f to others.src"
3057 if {[
lindex [get_property -quiet used_in_synthesis [
GetFile $f $fs]] 0] == 0} {
3058 dict lappend properties $f "nosynth"
3060 if {[
lindex [get_property -quiet used_in_implementation [
GetFile $f $fs]] 0] == 0} {
3061 dict lappend properties $f "noimpl"
3063 if {[
lindex [get_property -quiet used_in_simulation [
GetFile $f $fs]] 0] == 0} {
3064 dict lappend properties $f "nosim"
3066 if {[
lindex [get_property -quiet IS_MANAGED [
GetFile $f $fs]] 0] == 0 && [
file extension $f] != ".xcix"} {
3067 dict lappend properties $f "locked"
3073 dict lappend properties "Simulator" [get_property target_simulator [current_project]]
3076 set file [open $project_file r]
3077 set in_file_manager 0
3079 while {[
gets $file line] >= 0} {
3081 if {[regexp {^KEY ActiveRoot \"([^\"]+)\"} $line -> value]} {
3082 set top [
string range $value 0 [
expr {[string first "::" $value] - 1}]]
3086 if {[regexp {^LIST FileManager} $line]} {
3087 set in_file_manager 1
3092 if {$in_file_manager && [regexp {^ENDLIST} $line]} {
3097 if {$in_file_manager && [regexp {^VALUE \"([^\"]+)} $line -> value]} {
3100 lassign [
split $value ,] file_path file_type
3103 set library "others"
3104 while {[
gets $file line] >= 0} {
3105 if {$line == "ENDFILE"} {
3108 regexp {^LIBRARY=\"([^\"]+)} $line -> library
3109 regexp {^PARENT=\"([^\"]+)} $line -> parent_file
3111 Msg Debug "Found file ${file_path} in project.."
3112 if {$parent_file == ""} {
3113 if {$file_type == "hdl"} {
3115 if {[
IsInList "${library}.src" [
DictGet $srcsets "sources_1"]] == 0} {
3116 dict lappend srcsets "sources_1" "${library}.src"
3118 dict lappend libraries "${library}.src" $file_path
3122 if {[
GetModuleName $file_path] == [
string tolower $top] && $top != ""} {
3123 Msg Debug "Found top module $top in $file_path"
3124 dict lappend properties $file_path "top=$top"
3126 }
elseif {$file_type == "tb_hdl"} {
3128 dict lappend simsets "sim_1" "${library}.sim"
3130 dict lappend simlibraries "${library}.sim" $file_path
3131 }
elseif {$file_type == "io_pdc" || $file_type == "sdc"} {
3133 dict lappend consets "constrs_1" "sources.con"
3135 dict lappend constraints "sources.con" $file_path
3142 set fileData [read [open $project_file]]
3144 set project_path [
file dirname $project_file]
3147 regsub {<\?xml.*\?>} $fileData "" fileData
3150 regexp {<Implementation.*?>(.*)</Implementation>} $fileData -> implementationContent
3154 set sourceRegex {<Source name="([^"]*?)" type="([^"]*?)" type_short="([^"]*?)".*?>(.*?)</Source>}
3156 set optionsRegex {<Options(.*?)\/>}
3157 regexp $optionsRegex $implementationContent -> prj_options
3158 foreach option $prj_options {
3159 if {[regexp {^top=\"([^\"]+)\"} $option match result]} {
3164 while {[regexp $sourceRegex $implementationContent match name type type_short optionsContent]} {
3165 Msg Debug "Found file ${name} in project..."
3166 set file_path [
file normalize $project_path/$name]
3168 set optionsRegex {<Options(.*?)\/>}
3169 regexp $optionsRegex $optionsContent -> options
3170 set library "others"
3172 foreach option $options {
3173 if {[
string first "System Verilog" $option]} {
3176 if {[regexp {^lib=\"([^\"]+)\"} $option match1 result]} {
3181 if {[regexp {syn_sim="([^"]*?)"} $match match_sim simonly]} {
3186 if {$type_short == "VHDL" || $type_short == "Verilog" || $type_short == "IPX"} {
3187 if {$ext == ".src"} {
3188 if {[
IsInList "${library}${ext}" [
DictGet $srcsets "sources_1"]] == 0} {
3189 dict lappend srcsets "sources_1" "${library}${ext}"
3191 dict lappend libraries "${library}${ext}" $file_path
3192 }
elseif {$ext == ".sim"} {
3194 dict lappend simsets "sim_1" "${library}.sim"
3196 dict lappend simlibraries "${library}.sim" $file_path
3202 Msg Debug "Found top module $top in $file_path"
3203 dict lappend properties $file_path "top=$top"
3205 }
elseif {$type_short == "SDC"} {
3207 dict lappend consets "constrs_1" "sources.con"
3209 dict lappend constraints "sources.con" $file_path
3213 regsub -- $match $implementationContent "" implementationContent
3216 return [list $libraries $properties $simlibraries $constraints $srcsets $simsets $consets]
3223 proc GetProjectFlavour {proj_name} {
3225 set flavour [
string map {. ""} [
file extension $proj_name]]
3226 if {$flavour != ""} {
3227 if {[
string is integer $flavour]} {
3228 Msg Info "Project $proj_name has flavour = $flavour, the generic variable FLAVOUR will be set to $flavour"
3230 Msg Warning "Project name has a unexpected non numeric extension, flavour will be set to -1"
3247 proc GetProjectVersion {proj_dir repo_path {ext_path ""} {sim 0}} {
3248 if {![
file exists $proj_dir]} {
3249 Msg CriticalWarning "$proj_dir not found"
3259 Msg Warning "Repository is not clean"
3267 Msg Debug "Project version $v_proj, latest tag $v_last"
3269 Msg Info "The specified project was modified since official version."
3276 Msg Info "The specified project was modified in the latest official version $ret"
3277 }
elseif {$comp == -1} {
3278 Msg Info "The specified project was modified in a past official version $ret"
3294 proc GetRepoVersions {proj_dir repo_path {ext_path ""} {sim 0}} {
3295 if {[
catch {
package require cmdline} ERROR]} {
3296 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
3311 lappend SHAs [
GetSHA {Hog}]
3315 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
3316 Msg Info "Hog submodule [
pwd] clean."
3317 lassign [
GetVer ./] hog_ver hog_hash
3319 Msg CriticalWarning "Hog submodule [
pwd] not clean, commit hash will be set to 0."
3320 set hog_hash "0000000"
3321 set hog_ver "00000000"
3326 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
3327 Msg Info "Git working directory [
pwd] clean."
3330 Msg CriticalWarning "Git working directory [
pwd] not clean, commit hash, and version will be set to 0."
3335 lassign [
GetVer [
join $conf_files]] top_ver top_hash
3336 lappend SHAs $top_hash
3337 lappend versions $top_ver
3344 lassign [
GetHogFiles -list_files "*.src" -sha_mode "./list/" $repo_path] src_files dummy
3345 dict for {f files} $src_files {
3346 # library names have a .src extension in values returned by GetHogFiles
3347 set name [file rootname [file tail $f]]
3348 if {[file ext $f] == ".oth"} {
3351 lassign [GetVer $files] ver hash
3352 # Msg Info "Found source list file $f, version: $ver commit SHA: $hash"
3354 lappend versions $ver
3356 lappend hashes $hash
3363 lassign [
GetHogFiles -list_files "*.con" -sha_mode "./list/" $repo_path] cons_files dummy
3364 dict for {f files} $cons_files {
3365 #library names have a .con extension in values returned by GetHogFiles
3366 set name [file rootname [file tail $f]]
3367 lassign [GetVer $files] ver hash
3368 #Msg Info "Found constraint list file $f, version: $ver commit SHA: $hash"
3370 Msg CriticalWarning "Constraints file $f not found in Git."
3372 lappend cons_hashes $hash
3374 lappend versions $ver
3381 lassign [
GetHogFiles -list_files "*.sim" -sha_mode "./list/" $repo_path] sim_files dummy
3382 dict for {f files} $sim_files {
3383 #library names have a .sim extension in values returned by GetHogFiles
3384 set name [file rootname [file tail $f]]
3385 lassign [GetVer $files] ver hash
3386 #Msg Info "Found simulation list file $f, version: $ver commit SHA: $hash"
3387 lappend sim_hashes $hash
3389 lappend versions $ver
3397 Msg CriticalWarning "No hashes found for constraints files (not in git)"
3400 set cons_hash [
string tolower [
Git "log --format=%h -1 $cons_hashes"]]
3407 set ext_files [glob -nocomplain "./list/*.ext"]
3410 foreach f $ext_files {
3411 set name [
file rootname [
file tail $f]]
3414 lappend ext_names $name
3415 lappend ext_hashes $hash
3418 lappend versions $ext_ver
3421 set file_data [read $fp]
3423 set data [
split $file_data "\n"]
3425 foreach line $data {
3426 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
3428 set file_and_prop [regexp -all -inline {\S+} $line]
3429 set hdlfile [
lindex $file_and_prop 0]
3430 set hdlfile $ext_path/$hdlfile
3431 if {[
file exists $hdlfile]} {
3432 set hash [
lindex $file_and_prop 1]
3433 set current_hash [
Md5Sum $hdlfile]
3434 if {[
string first $hash $current_hash] == -1} {
3435 Msg CriticalWarning "File $hdlfile has a wrong hash. Current checksum: $current_hash, expected: $hash"
3443 if {[
llength [glob -nocomplain ./list/*.ipb]] > 0} {
3445 lassign [
GetHogFiles -list_files "*.ipb" -sha_mode "./list/" $repo_path] xml_files dummy
3446 lassign [
GetVer [dict get $xml_files "xml.ipb"]] xml_ver xml_hash
3447 lappend SHAs $xml_hash
3448 lappend versions $xml_ver
3452 Msg Info "This project does not use IPbus XMLs"
3457 set user_ip_repos ""
3458 set user_ip_repo_hashes ""
3459 set user_ip_repo_vers ""
3461 if {[
file exists [
lindex $conf_files 0]]} {
3462 set PROPERTIES [
ReadConf [
lindex $conf_files 0]]
3463 if {[dict exists $PROPERTIES main]} {
3464 set main [dict get $PROPERTIES main]
3465 dict for {p v} $main {
3466 if {[string tolower $p] == "ip_repo_paths"} {
3468 if {[file isdirectory "$repo_path/$repo"]} {
3469 set repo_file_list [glob -nocomplain "$repo_path/$repo/*"]
3470 if {[llength $repo_file_list] == 0} {
3471 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3473 lappend user_ip_repos "$repo_path/$repo"
3482 foreach repo $user_ip_repos {
3483 if {[
file isdirectory $repo]} {
3484 set repo_file_list [glob -nocomplain "$repo/*"]
3485 if {[
llength $repo_file_list] != 0} {
3486 lassign [
GetVer $repo] ver sha
3487 lappend user_ip_repo_hashes $sha
3488 lappend user_ip_repo_vers $ver
3489 lappend versions $ver
3491 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3494 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory does not exist."
3503 while {$found == 0} {
3504 set global_commit [
Git "log --format=%h -1 --abbrev=7 $SHAs"]
3509 if {$common_child == 0} {
3510 Msg CriticalWarning "The commit $sha is not an ancestor of the global commit $global_commit, which is OK. \
3511 But $sha and $global_commit do not have any common child, which is NOT OK. \
3512 This is probably do to a REBASE that is forbidden in Hog methodology as it changes git history. \
3513 Hog cannot guarantee the accuracy of the SHAs. \
3514 A way to fix this is to make a commit that touches all the projects in the repositories (e.g. change the Hog version), \
3515 but please do not rebase in the official branches in the future."
3517 Msg Info "The commit $sha is not an ancestor of the global commit $global_commit, adding the first common child $common_child instead..."
3518 lappend SHAs $common_child
3528 set global_commit "0000000"
3529 set global_version "00000000"
3534 set top_hash [
format %+07s $top_hash]
3535 set cons_hash [
format %+07s $cons_hash]
3536 return [list $global_commit $global_version \
3537 $hog_hash $hog_ver $top_hash $top_ver \
3538 $libs $hashes $vers $cons_ver $cons_hash \
3539 $ext_names $ext_hashes $xml_hash $xml_ver \
3540 $user_ip_repos $user_ip_repo_hashes $user_ip_repo_vers]
3549 proc GetSHA {{path ""}} {
3551 lassign [
GitRet {log --format=%h --abbrev=7 -1}] status result
3553 return [
string tolower $result]
3555 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3561 set repo_path [
lindex [
Git {rev-parse --show-toplevel}] 0]
3565 set file_in_module 0
3566 if {[
file exists $repo_path/.gitmodules]} {
3567 lassign [
GitRet "config --file $repo_path/.gitmodules --get-regexp path"] status result
3569 set submodules [
split $result "\n"]
3572 Msg Warning "Something went wrong while trying to find submodules: $result"
3575 foreach mod $submodules {
3576 set module [
lindex $mod 1]
3577 if {[
string first "$repo_path/$module" $f] == 0} {
3579 set file_in_module 1
3580 lappend paths "$repo_path/$module"
3585 if {$file_in_module == 0} {
3591 lassign [
GitRet {log --format=%h --abbrev=7 -1} $paths] status result
3593 return [
string tolower $result]
3595 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3598 return [
string tolower $result]
3602 proc GetSimulators {} {
3603 set SIMULATORS [list "modelsim" "questa" "riviera" "activehdl" "ies" "vcs"]
3608 proc GetTopFile {} {
3610 set compile_order_prop [get_property source_mgmt_mode [current_project]]
3611 if {$compile_order_prop ne "All"} {
3612 Msg CriticalWarning "Compile order is not set to automatic, setting it now..."
3613 set_property source_mgmt_mode All [current_project]
3614 update_compile_order -fileset sources_1
3616 return [
lindex [get_files -quiet -compile_order sources -used_in synthesis -filter {FILE_TYPE =~ "VHDL*" || FILE_TYPE =~ "*Verilog*" }] end]
3617 }
elseif {[
IsISE]} {
3618 debug::design_graph_mgr -create [current_fileset]
3619 debug::design_graph -add_fileset [current_fileset]
3620 debug::design_graph -update_all
3621 return [
lindex [debug::design_graph -get_compile_order] end]
3623 Msg Error "GetTopFile not yet implemented for this IDE"
3628 proc GetTopModule {} {
3630 return [get_property top [current_fileset]]
3632 Msg Error "GetTopModule not yet implemented for this IDE"
3642 proc GetVer {path {force_develop 0}} {
3646 Msg CriticalWarning "Empty SHA found for ${path}. Commit to Git to resolve this warning."
3649 set p [
lindex $path 0]
3650 if {[
file isdirectory $p]} {
3653 cd [
file dirname $p]
3655 set repo_path [
Git {rev-parse --show-toplevel}]
3658 return [list [
GetVerFromSHA $SHA $repo_path $force_develop] $SHA]
3669 proc GetVerFromSHA {SHA repo_path {force_develop 0}} {
3671 Msg CriticalWarning "Empty SHA found"
3674 lassign [
GitRet "tag --sort=creatordate --contain $SHA -l v*.*.* -l b*v*.*.*"] status result
3677 if {[regexp {^ *$} $result]} {
3679 lassign [
GitRet "log --oneline --pretty=\"%d\""] status2 tag_list
3682 set pattern {tag: v\d+\.\d+\.\d+}
3683 set real_tag_list {}
3684 foreach x $tag_list {
3685 set x_untrimmed [regexp -all -inline $pattern $x]
3686 regsub "tag: " $x_untrimmed "" x_trimmed
3687 set tt [
lindex $x_trimmed 0]
3688 if {![
string equal $tt ""]} {
3689 lappend real_tag_list $tt
3693 Msg Debug "Cleaned up list: $real_tag_list."
3695 set sorted_tags [lsort -decreasing -command CompareVersions $real_tag_list]
3697 Msg Debug "Sorted Tag list: $sorted_tags"
3699 set tag [
lindex $sorted_tags 0]
3702 set pattern {v\d+\.\d+\.\d+}
3703 if {![regexp $pattern $tag]} {
3704 Msg CriticalWarning "No Hog version tags found in this repository."
3709 set repo_conf $repo_path/Top/repo.conf
3713 set hotfix_prefix "hotfix/"
3714 set minor_prefix "minor_version/"
3715 set major_prefix "major_version/"
3717 set enable_develop_branch $force_develop
3719 set branch_name [
Git {rev-parse --abbrev-ref HEAD}]
3721 if {[
file exists $repo_conf]} {
3722 set PROPERTIES [
ReadConf $repo_conf]
3724 if {[dict exists $PROPERTIES main]} {
3725 set mainDict [dict get $PROPERTIES main]
3728 if {[dict exists $mainDict ENABLE_DEVELOP_BRANCH]} {
3729 set enable_develop_branch [dict get $mainDict ENABLE_DEVELOP_BRANCH]
3735 if {[dict exists $PROPERTIES prefixes]} {
3736 set prefixDict [dict get $PROPERTIES prefixes]
3738 if {[dict exists $prefixDict HOTFIX]} {
3739 set hotfix_prefix [dict get $prefixDict HOTFIX]
3741 if {[dict exists $prefixDict MINOR_VERSION]} {
3742 set minor_prefix [dict get $prefixDict MINOR_VERSION]
3744 if {[dict exists $prefixDict MAJOR_VERSION]} {
3745 set major_prefix [dict get $prefixDict MAJOR_VERSION]
3751 if {$enable_develop_branch == 1} {
3752 if {[
string match "$hotfix_prefix*" $branch_name]} {
3757 if {[
string match "$major_prefix*" $branch_name]} {
3759 set version_level major
3760 }
elseif {[
string match "$minor_prefix*" $branch_name] || ($enable_develop_branch == 1 && $is_hotfix == 0)} {
3762 set version_level minor
3765 set version_level patch
3769 Msg CriticalWarning "Tag $tag does not contain a Hog compatible version in this repository."
3772 }
elseif {$mr == 0} {
3773 switch $version_level {
3788 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."
3794 set vers [
split $result "\n"]
3795 set ver [
lindex $vers 0]
3797 if {[regexp {^v.*$} $v]} {
3805 Msg CriticalWarning "Error while trying to find tag for $SHA"
3813 set M [
format %02X $M]
3814 set m [
format %02X $m]
3815 set c [
format %04X $c]
3816 }
elseif {$M > -1} {
3818 set M [
format %02X $M]
3819 set m [
format %02X $m]
3820 set c [
format %04X $c]
3822 Msg Warning "Tag does not contain a properly formatted version: $ver"
3823 set M [
format %02X 0]
3824 set m [
format %02X 0]
3825 set c [
format %04X 0]
3838 proc Git {command {files ""}} {
3839 lassign [
GitRet $command $files] ret result
3841 Msg Error "Code $ret returned by git running: $command -- $files"
3852 proc GetModuleName {filename} {
3854 if {![
file exists $filename]} {
3855 Msg CriticalWarning "Error: File $filename does not exist."
3860 set fileId [open $filename r]
3863 set file_content [read $fileId]
3869 if {[
file extension $filename] == ".vhd" || [
file extension $filename] == ".vhdl"} {
3871 set file_content [
string tolower $file_content]
3873 set pattern {(?m)^\s*entity\s+(\S+)\s+is}
3874 }
elseif {[
file extension $filename] == ".v" || [
file extension $filename] == ".sv"} {
3876 set pattern {\n\s*module\s*(\w+)(\s*|\(|\n)}
3878 Msg Debug "File is neither VHDL nor Verilog... Returning empty string..."
3883 if {[regexp $pattern $file_content match module_name]} {
3886 Msg Debug "No module was found in $filename. Returning an empty string..."
3894 proc GetVerilogGenerics {file} {
3895 set fp [open $file r]
3901 foreach line [
split $data "\n"] {
3902 regsub "^\\s*\/\/.*" $line "" line
3903 regsub "(.*)\/\/.*" $line {\1} line
3904 if {![
string equal $line ""]} {
3905 append lines $line " "
3910 regsub -all {/\*.*\*/} $lines "" lines
3913 set punctuation [list]
3914 foreach char [list "(" ")" ";" "," " " "!" "<=" ":=" "=" "\[" "\]"] {
3915 lappend punctuation $char "\000$char\000"
3919 set tokens [
split [
string map $punctuation $lines] \000]
3921 set parameters [dict create]
3930 foreach token $tokens {
3931 set token [
string trim $token]
3932 if {![
string equal "" $token]} {
3933 if {[
string equal [
string tolower $token] "parameter"]} {
3934 set state $PARAM_NAME
3935 }
elseif {[
string equal $token ")"] || [
string equal $token ";"]} {
3937 }
elseif {$state == $PARAM_WIDTH} {
3938 if {[
string equal $token "\]"]} {
3939 set state $PARAM_NAME
3941 }
elseif {$state == $PARAM_VALUE} {
3942 if {[
string equal $token ","]} {
3943 set state $PARAM_NAME
3944 }
elseif {[
string equal $token ";"]} {
3949 }
elseif {$state == $PARAM_NAME} {
3950 if {[
string equal $token "="]} {
3951 set state $PARAM_VALUE
3952 }
elseif {[
string equal $token "\["]} {
3953 set state $PARAM_WIDTH
3954 }
elseif {[
string equal $token ","]} {
3955 set state $PARAM_NAME
3956 }
elseif {[
string equal $token ";"]} {
3958 }
elseif {[
string equal $token ")"]} {
3961 dict set parameters $token "integer"
3974 proc GetVhdlGenerics {file {entity ""}} {
3975 set fp [open $file r]
3981 foreach line [
split $data "\n"] {
3982 regsub "^\\s*--.*" $line "" line
3983 regsub "(.*)--.*" $line {\1} line
3984 if {![
string equal $line ""]} {
3985 append lines $line " "
3990 set generic_block ""
3991 set generics [dict create]
3993 if {1 == [
string equal $entity ""]} {
3994 regexp {(?i).*entity\s+([^\s]+)\s+is} $lines _ entity
3997 set generics_regexp "(?i).*entity\\s+$entity\\s+is\\s+generic\\s*\\((.*)\\)\\s*;\\s*port.*end.*$entity"
3999 if {[regexp $generics_regexp $lines _ generic_block]} {
4001 foreach line [
split $generic_block ";"] {
4003 regexp {(.*):\s*([A-Za-z0-9_]+).*} $line _ generic type
4006 set splits [
split $generic ","]
4007 foreach split $splits {
4008 dict set generics [
string trim $split] [
string trim $type]
4016 proc GHDL {command logfile} {
4017 set ret [
catch {
exec -ignorestderr ghdl {*}$command >>& $logfile} result options]
4022 return [list $ret $result]
4034 proc GitRet {command {files ""}} {
4037 set ret [
catch {
exec -ignorestderr git {*}$command} result]
4039 set ret [
catch {
exec -ignorestderr git {*}$command -- {*}$files} result]
4041 return [list $ret $result]
4049 proc GitVersion {target_version} {
4050 set ver [
split $target_version "."]
4051 set v [
Git --version]
4053 set current_ver [
split [
lindex $v 2] "."]
4054 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
4055 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
4056 return [
expr {$target <= $current}]
4070 proc HandleIP {what_to_do xci_file ip_path repo_path {gen_dir "."} {force 0}} {
4072 if {!($what_to_do eq "push") && !($what_to_do eq "pull")} {
4073 Msg Error "You must specify push or pull as first argument."
4076 if {[
catch {
package require tar} TARPACKAGE]} {
4077 Msg CriticalWarning "Cannot find package tar. You can fix this by installing package \"tcllib\""
4088 if {[regexp {^[^/]+:} $ip_path]} {
4092 lassign [
ExecuteRet rclone --version] rclone_ret rclone_ver
4093 if {$rclone_ret != 0} {
4094 Msg CriticalWarning "Rclone path specified but rclone not found or failed: $rclone_ver"
4098 Msg Info "IP remote directory path, on Rclone, is set to: $ip_path"
4100 if {[
info exists env(HOG_RCLONE_CONFIG)]} {
4101 Msg Info "Using rclone config from environment variable HOG_RCLONE_CONFIG: $env(HOG_RCLONE_CONFIG)"
4102 set config_path $env(HOG_RCLONE_CONFIG)
4104 set config_path "/dev/null"
4105 Msg Info "Environment variable HOG_RCLONE_CONFIG not set, using rclone environmental variables..."
4108 set remote_name "[
lindex [
split $ip_path ":"] 0]:"
4109 lassign [
ExecuteRet rclone listremotes --config $config_path] rclone_list_ret remotes
4110 if {$rclone_list_ret != 0} {
4111 Msg CriticalWarning "Could not list rclone remotes: $remotes"
4115 if {![
IsInList $remote_name $remotes]} {
4116 Msg CriticalWarning "Rclone remote $remote_name not found among available remotes: $remotes"
4122 }
elseif {[
string first "/eos/" $ip_path] == 0} {
4125 lassign [
eos "ls $ip_path"] ret result
4127 Msg CriticalWarning "Could not run ls for for EOS path: $ip_path (error: $result). \
4128 Either the drectory does not exist or there are (temporary) problem with EOS."
4132 Msg Info "IP remote directory path, on EOS, is set to: $ip_path"
4138 if {!([
file exists $xci_file])} {
4139 Msg CriticalWarning "Could not find $xci_file."
4145 set xci_path [
file dirname $xci_file]
4146 set xci_name [
file tail $xci_file]
4147 set xci_ip_name [
file rootname [
file tail $xci_file]]
4148 set xci_dir_name [
file tail $xci_path]
4149 set gen_path $gen_dir
4151 set hash [
Md5Sum $xci_file]
4152 set file_name $xci_name\_$hash
4154 Msg Info "Preparing to $what_to_do IP: $xci_name..."
4156 if {$what_to_do eq "push"} {
4159 if {$on_rclone == 1} {
4160 lassign [
ExecuteRet rclone ls $ip_path/$file_name.tar --config $config_path] ret result
4165 Msg Info "IP already in the Rclone repository, will not copy..."
4167 Msg Info "IP already in the Rclone repository, will forcefully replace..."
4172 }
elseif {$on_eos == 1} {
4173 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4178 Msg Info "IP already in the EOS repository, will not copy..."
4180 Msg Info "IP already in the EOS repository, will forcefully replace..."
4186 if {[
file exists "$ip_path/$file_name.tar"]} {
4188 Msg Info "IP already in the local repository, will not copy..."
4190 Msg Info "IP already in the local repository, will forcefully replace..."
4199 if {$will_copy == 1} {
4201 Msg Info "Looking for generated files in $gen_path..."
4202 set ip_gen_files [glob -nocomplain $gen_path/*]
4206 if {[
llength $ip_gen_files] > 0} {
4207 Msg Info "Found some IP synthesised files matching $xci_ip_name"
4208 if {$will_remove == 1} {
4209 Msg Info "Removing old synthesised directory $ip_path/$file_name.tar..."
4210 if {$on_rclone == 1} {
4211 lassign [
ExecuteRet rclone delete $ip_path/$file_name.tar --config $config_path] ret result
4213 Msg CriticalWarning "Could not delete file from Rclone: $result"
4215 }
elseif {$on_eos == 1} {
4216 eos "rm -rf $ip_path/$file_name.tar" 5
4218 file delete -force "$ip_path/$file_name.tar"
4222 Msg Info "Creating local archive with IP generated files..."
4225 foreach f $ip_gen_files {
4226 lappend tar_files "[
Relative [
file normalize $repo_path] $f]"
4229 ::tar::create $file_name.tar $tar_files
4231 Msg Info "Copying IP generated files for $xci_name..."
4232 if {$on_rclone == 1} {
4233 lassign [
ExecuteRet rclone copyto $file_name.tar $ip_path/$file_name.tar --config $config_path] ret result
4235 Msg CriticalWarning "Something went wrong when copying the IP files to Rclone. Error message: $result"
4237 }
elseif {$on_eos == 1} {
4238 lassign [
ExecuteRet xrdcp -f -s $file_name.tar $::env(EOS_MGM_URL)//$ip_path/] ret msg
4240 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4243 Copy "$file_name.tar" "$ip_path/"
4245 Msg Info "Removing local archive"
4246 file delete $file_name.tar
4248 Msg Warning "Could not find synthesized files matching $gen_path/$file_name*"
4251 }
elseif {$what_to_do eq "pull"} {
4252 if {$on_rclone == 1} {
4253 lassign [
ExecuteRet rclone ls $ip_path/$file_name.tar --config $config_path] ret result
4255 Msg Info "Nothing for $xci_name was found in the Rclone repository, cannot pull."
4259 Msg Info "IP $xci_name found in the Rclone repository $ip_path, copying it locally to $repo_path..."
4260 lassign [
ExecuteRet rclone copyto $ip_path/$file_name.tar $file_name.tar --config $config_path] ret_copy result_copy
4261 if {$ret_copy != 0} {
4262 Msg CriticalWarning "Something went wrong when copying the IP files from Rclone. Error message: $result_copy"
4265 }
elseif {$on_eos == 1} {
4266 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4268 Msg Info "Nothing for $xci_name was found in the EOS repository, cannot pull."
4272 set remote_tar "$::env(EOS_MGM_URL)//$ip_path/$file_name.tar"
4273 Msg Info "IP $xci_name found in the repository $remote_tar, copying it locally to $repo_path..."
4275 lassign [
ExecuteRet xrdcp -f -r -s $remote_tar $repo_path] ret msg
4277 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4281 if {[
file exists "$ip_path/$file_name.tar"]} {
4282 Msg Info "IP $xci_name found in local repository $ip_path/$file_name.tar, copying it locally to $repo_path..."
4283 Copy $ip_path/$file_name.tar $repo_path
4285 Msg Info "Nothing for $xci_name was found in the local IP repository, cannot pull."
4291 if {[
file exists $file_name.tar]} {
4292 remove_files $xci_file
4293 Msg Info "Extracting IP files from archive to $repo_path..."
4294 ::tar::untar $file_name.tar -dir $repo_path -noperms
4295 Msg Info "Removing local archive"
4296 file delete $file_name.tar
4297 add_files -norecurse -fileset sources_1 $xci_file
4310 proc HexVersionToString {version} {
4311 scan [
string range $version 0 1] %x M
4312 scan [
string range $version 2 3] %x m
4313 scan [
string range $version 4 7] %x c
4318 proc ImportTclLib {} {
4321 if {[
info exists env(HOG_TCLLIB_PATH)]} {
4322 lappend auto_path $env(HOG_TCLLIB_PATH)
4325 puts "ERROR: To run Hog with Microsemi Libero SoC or Lattice Diamond, you need to define the HOG_TCLLIB_PATH variable."
4340 proc InitLauncher {script tcl_path parameters commands argv {custom_commands ""}} {
4341 set repo_path [
file normalize "$tcl_path/../.."]
4343 set bin_path [
file normalize "$tcl_path/../../bin"]
4344 set top_path [
file normalize "$tcl_path/../../Top"]
4346 set cmd_lines [
split $commands "\n"]
4348 set command_options [dict create]
4349 set directive_descriptions [dict create]
4350 set directive_names [dict create]
4351 set common_directive_names [dict create]
4352 set custom_command ""
4353 set custom_command_options ""
4355 foreach l $cmd_lines {
4357 if {[regexp {\\(.*) \{\#} $l minc d]} {
4358 lappend directives_with_projects $d
4362 if {[regexp {\\(.*) \{} $l minc regular_expression]} {
4363 lappend directive_regex $regular_expression
4367 if {[regexp {\#\s*NAME(\*)?:\s*(.*)\s*} $l minc star name]} {
4368 dict set directive_names $name $regular_expression
4370 dict set common_directive_names $name $regular_expression
4373 set directive_names [
DictSort $directive_names]
4374 set common_directive_names [
DictSort $common_directive_names]
4377 if {[regexp {\#\s*DESCRIPTION:\s*(.*)\s*} $l minc x]} {
4378 dict set directive_descriptions $regular_expression $x
4382 if {[regexp {\#\s*OPTIONS:\s*(.*)\s*} $l minc x]} {
4383 dict set command_options $regular_expression [
split [regsub -all {[ \t\n]+} $x {}] ","]
4387 set short_usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nMost common directives (case insensitive):"
4389 dict for {key value} $common_directive_names {
4390 set short_usage "$short_usage\n - $key: [dict get $directive_descriptions $value]"
4394 if {[
string length $custom_commands] > 0} {
4395 Msg Debug "Found custom commands to add to short short_usage."
4396 set short_usage "$short_usage\n\nCustom commands:"
4397 dict for {key command} $custom_commands {
4398 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4399 set short_usage "$short_usage\n - $key: [dict get $command DESCRIPTION]"
4405 set short_usage "$short_usage\n\n\
4406 To see all the available directives, run:\n./Hog/Do HELP\n\n\
4407 To list available options for the chosen directive run:\n\
4408 ./Hog/Do <directive> HELP\n
4411 set usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nDirectives (case insensitive):"
4413 dict for {key value} $directive_names {
4414 set usage "$usage\n - $key: [dict get $directive_descriptions $value]"
4418 if {[
string length $custom_commands] > 0} {
4419 Msg Debug "Found custom commands to add to short usage."
4420 set usage "$usage\n\nCustom commands:"
4421 dict for {key command} $custom_commands {
4422 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4423 set usage "$usage\n - $key: [dict get $command DESCRIPTION]"
4428 set usage "$usage\n\nTo list available options for the chosen directive run:\n./Hog/Do <directive> HELP"
4437 Msg Debug "HogEnv.conf found"
4445 if {[
catch {
package require cmdline} ERROR]} {
4446 Msg Debug "The cmdline Tcl package was not found, sourcing it from Hog..."
4447 source $tcl_path/utils/cmdline.tcl
4450 set argv [regsub -all {(?i) HELP\y} $argv " -help"]
4455 set custom_parameters [list]
4456 dict for {key command} $custom_commands {
4457 set custom_parameters [concat $custom_parameters [dict get $command CUSTOM_OPTIONS]]
4460 lassign [
GetOptions $argv [
concat $custom_parameters $parameters]] option_list arg_list
4462 if {[
IsInList "-all" $option_list]} {
4471 set directive [
string toupper [
lindex $arg_list 0]]
4474 set argument_is_no_project 1
4476 set NO_DIRECTIVE_FOUND 0
4477 switch -regexp -- $directive "$commands"
4478 if {$NO_DIRECTIVE_FOUND == 1} {
4479 if {[
string length $custom_commands] > 0 && [dict exists $custom_commands $directive]} {
4480 set custom_command $directive
4481 set custom_command_hog_parameters [dict get $custom_commands $directive OPTIONS]
4482 set custom_command_options [dict get $custom_commands $directive CUSTOM_OPTIONS]
4483 set custom_command_options [
concat $custom_command_hog_parameters $custom_command_options]
4485 Msg Status "ERROR: Unknown directive $directive.\n\n"
4491 if {[
IsInList $directive $directives_with_projects 1]} {
4492 set argument_is_no_project 0
4496 if {$directive != ""} {
4497 if {[
IsInList $directive $directives_with_projects 1]} {
4498 puts "usage: ./Hog/Do \[OPTIONS\] $directive <project>\n"
4499 }
elseif {[regexp "^COMPSIM(LIB)?$" $directive]} {
4500 puts "usage: ./Hog/Do \[OPTIONS\] $directive <simulator>\n"
4502 puts "usage: ./Hog/Do \[OPTIONS\] $directive \n"
4505 dict for {dir desc} $directive_descriptions {
4506 if {[regexp $dir $directive]} {
4514 if {$custom_command ne ""} {
4515 if {[
llength $custom_command_options] > 0} {
4516 puts "Available options:"
4518 foreach custom_option $custom_command_options {
4519 set n [
llength $custom_option]
4521 lassign $custom_option opt help
4524 }
elseif {$n == 3} {
4525 lassign $custom_option opt def help
4526 puts " -$opt <argument>"
4528 puts " $help (default: $def)"
4533 Msg Warning "Custom option spec has invalid arity (expected 2 or 3): $custom_option"
4538 dict for {dir opts} $command_options {
4539 if {[regexp $dir $directive]} {
4540 puts "Available options:"
4542 foreach par $parameters {
4543 if {$opt == [lindex $par 0]} {
4544 if {[regexp {\.arg$} $opt]} {
4545 set opt_name [regsub {\.arg$} $opt ""]
4546 puts " -$opt_name <argument>"
4550 puts " [lindex $par [llength $par]-1]"
4564 if {$custom_command ne ""} {
4565 set parameters [
concat $parameters $custom_command_options]
4568 if {[
catch {
array set options [
cmdline::getoptions option_list $parameters $usage]} err]} {
4569 Msg Status "\nERROR: Syntax error, probably unknown option.\n\n USAGE: $err"
4573 if {[
llength $arg_list] <= $min_n_of_args || [
llength $arg_list] > $max_n_of_args} {
4574 Msg Status "\nERROR: Wrong number of arguments: [
llength $argv]"
4579 set project [
lindex $arg_list 1]
4581 if {$argument_is_no_project == 0} {
4583 regsub "^(\./)?Top/" $project "" project
4585 regsub "/? *\$" $project "" project
4591 Msg Debug "Option list:"
4592 foreach {key value} [
array get options] {
4593 Msg Debug "$key => $value"
4600 if {$proj_conf != 0} {
4603 lassign [
GetIDECommand $proj_conf] cmd before_tcl_script after_tcl_script end_marker
4604 Msg Info "Project $project uses $cmd IDE"
4607 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4609 if {$custom_command ne ""} {
4610 if { [dict exists $custom_commands $directive IDE] } {
4611 lassign [
GetIDECommand "" [dict get $custom_commands $directive IDE]] cmd before_tcl_script after_tcl_script end_marker
4612 Msg Info "Custom command: $custom_command uses $cmd IDE"
4613 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4615 set command "custom_tcl"
4617 }
elseif {$argument_is_no_project == 1} {
4619 Msg Debug "$project will be used as first argument"
4620 }
elseif {$project != ""} {
4623 }
elseif {$min_n_of_args < 0} {
4636 set project_group [
file dirname $project]
4637 set project_name $project
4638 set project [
file tail $project]
4639 Msg Debug "InitLauncher: project_group=$project_group, project_name=$project_name, project=$project"
4641 return [list $directive $project $project_name $project_group $repo_path $old_path $bin_path $top_path $usage $short_usage $command $cmd [
array get options]]
4648 proc IsCommitAncestor {ancestor commit} {
4649 lassign [
GitRet "merge-base --is-ancestor $ancestor $commit"] status result
4658 return [
expr {[info commands sys_install] != ""}]
4663 return [
expr {[info commands get_libero_version] != ""}]
4672 proc IsInList {element list {regex 0} {nocase 0}} {
4676 if {[regexp -nocase $x $element]} {
4680 if {[regexp $x $element]} {
4684 }
elseif {$regex == 0} {
4686 if {[
string tolower $x] eq [
string tolower $element]} {
4690 if {$x eq $element} {
4703 return [
expr {[string first PlanAhead [version]] == 0}]
4711 if {[
catch {
package require ::quartus::flow} result]} {
4724 proc IsRelativePath {path} {
4725 if {[
string index $path 0] == "/" || [
string index $path 0] == "~"} {
4733 proc IsSynplify {} {
4734 return [
expr {[info commands program_version] != ""}]
4739 return [
expr {![IsQuartus] && ![IsXilinx] && ![IsVitisClassic] && ![IsLibero] && ![IsSynplify] && ![IsDiamond]}]
4747 proc IsVerilog {file} {
4748 if {[
file extension $file] == ".v" || [
file extension $file] == ".sv"} {
4760 proc IsVersal {part} {
4761 if {[get_property ARCHITECTURE [get_parts $part]] eq "versal"} {
4771 return [
expr {[string first Vivado [version]] == 0}]
4779 if {[
info commands version] != ""} {
4780 set current_version [version]
4781 if {[
string first PlanAhead $current_version] == 0 || [
string first Vivado $current_version] == 0} {
4783 }
elseif {[
string first xsct $current_version] == 0} {
4786 Msg Warning "This IDE has the version command but it is not PlanAhead or Vivado: $current_version"
4795 proc IsVitisClassic {} {
4796 return [
expr {[info commands platform] != ""}]
4804 proc IsZynq {part} {
4805 if {[regexp {^(xc7z|xczu).*} $part]} {
4812 proc ImportGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
4813 set list_path "$repo_path/Top/$project_name/list"
4814 lassign [
GetHogFiles -list_files {.src,.ext,.sim} -ext_path $ext_path $list_path $repo_path] src_files properties filesets
4819 set properties [
DictGet $simset_dict "properties"]
4820 set options [
DictGet $properties "options"]
4823 set workdir Projects/$project_name/ghdl
4824 file delete -force $workdir
4826 set import_log "$workdir/ghdl-import-${simset_name}.log"
4827 dict for {lib sources} $src_files {
4828 set libname [file rootname $lib]
4829 foreach f $sources {
4830 if {[file extension $f] != ".vhd" && [file extension $f] != ".vhdl"} {
4831 Msg Info "File $f is not a VHDL file, copying it in workfolder..."
4832 file copy -force $f $workdir
4834 set file_path [Relative $repo_path $f]
4835 set import_log_file [open $import_log "a"]
4836 puts "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
4837 puts $import_log_file "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
4838 close $import_log_file
4839 lassign [GHDL "-i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path" $import_log] ret result
4841 Msg CriticalWarning "GHDL import failed for file $f: $result"
4850 proc LaunchGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
4853 set sim_props [
DictGet $simset_dict "properties"]
4854 set options [
DictGet $sim_props "options"]
4855 set runopts [
DictGet $sim_props "run_options"]
4857 dict for {prop_name prop_val} $sim_props {
4858 set prop_name [string toupper $prop_name]
4859 if {$prop_name == "TOP"} {
4860 set top_sim $prop_val
4863 set workdir $repo_path/Projects/$project_name/ghdl
4864 set make_log "$workdir/ghdl-make-${simset_name}.log"
4865 set run_log "$workdir/ghdl-run-${simset_name}.log"
4868 set make_log_file [open $make_log "w"]
4870 puts "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
4871 puts $make_log_file "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
4872 close $make_log_file
4874 lassign [
GHDL "-m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim" $make_log] ret result
4877 Msg Error "GHDL make failed for $top_sim: $result"
4881 set run_log_file [open $run_log "w"]
4882 puts "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
4883 puts $run_log_file "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
4886 lassign [
GHDL "-r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts" $run_log] ret result
4890 Msg Error "GHDL run failed for $top_sim: $result"
4905 proc LaunchImplementation {reset do_create run_folder project_name {repo_path .} {njobs 4} {do_bitstream 0}} {
4906 Msg Info "Starting implementation flow..."
4908 if {$reset == 1 && $do_create == 0} {
4909 Msg Info "Resetting run before launching implementation..."
4914 source $repo_path/Hog/Tcl/integrated/pre-implementation.tcl
4917 if {$do_bitstream == 1} {
4918 launch_runs impl_1 -to_step [
BinaryStepName [get_property PART [current_project]]] -jobs $njobs -dir $run_folder
4920 launch_runs impl_1 -jobs $njobs -dir $run_folder
4925 Msg Info "running post-implementation"
4926 source $repo_path/Hog/Tcl/integrated/post-implementation.tcl
4927 if {$do_bitstream == 1} {
4928 Msg Info "running pre-bitstream"
4929 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
4930 Msg Info "running post-bitstream"
4931 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
4935 set prog [get_property PROGRESS [get_runs impl_1]]
4936 set status [get_property STATUS [get_runs impl_1]]
4937 Msg Info "Run: impl_1 progress: $prog, status : $status"
4941 set status_file [open "$run_folder/timing.txt" "w"]
4942 puts $status_file "## $project_name Timing summary"
4944 set f [open [
lindex [glob "$run_folder/impl_1/*.twr" 0]]]
4946 while {[
gets $f line] >= 0} {
4947 if {[
string match "Timing summary:" $line]} {
4948 while {[
gets $f line] >= 0} {
4949 if {[
string match "Timing errors:*" $line]} {
4950 set errs [regexp -inline -- {[0-9]+} $line]
4952 if {[
string match "*Footnotes*" $line]} {
4955 puts $status_file "$line"
4964 Msg Info "Time requirements are met"
4965 file rename -force "$run_folder/timing.txt" "$run_folder/timing_ok.txt"
4968 Msg CriticalWarning "Time requirements are NOT met"
4969 file rename -force "$run_folder/timing.txt" "$run_folder/timing_error.txt"
4975 set wns [get_property STATS.WNS [get_runs [current_run]]]
4976 set tns [get_property STATS.TNS [get_runs [current_run]]]
4977 set whs [get_property STATS.WHS [get_runs [current_run]]]
4978 set ths [get_property STATS.THS [get_runs [current_run]]]
4979 set tpws [get_property STATS.TPWS [get_runs [current_run]]]
4981 if {$wns >= 0 && $whs >= 0 && $tpws >= 0} {
4982 Msg Info "Time requirements are met"
4983 set status_file [open "$run_folder/timing_ok.txt" "w"]
4986 Msg CriticalWarning "Time requirements are NOT met"
4987 set status_file [open "$run_folder/timing_error.txt" "w"]
4991 Msg Status "*** Timing summary ***"
4992 Msg Status "WNS: $wns"
4993 Msg Status "TNS: $tns"
4994 Msg Status "WHS: $whs"
4995 Msg Status "THS: $ths"
4996 Msg Status "TPWS: $tpws"
5002 puts $status_file "## $project_name Timing summary"
5004 m add row "| **Parameter** | \"**value (ns)**\" |"
5005 m add row "| --- | --- |"
5006 m add row "| WNS: | $wns |"
5007 m add row "| TNS: | $tns |"
5008 m add row "| WHS: | $whs |"
5009 m add row "| THS: | $ths |"
5010 m add row "| TPWS: | $tpws |"
5012 puts $status_file [m format 2string]
5013 puts $status_file "\n"
5014 if {$timing_ok == 1} {
5015 puts $status_file " Time requirements are met."
5017 puts $status_file "Time requirements are **NOT** met."
5019 puts $status_file "\n\n"
5023 if {$prog ne "100%"} {
5024 Msg Error "Implementation error"
5029 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5031 Msg Info "Git describe set to $describe"
5033 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5038 if {[
file exists $run_folder/versions.txt]} {
5039 file copy -force $run_folder/versions.txt $dst_dir
5041 Msg Warning "No versions file found in $run_folder/versions.txt"
5044 set timing_files [glob -nocomplain "$run_folder/timing_*.txt"]
5045 set timing_file [
file normalize [
lindex $timing_files 0]]
5047 if {[
file exists $timing_file]} {
5048 file copy -force $timing_file $dst_dir/
5050 Msg Warning "No timing file found, not a problem if running locally"
5054 if {[
IsVersal [get_property part [current_project]]]} {
5055 if {[get_property segmented_configuration [current_project]] == 1} {
5056 Msg Info "Versal Segmented configuration detected: exporting XSA file..."
5057 set xsa_name "$dst_dir/[
file tail $project_name]\-$describe.xsa"
5058 write_hw_platform -fixed -force -file $xsa_name
5062 set revision [get_current_revision]
5064 if {[
catch {execute_module -tool fit} result]} {
5065 Msg Error "Result: $result\n"
5066 Msg Error "Place & Route failed. See the report file.\n"
5068 Msg Info "\nINFO: Place & Route was successful for revision $revision.\n"
5071 if {[
catch {execute_module -tool sta -args "--do_report_timing"} result]} {
5072 Msg Error "Result: $result\n"
5073 Msg Error "Time Quest failed. See the report file.\n"
5075 Msg Info "Time Quest was successfully run for revision $revision.\n"
5078 set panel "Timing Analyzer||Timing Analyzer Summary"
5079 set device [get_report_panel_data -name $panel -col 1 -row_name "Device Name"]
5080 set timing_model [get_report_panel_data -name $panel -col 1 -row_name "Timing Models"]
5081 set delay_model [get_report_panel_data -name $panel -col 1 -row_name "Delay Model"]
5083 Msg Info "*******************************************************************"
5084 Msg Info "Device: $device"
5085 Msg Info "Timing Models: $timing_model"
5086 Msg Info "Delay Model: $delay_model"
5089 Msg Info "*******************************************************************"
5092 Msg Info "Starting implementation flow..."
5093 if {[
catch {run_tool -name {PLACEROUTE}}]} {
5094 Msg Error "PLACEROUTE FAILED!"
5096 Msg Info "PLACEROUTE PASSED."
5100 Msg Info "Run VERIFYTIMING ..."
5101 if {[
catch {run_tool -name {VERIFYTIMING} -script {Hog/Tcl/integrated/libero_timing.tcl}}]} {
5102 Msg CriticalWarning "VERIFYTIMING FAILED!"
5104 Msg Info "VERIFYTIMING PASSED \n"
5110 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5112 Msg Info "Git describe set to $describe"
5114 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5115 file mkdir $dst_dir/reports
5118 if {[
file exists $run_folder/versions.txt]} {
5119 file copy -force $run_folder/versions.txt $dst_dir
5121 Msg Warning "No versions file found in $run_folder/versions.txt"
5124 set timing_file_path [
file normalize "$repo_path/Projects/timing_libero.txt"]
5125 if {[
file exists $timing_file_path]} {
5126 file copy -force $timing_file_path $dst_dir/reports/Timing.txt
5127 set timing_file [open $timing_file_path "r"]
5128 set status_file [open "$dst_dir/timing.txt" "w"]
5129 puts $status_file "## $project_name Timing summary\n\n"
5130 puts $status_file "| | |"
5131 puts $status_file "| --- | --- |"
5132 while {[
gets $timing_file line] >= 0} {
5133 if {[
string match "SUMMARY" $line]} {
5134 while {[
gets $timing_file line] >= 0} {
5135 if {[
string match "END SUMMARY" $line]} {
5138 if {[
string first ":" $line] == -1} {
5141 set out_string "| [
string map {: | } $line] |"
5142 puts $status_file "$out_string"
5147 Msg Warning "No timing file found, not a problem if running locally"
5152 set force_rst "-forceOne"
5154 prj_run Map $force_rst
5155 prj_run PAR $force_rst
5167 proc GenerateBitstreamOnly {project_name {repo_path .}} {
5169 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5171 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5173 cd Projects/$project_name/$project_name.runs/impl_1
5174 Msg Info "Running pre-bitstream..."
5175 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5177 Msg Info "Writing bitstream for $project_name..."
5179 write_bitstream -force $dst_dir/$project_name-$describe.bit
5181 Msg Info "Running post-bitstream..."
5182 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5191 proc LaunchSimulation {project_name lib_path simsets {repo_path .} {scripts_only 0} {compile_only 0}} {
5194 set project [
file tail $project_name]
5195 set main_sim_folder [
file normalize "$repo_path/Projects/$project_name/$project.sim/"]
5197 if {$simsets != ""} {
5198 dict for {simset sim_dict} $simsets {
5199 lappend simsets_todo $simset
5201 Msg Info "Will run only the following simulation's sets (if they exist): $simsets_todo"
5204 if {$scripts_only == 1} {
5205 Msg Info "Only generating simulation scripts, not running simulations..."
5208 if {$compile_only == 1} {
5209 Msg Info "Only compiling simulation libraries, not running simulations..."
5214 set sim_dic [dict create]
5216 Msg Info "Retrieving list of simulation sets..."
5217 foreach s [get_filesets] {
5219 set use_simpass_str 0
5222 set type [get_property FILESET_TYPE $s]
5223 if {$type eq "SimulationSrcs"} {
5224 if {$simsets_todo != "" && $s ni $simsets_todo} {
5225 Msg Info "Skipping $s as it was not specified with the -simset option..."
5228 set sim_dict [
DictGet $simsets $s]
5229 set simulator [
DictGet $sim_dict "simulator"]
5230 set_property "target_simulator" $simulator [current_project]
5231 set hog_sim_props [
DictGet $sim_dict "hog"]
5232 dict for {prop_name prop_val} $hog_sim_props {
5233 # If HOG_SIMPASS_STR is set, use the HOG_SIMPASS_STR string to search for in logs, after simulation is done
5234 if {[string toupper $prop_name] == "HOG_SIMPASS_STR" && $prop_val != ""} {
5235 Msg Info "Setting simulation pass string as '$prop_val'"
5236 set use_simpass_str 1
5237 set simpass_str $prop_val
5239 if {[string toupper $prop_name] == "HOG_SILENT_SIM" && $prop_val == 1} {
5240 set quiet_sim " -quiet"
5246 Msg Info "Creating simulation scripts for $s..."
5247 if {[
file exists $repo_path/Top/$project_name/pre-simulation.tcl]} {
5248 Msg Info "Running $repo_path/Top/$project_name/pre-simulation.tcl"
5249 source $repo_path/Top/$project_name/pre-simulation.tcl
5251 if {[
file exists $repo_path/Top/$project_name/pre-$s-simulation.tcl]} {
5252 Msg Info "Running $repo_path/Top/$project_name/pre-$s-simulation.tcl"
5253 source Running $repo_path/Top/$project_name/pre-$s-simulation.tcl
5255 current_fileset -simset $s
5256 set sim_dir $main_sim_folder/$s/behav
5257 set sim_output_logfile $sim_dir/xsim/simulate.log
5258 if {([
string tolower $simulator] eq "xsim")} {
5259 set sim_name "xsim:$s"
5261 set simulation_command "launch_simulation $quiet_sim -simset [get_filesets $s]"
5262 if {[
catch $simulation_command log]} {
5265 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5266 lappend failed $sim_name
5271 if {$use_simpass_str == 1} {
5274 set file_desc [open $sim_output_logfile r]
5275 set log [read $file_desc]
5278 Msg Info "Searching for simulation pass string: '$simpass_str'"
5279 if {[
string first $simpass_str $log] == -1} {
5280 Msg CriticalWarning "Simulation failed for $s, error info: '$simpass_str' NOT found!"
5281 lappend failed $sim_name
5284 lappend success $sim_name
5288 lappend success $sim_name
5292 Msg Info "Simulation library path is set to $lib_path."
5294 if {!([
file exists $lib_path])} {
5295 Msg Warning "Could not find simulation library path: $lib_path, $simulator simulation will not work."
5299 if {$simlib_ok == 1} {
5300 set_property "compxlib.${simulator}_compiled_library_dir" [
file normalize $lib_path] [current_project]
5301 launch_simulation -scripts_only -simset [get_filesets $s]
5302 set top_name [get_property TOP $s]
5303 set sim_script [
file normalize $sim_dir/$simulator/]
5304 Msg Info "Adding simulation script location $sim_script for $s..."
5305 lappend sim_scripts $sim_script
5306 dict append sim_dic $sim_script $s
5308 Msg Error "Cannot run $simulator simulations without a valid library path"
5315 if {[
info exists sim_scripts] && $scripts_only == 0} {
5317 Msg Info "Generating IP simulation targets, if any..."
5319 foreach ip [get_ips] {
5320 generate_target simulation -quiet $ip
5325 Msg Info "====== Starting simulations runs ======"
5328 foreach s $sim_scripts {
5330 set cmd ./compile.sh
5331 Msg Info " ************* Compiling: $s ************* "
5333 set sim_name "comp:[dict get $sim_dic $s]"
5335 Msg CriticalWarning "Compilation failed for $s, error info: $::errorInfo"
5336 lappend failed $sim_name
5338 lappend success $sim_name
5340 Msg Info "###################### Compilation log starts ######################"
5341 Msg Info "\n\n$log\n\n"
5342 Msg Info "###################### Compilation log ends ######################"
5344 if {$compile_only == 1} {
5347 if {[
file exists "./elaborate.sh"] } {
5348 set cmd ./elaborate.sh
5349 Msg Info " ************* Elaborating: $s ************* "
5351 set sim_name "elab:[dict get $sim_dic $s]"
5353 Msg CriticalWarning "Elaboration failed for $s, error info: $::errorInfo"
5354 lappend failed $sim_name
5356 lappend success $sim_name
5358 Msg Info "###################### Elaboration log starts ######################"
5359 Msg Info "\n\n$log\n\n"
5360 Msg Info "###################### Elaboration log ends ######################"
5362 set cmd ./simulate.sh
5363 Msg Info " ************* Simulating: $s ************* "
5368 if {$use_simpass_str == 1} {
5369 if {[
string first $simpass_str $log] == -1} {
5373 Msg Debug "Simulation pass string not set, relying on simulator exit code."
5377 set sim_name "sim:[dict get $sim_dic $s]"
5379 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5380 lappend failed $sim_name
5382 lappend success $sim_name
5384 Msg Info "###################### Simulation log starts ######################"
5385 Msg Info "\n\n$log\n\n"
5386 Msg Info "###################### Simulation log ends ######################"
5391 if {[
llength $success] > 0} {
5392 set successes [
join $success "\n"]
5393 Msg Info "The following simulation sets were successful:\n\n$successes\n\n"
5396 if {[
llength $failed] > 0} {
5397 set failures [
join $failed "\n"]
5398 Msg Error "The following simulation sets have failed:\n\n$failures\n\n"
5400 }
elseif {[
llength $success] > 0} {
5401 Msg Info "All the [
llength $success] compilations, elaborations and simulations were successful."
5404 Msg Info "Simulation done."
5406 Msg Warning "Simulation is not yet supported for [
GetIDEName]."
5414 proc LaunchRTLAnalysis {repo_path} {
5416 Msg Info "Starting RTL Analysis..."
5418 synth_design -rtl -name rtl_1
5420 Msg Warning "RTL Analysis is not yet supported for [
GetIDEName]."
5433 proc LaunchSynthesis {reset do_create run_folder project_name {repo_path .} {ext_path ""} {njobs 4}} {
5435 if {$reset == 1 && $do_create == 0} {
5436 Msg Info "Resetting run before launching synthesis..."
5440 source $repo_path/Hog/Tcl/integrated/pre-synthesis.tcl
5442 launch_runs synth_1 -jobs $njobs -dir $run_folder
5444 set prog [get_property PROGRESS [get_runs synth_1]]
5445 set status [get_property STATUS [get_runs synth_1]]
5446 Msg Info "Run: synth_1 progress: $prog, status : $status"
5453 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path $ext_path] sha
5455 Msg Info "Git describe set to $describe"
5458 set xci_file [get_property IP_FILE $ip]
5460 set xci_path [
file dirname $xci_file]
5461 set xci_ip_name [
file rootname [
file tail $xci_file]]
5462 foreach rptfile [glob -nocomplain -directory $xci_path *.rpt] {
5463 file copy $rptfile $repo_path/bin/$project_name-$describe/reports
5467 if {$prog ne "100%"} {
5468 Msg Error "Synthesis error, status is: $status"
5472 set project [
file tail [
file rootname $project_name]]
5474 Msg Info "Number of jobs set to $njobs."
5475 set_global_assignment -name NUM_PARALLEL_PROCESSORS $njobs
5479 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$project_name] $repo_path] sha
5482 set revision [get_current_revision]
5485 set tool_and_command [
split [get_global_assignment -name PRE_FLOW_SCRIPT_FILE] ":"]
5486 set tool [
lindex $tool_and_command 0]
5487 set pre_flow_script [
lindex $tool_and_command 1]
5488 set cmd "$tool -t $pre_flow_script quartus_map $project $revision"
5494 Msg Warning "Can not execute command $cmd"
5495 Msg Warning "LOG: $log"
5497 Msg Info "Pre flow script executed!"
5501 if {![is_project_open]} {
5502 Msg Info "Re-opening project file $project_name..."
5503 project_open $project -current_revision
5507 if {[
catch {execute_module -tool ipg -args "--clean"} result]} {
5508 Msg Error "Result: $result\n"
5509 Msg Error "IP Generation failed. See the report file.\n"
5511 Msg Info "IP Generation was successful for revision $revision.\n"
5515 if {[
catch {execute_module -tool map -args "--parallel"} result]} {
5516 Msg Error "Result: $result\n"
5517 Msg Error "Analysis & Synthesis failed. See the report file.\n"
5519 Msg Info "Analysis & Synthesis was successful for revision $revision.\n"
5523 defvar_set -name RWNETLIST_32_64_MIXED_FLOW -value 0
5525 Msg Info "Run SYNTHESIS..."
5526 if {[
catch {run_tool -name {SYNTHESIZE}}]} {
5527 Msg Error "SYNTHESIZE FAILED!"
5529 Msg Info "SYNTHESIZE PASSED!"
5535 set force_rst "-forceOne"
5537 prj_run Synthesis $force_rst
5538 if {[prj_syn] == "synplify"} {
5539 prj_run Translate $force_rst
5542 Msg Error "Impossible condition. You need to run this in an IDE."
5553 proc LaunchVitisBuild {project_name {repo_path .} {stage "presynth"}} {
5554 set proj_name $project_name
5555 set bin_dir [
file normalize "$repo_path/bin"]
5559 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
5560 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$proj_name] $repo_path] commit version hog_hash hog_ver top_hash top_ver \
5561 libs hashes vers cons_ver cons_hash ext_names ext_hashes xml_hash xml_ver user_ip_repos user_ip_hashes user_ip_vers
5563 if {$commit == 0 } {
set commit $this_commit}
5567 foreach app_name [dict keys $ws_apps] {
5568 app config -name $app_name -set build-config Release
5571 WriteGenerics "vitisbuild" $repo_path $proj_name $date $timee $commit $version $top_hash $top_ver $hog_hash $hog_ver $cons_ver $cons_hash $libs \
5572 $vers $hashes $ext_names $ext_hashes $user_ip_repos $user_ip_vers $user_ip_hashes $flavour $xml_ver $xml_hash
5573 foreach app_name [dict keys $ws_apps] { app build -name $app_name}
5575 if {$stage == "presynth"} {
5576 Msg Info "Done building apps for $project_name..."
5580 Msg Info "Evaluating Git sha for $project_name..."
5581 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5584 Msg Info "Hog describe set to: $describe"
5585 set dst_dir [
file normalize "$bin_dir/$proj_name\-$describe"]
5586 if {![
file exists $dst_dir]} {
5587 Msg Info "Creating $dst_dir..."
5591 foreach app_name [dict keys $ws_apps] {
5592 set main_file "$repo_path/Projects/$project_name/vitis_classic/$app_name/Release/$app_name.elf"
5593 set dst_main [
file normalize "$dst_dir/[
file tail $proj_name]\-$app_name\-$describe.elf"]
5595 if {![
file exists $main_file]} {
5596 Msg Error "No Vitis .elf file found. Perhaps there was an issue building it?"
5600 Msg Info "Copying main binary file $main_file into $dst_main..."
5601 file copy -force $main_file $dst_main
5610 proc GetProcFromProps {repo_path props platform} {
5611 if {[dict exists $props "platform:$platform" "BIF"]} {
5612 set bif_file [dict get $props "platform:$platform" "BIF"]
5614 set bif_file "$repo_path/$bif_file"
5618 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
5628 proc GetBifFromProps {repo_path props platform} {
5629 if {[dict exists $props "platform:$platform" "BIF"]} {
5630 set bif_file [dict get $props "platform:$platform" "BIF"]
5632 set bif_file "$repo_path/$bif_file"
5636 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
5645 proc GetPartFromProps {props} {
5646 if {[dict exists $props "main" "PART"]} {
5647 return [
string tolower [dict get $props "main" "PART"]]
5649 Msg Error "Part number not found in properties"
5658 proc GetArchFromPart {part} {
5660 if {[
string match "xczu*" $part]} {
5662 }
elseif {[
string match "xc7z*" $part]} {
5664 }
elseif {[
string match "xck26*" $part]} {
5667 Msg CriticalWarning "Unknown part number: $part"
5676 proc GetAppsFromProps {props {list_names 0}} {
5677 set prop_apps [dict filter $props key {app:*}]
5678 set apps [dict create]
5679 set app_names [list]
5681 dict for {app_key app_value} $prop_apps {
5682 if {[regexp {^app:(.+)$} $app_key -> app_name]} {
5683 set app_name [string trim [string tolower $app_name]]
5684 # Convert only the keys of the inner dictionary to lowercase
5685 set app_value_lower [dict create]
5686 dict for {key value} $app_value {
5687 dict set app_value_lower [string tolower $key] $value
5689 dict set apps $app_name $app_value_lower
5690 lappend app_names $app_name
5693 if {$list_names eq 1} {
5704 proc GetPlatformsFromProps {props {list_names 0} {lower_case 0}} {
5705 set platforms [dict create]
5706 set platform_names [list]
5707 set prop_platforms [dict filter $props key {platform:*}]
5709 dict for {platform_key platform_value} $prop_platforms {
5710 if {[regexp {^platform:(.+)$} $platform_key -> platform_name]} {
5711 if {$lower_case == 1} {
5712 set platform_name [string trim [string tolower $platform_name]]
5714 set platform_name [string trim $platform_name]
5716 dict set platforms $platform_name $platform_value
5717 lappend platform_names $platform_name
5720 if {$list_names eq 1} {
5721 return $platform_names
5736 proc GenerateBootArtifacts {properties repo_path proj_dir bin_dir proj_name describe bitfile mmi_file} {
5737 set elf_list [glob -nocomplain "$bin_dir/*.elf"]
5741 if {[
llength $elf_list] == 0} {
5742 Msg Warning "No ELF files found in $bin_dir, skipping generation of boot artifacts"
5746 if {![
file exists $bitfile]} {
5747 Msg Warning "Bitfile $bitfile does not exist, skipping generation of boot artifacts"
5751 Msg Info "Generating boot artifacts for $proj_name..."
5752 Msg Info "Found apps: $apps"
5753 Msg Info "Found platforms: $platforms"
5757 foreach elf_file $elf_list {
5758 set elf_name [
file rootname [
file tail $elf_file]]
5759 Msg Info "Found elf name: $elf_name"
5760 Msg Info "Removing $describe from elf"
5763 if {[regexp "^(.+)-(.+)-$describe\$" $elf_name -> project_name elf_app]} {
5764 set elf_app [
string trim [
string tolower $elf_app]]
5765 Msg Info "Found elf_app: $elf_app"
5767 Msg Error "Could not extract app name from elf file: $elf_name"
5770 Msg Info "Removed project name ($project_name) and $describe from elf"
5772 set app_conf [dict get $apps $elf_app]
5773 set plat [dict get $app_conf "platform"]
5774 set app_proc [dict get $app_conf "proc"]
5777 if {[regexp -nocase {microblaze|risc} $app_proc]} {
5778 Msg Info "Detected soft processor ($app_proc) for $elf_app, updating bitstream memory with ELF file..."
5781 if {[dict size $proc_map] == 0} {
5782 Msg Error "Failed to read map from $proc_map_file"
5785 Msg Info "Found processor map: $proc_map"
5787 set proc_cell [
lindex [
split [dict get $proc_map $app_proc] ":"] 1]
5788 Msg Info "Updating memory at processor cell: $proc_cell"
5790 set update_mem_cmd "updatemem -force -meminfo $mmi_file -data $elf_file -bit $bitfile -proc $proc_cell -out $bitfile"
5791 set ret [
catch {
exec -ignorestderr {*}$update_mem_cmd >@ stdout} result]
5793 Msg Error "Error updating memory for $elf_app: $result"
5795 Msg Info "Done updating memory for $elf_app"
5798 Msg Info "Detected hard processor ($app_proc) for $elf_app. Make sure the .elf file is defined in the platform ($plat)\
5799 .bif file to be included in the bootable binary image (.bin) generation."
5805 foreach plat $platforms {
5807 if {$bif_file != ""} {
5808 Msg Info "Generating bootable binary image (.bin) for $plat"
5810 Msg Info "Architecture: $arch"
5811 Msg Info "BIF file: $bif_file"
5812 set bootgen_cmd "bootgen -arch $arch -image $bif_file -o i $bin_dir/$proj_name-$plat-$describe.bin -w on"
5813 set ret [
catch {
exec -ignorestderr {*}$bootgen_cmd >@ stdout} result]
5815 Msg Error "Error generating bootable binary image (.bin) for $elf_app: $result"
5817 Msg Info "Done generating bootable binary image (.bin) for $plat"
5826 proc ReadProcMap {proc_map_file} {
5827 set proc_map [dict create]
5828 if {[
file exists $proc_map_file]} {
5829 set f [open $proc_map_file "r"]
5830 while {[
gets $f line] >= 0} {
5831 Msg Debug "Line: $line"
5832 if {[regexp {^(\S+)\s+(.+)$} $line -> key value]} {
5833 Msg Debug "Found key: $key, value: $value in proc map file"
5834 dict set proc_map $key $value
5848 proc ListProjects {{repo_path .} {print 1} {ret_conf 0}} {
5849 set top_path [
file normalize $repo_path/Top]
5850 set confs [
findFiles [
file normalize $top_path] hog.conf]
5852 set confs [lsort $confs]
5856 set p [
Relative $top_path [
file dirname $c]]
5859 if {$description eq "test"} {
5860 set description " - Test project"
5861 }
elseif {$description ne ""} {
5862 set description " - $description"
5865 if {$print == 1 || $description ne " - Test project"} {
5867 set g [
file dirname $p]
5878 if {$ret_conf == 0} {
5890 proc Md5Sum {file_name} {
5891 if {!([
file exists $file_name])} {
5892 Msg Warning "Could not find $file_name."
5895 if {[
catch {
package require md5 2.0.7} result]} {
5896 Msg Warning "Tcl package md5 version 2.0.7 not found ($result), will use command line..."
5897 set hash [
lindex [
Execute md5sum $file_name] 0]
5899 set file_hash [
string tolower [md5::md5 -hex -file $file_name]]
5913 proc MergeDict {dict0 dict1 {remove_duplicates 1}} {
5914 set outdict [dict merge $dict1 $dict0]
5915 foreach key [dict keys $dict1] {
5916 if {[dict exists $dict0 $key]} {
5917 set temp_list [dict get $dict1 $key]
5918 foreach item $temp_list {
5920 if {[
IsInList $item [
DictGet $outdict $key]] == 0 || $remove_duplicates == 0} {
5922 dict lappend outdict $key $item
5934 proc MoveElementToEnd {inputList element} {
5935 set index [lsearch $inputList $element]
5937 set inputList [
lreplace $inputList $index $index]
5938 lappend inputList $element
5947 proc OpenProject {project_file repo_path} {
5949 open_project $project_file
5951 set project_folder [
file dirname $project_file]
5952 set project [
file tail [
file rootname $project_file]]
5953 if {[
file exists $project_folder]} {
5955 if {![is_project_open]} {
5956 Msg Info "Opening existing project file $project_file..."
5957 project_open $project -current_revision
5960 Msg Error "Project directory not found for $project_file."
5964 Msg Info "Opening existing project file $project_file..."
5966 open_project -file $project_file -do_backup_on_convert 1 -backup_file {./Projects/$project_file.zip}
5968 Msg Info "Opening existing project file $project_file..."
5969 prj_project open $project_file
5971 Msg Error "This IDE is currently not supported by Hog. Exiting!"
5978 return $tcl_platform(platform)
5988 proc ParseJSON {JSON_FILE JSON_KEY} {
5989 set result [
catch {
package require Tcl 8.4} TclFound]
5990 if {"$result" != "0"} {
5991 Msg CriticalWarning "Cannot find Tcl package version equal or higher than 8.4.\n $TclFound\n Exiting"
5995 set result [
catch {
package require json} JsonFound]
5996 if {"$result" != "0"} {
5997 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
6000 set JsonDict [json::json2dict $JSON_FILE]
6001 set result [
catch {dict get $JsonDict $JSON_KEY} RETURNVALUE]
6002 if {"$result" != "0"} {
6003 Msg CriticalWarning "Cannot find $JSON_KEY in $JSON_FILE\n Exiting"
6016 proc ProjectExists {project {repo_path .}} {
6017 set index [lsearch -exact [
ListProjects $repo_path 0] $project]
6023 Msg Error "Project $project not found in repository $repo_path"
6034 proc ReadConf {file_name} {
6035 if {[
catch {
package require inifile 0.2.3} ERROR]} {
6036 Msg Error "Could not find inifile package version 0.2.3 or higher.\n
6037 To use ghdl, libero or diamond with Hog, you need to install the tcllib package\n
6038 You can install it with 'sudo apt install tcllib' on Debian/Ubuntu or 'sudo dnf install tcllib' on Fedora/RedHat/CentOs."
6042 ::ini::commentchar "#"
6043 set f [::ini::open $file_name]
6044 set properties [dict create]
6045 foreach sec [::ini::sections $f] {
6047 if {$new_sec == "files"} {
6050 set key_pairs [::ini::get $f $sec]
6052 regsub -all {\{\"} $key_pairs "\{" key_pairs
6053 regsub -all {\"\}} $key_pairs "\}" key_pairs
6055 dict set properties $new_sec [dict create {*}$key_pairs]
6068 proc ReadExtraFileList {extra_file_name} {
6069 set extra_file_dict [dict create]
6070 if {[
file exists $extra_file_name]} {
6071 set file [open $extra_file_name "r"]
6072 set file_data [read $file]
6075 set data [
split $file_data "\n"]
6076 foreach line $data {
6077 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
6078 set ip_and_md5 [regexp -all -inline {\S+} $line]
6079 dict lappend extra_file_dict "[
lindex $ip_and_md5 0]" "[
lindex $ip_and_md5 1]"
6083 return $extra_file_dict
6103 proc ReadListFile {args} {
6106 if {[
catch {
package require cmdline} ERROR]} {
6107 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
6113 {lib.arg "" "The name of the library files will be added to, if not given will be extracted from the file name."}
6114 {fileset.arg "" "The name of the library, from the main list file"}
6115 {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."}
6116 {print_log "If set, will use PrintFileTree for the VIEW directive"}
6117 {indent.arg "" "Used to indent files with the VIEW directive"}
6120 set usage "USAGE: ReadListFile \[options\] <list file> <path>"
6121 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
6127 set list_file [
lindex $args 0]
6128 set path [
lindex $args 1]
6129 set sha_mode $options(sha_mode)
6130 set lib $options(lib)
6131 set fileset $options(fileset)
6132 set print_log $options(print_log)
6133 set indent $options(indent)
6135 if {$sha_mode == 1} {
6136 set sha_mode_opt "-sha_mode"
6141 if {$print_log == 1} {
6142 set print_log_opt "-print_log"
6144 set print_log_opt ""
6149 set lib [
file rootname [
file tail $list_file]]
6151 set fp [open $list_file r]
6152 set file_data [read $fp]
6154 set list_file_ext [
file extension $list_file]
6155 switch $list_file_ext {
6157 if {$fileset eq ""} {
6163 set fileset "constrs_1"
6166 set fileset "sources_1"
6170 set libraries [dict create]
6171 set filesets [dict create]
6172 set properties [dict create]
6174 set data [
split $file_data "\n"]
6176 set n [
llength $data]
6178 if {$print_log == 1} {
6179 if {$indent eq ""} {
6180 set list_file_rel [
file tail $list_file]
6181 Msg Status "\n$list_file_rel"
6185 Msg Debug "$n lines read from $list_file."
6188 foreach line $data {
6190 if {![regexp {^[\t\s]*$} $line] & ![regexp {^[\t\s]*\#} $line]} {
6191 set file_and_prop [regexp -all -inline {\S+} $line]
6192 set srcfile [
lindex $file_and_prop 0]
6193 set srcfile "$path/$srcfile"
6195 set srcfiles [glob -nocomplain $srcfile]
6198 if {$srcfiles != $srcfile && ![
string equal $srcfiles ""]} {
6199 Msg Debug "Wildcard source expanded from $srcfile to $srcfiles"
6201 if {![
file exists $srcfile]} {
6202 if {$print_log == 0} {
6203 Msg CriticalWarning "File: $srcfile (from list file: $list_file) does not exist."
6209 foreach vhdlfile $srcfiles {
6210 if {[
file exists $vhdlfile]} {
6211 set vhdlfile [
file normalize $vhdlfile]
6212 set extension [
file extension $vhdlfile]
6214 set prop [
lrange $file_and_prop 1 end]
6217 set library [
lindex [regexp -inline {\ylib\s*=\s*(.+?)\y.*} $prop] 1]
6218 if {$library == ""} {
6222 if {$extension == $list_file_ext} {
6225 set ref_path [
lindex [regexp -inline {\ypath\s*=\s*(\S+).*} $prop] 1]
6226 if {$ref_path eq ""} {
6229 set ref_path [
file normalize $path/$ref_path]
6231 Msg Debug "List file $vhdlfile found in list file, recursively opening it using path \"$ref_path\"..."
6232 if {$print_log == 1} {
6233 if {[
file normalize $last_printed] ne [
file normalize $vhdlfile]} {
6234 Msg Status "$indent Inside [
file tail $vhdlfile]:"
6238 lassign [
ReadListFile {*}"-indent \" $indent\" -lib $library -fileset $fileset $sha_mode_opt $print_log_opt $vhdlfile $ref_path"] l p fs
6240 set properties [
MergeDict $p $properties]
6242 }
elseif {[lsearch {.src .sim .con ReadExtraFileList} $extension] >= 0} {
6244 Msg Error "$vhdlfile cannot be included into $list_file, $extension files must be included into $extension files."
6247 regsub -all " *= *" $prop "=" prop
6251 if {[
string first "lib=" $p] == -1} {
6253 set pos [
string first "=" $p]
6257 set prop_name [
string range $p 0 [
expr {$pos - 1}]]
6260 dict lappend properties $vhdlfile $p
6261 Msg Debug "Adding property $p to $vhdlfile..."
6262 }
elseif {$list_file_ext != ".ipb"} {
6263 Msg Warning "Setting Property $p is not supported for file $vhdlfile or it is already its default. \
6268 if {[lsearch {.xci .ip .bd .xcix} $extension] >= 0} {
6270 set lib_name "ips.src"
6271 }
elseif {[
IsInList $extension {.vhd .vhdl}] || $list_file_ext == ".sim"} {
6273 if {![
IsInList $extension {.vhd .vhdl}]} {
6274 set lib_name "others.sim"
6276 set lib_name "$library$list_file_ext"
6278 }
elseif {$list_file_ext == ".con"} {
6279 set lib_name "sources.con"
6280 }
elseif {$list_file_ext == ".ipb"} {
6281 set lib_name "xml.ipb"
6282 }
elseif { [
IsInList $list_file_ext {.src}] && [
IsInList $extension {.c .cpp .h .hpp}] } {
6284 set lib_name "$library$list_file_ext"
6287 set lib_name "others.src"
6290 Msg Debug "Appending $vhdlfile to $lib_name list..."
6291 dict lappend libraries $lib_name $vhdlfile
6292 if {$sha_mode != 0 && [
file type $vhdlfile] eq "link"} {
6295 dict lappend libraries $lib_name $real_file
6296 Msg Debug "File $vhdlfile is a soft link, also adding the real file: $real_file"
6301 if {[dict exists $filesets $fileset] == 0} {
6303 Msg Debug "Adding $fileset to the fileset dictionary..."
6304 Msg Debug "Adding library $lib_name to fileset $fileset..."
6305 dict set filesets $fileset $lib_name
6309 Msg Debug "Adding library $lib_name to fileset $fileset..."
6310 dict lappend filesets $fileset $lib_name
6316 Msg CriticalWarning "File $vhdlfile not found."
6322 if {$sha_mode != 0} {
6324 if {$list_file_ext eq ".ipb"} {
6325 set sha_lib "xml.ipb"
6327 set sha_lib $lib$list_file_ext
6329 dict lappend libraries $sha_lib [
file normalize $list_file]
6330 if {[
file type $list_file] eq "link"} {
6333 dict lappend libraries $lib$list_file_ext $real_file
6334 Msg Debug "List file $list_file is a soft link, also adding the real file: $real_file"
6337 return [list $libraries $properties $filesets]
6346 proc Relative {base dst {quiet 0}} {
6347 if {![
string equal [
file pathtype $base] [
file pathtype $dst]]} {
6349 Msg CriticalWarning "Unable to compute relation for paths of different path types: [
file pathtype $base] vs. [
file pathtype $dst], ($base vs. $dst)"
6354 set base [
file normalize [
file join [
pwd] $base]]
6355 set dst [
file normalize [
file join [
pwd] $dst]]
6358 set base [
file split $base]
6359 set dst [
file split $dst]
6361 while {[
string equal [
lindex $dst 0] [
lindex $base 0]]} {
6362 set dst [
lrange $dst 1 end]
6363 set base [
lrange $base 1 end]
6364 if {![
llength $dst]} {break}
6367 set dstlen [
llength $dst]
6368 set baselen [
llength $base]
6370 if {($dstlen == 0) && ($baselen == 0)} {
6373 while {$baselen > 0} {
6374 set dst [
linsert $dst 0 ..]
6377 set dst [
eval [
linsert $dst 0 file join]]
6388 proc RelativeLocal {pathName filePath} {
6389 if {[
string first [
file normalize $pathName] [
file normalize $filePath]] != -1} {
6390 return [
Relative $pathName $filePath]
6401 proc RemoveDuplicates {mydict} {
6402 set new_dict [dict create]
6403 foreach key [dict keys $mydict] {
6404 set values [
DictGet $mydict $key]
6405 foreach value $values {
6406 set idxs [
lreverse [
lreplace [lsearch -exact -all $values $value] 0 0]]
6408 set values [
lreplace $values $idx $idx]
6411 dict set new_dict $key $values
6422 proc ResetRepoFiles {reset_file} {
6423 if {[
file exists $reset_file]} {
6424 Msg Info "Found $reset_file, opening it..."
6425 set fp [open $reset_file r]
6426 set wild_cards [lsearch -all -inline -not -regexp [
split [read $fp] "\n"] "^ *$"]
6428 Msg Info "Found the following files/wild cards to restore if modified: $wild_cards..."
6429 foreach w $wild_cards {
6431 if {[
llength $mod_files] > 0} {
6432 Msg Info "Found modified $w files: $mod_files, will restore them..."
6435 Msg Info "No modified $w files found."
6446 proc RestoreModifiedFiles {{repo_path "."} {pattern "."}} {
6449 set ret [
Git checkout $pattern]
6460 proc SearchHogProjects {dir} {
6461 set projects_list {}
6462 if {[
file exists $dir]} {
6463 if {[
file isdirectory $dir]} {
6464 foreach proj_dir [glob -nocomplain -types d $dir/*] {
6465 if {![regexp {^.*Top/+(.*)$} $proj_dir dummy proj_name]} {
6466 Msg Warning "Could not parse Top directory $dir"
6469 if {[
file exists "$proj_dir/hog.conf"]} {
6470 lappend projects_list $proj_name
6473 lappend projects_list $p
6478 Msg Error "Input $dir is not a directory!"
6481 Msg Error "Directory $dir doesn't exist!"
6483 return $projects_list
6492 proc SetGenericsSimulation {repo_path proj_dir target} {
6493 set top_dir "$repo_path/Top/$proj_dir"
6494 set simsets [get_filesets]
6495 if {$simsets != ""} {
6496 foreach simset $simsets {
6498 if {[get_property FILESET_TYPE $simset] != "SimulationSrcs"} {
6502 set merged_generics_dict [dict create]
6506 set simset_generics [
DictGet $simset_dict "generics"]
6507 set merged_generics_dict [
MergeDict $merged_generics_dict $simset_generics 0]
6509 set_property generic $generic_str [get_filesets $simset]
6510 Msg Debug "Setting generics $generic_str for simulator $target\
6511 and simulation file-set $simset..."
6523 proc SetTopProperty {top_module fileset} {
6524 Msg Info "Setting TOP property to $top_module module"
6527 set_property "top" $top_module [get_filesets $fileset]
6530 set_global_assignment -name TOP_LEVEL_ENTITY $top_module
6532 set_root -module $top_module
6534 prj_impl option top $top_module
6539 proc VIVADO_PATH_PROPERTIES {} {
6540 return {"\.*\.TCL\.PRE$" "^.*\.TCL\.POST$" "^RQS_FILES$" "^INCREMENTAL\_CHECKPOINT$" "NOC\_SOLUTION\_FILE"}
6544 proc VITIS_PATH_PROPERTIES {} {
6545 return {"^HW$" "^XPFM$" "^LINKER-SCRIPT$" "^LIBRARIES$" "^LIBRARY-SEARCH-PATH$"}
6555 proc WriteConf {file_name config {comment ""}} {
6556 if {[
catch {
package require inifile 0.2.3} ERROR]} {
6557 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
6561 ::ini::commentchar "#"
6562 set f [::ini::open $file_name w]
6564 foreach sec [dict keys $config] {
6565 set section [dict get $config $sec]
6566 dict for {p v} $section {
6567 if {[string trim $v] == ""} {
6568 Msg Warning "Property $p has empty value. Skipping..."
6571 ::ini::set $f $sec $p $v
6576 if {![
string equal "$comment" ""]} {
6577 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $comment
6578 set hog_header "Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
6579 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $hog_header
6594 proc WriteGenerics {mode repo_path design date timee\
6595 commit version top_hash top_ver hog_hash hog_ver \
6596 cons_ver cons_hash libs vers hashes ext_names ext_hashes \
6597 user_ip_repos user_ip_vers user_ip_hashes flavour {xml_ver ""} {xml_hash ""}} {
6598 Msg Info "Passing parameters/generics to project's top module..."
6601 set generic_string [
concat \
6613 if {$xml_hash != "" && $xml_ver != ""} {
6614 lappend generic_string \
6619 foreach l $libs v $vers h $hashes {
6625 set ver [regsub -all {[\.-]} $ver {_}]
6626 set hash [regsub -all {[\.-]} $hash {_}]
6627 lappend generic_string "$ver" "$hash"
6630 foreach e $ext_names h $ext_hashes {
6632 lappend generic_string "$hash"
6635 foreach repo $user_ip_repos v $user_ip_vers h $user_ip_hashes {
6636 set repo_name [
file tail $repo]
6637 set ver "[
string toupper $repo_name]_VER=[
FormatGeneric $v]"
6638 set hash "[
string toupper $repo_name]_SHA=[
FormatGeneric $h]"
6639 set ver [regsub -all {[\.-]} $ver {_}]
6640 set hash [regsub -all {[\.-]} $hash {_}]
6641 lappend generic_string "$ver" "$hash"
6644 if {$flavour != -1} {
6645 lappend generic_string "FLAVOUR=$flavour"
6651 set generic_string "$prj_generics $generic_string"
6657 if {$mode == "create" || [
IsISE]} {
6660 if {[
file exists $top_file]} {
6663 Msg Debug "Found top level generics $generics in $top_file"
6665 set filtered_generic_string ""
6667 foreach generic_to_set [
split [
string trim $generic_string]] {
6668 set key [
lindex [
split $generic_to_set "="] 0]
6669 if {[dict exists $generics $key]} {
6670 Msg Debug "Hog generic $key found in $top_name"
6671 lappend filtered_generic_string "$generic_to_set"
6673 Msg Warning "Generic $key is passed by Hog but is NOT present in $top_name."
6679 set generic_string $filtered_generic_string
6684 set_property generic $generic_string [current_fileset]
6685 Msg Info "Setting parameters/generics..."
6686 Msg Debug "Detailed parameters/generics: $generic_string"
6691 set simulator [get_property target_simulator [current_project]]
6692 if {$mode == "create"} {
6699 Msg Info "Setting Synplify parameters/generics one by one..."
6700 foreach generic $generic_string {
6701 Msg Debug "Setting Synplify generic: $generic"
6702 set_option -hdl_param -set "$generic"
6705 Msg Info "Setting Diamond parameters/generics one by one..."
6706 prj_impl option -impl Implementation0 HDL_PARAM "$generic_string"
6708 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
6710 foreach app_name [dict keys $ws_apps] {
6711 set defined_symbols [app config -name $app_name -get define-compiler-symbols]
6712 foreach generic_to_set [
split [
string trim $generic_string]] {
6713 set key [
lindex [
split $generic_to_set "="] 0]
6714 set value [
lindex [
split $generic_to_set "="] 1]
6715 if {[
string match "32'h*" $value]} {
6716 set value [
string map {"32'h" "0x"} $value]
6719 foreach symbol [
split $defined_symbols ";"] {
6720 if {[
string match "$key=*" $symbol]} {
6721 Msg Debug "Generic $key found in $app_name, removing it..."
6722 app config -name $app_name -remove define-compiler-symbols "$symbol"
6726 Msg Info "Setting Vitis parameters/generics for app $app_name: $key=$value"
6727 app config -name $app_name define-compiler-symbols "$key=$value"
6739 proc WriteGenericsToBdIPs {mode repo_path proj generic_string} {
6740 Msg Debug "Parameters/generics passed to WriteGenericsToIP: $generic_string"
6742 set bd_ip_generics false
6744 if {[dict exists $properties "hog"]} {
6745 set propDict [dict get $properties "hog"]
6746 if {[dict exists $propDict "PASS_GENERICS_TO_BD_IPS"]} {
6747 set bd_ip_generics [dict get $propDict "PASS_GENERICS_TO_BD_IPS"]
6751 if {[
string compare [
string tolower $bd_ip_generics] "false"] == 0} {
6755 if {$mode == "synth"} {
6756 Msg Info "Attempting to apply generics pre-synthesis..."
6757 set PARENT_PRJ [get_property "PARENT.PROJECT_PATH" [current_project]]
6758 set workaround [open "$repo_path/Projects/$proj/.hog/presynth_workaround.tcl" "w"]
6759 puts $workaround "source \[lindex \$argv 0\];"
6760 puts $workaround "open_project \[lindex \$argv 1\];"
6761 puts $workaround "WriteGenericsToBdIPs \[lindex \$argv 2\] \[lindex \$argv 3\] \[lindex \$argv 4\] \[lindex \$argv 5\];"
6762 puts $workaround "close_project"
6766 exec vivado -mode batch -source $repo_path/Projects/$proj/.hog/presynth_workaround.tcl \
6767 -tclargs $repo_path/Hog/Tcl/hog.tcl $PARENT_PRJ \
6768 "childprocess" $repo_path $proj $generic_string
6771 Msg Error "Encountered an error while attempting workaround: $errMsg"
6773 file delete $repo_path/Projects/$proj/.hog/presynth_workaround.tcl
6775 Msg Info "Done applying generics pre-synthesis."
6779 Msg Info "Looking for IPs to add generics to..."
6780 set ips_generic_string ""
6781 foreach generic_to_set [
split [
string trim $generic_string]] {
6782 set key [
lindex [
split $generic_to_set "="] 0]
6783 set value [
lindex [
split $generic_to_set "="] 1]
6784 append ips_generic_string "CONFIG.$key $value "
6788 if {[
string compare [
string tolower $bd_ip_generics] "true"] == 0} {
6791 set ip_regex $bd_ip_generics
6794 set ip_list [get_ips -regex $ip_regex]
6795 Msg Debug "IPs found with regex \{$ip_regex\}: $ip_list"
6797 set regen_targets {}
6799 foreach {ip} $ip_list {
6800 set WARN_ABOUT_IP false
6801 set ip_props [list_property [get_ips $ip]]
6804 if {[lsearch -exact $ip_props "IS_BD_CONTEXT"] == -1} {
6808 if {[get_property "IS_BD_CONTEXT" [get_ips $ip]] eq "1"} {
6809 foreach {ip_prop} $ip_props {
6810 if {[dict exists $ips_generic_string $ip_prop]} {
6811 if {$WARN_ABOUT_IP == false} {
6812 lappend regen_targets [get_property SCOPE [get_ips $ip]]
6813 Msg Warning "The ip \{$ip\} contains generics that are set by Hog.\
6814 If this is IP is apart of a block design, the .bd file may contain stale, unused, values.\
6815 Hog will always apply the most up-to-date values to the IP during synthesis,\
6816 however these values may or may not be reflected in the .bd file."
6817 set WARN_ABOUT_IP true
6822 set xci_path [get_property IP_FILE [get_ips $ip]]
6824 if {[
string equal $generic_format "ERROR"]} {
6825 Msg Warning "Could not find format for generic $ip_prop in IP $ip. Skipping..."
6829 set value_to_set [dict get $ips_generic_string $ip_prop]
6830 switch -exact $generic_format {
6832 if {[
string match "32'h*" $value_to_set]} {
6833 scan [
string map {"32'h" ""} $value_to_set] "%x" value_to_set
6837 set value_to_set [
expr {$value_to_set ? "true" : "false"}]
6840 if {[
string match "32'h*" $value_to_set]} {
6841 binary scan [
binary format H* [
string map {"32'h" ""} $value_to_set]] d value_to_set
6845 if {[
string match "32'h*" $value_to_set]} {
6846 set value_to_set [
string map {"32'h" "0x"} $value_to_set]
6850 set value_to_set [
format "%s" $value_to_set]
6853 Msg Warning "Unknown generic format $generic_format for IP $ip. Will attempt to pass as string..."
6858 Msg Info "The IP \{$ip\} contains: $ip_prop ($generic_format), setting it to $value_to_set."
6859 if {[
catch {set_property -name $ip_prop -value $value_to_set -objects [get_ips $ip]} prop_error]} {
6860 Msg CriticalWarning "Failed to set property $ip_prop to $value_to_set for IP \{$ip\}: $prop_error"
6867 foreach {regen_target} [lsort -unique $regen_targets] {
6868 Msg Info "Regenerating target: $regen_target"
6869 if {[
catch {generate_target -force all [get_files $regen_target]} prop_error]} {
6870 Msg CriticalWarning "Failed to regen targets: $prop_error"
6878 proc GetGenericFormatFromXciXML {generic_name xml_file} {
6879 if {![
file exists $xml_file]} {
6880 Msg Error "Could not find XML file: $xml_file"
6884 set fp [open $xml_file r]
6885 set xci_data [read $fp]
6888 set paramType "string"
6889 set modelparam_regex [
format {^.*\y%s\y.*$} [
string map {"CONFIG." "MODELPARAM_VALUE."} $generic_name]]
6890 set format_regex {format="([^"]+)"}
6892 set line [
lindex [regexp -inline -line $modelparam_regex $xci_data] 0]
6893 Msg Debug "line: $line"
6895 if {[regexp $format_regex $line match format_value]} {
6896 Msg Debug "Extracted: $format_value format from xml"
6897 set paramType $format_value
6899 Msg Debug "No format found, using string"
6908 proc GetGenericFormatFromXci {generic_name xci_file} {
6909 if {![
file exists $xci_file]} {
6910 Msg Error "Could not find XCI file: $xci_file"
6914 set fp [open $xci_file r]
6915 set xci_data [read $fp]
6918 set paramType "string"
6919 if {[
string first "xilinx.com:schema:json_instance:1.0" $xci_data] == -1} {
6920 Msg Debug "XCI format is not JSON, trying XML..."
6921 set xml_file "[
file rootname $xci_file].xml"
6925 set generic_name [
string map {"CONFIG." ""} $generic_name]
6926 set ip_inst [
ParseJSON $xci_data "ip_inst"]
6927 set parameters [dict get $ip_inst parameters]
6928 set component_parameters [dict get $parameters component_parameters]
6929 if {[dict exists $component_parameters $generic_name]} {
6930 set generic_info [dict get $component_parameters $generic_name]
6931 if {[dict exists [
lindex $generic_info 0] format]} {
6932 set paramType [dict get [
lindex $generic_info 0] format]
6933 Msg Debug "Extracted: $paramType format from xci"
6936 Msg Debug "No format found, using string"
6949 proc WriteGitLabCIYAML {proj_name {ci_conf ""}} {
6950 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
6951 Msg CriticalWarning "Cannot find package YAML.\n Error message: $YAMLPACKAGE. \
6952 If you are running on tclsh, you can fix this by installing package \"tcllib\""
6957 if {$ci_conf != ""} {
6959 foreach sec [dict keys $ci_confs] {
6960 if {[
string first : $sec] == -1} {
6961 lappend job_list $sec
6965 set job_list {"generate_project" "simulate_project"}
6969 set out_yaml [huddle create]
6970 foreach job $job_list {
6972 set huddle_tags [huddle list]
6974 set sec_dict [dict create]
6976 if {$ci_confs != ""} {
6977 foreach var [dict keys [dict get $ci_confs $job]] {
6978 if {$var == "tags"} {
6979 set tag_section "tags"
6980 set tags [dict get [dict get $ci_confs $job] $var]
6981 set tags [
split $tags ","]
6983 set tag_list [huddle list $tag]
6984 set huddle_tags [huddle combine $huddle_tags $tag_list]
6987 dict set sec_dict $var [dict get [dict get $ci_confs $job] $var]
6993 set huddle_variables [huddle create "PROJECT_NAME" $proj_name "extends" ".vars"]
6994 if {[dict exists $ci_confs "$job:variables"]} {
6995 set var_dict [dict get $ci_confs $job:variables]
6996 foreach var [dict keys $var_dict] {
6998 set value [dict get $var_dict "$var"]
6999 set var_inner [huddle create "$var" "$value"]
7000 set huddle_variables [huddle combine $huddle_variables $var_inner]
7005 set middle [huddle create "extends" ".$job" "variables" $huddle_variables]
7006 foreach sec [dict keys $sec_dict] {
7007 set value [dict get $sec_dict $sec]
7008 set var_inner [huddle create "$sec" "$value"]
7009 set middle [huddle combine $middle $var_inner]
7011 if {$tag_section != ""} {
7012 set middle2 [huddle create "$tag_section" $huddle_tags]
7013 set middle [huddle combine $middle $middle2]
7016 set outer [huddle create "$job:$proj_name" $middle]
7017 set out_yaml [huddle combine $out_yaml $outer]
7020 return [
string trimleft [yaml::huddle2yaml $out_yaml] "-"]
7030 proc WriteListFiles {libs props list_path repo_path {ext_path ""}} {
7032 foreach lib [dict keys $libs] {
7033 if {[
llength [
DictGet $libs $lib]] > 0} {
7034 set list_file_name $list_path$lib
7035 set list_file [open $list_file_name w]
7036 Msg Info "Writing $list_file_name..."
7037 puts $list_file "#Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
7038 foreach file [
DictGet $libs $lib] {
7040 set prop [
DictGet $props $file]
7044 puts $list_file "$file_path $prop"
7047 set ext_list_file [open "[
file rootname $list_file].ext" a]
7048 puts $ext_list_file "$file_path $prop"
7049 close $ext_list_file
7052 Msg Warning "The path of file $file is not relative to your repository. Please check!"
7068 proc WriteSimListFile {simset libs props simsets list_path repo_path {force 0}} {
7070 set list_file_name $list_path/${simset}.sim
7071 if {$force == 0 && [
file exists $list_file_name]} {
7072 Msg Info "List file $list_file_name already exists, skipping..."
7076 set list_file [open $list_file_name a+]
7079 puts $list_file "\[files\]"
7080 Msg Info "Writing $list_file_name..."
7081 foreach lib [
DictGet $simsets $simset] {
7082 foreach file [
DictGet $libs $lib] {
7084 set prop [
DictGet $props $file]
7088 set lib_name [
file rootname $lib]
7089 if {$lib_name != $simset && [
file extension $file] == ".vhd" && [
file extension $file] == ""} {
7090 lappend prop "lib=$lib_name"
7092 puts $list_file "$file_path $prop"
7095 Msg Warning "The path of file $file is not relative to your repository. Please check!"
7107 proc WriteToFile {File msg} {
7108 set f [open $File a+]
7119 proc WriteUtilizationSummary {input output project_name run} {
7120 set f [open $input "r"]
7121 set o [open $output "a"]
7122 puts $o "## $project_name $run Utilization report\n\n"
7123 struct::matrix util_m
7124 util_m add columns 14
7127 util_m add row "| **Site Type** | **Used** | **Fixed** | **Prohibited** | **Available** | **Util%** |"
7128 util_m add row "| --- | --- | --- | --- | --- | --- |"
7130 util_m add row "| **Site Type** | **Used** | **Fixed** | **Available** | **Util%** |"
7131 util_m add row "| --- | --- | --- | --- | --- |"
7141 while {[
gets $f line] >= 0} {
7142 if {([
string first "| CLB LUTs" $line] >= 0 || [
string first "| Slice LUTs" $line] >= 0) && $luts == 0} {
7143 util_m add row $line
7146 if {([
string first "| CLB Registers" $line] >= 0 || [
string first "| Slice Registers" $line] >= 0) && $regs == 0} {
7147 util_m add row $line
7150 if {[
string first "| Block RAM Tile" $line] >= 0 && $bram == 0} {
7151 util_m add row $line
7154 if {[
string first "URAM " $line] >= 0 && $uram == 0} {
7155 util_m add row $line
7158 if {[
string first "DSPs" $line] >= 0 && $dsps == 0} {
7159 util_m add row $line
7162 if {[
string first "Bonded IOB" $line] >= 0 && $ios == 0} {
7163 util_m add row $line
7170 puts $o [util_m format 2string]
7176 Msg Error "Found Git version older than 2.7.2. Hog will not work as expected, exiting now."
7186 if {![
catch {
exec curl --silent --show-error --version}]} {
7187 if {![
catch {
exec curl --silent --show-error -I https://gitlab.com}]} {
7188 return [list curl --silent --show-error]
7192 if {![
catch {
exec env -u LD_LIBRARY_PATH curl --silent --show-error --version}]} {
7193 if {![
catch {
exec env -u LD_LIBRARY_PATH curl --silent --show-error -I https://gitlab.com}]} {
7194 return [list env -u LD_LIBRARY_PATH curl --silent --show-error]
7198 error "Cannot find a working curl invocation"