18 if {![
info exists tcl_path]} {
19 set tcl_path [
file normalize "[
file dirname [
info script]]"]
22 set hog_path [
file normalize "[
file dirname [
info script]]"]
24 source "$hog_path/utils/Logger.tcl"
28 set CI_STAGES {"generate_project" "simulate_project"}
29 set CI_PROPS {"-synth_only"}
38 proc AddFile {file fileset} {
40 add_files -norecurse -fileset $fileset $file
50 proc AddHogFiles {libraries properties filesets} {
51 Msg Info "Adding source files to project..."
52 Msg Debug "Filesets: $filesets"
53 Msg Debug "Libraries: $libraries"
54 Msg Debug "Properties: $properties"
57 set synth_conf_command "organize_tool_files -tool {SYNTHESIZE} -input_type {constraint}"
59 set timing_conf_command "organize_tool_files -tool {VERIFYTIMING} -input_type {constraint}"
61 set place_conf_command "organize_tool_files -tool {PLACEROUTE} -input_type {constraint}"
65 foreach fileset [dict keys $filesets] {
66 Msg Debug "Fileset: $fileset"
69 if {[
string equal [get_filesets -quiet $fileset] ""]} {
71 create_fileset -simset $fileset
74 current_fileset -simset [get_filesets $fileset]
75 set simulation [get_filesets $fileset]
77 set_property -name {$simulator.compile.vhdl_syntax} -value {2008} -objects $simulation
79 set_property SOURCE_SET sources_1 $simulation
83 set libs_in_fileset [
DictGet $filesets $fileset]
84 if {[
IsInList "ips.src" $libs_in_fileset]} {
93 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
96 set vitis_workspace "$globalSettings::build_dir/vitis_unified"
97 set python_script "$globalSettings::repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"
99 if {![
ExecuteVitisUnifiedCommand $python_script "app_list" [list $vitis_workspace] "Failed to get app list from Vitis Unified" json_output]} {
100 Msg Warning "Failed to get app list from Vitis Unified"
103 if {[
catch {
package require json}]} {
104 Msg Warning "JSON package not available for parsing Vitis Unified app list"
107 set json_output_filtered ""
108 if {[regexp -lineanchor {\{.*\}} $json_output json_output_filtered]} {
109 set ws_apps [json::json2dict $json_output_filtered]
111 set ws_apps [json::json2dict $json_output]
118 if {$ws_apps ne ""} {
119 dict for {app_name app_config} $ws_apps {
120 set app_lib [string tolower "app_$app_name\.src"]
121 if {![IsInList $app_lib $libs_in_fileset 0 1]} {
122 Msg Warning "App '$app_name' exists in workspace but no corresponding sourcefile '$app_lib' found. \
123 Make sure you have a list file with the correct naming convention: \[app_<app_name>\.src\]"
132 set app_files_dict [dict create]
136 set reapply_targets [dict create]
137 foreach lib $libs_in_fileset {
138 Msg Debug "lib: $lib \n"
139 set lib_files [
DictGet $libraries $lib]
140 Msg Debug "Files in $lib: $lib_files"
141 set rootlib [
file rootname [
file tail $lib]]
142 set ext [
file extension $lib]
143 Msg Debug "lib: $lib ext: $ext fileset: $fileset"
145 if {[
IsXilinx] && !([
info exists globalSettings::vitis_only_pass] && $globalSettings::vitis_only_pass == 1)} {
147 if {[
string match "app_*" [
string tolower $lib]]} {
150 Msg Debug "Adding $lib to $fileset"
151 add_files -norecurse -fileset $fileset $lib_files
153 foreach f $lib_files {
154 set file_obj [get_files -of_objects [get_filesets $fileset] [list "*$f"]]
156 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
157 set_property -name "library" -value $rootlib -objects $file_obj
161 set props [
DictGet $properties $f]
162 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
164 if {[lsearch -inline -regexp $props "93"] < 0} {
167 if {[lsearch -inline -regexp $props "2008"] >= 0} {
168 set vhdl_year "VHDL 2008"
169 }
elseif {[lsearch -inline -regexp $props "2019"] >= 0} {
171 set vhdl_year "VHDL 2019"
173 Msg CriticalWarning "VHDL 2019 is not supported\
174 in Vivado version older than 2023.2.\
175 Using VHDL 2008, but this might not work."
176 set vhdl_year "VHDL 2008"
180 set vhdl_year "VHDL 2008"
182 Msg Debug "File type for $f is $vhdl_year"
183 set_property -name "file_type" -value $vhdl_year -objects $file_obj
186 Msg Debug "Filetype is VHDL 93 for $f"
191 if {[lsearch -inline -regexp $props "SystemVerilog"] > 0} {
194 set_property -name "file_type" -value "SystemVerilog" -objects $file_obj
195 Msg Debug "Filetype is SystemVerilog for $f"
197 Msg Warning "Xilinx PlanAhead/ISE does not support SystemVerilog.\
198 Property not set for $f"
203 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
205 Msg Info "Setting $top as top module for file set $fileset..."
206 set globalSettings::synth_top_module $top
210 if {[lsearch -inline -regexp $props "verilog_header"] >= 0} {
211 Msg Debug "Setting verilog header type for $f..."
212 set_property file_type {Verilog Header} [get_files $f]
213 }
elseif {[lsearch -inline -regexp $props "verilog_template"] >= 0} {
215 Msg Debug "Setting verilog template type for $f..."
216 set_property file_type {Verilog Template} [get_files $f]
217 }
elseif {[lsearch -inline -regexp $props "verilog"] >= 0} {
219 Msg Debug "Setting verilog type for $f..."
220 set_property file_type {Verilog} [get_files $f]
224 if {[lsearch -inline -regexp $props "nosynth"] >= 0} {
225 Msg Debug "Setting not used in synthesis for $f..."
226 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
230 if {[lsearch -inline -regexp $props "noimpl"] >= 0} {
231 Msg Debug "Setting not used in implementation for $f..."
232 set_property -name "used_in_implementation" -value "false" -objects $file_obj
236 if {[lsearch -inline -regexp $props "nosim"] >= 0} {
237 Msg Debug "Setting not used in simulation for $f..."
238 set_property -name "used_in_simulation" -value "false" -objects $file_obj
243 set top_sim [
lindex [regexp -inline {\ytopsim\s*=\s*(.+?)\y.*} $props] 1]
244 if {$top_sim != ""} {
245 Msg Warning "Setting the simulation top module with the topsim property is deprecated.\
246 Please set this property in the \[properties\] section of your .sim list file,
247 or in the \[$fileset\] section of your sim.conf,\
248 by adding the following line.\ntop=$top_sim"
252 set sim_runtime [
lindex [regexp -inline {\yruntime\s*=\s*(.+?)\y.*} $props] 1]
253 if {$sim_runtime != ""} {
254 Msg Warning "Setting the simulation runtime using the runtime= property is deprecated.\
255 Please set this property in the \[properties\] section of your .sim list file,\
256 or in the \[$fileset\] section of your sim.conf,\
257 by adding the following line.\n<simulator_name>.simulate.runtime=$sim_runtime"
261 if {[lsearch -inline -regexp $props "wavefile"] >= 0} {
262 Msg Warning "Setting a wave do file using the wavefile property is deprecated.\
263 Set this property in the sim.conf file under the \[$fileset\] section,\
264 or in the \[properties\] section of the .sim list file,\
265 by adding the following line .\n<simulator_name>.simulate.custom_wave_do=[
file tail $f]"
269 if {[lsearch -inline -regexp $props "dofile"] >= 0} {
270 Msg Warning "Setting a wave do file using the dofile property is deprecated.\
271 Set this property in the sim.conf file under the \[$fileset\] section,\
272 or in the \[properties\] section of the .sim list file,\
273 by adding the following line .\n<simulator_name>.simulate.custom_do=[
file tail $f]"
277 if {[lsearch -inline -regexp $props "locked"] >= 0 && $ext == ".ip"} {
278 Msg Info "Locking IP $f..."
279 set_property IS_MANAGED 0 [get_files $f]
283 if {[
file extension $f] == ".bd"} {
284 Msg Info "Generating Target for [
file tail $f],\
285 please remember to commit the (possible) changed file."
286 generate_target all [get_files $f]
289 dict for {fo pd} $reapply_targets {
290 set ref [dict get $pd ref]
291 set cell [dict get $pd cell]
292 Msg Debug "Reapplying ref: $ref and cell:$cell to $fo"
293 set_property SCOPED_TO_REF $ref $fo
294 set_property SCOPED_TO_CELLS [split $cell ","] $fo
299 if {[
file extension $f] == ".tcl" && $ext != ".con"} {
300 if {[lsearch -inline -regexp $props "source"] >= 0} {
301 Msg Info "Sourcing Tcl script $f,\
302 and setting it not used in synthesis, implementation and simulation..."
304 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
305 set_property -name "used_in_implementation" -value "false" -objects $file_obj
306 set_property -name "used_in_simulation" -value "false" -objects $file_obj
311 set ref [
lindex [regexp -inline {\yscoped_to_ref\s*=\s*([^ ]+)} $props] 1]
312 set cell [
lindex [regexp -inline {\yscoped_to_cells\s*=\s*([^ ]+)} $props] 1]
313 if {[
file extension $f] == ".elf" || (([
file extension $f] == ".tcl" || [
file extension $f] == ".xdc") && $ext == ".con")} {
314 if {[
file extension $f] == ".elf" } { dict set reapply_targets $file_obj [dict create ref $ref cell $cell]}
316 set_property SCOPED_TO_REF $ref $file_obj
319 set_property SCOPED_TO_CELLS [
split $cell ","] $file_obj
323 Msg Info "[
llength $lib_files] file/s added to library $rootlib..."
326 if {$ext == ".sim"} {
327 Msg Warning "Simulation files not supported in Quartus Prime mode... Skipping $lib"
329 if {![is_project_open]} {
330 Msg Error "Project is closed"
332 foreach cur_file $lib_files {
336 set props [
DictGet $properties $cur_file]
339 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
341 Msg Info "Setting $top as top module for file set $fileset..."
342 set globalSettings::synth_top_module $top
345 if {[
string first "VHDL" $file_type] != -1} {
346 if {[
string first "1987" $props] != -1} {
347 set hdl_version "VHDL_1987"
348 }
elseif {[
string first "1993" $props] != -1} {
349 set hdl_version "VHDL_1993"
350 }
elseif {[
string first "2008" $props] != -1} {
351 set hdl_version "VHDL_2008"
353 set hdl_version "default"
355 if {$hdl_version == "default"} {
356 set_global_assignment -name $file_type $cur_file -library $rootlib
358 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version -library $rootlib
360 }
elseif {[
string first "SYSTEMVERILOG" $file_type] != -1} {
362 if {[
string first "2005" $props] != -1} {
363 set hdl_version "systemverilog_2005"
364 }
elseif {[
string first "2009" $props] != -1} {
365 set hdl_version "systemverilog_2009"
367 set hdl_version "default"
369 if {$hdl_version == "default"} {
370 set_global_assignment -name $file_type $cur_file
372 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
374 }
elseif {[
string first "VERILOG" $file_type] != -1} {
376 if {[
string first "1995" $props] != -1} {
377 set hdl_version "verilog_1995"
378 }
elseif {[
string first "2001" $props] != -1} {
379 set hdl_version "verilog_2001"
381 set hdl_version "default"
383 if {$hdl_version == "default"} {
384 set_global_assignment -name $file_type $cur_file
386 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
388 }
elseif {[
string first "SOURCE" $file_type] != -1 || [
string first "COMMAND_MACRO" $file_type] != -1} {
389 set_global_assignment -name $file_type $cur_file
390 if {$ext == ".con"} {
392 }
elseif {$ext == ".src"} {
394 if {[
string first "qsys" $props] != -1} {
397 regsub -all {\{||qsys||\}} $props $emptyString props
399 set qsysPath [
file dirname $cur_file]
400 set qsysName "[
file rootname [
file tail $cur_file]].qsys"
401 set qsysFile "$qsysPath/$qsysName"
402 set qsysLogFile "$qsysPath/[
file rootname [
file tail $cur_file]].qsys-script.log"
405 if {![
info exists ::env(QSYS_ROOTDIR)]} {
406 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
407 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
408 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
410 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
413 set qsys_rootdir $::env(QSYS_ROOTDIR)
416 set cmd "$qsys_rootdir/qsys-script"
417 set cmd_options " --script=$cur_file"
418 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
419 Msg Info "Executing: $cmd $cmd_options"
420 Msg Info "Saving logfile in: $qsysLogFile"
421 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
422 set makeRet [
lindex [dict get $opt -errorcode] end]
423 Msg CriticalWarning "$cmd returned with $makeRet"
426 Msg Error " Could not execute command $cmd"
430 if {[
file exists $qsysName] != 0} {
431 file rename -force $qsysName $qsysFile
433 set qsysMd5Sum [
Md5Sum $qsysFile]
435 set fileDir [
file normalize "./hogTmp"]
436 set fileName "$fileDir/.hogQsys.md5"
437 if {![
file exists $fileDir]} {
440 set hogQsysFile [open $fileName "a"]
441 set fileEntry "$qsysFile\t$qsysMd5Sum"
442 puts $hogQsysFile $fileEntry
445 Msg ERROR "Error while moving the generated qsys file to final location: $qsysName not found!"
447 if {[
file exists $qsysFile] != 0} {
448 if {[
string first "noadd" $props] == -1} {
450 set_global_assignment -name $qsysFileType $qsysFile
452 regsub -all {noadd} $props $emptyString props
454 if {[
string first "nogenerate" $props] == -1} {
458 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
462 }
elseif {[
string first "QSYS" $file_type] != -1} {
464 regsub -all {\{||\}} $props $emptyString props
465 if {[
string first "noadd" $props] == -1} {
466 set_global_assignment -name $file_type $cur_file
468 regsub -all {noadd} $props $emptyString props
472 if {[
string first "nogenerate" $props] == -1} {
476 set_global_assignment -name $file_type $cur_file -library $rootlib
481 if {$ext == ".con"} {
482 set vld_exts {.sdc .pin .dcf .gcf .pdc .ndc .fdc .crt .vcd }
483 foreach con_file $lib_files {
485 set con_ext [
file extension $con_file]
486 if {[
IsInList [
file extension $con_file] $vld_exts]} {
487 set option [
string map {. -} $con_ext]
488 set option [
string map {fdc net_fdc} $option]
489 set option [
string map {pdc io_pdc} $option]
490 create_links -convert_EDN_to_HDL 0 -library {work} $option $con_file
492 set props [
DictGet $properties $con_file]
494 if {$con_ext == ".sdc"} {
495 if {[lsearch $props "notiming"] >= 0} {
496 Msg Info "Excluding $con_file from timing verification..."
498 Msg Info "Adding $con_file to time verification"
499 append timing_conf_command " -file $con_file"
503 if {[lsearch $props "nosynth"] >= 0} {
504 Msg Info "Excluding $con_file from synthesis..."
506 Msg Info "Adding $con_file to synthesis"
507 append synth_conf_command " -file $con_file"
512 if {$con_ext == ".pdc" || $con_ext == ".sdc"} {
513 if {[lsearch $props "noplace"] >= 0} {
514 Msg Info "Excluding $con_file from place and route..."
516 Msg Info "Adding $con_file to place and route"
517 append place_conf_command " -file $con_file"
522 Msg CriticalWarning "Constraint file $con_file does not have a valid extension. Allowed extensions are: \n $vld_exts"
525 }
elseif {$ext == ".src"} {
526 foreach f $lib_files {
527 Msg Debug "Adding source $f to library $rootlib..."
528 create_links -library $rootlib -hdl_source $f
530 }
elseif {$ext == ".sim"} {
531 Msg Debug "Adding stimulus file $f to library..."
532 create_links -library $rootlib -stimulus $f
534 build_design_hierarchy
535 foreach cur_file $lib_files {
539 set props [
DictGet $properties $cur_file]
542 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
544 Msg Info "Setting $top as top module for file set $rootlib..."
545 set globalSettings::synth_top_module "${top}::$rootlib"
550 if {$ext == ".src" || $ext == ".con" || $ext == ".ext"} {
551 foreach f $lib_files {
552 Msg Debug "Diamond: adding source file $f to library $rootlib..."
553 prj_src add -work $rootlib $f
554 set props [
DictGet $properties $f]
556 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
558 Msg Info "Setting $top as top module for the project..."
559 set globalSettings::synth_top_module $top
563 if {[lsearch -inline -regexp $props "enable"] >= 0} {
564 Msg Debug "Setting $f as active Logic Preference file"
568 }
elseif {$ext == ".sim"} {
569 foreach f $lib_files {
570 Msg Debug "Diamond Adding simulation file $f to library $rootlib..."
571 prj_src add -work $rootlib -simulate_only $f
578 foreach app_name [dict keys $ws_apps] {
579 foreach f $lib_files {
580 if {[
string tolower $rootlib] != [
string tolower "app_$app_name"]} {
584 Msg Info "Adding source file $f from lib: $lib to vitis app \[$app_name\]..."
585 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
586 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
587 Msg Debug "Project_f_path is $proj_f_path"
589 importsources -name $app_name -soft-link -path $f -target $proj_f_path
593 Msg Debug "Vitis Unified: Collecting files for apps from library $rootlib..."
597 if {[dict size $ws_apps] == 0} {
598 Msg Debug "No apps found in workspace, skipping file collection"
600 Msg Debug "Found [dict size $ws_apps] app(s) in workspace"
601 Msg Debug "Processing library: $rootlib with [
llength $lib_files] file(s)"
602 foreach app_name [dict keys $ws_apps] {
603 set expected_lib [
string tolower "app_$app_name"]
604 Msg Debug "Checking files for app: $app_name (looking for lib: $expected_lib, current lib: [
string tolower $rootlib])"
605 foreach f $lib_files {
606 if {[
string tolower $rootlib] != $expected_lib} {
609 Msg Debug "File $f matches app $app_name"
610 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
611 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
612 set proj_f_path [
string trimleft $proj_f_path "/"]
614 if {![dict exists $app_files_dict $app_name]} {
615 dict set app_files_dict $app_name files [list]
616 dict set app_files_dict $app_name target $proj_f_path
617 Msg Debug "Initialized app_files_dict for $app_name with target: $proj_f_path"
620 set current_files [dict get $app_files_dict $app_name files]
621 lappend current_files $f
622 dict set app_files_dict $app_name files $current_files
623 set current_count [
llength [dict get $app_files_dict $app_name files]]
624 Msg Debug "Added file $f to app_files_dict for $app_name, current count: $current_count"
634 set python_script "$globalSettings::repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"
635 set vitis_workspace "$globalSettings::build_dir/vitis_unified"
639 set env(HOG_VITIS_VER) $vitis_version
640 Msg Debug "Vitis version: $vitis_version (set in HOG_VITIS_VER environment variable)"
642 dict for {app_name app_data} $app_files_dict {
643 set files_list [dict get $app_data files]
644 set target_path [dict get $app_data target]
646 if {[llength $files_list] > 0} {
647 Msg Info "Adding [llength $files_list] source file(s) to vitis app \[$app_name\]..."
648 Msg Debug "Files: $files_list"
649 Msg Debug "Target path: $target_path"
651 # Convert Tcl list to JSON array for Python
654 foreach f $files_list {
656 append files_json ", "
658 # Escape backslashes and quotes in file paths
659 set escaped_f [string map {\\ \\\\ \" \\\"} $f]
660 append files_json "\"$escaped_f\""
663 append files_json "\]"
665 Msg Debug "JSON string: $files_json"
667 set error_msg "Failed to add files to app $app_name"
668 if {![ExecuteVitisUnifiedCommand $python_script "add_app_files" \
669 [list $app_name $files_json $vitis_workspace $target_path] \
671 Msg Error "Failed to add files to Vitis Unified app '$app_name'"
675 Msg Warning "No files to add for app '$app_name'"
682 if {[
DictGet $filesets "sim_1"] == ""} {
683 delete_fileset -quiet [get_filesets -quiet "sim_1"]
689 if {$synth_conf == 1} {
690 Msg Info $synth_conf_command
691 eval $synth_conf_command
693 if {$timing_conf == 1} {
694 Msg Info $timing_conf_command
695 eval $timing_conf_command
697 if {$place_conf == 1} {
698 Msg Info $place_conf_command
699 eval $place_conf_command
705 proc ALLOWED_PROPS {} {
706 return [dict create ".vhd" [list "93" "nosynth" "noimpl" "nosim" "1987" "1993" "2008" "2019"] \
707 ".vhdl" [list "93" "nosynth" "noimpl" "nosim" "1987" "1993" "2008" "2019"] \
708 ".bd" [list "nosim"] \
709 ".v" [list "SystemVerilog" "verilog_header" "nosynth" "noimpl" "nosim" "1995" "2001"] \
710 ".sv" [list "verilog" "verilog_header" "nosynth" "noimpl" "nosim" "2005" "2009"] \
711 ".svp" [list "verilog" "verilog_header" "nosynth" "noimpl" "nosim" "2005" "2009"] \
712 ".do" [list "nosim"] \
713 ".udo" [list "nosim"] \
714 ".xci" [list "nosynth" "noimpl" "nosim" "locked"] \
715 ".xdc" [list "nosynth" "noimpl" "scoped_to_ref" "scoped_to_cells"] \
716 ".tcl" [list "nosynth" "noimpl" "nosim" "scoped_to_ref" "scoped_to_cells" "source" "qsys" "noadd"\
717 "--block-symbol-file" "--clear-output-directory" "--example-design"\
718 "--export-qsys-script" "--family" "--greybox" "--ipxact"\
719 "--jvm-max-heap-size" "--parallel" "--part" "--search-path"\
720 "--simulation" "--synthesis" "--testbench" "--testbench-simulation"\
721 "--upgrade-ip-cores" "--upgrade-variation-file"
723 ".qsys" [list "nogenerate" "noadd" "--block-symbol-file" "--clear-output-directory" "--example-design"\
724 "--export-qsys-script" "--family" "--greybox" "--ipxact" "--jvm-max-heap-size" "--parallel"\
725 "--part" "--search-path" "--simulation" "--synthesis" "--testbench" "--testbench-simulation"\
726 "--upgrade-ip-cores" "--upgrade-variation-file"
728 ".sdc" [list "notiming" "nosynth" "noplace"] \
729 ".elf" [list "scoped_to_ref" "scoped_to_cells" "nosim" "noimpl"] \
730 ".pdc" [list "nosynth" "noplace"] \
731 ".lpf" [list "enable"]]
742 proc BinaryStepName {part} {
744 return "WRITE_DEVICE_IMAGE"
748 return "WRITE_BITSTREAM"
755 set essential_vars [dict create \
756 "HOG_USER" "NOT defined. This variable is essential for git to work properly. \
757 It should be set to the username for your service account (a valid git account)." \
758 "HOG_EMAIL" "NOT defined. This variable is essential for git to work properly. It should be set to your service's account email."\
759 "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."
763 dict for {var msg} $essential_vars {
764 if {![info exists env($var)]} {
765 Msg CriticalWarning "Essential environment variable $var is $msg"
768 Msg Info "Found environment variable $var."
772 set additional_vars [dict create \
773 "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." \
774 "HOG_TARGET_BRANCH" "NOT defined. Default branch for merge is \"master\""\
775 "HOG_CREATE_OFFICIAL_RELEASE" "NOT defined. \
776 Set this variable to '1' to make Hog create an official release in GitHub/Gitlab with the binaries generated in the CI."\
777 "HOG_USE_DOXYGEN" "NOT defined. \
778 Set this variable to 1 to make Hog-CI run Doxygen and copy the official documentation over when you merge to the official branch."
781 if {([
info exists env(HOG_OFFICIAL_BIN_EOS_PATH)] && $env(HOG_OFFICIAL_BIN_EOS_PATH) ne "") || \
782 ([
info exists env(HOG_OFFICIAL_BIN_PATH)] && [
string match "/eos/*" $env(HOG_OFFICIAL_BIN_PATH)])} {
783 Msg Info "Official binary path points to EOS. Checking EOS environment variables for uploads..."
784 if {[
info exists env(HOG_OFFICIAL_BIN_PATH)]} {
785 Msg CriticalWarning "Variable HOG_OFFICIAL_BIN_EOS_PATH is defined. \
786 From Hog2026.2 this variable will be deprecated. Please, use HOG_OFFICIAL_BIN_PATH instead."
788 if {![
info exists env(EOS_PASSWORD)]} {
789 if {![
info exists env(HOG_PASSWORD)]} {
790 Msg Warning "Neither EOS_PASSWORD nor HOG_PASSWORD environment variable is defined. \
791 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."
793 Msg Info "HOG_PASSWORD environment variable is defined and will be used as password for EOS uploads. \
794 If you want to use a different password for EOS uploads, please set the EOS_PASSWORD environment variable."
797 Msg Info "EOS_PASSWORD environment variable is defined and will be used as password for EOS uploads."
800 if {![
info exists env(EOS_USER)]} {
801 Msg Info "EOS_USER environment variable is not defined. Assuming EOS username is the same as HOG_USER."
803 Msg Info "EOS_USER environment variable is defined and will be used as username for EOS uploads."
806 if {![
info exists env(EOS_MGM_URL)]} {
807 Msg Info "EOS_MGM_URL environment variable is not defined. Assuming default value of root://eosuser.cern.ch."
809 Msg Info "EOS_MGM_URL environment variable is defined and will be used as MGM URL for EOS uploads."
811 }
elseif {[
info exists env(HOG_OFFICIAL_BIN_PATH)] } {
812 Msg Info "Variable HOG_OFFICIAL_BIN_PATH is defined. Hog will copy the official binary files to the path defined in this variable. \
813 Please make sure this path is correct and has enough space to store the binaries."
815 Msg Info "No official binary path defined. Hog will not be able to upload binaries."
821 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
829 proc CheckEnv {project_name ide} {
832 set essential_commands [dict create "git" "--version" "$ide" "-version"]
833 set additional_commands [dict create \
840 set additional_vars [dict create \
841 "HOG_PATH" "NOT defined. Hog might work as long as all the necessary executable are in the PATH variable."\
842 "HOG_XIL_LICENSE" "NOT defined. If this variable is not set to the license servers separated by comas, \
843 you need some alternative way of getting your Xilinx license (for example a license file on the machine)."\
844 "LM_LICENSE_FILE" "NOT defined. This variable should be set the Quartus/Libero license servers separated by semicolon. \
845 If not, you need an alternative way of getting your Quartus/Libero license."\
846 "HOG_LD_LIBRARY_PATH" "NOT defined. Hog might work as long as all the necessary library are found."\
847 "HOG_SIMULATION_LIB_PATH" "NOT defined. Hog-CI will not be able to run simulations using third-party simulators."\
848 "HOG_CHECK_PROJVER" "NOT defined. Hog will NOT check the CI project version. \
849 Set this variable to '1' if you want Hog to check the CI project version before creating the HDL project in Create_Project stage. \
850 If the project has not been changed with respect to the target branch, the CI will skip this project" \
851 "HOG_CHECK_SYNTAX" "NOT defined. Hog will NOT check the syntax. \
852 Set this variable to '1' if you want Hog to check the syntax after creating the HDL project in Create_Project stage." \
853 "HOG_NO_BITSTREAM" "NOT defined. Hog-CI will run the implementation up to the write_bitstream stage and create bit files." \
854 "HOG_NO_RESET_BD" "NOT defined or not equal to 1. Hog will reset .bd files (if any) before starting synthesis."\
855 "HOG_IP_PATH" "NOT defined. Hog-CI will NOT use an EOS/LOCAL IP repository to speed up the IP synthesis." \
856 "HOG_RESET_FILES" "NOT defined. Hog-CI will NOT reset any files."\
857 "HOG_NJOBS" "NOT defined. Hog-CI will build IPs with default number of jobs (4)."\
858 "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"
860 Msg Info "Checking environment to run Hog-CI for project $project_name with IDE $ide..."
862 Msg Info "Checking essential commands..."
863 dict for {cmd ver} $essential_commands {
864 if {[catch {exec which $cmd}]} {
865 Msg CriticalWarning "$cmd executable not found. Hog-CI cannot run!"
868 Msg Info "Found executable $cmd."
869 if {$cmd == "ghdl"} {
870 Msg Info [exec $cmd --version]
871 } elseif {$cmd != "diamond"} {
872 Msg Info [exec $cmd $ver]
877 Msg Info "Checking additional commands..."
878 dict for {cmd ver} $additional_commands {
879 if {[catch {exec which $cmd}]} {
880 Msg Warning "$cmd executable not found."
882 Msg Info "Found executable $cmd."
884 Msg Info [exec $cmd $ver]
889 if {$ide == "libero"} {
890 Msg Info "Checking essential environment variables..."
892 if {![
info exists env(HOG_TCLLIB_PATH)]} {
893 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."
896 Msg Info "HOG_TCLLIB_PATH is set. Hog-CI can run with Libero."
900 Msg Info "Checking additional environment variables..."
901 dict for {var msg} $additional_vars {
902 if {![info exists env($var)]} {
903 Msg Info "Environment variable $var is $msg"
905 Msg Info "Found environment variable $var."
910 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
918 proc CheckProjVer {repo_path project {sim 0} {ext_path ""}} {
922 Msg Info "Will check also the version of the simulation files..."
926 if {[
info exists env(HOG_PUSH_TOKEN)] && [
info exist env(CI_PROJECT_ID)] && [
info exist env(CI_API_V4_URL)] } {
927 set token $env(HOG_PUSH_TOKEN)
928 set api_url $env(CI_API_V4_URL)
929 set project_id $env(CI_PROJECT_ID)
931 set curl_cmd [
GetCurl $api_url]
932 if {[
info exist env(CI_JOB_NAME)] && $env(CI_JOB_NAME) == "check_branch_state" } {
941 set project_dir $repo_path/Top/$project
944 Msg Info "$project was modified, continuing with the CI..."
946 Msg Info "Checking if the project has been already built in a previous CI run..."
948 if {$sha == [
GetSHA $repo_path]} {
949 Msg Info "Project was modified in the current commit, Hog will proceed with the build workflow."
952 Msg Info "Checking if project $project has been built in a previous CI run with sha $sha..."
953 set result [
catch {
package require json} JsonFound]
954 if {"$result" != "0"} {
955 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
958 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/$project_id/pipelines"] ret content
959 set pipeline_dict [json::json2dict $content]
960 if {[
llength $pipeline_dict] > 0} {
961 foreach pip $pipeline_dict {
963 set source [
DictGet $pip source]
964 if {$source == "merge_request_event" && [
string first $sha $pip_sha] != -1} {
965 Msg Info "Found pipeline with sha $pip_sha for project $project"
966 set pipeline_id [
DictGet $pip id]
968 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/${project_id}/pipelines/${pipeline_id}/jobs?pagination=keyset&per_page=100"] ret2 content2
969 set jobs_dict [json::json2dict $content2]
970 if {[
llength $jobs_dict] > 0} {
971 foreach job $jobs_dict {
972 set job_name [
DictGet $job name]
974 set artifacts [
DictGet $job artifacts_file]
975 set status [
DictGet $job status]
976 set current_job_name $env(CI_JOB_NAME)
977 if {$current_job_name == $job_name && $status == "success"} {
979 lassign [
ExecuteRet {*}$curl_cmd --location --output artifacts.zip --header "PRIVATE-TOKEN: $token" --url "$api_url/projects/$project_id/jobs/$job_id/artifacts"] ret3 content3
981 Msg CriticalWarning "Cannot download artifacts for job $job_name with id $job_id"
984 lassign [
ExecuteRet unzip -o $repo_path/artifacts.zip] ret_zip
988 Msg Info "Artifacts for job $job_name with id $job_id downloaded and unzipped."
989 file mkdir $repo_path/Projects/$project
990 set fp [open "$repo_path/Projects/$project/skip.me" w+]
1002 }
elseif {$ver != -1} {
1003 Msg Info "$project was not modified since version: $ver."
1004 file mkdir $repo_path/Projects/$project
1005 set fp [open "$repo_path/Projects/$project/skip.me" w+]
1009 Msg Error "Impossible to check the project version. Most likely the repository is not clean. Please, commit your changes before running this command."
1020 proc CheckSyntax {project_name repo_path {project_file ""}} {
1022 update_compile_order
1023 set syntax [check_syntax -return_string]
1024 if {[
string first "CRITICAL" $syntax] != -1} {
1029 lassign [
GetHogFiles -list_files "*.src" "$repo_path/Top/$project_name/list/" $repo_path] src_files dummy
1030 dict for {lib files} $src_files {
1032 set file_extension [file extension $f]
1033 if {$file_extension == ".vhd" || $file_extension == ".vhdl" || $file_extension == ".v" || $file_extension == ".sv"} {
1034 if {[catch {execute_module -tool map -args "--analyze_file=$f"} result]} {
1035 Msg Error "\nResult: $result\n"
1036 Msg Error "Check syntax failed.\n"
1038 if {$result == ""} {
1039 Msg Info "Check syntax was successful for $f.\n"
1041 Msg Warning "Found syntax error in file $f:\n $result\n"
1048 lassign [
GetProjectFiles $project_file] prjLibraries prjProperties prjSimLibraries prjConstraints prjSrcSets prjSimSets prjConSets
1049 dict for {lib sources} $prjLibraries {
1050 if {[file extension $lib] == ".src"} {
1051 foreach f $sources {
1052 Msg Info "Checking Syntax of $f"
1058 Msg Info "The Checking Syntax is not supported by this IDE. Skipping..."
1063 proc CloseProject {} {
1085 proc CompareVersions {ver1 ver2} {
1094 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver1 - x y z]} {
1095 set ver1 [list $x $y $z]
1097 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver2 - x y z]} {
1098 set ver2 [list $x $y $z]
1102 set v1 [
join $ver1 ""]
1104 set v2 [
join $ver2 ""]
1107 if {[
string is integer $v1] && [
string is integer $v2]} {
1108 set ver1 [
expr {[scan [lindex $ver1 0] %d] * 1000000 + [scan [lindex $ver1 1] %d] * 1000 + [scan [lindex $ver1 2] %d]}]
1109 set ver2 [
expr {[scan [lindex $ver2 0] %d] * 1000000 + [scan [lindex $ver2 1] %d] * 1000 + [scan [lindex $ver2 2] %d]}]
1111 if {$ver1 > $ver2} {
1113 }
elseif {$ver1 == $ver2} {
1119 Msg Warning "Version is not numeric: $ver1, $ver2"
1122 return [
expr {$ret}]
1129 proc CheckExtraFiles {libraries constraints simlibraries} {
1132 lassign [
GetProjectFiles] prjLibraries prjProperties prjSimLibraries prjConstraints
1133 set prj_dir [get_property DIRECTORY [current_project]]
1134 file mkdir "$prj_dir/.hog"
1135 set extra_file_name "$prj_dir/.hog/extra.files"
1136 set new_extra_file [open $extra_file_name "w"]
1138 dict for {prjLib prjFiles} $prjLibraries {
1139 foreach prjFile $prjFiles {
1140 if {[file extension $prjFile] == ".xcix"} {
1141 Msg Warning "IP $prjFile is packed in a .xcix core container. \
1142 This files are not suitable for version control systems. We recommend to use .xci files instead."
1145 if {[file extension $prjFile] == ".xci" && [get_property CORE_CONTAINER [get_files $prjFile]] != ""} {
1146 Msg Info "$prjFile is a virtual IP file in a core container. Ignoring it..."
1150 if {[IsInList $prjFile [DictGet $libraries $prjLib]] == 0} {
1151 if {[file extension $prjFile] == ".bd"} {
1152 # Generating BD products to save md5sum of already modified BD
1153 Msg Info "Generating targets of $prjFile..."
1154 generate_target all [get_files $prjFile]
1156 puts $new_extra_file "$prjFile [Md5Sum $prjFile]"
1157 Msg Info "$prjFile (lib: $prjLib) has been generated by an external script. Adding to $extra_file_name..."
1161 close $new_extra_file
1162 set extra_sim_file "$prj_dir/.hog/extrasim.files"
1163 set new_extra_file [open $extra_sim_file "w"]
1165 dict for {prjSimLib prjSimFiles} $prjSimLibraries {
1166 foreach prjSimFile $prjSimFiles {
1167 if {[IsInList $prjSimFile [DictGet $simlibraries $prjSimLib]] == 0} {
1168 puts $new_extra_file "$prjSimFile [Md5Sum $prjSimFile]"
1169 Msg Info "$prjSimFile (lib: $prjSimLib) has been generated by an external script. Adding to $extra_sim_file..."
1173 close $new_extra_file
1174 set extra_con_file "$prj_dir/.hog/extracon.files"
1175 set new_extra_file [open $extra_con_file "w"]
1177 dict for {prjConLib prjConFiles} $prjConstraints {
1178 foreach prjConFile $prjConFiles {
1179 if {[IsInList $prjConFile [DictGet $constraints $prjConLib]] == 0} {
1180 puts $new_extra_file "$prjConFile [Md5Sum $prjConFile]"
1181 Msg Info "$prjConFile has been generated by an external script. Adding to $extra_con_file..."
1185 close $new_extra_file
1192 proc CheckLatestHogRelease {{repo_path .}} {
1195 set current_ver [
Git {describe --always}]
1196 Msg Debug "Current version: $current_ver"
1197 set current_sha [
Git "log $current_ver -1 --format=format:%H"]
1198 Msg Debug "Current SHA: $current_sha"
1201 if {[
OS] == "windows"} {
1202 Msg Info "On windows we cannot set a timeout on 'git fetch', hopefully nothing will go wrong..."
1205 Msg Info "Checking for latest Hog release, can take up to 5 seconds..."
1208 set master_ver [
Git "describe origin/master"]
1209 Msg Debug "Master version: $master_ver"
1210 set master_sha [
Git "log $master_ver -1 --format=format:%H"]
1211 Msg Debug "Master SHA: $master_sha"
1212 set merge_base [
Git "merge-base $current_sha $master_sha"]
1213 Msg Debug "merge base: $merge_base"
1216 if {$merge_base != $master_sha} {
1218 Msg Info "Version $master_ver has been released (https://gitlab.com/hog-cern/Hog/-/releases/$master_ver)"
1219 Msg Status "You should consider updating Hog submodule with the following instructions:"
1221 Msg Status "cd Hog && git checkout master && git pull"
1223 Msg Status "Also update the ref: in your .gitlab-ci.yml to $master_ver"
1227 Msg Info "Latest official version is $master_ver, nothing to do."
1239 proc CheckYmlRef {repo_path allow_failure} {
1240 if {$allow_failure} {
1241 set MSG_TYPE CriticalWarning
1246 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
1247 Msg CriticalWarning "Cannot find package YAML, skipping consistency check of \"ref\" in gilab-ci.yaml file.\n Error message: $YAMLPACKAGE
1248 You can fix this by installing package \"tcllib\""
1256 if {[
file exists .gitlab-ci.yml]} {
1260 if {[
file exists .gitlab-ci.yml]} {
1261 set fp [open ".gitlab-ci.yml" r]
1262 set file_data [read $fp]
1265 Msg $MSG_TYPE "Cannot open file .gitlab-ci.yml"
1269 set file_data "\n$file_data\n\n"
1271 if {[
catch {::yaml::yaml2dict -stream $file_data} yamlDict]} {
1272 Msg $MSG_TYPE "Parsing $repo_path/.gitlab-ci.yml failed. To fix this, check that yaml syntax is respected, remember not to use tabs."
1276 dict for {dictKey dictValue} $yamlDict {
1277 #looking for Hog include in .gitlab-ci.yml
1278 if {"$dictKey" == "include" && (
1279 [lsearch [split $dictValue " {}"] "/hog.yml"] != "-1" ||
1280 [lsearch [split $dictValue " {}"] "/hog-dynamic.yml"] != "-1"
1282 set YML_REF [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "ref"] + 1}]]
1283 set YML_NAME [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "file"] + 1}]]
1287 if {$YML_REF == ""} {
1288 Msg Warning "Hog version not specified in the .gitlab-ci.yml. Assuming that master branch is used."
1290 set YML_REF_F [
Git {name-rev --tags --name-only origin/master}]
1293 set YML_REF_F [regsub -all "'" $YML_REF ""]
1296 if {$YML_NAME == ""} {
1297 Msg $MSG_TYPE "Hog included yml file not specified, assuming hog.yml"
1298 set YML_NAME_F hog.yml
1300 set YML_NAME_F [regsub -all "^/" $YML_NAME ""]
1303 lappend YML_FILES $YML_NAME_F
1309 if {[
catch {::yaml::yaml2dict -file $YML_NAME_F} yamlDict]} {
1310 Msg $MSG_TYPE "Parsing $YML_NAME_F failed."
1314 dict for {dictKey dictValue} $yamlDict {
1315 #looking for included files
1316 if {"$dictKey" == "include"} {
1317 foreach v $dictValue {
1318 lappend YML_FILES [lindex [split $v " "] [expr {[lsearch -dictionary [split $v " "] "local"] + 1}]]
1324 Msg Info "Found the following yml files: $YML_FILES"
1326 set HOGYML_SHA [
GetSHA $YML_FILES]
1327 lassign [
GitRet "log --format=%h -1 --abbrev=7 $YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1329 lassign [
GitRet "log --format=%h -1 --abbrev=7 origin/$YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1331 Msg $MSG_TYPE "Error in project .gitlab-ci.yml. ref: $YML_REF not found"
1332 set EXPECTEDYML_SHA ""
1335 if {!($EXPECTEDYML_SHA eq "")} {
1336 if {$HOGYML_SHA == $EXPECTEDYML_SHA} {
1337 Msg Info "Hog included file $YML_FILES matches with $YML_REF in .gitlab-ci.yml."
1339 Msg $MSG_TYPE "HOG $YML_FILES SHA mismatch.
1340 From Hog submodule: $HOGYML_SHA
1341 From ref in .gitlab-ci.yml: $EXPECTEDYML_SHA
1342 You can fix this in 2 ways: by changing the ref in your repository or by changing the Hog submodule commit"
1345 Msg $MSG_TYPE "One or more of the following files could not be found $YML_FILES in Hog at $YML_REF"
1348 Msg Info ".gitlab-ci.yml not found in $repo_path. Skipping this step"
1369 proc CompareLibDicts {proj_libs list_libs proj_sets list_sets proj_props list_props {severity "CriticalWarning"} {outFile ""} {extraFiles ""}} {
1370 set extra_files $extraFiles
1372 set out_prjlibs $proj_libs
1373 set out_prjprops $proj_props
1375 dict for {prjSet prjLibraries} $proj_sets {
1376 # Check if sets is also in list files
1377 if {[IsInList $prjSet $list_sets]} {
1378 set listLibraries [DictGet $list_sets $prjSet]
1379 # Loop over libraries in fileset
1380 foreach prjLib $prjLibraries {
1381 set prjFiles [DictGet $proj_libs $prjLib]
1382 # Check if library exists in list files
1383 if {[IsInList $prjLib $listLibraries]} {
1384 # Loop over files in library
1385 set listFiles [DictGet $list_libs $prjLib]
1386 foreach prjFile $prjFiles {
1387 set idx [lsearch -exact $listFiles $prjFile]
1388 set listFiles [lreplace $listFiles $idx $idx]
1390 # File is in project but not in list libraries, check if it was generated at creation time...
1391 if {[dict exists $extra_files $prjFile]} {
1392 # File was generated at creation time, checking the md5sum
1393 # Removing the file from the prjFiles list
1394 set idx2 [lsearch -exact $prjFiles $prjFile]
1395 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1396 set new_md5sum [Md5Sum $prjFile]
1397 set old_md5sum [DictGet $extra_files $prjFile]
1398 if {$new_md5sum != $old_md5sum} {
1399 # tclint-disable-next-line line-length
1400 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
1403 set extra_files [dict remove $extra_files $prjFile]
1405 # File is neither in list files nor in extra_files
1406 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1410 # File is both in list files and project, checking properties...
1411 set prjProps [DictGet $proj_props $prjFile]
1412 set listProps [DictGet $list_props $prjFile]
1413 # Check if it is a potential sourced file
1414 if {[IsInList "nosynth" $prjProps] && [IsInList "noimpl" $prjProps] && [IsInList "nosim" $prjProps]} {
1415 # Check if it is sourced
1416 set idx_source [lsearch -exact $listProps "source"]
1417 if {$idx_source >= 0} {
1418 # It is sourced, let's replace the individual properties with source
1419 set idx [lsearch -exact $prjProps "noimpl"]
1420 set prjProps [lreplace $prjProps $idx $idx]
1421 set idx [lsearch -exact $prjProps "nosynth"]
1422 set prjProps [lreplace $prjProps $idx $idx]
1423 set idx [lsearch -exact $prjProps "nosim"]
1424 set prjProps [lreplace $prjProps $idx $idx]
1425 lappend prjProps "source"
1429 foreach prjProp $prjProps {
1430 set idx [lsearch -exact $listProps $prjProp]
1431 set listProps [lreplace $listProps $idx $idx]
1433 MsgAndLog "Property $prjProp of $prjFile was set in project but not in list files" $severity $outFile
1438 foreach listProp $listProps {
1439 if {[string first $listProp "topsim="] == -1 && [string first $listProp "enable"] == -1} {
1440 MsgAndLog "Property $listProp of $prjFile was found in list files but not set in project." $severity $outFile
1445 # Update project prjProps
1446 dict set out_prjprops $prjFile $prjProps
1449 # Loop over remaining files in list libraries
1450 foreach listFile $listFiles {
1451 MsgAndLog "$listFile was found in list files but not in project." $severity $outFile
1455 # Check extra files again...
1456 foreach prjFile $prjFiles {
1457 if {[dict exists $extra_files $prjFile]} {
1458 # File was generated at creation time, checking the md5sum
1459 # Removing the file from the prjFiles list
1460 set idx2 [lsearch -exact $prjFiles $prjFile]
1461 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1462 set new_md5sum [Md5Sum $prjFile]
1463 set old_md5sum [DictGet $extra_files $prjFile]
1464 if {$new_md5sum != $old_md5sum} {
1465 # tclint-disable-next-line line-length
1466 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
1469 set extra_files [dict remove $extra_files $prjFile]
1471 # File is neither in list files nor in extra_files
1472 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1477 # Update prjLibraries
1478 dict set out_prjlibs $prjLib $prjFiles
1481 MsgAndLog "Fileset $prjSet found in project but not in list files" $severity $outFile
1486 return [list $n_diffs $extra_files $out_prjlibs $out_prjprops]
1496 proc CompareVHDL {file1 file2} {
1497 set a [open $file1 r]
1498 set b [open $file2 r]
1500 while {[
gets $a line] != -1} {
1501 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1502 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1508 while {[
gets $b line] != -1} {
1509 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1510 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1519 foreach x $f1 y $f2 {
1521 lappend diff "> $x\n< $y\n\n"
1534 proc Copy {i_dirs o_dir} {
1535 foreach i_dir $i_dirs {
1536 if {[
file isdirectory $i_dir] && [
file isdirectory $o_dir]} {
1537 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]])} {
1538 file delete -force $o_dir/[
file tail $i_dir]
1542 file copy -force $i_dir $o_dir
1557 proc CopyIPbusXMLs {proj_dir path dst {xml_version "0.0.0"} {xml_sha "00000000"} {use_ipbus_sw 0} {generate 0}} {
1558 if {$use_ipbus_sw == 1} {
1559 lassign [
ExecuteRet python3 -c "from __future__ import print_function; from sys import path;print(':'.join(path\[1:\]))"] ret msg
1561 set ::env(PYTHONPATH) $msg
1562 lassign [
ExecuteRet gen_ipbus_addr_decode -h] ret msg
1569 Msg CriticalWarning "Problem while trying to run python: $msg"
1572 set dst [
file normalize $dst]
1574 if {$can_generate == 0} {
1575 if {$generate == 1} {
1576 Msg Error "Cannot generate IPbus address files, IPbus executable gen_ipbus_addr_decode not found or not working: $msg"
1579 Msg Warning "IPbus executable gen_ipbus_addr_decode not found or not working, will not verify IPbus address tables."
1586 set ipb_files [glob -nocomplain $proj_dir/list/*.ipb]
1587 set n_ipb_files [
llength $ipb_files]
1588 if {$n_ipb_files == 0} {
1589 Msg CriticalWarning "No files with .ipb extension found in $proj_dir/list."
1592 set libraries [dict create]
1593 set vhdl_dict [dict create]
1595 foreach ipb_file $ipb_files {
1601 set xmlfiles [dict get $libraries "xml.ipb"]
1603 set xml_list_error 0
1604 foreach xmlfile $xmlfiles {
1605 if {[
file isdirectory $xmlfile]} {
1606 Msg CriticalWarning "Directory $xmlfile listed in xml list file $list_file. Directories are not supported!"
1607 set xml_list_error 1
1610 if {[
file exists $xmlfile]} {
1611 if {[dict exists $vhdl_dict $xmlfile]} {
1612 set vhdl_file [
file normalize [dict get $vhdl_dict $xmlfile]]
1616 lappend vhdls $vhdl_file
1617 set xmlfile [
file normalize $xmlfile]
1618 Msg Info "Copying $xmlfile to $dst and replacing place holders..."
1619 set in [open $xmlfile r]
1621 if {[regexp \/xml\/+(.*)$ $xmlfile XXX out_with_dir]} {
1622 set out_file $dst/$out_with_dir
1623 lappend xmls $out_with_dir
1624 Msg Debug "xml file $xmlfile is contained in a directory called 'xml', so file will be copied to $out_file"
1625 set out_dir [
file dir $out_file]
1626 if {![
file exists $out_dir]} {
1630 set out_file $dst/[
file tail $xmlfile]
1631 lappend xmls [
file tail $xmlfile]
1634 set out [open $out_file w]
1635 while {[
gets $in line] != -1} {
1636 set new_line [regsub {(.*)__VERSION__(.*)} $line "\\1$xml_version\\2"]
1637 set new_line2 [regsub {(.*)__GIT_SHA__(.*)} $new_line "\\1$xml_sha\\2"]
1638 puts $out $new_line2
1644 Msg Warning "XML file $xmlfile not found"
1647 if {${xml_list_error}} {
1648 Msg Error "Invalid files added to $list_file!"
1651 set cnt [
llength $xmls]
1652 Msg Info "$cnt xml file/s copied"
1655 if {$can_generate == 1} {
1658 file mkdir "address_decode"
1660 foreach x $xmls v $vhdls {
1662 set x [
file normalize ../$x]
1663 if {[
file exists $x]} {
1664 lassign [
ExecuteRet gen_ipbus_addr_decode --no-timestamp $x 2>&1] status log
1666 set generated_vhdl ./ipbus_decode_[
file rootname [
file tail $x]].vhd
1667 if {$generate == 1} {
1668 Msg Info "Copying generated VHDL file $generated_vhdl into $v (replacing if necessary)"
1669 file copy -force -- $generated_vhdl $v
1671 if {[
file exists $v]} {
1673 set n [
llength $diff]
1675 Msg CriticalWarning "$v does not correspond to its XML $x, [
expr {$n / 3}] line/s differ:"
1676 Msg Status [
join $diff "\n"]
1677 set diff_file [open ../diff_[
file rootname [
file tail $x]].txt w]
1678 puts $diff_file $diff
1681 Msg Info "[
file tail $x] and $v match."
1684 Msg Warning "VHDL address map file $v not found."
1688 Msg Warning "Address map generation failed for [
file tail $x]: $log"
1691 Msg Warning "Copied XML file $x not found."
1694 Msg Info "Skipped verification of [
file tail $x] as no VHDL file was specified."
1698 file delete -force address_decode
1710 proc DescriptionFromConf {conf_file} {
1711 set f [open $conf_file "r"]
1712 set lines [
split [read $f] "\n"]
1714 set second_line [
lindex $lines 1]
1717 if {![regexp {\#+ *(.+)} $second_line - description]} {
1721 if {[regexp -all {test|Test|TEST} $description]} {
1722 set description "test"
1735 proc DictGet {dictName keyName {default ""}} {
1736 if {[dict exists $dictName $keyName]} {
1737 return [dict get $dictName $keyName]
1748 proc DictSort {dict args} {
1750 foreach key [lsort {*}$args [dict keys $dict]] {
1751 dict set res $key [dict get $dict $key]
1761 proc DoxygenVersion {target_version} {
1762 set ver [
split $target_version "."]
1763 set v [
Execute doxygen --version]
1764 Msg Info "Found Doxygen version: $v"
1765 set current_ver [
split $v ". "]
1766 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
1767 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
1769 return [
expr {$target <= $current}]
1780 proc eos {command {attempt 1}} {
1782 if {![
info exists env(EOS_MGM_URL)]} {
1783 Msg Warning "Environment variable EOS_MGM_URL not set, setting it to default value root://eosuser.cern.ch"
1784 set ::env(EOS_MGM_URL) "root://eosuser.cern.ch"
1787 Msg Warning "The value of attempt should be 1 or more, not $attempt, setting it to 1 as default"
1790 for {
set i 0} {$i < $attempt} {
incr i} {
1791 set ret [
catch {
exec -ignorestderr eos {*}$command} result]
1796 set wait [
expr {1 + int(rand() * 29)}]
1797 Msg Warning "Command $command failed ($i/$attempt): $result, trying again in $wait seconds..."
1798 after [
expr {$wait * 1000}]
1802 return [list $ret $result]
1812 proc Execute {args} {
1816 Msg Error "Command [
join $args] returned error code: $ret"
1830 proc ExecuteRet {args} {
1832 if {[
llength $args] == 0} {
1833 Msg CriticalWarning "No argument given"
1837 set ret [
catch {
exec -ignorestderr {*}$args} result]
1840 return [list $ret $result]
1847 proc ExtractFilesSection {file_data} {
1848 set in_files_section 0
1851 foreach line $file_data {
1852 if {[regexp {^ *\[ *files *\]} $line]} {
1853 set in_files_section 1
1856 if {$in_files_section} {
1857 if {[regexp {^ *\[.*\]} $line]} {
1860 lappend result $line
1865 if {!$in_files_section} {
1879 proc ExtractVersionFromTag {tag} {
1880 if {[regexp {^(?:b(\d+))?v(\d+)\.(\d+).(\d+)(?:-\d+)?$} $tag -> mr M m p]} {
1885 Msg Warning "Repository tag $tag is not in a Hog-compatible format."
1891 return [list $M $m $p $mr]
1901 proc FileCommitted {File} {
1903 set currentDir [
pwd]
1904 cd [
file dirname [
file normalize $File]]
1905 set GitLog [
Git ls-files [
file tail $File]]
1906 if {$GitLog == ""} {
1907 Msg CriticalWarning "File [
file normalize $File] is not in the git repository. Please add it with:\n git add [
file normalize $File]\n"
1918 proc FindCommonGitChild {SHA1 SHA2} {
1920 set commits [
Git {log --oneline --merges}]
1923 foreach line [
split $commits "\n"] {
1924 set commit [
lindex [
split $line] 0]
1928 set ancestor $commit
1941 proc findFiles {basedir pattern} {
1944 set basedir [
string trimright [
file join [
file normalize $basedir] { }]]
1950 foreach fileName [glob -nocomplain -type {f r} -path $basedir $pattern] {
1951 lappend fileList $fileName
1955 foreach dirName [glob -nocomplain -type {d r} -path $basedir *] {
1958 set subDirList [
findFiles $dirName $pattern]
1959 if {[
llength $subDirList] > 0} {
1960 foreach subDirFile $subDirList {
1961 lappend fileList $subDirFile
1972 proc FindFileType {file_name} {
1973 set extension [
file extension $file_name]
1976 set file_extension "USE_SIGNALTAP_FILE"
1979 set file_extension "VHDL_FILE"
1982 set file_extension "VHDL_FILE"
1985 set file_extension "VERILOG_FILE"
1988 set file_extension "SYSTEMVERILOG_FILE"
1991 set file_extension "SDC_FILE"
1994 set file_extension "PDC_FILE"
1997 set file_extension "NDC_FILE"
2000 set file_extension "FDC_FILE"
2003 set file_extension "SOURCE_FILE"
2006 set file_extension "IP_FILE"
2009 set file_extension "QSYS_FILE"
2012 set file_extension "QIP_FILE"
2015 set file_extension "SIP_FILE"
2018 set file_extension "BSF_FILE"
2021 set file_extension "BDF_FILE"
2024 set file_extension "COMMAND_MACRO_FILE"
2027 set file_extension "VQM_FILE"
2030 set file_extension "ERROR"
2031 Msg Error "Unknown file extension $extension"
2034 return $file_extension
2041 proc FindNewestVersion {versions} {
2042 set new_ver 00000000
2043 foreach ver $versions {
2045 if {[
expr 0x$ver > 0x$new_ver]} {
2055 proc FindRepoRoot {start_dir} {
2056 if {[
file pathtype $start_dir] eq "relative"} {
2057 set dir [
file normalize [
file join [
pwd] $start_dir]]
2062 if {[
file exists [
file join $dir Top]] && [
file exists [
file join $dir Projects]]} {
2065 set parent [
file dirname $dir]
2066 if {$parent eq $dir} {
2078 proc FindVhdlVersion {file_name} {
2079 set extension [
file extension $file_name]
2082 set vhdl_version "-hdl_version VHDL_2008"
2085 set vhdl_version "-hdl_version VHDL_2008"
2092 return $vhdl_version
2099 proc FormatGeneric {generic} {
2100 if {[
string is integer "0x$generic"]} {
2101 return [
format "32'h%08X" "0x$generic"]
2104 return [
format "32'h%08X" 0]
2114 proc GenerateBitstream {{run_folder ""} {repo_path .} {njobs 1}} {
2115 Msg Info "Starting write bitstream flow..."
2117 set revision [get_current_revision]
2118 if {[
catch {execute_module -tool asm} result]} {
2119 Msg Error "Result: $result\n"
2120 Msg Error "Generate bitstream failed. See the report file.\n"
2122 Msg Info "Generate bitstream was successful for revision $revision.\n"
2125 Msg Info "Run GENERATEPROGRAMMINGDATA ..."
2126 if {[
catch {run_tool -name {GENERATEPROGRAMMINGDATA}}]} {
2127 Msg Error "GENERATEPROGRAMMINGDATA FAILED!"
2129 Msg Info "GENERATEPROGRAMMINGDATA PASSED."
2131 Msg Info "Sourcing Hog/Tcl/integrated/post-bitstream.tcl"
2132 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
2134 prj_run Export -impl Implementation0 -task Bitgen
2144 proc GenerateQsysSystem {qsysFile commandOpts} {
2146 if {[
file exists $qsysFile] != 0} {
2147 set qsysPath [
file dirname $qsysFile]
2148 set qsysName [
file rootname [
file tail $qsysFile]]
2149 set qsysIPDir "$qsysPath/$qsysName"
2150 set qsysLogFile "$qsysPath/$qsysName.qsys-generate.log"
2153 if {![
info exists ::env(QSYS_ROOTDIR)]} {
2154 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
2155 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
2156 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
2158 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
2161 set qsys_rootdir $::env(QSYS_ROOTDIR)
2164 set cmd "$qsys_rootdir/qsys-generate"
2165 set cmd_options "$qsysFile --output-directory=$qsysIPDir $commandOpts"
2166 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
2167 Msg Info "Executing: $cmd $cmd_options"
2168 Msg Info "Saving logfile in: $qsysLogFile"
2169 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
2170 set makeRet [
lindex [dict get $opt -errorcode] end]
2171 Msg CriticalWarning "$cmd returned with $makeRet"
2174 Msg Error " Could not execute command $cmd"
2178 set qsysIPFileList [
concat \
2179 [glob -nocomplain -directory $qsysIPDir -types f *.ip *.qip] \
2180 [glob -nocomplain -directory "$qsysIPDir/synthesis" -types f *.ip *.qip *.vhd *.vhdl]
2182 foreach qsysIPFile $qsysIPFileList {
2183 if {[
file exists $qsysIPFile] != 0} {
2185 set_global_assignment -name $qsysIPFileType $qsysIPFile
2187 set IpMd5Sum [
Md5Sum $qsysIPFile]
2189 set fileDir [
file normalize "./hogTmp"]
2190 set fileName "$fileDir/.hogQsys.md5"
2191 if {![
file exists $fileDir]} {
2194 set hogQsysFile [open $fileName "a"]
2195 set fileEntry "$qsysIPFile\t$IpMd5Sum"
2196 puts $hogQsysFile $fileEntry
2201 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
2211 proc GenericToSimulatorString {prop_dict target} {
2213 dict for {theKey theValue} $prop_dict {
2214 set theValue [string trim $theValue]
2223 regexp {([0-9]*)('h)([0-9a-fA-F]*)} $theValue valueHexFull valueNumBits valueHexFlag valueHex
2224 regexp {^([0-9]*)$} $theValue valueIntFull ValueInt
2225 regexp {(?!^\d+$)^.+$} $theValue valueStrFull ValueStr
2226 if {[string tolower $target] == "vivado" || [string tolower $target] == "xsim"} {
2227 if {[string tolower $theValue] == "true" || [string tolower $theValue] == "false"} {
2228 set prj_generics "$prj_generics $theKey=[string tolower $theValue]"
2229 } elseif {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2230 set prj_generics "$prj_generics $theKey=$valueHexFull"
2231 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2232 set prj_generics "$prj_generics $theKey=$ValueInt"
2233 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2234 set prj_generics "$prj_generics $theKey=\"$ValueStr\""
2236 set prj_generics "$prj_generics $theKey=\"$theValue\""
2238 } elseif {[lsearch -exact [GetSimulators] [string tolower $target]] >= 0} {
2239 if {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2241 scan $valueNumBits %d numBits
2243 scan $valueHex %x numHex
2244 binary scan [binary format "I" $numHex] "B*" binval
2245 set numBits [expr {$numBits - 1}]
2246 set numBin [string range $binval end-$numBits end]
2247 set prj_generics "$prj_generics $theKey=\"$numBin\""
2248 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2249 set prj_generics "$prj_generics $theKey=$ValueInt"
2250 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2251 set prj_generics "$prj_generics {$theKey=\"$ValueStr\"}"
2253 set prj_generics "$prj_generics {$theKey=\"$theValue\"}"
2256 Msg Warning "Target : $target not implemented"
2259 return $prj_generics
2267 proc GetConfFiles {proj_dir} {
2268 Msg Debug "GetConfFiles called with proj_dir=$proj_dir"
2269 if {![
file isdirectory $proj_dir]} {
2270 Msg Error "$proj_dir is supposed to be the top project directory"
2273 set conf_file [
file normalize $proj_dir/hog.conf]
2274 set sim_file [
file normalize $proj_dir/sim.conf]
2275 set pre_tcl [
file normalize $proj_dir/pre-creation.tcl]
2276 set post_tcl [
file normalize $proj_dir/post-creation.tcl]
2277 set pre_rtl [
file normalize $proj_dir/pre-rtl.tcl]
2278 set post_rtl [
file normalize $proj_dir/post-rtl.tcl]
2280 return [list $conf_file $sim_file $pre_tcl $post_tcl $pre_rtl $post_rtl]
2289 proc GetCustomCommands {parameters {directory .}} {
2290 set commands_dict [dict create]
2291 set commands_files [glob -nocomplain $directory/*.tcl]
2293 if {[
llength $commands_files] == 0} {
2297 foreach file $commands_files {
2302 if {$custom_cmd eq ""} {
2307 set custom_cmd_name [dict get $custom_cmd NAME]
2309 Msg Debug "Loaded custom command '$custom_cmd_name' from $file"
2312 if {[dict exists $commands_dict $custom_cmd_name]} {
2313 Msg Error "Custom command '$custom_cmd_name' in $file already defined as: \[dict get $commands_dict $custom_cmd_name\]. Skipping."
2319 set custom_cmd_name [
string toupper $custom_cmd_name]
2320 dict set commands_dict $custom_cmd_name $custom_cmd
2323 return $commands_dict
2326 proc SanitizeCustomCommand {cmdDict file parameters} {
2329 foreach k [dict keys $cmdDict] {
2330 set K [
string toupper $k]
2331 dict set normalized $K [dict get $cmdDict $k]
2334 set cmdDict $normalized
2335 if {![dict exists $cmdDict NAME]} {
2336 Msg Error "Custom command in $file missing required key NAME. Skipping."
2339 if {![dict exists $cmdDict SCRIPT]} {
2340 Msg Error "Custom command '$[dict get $cmdDict NAME]' in $file missing SCRIPT. Skipping."
2345 set allowed {NAME DESCRIPTION OPTIONS CUSTOM_OPTIONS SCRIPT IDE NO_EXIT}
2346 foreach k [dict keys $cmdDict] {
2347 if {[lsearch -exact $allowed $k] < 0} {
2348 Msg Warning "Custom command '[dict get $cmdDict NAME]' in $file: unknown key '$k'. Allowed: $allowed. Skipping."
2353 set name [
string trim [dict get $cmdDict NAME]]
2355 Msg Error "Custom command in $file has empty NAME. Skipping."
2359 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]+$} $name]} {
2360 Msg Error "Custom command NAME '$name' (file $file) contains invalid characters."
2364 if {![dict exists $cmdDict DESCRIPTION]} {
2365 dict set cmdDict DESCRIPTION "No description provided."
2369 set hog_parameters {}
2370 foreach p $parameters {
2371 lappend hog_parameters [
lindex $p 0]
2376 if {[dict exists $cmdDict OPTIONS]} {
2377 set raw_opts [dict get $cmdDict OPTIONS]
2378 if {![
llength $raw_opts]} {
2382 foreach item $raw_opts {
2384 foreach p $parameters {
2385 set hog_parameter [
lindex $p 0]
2386 if { $item eq $hog_parameter } {
2387 lappend hog_options $p
2393 Msg Warning "Custom command '$name' in $file: option '$item' not found in Hog parameters. Skipping."
2396 dict set cmdDict OPTIONS $hog_options
2398 dict set cmdDict CUSTOM_OPTIONS {}
2405 if {[dict exists $cmdDict CUSTOM_OPTIONS]} {
2406 set raw_opts [dict get $cmdDict CUSTOM_OPTIONS]
2407 if {![
llength $raw_opts]} {
2410 foreach item $raw_opts {
2412 if {[
llength $item] != 2 && [
llength $item] != 3} {
2413 Msg Error "Bad custom option: \[$item\]. Custom command '$name' in $file: \
2414 each CUSTOM_OPTIONS entry must be {option \"help\"} for flags \
2415 and {option \"default_value\" \"help\"} for options with arguments. Skipping command."
2419 if {[
llength $item] == 2} {
2420 lassign $item opt help
2423 lassign $item opt def help
2426 if { [
IsInList $opt $hog_parameters] == 1 } {
2427 Msg Warning "Custom command '$name' in $file: option '$opt' already defined in Hog parameters. Skipping."
2433 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]*(\.arg)?$} $opt]} {
2434 Msg Error "Custom command '$name' in $file: invalid option name '$opt'."
2439 Msg Warning "Custom command '$name' option '$opt' has empty help text."
2443 dict set cmdDict CUSTOM_OPTIONS {}
2447 if {[dict exists $cmdDict NO_EXIT]} {
2448 set no_exit [dict get $cmdDict NO_EXIT]
2449 set no_exit [
string tolower [
string trim $no_exit]]
2451 if {$no_exit eq "1" || $no_exit eq "true"} {
2457 dict set cmdDict NO_EXIT $no_exit
2459 dict set cmdDict NO_EXIT 0
2465 proc LoadCustomCommandFile {file parameters} {
2467 set dir [
file dirname $file]
2469 unset -nocomplain ::hog_command
2470 set rc [
catch {source $file} err]
2473 Msg Error "Error sourcing custom command file $file: $err"
2476 if {![
info exists ::hog_command]} {
2477 Msg Warning "File $file did not define ::hog_command. Skipping."
2480 set cmdDict $::hog_command
2482 if {[
catch {dict size $cmdDict}]} {
2483 Msg Error "In $file ::hog_command is not a valid dict. Skipping."
2493 proc GetDateAndTime {commit} {
2494 set clock_seconds [
clock seconds]
2497 set date [
Git "log -1 --format=%cd --date=format:%d%m%Y $commit"]
2498 set timee [
Git "log -1 --format=%cd --date=format:00%H%M%S $commit"]
2500 Msg Warning "Found Git version older than 2.9.3. Using current date and time instead of commit time."
2501 set date [
clock format $clock_seconds -format {%d%m%Y}]
2502 set timee [
clock format $clock_seconds -format {00%H%M%S}]
2504 return [list $date $timee]
2516 proc GetFile {file fileset} {
2519 set Files [get_files -all $file -of_object [get_filesets $fileset]]
2520 set f [
lindex $Files 0]
2528 puts "***DEBUG Hog:GetFile $file"
2537 proc GetFileGenerics {filename {entity ""}} {
2539 if {[
string equal $file_type "VERILOG_FILE"] || [
string equal $file_type "SYSTEMVERILOG_FILE"]} {
2541 }
elseif {[
string equal $file_type "VHDL_FILE"]} {
2544 Msg CriticalWarning "Could not determine extension of top level file."
2553 proc GetGenericsFromConf {proj_dir} {
2554 set generics_dict [dict create]
2555 set top_dir "Top/$proj_dir"
2556 set conf_file "$top_dir/hog.conf"
2558 Msg Debug "GetGenericsFromConf called with proj_dir=$proj_dir, top_dir=$top_dir"
2560 if {[
file exists $conf_file]} {
2562 if {[dict exists $properties generics]} {
2563 set generics_dict [dict get $properties generics]
2566 Msg Warning "File $conf_file not found."
2568 return $generics_dict
2581 proc GetSimSets {project_name repo_path {simsets ""} {ghdl 0} {no_conf 0}} {
2582 set simsets_dict [dict create]
2583 set list_dir "$repo_path/Top/$project_name/list"
2585 if {$simsets != ""} {
2586 foreach s $simsets {
2587 set list_file "$list_dir/$s.sim"
2588 if {[
file exists $list_file]} {
2589 lappend list_files $list_file
2590 }
elseif {$s != "sim_1"} {
2591 Msg CriticalWarning "Simulation set list file $list_file not found."
2596 set list_files [glob -nocomplain -directory $list_dir "*.sim"]
2600 set proj_dir [
file normalize $repo_path/Top/$project_name]
2601 set sim_file [
file normalize $proj_dir/sim.conf]
2603 foreach list_file $list_files {
2604 set file_name [
file tail $list_file]
2605 set simset_name [
file rootname $file_name]
2606 set fp [open $list_file r]
2607 set file_data [read $fp]
2609 set data [
split $file_data "\n"]
2611 set firstline [
lindex $data 0]
2613 if {[regexp {^ *\# *Simulator} $firstline]} {
2614 set simulator_prop [regexp -all -inline {\S+} $firstline]
2615 set simulator [
string tolower [
lindex $simulator_prop end]]
2617 Msg Warning "Simulator not set in $simset_name.sim. \
2618 The first line of $simset_name.sim should be #Simulator <SIMULATOR_NAME>,\
2619 where <SIMULATOR_NAME> can be xsim, questa, modelsim, ghdl, riviera, activehdl,\
2620 ies, or vcs, e.g. #Simulator questa.\
2621 Setting simulator by default to xsim."
2622 set simulator "xsim"
2624 if {$simulator eq "skip_simulation"} {
2625 Msg Info "Skipping simulation for $simset_name"
2628 if {($ghdl == 1 && $simulator != "ghdl") || ($ghdl == 0 && $simulator == "ghdl")} {
2632 set SIM_PROPERTIES ""
2633 if {[
file exists $sim_file] && $no_conf == 0} {
2634 set SIM_PROPERTIES [
ReadConf $sim_file]
2637 set global_sim_props [dict create]
2638 dict set global_sim_props "properties" [
DictGet $SIM_PROPERTIES "sim"]
2639 dict set global_sim_props "generics" [
DictGet $SIM_PROPERTIES "generics"]
2640 dict set global_sim_props "hog" [
DictGet $SIM_PROPERTIES "hog"]
2643 set sim_dict [dict create]
2644 dict set sim_dict "simulator" $simulator
2645 if {[dict exists $SIM_PROPERTIES $simset_name]} {
2646 dict set sim_dict "properties" [
DictGet $SIM_PROPERTIES $simset_name]
2647 dict set sim_dict "generics" [
DictGet $SIM_PROPERTIES "$simset_name:generics"]
2648 dict set sim_dict "hog" [
DictGet $SIM_PROPERTIES "$simset_name:hog"]
2649 }
elseif {$no_conf == 0} {
2651 set conf_dict [
ReadConf $list_file]
2652 set sim_dict [
MergeDict $sim_dict $conf_dict 0]
2654 set sim_dict [
MergeDict $sim_dict $global_sim_props 0]
2655 dict set simsets_dict $simset_name $sim_dict
2657 return $simsets_dict
2665 proc GetSimsetGenericsFromConf {proj_dir} {
2666 set simsets_generics_dict [dict create]
2667 set top_dir "Top/$proj_dir"
2668 set conf_file "$top_dir/sim.conf"
2671 if {[
file exists $conf_file]} {
2674 set simsets_generics_dict [dict filter $properties key *:generics]
2676 Msg Warning "File $conf_file not found."
2678 return $simsets_generics_dict
2689 proc GetGroupName {proj_dir repo_dir} {
2690 if {[regexp {^(.*)/(Top|Projects)/+(.*?)/*$} $proj_dir dummy possible_repo_dir proj_or_top dir]} {
2692 if {[
file normalize $repo_dir] eq [
file normalize $possible_repo_dir]} {
2693 set group [
file dir $dir]
2694 if {$group == "."} {
2699 Msg Warning "Project directory $proj_dir seems to be in $possible_repo_dir which is not a the main Git repository $repo_dir."
2702 Msg Warning "Could not parse project directory $proj_dir"
2715 proc GetHogDescribe {proj_dir {repo_path .}} {
2716 lassign [
GetRepoVersions $proj_dir $repo_path] global_commit global_version
2717 if {$global_commit == 0} {
2719 set new_sha "[
string toupper [
GetSHA]]"
2722 set new_sha [
string toupper $global_commit]
2742 proc GetHogFiles {args} {
2745 if {[
catch {
package require cmdline} ERROR]} {
2746 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
2753 {list_files.arg "" "The file wildcard, if not specified all Hog list files will be looked for."}
2754 {sha_mode "Forwarded to ReadListFile, see there for info."}
2755 {ext_path.arg "" "Path for the external libraries forwarded to ReadListFile."}
2756 {print_log "Forwarded to ReadListFile, see there for info."}
2758 set usage "USAGE: GetHogFiles \[options\] <list path> <repository path>"
2759 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
2763 set list_path [
lindex $args 0]
2764 set repo_path [
lindex $args 1]
2766 set list_files $options(list_files)
2767 set sha_mode $options(sha_mode)
2768 set ext_path $options(ext_path)
2769 set print_log $options(print_log)
2771 if {$sha_mode == 1} {
2772 set sha_mode_opt "-sha_mode"
2777 if {$print_log == 1} {
2778 set print_log_opt "-print_log"
2780 set print_log_opt ""
2784 if {$list_files == ""} {
2785 set list_files {.src,.con,.sim,.ext}
2787 set libraries [dict create]
2788 set properties [dict create]
2789 set list_files [glob -nocomplain -directory $list_path "*{$list_files}"]
2790 set filesets [dict create]
2792 foreach f $list_files {
2793 set ext [
file extension $f]
2794 if {$ext == ".ext"} {
2795 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $ext_path"] l p fs
2797 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $repo_path"] l p fs
2800 set properties [
MergeDict $p $properties]
2801 Msg Debug "list file $f, filesets: $fs"
2803 Msg Debug "Merged filesets $filesets"
2811 set proj_conf [
file normalize [
file join [
file dirname $list_path] hog.conf]]
2812 if {[
file exists $proj_conf]} {
2814 set already_tracked [dict create]
2815 dict for {libname libfiles} $libraries {
2816 foreach lf $libfiles { dict set already_tracked [file normalize $lf] 1 }
2818 dict for {comp_name cfg_abs} $hls_configs {
2819 if {[dict exists $already_tracked $cfg_abs]} {
2820 Msg Debug "HLS component '$comp_name': cfg $cfg_abs already tracked via a .src, skipping conf-based GetHogFiles discovery."
2823 set lib_name "${comp_name}.src"
2824 dict lappend libraries $lib_name $cfg_abs
2825 dict set already_tracked $cfg_abs 1
2826 set hls_extras [ExpandHlsConfigFiles $cfg_abs]
2827 Msg Info "HLS component '$comp_name' (from hog.conf): tracking [expr {1 + [llength $hls_extras]}] file(s) via $cfg_abs"
2828 if {$print_log == 1} {
2829 Msg Status "\nhog.conf \[hls:$comp_name\] (auto-discovered)"
2830 set rel_cfg [Relative $repo_path $cfg_abs 1]
2831 if {$rel_cfg eq ""} { set rel_cfg $cfg_abs }
2832 if {[llength $hls_extras] == 0} {
2833 Msg Status "└── $rel_cfg"
2835 Msg Status "├── $rel_cfg"
2836 Msg Status " Inside [file tail $cfg_abs] (HLS auto-expand):"
2839 set n_extras [llength $hls_extras]
2841 foreach hls_extra $hls_extras {
2843 if {[dict exists $already_tracked $hls_extra]} { continue }
2844 dict lappend libraries $lib_name $hls_extra
2845 dict set already_tracked $hls_extra 1
2846 if {$print_log == 1} {
2847 if {$i == $n_extras} { set pad "└──" } else { set pad "├──" }
2848 set rel_extra [Relative [file dirname $cfg_abs] $hls_extra 1]
2849 if {$rel_extra eq ""} { set rel_extra $hls_extra }
2850 Msg Status " $pad $rel_extra"
2856 return [list $libraries $properties $filesets]
2863 proc GetIDECommand {proj_conf {custom_ver ""}} {
2864 if {$custom_ver ne ""} {
2865 set ide_name_and_ver [
string tolower "$custom_ver"]
2866 }
elseif {[
file exists $proj_conf]} {
2867 set ide_name_and_ver [
string tolower [
GetIDEFromConf $proj_conf]]
2869 Msg Error "Configuration file $proj_conf not found."
2872 set ide_name [
lindex [regexp -all -inline {\S+} $ide_name_and_ver] 0]
2874 if {$ide_name eq "vivado" || $ide_name eq "vivado_vitis_classic" || $ide_name eq "vivado_vitis_unified" || $ide_name eq "vitis_unified"} {
2875 set command "vivado"
2877 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2878 set after_tcl_script " -tclargs "
2880 }
elseif {$ide_name eq "planahead"} {
2881 set command "planAhead"
2883 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2884 set after_tcl_script " -tclargs "
2886 }
elseif {$ide_name eq "quartus"} {
2887 set command "quartus_sh"
2889 set before_tcl_script " -t "
2890 set after_tcl_script " "
2892 }
elseif {$ide_name eq "libero"} {
2895 set command "libero"
2896 set before_tcl_script "SCRIPT:"
2897 set after_tcl_script " SCRIPT_ARGS:\""
2899 }
elseif {$ide_name eq "diamond"} {
2900 set command "diamondc"
2901 set before_tcl_script " "
2902 set after_tcl_script " "
2904 }
elseif {$ide_name eq "vitis_classic"} {
2907 set before_tcl_script ""
2908 set after_tcl_script " "
2910 }
elseif {$ide_name eq "ghdl"} {
2912 set before_tcl_script " "
2913 set after_tcl_script " "
2916 Msg Error "IDE: $ide_name not known."
2919 return [list $command $before_tcl_script $after_tcl_script $end_marker]
2925 proc GetIDEFromConf {conf_file} {
2926 set f [open $conf_file "r"]
2929 if {[regexp -all {^\# *(\w*) *(vitis_(?:classic|unified))? *(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)?(_.*)? *$} $line dummy ide vitisflag version patch]} {
2930 if {[
info exists vitisflag] && $vitisflag != ""} {
2931 set ide "${ide}_${vitisflag}"
2934 if {[
info exists version] && $version != ""} {
2940 set ret [list $ide $ver]
2942 Msg CriticalWarning "The first line of hog.conf should be \#<IDE name> <version>, \
2943 where <IDE name>. is quartus, vivado, planahead, libero, diamond or ghdl, \
2944 and <version> the tool version, e.g. \#vivado 2020.2. Will assume vivado."
2945 set ret [list "vivado" "0.0.0"]
2952 proc GetIDEName {} {
2954 return "ISE/PlanAhead"
2972 proc GetIDEVersion {} {
2975 regexp {\d+\.\d+(\.\d+)?} [version -short] ver
2980 regexp {[\.0-9]+} $quartus(version) ver
2983 set ver [get_libero_version]
2986 regexp {\d+\.\d+(\.\d+)?} [sys_install version] ver
2989 regexp {\d+\.\d+(\.\d+)?} [version] ver
2992 set vitis_output [
exec vitis --version 2>@1]
2993 regexp {[Vv]itis\s+v?(\d+\.\d+(?:\.\d+)?)} $vitis_output -> ver
3007 proc GetLinkedFile {link_file} {
3008 if {[
file type $link_file] eq "link"} {
3009 if {[
OS] == "windows"} {
3011 lassign [
ExecuteRet realpath $link_file] ret msg
3012 lassign [
ExecuteRet cygpath -m $msg] ret2 msg2
3013 if {$ret == 0 && $ret2 == 0} {
3015 Msg Debug "Found link file $link_file on Windows, the linked file is: $real_file"
3017 Msg CriticalWarning "[
file normalize $link_file] is a soft link. \
3018 Soft link are not supported on Windows and readlink.exe or cygpath.exe did not work: readlink=$ret: $msg, cygpath=$ret2: $msg2."
3019 set real_file $link_file
3023 set linked_file [
file link $link_file]
3024 set real_file [
file normalize [
file dirname $link_file]/$linked_file]
3027 if {![
file exists $real_file]} {
3028 Msg Warning "$link_file is a broken link, because the linked file: $real_file does not exist."
3031 Msg Warning "$link file is not a soft link"
3032 set real_file $link_file
3045 proc GetMaxThreads {proj_dir} {
3047 if {[
file exists $proj_dir/hog.conf]} {
3049 if {[dict exists $properties parameters]} {
3050 set propDict [dict get $properties parameters]
3051 if {[dict exists $propDict MAX_THREADS]} {
3052 set maxThreads [dict get $propDict MAX_THREADS]
3056 Msg Warning "File $proj_dir/hog.conf not found. Max threads will be set to default value 1"
3069 proc GetModifiedFiles {{repo_path "."} {pattern "."}} {
3072 set ret [
Git "ls-files --modified $pattern"]
3081 proc GetOptions {argv parameters} {
3084 set param_list [list]
3085 set option_list [list]
3087 foreach p $parameters {
3088 lappend param_list [
lindex $p 0]
3092 while {$index < [
llength $argv]} {
3093 set arg [
lindex $argv $index]
3094 if {[
string first - $arg] == 0} {
3095 set option [
string trimleft $arg "-"]
3097 lappend option_list $arg
3098 if {[lsearch -regex $param_list "$option\[.arg]?"] >= 0 } {
3099 if {[lsearch -regex $param_list "$option\[.arg]"] >= 0 } {
3100 lappend option_list [
lindex $argv $index]
3105 lappend arg_list $arg
3109 Msg Debug "Argv: $argv"
3110 Msg Debug "Options: $option_list"
3111 Msg Debug "Arguments: $arg_list"
3112 return [list $option_list $arg_list]
3130 proc GetProjectFiles {{project_file ""}} {
3131 set libraries [dict create]
3132 set simlibraries [dict create]
3133 set constraints [dict create]
3134 set properties [dict create]
3135 set consets [dict create]
3136 set srcsets [dict create]
3137 set simsets [dict create]
3140 set all_filesets [get_filesets]
3141 set simulator [get_property target_simulator [current_project]]
3142 set top [get_property "top" [current_fileset]]
3144 dict lappend properties $topfile "top=$top"
3146 foreach fs $all_filesets {
3147 if {$fs == "utils_1"} {
3152 set all_files [get_files -quiet -of_objects [get_filesets $fs]]
3153 set fs_type [get_property FILESET_TYPE [get_filesets $fs]]
3155 if {$fs_type == "BlockSrcs"} {
3157 set dict_fs "sources_1"
3161 foreach f $all_files {
3168 if {[
lindex [get_property IS_GENERATED [
GetFile $f $fs]] 0] != 0} {
3173 if {[get_property FILE_TYPE [
GetFile $f $fs]] == "Configuration Files"} {
3179 if {[get_property CORE_CONTAINER [
GetFile $f $fs]] != ""} {
3180 if {[
file extension $f] == ".xcix"} {
3181 set f [get_property CORE_CONTAINER [
GetFile $f $fs]]
3189 if {[get_property SCOPED_TO_REF [
GetFile $f $fs]] != ""} {
3190 dict lappend properties $f "scoped_to_ref=[get_property SCOPED_TO_REF [
GetFile $f $fs]]"
3195 if {[get_property SCOPED_TO_CELLS [
GetFile $f $fs]] != ""} {
3196 dict lappend properties $f "scoped_to_cells=[regsub " " [get_property SCOPED_TO_CELLS [
GetFile $f $fs]] ","]"
3200 if {[
IsInList "PARENT_COMPOSITE_FILE" [list_property [
GetFile $f $fs]]]} {
3205 if {[
file tail $f] == "nocattrs.dat"} {
3210 if {[
file extension $f] != ".coe"} {
3211 set f [
file normalize $f]
3214 set type [get_property FILE_TYPE [
GetFile $f $fs]]
3216 set lib [get_property -quiet LIBRARY [
GetFile $f $fs]]
3219 Msg Debug "File $f Extension [
file extension $f] Type [
lindex $type 0]"
3221 if {[
string equal [
lindex $type 0] "VHDL"] && [
llength $type] == 1} {
3223 }
elseif {[
string equal [
lindex $type 0] "Block"] && [
string equal [
lindex $type 1] "Designs"]} {
3226 }
elseif {[
string equal $type "SystemVerilog"] && [
file extension $f] != ".sv" && [
file extension $f] != ".svp"} {
3227 set prop "SystemVerilog"
3228 }
elseif {[
string equal [
lindex $type 0] "XDC"] && [
file extension $f] != ".xdc"} {
3230 }
elseif {[
string equal $type "Verilog Header"] && [
file extension $f] != ".vh" && [
file extension $f] != ".svh"} {
3231 set prop "verilog_header"
3232 }
elseif {[
string equal $type "Verilog Template"] && [
file extension $f] == ".v" && [
file extension $f] != ".sv"} {
3233 set prop "verilog_template"
3235 set type [
lindex $type 0]
3239 if {![
string equal $prop ""]} {
3240 dict lappend properties $f $prop
3243 if {[
string equal $fs_type "SimulationSrcs"]} {
3245 if {[
string equal $type "VHDL"]} {
3246 set library "${lib}.sim"
3248 set library "others.sim"
3252 dict lappend simsets $dict_fs $library
3255 dict lappend simlibraries $library $f
3256 }
elseif {[
string equal $type "VHDL"]} {
3259 dict lappend srcsets $dict_fs "${lib}.src"
3261 dict lappend libraries "${lib}.src" $f
3262 }
elseif {[
string first "IP" $type] != -1} {
3265 dict lappend srcsets $dict_fs "ips.src"
3267 dict lappend libraries "ips.src" $f
3268 Msg Debug "Appending $f to ips.src"
3269 }
elseif {[
string equal $fs_type "Constrs"]} {
3272 dict lappend consets $dict_fs "sources.con"
3274 dict lappend constraints "sources.con" $f
3278 dict lappend srcsets $dict_fs "others.src"
3280 dict lappend libraries "others.src" $f
3281 Msg Debug "Appending $f to others.src"
3284 if {[
lindex [get_property -quiet used_in_synthesis [
GetFile $f $fs]] 0] == 0} {
3285 dict lappend properties $f "nosynth"
3287 if {[
lindex [get_property -quiet used_in_implementation [
GetFile $f $fs]] 0] == 0} {
3288 dict lappend properties $f "noimpl"
3290 if {[
lindex [get_property -quiet used_in_simulation [
GetFile $f $fs]] 0] == 0} {
3291 dict lappend properties $f "nosim"
3293 if {[
lindex [get_property -quiet IS_MANAGED [
GetFile $f $fs]] 0] == 0 && [
file extension $f] != ".xcix"} {
3294 dict lappend properties $f "locked"
3300 dict lappend properties "Simulator" [get_property target_simulator [current_project]]
3303 set file [open $project_file r]
3304 set in_file_manager 0
3306 while {[
gets $file line] >= 0} {
3308 if {[regexp {^KEY ActiveRoot \"([^\"]+)\"} $line -> value]} {
3309 set top [
string range $value 0 [
expr {[string first "::" $value] - 1}]]
3313 if {[regexp {^LIST FileManager} $line]} {
3314 set in_file_manager 1
3319 if {$in_file_manager && [regexp {^ENDLIST} $line]} {
3324 if {$in_file_manager && [regexp {^VALUE \"([^\"]+)} $line -> value]} {
3327 lassign [
split $value ,] file_path file_type
3330 set library "others"
3331 while {[
gets $file line] >= 0} {
3332 if {$line == "ENDFILE"} {
3335 regexp {^LIBRARY=\"([^\"]+)} $line -> library
3336 regexp {^PARENT=\"([^\"]+)} $line -> parent_file
3338 Msg Debug "Found file ${file_path} in project.."
3339 if {$parent_file == ""} {
3340 if {$file_type == "hdl"} {
3342 if {[
IsInList "${library}.src" [
DictGet $srcsets "sources_1"]] == 0} {
3343 dict lappend srcsets "sources_1" "${library}.src"
3345 dict lappend libraries "${library}.src" $file_path
3349 if {[
GetModuleName $file_path] == [
string tolower $top] && $top != ""} {
3350 Msg Debug "Found top module $top in $file_path"
3351 dict lappend properties $file_path "top=$top"
3353 }
elseif {$file_type == "tb_hdl"} {
3355 dict lappend simsets "sim_1" "${library}.sim"
3357 dict lappend simlibraries "${library}.sim" $file_path
3358 }
elseif {$file_type == "io_pdc" || $file_type == "sdc"} {
3360 dict lappend consets "constrs_1" "sources.con"
3362 dict lappend constraints "sources.con" $file_path
3369 set fileData [read [open $project_file]]
3371 set project_path [
file dirname $project_file]
3374 regsub {<\?xml.*\?>} $fileData "" fileData
3377 regexp {<Implementation.*?>(.*)</Implementation>} $fileData -> implementationContent
3381 set sourceRegex {<Source name="([^"]*?)" type="([^"]*?)" type_short="([^"]*?)".*?>(.*?)</Source>}
3383 set optionsRegex {<Options(.*?)\/>}
3384 regexp $optionsRegex $implementationContent -> prj_options
3385 foreach option $prj_options {
3386 if {[regexp {^top=\"([^\"]+)\"} $option match result]} {
3391 while {[regexp $sourceRegex $implementationContent match name type type_short optionsContent]} {
3392 Msg Debug "Found file ${name} in project..."
3393 set file_path [
file normalize $project_path/$name]
3395 set optionsRegex {<Options(.*?)\/>}
3396 regexp $optionsRegex $optionsContent -> options
3397 set library "others"
3399 foreach option $options {
3400 if {[
string first "System Verilog" $option]} {
3403 if {[regexp {^lib=\"([^\"]+)\"} $option match1 result]} {
3408 if {[regexp {syn_sim="([^"]*?)"} $match match_sim simonly]} {
3413 if {$type_short == "VHDL" || $type_short == "Verilog" || $type_short == "IPX"} {
3414 if {$ext == ".src"} {
3415 if {[
IsInList "${library}${ext}" [
DictGet $srcsets "sources_1"]] == 0} {
3416 dict lappend srcsets "sources_1" "${library}${ext}"
3418 dict lappend libraries "${library}${ext}" $file_path
3419 }
elseif {$ext == ".sim"} {
3421 dict lappend simsets "sim_1" "${library}.sim"
3423 dict lappend simlibraries "${library}.sim" $file_path
3429 Msg Debug "Found top module $top in $file_path"
3430 dict lappend properties $file_path "top=$top"
3432 }
elseif {$type_short == "SDC"} {
3434 dict lappend consets "constrs_1" "sources.con"
3436 dict lappend constraints "sources.con" $file_path
3440 regsub -- $match $implementationContent "" implementationContent
3443 return [list $libraries $properties $simlibraries $constraints $srcsets $simsets $consets]
3450 proc GetProjectFlavour {proj_name} {
3452 set flavour [
string map {. ""} [
file extension $proj_name]]
3453 if {$flavour != ""} {
3454 if {[
string is integer $flavour]} {
3455 Msg Info "Project $proj_name has flavour = $flavour, the generic variable FLAVOUR will be set to $flavour"
3457 Msg Warning "Project name has a unexpected non numeric extension, flavour will be set to -1"
3474 proc GetProjectVersion {proj_dir repo_path {ext_path ""} {sim 0}} {
3475 if {![
file exists $proj_dir]} {
3476 Msg CriticalWarning "$proj_dir not found"
3483 lassign [
GitRet {describe --tags --abbrev=0 --match "v*"}] ret result
3485 Msg CriticalWarning "No Hog versioning tags (v*) in repo"
3492 Msg Warning "Repository is not clean"
3500 Msg Debug "Project version $v_proj, latest tag $v_last"
3502 Msg Debug "The specified project was modified since official version."
3509 Msg Info "The specified project was modified in the latest official version $ret"
3510 }
elseif {$comp == -1} {
3511 Msg Info "The specified project was modified in a past official version $ret"
3527 proc GetRepoVersions {proj_dir repo_path {ext_path ""} {sim 0}} {
3528 if {[
catch {
package require cmdline} ERROR]} {
3529 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
3544 lappend SHAs [
GetSHA {Hog}]
3548 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
3549 Msg Debug "Hog submodule [
pwd] clean."
3550 lassign [
GetVer ./] hog_ver hog_hash
3552 Msg CriticalWarning "Hog submodule [
pwd] not clean, commit hash will be set to 0."
3553 set hog_hash "0000000"
3554 set hog_ver "00000000"
3560 foreach conf_file $conf_files {
3561 if {[
string match "$repo_path/*" $conf_file]} {
3562 set conf_file [
string replace $conf_file 0 [
string length $repo_path]]
3564 lappend project_files $conf_file
3567 lappend project_files Hog
3570 lassign [
GetVer [
join $conf_files]] top_ver top_hash
3571 lappend SHAs $top_hash
3572 lappend versions $top_ver
3579 lassign [
GetHogFiles -list_files "*.src" -sha_mode "$proj_dir/list/" $repo_path] src_files dummy
3580 dict for {f files} $src_files {
3581 # library names have a .src extension in values returned by GetHogFiles
3582 set name [file rootname [file tail $f]]
3583 if {[file ext $f] == ".oth"} {
3586 lassign [GetVer $files] ver hash
3587 # Msg Info "Found source list file $f, version: $ver commit SHA: $hash"
3589 lappend versions $ver
3591 lappend hashes $hash
3593 set relative_files ""
3594 foreach fil $files {
3595 if {[string match "$repo_path/*" $fil]} {
3596 set fil [string replace $fil 0 [string length $repo_path]]
3598 lappend relative_files $fil
3601 lappend project_files $f {*}$relative_files
3607 lassign [
GetHogFiles -list_files "*.con" -sha_mode "$proj_dir/list/" $repo_path] cons_files dummy
3608 dict for {f files} $cons_files {
3609 #library names have a .con extension in values returned by GetHogFiles
3610 set name [file rootname [file tail $f]]
3611 lassign [GetVer $files] ver hash
3612 #Msg Info "Found constraint list file $f, version: $ver commit SHA: $hash"
3614 Msg CriticalWarning "Constraints file $f not found in Git."
3616 lappend cons_hashes $hash
3618 lappend versions $ver
3619 set relative_files ""
3620 foreach fil $files {
3621 if {[string match "$repo_path/*" $fil]} {
3622 set fil [string replace $fil 0 [string length $repo_path]]
3624 lappend relative_files $fil
3626 lappend project_files $f {*}$relative_files
3633 lassign [
GetHogFiles -list_files "*.sim" -sha_mode "$proj_dir/list/" $repo_path] sim_files dummy
3634 dict for {f files} $sim_files {
3635 #library names have a .sim extension in values returned by GetHogFiles
3636 set name [file rootname [file tail $f]]
3637 lassign [GetVer $files] ver hash
3638 #Msg Info "Found simulation list file $f, version: $ver commit SHA: $hash"
3639 lappend sim_hashes $hash
3641 lappend versions $ver
3642 set relative_files ""
3643 foreach fil $files {
3644 if {[string match "$repo_path/*" $fil]} {
3645 set fil [string replace $fil 0 [string length $repo_path]]
3647 lappend relative_files $fil
3649 lappend project_files $f {*}$relative_files
3657 Msg CriticalWarning "No hashes found for constraints files (not in git)"
3660 set cons_hash [
string tolower [
Git "log --format=%h -1 $cons_hashes"]]
3667 set ext_files [glob -nocomplain "$proj_dir/list/*.ext"]
3670 foreach f $ext_files {
3671 set name [
file rootname [
file tail $f]]
3674 lappend ext_names $name
3675 lappend ext_hashes $hash
3678 lappend versions $ext_ver
3679 lappend project_files $f
3682 set file_data [read $fp]
3684 set data [
split $file_data "\n"]
3686 foreach line $data {
3687 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
3689 set file_and_prop [regexp -all -inline {\S+} $line]
3690 set hdlfile [
lindex $file_and_prop 0]
3691 set hdlfile $ext_path/$hdlfile
3692 if {[
file exists $hdlfile]} {
3693 set hash [
lindex $file_and_prop 1]
3694 set current_hash [
Md5Sum $hdlfile]
3695 if {[
string first $hash $current_hash] == -1} {
3696 Msg CriticalWarning "File $hdlfile has a wrong hash. Current checksum: $current_hash, expected: $hash"
3704 if {[
llength [glob -nocomplain $proj_dir/list/*.ipb]] > 0} {
3706 lassign [
GetHogFiles -list_files "*.ipb" -sha_mode "$proj_dir/list/" $repo_path] xml_files dummy
3707 set xml_source_files [dict get $xml_files "xml.ipb"]
3708 lassign [
GetVer $xml_source_files] xml_ver xml_hash
3709 lappend SHAs $xml_hash
3710 lappend versions $xml_ver
3711 set relative_files ""
3712 foreach fil $xml_source_files {
3713 if {[
string match "$repo_path/*" $fil]} {
3714 set fil [
string replace $fil 0 [
string length $repo_path]]
3716 lappend relative_files $fil
3718 lappend project_files {*}[glob $proj_dir/list/*.ipb] {*}$relative_files
3721 Msg Debug "This project does not use IPbus XMLs"
3726 set user_ip_repos ""
3727 set user_ip_repo_hashes ""
3728 set user_ip_repo_vers ""
3730 if {[
file exists [
lindex $conf_files 0]]} {
3731 set PROPERTIES [
ReadConf [
lindex $conf_files 0]]
3732 if {[dict exists $PROPERTIES main]} {
3733 set main [dict get $PROPERTIES main]
3734 dict for {p v} $main {
3735 if {[string tolower $p] == "ip_repo_paths"} {
3737 if {[file isdirectory "$repo_path/$repo"]} {
3738 set repo_file_list [glob -nocomplain "$repo_path/$repo/*"]
3739 if {[llength $repo_file_list] == 0} {
3740 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3742 lappend user_ip_repos "$repo_path/$repo"
3751 foreach repo $user_ip_repos {
3752 if {[
file isdirectory $repo]} {
3753 set repo_file_list [glob -nocomplain "$repo/*"]
3754 if {[
llength $repo_file_list] != 0} {
3755 lassign [
GetVer $repo] ver sha
3756 lappend user_ip_repo_hashes $sha
3757 lappend user_ip_repo_vers $ver
3758 lappend versions $ver
3759 lappend project_files $repo
3761 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3764 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory does not exist."
3779 append dirty [
Git "status --untracked-files=no --porcelain" $project_files]
3783 Msg Debug "Project-relevant files are clean."
3786 Msg CriticalWarning "Project-relevant files not clean, commit hash and version will be set to 0."
3793 while {$found == 0} {
3794 set global_commit [
Git "log --format=%h -1 --abbrev=7 $SHAs"]
3799 if {$common_child == 0} {
3800 Msg CriticalWarning "The commit $sha is not an ancestor of the global commit $global_commit, which is OK. \
3801 But $sha and $global_commit do not have any common child, which is NOT OK. \
3802 This is probably do to a REBASE that is forbidden in Hog methodology as it changes git history. \
3803 Hog cannot guarantee the accuracy of the SHAs. \
3804 A way to fix this is to make a commit that touches all the projects in the repositories (e.g. change the Hog version), \
3805 but please do not rebase in the official branches in the future."
3807 Msg Info "The commit $sha is not an ancestor of the global commit $global_commit, adding the first common child $common_child instead..."
3808 lappend SHAs $common_child
3818 set global_commit "0000000"
3819 set global_version "00000000"
3824 set top_hash [
format %+07s $top_hash]
3825 set cons_hash [
format %+07s $cons_hash]
3826 return [list $global_commit $global_version \
3827 $hog_hash $hog_ver $top_hash $top_ver \
3828 $libs $hashes $vers $cons_ver $cons_hash \
3829 $ext_names $ext_hashes $xml_hash $xml_ver \
3830 $user_ip_repos $user_ip_repo_hashes $user_ip_repo_vers]
3839 proc GetSHA {{path ""}} {
3842 lassign [
GitRet {log --format=%h --abbrev=7 -1}] status result
3844 return [
string tolower $result]
3846 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3852 set repo_path [
lindex [
Git {rev-parse --show-toplevel}] 0]
3857 set file_in_module 0
3858 if {[
file exists .gitmodules]} {
3859 lassign [
GitRet "config --file .gitmodules --get-regexp path"] status result
3861 set submodules [
split $result "\n"]
3864 Msg Warning "Something went wrong while trying to find submodules: $result"
3867 foreach mod $submodules {
3868 set module [
lindex $mod 1]
3869 if {[
string first "$repo_path/$module" $f] == 0} {
3871 set file_in_module 1
3872 lappend paths "$module"
3877 if {$file_in_module == 0} {
3883 lassign [
GitRet {log --format=%h --abbrev=7 -1} $paths] status result
3885 return [
string tolower $result]
3887 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3891 return [
string tolower $result]
3895 proc GetSimulators {} {
3896 set SIMULATORS [list "modelsim" "questa" "riviera" "activehdl" "ies" "vcs"]
3901 proc GetTopFile {} {
3903 set compile_order_prop [get_property source_mgmt_mode [current_project]]
3904 if {$compile_order_prop ne "All"} {
3905 Msg CriticalWarning "Compile order is not set to automatic, setting it now..."
3906 set_property source_mgmt_mode All [current_project]
3907 update_compile_order -fileset sources_1
3909 return [
lindex [get_files -quiet -compile_order sources -used_in synthesis -filter {FILE_TYPE =~ "VHDL*" || FILE_TYPE =~ "*Verilog*" }] end]
3910 }
elseif {[
IsISE]} {
3911 debug::design_graph_mgr -create [current_fileset]
3912 debug::design_graph -add_fileset [current_fileset]
3913 debug::design_graph -update_all
3914 return [
lindex [debug::design_graph -get_compile_order] end]
3916 Msg Error "GetTopFile not yet implemented for this IDE"
3921 proc GetTopModule {} {
3923 return [get_property top [current_fileset]]
3925 Msg Error "GetTopModule not yet implemented for this IDE"
3935 proc GetVer {path {force_develop 0}} {
3939 Msg CriticalWarning "Empty SHA found for ${path}. Commit to Git to resolve this warning."
3942 set p [
lindex $path 0]
3943 if {[
file isdirectory $p]} {
3946 cd [
file dirname $p]
3948 set repo_path [
Git {rev-parse --show-toplevel}]
3951 return [list [
GetVerFromSHA $SHA $repo_path $force_develop] $SHA]
3962 proc GetVerFromSHA {SHA repo_path {force_develop 0} } {
3964 Msg CriticalWarning "Empty SHA found"
3967 lassign [
GitRet "tag --sort=creatordate --contain $SHA -l v*.*.* -l b*v*.*.*"] status result
3970 if {[regexp {^ *$} $result]} {
3972 lassign [
GitRet "log --oneline --pretty=\"%d\""] status2 tag_list
3975 set pattern {tag: v\d+\.\d+\.\d+}
3976 set real_tag_list {}
3977 foreach x $tag_list {
3978 set x_untrimmed [regexp -all -inline $pattern $x]
3979 regsub "tag: " $x_untrimmed "" x_trimmed
3980 set tt [
lindex $x_trimmed 0]
3981 if {![
string equal $tt ""]} {
3982 lappend real_tag_list $tt
3986 Msg Debug "Cleaned up list: $real_tag_list."
3988 set sorted_tags [lsort -decreasing -command CompareVersions $real_tag_list]
3990 Msg Debug "Sorted Tag list: $sorted_tags"
3992 set tag [
lindex $sorted_tags 0]
3995 set pattern {v\d+\.\d+\.\d+}
3996 if {![regexp $pattern $tag]} {
3997 Msg CriticalWarning "No Hog version tags found in this repository."
4002 set repo_conf $repo_path/Top/repo.conf
4006 set hotfix_prefix "hotfix/"
4007 set minor_prefix "minor_version/"
4008 set major_prefix "major_version/"
4010 set enable_develop_branch $force_develop
4012 set branch_name [
Git {rev-parse --abbrev-ref HEAD}]
4014 if {[
file exists $repo_conf]} {
4015 set PROPERTIES [
ReadConf $repo_conf]
4017 if {[dict exists $PROPERTIES main]} {
4018 set mainDict [dict get $PROPERTIES main]
4021 if {[dict exists $mainDict ENABLE_DEVELOP_BRANCH]} {
4022 set enable_develop_branch [dict get $mainDict ENABLE_DEVELOP_BRANCH]
4028 if {[dict exists $PROPERTIES prefixes]} {
4029 set prefixDict [dict get $PROPERTIES prefixes]
4031 if {[dict exists $prefixDict HOTFIX]} {
4032 set hotfix_prefix [dict get $prefixDict HOTFIX]
4034 if {[dict exists $prefixDict MINOR_VERSION]} {
4035 set minor_prefix [dict get $prefixDict MINOR_VERSION]
4037 if {[dict exists $prefixDict MAJOR_VERSION]} {
4038 set major_prefix [dict get $prefixDict MAJOR_VERSION]
4044 if {[
string match "HEAD" $branch_name]} {
4045 Msg Debug "Detached HEAD detected - attempting to find branch name"
4050 set log_refs [
Git {show -s --pretty=%D HEAD}]
4051 set branch_list [
split $log_refs ","]
4052 Msg Debug "list of possible branch refs $log_refs"
4058 set match_prefixes [list $hotfix_prefix $minor_prefix $major_prefix]
4059 set prev_branch_name $branch_name
4061 foreach br $branch_list {
4062 foreach pr $match_prefixes {
4063 if {[
string match "$pr*" [
string trim $br]]} {
4064 set branch_name [
string trim $br]
4070 if {!$match_count == 1} {
4071 set branch_name $prev_branch_name
4072 Msg Debug "Branch name not found. Using $branch_name"
4074 Msg Debug "Branch name found: $branch_name"
4078 if {$enable_develop_branch == 1} {
4079 if {[
string match "$hotfix_prefix*" $branch_name]} {
4084 if {[
string match "$major_prefix*" $branch_name]} {
4086 set version_level major
4087 }
elseif {[
string match "$minor_prefix*" $branch_name] || ($enable_develop_branch == 1 && $is_hotfix == 0)} {
4089 set version_level minor
4092 set version_level patch
4096 Msg CriticalWarning "Tag $tag does not contain a Hog compatible version in this repository."
4099 }
elseif {$mr == 0} {
4100 switch $version_level {
4115 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."
4121 set vers [
split $result "\n"]
4122 set ver [
lindex $vers 0]
4124 if {[regexp {^v.*$} $v]} {
4132 Msg CriticalWarning "Error while trying to find tag for $SHA"
4140 set M [
format %02X $M]
4141 set m [
format %02X $m]
4142 set c [
format %04X $c]
4143 }
elseif {$M > -1} {
4145 set M [
format %02X $M]
4146 set m [
format %02X $m]
4147 set c [
format %04X $c]
4149 Msg Warning "Tag does not contain a properly formatted version: $ver"
4150 set M [
format %02X 0]
4151 set m [
format %02X 0]
4152 set c [
format %04X 0]
4165 proc Git {command {files ""}} {
4166 lassign [
GitRet $command $files] ret result
4168 Msg Error "Code $ret returned by git running: $command -- $files"
4179 proc GetModuleName {filename} {
4181 if {![
file exists $filename]} {
4182 Msg CriticalWarning "Error: File $filename does not exist."
4187 set fileId [open $filename r]
4190 set file_content [read $fileId]
4196 if {[
file extension $filename] == ".vhd" || [
file extension $filename] == ".vhdl"} {
4198 set file_content [
string tolower $file_content]
4200 set pattern {(?m)^\s*entity\s+(\S+)\s+is}
4201 }
elseif {[
file extension $filename] == ".v" || [
file extension $filename] == ".sv"} {
4203 set pattern {\n\s*module\s*(\w+)(\s*|\(|\n)}
4205 Msg Debug "File is neither VHDL nor Verilog... Returning empty string..."
4210 if {[regexp $pattern $file_content match module_name]} {
4213 Msg Debug "No module was found in $filename. Returning an empty string..."
4221 proc GetVerilogGenerics {file} {
4222 set fp [open $file r]
4228 foreach line [
split $data "\n"] {
4229 regsub "^\\s*\/\/.*" $line "" line
4230 regsub "(.*)\/\/.*" $line {\1} line
4231 if {![
string equal $line ""]} {
4232 append lines $line " "
4237 regsub -all {/\*.*\*/} $lines "" lines
4240 set punctuation [list]
4241 foreach char [list "(" ")" ";" "," " " "!" "<=" ":=" "=" "\[" "\]"] {
4242 lappend punctuation $char "\000$char\000"
4246 set tokens [
split [
string map $punctuation $lines] \000]
4248 set parameters [dict create]
4257 foreach token $tokens {
4258 set token [
string trim $token]
4259 if {![
string equal "" $token]} {
4260 if {[
string equal [
string tolower $token] "parameter"]} {
4261 set state $PARAM_NAME
4262 }
elseif {[
string equal $token ")"] || [
string equal $token ";"]} {
4264 }
elseif {$state == $PARAM_WIDTH} {
4265 if {[
string equal $token "\]"]} {
4266 set state $PARAM_NAME
4268 }
elseif {$state == $PARAM_VALUE} {
4269 if {[
string equal $token ","]} {
4270 set state $PARAM_NAME
4271 }
elseif {[
string equal $token ";"]} {
4276 }
elseif {$state == $PARAM_NAME} {
4277 if {[
string equal $token "="]} {
4278 set state $PARAM_VALUE
4279 }
elseif {[
string equal $token "\["]} {
4280 set state $PARAM_WIDTH
4281 }
elseif {[
string equal $token ","]} {
4282 set state $PARAM_NAME
4283 }
elseif {[
string equal $token ";"]} {
4285 }
elseif {[
string equal $token ")"]} {
4288 dict set parameters $token "integer"
4301 proc GetVhdlGenerics {file {entity ""}} {
4302 set fp [open $file r]
4308 foreach line [
split $data "\n"] {
4309 regsub "^\\s*--.*" $line "" line
4310 regsub "(.*)--.*" $line {\1} line
4311 if {![
string equal $line ""]} {
4312 append lines $line " "
4317 set generic_block ""
4318 set generics [dict create]
4320 if {1 == [
string equal $entity ""]} {
4321 regexp {(?i).*entity\s+([^\s]+)\s+is} $lines _ entity
4324 set generics_regexp "(?i).*entity\\s+$entity\\s+is\\s+generic\\s*\\((.*)\\)\\s*;\\s*port.*end.*$entity"
4326 if {[regexp $generics_regexp $lines _ generic_block]} {
4328 foreach line [
split $generic_block ";"] {
4330 regexp {(.*):\s*([A-Za-z0-9_]+).*} $line _ generic type
4333 set splits [
split $generic ","]
4334 foreach split $splits {
4335 dict set generics [
string trim $split] [
string trim $type]
4343 proc GHDL {command logfile} {
4344 set ret [
catch {
exec -ignorestderr ghdl {*}$command >>& $logfile} result options]
4349 return [list $ret $result]
4361 proc GitRet {command {files ""}} {
4364 set ret [
catch {
exec -ignorestderr git {*}$command} result]
4366 set ret [
catch {
exec -ignorestderr git {*}$command -- {*}$files} result]
4368 return [list $ret $result]
4376 proc GitVersion {target_version} {
4377 set ver [
split $target_version "."]
4378 set v [
Git --version]
4380 set current_ver [
split [
lindex $v 2] "."]
4381 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
4382 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
4383 return [
expr {$target <= $current}]
4398 proc HandleIP {what_to_do xci_file ip_path repo_path {gen_dir "."} {force 0}} {
4400 if {!($what_to_do eq "push") && !($what_to_do eq "pull")} {
4401 Msg Error "You must specify push or pull as first argument."
4404 if {[
catch {
package require tar} TARPACKAGE]} {
4405 Msg CriticalWarning "Cannot find package tar. You can fix this by installing package \"tcllib\""
4416 if {[regexp {^[^/]+:} $ip_path]} {
4420 lassign [
ExecuteRet rclone --version] rclone_ret rclone_ver
4421 if {$rclone_ret != 0} {
4422 Msg CriticalWarning "Rclone path specified but rclone not found or failed: $rclone_ver"
4426 Msg Info "IP remote directory path, on Rclone, is set to: $ip_path"
4428 if {[
info exists env(HOG_RCLONE_CONFIG)]} {
4429 Msg Info "Using rclone config from environment variable HOG_RCLONE_CONFIG: $env(HOG_RCLONE_CONFIG)"
4430 set config_path $env(HOG_RCLONE_CONFIG)
4432 set config_path "/dev/null"
4433 Msg Info "Environment variable HOG_RCLONE_CONFIG not set, using rclone environmental variables..."
4436 set remote_name "[
lindex [
split $ip_path ":"] 0]:"
4437 lassign [
ExecuteRet rclone listremotes --config $config_path] rclone_list_ret remotes
4438 if {$rclone_list_ret != 0} {
4439 Msg CriticalWarning "Could not list rclone remotes: $remotes"
4443 if {![
IsInList $remote_name $remotes]} {
4444 Msg CriticalWarning "Rclone remote $remote_name not found among available remotes: $remotes"
4450 }
elseif {[
string first "/eos/" $ip_path] == 0} {
4453 if {!([
info exists ::env(ENABLE_EOS)] && $::env(ENABLE_EOS) == 1)} {
4454 Msg Warning "IP remote directory path is on EOS but kinit was not successfull or not done. I will not copy IPs from/to EOS."
4459 if {[
file isdirectory $ip_path]} {
4460 Msg Info "Eos is mounted in the current machine. Treating it as a normal directory..."
4463 lassign [
eos "ls $ip_path"] ret result
4465 Msg CriticalWarning "Could not run ls for for EOS path: $ip_path (error: $result). \
4466 Either the drectory does not exist or there are (temporary) problem with EOS."
4470 Msg Info "IP remote directory path, on EOS, is set to: $ip_path"
4477 if {!([
file exists $xci_file])} {
4478 Msg CriticalWarning "Could not find $xci_file."
4484 set xci_path [
file dirname $xci_file]
4485 set xci_name [
file tail $xci_file]
4486 set xci_ip_name [
file rootname [
file tail $xci_file]]
4487 set xci_dir_name [
file tail $xci_path]
4488 set gen_path $gen_dir
4490 set hash [
Md5Sum $xci_file]
4491 set file_name $xci_name\_$hash
4493 Msg Info "Preparing to $what_to_do IP: $xci_name..."
4495 if {$what_to_do eq "push"} {
4498 if {$on_rclone == 1} {
4499 lassign [
ExecuteRet rclone ls $ip_path/$file_name.tar --config $config_path] ret result
4504 Msg Info "IP already in the Rclone repository, will not copy..."
4506 Msg Info "IP already in the Rclone repository, will forcefully replace..."
4511 }
elseif {$on_eos == 1} {
4512 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4517 Msg Info "IP already in the EOS repository, will not copy..."
4519 Msg Info "IP already in the EOS repository, will forcefully replace..."
4525 if {[
file exists "$ip_path/$file_name.tar"]} {
4527 Msg Info "IP already in the local repository, will not copy..."
4529 Msg Info "IP already in the local repository, will forcefully replace..."
4538 if {$will_copy == 1} {
4540 Msg Info "Looking for generated files in $gen_path..."
4541 set ip_gen_files [glob -nocomplain $gen_path/*]
4545 if {[
llength $ip_gen_files] > 0} {
4546 Msg Info "Found some IP synthesised files matching $xci_ip_name"
4547 if {$will_remove == 1} {
4548 Msg Info "Removing old synthesised directory $ip_path/$file_name.tar..."
4549 if {$on_rclone == 1} {
4550 lassign [
ExecuteRet rclone delete $ip_path/$file_name.tar --config $config_path] ret result
4552 Msg CriticalWarning "Could not delete file from Rclone: $result"
4554 }
elseif {$on_eos == 1} {
4555 eos "rm -rf $ip_path/$file_name.tar" 5
4557 file delete -force "$ip_path/$file_name.tar"
4561 Msg Info "Creating local archive with IP generated files..."
4564 set at_least_one_long 0
4565 foreach f $ip_gen_files {
4566 set new_f "[
Relative [
file normalize $repo_path] $f]"
4567 set len [
string length $new_f]
4569 Msg Warning "One file in $xci_ip_name is too long ($len chars): $new_f"
4570 set at_least_one_long 1
4572 lappend tar_files $new_f
4575 Msg Debug "Tar files: $tar_files"
4576 if {$at_least_one_long == 1} {
4577 Msg Warning "Using regular tar, please cross your fingers..."
4579 lassign [
ExecuteRet tar -cf $file_name.tar {*}$tar_files] ret_tar result_tar
4580 if {$ret_tar != 0} {
4581 Msg CriticalWarning "Something went wrong when using regular tar. Error message: $result_tar"
4584 ::tar::create $file_name.tar $tar_files
4587 Msg Info "Copying IP generated files for $xci_name..."
4588 if {$on_rclone == 1} {
4589 lassign [
ExecuteRet rclone copyto $file_name.tar $ip_path/$file_name.tar --config $config_path] ret result
4591 Msg CriticalWarning "Something went wrong when copying the IP files to Rclone. Error message: $result"
4593 }
elseif {$on_eos == 1} {
4594 lassign [
ExecuteRet xrdcp -f -s $file_name.tar $::env(EOS_MGM_URL)//$ip_path/] ret msg
4596 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4599 Copy "$file_name.tar" "$ip_path/"
4601 Msg Info "Removing local archive"
4602 file delete $file_name.tar
4604 Msg Warning "Could not find synthesized files matching $gen_path/$file_name*"
4607 }
elseif {$what_to_do eq "pull"} {
4608 if {$on_rclone == 1} {
4609 lassign [
ExecuteRet rclone ls $ip_path/$file_name.tar --config $config_path] ret result
4611 Msg Info "Nothing for $xci_name was found in the Rclone repository, cannot pull."
4615 Msg Info "IP $xci_name found in the Rclone repository $ip_path, copying it locally to $repo_path..."
4616 lassign [
ExecuteRet rclone copyto $ip_path/$file_name.tar $file_name.tar --config $config_path] ret_copy result_copy
4617 if {$ret_copy != 0} {
4618 Msg CriticalWarning "Something went wrong when copying the IP files from Rclone. Error message: $result_copy"
4621 }
elseif {$on_eos == 1} {
4622 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4624 Msg Info "Nothing for $xci_name was found in the EOS repository, cannot pull."
4628 set remote_tar "$::env(EOS_MGM_URL)//$ip_path/$file_name.tar"
4629 Msg Info "IP $xci_name found in the repository $remote_tar, copying it locally to $repo_path..."
4631 lassign [
ExecuteRet xrdcp -f -r -s $remote_tar $repo_path] ret msg
4633 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4637 if {[
file exists "$ip_path/$file_name.tar"]} {
4638 Msg Info "IP $xci_name found in local repository $ip_path/$file_name.tar, copying it locally to $repo_path..."
4639 Copy $ip_path/$file_name.tar $repo_path
4641 Msg Info "Nothing for $xci_name was found in the local IP repository, cannot pull."
4647 if {[
file exists $file_name.tar]} {
4648 remove_files $xci_file
4649 Msg Info "Extracting IP files from archive to $repo_path..."
4650 ::tar::untar $file_name.tar -dir $repo_path -noperms
4651 Msg Info "Removing local archive"
4652 file delete $file_name.tar
4653 add_files -norecurse -fileset sources_1 $xci_file
4666 proc HexVersionToString {version} {
4667 scan [
string range $version 0 1] %x M
4668 scan [
string range $version 2 3] %x m
4669 scan [
string range $version 4 7] %x c
4674 proc ImportTclLib {} {
4677 if {[
info exists env(HOG_TCLLIB_PATH)]} {
4678 lappend auto_path $env(HOG_TCLLIB_PATH)
4681 puts "ERROR: To run Hog with Microsemi Libero SoC or Lattice Diamond, you need to define the HOG_TCLLIB_PATH variable."
4696 proc InitLauncher {script tcl_path parameters commands argv {custom_commands ""}} {
4697 set repo_path [
file normalize "$tcl_path/../.."]
4699 set bin_path [
file normalize "$tcl_path/../../bin"]
4700 set top_path [
file normalize "$tcl_path/../../Top"]
4702 set cmd_lines [
split $commands "\n"]
4704 set command_options [dict create]
4705 set directive_descriptions [dict create]
4706 set directive_names [dict create]
4707 set common_directive_names [dict create]
4708 set directives_with_optional_projects ""
4709 set custom_command ""
4710 set custom_command_options ""
4712 foreach l $cmd_lines {
4714 if {[regexp {\\(.*) \{\#} $l minc d]} {
4715 lappend directives_with_projects $d
4716 set current_directive $d
4719 if {[regexp {^[^#]* allow_empty_proj} $l minc dd]} {
4720 lappend directives_with_optional_projects $current_directive
4724 if {[regexp {\\(.*) \{} $l minc regular_expression]} {
4725 lappend directive_regex $regular_expression
4729 if {[regexp {\#\s*NAME(\*)?:\s*(.*)\s*} $l minc star name]} {
4730 dict set directive_names $name $regular_expression
4732 dict set common_directive_names $name $regular_expression
4735 set directive_names [
DictSort $directive_names]
4736 set common_directive_names [
DictSort $common_directive_names]
4739 if {[regexp {\#\s*DESCRIPTION:\s*(.*)\s*} $l minc x]} {
4740 dict set directive_descriptions $regular_expression $x
4744 if {[regexp {\#\s*OPTIONS:\s*(.*)\s*} $l minc x]} {
4745 dict set command_options $regular_expression [
split [regsub -all {[ \t\n]+} $x {}] ","]
4749 set short_usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nMost common directives (case insensitive):"
4751 dict for {key value} $common_directive_names {
4752 set short_usage "$short_usage\n - $key: [dict get $directive_descriptions $value]"
4755 if {[
string length $custom_commands] > 0} {
4756 Msg Debug "Found custom commands to add to short short_usage."
4757 set short_usage "$short_usage\n\nCustom commands:"
4758 dict for {key command} $custom_commands {
4759 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4760 set short_usage "$short_usage\n - $key: [dict get $command DESCRIPTION]"
4765 set short_usage "$short_usage\n\n\
4766 To see all the available directives, run:\n./Hog/Do HELP\n\n\
4767 To list available options for the chosen directive run:\n\
4768 ./Hog/Do <directive> HELP\n
4771 set usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nDirectives (case insensitive):"
4773 dict for {key value} $directive_names {
4774 set usage "$usage\n - $key: [dict get $directive_descriptions $value]"
4778 if {[
string length $custom_commands] > 0} {
4779 Msg Debug "Found custom commands to add to short usage."
4780 set usage "$usage\n\nCustom commands:"
4781 dict for {key command} $custom_commands {
4782 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4783 set usage "$usage\n - $key: [dict get $command DESCRIPTION]"
4788 set usage "$usage\n\nTo list available options for the chosen directive run:\n./Hog/Do <directive> HELP"
4797 Msg Debug "HogEnv.conf found"
4805 if {[
catch {
package require cmdline} ERROR]} {
4806 Msg Debug "The cmdline Tcl package was not found, sourcing it from Hog..."
4807 source $tcl_path/utils/cmdline.tcl
4810 set argv [regsub -all {(?i) HELP\y} $argv " -help"]
4815 set custom_parameters [list]
4816 dict for {key command} $custom_commands {
4817 set custom_parameters [concat $custom_parameters [dict get $command CUSTOM_OPTIONS]]
4820 lassign [
GetOptions $argv [
concat $custom_parameters $parameters]] option_list arg_list
4822 if {[
IsInList "-all" $option_list]} {
4831 set directive [
string toupper [
lindex $arg_list 0]]
4834 set argument_is_no_project 1
4836 set NO_DIRECTIVE_FOUND 0
4837 switch -regexp -- $directive "$commands"
4839 if {$NO_DIRECTIVE_FOUND == 1} {
4840 if {[
string length $custom_commands] > 0 && [dict exists $custom_commands $directive]} {
4841 set custom_command $directive
4842 set custom_command_hog_parameters [dict get $custom_commands $directive OPTIONS]
4843 set custom_command_options [dict get $custom_commands $directive CUSTOM_OPTIONS]
4844 set custom_command_options [
concat $custom_command_hog_parameters $custom_command_options]
4846 Msg Status "ERROR: Unknown directive $directive.\n\n"
4852 if {[
IsInList $directive $directives_with_projects 1]} {
4853 set argument_is_no_project 0
4857 if {$directive != ""} {
4858 if {[
IsInList $directive $directives_with_projects 1]} {
4859 puts "usage: ./Hog/Do \[OPTIONS\] $directive <project>\n"
4860 }
elseif {[regexp "^COMPSIM(LIB)?$" $directive]} {
4861 puts "usage: ./Hog/Do \[OPTIONS\] $directive <simulator>\n"
4863 puts "usage: ./Hog/Do \[OPTIONS\] $directive \n"
4866 dict for {dir desc} $directive_descriptions {
4867 if {[regexp $dir $directive]} {
4874 if {$custom_command ne ""} {
4875 if {[
llength $custom_command_options] > 0} {
4876 puts "Available options:"
4878 foreach custom_option $custom_command_options {
4879 set n [
llength $custom_option]
4881 lassign $custom_option opt help
4884 }
elseif {$n == 3} {
4885 lassign $custom_option opt def help
4886 puts " -$opt <argument>"
4888 puts " $help (default: $def)"
4893 Msg Warning "Custom option spec has invalid arity (expected 2 or 3): $custom_option"
4898 dict for {dir opts} $command_options {
4899 if {[regexp $dir $directive]} {
4900 puts "Available options:"
4902 foreach par $parameters {
4903 if {$opt == [lindex $par 0]} {
4904 if {[regexp {\.arg$} $opt]} {
4905 set opt_name [regsub {\.arg$} $opt ""]
4906 puts " -$opt_name <argument>"
4910 puts " [lindex $par [llength $par]-1]"
4924 if {$custom_command ne ""} {
4925 set parameters [
concat $parameters $custom_command_options]
4928 if {[
catch {
array set options [
cmdline::getoptions option_list $parameters $usage]} err]} {
4929 Msg Status "\nERROR: Syntax error, probably unknown option.\n\n USAGE: $err"
4933 if {[
llength $arg_list] <= $min_n_of_args || [
llength $arg_list] > $max_n_of_args} {
4934 Msg Status "\nERROR: Wrong number of arguments: [
llength $argv]"
4939 set project [
lindex $arg_list 1]
4940 set optional_project [
IsInList $directive $directives_with_optional_projects 1]
4942 if {$argument_is_no_project == 0} {
4944 regsub "^(\./)?Top/" $project "" project
4946 regsub "/? *\$" $project "" project
4948 if {$project eq "" && $optional_project == 1} {
4957 Msg Debug "Option list:"
4958 foreach {key value} [
array get options] {
4959 Msg Debug "$key => $value"
4966 if {$proj_conf != 0} {
4969 lassign [
GetIDECommand $proj_conf] cmd before_tcl_script after_tcl_script end_marker
4970 Msg Info "Project $project uses $cmd IDE"
4973 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4975 if {$custom_command ne ""} {
4976 if { [dict exists $custom_commands $directive IDE] } {
4977 lassign [
GetIDECommand "" [dict get $custom_commands $directive IDE]] cmd before_tcl_script after_tcl_script end_marker
4978 Msg Info "Custom command: $custom_command uses $cmd IDE"
4979 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4981 set command "custom_tcl"
4983 }
elseif {$argument_is_no_project == 1} {
4985 Msg Debug "$project will be used as first argument"
4986 }
elseif {$project != ""} {
4989 }
elseif {$min_n_of_args < 0} {
4994 if {$optional_project == 0} {
5006 set project_group [
file dirname $project]
5007 set project_name $project
5008 set project [
file tail $project]
5009 Msg Debug "InitLauncher: project_group=$project_group, project_name=$project_name, project=$project"
5011 return [list $directive $project $project_name $project_group $repo_path $old_path $bin_path $top_path $usage $short_usage $command $cmd [
array get options]]
5018 proc IsCommitAncestor {ancestor commit} {
5019 lassign [
GitRet "merge-base --is-ancestor $ancestor $commit"] status result
5028 return [
expr {[info commands sys_install] != ""}]
5033 return [
expr {[info commands get_libero_version] != ""}]
5042 proc IsInList {element list {regex 0} {nocase 0}} {
5046 if {[regexp -nocase $x $element]} {
5050 if {[regexp $x $element]} {
5054 }
elseif {$regex == 0} {
5056 if {[
string tolower $x] eq [
string tolower $element]} {
5060 if {$x eq $element} {
5073 return [
expr {[string first PlanAhead [version]] == 0}]
5081 if {[
catch {
package require ::quartus::flow} result]} {
5094 proc IsRelativePath {path} {
5095 if {[
string index $path 0] == "/" || [
string index $path 0] == "~"} {
5103 proc IsSynplify {} {
5104 return [
expr {[info commands program_version] != ""}]
5109 return [
expr {![IsQuartus] && ![IsXilinx] && ![IsVitisClassic] && ![IsVitisUnified] && ![IsLibero] && ![IsSynplify] && ![IsDiamond]}]
5117 proc IsVerilog {file} {
5118 if {[
file extension $file] == ".v" || [
file extension $file] == ".sv"} {
5130 proc IsVersal {part} {
5131 if {[get_property ARCHITECTURE [get_parts $part]] eq "versal"} {
5141 return [
expr {[string first Vivado [version]] == 0}]
5149 if {[
info commands version] != ""} {
5150 set current_version [version]
5151 if {[
string first PlanAhead $current_version] == 0 || [
string first Vivado $current_version] == 0} {
5153 }
elseif {[
string first xsct $current_version] == 0} {
5156 Msg Warning "This IDE has the version command but it is not PlanAhead or Vivado: $current_version"
5165 proc IsVitisClassic {} {
5166 if {[
info exists globalSettings::vitis_classic]} {
5167 return $globalSettings::vitis_classic
5169 return [
expr {[info commands platform] != ""}]
5173 proc IsVitisUnified {} {
5174 if {[
info exists globalSettings::vitis_unified]} {
5175 return $globalSettings::vitis_unified
5189 proc ExecuteVitisUnifiedCommand {python_script command args {error_prefix "Failed to execute command"} {output_var ""}} {
5190 set cmdlist [list vitis -s $python_script $command]
5192 lappend cmdlist $arg
5194 lappend cmdlist 2>@1
5196 Msg Debug "Executing: vitis -s $python_script $command $args"
5199 set env(PYTHONUNBUFFERED) "1"
5202 if {[
catch {
set pipe [open "|$cmdlist" "r"]} err]} {
5203 Msg Error "$error_prefix: Failed to open pipe: $err"
5207 fconfigure $pipe -buffering line
5208 set script_output ""
5209 set vitis_version ""
5212 set vitis_banner_patterns {
5213 "*Vitis Development Environment*"
5216 "*Copyright*Xilinx*"
5217 "*Copyright*Advanced Micro Devices*"
5218 "*All Rights Reserved*"
5222 while {[
gets $pipe line] >= 0} {
5224 if {[
string match "*Vitis v*" $line]} {
5225 if {[regexp {Vitis\s+v([0-9]+)\.([0-9]+)(?:\.[0-9]+)?} $line -> major minor]} {
5226 set year_last_two [
string range $major end-1 end]
5227 set vitis_version "$year_last_two.$minor"
5233 foreach pattern $vitis_banner_patterns {
5234 if {[
string match $pattern $line]} {
5240 if {![
string match "INFO:*" $line] && ![
string match "WARNING:*" $line] && ![
string match "ERROR:*" $line] && ![
string match "DEBUG:*" $line]} {
5241 if {$vitis_version ne ""} {
5242 set line "INFO: \[Vitis_v$vitis_version\] $line"
5244 set line "INFO: $line"
5248 append script_output "$line\n"
5255 if {[
catch {
close $pipe} err]} {
5256 if {[regexp {exit (\d+)} $err -> exit_code]} {
5257 if {$exit_code != 0} {
5258 Msg Error "$error_prefix (exit code: $exit_code)"
5259 if {$output_var ne ""} {
5260 upvar $output_var output
5261 set output $script_output
5266 Msg Error "$error_prefix: $err"
5267 if {$output_var ne ""} {
5268 upvar $output_var output
5269 set output $script_output
5276 if {$output_var ne ""} {
5277 upvar $output_var output
5278 set output $script_output
5289 proc IsZynq {part} {
5290 if {[regexp {^(xc7z|xczu).*} $part]} {
5297 proc ImportGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
5298 set list_path "$repo_path/Top/$project_name/list"
5299 lassign [
GetHogFiles -list_files {.src,.ext,.sim} -ext_path $ext_path $list_path $repo_path] src_files properties filesets
5304 set properties [
DictGet $simset_dict "properties"]
5305 set options [
DictGet $properties "options"]
5308 set workdir Projects/$project_name/ghdl
5309 file delete -force $workdir
5311 set import_log "$workdir/ghdl-import-${simset_name}.log"
5312 dict for {lib sources} $src_files {
5313 set libname [file rootname $lib]
5314 foreach f $sources {
5315 if {[file extension $f] != ".vhd" && [file extension $f] != ".vhdl"} {
5316 Msg Info "File $f is not a VHDL file, copying it in workfolder..."
5317 file copy -force $f $workdir
5319 set file_path [Relative $repo_path $f]
5320 set import_log_file [open $import_log "a"]
5321 puts "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
5322 puts $import_log_file "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
5323 close $import_log_file
5324 lassign [GHDL "-i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path" $import_log] ret result
5326 Msg CriticalWarning "GHDL import failed for file $f: $result"
5335 proc LaunchGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
5338 set sim_props [
DictGet $simset_dict "properties"]
5339 set options [
DictGet $sim_props "options"]
5340 set runopts [
DictGet $sim_props "run_options"]
5342 dict for {prop_name prop_val} $sim_props {
5343 set prop_name [string toupper $prop_name]
5344 if {$prop_name == "TOP"} {
5345 set top_sim $prop_val
5348 set workdir $repo_path/Projects/$project_name/ghdl
5349 set make_log "$workdir/ghdl-make-${simset_name}.log"
5350 set run_log "$workdir/ghdl-run-${simset_name}.log"
5353 set make_log_file [open $make_log "w"]
5355 puts "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
5356 puts $make_log_file "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
5357 close $make_log_file
5359 lassign [
GHDL "-m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim" $make_log] ret result
5362 Msg Error "GHDL make failed for $top_sim: $result"
5366 set run_log_file [open $run_log "w"]
5367 puts "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
5368 puts $run_log_file "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
5371 lassign [
GHDL "-r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts" $run_log] ret result
5375 Msg Error "GHDL run failed for $top_sim: $result"
5390 proc LaunchImplementation {reset do_create run_folder project_name {repo_path .} {njobs 4} {do_bitstream 0}} {
5391 Msg Info "Starting implementation flow..."
5394 Msg Info "Resetting run before launching implementation..."
5399 if { [
file exist "$run_folder/timing_ok.txt"] } {
5400 file delete "$run_folder/timing_ok.txt"
5402 if { [
file exist "$run_folder/timing_error.txt"] } {
5403 file delete "$run_folder/timing_error.txt"
5407 source $repo_path/Hog/Tcl/integrated/pre-implementation.tcl
5410 if {$do_bitstream == 1} {
5411 launch_runs impl_1 -to_step [
BinaryStepName [get_property PART [current_project]]] -jobs $njobs -dir $run_folder
5413 launch_runs impl_1 -jobs $njobs -dir $run_folder
5418 Msg Info "running post-implementation"
5419 source $repo_path/Hog/Tcl/integrated/post-implementation.tcl
5420 if {$do_bitstream == 1} {
5421 Msg Info "running pre-bitstream"
5422 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5423 Msg Info "running post-bitstream"
5424 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5428 set prog [get_property PROGRESS [get_runs impl_1]]
5429 set status [get_property STATUS [get_runs impl_1]]
5430 Msg Info "Run: impl_1 progress: $prog, status : $status"
5434 set status_file [open "$run_folder/timing.txt" "w"]
5435 puts $status_file "## $project_name Timing summary"
5437 set f [open [
lindex [glob "$run_folder/impl_1/*.twr" 0]]]
5439 while {[
gets $f line] >= 0} {
5440 if {[
string match "Timing summary:" $line]} {
5441 while {[
gets $f line] >= 0} {
5442 if {[
string match "Timing errors:*" $line]} {
5443 set errs [regexp -inline -- {[0-9]+} $line]
5445 if {[
string match "*Footnotes*" $line]} {
5448 puts $status_file "$line"
5457 Msg Info "Time requirements are met"
5458 file rename -force "$run_folder/timing.txt" "$run_folder/timing_ok.txt"
5461 Msg CriticalWarning "Time requirements are NOT met"
5462 file rename -force "$run_folder/timing.txt" "$run_folder/timing_error.txt"
5468 set wns [get_property STATS.WNS [get_runs [current_run]]]
5469 set tns [get_property STATS.TNS [get_runs [current_run]]]
5470 set whs [get_property STATS.WHS [get_runs [current_run]]]
5471 set ths [get_property STATS.THS [get_runs [current_run]]]
5472 set tpws [get_property STATS.TPWS [get_runs [current_run]]]
5474 if {$wns >= 0 && $whs >= 0 && $tpws >= 0} {
5475 Msg Info "Time requirements are met"
5476 set status_file [open "$run_folder/timing_ok.txt" "w"]
5479 Msg CriticalWarning "Time requirements are NOT met"
5480 set status_file [open "$run_folder/timing_error.txt" "w"]
5484 Msg Status "*** Timing summary ***"
5485 Msg Status "WNS: $wns"
5486 Msg Status "TNS: $tns"
5487 Msg Status "WHS: $whs"
5488 Msg Status "THS: $ths"
5489 Msg Status "TPWS: $tpws"
5495 puts $status_file "## $project_name Timing summary"
5497 m add row "| **Parameter** | \"**value (ns)**\" |"
5498 m add row "| --- | --- |"
5499 m add row "| WNS: | $wns |"
5500 m add row "| TNS: | $tns |"
5501 m add row "| WHS: | $whs |"
5502 m add row "| THS: | $ths |"
5503 m add row "| TPWS: | $tpws |"
5505 puts $status_file [m format 2string]
5506 puts $status_file "\n"
5507 if {$timing_ok == 1} {
5508 puts $status_file " Time requirements are met."
5510 puts $status_file "Time requirements are **NOT** met."
5512 puts $status_file "\n\n"
5516 if {$prog ne "100%"} {
5517 Msg Error "Implementation error"
5522 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5523 Msg Info "Git describe set to $describe"
5525 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5530 if {[
file exists $run_folder/versions.txt]} {
5531 file copy -force $run_folder/versions.txt $dst_dir
5533 Msg Warning "No versions file found in $run_folder/versions.txt"
5536 set timing_files [glob -nocomplain "$run_folder/timing_*.txt"]
5537 set timing_file [
file normalize [
lindex $timing_files 0]]
5539 if {[
file exists $timing_file]} {
5540 file copy -force $timing_file $dst_dir/
5542 Msg Warning "No timing file found, not a problem if running locally"
5546 if {[
IsVersal [get_property part [current_project]]]} {
5547 if {[get_property segmented_configuration [current_project]] == 1} {
5548 Msg Info "Versal Segmented configuration detected: exporting XSA file..."
5549 set xsa_name "$dst_dir/[
file tail $project_name]\-$describe.xsa"
5550 write_hw_platform -fixed -force -file $xsa_name
5554 set revision [get_current_revision]
5556 if {[
catch {execute_module -tool fit} result]} {
5557 Msg Error "Result: $result\n"
5558 Msg Error "Place & Route failed. See the report file.\n"
5560 Msg Info "\nINFO: Place & Route was successful for revision $revision.\n"
5563 if {[
catch {execute_module -tool sta -args "--do_report_timing"} result]} {
5564 Msg Error "Result: $result\n"
5565 Msg Error "Time Quest failed. See the report file.\n"
5567 Msg Info "Time Quest was successfully run for revision $revision.\n"
5570 set panel "Timing Analyzer||Timing Analyzer Summary"
5571 set device [get_report_panel_data -name $panel -col 1 -row_name "Device Name"]
5572 set timing_model [get_report_panel_data -name $panel -col 1 -row_name "Timing Models"]
5573 set delay_model [get_report_panel_data -name $panel -col 1 -row_name "Delay Model"]
5575 Msg Info "*******************************************************************"
5576 Msg Info "Device: $device"
5577 Msg Info "Timing Models: $timing_model"
5578 Msg Info "Delay Model: $delay_model"
5581 Msg Info "*******************************************************************"
5584 Msg Info "Starting implementation flow..."
5585 if {[
catch {run_tool -name {PLACEROUTE}}]} {
5586 Msg Error "PLACEROUTE FAILED!"
5588 Msg Info "PLACEROUTE PASSED."
5592 Msg Info "Run VERIFYTIMING ..."
5593 if {[
catch {run_tool -name {VERIFYTIMING} -script {Hog/Tcl/integrated/libero_timing.tcl}}]} {
5594 Msg CriticalWarning "VERIFYTIMING FAILED!"
5596 Msg Info "VERIFYTIMING PASSED \n"
5602 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5603 Msg Info "Git describe set to $describe"
5605 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5606 file mkdir $dst_dir/reports
5609 if {[
file exists $run_folder/versions.txt]} {
5610 file copy -force $run_folder/versions.txt $dst_dir
5612 Msg Warning "No versions file found in $run_folder/versions.txt"
5615 set timing_file_path [
file normalize "$repo_path/Projects/timing_libero.txt"]
5616 if {[
file exists $timing_file_path]} {
5617 file copy -force $timing_file_path $dst_dir/reports/Timing.txt
5618 set timing_file [open $timing_file_path "r"]
5619 set status_file [open "$dst_dir/timing.txt" "w"]
5620 puts $status_file "## $project_name Timing summary\n\n"
5621 puts $status_file "| | |"
5622 puts $status_file "| --- | --- |"
5623 while {[
gets $timing_file line] >= 0} {
5624 if {[
string match "SUMMARY" $line]} {
5625 while {[
gets $timing_file line] >= 0} {
5626 if {[
string match "END SUMMARY" $line]} {
5629 if {[
string first ":" $line] == -1} {
5632 set out_string "| [
string map {: | } $line] |"
5633 puts $status_file "$out_string"
5638 Msg Warning "No timing file found, not a problem if running locally"
5643 set force_rst "-forceOne"
5645 prj_run Map $force_rst
5646 prj_run PAR $force_rst
5658 proc GenerateBitstreamOnly {project_name {repo_path .}} {
5662 set project_file [
file normalize "$repo_path/Projects/$project_name/$project_name.xpr"]
5663 if {![
file exists $project_file]} {
5664 Msg Error "Project file not found: $project_file. Please create the project first."
5671 set impl_runs [get_runs -quiet impl_1]
5672 if {[
llength $impl_runs] == 0} {
5673 Msg Error "Implementation run 'impl_1' does not exist. Please run implementation first."
5677 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5678 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5680 cd Projects/$project_name/$project_name.runs/impl_1
5681 Msg Info "Running pre-bitstream..."
5682 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5684 Msg Info "Writing bitstream for $project_name..."
5686 write_bitstream -force $dst_dir/$project_name-$describe.bit
5688 Msg Info "Running post-bitstream..."
5689 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5698 proc LaunchSimulation {project_name lib_path simsets {repo_path .} {scripts_only 0} {compile_only 0}} {
5701 set project [
file tail $project_name]
5702 set main_sim_folder [
file normalize "$repo_path/Projects/$project_name/$project.sim/"]
5704 if {$simsets != ""} {
5705 dict for {simset sim_dict} $simsets {
5706 lappend simsets_todo $simset
5708 Msg Info "Will run only the following simulation's sets (if they exist): $simsets_todo"
5711 if {$scripts_only == 1} {
5712 Msg Info "Only generating simulation scripts, not running simulations..."
5715 if {$compile_only == 1} {
5716 Msg Info "Only compiling simulation libraries, not running simulations..."
5721 set sim_dic [dict create]
5723 Msg Info "Retrieving list of simulation sets..."
5724 foreach s [get_filesets] {
5726 set use_simpass_str 0
5729 set type [get_property FILESET_TYPE $s]
5730 if {$type eq "SimulationSrcs"} {
5731 if {$simsets_todo != "" && $s ni $simsets_todo} {
5732 Msg Info "Skipping $s as it was not specified with the -simset option..."
5735 set sim_dict [
DictGet $simsets $s]
5736 set simulator [
DictGet $sim_dict "simulator"]
5737 set_property "target_simulator" $simulator [current_project]
5738 set hog_sim_props [
DictGet $sim_dict "hog"]
5739 dict for {prop_name prop_val} $hog_sim_props {
5740 # If HOG_SIMPASS_STR is set, use the HOG_SIMPASS_STR string to search for in logs, after simulation is done
5741 if {[string toupper $prop_name] == "HOG_SIMPASS_STR" && $prop_val != ""} {
5742 Msg Info "Setting simulation pass string as '$prop_val'"
5743 set use_simpass_str 1
5744 set simpass_str $prop_val
5746 if {[string toupper $prop_name] == "HOG_SILENT_SIM" && $prop_val == 1} {
5747 set quiet_sim " -quiet"
5753 Msg Info "Creating simulation scripts for $s..."
5754 if {[
file exists $repo_path/Top/$project_name/pre-simulation.tcl]} {
5755 Msg Info "Running $repo_path/Top/$project_name/pre-simulation.tcl"
5756 source $repo_path/Top/$project_name/pre-simulation.tcl
5758 if {[
file exists $repo_path/Top/$project_name/pre-$s-simulation.tcl]} {
5759 Msg Info "Running $repo_path/Top/$project_name/pre-$s-simulation.tcl"
5760 source Running $repo_path/Top/$project_name/pre-$s-simulation.tcl
5762 current_fileset -simset $s
5763 set sim_dir $main_sim_folder/$s/behav
5764 set sim_output_logfile $sim_dir/xsim/simulate.log
5765 if {([
string tolower $simulator] eq "xsim")} {
5766 set sim_name "xsim:$s"
5768 set simulation_command "launch_simulation $quiet_sim -simset [get_filesets $s]"
5769 if {[
catch $simulation_command log]} {
5772 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5773 lappend failed $sim_name
5778 if {$use_simpass_str == 1} {
5781 set file_desc [open $sim_output_logfile r]
5782 set log [read $file_desc]
5785 Msg Info "Searching for simulation pass string: '$simpass_str'"
5786 if {[
string first $simpass_str $log] == -1} {
5787 Msg CriticalWarning "Simulation failed for $s, error info: '$simpass_str' NOT found!"
5788 lappend failed $sim_name
5791 lappend success $sim_name
5795 lappend success $sim_name
5799 Msg Info "Simulation library path is set to $lib_path."
5801 if {!([
file exists $lib_path])} {
5802 Msg Warning "Could not find simulation library path: $lib_path, $simulator simulation will not work."
5806 if {$simlib_ok == 1} {
5807 set_property "compxlib.${simulator}_compiled_library_dir" [
file normalize $lib_path] [current_project]
5808 launch_simulation -scripts_only -simset [get_filesets $s]
5809 set top_name [get_property TOP $s]
5810 set sim_script [
file normalize $sim_dir/$simulator/]
5811 Msg Info "Adding simulation script location $sim_script for $s..."
5812 lappend sim_scripts $sim_script
5813 dict append sim_dic $sim_script $s
5815 Msg Error "Cannot run $simulator simulations without a valid library path"
5822 if {[
info exists sim_scripts] && $scripts_only == 0} {
5824 Msg Info "Generating IP simulation targets, if any..."
5826 foreach ip [get_ips] {
5827 generate_target simulation -quiet $ip
5832 Msg Info "====== Starting simulations runs ======"
5835 foreach s $sim_scripts {
5837 set cmd ./compile.sh
5838 Msg Info " ************* Compiling: $s ************* "
5840 set sim_name "comp:[dict get $sim_dic $s]"
5842 Msg CriticalWarning "Compilation failed for $s, error info: $::errorInfo"
5843 lappend failed $sim_name
5845 lappend success $sim_name
5847 Msg Info "###################### Compilation log starts ######################"
5848 Msg Info "\n\n$log\n\n"
5849 Msg Info "###################### Compilation log ends ######################"
5851 if {$compile_only == 1} {
5854 if {[
file exists "./elaborate.sh"] } {
5855 set cmd ./elaborate.sh
5856 Msg Info " ************* Elaborating: $s ************* "
5858 set sim_name "elab:[dict get $sim_dic $s]"
5860 Msg CriticalWarning "Elaboration failed for $s, error info: $::errorInfo"
5861 lappend failed $sim_name
5863 lappend success $sim_name
5865 Msg Info "###################### Elaboration log starts ######################"
5866 Msg Info "\n\n$log\n\n"
5867 Msg Info "###################### Elaboration log ends ######################"
5869 set cmd ./simulate.sh
5870 Msg Info " ************* Simulating: $s ************* "
5875 if {$use_simpass_str == 1} {
5876 if {[
string first $simpass_str $log] == -1} {
5880 Msg Debug "Simulation pass string not set, relying on simulator exit code."
5884 set sim_name "sim:[dict get $sim_dic $s]"
5886 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5887 lappend failed $sim_name
5889 lappend success $sim_name
5891 Msg Info "###################### Simulation log starts ######################"
5892 Msg Info "\n\n$log\n\n"
5893 Msg Info "###################### Simulation log ends ######################"
5898 if {[
llength $success] > 0} {
5899 set successes [
join $success "\n"]
5900 Msg Info "The following simulation sets were successful:\n\n$successes\n\n"
5903 if {[
llength $failed] > 0} {
5904 set failures [
join $failed "\n"]
5905 Msg Error "The following simulation sets have failed:\n\n$failures\n\n"
5907 }
elseif {[
llength $success] > 0} {
5908 Msg Info "All the [
llength $success] compilations, elaborations and simulations were successful."
5911 Msg Info "Simulation done."
5913 Msg Warning "Simulation is not yet supported for [
GetIDEName]."
5920 proc LaunchRTLAnalysis {repo_path {pre_rtl_file ""} {post_rtl_file ""}} {
5922 if {[
file exists $pre_rtl_file]} {
5923 Msg Info "Found pre-rtl Tcl script $pre_rtl_file, executing it..."
5924 source $pre_rtl_file
5926 Msg Info "Starting RTL Analysis..."
5928 synth_design -rtl -name rtl_1
5929 if {[
file exists $post_rtl_file]} {
5930 Msg Info "Found post-rtl Tcl script $post_rtl_file, executing it..."
5931 source $post_rtl_file
5934 Msg Warning "RTL Analysis is not yet supported for [
GetIDEName]."
5947 proc LaunchSynthesis {reset do_create run_folder project_name {repo_path .} {ext_path ""} {njobs 4}} {
5950 Msg Info "Resetting run before launching synthesis..."
5954 source $repo_path/Hog/Tcl/integrated/pre-synthesis.tcl
5956 launch_runs synth_1 -jobs $njobs -dir $run_folder
5958 set prog [get_property PROGRESS [get_runs synth_1]]
5959 set status [get_property STATUS [get_runs synth_1]]
5960 Msg Info "Run: synth_1 progress: $prog, status : $status"
5967 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5968 Msg Info "Git describe set to $describe"
5971 set xci_file [get_property IP_FILE $ip]
5973 set xci_path [
file dirname $xci_file]
5974 set xci_ip_name [
file rootname [
file tail $xci_file]]
5975 foreach rptfile [glob -nocomplain -directory $xci_path *.rpt] {
5976 file copy $rptfile $repo_path/bin/$project_name-$describe/reports
5980 if {$prog ne "100%"} {
5981 Msg Error "Synthesis error, status is: $status"
5985 set project [
file tail [
file rootname $project_name]]
5987 Msg Info "Number of jobs set to $njobs."
5988 set_global_assignment -name NUM_PARALLEL_PROCESSORS $njobs
5992 set describe [
GetHogDescribe [
file normalize $repo_path/Top/$project_name] $repo_path]
5994 set revision [get_current_revision]
5997 set tool_and_command [
split [get_global_assignment -name PRE_FLOW_SCRIPT_FILE] ":"]
5998 set tool [
lindex $tool_and_command 0]
5999 set pre_flow_script [
lindex $tool_and_command 1]
6000 set cmd "$tool -t $pre_flow_script quartus_map $project $revision"
6006 Msg Warning "Can not execute command $cmd"
6007 Msg Warning "LOG: $log"
6009 Msg Info "Pre flow script executed!"
6010 Msg Info "Pre flow script log: \n$log"
6014 if {![is_project_open]} {
6015 Msg Info "Re-opening project file $project_name..."
6016 project_open $project -current_revision
6020 if {[
catch {execute_module -tool ipg -args "--clean"} result]} {
6021 Msg Error "Result: $result\n"
6022 Msg Error "IP Generation failed. See the report file.\n"
6024 Msg Info "IP Generation was successful for revision $revision.\n"
6028 if {[
catch {execute_module -tool map -args "--parallel"} result]} {
6029 Msg Error "Result: $result\n"
6030 Msg Error "Analysis & Synthesis failed. See the report file.\n"
6032 Msg Info "Analysis & Synthesis was successful for revision $revision.\n"
6036 defvar_set -name RWNETLIST_32_64_MIXED_FLOW -value 0
6038 Msg Info "Run SYNTHESIS..."
6039 if {[
catch {run_tool -name {SYNTHESIZE}}]} {
6040 Msg Error "SYNTHESIZE FAILED!"
6042 Msg Info "SYNTHESIZE PASSED!"
6048 set force_rst "-forceOne"
6050 prj_run Synthesis $force_rst
6051 if {[prj_syn] == "synplify"} {
6052 prj_run Translate $force_rst
6055 Msg Error "Impossible condition. You need to run this in an IDE."
6066 proc LaunchVitisBuild {project_name {repo_path .} {stage "presynth"}} {
6067 set proj_name $project_name
6068 set bin_dir [
file normalize "$repo_path/bin"]
6074 set vitis_workspace [
file normalize "$repo_path/Projects/$project_name/vitis_unified"]
6075 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"]
6077 if {![
ExecuteVitisUnifiedCommand $python_script "app_list" [list $vitis_workspace] "Failed to get app list from Vitis Unified" json_output]} {
6078 Msg Error "Failed to get app list from Vitis Unified"
6081 if {[
catch {
package require json}]} {
6082 Msg Error "JSON package not available for parsing Vitis Unified app list"
6085 set json_output_filtered ""
6086 if {[regexp -lineanchor {\{.*\}} $json_output json_output_filtered]} {
6087 set ws_apps [json::json2dict $json_output_filtered]
6089 set ws_apps [json::json2dict $json_output]
6094 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
6096 Msg Error "Impossible condition. You need to run this in a Vitis Unified or Vitis Classic IDE."
6101 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$proj_name] $repo_path] commit version hog_hash hog_ver top_hash top_ver \
6102 libs hashes vers cons_ver cons_hash ext_names ext_hashes xml_hash xml_ver user_ip_repos user_ip_hashes user_ip_vers
6104 if {$commit == 0 } {
set commit $this_commit}
6111 foreach app_name [dict keys $ws_apps] {
6112 app config -name $app_name -set build-config Release
6116 WriteGenerics "vitisbuild" $repo_path $proj_name $date $timee $commit $version $top_hash $top_ver $hog_hash $hog_ver $cons_ver $cons_hash $libs \
6117 $vers $hashes $ext_names $ext_hashes $user_ip_repos $user_ip_vers $user_ip_hashes $flavour $xml_ver $xml_hash
6120 foreach app_name [dict keys $ws_apps] {
6123 if {![
ExecuteVitisUnifiedCommand $python_script "build_app" [list $app_name $vitis_workspace] "Failed to build app $app_name"]} {
6124 Msg Error "Failed to build app $app_name"
6128 app build -name $app_name
6132 if {$stage == "presynth"} {
6133 Msg Info "Done building apps for $project_name..."
6136 if {[
info exists ::globalSettings::vitis_only_pass] && $::globalSettings::vitis_only_pass == 1} {
6137 Msg Info "Skipping bin directory creation in vitis_only mode (post-bitstream.tcl handles artifacts)."
6141 Msg Info "Evaluating Hog describe for $project_name..."
6142 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6143 Msg Info "Hog describe set to: $describe"
6144 set dst_dir [
file normalize "$bin_dir/$proj_name\-$describe"]
6145 if {![
file exists $dst_dir]} {
6146 Msg Info "Creating $dst_dir..."
6150 foreach app_name [dict keys $ws_apps] {
6152 set main_file "$repo_path/Projects/$project_name/vitis_unified/$app_name/build/$app_name.elf"
6154 set main_file "$repo_path/Projects/$project_name/vitis_classic/$app_name/Release/$app_name.elf"
6156 set dst_main [
file normalize "$dst_dir/[
file tail $proj_name]\-$app_name\-$describe.elf"]
6158 if {![
file exists $main_file]} {
6159 Msg Error "No Vitis .elf file found. Perhaps there was an issue building it?"
6163 Msg Info "Copying main binary file $main_file into $dst_main..."
6164 file copy -force $main_file $dst_main
6179 proc LaunchHlsBuild {project_name {repo_path .}} {
6180 set proj_name $project_name
6181 set bin_dir [
file normalize "$repo_path/bin"]
6185 set conf_file [
file normalize "$repo_path/Top/$project_name/hog.conf"]
6186 if {![
file exists $conf_file]} {
6187 Msg Error "Configuration file not found: $conf_file"
6190 set properties [
ReadConf $conf_file]
6191 set hls_components [dict filter $properties key {hls:*}]
6193 if {[dict size $hls_components] == 0} {
6194 Msg Info "No HLS components found for project $project_name"
6198 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/HlsCommands.py"]
6206 set ide_name [
string tolower [
lindex $ide_info 0]]
6207 if {$ide_name eq "vivado_vitis_unified"} {
6208 set is_mixed_project 1
6210 set is_mixed_project 0
6213 dict for {hls_key hls_props} $hls_components {
6214 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {
6217 set component_name [string trim $component_name]
6218 Msg Info "Building HLS component: $component_name"
6220 # Read HLS_CONFIG path from hog.conf (mandatory)
6222 if {[dict exists $hls_props hls_config]} {
6223 set hls_cfg_rel [dict get $hls_props hls_config]
6224 } elseif {[dict exists $hls_props HLS_CONFIG]} {
6225 set hls_cfg_rel [dict get $hls_props HLS_CONFIG]
6227 if {$hls_cfg_rel eq ""} {
6228 Msg Error "HLS component '$component_name' missing HLS_CONFIG in hog.conf"
6232 set cfg_file [file normalize "$repo_path/$hls_cfg_rel"]
6233 if {![file exists $cfg_file]} {
6234 Msg Error "HLS config file not found: $cfg_file (from HLS_CONFIG=$hls_cfg_rel)"
6238 set hls_work_dir [file normalize "$repo_path/Projects/$project_name/vitis_unified/$component_name"]
6239 file mkdir $hls_work_dir
6242 Msg Info "Running C synthesis for HLS component '$component_name'..."
6243 if {![ExecuteVitisUnifiedCommand $python_script "synthesis" \
6244 [list $component_name $cfg_file $hls_work_dir] \
6245 "Failed to run C synthesis for $component_name"]} {
6246 Msg Error "C synthesis failed for HLS component '$component_name'"
6251 Msg Info "Running implementation for HLS component '$component_name'..."
6252 if {![ExecuteVitisUnifiedCommand $python_script "impl" \
6253 [list $component_name $cfg_file $hls_work_dir] \
6254 "Failed to run implementation for $component_name"]} {
6255 Msg Error "Implementation failed for HLS component '$component_name'"
6259 # Export VHDL to source tree if VHDL_OUTPUT is set
6261 if {[dict exists $hls_props vhdl_output]} {
6262 set vhdl_output [dict get $hls_props vhdl_output]
6263 } elseif {[dict exists $hls_props VHDL_OUTPUT]} {
6264 set vhdl_output [dict get $hls_props VHDL_OUTPUT]
6266 if {$vhdl_output ne ""} {
6267 set vhdl_output_dir [file normalize "$repo_path/$vhdl_output"]
6268 Msg Info "Exporting VHDL for '$component_name' to $vhdl_output_dir..."
6269 if {![ExecuteVitisUnifiedCommand $python_script "export_rtl" \
6270 [list $component_name $hls_work_dir $vhdl_output_dir vhdl] \
6271 "Failed to export VHDL for $component_name"]} {
6272 Msg Warning "Could not export VHDL for HLS component '$component_name'"
6276 # Export Verilog to source tree if VERILOG_OUTPUT is set
6277 set verilog_output ""
6278 if {[dict exists $hls_props verilog_output]} {
6279 set verilog_output [dict get $hls_props verilog_output]
6280 } elseif {[dict exists $hls_props VERILOG_OUTPUT]} {
6281 set verilog_output [dict get $hls_props VERILOG_OUTPUT]
6283 if {$verilog_output ne ""} {
6284 set verilog_output_dir [file normalize "$repo_path/$verilog_output"]
6285 Msg Info "Exporting Verilog for '$component_name' to $verilog_output_dir..."
6286 if {![ExecuteVitisUnifiedCommand $python_script "export_rtl" \
6287 [list $component_name $hls_work_dir $verilog_output_dir verilog] \
6288 "Failed to export Verilog for $component_name"]} {
6289 Msg Warning "Could not export Verilog for HLS component '$component_name'"
6293 # Export IP catalog ZIP to source tree if IP_OUTPUT is set
6295 if {[dict exists $hls_props ip_output]} {
6296 set ip_output [dict get $hls_props ip_output]
6297 } elseif {[dict exists $hls_props IP_OUTPUT]} {
6298 set ip_output [dict get $hls_props IP_OUTPUT]
6300 if {$ip_output ne ""} {
6301 set ip_output_dir [file normalize "$repo_path/$ip_output"]
6302 Msg Info "Exporting IP catalog for '$component_name' to $ip_output_dir..."
6303 if {![ExecuteVitisUnifiedCommand $python_script "export_ip" \
6304 [list $component_name $hls_work_dir $ip_output_dir] \
6305 "Failed to export IP for $component_name"]} {
6306 Msg Error "Could not export IP for HLS component '$component_name'. Make sure package.output.format=ip_catalog is set in hls_config.cfg."
6310 # Collect reports into bin/ for CI and release notes
6311 Msg Info "Evaluating Hog describe for $project_name..."
6312 set describe [GetHogDescribe [file normalize ./Top/$project_name] $repo_path]
6313 Msg Info "Hog describe set to: $describe"
6314 set dst_dir [file normalize "$bin_dir/$proj_name\-$describe"]
6315 if {![file exists $dst_dir]} {
6316 Msg Info "Creating $dst_dir..."
6320 # Mixed projects group HLS components under bin/<proj>/vitis_hls/; pure
6321 # vitis_unified projects place them directly under bin/<proj>/
6322 if {$is_mixed_project} {
6323 set hls_out_dir [file normalize "$dst_dir/vitis_hls"]
6325 set hls_out_dir $dst_dir
6328 Msg Info "Collecting HLS reports for component '$component_name'..."
6329 if {![ExecuteVitisUnifiedCommand $python_script "collect_reports" \
6330 [list $component_name $hls_work_dir $hls_out_dir] \
6331 "Failed to collect HLS reports for $component_name"]} {
6332 Msg Warning "No reports found for HLS component '$component_name'"
6335 # Generate markdown summary files (utilization.txt, timing_ok.txt /
6336 # timing_error.txt) under <hls_out_dir>/<component>/. File names mirror the
6337 # Vivado convention so the existing CI logic (release notes assembly and
6338 # timing-failure detection) works for HLS components too
6339 Msg Info "Generating HLS release-notes summary for component '$component_name'..."
6340 if {![ExecuteVitisUnifiedCommand $python_script "release_notes" \
6341 [list $component_name $hls_work_dir $hls_out_dir] \
6342 "Failed to generate HLS summary for $component_name"]} {
6343 Msg Warning "Could not generate HLS summary for component '$component_name'"
6346 Msg Info "HLS component '$component_name' built successfully"
6351 if {!$is_mixed_project && [
info exists dst_dir] && [
file isdirectory $dst_dir]} {
6352 set versions_file [
file normalize "$dst_dir/versions.txt"]
6353 Msg Info "Generating top-level versions.txt for pure-HLS project at $versions_file"
6355 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$project_name] $repo_path] \
6356 commit version hog_hash hog_ver top_hash top_ver \
6357 libs hashes vers cons_ver cons_hash \
6358 ext_names ext_hashes xml_hash xml_ver \
6359 user_ip_repos user_ip_hashes user_ip_vers
6360 if {$commit == 0} {
set commit [
GetSHA]}
6364 set fh [open $versions_file "w"]
6365 puts $fh "## $proj_name Version Table\n"
6366 puts $fh "| **File set** | **Commit SHA** | **Version** |"
6367 puts $fh "| --- | --- | --- |"
6368 puts $fh "| Global | $commit | $version_str |"
6369 puts $fh "| Top Directory | $top_hash | $top_ver_str |"
6370 puts $fh "| Hog | $hog_hash | $hog_ver_str |"
6371 foreach l $libs v $vers h $hashes {
6373 puts $fh "| **Lib:** $l | $h | $v_str |"
6378 Msg Warning "Could not generate top-level versions.txt for pure-HLS project: $err"
6382 Msg Info "Done building HLS components for $project_name"
6398 proc LaunchHlsSimulation {project_name {repo_path .} {hls_simsets {}}} {
6401 set conf_file [
file normalize "$repo_path/Top/$project_name/hog.conf"]
6402 if {![
file exists $conf_file]} {
6403 Msg Error "Configuration file not found: $conf_file"
6406 set properties [
ReadConf $conf_file]
6407 set hls_components [dict filter $properties key {hls:*}]
6409 if {[dict size $hls_components] == 0} {
6410 Msg Info "No HLS components found for project $project_name"
6416 set targeted_mode [
expr {[llength $hls_simsets] > 0}]
6417 set targets [dict create]
6418 foreach entry $hls_simsets {
6419 if {[regexp {^(csim|cosim):(.+)$} $entry -> sim_type comp_name]} {
6420 dict lappend targets $comp_name $sim_type
6422 Msg Error "Invalid HLS simset format '$entry'. Expected 'csim:<component>' or 'cosim:<component>'."
6423 Msg Info "Available HLS components in hog.conf:"
6424 dict for {hls_key hls_props} $hls_components {
6425 if {[regexp {^hls:(.+)$} $hls_key -> cname]} {
6426 Msg Info " - csim:[string trim $cname]"
6427 Msg Info " - cosim:[string trim $cname]"
6434 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/HlsCommands.py"]
6436 dict for {hls_key hls_props} $hls_components {
6437 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {
6440 set component_name [string trim $component_name]
6442 # In targeted mode, skip components not in the request
6443 if {$targeted_mode && ![dict exists $targets $component_name]} {
6447 # Read HLS_CONFIG path from hog.conf (mandatory)
6449 if {[dict exists $hls_props hls_config]} {
6450 set hls_cfg_rel [dict get $hls_props hls_config]
6451 } elseif {[dict exists $hls_props HLS_CONFIG]} {
6452 set hls_cfg_rel [dict get $hls_props HLS_CONFIG]
6454 if {$hls_cfg_rel eq ""} {
6455 Msg Error "HLS component '$component_name' missing HLS_CONFIG in hog.conf"
6459 set cfg_file [file normalize "$repo_path/$hls_cfg_rel"]
6460 if {![file exists $cfg_file]} {
6461 Msg Error "HLS config file not found: $cfg_file (from HLS_CONFIG=$hls_cfg_rel)"
6465 set hls_work_dir [file normalize "$repo_path/Projects/$project_name/vitis_unified/$component_name"]
6466 file mkdir $hls_work_dir
6468 # Determine which simulations to run
6469 if {$targeted_mode} {
6470 # User explicitly requested these sim types via -simset
6471 set sim_types [dict get $targets $component_name]
6472 set run_csim [expr {"csim" in $sim_types}]
6473 set run_cosim [expr {"cosim" in $sim_types}]
6475 # Validate: check if the requested sim is enabled in hog.conf
6476 foreach st $sim_types {
6478 if {[dict exists $hls_props $st]} {
6479 set flag_val [dict get $hls_props $st]
6480 } elseif {[dict exists $hls_props [string toupper $st]]} {
6481 set flag_val [dict get $hls_props [string toupper $st]]
6483 set is_enabled [expr {[string tolower $flag_val] eq "true" || $flag_val eq "1"}]
6485 set upper_st [string toupper $st]
6486 Msg Warning "HLS simulation '$st' requested for '$component_name' but\
6487 $upper_st is not enabled in \[hls:$component_name\] section of\
6488 hog.conf. Running it anyway."
6489 Msg Info "To enable it permanently, add '$upper_st=true' under\
6490 \[hls:$component_name\] in Top/$project_name/hog.conf"
6494 # Default mode: read flags from hog.conf
6497 foreach {key_lower key_upper} {csim CSIM cosim COSIM} {
6499 if {[dict exists $hls_props $key_lower]} {
6500 set val [dict get $hls_props $key_lower]
6501 } elseif {[dict exists $hls_props $key_upper]} {
6502 set val [dict get $hls_props $key_upper]
6504 if {[string tolower $val] eq "true" || $val eq "1"} {
6505 set run_$key_lower 1
6509 if {!$run_csim && !$run_cosim} {
6510 Msg Info "No HLS simulations enabled for '$component_name' (set CSIM=true and/or COSIM=true in \[hls:$component_name\] in hog.conf)"
6515 Msg Info "Running HLS simulations for component: $component_name"
6519 Msg Info "Running C simulation for HLS component '$component_name'..."
6520 if {![ExecuteVitisUnifiedCommand $python_script "csim" \
6521 [list $component_name $cfg_file $hls_work_dir] \
6522 "Failed to run C simulation for $component_name"]} {
6523 Msg Error "C simulation failed for HLS component '$component_name'"
6528 # COSIM (auto-run synthesis if needed)
6530 set syn_done_marker [file normalize "$hls_work_dir/hls/syn"]
6531 if {![file exists $syn_done_marker]} {
6532 Msg Info "C synthesis output not found for '$component_name', running synthesis automatically before co-simulation..."
6533 if {![ExecuteVitisUnifiedCommand $python_script "synthesis" \
6534 [list $component_name $cfg_file $hls_work_dir] \
6535 "Failed to run C synthesis for $component_name"]} {
6536 Msg Error "C synthesis failed for HLS component '$component_name', cannot proceed with co-simulation"
6541 Msg Info "Running C/RTL co-simulation for HLS component '$component_name'..."
6542 if {![ExecuteVitisUnifiedCommand $python_script "cosim" \
6543 [list $component_name $cfg_file $hls_work_dir] \
6544 "Failed to run co-simulation for $component_name"]} {
6545 Msg Error "C/RTL co-simulation failed for HLS component '$component_name'"
6550 Msg Info "HLS simulations completed for '$component_name'"
6553 Msg Info "Done with HLS simulations for $project_name"
6561 proc GetProcFromProps {repo_path props platform} {
6562 if {[dict exists $props "platform:$platform" "BIF"]} {
6563 set bif_file [dict get $props "platform:$platform" "BIF"]
6565 set bif_file "$repo_path/$bif_file"
6569 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
6579 proc GetBifFromProps {repo_path props platform} {
6580 if {[dict exists $props "platform:$platform" "BIF"]} {
6581 set bif_file [dict get $props "platform:$platform" "BIF"]
6583 set bif_file "$repo_path/$bif_file"
6587 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
6596 proc GetPartFromProps {props} {
6597 if {[dict exists $props "main" "PART"]} {
6598 return [
string tolower [dict get $props "main" "PART"]]
6600 Msg Error "Part number not found in properties"
6609 proc GetArchFromPart {part} {
6611 if {[
string match "xczu*" $part]} {
6613 }
elseif {[
string match "xc7z*" $part]} {
6615 }
elseif {[
string match "xck26*" $part]} {
6618 Msg CriticalWarning "Unknown part number: $part"
6627 proc GetAppsFromProps {props {list_names 0}} {
6628 set prop_apps [dict filter $props key {app:*}]
6629 set apps [dict create]
6630 set app_names [list]
6632 dict for {app_key app_value} $prop_apps {
6633 if {[regexp {^app:(.+)$} $app_key -> app_name]} {
6634 set app_name [string trim [string tolower $app_name]]
6635 # Convert only the keys of the inner dictionary to lowercase
6636 set app_value_lower [dict create]
6637 dict for {key value} $app_value {
6638 dict set app_value_lower [string tolower $key] $value
6640 dict set apps $app_name $app_value_lower
6641 lappend app_names $app_name
6644 if {$list_names eq 1} {
6655 proc GetPlatformsFromProps {props {list_names 0} {lower_case 0}} {
6656 set platforms [dict create]
6657 set platform_names [list]
6658 set prop_platforms [dict filter $props key {platform:*}]
6660 dict for {platform_key platform_value} $prop_platforms {
6661 if {[regexp {^platform:(.+)$} $platform_key -> platform_name]} {
6662 if {$lower_case == 1} {
6663 set platform_name [string trim [string tolower $platform_name]]
6665 set platform_name [string trim $platform_name]
6667 dict set platforms $platform_name $platform_value
6668 lappend platform_names $platform_name
6671 if {$list_names eq 1} {
6672 return $platform_names
6687 proc GenerateBootArtifacts {properties repo_path proj_dir bin_dir proj_name describe bitfile mmi_file} {
6688 set elf_list [glob -nocomplain "$bin_dir/*.elf"]
6692 if {[
llength $elf_list] == 0} {
6693 Msg Warning "No ELF files found in $bin_dir, skipping generation of boot artifacts"
6697 if {![
file exists $bitfile]} {
6698 Msg Warning "Bitfile $bitfile does not exist, skipping generation of boot artifacts"
6702 Msg Info "Generating boot artifacts for $proj_name..."
6703 Msg Info "Found apps: $apps"
6704 Msg Info "Found platforms: $platforms"
6708 foreach elf_file $elf_list {
6709 set elf_name [
file rootname [
file tail $elf_file]]
6710 Msg Info "Found elf name: $elf_name"
6711 Msg Info "Removing $describe from elf"
6714 if {[regexp "^(.+)-(.+)-$describe\$" $elf_name -> project_name elf_app]} {
6715 set elf_app [
string trim [
string tolower $elf_app]]
6716 Msg Info "Found elf_app: $elf_app"
6718 Msg Error "Could not extract app name from elf file: $elf_name"
6721 Msg Info "Removed project name ($project_name) and $describe from elf"
6723 set app_conf [dict get $apps $elf_app]
6724 set plat [dict get $app_conf "platform"]
6725 set app_proc [dict get $app_conf "proc"]
6728 if {[regexp -nocase {microblaze|risc} $app_proc]} {
6729 Msg Info "Detected soft processor ($app_proc) for $elf_app, updating bitstream memory with ELF file..."
6732 if {[dict size $proc_map] == 0} {
6733 Msg Error "Failed to read map from $proc_map_file"
6736 Msg Info "Found processor map: $proc_map"
6738 set proc_cell [
lindex [
split [dict get $proc_map $app_proc] ":"] 1]
6739 Msg Info "Updating memory at processor cell: $proc_cell"
6741 set update_mem_cmd "updatemem -force -meminfo $mmi_file -data $elf_file -bit $bitfile -proc $proc_cell -out $bitfile"
6742 set ret [
catch {
exec -ignorestderr {*}$update_mem_cmd >@ stdout} result]
6744 Msg Error "Error updating memory for $elf_app: $result"
6746 Msg Info "Done updating memory for $elf_app"
6749 Msg Info "Detected hard processor ($app_proc) for $elf_app. Make sure the .elf file is defined in the platform ($plat)\
6750 .bif file to be included in the bootable binary image (.bin) generation."
6756 foreach plat $platforms {
6758 if {$bif_file != ""} {
6759 Msg Info "Generating bootable binary image (.bin) for $plat"
6761 Msg Info "Architecture: $arch"
6762 Msg Info "BIF file: $bif_file"
6763 set bootgen_cmd "bootgen -arch $arch -image $bif_file -o i $bin_dir/$proj_name-$plat-$describe.bin -w on"
6764 set ret [
catch {
exec -ignorestderr {*}$bootgen_cmd >@ stdout} result]
6766 Msg Error "Error generating bootable binary image (.bin) for $elf_app: $result"
6768 Msg Info "Done generating bootable binary image (.bin) for $plat"
6777 proc ReadProcMap {proc_map_file} {
6778 set proc_map [dict create]
6779 if {[
file exists $proc_map_file]} {
6780 set f [open $proc_map_file "r"]
6781 while {[
gets $f line] >= 0} {
6782 Msg Debug "Line: $line"
6783 if {[regexp {^(\S+)\s+(.+)$} $line -> key value]} {
6784 Msg Debug "Found key: $key, value: $value in proc map file"
6785 dict set proc_map $key $value
6799 proc ListProjects {{repo_path .} {print 1} {ret_conf 0} {silent 0}} {
6800 set top_path [
file normalize $repo_path/Top]
6801 set confs [
findFiles [
file normalize $top_path] hog.conf]
6803 set confs [lsort $confs]
6807 set p [
Relative $top_path [
file dirname $c]]
6810 if {$description eq "test"} {
6811 set description " - Test project"
6812 }
elseif {$description ne ""} {
6813 set description " - $description"
6816 if {$print == 1 || $description ne " - Test project"} {
6818 set g [
file dirname $p]
6821 if {$silent ==0 } {
Msg Status ""}
6829 if {$ret_conf == 0} {
6841 proc Md5Sum {file_name} {
6842 if {!([
file exists $file_name])} {
6843 Msg Warning "Could not find $file_name."
6846 if {[
catch {
package require md5 2.0.7} result]} {
6847 Msg Warning "Tcl package md5 version 2.0.7 not found ($result), will use command line..."
6848 set hash [
lindex [
Execute md5sum $file_name] 0]
6850 set file_hash [
string tolower [md5::md5 -hex -file $file_name]]
6864 proc MergeDict {dict0 dict1 {remove_duplicates 1}} {
6865 set outdict [dict merge $dict1 $dict0]
6866 foreach key [dict keys $dict1] {
6867 if {[dict exists $dict0 $key]} {
6868 set temp_list [dict get $dict1 $key]
6869 foreach item $temp_list {
6871 if {[
IsInList $item [
DictGet $outdict $key]] == 0 || $remove_duplicates == 0} {
6873 dict lappend outdict $key $item
6885 proc MoveElementToEnd {inputList element} {
6886 set index [lsearch $inputList $element]
6888 set inputList [
lreplace $inputList $index $index]
6889 lappend inputList $element
6898 proc OpenProject {project_file repo_path} {
6900 open_project $project_file
6902 set project_folder [
file dirname $project_file]
6903 set project [
file tail [
file rootname $project_file]]
6904 if {[
file exists $project_folder]} {
6906 if {![is_project_open]} {
6907 Msg Info "Opening existing project file $project_file..."
6908 project_open $project -current_revision
6911 Msg Error "Project directory not found for $project_file."
6915 Msg Info "Opening existing project file $project_file..."
6917 open_project -file $project_file -do_backup_on_convert 1 -backup_file {./Projects/$project_file.zip}
6919 Msg Info "Opening existing project file $project_file..."
6920 prj_project open $project_file
6922 Msg Error "This IDE is currently not supported by Hog. Exiting!"
6929 return $tcl_platform(platform)
6939 proc ParseJSON {JSON_FILE JSON_KEY} {
6940 set result [
catch {
package require Tcl 8.4} TclFound]
6941 if {"$result" != "0"} {
6942 Msg CriticalWarning "Cannot find Tcl package version equal or higher than 8.4.\n $TclFound\n Exiting"
6946 set result [
catch {
package require json} JsonFound]
6947 if {"$result" != "0"} {
6948 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
6951 set JsonDict [json::json2dict $JSON_FILE]
6952 set result [
catch {dict get $JsonDict $JSON_KEY} RETURNVALUE]
6953 if {"$result" != "0"} {
6954 Msg CriticalWarning "Cannot find $JSON_KEY in $JSON_FILE\n Exiting"
6967 proc ProjectExists {project {repo_path .}} {
6968 set index [lsearch -exact [
ListProjects $repo_path 0] $project]
6974 Msg Warning "Project not found. Available projects in $repo_path are"
6977 Msg Error "Project $project not found in repository $repo_path"
6988 proc ReadConf {file_name} {
6989 if {[
catch {
package require inifile 0.2.3} ERROR]} {
6990 Msg Error "Could not find inifile package version 0.2.3 or higher.\n
6991 To use ghdl, libero or diamond with Hog, you need to install the tcllib package\n
6992 You can install it with 'sudo apt install tcllib' on Debian/Ubuntu or 'sudo dnf install tcllib' on Fedora/RedHat/CentOs."
6996 ::ini::commentchar "#"
6997 set f [::ini::open $file_name]
6998 set properties [dict create]
6999 foreach sec [::ini::sections $f] {
7001 if {$new_sec == "files"} {
7004 set key_pairs [::ini::get $f $sec]
7006 regsub -all {\{\"} $key_pairs "\{" key_pairs
7007 regsub -all {\"\}} $key_pairs "\}" key_pairs
7009 dict set properties $new_sec [dict create {*}$key_pairs]
7022 proc ReadExtraFileList {extra_file_name} {
7023 set extra_file_dict [dict create]
7024 if {[
file exists $extra_file_name]} {
7025 set file [open $extra_file_name "r"]
7026 set file_data [read $file]
7029 set data [
split $file_data "\n"]
7030 foreach line $data {
7031 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
7032 set ip_and_md5 [regexp -all -inline {\S+} $line]
7033 dict lappend extra_file_dict "[
lindex $ip_and_md5 0]" "[
lindex $ip_and_md5 1]"
7037 return $extra_file_dict
7048 proc ExpandHlsConfigFiles {cfg_file} {
7050 if {![
file exists $cfg_file] || ![
file isfile $cfg_file]} {
7053 set cfg_dir [
file normalize [
file dirname $cfg_file]]
7055 if {[
catch {
set fp [open $cfg_file r]} err]} {
7056 Msg Warning "ExpandHlsConfigFiles: cannot open $cfg_file: $err"
7059 set lines [
split [read $fp] "\n"]
7062 foreach line $lines {
7063 if {[regexp {^[\t\s]*$} $line]} { continue}
7064 if {[regexp {^[\t\s]*\#} $line]} { continue}
7065 if {[regexp {^\s*\[.*\]\s*$} $line]} { continue}
7066 if {![regexp {^\s*[^=\s]+\s*=\s*(.+?)\s*$} $line -> value]} { continue}
7068 foreach tok [regexp -all -inline {\S+} $value] {
7069 if {[
file pathtype $tok] eq "absolute"} {
7070 set candidate [
file normalize $tok]
7072 set candidate [
file normalize [
file join $cfg_dir $tok]]
7074 if {[
file isfile $candidate]} {
7075 lappend out $candidate
7076 }
elseif {[
file isdirectory $candidate]} {
7077 set stack [list $candidate]
7078 while {[
llength $stack] > 0} {
7079 set d [
lindex $stack 0]
7080 set stack [
lrange $stack 1 end]
7081 foreach f [glob -nocomplain -directory $d -types f *] {
7082 lappend out [
file normalize $f]
7084 foreach sub [glob -nocomplain -directory $d -types d *] {
7091 return [lsort -unique $out]
7101 proc GetHlsConfigsFromProjConf {conf_file repo_path} {
7102 set out [dict create]
7103 if {![
file exists $conf_file]} {
return $out}
7104 if {[
catch {
set properties [
ReadConf $conf_file]} err]} {
7105 Msg Debug "GetHlsConfigsFromProjConf: cannot parse $conf_file: $err"
7108 set hls_components [dict filter $properties key {hls:*}]
7109 dict for {hls_key hls_props} $hls_components {
7110 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} { continue }
7111 set component_name [string trim $component_name]
7113 if {[dict exists $hls_props hls_config]} {
7114 set cfg_rel [dict get $hls_props hls_config]
7115 } elseif {[dict exists $hls_props HLS_CONFIG]} {
7116 set cfg_rel [dict get $hls_props HLS_CONFIG]
7118 if {$cfg_rel eq ""} { continue }
7119 set cfg_abs [file normalize "$repo_path/$cfg_rel"]
7120 if {![file exists $cfg_abs]} {
7121 Msg CriticalWarning "HLS component '$component_name': HLS_CONFIG=$cfg_rel does not exist on disk."
7124 dict set out $component_name $cfg_abs
7146 proc ReadListFile {args} {
7149 if {[
catch {
package require cmdline} ERROR]} {
7150 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
7156 {lib.arg "" "The name of the library files will be added to, if not given will be extracted from the file name."}
7157 {fileset.arg "" "The name of the library, from the main list file"}
7158 {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."}
7159 {print_log "If set, will use PrintFileTree for the VIEW directive"}
7160 {indent.arg "" "Used to indent files with the VIEW directive"}
7163 set usage "USAGE: ReadListFile \[options\] <list file> <path>"
7164 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
7170 set list_file [
lindex $args 0]
7171 set path [
lindex $args 1]
7172 set sha_mode $options(sha_mode)
7173 set lib $options(lib)
7174 set fileset $options(fileset)
7175 set print_log $options(print_log)
7176 set indent $options(indent)
7178 if {$sha_mode == 1} {
7179 set sha_mode_opt "-sha_mode"
7184 if {$print_log == 1} {
7185 set print_log_opt "-print_log"
7187 set print_log_opt ""
7192 set lib [
file rootname [
file tail $list_file]]
7194 set fp [open $list_file r]
7195 set file_data [read $fp]
7197 set list_file_ext [
file extension $list_file]
7198 switch $list_file_ext {
7200 if {$fileset eq ""} {
7206 set fileset "constrs_1"
7209 set fileset "sources_1"
7213 set libraries [dict create]
7214 set filesets [dict create]
7215 set properties [dict create]
7217 set data [
split $file_data "\n"]
7219 set n [
llength $data]
7221 if {$print_log == 1} {
7222 if {$indent eq ""} {
7223 set list_file_rel [
file tail $list_file]
7224 Msg Status "\n$list_file_rel"
7228 Msg Debug "$n lines read from $list_file."
7231 foreach line $data {
7233 if {![regexp {^[\t\s]*$} $line] & ![regexp {^[\t\s]*\#} $line]} {
7234 set file_and_prop [regexp -all -inline {\S+} $line]
7235 set srcfile [
lindex $file_and_prop 0]
7236 set srcfile "$path/$srcfile"
7238 set srcfiles [glob -nocomplain $srcfile]
7241 if {$srcfiles != $srcfile && ![
string equal $srcfiles ""]} {
7242 Msg Debug "Wildcard source expanded from $srcfile to $srcfiles"
7244 if {![
file exists $srcfile]} {
7245 if {$print_log == 0} {
7246 Msg CriticalWarning "File: $srcfile (from list file: $list_file) does not exist."
7252 foreach vhdlfile $srcfiles {
7253 if {[
file exists $vhdlfile]} {
7254 set vhdlfile [
file normalize $vhdlfile]
7255 set extension [
file extension $vhdlfile]
7257 set prop [
lrange $file_and_prop 1 end]
7260 set library [
lindex [regexp -inline {\ylib\s*=\s*(.+?)\y.*} $prop] 1]
7261 if {$library == ""} {
7265 if {$extension == $list_file_ext} {
7268 set ref_path [
lindex [regexp -inline {\ypath\s*=\s*(\S+).*} $prop] 1]
7269 if {$ref_path eq ""} {
7272 set ref_path [
file normalize $path/$ref_path]
7274 Msg Debug "List file $vhdlfile found in list file, recursively opening it using path \"$ref_path\"..."
7275 if {$print_log == 1} {
7276 if {[
file normalize $last_printed] ne [
file normalize $vhdlfile]} {
7277 Msg Status "$indent Inside [
file tail $vhdlfile]:"
7281 lassign [
ReadListFile {*}"-indent \" $indent\" -lib $library -fileset $fileset $sha_mode_opt $print_log_opt $vhdlfile $ref_path"] l p fs
7283 set properties [
MergeDict $p $properties]
7285 }
elseif {[lsearch {.src .sim .con ReadExtraFileList} $extension] >= 0} {
7287 Msg Error "$vhdlfile cannot be included into $list_file, $extension files must be included into $extension files."
7290 regsub -all " *= *" $prop "=" prop
7294 if {[
string first "lib=" $p] == -1} {
7296 set pos [
string first "=" $p]
7300 set prop_name [
string range $p 0 [
expr {$pos - 1}]]
7303 if {$list_file_ext eq ".ipb"} {
7304 dict lappend properties $vhdlfile $path/$p
7306 dict lappend properties $vhdlfile $p
7308 Msg Debug "Adding property $p to $vhdlfile..."
7309 }
elseif {$list_file_ext != ".ipb"} {
7310 Msg Warning "Setting Property $p is not supported for file $vhdlfile or it is already its default. \
7315 if {[lsearch {.xci .ip .bd .xcix} $extension] >= 0} {
7317 set lib_name "ips.src"
7318 }
elseif {[
IsInList $extension {.vhd .vhdl}] || $list_file_ext == ".sim"} {
7320 if {![
IsInList $extension {.vhd .vhdl}]} {
7321 set lib_name "others.sim"
7323 set lib_name "$library$list_file_ext"
7325 }
elseif {$list_file_ext == ".con"} {
7326 set lib_name "sources.con"
7327 }
elseif {$list_file_ext == ".ipb"} {
7328 set lib_name "xml.ipb"
7329 }
elseif { [
IsInList $list_file_ext {.src}] && [
IsInList $extension {.c .cpp .h .hpp}] } {
7331 set lib_name "$library$list_file_ext"
7334 set lib_name "others.src"
7337 Msg Debug "Appending $vhdlfile to $lib_name list..."
7338 dict lappend libraries $lib_name $vhdlfile
7339 if {$sha_mode != 0 && [
file type $vhdlfile] eq "link"} {
7342 dict lappend libraries $lib_name $real_file
7343 Msg Debug "File $vhdlfile is a soft link, also adding the real file: $real_file"
7351 if {$list_file_ext eq ".src" && $extension eq ".cfg"} {
7353 if {$print_log == 1 && [
llength $hls_extras] > 0} {
7354 Msg Status "$indent Inside [
file tail $vhdlfile] (HLS auto-expand):"
7356 set n_extras [
llength $hls_extras]
7358 foreach hls_extra $hls_extras {
7360 if {$hls_extra eq $vhdlfile} { continue}
7361 Msg Debug "HLS cfg expansion: adding $hls_extra (from $vhdlfile) to $lib_name"
7362 if {$print_log == 1} {
7363 if {$i == $n_extras} {
set pad "└──"}
else {
set pad "├──"}
7364 set rel [
Relative [
file dirname $vhdlfile] $hls_extra]
7365 Msg Status "$indent $pad $rel"
7367 dict lappend libraries $lib_name $hls_extra
7368 if {$sha_mode != 0 && [
file type $hls_extra] eq "link"} {
7370 dict lappend libraries $lib_name $real_extra
7371 Msg Debug "HLS expanded file $hls_extra is a soft link, also adding $real_extra"
7378 if {[dict exists $filesets $fileset] == 0} {
7380 Msg Debug "Adding $fileset to the fileset dictionary..."
7381 Msg Debug "Adding library $lib_name to fileset $fileset..."
7382 dict set filesets $fileset $lib_name
7386 Msg Debug "Adding library $lib_name to fileset $fileset..."
7387 dict lappend filesets $fileset $lib_name
7393 Msg CriticalWarning "File $vhdlfile not found."
7399 if {$sha_mode != 0} {
7401 if {$list_file_ext eq ".ipb"} {
7402 set sha_lib "xml.ipb"
7404 set sha_lib $lib$list_file_ext
7406 dict lappend libraries $sha_lib [
file normalize $list_file]
7407 if {[
file type $list_file] eq "link"} {
7410 dict lappend libraries $lib$list_file_ext $real_file
7411 Msg Debug "List file $list_file is a soft link, also adding the real file: $real_file"
7414 return [list $libraries $properties $filesets]
7423 proc Relative {base dst {quiet 0}} {
7424 if {![
string equal [
file pathtype $base] [
file pathtype $dst]]} {
7426 Msg CriticalWarning "Unable to compute relation for paths of different path types: [
file pathtype $base] vs. [
file pathtype $dst], ($base vs. $dst)"
7431 set base [
file normalize [
file join [
pwd] $base]]
7432 set dst [
file normalize [
file join [
pwd] $dst]]
7435 set base [
file split $base]
7436 set dst [
file split $dst]
7438 while {[
string equal [
lindex $dst 0] [
lindex $base 0]]} {
7439 set dst [
lrange $dst 1 end]
7440 set base [
lrange $base 1 end]
7441 if {![
llength $dst]} {break}
7444 set dstlen [
llength $dst]
7445 set baselen [
llength $base]
7447 if {($dstlen == 0) && ($baselen == 0)} {
7450 while {$baselen > 0} {
7451 set dst [
linsert $dst 0 ..]
7454 set dst [
eval [
linsert $dst 0 file join]]
7465 proc RelativeLocal {pathName filePath} {
7466 if {[
string first [
file normalize $pathName] [
file normalize $filePath]] != -1} {
7467 return [
Relative $pathName $filePath]
7478 proc RemoveDuplicates {mydict} {
7479 set new_dict [dict create]
7480 foreach key [dict keys $mydict] {
7481 set values [
DictGet $mydict $key]
7482 foreach value $values {
7483 set idxs [
lreverse [
lreplace [lsearch -exact -all $values $value] 0 0]]
7485 set values [
lreplace $values $idx $idx]
7488 dict set new_dict $key $values
7499 proc ResetRepoFiles {reset_file} {
7500 if {[
file exists $reset_file]} {
7501 Msg Info "Found $reset_file, opening it..."
7502 set fp [open $reset_file r]
7503 set wild_cards [lsearch -all -inline -not -regexp [
split [read $fp] "\n"] "^ *$"]
7505 Msg Info "Found the following files/wild cards to restore if modified: $wild_cards..."
7506 foreach w $wild_cards {
7508 if {[
llength $mod_files] > 0} {
7509 Msg Info "Found modified $w files: $mod_files, will restore them..."
7512 Msg Info "No modified $w files found."
7523 proc RestoreModifiedFiles {{repo_path "."} {pattern "."}} {
7526 set ret [
Git checkout $pattern]
7537 proc SearchHogProjects {dir} {
7538 set projects_list {}
7539 if {[
file exists $dir]} {
7540 if {[
file isdirectory $dir]} {
7541 foreach proj_dir [glob -nocomplain -types d $dir/*] {
7542 if {![regexp {^.*Top/+(.*)$} $proj_dir dummy proj_name]} {
7543 Msg Warning "Could not parse Top directory $dir"
7546 if {[
file exists "$proj_dir/hog.conf"]} {
7547 lappend projects_list $proj_name
7550 lappend projects_list $p
7555 Msg Error "Input $dir is not a directory!"
7558 Msg Error "Directory $dir doesn't exist!"
7560 return $projects_list
7569 proc SetGenericsSimulation {repo_path proj_dir target} {
7570 set top_dir "$repo_path/Top/$proj_dir"
7571 set simsets [get_filesets]
7572 if {$simsets != ""} {
7573 foreach simset $simsets {
7575 if {[get_property FILESET_TYPE $simset] != "SimulationSrcs"} {
7579 set merged_generics_dict [dict create]
7583 set simset_generics [
DictGet $simset_dict "generics"]
7584 set merged_generics_dict [
MergeDict $merged_generics_dict $simset_generics 0]
7587 Msg Debug "TOP = [get_property top [get_filesets sources_1]]"
7588 Msg Debug "GENERICS = [get_property generic [get_filesets sources_1]]"
7590 set_property generic $generic_str [get_filesets $simset]
7591 Msg Info "Setting generics $generic_str for simulator $target\
7592 and simulation file-set $simset..."
7604 proc SetTopProperty {top_module fileset} {
7605 Msg Info "Setting TOP property to $top_module module"
7608 set_property "top" $top_module [get_filesets $fileset]
7611 set_global_assignment -name TOP_LEVEL_ENTITY $top_module
7613 set_root -module $top_module
7615 prj_impl option top $top_module
7620 proc VIVADO_PATH_PROPERTIES {} {
7621 return {"\.*\.TCL\.PRE$" "^.*\.TCL\.POST$" "^RQS_FILES$" "^INCREMENTAL\_CHECKPOINT$" "NOC\_SOLUTION\_FILE"}
7625 proc VITIS_PATH_PROPERTIES {} {
7626 return {"^HW$" "^XPFM$" "^LINKER-SCRIPT$" "^LIBRARIES$" "^LIBRARY-SEARCH-PATH$"}
7636 proc WriteConf {file_name config {comment ""}} {
7637 if {[
catch {
package require inifile 0.2.3} ERROR]} {
7638 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
7642 ::ini::commentchar "#"
7643 set f [::ini::open $file_name w]
7645 foreach sec [dict keys $config] {
7646 set section [dict get $config $sec]
7647 dict for {p v} $section {
7648 if {[string trim $v] == ""} {
7649 Msg Warning "Property $p has empty value. Skipping..."
7652 ::ini::set $f $sec $p $v
7657 if {![
string equal "$comment" ""]} {
7658 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $comment
7659 set hog_header "Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
7660 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $hog_header
7675 proc WriteGenerics {mode repo_path design date timee\
7676 commit version top_hash top_ver hog_hash hog_ver \
7677 cons_ver cons_hash libs vers hashes ext_names ext_hashes \
7678 user_ip_repos user_ip_vers user_ip_hashes flavour {xml_ver ""} {xml_hash ""}} {
7679 Msg Info "Passing parameters/generics to project's top module..."
7682 set generic_string [
concat \
7695 if {$xml_hash != "" && $xml_ver != ""} {
7696 lappend generic_string \
7701 foreach l $libs v $vers h $hashes {
7707 set ver [regsub -all {[\.-]} $ver {_}]
7708 set hash [regsub -all {[\.-]} $hash {_}]
7709 lappend generic_string "$ver" "$hash"
7712 foreach e $ext_names h $ext_hashes {
7714 lappend generic_string "$hash"
7717 foreach repo $user_ip_repos v $user_ip_vers h $user_ip_hashes {
7718 set repo_name [
file tail $repo]
7719 set ver "[
string toupper $repo_name]_VER=[
FormatGeneric $v]"
7720 set hash "[
string toupper $repo_name]_SHA=[
FormatGeneric $h]"
7721 set ver [regsub -all {[\.-]} $ver {_}]
7722 set hash [regsub -all {[\.-]} $hash {_}]
7723 lappend generic_string "$ver" "$hash"
7726 if {$flavour != -1} {
7727 lappend generic_string "FLAVOUR=$flavour"
7733 set generic_string "$prj_generics $generic_string"
7739 if {$mode == "create" || [
IsISE]} {
7742 if {[
file exists $top_file]} {
7745 Msg Debug "Found top level generics $generics in $top_file"
7747 set filtered_generic_string ""
7749 foreach generic_to_set [
split [
string trim $generic_string]] {
7750 set key [
lindex [
split $generic_to_set "="] 0]
7751 if {[dict exists $generics $key]} {
7752 Msg Debug "Hog generic $key found in $top_name"
7753 lappend filtered_generic_string "$generic_to_set"
7755 Msg Warning "Generic $key is passed by Hog but is NOT present in $top_name."
7761 set generic_string $filtered_generic_string
7766 set_property generic $generic_string [current_fileset]
7767 Msg Info "Setting parameters/generics..."
7768 Msg Debug "Detailed parameters/generics: $generic_string"
7773 set simulator [get_property target_simulator [current_project]]
7774 if {$mode == "create"} {
7781 Msg Info "Setting Synplify parameters/generics one by one..."
7782 foreach generic $generic_string {
7783 Msg Debug "Setting Synplify generic: $generic"
7784 set_option -hdl_param -set "$generic"
7787 Msg Info "Setting Diamond parameters/generics one by one..."
7788 prj_impl option -impl Implementation0 HDL_PARAM "$generic_string"
7790 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
7792 foreach app_name [dict keys $ws_apps] {
7793 set defined_symbols [app config -name $app_name -get define-compiler-symbols]
7794 foreach generic_to_set [
split [
string trim $generic_string]] {
7795 set key [
lindex [
split $generic_to_set "="] 0]
7796 set value [
lindex [
split $generic_to_set "="] 1]
7797 if {[
string match "32'h*" $value]} {
7798 set value [
string map {"32'h" "0x"} $value]
7801 foreach symbol [
split $defined_symbols ";"] {
7802 if {[
string match "$key=*" $symbol]} {
7803 Msg Debug "Generic $key found in $app_name, removing it..."
7804 app config -name $app_name -remove define-compiler-symbols "$symbol"
7808 Msg Info "Setting Vitis parameters/generics for app $app_name: $key=$value"
7809 app config -name $app_name define-compiler-symbols "$key=$value"
7821 proc WriteGenericsToBdIPs {mode repo_path proj generic_string} {
7822 Msg Debug "Parameters/generics passed to WriteGenericsToIP: $generic_string"
7824 set bd_ip_generics false
7826 if {[dict exists $properties "hog"]} {
7827 set propDict [dict get $properties "hog"]
7828 if {[dict exists $propDict "PASS_GENERICS_TO_BD_IPS"]} {
7829 set bd_ip_generics [dict get $propDict "PASS_GENERICS_TO_BD_IPS"]
7833 if {[
string compare [
string tolower $bd_ip_generics] "false"] == 0} {
7837 if {$mode == "synth"} {
7838 Msg Info "Attempting to apply generics pre-synthesis..."
7839 set PARENT_PRJ [get_property "PARENT.PROJECT_PATH" [current_project]]
7840 set workaround [open "$repo_path/Projects/$proj/.hog/presynth_workaround.tcl" "w"]
7841 puts $workaround "source \[lindex \$argv 0\];"
7842 puts $workaround "open_project \[lindex \$argv 1\];"
7843 puts $workaround "WriteGenericsToBdIPs \[lindex \$argv 2\] \[lindex \$argv 3\] \[lindex \$argv 4\] \[lindex \$argv 5\];"
7844 puts $workaround "close_project"
7848 exec vivado -mode batch -source $repo_path/Projects/$proj/.hog/presynth_workaround.tcl \
7849 -tclargs $repo_path/Hog/Tcl/hog.tcl $PARENT_PRJ \
7850 "childprocess" $repo_path $proj $generic_string
7853 Msg Error "Encountered an error while attempting workaround: $errMsg"
7855 file delete $repo_path/Projects/$proj/.hog/presynth_workaround.tcl
7857 Msg Info "Done applying generics pre-synthesis."
7861 Msg Info "Looking for IPs to add generics to..."
7862 set ips_generic_string ""
7863 foreach generic_to_set [
split [
string trim $generic_string]] {
7864 set key [
lindex [
split $generic_to_set "="] 0]
7865 set value [
lindex [
split $generic_to_set "="] 1]
7866 append ips_generic_string "CONFIG.$key $value "
7870 if {[
string compare [
string tolower $bd_ip_generics] "true"] == 0} {
7873 set ip_regex $bd_ip_generics
7876 set ip_list [get_ips -regex $ip_regex]
7877 Msg Debug "IPs found with regex \{$ip_regex\}: $ip_list"
7879 set regen_targets {}
7881 foreach {ip} $ip_list {
7882 set WARN_ABOUT_IP false
7883 set ip_props [list_property [get_ips $ip]]
7886 if {[lsearch -exact $ip_props "IS_BD_CONTEXT"] == -1} {
7890 if {[get_property "IS_BD_CONTEXT" [get_ips $ip]] eq "1"} {
7891 foreach {ip_prop} $ip_props {
7892 if {[dict exists $ips_generic_string $ip_prop]} {
7893 if {$WARN_ABOUT_IP == false} {
7894 lappend regen_targets [get_property SCOPE [get_ips $ip]]
7895 Msg Warning "The ip \{$ip\} contains generics that are set by Hog.\
7896 If this is IP is apart of a block design, the .bd file may contain stale, unused, values.\
7897 Hog will always apply the most up-to-date values to the IP during synthesis,\
7898 however these values may or may not be reflected in the .bd file."
7899 set WARN_ABOUT_IP true
7904 set xci_path [get_property IP_FILE [get_ips $ip]]
7906 if {[
string equal $generic_format "ERROR"]} {
7907 Msg Warning "Could not find format for generic $ip_prop in IP $ip. Skipping..."
7911 set value_to_set [dict get $ips_generic_string $ip_prop]
7912 switch -exact $generic_format {
7914 if {[
string match "32'h*" $value_to_set]} {
7915 scan [
string map {"32'h" ""} $value_to_set] "%x" value_to_set
7919 set value_to_set [
expr {$value_to_set ? "true" : "false"}]
7922 if {[
string match "32'h*" $value_to_set]} {
7923 binary scan [
binary format H* [
string map {"32'h" ""} $value_to_set]] d value_to_set
7927 if {[
string match "32'h*" $value_to_set]} {
7928 set value_to_set [
string map {"32'h" "0x"} $value_to_set]
7932 set value_to_set [
format "%s" $value_to_set]
7935 Msg Warning "Unknown generic format $generic_format for IP $ip. Will attempt to pass as string..."
7940 Msg Info "The IP \{$ip\} contains: $ip_prop ($generic_format), setting it to $value_to_set."
7941 if {[
catch {set_property -name $ip_prop -value $value_to_set -objects [get_ips $ip]} prop_error]} {
7942 Msg CriticalWarning "Failed to set property $ip_prop to $value_to_set for IP \{$ip\}: $prop_error"
7949 foreach {regen_target} [lsort -unique $regen_targets] {
7950 Msg Info "Regenerating target: $regen_target"
7951 if {[
catch {generate_target -force all [get_files $regen_target]} prop_error]} {
7952 Msg CriticalWarning "Failed to regen targets: $prop_error"
7960 proc GetGenericFormatFromXciXML {generic_name xml_file} {
7961 if {![
file exists $xml_file]} {
7962 Msg Error "Could not find XML file: $xml_file"
7966 set fp [open $xml_file r]
7967 set xci_data [read $fp]
7970 set paramType "string"
7971 set modelparam_regex [
format {^.*\y%s\y.*$} [
string map {"CONFIG." "MODELPARAM_VALUE."} $generic_name]]
7972 set format_regex {format="([^"]+)"}
7974 set line [
lindex [regexp -inline -line $modelparam_regex $xci_data] 0]
7975 Msg Debug "line: $line"
7977 if {[regexp $format_regex $line match format_value]} {
7978 Msg Debug "Extracted: $format_value format from xml"
7979 set paramType $format_value
7981 Msg Debug "No format found, using string"
7990 proc GetGenericFormatFromXci {generic_name xci_file} {
7991 if {![
file exists $xci_file]} {
7992 Msg Error "Could not find XCI file: $xci_file"
7996 set fp [open $xci_file r]
7997 set xci_data [read $fp]
8000 set paramType "string"
8001 if {[
string first "xilinx.com:schema:json_instance:1.0" $xci_data] == -1} {
8002 Msg Debug "XCI format is not JSON, trying XML..."
8003 set xml_file "[
file rootname $xci_file].xml"
8007 set generic_name [
string map {"CONFIG." ""} $generic_name]
8008 set ip_inst [
ParseJSON $xci_data "ip_inst"]
8009 set parameters [dict get $ip_inst parameters]
8010 set component_parameters [dict get $parameters component_parameters]
8011 if {[dict exists $component_parameters $generic_name]} {
8012 set generic_info [dict get $component_parameters $generic_name]
8013 if {[dict exists [
lindex $generic_info 0] format]} {
8014 set paramType [dict get [
lindex $generic_info 0] format]
8015 Msg Debug "Extracted: $paramType format from xci"
8018 Msg Debug "No format found, using string"
8031 proc WriteGitLabCIYAML {proj_name {ci_conf ""}} {
8032 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
8033 Msg CriticalWarning "Cannot find package YAML.\n Error message: $YAMLPACKAGE. \
8034 If you are running on tclsh, you can fix this by installing package \"tcllib\""
8039 if {$ci_conf != ""} {
8041 foreach sec [dict keys $ci_confs] {
8042 if {[
string first : $sec] == -1} {
8043 lappend job_list $sec
8047 set job_list {"generate_project" "simulate_project"}
8051 set out_yaml [huddle create]
8052 foreach job $job_list {
8054 set huddle_tags [huddle list]
8056 set sec_dict [dict create]
8058 if {$ci_confs != ""} {
8059 foreach var [dict keys [dict get $ci_confs $job]] {
8060 if {$var == "tags"} {
8061 set tag_section "tags"
8062 set tags [dict get [dict get $ci_confs $job] $var]
8063 set tags [
split $tags ","]
8065 set tag_list [huddle list $tag]
8066 set huddle_tags [huddle combine $huddle_tags $tag_list]
8069 dict set sec_dict $var [dict get [dict get $ci_confs $job] $var]
8075 set huddle_variables [huddle create "PROJECT_NAME" $proj_name "extends" ".vars"]
8076 if {[dict exists $ci_confs "$job:variables"]} {
8077 set var_dict [dict get $ci_confs $job:variables]
8078 foreach var [dict keys $var_dict] {
8080 set value [dict get $var_dict "$var"]
8081 set var_inner [huddle create "$var" "$value"]
8082 set huddle_variables [huddle combine $huddle_variables $var_inner]
8087 set middle [huddle create "extends" ".$job" "variables" $huddle_variables]
8088 foreach sec [dict keys $sec_dict] {
8089 set value [dict get $sec_dict $sec]
8090 set var_inner [huddle create "$sec" "$value"]
8091 set middle [huddle combine $middle $var_inner]
8093 if {$tag_section != ""} {
8094 set middle2 [huddle create "$tag_section" $huddle_tags]
8095 set middle [huddle combine $middle $middle2]
8098 set outer [huddle create "$job:$proj_name" $middle]
8099 set out_yaml [huddle combine $out_yaml $outer]
8102 return [
string trimleft [yaml::huddle2yaml $out_yaml] "-"]
8112 proc WriteListFiles {libs props list_path repo_path {ext_path ""}} {
8114 foreach lib [dict keys $libs] {
8115 if {[
llength [
DictGet $libs $lib]] > 0} {
8116 set list_file_name $list_path$lib
8117 set list_file [open $list_file_name w]
8118 Msg Info "Writing $list_file_name..."
8119 puts $list_file "#Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
8120 foreach file [
DictGet $libs $lib] {
8122 set prop [
DictGet $props $file]
8126 puts $list_file "$file_path $prop"
8129 set ext_list_file [open "[
file rootname $list_file].ext" a]
8130 puts $ext_list_file "$file_path $prop"
8131 close $ext_list_file
8134 Msg Warning "The path of file $file is not relative to your repository. Please check!"
8150 proc WriteSimListFile {simset libs props simsets list_path repo_path {force 0}} {
8152 set list_file_name $list_path/${simset}.sim
8153 if {$force == 0 && [
file exists $list_file_name]} {
8154 Msg Info "List file $list_file_name already exists, skipping..."
8158 set list_file [open $list_file_name a+]
8161 puts $list_file "\[files\]"
8162 Msg Info "Writing $list_file_name..."
8163 foreach lib [
DictGet $simsets $simset] {
8164 foreach file [
DictGet $libs $lib] {
8166 set prop [
DictGet $props $file]
8170 set lib_name [
file rootname $lib]
8171 if {$lib_name != $simset && [
file extension $file] == ".vhd" && [
file extension $file] == ""} {
8172 lappend prop "lib=$lib_name"
8174 puts $list_file "$file_path $prop"
8177 Msg Warning "The path of file $file is not relative to your repository. Please check!"
8189 proc WriteToFile {File msg} {
8190 set f [open $File a+]
8201 proc WriteUtilizationSummary {input output project_name run} {
8202 set f [open $input "r"]
8203 set o [open $output "a"]
8204 puts $o "## $project_name $run Utilization report\n\n"
8205 struct::matrix util_m
8206 util_m add columns 14
8209 util_m add row "| **Site Type** | **Used** | **Fixed** | **Prohibited** | **Available** | **Util%** |"
8210 util_m add row "| --- | --- | --- | --- | --- | --- |"
8212 util_m add row "| **Site Type** | **Used** | **Fixed** | **Available** | **Util%** |"
8213 util_m add row "| --- | --- | --- | --- | --- |"
8223 while {[
gets $f line] >= 0} {
8224 if {([
string first "| CLB LUTs" $line] >= 0 || [
string first "| Slice LUTs" $line] >= 0) && $luts == 0} {
8225 util_m add row $line
8228 if {([
string first "| CLB Registers" $line] >= 0 || [
string first "| Slice Registers" $line] >= 0) && $regs == 0} {
8229 util_m add row $line
8232 if {[
string first "| Block RAM Tile" $line] >= 0 && $bram == 0} {
8233 util_m add row $line
8236 if {[
string first "URAM " $line] >= 0 && $uram == 0} {
8237 util_m add row $line
8240 if {[
string first "DSPs" $line] >= 0 && $dsps == 0} {
8241 util_m add row $line
8244 if {[
string first "Bonded IOB" $line] >= 0 && $ios == 0} {
8245 util_m add row $line
8252 puts $o [util_m format 2string]
8258 Msg Error "Found Git version older than 2.7.2. Hog will not work as expected, exiting now."
8267 proc GetCurl {{gitlab_url "https://gitlab.com" }} {
8268 if {[
auto_execok curl] == ""} {
8269 Msg Warning "Cannot find a working curl invocation"
8274 set cmd [list curl --silent --show-error]
8276 set cmd [list env -u LD_LIBRARY_PATH curl --silent --show-error]
8279 if {![
catch {
exec {*}$cmd -I $gitlab_url}]} {
8282 Msg Warning "Impossible to contact $gitlab_url"