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\""
4084 if {[
string first "/eos/" $ip_path] == 0} {
4092 lassign [
eos "ls $ip_path"] ret result
4094 Msg CriticalWarning "Could not run ls for for EOS path: $ip_path (error: $result). \
4095 Either the drectory does not exist or there are (temporary) problem with EOS."
4099 Msg Info "IP remote directory path, on EOS, is set to: $ip_path"
4105 if {!([
file exists $xci_file])} {
4106 Msg CriticalWarning "Could not find $xci_file."
4112 set xci_path [
file dirname $xci_file]
4113 set xci_name [
file tail $xci_file]
4114 set xci_ip_name [
file rootname [
file tail $xci_file]]
4115 set xci_dir_name [
file tail $xci_path]
4116 set gen_path $gen_dir
4118 set hash [
Md5Sum $xci_file]
4119 set file_name $xci_name\_$hash
4121 Msg Info "Preparing to $what_to_do IP: $xci_name..."
4123 if {$what_to_do eq "push"} {
4127 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4132 Msg Info "IP already in the EOS repository, will not copy..."
4134 Msg Info "IP already in the EOS repository, will forcefully replace..."
4140 if {[
file exists "$ip_path/$file_name.tar"]} {
4142 Msg Info "IP already in the local repository, will not copy..."
4144 Msg Info "IP already in the local repository, will forcefully replace..."
4153 if {$will_copy == 1} {
4155 Msg Info "Looking for generated files in $gen_path..."
4156 set ip_gen_files [glob -nocomplain $gen_path/*]
4160 if {[
llength $ip_gen_files] > 0} {
4161 Msg Info "Found some IP synthesised files matching $xci_ip_name"
4162 if {$will_remove == 1} {
4163 Msg Info "Removing old synthesised directory $ip_path/$file_name.tar..."
4165 eos "rm -rf $ip_path/$file_name.tar" 5
4167 file delete -force "$ip_path/$file_name.tar"
4171 Msg Info "Creating local archive with IP generated files..."
4174 foreach f $ip_gen_files {
4175 lappend tar_files "[
Relative [
file normalize $repo_path] $f]"
4178 ::tar::create $file_name.tar $tar_files
4180 Msg Info "Copying IP generated files for $xci_name..."
4182 lassign [
ExecuteRet xrdcp -f -s $file_name.tar $::env(EOS_MGM_URL)//$ip_path/] ret msg
4184 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4187 Copy "$file_name.tar" "$ip_path/"
4189 Msg Info "Removing local archive"
4190 file delete $file_name.tar
4192 Msg Warning "Could not find synthesized files matching $gen_path/$file_name*"
4195 }
elseif {$what_to_do eq "pull"} {
4197 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4199 Msg Info "Nothing for $xci_name was found in the EOS repository, cannot pull."
4203 set remote_tar "$::env(EOS_MGM_URL)//$ip_path/$file_name.tar"
4204 Msg Info "IP $xci_name found in the repository $remote_tar, copying it locally to $repo_path..."
4206 lassign [
ExecuteRet xrdcp -f -r -s $remote_tar $repo_path] ret msg
4208 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4212 if {[
file exists "$ip_path/$file_name.tar"]} {
4213 Msg Info "IP $xci_name found in local repository $ip_path/$file_name.tar, copying it locally to $repo_path..."
4214 Copy $ip_path/$file_name.tar $repo_path
4216 Msg Info "Nothing for $xci_name was found in the local IP repository, cannot pull."
4222 if {[
file exists $file_name.tar]} {
4223 remove_files $xci_file
4224 Msg Info "Extracting IP files from archive to $repo_path..."
4225 ::tar::untar $file_name.tar -dir $repo_path -noperms
4226 Msg Info "Removing local archive"
4227 file delete $file_name.tar
4228 add_files -norecurse -fileset sources_1 $xci_file
4241 proc HexVersionToString {version} {
4242 scan [
string range $version 0 1] %x M
4243 scan [
string range $version 2 3] %x m
4244 scan [
string range $version 4 7] %x c
4249 proc ImportTclLib {} {
4252 if {[
info exists env(HOG_TCLLIB_PATH)]} {
4253 lappend auto_path $env(HOG_TCLLIB_PATH)
4256 puts "ERROR: To run Hog with Microsemi Libero SoC or Lattice Diamond, you need to define the HOG_TCLLIB_PATH variable."
4271 proc InitLauncher {script tcl_path parameters commands argv {custom_commands ""}} {
4272 set repo_path [
file normalize "$tcl_path/../.."]
4274 set bin_path [
file normalize "$tcl_path/../../bin"]
4275 set top_path [
file normalize "$tcl_path/../../Top"]
4277 set cmd_lines [
split $commands "\n"]
4279 set command_options [dict create]
4280 set directive_descriptions [dict create]
4281 set directive_names [dict create]
4282 set common_directive_names [dict create]
4283 set custom_command ""
4284 set custom_command_options ""
4286 foreach l $cmd_lines {
4288 if {[regexp {\\(.*) \{\#} $l minc d]} {
4289 lappend directives_with_projects $d
4293 if {[regexp {\\(.*) \{} $l minc regular_expression]} {
4294 lappend directive_regex $regular_expression
4298 if {[regexp {\#\s*NAME(\*)?:\s*(.*)\s*} $l minc star name]} {
4299 dict set directive_names $name $regular_expression
4301 dict set common_directive_names $name $regular_expression
4304 set directive_names [
DictSort $directive_names]
4305 set common_directive_names [
DictSort $common_directive_names]
4308 if {[regexp {\#\s*DESCRIPTION:\s*(.*)\s*} $l minc x]} {
4309 dict set directive_descriptions $regular_expression $x
4313 if {[regexp {\#\s*OPTIONS:\s*(.*)\s*} $l minc x]} {
4314 dict set command_options $regular_expression [
split [regsub -all {[ \t\n]+} $x {}] ","]
4318 set short_usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nMost common directives (case insensitive):"
4320 dict for {key value} $common_directive_names {
4321 set short_usage "$short_usage\n - $key: [dict get $directive_descriptions $value]"
4325 if {[
string length $custom_commands] > 0} {
4326 Msg Debug "Found custom commands to add to short short_usage."
4327 set short_usage "$short_usage\n\nCustom commands:"
4328 dict for {key command} $custom_commands {
4329 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4330 set short_usage "$short_usage\n - $key: [dict get $command DESCRIPTION]"
4336 set short_usage "$short_usage\n\n\
4337 To see all the available directives, run:\n./Hog/Do HELP\n\n\
4338 To list available options for the chosen directive run:\n\
4339 ./Hog/Do <directive> HELP\n
4342 set usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nDirectives (case insensitive):"
4344 dict for {key value} $directive_names {
4345 set usage "$usage\n - $key: [dict get $directive_descriptions $value]"
4349 if {[
string length $custom_commands] > 0} {
4350 Msg Debug "Found custom commands to add to short usage."
4351 set usage "$usage\n\nCustom commands:"
4352 dict for {key command} $custom_commands {
4353 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4354 set usage "$usage\n - $key: [dict get $command DESCRIPTION]"
4359 set usage "$usage\n\nTo list available options for the chosen directive run:\n./Hog/Do <directive> HELP"
4368 Msg Debug "HogEnv.conf found"
4376 if {[
catch {
package require cmdline} ERROR]} {
4377 Msg Debug "The cmdline Tcl package was not found, sourcing it from Hog..."
4378 source $tcl_path/utils/cmdline.tcl
4381 set argv [regsub -all {(?i) HELP\y} $argv " -help"]
4386 set custom_parameters [list]
4387 dict for {key command} $custom_commands {
4388 set custom_parameters [concat $custom_parameters [dict get $command CUSTOM_OPTIONS]]
4391 lassign [
GetOptions $argv [
concat $custom_parameters $parameters]] option_list arg_list
4393 if {[
IsInList "-all" $option_list]} {
4402 set directive [
string toupper [
lindex $arg_list 0]]
4405 set argument_is_no_project 1
4407 set NO_DIRECTIVE_FOUND 0
4408 switch -regexp -- $directive "$commands"
4409 if {$NO_DIRECTIVE_FOUND == 1} {
4410 if {[
string length $custom_commands] > 0 && [dict exists $custom_commands $directive]} {
4411 set custom_command $directive
4412 set custom_command_hog_parameters [dict get $custom_commands $directive OPTIONS]
4413 set custom_command_options [dict get $custom_commands $directive CUSTOM_OPTIONS]
4414 set custom_command_options [
concat $custom_command_hog_parameters $custom_command_options]
4416 Msg Status "ERROR: Unknown directive $directive.\n\n"
4422 if {[
IsInList $directive $directives_with_projects 1]} {
4423 set argument_is_no_project 0
4427 if {$directive != ""} {
4428 if {[
IsInList $directive $directives_with_projects 1]} {
4429 puts "usage: ./Hog/Do \[OPTIONS\] $directive <project>\n"
4430 }
elseif {[regexp "^COMPSIM(LIB)?$" $directive]} {
4431 puts "usage: ./Hog/Do \[OPTIONS\] $directive <simulator>\n"
4433 puts "usage: ./Hog/Do \[OPTIONS\] $directive \n"
4436 dict for {dir desc} $directive_descriptions {
4437 if {[regexp $dir $directive]} {
4445 if {$custom_command ne ""} {
4446 if {[
llength $custom_command_options] > 0} {
4447 puts "Available options:"
4449 foreach custom_option $custom_command_options {
4450 set n [
llength $custom_option]
4452 lassign $custom_option opt help
4455 }
elseif {$n == 3} {
4456 lassign $custom_option opt def help
4457 puts " -$opt <argument>"
4459 puts " $help (default: $def)"
4464 Msg Warning "Custom option spec has invalid arity (expected 2 or 3): $custom_option"
4469 dict for {dir opts} $command_options {
4470 if {[regexp $dir $directive]} {
4471 puts "Available options:"
4473 foreach par $parameters {
4474 if {$opt == [lindex $par 0]} {
4475 if {[regexp {\.arg$} $opt]} {
4476 set opt_name [regsub {\.arg$} $opt ""]
4477 puts " -$opt_name <argument>"
4481 puts " [lindex $par [llength $par]-1]"
4495 if {$custom_command ne ""} {
4496 set parameters [
concat $parameters $custom_command_options]
4499 if {[
catch {
array set options [
cmdline::getoptions option_list $parameters $usage]} err]} {
4500 Msg Status "\nERROR: Syntax error, probably unknown option.\n\n USAGE: $err"
4504 if {[
llength $arg_list] <= $min_n_of_args || [
llength $arg_list] > $max_n_of_args} {
4505 Msg Status "\nERROR: Wrong number of arguments: [
llength $argv]"
4510 set project [
lindex $arg_list 1]
4512 if {$argument_is_no_project == 0} {
4514 regsub "^(\./)?Top/" $project "" project
4516 regsub "/? *\$" $project "" project
4522 Msg Debug "Option list:"
4523 foreach {key value} [
array get options] {
4524 Msg Debug "$key => $value"
4531 if {$proj_conf != 0} {
4534 lassign [
GetIDECommand $proj_conf] cmd before_tcl_script after_tcl_script end_marker
4535 Msg Info "Project $project uses $cmd IDE"
4538 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4540 if {$custom_command ne ""} {
4541 if { [dict exists $custom_commands $directive IDE] } {
4542 lassign [
GetIDECommand "" [dict get $custom_commands $directive IDE]] cmd before_tcl_script after_tcl_script end_marker
4543 Msg Info "Custom command: $custom_command uses $cmd IDE"
4544 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4546 set command "custom_tcl"
4548 }
elseif {$argument_is_no_project == 1} {
4550 Msg Debug "$project will be used as first argument"
4551 }
elseif {$project != ""} {
4554 }
elseif {$min_n_of_args < 0} {
4567 set project_group [
file dirname $project]
4568 set project_name $project
4569 set project [
file tail $project]
4570 Msg Debug "InitLauncher: project_group=$project_group, project_name=$project_name, project=$project"
4572 return [list $directive $project $project_name $project_group $repo_path $old_path $bin_path $top_path $usage $short_usage $command $cmd [
array get options]]
4579 proc IsCommitAncestor {ancestor commit} {
4580 lassign [
GitRet "merge-base --is-ancestor $ancestor $commit"] status result
4589 return [
expr {[info commands sys_install] != ""}]
4594 return [
expr {[info commands get_libero_version] != ""}]
4603 proc IsInList {element list {regex 0} {nocase 0}} {
4607 if {[regexp -nocase $x $element]} {
4611 if {[regexp $x $element]} {
4615 }
elseif {$regex == 0} {
4617 if {[
string tolower $x] eq [
string tolower $element]} {
4621 if {$x eq $element} {
4634 return [
expr {[string first PlanAhead [version]] == 0}]
4642 if {[
catch {
package require ::quartus::flow} result]} {
4655 proc IsRelativePath {path} {
4656 if {[
string index $path 0] == "/" || [
string index $path 0] == "~"} {
4664 proc IsSynplify {} {
4665 return [
expr {[info commands program_version] != ""}]
4670 return [
expr {![IsQuartus] && ![IsXilinx] && ![IsVitisClassic] && ![IsLibero] && ![IsSynplify] && ![IsDiamond]}]
4678 proc IsVerilog {file} {
4679 if {[
file extension $file] == ".v" || [
file extension $file] == ".sv"} {
4691 proc IsVersal {part} {
4692 if {[get_property ARCHITECTURE [get_parts $part]] eq "versal"} {
4702 return [
expr {[string first Vivado [version]] == 0}]
4710 if {[
info commands version] != ""} {
4711 set current_version [version]
4712 if {[
string first PlanAhead $current_version] == 0 || [
string first Vivado $current_version] == 0} {
4714 }
elseif {[
string first xsct $current_version] == 0} {
4717 Msg Warning "This IDE has the version command but it is not PlanAhead or Vivado: $current_version"
4726 proc IsVitisClassic {} {
4727 return [
expr {[info commands platform] != ""}]
4735 proc IsZynq {part} {
4736 if {[regexp {^(xc7z|xczu).*} $part]} {
4743 proc ImportGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
4744 set list_path "$repo_path/Top/$project_name/list"
4745 lassign [
GetHogFiles -list_files {.src,.ext,.sim} -ext_path $ext_path $list_path $repo_path] src_files properties filesets
4750 set properties [
DictGet $simset_dict "properties"]
4751 set options [
DictGet $properties "options"]
4754 set workdir Projects/$project_name/ghdl
4755 file delete -force $workdir
4757 set import_log "$workdir/ghdl-import-${simset_name}.log"
4758 dict for {lib sources} $src_files {
4759 set libname [file rootname $lib]
4760 foreach f $sources {
4761 if {[file extension $f] != ".vhd" && [file extension $f] != ".vhdl"} {
4762 Msg Info "File $f is not a VHDL file, copying it in workfolder..."
4763 file copy -force $f $workdir
4765 set file_path [Relative $repo_path $f]
4766 set import_log_file [open $import_log "a"]
4767 puts "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
4768 puts $import_log_file "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
4769 close $import_log_file
4770 lassign [GHDL "-i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path" $import_log] ret result
4772 Msg CriticalWarning "GHDL import failed for file $f: $result"
4781 proc LaunchGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
4784 set sim_props [
DictGet $simset_dict "properties"]
4785 set options [
DictGet $sim_props "options"]
4786 set runopts [
DictGet $sim_props "run_options"]
4788 dict for {prop_name prop_val} $sim_props {
4789 set prop_name [string toupper $prop_name]
4790 if {$prop_name == "TOP"} {
4791 set top_sim $prop_val
4794 set workdir $repo_path/Projects/$project_name/ghdl
4795 set make_log "$workdir/ghdl-make-${simset_name}.log"
4796 set run_log "$workdir/ghdl-run-${simset_name}.log"
4799 set make_log_file [open $make_log "w"]
4801 puts "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
4802 puts $make_log_file "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
4803 close $make_log_file
4805 lassign [
GHDL "-m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim" $make_log] ret result
4808 Msg Error "GHDL make failed for $top_sim: $result"
4812 set run_log_file [open $run_log "w"]
4813 puts "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
4814 puts $run_log_file "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
4817 lassign [
GHDL "-r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts" $run_log] ret result
4821 Msg Error "GHDL run failed for $top_sim: $result"
4836 proc LaunchImplementation {reset do_create run_folder project_name {repo_path .} {njobs 4} {do_bitstream 0}} {
4837 Msg Info "Starting implementation flow..."
4839 if {$reset == 1 && $do_create == 0} {
4840 Msg Info "Resetting run before launching implementation..."
4845 source $repo_path/Hog/Tcl/integrated/pre-implementation.tcl
4848 if {$do_bitstream == 1} {
4849 launch_runs impl_1 -to_step [
BinaryStepName [get_property PART [current_project]]] -jobs $njobs -dir $run_folder
4851 launch_runs impl_1 -jobs $njobs -dir $run_folder
4856 Msg Info "running post-implementation"
4857 source $repo_path/Hog/Tcl/integrated/post-implementation.tcl
4858 if {$do_bitstream == 1} {
4859 Msg Info "running pre-bitstream"
4860 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
4861 Msg Info "running post-bitstream"
4862 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
4866 set prog [get_property PROGRESS [get_runs impl_1]]
4867 set status [get_property STATUS [get_runs impl_1]]
4868 Msg Info "Run: impl_1 progress: $prog, status : $status"
4872 set status_file [open "$run_folder/timing.txt" "w"]
4873 puts $status_file "## $project_name Timing summary"
4875 set f [open [
lindex [glob "$run_folder/impl_1/*.twr" 0]]]
4877 while {[
gets $f line] >= 0} {
4878 if {[
string match "Timing summary:" $line]} {
4879 while {[
gets $f line] >= 0} {
4880 if {[
string match "Timing errors:*" $line]} {
4881 set errs [regexp -inline -- {[0-9]+} $line]
4883 if {[
string match "*Footnotes*" $line]} {
4886 puts $status_file "$line"
4895 Msg Info "Time requirements are met"
4896 file rename -force "$run_folder/timing.txt" "$run_folder/timing_ok.txt"
4899 Msg CriticalWarning "Time requirements are NOT met"
4900 file rename -force "$run_folder/timing.txt" "$run_folder/timing_error.txt"
4906 set wns [get_property STATS.WNS [get_runs [current_run]]]
4907 set tns [get_property STATS.TNS [get_runs [current_run]]]
4908 set whs [get_property STATS.WHS [get_runs [current_run]]]
4909 set ths [get_property STATS.THS [get_runs [current_run]]]
4910 set tpws [get_property STATS.TPWS [get_runs [current_run]]]
4912 if {$wns >= 0 && $whs >= 0 && $tpws >= 0} {
4913 Msg Info "Time requirements are met"
4914 set status_file [open "$run_folder/timing_ok.txt" "w"]
4917 Msg CriticalWarning "Time requirements are NOT met"
4918 set status_file [open "$run_folder/timing_error.txt" "w"]
4922 Msg Status "*** Timing summary ***"
4923 Msg Status "WNS: $wns"
4924 Msg Status "TNS: $tns"
4925 Msg Status "WHS: $whs"
4926 Msg Status "THS: $ths"
4927 Msg Status "TPWS: $tpws"
4933 puts $status_file "## $project_name Timing summary"
4935 m add row "| **Parameter** | \"**value (ns)**\" |"
4936 m add row "| --- | --- |"
4937 m add row "| WNS: | $wns |"
4938 m add row "| TNS: | $tns |"
4939 m add row "| WHS: | $whs |"
4940 m add row "| THS: | $ths |"
4941 m add row "| TPWS: | $tpws |"
4943 puts $status_file [m format 2string]
4944 puts $status_file "\n"
4945 if {$timing_ok == 1} {
4946 puts $status_file " Time requirements are met."
4948 puts $status_file "Time requirements are **NOT** met."
4950 puts $status_file "\n\n"
4954 if {$prog ne "100%"} {
4955 Msg Error "Implementation error"
4960 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
4962 Msg Info "Git describe set to $describe"
4964 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
4969 if {[
file exists $run_folder/versions.txt]} {
4970 file copy -force $run_folder/versions.txt $dst_dir
4972 Msg Warning "No versions file found in $run_folder/versions.txt"
4975 set timing_files [glob -nocomplain "$run_folder/timing_*.txt"]
4976 set timing_file [
file normalize [
lindex $timing_files 0]]
4978 if {[
file exists $timing_file]} {
4979 file copy -force $timing_file $dst_dir/
4981 Msg Warning "No timing file found, not a problem if running locally"
4985 if {[
IsVersal [get_property part [current_project]]]} {
4986 if {[get_property segmented_configuration [current_project]] == 1} {
4987 Msg Info "Versal Segmented configuration detected: exporting XSA file..."
4988 set xsa_name "$dst_dir/[
file tail $project_name]\-$describe.xsa"
4989 write_hw_platform -fixed -force -file $xsa_name
4993 set revision [get_current_revision]
4995 if {[
catch {execute_module -tool fit} result]} {
4996 Msg Error "Result: $result\n"
4997 Msg Error "Place & Route failed. See the report file.\n"
4999 Msg Info "\nINFO: Place & Route was successful for revision $revision.\n"
5002 if {[
catch {execute_module -tool sta -args "--do_report_timing"} result]} {
5003 Msg Error "Result: $result\n"
5004 Msg Error "Time Quest failed. See the report file.\n"
5006 Msg Info "Time Quest was successfully run for revision $revision.\n"
5009 set panel "Timing Analyzer||Timing Analyzer Summary"
5010 set device [get_report_panel_data -name $panel -col 1 -row_name "Device Name"]
5011 set timing_model [get_report_panel_data -name $panel -col 1 -row_name "Timing Models"]
5012 set delay_model [get_report_panel_data -name $panel -col 1 -row_name "Delay Model"]
5014 Msg Info "*******************************************************************"
5015 Msg Info "Device: $device"
5016 Msg Info "Timing Models: $timing_model"
5017 Msg Info "Delay Model: $delay_model"
5020 Msg Info "*******************************************************************"
5023 Msg Info "Starting implementation flow..."
5024 if {[
catch {run_tool -name {PLACEROUTE}}]} {
5025 Msg Error "PLACEROUTE FAILED!"
5027 Msg Info "PLACEROUTE PASSED."
5031 Msg Info "Run VERIFYTIMING ..."
5032 if {[
catch {run_tool -name {VERIFYTIMING} -script {Hog/Tcl/integrated/libero_timing.tcl}}]} {
5033 Msg CriticalWarning "VERIFYTIMING FAILED!"
5035 Msg Info "VERIFYTIMING PASSED \n"
5041 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5043 Msg Info "Git describe set to $describe"
5045 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5046 file mkdir $dst_dir/reports
5049 if {[
file exists $run_folder/versions.txt]} {
5050 file copy -force $run_folder/versions.txt $dst_dir
5052 Msg Warning "No versions file found in $run_folder/versions.txt"
5055 set timing_file_path [
file normalize "$repo_path/Projects/timing_libero.txt"]
5056 if {[
file exists $timing_file_path]} {
5057 file copy -force $timing_file_path $dst_dir/reports/Timing.txt
5058 set timing_file [open $timing_file_path "r"]
5059 set status_file [open "$dst_dir/timing.txt" "w"]
5060 puts $status_file "## $project_name Timing summary\n\n"
5061 puts $status_file "| | |"
5062 puts $status_file "| --- | --- |"
5063 while {[
gets $timing_file line] >= 0} {
5064 if {[
string match "SUMMARY" $line]} {
5065 while {[
gets $timing_file line] >= 0} {
5066 if {[
string match "END SUMMARY" $line]} {
5069 if {[
string first ":" $line] == -1} {
5072 set out_string "| [
string map {: | } $line] |"
5073 puts $status_file "$out_string"
5078 Msg Warning "No timing file found, not a problem if running locally"
5083 set force_rst "-forceOne"
5085 prj_run Map $force_rst
5086 prj_run PAR $force_rst
5098 proc GenerateBitstreamOnly {project_name {repo_path .}} {
5100 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5102 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5104 cd Projects/$project_name/$project_name.runs/impl_1
5105 Msg Info "Running pre-bitstream..."
5106 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5108 Msg Info "Writing bitstream for $project_name..."
5110 write_bitstream -force $dst_dir/$project_name-$describe.bit
5112 Msg Info "Running post-bitstream..."
5113 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5122 proc LaunchSimulation {project_name lib_path simsets {repo_path .} {scripts_only 0} {compile_only 0}} {
5125 set project [
file tail $project_name]
5126 set main_sim_folder [
file normalize "$repo_path/Projects/$project_name/$project.sim/"]
5128 if {$simsets != ""} {
5129 dict for {simset sim_dict} $simsets {
5130 lappend simsets_todo $simset
5132 Msg Info "Will run only the following simulation's sets (if they exist): $simsets_todo"
5135 if {$scripts_only == 1} {
5136 Msg Info "Only generating simulation scripts, not running simulations..."
5139 if {$compile_only == 1} {
5140 Msg Info "Only compiling simulation libraries, not running simulations..."
5145 set sim_dic [dict create]
5147 Msg Info "Retrieving list of simulation sets..."
5148 foreach s [get_filesets] {
5150 set use_simpass_str 0
5153 set type [get_property FILESET_TYPE $s]
5154 if {$type eq "SimulationSrcs"} {
5155 if {$simsets_todo != "" && $s ni $simsets_todo} {
5156 Msg Info "Skipping $s as it was not specified with the -simset option..."
5159 set sim_dict [
DictGet $simsets $s]
5160 set simulator [
DictGet $sim_dict "simulator"]
5161 set_property "target_simulator" $simulator [current_project]
5162 set hog_sim_props [
DictGet $sim_dict "hog"]
5163 dict for {prop_name prop_val} $hog_sim_props {
5164 # If HOG_SIMPASS_STR is set, use the HOG_SIMPASS_STR string to search for in logs, after simulation is done
5165 if {[string toupper $prop_name] == "HOG_SIMPASS_STR" && $prop_val != ""} {
5166 Msg Info "Setting simulation pass string as '$prop_val'"
5167 set use_simpass_str 1
5168 set simpass_str $prop_val
5170 if {[string toupper $prop_name] == "HOG_SILENT_SIM" && $prop_val == 1} {
5171 set quiet_sim " -quiet"
5177 Msg Info "Creating simulation scripts for $s..."
5178 if {[
file exists $repo_path/Top/$project_name/pre-simulation.tcl]} {
5179 Msg Info "Running $repo_path/Top/$project_name/pre-simulation.tcl"
5180 source $repo_path/Top/$project_name/pre-simulation.tcl
5182 if {[
file exists $repo_path/Top/$project_name/pre-$s-simulation.tcl]} {
5183 Msg Info "Running $repo_path/Top/$project_name/pre-$s-simulation.tcl"
5184 source Running $repo_path/Top/$project_name/pre-$s-simulation.tcl
5186 current_fileset -simset $s
5187 set sim_dir $main_sim_folder/$s/behav
5188 set sim_output_logfile $sim_dir/xsim/simulate.log
5189 if {([
string tolower $simulator] eq "xsim")} {
5190 set sim_name "xsim:$s"
5192 set simulation_command "launch_simulation $quiet_sim -simset [get_filesets $s]"
5193 if {[
catch $simulation_command log]} {
5196 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5197 lappend failed $sim_name
5202 if {$use_simpass_str == 1} {
5205 set file_desc [open $sim_output_logfile r]
5206 set log [read $file_desc]
5209 Msg Info "Searching for simulation pass string: '$simpass_str'"
5210 if {[
string first $simpass_str $log] == -1} {
5211 Msg CriticalWarning "Simulation failed for $s, error info: '$simpass_str' NOT found!"
5212 lappend failed $sim_name
5215 lappend success $sim_name
5219 lappend success $sim_name
5223 Msg Info "Simulation library path is set to $lib_path."
5225 if {!([
file exists $lib_path])} {
5226 Msg Warning "Could not find simulation library path: $lib_path, $simulator simulation will not work."
5230 if {$simlib_ok == 1} {
5231 set_property "compxlib.${simulator}_compiled_library_dir" [
file normalize $lib_path] [current_project]
5232 launch_simulation -scripts_only -simset [get_filesets $s]
5233 set top_name [get_property TOP $s]
5234 set sim_script [
file normalize $sim_dir/$simulator/]
5235 Msg Info "Adding simulation script location $sim_script for $s..."
5236 lappend sim_scripts $sim_script
5237 dict append sim_dic $sim_script $s
5239 Msg Error "Cannot run $simulator simulations without a valid library path"
5246 if {[
info exists sim_scripts] && $scripts_only == 0} {
5248 Msg Info "Generating IP simulation targets, if any..."
5250 foreach ip [get_ips] {
5251 generate_target simulation -quiet $ip
5256 Msg Info "====== Starting simulations runs ======"
5259 foreach s $sim_scripts {
5261 set cmd ./compile.sh
5262 Msg Info " ************* Compiling: $s ************* "
5264 set sim_name "comp:[dict get $sim_dic $s]"
5266 Msg CriticalWarning "Compilation failed for $s, error info: $::errorInfo"
5267 lappend failed $sim_name
5269 lappend success $sim_name
5271 Msg Info "###################### Compilation log starts ######################"
5272 Msg Info "\n\n$log\n\n"
5273 Msg Info "###################### Compilation log ends ######################"
5275 if {$compile_only == 1} {
5278 if {[
file exists "./elaborate.sh"] } {
5279 set cmd ./elaborate.sh
5280 Msg Info " ************* Elaborating: $s ************* "
5282 set sim_name "elab:[dict get $sim_dic $s]"
5284 Msg CriticalWarning "Elaboration failed for $s, error info: $::errorInfo"
5285 lappend failed $sim_name
5287 lappend success $sim_name
5289 Msg Info "###################### Elaboration log starts ######################"
5290 Msg Info "\n\n$log\n\n"
5291 Msg Info "###################### Elaboration log ends ######################"
5293 set cmd ./simulate.sh
5294 Msg Info " ************* Simulating: $s ************* "
5299 if {$use_simpass_str == 1} {
5300 if {[
string first $simpass_str $log] == -1} {
5304 Msg Debug "Simulation pass string not set, relying on simulator exit code."
5308 set sim_name "sim:[dict get $sim_dic $s]"
5310 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5311 lappend failed $sim_name
5313 lappend success $sim_name
5315 Msg Info "###################### Simulation log starts ######################"
5316 Msg Info "\n\n$log\n\n"
5317 Msg Info "###################### Simulation log ends ######################"
5322 if {[
llength $success] > 0} {
5323 set successes [
join $success "\n"]
5324 Msg Info "The following simulation sets were successful:\n\n$successes\n\n"
5327 if {[
llength $failed] > 0} {
5328 set failures [
join $failed "\n"]
5329 Msg Error "The following simulation sets have failed:\n\n$failures\n\n"
5331 }
elseif {[
llength $success] > 0} {
5332 Msg Info "All the [
llength $success] compilations, elaborations and simulations were successful."
5335 Msg Info "Simulation done."
5337 Msg Warning "Simulation is not yet supported for [
GetIDEName]."
5345 proc LaunchRTLAnalysis {repo_path} {
5347 Msg Info "Starting RTL Analysis..."
5349 synth_design -rtl -name rtl_1
5351 Msg Warning "RTL Analysis is not yet supported for [
GetIDEName]."
5364 proc LaunchSynthesis {reset do_create run_folder project_name {repo_path .} {ext_path ""} {njobs 4}} {
5366 if {$reset == 1 && $do_create == 0} {
5367 Msg Info "Resetting run before launching synthesis..."
5371 source $repo_path/Hog/Tcl/integrated/pre-synthesis.tcl
5373 launch_runs synth_1 -jobs $njobs -dir $run_folder
5375 set prog [get_property PROGRESS [get_runs synth_1]]
5376 set status [get_property STATUS [get_runs synth_1]]
5377 Msg Info "Run: synth_1 progress: $prog, status : $status"
5384 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path $ext_path] sha
5386 Msg Info "Git describe set to $describe"
5389 set xci_file [get_property IP_FILE $ip]
5391 set xci_path [
file dirname $xci_file]
5392 set xci_ip_name [
file rootname [
file tail $xci_file]]
5393 foreach rptfile [glob -nocomplain -directory $xci_path *.rpt] {
5394 file copy $rptfile $repo_path/bin/$project_name-$describe/reports
5398 if {$prog ne "100%"} {
5399 Msg Error "Synthesis error, status is: $status"
5403 set project [
file tail [
file rootname $project_name]]
5405 Msg Info "Number of jobs set to $njobs."
5406 set_global_assignment -name NUM_PARALLEL_PROCESSORS $njobs
5410 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$project_name] $repo_path] sha
5413 set revision [get_current_revision]
5416 set tool_and_command [
split [get_global_assignment -name PRE_FLOW_SCRIPT_FILE] ":"]
5417 set tool [
lindex $tool_and_command 0]
5418 set pre_flow_script [
lindex $tool_and_command 1]
5419 set cmd "$tool -t $pre_flow_script quartus_map $project $revision"
5425 Msg Warning "Can not execute command $cmd"
5426 Msg Warning "LOG: $log"
5428 Msg Info "Pre flow script executed!"
5432 if {![is_project_open]} {
5433 Msg Info "Re-opening project file $project_name..."
5434 project_open $project -current_revision
5438 if {[
catch {execute_module -tool ipg -args "--clean"} result]} {
5439 Msg Error "Result: $result\n"
5440 Msg Error "IP Generation failed. See the report file.\n"
5442 Msg Info "IP Generation was successful for revision $revision.\n"
5446 if {[
catch {execute_module -tool map -args "--parallel"} result]} {
5447 Msg Error "Result: $result\n"
5448 Msg Error "Analysis & Synthesis failed. See the report file.\n"
5450 Msg Info "Analysis & Synthesis was successful for revision $revision.\n"
5454 defvar_set -name RWNETLIST_32_64_MIXED_FLOW -value 0
5456 Msg Info "Run SYNTHESIS..."
5457 if {[
catch {run_tool -name {SYNTHESIZE}}]} {
5458 Msg Error "SYNTHESIZE FAILED!"
5460 Msg Info "SYNTHESIZE PASSED!"
5466 set force_rst "-forceOne"
5468 prj_run Synthesis $force_rst
5469 if {[prj_syn] == "synplify"} {
5470 prj_run Translate $force_rst
5473 Msg Error "Impossible condition. You need to run this in an IDE."
5484 proc LaunchVitisBuild {project_name {repo_path .} {stage "presynth"}} {
5485 set proj_name $project_name
5486 set bin_dir [
file normalize "$repo_path/bin"]
5490 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
5491 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$proj_name] $repo_path] commit version hog_hash hog_ver top_hash top_ver \
5492 libs hashes vers cons_ver cons_hash ext_names ext_hashes xml_hash xml_ver user_ip_repos user_ip_hashes user_ip_vers
5494 if {$commit == 0 } {
set commit $this_commit}
5498 foreach app_name [dict keys $ws_apps] {
5499 app config -name $app_name -set build-config Release
5502 WriteGenerics "vitisbuild" $repo_path $proj_name $date $timee $commit $version $top_hash $top_ver $hog_hash $hog_ver $cons_ver $cons_hash $libs \
5503 $vers $hashes $ext_names $ext_hashes $user_ip_repos $user_ip_vers $user_ip_hashes $flavour $xml_ver $xml_hash
5504 foreach app_name [dict keys $ws_apps] { app build -name $app_name}
5506 if {$stage == "presynth"} {
5507 Msg Info "Done building apps for $project_name..."
5511 Msg Info "Evaluating Git sha for $project_name..."
5512 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5515 Msg Info "Hog describe set to: $describe"
5516 set dst_dir [
file normalize "$bin_dir/$proj_name\-$describe"]
5517 if {![
file exists $dst_dir]} {
5518 Msg Info "Creating $dst_dir..."
5522 foreach app_name [dict keys $ws_apps] {
5523 set main_file "$repo_path/Projects/$project_name/vitis_classic/$app_name/Release/$app_name.elf"
5524 set dst_main [
file normalize "$dst_dir/[
file tail $proj_name]\-$app_name\-$describe.elf"]
5526 if {![
file exists $main_file]} {
5527 Msg Error "No Vitis .elf file found. Perhaps there was an issue building it?"
5531 Msg Info "Copying main binary file $main_file into $dst_main..."
5532 file copy -force $main_file $dst_main
5541 proc GetProcFromProps {repo_path props platform} {
5542 if {[dict exists $props "platform:$platform" "BIF"]} {
5543 set bif_file [dict get $props "platform:$platform" "BIF"]
5545 set bif_file "$repo_path/$bif_file"
5549 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
5559 proc GetBifFromProps {repo_path props platform} {
5560 if {[dict exists $props "platform:$platform" "BIF"]} {
5561 set bif_file [dict get $props "platform:$platform" "BIF"]
5563 set bif_file "$repo_path/$bif_file"
5567 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
5576 proc GetPartFromProps {props} {
5577 if {[dict exists $props "main" "PART"]} {
5578 return [
string tolower [dict get $props "main" "PART"]]
5580 Msg Error "Part number not found in properties"
5589 proc GetArchFromPart {part} {
5591 if {[
string match "xczu*" $part]} {
5593 }
elseif {[
string match "xc7z*" $part]} {
5595 }
elseif {[
string match "xck26*" $part]} {
5598 Msg CriticalWarning "Unknown part number: $part"
5607 proc GetAppsFromProps {props {list_names 0}} {
5608 set prop_apps [dict filter $props key {app:*}]
5609 set apps [dict create]
5610 set app_names [list]
5612 dict for {app_key app_value} $prop_apps {
5613 if {[regexp {^app:(.+)$} $app_key -> app_name]} {
5614 set app_name [string trim [string tolower $app_name]]
5615 # Convert only the keys of the inner dictionary to lowercase
5616 set app_value_lower [dict create]
5617 dict for {key value} $app_value {
5618 dict set app_value_lower [string tolower $key] $value
5620 dict set apps $app_name $app_value_lower
5621 lappend app_names $app_name
5624 if {$list_names eq 1} {
5635 proc GetPlatformsFromProps {props {list_names 0} {lower_case 0}} {
5636 set platforms [dict create]
5637 set platform_names [list]
5638 set prop_platforms [dict filter $props key {platform:*}]
5640 dict for {platform_key platform_value} $prop_platforms {
5641 if {[regexp {^platform:(.+)$} $platform_key -> platform_name]} {
5642 if {$lower_case == 1} {
5643 set platform_name [string trim [string tolower $platform_name]]
5645 set platform_name [string trim $platform_name]
5647 dict set platforms $platform_name $platform_value
5648 lappend platform_names $platform_name
5651 if {$list_names eq 1} {
5652 return $platform_names
5667 proc GenerateBootArtifacts {properties repo_path proj_dir bin_dir proj_name describe bitfile mmi_file} {
5668 set elf_list [glob -nocomplain "$bin_dir/*.elf"]
5672 if {[
llength $elf_list] == 0} {
5673 Msg Warning "No ELF files found in $bin_dir, skipping generation of boot artifacts"
5677 if {![
file exists $bitfile]} {
5678 Msg Warning "Bitfile $bitfile does not exist, skipping generation of boot artifacts"
5682 Msg Info "Generating boot artifacts for $proj_name..."
5683 Msg Info "Found apps: $apps"
5684 Msg Info "Found platforms: $platforms"
5688 foreach elf_file $elf_list {
5689 set elf_name [
file rootname [
file tail $elf_file]]
5690 Msg Info "Found elf name: $elf_name"
5691 Msg Info "Removing $describe from elf"
5694 if {[regexp "^(.+)-(.+)-$describe\$" $elf_name -> project_name elf_app]} {
5695 set elf_app [
string trim [
string tolower $elf_app]]
5696 Msg Info "Found elf_app: $elf_app"
5698 Msg Error "Could not extract app name from elf file: $elf_name"
5701 Msg Info "Removed project name ($project_name) and $describe from elf"
5703 set app_conf [dict get $apps $elf_app]
5704 set plat [dict get $app_conf "platform"]
5705 set app_proc [dict get $app_conf "proc"]
5708 if {[regexp -nocase {microblaze|risc} $app_proc]} {
5709 Msg Info "Detected soft processor ($app_proc) for $elf_app, updating bitstream memory with ELF file..."
5712 if {[dict size $proc_map] == 0} {
5713 Msg Error "Failed to read map from $proc_map_file"
5716 Msg Info "Found processor map: $proc_map"
5718 set proc_cell [
lindex [
split [dict get $proc_map $app_proc] ":"] 1]
5719 Msg Info "Updating memory at processor cell: $proc_cell"
5721 set update_mem_cmd "updatemem -force -meminfo $mmi_file -data $elf_file -bit $bitfile -proc $proc_cell -out $bitfile"
5722 set ret [
catch {
exec -ignorestderr {*}$update_mem_cmd >@ stdout} result]
5724 Msg Error "Error updating memory for $elf_app: $result"
5726 Msg Info "Done updating memory for $elf_app"
5729 Msg Info "Detected hard processor ($app_proc) for $elf_app. Make sure the .elf file is defined in the platform ($plat)\
5730 .bif file to be included in the bootable binary image (.bin) generation."
5736 foreach plat $platforms {
5738 if {$bif_file != ""} {
5739 Msg Info "Generating bootable binary image (.bin) for $plat"
5741 Msg Info "Architecture: $arch"
5742 Msg Info "BIF file: $bif_file"
5743 set bootgen_cmd "bootgen -arch $arch -image $bif_file -o i $bin_dir/$proj_name-$plat-$describe.bin -w on"
5744 set ret [
catch {
exec -ignorestderr {*}$bootgen_cmd >@ stdout} result]
5746 Msg Error "Error generating bootable binary image (.bin) for $elf_app: $result"
5748 Msg Info "Done generating bootable binary image (.bin) for $plat"
5757 proc ReadProcMap {proc_map_file} {
5758 set proc_map [dict create]
5759 if {[
file exists $proc_map_file]} {
5760 set f [open $proc_map_file "r"]
5761 while {[
gets $f line] >= 0} {
5762 Msg Debug "Line: $line"
5763 if {[regexp {^(\S+)\s+(.+)$} $line -> key value]} {
5764 Msg Debug "Found key: $key, value: $value in proc map file"
5765 dict set proc_map $key $value
5779 proc ListProjects {{repo_path .} {print 1} {ret_conf 0}} {
5780 set top_path [
file normalize $repo_path/Top]
5781 set confs [
findFiles [
file normalize $top_path] hog.conf]
5783 set confs [lsort $confs]
5787 set p [
Relative $top_path [
file dirname $c]]
5790 if {$description eq "test"} {
5791 set description " - Test project"
5792 }
elseif {$description ne ""} {
5793 set description " - $description"
5796 if {$print == 1 || $description ne " - Test project"} {
5798 set g [
file dirname $p]
5809 if {$ret_conf == 0} {
5821 proc Md5Sum {file_name} {
5822 if {!([
file exists $file_name])} {
5823 Msg Warning "Could not find $file_name."
5826 if {[
catch {
package require md5 2.0.7} result]} {
5827 Msg Warning "Tcl package md5 version 2.0.7 not found ($result), will use command line..."
5828 set hash [
lindex [
Execute md5sum $file_name] 0]
5830 set file_hash [
string tolower [md5::md5 -hex -file $file_name]]
5844 proc MergeDict {dict0 dict1 {remove_duplicates 1}} {
5845 set outdict [dict merge $dict1 $dict0]
5846 foreach key [dict keys $dict1] {
5847 if {[dict exists $dict0 $key]} {
5848 set temp_list [dict get $dict1 $key]
5849 foreach item $temp_list {
5851 if {[
IsInList $item [
DictGet $outdict $key]] == 0 || $remove_duplicates == 0} {
5853 dict lappend outdict $key $item
5865 proc MoveElementToEnd {inputList element} {
5866 set index [lsearch $inputList $element]
5868 set inputList [
lreplace $inputList $index $index]
5869 lappend inputList $element
5878 proc OpenProject {project_file repo_path} {
5880 open_project $project_file
5882 set project_folder [
file dirname $project_file]
5883 set project [
file tail [
file rootname $project_file]]
5884 if {[
file exists $project_folder]} {
5886 if {![is_project_open]} {
5887 Msg Info "Opening existing project file $project_file..."
5888 project_open $project -current_revision
5891 Msg Error "Project directory not found for $project_file."
5895 Msg Info "Opening existing project file $project_file..."
5897 open_project -file $project_file -do_backup_on_convert 1 -backup_file {./Projects/$project_file.zip}
5899 Msg Info "Opening existing project file $project_file..."
5900 prj_project open $project_file
5902 Msg Error "This IDE is currently not supported by Hog. Exiting!"
5909 return $tcl_platform(platform)
5919 proc ParseJSON {JSON_FILE JSON_KEY} {
5920 set result [
catch {
package require Tcl 8.4} TclFound]
5921 if {"$result" != "0"} {
5922 Msg CriticalWarning "Cannot find Tcl package version equal or higher than 8.4.\n $TclFound\n Exiting"
5926 set result [
catch {
package require json} JsonFound]
5927 if {"$result" != "0"} {
5928 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
5931 set JsonDict [json::json2dict $JSON_FILE]
5932 set result [
catch {dict get $JsonDict $JSON_KEY} RETURNVALUE]
5933 if {"$result" != "0"} {
5934 Msg CriticalWarning "Cannot find $JSON_KEY in $JSON_FILE\n Exiting"
5947 proc ProjectExists {project {repo_path .}} {
5948 set index [lsearch -exact [
ListProjects $repo_path 0] $project]
5954 Msg Error "Project $project not found in repository $repo_path"
5965 proc ReadConf {file_name} {
5966 if {[
catch {
package require inifile 0.2.3} ERROR]} {
5967 Msg Error "Could not find inifile package version 0.2.3 or higher.\n
5968 To use ghdl, libero or diamond with Hog, you need to install the tcllib package\n
5969 You can install it with 'sudo apt install tcllib' on Debian/Ubuntu or 'sudo dnf install tcllib' on Fedora/RedHat/CentOs."
5973 ::ini::commentchar "#"
5974 set f [::ini::open $file_name]
5975 set properties [dict create]
5976 foreach sec [::ini::sections $f] {
5978 if {$new_sec == "files"} {
5981 set key_pairs [::ini::get $f $sec]
5983 regsub -all {\{\"} $key_pairs "\{" key_pairs
5984 regsub -all {\"\}} $key_pairs "\}" key_pairs
5986 dict set properties $new_sec [dict create {*}$key_pairs]
5999 proc ReadExtraFileList {extra_file_name} {
6000 set extra_file_dict [dict create]
6001 if {[
file exists $extra_file_name]} {
6002 set file [open $extra_file_name "r"]
6003 set file_data [read $file]
6006 set data [
split $file_data "\n"]
6007 foreach line $data {
6008 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
6009 set ip_and_md5 [regexp -all -inline {\S+} $line]
6010 dict lappend extra_file_dict "[
lindex $ip_and_md5 0]" "[
lindex $ip_and_md5 1]"
6014 return $extra_file_dict
6034 proc ReadListFile {args} {
6037 if {[
catch {
package require cmdline} ERROR]} {
6038 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
6044 {lib.arg "" "The name of the library files will be added to, if not given will be extracted from the file name."}
6045 {fileset.arg "" "The name of the library, from the main list file"}
6046 {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."}
6047 {print_log "If set, will use PrintFileTree for the VIEW directive"}
6048 {indent.arg "" "Used to indent files with the VIEW directive"}
6051 set usage "USAGE: ReadListFile \[options\] <list file> <path>"
6052 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
6058 set list_file [
lindex $args 0]
6059 set path [
lindex $args 1]
6060 set sha_mode $options(sha_mode)
6061 set lib $options(lib)
6062 set fileset $options(fileset)
6063 set print_log $options(print_log)
6064 set indent $options(indent)
6066 if {$sha_mode == 1} {
6067 set sha_mode_opt "-sha_mode"
6072 if {$print_log == 1} {
6073 set print_log_opt "-print_log"
6075 set print_log_opt ""
6080 set lib [
file rootname [
file tail $list_file]]
6082 set fp [open $list_file r]
6083 set file_data [read $fp]
6085 set list_file_ext [
file extension $list_file]
6086 switch $list_file_ext {
6088 if {$fileset eq ""} {
6094 set fileset "constrs_1"
6097 set fileset "sources_1"
6101 set libraries [dict create]
6102 set filesets [dict create]
6103 set properties [dict create]
6105 set data [
split $file_data "\n"]
6107 set n [
llength $data]
6109 if {$print_log == 1} {
6110 if {$indent eq ""} {
6111 set list_file_rel [
file tail $list_file]
6112 Msg Status "\n$list_file_rel"
6116 Msg Debug "$n lines read from $list_file."
6119 foreach line $data {
6121 if {![regexp {^[\t\s]*$} $line] & ![regexp {^[\t\s]*\#} $line]} {
6122 set file_and_prop [regexp -all -inline {\S+} $line]
6123 set srcfile [
lindex $file_and_prop 0]
6124 set srcfile "$path/$srcfile"
6126 set srcfiles [glob -nocomplain $srcfile]
6129 if {$srcfiles != $srcfile && ![
string equal $srcfiles ""]} {
6130 Msg Debug "Wildcard source expanded from $srcfile to $srcfiles"
6132 if {![
file exists $srcfile]} {
6133 if {$print_log == 0} {
6134 Msg CriticalWarning "File: $srcfile (from list file: $list_file) does not exist."
6140 foreach vhdlfile $srcfiles {
6141 if {[
file exists $vhdlfile]} {
6142 set vhdlfile [
file normalize $vhdlfile]
6143 set extension [
file extension $vhdlfile]
6145 set prop [
lrange $file_and_prop 1 end]
6148 set library [
lindex [regexp -inline {\ylib\s*=\s*(.+?)\y.*} $prop] 1]
6149 if {$library == ""} {
6153 if {$extension == $list_file_ext} {
6156 set ref_path [
lindex [regexp -inline {\ypath\s*=\s*(\S+).*} $prop] 1]
6157 if {$ref_path eq ""} {
6160 set ref_path [
file normalize $path/$ref_path]
6162 Msg Debug "List file $vhdlfile found in list file, recursively opening it using path \"$ref_path\"..."
6163 if {$print_log == 1} {
6164 if {[
file normalize $last_printed] ne [
file normalize $vhdlfile]} {
6165 Msg Status "$indent Inside [
file tail $vhdlfile]:"
6169 lassign [
ReadListFile {*}"-indent \" $indent\" -lib $library -fileset $fileset $sha_mode_opt $print_log_opt $vhdlfile $ref_path"] l p fs
6171 set properties [
MergeDict $p $properties]
6173 }
elseif {[lsearch {.src .sim .con ReadExtraFileList} $extension] >= 0} {
6175 Msg Error "$vhdlfile cannot be included into $list_file, $extension files must be included into $extension files."
6178 regsub -all " *= *" $prop "=" prop
6182 if {[
string first "lib=" $p] == -1} {
6184 set pos [
string first "=" $p]
6188 set prop_name [
string range $p 0 [
expr {$pos - 1}]]
6191 dict lappend properties $vhdlfile $p
6192 Msg Debug "Adding property $p to $vhdlfile..."
6193 }
elseif {$list_file_ext != ".ipb"} {
6194 Msg Warning "Setting Property $p is not supported for file $vhdlfile or it is already its default. \
6199 if {[lsearch {.xci .ip .bd .xcix} $extension] >= 0} {
6201 set lib_name "ips.src"
6202 }
elseif {[
IsInList $extension {.vhd .vhdl}] || $list_file_ext == ".sim"} {
6204 if {![
IsInList $extension {.vhd .vhdl}]} {
6205 set lib_name "others.sim"
6207 set lib_name "$library$list_file_ext"
6209 }
elseif {$list_file_ext == ".con"} {
6210 set lib_name "sources.con"
6211 }
elseif {$list_file_ext == ".ipb"} {
6212 set lib_name "xml.ipb"
6213 }
elseif { [
IsInList $list_file_ext {.src}] && [
IsInList $extension {.c .cpp .h .hpp}] } {
6215 set lib_name "$library$list_file_ext"
6218 set lib_name "others.src"
6221 Msg Debug "Appending $vhdlfile to $lib_name list..."
6222 dict lappend libraries $lib_name $vhdlfile
6223 if {$sha_mode != 0 && [
file type $vhdlfile] eq "link"} {
6226 dict lappend libraries $lib_name $real_file
6227 Msg Debug "File $vhdlfile is a soft link, also adding the real file: $real_file"
6232 if {[dict exists $filesets $fileset] == 0} {
6234 Msg Debug "Adding $fileset to the fileset dictionary..."
6235 Msg Debug "Adding library $lib_name to fileset $fileset..."
6236 dict set filesets $fileset $lib_name
6240 Msg Debug "Adding library $lib_name to fileset $fileset..."
6241 dict lappend filesets $fileset $lib_name
6247 Msg CriticalWarning "File $vhdlfile not found."
6253 if {$sha_mode != 0} {
6255 if {$list_file_ext eq ".ipb"} {
6256 set sha_lib "xml.ipb"
6258 set sha_lib $lib$list_file_ext
6260 dict lappend libraries $sha_lib [
file normalize $list_file]
6261 if {[
file type $list_file] eq "link"} {
6264 dict lappend libraries $lib$list_file_ext $real_file
6265 Msg Debug "List file $list_file is a soft link, also adding the real file: $real_file"
6268 return [list $libraries $properties $filesets]
6277 proc Relative {base dst {quiet 0}} {
6278 if {![
string equal [
file pathtype $base] [
file pathtype $dst]]} {
6280 Msg CriticalWarning "Unable to compute relation for paths of different path types: [
file pathtype $base] vs. [
file pathtype $dst], ($base vs. $dst)"
6285 set base [
file normalize [
file join [
pwd] $base]]
6286 set dst [
file normalize [
file join [
pwd] $dst]]
6289 set base [
file split $base]
6290 set dst [
file split $dst]
6292 while {[
string equal [
lindex $dst 0] [
lindex $base 0]]} {
6293 set dst [
lrange $dst 1 end]
6294 set base [
lrange $base 1 end]
6295 if {![
llength $dst]} {break}
6298 set dstlen [
llength $dst]
6299 set baselen [
llength $base]
6301 if {($dstlen == 0) && ($baselen == 0)} {
6304 while {$baselen > 0} {
6305 set dst [
linsert $dst 0 ..]
6308 set dst [
eval [
linsert $dst 0 file join]]
6319 proc RelativeLocal {pathName filePath} {
6320 if {[
string first [
file normalize $pathName] [
file normalize $filePath]] != -1} {
6321 return [
Relative $pathName $filePath]
6332 proc RemoveDuplicates {mydict} {
6333 set new_dict [dict create]
6334 foreach key [dict keys $mydict] {
6335 set values [
DictGet $mydict $key]
6336 foreach value $values {
6337 set idxs [
lreverse [
lreplace [lsearch -exact -all $values $value] 0 0]]
6339 set values [
lreplace $values $idx $idx]
6342 dict set new_dict $key $values
6353 proc ResetRepoFiles {reset_file} {
6354 if {[
file exists $reset_file]} {
6355 Msg Info "Found $reset_file, opening it..."
6356 set fp [open $reset_file r]
6357 set wild_cards [lsearch -all -inline -not -regexp [
split [read $fp] "\n"] "^ *$"]
6359 Msg Info "Found the following files/wild cards to restore if modified: $wild_cards..."
6360 foreach w $wild_cards {
6362 if {[
llength $mod_files] > 0} {
6363 Msg Info "Found modified $w files: $mod_files, will restore them..."
6366 Msg Info "No modified $w files found."
6377 proc RestoreModifiedFiles {{repo_path "."} {pattern "."}} {
6380 set ret [
Git checkout $pattern]
6391 proc SearchHogProjects {dir} {
6392 set projects_list {}
6393 if {[
file exists $dir]} {
6394 if {[
file isdirectory $dir]} {
6395 foreach proj_dir [glob -nocomplain -types d $dir/*] {
6396 if {![regexp {^.*Top/+(.*)$} $proj_dir dummy proj_name]} {
6397 Msg Warning "Could not parse Top directory $dir"
6400 if {[
file exists "$proj_dir/hog.conf"]} {
6401 lappend projects_list $proj_name
6404 lappend projects_list $p
6409 Msg Error "Input $dir is not a directory!"
6412 Msg Error "Directory $dir doesn't exist!"
6414 return $projects_list
6423 proc SetGenericsSimulation {repo_path proj_dir target} {
6424 set top_dir "$repo_path/Top/$proj_dir"
6425 set simsets [get_filesets]
6426 if {$simsets != ""} {
6427 foreach simset $simsets {
6429 if {[get_property FILESET_TYPE $simset] != "SimulationSrcs"} {
6433 set merged_generics_dict [dict create]
6437 set simset_generics [
DictGet $simset_dict "generics"]
6438 set merged_generics_dict [
MergeDict $merged_generics_dict $simset_generics 0]
6440 set_property generic $generic_str [get_filesets $simset]
6441 Msg Debug "Setting generics $generic_str for simulator $target\
6442 and simulation file-set $simset..."
6454 proc SetTopProperty {top_module fileset} {
6455 Msg Info "Setting TOP property to $top_module module"
6458 set_property "top" $top_module [get_filesets $fileset]
6461 set_global_assignment -name TOP_LEVEL_ENTITY $top_module
6463 set_root -module $top_module
6465 prj_impl option top $top_module
6470 proc VIVADO_PATH_PROPERTIES {} {
6471 return {"\.*\.TCL\.PRE$" "^.*\.TCL\.POST$" "^RQS_FILES$" "^INCREMENTAL\_CHECKPOINT$" "NOC\_SOLUTION\_FILE"}
6475 proc VITIS_PATH_PROPERTIES {} {
6476 return {"^HW$" "^XPFM$" "^LINKER-SCRIPT$" "^LIBRARIES$" "^LIBRARY-SEARCH-PATH$"}
6486 proc WriteConf {file_name config {comment ""}} {
6487 if {[
catch {
package require inifile 0.2.3} ERROR]} {
6488 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
6492 ::ini::commentchar "#"
6493 set f [::ini::open $file_name w]
6495 foreach sec [dict keys $config] {
6496 set section [dict get $config $sec]
6497 dict for {p v} $section {
6498 if {[string trim $v] == ""} {
6499 Msg Warning "Property $p has empty value. Skipping..."
6502 ::ini::set $f $sec $p $v
6507 if {![
string equal "$comment" ""]} {
6508 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $comment
6509 set hog_header "Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
6510 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $hog_header
6525 proc WriteGenerics {mode repo_path design date timee\
6526 commit version top_hash top_ver hog_hash hog_ver \
6527 cons_ver cons_hash libs vers hashes ext_names ext_hashes \
6528 user_ip_repos user_ip_vers user_ip_hashes flavour {xml_ver ""} {xml_hash ""}} {
6529 Msg Info "Passing parameters/generics to project's top module..."
6532 set generic_string [
concat \
6544 if {$xml_hash != "" && $xml_ver != ""} {
6545 lappend generic_string \
6550 foreach l $libs v $vers h $hashes {
6556 set ver [regsub -all {[\.-]} $ver {_}]
6557 set hash [regsub -all {[\.-]} $hash {_}]
6558 lappend generic_string "$ver" "$hash"
6561 foreach e $ext_names h $ext_hashes {
6563 lappend generic_string "$hash"
6566 foreach repo $user_ip_repos v $user_ip_vers h $user_ip_hashes {
6567 set repo_name [
file tail $repo]
6568 set ver "[
string toupper $repo_name]_VER=[
FormatGeneric $v]"
6569 set hash "[
string toupper $repo_name]_SHA=[
FormatGeneric $h]"
6570 set ver [regsub -all {[\.-]} $ver {_}]
6571 set hash [regsub -all {[\.-]} $hash {_}]
6572 lappend generic_string "$ver" "$hash"
6575 if {$flavour != -1} {
6576 lappend generic_string "FLAVOUR=$flavour"
6582 set generic_string "$prj_generics $generic_string"
6588 if {$mode == "create" || [
IsISE]} {
6591 if {[
file exists $top_file]} {
6594 Msg Debug "Found top level generics $generics in $top_file"
6596 set filtered_generic_string ""
6598 foreach generic_to_set [
split [
string trim $generic_string]] {
6599 set key [
lindex [
split $generic_to_set "="] 0]
6600 if {[dict exists $generics $key]} {
6601 Msg Debug "Hog generic $key found in $top_name"
6602 lappend filtered_generic_string "$generic_to_set"
6604 Msg Warning "Generic $key is passed by Hog but is NOT present in $top_name."
6610 set generic_string $filtered_generic_string
6615 set_property generic $generic_string [current_fileset]
6616 Msg Info "Setting parameters/generics..."
6617 Msg Debug "Detailed parameters/generics: $generic_string"
6622 set simulator [get_property target_simulator [current_project]]
6623 if {$mode == "create"} {
6630 Msg Info "Setting Synplify parameters/generics one by one..."
6631 foreach generic $generic_string {
6632 Msg Debug "Setting Synplify generic: $generic"
6633 set_option -hdl_param -set "$generic"
6636 Msg Info "Setting Diamond parameters/generics one by one..."
6637 prj_impl option -impl Implementation0 HDL_PARAM "$generic_string"
6639 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
6641 foreach app_name [dict keys $ws_apps] {
6642 set defined_symbols [app config -name $app_name -get define-compiler-symbols]
6643 foreach generic_to_set [
split [
string trim $generic_string]] {
6644 set key [
lindex [
split $generic_to_set "="] 0]
6645 set value [
lindex [
split $generic_to_set "="] 1]
6646 if {[
string match "32'h*" $value]} {
6647 set value [
string map {"32'h" "0x"} $value]
6650 foreach symbol [
split $defined_symbols ";"] {
6651 if {[
string match "$key=*" $symbol]} {
6652 Msg Debug "Generic $key found in $app_name, removing it..."
6653 app config -name $app_name -remove define-compiler-symbols "$symbol"
6657 Msg Info "Setting Vitis parameters/generics for app $app_name: $key=$value"
6658 app config -name $app_name define-compiler-symbols "$key=$value"
6670 proc WriteGenericsToBdIPs {mode repo_path proj generic_string} {
6671 Msg Debug "Parameters/generics passed to WriteGenericsToIP: $generic_string"
6673 set bd_ip_generics false
6675 if {[dict exists $properties "hog"]} {
6676 set propDict [dict get $properties "hog"]
6677 if {[dict exists $propDict "PASS_GENERICS_TO_BD_IPS"]} {
6678 set bd_ip_generics [dict get $propDict "PASS_GENERICS_TO_BD_IPS"]
6682 if {[
string compare [
string tolower $bd_ip_generics] "false"] == 0} {
6686 if {$mode == "synth"} {
6687 Msg Info "Attempting to apply generics pre-synthesis..."
6688 set PARENT_PRJ [get_property "PARENT.PROJECT_PATH" [current_project]]
6689 set workaround [open "$repo_path/Projects/$proj/.hog/presynth_workaround.tcl" "w"]
6690 puts $workaround "source \[lindex \$argv 0\];"
6691 puts $workaround "open_project \[lindex \$argv 1\];"
6692 puts $workaround "WriteGenericsToBdIPs \[lindex \$argv 2\] \[lindex \$argv 3\] \[lindex \$argv 4\] \[lindex \$argv 5\];"
6693 puts $workaround "close_project"
6697 exec vivado -mode batch -source $repo_path/Projects/$proj/.hog/presynth_workaround.tcl \
6698 -tclargs $repo_path/Hog/Tcl/hog.tcl $PARENT_PRJ \
6699 "childprocess" $repo_path $proj $generic_string
6702 Msg Error "Encountered an error while attempting workaround: $errMsg"
6704 file delete $repo_path/Projects/$proj/.hog/presynth_workaround.tcl
6706 Msg Info "Done applying generics pre-synthesis."
6710 Msg Info "Looking for IPs to add generics to..."
6711 set ips_generic_string ""
6712 foreach generic_to_set [
split [
string trim $generic_string]] {
6713 set key [
lindex [
split $generic_to_set "="] 0]
6714 set value [
lindex [
split $generic_to_set "="] 1]
6715 append ips_generic_string "CONFIG.$key $value "
6719 if {[
string compare [
string tolower $bd_ip_generics] "true"] == 0} {
6722 set ip_regex $bd_ip_generics
6725 set ip_list [get_ips -regex $ip_regex]
6726 Msg Debug "IPs found with regex \{$ip_regex\}: $ip_list"
6728 set regen_targets {}
6730 foreach {ip} $ip_list {
6731 set WARN_ABOUT_IP false
6732 set ip_props [list_property [get_ips $ip]]
6735 if {[lsearch -exact $ip_props "IS_BD_CONTEXT"] == -1} {
6739 if {[get_property "IS_BD_CONTEXT" [get_ips $ip]] eq "1"} {
6740 foreach {ip_prop} $ip_props {
6741 if {[dict exists $ips_generic_string $ip_prop]} {
6742 if {$WARN_ABOUT_IP == false} {
6743 lappend regen_targets [get_property SCOPE [get_ips $ip]]
6744 Msg Warning "The ip \{$ip\} contains generics that are set by Hog.\
6745 If this is IP is apart of a block design, the .bd file may contain stale, unused, values.\
6746 Hog will always apply the most up-to-date values to the IP during synthesis,\
6747 however these values may or may not be reflected in the .bd file."
6748 set WARN_ABOUT_IP true
6753 set xci_path [get_property IP_FILE [get_ips $ip]]
6755 if {[
string equal $generic_format "ERROR"]} {
6756 Msg Warning "Could not find format for generic $ip_prop in IP $ip. Skipping..."
6760 set value_to_set [dict get $ips_generic_string $ip_prop]
6761 switch -exact $generic_format {
6763 if {[
string match "32'h*" $value_to_set]} {
6764 scan [
string map {"32'h" ""} $value_to_set] "%x" value_to_set
6768 set value_to_set [
expr {$value_to_set ? "true" : "false"}]
6771 if {[
string match "32'h*" $value_to_set]} {
6772 binary scan [
binary format H* [
string map {"32'h" ""} $value_to_set]] d value_to_set
6776 if {[
string match "32'h*" $value_to_set]} {
6777 set value_to_set [
string map {"32'h" "0x"} $value_to_set]
6781 set value_to_set [
format "%s" $value_to_set]
6784 Msg Warning "Unknown generic format $generic_format for IP $ip. Will attempt to pass as string..."
6789 Msg Info "The IP \{$ip\} contains: $ip_prop ($generic_format), setting it to $value_to_set."
6790 if {[
catch {set_property -name $ip_prop -value $value_to_set -objects [get_ips $ip]} prop_error]} {
6791 Msg CriticalWarning "Failed to set property $ip_prop to $value_to_set for IP \{$ip\}: $prop_error"
6798 foreach {regen_target} [lsort -unique $regen_targets] {
6799 Msg Info "Regenerating target: $regen_target"
6800 if {[
catch {generate_target -force all [get_files $regen_target]} prop_error]} {
6801 Msg CriticalWarning "Failed to regen targets: $prop_error"
6809 proc GetGenericFormatFromXciXML {generic_name xml_file} {
6810 if {![
file exists $xml_file]} {
6811 Msg Error "Could not find XML file: $xml_file"
6815 set fp [open $xml_file r]
6816 set xci_data [read $fp]
6819 set paramType "string"
6820 set modelparam_regex [
format {^.*\y%s\y.*$} [
string map {"CONFIG." "MODELPARAM_VALUE."} $generic_name]]
6821 set format_regex {format="([^"]+)"}
6823 set line [
lindex [regexp -inline -line $modelparam_regex $xci_data] 0]
6824 Msg Debug "line: $line"
6826 if {[regexp $format_regex $line match format_value]} {
6827 Msg Debug "Extracted: $format_value format from xml"
6828 set paramType $format_value
6830 Msg Debug "No format found, using string"
6839 proc GetGenericFormatFromXci {generic_name xci_file} {
6840 if {![
file exists $xci_file]} {
6841 Msg Error "Could not find XCI file: $xci_file"
6845 set fp [open $xci_file r]
6846 set xci_data [read $fp]
6849 set paramType "string"
6850 if {[
string first "xilinx.com:schema:json_instance:1.0" $xci_data] == -1} {
6851 Msg Debug "XCI format is not JSON, trying XML..."
6852 set xml_file "[
file rootname $xci_file].xml"
6856 set generic_name [
string map {"CONFIG." ""} $generic_name]
6857 set ip_inst [
ParseJSON $xci_data "ip_inst"]
6858 set parameters [dict get $ip_inst parameters]
6859 set component_parameters [dict get $parameters component_parameters]
6860 if {[dict exists $component_parameters $generic_name]} {
6861 set generic_info [dict get $component_parameters $generic_name]
6862 if {[dict exists [
lindex $generic_info 0] format]} {
6863 set paramType [dict get [
lindex $generic_info 0] format]
6864 Msg Debug "Extracted: $paramType format from xci"
6867 Msg Debug "No format found, using string"
6880 proc WriteGitLabCIYAML {proj_name {ci_conf ""}} {
6881 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
6882 Msg CriticalWarning "Cannot find package YAML.\n Error message: $YAMLPACKAGE. \
6883 If you are running on tclsh, you can fix this by installing package \"tcllib\""
6888 if {$ci_conf != ""} {
6890 foreach sec [dict keys $ci_confs] {
6891 if {[
string first : $sec] == -1} {
6892 lappend job_list $sec
6896 set job_list {"generate_project" "simulate_project"}
6900 set out_yaml [huddle create]
6901 foreach job $job_list {
6903 set huddle_tags [huddle list]
6905 set sec_dict [dict create]
6907 if {$ci_confs != ""} {
6908 foreach var [dict keys [dict get $ci_confs $job]] {
6909 if {$var == "tags"} {
6910 set tag_section "tags"
6911 set tags [dict get [dict get $ci_confs $job] $var]
6912 set tags [
split $tags ","]
6914 set tag_list [huddle list $tag]
6915 set huddle_tags [huddle combine $huddle_tags $tag_list]
6918 dict set sec_dict $var [dict get [dict get $ci_confs $job] $var]
6924 set huddle_variables [huddle create "PROJECT_NAME" $proj_name "extends" ".vars"]
6925 if {[dict exists $ci_confs "$job:variables"]} {
6926 set var_dict [dict get $ci_confs $job:variables]
6927 foreach var [dict keys $var_dict] {
6929 set value [dict get $var_dict "$var"]
6930 set var_inner [huddle create "$var" "$value"]
6931 set huddle_variables [huddle combine $huddle_variables $var_inner]
6936 set middle [huddle create "extends" ".$job" "variables" $huddle_variables]
6937 foreach sec [dict keys $sec_dict] {
6938 set value [dict get $sec_dict $sec]
6939 set var_inner [huddle create "$sec" "$value"]
6940 set middle [huddle combine $middle $var_inner]
6942 if {$tag_section != ""} {
6943 set middle2 [huddle create "$tag_section" $huddle_tags]
6944 set middle [huddle combine $middle $middle2]
6947 set outer [huddle create "$job:$proj_name" $middle]
6948 set out_yaml [huddle combine $out_yaml $outer]
6951 return [
string trimleft [yaml::huddle2yaml $out_yaml] "-"]
6961 proc WriteListFiles {libs props list_path repo_path {ext_path ""}} {
6963 foreach lib [dict keys $libs] {
6964 if {[
llength [
DictGet $libs $lib]] > 0} {
6965 set list_file_name $list_path$lib
6966 set list_file [open $list_file_name w]
6967 Msg Info "Writing $list_file_name..."
6968 puts $list_file "#Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
6969 foreach file [
DictGet $libs $lib] {
6971 set prop [
DictGet $props $file]
6975 puts $list_file "$file_path $prop"
6978 set ext_list_file [open "[
file rootname $list_file].ext" a]
6979 puts $ext_list_file "$file_path $prop"
6980 close $ext_list_file
6983 Msg Warning "The path of file $file is not relative to your repository. Please check!"
6999 proc WriteSimListFile {simset libs props simsets list_path repo_path {force 0}} {
7001 set list_file_name $list_path/${simset}.sim
7002 if {$force == 0 && [
file exists $list_file_name]} {
7003 Msg Info "List file $list_file_name already exists, skipping..."
7007 set list_file [open $list_file_name a+]
7010 puts $list_file "\[files\]"
7011 Msg Info "Writing $list_file_name..."
7012 foreach lib [
DictGet $simsets $simset] {
7013 foreach file [
DictGet $libs $lib] {
7015 set prop [
DictGet $props $file]
7019 set lib_name [
file rootname $lib]
7020 if {$lib_name != $simset && [
file extension $file] == ".vhd" && [
file extension $file] == ""} {
7021 lappend prop "lib=$lib_name"
7023 puts $list_file "$file_path $prop"
7026 Msg Warning "The path of file $file is not relative to your repository. Please check!"
7038 proc WriteToFile {File msg} {
7039 set f [open $File a+]
7050 proc WriteUtilizationSummary {input output project_name run} {
7051 set f [open $input "r"]
7052 set o [open $output "a"]
7053 puts $o "## $project_name $run Utilization report\n\n"
7054 struct::matrix util_m
7055 util_m add columns 14
7058 util_m add row "| **Site Type** | **Used** | **Fixed** | **Prohibited** | **Available** | **Util%** |"
7059 util_m add row "| --- | --- | --- | --- | --- | --- |"
7061 util_m add row "| **Site Type** | **Used** | **Fixed** | **Available** | **Util%** |"
7062 util_m add row "| --- | --- | --- | --- | --- |"
7072 while {[
gets $f line] >= 0} {
7073 if {([
string first "| CLB LUTs" $line] >= 0 || [
string first "| Slice LUTs" $line] >= 0) && $luts == 0} {
7074 util_m add row $line
7077 if {([
string first "| CLB Registers" $line] >= 0 || [
string first "| Slice Registers" $line] >= 0) && $regs == 0} {
7078 util_m add row $line
7081 if {[
string first "| Block RAM Tile" $line] >= 0 && $bram == 0} {
7082 util_m add row $line
7085 if {[
string first "URAM " $line] >= 0 && $uram == 0} {
7086 util_m add row $line
7089 if {[
string first "DSPs" $line] >= 0 && $dsps == 0} {
7090 util_m add row $line
7093 if {[
string first "Bonded IOB" $line] >= 0 && $ios == 0} {
7094 util_m add row $line
7101 puts $o [util_m format 2string]
7107 Msg Error "Found Git version older than 2.7.2. Hog will not work as expected, exiting now."