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" "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 ""}} {
796 Msg Info "Will check also the version of the simulation files..."
800 if {[
info exists env(HOG_PUSH_TOKEN)] && [
info exist env(CI_PROJECT_ID)] && [
info exist env(CI_API_V4_URL)] } {
801 set token $env(HOG_PUSH_TOKEN)
802 set api_url $env(CI_API_V4_URL)
803 set project_id $env(CI_PROJECT_ID)
808 set project_dir $repo_path/Top/$project
811 Msg Info "$project was modified, continuing with the CI..."
813 Msg Info "Checking if the project has been already built in a previous CI run..."
815 if {$sha == [
GetSHA $repo_path]} {
816 Msg Info "Project was modified in the current commit, Hog will proceed with the build workflow."
819 Msg Info "Checking if project $project has been built in a previous CI run with sha $sha..."
820 set result [
catch {
package require json} JsonFound]
821 if {"$result" != "0"} {
822 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
825 lassign [
ExecuteRet curl --header "PRIVATE-TOKEN: $token" "$api_url/projects/$project_id/pipelines"] ret content
826 set pipeline_dict [json::json2dict $content]
827 if {[
llength $pipeline_dict] > 0} {
828 foreach pip $pipeline_dict {
830 set source [
DictGet $pip source]
831 if {$source == "merge_request_event" && [
string first $sha $pip_sha] != -1} {
832 Msg Info "Found pipeline with sha $pip_sha for project $project"
833 set pipeline_id [
DictGet $pip id]
835 lassign [
ExecuteRet curl --header "PRIVATE-TOKEN: $token" "$api_url/projects/${project_id}/pipelines/${pipeline_id}/jobs?pagination=keyset&per_page=100"] ret2 content2
836 set jobs_dict [json::json2dict $content2]
837 if {[
llength $jobs_dict] > 0} {
838 foreach job $jobs_dict {
839 set job_name [
DictGet $job name]
841 set artifacts [
DictGet $job artifacts_file]
842 set status [
DictGet $job status]
843 set current_job_name $env(CI_JOB_NAME)
844 if {$current_job_name == $job_name && $status == "success"} {
846 lassign [
ExecuteRet curl --location --output artifacts.zip --header "PRIVATE-TOKEN: $token" --url "$api_url/projects/$project_id/jobs/$job_id/artifacts"] ret3 content3
848 Msg CriticalWarning "Cannot download artifacts for job $job_name with id $job_id"
851 lassign [
ExecuteRet unzip -o $repo_path/artifacts.zip] ret_zip
855 Msg Info "Artifacts for job $job_name with id $job_id downloaded and unzipped."
856 file mkdir $repo_path/Projects/$project
857 set fp [open "$repo_path/Projects/$project/skip.me" w+]
869 }
elseif {$ver != -1} {
870 Msg Info "$project was not modified since version: $ver, disabling the CI..."
871 file mkdir $repo_path/Projects/$project
872 set fp [open "$repo_path/Projects/$project/skip.me" w+]
876 Msg Error "Impossible to check the project version. Most likely the repository is not clean. Please, commit your changes before running this command."
886 proc CheckSyntax {project_name repo_path {project_file ""}} {
889 set syntax [check_syntax -return_string]
890 if {[
string first "CRITICAL" $syntax] != -1} {
895 lassign [
GetHogFiles -list_files "*.src" "$repo_path/Top/$project_name/list/" $repo_path] src_files dummy
896 dict for {lib files} $src_files {
898 set file_extension [file extension $f]
899 if {$file_extension == ".vhd" || $file_extension == ".vhdl" || $file_extension == ".v" || $file_extension == ".sv"} {
900 if {[catch {execute_module -tool map -args "--analyze_file=$f"} result]} {
901 Msg Error "\nResult: $result\n"
902 Msg Error "Check syntax failed.\n"
905 Msg Info "Check syntax was successful for $f.\n"
907 Msg Warning "Found syntax error in file $f:\n $result\n"
914 lassign [
GetProjectFiles $project_file] prjLibraries prjProperties prjSimLibraries prjConstraints prjSrcSets prjSimSets prjConSets
915 dict for {lib sources} $prjLibraries {
916 if {[file extension $lib] == ".src"} {
918 Msg Info "Checking Syntax of $f"
924 Msg Info "The Checking Syntax is not supported by this IDE. Skipping..."
929 proc CloseProject {} {
951 proc CompareVersions {ver1 ver2} {
960 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver1 - x y z]} {
961 set ver1 [list $x $y $z]
963 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver2 - x y z]} {
964 set ver2 [list $x $y $z]
968 set v1 [
join $ver1 ""]
970 set v2 [
join $ver2 ""]
973 if {[
string is integer $v1] && [
string is integer $v2]} {
974 set ver1 [
expr {[scan [lindex $ver1 0] %d] * 1000000 + [scan [lindex $ver1 1] %d] * 1000 + [scan [lindex $ver1 2] %d]}]
975 set ver2 [
expr {[scan [lindex $ver2 0] %d] * 1000000 + [scan [lindex $ver2 1] %d] * 1000 + [scan [lindex $ver2 2] %d]}]
979 }
elseif {$ver1 == $ver2} {
985 Msg Warning "Version is not numeric: $ver1, $ver2"
995 proc CheckExtraFiles {libraries constraints simlibraries} {
998 lassign [
GetProjectFiles] prjLibraries prjProperties prjSimLibraries prjConstraints
999 set prj_dir [get_property DIRECTORY [current_project]]
1000 file mkdir "$prj_dir/.hog"
1001 set extra_file_name "$prj_dir/.hog/extra.files"
1002 set new_extra_file [open $extra_file_name "w"]
1004 dict for {prjLib prjFiles} $prjLibraries {
1005 foreach prjFile $prjFiles {
1006 if {[file extension $prjFile] == ".xcix"} {
1007 Msg Warning "IP $prjFile is packed in a .xcix core container. \
1008 This files are not suitable for version control systems. We recommend to use .xci files instead."
1011 if {[file extension $prjFile] == ".xci" && [get_property CORE_CONTAINER [get_files $prjFile]] != ""} {
1012 Msg Info "$prjFile is a virtual IP file in a core container. Ignoring it..."
1016 if {[IsInList $prjFile [DictGet $libraries $prjLib]] == 0} {
1017 if {[file extension $prjFile] == ".bd"} {
1018 # Generating BD products to save md5sum of already modified BD
1019 Msg Info "Generating targets of $prjFile..."
1020 generate_target all [get_files $prjFile]
1022 puts $new_extra_file "$prjFile [Md5Sum $prjFile]"
1023 Msg Info "$prjFile (lib: $prjLib) has been generated by an external script. Adding to $extra_file_name..."
1027 close $new_extra_file
1028 set extra_sim_file "$prj_dir/.hog/extrasim.files"
1029 set new_extra_file [open $extra_sim_file "w"]
1031 dict for {prjSimLib prjSimFiles} $prjSimLibraries {
1032 foreach prjSimFile $prjSimFiles {
1033 if {[IsInList $prjSimFile [DictGet $simlibraries $prjSimLib]] == 0} {
1034 puts $new_extra_file "$prjSimFile [Md5Sum $prjSimFile]"
1035 Msg Info "$prjSimFile (lib: $prjSimLib) has been generated by an external script. Adding to $extra_sim_file..."
1039 close $new_extra_file
1040 set extra_con_file "$prj_dir/.hog/extracon.files"
1041 set new_extra_file [open $extra_con_file "w"]
1043 dict for {prjConLib prjConFiles} $prjConstraints {
1044 foreach prjConFile $prjConFiles {
1045 if {[IsInList $prjConFile [DictGet $constraints $prjConLib]] == 0} {
1046 puts $new_extra_file "$prjConFile [Md5Sum $prjConFile]"
1047 Msg Info "$prjConFile has been generated by an external script. Adding to $extra_con_file..."
1051 close $new_extra_file
1058 proc CheckLatestHogRelease {{repo_path .}} {
1061 set current_ver [
Git {describe --always}]
1062 Msg Debug "Current version: $current_ver"
1063 set current_sha [
Git "log $current_ver -1 --format=format:%H"]
1064 Msg Debug "Current SHA: $current_sha"
1067 if {[
OS] == "windows"} {
1068 Msg Info "On windows we cannot set a timeout on 'git fetch', hopefully nothing will go wrong..."
1071 Msg Info "Checking for latest Hog release, can take up to 5 seconds..."
1074 set master_ver [
Git "describe origin/master"]
1075 Msg Debug "Master version: $master_ver"
1076 set master_sha [
Git "log $master_ver -1 --format=format:%H"]
1077 Msg Debug "Master SHA: $master_sha"
1078 set merge_base [
Git "merge-base $current_sha $master_sha"]
1079 Msg Debug "merge base: $merge_base"
1082 if {$merge_base != $master_sha} {
1084 Msg Info "Version $master_ver has been released (https://gitlab.com/hog-cern/Hog/-/releases/$master_ver)"
1085 Msg Status "You should consider updating Hog submodule with the following instructions:"
1087 Msg Status "cd Hog && git checkout master && git pull"
1089 Msg Status "Also update the ref: in your .gitlab-ci.yml to $master_ver"
1093 Msg Info "Latest official version is $master_ver, nothing to do."
1105 proc CheckYmlRef {repo_path allow_failure} {
1106 if {$allow_failure} {
1107 set MSG_TYPE CriticalWarning
1112 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
1113 Msg CriticalWarning "Cannot find package YAML, skipping consistency check of \"ref\" in gilab-ci.yaml file.\n Error message: $YAMLPACKAGE
1114 You can fix this by installing package \"tcllib\""
1122 if {[
file exists .gitlab-ci.yml]} {
1126 if {[
file exists .gitlab-ci.yml]} {
1127 set fp [open ".gitlab-ci.yml" r]
1128 set file_data [read $fp]
1131 Msg $MSG_TYPE "Cannot open file .gitlab-ci.yml"
1135 set file_data "\n$file_data\n\n"
1137 if {[
catch {::yaml::yaml2dict -stream $file_data} yamlDict]} {
1138 Msg $MSG_TYPE "Parsing $repo_path/.gitlab-ci.yml failed. To fix this, check that yaml syntax is respected, remember not to use tabs."
1142 dict for {dictKey dictValue} $yamlDict {
1143 #looking for Hog include in .gitlab-ci.yml
1144 if {"$dictKey" == "include" && (
1145 [lsearch [split $dictValue " {}"] "/hog.yml"] != "-1" ||
1146 [lsearch [split $dictValue " {}"] "/hog-dynamic.yml"] != "-1"
1148 set YML_REF [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "ref"] + 1}]]
1149 set YML_NAME [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "file"] + 1}]]
1153 if {$YML_REF == ""} {
1154 Msg Warning "Hog version not specified in the .gitlab-ci.yml. Assuming that master branch is used."
1156 set YML_REF_F [
Git {name-rev --tags --name-only origin/master}]
1159 set YML_REF_F [regsub -all "'" $YML_REF ""]
1162 if {$YML_NAME == ""} {
1163 Msg $MSG_TYPE "Hog included yml file not specified, assuming hog.yml"
1164 set YML_NAME_F hog.yml
1166 set YML_NAME_F [regsub -all "^/" $YML_NAME ""]
1169 lappend YML_FILES $YML_NAME_F
1175 if {[
catch {::yaml::yaml2dict -file $YML_NAME_F} yamlDict]} {
1176 Msg $MSG_TYPE "Parsing $YML_NAME_F failed."
1180 dict for {dictKey dictValue} $yamlDict {
1181 #looking for included files
1182 if {"$dictKey" == "include"} {
1183 foreach v $dictValue {
1184 lappend YML_FILES [lindex [split $v " "] [expr {[lsearch -dictionary [split $v " "] "local"] + 1}]]
1190 Msg Info "Found the following yml files: $YML_FILES"
1192 set HOGYML_SHA [
GetSHA $YML_FILES]
1193 lassign [
GitRet "log --format=%h -1 --abbrev=7 $YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1195 lassign [
GitRet "log --format=%h -1 --abbrev=7 origin/$YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1197 Msg $MSG_TYPE "Error in project .gitlab-ci.yml. ref: $YML_REF not found"
1198 set EXPECTEDYML_SHA ""
1201 if {!($EXPECTEDYML_SHA eq "")} {
1202 if {$HOGYML_SHA == $EXPECTEDYML_SHA} {
1203 Msg Info "Hog included file $YML_FILES matches with $YML_REF in .gitlab-ci.yml."
1205 Msg $MSG_TYPE "HOG $YML_FILES SHA mismatch.
1206 From Hog submodule: $HOGYML_SHA
1207 From ref in .gitlab-ci.yml: $EXPECTEDYML_SHA
1208 You can fix this in 2 ways: by changing the ref in your repository or by changing the Hog submodule commit"
1211 Msg $MSG_TYPE "One or more of the following files could not be found $YML_FILES in Hog at $YML_REF"
1214 Msg Info ".gitlab-ci.yml not found in $repo_path. Skipping this step"
1235 proc CompareLibDicts {proj_libs list_libs proj_sets list_sets proj_props list_props {severity "CriticalWarning"} {outFile ""} {extraFiles ""}} {
1236 set extra_files $extraFiles
1238 set out_prjlibs $proj_libs
1239 set out_prjprops $proj_props
1241 dict for {prjSet prjLibraries} $proj_sets {
1242 # Check if sets is also in list files
1243 if {[IsInList $prjSet $list_sets]} {
1244 set listLibraries [DictGet $list_sets $prjSet]
1245 # Loop over libraries in fileset
1246 foreach prjLib $prjLibraries {
1247 set prjFiles [DictGet $proj_libs $prjLib]
1248 # Check if library exists in list files
1249 if {[IsInList $prjLib $listLibraries]} {
1250 # Loop over files in library
1251 set listFiles [DictGet $list_libs $prjLib]
1252 foreach prjFile $prjFiles {
1253 set idx [lsearch -exact $listFiles $prjFile]
1254 set listFiles [lreplace $listFiles $idx $idx]
1256 # File is in project but not in list libraries, check if it was generated at creation time...
1257 if {[dict exists $extra_files $prjFile]} {
1258 # File was generated at creation time, checking the md5sum
1259 # Removing the file from the prjFiles list
1260 set idx2 [lsearch -exact $prjFiles $prjFile]
1261 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1262 set new_md5sum [Md5Sum $prjFile]
1263 set old_md5sum [DictGet $extra_files $prjFile]
1264 if {$new_md5sum != $old_md5sum} {
1265 # tclint-disable-next-line line-length
1266 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
1269 set extra_files [dict remove $extra_files $prjFile]
1271 # File is neither in list files nor in extra_files
1272 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1276 # File is both in list files and project, checking properties...
1277 set prjProps [DictGet $proj_props $prjFile]
1278 set listProps [DictGet $list_props $prjFile]
1279 # Check if it is a potential sourced file
1280 if {[IsInList "nosynth" $prjProps] && [IsInList "noimpl" $prjProps] && [IsInList "nosim" $prjProps]} {
1281 # Check if it is sourced
1282 set idx_source [lsearch -exact $listProps "source"]
1283 if {$idx_source >= 0} {
1284 # It is sourced, let's replace the individual properties with source
1285 set idx [lsearch -exact $prjProps "noimpl"]
1286 set prjProps [lreplace $prjProps $idx $idx]
1287 set idx [lsearch -exact $prjProps "nosynth"]
1288 set prjProps [lreplace $prjProps $idx $idx]
1289 set idx [lsearch -exact $prjProps "nosim"]
1290 set prjProps [lreplace $prjProps $idx $idx]
1291 lappend prjProps "source"
1295 foreach prjProp $prjProps {
1296 set idx [lsearch -exact $listProps $prjProp]
1297 set listProps [lreplace $listProps $idx $idx]
1299 MsgAndLog "Property $prjProp of $prjFile was set in project but not in list files" $severity $outFile
1304 foreach listProp $listProps {
1305 if {[string first $listProp "topsim="] == -1 && [string first $listProp "enable"] == -1} {
1306 MsgAndLog "Property $listProp of $prjFile was found in list files but not set in project." $severity $outFile
1311 # Update project prjProps
1312 dict set out_prjprops $prjFile $prjProps
1315 # Loop over remaining files in list libraries
1316 foreach listFile $listFiles {
1317 MsgAndLog "$listFile was found in list files but not in project." $severity $outFile
1321 # Check extra files again...
1322 foreach prjFile $prjFiles {
1323 if {[dict exists $extra_files $prjFile]} {
1324 # File was generated at creation time, checking the md5sum
1325 # Removing the file from the prjFiles list
1326 set idx2 [lsearch -exact $prjFiles $prjFile]
1327 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1328 set new_md5sum [Md5Sum $prjFile]
1329 set old_md5sum [DictGet $extra_files $prjFile]
1330 if {$new_md5sum != $old_md5sum} {
1331 # tclint-disable-next-line line-length
1332 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
1335 set extra_files [dict remove $extra_files $prjFile]
1337 # File is neither in list files nor in extra_files
1338 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1343 # Update prjLibraries
1344 dict set out_prjlibs $prjLib $prjFiles
1347 MsgAndLog "Fileset $prjSet found in project but not in list files" $severity $outFile
1352 return [list $n_diffs $extra_files $out_prjlibs $out_prjprops]
1362 proc CompareVHDL {file1 file2} {
1363 set a [open $file1 r]
1364 set b [open $file2 r]
1366 while {[
gets $a line] != -1} {
1367 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1368 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1374 while {[
gets $b line] != -1} {
1375 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1376 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1385 foreach x $f1 y $f2 {
1387 lappend diff "> $x\n< $y\n\n"
1400 proc Copy {i_dirs o_dir} {
1401 foreach i_dir $i_dirs {
1402 if {[
file isdirectory $i_dir] && [
file isdirectory $o_dir]} {
1403 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]])} {
1404 file delete -force $o_dir/[
file tail $i_dir]
1408 file copy -force $i_dir $o_dir
1423 proc CopyIPbusXMLs {proj_dir path dst {xml_version "0.0.0"} {xml_sha "00000000"} {use_ipbus_sw 0} {generate 0}} {
1424 if {$use_ipbus_sw == 1} {
1425 lassign [
ExecuteRet python3 -c "from __future__ import print_function; from sys import path;print(':'.join(path\[1:\]))"] ret msg
1427 set ::env(PYTHONPATH) $msg
1428 lassign [
ExecuteRet gen_ipbus_addr_decode -h] ret msg
1435 Msg CriticalWarning "Problem while trying to run python: $msg"
1438 set dst [
file normalize $dst]
1440 if {$can_generate == 0} {
1441 if {$generate == 1} {
1442 Msg Error "Cannot generate IPbus address files, IPbus executable gen_ipbus_addr_decode not found or not working: $msg"
1445 Msg Warning "IPbus executable gen_ipbus_addr_decode not found or not working, will not verify IPbus address tables."
1452 set ipb_files [glob -nocomplain $proj_dir/list/*.ipb]
1453 set n_ipb_files [
llength $ipb_files]
1454 if {$n_ipb_files == 0} {
1455 Msg CriticalWarning "No files with .ipb extension found in $proj_dir/list."
1458 set libraries [dict create]
1459 set vhdl_dict [dict create]
1461 foreach ipb_file $ipb_files {
1467 set xmlfiles [dict get $libraries "xml.ipb"]
1469 set xml_list_error 0
1470 foreach xmlfile $xmlfiles {
1471 if {[
file isdirectory $xmlfile]} {
1472 Msg CriticalWarning "Directory $xmlfile listed in xml list file $list_file. Directories are not supported!"
1473 set xml_list_error 1
1476 if {[
file exists $xmlfile]} {
1477 if {[dict exists $vhdl_dict $xmlfile]} {
1478 set vhdl_file [
file normalize $path/[dict get $vhdl_dict $xmlfile]]
1482 lappend vhdls $vhdl_file
1483 set xmlfile [
file normalize $xmlfile]
1484 Msg Info "Copying $xmlfile to $dst and replacing place holders..."
1485 set in [open $xmlfile r]
1486 set out [open $dst/[
file tail $xmlfile] w]
1488 while {[
gets $in line] != -1} {
1489 set new_line [regsub {(.*)__VERSION__(.*)} $line "\\1$xml_version\\2"]
1490 set new_line2 [regsub {(.*)__GIT_SHA__(.*)} $new_line "\\1$xml_sha\\2"]
1491 puts $out $new_line2
1495 lappend xmls [
file tail $xmlfile]
1497 Msg Warning "XML file $xmlfile not found"
1500 if {${xml_list_error}} {
1501 Msg Error "Invalid files added to $list_file!"
1504 set cnt [
llength $xmls]
1505 Msg Info "$cnt xml file/s copied"
1508 if {$can_generate == 1} {
1511 file mkdir "address_decode"
1513 foreach x $xmls v $vhdls {
1515 set x [
file normalize ../$x]
1516 if {[
file exists $x]} {
1517 lassign [
ExecuteRet gen_ipbus_addr_decode --no-timestamp $x 2>&1] status log
1519 set generated_vhdl ./ipbus_decode_[
file rootname [
file tail $x]].vhd
1520 if {$generate == 1} {
1521 Msg Info "Copying generated VHDL file $generated_vhdl into $v (replacing if necessary)"
1522 file copy -force -- $generated_vhdl $v
1524 if {[
file exists $v]} {
1526 set n [
llength $diff]
1528 Msg CriticalWarning "$v does not correspond to its XML $x, [
expr {$n / 3}] line/s differ:"
1529 Msg Status [
join $diff "\n"]
1530 set diff_file [open ../diff_[
file rootname [
file tail $x]].txt w]
1531 puts $diff_file $diff
1534 Msg Info "[
file tail $x] and $v match."
1537 Msg Warning "VHDL address map file $v not found."
1541 Msg Warning "Address map generation failed for [
file tail $x]: $log"
1544 Msg Warning "Copied XML file $x not found."
1547 Msg Info "Skipped verification of [
file tail $x] as no VHDL file was specified."
1551 file delete -force address_decode
1563 proc DescriptionFromConf {conf_file} {
1564 set f [open $conf_file "r"]
1565 set lines [
split [read $f] "\n"]
1567 set second_line [
lindex $lines 1]
1570 if {![regexp {\#+ *(.+)} $second_line - description]} {
1574 if {[regexp -all {test|Test|TEST} $description]} {
1575 set description "test"
1588 proc DictGet {dictName keyName {default ""}} {
1589 if {[dict exists $dictName $keyName]} {
1590 return [dict get $dictName $keyName]
1601 proc DictSort {dict args} {
1603 foreach key [lsort {*}$args [dict keys $dict]] {
1604 dict set res $key [dict get $dict $key]
1614 proc DoxygenVersion {target_version} {
1615 set ver [
split $target_version "."]
1616 set v [
Execute doxygen --version]
1617 Msg Info "Found Doxygen version: $v"
1618 set current_ver [
split $v ". "]
1619 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
1620 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
1622 return [
expr {$target <= $current}]
1633 proc eos {command {attempt 1}} {
1635 if {![
info exists env(EOS_MGM_URL)]} {
1636 Msg Warning "Environment variable EOS_MGM_URL not set, setting it to default value root://eosuser.cern.ch"
1637 set ::env(EOS_MGM_URL) "root://eosuser.cern.ch"
1640 Msg Warning "The value of attempt should be 1 or more, not $attempt, setting it to 1 as default"
1643 for {
set i 0} {$i < $attempt} {
incr i} {
1644 set ret [
catch {
exec -ignorestderr eos {*}$command} result]
1649 set wait [
expr {1 + int(rand() * 29)}]
1650 Msg Warning "Command $command failed ($i/$attempt): $result, trying again in $wait seconds..."
1651 after [
expr {$wait * 1000}]
1655 return [list $ret $result]
1665 proc Execute {args} {
1669 Msg Error "Command [
join $args] returned error code: $ret"
1683 proc ExecuteRet {args} {
1685 if {[
llength $args] == 0} {
1686 Msg CriticalWarning "No argument given"
1690 set ret [
catch {
exec -ignorestderr {*}$args} result]
1693 return [list $ret $result]
1700 proc ExtractFilesSection {file_data} {
1701 set in_files_section 0
1704 foreach line $file_data {
1705 if {[regexp {^ *\[ *files *\]} $line]} {
1706 set in_files_section 1
1709 if {$in_files_section} {
1710 if {[regexp {^ *\[.*\]} $line]} {
1713 lappend result $line
1718 if {!$in_files_section} {
1732 proc ExtractVersionFromTag {tag} {
1733 if {[regexp {^(?:b(\d+))?v(\d+)\.(\d+).(\d+)(?:-\d+)?$} $tag -> mr M m p]} {
1738 Msg Warning "Repository tag $tag is not in a Hog-compatible format."
1744 return [list $M $m $p $mr]
1754 proc FileCommitted {File} {
1756 set currentDir [
pwd]
1757 cd [
file dirname [
file normalize $File]]
1758 set GitLog [
Git ls-files [
file tail $File]]
1759 if {$GitLog == ""} {
1760 Msg CriticalWarning "File [
file normalize $File] is not in the git repository. Please add it with:\n git add [
file normalize $File]\n"
1771 proc FindCommonGitChild {SHA1 SHA2} {
1773 set commits [
Git {log --oneline --merges}]
1776 foreach line [
split $commits "\n"] {
1777 set commit [
lindex [
split $line] 0]
1781 set ancestor $commit
1794 proc findFiles {basedir pattern} {
1797 set basedir [
string trimright [
file join [
file normalize $basedir] { }]]
1803 foreach fileName [glob -nocomplain -type {f r} -path $basedir $pattern] {
1804 lappend fileList $fileName
1808 foreach dirName [glob -nocomplain -type {d r} -path $basedir *] {
1811 set subDirList [
findFiles $dirName $pattern]
1812 if {[
llength $subDirList] > 0} {
1813 foreach subDirFile $subDirList {
1814 lappend fileList $subDirFile
1825 proc FindFileType {file_name} {
1826 set extension [
file extension $file_name]
1829 set file_extension "USE_SIGNALTAP_FILE"
1832 set file_extension "VHDL_FILE"
1835 set file_extension "VHDL_FILE"
1838 set file_extension "VERILOG_FILE"
1841 set file_extension "SYSTEMVERILOG_FILE"
1844 set file_extension "SDC_FILE"
1847 set file_extension "PDC_FILE"
1850 set file_extension "NDC_FILE"
1853 set file_extension "FDC_FILE"
1856 set file_extension "SOURCE_FILE"
1859 set file_extension "IP_FILE"
1862 set file_extension "QSYS_FILE"
1865 set file_extension "QIP_FILE"
1868 set file_extension "SIP_FILE"
1871 set file_extension "BSF_FILE"
1874 set file_extension "BDF_FILE"
1877 set file_extension "COMMAND_MACRO_FILE"
1880 set file_extension "VQM_FILE"
1883 set file_extension "ERROR"
1884 Msg Error "Unknown file extension $extension"
1887 return $file_extension
1894 proc FindNewestVersion {versions} {
1895 set new_ver 00000000
1896 foreach ver $versions {
1898 if {[
expr 0x$ver > 0x$new_ver]} {
1910 proc FindVhdlVersion {file_name} {
1911 set extension [
file extension $file_name]
1914 set vhdl_version "-hdl_version VHDL_2008"
1917 set vhdl_version "-hdl_version VHDL_2008"
1924 return $vhdl_version
1931 proc FormatGeneric {generic} {
1932 if {[
string is integer "0x$generic"]} {
1933 return [
format "32'h%08X" "0x$generic"]
1936 return [
format "32'h%08X" 0]
1946 proc GenerateBitstream {{run_folder ""} {repo_path .} {njobs 1}} {
1947 Msg Info "Starting write bitstream flow..."
1949 set revision [get_current_revision]
1950 if {[
catch {execute_module -tool asm} result]} {
1951 Msg Error "Result: $result\n"
1952 Msg Error "Generate bitstream failed. See the report file.\n"
1954 Msg Info "Generate bitstream was successful for revision $revision.\n"
1957 Msg Info "Run GENERATEPROGRAMMINGDATA ..."
1958 if {[
catch {run_tool -name {GENERATEPROGRAMMINGDATA}}]} {
1959 Msg Error "GENERATEPROGRAMMINGDATA FAILED!"
1961 Msg Info "GENERATEPROGRAMMINGDATA PASSED."
1963 Msg Info "Sourcing Hog/Tcl/integrated/post-bitstream.tcl"
1964 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
1966 prj_run Export -impl Implementation0 -task Bitgen
1976 proc GenerateQsysSystem {qsysFile commandOpts} {
1978 if {[
file exists $qsysFile] != 0} {
1979 set qsysPath [
file dirname $qsysFile]
1980 set qsysName [
file rootname [
file tail $qsysFile]]
1981 set qsysIPDir "$qsysPath/$qsysName"
1982 set qsysLogFile "$qsysPath/$qsysName.qsys-generate.log"
1985 if {![
info exists ::env(QSYS_ROOTDIR)]} {
1986 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
1987 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
1988 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
1990 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
1993 set qsys_rootdir $::env(QSYS_ROOTDIR)
1996 set cmd "$qsys_rootdir/qsys-generate"
1997 set cmd_options "$qsysFile --output-directory=$qsysIPDir $commandOpts"
1998 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
1999 Msg Info "Executing: $cmd $cmd_options"
2000 Msg Info "Saving logfile in: $qsysLogFile"
2001 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
2002 set makeRet [
lindex [dict get $opt -errorcode] end]
2003 Msg CriticalWarning "$cmd returned with $makeRet"
2006 Msg Error " Could not execute command $cmd"
2010 set qsysIPFileList [
concat \
2011 [glob -nocomplain -directory $qsysIPDir -types f *.ip *.qip] \
2012 [glob -nocomplain -directory "$qsysIPDir/synthesis" -types f *.ip *.qip *.vhd *.vhdl]
2014 foreach qsysIPFile $qsysIPFileList {
2015 if {[
file exists $qsysIPFile] != 0} {
2017 set_global_assignment -name $qsysIPFileType $qsysIPFile
2019 set IpMd5Sum [
Md5Sum $qsysIPFile]
2021 set fileDir [
file normalize "./hogTmp"]
2022 set fileName "$fileDir/.hogQsys.md5"
2023 if {![
file exists $fileDir]} {
2026 set hogQsysFile [open $fileName "a"]
2027 set fileEntry "$qsysIPFile\t$IpMd5Sum"
2028 puts $hogQsysFile $fileEntry
2033 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
2043 proc GenericToSimulatorString {prop_dict target} {
2045 dict for {theKey theValue} $prop_dict {
2054 regexp {([0-9]*)('h)([0-9a-fA-F]*)} $theValue valueHexFull valueNumBits valueHexFlag valueHex
2055 regexp {^([0-9]*)$} $theValue valueIntFull ValueInt
2056 regexp {(?!^\d+$)^.+$} $theValue valueStrFull ValueStr
2057 if {[string tolower $target] == "vivado" || [string tolower $target] == "xsim"} {
2058 if {[string tolower $theValue] == "true" || [string tolower $theValue] == "false"} {
2059 set prj_generics "$prj_generics $theKey=$theValue"
2060 } elseif {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2061 set prj_generics "$prj_generics $theKey=$valueHexFull"
2062 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2063 set prj_generics "$prj_generics $theKey=$ValueInt"
2064 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2065 set prj_generics "$prj_generics $theKey=\"$ValueStr\""
2067 set prj_generics "$prj_generics $theKey=\"$theValue\""
2069 } elseif {[lsearch -exact [GetSimulators] [string tolower $target]] >= 0} {
2070 if {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2072 scan $valueNumBits %d numBits
2074 scan $valueHex %x numHex
2075 binary scan [binary format "I" $numHex] "B*" binval
2076 set numBits [expr {$numBits - 1}]
2077 set numBin [string range $binval end-$numBits end]
2078 set prj_generics "$prj_generics $theKey=\"$numBin\""
2079 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2080 set prj_generics "$prj_generics $theKey=$ValueInt"
2081 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2082 set prj_generics "$prj_generics {$theKey=\"$ValueStr\"}"
2084 set prj_generics "$prj_generics {$theKey=\"$theValue\"}"
2087 Msg Warning "Target : $target not implemented"
2090 return $prj_generics
2098 proc GetConfFiles {proj_dir} {
2099 Msg Debug "GetConfFiles called with proj_dir=$proj_dir"
2100 if {![
file isdirectory $proj_dir]} {
2101 Msg Error "$proj_dir is supposed to be the top project directory"
2104 set conf_file [
file normalize $proj_dir/hog.conf]
2105 set sim_file [
file normalize $proj_dir/sim.conf]
2106 set pre_tcl [
file normalize $proj_dir/pre-creation.tcl]
2107 set post_tcl [
file normalize $proj_dir/post-creation.tcl]
2109 return [list $conf_file $sim_file $pre_tcl $post_tcl]
2118 proc GetCustomCommands {parameters {directory .}} {
2119 set commands_dict [dict create]
2120 set commands_files [glob -nocomplain $directory/*.tcl]
2122 if {[
llength $commands_files] == 0} {
2126 foreach file $commands_files {
2131 if {$custom_cmd eq ""} {
2136 set custom_cmd_name [dict get $custom_cmd NAME]
2138 Msg Debug "Loaded custom command '$custom_cmd_name' from $file"
2141 if {[dict exists $commands_dict $custom_cmd_name]} {
2142 Msg Error "Custom command '$custom_cmd_name' in $file already defined as: \[dict get $commands_dict $custom_cmd_name\]. Skipping."
2148 set custom_cmd_name [
string toupper $custom_cmd_name]
2149 dict set commands_dict $custom_cmd_name $custom_cmd
2152 return $commands_dict
2155 proc SanitizeCustomCommand {cmdDict file parameters} {
2158 foreach k [dict keys $cmdDict] {
2159 set K [
string toupper $k]
2160 dict set normalized $K [dict get $cmdDict $k]
2163 set cmdDict $normalized
2164 if {![dict exists $cmdDict NAME]} {
2165 Msg Error "Custom command in $file missing required key NAME. Skipping."
2168 if {![dict exists $cmdDict SCRIPT]} {
2169 Msg Error "Custom command '$[dict get $cmdDict NAME]' in $file missing SCRIPT. Skipping."
2174 set allowed {NAME DESCRIPTION OPTIONS CUSTOM_OPTIONS SCRIPT IDE NO_EXIT}
2175 foreach k [dict keys $cmdDict] {
2176 if {[lsearch -exact $allowed $k] < 0} {
2177 Msg Warning "Custom command '[dict get $cmdDict NAME]' in $file: unknown key '$k'. Allowed: $allowed. Skipping."
2182 set name [
string trim [dict get $cmdDict NAME]]
2184 Msg Error "Custom command in $file has empty NAME. Skipping."
2188 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]+$} $name]} {
2189 Msg Error "Custom command NAME '$name' (file $file) contains invalid characters."
2193 if {![dict exists $cmdDict DESCRIPTION]} {
2194 dict set cmdDict DESCRIPTION "No description provided."
2198 set hog_parameters {}
2199 foreach p $parameters {
2200 lappend hog_parameters [
lindex $p 0]
2205 if {[dict exists $cmdDict OPTIONS]} {
2206 set raw_opts [dict get $cmdDict OPTIONS]
2207 if {![
llength $raw_opts]} {
2211 foreach item $raw_opts {
2213 foreach p $parameters {
2214 set hog_parameter [
lindex $p 0]
2215 if { $item eq $hog_parameter } {
2216 lappend hog_options $p
2222 Msg Warning "Custom command '$name' in $file: option '$item' not found in Hog parameters. Skipping."
2225 dict set cmdDict OPTIONS $hog_options
2227 dict set cmdDict CUSTOM_OPTIONS {}
2234 if {[dict exists $cmdDict CUSTOM_OPTIONS]} {
2235 set raw_opts [dict get $cmdDict CUSTOM_OPTIONS]
2236 if {![
llength $raw_opts]} {
2239 foreach item $raw_opts {
2241 if {[
llength $item] != 2 && [
llength $item] != 3} {
2242 Msg Error "Bad custom option: \[$item\]. Custom command '$name' in $file: \
2243 each CUSTOM_OPTIONS entry must be {option \"help\"} for flags \
2244 and {option \"default_value\" \"help\"} for options with arguments. Skipping command."
2248 if {[
llength $item] == 2} {
2249 lassign $item opt help
2252 lassign $item opt def help
2255 if { [
IsInList $opt $hog_parameters] == 1 } {
2256 Msg Warning "Custom command '$name' in $file: option '$opt' already defined in Hog parameters. Skipping."
2262 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]*(\.arg)?$} $opt]} {
2263 Msg Error "Custom command '$name' in $file: invalid option name '$opt'."
2268 Msg Warning "Custom command '$name' option '$opt' has empty help text."
2272 dict set cmdDict CUSTOM_OPTIONS {}
2276 if {[dict exists $cmdDict NO_EXIT]} {
2277 set no_exit [dict get $cmdDict NO_EXIT]
2278 set no_exit [
string tolower [
string trim $no_exit]]
2280 if {$no_exit eq "1" || $no_exit eq "true"} {
2286 dict set cmdDict NO_EXIT $no_exit
2288 dict set cmdDict NO_EXIT 0
2294 proc LoadCustomCommandFile {file parameters} {
2296 set dir [
file dirname $file]
2298 unset -nocomplain ::hog_command
2299 set rc [
catch {source $file} err]
2302 Msg Error "Error sourcing custom command file $file: $err"
2305 if {![
info exists ::hog_command]} {
2306 Msg Warning "File $file did not define ::hog_command. Skipping."
2309 set cmdDict $::hog_command
2311 if {[
catch {dict size $cmdDict}]} {
2312 Msg Error "In $file ::hog_command is not a valid dict. Skipping."
2322 proc GetDateAndTime {commit} {
2323 set clock_seconds [
clock seconds]
2326 set date [
Git "log -1 --format=%cd --date=format:%d%m%Y $commit"]
2327 set timee [
Git "log -1 --format=%cd --date=format:00%H%M%S $commit"]
2329 Msg Warning "Found Git version older than 2.9.3. Using current date and time instead of commit time."
2330 set date [
clock format $clock_seconds -format {%d%m%Y}]
2331 set timee [
clock format $clock_seconds -format {00%H%M%S}]
2333 return [list $date $timee]
2345 proc GetFile {file fileset} {
2348 set Files [get_files -all $file -of_object [get_filesets $fileset]]
2349 set f [
lindex $Files 0]
2357 puts "***DEBUG Hog:GetFile $file"
2366 proc GetFileGenerics {filename {entity ""}} {
2368 if {[
string equal $file_type "VERILOG_FILE"] || [
string equal $file_type "SYSTEMVERILOG_FILE"]} {
2370 }
elseif {[
string equal $file_type "VHDL_FILE"]} {
2373 Msg CriticalWarning "Could not determine extension of top level file."
2382 proc GetGenericsFromConf {proj_dir} {
2383 set generics_dict [dict create]
2384 set top_dir "Top/$proj_dir"
2385 set conf_file "$top_dir/hog.conf"
2387 Msg Debug "GetGenericsFromConf called with proj_dir=$proj_dir, top_dir=$top_dir"
2389 if {[
file exists $conf_file]} {
2391 if {[dict exists $properties generics]} {
2392 set generics_dict [dict get $properties generics]
2395 Msg Warning "File $conf_file not found."
2397 return $generics_dict
2410 proc GetSimSets {project_name repo_path {simsets ""} {ghdl 0} {no_conf 0}} {
2411 set simsets_dict [dict create]
2412 set list_dir "$repo_path/Top/$project_name/list"
2414 if {$simsets != ""} {
2415 foreach s $simsets {
2416 set list_file "$list_dir/$s.sim"
2417 if {[
file exists $list_file]} {
2418 lappend list_files $list_file
2419 }
elseif {$s != "sim_1"} {
2420 Msg CriticalWarning "Simulation set list file $list_file not found."
2425 set list_files [glob -nocomplain -directory $list_dir "*.sim"]
2429 set proj_dir [
file normalize $repo_path/Top/$project_name]
2430 set sim_file [
file normalize $proj_dir/sim.conf]
2432 foreach list_file $list_files {
2433 set file_name [
file tail $list_file]
2434 set simset_name [
file rootname $file_name]
2435 set fp [open $list_file r]
2436 set file_data [read $fp]
2438 set data [
split $file_data "\n"]
2440 set firstline [
lindex $data 0]
2442 if {[regexp {^ *\# *Simulator} $firstline]} {
2443 set simulator_prop [regexp -all -inline {\S+} $firstline]
2444 set simulator [
string tolower [
lindex $simulator_prop end]]
2446 Msg Warning "Simulator not set in $simset_name.sim. \
2447 The first line of $simset_name.sim should be #Simulator <SIMULATOR_NAME>,\
2448 where <SIMULATOR_NAME> can be xsim, questa, modelsim, ghdl, riviera, activehdl,\
2449 ies, or vcs, e.g. #Simulator questa.\
2450 Setting simulator by default to xsim."
2451 set simulator "xsim"
2453 if {$simulator eq "skip_simulation"} {
2454 Msg Info "Skipping simulation for $simset_name"
2457 if {($ghdl == 1 && $simulator != "ghdl") || ($ghdl == 0 && $simulator == "ghdl")} {
2461 set SIM_PROPERTIES ""
2462 if {[
file exists $sim_file] && $no_conf == 0} {
2463 set SIM_PROPERTIES [
ReadConf $sim_file]
2466 set global_sim_props [dict create]
2467 dict set global_sim_props "properties" [
DictGet $SIM_PROPERTIES "sim"]
2468 dict set global_sim_props "generics" [
DictGet $SIM_PROPERTIES "generics"]
2469 dict set global_sim_props "hog" [
DictGet $SIM_PROPERTIES "hog"]
2472 set sim_dict [dict create]
2473 dict set sim_dict "simulator" $simulator
2474 if {[dict exists $SIM_PROPERTIES $simset_name]} {
2475 dict set sim_dict "properties" [
DictGet $SIM_PROPERTIES $simset_name]
2476 dict set sim_dict "generics" [
DictGet $SIM_PROPERTIES "$simset_name:generics"]
2477 dict set sim_dict "hog" [
DictGet $SIM_PROPERTIES "$simset_name:hog"]
2478 }
elseif {$no_conf == 0} {
2480 set conf_dict [
ReadConf $list_file]
2481 set sim_dict [
MergeDict $sim_dict $conf_dict 0]
2483 set sim_dict [
MergeDict $sim_dict $global_sim_props 0]
2484 dict set simsets_dict $simset_name $sim_dict
2486 return $simsets_dict
2494 proc GetSimsetGenericsFromConf {proj_dir} {
2495 set simsets_generics_dict [dict create]
2496 set top_dir "Top/$proj_dir"
2497 set conf_file "$top_dir/sim.conf"
2500 if {[
file exists $conf_file]} {
2503 set simsets_generics_dict [dict filter $properties key *:generics]
2505 Msg Warning "File $conf_file not found."
2507 return $simsets_generics_dict
2518 proc GetGroupName {proj_dir repo_dir} {
2519 if {[regexp {^(.*)/(Top|Projects)/+(.*?)/*$} $proj_dir dummy possible_repo_dir proj_or_top dir]} {
2521 if {[
file normalize $repo_dir] eq [
file normalize $possible_repo_dir]} {
2522 set group [
file dir $dir]
2523 if {$group == "."} {
2528 Msg Warning "Project directory $proj_dir seems to be in $possible_repo_dir which is not a the main Git repository $repo_dir."
2531 Msg Warning "Could not parse project directory $proj_dir"
2544 proc GetHogDescribe {sha {repo_path .}} {
2547 set new_sha "[
string toupper [
GetSHA]]"
2550 set new_sha [
string toupper $sha]
2570 proc GetHogFiles {args} {
2573 if {[
catch {
package require cmdline} ERROR]} {
2574 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
2581 {list_files.arg "" "The file wildcard, if not specified all Hog list files will be looked for."}
2582 {sha_mode "Forwarded to ReadListFile, see there for info."}
2583 {ext_path.arg "" "Path for the external libraries forwarded to ReadListFile."}
2584 {print_log "Forwarded to ReadListFile, see there for info."}
2586 set usage "USAGE: GetHogFiles \[options\] <list path> <repository path>"
2587 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
2591 set list_path [
lindex $args 0]
2592 set repo_path [
lindex $args 1]
2594 set list_files $options(list_files)
2595 set sha_mode $options(sha_mode)
2596 set ext_path $options(ext_path)
2597 set print_log $options(print_log)
2599 if {$sha_mode == 1} {
2600 set sha_mode_opt "-sha_mode"
2605 if {$print_log == 1} {
2606 set print_log_opt "-print_log"
2608 set print_log_opt ""
2612 if {$list_files == ""} {
2613 set list_files {.src,.con,.sim,.ext}
2615 set libraries [dict create]
2616 set properties [dict create]
2617 set list_files [glob -nocomplain -directory $list_path "*{$list_files}"]
2618 set filesets [dict create]
2620 foreach f $list_files {
2621 set ext [
file extension $f]
2622 if {$ext == ".ext"} {
2623 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $ext_path"] l p fs
2625 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $repo_path"] l p fs
2628 set properties [
MergeDict $p $properties]
2629 Msg Debug "list file $f, filesets: $fs"
2631 Msg Debug "Merged filesets $filesets"
2633 return [list $libraries $properties $filesets]
2640 proc GetIDECommand {proj_conf {custom_ver ""}} {
2641 if {$custom_ver ne ""} {
2642 set ide_name_and_ver [
string tolower "$custom_ver"]
2643 }
elseif {[
file exists $proj_conf]} {
2644 set ide_name_and_ver [
string tolower [
GetIDEFromConf $proj_conf]]
2646 Msg Error "Configuration file $proj_conf not found."
2649 set ide_name [
lindex [regexp -all -inline {\S+} $ide_name_and_ver] 0]
2651 if {$ide_name eq "vivado" || $ide_name eq "vivado_vitis_classic"} {
2652 set command "vivado"
2654 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2655 set after_tcl_script " -tclargs "
2657 }
elseif {$ide_name eq "planahead"} {
2658 set command "planAhead"
2660 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2661 set after_tcl_script " -tclargs "
2663 }
elseif {$ide_name eq "quartus"} {
2664 set command "quartus_sh"
2666 set before_tcl_script " -t "
2667 set after_tcl_script " "
2669 }
elseif {$ide_name eq "libero"} {
2672 set command "libero"
2673 set before_tcl_script "SCRIPT:"
2674 set after_tcl_script " SCRIPT_ARGS:\""
2676 }
elseif {$ide_name eq "diamond"} {
2677 set command "diamondc"
2678 set before_tcl_script " "
2679 set after_tcl_script " "
2681 }
elseif {$ide_name eq "vitis_classic"} {
2684 set before_tcl_script ""
2685 set after_tcl_script " "
2687 }
elseif {$ide_name eq "ghdl"} {
2689 set before_tcl_script " "
2690 set after_tcl_script " "
2693 Msg Error "IDE: $ide_name not known."
2696 return [list $command $before_tcl_script $after_tcl_script $end_marker]
2702 proc GetIDEFromConf {conf_file} {
2703 set f [open $conf_file "r"]
2706 if {[regexp -all {^\# *(\w*) *(vitis_classic)? *(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)?(_.*)? *$} $line dummy ide vitisflag version patch]} {
2707 if {[
info exists vitisflag] && $vitisflag != ""} {
2708 set ide "${ide}_${vitisflag}"
2711 if {[
info exists version] && $version != ""} {
2717 set ret [list $ide $ver]
2719 Msg CriticalWarning "The first line of hog.conf should be \#<IDE name> <version>, \
2720 where <IDE name>. is quartus, vivado, planahead, libero, diamond or ghdl, \
2721 and <version> the tool version, e.g. \#vivado 2020.2. Will assume vivado."
2722 set ret [list "vivado" "0.0.0"]
2729 proc GetIDEName {} {
2731 return "ISE/PlanAhead"
2749 proc GetIDEVersion {} {
2752 regexp {\d+\.\d+(\.\d+)?} [version -short] ver
2757 regexp {[\.0-9]+} $quartus(version) ver
2760 set ver [get_libero_version]
2762 regexp {\d+\.\d+(\.\d+)?} [sys_install version] ver
2764 regexp {\d+\.\d+(\.\d+)?} [version] ver
2778 proc GetLinkedFile {link_file} {
2779 if {[
file type $link_file] eq "link"} {
2780 if {[
OS] == "windows"} {
2782 lassign [
ExecuteRet realpath $link_file] ret msg
2783 lassign [
ExecuteRet cygpath -m $msg] ret2 msg2
2784 if {$ret == 0 && $ret2 == 0} {
2786 Msg Debug "Found link file $link_file on Windows, the linked file is: $real_file"
2788 Msg CriticalWarning "[
file normalize $link_file] is a soft link. \
2789 Soft link are not supported on Windows and readlink.exe or cygpath.exe did not work: readlink=$ret: $msg, cygpath=$ret2: $msg2."
2790 set real_file $link_file
2794 set linked_file [
file link $link_file]
2795 set real_file [
file normalize [
file dirname $link_file]/$linked_file]
2798 if {![
file exists $real_file]} {
2799 Msg Warning "$link_file is a broken link, because the linked file: $real_file does not exist."
2802 Msg Warning "$link file is not a soft link"
2803 set real_file $link_file
2816 proc GetMaxThreads {proj_dir} {
2818 if {[
file exists $proj_dir/hog.conf]} {
2820 if {[dict exists $properties parameters]} {
2821 set propDict [dict get $properties parameters]
2822 if {[dict exists $propDict MAX_THREADS]} {
2823 set maxThreads [dict get $propDict MAX_THREADS]
2827 Msg Warning "File $proj_dir/hog.conf not found. Max threads will be set to default value 1"
2840 proc GetModifiedFiles {{repo_path "."} {pattern "."}} {
2843 set ret [
Git "ls-files --modified $pattern"]
2852 proc GetOptions {argv parameters} {
2855 set param_list [list]
2856 set option_list [list]
2858 foreach p $parameters {
2859 lappend param_list [
lindex $p 0]
2863 while {$index < [
llength $argv]} {
2864 set arg [
lindex $argv $index]
2865 if {[
string first - $arg] == 0} {
2866 set option [
string trimleft $arg "-"]
2868 lappend option_list $arg
2869 if {[lsearch -regex $param_list "$option\[.arg]?"] >= 0 } {
2870 if {[lsearch -regex $param_list "$option\[.arg]"] >= 0 } {
2871 lappend option_list [
lindex $argv $index]
2876 lappend arg_list $arg
2880 Msg Debug "Argv: $argv"
2881 Msg Debug "Options: $option_list"
2882 Msg Debug "Arguments: $arg_list"
2883 return [list $option_list $arg_list]
2901 proc GetProjectFiles {{project_file ""}} {
2902 set libraries [dict create]
2903 set simlibraries [dict create]
2904 set constraints [dict create]
2905 set properties [dict create]
2906 set consets [dict create]
2907 set srcsets [dict create]
2908 set simsets [dict create]
2911 set all_filesets [get_filesets]
2912 set simulator [get_property target_simulator [current_project]]
2913 set top [get_property "top" [current_fileset]]
2915 dict lappend properties $topfile "top=$top"
2917 foreach fs $all_filesets {
2918 if {$fs == "utils_1"} {
2923 set all_files [get_files -quiet -of_objects [get_filesets $fs]]
2924 set fs_type [get_property FILESET_TYPE [get_filesets $fs]]
2926 if {$fs_type == "BlockSrcs"} {
2928 set dict_fs "sources_1"
2932 foreach f $all_files {
2939 if {[
lindex [get_property IS_GENERATED [
GetFile $f $fs]] 0] != 0} {
2944 if {[get_property FILE_TYPE [
GetFile $f $fs]] == "Configuration Files"} {
2950 if {[get_property CORE_CONTAINER [
GetFile $f $fs]] != ""} {
2951 if {[
file extension $f] == ".xcix"} {
2952 set f [get_property CORE_CONTAINER [
GetFile $f $fs]]
2960 if {[get_property SCOPED_TO_REF [
GetFile $f $fs]] != ""} {
2961 dict lappend properties $f "scoped_to_ref=[get_property SCOPED_TO_REF [
GetFile $f $fs]]"
2966 if {[get_property SCOPED_TO_CELLS [
GetFile $f $fs]] != ""} {
2967 dict lappend properties $f "scoped_to_cells=[get_property SCOPED_TO_CELLS [
GetFile $f $fs]]"
2971 if {[
IsInList "PARENT_COMPOSITE_FILE" [list_property [
GetFile $f $fs]]]} {
2976 if {[
file tail $f] == "nocattrs.dat"} {
2981 if {[
file extension $f] != ".coe"} {
2982 set f [
file normalize $f]
2985 set type [get_property FILE_TYPE [
GetFile $f $fs]]
2987 set lib [get_property -quiet LIBRARY [
GetFile $f $fs]]
2990 Msg Debug "File $f Extension [
file extension $f] Type [
lindex $type 0]"
2992 if {[
string equal [
lindex $type 0] "VHDL"] && [
llength $type] == 1} {
2994 }
elseif {[
string equal [
lindex $type 0] "Block"] && [
string equal [
lindex $type 1] "Designs"]} {
2997 }
elseif {[
string equal $type "SystemVerilog"] && [
file extension $f] != ".sv"} {
2998 set prop "SystemVerilog"
2999 }
elseif {[
string equal [
lindex $type 0] "XDC"] && [
file extension $f] != ".xdc"} {
3001 }
elseif {[
string equal $type "Verilog Header"] && [
file extension $f] != ".vh" && [
file extension $f] != ".svh"} {
3002 set prop "verilog_header"
3003 }
elseif {[
string equal $type "Verilog Template"] && [
file extension $f] == ".v" && [
file extension $f] != ".sv"} {
3004 set prop "verilog_template"
3006 set type [
lindex $type 0]
3010 if {![
string equal $prop ""]} {
3011 dict lappend properties $f $prop
3014 if {[
string equal $fs_type "SimulationSrcs"]} {
3016 if {[
string equal $type "VHDL"]} {
3017 set library "${lib}.sim"
3019 set library "others.sim"
3023 dict lappend simsets $dict_fs $library
3026 dict lappend simlibraries $library $f
3027 }
elseif {[
string equal $type "VHDL"]} {
3030 dict lappend srcsets $dict_fs "${lib}.src"
3032 dict lappend libraries "${lib}.src" $f
3033 }
elseif {[
string first "IP" $type] != -1} {
3036 dict lappend srcsets $dict_fs "ips.src"
3038 dict lappend libraries "ips.src" $f
3039 Msg Debug "Appending $f to ips.src"
3040 }
elseif {[
string equal $fs_type "Constrs"]} {
3043 dict lappend consets $dict_fs "sources.con"
3045 dict lappend constraints "sources.con" $f
3049 dict lappend srcsets $dict_fs "others.src"
3051 dict lappend libraries "others.src" $f
3052 Msg Debug "Appending $f to others.src"
3055 if {[
lindex [get_property -quiet used_in_synthesis [
GetFile $f $fs]] 0] == 0} {
3056 dict lappend properties $f "nosynth"
3058 if {[
lindex [get_property -quiet used_in_implementation [
GetFile $f $fs]] 0] == 0} {
3059 dict lappend properties $f "noimpl"
3061 if {[
lindex [get_property -quiet used_in_simulation [
GetFile $f $fs]] 0] == 0} {
3062 dict lappend properties $f "nosim"
3064 if {[
lindex [get_property -quiet IS_MANAGED [
GetFile $f $fs]] 0] == 0 && [
file extension $f] != ".xcix"} {
3065 dict lappend properties $f "locked"
3071 dict lappend properties "Simulator" [get_property target_simulator [current_project]]
3074 set file [open $project_file r]
3075 set in_file_manager 0
3077 while {[
gets $file line] >= 0} {
3079 if {[regexp {^KEY ActiveRoot \"([^\"]+)\"} $line -> value]} {
3080 set top [
string range $value 0 [
expr {[string first "::" $value] - 1}]]
3084 if {[regexp {^LIST FileManager} $line]} {
3085 set in_file_manager 1
3090 if {$in_file_manager && [regexp {^ENDLIST} $line]} {
3095 if {$in_file_manager && [regexp {^VALUE \"([^\"]+)} $line -> value]} {
3098 lassign [
split $value ,] file_path file_type
3101 set library "others"
3102 while {[
gets $file line] >= 0} {
3103 if {$line == "ENDFILE"} {
3106 regexp {^LIBRARY=\"([^\"]+)} $line -> library
3107 regexp {^PARENT=\"([^\"]+)} $line -> parent_file
3109 Msg Debug "Found file ${file_path} in project.."
3110 if {$parent_file == ""} {
3111 if {$file_type == "hdl"} {
3113 if {[
IsInList "${library}.src" [
DictGet $srcsets "sources_1"]] == 0} {
3114 dict lappend srcsets "sources_1" "${library}.src"
3116 dict lappend libraries "${library}.src" $file_path
3120 if {[
GetModuleName $file_path] == [
string tolower $top] && $top != ""} {
3121 Msg Debug "Found top module $top in $file_path"
3122 dict lappend properties $file_path "top=$top"
3124 }
elseif {$file_type == "tb_hdl"} {
3126 dict lappend simsets "sim_1" "${library}.sim"
3128 dict lappend simlibraries "${library}.sim" $file_path
3129 }
elseif {$file_type == "io_pdc" || $file_type == "sdc"} {
3131 dict lappend consets "constrs_1" "sources.con"
3133 dict lappend constraints "sources.con" $file_path
3140 set fileData [read [open $project_file]]
3142 set project_path [
file dirname $project_file]
3145 regsub {<\?xml.*\?>} $fileData "" fileData
3148 regexp {<Implementation.*?>(.*)</Implementation>} $fileData -> implementationContent
3152 set sourceRegex {<Source name="([^"]*?)" type="([^"]*?)" type_short="([^"]*?)".*?>(.*?)</Source>}
3154 set optionsRegex {<Options(.*?)\/>}
3155 regexp $optionsRegex $implementationContent -> prj_options
3156 foreach option $prj_options {
3157 if {[regexp {^top=\"([^\"]+)\"} $option match result]} {
3162 while {[regexp $sourceRegex $implementationContent match name type type_short optionsContent]} {
3163 Msg Debug "Found file ${name} in project..."
3164 set file_path [
file normalize $project_path/$name]
3166 set optionsRegex {<Options(.*?)\/>}
3167 regexp $optionsRegex $optionsContent -> options
3168 set library "others"
3170 foreach option $options {
3171 if {[
string first "System Verilog" $option]} {
3174 if {[regexp {^lib=\"([^\"]+)\"} $option match1 result]} {
3179 if {[regexp {syn_sim="([^"]*?)"} $match match_sim simonly]} {
3184 if {$type_short == "VHDL" || $type_short == "Verilog" || $type_short == "IPX"} {
3185 if {$ext == ".src"} {
3186 if {[
IsInList "${library}${ext}" [
DictGet $srcsets "sources_1"]] == 0} {
3187 dict lappend srcsets "sources_1" "${library}${ext}"
3189 dict lappend libraries "${library}${ext}" $file_path
3190 }
elseif {$ext == ".sim"} {
3192 dict lappend simsets "sim_1" "${library}.sim"
3194 dict lappend simlibraries "${library}.sim" $file_path
3200 Msg Debug "Found top module $top in $file_path"
3201 dict lappend properties $file_path "top=$top"
3203 }
elseif {$type_short == "SDC"} {
3205 dict lappend consets "constrs_1" "sources.con"
3207 dict lappend constraints "sources.con" $file_path
3211 regsub -- $match $implementationContent "" implementationContent
3214 return [list $libraries $properties $simlibraries $constraints $srcsets $simsets $consets]
3221 proc GetProjectFlavour {proj_name} {
3223 set flavour [
string map {. ""} [
file extension $proj_name]]
3224 if {$flavour != ""} {
3225 if {[
string is integer $flavour]} {
3226 Msg Info "Project $proj_name has flavour = $flavour, the generic variable FLAVOUR will be set to $flavour"
3228 Msg Warning "Project name has a unexpected non numeric extension, flavour will be set to -1"
3245 proc GetProjectVersion {proj_dir repo_path {ext_path ""} {sim 0}} {
3246 if {![
file exists $proj_dir]} {
3247 Msg CriticalWarning "$proj_dir not found"
3257 Msg Warning "Repository is not clean"
3265 Msg Debug "Project version $v_proj, latest tag $v_last"
3267 Msg Info "The specified project was modified since official version."
3274 Msg Info "The specified project was modified in the latest official version $ret"
3275 }
elseif {$comp == -1} {
3276 Msg Info "The specified project was modified in a past official version $ret"
3292 proc GetRepoVersions {proj_dir repo_path {ext_path ""} {sim 0}} {
3293 if {[
catch {
package require cmdline} ERROR]} {
3294 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
3309 lappend SHAs [
GetSHA {Hog}]
3313 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
3314 Msg Info "Hog submodule [
pwd] clean."
3315 lassign [
GetVer ./] hog_ver hog_hash
3317 Msg CriticalWarning "Hog submodule [
pwd] not clean, commit hash will be set to 0."
3318 set hog_hash "0000000"
3319 set hog_ver "00000000"
3324 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
3325 Msg Info "Git working directory [
pwd] clean."
3328 Msg CriticalWarning "Git working directory [
pwd] not clean, commit hash, and version will be set to 0."
3333 lassign [
GetVer [
join $conf_files]] top_ver top_hash
3334 lappend SHAs $top_hash
3335 lappend versions $top_ver
3342 lassign [
GetHogFiles -list_files "*.src" -sha_mode "./list/" $repo_path] src_files dummy
3343 dict for {f files} $src_files {
3344 # library names have a .src extension in values returned by GetHogFiles
3345 set name [file rootname [file tail $f]]
3346 if {[file ext $f] == ".oth"} {
3349 lassign [GetVer $files] ver hash
3350 # Msg Info "Found source list file $f, version: $ver commit SHA: $hash"
3352 lappend versions $ver
3354 lappend hashes $hash
3361 lassign [
GetHogFiles -list_files "*.con" -sha_mode "./list/" $repo_path] cons_files dummy
3362 dict for {f files} $cons_files {
3363 #library names have a .con extension in values returned by GetHogFiles
3364 set name [file rootname [file tail $f]]
3365 lassign [GetVer $files] ver hash
3366 #Msg Info "Found constraint list file $f, version: $ver commit SHA: $hash"
3368 Msg CriticalWarning "Constraints file $f not found in Git."
3370 lappend cons_hashes $hash
3372 lappend versions $ver
3379 lassign [
GetHogFiles -list_files "*.sim" -sha_mode "./list/" $repo_path] sim_files dummy
3380 dict for {f files} $sim_files {
3381 #library names have a .sim extension in values returned by GetHogFiles
3382 set name [file rootname [file tail $f]]
3383 lassign [GetVer $files] ver hash
3384 #Msg Info "Found simulation list file $f, version: $ver commit SHA: $hash"
3385 lappend sim_hashes $hash
3387 lappend versions $ver
3395 Msg CriticalWarning "No hashes found for constraints files (not in git)"
3398 set cons_hash [
string tolower [
Git "log --format=%h -1 $cons_hashes"]]
3405 set ext_files [glob -nocomplain "./list/*.ext"]
3408 foreach f $ext_files {
3409 set name [
file rootname [
file tail $f]]
3412 lappend ext_names $name
3413 lappend ext_hashes $hash
3416 lappend versions $ext_ver
3419 set file_data [read $fp]
3421 set data [
split $file_data "\n"]
3423 foreach line $data {
3424 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
3426 set file_and_prop [regexp -all -inline {\S+} $line]
3427 set hdlfile [
lindex $file_and_prop 0]
3428 set hdlfile $ext_path/$hdlfile
3429 if {[
file exists $hdlfile]} {
3430 set hash [
lindex $file_and_prop 1]
3431 set current_hash [
Md5Sum $hdlfile]
3432 if {[
string first $hash $current_hash] == -1} {
3433 Msg CriticalWarning "File $hdlfile has a wrong hash. Current checksum: $current_hash, expected: $hash"
3441 if {[
llength [glob -nocomplain ./list/*.ipb]] > 0} {
3443 lassign [
GetHogFiles -list_files "*.ipb" -sha_mode "./list/" $repo_path] xml_files dummy
3444 lassign [
GetVer [dict get $xml_files "xml.ipb"]] xml_ver xml_hash
3445 lappend SHAs $xml_hash
3446 lappend versions $xml_ver
3450 Msg Info "This project does not use IPbus XMLs"
3455 set user_ip_repos ""
3456 set user_ip_repo_hashes ""
3457 set user_ip_repo_vers ""
3459 if {[
file exists [
lindex $conf_files 0]]} {
3460 set PROPERTIES [
ReadConf [
lindex $conf_files 0]]
3461 if {[dict exists $PROPERTIES main]} {
3462 set main [dict get $PROPERTIES main]
3463 dict for {p v} $main {
3464 if {[string tolower $p] == "ip_repo_paths"} {
3466 if {[file isdirectory "$repo_path/$repo"]} {
3467 set repo_file_list [glob -nocomplain "$repo_path/$repo/*"]
3468 if {[llength $repo_file_list] == 0} {
3469 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3471 lappend user_ip_repos "$repo_path/$repo"
3480 foreach repo $user_ip_repos {
3481 if {[
file isdirectory $repo]} {
3482 set repo_file_list [glob -nocomplain "$repo/*"]
3483 if {[
llength $repo_file_list] != 0} {
3484 lassign [
GetVer $repo] ver sha
3485 lappend user_ip_repo_hashes $sha
3486 lappend user_ip_repo_vers $ver
3487 lappend versions $ver
3489 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3492 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory does not exist."
3501 while {$found == 0} {
3502 set global_commit [
Git "log --format=%h -1 --abbrev=7 $SHAs"]
3507 if {$common_child == 0} {
3508 Msg CriticalWarning "The commit $sha is not an ancestor of the global commit $global_commit, which is OK. \
3509 But $sha and $global_commit do not have any common child, which is NOT OK. \
3510 This is probably do to a REBASE that is forbidden in Hog methodology as it changes git history. \
3511 Hog cannot guarantee the accuracy of the SHAs. \
3512 A way to fix this is to make a commit that touches all the projects in the repositories (e.g. change the Hog version), \
3513 but please do not rebase in the official branches in the future."
3515 Msg Info "The commit $sha is not an ancestor of the global commit $global_commit, adding the first common child $common_child instead..."
3516 lappend SHAs $common_child
3526 set global_commit "0000000"
3527 set global_version "00000000"
3532 set top_hash [
format %+07s $top_hash]
3533 set cons_hash [
format %+07s $cons_hash]
3534 return [list $global_commit $global_version \
3535 $hog_hash $hog_ver $top_hash $top_ver \
3536 $libs $hashes $vers $cons_ver $cons_hash \
3537 $ext_names $ext_hashes $xml_hash $xml_ver \
3538 $user_ip_repos $user_ip_repo_hashes $user_ip_repo_vers]
3547 proc GetSHA {{path ""}} {
3549 lassign [
GitRet {log --format=%h --abbrev=7 -1}] status result
3551 return [
string tolower $result]
3553 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3559 set repo_path [
lindex [
Git {rev-parse --show-toplevel}] 0]
3563 set file_in_module 0
3564 if {[
file exists $repo_path/.gitmodules]} {
3565 lassign [
GitRet "config --file $repo_path/.gitmodules --get-regexp path"] status result
3567 set submodules [
split $result "\n"]
3570 Msg Warning "Something went wrong while trying to find submodules: $result"
3573 foreach mod $submodules {
3574 set module [
lindex $mod 1]
3575 if {[
string first "$repo_path/$module" $f] == 0} {
3577 set file_in_module 1
3578 lappend paths "$repo_path/$module"
3583 if {$file_in_module == 0} {
3589 lassign [
GitRet {log --format=%h --abbrev=7 -1} $paths] status result
3591 return [
string tolower $result]
3593 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3596 return [
string tolower $result]
3600 proc GetSimulators {} {
3601 set SIMULATORS [list "modelsim" "questa" "riviera" "activehdl" "ies" "vcs"]
3606 proc GetTopFile {} {
3608 set compile_order_prop [get_property source_mgmt_mode [current_project]]
3609 if {$compile_order_prop ne "All"} {
3610 Msg CriticalWarning "Compile order is not set to automatic, setting it now..."
3611 set_property source_mgmt_mode All [current_project]
3612 update_compile_order -fileset sources_1
3614 return [
lindex [get_files -quiet -compile_order sources -used_in synthesis -filter {FILE_TYPE =~ "VHDL*" || FILE_TYPE =~ "*Verilog*" }] end]
3615 }
elseif {[
IsISE]} {
3616 debug::design_graph_mgr -create [current_fileset]
3617 debug::design_graph -add_fileset [current_fileset]
3618 debug::design_graph -update_all
3619 return [
lindex [debug::design_graph -get_compile_order] end]
3621 Msg Error "GetTopFile not yet implemented for this IDE"
3626 proc GetTopModule {} {
3628 return [get_property top [current_fileset]]
3630 Msg Error "GetTopModule not yet implemented for this IDE"
3640 proc GetVer {path {force_develop 0}} {
3644 Msg CriticalWarning "Empty SHA found for ${path}. Commit to Git to resolve this warning."
3647 set p [
lindex $path 0]
3648 if {[
file isdirectory $p]} {
3651 cd [
file dirname $p]
3653 set repo_path [
Git {rev-parse --show-toplevel}]
3656 return [list [
GetVerFromSHA $SHA $repo_path $force_develop] $SHA]
3667 proc GetVerFromSHA {SHA repo_path {force_develop 0}} {
3669 Msg CriticalWarning "Empty SHA found"
3672 lassign [
GitRet "tag --sort=creatordate --contain $SHA -l v*.*.* -l b*v*.*.*"] status result
3675 if {[regexp {^ *$} $result]} {
3677 lassign [
GitRet "log --oneline --pretty=\"%d\""] status2 tag_list
3680 set pattern {tag: v\d+\.\d+\.\d+}
3681 set real_tag_list {}
3682 foreach x $tag_list {
3683 set x_untrimmed [regexp -all -inline $pattern $x]
3684 regsub "tag: " $x_untrimmed "" x_trimmed
3685 set tt [
lindex $x_trimmed 0]
3686 if {![
string equal $tt ""]} {
3687 lappend real_tag_list $tt
3691 Msg Debug "Cleaned up list: $real_tag_list."
3693 set sorted_tags [lsort -decreasing -command CompareVersions $real_tag_list]
3695 Msg Debug "Sorted Tag list: $sorted_tags"
3697 set tag [
lindex $sorted_tags 0]
3700 set pattern {v\d+\.\d+\.\d+}
3701 if {![regexp $pattern $tag]} {
3702 Msg CriticalWarning "No Hog version tags found in this repository."
3707 set repo_conf $repo_path/Top/repo.conf
3711 set hotfix_prefix "hotfix/"
3712 set minor_prefix "minor_version/"
3713 set major_prefix "major_version/"
3715 set enable_develop_branch $force_develop
3717 set branch_name [
Git {rev-parse --abbrev-ref HEAD}]
3719 if {[
file exists $repo_conf]} {
3720 set PROPERTIES [
ReadConf $repo_conf]
3722 if {[dict exists $PROPERTIES main]} {
3723 set mainDict [dict get $PROPERTIES main]
3726 if {[dict exists $mainDict ENABLE_DEVELOP_BRANCH]} {
3727 set enable_develop_branch [dict get $mainDict ENABLE_DEVELOP_BRANCH]
3733 if {[dict exists $PROPERTIES prefixes]} {
3734 set prefixDict [dict get $PROPERTIES prefixes]
3736 if {[dict exists $prefixDict HOTFIX]} {
3737 set hotfix_prefix [dict get $prefixDict HOTFIX]
3739 if {[dict exists $prefixDict MINOR_VERSION]} {
3740 set minor_prefix [dict get $prefixDict MINOR_VERSION]
3742 if {[dict exists $prefixDict MAJOR_VERSION]} {
3743 set major_prefix [dict get $prefixDict MAJOR_VERSION]
3749 if {$enable_develop_branch == 1} {
3750 if {[
string match "$hotfix_prefix*" $branch_name]} {
3755 if {[
string match "$major_prefix*" $branch_name]} {
3757 set version_level major
3758 }
elseif {[
string match "$minor_prefix*" $branch_name] || ($enable_develop_branch == 1 && $is_hotfix == 0)} {
3760 set version_level minor
3763 set version_level patch
3767 Msg CriticalWarning "Tag $tag does not contain a Hog compatible version in this repository."
3770 }
elseif {$mr == 0} {
3771 switch $version_level {
3786 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."
3792 set vers [
split $result "\n"]
3793 set ver [
lindex $vers 0]
3795 if {[regexp {^v.*$} $v]} {
3803 Msg CriticalWarning "Error while trying to find tag for $SHA"
3811 set M [
format %02X $M]
3812 set m [
format %02X $m]
3813 set c [
format %04X $c]
3814 }
elseif {$M > -1} {
3816 set M [
format %02X $M]
3817 set m [
format %02X $m]
3818 set c [
format %04X $c]
3820 Msg Warning "Tag does not contain a properly formatted version: $ver"
3821 set M [
format %02X 0]
3822 set m [
format %02X 0]
3823 set c [
format %04X 0]
3836 proc Git {command {files ""}} {
3837 lassign [
GitRet $command $files] ret result
3839 Msg Error "Code $ret returned by git running: $command -- $files"
3850 proc GetModuleName {filename} {
3852 if {![
file exists $filename]} {
3853 Msg CriticalWarning "Error: File $filename does not exist."
3858 set fileId [open $filename r]
3861 set file_content [read $fileId]
3867 if {[
file extension $filename] == ".vhd" || [
file extension $filename] == ".vhdl"} {
3869 set file_content [
string tolower $file_content]
3871 set pattern {(?m)^\s*entity\s+(\S+)\s+is}
3872 }
elseif {[
file extension $filename] == ".v" || [
file extension $filename] == ".sv"} {
3874 set pattern {\n\s*module\s*(\w+)(\s*|\(|\n)}
3876 Msg Debug "File is neither VHDL nor Verilog... Returning empty string..."
3881 if {[regexp $pattern $file_content match module_name]} {
3884 Msg Debug "No module was found in $filename. Returning an empty string..."
3892 proc GetVerilogGenerics {file} {
3893 set fp [open $file r]
3899 foreach line [
split $data "\n"] {
3900 regsub "^\\s*\/\/.*" $line "" line
3901 regsub "(.*)\/\/.*" $line {\1} line
3902 if {![
string equal $line ""]} {
3903 append lines $line " "
3908 regsub -all {/\*.*\*/} $lines "" lines
3911 set punctuation [list]
3912 foreach char [list "(" ")" ";" "," " " "!" "<=" ":=" "=" "\[" "\]"] {
3913 lappend punctuation $char "\000$char\000"
3917 set tokens [
split [
string map $punctuation $lines] \000]
3919 set parameters [dict create]
3928 foreach token $tokens {
3929 set token [
string trim $token]
3930 if {![
string equal "" $token]} {
3931 if {[
string equal [
string tolower $token] "parameter"]} {
3932 set state $PARAM_NAME
3933 }
elseif {[
string equal $token ")"] || [
string equal $token ";"]} {
3935 }
elseif {$state == $PARAM_WIDTH} {
3936 if {[
string equal $token "\]"]} {
3937 set state $PARAM_NAME
3939 }
elseif {$state == $PARAM_VALUE} {
3940 if {[
string equal $token ","]} {
3941 set state $PARAM_NAME
3942 }
elseif {[
string equal $token ";"]} {
3947 }
elseif {$state == $PARAM_NAME} {
3948 if {[
string equal $token "="]} {
3949 set state $PARAM_VALUE
3950 }
elseif {[
string equal $token "\["]} {
3951 set state $PARAM_WIDTH
3952 }
elseif {[
string equal $token ","]} {
3953 set state $PARAM_NAME
3954 }
elseif {[
string equal $token ";"]} {
3956 }
elseif {[
string equal $token ")"]} {
3959 dict set parameters $token "integer"
3972 proc GetVhdlGenerics {file {entity ""}} {
3973 set fp [open $file r]
3979 foreach line [
split $data "\n"] {
3980 regsub "^\\s*--.*" $line "" line
3981 regsub "(.*)--.*" $line {\1} line
3982 if {![
string equal $line ""]} {
3983 append lines $line " "
3988 set generic_block ""
3989 set generics [dict create]
3991 if {1 == [
string equal $entity ""]} {
3992 regexp {(?i).*entity\s+([^\s]+)\s+is} $lines _ entity
3995 set generics_regexp "(?i).*entity\\s+$entity\\s+is\\s+generic\\s*\\((.*)\\)\\s*;\\s*port.*end.*$entity"
3997 if {[regexp $generics_regexp $lines _ generic_block]} {
3999 foreach line [
split $generic_block ";"] {
4001 regexp {(.*):\s*([A-Za-z0-9_]+).*} $line _ generic type
4004 set splits [
split $generic ","]
4005 foreach split $splits {
4006 dict set generics [
string trim $split] [
string trim $type]
4014 proc GHDL {command logfile} {
4015 set ret [
catch {
exec -ignorestderr ghdl {*}$command >>& $logfile} result options]
4020 return [list $ret $result]
4032 proc GitRet {command {files ""}} {
4035 set ret [
catch {
exec -ignorestderr git {*}$command} result]
4037 set ret [
catch {
exec -ignorestderr git {*}$command -- {*}$files} result]
4039 return [list $ret $result]
4047 proc GitVersion {target_version} {
4048 set ver [
split $target_version "."]
4049 set v [
Git --version]
4051 set current_ver [
split [
lindex $v 2] "."]
4052 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
4053 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
4054 return [
expr {$target <= $current}]
4068 proc HandleIP {what_to_do xci_file ip_path repo_path {gen_dir "."} {force 0}} {
4070 if {!($what_to_do eq "push") && !($what_to_do eq "pull")} {
4071 Msg Error "You must specify push or pull as first argument."
4074 if {[
catch {
package require tar} TARPACKAGE]} {
4075 Msg CriticalWarning "Cannot find package tar. You can fix this by installing package \"tcllib\""
4086 if {[regexp {^[^/]+:} $ip_path]} {
4090 lassign [
ExecuteRet rclone --version] rclone_ret rclone_ver
4091 if {$rclone_ret != 0} {
4092 Msg CriticalWarning "Rclone path specified but rclone not found or failed: $rclone_ver"
4096 Msg Info "IP remote directory path, on Rclone, is set to: $ip_path"
4098 if {[
info exists env(HOG_RCLONE_CONFIG)]} {
4099 Msg Info "Using rclone config from environment variable HOG_RCLONE_CONFIG: $env(HOG_RCLONE_CONFIG)"
4100 set config_path $env(HOG_RCLONE_CONFIG)
4102 set config_path "/dev/null"
4103 Msg Info "Environment variable HOG_RCLONE_CONFIG not set, using rclone environmental variables..."
4106 set remote_name "[
lindex [
split $ip_path ":"] 0]:"
4107 lassign [
ExecuteRet rclone listremotes --config $config_path] rclone_list_ret remotes
4108 if {$rclone_list_ret != 0} {
4109 Msg CriticalWarning "Could not list rclone remotes: $remotes"
4113 if {![
IsInList $remote_name $remotes]} {
4114 Msg CriticalWarning "Rclone remote $remote_name not found among available remotes: $remotes"
4120 }
elseif {[
string first "/eos/" $ip_path] == 0} {
4123 lassign [
eos "ls $ip_path"] ret result
4125 Msg CriticalWarning "Could not run ls for for EOS path: $ip_path (error: $result). \
4126 Either the drectory does not exist or there are (temporary) problem with EOS."
4130 Msg Info "IP remote directory path, on EOS, is set to: $ip_path"
4136 if {!([
file exists $xci_file])} {
4137 Msg CriticalWarning "Could not find $xci_file."
4143 set xci_path [
file dirname $xci_file]
4144 set xci_name [
file tail $xci_file]
4145 set xci_ip_name [
file rootname [
file tail $xci_file]]
4146 set xci_dir_name [
file tail $xci_path]
4147 set gen_path $gen_dir
4149 set hash [
Md5Sum $xci_file]
4150 set file_name $xci_name\_$hash
4152 Msg Info "Preparing to $what_to_do IP: $xci_name..."
4154 if {$what_to_do eq "push"} {
4157 if {$on_rclone == 1} {
4158 lassign [
ExecuteRet rclone ls $ip_path/$file_name.tar --config $config_path] ret result
4163 Msg Info "IP already in the Rclone repository, will not copy..."
4165 Msg Info "IP already in the Rclone repository, will forcefully replace..."
4170 }
elseif {$on_eos == 1} {
4171 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4176 Msg Info "IP already in the EOS repository, will not copy..."
4178 Msg Info "IP already in the EOS repository, will forcefully replace..."
4184 if {[
file exists "$ip_path/$file_name.tar"]} {
4186 Msg Info "IP already in the local repository, will not copy..."
4188 Msg Info "IP already in the local repository, will forcefully replace..."
4197 if {$will_copy == 1} {
4199 Msg Info "Looking for generated files in $gen_path..."
4200 set ip_gen_files [glob -nocomplain $gen_path/*]
4204 if {[
llength $ip_gen_files] > 0} {
4205 Msg Info "Found some IP synthesised files matching $xci_ip_name"
4206 if {$will_remove == 1} {
4207 Msg Info "Removing old synthesised directory $ip_path/$file_name.tar..."
4208 if {$on_rclone == 1} {
4209 lassign [
ExecuteRet rclone delete $ip_path/$file_name.tar --config $config_path] ret result
4211 Msg CriticalWarning "Could not delete file from Rclone: $result"
4213 }
elseif {$on_eos == 1} {
4214 eos "rm -rf $ip_path/$file_name.tar" 5
4216 file delete -force "$ip_path/$file_name.tar"
4220 Msg Info "Creating local archive with IP generated files..."
4223 foreach f $ip_gen_files {
4224 lappend tar_files "[
Relative [
file normalize $repo_path] $f]"
4227 ::tar::create $file_name.tar $tar_files
4229 Msg Info "Copying IP generated files for $xci_name..."
4230 if {$on_rclone == 1} {
4231 lassign [
ExecuteRet rclone copyto $file_name.tar $ip_path/$file_name.tar --config $config_path] ret result
4233 Msg CriticalWarning "Something went wrong when copying the IP files to Rclone. Error message: $result"
4235 }
elseif {$on_eos == 1} {
4236 lassign [
ExecuteRet xrdcp -f -s $file_name.tar $::env(EOS_MGM_URL)//$ip_path/] ret msg
4238 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4241 Copy "$file_name.tar" "$ip_path/"
4243 Msg Info "Removing local archive"
4244 file delete $file_name.tar
4246 Msg Warning "Could not find synthesized files matching $gen_path/$file_name*"
4249 }
elseif {$what_to_do eq "pull"} {
4250 if {$on_rclone == 1} {
4251 lassign [
ExecuteRet rclone ls $ip_path/$file_name.tar --config $config_path] ret result
4253 Msg Info "Nothing for $xci_name was found in the Rclone repository, cannot pull."
4257 Msg Info "IP $xci_name found in the Rclone repository $ip_path, copying it locally to $repo_path..."
4258 lassign [
ExecuteRet rclone copyto $ip_path/$file_name.tar $file_name.tar --config $config_path] ret_copy result_copy
4259 if {$ret_copy != 0} {
4260 Msg CriticalWarning "Something went wrong when copying the IP files from Rclone. Error message: $result_copy"
4263 }
elseif {$on_eos == 1} {
4264 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4266 Msg Info "Nothing for $xci_name was found in the EOS repository, cannot pull."
4270 set remote_tar "$::env(EOS_MGM_URL)//$ip_path/$file_name.tar"
4271 Msg Info "IP $xci_name found in the repository $remote_tar, copying it locally to $repo_path..."
4273 lassign [
ExecuteRet xrdcp -f -r -s $remote_tar $repo_path] ret msg
4275 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4279 if {[
file exists "$ip_path/$file_name.tar"]} {
4280 Msg Info "IP $xci_name found in local repository $ip_path/$file_name.tar, copying it locally to $repo_path..."
4281 Copy $ip_path/$file_name.tar $repo_path
4283 Msg Info "Nothing for $xci_name was found in the local IP repository, cannot pull."
4289 if {[
file exists $file_name.tar]} {
4290 remove_files $xci_file
4291 Msg Info "Extracting IP files from archive to $repo_path..."
4292 ::tar::untar $file_name.tar -dir $repo_path -noperms
4293 Msg Info "Removing local archive"
4294 file delete $file_name.tar
4295 add_files -norecurse -fileset sources_1 $xci_file
4308 proc HexVersionToString {version} {
4309 scan [
string range $version 0 1] %x M
4310 scan [
string range $version 2 3] %x m
4311 scan [
string range $version 4 7] %x c
4316 proc ImportTclLib {} {
4319 if {[
info exists env(HOG_TCLLIB_PATH)]} {
4320 lappend auto_path $env(HOG_TCLLIB_PATH)
4323 puts "ERROR: To run Hog with Microsemi Libero SoC or Lattice Diamond, you need to define the HOG_TCLLIB_PATH variable."
4338 proc InitLauncher {script tcl_path parameters commands argv {custom_commands ""}} {
4339 set repo_path [
file normalize "$tcl_path/../.."]
4341 set bin_path [
file normalize "$tcl_path/../../bin"]
4342 set top_path [
file normalize "$tcl_path/../../Top"]
4344 set cmd_lines [
split $commands "\n"]
4346 set command_options [dict create]
4347 set directive_descriptions [dict create]
4348 set directive_names [dict create]
4349 set common_directive_names [dict create]
4350 set custom_command ""
4351 set custom_command_options ""
4353 foreach l $cmd_lines {
4355 if {[regexp {\\(.*) \{\#} $l minc d]} {
4356 lappend directives_with_projects $d
4360 if {[regexp {\\(.*) \{} $l minc regular_expression]} {
4361 lappend directive_regex $regular_expression
4365 if {[regexp {\#\s*NAME(\*)?:\s*(.*)\s*} $l minc star name]} {
4366 dict set directive_names $name $regular_expression
4368 dict set common_directive_names $name $regular_expression
4371 set directive_names [
DictSort $directive_names]
4372 set common_directive_names [
DictSort $common_directive_names]
4375 if {[regexp {\#\s*DESCRIPTION:\s*(.*)\s*} $l minc x]} {
4376 dict set directive_descriptions $regular_expression $x
4380 if {[regexp {\#\s*OPTIONS:\s*(.*)\s*} $l minc x]} {
4381 dict set command_options $regular_expression [
split [regsub -all {[ \t\n]+} $x {}] ","]
4385 set short_usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nMost common directives (case insensitive):"
4387 dict for {key value} $common_directive_names {
4388 set short_usage "$short_usage\n - $key: [dict get $directive_descriptions $value]"
4392 if {[
string length $custom_commands] > 0} {
4393 Msg Debug "Found custom commands to add to short short_usage."
4394 set short_usage "$short_usage\n\nCustom commands:"
4395 dict for {key command} $custom_commands {
4396 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4397 set short_usage "$short_usage\n - $key: [dict get $command DESCRIPTION]"
4403 set short_usage "$short_usage\n\n\
4404 To see all the available directives, run:\n./Hog/Do HELP\n\n\
4405 To list available options for the chosen directive run:\n\
4406 ./Hog/Do <directive> HELP\n
4409 set usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nDirectives (case insensitive):"
4411 dict for {key value} $directive_names {
4412 set usage "$usage\n - $key: [dict get $directive_descriptions $value]"
4416 if {[
string length $custom_commands] > 0} {
4417 Msg Debug "Found custom commands to add to short usage."
4418 set usage "$usage\n\nCustom commands:"
4419 dict for {key command} $custom_commands {
4420 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4421 set usage "$usage\n - $key: [dict get $command DESCRIPTION]"
4426 set usage "$usage\n\nTo list available options for the chosen directive run:\n./Hog/Do <directive> HELP"
4435 Msg Debug "HogEnv.conf found"
4443 if {[
catch {
package require cmdline} ERROR]} {
4444 Msg Debug "The cmdline Tcl package was not found, sourcing it from Hog..."
4445 source $tcl_path/utils/cmdline.tcl
4448 set argv [regsub -all {(?i) HELP\y} $argv " -help"]
4453 set custom_parameters [list]
4454 dict for {key command} $custom_commands {
4455 set custom_parameters [concat $custom_parameters [dict get $command CUSTOM_OPTIONS]]
4458 lassign [
GetOptions $argv [
concat $custom_parameters $parameters]] option_list arg_list
4460 if {[
IsInList "-all" $option_list]} {
4469 set directive [
string toupper [
lindex $arg_list 0]]
4472 set argument_is_no_project 1
4474 set NO_DIRECTIVE_FOUND 0
4475 switch -regexp -- $directive "$commands"
4476 if {$NO_DIRECTIVE_FOUND == 1} {
4477 if {[
string length $custom_commands] > 0 && [dict exists $custom_commands $directive]} {
4478 set custom_command $directive
4479 set custom_command_hog_parameters [dict get $custom_commands $directive OPTIONS]
4480 set custom_command_options [dict get $custom_commands $directive CUSTOM_OPTIONS]
4481 set custom_command_options [
concat $custom_command_hog_parameters $custom_command_options]
4483 Msg Status "ERROR: Unknown directive $directive.\n\n"
4489 if {[
IsInList $directive $directives_with_projects 1]} {
4490 set argument_is_no_project 0
4494 if {$directive != ""} {
4495 if {[
IsInList $directive $directives_with_projects 1]} {
4496 puts "usage: ./Hog/Do \[OPTIONS\] $directive <project>\n"
4497 }
elseif {[regexp "^COMPSIM(LIB)?$" $directive]} {
4498 puts "usage: ./Hog/Do \[OPTIONS\] $directive <simulator>\n"
4500 puts "usage: ./Hog/Do \[OPTIONS\] $directive \n"
4503 dict for {dir desc} $directive_descriptions {
4504 if {[regexp $dir $directive]} {
4512 if {$custom_command ne ""} {
4513 if {[
llength $custom_command_options] > 0} {
4514 puts "Available options:"
4516 foreach custom_option $custom_command_options {
4517 set n [
llength $custom_option]
4519 lassign $custom_option opt help
4522 }
elseif {$n == 3} {
4523 lassign $custom_option opt def help
4524 puts " -$opt <argument>"
4526 puts " $help (default: $def)"
4531 Msg Warning "Custom option spec has invalid arity (expected 2 or 3): $custom_option"
4536 dict for {dir opts} $command_options {
4537 if {[regexp $dir $directive]} {
4538 puts "Available options:"
4540 foreach par $parameters {
4541 if {$opt == [lindex $par 0]} {
4542 if {[regexp {\.arg$} $opt]} {
4543 set opt_name [regsub {\.arg$} $opt ""]
4544 puts " -$opt_name <argument>"
4548 puts " [lindex $par [llength $par]-1]"
4562 if {$custom_command ne ""} {
4563 set parameters [
concat $parameters $custom_command_options]
4566 if {[
catch {
array set options [
cmdline::getoptions option_list $parameters $usage]} err]} {
4567 Msg Status "\nERROR: Syntax error, probably unknown option.\n\n USAGE: $err"
4571 if {[
llength $arg_list] <= $min_n_of_args || [
llength $arg_list] > $max_n_of_args} {
4572 Msg Status "\nERROR: Wrong number of arguments: [
llength $argv]"
4577 set project [
lindex $arg_list 1]
4579 if {$argument_is_no_project == 0} {
4581 regsub "^(\./)?Top/" $project "" project
4583 regsub "/? *\$" $project "" project
4589 Msg Debug "Option list:"
4590 foreach {key value} [
array get options] {
4591 Msg Debug "$key => $value"
4598 if {$proj_conf != 0} {
4601 lassign [
GetIDECommand $proj_conf] cmd before_tcl_script after_tcl_script end_marker
4602 Msg Info "Project $project uses $cmd IDE"
4605 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4607 if {$custom_command ne ""} {
4608 if { [dict exists $custom_commands $directive IDE] } {
4609 lassign [
GetIDECommand "" [dict get $custom_commands $directive IDE]] cmd before_tcl_script after_tcl_script end_marker
4610 Msg Info "Custom command: $custom_command uses $cmd IDE"
4611 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4613 set command "custom_tcl"
4615 }
elseif {$argument_is_no_project == 1} {
4617 Msg Debug "$project will be used as first argument"
4618 }
elseif {$project != ""} {
4621 }
elseif {$min_n_of_args < 0} {
4634 set project_group [
file dirname $project]
4635 set project_name $project
4636 set project [
file tail $project]
4637 Msg Debug "InitLauncher: project_group=$project_group, project_name=$project_name, project=$project"
4639 return [list $directive $project $project_name $project_group $repo_path $old_path $bin_path $top_path $usage $short_usage $command $cmd [
array get options]]
4646 proc IsCommitAncestor {ancestor commit} {
4647 lassign [
GitRet "merge-base --is-ancestor $ancestor $commit"] status result
4656 return [
expr {[info commands sys_install] != ""}]
4661 return [
expr {[info commands get_libero_version] != ""}]
4670 proc IsInList {element list {regex 0} {nocase 0}} {
4674 if {[regexp -nocase $x $element]} {
4678 if {[regexp $x $element]} {
4682 }
elseif {$regex == 0} {
4684 if {[
string tolower $x] eq [
string tolower $element]} {
4688 if {$x eq $element} {
4701 return [
expr {[string first PlanAhead [version]] == 0}]
4709 if {[
catch {
package require ::quartus::flow} result]} {
4722 proc IsRelativePath {path} {
4723 if {[
string index $path 0] == "/" || [
string index $path 0] == "~"} {
4731 proc IsSynplify {} {
4732 return [
expr {[info commands program_version] != ""}]
4737 return [
expr {![IsQuartus] && ![IsXilinx] && ![IsVitisClassic] && ![IsLibero] && ![IsSynplify] && ![IsDiamond]}]
4745 proc IsVerilog {file} {
4746 if {[
file extension $file] == ".v" || [
file extension $file] == ".sv"} {
4758 proc IsVersal {part} {
4759 if {[get_property ARCHITECTURE [get_parts $part]] eq "versal"} {
4769 return [
expr {[string first Vivado [version]] == 0}]
4777 if {[
info commands version] != ""} {
4778 set current_version [version]
4779 if {[
string first PlanAhead $current_version] == 0 || [
string first Vivado $current_version] == 0} {
4781 }
elseif {[
string first xsct $current_version] == 0} {
4784 Msg Warning "This IDE has the version command but it is not PlanAhead or Vivado: $current_version"
4793 proc IsVitisClassic {} {
4794 return [
expr {[info commands platform] != ""}]
4802 proc IsZynq {part} {
4803 if {[regexp {^(xc7z|xczu).*} $part]} {
4810 proc ImportGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
4811 set list_path "$repo_path/Top/$project_name/list"
4812 lassign [
GetHogFiles -list_files {.src,.ext,.sim} -ext_path $ext_path $list_path $repo_path] src_files properties filesets
4817 set properties [
DictGet $simset_dict "properties"]
4818 set options [
DictGet $properties "options"]
4821 set workdir Projects/$project_name/ghdl
4822 file delete -force $workdir
4824 set import_log "$workdir/ghdl-import-${simset_name}.log"
4825 dict for {lib sources} $src_files {
4826 set libname [file rootname $lib]
4827 foreach f $sources {
4828 if {[file extension $f] != ".vhd" && [file extension $f] != ".vhdl"} {
4829 Msg Info "File $f is not a VHDL file, copying it in workfolder..."
4830 file copy -force $f $workdir
4832 set file_path [Relative $repo_path $f]
4833 set import_log_file [open $import_log "a"]
4834 puts "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
4835 puts $import_log_file "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
4836 close $import_log_file
4837 lassign [GHDL "-i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path" $import_log] ret result
4839 Msg CriticalWarning "GHDL import failed for file $f: $result"
4848 proc LaunchGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
4851 set sim_props [
DictGet $simset_dict "properties"]
4852 set options [
DictGet $sim_props "options"]
4853 set runopts [
DictGet $sim_props "run_options"]
4855 dict for {prop_name prop_val} $sim_props {
4856 set prop_name [string toupper $prop_name]
4857 if {$prop_name == "TOP"} {
4858 set top_sim $prop_val
4861 set workdir $repo_path/Projects/$project_name/ghdl
4862 set make_log "$workdir/ghdl-make-${simset_name}.log"
4863 set run_log "$workdir/ghdl-run-${simset_name}.log"
4866 set make_log_file [open $make_log "w"]
4868 puts "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
4869 puts $make_log_file "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
4870 close $make_log_file
4872 lassign [
GHDL "-m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim" $make_log] ret result
4875 Msg Error "GHDL make failed for $top_sim: $result"
4879 set run_log_file [open $run_log "w"]
4880 puts "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
4881 puts $run_log_file "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
4884 lassign [
GHDL "-r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts" $run_log] ret result
4888 Msg Error "GHDL run failed for $top_sim: $result"
4903 proc LaunchImplementation {reset do_create run_folder project_name {repo_path .} {njobs 4} {do_bitstream 0}} {
4904 Msg Info "Starting implementation flow..."
4906 if {$reset == 1 && $do_create == 0} {
4907 Msg Info "Resetting run before launching implementation..."
4912 source $repo_path/Hog/Tcl/integrated/pre-implementation.tcl
4915 if {$do_bitstream == 1} {
4916 launch_runs impl_1 -to_step [
BinaryStepName [get_property PART [current_project]]] -jobs $njobs -dir $run_folder
4918 launch_runs impl_1 -jobs $njobs -dir $run_folder
4923 Msg Info "running post-implementation"
4924 source $repo_path/Hog/Tcl/integrated/post-implementation.tcl
4925 if {$do_bitstream == 1} {
4926 Msg Info "running pre-bitstream"
4927 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
4928 Msg Info "running post-bitstream"
4929 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
4933 set prog [get_property PROGRESS [get_runs impl_1]]
4934 set status [get_property STATUS [get_runs impl_1]]
4935 Msg Info "Run: impl_1 progress: $prog, status : $status"
4939 set status_file [open "$run_folder/timing.txt" "w"]
4940 puts $status_file "## $project_name Timing summary"
4942 set f [open [
lindex [glob "$run_folder/impl_1/*.twr" 0]]]
4944 while {[
gets $f line] >= 0} {
4945 if {[
string match "Timing summary:" $line]} {
4946 while {[
gets $f line] >= 0} {
4947 if {[
string match "Timing errors:*" $line]} {
4948 set errs [regexp -inline -- {[0-9]+} $line]
4950 if {[
string match "*Footnotes*" $line]} {
4953 puts $status_file "$line"
4962 Msg Info "Time requirements are met"
4963 file rename -force "$run_folder/timing.txt" "$run_folder/timing_ok.txt"
4966 Msg CriticalWarning "Time requirements are NOT met"
4967 file rename -force "$run_folder/timing.txt" "$run_folder/timing_error.txt"
4973 set wns [get_property STATS.WNS [get_runs [current_run]]]
4974 set tns [get_property STATS.TNS [get_runs [current_run]]]
4975 set whs [get_property STATS.WHS [get_runs [current_run]]]
4976 set ths [get_property STATS.THS [get_runs [current_run]]]
4977 set tpws [get_property STATS.TPWS [get_runs [current_run]]]
4979 if {$wns >= 0 && $whs >= 0 && $tpws >= 0} {
4980 Msg Info "Time requirements are met"
4981 set status_file [open "$run_folder/timing_ok.txt" "w"]
4984 Msg CriticalWarning "Time requirements are NOT met"
4985 set status_file [open "$run_folder/timing_error.txt" "w"]
4989 Msg Status "*** Timing summary ***"
4990 Msg Status "WNS: $wns"
4991 Msg Status "TNS: $tns"
4992 Msg Status "WHS: $whs"
4993 Msg Status "THS: $ths"
4994 Msg Status "TPWS: $tpws"
5000 puts $status_file "## $project_name Timing summary"
5002 m add row "| **Parameter** | \"**value (ns)**\" |"
5003 m add row "| --- | --- |"
5004 m add row "| WNS: | $wns |"
5005 m add row "| TNS: | $tns |"
5006 m add row "| WHS: | $whs |"
5007 m add row "| THS: | $ths |"
5008 m add row "| TPWS: | $tpws |"
5010 puts $status_file [m format 2string]
5011 puts $status_file "\n"
5012 if {$timing_ok == 1} {
5013 puts $status_file " Time requirements are met."
5015 puts $status_file "Time requirements are **NOT** met."
5017 puts $status_file "\n\n"
5021 if {$prog ne "100%"} {
5022 Msg Error "Implementation error"
5027 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5029 Msg Info "Git describe set to $describe"
5031 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5036 if {[
file exists $run_folder/versions.txt]} {
5037 file copy -force $run_folder/versions.txt $dst_dir
5039 Msg Warning "No versions file found in $run_folder/versions.txt"
5042 set timing_files [glob -nocomplain "$run_folder/timing_*.txt"]
5043 set timing_file [
file normalize [
lindex $timing_files 0]]
5045 if {[
file exists $timing_file]} {
5046 file copy -force $timing_file $dst_dir/
5048 Msg Warning "No timing file found, not a problem if running locally"
5052 if {[
IsVersal [get_property part [current_project]]]} {
5053 if {[get_property segmented_configuration [current_project]] == 1} {
5054 Msg Info "Versal Segmented configuration detected: exporting XSA file..."
5055 set xsa_name "$dst_dir/[
file tail $project_name]\-$describe.xsa"
5056 write_hw_platform -fixed -force -file $xsa_name
5060 set revision [get_current_revision]
5062 if {[
catch {execute_module -tool fit} result]} {
5063 Msg Error "Result: $result\n"
5064 Msg Error "Place & Route failed. See the report file.\n"
5066 Msg Info "\nINFO: Place & Route was successful for revision $revision.\n"
5069 if {[
catch {execute_module -tool sta -args "--do_report_timing"} result]} {
5070 Msg Error "Result: $result\n"
5071 Msg Error "Time Quest failed. See the report file.\n"
5073 Msg Info "Time Quest was successfully run for revision $revision.\n"
5076 set panel "Timing Analyzer||Timing Analyzer Summary"
5077 set device [get_report_panel_data -name $panel -col 1 -row_name "Device Name"]
5078 set timing_model [get_report_panel_data -name $panel -col 1 -row_name "Timing Models"]
5079 set delay_model [get_report_panel_data -name $panel -col 1 -row_name "Delay Model"]
5081 Msg Info "*******************************************************************"
5082 Msg Info "Device: $device"
5083 Msg Info "Timing Models: $timing_model"
5084 Msg Info "Delay Model: $delay_model"
5087 Msg Info "*******************************************************************"
5090 Msg Info "Starting implementation flow..."
5091 if {[
catch {run_tool -name {PLACEROUTE}}]} {
5092 Msg Error "PLACEROUTE FAILED!"
5094 Msg Info "PLACEROUTE PASSED."
5098 Msg Info "Run VERIFYTIMING ..."
5099 if {[
catch {run_tool -name {VERIFYTIMING} -script {Hog/Tcl/integrated/libero_timing.tcl}}]} {
5100 Msg CriticalWarning "VERIFYTIMING FAILED!"
5102 Msg Info "VERIFYTIMING PASSED \n"
5108 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5110 Msg Info "Git describe set to $describe"
5112 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5113 file mkdir $dst_dir/reports
5116 if {[
file exists $run_folder/versions.txt]} {
5117 file copy -force $run_folder/versions.txt $dst_dir
5119 Msg Warning "No versions file found in $run_folder/versions.txt"
5122 set timing_file_path [
file normalize "$repo_path/Projects/timing_libero.txt"]
5123 if {[
file exists $timing_file_path]} {
5124 file copy -force $timing_file_path $dst_dir/reports/Timing.txt
5125 set timing_file [open $timing_file_path "r"]
5126 set status_file [open "$dst_dir/timing.txt" "w"]
5127 puts $status_file "## $project_name Timing summary\n\n"
5128 puts $status_file "| | |"
5129 puts $status_file "| --- | --- |"
5130 while {[
gets $timing_file line] >= 0} {
5131 if {[
string match "SUMMARY" $line]} {
5132 while {[
gets $timing_file line] >= 0} {
5133 if {[
string match "END SUMMARY" $line]} {
5136 if {[
string first ":" $line] == -1} {
5139 set out_string "| [
string map {: | } $line] |"
5140 puts $status_file "$out_string"
5145 Msg Warning "No timing file found, not a problem if running locally"
5150 set force_rst "-forceOne"
5152 prj_run Map $force_rst
5153 prj_run PAR $force_rst
5165 proc GenerateBitstreamOnly {project_name {repo_path .}} {
5167 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5169 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5171 cd Projects/$project_name/$project_name.runs/impl_1
5172 Msg Info "Running pre-bitstream..."
5173 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5175 Msg Info "Writing bitstream for $project_name..."
5177 write_bitstream -force $dst_dir/$project_name-$describe.bit
5179 Msg Info "Running post-bitstream..."
5180 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5189 proc LaunchSimulation {project_name lib_path simsets {repo_path .} {scripts_only 0} {compile_only 0}} {
5192 set project [
file tail $project_name]
5193 set main_sim_folder [
file normalize "$repo_path/Projects/$project_name/$project.sim/"]
5195 if {$simsets != ""} {
5196 dict for {simset sim_dict} $simsets {
5197 lappend simsets_todo $simset
5199 Msg Info "Will run only the following simulation's sets (if they exist): $simsets_todo"
5202 if {$scripts_only == 1} {
5203 Msg Info "Only generating simulation scripts, not running simulations..."
5206 if {$compile_only == 1} {
5207 Msg Info "Only compiling simulation libraries, not running simulations..."
5212 set sim_dic [dict create]
5214 Msg Info "Retrieving list of simulation sets..."
5215 foreach s [get_filesets] {
5217 set use_simpass_str 0
5220 set type [get_property FILESET_TYPE $s]
5221 if {$type eq "SimulationSrcs"} {
5222 if {$simsets_todo != "" && $s ni $simsets_todo} {
5223 Msg Info "Skipping $s as it was not specified with the -simset option..."
5226 set sim_dict [
DictGet $simsets $s]
5227 set simulator [
DictGet $sim_dict "simulator"]
5228 set_property "target_simulator" $simulator [current_project]
5229 set hog_sim_props [
DictGet $sim_dict "hog"]
5230 dict for {prop_name prop_val} $hog_sim_props {
5231 # If HOG_SIMPASS_STR is set, use the HOG_SIMPASS_STR string to search for in logs, after simulation is done
5232 if {[string toupper $prop_name] == "HOG_SIMPASS_STR" && $prop_val != ""} {
5233 Msg Info "Setting simulation pass string as '$prop_val'"
5234 set use_simpass_str 1
5235 set simpass_str $prop_val
5237 if {[string toupper $prop_name] == "HOG_SILENT_SIM" && $prop_val == 1} {
5238 set quiet_sim " -quiet"
5244 Msg Info "Creating simulation scripts for $s..."
5245 if {[
file exists $repo_path/Top/$project_name/pre-simulation.tcl]} {
5246 Msg Info "Running $repo_path/Top/$project_name/pre-simulation.tcl"
5247 source $repo_path/Top/$project_name/pre-simulation.tcl
5249 if {[
file exists $repo_path/Top/$project_name/pre-$s-simulation.tcl]} {
5250 Msg Info "Running $repo_path/Top/$project_name/pre-$s-simulation.tcl"
5251 source Running $repo_path/Top/$project_name/pre-$s-simulation.tcl
5253 current_fileset -simset $s
5254 set sim_dir $main_sim_folder/$s/behav
5255 set sim_output_logfile $sim_dir/xsim/simulate.log
5256 if {([
string tolower $simulator] eq "xsim")} {
5257 set sim_name "xsim:$s"
5259 set simulation_command "launch_simulation $quiet_sim -simset [get_filesets $s]"
5260 if {[
catch $simulation_command log]} {
5263 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5264 lappend failed $sim_name
5269 if {$use_simpass_str == 1} {
5272 set file_desc [open $sim_output_logfile r]
5273 set log [read $file_desc]
5276 Msg Info "Searching for simulation pass string: '$simpass_str'"
5277 if {[
string first $simpass_str $log] == -1} {
5278 Msg CriticalWarning "Simulation failed for $s, error info: '$simpass_str' NOT found!"
5279 lappend failed $sim_name
5282 lappend success $sim_name
5286 lappend success $sim_name
5290 Msg Info "Simulation library path is set to $lib_path."
5292 if {!([
file exists $lib_path])} {
5293 Msg Warning "Could not find simulation library path: $lib_path, $simulator simulation will not work."
5297 if {$simlib_ok == 1} {
5298 set_property "compxlib.${simulator}_compiled_library_dir" [
file normalize $lib_path] [current_project]
5299 launch_simulation -scripts_only -simset [get_filesets $s]
5300 set top_name [get_property TOP $s]
5301 set sim_script [
file normalize $sim_dir/$simulator/]
5302 Msg Info "Adding simulation script location $sim_script for $s..."
5303 lappend sim_scripts $sim_script
5304 dict append sim_dic $sim_script $s
5306 Msg Error "Cannot run $simulator simulations without a valid library path"
5313 if {[
info exists sim_scripts] && $scripts_only == 0} {
5315 Msg Info "Generating IP simulation targets, if any..."
5317 foreach ip [get_ips] {
5318 generate_target simulation -quiet $ip
5323 Msg Info "====== Starting simulations runs ======"
5326 foreach s $sim_scripts {
5328 set cmd ./compile.sh
5329 Msg Info " ************* Compiling: $s ************* "
5331 set sim_name "comp:[dict get $sim_dic $s]"
5333 Msg CriticalWarning "Compilation failed for $s, error info: $::errorInfo"
5334 lappend failed $sim_name
5336 lappend success $sim_name
5338 Msg Info "###################### Compilation log starts ######################"
5339 Msg Info "\n\n$log\n\n"
5340 Msg Info "###################### Compilation log ends ######################"
5342 if {$compile_only == 1} {
5345 if {[
file exists "./elaborate.sh"] } {
5346 set cmd ./elaborate.sh
5347 Msg Info " ************* Elaborating: $s ************* "
5349 set sim_name "elab:[dict get $sim_dic $s]"
5351 Msg CriticalWarning "Elaboration failed for $s, error info: $::errorInfo"
5352 lappend failed $sim_name
5354 lappend success $sim_name
5356 Msg Info "###################### Elaboration log starts ######################"
5357 Msg Info "\n\n$log\n\n"
5358 Msg Info "###################### Elaboration log ends ######################"
5360 set cmd ./simulate.sh
5361 Msg Info " ************* Simulating: $s ************* "
5366 if {$use_simpass_str == 1} {
5367 if {[
string first $simpass_str $log] == -1} {
5371 Msg Debug "Simulation pass string not set, relying on simulator exit code."
5375 set sim_name "sim:[dict get $sim_dic $s]"
5377 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5378 lappend failed $sim_name
5380 lappend success $sim_name
5382 Msg Info "###################### Simulation log starts ######################"
5383 Msg Info "\n\n$log\n\n"
5384 Msg Info "###################### Simulation log ends ######################"
5389 if {[
llength $success] > 0} {
5390 set successes [
join $success "\n"]
5391 Msg Info "The following simulation sets were successful:\n\n$successes\n\n"
5394 if {[
llength $failed] > 0} {
5395 set failures [
join $failed "\n"]
5396 Msg Error "The following simulation sets have failed:\n\n$failures\n\n"
5398 }
elseif {[
llength $success] > 0} {
5399 Msg Info "All the [
llength $success] compilations, elaborations and simulations were successful."
5402 Msg Info "Simulation done."
5404 Msg Warning "Simulation is not yet supported for [
GetIDEName]."
5412 proc LaunchRTLAnalysis {repo_path} {
5414 Msg Info "Starting RTL Analysis..."
5416 synth_design -rtl -name rtl_1
5418 Msg Warning "RTL Analysis is not yet supported for [
GetIDEName]."
5431 proc LaunchSynthesis {reset do_create run_folder project_name {repo_path .} {ext_path ""} {njobs 4}} {
5433 if {$reset == 1 && $do_create == 0} {
5434 Msg Info "Resetting run before launching synthesis..."
5438 source $repo_path/Hog/Tcl/integrated/pre-synthesis.tcl
5440 launch_runs synth_1 -jobs $njobs -dir $run_folder
5442 set prog [get_property PROGRESS [get_runs synth_1]]
5443 set status [get_property STATUS [get_runs synth_1]]
5444 Msg Info "Run: synth_1 progress: $prog, status : $status"
5451 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path $ext_path] sha
5453 Msg Info "Git describe set to $describe"
5456 set xci_file [get_property IP_FILE $ip]
5458 set xci_path [
file dirname $xci_file]
5459 set xci_ip_name [
file rootname [
file tail $xci_file]]
5460 foreach rptfile [glob -nocomplain -directory $xci_path *.rpt] {
5461 file copy $rptfile $repo_path/bin/$project_name-$describe/reports
5465 if {$prog ne "100%"} {
5466 Msg Error "Synthesis error, status is: $status"
5470 set project [
file tail [
file rootname $project_name]]
5472 Msg Info "Number of jobs set to $njobs."
5473 set_global_assignment -name NUM_PARALLEL_PROCESSORS $njobs
5477 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$project_name] $repo_path] sha
5480 set revision [get_current_revision]
5483 set tool_and_command [
split [get_global_assignment -name PRE_FLOW_SCRIPT_FILE] ":"]
5484 set tool [
lindex $tool_and_command 0]
5485 set pre_flow_script [
lindex $tool_and_command 1]
5486 set cmd "$tool -t $pre_flow_script quartus_map $project $revision"
5492 Msg Warning "Can not execute command $cmd"
5493 Msg Warning "LOG: $log"
5495 Msg Info "Pre flow script executed!"
5499 if {![is_project_open]} {
5500 Msg Info "Re-opening project file $project_name..."
5501 project_open $project -current_revision
5505 if {[
catch {execute_module -tool ipg -args "--clean"} result]} {
5506 Msg Error "Result: $result\n"
5507 Msg Error "IP Generation failed. See the report file.\n"
5509 Msg Info "IP Generation was successful for revision $revision.\n"
5513 if {[
catch {execute_module -tool map -args "--parallel"} result]} {
5514 Msg Error "Result: $result\n"
5515 Msg Error "Analysis & Synthesis failed. See the report file.\n"
5517 Msg Info "Analysis & Synthesis was successful for revision $revision.\n"
5521 defvar_set -name RWNETLIST_32_64_MIXED_FLOW -value 0
5523 Msg Info "Run SYNTHESIS..."
5524 if {[
catch {run_tool -name {SYNTHESIZE}}]} {
5525 Msg Error "SYNTHESIZE FAILED!"
5527 Msg Info "SYNTHESIZE PASSED!"
5533 set force_rst "-forceOne"
5535 prj_run Synthesis $force_rst
5536 if {[prj_syn] == "synplify"} {
5537 prj_run Translate $force_rst
5540 Msg Error "Impossible condition. You need to run this in an IDE."
5551 proc LaunchVitisBuild {project_name {repo_path .} {stage "presynth"}} {
5552 set proj_name $project_name
5553 set bin_dir [
file normalize "$repo_path/bin"]
5557 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
5558 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$proj_name] $repo_path] commit version hog_hash hog_ver top_hash top_ver \
5559 libs hashes vers cons_ver cons_hash ext_names ext_hashes xml_hash xml_ver user_ip_repos user_ip_hashes user_ip_vers
5561 if {$commit == 0 } {
set commit $this_commit}
5565 foreach app_name [dict keys $ws_apps] {
5566 app config -name $app_name -set build-config Release
5569 WriteGenerics "vitisbuild" $repo_path $proj_name $date $timee $commit $version $top_hash $top_ver $hog_hash $hog_ver $cons_ver $cons_hash $libs \
5570 $vers $hashes $ext_names $ext_hashes $user_ip_repos $user_ip_vers $user_ip_hashes $flavour $xml_ver $xml_hash
5571 foreach app_name [dict keys $ws_apps] { app build -name $app_name}
5573 if {$stage == "presynth"} {
5574 Msg Info "Done building apps for $project_name..."
5578 Msg Info "Evaluating Git sha for $project_name..."
5579 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5582 Msg Info "Hog describe set to: $describe"
5583 set dst_dir [
file normalize "$bin_dir/$proj_name\-$describe"]
5584 if {![
file exists $dst_dir]} {
5585 Msg Info "Creating $dst_dir..."
5589 foreach app_name [dict keys $ws_apps] {
5590 set main_file "$repo_path/Projects/$project_name/vitis_classic/$app_name/Release/$app_name.elf"
5591 set dst_main [
file normalize "$dst_dir/[
file tail $proj_name]\-$app_name\-$describe.elf"]
5593 if {![
file exists $main_file]} {
5594 Msg Error "No Vitis .elf file found. Perhaps there was an issue building it?"
5598 Msg Info "Copying main binary file $main_file into $dst_main..."
5599 file copy -force $main_file $dst_main
5608 proc GetProcFromProps {repo_path props platform} {
5609 if {[dict exists $props "platform:$platform" "BIF"]} {
5610 set bif_file [dict get $props "platform:$platform" "BIF"]
5612 set bif_file "$repo_path/$bif_file"
5616 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
5626 proc GetBifFromProps {repo_path props platform} {
5627 if {[dict exists $props "platform:$platform" "BIF"]} {
5628 set bif_file [dict get $props "platform:$platform" "BIF"]
5630 set bif_file "$repo_path/$bif_file"
5634 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
5643 proc GetPartFromProps {props} {
5644 if {[dict exists $props "main" "PART"]} {
5645 return [
string tolower [dict get $props "main" "PART"]]
5647 Msg Error "Part number not found in properties"
5656 proc GetArchFromPart {part} {
5658 if {[
string match "xczu*" $part]} {
5660 }
elseif {[
string match "xc7z*" $part]} {
5662 }
elseif {[
string match "xck26*" $part]} {
5665 Msg CriticalWarning "Unknown part number: $part"
5674 proc GetAppsFromProps {props {list_names 0}} {
5675 set prop_apps [dict filter $props key {app:*}]
5676 set apps [dict create]
5677 set app_names [list]
5679 dict for {app_key app_value} $prop_apps {
5680 if {[regexp {^app:(.+)$} $app_key -> app_name]} {
5681 set app_name [string trim [string tolower $app_name]]
5682 # Convert only the keys of the inner dictionary to lowercase
5683 set app_value_lower [dict create]
5684 dict for {key value} $app_value {
5685 dict set app_value_lower [string tolower $key] $value
5687 dict set apps $app_name $app_value_lower
5688 lappend app_names $app_name
5691 if {$list_names eq 1} {
5702 proc GetPlatformsFromProps {props {list_names 0} {lower_case 0}} {
5703 set platforms [dict create]
5704 set platform_names [list]
5705 set prop_platforms [dict filter $props key {platform:*}]
5707 dict for {platform_key platform_value} $prop_platforms {
5708 if {[regexp {^platform:(.+)$} $platform_key -> platform_name]} {
5709 if {$lower_case == 1} {
5710 set platform_name [string trim [string tolower $platform_name]]
5712 set platform_name [string trim $platform_name]
5714 dict set platforms $platform_name $platform_value
5715 lappend platform_names $platform_name
5718 if {$list_names eq 1} {
5719 return $platform_names
5734 proc GenerateBootArtifacts {properties repo_path proj_dir bin_dir proj_name describe bitfile mmi_file} {
5735 set elf_list [glob -nocomplain "$bin_dir/*.elf"]
5739 if {[
llength $elf_list] == 0} {
5740 Msg Warning "No ELF files found in $bin_dir, skipping generation of boot artifacts"
5744 if {![
file exists $bitfile]} {
5745 Msg Warning "Bitfile $bitfile does not exist, skipping generation of boot artifacts"
5749 Msg Info "Generating boot artifacts for $proj_name..."
5750 Msg Info "Found apps: $apps"
5751 Msg Info "Found platforms: $platforms"
5755 foreach elf_file $elf_list {
5756 set elf_name [
file rootname [
file tail $elf_file]]
5757 Msg Info "Found elf name: $elf_name"
5758 Msg Info "Removing $describe from elf"
5761 if {[regexp "^(.+)-(.+)-$describe\$" $elf_name -> project_name elf_app]} {
5762 set elf_app [
string trim [
string tolower $elf_app]]
5763 Msg Info "Found elf_app: $elf_app"
5765 Msg Error "Could not extract app name from elf file: $elf_name"
5768 Msg Info "Removed project name ($project_name) and $describe from elf"
5770 set app_conf [dict get $apps $elf_app]
5771 set plat [dict get $app_conf "platform"]
5772 set app_proc [dict get $app_conf "proc"]
5775 if {[regexp -nocase {microblaze|risc} $app_proc]} {
5776 Msg Info "Detected soft processor ($app_proc) for $elf_app, updating bitstream memory with ELF file..."
5779 if {[dict size $proc_map] == 0} {
5780 Msg Error "Failed to read map from $proc_map_file"
5783 Msg Info "Found processor map: $proc_map"
5785 set proc_cell [
lindex [
split [dict get $proc_map $app_proc] ":"] 1]
5786 Msg Info "Updating memory at processor cell: $proc_cell"
5788 set update_mem_cmd "updatemem -force -meminfo $mmi_file -data $elf_file -bit $bitfile -proc $proc_cell -out $bitfile"
5789 set ret [
catch {
exec -ignorestderr {*}$update_mem_cmd >@ stdout} result]
5791 Msg Error "Error updating memory for $elf_app: $result"
5793 Msg Info "Done updating memory for $elf_app"
5796 Msg Info "Detected hard processor ($app_proc) for $elf_app. Make sure the .elf file is defined in the platform ($plat)\
5797 .bif file to be included in the bootable binary image (.bin) generation."
5803 foreach plat $platforms {
5805 if {$bif_file != ""} {
5806 Msg Info "Generating bootable binary image (.bin) for $plat"
5808 Msg Info "Architecture: $arch"
5809 Msg Info "BIF file: $bif_file"
5810 set bootgen_cmd "bootgen -arch $arch -image $bif_file -o i $bin_dir/$proj_name-$plat-$describe.bin -w on"
5811 set ret [
catch {
exec -ignorestderr {*}$bootgen_cmd >@ stdout} result]
5813 Msg Error "Error generating bootable binary image (.bin) for $elf_app: $result"
5815 Msg Info "Done generating bootable binary image (.bin) for $plat"
5824 proc ReadProcMap {proc_map_file} {
5825 set proc_map [dict create]
5826 if {[
file exists $proc_map_file]} {
5827 set f [open $proc_map_file "r"]
5828 while {[
gets $f line] >= 0} {
5829 Msg Debug "Line: $line"
5830 if {[regexp {^(\S+)\s+(.+)$} $line -> key value]} {
5831 Msg Debug "Found key: $key, value: $value in proc map file"
5832 dict set proc_map $key $value
5846 proc ListProjects {{repo_path .} {print 1} {ret_conf 0}} {
5847 set top_path [
file normalize $repo_path/Top]
5848 set confs [
findFiles [
file normalize $top_path] hog.conf]
5850 set confs [lsort $confs]
5854 set p [
Relative $top_path [
file dirname $c]]
5857 if {$description eq "test"} {
5858 set description " - Test project"
5859 }
elseif {$description ne ""} {
5860 set description " - $description"
5863 if {$print == 1 || $description ne " - Test project"} {
5865 set g [
file dirname $p]
5876 if {$ret_conf == 0} {
5888 proc Md5Sum {file_name} {
5889 if {!([
file exists $file_name])} {
5890 Msg Warning "Could not find $file_name."
5893 if {[
catch {
package require md5 2.0.7} result]} {
5894 Msg Warning "Tcl package md5 version 2.0.7 not found ($result), will use command line..."
5895 set hash [
lindex [
Execute md5sum $file_name] 0]
5897 set file_hash [
string tolower [md5::md5 -hex -file $file_name]]
5911 proc MergeDict {dict0 dict1 {remove_duplicates 1}} {
5912 set outdict [dict merge $dict1 $dict0]
5913 foreach key [dict keys $dict1] {
5914 if {[dict exists $dict0 $key]} {
5915 set temp_list [dict get $dict1 $key]
5916 foreach item $temp_list {
5918 if {[
IsInList $item [
DictGet $outdict $key]] == 0 || $remove_duplicates == 0} {
5920 dict lappend outdict $key $item
5932 proc MoveElementToEnd {inputList element} {
5933 set index [lsearch $inputList $element]
5935 set inputList [
lreplace $inputList $index $index]
5936 lappend inputList $element
5945 proc OpenProject {project_file repo_path} {
5947 open_project $project_file
5949 set project_folder [
file dirname $project_file]
5950 set project [
file tail [
file rootname $project_file]]
5951 if {[
file exists $project_folder]} {
5953 if {![is_project_open]} {
5954 Msg Info "Opening existing project file $project_file..."
5955 project_open $project -current_revision
5958 Msg Error "Project directory not found for $project_file."
5962 Msg Info "Opening existing project file $project_file..."
5964 open_project -file $project_file -do_backup_on_convert 1 -backup_file {./Projects/$project_file.zip}
5966 Msg Info "Opening existing project file $project_file..."
5967 prj_project open $project_file
5969 Msg Error "This IDE is currently not supported by Hog. Exiting!"
5976 return $tcl_platform(platform)
5986 proc ParseJSON {JSON_FILE JSON_KEY} {
5987 set result [
catch {
package require Tcl 8.4} TclFound]
5988 if {"$result" != "0"} {
5989 Msg CriticalWarning "Cannot find Tcl package version equal or higher than 8.4.\n $TclFound\n Exiting"
5993 set result [
catch {
package require json} JsonFound]
5994 if {"$result" != "0"} {
5995 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
5998 set JsonDict [json::json2dict $JSON_FILE]
5999 set result [
catch {dict get $JsonDict $JSON_KEY} RETURNVALUE]
6000 if {"$result" != "0"} {
6001 Msg CriticalWarning "Cannot find $JSON_KEY in $JSON_FILE\n Exiting"
6014 proc ProjectExists {project {repo_path .}} {
6015 set index [lsearch -exact [
ListProjects $repo_path 0] $project]
6021 Msg Error "Project $project not found in repository $repo_path"
6032 proc ReadConf {file_name} {
6033 if {[
catch {
package require inifile 0.2.3} ERROR]} {
6034 Msg Error "Could not find inifile package version 0.2.3 or higher.\n
6035 To use ghdl, libero or diamond with Hog, you need to install the tcllib package\n
6036 You can install it with 'sudo apt install tcllib' on Debian/Ubuntu or 'sudo dnf install tcllib' on Fedora/RedHat/CentOs."
6040 ::ini::commentchar "#"
6041 set f [::ini::open $file_name]
6042 set properties [dict create]
6043 foreach sec [::ini::sections $f] {
6045 if {$new_sec == "files"} {
6048 set key_pairs [::ini::get $f $sec]
6050 regsub -all {\{\"} $key_pairs "\{" key_pairs
6051 regsub -all {\"\}} $key_pairs "\}" key_pairs
6053 dict set properties $new_sec [dict create {*}$key_pairs]
6066 proc ReadExtraFileList {extra_file_name} {
6067 set extra_file_dict [dict create]
6068 if {[
file exists $extra_file_name]} {
6069 set file [open $extra_file_name "r"]
6070 set file_data [read $file]
6073 set data [
split $file_data "\n"]
6074 foreach line $data {
6075 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
6076 set ip_and_md5 [regexp -all -inline {\S+} $line]
6077 dict lappend extra_file_dict "[
lindex $ip_and_md5 0]" "[
lindex $ip_and_md5 1]"
6081 return $extra_file_dict
6101 proc ReadListFile {args} {
6104 if {[
catch {
package require cmdline} ERROR]} {
6105 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
6111 {lib.arg "" "The name of the library files will be added to, if not given will be extracted from the file name."}
6112 {fileset.arg "" "The name of the library, from the main list file"}
6113 {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."}
6114 {print_log "If set, will use PrintFileTree for the VIEW directive"}
6115 {indent.arg "" "Used to indent files with the VIEW directive"}
6118 set usage "USAGE: ReadListFile \[options\] <list file> <path>"
6119 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
6125 set list_file [
lindex $args 0]
6126 set path [
lindex $args 1]
6127 set sha_mode $options(sha_mode)
6128 set lib $options(lib)
6129 set fileset $options(fileset)
6130 set print_log $options(print_log)
6131 set indent $options(indent)
6133 if {$sha_mode == 1} {
6134 set sha_mode_opt "-sha_mode"
6139 if {$print_log == 1} {
6140 set print_log_opt "-print_log"
6142 set print_log_opt ""
6147 set lib [
file rootname [
file tail $list_file]]
6149 set fp [open $list_file r]
6150 set file_data [read $fp]
6152 set list_file_ext [
file extension $list_file]
6153 switch $list_file_ext {
6155 if {$fileset eq ""} {
6161 set fileset "constrs_1"
6164 set fileset "sources_1"
6168 set libraries [dict create]
6169 set filesets [dict create]
6170 set properties [dict create]
6172 set data [
split $file_data "\n"]
6174 set n [
llength $data]
6176 if {$print_log == 1} {
6177 if {$indent eq ""} {
6178 set list_file_rel [
file tail $list_file]
6179 Msg Status "\n$list_file_rel"
6183 Msg Debug "$n lines read from $list_file."
6186 foreach line $data {
6188 if {![regexp {^[\t\s]*$} $line] & ![regexp {^[\t\s]*\#} $line]} {
6189 set file_and_prop [regexp -all -inline {\S+} $line]
6190 set srcfile [
lindex $file_and_prop 0]
6191 set srcfile "$path/$srcfile"
6193 set srcfiles [glob -nocomplain $srcfile]
6196 if {$srcfiles != $srcfile && ![
string equal $srcfiles ""]} {
6197 Msg Debug "Wildcard source expanded from $srcfile to $srcfiles"
6199 if {![
file exists $srcfile]} {
6200 if {$print_log == 0} {
6201 Msg CriticalWarning "File: $srcfile (from list file: $list_file) does not exist."
6207 foreach vhdlfile $srcfiles {
6208 if {[
file exists $vhdlfile]} {
6209 set vhdlfile [
file normalize $vhdlfile]
6210 set extension [
file extension $vhdlfile]
6212 set prop [
lrange $file_and_prop 1 end]
6215 set library [
lindex [regexp -inline {\ylib\s*=\s*(.+?)\y.*} $prop] 1]
6216 if {$library == ""} {
6220 if {$extension == $list_file_ext} {
6223 set ref_path [
lindex [regexp -inline {\ypath\s*=\s*(\S+).*} $prop] 1]
6224 if {$ref_path eq ""} {
6227 set ref_path [
file normalize $path/$ref_path]
6229 Msg Debug "List file $vhdlfile found in list file, recursively opening it using path \"$ref_path\"..."
6230 if {$print_log == 1} {
6231 if {[
file normalize $last_printed] ne [
file normalize $vhdlfile]} {
6232 Msg Status "$indent Inside [
file tail $vhdlfile]:"
6236 lassign [
ReadListFile {*}"-indent \" $indent\" -lib $library -fileset $fileset $sha_mode_opt $print_log_opt $vhdlfile $ref_path"] l p fs
6238 set properties [
MergeDict $p $properties]
6240 }
elseif {[lsearch {.src .sim .con ReadExtraFileList} $extension] >= 0} {
6242 Msg Error "$vhdlfile cannot be included into $list_file, $extension files must be included into $extension files."
6245 regsub -all " *= *" $prop "=" prop
6249 if {[
string first "lib=" $p] == -1} {
6251 set pos [
string first "=" $p]
6255 set prop_name [
string range $p 0 [
expr {$pos - 1}]]
6258 dict lappend properties $vhdlfile $p
6259 Msg Debug "Adding property $p to $vhdlfile..."
6260 }
elseif {$list_file_ext != ".ipb"} {
6261 Msg Warning "Setting Property $p is not supported for file $vhdlfile or it is already its default. \
6266 if {[lsearch {.xci .ip .bd .xcix} $extension] >= 0} {
6268 set lib_name "ips.src"
6269 }
elseif {[
IsInList $extension {.vhd .vhdl}] || $list_file_ext == ".sim"} {
6271 if {![
IsInList $extension {.vhd .vhdl}]} {
6272 set lib_name "others.sim"
6274 set lib_name "$library$list_file_ext"
6276 }
elseif {$list_file_ext == ".con"} {
6277 set lib_name "sources.con"
6278 }
elseif {$list_file_ext == ".ipb"} {
6279 set lib_name "xml.ipb"
6280 }
elseif { [
IsInList $list_file_ext {.src}] && [
IsInList $extension {.c .cpp .h .hpp}] } {
6282 set lib_name "$library$list_file_ext"
6285 set lib_name "others.src"
6288 Msg Debug "Appending $vhdlfile to $lib_name list..."
6289 dict lappend libraries $lib_name $vhdlfile
6290 if {$sha_mode != 0 && [
file type $vhdlfile] eq "link"} {
6293 dict lappend libraries $lib_name $real_file
6294 Msg Debug "File $vhdlfile is a soft link, also adding the real file: $real_file"
6299 if {[dict exists $filesets $fileset] == 0} {
6301 Msg Debug "Adding $fileset to the fileset dictionary..."
6302 Msg Debug "Adding library $lib_name to fileset $fileset..."
6303 dict set filesets $fileset $lib_name
6307 Msg Debug "Adding library $lib_name to fileset $fileset..."
6308 dict lappend filesets $fileset $lib_name
6314 Msg CriticalWarning "File $vhdlfile not found."
6320 if {$sha_mode != 0} {
6322 if {$list_file_ext eq ".ipb"} {
6323 set sha_lib "xml.ipb"
6325 set sha_lib $lib$list_file_ext
6327 dict lappend libraries $sha_lib [
file normalize $list_file]
6328 if {[
file type $list_file] eq "link"} {
6331 dict lappend libraries $lib$list_file_ext $real_file
6332 Msg Debug "List file $list_file is a soft link, also adding the real file: $real_file"
6335 return [list $libraries $properties $filesets]
6344 proc Relative {base dst {quiet 0}} {
6345 if {![
string equal [
file pathtype $base] [
file pathtype $dst]]} {
6347 Msg CriticalWarning "Unable to compute relation for paths of different path types: [
file pathtype $base] vs. [
file pathtype $dst], ($base vs. $dst)"
6352 set base [
file normalize [
file join [
pwd] $base]]
6353 set dst [
file normalize [
file join [
pwd] $dst]]
6356 set base [
file split $base]
6357 set dst [
file split $dst]
6359 while {[
string equal [
lindex $dst 0] [
lindex $base 0]]} {
6360 set dst [
lrange $dst 1 end]
6361 set base [
lrange $base 1 end]
6362 if {![
llength $dst]} {break}
6365 set dstlen [
llength $dst]
6366 set baselen [
llength $base]
6368 if {($dstlen == 0) && ($baselen == 0)} {
6371 while {$baselen > 0} {
6372 set dst [
linsert $dst 0 ..]
6375 set dst [
eval [
linsert $dst 0 file join]]
6386 proc RelativeLocal {pathName filePath} {
6387 if {[
string first [
file normalize $pathName] [
file normalize $filePath]] != -1} {
6388 return [
Relative $pathName $filePath]
6399 proc RemoveDuplicates {mydict} {
6400 set new_dict [dict create]
6401 foreach key [dict keys $mydict] {
6402 set values [
DictGet $mydict $key]
6403 foreach value $values {
6404 set idxs [
lreverse [
lreplace [lsearch -exact -all $values $value] 0 0]]
6406 set values [
lreplace $values $idx $idx]
6409 dict set new_dict $key $values
6420 proc ResetRepoFiles {reset_file} {
6421 if {[
file exists $reset_file]} {
6422 Msg Info "Found $reset_file, opening it..."
6423 set fp [open $reset_file r]
6424 set wild_cards [lsearch -all -inline -not -regexp [
split [read $fp] "\n"] "^ *$"]
6426 Msg Info "Found the following files/wild cards to restore if modified: $wild_cards..."
6427 foreach w $wild_cards {
6429 if {[
llength $mod_files] > 0} {
6430 Msg Info "Found modified $w files: $mod_files, will restore them..."
6433 Msg Info "No modified $w files found."
6444 proc RestoreModifiedFiles {{repo_path "."} {pattern "."}} {
6447 set ret [
Git checkout $pattern]
6458 proc SearchHogProjects {dir} {
6459 set projects_list {}
6460 if {[
file exists $dir]} {
6461 if {[
file isdirectory $dir]} {
6462 foreach proj_dir [glob -nocomplain -types d $dir/*] {
6463 if {![regexp {^.*Top/+(.*)$} $proj_dir dummy proj_name]} {
6464 Msg Warning "Could not parse Top directory $dir"
6467 if {[
file exists "$proj_dir/hog.conf"]} {
6468 lappend projects_list $proj_name
6471 lappend projects_list $p
6476 Msg Error "Input $dir is not a directory!"
6479 Msg Error "Directory $dir doesn't exist!"
6481 return $projects_list
6490 proc SetGenericsSimulation {repo_path proj_dir target} {
6491 set top_dir "$repo_path/Top/$proj_dir"
6492 set simsets [get_filesets]
6493 if {$simsets != ""} {
6494 foreach simset $simsets {
6496 if {[get_property FILESET_TYPE $simset] != "SimulationSrcs"} {
6500 set merged_generics_dict [dict create]
6504 set simset_generics [
DictGet $simset_dict "generics"]
6505 set merged_generics_dict [
MergeDict $merged_generics_dict $simset_generics 0]
6507 set_property generic $generic_str [get_filesets $simset]
6508 Msg Debug "Setting generics $generic_str for simulator $target\
6509 and simulation file-set $simset..."
6521 proc SetTopProperty {top_module fileset} {
6522 Msg Info "Setting TOP property to $top_module module"
6525 set_property "top" $top_module [get_filesets $fileset]
6528 set_global_assignment -name TOP_LEVEL_ENTITY $top_module
6530 set_root -module $top_module
6532 prj_impl option top $top_module
6537 proc VIVADO_PATH_PROPERTIES {} {
6538 return {"\.*\.TCL\.PRE$" "^.*\.TCL\.POST$" "^RQS_FILES$" "^INCREMENTAL\_CHECKPOINT$" "NOC\_SOLUTION\_FILE"}
6542 proc VITIS_PATH_PROPERTIES {} {
6543 return {"^HW$" "^XPFM$" "^LINKER-SCRIPT$" "^LIBRARIES$" "^LIBRARY-SEARCH-PATH$"}
6553 proc WriteConf {file_name config {comment ""}} {
6554 if {[
catch {
package require inifile 0.2.3} ERROR]} {
6555 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
6559 ::ini::commentchar "#"
6560 set f [::ini::open $file_name w]
6562 foreach sec [dict keys $config] {
6563 set section [dict get $config $sec]
6564 dict for {p v} $section {
6565 if {[string trim $v] == ""} {
6566 Msg Warning "Property $p has empty value. Skipping..."
6569 ::ini::set $f $sec $p $v
6574 if {![
string equal "$comment" ""]} {
6575 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $comment
6576 set hog_header "Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
6577 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $hog_header
6592 proc WriteGenerics {mode repo_path design date timee\
6593 commit version top_hash top_ver hog_hash hog_ver \
6594 cons_ver cons_hash libs vers hashes ext_names ext_hashes \
6595 user_ip_repos user_ip_vers user_ip_hashes flavour {xml_ver ""} {xml_hash ""}} {
6596 Msg Info "Passing parameters/generics to project's top module..."
6599 set generic_string [
concat \
6611 if {$xml_hash != "" && $xml_ver != ""} {
6612 lappend generic_string \
6617 foreach l $libs v $vers h $hashes {
6623 set ver [regsub -all {[\.-]} $ver {_}]
6624 set hash [regsub -all {[\.-]} $hash {_}]
6625 lappend generic_string "$ver" "$hash"
6628 foreach e $ext_names h $ext_hashes {
6630 lappend generic_string "$hash"
6633 foreach repo $user_ip_repos v $user_ip_vers h $user_ip_hashes {
6634 set repo_name [
file tail $repo]
6635 set ver "[
string toupper $repo_name]_VER=[
FormatGeneric $v]"
6636 set hash "[
string toupper $repo_name]_SHA=[
FormatGeneric $h]"
6637 set ver [regsub -all {[\.-]} $ver {_}]
6638 set hash [regsub -all {[\.-]} $hash {_}]
6639 lappend generic_string "$ver" "$hash"
6642 if {$flavour != -1} {
6643 lappend generic_string "FLAVOUR=$flavour"
6649 set generic_string "$prj_generics $generic_string"
6655 if {$mode == "create" || [
IsISE]} {
6658 if {[
file exists $top_file]} {
6661 Msg Debug "Found top level generics $generics in $top_file"
6663 set filtered_generic_string ""
6665 foreach generic_to_set [
split [
string trim $generic_string]] {
6666 set key [
lindex [
split $generic_to_set "="] 0]
6667 if {[dict exists $generics $key]} {
6668 Msg Debug "Hog generic $key found in $top_name"
6669 lappend filtered_generic_string "$generic_to_set"
6671 Msg Warning "Generic $key is passed by Hog but is NOT present in $top_name."
6677 set generic_string $filtered_generic_string
6682 set_property generic $generic_string [current_fileset]
6683 Msg Info "Setting parameters/generics..."
6684 Msg Debug "Detailed parameters/generics: $generic_string"
6689 set simulator [get_property target_simulator [current_project]]
6690 if {$mode == "create"} {
6697 Msg Info "Setting Synplify parameters/generics one by one..."
6698 foreach generic $generic_string {
6699 Msg Debug "Setting Synplify generic: $generic"
6700 set_option -hdl_param -set "$generic"
6703 Msg Info "Setting Diamond parameters/generics one by one..."
6704 prj_impl option -impl Implementation0 HDL_PARAM "$generic_string"
6706 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
6708 foreach app_name [dict keys $ws_apps] {
6709 set defined_symbols [app config -name $app_name -get define-compiler-symbols]
6710 foreach generic_to_set [
split [
string trim $generic_string]] {
6711 set key [
lindex [
split $generic_to_set "="] 0]
6712 set value [
lindex [
split $generic_to_set "="] 1]
6713 if {[
string match "32'h*" $value]} {
6714 set value [
string map {"32'h" "0x"} $value]
6717 foreach symbol [
split $defined_symbols ";"] {
6718 if {[
string match "$key=*" $symbol]} {
6719 Msg Debug "Generic $key found in $app_name, removing it..."
6720 app config -name $app_name -remove define-compiler-symbols "$symbol"
6724 Msg Info "Setting Vitis parameters/generics for app $app_name: $key=$value"
6725 app config -name $app_name define-compiler-symbols "$key=$value"
6737 proc WriteGenericsToBdIPs {mode repo_path proj generic_string} {
6738 Msg Debug "Parameters/generics passed to WriteGenericsToIP: $generic_string"
6740 set bd_ip_generics false
6742 if {[dict exists $properties "hog"]} {
6743 set propDict [dict get $properties "hog"]
6744 if {[dict exists $propDict "PASS_GENERICS_TO_BD_IPS"]} {
6745 set bd_ip_generics [dict get $propDict "PASS_GENERICS_TO_BD_IPS"]
6749 if {[
string compare [
string tolower $bd_ip_generics] "false"] == 0} {
6753 if {$mode == "synth"} {
6754 Msg Info "Attempting to apply generics pre-synthesis..."
6755 set PARENT_PRJ [get_property "PARENT.PROJECT_PATH" [current_project]]
6756 set workaround [open "$repo_path/Projects/$proj/.hog/presynth_workaround.tcl" "w"]
6757 puts $workaround "source \[lindex \$argv 0\];"
6758 puts $workaround "open_project \[lindex \$argv 1\];"
6759 puts $workaround "WriteGenericsToBdIPs \[lindex \$argv 2\] \[lindex \$argv 3\] \[lindex \$argv 4\] \[lindex \$argv 5\];"
6760 puts $workaround "close_project"
6764 exec vivado -mode batch -source $repo_path/Projects/$proj/.hog/presynth_workaround.tcl \
6765 -tclargs $repo_path/Hog/Tcl/hog.tcl $PARENT_PRJ \
6766 "childprocess" $repo_path $proj $generic_string
6769 Msg Error "Encountered an error while attempting workaround: $errMsg"
6771 file delete $repo_path/Projects/$proj/.hog/presynth_workaround.tcl
6773 Msg Info "Done applying generics pre-synthesis."
6777 Msg Info "Looking for IPs to add generics to..."
6778 set ips_generic_string ""
6779 foreach generic_to_set [
split [
string trim $generic_string]] {
6780 set key [
lindex [
split $generic_to_set "="] 0]
6781 set value [
lindex [
split $generic_to_set "="] 1]
6782 append ips_generic_string "CONFIG.$key $value "
6786 if {[
string compare [
string tolower $bd_ip_generics] "true"] == 0} {
6789 set ip_regex $bd_ip_generics
6792 set ip_list [get_ips -regex $ip_regex]
6793 Msg Debug "IPs found with regex \{$ip_regex\}: $ip_list"
6795 set regen_targets {}
6797 foreach {ip} $ip_list {
6798 set WARN_ABOUT_IP false
6799 set ip_props [list_property [get_ips $ip]]
6802 if {[lsearch -exact $ip_props "IS_BD_CONTEXT"] == -1} {
6806 if {[get_property "IS_BD_CONTEXT" [get_ips $ip]] eq "1"} {
6807 foreach {ip_prop} $ip_props {
6808 if {[dict exists $ips_generic_string $ip_prop]} {
6809 if {$WARN_ABOUT_IP == false} {
6810 lappend regen_targets [get_property SCOPE [get_ips $ip]]
6811 Msg Warning "The ip \{$ip\} contains generics that are set by Hog.\
6812 If this is IP is apart of a block design, the .bd file may contain stale, unused, values.\
6813 Hog will always apply the most up-to-date values to the IP during synthesis,\
6814 however these values may or may not be reflected in the .bd file."
6815 set WARN_ABOUT_IP true
6820 set xci_path [get_property IP_FILE [get_ips $ip]]
6822 if {[
string equal $generic_format "ERROR"]} {
6823 Msg Warning "Could not find format for generic $ip_prop in IP $ip. Skipping..."
6827 set value_to_set [dict get $ips_generic_string $ip_prop]
6828 switch -exact $generic_format {
6830 if {[
string match "32'h*" $value_to_set]} {
6831 scan [
string map {"32'h" ""} $value_to_set] "%x" value_to_set
6835 set value_to_set [
expr {$value_to_set ? "true" : "false"}]
6838 if {[
string match "32'h*" $value_to_set]} {
6839 binary scan [
binary format H* [
string map {"32'h" ""} $value_to_set]] d value_to_set
6843 if {[
string match "32'h*" $value_to_set]} {
6844 set value_to_set [
string map {"32'h" "0x"} $value_to_set]
6848 set value_to_set [
format "%s" $value_to_set]
6851 Msg Warning "Unknown generic format $generic_format for IP $ip. Will attempt to pass as string..."
6856 Msg Info "The IP \{$ip\} contains: $ip_prop ($generic_format), setting it to $value_to_set."
6857 if {[
catch {set_property -name $ip_prop -value $value_to_set -objects [get_ips $ip]} prop_error]} {
6858 Msg CriticalWarning "Failed to set property $ip_prop to $value_to_set for IP \{$ip\}: $prop_error"
6865 foreach {regen_target} [lsort -unique $regen_targets] {
6866 Msg Info "Regenerating target: $regen_target"
6867 if {[
catch {generate_target -force all [get_files $regen_target]} prop_error]} {
6868 Msg CriticalWarning "Failed to regen targets: $prop_error"
6876 proc GetGenericFormatFromXciXML {generic_name xml_file} {
6877 if {![
file exists $xml_file]} {
6878 Msg Error "Could not find XML file: $xml_file"
6882 set fp [open $xml_file r]
6883 set xci_data [read $fp]
6886 set paramType "string"
6887 set modelparam_regex [
format {^.*\y%s\y.*$} [
string map {"CONFIG." "MODELPARAM_VALUE."} $generic_name]]
6888 set format_regex {format="([^"]+)"}
6890 set line [
lindex [regexp -inline -line $modelparam_regex $xci_data] 0]
6891 Msg Debug "line: $line"
6893 if {[regexp $format_regex $line match format_value]} {
6894 Msg Debug "Extracted: $format_value format from xml"
6895 set paramType $format_value
6897 Msg Debug "No format found, using string"
6906 proc GetGenericFormatFromXci {generic_name xci_file} {
6907 if {![
file exists $xci_file]} {
6908 Msg Error "Could not find XCI file: $xci_file"
6912 set fp [open $xci_file r]
6913 set xci_data [read $fp]
6916 set paramType "string"
6917 if {[
string first "xilinx.com:schema:json_instance:1.0" $xci_data] == -1} {
6918 Msg Debug "XCI format is not JSON, trying XML..."
6919 set xml_file "[
file rootname $xci_file].xml"
6923 set generic_name [
string map {"CONFIG." ""} $generic_name]
6924 set ip_inst [
ParseJSON $xci_data "ip_inst"]
6925 set parameters [dict get $ip_inst parameters]
6926 set component_parameters [dict get $parameters component_parameters]
6927 if {[dict exists $component_parameters $generic_name]} {
6928 set generic_info [dict get $component_parameters $generic_name]
6929 if {[dict exists [
lindex $generic_info 0] format]} {
6930 set paramType [dict get [
lindex $generic_info 0] format]
6931 Msg Debug "Extracted: $paramType format from xci"
6934 Msg Debug "No format found, using string"
6947 proc WriteGitLabCIYAML {proj_name {ci_conf ""}} {
6948 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
6949 Msg CriticalWarning "Cannot find package YAML.\n Error message: $YAMLPACKAGE. \
6950 If you are running on tclsh, you can fix this by installing package \"tcllib\""
6955 if {$ci_conf != ""} {
6957 foreach sec [dict keys $ci_confs] {
6958 if {[
string first : $sec] == -1} {
6959 lappend job_list $sec
6963 set job_list {"generate_project" "simulate_project"}
6967 set out_yaml [huddle create]
6968 foreach job $job_list {
6970 set huddle_tags [huddle list]
6972 set sec_dict [dict create]
6974 if {$ci_confs != ""} {
6975 foreach var [dict keys [dict get $ci_confs $job]] {
6976 if {$var == "tags"} {
6977 set tag_section "tags"
6978 set tags [dict get [dict get $ci_confs $job] $var]
6979 set tags [
split $tags ","]
6981 set tag_list [huddle list $tag]
6982 set huddle_tags [huddle combine $huddle_tags $tag_list]
6985 dict set sec_dict $var [dict get [dict get $ci_confs $job] $var]
6991 set huddle_variables [huddle create "PROJECT_NAME" $proj_name "extends" ".vars"]
6992 if {[dict exists $ci_confs "$job:variables"]} {
6993 set var_dict [dict get $ci_confs $job:variables]
6994 foreach var [dict keys $var_dict] {
6996 set value [dict get $var_dict "$var"]
6997 set var_inner [huddle create "$var" "$value"]
6998 set huddle_variables [huddle combine $huddle_variables $var_inner]
7003 set middle [huddle create "extends" ".$job" "variables" $huddle_variables]
7004 foreach sec [dict keys $sec_dict] {
7005 set value [dict get $sec_dict $sec]
7006 set var_inner [huddle create "$sec" "$value"]
7007 set middle [huddle combine $middle $var_inner]
7009 if {$tag_section != ""} {
7010 set middle2 [huddle create "$tag_section" $huddle_tags]
7011 set middle [huddle combine $middle $middle2]
7014 set outer [huddle create "$job:$proj_name" $middle]
7015 set out_yaml [huddle combine $out_yaml $outer]
7018 return [
string trimleft [yaml::huddle2yaml $out_yaml] "-"]
7028 proc WriteListFiles {libs props list_path repo_path {ext_path ""}} {
7030 foreach lib [dict keys $libs] {
7031 if {[
llength [
DictGet $libs $lib]] > 0} {
7032 set list_file_name $list_path$lib
7033 set list_file [open $list_file_name w]
7034 Msg Info "Writing $list_file_name..."
7035 puts $list_file "#Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
7036 foreach file [
DictGet $libs $lib] {
7038 set prop [
DictGet $props $file]
7042 puts $list_file "$file_path $prop"
7045 set ext_list_file [open "[
file rootname $list_file].ext" a]
7046 puts $ext_list_file "$file_path $prop"
7047 close $ext_list_file
7050 Msg Warning "The path of file $file is not relative to your repository. Please check!"
7066 proc WriteSimListFile {simset libs props simsets list_path repo_path {force 0}} {
7068 set list_file_name $list_path/${simset}.sim
7069 if {$force == 0 && [
file exists $list_file_name]} {
7070 Msg Info "List file $list_file_name already exists, skipping..."
7074 set list_file [open $list_file_name a+]
7077 puts $list_file "\[files\]"
7078 Msg Info "Writing $list_file_name..."
7079 foreach lib [
DictGet $simsets $simset] {
7080 foreach file [
DictGet $libs $lib] {
7082 set prop [
DictGet $props $file]
7086 set lib_name [
file rootname $lib]
7087 if {$lib_name != $simset && [
file extension $file] == ".vhd" && [
file extension $file] == ""} {
7088 lappend prop "lib=$lib_name"
7090 puts $list_file "$file_path $prop"
7093 Msg Warning "The path of file $file is not relative to your repository. Please check!"
7105 proc WriteToFile {File msg} {
7106 set f [open $File a+]
7117 proc WriteUtilizationSummary {input output project_name run} {
7118 set f [open $input "r"]
7119 set o [open $output "a"]
7120 puts $o "## $project_name $run Utilization report\n\n"
7121 struct::matrix util_m
7122 util_m add columns 14
7125 util_m add row "| **Site Type** | **Used** | **Fixed** | **Prohibited** | **Available** | **Util%** |"
7126 util_m add row "| --- | --- | --- | --- | --- | --- |"
7128 util_m add row "| **Site Type** | **Used** | **Fixed** | **Available** | **Util%** |"
7129 util_m add row "| --- | --- | --- | --- | --- |"
7139 while {[
gets $f line] >= 0} {
7140 if {([
string first "| CLB LUTs" $line] >= 0 || [
string first "| Slice LUTs" $line] >= 0) && $luts == 0} {
7141 util_m add row $line
7144 if {([
string first "| CLB Registers" $line] >= 0 || [
string first "| Slice Registers" $line] >= 0) && $regs == 0} {
7145 util_m add row $line
7148 if {[
string first "| Block RAM Tile" $line] >= 0 && $bram == 0} {
7149 util_m add row $line
7152 if {[
string first "URAM " $line] >= 0 && $uram == 0} {
7153 util_m add row $line
7156 if {[
string first "DSPs" $line] >= 0 && $dsps == 0} {
7157 util_m add row $line
7160 if {[
string first "Bonded IOB" $line] >= 0 && $ios == 0} {
7161 util_m add row $line
7168 puts $o [util_m format 2string]
7174 Msg Error "Found Git version older than 2.7.2. Hog will not work as expected, exiting now."