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*([^ ]+)} $props] 1]
269 set cell [
lindex [regexp -inline {\yscoped_to_cells\s*=\s*([^ ]+)} $props] 1]
270 if {[
file extension $f] == ".elf" || (([
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 [
split $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 ".svp" [list "verilog" "verilog_header" "nosynth" "noimpl" "nosim" "2005" "2009"] \
588 ".do" [list "nosim"] \
589 ".udo" [list "nosim"] \
590 ".xci" [list "nosynth" "noimpl" "nosim" "locked"] \
591 ".xdc" [list "nosynth" "noimpl" "scoped_to_ref" "scoped_to_cells"] \
592 ".tcl" [list "nosynth" "noimpl" "nosim" "source" "qsys" "noadd"\
593 "--block-symbol-file" "--clear-output-directory" "--example-design"\
594 "--export-qsys-script" "--family" "--greybox" "--ipxact"\
595 "--jvm-max-heap-size" "--parallel" "--part" "--search-path"\
596 "--simulation" "--synthesis" "--testbench" "--testbench-simulation"\
597 "--upgrade-ip-cores" "--upgrade-variation-file"
599 ".qsys" [list "nogenerate" "noadd" "--block-symbol-file" "--clear-output-directory" "--example-design"\
600 "--export-qsys-script" "--family" "--greybox" "--ipxact" "--jvm-max-heap-size" "--parallel"\
601 "--part" "--search-path" "--simulation" "--synthesis" "--testbench" "--testbench-simulation"\
602 "--upgrade-ip-cores" "--upgrade-variation-file"
604 ".sdc" [list "notiming" "nosynth" "noplace"] \
605 ".elf" [list "scoped_to_ref" "scoped_to_cells" "nosim" "noimpl"] \
606 ".pdc" [list "nosynth" "noplace"] \
607 ".lpf" [list "enable"]]
618 proc BinaryStepName {part} {
620 return "WRITE_DEVICE_IMAGE"
624 return "WRITE_BITSTREAM"
631 set essential_vars [dict create \
632 "HOG_USER" "NOT defined. This variable is essential for git to work properly. \
633 It should be set to the username for your service account (a valid git account)." \
634 "HOG_EMAIL" "NOT defined. This variable is essential for git to work properly. It should be set to your service's account email."\
635 "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."
639 dict for {var msg} $essential_vars {
640 if {![info exists env($var)]} {
641 Msg CriticalWarning "Essential environment variable $var is $msg"
644 Msg Info "Found environment variable $var."
648 set additional_vars [dict create \
649 "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." \
650 "HOG_TARGET_BRANCH" "NOT defined. Default branch for merge is \"master\""\
651 "HOG_CREATE_OFFICIAL_RELEASE" "NOT defined. \
652 Set this variable to '1' to make Hog create an official release in GitHub/Gitlab with the binaries generated in the CI."\
653 "HOG_USE_DOXYGEN" "NOT defined. \
654 Set this variable to 1 to make Hog-CI run Doxygen and copy the official documentation over when you merge to the official branch."
657 if {([
info exists env(HOG_OFFICIAL_BIN_EOS_PATH)] && $env(HOG_OFFICIAL_BIN_EOS_PATH) ne "") || \
658 ([
info exists env(HOG_OFFICIAL_BIN_PATH)] && [
string match "/eos/*" $env(HOG_OFFICIAL_BIN_PATH)])} {
659 Msg Info "Official binary path points to EOS. Checking EOS environment variables for uploads..."
660 if {[
info exists env(HOG_OFFICIAL_BIN_PATH)]} {
661 Msg CriticalWarning "Variable HOG_OFFICIAL_BIN_EOS_PATH is defined. \
662 From Hog2026.2 this variable will be deprecated. Please, use HOG_OFFICIAL_BIN_PATH instead."
664 if {![
info exists env(EOS_PASSWORD)]} {
665 if {![
info exists env(HOG_PASSWORD)]} {
666 Msg Warning "Neither EOS_PASSWORD nor HOG_PASSWORD environment variable is defined. \
667 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."
669 Msg Info "HOG_PASSWORD environment variable is defined and will be used as password for EOS uploads. \
670 If you want to use a different password for EOS uploads, please set the EOS_PASSWORD environment variable."
673 Msg Info "EOS_PASSWORD environment variable is defined and will be used as password for EOS uploads."
676 if {![
info exists env(EOS_USER)]} {
677 Msg Info "EOS_USER environment variable is not defined. Assuming EOS username is the same as HOG_USER."
679 Msg Info "EOS_USER environment variable is defined and will be used as username for EOS uploads."
682 if {![
info exists env(EOS_MGM_URL)]} {
683 Msg Info "EOS_MGM_URL environment variable is not defined. Assuming default value of root://eosuser.cern.ch."
685 Msg Info "EOS_MGM_URL environment variable is defined and will be used as MGM URL for EOS uploads."
687 }
elseif {[
info exists env(HOG_OFFICIAL_BIN_PATH)] } {
688 Msg Info "Variable HOG_OFFICIAL_BIN_PATH is defined. Hog will copy the official binary files to the path defined in this variable. \
689 Please make sure this path is correct and has enough space to store the binaries."
691 Msg Info "No official binary path defined. Hog will not be able to upload binaries."
697 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
705 proc CheckEnv {project_name ide} {
708 set essential_commands [dict create "git" "--version" "$ide" "-version"]
709 set additional_commands [dict create \
716 set additional_vars [dict create \
717 "HOG_PATH" "NOT defined. Hog might work as long as all the necessary executable are in the PATH variable."\
718 "HOG_XIL_LICENSE" "NOT defined. If this variable is not set to the license servers separated by comas, \
719 you need some alternative way of getting your Xilinx license (for example a license file on the machine)."\
720 "LM_LICENSE_FILE" "NOT defined. This variable should be set the Quartus/Libero license servers separated by semicolon. \
721 If not, you need an alternative way of getting your Quartus/Libero license."\
722 "HOG_LD_LIBRARY_PATH" "NOT defined. Hog might work as long as all the necessary library are found."\
723 "HOG_SIMULATION_LIB_PATH" "NOT defined. Hog-CI will not be able to run simulations using third-party simulators."\
724 "HOG_CHECK_PROJVER" "NOT defined. Hog will NOT check the CI project version. \
725 Set this variable to '1' if you want Hog to check the CI project version before creating the HDL project in Create_Project stage. \
726 If the project has not been changed with respect to the target branch, the CI will skip this project" \
727 "HOG_CHECK_SYNTAX" "NOT defined. Hog will NOT check the syntax. \
728 Set this variable to '1' if you want Hog to check the syntax after creating the HDL project in Create_Project stage." \
729 "HOG_NO_BITSTREAM" "NOT defined. Hog-CI will run the implementation up to the write_bitstream stage and create bit files." \
730 "HOG_NO_RESET_BD" "NOT defined or not equal to 1. Hog will reset .bd files (if any) before starting synthesis."\
731 "HOG_IP_PATH" "NOT defined. Hog-CI will NOT use an EOS/LOCAL IP repository to speed up the IP synthesis." \
732 "HOG_RESET_FILES" "NOT defined. Hog-CI will NOT reset any files."\
733 "HOG_NJOBS" "NOT defined. Hog-CI will build IPs with default number of jobs (4)."\
734 "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"
736 Msg Info "Checking environment to run Hog-CI for project $project_name with IDE $ide..."
738 Msg Info "Checking essential commands..."
739 dict for {cmd ver} $essential_commands {
740 if {[catch {exec which $cmd}]} {
741 Msg CriticalWarning "$cmd executable not found. Hog-CI cannot run!"
744 Msg Info "Found executable $cmd."
745 if {$cmd == "ghdl"} {
746 Msg Info [exec $cmd --version]
747 } elseif {$cmd != "diamond"} {
748 Msg Info [exec $cmd $ver]
753 Msg Info "Checking additional commands..."
754 dict for {cmd ver} $additional_commands {
755 if {[catch {exec which $cmd}]} {
756 Msg Warning "$cmd executable not found."
758 Msg Info "Found executable $cmd."
760 Msg Info [exec $cmd $ver]
765 if {$ide == "libero"} {
766 Msg Info "Checking essential environment variables..."
768 if {![
info exists env(HOG_TCLLIB_PATH)]} {
769 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."
772 Msg Info "HOG_TCLLIB_PATH is set. Hog-CI can run with Libero."
776 Msg Info "Checking additional environment variables..."
777 dict for {var msg} $additional_vars {
778 if {![info exists env($var)]} {
779 Msg Info "Environment variable $var is $msg"
781 Msg Info "Found environment variable $var."
786 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
794 proc CheckProjVer {repo_path project {sim 0} {ext_path ""}} {
797 Msg Info "Will check also the version of the simulation files..."
801 if {[
info exists env(HOG_PUSH_TOKEN)] && [
info exist env(CI_PROJECT_ID)] && [
info exist env(CI_API_V4_URL)] } {
802 set token $env(HOG_PUSH_TOKEN)
803 set api_url $env(CI_API_V4_URL)
804 set project_id $env(CI_PROJECT_ID)
809 set project_dir $repo_path/Top/$project
812 Msg Info "$project was modified, continuing with the CI..."
814 Msg Info "Checking if the project has been already built in a previous CI run..."
816 if {$sha == [
GetSHA $repo_path]} {
817 Msg Info "Project was modified in the current commit, Hog will proceed with the build workflow."
820 Msg Info "Checking if project $project has been built in a previous CI run with sha $sha..."
821 set result [
catch {
package require json} JsonFound]
822 if {"$result" != "0"} {
823 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
826 lassign [
ExecuteRet curl --header "PRIVATE-TOKEN: $token" "$api_url/projects/$project_id/pipelines"] ret content
827 set pipeline_dict [json::json2dict $content]
828 if {[
llength $pipeline_dict] > 0} {
829 foreach pip $pipeline_dict {
831 set source [
DictGet $pip source]
832 if {$source == "merge_request_event" && [
string first $sha $pip_sha] != -1} {
833 Msg Info "Found pipeline with sha $pip_sha for project $project"
834 set pipeline_id [
DictGet $pip id]
836 lassign [
ExecuteRet curl --header "PRIVATE-TOKEN: $token" "$api_url/projects/${project_id}/pipelines/${pipeline_id}/jobs?pagination=keyset&per_page=100"] ret2 content2
837 set jobs_dict [json::json2dict $content2]
838 if {[
llength $jobs_dict] > 0} {
839 foreach job $jobs_dict {
840 set job_name [
DictGet $job name]
842 set artifacts [
DictGet $job artifacts_file]
843 set status [
DictGet $job status]
844 set current_job_name $env(CI_JOB_NAME)
845 if {$current_job_name == $job_name && $status == "success"} {
847 lassign [
ExecuteRet curl --location --output artifacts.zip --header "PRIVATE-TOKEN: $token" --url "$api_url/projects/$project_id/jobs/$job_id/artifacts"] ret3 content3
849 Msg CriticalWarning "Cannot download artifacts for job $job_name with id $job_id"
852 lassign [
ExecuteRet unzip -o $repo_path/artifacts.zip] ret_zip
856 Msg Info "Artifacts for job $job_name with id $job_id downloaded and unzipped."
857 file mkdir $repo_path/Projects/$project
858 set fp [open "$repo_path/Projects/$project/skip.me" w+]
870 }
elseif {$ver != -1} {
871 Msg Info "$project was not modified since version: $ver, disabling the CI..."
872 file mkdir $repo_path/Projects/$project
873 set fp [open "$repo_path/Projects/$project/skip.me" w+]
877 Msg Error "Impossible to check the project version. Most likely the repository is not clean. Please, commit your changes before running this command."
887 proc CheckSyntax {project_name repo_path {project_file ""}} {
890 set syntax [check_syntax -return_string]
891 if {[
string first "CRITICAL" $syntax] != -1} {
896 lassign [
GetHogFiles -list_files "*.src" "$repo_path/Top/$project_name/list/" $repo_path] src_files dummy
897 dict for {lib files} $src_files {
899 set file_extension [file extension $f]
900 if {$file_extension == ".vhd" || $file_extension == ".vhdl" || $file_extension == ".v" || $file_extension == ".sv"} {
901 if {[catch {execute_module -tool map -args "--analyze_file=$f"} result]} {
902 Msg Error "\nResult: $result\n"
903 Msg Error "Check syntax failed.\n"
906 Msg Info "Check syntax was successful for $f.\n"
908 Msg Warning "Found syntax error in file $f:\n $result\n"
915 lassign [
GetProjectFiles $project_file] prjLibraries prjProperties prjSimLibraries prjConstraints prjSrcSets prjSimSets prjConSets
916 dict for {lib sources} $prjLibraries {
917 if {[file extension $lib] == ".src"} {
919 Msg Info "Checking Syntax of $f"
925 Msg Info "The Checking Syntax is not supported by this IDE. Skipping..."
930 proc CloseProject {} {
952 proc CompareVersions {ver1 ver2} {
961 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver1 - x y z]} {
962 set ver1 [list $x $y $z]
964 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver2 - x y z]} {
965 set ver2 [list $x $y $z]
969 set v1 [
join $ver1 ""]
971 set v2 [
join $ver2 ""]
974 if {[
string is integer $v1] && [
string is integer $v2]} {
975 set ver1 [
expr {[scan [lindex $ver1 0] %d] * 1000000 + [scan [lindex $ver1 1] %d] * 1000 + [scan [lindex $ver1 2] %d]}]
976 set ver2 [
expr {[scan [lindex $ver2 0] %d] * 1000000 + [scan [lindex $ver2 1] %d] * 1000 + [scan [lindex $ver2 2] %d]}]
980 }
elseif {$ver1 == $ver2} {
986 Msg Warning "Version is not numeric: $ver1, $ver2"
996 proc CheckExtraFiles {libraries constraints simlibraries} {
999 lassign [
GetProjectFiles] prjLibraries prjProperties prjSimLibraries prjConstraints
1000 set prj_dir [get_property DIRECTORY [current_project]]
1001 file mkdir "$prj_dir/.hog"
1002 set extra_file_name "$prj_dir/.hog/extra.files"
1003 set new_extra_file [open $extra_file_name "w"]
1005 dict for {prjLib prjFiles} $prjLibraries {
1006 foreach prjFile $prjFiles {
1007 if {[file extension $prjFile] == ".xcix"} {
1008 Msg Warning "IP $prjFile is packed in a .xcix core container. \
1009 This files are not suitable for version control systems. We recommend to use .xci files instead."
1012 if {[file extension $prjFile] == ".xci" && [get_property CORE_CONTAINER [get_files $prjFile]] != ""} {
1013 Msg Info "$prjFile is a virtual IP file in a core container. Ignoring it..."
1017 if {[IsInList $prjFile [DictGet $libraries $prjLib]] == 0} {
1018 if {[file extension $prjFile] == ".bd"} {
1019 # Generating BD products to save md5sum of already modified BD
1020 Msg Info "Generating targets of $prjFile..."
1021 generate_target all [get_files $prjFile]
1023 puts $new_extra_file "$prjFile [Md5Sum $prjFile]"
1024 Msg Info "$prjFile (lib: $prjLib) has been generated by an external script. Adding to $extra_file_name..."
1028 close $new_extra_file
1029 set extra_sim_file "$prj_dir/.hog/extrasim.files"
1030 set new_extra_file [open $extra_sim_file "w"]
1032 dict for {prjSimLib prjSimFiles} $prjSimLibraries {
1033 foreach prjSimFile $prjSimFiles {
1034 if {[IsInList $prjSimFile [DictGet $simlibraries $prjSimLib]] == 0} {
1035 puts $new_extra_file "$prjSimFile [Md5Sum $prjSimFile]"
1036 Msg Info "$prjSimFile (lib: $prjSimLib) has been generated by an external script. Adding to $extra_sim_file..."
1040 close $new_extra_file
1041 set extra_con_file "$prj_dir/.hog/extracon.files"
1042 set new_extra_file [open $extra_con_file "w"]
1044 dict for {prjConLib prjConFiles} $prjConstraints {
1045 foreach prjConFile $prjConFiles {
1046 if {[IsInList $prjConFile [DictGet $constraints $prjConLib]] == 0} {
1047 puts $new_extra_file "$prjConFile [Md5Sum $prjConFile]"
1048 Msg Info "$prjConFile has been generated by an external script. Adding to $extra_con_file..."
1052 close $new_extra_file
1059 proc CheckLatestHogRelease {{repo_path .}} {
1062 set current_ver [
Git {describe --always}]
1063 Msg Debug "Current version: $current_ver"
1064 set current_sha [
Git "log $current_ver -1 --format=format:%H"]
1065 Msg Debug "Current SHA: $current_sha"
1068 if {[
OS] == "windows"} {
1069 Msg Info "On windows we cannot set a timeout on 'git fetch', hopefully nothing will go wrong..."
1072 Msg Info "Checking for latest Hog release, can take up to 5 seconds..."
1075 set master_ver [
Git "describe origin/master"]
1076 Msg Debug "Master version: $master_ver"
1077 set master_sha [
Git "log $master_ver -1 --format=format:%H"]
1078 Msg Debug "Master SHA: $master_sha"
1079 set merge_base [
Git "merge-base $current_sha $master_sha"]
1080 Msg Debug "merge base: $merge_base"
1083 if {$merge_base != $master_sha} {
1085 Msg Info "Version $master_ver has been released (https://gitlab.com/hog-cern/Hog/-/releases/$master_ver)"
1086 Msg Status "You should consider updating Hog submodule with the following instructions:"
1088 Msg Status "cd Hog && git checkout master && git pull"
1090 Msg Status "Also update the ref: in your .gitlab-ci.yml to $master_ver"
1094 Msg Info "Latest official version is $master_ver, nothing to do."
1106 proc CheckYmlRef {repo_path allow_failure} {
1107 if {$allow_failure} {
1108 set MSG_TYPE CriticalWarning
1113 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
1114 Msg CriticalWarning "Cannot find package YAML, skipping consistency check of \"ref\" in gilab-ci.yaml file.\n Error message: $YAMLPACKAGE
1115 You can fix this by installing package \"tcllib\""
1123 if {[
file exists .gitlab-ci.yml]} {
1127 if {[
file exists .gitlab-ci.yml]} {
1128 set fp [open ".gitlab-ci.yml" r]
1129 set file_data [read $fp]
1132 Msg $MSG_TYPE "Cannot open file .gitlab-ci.yml"
1136 set file_data "\n$file_data\n\n"
1138 if {[
catch {::yaml::yaml2dict -stream $file_data} yamlDict]} {
1139 Msg $MSG_TYPE "Parsing $repo_path/.gitlab-ci.yml failed. To fix this, check that yaml syntax is respected, remember not to use tabs."
1143 dict for {dictKey dictValue} $yamlDict {
1144 #looking for Hog include in .gitlab-ci.yml
1145 if {"$dictKey" == "include" && (
1146 [lsearch [split $dictValue " {}"] "/hog.yml"] != "-1" ||
1147 [lsearch [split $dictValue " {}"] "/hog-dynamic.yml"] != "-1"
1149 set YML_REF [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "ref"] + 1}]]
1150 set YML_NAME [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "file"] + 1}]]
1154 if {$YML_REF == ""} {
1155 Msg Warning "Hog version not specified in the .gitlab-ci.yml. Assuming that master branch is used."
1157 set YML_REF_F [
Git {name-rev --tags --name-only origin/master}]
1160 set YML_REF_F [regsub -all "'" $YML_REF ""]
1163 if {$YML_NAME == ""} {
1164 Msg $MSG_TYPE "Hog included yml file not specified, assuming hog.yml"
1165 set YML_NAME_F hog.yml
1167 set YML_NAME_F [regsub -all "^/" $YML_NAME ""]
1170 lappend YML_FILES $YML_NAME_F
1176 if {[
catch {::yaml::yaml2dict -file $YML_NAME_F} yamlDict]} {
1177 Msg $MSG_TYPE "Parsing $YML_NAME_F failed."
1181 dict for {dictKey dictValue} $yamlDict {
1182 #looking for included files
1183 if {"$dictKey" == "include"} {
1184 foreach v $dictValue {
1185 lappend YML_FILES [lindex [split $v " "] [expr {[lsearch -dictionary [split $v " "] "local"] + 1}]]
1191 Msg Info "Found the following yml files: $YML_FILES"
1193 set HOGYML_SHA [
GetSHA $YML_FILES]
1194 lassign [
GitRet "log --format=%h -1 --abbrev=7 $YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1196 lassign [
GitRet "log --format=%h -1 --abbrev=7 origin/$YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1198 Msg $MSG_TYPE "Error in project .gitlab-ci.yml. ref: $YML_REF not found"
1199 set EXPECTEDYML_SHA ""
1202 if {!($EXPECTEDYML_SHA eq "")} {
1203 if {$HOGYML_SHA == $EXPECTEDYML_SHA} {
1204 Msg Info "Hog included file $YML_FILES matches with $YML_REF in .gitlab-ci.yml."
1206 Msg $MSG_TYPE "HOG $YML_FILES SHA mismatch.
1207 From Hog submodule: $HOGYML_SHA
1208 From ref in .gitlab-ci.yml: $EXPECTEDYML_SHA
1209 You can fix this in 2 ways: by changing the ref in your repository or by changing the Hog submodule commit"
1212 Msg $MSG_TYPE "One or more of the following files could not be found $YML_FILES in Hog at $YML_REF"
1215 Msg Info ".gitlab-ci.yml not found in $repo_path. Skipping this step"
1236 proc CompareLibDicts {proj_libs list_libs proj_sets list_sets proj_props list_props {severity "CriticalWarning"} {outFile ""} {extraFiles ""}} {
1237 set extra_files $extraFiles
1239 set out_prjlibs $proj_libs
1240 set out_prjprops $proj_props
1242 dict for {prjSet prjLibraries} $proj_sets {
1243 # Check if sets is also in list files
1244 if {[IsInList $prjSet $list_sets]} {
1245 set listLibraries [DictGet $list_sets $prjSet]
1246 # Loop over libraries in fileset
1247 foreach prjLib $prjLibraries {
1248 set prjFiles [DictGet $proj_libs $prjLib]
1249 # Check if library exists in list files
1250 if {[IsInList $prjLib $listLibraries]} {
1251 # Loop over files in library
1252 set listFiles [DictGet $list_libs $prjLib]
1253 foreach prjFile $prjFiles {
1254 set idx [lsearch -exact $listFiles $prjFile]
1255 set listFiles [lreplace $listFiles $idx $idx]
1257 # File is in project but not in list libraries, check if it was generated at creation time...
1258 if {[dict exists $extra_files $prjFile]} {
1259 # File was generated at creation time, checking the md5sum
1260 # Removing the file from the prjFiles list
1261 set idx2 [lsearch -exact $prjFiles $prjFile]
1262 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1263 set new_md5sum [Md5Sum $prjFile]
1264 set old_md5sum [DictGet $extra_files $prjFile]
1265 if {$new_md5sum != $old_md5sum} {
1266 # tclint-disable-next-line line-length
1267 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
1270 set extra_files [dict remove $extra_files $prjFile]
1272 # File is neither in list files nor in extra_files
1273 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1277 # File is both in list files and project, checking properties...
1278 set prjProps [DictGet $proj_props $prjFile]
1279 set listProps [DictGet $list_props $prjFile]
1280 # Check if it is a potential sourced file
1281 if {[IsInList "nosynth" $prjProps] && [IsInList "noimpl" $prjProps] && [IsInList "nosim" $prjProps]} {
1282 # Check if it is sourced
1283 set idx_source [lsearch -exact $listProps "source"]
1284 if {$idx_source >= 0} {
1285 # It is sourced, let's replace the individual properties with source
1286 set idx [lsearch -exact $prjProps "noimpl"]
1287 set prjProps [lreplace $prjProps $idx $idx]
1288 set idx [lsearch -exact $prjProps "nosynth"]
1289 set prjProps [lreplace $prjProps $idx $idx]
1290 set idx [lsearch -exact $prjProps "nosim"]
1291 set prjProps [lreplace $prjProps $idx $idx]
1292 lappend prjProps "source"
1296 foreach prjProp $prjProps {
1297 set idx [lsearch -exact $listProps $prjProp]
1298 set listProps [lreplace $listProps $idx $idx]
1300 MsgAndLog "Property $prjProp of $prjFile was set in project but not in list files" $severity $outFile
1305 foreach listProp $listProps {
1306 if {[string first $listProp "topsim="] == -1 && [string first $listProp "enable"] == -1} {
1307 MsgAndLog "Property $listProp of $prjFile was found in list files but not set in project." $severity $outFile
1312 # Update project prjProps
1313 dict set out_prjprops $prjFile $prjProps
1316 # Loop over remaining files in list libraries
1317 foreach listFile $listFiles {
1318 MsgAndLog "$listFile was found in list files but not in project." $severity $outFile
1322 # Check extra files again...
1323 foreach prjFile $prjFiles {
1324 if {[dict exists $extra_files $prjFile]} {
1325 # File was generated at creation time, checking the md5sum
1326 # Removing the file from the prjFiles list
1327 set idx2 [lsearch -exact $prjFiles $prjFile]
1328 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1329 set new_md5sum [Md5Sum $prjFile]
1330 set old_md5sum [DictGet $extra_files $prjFile]
1331 if {$new_md5sum != $old_md5sum} {
1332 # tclint-disable-next-line line-length
1333 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
1336 set extra_files [dict remove $extra_files $prjFile]
1338 # File is neither in list files nor in extra_files
1339 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1344 # Update prjLibraries
1345 dict set out_prjlibs $prjLib $prjFiles
1348 MsgAndLog "Fileset $prjSet found in project but not in list files" $severity $outFile
1353 return [list $n_diffs $extra_files $out_prjlibs $out_prjprops]
1363 proc CompareVHDL {file1 file2} {
1364 set a [open $file1 r]
1365 set b [open $file2 r]
1367 while {[
gets $a line] != -1} {
1368 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1369 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1375 while {[
gets $b line] != -1} {
1376 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1377 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1386 foreach x $f1 y $f2 {
1388 lappend diff "> $x\n< $y\n\n"
1401 proc Copy {i_dirs o_dir} {
1402 foreach i_dir $i_dirs {
1403 if {[
file isdirectory $i_dir] && [
file isdirectory $o_dir]} {
1404 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]])} {
1405 file delete -force $o_dir/[
file tail $i_dir]
1409 file copy -force $i_dir $o_dir
1424 proc CopyIPbusXMLs {proj_dir path dst {xml_version "0.0.0"} {xml_sha "00000000"} {use_ipbus_sw 0} {generate 0}} {
1425 if {$use_ipbus_sw == 1} {
1426 lassign [
ExecuteRet python3 -c "from __future__ import print_function; from sys import path;print(':'.join(path\[1:\]))"] ret msg
1428 set ::env(PYTHONPATH) $msg
1429 lassign [
ExecuteRet gen_ipbus_addr_decode -h] ret msg
1436 Msg CriticalWarning "Problem while trying to run python: $msg"
1439 set dst [
file normalize $dst]
1441 if {$can_generate == 0} {
1442 if {$generate == 1} {
1443 Msg Error "Cannot generate IPbus address files, IPbus executable gen_ipbus_addr_decode not found or not working: $msg"
1446 Msg Warning "IPbus executable gen_ipbus_addr_decode not found or not working, will not verify IPbus address tables."
1453 set ipb_files [glob -nocomplain $proj_dir/list/*.ipb]
1454 set n_ipb_files [
llength $ipb_files]
1455 if {$n_ipb_files == 0} {
1456 Msg CriticalWarning "No files with .ipb extension found in $proj_dir/list."
1459 set libraries [dict create]
1460 set vhdl_dict [dict create]
1462 foreach ipb_file $ipb_files {
1468 set xmlfiles [dict get $libraries "xml.ipb"]
1470 set xml_list_error 0
1471 foreach xmlfile $xmlfiles {
1472 if {[
file isdirectory $xmlfile]} {
1473 Msg CriticalWarning "Directory $xmlfile listed in xml list file $list_file. Directories are not supported!"
1474 set xml_list_error 1
1477 if {[
file exists $xmlfile]} {
1478 if {[dict exists $vhdl_dict $xmlfile]} {
1479 set vhdl_file [
file normalize $path/[dict get $vhdl_dict $xmlfile]]
1483 lappend vhdls $vhdl_file
1484 set xmlfile [
file normalize $xmlfile]
1485 Msg Info "Copying $xmlfile to $dst and replacing place holders..."
1486 set in [open $xmlfile r]
1487 set out [open $dst/[
file tail $xmlfile] w]
1489 while {[
gets $in line] != -1} {
1490 set new_line [regsub {(.*)__VERSION__(.*)} $line "\\1$xml_version\\2"]
1491 set new_line2 [regsub {(.*)__GIT_SHA__(.*)} $new_line "\\1$xml_sha\\2"]
1492 puts $out $new_line2
1496 lappend xmls [
file tail $xmlfile]
1498 Msg Warning "XML file $xmlfile not found"
1501 if {${xml_list_error}} {
1502 Msg Error "Invalid files added to $list_file!"
1505 set cnt [
llength $xmls]
1506 Msg Info "$cnt xml file/s copied"
1509 if {$can_generate == 1} {
1512 file mkdir "address_decode"
1514 foreach x $xmls v $vhdls {
1516 set x [
file normalize ../$x]
1517 if {[
file exists $x]} {
1518 lassign [
ExecuteRet gen_ipbus_addr_decode --no-timestamp $x 2>&1] status log
1520 set generated_vhdl ./ipbus_decode_[
file rootname [
file tail $x]].vhd
1521 if {$generate == 1} {
1522 Msg Info "Copying generated VHDL file $generated_vhdl into $v (replacing if necessary)"
1523 file copy -force -- $generated_vhdl $v
1525 if {[
file exists $v]} {
1527 set n [
llength $diff]
1529 Msg CriticalWarning "$v does not correspond to its XML $x, [
expr {$n / 3}] line/s differ:"
1530 Msg Status [
join $diff "\n"]
1531 set diff_file [open ../diff_[
file rootname [
file tail $x]].txt w]
1532 puts $diff_file $diff
1535 Msg Info "[
file tail $x] and $v match."
1538 Msg Warning "VHDL address map file $v not found."
1542 Msg Warning "Address map generation failed for [
file tail $x]: $log"
1545 Msg Warning "Copied XML file $x not found."
1548 Msg Info "Skipped verification of [
file tail $x] as no VHDL file was specified."
1552 file delete -force address_decode
1564 proc DescriptionFromConf {conf_file} {
1565 set f [open $conf_file "r"]
1566 set lines [
split [read $f] "\n"]
1568 set second_line [
lindex $lines 1]
1571 if {![regexp {\#+ *(.+)} $second_line - description]} {
1575 if {[regexp -all {test|Test|TEST} $description]} {
1576 set description "test"
1589 proc DictGet {dictName keyName {default ""}} {
1590 if {[dict exists $dictName $keyName]} {
1591 return [dict get $dictName $keyName]
1602 proc DictSort {dict args} {
1604 foreach key [lsort {*}$args [dict keys $dict]] {
1605 dict set res $key [dict get $dict $key]
1615 proc DoxygenVersion {target_version} {
1616 set ver [
split $target_version "."]
1617 set v [
Execute doxygen --version]
1618 Msg Info "Found Doxygen version: $v"
1619 set current_ver [
split $v ". "]
1620 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
1621 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
1623 return [
expr {$target <= $current}]
1634 proc eos {command {attempt 1}} {
1636 if {![
info exists env(EOS_MGM_URL)]} {
1637 Msg Warning "Environment variable EOS_MGM_URL not set, setting it to default value root://eosuser.cern.ch"
1638 set ::env(EOS_MGM_URL) "root://eosuser.cern.ch"
1641 Msg Warning "The value of attempt should be 1 or more, not $attempt, setting it to 1 as default"
1644 for {
set i 0} {$i < $attempt} {
incr i} {
1645 set ret [
catch {
exec -ignorestderr eos {*}$command} result]
1650 set wait [
expr {1 + int(rand() * 29)}]
1651 Msg Warning "Command $command failed ($i/$attempt): $result, trying again in $wait seconds..."
1652 after [
expr {$wait * 1000}]
1656 return [list $ret $result]
1666 proc Execute {args} {
1670 Msg Error "Command [
join $args] returned error code: $ret"
1684 proc ExecuteRet {args} {
1686 if {[
llength $args] == 0} {
1687 Msg CriticalWarning "No argument given"
1691 set ret [
catch {
exec -ignorestderr {*}$args} result]
1694 return [list $ret $result]
1701 proc ExtractFilesSection {file_data} {
1702 set in_files_section 0
1705 foreach line $file_data {
1706 if {[regexp {^ *\[ *files *\]} $line]} {
1707 set in_files_section 1
1710 if {$in_files_section} {
1711 if {[regexp {^ *\[.*\]} $line]} {
1714 lappend result $line
1719 if {!$in_files_section} {
1733 proc ExtractVersionFromTag {tag} {
1734 if {[regexp {^(?:b(\d+))?v(\d+)\.(\d+).(\d+)(?:-\d+)?$} $tag -> mr M m p]} {
1739 Msg Warning "Repository tag $tag is not in a Hog-compatible format."
1745 return [list $M $m $p $mr]
1755 proc FileCommitted {File} {
1757 set currentDir [
pwd]
1758 cd [
file dirname [
file normalize $File]]
1759 set GitLog [
Git ls-files [
file tail $File]]
1760 if {$GitLog == ""} {
1761 Msg CriticalWarning "File [
file normalize $File] is not in the git repository. Please add it with:\n git add [
file normalize $File]\n"
1772 proc FindCommonGitChild {SHA1 SHA2} {
1774 set commits [
Git {log --oneline --merges}]
1777 foreach line [
split $commits "\n"] {
1778 set commit [
lindex [
split $line] 0]
1782 set ancestor $commit
1795 proc findFiles {basedir pattern} {
1798 set basedir [
string trimright [
file join [
file normalize $basedir] { }]]
1804 foreach fileName [glob -nocomplain -type {f r} -path $basedir $pattern] {
1805 lappend fileList $fileName
1809 foreach dirName [glob -nocomplain -type {d r} -path $basedir *] {
1812 set subDirList [
findFiles $dirName $pattern]
1813 if {[
llength $subDirList] > 0} {
1814 foreach subDirFile $subDirList {
1815 lappend fileList $subDirFile
1826 proc FindFileType {file_name} {
1827 set extension [
file extension $file_name]
1830 set file_extension "USE_SIGNALTAP_FILE"
1833 set file_extension "VHDL_FILE"
1836 set file_extension "VHDL_FILE"
1839 set file_extension "VERILOG_FILE"
1842 set file_extension "SYSTEMVERILOG_FILE"
1845 set file_extension "SDC_FILE"
1848 set file_extension "PDC_FILE"
1851 set file_extension "NDC_FILE"
1854 set file_extension "FDC_FILE"
1857 set file_extension "SOURCE_FILE"
1860 set file_extension "IP_FILE"
1863 set file_extension "QSYS_FILE"
1866 set file_extension "QIP_FILE"
1869 set file_extension "SIP_FILE"
1872 set file_extension "BSF_FILE"
1875 set file_extension "BDF_FILE"
1878 set file_extension "COMMAND_MACRO_FILE"
1881 set file_extension "VQM_FILE"
1884 set file_extension "ERROR"
1885 Msg Error "Unknown file extension $extension"
1888 return $file_extension
1895 proc FindNewestVersion {versions} {
1896 set new_ver 00000000
1897 foreach ver $versions {
1899 if {[
expr 0x$ver > 0x$new_ver]} {
1911 proc FindVhdlVersion {file_name} {
1912 set extension [
file extension $file_name]
1915 set vhdl_version "-hdl_version VHDL_2008"
1918 set vhdl_version "-hdl_version VHDL_2008"
1925 return $vhdl_version
1932 proc FormatGeneric {generic} {
1933 if {[
string is integer "0x$generic"]} {
1934 return [
format "32'h%08X" "0x$generic"]
1937 return [
format "32'h%08X" 0]
1947 proc GenerateBitstream {{run_folder ""} {repo_path .} {njobs 1}} {
1948 Msg Info "Starting write bitstream flow..."
1950 set revision [get_current_revision]
1951 if {[
catch {execute_module -tool asm} result]} {
1952 Msg Error "Result: $result\n"
1953 Msg Error "Generate bitstream failed. See the report file.\n"
1955 Msg Info "Generate bitstream was successful for revision $revision.\n"
1958 Msg Info "Run GENERATEPROGRAMMINGDATA ..."
1959 if {[
catch {run_tool -name {GENERATEPROGRAMMINGDATA}}]} {
1960 Msg Error "GENERATEPROGRAMMINGDATA FAILED!"
1962 Msg Info "GENERATEPROGRAMMINGDATA PASSED."
1964 Msg Info "Sourcing Hog/Tcl/integrated/post-bitstream.tcl"
1965 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
1967 prj_run Export -impl Implementation0 -task Bitgen
1977 proc GenerateQsysSystem {qsysFile commandOpts} {
1979 if {[
file exists $qsysFile] != 0} {
1980 set qsysPath [
file dirname $qsysFile]
1981 set qsysName [
file rootname [
file tail $qsysFile]]
1982 set qsysIPDir "$qsysPath/$qsysName"
1983 set qsysLogFile "$qsysPath/$qsysName.qsys-generate.log"
1986 if {![
info exists ::env(QSYS_ROOTDIR)]} {
1987 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
1988 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
1989 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
1991 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
1994 set qsys_rootdir $::env(QSYS_ROOTDIR)
1997 set cmd "$qsys_rootdir/qsys-generate"
1998 set cmd_options "$qsysFile --output-directory=$qsysIPDir $commandOpts"
1999 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
2000 Msg Info "Executing: $cmd $cmd_options"
2001 Msg Info "Saving logfile in: $qsysLogFile"
2002 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
2003 set makeRet [
lindex [dict get $opt -errorcode] end]
2004 Msg CriticalWarning "$cmd returned with $makeRet"
2007 Msg Error " Could not execute command $cmd"
2011 set qsysIPFileList [
concat \
2012 [glob -nocomplain -directory $qsysIPDir -types f *.ip *.qip] \
2013 [glob -nocomplain -directory "$qsysIPDir/synthesis" -types f *.ip *.qip *.vhd *.vhdl]
2015 foreach qsysIPFile $qsysIPFileList {
2016 if {[
file exists $qsysIPFile] != 0} {
2018 set_global_assignment -name $qsysIPFileType $qsysIPFile
2020 set IpMd5Sum [
Md5Sum $qsysIPFile]
2022 set fileDir [
file normalize "./hogTmp"]
2023 set fileName "$fileDir/.hogQsys.md5"
2024 if {![
file exists $fileDir]} {
2027 set hogQsysFile [open $fileName "a"]
2028 set fileEntry "$qsysIPFile\t$IpMd5Sum"
2029 puts $hogQsysFile $fileEntry
2034 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
2044 proc GenericToSimulatorString {prop_dict target} {
2046 dict for {theKey theValue} $prop_dict {
2055 regexp {([0-9]*)('h)([0-9a-fA-F]*)} $theValue valueHexFull valueNumBits valueHexFlag valueHex
2056 regexp {^([0-9]*)$} $theValue valueIntFull ValueInt
2057 regexp {(?!^\d+$)^.+$} $theValue valueStrFull ValueStr
2058 if {[string tolower $target] == "vivado" || [string tolower $target] == "xsim"} {
2059 if {[string tolower $theValue] == "true" || [string tolower $theValue] == "false"} {
2060 set prj_generics "$prj_generics $theKey=$theValue"
2061 } elseif {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2062 set prj_generics "$prj_generics $theKey=$valueHexFull"
2063 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2064 set prj_generics "$prj_generics $theKey=$ValueInt"
2065 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2066 set prj_generics "$prj_generics $theKey=\"$ValueStr\""
2068 set prj_generics "$prj_generics $theKey=\"$theValue\""
2070 } elseif {[lsearch -exact [GetSimulators] [string tolower $target]] >= 0} {
2071 if {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2073 scan $valueNumBits %d numBits
2075 scan $valueHex %x numHex
2076 binary scan [binary format "I" $numHex] "B*" binval
2077 set numBits [expr {$numBits - 1}]
2078 set numBin [string range $binval end-$numBits end]
2079 set prj_generics "$prj_generics $theKey=\"$numBin\""
2080 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2081 set prj_generics "$prj_generics $theKey=$ValueInt"
2082 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2083 set prj_generics "$prj_generics {$theKey=\"$ValueStr\"}"
2085 set prj_generics "$prj_generics {$theKey=\"$theValue\"}"
2088 Msg Warning "Target : $target not implemented"
2091 return $prj_generics
2099 proc GetConfFiles {proj_dir} {
2100 Msg Debug "GetConfFiles called with proj_dir=$proj_dir"
2101 if {![
file isdirectory $proj_dir]} {
2102 Msg Error "$proj_dir is supposed to be the top project directory"
2105 set conf_file [
file normalize $proj_dir/hog.conf]
2106 set sim_file [
file normalize $proj_dir/sim.conf]
2107 set pre_tcl [
file normalize $proj_dir/pre-creation.tcl]
2108 set post_tcl [
file normalize $proj_dir/post-creation.tcl]
2110 return [list $conf_file $sim_file $pre_tcl $post_tcl]
2119 proc GetCustomCommands {parameters {directory .}} {
2120 set commands_dict [dict create]
2121 set commands_files [glob -nocomplain $directory/*.tcl]
2123 if {[
llength $commands_files] == 0} {
2127 foreach file $commands_files {
2132 if {$custom_cmd eq ""} {
2137 set custom_cmd_name [dict get $custom_cmd NAME]
2139 Msg Debug "Loaded custom command '$custom_cmd_name' from $file"
2142 if {[dict exists $commands_dict $custom_cmd_name]} {
2143 Msg Error "Custom command '$custom_cmd_name' in $file already defined as: \[dict get $commands_dict $custom_cmd_name\]. Skipping."
2149 set custom_cmd_name [
string toupper $custom_cmd_name]
2150 dict set commands_dict $custom_cmd_name $custom_cmd
2153 return $commands_dict
2156 proc SanitizeCustomCommand {cmdDict file parameters} {
2159 foreach k [dict keys $cmdDict] {
2160 set K [
string toupper $k]
2161 dict set normalized $K [dict get $cmdDict $k]
2164 set cmdDict $normalized
2165 if {![dict exists $cmdDict NAME]} {
2166 Msg Error "Custom command in $file missing required key NAME. Skipping."
2169 if {![dict exists $cmdDict SCRIPT]} {
2170 Msg Error "Custom command '$[dict get $cmdDict NAME]' in $file missing SCRIPT. Skipping."
2175 set allowed {NAME DESCRIPTION OPTIONS CUSTOM_OPTIONS SCRIPT IDE NO_EXIT}
2176 foreach k [dict keys $cmdDict] {
2177 if {[lsearch -exact $allowed $k] < 0} {
2178 Msg Warning "Custom command '[dict get $cmdDict NAME]' in $file: unknown key '$k'. Allowed: $allowed. Skipping."
2183 set name [
string trim [dict get $cmdDict NAME]]
2185 Msg Error "Custom command in $file has empty NAME. Skipping."
2189 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]+$} $name]} {
2190 Msg Error "Custom command NAME '$name' (file $file) contains invalid characters."
2194 if {![dict exists $cmdDict DESCRIPTION]} {
2195 dict set cmdDict DESCRIPTION "No description provided."
2199 set hog_parameters {}
2200 foreach p $parameters {
2201 lappend hog_parameters [
lindex $p 0]
2206 if {[dict exists $cmdDict OPTIONS]} {
2207 set raw_opts [dict get $cmdDict OPTIONS]
2208 if {![
llength $raw_opts]} {
2212 foreach item $raw_opts {
2214 foreach p $parameters {
2215 set hog_parameter [
lindex $p 0]
2216 if { $item eq $hog_parameter } {
2217 lappend hog_options $p
2223 Msg Warning "Custom command '$name' in $file: option '$item' not found in Hog parameters. Skipping."
2226 dict set cmdDict OPTIONS $hog_options
2228 dict set cmdDict CUSTOM_OPTIONS {}
2235 if {[dict exists $cmdDict CUSTOM_OPTIONS]} {
2236 set raw_opts [dict get $cmdDict CUSTOM_OPTIONS]
2237 if {![
llength $raw_opts]} {
2240 foreach item $raw_opts {
2242 if {[
llength $item] != 2 && [
llength $item] != 3} {
2243 Msg Error "Bad custom option: \[$item\]. Custom command '$name' in $file: \
2244 each CUSTOM_OPTIONS entry must be {option \"help\"} for flags \
2245 and {option \"default_value\" \"help\"} for options with arguments. Skipping command."
2249 if {[
llength $item] == 2} {
2250 lassign $item opt help
2253 lassign $item opt def help
2256 if { [
IsInList $opt $hog_parameters] == 1 } {
2257 Msg Warning "Custom command '$name' in $file: option '$opt' already defined in Hog parameters. Skipping."
2263 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]*(\.arg)?$} $opt]} {
2264 Msg Error "Custom command '$name' in $file: invalid option name '$opt'."
2269 Msg Warning "Custom command '$name' option '$opt' has empty help text."
2273 dict set cmdDict CUSTOM_OPTIONS {}
2277 if {[dict exists $cmdDict NO_EXIT]} {
2278 set no_exit [dict get $cmdDict NO_EXIT]
2279 set no_exit [
string tolower [
string trim $no_exit]]
2281 if {$no_exit eq "1" || $no_exit eq "true"} {
2287 dict set cmdDict NO_EXIT $no_exit
2289 dict set cmdDict NO_EXIT 0
2295 proc LoadCustomCommandFile {file parameters} {
2297 set dir [
file dirname $file]
2299 unset -nocomplain ::hog_command
2300 set rc [
catch {source $file} err]
2303 Msg Error "Error sourcing custom command file $file: $err"
2306 if {![
info exists ::hog_command]} {
2307 Msg Warning "File $file did not define ::hog_command. Skipping."
2310 set cmdDict $::hog_command
2312 if {[
catch {dict size $cmdDict}]} {
2313 Msg Error "In $file ::hog_command is not a valid dict. Skipping."
2323 proc GetDateAndTime {commit} {
2324 set clock_seconds [
clock seconds]
2327 set date [
Git "log -1 --format=%cd --date=format:%d%m%Y $commit"]
2328 set timee [
Git "log -1 --format=%cd --date=format:00%H%M%S $commit"]
2330 Msg Warning "Found Git version older than 2.9.3. Using current date and time instead of commit time."
2331 set date [
clock format $clock_seconds -format {%d%m%Y}]
2332 set timee [
clock format $clock_seconds -format {00%H%M%S}]
2334 return [list $date $timee]
2346 proc GetFile {file fileset} {
2349 set Files [get_files -all $file -of_object [get_filesets $fileset]]
2350 set f [
lindex $Files 0]
2358 puts "***DEBUG Hog:GetFile $file"
2367 proc GetFileGenerics {filename {entity ""}} {
2369 if {[
string equal $file_type "VERILOG_FILE"] || [
string equal $file_type "SYSTEMVERILOG_FILE"]} {
2371 }
elseif {[
string equal $file_type "VHDL_FILE"]} {
2374 Msg CriticalWarning "Could not determine extension of top level file."
2383 proc GetGenericsFromConf {proj_dir} {
2384 set generics_dict [dict create]
2385 set top_dir "Top/$proj_dir"
2386 set conf_file "$top_dir/hog.conf"
2388 Msg Debug "GetGenericsFromConf called with proj_dir=$proj_dir, top_dir=$top_dir"
2390 if {[
file exists $conf_file]} {
2392 if {[dict exists $properties generics]} {
2393 set generics_dict [dict get $properties generics]
2396 Msg Warning "File $conf_file not found."
2398 return $generics_dict
2411 proc GetSimSets {project_name repo_path {simsets ""} {ghdl 0} {no_conf 0}} {
2412 set simsets_dict [dict create]
2413 set list_dir "$repo_path/Top/$project_name/list"
2415 if {$simsets != ""} {
2416 foreach s $simsets {
2417 set list_file "$list_dir/$s.sim"
2418 if {[
file exists $list_file]} {
2419 lappend list_files $list_file
2420 }
elseif {$s != "sim_1"} {
2421 Msg CriticalWarning "Simulation set list file $list_file not found."
2426 set list_files [glob -nocomplain -directory $list_dir "*.sim"]
2430 set proj_dir [
file normalize $repo_path/Top/$project_name]
2431 set sim_file [
file normalize $proj_dir/sim.conf]
2433 foreach list_file $list_files {
2434 set file_name [
file tail $list_file]
2435 set simset_name [
file rootname $file_name]
2436 set fp [open $list_file r]
2437 set file_data [read $fp]
2439 set data [
split $file_data "\n"]
2441 set firstline [
lindex $data 0]
2443 if {[regexp {^ *\# *Simulator} $firstline]} {
2444 set simulator_prop [regexp -all -inline {\S+} $firstline]
2445 set simulator [
string tolower [
lindex $simulator_prop end]]
2447 Msg Warning "Simulator not set in $simset_name.sim. \
2448 The first line of $simset_name.sim should be #Simulator <SIMULATOR_NAME>,\
2449 where <SIMULATOR_NAME> can be xsim, questa, modelsim, ghdl, riviera, activehdl,\
2450 ies, or vcs, e.g. #Simulator questa.\
2451 Setting simulator by default to xsim."
2452 set simulator "xsim"
2454 if {$simulator eq "skip_simulation"} {
2455 Msg Info "Skipping simulation for $simset_name"
2458 if {($ghdl == 1 && $simulator != "ghdl") || ($ghdl == 0 && $simulator == "ghdl")} {
2462 set SIM_PROPERTIES ""
2463 if {[
file exists $sim_file] && $no_conf == 0} {
2464 set SIM_PROPERTIES [
ReadConf $sim_file]
2467 set global_sim_props [dict create]
2468 dict set global_sim_props "properties" [
DictGet $SIM_PROPERTIES "sim"]
2469 dict set global_sim_props "generics" [
DictGet $SIM_PROPERTIES "generics"]
2470 dict set global_sim_props "hog" [
DictGet $SIM_PROPERTIES "hog"]
2473 set sim_dict [dict create]
2474 dict set sim_dict "simulator" $simulator
2475 if {[dict exists $SIM_PROPERTIES $simset_name]} {
2476 dict set sim_dict "properties" [
DictGet $SIM_PROPERTIES $simset_name]
2477 dict set sim_dict "generics" [
DictGet $SIM_PROPERTIES "$simset_name:generics"]
2478 dict set sim_dict "hog" [
DictGet $SIM_PROPERTIES "$simset_name:hog"]
2479 }
elseif {$no_conf == 0} {
2481 set conf_dict [
ReadConf $list_file]
2482 set sim_dict [
MergeDict $sim_dict $conf_dict 0]
2484 set sim_dict [
MergeDict $sim_dict $global_sim_props 0]
2485 dict set simsets_dict $simset_name $sim_dict
2487 return $simsets_dict
2495 proc GetSimsetGenericsFromConf {proj_dir} {
2496 set simsets_generics_dict [dict create]
2497 set top_dir "Top/$proj_dir"
2498 set conf_file "$top_dir/sim.conf"
2501 if {[
file exists $conf_file]} {
2504 set simsets_generics_dict [dict filter $properties key *:generics]
2506 Msg Warning "File $conf_file not found."
2508 return $simsets_generics_dict
2519 proc GetGroupName {proj_dir repo_dir} {
2520 if {[regexp {^(.*)/(Top|Projects)/+(.*?)/*$} $proj_dir dummy possible_repo_dir proj_or_top dir]} {
2522 if {[
file normalize $repo_dir] eq [
file normalize $possible_repo_dir]} {
2523 set group [
file dir $dir]
2524 if {$group == "."} {
2529 Msg Warning "Project directory $proj_dir seems to be in $possible_repo_dir which is not a the main Git repository $repo_dir."
2532 Msg Warning "Could not parse project directory $proj_dir"
2545 proc GetHogDescribe {sha {repo_path .}} {
2548 set new_sha "[
string toupper [
GetSHA]]"
2551 set new_sha [
string toupper $sha]
2571 proc GetHogFiles {args} {
2574 if {[
catch {
package require cmdline} ERROR]} {
2575 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
2582 {list_files.arg "" "The file wildcard, if not specified all Hog list files will be looked for."}
2583 {sha_mode "Forwarded to ReadListFile, see there for info."}
2584 {ext_path.arg "" "Path for the external libraries forwarded to ReadListFile."}
2585 {print_log "Forwarded to ReadListFile, see there for info."}
2587 set usage "USAGE: GetHogFiles \[options\] <list path> <repository path>"
2588 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
2592 set list_path [
lindex $args 0]
2593 set repo_path [
lindex $args 1]
2595 set list_files $options(list_files)
2596 set sha_mode $options(sha_mode)
2597 set ext_path $options(ext_path)
2598 set print_log $options(print_log)
2600 if {$sha_mode == 1} {
2601 set sha_mode_opt "-sha_mode"
2606 if {$print_log == 1} {
2607 set print_log_opt "-print_log"
2609 set print_log_opt ""
2613 if {$list_files == ""} {
2614 set list_files {.src,.con,.sim,.ext}
2616 set libraries [dict create]
2617 set properties [dict create]
2618 set list_files [glob -nocomplain -directory $list_path "*{$list_files}"]
2619 set filesets [dict create]
2621 foreach f $list_files {
2622 set ext [
file extension $f]
2623 if {$ext == ".ext"} {
2624 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $ext_path"] l p fs
2626 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $repo_path"] l p fs
2629 set properties [
MergeDict $p $properties]
2630 Msg Debug "list file $f, filesets: $fs"
2632 Msg Debug "Merged filesets $filesets"
2634 return [list $libraries $properties $filesets]
2641 proc GetIDECommand {proj_conf {custom_ver ""}} {
2642 if {$custom_ver ne ""} {
2643 set ide_name_and_ver [
string tolower "$custom_ver"]
2644 }
elseif {[
file exists $proj_conf]} {
2645 set ide_name_and_ver [
string tolower [
GetIDEFromConf $proj_conf]]
2647 Msg Error "Configuration file $proj_conf not found."
2650 set ide_name [
lindex [regexp -all -inline {\S+} $ide_name_and_ver] 0]
2652 if {$ide_name eq "vivado" || $ide_name eq "vivado_vitis_classic"} {
2653 set command "vivado"
2655 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2656 set after_tcl_script " -tclargs "
2658 }
elseif {$ide_name eq "planahead"} {
2659 set command "planAhead"
2661 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2662 set after_tcl_script " -tclargs "
2664 }
elseif {$ide_name eq "quartus"} {
2665 set command "quartus_sh"
2667 set before_tcl_script " -t "
2668 set after_tcl_script " "
2670 }
elseif {$ide_name eq "libero"} {
2673 set command "libero"
2674 set before_tcl_script "SCRIPT:"
2675 set after_tcl_script " SCRIPT_ARGS:\""
2677 }
elseif {$ide_name eq "diamond"} {
2678 set command "diamondc"
2679 set before_tcl_script " "
2680 set after_tcl_script " "
2682 }
elseif {$ide_name eq "vitis_classic"} {
2685 set before_tcl_script ""
2686 set after_tcl_script " "
2688 }
elseif {$ide_name eq "ghdl"} {
2690 set before_tcl_script " "
2691 set after_tcl_script " "
2694 Msg Error "IDE: $ide_name not known."
2697 return [list $command $before_tcl_script $after_tcl_script $end_marker]
2703 proc GetIDEFromConf {conf_file} {
2704 set f [open $conf_file "r"]
2707 if {[regexp -all {^\# *(\w*) *(vitis_classic)? *(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)?(_.*)? *$} $line dummy ide vitisflag version patch]} {
2708 if {[
info exists vitisflag] && $vitisflag != ""} {
2709 set ide "${ide}_${vitisflag}"
2712 if {[
info exists version] && $version != ""} {
2718 set ret [list $ide $ver]
2720 Msg CriticalWarning "The first line of hog.conf should be \#<IDE name> <version>, \
2721 where <IDE name>. is quartus, vivado, planahead, libero, diamond or ghdl, \
2722 and <version> the tool version, e.g. \#vivado 2020.2. Will assume vivado."
2723 set ret [list "vivado" "0.0.0"]
2730 proc GetIDEName {} {
2732 return "ISE/PlanAhead"
2750 proc GetIDEVersion {} {
2753 regexp {\d+\.\d+(\.\d+)?} [version -short] ver
2758 regexp {[\.0-9]+} $quartus(version) ver
2761 set ver [get_libero_version]
2763 regexp {\d+\.\d+(\.\d+)?} [sys_install version] ver
2765 regexp {\d+\.\d+(\.\d+)?} [version] ver
2779 proc GetLinkedFile {link_file} {
2780 if {[
file type $link_file] eq "link"} {
2781 if {[
OS] == "windows"} {
2783 lassign [
ExecuteRet realpath $link_file] ret msg
2784 lassign [
ExecuteRet cygpath -m $msg] ret2 msg2
2785 if {$ret == 0 && $ret2 == 0} {
2787 Msg Debug "Found link file $link_file on Windows, the linked file is: $real_file"
2789 Msg CriticalWarning "[
file normalize $link_file] is a soft link. \
2790 Soft link are not supported on Windows and readlink.exe or cygpath.exe did not work: readlink=$ret: $msg, cygpath=$ret2: $msg2."
2791 set real_file $link_file
2795 set linked_file [
file link $link_file]
2796 set real_file [
file normalize [
file dirname $link_file]/$linked_file]
2799 if {![
file exists $real_file]} {
2800 Msg Warning "$link_file is a broken link, because the linked file: $real_file does not exist."
2803 Msg Warning "$link file is not a soft link"
2804 set real_file $link_file
2817 proc GetMaxThreads {proj_dir} {
2819 if {[
file exists $proj_dir/hog.conf]} {
2821 if {[dict exists $properties parameters]} {
2822 set propDict [dict get $properties parameters]
2823 if {[dict exists $propDict MAX_THREADS]} {
2824 set maxThreads [dict get $propDict MAX_THREADS]
2828 Msg Warning "File $proj_dir/hog.conf not found. Max threads will be set to default value 1"
2841 proc GetModifiedFiles {{repo_path "."} {pattern "."}} {
2844 set ret [
Git "ls-files --modified $pattern"]
2853 proc GetOptions {argv parameters} {
2856 set param_list [list]
2857 set option_list [list]
2859 foreach p $parameters {
2860 lappend param_list [
lindex $p 0]
2864 while {$index < [
llength $argv]} {
2865 set arg [
lindex $argv $index]
2866 if {[
string first - $arg] == 0} {
2867 set option [
string trimleft $arg "-"]
2869 lappend option_list $arg
2870 if {[lsearch -regex $param_list "$option\[.arg]?"] >= 0 } {
2871 if {[lsearch -regex $param_list "$option\[.arg]"] >= 0 } {
2872 lappend option_list [
lindex $argv $index]
2877 lappend arg_list $arg
2881 Msg Debug "Argv: $argv"
2882 Msg Debug "Options: $option_list"
2883 Msg Debug "Arguments: $arg_list"
2884 return [list $option_list $arg_list]
2902 proc GetProjectFiles {{project_file ""}} {
2903 set libraries [dict create]
2904 set simlibraries [dict create]
2905 set constraints [dict create]
2906 set properties [dict create]
2907 set consets [dict create]
2908 set srcsets [dict create]
2909 set simsets [dict create]
2912 set all_filesets [get_filesets]
2913 set simulator [get_property target_simulator [current_project]]
2914 set top [get_property "top" [current_fileset]]
2916 dict lappend properties $topfile "top=$top"
2918 foreach fs $all_filesets {
2919 if {$fs == "utils_1"} {
2924 set all_files [get_files -quiet -of_objects [get_filesets $fs]]
2925 set fs_type [get_property FILESET_TYPE [get_filesets $fs]]
2927 if {$fs_type == "BlockSrcs"} {
2929 set dict_fs "sources_1"
2933 foreach f $all_files {
2940 if {[
lindex [get_property IS_GENERATED [
GetFile $f $fs]] 0] != 0} {
2945 if {[get_property FILE_TYPE [
GetFile $f $fs]] == "Configuration Files"} {
2951 if {[get_property CORE_CONTAINER [
GetFile $f $fs]] != ""} {
2952 if {[
file extension $f] == ".xcix"} {
2953 set f [get_property CORE_CONTAINER [
GetFile $f $fs]]
2961 if {[get_property SCOPED_TO_REF [
GetFile $f $fs]] != ""} {
2962 dict lappend properties $f "scoped_to_ref=[get_property SCOPED_TO_REF [
GetFile $f $fs]]"
2967 if {[get_property SCOPED_TO_CELLS [
GetFile $f $fs]] != ""} {
2968 dict lappend properties $f "scoped_to_cells=[regsub " " [get_property SCOPED_TO_CELLS [
GetFile $f $fs]] ","]"
2972 if {[
IsInList "PARENT_COMPOSITE_FILE" [list_property [
GetFile $f $fs]]]} {
2977 if {[
file tail $f] == "nocattrs.dat"} {
2982 if {[
file extension $f] != ".coe"} {
2983 set f [
file normalize $f]
2986 set type [get_property FILE_TYPE [
GetFile $f $fs]]
2988 set lib [get_property -quiet LIBRARY [
GetFile $f $fs]]
2991 Msg Debug "File $f Extension [
file extension $f] Type [
lindex $type 0]"
2993 if {[
string equal [
lindex $type 0] "VHDL"] && [
llength $type] == 1} {
2995 }
elseif {[
string equal [
lindex $type 0] "Block"] && [
string equal [
lindex $type 1] "Designs"]} {
2998 }
elseif {[
string equal $type "SystemVerilog"] && [
file extension $f] != ".sv" && [
file extension $f] != ".svp"} {
2999 set prop "SystemVerilog"
3000 }
elseif {[
string equal [
lindex $type 0] "XDC"] && [
file extension $f] != ".xdc"} {
3002 }
elseif {[
string equal $type "Verilog Header"] && [
file extension $f] != ".vh" && [
file extension $f] != ".svh"} {
3003 set prop "verilog_header"
3004 }
elseif {[
string equal $type "Verilog Template"] && [
file extension $f] == ".v" && [
file extension $f] != ".sv"} {
3005 set prop "verilog_template"
3007 set type [
lindex $type 0]
3011 if {![
string equal $prop ""]} {
3012 dict lappend properties $f $prop
3015 if {[
string equal $fs_type "SimulationSrcs"]} {
3017 if {[
string equal $type "VHDL"]} {
3018 set library "${lib}.sim"
3020 set library "others.sim"
3024 dict lappend simsets $dict_fs $library
3027 dict lappend simlibraries $library $f
3028 }
elseif {[
string equal $type "VHDL"]} {
3031 dict lappend srcsets $dict_fs "${lib}.src"
3033 dict lappend libraries "${lib}.src" $f
3034 }
elseif {[
string first "IP" $type] != -1} {
3037 dict lappend srcsets $dict_fs "ips.src"
3039 dict lappend libraries "ips.src" $f
3040 Msg Debug "Appending $f to ips.src"
3041 }
elseif {[
string equal $fs_type "Constrs"]} {
3044 dict lappend consets $dict_fs "sources.con"
3046 dict lappend constraints "sources.con" $f
3050 dict lappend srcsets $dict_fs "others.src"
3052 dict lappend libraries "others.src" $f
3053 Msg Debug "Appending $f to others.src"
3056 if {[
lindex [get_property -quiet used_in_synthesis [
GetFile $f $fs]] 0] == 0} {
3057 dict lappend properties $f "nosynth"
3059 if {[
lindex [get_property -quiet used_in_implementation [
GetFile $f $fs]] 0] == 0} {
3060 dict lappend properties $f "noimpl"
3062 if {[
lindex [get_property -quiet used_in_simulation [
GetFile $f $fs]] 0] == 0} {
3063 dict lappend properties $f "nosim"
3065 if {[
lindex [get_property -quiet IS_MANAGED [
GetFile $f $fs]] 0] == 0 && [
file extension $f] != ".xcix"} {
3066 dict lappend properties $f "locked"
3072 dict lappend properties "Simulator" [get_property target_simulator [current_project]]
3075 set file [open $project_file r]
3076 set in_file_manager 0
3078 while {[
gets $file line] >= 0} {
3080 if {[regexp {^KEY ActiveRoot \"([^\"]+)\"} $line -> value]} {
3081 set top [
string range $value 0 [
expr {[string first "::" $value] - 1}]]
3085 if {[regexp {^LIST FileManager} $line]} {
3086 set in_file_manager 1
3091 if {$in_file_manager && [regexp {^ENDLIST} $line]} {
3096 if {$in_file_manager && [regexp {^VALUE \"([^\"]+)} $line -> value]} {
3099 lassign [
split $value ,] file_path file_type
3102 set library "others"
3103 while {[
gets $file line] >= 0} {
3104 if {$line == "ENDFILE"} {
3107 regexp {^LIBRARY=\"([^\"]+)} $line -> library
3108 regexp {^PARENT=\"([^\"]+)} $line -> parent_file
3110 Msg Debug "Found file ${file_path} in project.."
3111 if {$parent_file == ""} {
3112 if {$file_type == "hdl"} {
3114 if {[
IsInList "${library}.src" [
DictGet $srcsets "sources_1"]] == 0} {
3115 dict lappend srcsets "sources_1" "${library}.src"
3117 dict lappend libraries "${library}.src" $file_path
3121 if {[
GetModuleName $file_path] == [
string tolower $top] && $top != ""} {
3122 Msg Debug "Found top module $top in $file_path"
3123 dict lappend properties $file_path "top=$top"
3125 }
elseif {$file_type == "tb_hdl"} {
3127 dict lappend simsets "sim_1" "${library}.sim"
3129 dict lappend simlibraries "${library}.sim" $file_path
3130 }
elseif {$file_type == "io_pdc" || $file_type == "sdc"} {
3132 dict lappend consets "constrs_1" "sources.con"
3134 dict lappend constraints "sources.con" $file_path
3141 set fileData [read [open $project_file]]
3143 set project_path [
file dirname $project_file]
3146 regsub {<\?xml.*\?>} $fileData "" fileData
3149 regexp {<Implementation.*?>(.*)</Implementation>} $fileData -> implementationContent
3153 set sourceRegex {<Source name="([^"]*?)" type="([^"]*?)" type_short="([^"]*?)".*?>(.*?)</Source>}
3155 set optionsRegex {<Options(.*?)\/>}
3156 regexp $optionsRegex $implementationContent -> prj_options
3157 foreach option $prj_options {
3158 if {[regexp {^top=\"([^\"]+)\"} $option match result]} {
3163 while {[regexp $sourceRegex $implementationContent match name type type_short optionsContent]} {
3164 Msg Debug "Found file ${name} in project..."
3165 set file_path [
file normalize $project_path/$name]
3167 set optionsRegex {<Options(.*?)\/>}
3168 regexp $optionsRegex $optionsContent -> options
3169 set library "others"
3171 foreach option $options {
3172 if {[
string first "System Verilog" $option]} {
3175 if {[regexp {^lib=\"([^\"]+)\"} $option match1 result]} {
3180 if {[regexp {syn_sim="([^"]*?)"} $match match_sim simonly]} {
3185 if {$type_short == "VHDL" || $type_short == "Verilog" || $type_short == "IPX"} {
3186 if {$ext == ".src"} {
3187 if {[
IsInList "${library}${ext}" [
DictGet $srcsets "sources_1"]] == 0} {
3188 dict lappend srcsets "sources_1" "${library}${ext}"
3190 dict lappend libraries "${library}${ext}" $file_path
3191 }
elseif {$ext == ".sim"} {
3193 dict lappend simsets "sim_1" "${library}.sim"
3195 dict lappend simlibraries "${library}.sim" $file_path
3201 Msg Debug "Found top module $top in $file_path"
3202 dict lappend properties $file_path "top=$top"
3204 }
elseif {$type_short == "SDC"} {
3206 dict lappend consets "constrs_1" "sources.con"
3208 dict lappend constraints "sources.con" $file_path
3212 regsub -- $match $implementationContent "" implementationContent
3215 return [list $libraries $properties $simlibraries $constraints $srcsets $simsets $consets]
3222 proc GetProjectFlavour {proj_name} {
3224 set flavour [
string map {. ""} [
file extension $proj_name]]
3225 if {$flavour != ""} {
3226 if {[
string is integer $flavour]} {
3227 Msg Info "Project $proj_name has flavour = $flavour, the generic variable FLAVOUR will be set to $flavour"
3229 Msg Warning "Project name has a unexpected non numeric extension, flavour will be set to -1"
3246 proc GetProjectVersion {proj_dir repo_path {ext_path ""} {sim 0}} {
3247 if {![
file exists $proj_dir]} {
3248 Msg CriticalWarning "$proj_dir not found"
3258 Msg Warning "Repository is not clean"
3266 Msg Debug "Project version $v_proj, latest tag $v_last"
3268 Msg Info "The specified project was modified since official version."
3275 Msg Info "The specified project was modified in the latest official version $ret"
3276 }
elseif {$comp == -1} {
3277 Msg Info "The specified project was modified in a past official version $ret"
3293 proc GetRepoVersions {proj_dir repo_path {ext_path ""} {sim 0}} {
3294 if {[
catch {
package require cmdline} ERROR]} {
3295 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
3310 lappend SHAs [
GetSHA {Hog}]
3314 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
3315 Msg Info "Hog submodule [
pwd] clean."
3316 lassign [
GetVer ./] hog_ver hog_hash
3318 Msg CriticalWarning "Hog submodule [
pwd] not clean, commit hash will be set to 0."
3319 set hog_hash "0000000"
3320 set hog_ver "00000000"
3325 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
3326 Msg Info "Git working directory [
pwd] clean."
3329 Msg CriticalWarning "Git working directory [
pwd] not clean, commit hash, and version will be set to 0."
3334 lassign [
GetVer [
join $conf_files]] top_ver top_hash
3335 lappend SHAs $top_hash
3336 lappend versions $top_ver
3343 lassign [
GetHogFiles -list_files "*.src" -sha_mode "./list/" $repo_path] src_files dummy
3344 dict for {f files} $src_files {
3345 # library names have a .src extension in values returned by GetHogFiles
3346 set name [file rootname [file tail $f]]
3347 if {[file ext $f] == ".oth"} {
3350 lassign [GetVer $files] ver hash
3351 # Msg Info "Found source list file $f, version: $ver commit SHA: $hash"
3353 lappend versions $ver
3355 lappend hashes $hash
3362 lassign [
GetHogFiles -list_files "*.con" -sha_mode "./list/" $repo_path] cons_files dummy
3363 dict for {f files} $cons_files {
3364 #library names have a .con extension in values returned by GetHogFiles
3365 set name [file rootname [file tail $f]]
3366 lassign [GetVer $files] ver hash
3367 #Msg Info "Found constraint list file $f, version: $ver commit SHA: $hash"
3369 Msg CriticalWarning "Constraints file $f not found in Git."
3371 lappend cons_hashes $hash
3373 lappend versions $ver
3380 lassign [
GetHogFiles -list_files "*.sim" -sha_mode "./list/" $repo_path] sim_files dummy
3381 dict for {f files} $sim_files {
3382 #library names have a .sim extension in values returned by GetHogFiles
3383 set name [file rootname [file tail $f]]
3384 lassign [GetVer $files] ver hash
3385 #Msg Info "Found simulation list file $f, version: $ver commit SHA: $hash"
3386 lappend sim_hashes $hash
3388 lappend versions $ver
3396 Msg CriticalWarning "No hashes found for constraints files (not in git)"
3399 set cons_hash [
string tolower [
Git "log --format=%h -1 $cons_hashes"]]
3406 set ext_files [glob -nocomplain "./list/*.ext"]
3409 foreach f $ext_files {
3410 set name [
file rootname [
file tail $f]]
3413 lappend ext_names $name
3414 lappend ext_hashes $hash
3417 lappend versions $ext_ver
3420 set file_data [read $fp]
3422 set data [
split $file_data "\n"]
3424 foreach line $data {
3425 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
3427 set file_and_prop [regexp -all -inline {\S+} $line]
3428 set hdlfile [
lindex $file_and_prop 0]
3429 set hdlfile $ext_path/$hdlfile
3430 if {[
file exists $hdlfile]} {
3431 set hash [
lindex $file_and_prop 1]
3432 set current_hash [
Md5Sum $hdlfile]
3433 if {[
string first $hash $current_hash] == -1} {
3434 Msg CriticalWarning "File $hdlfile has a wrong hash. Current checksum: $current_hash, expected: $hash"
3442 if {[
llength [glob -nocomplain ./list/*.ipb]] > 0} {
3444 lassign [
GetHogFiles -list_files "*.ipb" -sha_mode "./list/" $repo_path] xml_files dummy
3445 lassign [
GetVer [dict get $xml_files "xml.ipb"]] xml_ver xml_hash
3446 lappend SHAs $xml_hash
3447 lappend versions $xml_ver
3451 Msg Info "This project does not use IPbus XMLs"
3456 set user_ip_repos ""
3457 set user_ip_repo_hashes ""
3458 set user_ip_repo_vers ""
3460 if {[
file exists [
lindex $conf_files 0]]} {
3461 set PROPERTIES [
ReadConf [
lindex $conf_files 0]]
3462 if {[dict exists $PROPERTIES main]} {
3463 set main [dict get $PROPERTIES main]
3464 dict for {p v} $main {
3465 if {[string tolower $p] == "ip_repo_paths"} {
3467 if {[file isdirectory "$repo_path/$repo"]} {
3468 set repo_file_list [glob -nocomplain "$repo_path/$repo/*"]
3469 if {[llength $repo_file_list] == 0} {
3470 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3472 lappend user_ip_repos "$repo_path/$repo"
3481 foreach repo $user_ip_repos {
3482 if {[
file isdirectory $repo]} {
3483 set repo_file_list [glob -nocomplain "$repo/*"]
3484 if {[
llength $repo_file_list] != 0} {
3485 lassign [
GetVer $repo] ver sha
3486 lappend user_ip_repo_hashes $sha
3487 lappend user_ip_repo_vers $ver
3488 lappend versions $ver
3490 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3493 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory does not exist."
3502 while {$found == 0} {
3503 set global_commit [
Git "log --format=%h -1 --abbrev=7 $SHAs"]
3508 if {$common_child == 0} {
3509 Msg CriticalWarning "The commit $sha is not an ancestor of the global commit $global_commit, which is OK. \
3510 But $sha and $global_commit do not have any common child, which is NOT OK. \
3511 This is probably do to a REBASE that is forbidden in Hog methodology as it changes git history. \
3512 Hog cannot guarantee the accuracy of the SHAs. \
3513 A way to fix this is to make a commit that touches all the projects in the repositories (e.g. change the Hog version), \
3514 but please do not rebase in the official branches in the future."
3516 Msg Info "The commit $sha is not an ancestor of the global commit $global_commit, adding the first common child $common_child instead..."
3517 lappend SHAs $common_child
3527 set global_commit "0000000"
3528 set global_version "00000000"
3533 set top_hash [
format %+07s $top_hash]
3534 set cons_hash [
format %+07s $cons_hash]
3535 return [list $global_commit $global_version \
3536 $hog_hash $hog_ver $top_hash $top_ver \
3537 $libs $hashes $vers $cons_ver $cons_hash \
3538 $ext_names $ext_hashes $xml_hash $xml_ver \
3539 $user_ip_repos $user_ip_repo_hashes $user_ip_repo_vers]
3548 proc GetSHA {{path ""}} {
3550 lassign [
GitRet {log --format=%h --abbrev=7 -1}] status result
3552 return [
string tolower $result]
3554 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3560 set repo_path [
lindex [
Git {rev-parse --show-toplevel}] 0]
3564 set file_in_module 0
3565 if {[
file exists $repo_path/.gitmodules]} {
3566 lassign [
GitRet "config --file $repo_path/.gitmodules --get-regexp path"] status result
3568 set submodules [
split $result "\n"]
3571 Msg Warning "Something went wrong while trying to find submodules: $result"
3574 foreach mod $submodules {
3575 set module [
lindex $mod 1]
3576 if {[
string first "$repo_path/$module" $f] == 0} {
3578 set file_in_module 1
3579 lappend paths "$repo_path/$module"
3584 if {$file_in_module == 0} {
3590 lassign [
GitRet {log --format=%h --abbrev=7 -1} $paths] status result
3592 return [
string tolower $result]
3594 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3597 return [
string tolower $result]
3601 proc GetSimulators {} {
3602 set SIMULATORS [list "modelsim" "questa" "riviera" "activehdl" "ies" "vcs"]
3607 proc GetTopFile {} {
3609 set compile_order_prop [get_property source_mgmt_mode [current_project]]
3610 if {$compile_order_prop ne "All"} {
3611 Msg CriticalWarning "Compile order is not set to automatic, setting it now..."
3612 set_property source_mgmt_mode All [current_project]
3613 update_compile_order -fileset sources_1
3615 return [
lindex [get_files -quiet -compile_order sources -used_in synthesis -filter {FILE_TYPE =~ "VHDL*" || FILE_TYPE =~ "*Verilog*" }] end]
3616 }
elseif {[
IsISE]} {
3617 debug::design_graph_mgr -create [current_fileset]
3618 debug::design_graph -add_fileset [current_fileset]
3619 debug::design_graph -update_all
3620 return [
lindex [debug::design_graph -get_compile_order] end]
3622 Msg Error "GetTopFile not yet implemented for this IDE"
3627 proc GetTopModule {} {
3629 return [get_property top [current_fileset]]
3631 Msg Error "GetTopModule not yet implemented for this IDE"
3641 proc GetVer {path {force_develop 0}} {
3645 Msg CriticalWarning "Empty SHA found for ${path}. Commit to Git to resolve this warning."
3648 set p [
lindex $path 0]
3649 if {[
file isdirectory $p]} {
3652 cd [
file dirname $p]
3654 set repo_path [
Git {rev-parse --show-toplevel}]
3657 return [list [
GetVerFromSHA $SHA $repo_path $force_develop] $SHA]
3668 proc GetVerFromSHA {SHA repo_path {force_develop 0}} {
3670 Msg CriticalWarning "Empty SHA found"
3673 lassign [
GitRet "tag --sort=creatordate --contain $SHA -l v*.*.* -l b*v*.*.*"] status result
3676 if {[regexp {^ *$} $result]} {
3678 lassign [
GitRet "log --oneline --pretty=\"%d\""] status2 tag_list
3681 set pattern {tag: v\d+\.\d+\.\d+}
3682 set real_tag_list {}
3683 foreach x $tag_list {
3684 set x_untrimmed [regexp -all -inline $pattern $x]
3685 regsub "tag: " $x_untrimmed "" x_trimmed
3686 set tt [
lindex $x_trimmed 0]
3687 if {![
string equal $tt ""]} {
3688 lappend real_tag_list $tt
3692 Msg Debug "Cleaned up list: $real_tag_list."
3694 set sorted_tags [lsort -decreasing -command CompareVersions $real_tag_list]
3696 Msg Debug "Sorted Tag list: $sorted_tags"
3698 set tag [
lindex $sorted_tags 0]
3701 set pattern {v\d+\.\d+\.\d+}
3702 if {![regexp $pattern $tag]} {
3703 Msg CriticalWarning "No Hog version tags found in this repository."
3708 set repo_conf $repo_path/Top/repo.conf
3712 set hotfix_prefix "hotfix/"
3713 set minor_prefix "minor_version/"
3714 set major_prefix "major_version/"
3716 set enable_develop_branch $force_develop
3718 set branch_name [
Git {rev-parse --abbrev-ref HEAD}]
3720 if {[
file exists $repo_conf]} {
3721 set PROPERTIES [
ReadConf $repo_conf]
3723 if {[dict exists $PROPERTIES main]} {
3724 set mainDict [dict get $PROPERTIES main]
3727 if {[dict exists $mainDict ENABLE_DEVELOP_BRANCH]} {
3728 set enable_develop_branch [dict get $mainDict ENABLE_DEVELOP_BRANCH]
3734 if {[dict exists $PROPERTIES prefixes]} {
3735 set prefixDict [dict get $PROPERTIES prefixes]
3737 if {[dict exists $prefixDict HOTFIX]} {
3738 set hotfix_prefix [dict get $prefixDict HOTFIX]
3740 if {[dict exists $prefixDict MINOR_VERSION]} {
3741 set minor_prefix [dict get $prefixDict MINOR_VERSION]
3743 if {[dict exists $prefixDict MAJOR_VERSION]} {
3744 set major_prefix [dict get $prefixDict MAJOR_VERSION]
3750 if {$enable_develop_branch == 1} {
3751 if {[
string match "$hotfix_prefix*" $branch_name]} {
3756 if {[
string match "$major_prefix*" $branch_name]} {
3758 set version_level major
3759 }
elseif {[
string match "$minor_prefix*" $branch_name] || ($enable_develop_branch == 1 && $is_hotfix == 0)} {
3761 set version_level minor
3764 set version_level patch
3768 Msg CriticalWarning "Tag $tag does not contain a Hog compatible version in this repository."
3771 }
elseif {$mr == 0} {
3772 switch $version_level {
3787 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."
3793 set vers [
split $result "\n"]
3794 set ver [
lindex $vers 0]
3796 if {[regexp {^v.*$} $v]} {
3804 Msg CriticalWarning "Error while trying to find tag for $SHA"
3812 set M [
format %02X $M]
3813 set m [
format %02X $m]
3814 set c [
format %04X $c]
3815 }
elseif {$M > -1} {
3817 set M [
format %02X $M]
3818 set m [
format %02X $m]
3819 set c [
format %04X $c]
3821 Msg Warning "Tag does not contain a properly formatted version: $ver"
3822 set M [
format %02X 0]
3823 set m [
format %02X 0]
3824 set c [
format %04X 0]
3837 proc Git {command {files ""}} {
3838 lassign [
GitRet $command $files] ret result
3840 Msg Error "Code $ret returned by git running: $command -- $files"
3851 proc GetModuleName {filename} {
3853 if {![
file exists $filename]} {
3854 Msg CriticalWarning "Error: File $filename does not exist."
3859 set fileId [open $filename r]
3862 set file_content [read $fileId]
3868 if {[
file extension $filename] == ".vhd" || [
file extension $filename] == ".vhdl"} {
3870 set file_content [
string tolower $file_content]
3872 set pattern {(?m)^\s*entity\s+(\S+)\s+is}
3873 }
elseif {[
file extension $filename] == ".v" || [
file extension $filename] == ".sv"} {
3875 set pattern {\n\s*module\s*(\w+)(\s*|\(|\n)}
3877 Msg Debug "File is neither VHDL nor Verilog... Returning empty string..."
3882 if {[regexp $pattern $file_content match module_name]} {
3885 Msg Debug "No module was found in $filename. Returning an empty string..."
3893 proc GetVerilogGenerics {file} {
3894 set fp [open $file r]
3900 foreach line [
split $data "\n"] {
3901 regsub "^\\s*\/\/.*" $line "" line
3902 regsub "(.*)\/\/.*" $line {\1} line
3903 if {![
string equal $line ""]} {
3904 append lines $line " "
3909 regsub -all {/\*.*\*/} $lines "" lines
3912 set punctuation [list]
3913 foreach char [list "(" ")" ";" "," " " "!" "<=" ":=" "=" "\[" "\]"] {
3914 lappend punctuation $char "\000$char\000"
3918 set tokens [
split [
string map $punctuation $lines] \000]
3920 set parameters [dict create]
3929 foreach token $tokens {
3930 set token [
string trim $token]
3931 if {![
string equal "" $token]} {
3932 if {[
string equal [
string tolower $token] "parameter"]} {
3933 set state $PARAM_NAME
3934 }
elseif {[
string equal $token ")"] || [
string equal $token ";"]} {
3936 }
elseif {$state == $PARAM_WIDTH} {
3937 if {[
string equal $token "\]"]} {
3938 set state $PARAM_NAME
3940 }
elseif {$state == $PARAM_VALUE} {
3941 if {[
string equal $token ","]} {
3942 set state $PARAM_NAME
3943 }
elseif {[
string equal $token ";"]} {
3948 }
elseif {$state == $PARAM_NAME} {
3949 if {[
string equal $token "="]} {
3950 set state $PARAM_VALUE
3951 }
elseif {[
string equal $token "\["]} {
3952 set state $PARAM_WIDTH
3953 }
elseif {[
string equal $token ","]} {
3954 set state $PARAM_NAME
3955 }
elseif {[
string equal $token ";"]} {
3957 }
elseif {[
string equal $token ")"]} {
3960 dict set parameters $token "integer"
3973 proc GetVhdlGenerics {file {entity ""}} {
3974 set fp [open $file r]
3980 foreach line [
split $data "\n"] {
3981 regsub "^\\s*--.*" $line "" line
3982 regsub "(.*)--.*" $line {\1} line
3983 if {![
string equal $line ""]} {
3984 append lines $line " "
3989 set generic_block ""
3990 set generics [dict create]
3992 if {1 == [
string equal $entity ""]} {
3993 regexp {(?i).*entity\s+([^\s]+)\s+is} $lines _ entity
3996 set generics_regexp "(?i).*entity\\s+$entity\\s+is\\s+generic\\s*\\((.*)\\)\\s*;\\s*port.*end.*$entity"
3998 if {[regexp $generics_regexp $lines _ generic_block]} {
4000 foreach line [
split $generic_block ";"] {
4002 regexp {(.*):\s*([A-Za-z0-9_]+).*} $line _ generic type
4005 set splits [
split $generic ","]
4006 foreach split $splits {
4007 dict set generics [
string trim $split] [
string trim $type]
4015 proc GHDL {command logfile} {
4016 set ret [
catch {
exec -ignorestderr ghdl {*}$command >>& $logfile} result options]
4021 return [list $ret $result]
4033 proc GitRet {command {files ""}} {
4036 set ret [
catch {
exec -ignorestderr git {*}$command} result]
4038 set ret [
catch {
exec -ignorestderr git {*}$command -- {*}$files} result]
4040 return [list $ret $result]
4048 proc GitVersion {target_version} {
4049 set ver [
split $target_version "."]
4050 set v [
Git --version]
4052 set current_ver [
split [
lindex $v 2] "."]
4053 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
4054 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
4055 return [
expr {$target <= $current}]
4069 proc HandleIP {what_to_do xci_file ip_path repo_path {gen_dir "."} {force 0}} {
4071 if {!($what_to_do eq "push") && !($what_to_do eq "pull")} {
4072 Msg Error "You must specify push or pull as first argument."
4075 if {[
catch {
package require tar} TARPACKAGE]} {
4076 Msg CriticalWarning "Cannot find package tar. You can fix this by installing package \"tcllib\""
4085 if {[
string first "/eos/" $ip_path] == 0} {
4093 lassign [
eos "ls $ip_path"] ret result
4095 Msg CriticalWarning "Could not run ls for for EOS path: $ip_path (error: $result). \
4096 Either the drectory does not exist or there are (temporary) problem with EOS."
4100 Msg Info "IP remote directory path, on EOS, is set to: $ip_path"
4106 if {!([
file exists $xci_file])} {
4107 Msg CriticalWarning "Could not find $xci_file."
4113 set xci_path [
file dirname $xci_file]
4114 set xci_name [
file tail $xci_file]
4115 set xci_ip_name [
file rootname [
file tail $xci_file]]
4116 set xci_dir_name [
file tail $xci_path]
4117 set gen_path $gen_dir
4119 set hash [
Md5Sum $xci_file]
4120 set file_name $xci_name\_$hash
4122 Msg Info "Preparing to $what_to_do IP: $xci_name..."
4124 if {$what_to_do eq "push"} {
4128 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4133 Msg Info "IP already in the EOS repository, will not copy..."
4135 Msg Info "IP already in the EOS repository, will forcefully replace..."
4141 if {[
file exists "$ip_path/$file_name.tar"]} {
4143 Msg Info "IP already in the local repository, will not copy..."
4145 Msg Info "IP already in the local repository, will forcefully replace..."
4154 if {$will_copy == 1} {
4156 Msg Info "Looking for generated files in $gen_path..."
4157 set ip_gen_files [glob -nocomplain $gen_path/*]
4161 if {[
llength $ip_gen_files] > 0} {
4162 Msg Info "Found some IP synthesised files matching $xci_ip_name"
4163 if {$will_remove == 1} {
4164 Msg Info "Removing old synthesised directory $ip_path/$file_name.tar..."
4166 eos "rm -rf $ip_path/$file_name.tar" 5
4168 file delete -force "$ip_path/$file_name.tar"
4172 Msg Info "Creating local archive with IP generated files..."
4175 foreach f $ip_gen_files {
4176 lappend tar_files "[
Relative [
file normalize $repo_path] $f]"
4179 ::tar::create $file_name.tar $tar_files
4181 Msg Info "Copying IP generated files for $xci_name..."
4183 lassign [
ExecuteRet xrdcp -f -s $file_name.tar $::env(EOS_MGM_URL)//$ip_path/] ret msg
4185 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4188 Copy "$file_name.tar" "$ip_path/"
4190 Msg Info "Removing local archive"
4191 file delete $file_name.tar
4193 Msg Warning "Could not find synthesized files matching $gen_path/$file_name*"
4196 }
elseif {$what_to_do eq "pull"} {
4198 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4200 Msg Info "Nothing for $xci_name was found in the EOS repository, cannot pull."
4204 set remote_tar "$::env(EOS_MGM_URL)//$ip_path/$file_name.tar"
4205 Msg Info "IP $xci_name found in the repository $remote_tar, copying it locally to $repo_path..."
4207 lassign [
ExecuteRet xrdcp -f -r -s $remote_tar $repo_path] ret msg
4209 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4213 if {[
file exists "$ip_path/$file_name.tar"]} {
4214 Msg Info "IP $xci_name found in local repository $ip_path/$file_name.tar, copying it locally to $repo_path..."
4215 Copy $ip_path/$file_name.tar $repo_path
4217 Msg Info "Nothing for $xci_name was found in the local IP repository, cannot pull."
4223 if {[
file exists $file_name.tar]} {
4224 remove_files $xci_file
4225 Msg Info "Extracting IP files from archive to $repo_path..."
4226 ::tar::untar $file_name.tar -dir $repo_path -noperms
4227 Msg Info "Removing local archive"
4228 file delete $file_name.tar
4229 add_files -norecurse -fileset sources_1 $xci_file
4242 proc HexVersionToString {version} {
4243 scan [
string range $version 0 1] %x M
4244 scan [
string range $version 2 3] %x m
4245 scan [
string range $version 4 7] %x c
4250 proc ImportTclLib {} {
4253 if {[
info exists env(HOG_TCLLIB_PATH)]} {
4254 lappend auto_path $env(HOG_TCLLIB_PATH)
4257 puts "ERROR: To run Hog with Microsemi Libero SoC or Lattice Diamond, you need to define the HOG_TCLLIB_PATH variable."
4272 proc InitLauncher {script tcl_path parameters commands argv {custom_commands ""}} {
4273 set repo_path [
file normalize "$tcl_path/../.."]
4275 set bin_path [
file normalize "$tcl_path/../../bin"]
4276 set top_path [
file normalize "$tcl_path/../../Top"]
4278 set cmd_lines [
split $commands "\n"]
4280 set command_options [dict create]
4281 set directive_descriptions [dict create]
4282 set directive_names [dict create]
4283 set common_directive_names [dict create]
4284 set custom_command ""
4285 set custom_command_options ""
4287 foreach l $cmd_lines {
4289 if {[regexp {\\(.*) \{\#} $l minc d]} {
4290 lappend directives_with_projects $d
4294 if {[regexp {\\(.*) \{} $l minc regular_expression]} {
4295 lappend directive_regex $regular_expression
4299 if {[regexp {\#\s*NAME(\*)?:\s*(.*)\s*} $l minc star name]} {
4300 dict set directive_names $name $regular_expression
4302 dict set common_directive_names $name $regular_expression
4305 set directive_names [
DictSort $directive_names]
4306 set common_directive_names [
DictSort $common_directive_names]
4309 if {[regexp {\#\s*DESCRIPTION:\s*(.*)\s*} $l minc x]} {
4310 dict set directive_descriptions $regular_expression $x
4314 if {[regexp {\#\s*OPTIONS:\s*(.*)\s*} $l minc x]} {
4315 dict set command_options $regular_expression [
split [regsub -all {[ \t\n]+} $x {}] ","]
4319 set short_usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nMost common directives (case insensitive):"
4321 dict for {key value} $common_directive_names {
4322 set short_usage "$short_usage\n - $key: [dict get $directive_descriptions $value]"
4326 if {[
string length $custom_commands] > 0} {
4327 Msg Debug "Found custom commands to add to short short_usage."
4328 set short_usage "$short_usage\n\nCustom commands:"
4329 dict for {key command} $custom_commands {
4330 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4331 set short_usage "$short_usage\n - $key: [dict get $command DESCRIPTION]"
4337 set short_usage "$short_usage\n\n\
4338 To see all the available directives, run:\n./Hog/Do HELP\n\n\
4339 To list available options for the chosen directive run:\n\
4340 ./Hog/Do <directive> HELP\n
4343 set usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nDirectives (case insensitive):"
4345 dict for {key value} $directive_names {
4346 set usage "$usage\n - $key: [dict get $directive_descriptions $value]"
4350 if {[
string length $custom_commands] > 0} {
4351 Msg Debug "Found custom commands to add to short usage."
4352 set usage "$usage\n\nCustom commands:"
4353 dict for {key command} $custom_commands {
4354 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4355 set usage "$usage\n - $key: [dict get $command DESCRIPTION]"
4360 set usage "$usage\n\nTo list available options for the chosen directive run:\n./Hog/Do <directive> HELP"
4369 Msg Debug "HogEnv.conf found"
4377 if {[
catch {
package require cmdline} ERROR]} {
4378 Msg Debug "The cmdline Tcl package was not found, sourcing it from Hog..."
4379 source $tcl_path/utils/cmdline.tcl
4382 set argv [regsub -all {(?i) HELP\y} $argv " -help"]
4387 set custom_parameters [list]
4388 dict for {key command} $custom_commands {
4389 set custom_parameters [concat $custom_parameters [dict get $command CUSTOM_OPTIONS]]
4392 lassign [
GetOptions $argv [
concat $custom_parameters $parameters]] option_list arg_list
4394 if {[
IsInList "-all" $option_list]} {
4403 set directive [
string toupper [
lindex $arg_list 0]]
4406 set argument_is_no_project 1
4408 set NO_DIRECTIVE_FOUND 0
4409 switch -regexp -- $directive "$commands"
4410 if {$NO_DIRECTIVE_FOUND == 1} {
4411 if {[
string length $custom_commands] > 0 && [dict exists $custom_commands $directive]} {
4412 set custom_command $directive
4413 set custom_command_hog_parameters [dict get $custom_commands $directive OPTIONS]
4414 set custom_command_options [dict get $custom_commands $directive CUSTOM_OPTIONS]
4415 set custom_command_options [
concat $custom_command_hog_parameters $custom_command_options]
4417 Msg Status "ERROR: Unknown directive $directive.\n\n"
4423 if {[
IsInList $directive $directives_with_projects 1]} {
4424 set argument_is_no_project 0
4428 if {$directive != ""} {
4429 if {[
IsInList $directive $directives_with_projects 1]} {
4430 puts "usage: ./Hog/Do \[OPTIONS\] $directive <project>\n"
4431 }
elseif {[regexp "^COMPSIM(LIB)?$" $directive]} {
4432 puts "usage: ./Hog/Do \[OPTIONS\] $directive <simulator>\n"
4434 puts "usage: ./Hog/Do \[OPTIONS\] $directive \n"
4437 dict for {dir desc} $directive_descriptions {
4438 if {[regexp $dir $directive]} {
4446 if {$custom_command ne ""} {
4447 if {[
llength $custom_command_options] > 0} {
4448 puts "Available options:"
4450 foreach custom_option $custom_command_options {
4451 set n [
llength $custom_option]
4453 lassign $custom_option opt help
4456 }
elseif {$n == 3} {
4457 lassign $custom_option opt def help
4458 puts " -$opt <argument>"
4460 puts " $help (default: $def)"
4465 Msg Warning "Custom option spec has invalid arity (expected 2 or 3): $custom_option"
4470 dict for {dir opts} $command_options {
4471 if {[regexp $dir $directive]} {
4472 puts "Available options:"
4474 foreach par $parameters {
4475 if {$opt == [lindex $par 0]} {
4476 if {[regexp {\.arg$} $opt]} {
4477 set opt_name [regsub {\.arg$} $opt ""]
4478 puts " -$opt_name <argument>"
4482 puts " [lindex $par [llength $par]-1]"
4496 if {$custom_command ne ""} {
4497 set parameters [
concat $parameters $custom_command_options]
4500 if {[
catch {
array set options [
cmdline::getoptions option_list $parameters $usage]} err]} {
4501 Msg Status "\nERROR: Syntax error, probably unknown option.\n\n USAGE: $err"
4505 if {[
llength $arg_list] <= $min_n_of_args || [
llength $arg_list] > $max_n_of_args} {
4506 Msg Status "\nERROR: Wrong number of arguments: [
llength $argv]"
4511 set project [
lindex $arg_list 1]
4513 if {$argument_is_no_project == 0} {
4515 regsub "^(\./)?Top/" $project "" project
4517 regsub "/? *\$" $project "" project
4523 Msg Debug "Option list:"
4524 foreach {key value} [
array get options] {
4525 Msg Debug "$key => $value"
4532 if {$proj_conf != 0} {
4535 lassign [
GetIDECommand $proj_conf] cmd before_tcl_script after_tcl_script end_marker
4536 Msg Info "Project $project uses $cmd IDE"
4539 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4541 if {$custom_command ne ""} {
4542 if { [dict exists $custom_commands $directive IDE] } {
4543 lassign [
GetIDECommand "" [dict get $custom_commands $directive IDE]] cmd before_tcl_script after_tcl_script end_marker
4544 Msg Info "Custom command: $custom_command uses $cmd IDE"
4545 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4547 set command "custom_tcl"
4549 }
elseif {$argument_is_no_project == 1} {
4551 Msg Debug "$project will be used as first argument"
4552 }
elseif {$project != ""} {
4555 }
elseif {$min_n_of_args < 0} {
4568 set project_group [
file dirname $project]
4569 set project_name $project
4570 set project [
file tail $project]
4571 Msg Debug "InitLauncher: project_group=$project_group, project_name=$project_name, project=$project"
4573 return [list $directive $project $project_name $project_group $repo_path $old_path $bin_path $top_path $usage $short_usage $command $cmd [
array get options]]
4580 proc IsCommitAncestor {ancestor commit} {
4581 lassign [
GitRet "merge-base --is-ancestor $ancestor $commit"] status result
4590 return [
expr {[info commands sys_install] != ""}]
4595 return [
expr {[info commands get_libero_version] != ""}]
4604 proc IsInList {element list {regex 0} {nocase 0}} {
4608 if {[regexp -nocase $x $element]} {
4612 if {[regexp $x $element]} {
4616 }
elseif {$regex == 0} {
4618 if {[
string tolower $x] eq [
string tolower $element]} {
4622 if {$x eq $element} {
4635 return [
expr {[string first PlanAhead [version]] == 0}]
4643 if {[
catch {
package require ::quartus::flow} result]} {
4656 proc IsRelativePath {path} {
4657 if {[
string index $path 0] == "/" || [
string index $path 0] == "~"} {
4665 proc IsSynplify {} {
4666 return [
expr {[info commands program_version] != ""}]
4671 return [
expr {![IsQuartus] && ![IsXilinx] && ![IsVitisClassic] && ![IsLibero] && ![IsSynplify] && ![IsDiamond]}]
4679 proc IsVerilog {file} {
4680 if {[
file extension $file] == ".v" || [
file extension $file] == ".sv"} {
4692 proc IsVersal {part} {
4693 if {[get_property ARCHITECTURE [get_parts $part]] eq "versal"} {
4703 return [
expr {[string first Vivado [version]] == 0}]
4711 if {[
info commands version] != ""} {
4712 set current_version [version]
4713 if {[
string first PlanAhead $current_version] == 0 || [
string first Vivado $current_version] == 0} {
4715 }
elseif {[
string first xsct $current_version] == 0} {
4718 Msg Warning "This IDE has the version command but it is not PlanAhead or Vivado: $current_version"
4727 proc IsVitisClassic {} {
4728 return [
expr {[info commands platform] != ""}]
4736 proc IsZynq {part} {
4737 if {[regexp {^(xc7z|xczu).*} $part]} {
4744 proc ImportGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
4745 set list_path "$repo_path/Top/$project_name/list"
4746 lassign [
GetHogFiles -list_files {.src,.ext,.sim} -ext_path $ext_path $list_path $repo_path] src_files properties filesets
4751 set properties [
DictGet $simset_dict "properties"]
4752 set options [
DictGet $properties "options"]
4755 set workdir Projects/$project_name/ghdl
4756 file delete -force $workdir
4758 set import_log "$workdir/ghdl-import-${simset_name}.log"
4759 dict for {lib sources} $src_files {
4760 set libname [file rootname $lib]
4761 foreach f $sources {
4762 if {[file extension $f] != ".vhd" && [file extension $f] != ".vhdl"} {
4763 Msg Info "File $f is not a VHDL file, copying it in workfolder..."
4764 file copy -force $f $workdir
4766 set file_path [Relative $repo_path $f]
4767 set import_log_file [open $import_log "a"]
4768 puts "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
4769 puts $import_log_file "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
4770 close $import_log_file
4771 lassign [GHDL "-i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path" $import_log] ret result
4773 Msg CriticalWarning "GHDL import failed for file $f: $result"
4782 proc LaunchGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
4785 set sim_props [
DictGet $simset_dict "properties"]
4786 set options [
DictGet $sim_props "options"]
4787 set runopts [
DictGet $sim_props "run_options"]
4789 dict for {prop_name prop_val} $sim_props {
4790 set prop_name [string toupper $prop_name]
4791 if {$prop_name == "TOP"} {
4792 set top_sim $prop_val
4795 set workdir $repo_path/Projects/$project_name/ghdl
4796 set make_log "$workdir/ghdl-make-${simset_name}.log"
4797 set run_log "$workdir/ghdl-run-${simset_name}.log"
4800 set make_log_file [open $make_log "w"]
4802 puts "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
4803 puts $make_log_file "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
4804 close $make_log_file
4806 lassign [
GHDL "-m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim" $make_log] ret result
4809 Msg Error "GHDL make failed for $top_sim: $result"
4813 set run_log_file [open $run_log "w"]
4814 puts "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
4815 puts $run_log_file "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
4818 lassign [
GHDL "-r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts" $run_log] ret result
4822 Msg Error "GHDL run failed for $top_sim: $result"
4837 proc LaunchImplementation {reset do_create run_folder project_name {repo_path .} {njobs 4} {do_bitstream 0}} {
4838 Msg Info "Starting implementation flow..."
4840 if {$reset == 1 && $do_create == 0} {
4841 Msg Info "Resetting run before launching implementation..."
4846 source $repo_path/Hog/Tcl/integrated/pre-implementation.tcl
4849 if {$do_bitstream == 1} {
4850 launch_runs impl_1 -to_step [
BinaryStepName [get_property PART [current_project]]] -jobs $njobs -dir $run_folder
4852 launch_runs impl_1 -jobs $njobs -dir $run_folder
4857 Msg Info "running post-implementation"
4858 source $repo_path/Hog/Tcl/integrated/post-implementation.tcl
4859 if {$do_bitstream == 1} {
4860 Msg Info "running pre-bitstream"
4861 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
4862 Msg Info "running post-bitstream"
4863 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
4867 set prog [get_property PROGRESS [get_runs impl_1]]
4868 set status [get_property STATUS [get_runs impl_1]]
4869 Msg Info "Run: impl_1 progress: $prog, status : $status"
4873 set status_file [open "$run_folder/timing.txt" "w"]
4874 puts $status_file "## $project_name Timing summary"
4876 set f [open [
lindex [glob "$run_folder/impl_1/*.twr" 0]]]
4878 while {[
gets $f line] >= 0} {
4879 if {[
string match "Timing summary:" $line]} {
4880 while {[
gets $f line] >= 0} {
4881 if {[
string match "Timing errors:*" $line]} {
4882 set errs [regexp -inline -- {[0-9]+} $line]
4884 if {[
string match "*Footnotes*" $line]} {
4887 puts $status_file "$line"
4896 Msg Info "Time requirements are met"
4897 file rename -force "$run_folder/timing.txt" "$run_folder/timing_ok.txt"
4900 Msg CriticalWarning "Time requirements are NOT met"
4901 file rename -force "$run_folder/timing.txt" "$run_folder/timing_error.txt"
4907 set wns [get_property STATS.WNS [get_runs [current_run]]]
4908 set tns [get_property STATS.TNS [get_runs [current_run]]]
4909 set whs [get_property STATS.WHS [get_runs [current_run]]]
4910 set ths [get_property STATS.THS [get_runs [current_run]]]
4911 set tpws [get_property STATS.TPWS [get_runs [current_run]]]
4913 if {$wns >= 0 && $whs >= 0 && $tpws >= 0} {
4914 Msg Info "Time requirements are met"
4915 set status_file [open "$run_folder/timing_ok.txt" "w"]
4918 Msg CriticalWarning "Time requirements are NOT met"
4919 set status_file [open "$run_folder/timing_error.txt" "w"]
4923 Msg Status "*** Timing summary ***"
4924 Msg Status "WNS: $wns"
4925 Msg Status "TNS: $tns"
4926 Msg Status "WHS: $whs"
4927 Msg Status "THS: $ths"
4928 Msg Status "TPWS: $tpws"
4934 puts $status_file "## $project_name Timing summary"
4936 m add row "| **Parameter** | \"**value (ns)**\" |"
4937 m add row "| --- | --- |"
4938 m add row "| WNS: | $wns |"
4939 m add row "| TNS: | $tns |"
4940 m add row "| WHS: | $whs |"
4941 m add row "| THS: | $ths |"
4942 m add row "| TPWS: | $tpws |"
4944 puts $status_file [m format 2string]
4945 puts $status_file "\n"
4946 if {$timing_ok == 1} {
4947 puts $status_file " Time requirements are met."
4949 puts $status_file "Time requirements are **NOT** met."
4951 puts $status_file "\n\n"
4955 if {$prog ne "100%"} {
4956 Msg Error "Implementation error"
4961 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
4963 Msg Info "Git describe set to $describe"
4965 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
4970 if {[
file exists $run_folder/versions.txt]} {
4971 file copy -force $run_folder/versions.txt $dst_dir
4973 Msg Warning "No versions file found in $run_folder/versions.txt"
4976 set timing_files [glob -nocomplain "$run_folder/timing_*.txt"]
4977 set timing_file [
file normalize [
lindex $timing_files 0]]
4979 if {[
file exists $timing_file]} {
4980 file copy -force $timing_file $dst_dir/
4982 Msg Warning "No timing file found, not a problem if running locally"
4986 if {[
IsVersal [get_property part [current_project]]]} {
4987 if {[get_property segmented_configuration [current_project]] == 1} {
4988 Msg Info "Versal Segmented configuration detected: exporting XSA file..."
4989 set xsa_name "$dst_dir/[
file tail $project_name]\-$describe.xsa"
4990 write_hw_platform -fixed -force -file $xsa_name
4994 set revision [get_current_revision]
4996 if {[
catch {execute_module -tool fit} result]} {
4997 Msg Error "Result: $result\n"
4998 Msg Error "Place & Route failed. See the report file.\n"
5000 Msg Info "\nINFO: Place & Route was successful for revision $revision.\n"
5003 if {[
catch {execute_module -tool sta -args "--do_report_timing"} result]} {
5004 Msg Error "Result: $result\n"
5005 Msg Error "Time Quest failed. See the report file.\n"
5007 Msg Info "Time Quest was successfully run for revision $revision.\n"
5010 set panel "Timing Analyzer||Timing Analyzer Summary"
5011 set device [get_report_panel_data -name $panel -col 1 -row_name "Device Name"]
5012 set timing_model [get_report_panel_data -name $panel -col 1 -row_name "Timing Models"]
5013 set delay_model [get_report_panel_data -name $panel -col 1 -row_name "Delay Model"]
5015 Msg Info "*******************************************************************"
5016 Msg Info "Device: $device"
5017 Msg Info "Timing Models: $timing_model"
5018 Msg Info "Delay Model: $delay_model"
5021 Msg Info "*******************************************************************"
5024 Msg Info "Starting implementation flow..."
5025 if {[
catch {run_tool -name {PLACEROUTE}}]} {
5026 Msg Error "PLACEROUTE FAILED!"
5028 Msg Info "PLACEROUTE PASSED."
5032 Msg Info "Run VERIFYTIMING ..."
5033 if {[
catch {run_tool -name {VERIFYTIMING} -script {Hog/Tcl/integrated/libero_timing.tcl}}]} {
5034 Msg CriticalWarning "VERIFYTIMING FAILED!"
5036 Msg Info "VERIFYTIMING PASSED \n"
5042 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5044 Msg Info "Git describe set to $describe"
5046 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5047 file mkdir $dst_dir/reports
5050 if {[
file exists $run_folder/versions.txt]} {
5051 file copy -force $run_folder/versions.txt $dst_dir
5053 Msg Warning "No versions file found in $run_folder/versions.txt"
5056 set timing_file_path [
file normalize "$repo_path/Projects/timing_libero.txt"]
5057 if {[
file exists $timing_file_path]} {
5058 file copy -force $timing_file_path $dst_dir/reports/Timing.txt
5059 set timing_file [open $timing_file_path "r"]
5060 set status_file [open "$dst_dir/timing.txt" "w"]
5061 puts $status_file "## $project_name Timing summary\n\n"
5062 puts $status_file "| | |"
5063 puts $status_file "| --- | --- |"
5064 while {[
gets $timing_file line] >= 0} {
5065 if {[
string match "SUMMARY" $line]} {
5066 while {[
gets $timing_file line] >= 0} {
5067 if {[
string match "END SUMMARY" $line]} {
5070 if {[
string first ":" $line] == -1} {
5073 set out_string "| [
string map {: | } $line] |"
5074 puts $status_file "$out_string"
5079 Msg Warning "No timing file found, not a problem if running locally"
5084 set force_rst "-forceOne"
5086 prj_run Map $force_rst
5087 prj_run PAR $force_rst
5099 proc GenerateBitstreamOnly {project_name {repo_path .}} {
5101 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5103 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5105 cd Projects/$project_name/$project_name.runs/impl_1
5106 Msg Info "Running pre-bitstream..."
5107 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5109 Msg Info "Writing bitstream for $project_name..."
5111 write_bitstream -force $dst_dir/$project_name-$describe.bit
5113 Msg Info "Running post-bitstream..."
5114 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5123 proc LaunchSimulation {project_name lib_path simsets {repo_path .} {scripts_only 0} {compile_only 0}} {
5126 set project [
file tail $project_name]
5127 set main_sim_folder [
file normalize "$repo_path/Projects/$project_name/$project.sim/"]
5129 if {$simsets != ""} {
5130 dict for {simset sim_dict} $simsets {
5131 lappend simsets_todo $simset
5133 Msg Info "Will run only the following simulation's sets (if they exist): $simsets_todo"
5136 if {$scripts_only == 1} {
5137 Msg Info "Only generating simulation scripts, not running simulations..."
5140 if {$compile_only == 1} {
5141 Msg Info "Only compiling simulation libraries, not running simulations..."
5146 set sim_dic [dict create]
5148 Msg Info "Retrieving list of simulation sets..."
5149 foreach s [get_filesets] {
5151 set use_simpass_str 0
5154 set type [get_property FILESET_TYPE $s]
5155 if {$type eq "SimulationSrcs"} {
5156 if {$simsets_todo != "" && $s ni $simsets_todo} {
5157 Msg Info "Skipping $s as it was not specified with the -simset option..."
5160 set sim_dict [
DictGet $simsets $s]
5161 set simulator [
DictGet $sim_dict "simulator"]
5162 set_property "target_simulator" $simulator [current_project]
5163 set hog_sim_props [
DictGet $sim_dict "hog"]
5164 dict for {prop_name prop_val} $hog_sim_props {
5165 # If HOG_SIMPASS_STR is set, use the HOG_SIMPASS_STR string to search for in logs, after simulation is done
5166 if {[string toupper $prop_name] == "HOG_SIMPASS_STR" && $prop_val != ""} {
5167 Msg Info "Setting simulation pass string as '$prop_val'"
5168 set use_simpass_str 1
5169 set simpass_str $prop_val
5171 if {[string toupper $prop_name] == "HOG_SILENT_SIM" && $prop_val == 1} {
5172 set quiet_sim " -quiet"
5178 Msg Info "Creating simulation scripts for $s..."
5179 if {[
file exists $repo_path/Top/$project_name/pre-simulation.tcl]} {
5180 Msg Info "Running $repo_path/Top/$project_name/pre-simulation.tcl"
5181 source $repo_path/Top/$project_name/pre-simulation.tcl
5183 if {[
file exists $repo_path/Top/$project_name/pre-$s-simulation.tcl]} {
5184 Msg Info "Running $repo_path/Top/$project_name/pre-$s-simulation.tcl"
5185 source Running $repo_path/Top/$project_name/pre-$s-simulation.tcl
5187 current_fileset -simset $s
5188 set sim_dir $main_sim_folder/$s/behav
5189 set sim_output_logfile $sim_dir/xsim/simulate.log
5190 if {([
string tolower $simulator] eq "xsim")} {
5191 set sim_name "xsim:$s"
5193 set simulation_command "launch_simulation $quiet_sim -simset [get_filesets $s]"
5194 if {[
catch $simulation_command log]} {
5197 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5198 lappend failed $sim_name
5203 if {$use_simpass_str == 1} {
5206 set file_desc [open $sim_output_logfile r]
5207 set log [read $file_desc]
5210 Msg Info "Searching for simulation pass string: '$simpass_str'"
5211 if {[
string first $simpass_str $log] == -1} {
5212 Msg CriticalWarning "Simulation failed for $s, error info: '$simpass_str' NOT found!"
5213 lappend failed $sim_name
5216 lappend success $sim_name
5220 lappend success $sim_name
5224 Msg Info "Simulation library path is set to $lib_path."
5226 if {!([
file exists $lib_path])} {
5227 Msg Warning "Could not find simulation library path: $lib_path, $simulator simulation will not work."
5231 if {$simlib_ok == 1} {
5232 set_property "compxlib.${simulator}_compiled_library_dir" [
file normalize $lib_path] [current_project]
5233 launch_simulation -scripts_only -simset [get_filesets $s]
5234 set top_name [get_property TOP $s]
5235 set sim_script [
file normalize $sim_dir/$simulator/]
5236 Msg Info "Adding simulation script location $sim_script for $s..."
5237 lappend sim_scripts $sim_script
5238 dict append sim_dic $sim_script $s
5240 Msg Error "Cannot run $simulator simulations without a valid library path"
5247 if {[
info exists sim_scripts] && $scripts_only == 0} {
5249 Msg Info "Generating IP simulation targets, if any..."
5251 foreach ip [get_ips] {
5252 generate_target simulation -quiet $ip
5257 Msg Info "====== Starting simulations runs ======"
5260 foreach s $sim_scripts {
5262 set cmd ./compile.sh
5263 Msg Info " ************* Compiling: $s ************* "
5265 set sim_name "comp:[dict get $sim_dic $s]"
5267 Msg CriticalWarning "Compilation failed for $s, error info: $::errorInfo"
5268 lappend failed $sim_name
5270 lappend success $sim_name
5272 Msg Info "###################### Compilation log starts ######################"
5273 Msg Info "\n\n$log\n\n"
5274 Msg Info "###################### Compilation log ends ######################"
5276 if {$compile_only == 1} {
5279 if {[
file exists "./elaborate.sh"] } {
5280 set cmd ./elaborate.sh
5281 Msg Info " ************* Elaborating: $s ************* "
5283 set sim_name "elab:[dict get $sim_dic $s]"
5285 Msg CriticalWarning "Elaboration failed for $s, error info: $::errorInfo"
5286 lappend failed $sim_name
5288 lappend success $sim_name
5290 Msg Info "###################### Elaboration log starts ######################"
5291 Msg Info "\n\n$log\n\n"
5292 Msg Info "###################### Elaboration log ends ######################"
5294 set cmd ./simulate.sh
5295 Msg Info " ************* Simulating: $s ************* "
5300 if {$use_simpass_str == 1} {
5301 if {[
string first $simpass_str $log] == -1} {
5305 Msg Debug "Simulation pass string not set, relying on simulator exit code."
5309 set sim_name "sim:[dict get $sim_dic $s]"
5311 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5312 lappend failed $sim_name
5314 lappend success $sim_name
5316 Msg Info "###################### Simulation log starts ######################"
5317 Msg Info "\n\n$log\n\n"
5318 Msg Info "###################### Simulation log ends ######################"
5323 if {[
llength $success] > 0} {
5324 set successes [
join $success "\n"]
5325 Msg Info "The following simulation sets were successful:\n\n$successes\n\n"
5328 if {[
llength $failed] > 0} {
5329 set failures [
join $failed "\n"]
5330 Msg Error "The following simulation sets have failed:\n\n$failures\n\n"
5332 }
elseif {[
llength $success] > 0} {
5333 Msg Info "All the [
llength $success] compilations, elaborations and simulations were successful."
5336 Msg Info "Simulation done."
5338 Msg Warning "Simulation is not yet supported for [
GetIDEName]."
5346 proc LaunchRTLAnalysis {repo_path} {
5348 Msg Info "Starting RTL Analysis..."
5350 synth_design -rtl -name rtl_1
5352 Msg Warning "RTL Analysis is not yet supported for [
GetIDEName]."
5365 proc LaunchSynthesis {reset do_create run_folder project_name {repo_path .} {ext_path ""} {njobs 4}} {
5367 if {$reset == 1 && $do_create == 0} {
5368 Msg Info "Resetting run before launching synthesis..."
5372 source $repo_path/Hog/Tcl/integrated/pre-synthesis.tcl
5374 launch_runs synth_1 -jobs $njobs -dir $run_folder
5376 set prog [get_property PROGRESS [get_runs synth_1]]
5377 set status [get_property STATUS [get_runs synth_1]]
5378 Msg Info "Run: synth_1 progress: $prog, status : $status"
5385 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path $ext_path] sha
5387 Msg Info "Git describe set to $describe"
5390 set xci_file [get_property IP_FILE $ip]
5392 set xci_path [
file dirname $xci_file]
5393 set xci_ip_name [
file rootname [
file tail $xci_file]]
5394 foreach rptfile [glob -nocomplain -directory $xci_path *.rpt] {
5395 file copy $rptfile $repo_path/bin/$project_name-$describe/reports
5399 if {$prog ne "100%"} {
5400 Msg Error "Synthesis error, status is: $status"
5404 set project [
file tail [
file rootname $project_name]]
5406 Msg Info "Number of jobs set to $njobs."
5407 set_global_assignment -name NUM_PARALLEL_PROCESSORS $njobs
5411 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$project_name] $repo_path] sha
5414 set revision [get_current_revision]
5417 set tool_and_command [
split [get_global_assignment -name PRE_FLOW_SCRIPT_FILE] ":"]
5418 set tool [
lindex $tool_and_command 0]
5419 set pre_flow_script [
lindex $tool_and_command 1]
5420 set cmd "$tool -t $pre_flow_script quartus_map $project $revision"
5426 Msg Warning "Can not execute command $cmd"
5427 Msg Warning "LOG: $log"
5429 Msg Info "Pre flow script executed!"
5433 if {![is_project_open]} {
5434 Msg Info "Re-opening project file $project_name..."
5435 project_open $project -current_revision
5439 if {[
catch {execute_module -tool ipg -args "--clean"} result]} {
5440 Msg Error "Result: $result\n"
5441 Msg Error "IP Generation failed. See the report file.\n"
5443 Msg Info "IP Generation was successful for revision $revision.\n"
5447 if {[
catch {execute_module -tool map -args "--parallel"} result]} {
5448 Msg Error "Result: $result\n"
5449 Msg Error "Analysis & Synthesis failed. See the report file.\n"
5451 Msg Info "Analysis & Synthesis was successful for revision $revision.\n"
5455 defvar_set -name RWNETLIST_32_64_MIXED_FLOW -value 0
5457 Msg Info "Run SYNTHESIS..."
5458 if {[
catch {run_tool -name {SYNTHESIZE}}]} {
5459 Msg Error "SYNTHESIZE FAILED!"
5461 Msg Info "SYNTHESIZE PASSED!"
5467 set force_rst "-forceOne"
5469 prj_run Synthesis $force_rst
5470 if {[prj_syn] == "synplify"} {
5471 prj_run Translate $force_rst
5474 Msg Error "Impossible condition. You need to run this in an IDE."
5485 proc LaunchVitisBuild {project_name {repo_path .} {stage "presynth"}} {
5486 set proj_name $project_name
5487 set bin_dir [
file normalize "$repo_path/bin"]
5491 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
5492 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$proj_name] $repo_path] commit version hog_hash hog_ver top_hash top_ver \
5493 libs hashes vers cons_ver cons_hash ext_names ext_hashes xml_hash xml_ver user_ip_repos user_ip_hashes user_ip_vers
5495 if {$commit == 0 } {
set commit $this_commit}
5499 foreach app_name [dict keys $ws_apps] {
5500 app config -name $app_name -set build-config Release
5503 WriteGenerics "vitisbuild" $repo_path $proj_name $date $timee $commit $version $top_hash $top_ver $hog_hash $hog_ver $cons_ver $cons_hash $libs \
5504 $vers $hashes $ext_names $ext_hashes $user_ip_repos $user_ip_vers $user_ip_hashes $flavour $xml_ver $xml_hash
5505 foreach app_name [dict keys $ws_apps] { app build -name $app_name}
5507 if {$stage == "presynth"} {
5508 Msg Info "Done building apps for $project_name..."
5512 Msg Info "Evaluating Git sha for $project_name..."
5513 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
5516 Msg Info "Hog describe set to: $describe"
5517 set dst_dir [
file normalize "$bin_dir/$proj_name\-$describe"]
5518 if {![
file exists $dst_dir]} {
5519 Msg Info "Creating $dst_dir..."
5523 foreach app_name [dict keys $ws_apps] {
5524 set main_file "$repo_path/Projects/$project_name/vitis_classic/$app_name/Release/$app_name.elf"
5525 set dst_main [
file normalize "$dst_dir/[
file tail $proj_name]\-$app_name\-$describe.elf"]
5527 if {![
file exists $main_file]} {
5528 Msg Error "No Vitis .elf file found. Perhaps there was an issue building it?"
5532 Msg Info "Copying main binary file $main_file into $dst_main..."
5533 file copy -force $main_file $dst_main
5542 proc GetProcFromProps {repo_path props platform} {
5543 if {[dict exists $props "platform:$platform" "BIF"]} {
5544 set bif_file [dict get $props "platform:$platform" "BIF"]
5546 set bif_file "$repo_path/$bif_file"
5550 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
5560 proc GetBifFromProps {repo_path props platform} {
5561 if {[dict exists $props "platform:$platform" "BIF"]} {
5562 set bif_file [dict get $props "platform:$platform" "BIF"]
5564 set bif_file "$repo_path/$bif_file"
5568 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
5577 proc GetPartFromProps {props} {
5578 if {[dict exists $props "main" "PART"]} {
5579 return [
string tolower [dict get $props "main" "PART"]]
5581 Msg Error "Part number not found in properties"
5590 proc GetArchFromPart {part} {
5592 if {[
string match "xczu*" $part]} {
5594 }
elseif {[
string match "xc7z*" $part]} {
5596 }
elseif {[
string match "xck26*" $part]} {
5599 Msg CriticalWarning "Unknown part number: $part"
5608 proc GetAppsFromProps {props {list_names 0}} {
5609 set prop_apps [dict filter $props key {app:*}]
5610 set apps [dict create]
5611 set app_names [list]
5613 dict for {app_key app_value} $prop_apps {
5614 if {[regexp {^app:(.+)$} $app_key -> app_name]} {
5615 set app_name [string trim [string tolower $app_name]]
5616 # Convert only the keys of the inner dictionary to lowercase
5617 set app_value_lower [dict create]
5618 dict for {key value} $app_value {
5619 dict set app_value_lower [string tolower $key] $value
5621 dict set apps $app_name $app_value_lower
5622 lappend app_names $app_name
5625 if {$list_names eq 1} {
5636 proc GetPlatformsFromProps {props {list_names 0} {lower_case 0}} {
5637 set platforms [dict create]
5638 set platform_names [list]
5639 set prop_platforms [dict filter $props key {platform:*}]
5641 dict for {platform_key platform_value} $prop_platforms {
5642 if {[regexp {^platform:(.+)$} $platform_key -> platform_name]} {
5643 if {$lower_case == 1} {
5644 set platform_name [string trim [string tolower $platform_name]]
5646 set platform_name [string trim $platform_name]
5648 dict set platforms $platform_name $platform_value
5649 lappend platform_names $platform_name
5652 if {$list_names eq 1} {
5653 return $platform_names
5668 proc GenerateBootArtifacts {properties repo_path proj_dir bin_dir proj_name describe bitfile mmi_file} {
5669 set elf_list [glob -nocomplain "$bin_dir/*.elf"]
5673 if {[
llength $elf_list] == 0} {
5674 Msg Warning "No ELF files found in $bin_dir, skipping generation of boot artifacts"
5678 if {![
file exists $bitfile]} {
5679 Msg Warning "Bitfile $bitfile does not exist, skipping generation of boot artifacts"
5683 Msg Info "Generating boot artifacts for $proj_name..."
5684 Msg Info "Found apps: $apps"
5685 Msg Info "Found platforms: $platforms"
5689 foreach elf_file $elf_list {
5690 set elf_name [
file rootname [
file tail $elf_file]]
5691 Msg Info "Found elf name: $elf_name"
5692 Msg Info "Removing $describe from elf"
5695 if {[regexp "^(.+)-(.+)-$describe\$" $elf_name -> project_name elf_app]} {
5696 set elf_app [
string trim [
string tolower $elf_app]]
5697 Msg Info "Found elf_app: $elf_app"
5699 Msg Error "Could not extract app name from elf file: $elf_name"
5702 Msg Info "Removed project name ($project_name) and $describe from elf"
5704 set app_conf [dict get $apps $elf_app]
5705 set plat [dict get $app_conf "platform"]
5706 set app_proc [dict get $app_conf "proc"]
5709 if {[regexp -nocase {microblaze|risc} $app_proc]} {
5710 Msg Info "Detected soft processor ($app_proc) for $elf_app, updating bitstream memory with ELF file..."
5713 if {[dict size $proc_map] == 0} {
5714 Msg Error "Failed to read map from $proc_map_file"
5717 Msg Info "Found processor map: $proc_map"
5719 set proc_cell [
lindex [
split [dict get $proc_map $app_proc] ":"] 1]
5720 Msg Info "Updating memory at processor cell: $proc_cell"
5722 set update_mem_cmd "updatemem -force -meminfo $mmi_file -data $elf_file -bit $bitfile -proc $proc_cell -out $bitfile"
5723 set ret [
catch {
exec -ignorestderr {*}$update_mem_cmd >@ stdout} result]
5725 Msg Error "Error updating memory for $elf_app: $result"
5727 Msg Info "Done updating memory for $elf_app"
5730 Msg Info "Detected hard processor ($app_proc) for $elf_app. Make sure the .elf file is defined in the platform ($plat)\
5731 .bif file to be included in the bootable binary image (.bin) generation."
5737 foreach plat $platforms {
5739 if {$bif_file != ""} {
5740 Msg Info "Generating bootable binary image (.bin) for $plat"
5742 Msg Info "Architecture: $arch"
5743 Msg Info "BIF file: $bif_file"
5744 set bootgen_cmd "bootgen -arch $arch -image $bif_file -o i $bin_dir/$proj_name-$plat-$describe.bin -w on"
5745 set ret [
catch {
exec -ignorestderr {*}$bootgen_cmd >@ stdout} result]
5747 Msg Error "Error generating bootable binary image (.bin) for $elf_app: $result"
5749 Msg Info "Done generating bootable binary image (.bin) for $plat"
5758 proc ReadProcMap {proc_map_file} {
5759 set proc_map [dict create]
5760 if {[
file exists $proc_map_file]} {
5761 set f [open $proc_map_file "r"]
5762 while {[
gets $f line] >= 0} {
5763 Msg Debug "Line: $line"
5764 if {[regexp {^(\S+)\s+(.+)$} $line -> key value]} {
5765 Msg Debug "Found key: $key, value: $value in proc map file"
5766 dict set proc_map $key $value
5780 proc ListProjects {{repo_path .} {print 1} {ret_conf 0}} {
5781 set top_path [
file normalize $repo_path/Top]
5782 set confs [
findFiles [
file normalize $top_path] hog.conf]
5784 set confs [lsort $confs]
5788 set p [
Relative $top_path [
file dirname $c]]
5791 if {$description eq "test"} {
5792 set description " - Test project"
5793 }
elseif {$description ne ""} {
5794 set description " - $description"
5797 if {$print == 1 || $description ne " - Test project"} {
5799 set g [
file dirname $p]
5810 if {$ret_conf == 0} {
5822 proc Md5Sum {file_name} {
5823 if {!([
file exists $file_name])} {
5824 Msg Warning "Could not find $file_name."
5827 if {[
catch {
package require md5 2.0.7} result]} {
5828 Msg Warning "Tcl package md5 version 2.0.7 not found ($result), will use command line..."
5829 set hash [
lindex [
Execute md5sum $file_name] 0]
5831 set file_hash [
string tolower [md5::md5 -hex -file $file_name]]
5845 proc MergeDict {dict0 dict1 {remove_duplicates 1}} {
5846 set outdict [dict merge $dict1 $dict0]
5847 foreach key [dict keys $dict1] {
5848 if {[dict exists $dict0 $key]} {
5849 set temp_list [dict get $dict1 $key]
5850 foreach item $temp_list {
5852 if {[
IsInList $item [
DictGet $outdict $key]] == 0 || $remove_duplicates == 0} {
5854 dict lappend outdict $key $item
5866 proc MoveElementToEnd {inputList element} {
5867 set index [lsearch $inputList $element]
5869 set inputList [
lreplace $inputList $index $index]
5870 lappend inputList $element
5879 proc OpenProject {project_file repo_path} {
5881 open_project $project_file
5883 set project_folder [
file dirname $project_file]
5884 set project [
file tail [
file rootname $project_file]]
5885 if {[
file exists $project_folder]} {
5887 if {![is_project_open]} {
5888 Msg Info "Opening existing project file $project_file..."
5889 project_open $project -current_revision
5892 Msg Error "Project directory not found for $project_file."
5896 Msg Info "Opening existing project file $project_file..."
5898 open_project -file $project_file -do_backup_on_convert 1 -backup_file {./Projects/$project_file.zip}
5900 Msg Info "Opening existing project file $project_file..."
5901 prj_project open $project_file
5903 Msg Error "This IDE is currently not supported by Hog. Exiting!"
5910 return $tcl_platform(platform)
5920 proc ParseJSON {JSON_FILE JSON_KEY} {
5921 set result [
catch {
package require Tcl 8.4} TclFound]
5922 if {"$result" != "0"} {
5923 Msg CriticalWarning "Cannot find Tcl package version equal or higher than 8.4.\n $TclFound\n Exiting"
5927 set result [
catch {
package require json} JsonFound]
5928 if {"$result" != "0"} {
5929 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
5932 set JsonDict [json::json2dict $JSON_FILE]
5933 set result [
catch {dict get $JsonDict $JSON_KEY} RETURNVALUE]
5934 if {"$result" != "0"} {
5935 Msg CriticalWarning "Cannot find $JSON_KEY in $JSON_FILE\n Exiting"
5948 proc ProjectExists {project {repo_path .}} {
5949 set index [lsearch -exact [
ListProjects $repo_path 0] $project]
5955 Msg Error "Project $project not found in repository $repo_path"
5966 proc ReadConf {file_name} {
5967 if {[
catch {
package require inifile 0.2.3} ERROR]} {
5968 Msg Error "Could not find inifile package version 0.2.3 or higher.\n
5969 To use ghdl, libero or diamond with Hog, you need to install the tcllib package\n
5970 You can install it with 'sudo apt install tcllib' on Debian/Ubuntu or 'sudo dnf install tcllib' on Fedora/RedHat/CentOs."
5974 ::ini::commentchar "#"
5975 set f [::ini::open $file_name]
5976 set properties [dict create]
5977 foreach sec [::ini::sections $f] {
5979 if {$new_sec == "files"} {
5982 set key_pairs [::ini::get $f $sec]
5984 regsub -all {\{\"} $key_pairs "\{" key_pairs
5985 regsub -all {\"\}} $key_pairs "\}" key_pairs
5987 dict set properties $new_sec [dict create {*}$key_pairs]
6000 proc ReadExtraFileList {extra_file_name} {
6001 set extra_file_dict [dict create]
6002 if {[
file exists $extra_file_name]} {
6003 set file [open $extra_file_name "r"]
6004 set file_data [read $file]
6007 set data [
split $file_data "\n"]
6008 foreach line $data {
6009 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
6010 set ip_and_md5 [regexp -all -inline {\S+} $line]
6011 dict lappend extra_file_dict "[
lindex $ip_and_md5 0]" "[
lindex $ip_and_md5 1]"
6015 return $extra_file_dict
6035 proc ReadListFile {args} {
6038 if {[
catch {
package require cmdline} ERROR]} {
6039 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
6045 {lib.arg "" "The name of the library files will be added to, if not given will be extracted from the file name."}
6046 {fileset.arg "" "The name of the library, from the main list file"}
6047 {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."}
6048 {print_log "If set, will use PrintFileTree for the VIEW directive"}
6049 {indent.arg "" "Used to indent files with the VIEW directive"}
6052 set usage "USAGE: ReadListFile \[options\] <list file> <path>"
6053 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
6059 set list_file [
lindex $args 0]
6060 set path [
lindex $args 1]
6061 set sha_mode $options(sha_mode)
6062 set lib $options(lib)
6063 set fileset $options(fileset)
6064 set print_log $options(print_log)
6065 set indent $options(indent)
6067 if {$sha_mode == 1} {
6068 set sha_mode_opt "-sha_mode"
6073 if {$print_log == 1} {
6074 set print_log_opt "-print_log"
6076 set print_log_opt ""
6081 set lib [
file rootname [
file tail $list_file]]
6083 set fp [open $list_file r]
6084 set file_data [read $fp]
6086 set list_file_ext [
file extension $list_file]
6087 switch $list_file_ext {
6089 if {$fileset eq ""} {
6095 set fileset "constrs_1"
6098 set fileset "sources_1"
6102 set libraries [dict create]
6103 set filesets [dict create]
6104 set properties [dict create]
6106 set data [
split $file_data "\n"]
6108 set n [
llength $data]
6110 if {$print_log == 1} {
6111 if {$indent eq ""} {
6112 set list_file_rel [
file tail $list_file]
6113 Msg Status "\n$list_file_rel"
6117 Msg Debug "$n lines read from $list_file."
6120 foreach line $data {
6122 if {![regexp {^[\t\s]*$} $line] & ![regexp {^[\t\s]*\#} $line]} {
6123 set file_and_prop [regexp -all -inline {\S+} $line]
6124 set srcfile [
lindex $file_and_prop 0]
6125 set srcfile "$path/$srcfile"
6127 set srcfiles [glob -nocomplain $srcfile]
6130 if {$srcfiles != $srcfile && ![
string equal $srcfiles ""]} {
6131 Msg Debug "Wildcard source expanded from $srcfile to $srcfiles"
6133 if {![
file exists $srcfile]} {
6134 if {$print_log == 0} {
6135 Msg CriticalWarning "File: $srcfile (from list file: $list_file) does not exist."
6141 foreach vhdlfile $srcfiles {
6142 if {[
file exists $vhdlfile]} {
6143 set vhdlfile [
file normalize $vhdlfile]
6144 set extension [
file extension $vhdlfile]
6146 set prop [
lrange $file_and_prop 1 end]
6149 set library [
lindex [regexp -inline {\ylib\s*=\s*(.+?)\y.*} $prop] 1]
6150 if {$library == ""} {
6154 if {$extension == $list_file_ext} {
6157 set ref_path [
lindex [regexp -inline {\ypath\s*=\s*(\S+).*} $prop] 1]
6158 if {$ref_path eq ""} {
6161 set ref_path [
file normalize $path/$ref_path]
6163 Msg Debug "List file $vhdlfile found in list file, recursively opening it using path \"$ref_path\"..."
6164 if {$print_log == 1} {
6165 if {[
file normalize $last_printed] ne [
file normalize $vhdlfile]} {
6166 Msg Status "$indent Inside [
file tail $vhdlfile]:"
6170 lassign [
ReadListFile {*}"-indent \" $indent\" -lib $library -fileset $fileset $sha_mode_opt $print_log_opt $vhdlfile $ref_path"] l p fs
6172 set properties [
MergeDict $p $properties]
6174 }
elseif {[lsearch {.src .sim .con ReadExtraFileList} $extension] >= 0} {
6176 Msg Error "$vhdlfile cannot be included into $list_file, $extension files must be included into $extension files."
6179 regsub -all " *= *" $prop "=" prop
6183 if {[
string first "lib=" $p] == -1} {
6185 set pos [
string first "=" $p]
6189 set prop_name [
string range $p 0 [
expr {$pos - 1}]]
6192 dict lappend properties $vhdlfile $p
6193 Msg Debug "Adding property $p to $vhdlfile..."
6194 }
elseif {$list_file_ext != ".ipb"} {
6195 Msg Warning "Setting Property $p is not supported for file $vhdlfile or it is already its default. \
6200 if {[lsearch {.xci .ip .bd .xcix} $extension] >= 0} {
6202 set lib_name "ips.src"
6203 }
elseif {[
IsInList $extension {.vhd .vhdl}] || $list_file_ext == ".sim"} {
6205 if {![
IsInList $extension {.vhd .vhdl}]} {
6206 set lib_name "others.sim"
6208 set lib_name "$library$list_file_ext"
6210 }
elseif {$list_file_ext == ".con"} {
6211 set lib_name "sources.con"
6212 }
elseif {$list_file_ext == ".ipb"} {
6213 set lib_name "xml.ipb"
6214 }
elseif { [
IsInList $list_file_ext {.src}] && [
IsInList $extension {.c .cpp .h .hpp}] } {
6216 set lib_name "$library$list_file_ext"
6219 set lib_name "others.src"
6222 Msg Debug "Appending $vhdlfile to $lib_name list..."
6223 dict lappend libraries $lib_name $vhdlfile
6224 if {$sha_mode != 0 && [
file type $vhdlfile] eq "link"} {
6227 dict lappend libraries $lib_name $real_file
6228 Msg Debug "File $vhdlfile is a soft link, also adding the real file: $real_file"
6233 if {[dict exists $filesets $fileset] == 0} {
6235 Msg Debug "Adding $fileset to the fileset dictionary..."
6236 Msg Debug "Adding library $lib_name to fileset $fileset..."
6237 dict set filesets $fileset $lib_name
6241 Msg Debug "Adding library $lib_name to fileset $fileset..."
6242 dict lappend filesets $fileset $lib_name
6248 Msg CriticalWarning "File $vhdlfile not found."
6254 if {$sha_mode != 0} {
6256 if {$list_file_ext eq ".ipb"} {
6257 set sha_lib "xml.ipb"
6259 set sha_lib $lib$list_file_ext
6261 dict lappend libraries $sha_lib [
file normalize $list_file]
6262 if {[
file type $list_file] eq "link"} {
6265 dict lappend libraries $lib$list_file_ext $real_file
6266 Msg Debug "List file $list_file is a soft link, also adding the real file: $real_file"
6269 return [list $libraries $properties $filesets]
6278 proc Relative {base dst {quiet 0}} {
6279 if {![
string equal [
file pathtype $base] [
file pathtype $dst]]} {
6281 Msg CriticalWarning "Unable to compute relation for paths of different path types: [
file pathtype $base] vs. [
file pathtype $dst], ($base vs. $dst)"
6286 set base [
file normalize [
file join [
pwd] $base]]
6287 set dst [
file normalize [
file join [
pwd] $dst]]
6290 set base [
file split $base]
6291 set dst [
file split $dst]
6293 while {[
string equal [
lindex $dst 0] [
lindex $base 0]]} {
6294 set dst [
lrange $dst 1 end]
6295 set base [
lrange $base 1 end]
6296 if {![
llength $dst]} {break}
6299 set dstlen [
llength $dst]
6300 set baselen [
llength $base]
6302 if {($dstlen == 0) && ($baselen == 0)} {
6305 while {$baselen > 0} {
6306 set dst [
linsert $dst 0 ..]
6309 set dst [
eval [
linsert $dst 0 file join]]
6320 proc RelativeLocal {pathName filePath} {
6321 if {[
string first [
file normalize $pathName] [
file normalize $filePath]] != -1} {
6322 return [
Relative $pathName $filePath]
6333 proc RemoveDuplicates {mydict} {
6334 set new_dict [dict create]
6335 foreach key [dict keys $mydict] {
6336 set values [
DictGet $mydict $key]
6337 foreach value $values {
6338 set idxs [
lreverse [
lreplace [lsearch -exact -all $values $value] 0 0]]
6340 set values [
lreplace $values $idx $idx]
6343 dict set new_dict $key $values
6354 proc ResetRepoFiles {reset_file} {
6355 if {[
file exists $reset_file]} {
6356 Msg Info "Found $reset_file, opening it..."
6357 set fp [open $reset_file r]
6358 set wild_cards [lsearch -all -inline -not -regexp [
split [read $fp] "\n"] "^ *$"]
6360 Msg Info "Found the following files/wild cards to restore if modified: $wild_cards..."
6361 foreach w $wild_cards {
6363 if {[
llength $mod_files] > 0} {
6364 Msg Info "Found modified $w files: $mod_files, will restore them..."
6367 Msg Info "No modified $w files found."
6378 proc RestoreModifiedFiles {{repo_path "."} {pattern "."}} {
6381 set ret [
Git checkout $pattern]
6392 proc SearchHogProjects {dir} {
6393 set projects_list {}
6394 if {[
file exists $dir]} {
6395 if {[
file isdirectory $dir]} {
6396 foreach proj_dir [glob -nocomplain -types d $dir/*] {
6397 if {![regexp {^.*Top/+(.*)$} $proj_dir dummy proj_name]} {
6398 Msg Warning "Could not parse Top directory $dir"
6401 if {[
file exists "$proj_dir/hog.conf"]} {
6402 lappend projects_list $proj_name
6405 lappend projects_list $p
6410 Msg Error "Input $dir is not a directory!"
6413 Msg Error "Directory $dir doesn't exist!"
6415 return $projects_list
6424 proc SetGenericsSimulation {repo_path proj_dir target} {
6425 set top_dir "$repo_path/Top/$proj_dir"
6426 set simsets [get_filesets]
6427 if {$simsets != ""} {
6428 foreach simset $simsets {
6430 if {[get_property FILESET_TYPE $simset] != "SimulationSrcs"} {
6434 set merged_generics_dict [dict create]
6438 set simset_generics [
DictGet $simset_dict "generics"]
6439 set merged_generics_dict [
MergeDict $merged_generics_dict $simset_generics 0]
6441 set_property generic $generic_str [get_filesets $simset]
6442 Msg Debug "Setting generics $generic_str for simulator $target\
6443 and simulation file-set $simset..."
6455 proc SetTopProperty {top_module fileset} {
6456 Msg Info "Setting TOP property to $top_module module"
6459 set_property "top" $top_module [get_filesets $fileset]
6462 set_global_assignment -name TOP_LEVEL_ENTITY $top_module
6464 set_root -module $top_module
6466 prj_impl option top $top_module
6471 proc VIVADO_PATH_PROPERTIES {} {
6472 return {"\.*\.TCL\.PRE$" "^.*\.TCL\.POST$" "^RQS_FILES$" "^INCREMENTAL\_CHECKPOINT$" "NOC\_SOLUTION\_FILE"}
6476 proc VITIS_PATH_PROPERTIES {} {
6477 return {"^HW$" "^XPFM$" "^LINKER-SCRIPT$" "^LIBRARIES$" "^LIBRARY-SEARCH-PATH$"}
6487 proc WriteConf {file_name config {comment ""}} {
6488 if {[
catch {
package require inifile 0.2.3} ERROR]} {
6489 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
6493 ::ini::commentchar "#"
6494 set f [::ini::open $file_name w]
6496 foreach sec [dict keys $config] {
6497 set section [dict get $config $sec]
6498 dict for {p v} $section {
6499 if {[string trim $v] == ""} {
6500 Msg Warning "Property $p has empty value. Skipping..."
6503 ::ini::set $f $sec $p $v
6508 if {![
string equal "$comment" ""]} {
6509 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $comment
6510 set hog_header "Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
6511 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $hog_header
6526 proc WriteGenerics {mode repo_path design date timee\
6527 commit version top_hash top_ver hog_hash hog_ver \
6528 cons_ver cons_hash libs vers hashes ext_names ext_hashes \
6529 user_ip_repos user_ip_vers user_ip_hashes flavour {xml_ver ""} {xml_hash ""}} {
6530 Msg Info "Passing parameters/generics to project's top module..."
6533 set generic_string [
concat \
6545 if {$xml_hash != "" && $xml_ver != ""} {
6546 lappend generic_string \
6551 foreach l $libs v $vers h $hashes {
6557 set ver [regsub -all {[\.-]} $ver {_}]
6558 set hash [regsub -all {[\.-]} $hash {_}]
6559 lappend generic_string "$ver" "$hash"
6562 foreach e $ext_names h $ext_hashes {
6564 lappend generic_string "$hash"
6567 foreach repo $user_ip_repos v $user_ip_vers h $user_ip_hashes {
6568 set repo_name [
file tail $repo]
6569 set ver "[
string toupper $repo_name]_VER=[
FormatGeneric $v]"
6570 set hash "[
string toupper $repo_name]_SHA=[
FormatGeneric $h]"
6571 set ver [regsub -all {[\.-]} $ver {_}]
6572 set hash [regsub -all {[\.-]} $hash {_}]
6573 lappend generic_string "$ver" "$hash"
6576 if {$flavour != -1} {
6577 lappend generic_string "FLAVOUR=$flavour"
6583 set generic_string "$prj_generics $generic_string"
6589 if {$mode == "create" || [
IsISE]} {
6592 if {[
file exists $top_file]} {
6595 Msg Debug "Found top level generics $generics in $top_file"
6597 set filtered_generic_string ""
6599 foreach generic_to_set [
split [
string trim $generic_string]] {
6600 set key [
lindex [
split $generic_to_set "="] 0]
6601 if {[dict exists $generics $key]} {
6602 Msg Debug "Hog generic $key found in $top_name"
6603 lappend filtered_generic_string "$generic_to_set"
6605 Msg Warning "Generic $key is passed by Hog but is NOT present in $top_name."
6611 set generic_string $filtered_generic_string
6616 set_property generic $generic_string [current_fileset]
6617 Msg Info "Setting parameters/generics..."
6618 Msg Debug "Detailed parameters/generics: $generic_string"
6623 set simulator [get_property target_simulator [current_project]]
6624 if {$mode == "create"} {
6631 Msg Info "Setting Synplify parameters/generics one by one..."
6632 foreach generic $generic_string {
6633 Msg Debug "Setting Synplify generic: $generic"
6634 set_option -hdl_param -set "$generic"
6637 Msg Info "Setting Diamond parameters/generics one by one..."
6638 prj_impl option -impl Implementation0 HDL_PARAM "$generic_string"
6640 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
6642 foreach app_name [dict keys $ws_apps] {
6643 set defined_symbols [app config -name $app_name -get define-compiler-symbols]
6644 foreach generic_to_set [
split [
string trim $generic_string]] {
6645 set key [
lindex [
split $generic_to_set "="] 0]
6646 set value [
lindex [
split $generic_to_set "="] 1]
6647 if {[
string match "32'h*" $value]} {
6648 set value [
string map {"32'h" "0x"} $value]
6651 foreach symbol [
split $defined_symbols ";"] {
6652 if {[
string match "$key=*" $symbol]} {
6653 Msg Debug "Generic $key found in $app_name, removing it..."
6654 app config -name $app_name -remove define-compiler-symbols "$symbol"
6658 Msg Info "Setting Vitis parameters/generics for app $app_name: $key=$value"
6659 app config -name $app_name define-compiler-symbols "$key=$value"
6671 proc WriteGenericsToBdIPs {mode repo_path proj generic_string} {
6672 Msg Debug "Parameters/generics passed to WriteGenericsToIP: $generic_string"
6674 set bd_ip_generics false
6676 if {[dict exists $properties "hog"]} {
6677 set propDict [dict get $properties "hog"]
6678 if {[dict exists $propDict "PASS_GENERICS_TO_BD_IPS"]} {
6679 set bd_ip_generics [dict get $propDict "PASS_GENERICS_TO_BD_IPS"]
6683 if {[
string compare [
string tolower $bd_ip_generics] "false"] == 0} {
6687 if {$mode == "synth"} {
6688 Msg Info "Attempting to apply generics pre-synthesis..."
6689 set PARENT_PRJ [get_property "PARENT.PROJECT_PATH" [current_project]]
6690 set workaround [open "$repo_path/Projects/$proj/.hog/presynth_workaround.tcl" "w"]
6691 puts $workaround "source \[lindex \$argv 0\];"
6692 puts $workaround "open_project \[lindex \$argv 1\];"
6693 puts $workaround "WriteGenericsToBdIPs \[lindex \$argv 2\] \[lindex \$argv 3\] \[lindex \$argv 4\] \[lindex \$argv 5\];"
6694 puts $workaround "close_project"
6698 exec vivado -mode batch -source $repo_path/Projects/$proj/.hog/presynth_workaround.tcl \
6699 -tclargs $repo_path/Hog/Tcl/hog.tcl $PARENT_PRJ \
6700 "childprocess" $repo_path $proj $generic_string
6703 Msg Error "Encountered an error while attempting workaround: $errMsg"
6705 file delete $repo_path/Projects/$proj/.hog/presynth_workaround.tcl
6707 Msg Info "Done applying generics pre-synthesis."
6711 Msg Info "Looking for IPs to add generics to..."
6712 set ips_generic_string ""
6713 foreach generic_to_set [
split [
string trim $generic_string]] {
6714 set key [
lindex [
split $generic_to_set "="] 0]
6715 set value [
lindex [
split $generic_to_set "="] 1]
6716 append ips_generic_string "CONFIG.$key $value "
6720 if {[
string compare [
string tolower $bd_ip_generics] "true"] == 0} {
6723 set ip_regex $bd_ip_generics
6726 set ip_list [get_ips -regex $ip_regex]
6727 Msg Debug "IPs found with regex \{$ip_regex\}: $ip_list"
6729 set regen_targets {}
6731 foreach {ip} $ip_list {
6732 set WARN_ABOUT_IP false
6733 set ip_props [list_property [get_ips $ip]]
6736 if {[lsearch -exact $ip_props "IS_BD_CONTEXT"] == -1} {
6740 if {[get_property "IS_BD_CONTEXT" [get_ips $ip]] eq "1"} {
6741 foreach {ip_prop} $ip_props {
6742 if {[dict exists $ips_generic_string $ip_prop]} {
6743 if {$WARN_ABOUT_IP == false} {
6744 lappend regen_targets [get_property SCOPE [get_ips $ip]]
6745 Msg Warning "The ip \{$ip\} contains generics that are set by Hog.\
6746 If this is IP is apart of a block design, the .bd file may contain stale, unused, values.\
6747 Hog will always apply the most up-to-date values to the IP during synthesis,\
6748 however these values may or may not be reflected in the .bd file."
6749 set WARN_ABOUT_IP true
6754 set xci_path [get_property IP_FILE [get_ips $ip]]
6756 if {[
string equal $generic_format "ERROR"]} {
6757 Msg Warning "Could not find format for generic $ip_prop in IP $ip. Skipping..."
6761 set value_to_set [dict get $ips_generic_string $ip_prop]
6762 switch -exact $generic_format {
6764 if {[
string match "32'h*" $value_to_set]} {
6765 scan [
string map {"32'h" ""} $value_to_set] "%x" value_to_set
6769 set value_to_set [
expr {$value_to_set ? "true" : "false"}]
6772 if {[
string match "32'h*" $value_to_set]} {
6773 binary scan [
binary format H* [
string map {"32'h" ""} $value_to_set]] d value_to_set
6777 if {[
string match "32'h*" $value_to_set]} {
6778 set value_to_set [
string map {"32'h" "0x"} $value_to_set]
6782 set value_to_set [
format "%s" $value_to_set]
6785 Msg Warning "Unknown generic format $generic_format for IP $ip. Will attempt to pass as string..."
6790 Msg Info "The IP \{$ip\} contains: $ip_prop ($generic_format), setting it to $value_to_set."
6791 if {[
catch {set_property -name $ip_prop -value $value_to_set -objects [get_ips $ip]} prop_error]} {
6792 Msg CriticalWarning "Failed to set property $ip_prop to $value_to_set for IP \{$ip\}: $prop_error"
6799 foreach {regen_target} [lsort -unique $regen_targets] {
6800 Msg Info "Regenerating target: $regen_target"
6801 if {[
catch {generate_target -force all [get_files $regen_target]} prop_error]} {
6802 Msg CriticalWarning "Failed to regen targets: $prop_error"
6810 proc GetGenericFormatFromXciXML {generic_name xml_file} {
6811 if {![
file exists $xml_file]} {
6812 Msg Error "Could not find XML file: $xml_file"
6816 set fp [open $xml_file r]
6817 set xci_data [read $fp]
6820 set paramType "string"
6821 set modelparam_regex [
format {^.*\y%s\y.*$} [
string map {"CONFIG." "MODELPARAM_VALUE."} $generic_name]]
6822 set format_regex {format="([^"]+)"}
6824 set line [
lindex [regexp -inline -line $modelparam_regex $xci_data] 0]
6825 Msg Debug "line: $line"
6827 if {[regexp $format_regex $line match format_value]} {
6828 Msg Debug "Extracted: $format_value format from xml"
6829 set paramType $format_value
6831 Msg Debug "No format found, using string"
6840 proc GetGenericFormatFromXci {generic_name xci_file} {
6841 if {![
file exists $xci_file]} {
6842 Msg Error "Could not find XCI file: $xci_file"
6846 set fp [open $xci_file r]
6847 set xci_data [read $fp]
6850 set paramType "string"
6851 if {[
string first "xilinx.com:schema:json_instance:1.0" $xci_data] == -1} {
6852 Msg Debug "XCI format is not JSON, trying XML..."
6853 set xml_file "[
file rootname $xci_file].xml"
6857 set generic_name [
string map {"CONFIG." ""} $generic_name]
6858 set ip_inst [
ParseJSON $xci_data "ip_inst"]
6859 set parameters [dict get $ip_inst parameters]
6860 set component_parameters [dict get $parameters component_parameters]
6861 if {[dict exists $component_parameters $generic_name]} {
6862 set generic_info [dict get $component_parameters $generic_name]
6863 if {[dict exists [
lindex $generic_info 0] format]} {
6864 set paramType [dict get [
lindex $generic_info 0] format]
6865 Msg Debug "Extracted: $paramType format from xci"
6868 Msg Debug "No format found, using string"
6881 proc WriteGitLabCIYAML {proj_name {ci_conf ""}} {
6882 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
6883 Msg CriticalWarning "Cannot find package YAML.\n Error message: $YAMLPACKAGE. \
6884 If you are running on tclsh, you can fix this by installing package \"tcllib\""
6889 if {$ci_conf != ""} {
6891 foreach sec [dict keys $ci_confs] {
6892 if {[
string first : $sec] == -1} {
6893 lappend job_list $sec
6897 set job_list {"generate_project" "simulate_project"}
6901 set out_yaml [huddle create]
6902 foreach job $job_list {
6904 set huddle_tags [huddle list]
6906 set sec_dict [dict create]
6908 if {$ci_confs != ""} {
6909 foreach var [dict keys [dict get $ci_confs $job]] {
6910 if {$var == "tags"} {
6911 set tag_section "tags"
6912 set tags [dict get [dict get $ci_confs $job] $var]
6913 set tags [
split $tags ","]
6915 set tag_list [huddle list $tag]
6916 set huddle_tags [huddle combine $huddle_tags $tag_list]
6919 dict set sec_dict $var [dict get [dict get $ci_confs $job] $var]
6925 set huddle_variables [huddle create "PROJECT_NAME" $proj_name "extends" ".vars"]
6926 if {[dict exists $ci_confs "$job:variables"]} {
6927 set var_dict [dict get $ci_confs $job:variables]
6928 foreach var [dict keys $var_dict] {
6930 set value [dict get $var_dict "$var"]
6931 set var_inner [huddle create "$var" "$value"]
6932 set huddle_variables [huddle combine $huddle_variables $var_inner]
6937 set middle [huddle create "extends" ".$job" "variables" $huddle_variables]
6938 foreach sec [dict keys $sec_dict] {
6939 set value [dict get $sec_dict $sec]
6940 set var_inner [huddle create "$sec" "$value"]
6941 set middle [huddle combine $middle $var_inner]
6943 if {$tag_section != ""} {
6944 set middle2 [huddle create "$tag_section" $huddle_tags]
6945 set middle [huddle combine $middle $middle2]
6948 set outer [huddle create "$job:$proj_name" $middle]
6949 set out_yaml [huddle combine $out_yaml $outer]
6952 return [
string trimleft [yaml::huddle2yaml $out_yaml] "-"]
6962 proc WriteListFiles {libs props list_path repo_path {ext_path ""}} {
6964 foreach lib [dict keys $libs] {
6965 if {[
llength [
DictGet $libs $lib]] > 0} {
6966 set list_file_name $list_path$lib
6967 set list_file [open $list_file_name w]
6968 Msg Info "Writing $list_file_name..."
6969 puts $list_file "#Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
6970 foreach file [
DictGet $libs $lib] {
6972 set prop [
DictGet $props $file]
6976 puts $list_file "$file_path $prop"
6979 set ext_list_file [open "[
file rootname $list_file].ext" a]
6980 puts $ext_list_file "$file_path $prop"
6981 close $ext_list_file
6984 Msg Warning "The path of file $file is not relative to your repository. Please check!"
7000 proc WriteSimListFile {simset libs props simsets list_path repo_path {force 0}} {
7002 set list_file_name $list_path/${simset}.sim
7003 if {$force == 0 && [
file exists $list_file_name]} {
7004 Msg Info "List file $list_file_name already exists, skipping..."
7008 set list_file [open $list_file_name a+]
7011 puts $list_file "\[files\]"
7012 Msg Info "Writing $list_file_name..."
7013 foreach lib [
DictGet $simsets $simset] {
7014 foreach file [
DictGet $libs $lib] {
7016 set prop [
DictGet $props $file]
7020 set lib_name [
file rootname $lib]
7021 if {$lib_name != $simset && [
file extension $file] == ".vhd" && [
file extension $file] == ""} {
7022 lappend prop "lib=$lib_name"
7024 puts $list_file "$file_path $prop"
7027 Msg Warning "The path of file $file is not relative to your repository. Please check!"
7039 proc WriteToFile {File msg} {
7040 set f [open $File a+]
7051 proc WriteUtilizationSummary {input output project_name run} {
7052 set f [open $input "r"]
7053 set o [open $output "a"]
7054 puts $o "## $project_name $run Utilization report\n\n"
7055 struct::matrix util_m
7056 util_m add columns 14
7059 util_m add row "| **Site Type** | **Used** | **Fixed** | **Prohibited** | **Available** | **Util%** |"
7060 util_m add row "| --- | --- | --- | --- | --- | --- |"
7062 util_m add row "| **Site Type** | **Used** | **Fixed** | **Available** | **Util%** |"
7063 util_m add row "| --- | --- | --- | --- | --- |"
7073 while {[
gets $f line] >= 0} {
7074 if {([
string first "| CLB LUTs" $line] >= 0 || [
string first "| Slice LUTs" $line] >= 0) && $luts == 0} {
7075 util_m add row $line
7078 if {([
string first "| CLB Registers" $line] >= 0 || [
string first "| Slice Registers" $line] >= 0) && $regs == 0} {
7079 util_m add row $line
7082 if {[
string first "| Block RAM Tile" $line] >= 0 && $bram == 0} {
7083 util_m add row $line
7086 if {[
string first "URAM " $line] >= 0 && $uram == 0} {
7087 util_m add row $line
7090 if {[
string first "DSPs" $line] >= 0 && $dsps == 0} {
7091 util_m add row $line
7094 if {[
string first "Bonded IOB" $line] >= 0 && $ios == 0} {
7095 util_m add row $line
7102 puts $o [util_m format 2string]
7108 Msg Error "Found Git version older than 2.7.2. Hog will not work as expected, exiting now."