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
577 foreach app_name [dict keys $ws_apps] {
578 foreach f $lib_files {
579 if {[
string tolower $rootlib] != [
string tolower "app_$app_name"]} {
583 Msg Info "Adding source file $f from lib: $lib to vitis app \[$app_name\]..."
584 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
585 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
586 Msg Debug "Project_f_path is $proj_f_path"
588 importsources -name $app_name -soft-link -path $f -target $proj_f_path
592 Msg Debug "Vitis Unified: Collecting files for apps from library $rootlib..."
596 if {[dict size $ws_apps] == 0} {
597 Msg Debug "No apps found in workspace, skipping file collection"
599 Msg Debug "Found [dict size $ws_apps] app(s) in workspace"
600 Msg Debug "Processing library: $rootlib with [
llength $lib_files] file(s)"
601 foreach app_name [dict keys $ws_apps] {
602 set expected_lib [
string tolower "app_$app_name"]
603 Msg Debug "Checking files for app: $app_name (looking for lib: $expected_lib, current lib: [
string tolower $rootlib])"
604 foreach f $lib_files {
605 if {[
string tolower $rootlib] != $expected_lib} {
608 Msg Debug "File $f matches app $app_name"
609 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
610 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
611 set proj_f_path [
string trimleft $proj_f_path "/"]
613 if {![dict exists $app_files_dict $app_name]} {
614 dict set app_files_dict $app_name files [list]
615 dict set app_files_dict $app_name target $proj_f_path
616 Msg Debug "Initialized app_files_dict for $app_name with target: $proj_f_path"
619 set current_files [dict get $app_files_dict $app_name files]
620 lappend current_files $f
621 dict set app_files_dict $app_name files $current_files
622 set current_count [
llength [dict get $app_files_dict $app_name files]]
623 Msg Debug "Added file $f to app_files_dict for $app_name, current count: $current_count"
633 set python_script "$globalSettings::repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"
634 set vitis_workspace "$globalSettings::build_dir/vitis_unified"
638 set env(HOG_VITIS_VER) $vitis_version
639 Msg Debug "Vitis version: $vitis_version (set in HOG_VITIS_VER environment variable)"
641 dict for {app_name app_data} $app_files_dict {
642 set files_list [dict get $app_data files]
643 set target_path [dict get $app_data target]
645 if {[llength $files_list] > 0} {
646 Msg Info "Adding [llength $files_list] source file(s) to vitis app \[$app_name\]..."
647 Msg Debug "Files: $files_list"
648 Msg Debug "Target path: $target_path"
650 # Convert Tcl list to JSON array for Python
653 foreach f $files_list {
655 append files_json ", "
657 # Escape backslashes and quotes in file paths
658 set escaped_f [string map {\\ \\\\ \" \\\"} $f]
659 append files_json "\"$escaped_f\""
662 append files_json "\]"
664 Msg Debug "JSON string: $files_json"
666 set error_msg "Failed to add files to app $app_name"
668 ![ExecuteVitisUnifiedCommand $python_script "add_app_files" \
669 [list $app_name $files_json $vitis_workspace $target_path] \
672 Msg Error "Failed to add files to Vitis Unified app '$app_name'"
676 Msg Warning "No files to add for app '$app_name'"
683 if {[
DictGet $filesets "sim_1"] == ""} {
684 delete_fileset -quiet [get_filesets -quiet "sim_1"]
690 if {$synth_conf == 1} {
691 Msg Info $synth_conf_command
692 eval $synth_conf_command
694 if {$timing_conf == 1} {
695 Msg Info $timing_conf_command
696 eval $timing_conf_command
698 if {$place_conf == 1} {
699 Msg Info $place_conf_command
700 eval $place_conf_command
706 proc ALLOWED_PROPS {} {
707 return [dict create ".vhd" [list "93" "nosynth" "noimpl" "nosim" "1987" "1993" "2008" "2019"] \
708 ".vhdl" [list "93" "nosynth" "noimpl" "nosim" "1987" "1993" "2008" "2019"] \
709 ".bd" [list "nosim"] \
710 ".v" [list "SystemVerilog" "verilog_header" "nosynth" "noimpl" "nosim" "1995" "2001"] \
711 ".sv" [list "verilog" "verilog_header" "nosynth" "noimpl" "nosim" "2005" "2009"] \
712 ".svp" [list "verilog" "verilog_header" "nosynth" "noimpl" "nosim" "2005" "2009"] \
713 ".do" [list "nosim"] \
714 ".udo" [list "nosim"] \
715 ".xci" [list "nosynth" "noimpl" "nosim" "locked"] \
716 ".xdc" [list "nosynth" "noimpl" "scoped_to_ref" "scoped_to_cells"] \
717 ".tcl" [list "nosynth" "noimpl" "nosim" "scoped_to_ref" "scoped_to_cells" "source" "qsys" "noadd" \
718 "--block-symbol-file" "--clear-output-directory" "--example-design" \
719 "--export-qsys-script" "--family" "--greybox" "--ipxact" \
720 "--jvm-max-heap-size" "--parallel" "--part" "--search-path" \
721 "--simulation" "--synthesis" "--testbench" "--testbench-simulation" \
722 "--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"] \
727 ".sdc" [list "notiming" "nosynth" "noplace"] \
728 ".elf" [list "scoped_to_ref" "scoped_to_cells" "nosim" "noimpl"] \
729 ".pdc" [list "nosynth" "noplace"] \
730 ".lpf" [list "enable"]]
741 proc BinaryStepName {part} {
743 return "WRITE_DEVICE_IMAGE"
747 return "WRITE_BITSTREAM"
754 set essential_vars [dict create \
755 "HOG_USER" "NOT defined. This variable is essential for git to work properly. \
756 It should be set to the username for your service account (a valid git account)." \
757 "HOG_EMAIL" "NOT defined. This variable is essential for git to work properly. It should be set to your service's account email." \
758 "HOG_PUSH_TOKEN" "NOT defined. This variable is essential for git to work properly. \
759 It should be set to a Gitlab/GitHub API token for your service account."]
762 dict for {var msg} $essential_vars {
763 if {![info exists env($var)]} {
764 Msg CriticalWarning "Essential environment variable $var is $msg"
767 Msg Info "Found environment variable $var."
771 set additional_vars [dict create \
772 "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." \
773 "HOG_TARGET_BRANCH" "NOT defined. Default branch for merge is \"master\"" \
774 "HOG_CREATE_OFFICIAL_RELEASE" "NOT defined. \
775 Set this variable to '1' to make Hog create an official release in GitHub/Gitlab with the binaries generated in the CI." \
776 "HOG_USE_DOXYGEN" "NOT defined. \
777 Set this variable to 1 to make Hog-CI run Doxygen and copy the official documentation over when you merge to the official branch."]
780 ([
info exists env(HOG_OFFICIAL_BIN_EOS_PATH)] && $env(HOG_OFFICIAL_BIN_EOS_PATH) ne "") ||
781 ([
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."
820 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
828 proc CheckEnv {project_name ide} {
831 set essential_commands [dict create "git" "--version" "$ide" "-version"]
832 set additional_commands [dict create \
836 "rclone" "--version"]
838 set additional_vars [dict create \
839 "HOG_PATH" "NOT defined. Hog might work as long as all the necessary executable are in the PATH variable." \
840 "HOG_XIL_LICENSE" "NOT defined. If this variable is not set to the license servers separated by comas, \
841 you need some alternative way of getting your Xilinx license (for example a license file on the machine)." \
842 "LM_LICENSE_FILE" "NOT defined. This variable should be set the Quartus/Libero license servers separated by semicolon. \
843 If not, you need an alternative way of getting your Quartus/Libero license." \
844 "HOG_LD_LIBRARY_PATH" "NOT defined. Hog might work as long as all the necessary library are found." \
845 "HOG_SIMULATION_LIB_PATH" "NOT defined. Hog-CI will not be able to run simulations using third-party simulators." \
846 "HOG_CHECK_PROJVER" "NOT defined. Hog will NOT check the CI project version. \
847 Set this variable to '1' if you want Hog to check the CI project version before creating the HDL project in Create_Project stage. \
848 If the project has not been changed with respect to the target branch, the CI will skip this project" \
849 "HOG_CHECK_SYNTAX" "NOT defined. Hog will NOT check the syntax. \
850 Set this variable to '1' if you want Hog to check the syntax after creating the HDL project in Create_Project stage." \
851 "HOG_NO_BITSTREAM" "NOT defined. Hog-CI will run the implementation up to the write_bitstream stage and create bit files." \
852 "HOG_NO_RESET_BD" "NOT defined or not equal to 1. Hog will reset .bd files (if any) before starting synthesis." \
853 "HOG_IP_PATH" "NOT defined. Hog-CI will NOT use an EOS/LOCAL IP repository to speed up the IP synthesis." \
854 "HOG_RESET_FILES" "NOT defined. Hog-CI will NOT reset any files." \
855 "HOG_NJOBS" "NOT defined. Hog-CI will build IPs with default number of jobs (4)." \
856 "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"]
857 Msg Info "Checking environment to run Hog-CI for project $project_name with IDE $ide..."
859 Msg Info "Checking essential commands..."
860 dict for {cmd ver} $essential_commands {
861 if {[catch {exec which $cmd}]} {
862 Msg CriticalWarning "$cmd executable not found. Hog-CI cannot run!"
865 Msg Info "Found executable $cmd."
866 if {$cmd == "ghdl"} {
867 Msg Info [exec $cmd --version]
868 } elseif {$cmd != "diamond"} {
869 Msg Info [exec $cmd $ver]
874 Msg Info "Checking additional commands..."
875 dict for {cmd ver} $additional_commands {
876 if {[catch {exec which $cmd}]} {
877 Msg Warning "$cmd executable not found."
879 Msg Info "Found executable $cmd."
881 Msg Info [exec $cmd $ver]
886 if {$ide == "libero"} {
887 Msg Info "Checking essential environment variables..."
889 if {![
info exists env(HOG_TCLLIB_PATH)]} {
890 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."
893 Msg Info "HOG_TCLLIB_PATH is set. Hog-CI can run with Libero."
897 Msg Info "Checking additional environment variables..."
898 dict for {var msg} $additional_vars {
899 if {![info exists env($var)]} {
900 Msg Info "Environment variable $var is $msg"
902 Msg Info "Found environment variable $var."
907 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
920 proc CheckRemoteIpPath {repo_path ip_path} {
928 if {[regexp {^[^/]+:} $ip_path]} {
932 lassign [
ExecuteRet rclone --version] rclone_ret rclone_ver
933 if {$rclone_ret != 0} {
934 Msg Warning "Rclone path specified but rclone not found or failed: $rclone_ver"
938 Msg Info "IP remote directory path, on Rclone, is set to: $ip_path"
940 if {[
info exists env(HOG_RCLONE_CONFIG)]} {
941 Msg Info "Using rclone config from environment variable HOG_RCLONE_CONFIG: $env(HOG_RCLONE_CONFIG)"
942 set config_path $env(HOG_RCLONE_CONFIG)
944 set config_path "/dev/null"
945 Msg Info "Environment variable HOG_RCLONE_CONFIG not set, using rclone environmental variables..."
948 set remote_name "[
lindex [
split $ip_path ":"] 0]:"
949 lassign [
ExecuteRet rclone listremotes --config $config_path] rclone_list_ret remotes
950 if {$rclone_list_ret != 0} {
951 Msg Warning "Could not list rclone remotes: $remotes"
955 if {![
IsInList $remote_name $remotes]} {
956 Msg Warning "Rclone remote $remote_name not found among available remotes: $remotes"
962 }
elseif {[
string first "/eos/" $ip_path] == 0} {
965 if {!([
info exists ::env(ENABLE_EOS)] && $::env(ENABLE_EOS) == 1)} {
966 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."
971 if {[
file isdirectory $ip_path]} {
972 Msg Info "Eos is mounted in the current machine. Treating it as a normal directory..."
975 lassign [
eos "ls $ip_path"] ret result
977 Msg Warning "Could not run ls for for EOS path: $ip_path (error: $result). \
978 Either the drectory does not exist or there are (temporary) problem with EOS."
982 Msg Info "IP remote directory path, on EOS, is set to: $ip_path"
990 return [list 1 $on_eos $on_rclone $config_path]
993 proc CheckProjVer {repo_path project {sim 0} {ext_path ""}} {
997 Msg Info "Will check also the version of the simulation files..."
1001 if {[
info exists env(HOG_PUSH_TOKEN)] && [
info exist env(CI_PROJECT_ID)] && [
info exist env(CI_API_V4_URL)]} {
1002 set token $env(HOG_PUSH_TOKEN)
1003 set api_url $env(CI_API_V4_URL)
1004 set project_id $env(CI_PROJECT_ID)
1006 set curl_cmd [
GetCurl $api_url]
1007 if {[
info exist env(CI_JOB_NAME)] && $env(CI_JOB_NAME) == "check_branch_state"} {
1016 set project_dir $repo_path/Top/$project
1019 Msg Info "$project was modified, continuing with the CI..."
1020 if {$ci_run == 1 && $curl_cmd != 0 && ![
IsQuartus] && ![
IsISE]} {
1021 Msg Info "Checking if the project has been already built in a previous CI run..."
1023 if {$sha == [
GetSHA $repo_path]} {
1024 Msg Info "Project was modified in the current commit, Hog will proceed with the build workflow."
1027 Msg Info "Checking if project $project has been built in a previous CI run with sha $sha..."
1028 set result [
catch {
package require json} JsonFound]
1029 if {"$result" != "0"} {
1030 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
1033 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/$project_id/pipelines"] ret content
1034 set pipeline_dict [json::json2dict $content]
1035 if {[
llength $pipeline_dict] > 0} {
1036 foreach pip $pipeline_dict {
1037 set pip_sha [
DictGet $pip sha]
1038 set source [
DictGet $pip source]
1039 if {$source == "merge_request_event" && [
string first $sha $pip_sha] != -1} {
1040 Msg Info "Found pipeline with sha $pip_sha for project $project"
1041 set pipeline_id [
DictGet $pip id]
1043 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/${project_id}/pipelines/${pipeline_id}/jobs?pagination=keyset&per_page=100"] ret2 content2
1044 set jobs_dict [json::json2dict $content2]
1045 if {[
llength $jobs_dict] > 0} {
1046 foreach job $jobs_dict {
1047 set job_name [
DictGet $job name]
1049 set artifacts [
DictGet $job artifacts_file]
1050 set status [
DictGet $job status]
1051 set current_job_name $env(CI_JOB_NAME)
1052 if {$current_job_name == $job_name && $status == "success"} {
1054 lassign [
ExecuteRet {*}$curl_cmd --location --output artifacts.zip --header "PRIVATE-TOKEN: $token" --url "$api_url/projects/$project_id/jobs/$job_id/artifacts"] ret3 content3
1056 Msg CriticalWarning "Cannot download artifacts for job $job_name with id $job_id"
1059 lassign [
ExecuteRet unzip -o $repo_path/artifacts.zip] ret_zip
1060 if {$ret_zip != 0} {
1063 Msg Info "Artifacts for job $job_name with id $job_id downloaded and unzipped."
1064 file mkdir $repo_path/Projects/$project
1065 set fp [open "$repo_path/Projects/$project/skip.me" w+]
1077 }
elseif {$ver != -1} {
1078 Msg Info "$project was not modified since version: $ver."
1079 file mkdir $repo_path/Projects/$project
1080 set fp [open "$repo_path/Projects/$project/skip.me" w+]
1084 Msg Error "Impossible to check the project version. Most likely the repository is not clean. Please, commit your changes before running this command."
1095 proc CheckSyntax {project_name repo_path {project_file ""}} {
1097 update_compile_order
1098 set syntax [check_syntax -return_string]
1099 if {[
string first "CRITICAL" $syntax] != -1} {
1104 lassign [
GetHogFiles -list_files "*.src" "$repo_path/Top/$project_name/list/" $repo_path] src_files dummy
1105 dict for {lib files} $src_files {
1107 set file_extension [file extension $f]
1108 if {$file_extension == ".vhd" || $file_extension == ".vhdl" || $file_extension == ".v" || $file_extension == ".sv"} {
1109 if {[catch {execute_module -tool map -args "--analyze_file=$f"} result]} {
1110 Msg Error "\nResult: $result\n"
1111 Msg Error "Check syntax failed.\n"
1113 if {$result == ""} {
1114 Msg Info "Check syntax was successful for $f.\n"
1116 Msg Warning "Found syntax error in file $f:\n $result\n"
1123 lassign [
GetProjectFiles $project_file] prjLibraries prjProperties prjSimLibraries prjConstraints prjSrcSets prjSimSets prjConSets
1124 dict for {lib sources} $prjLibraries {
1125 if {[file extension $lib] == ".src"} {
1126 foreach f $sources {
1127 Msg Info "Checking Syntax of $f"
1133 Msg Info "The Checking Syntax is not supported by this IDE. Skipping..."
1138 proc CloseProject {} {
1160 proc CompareVersions {ver1 ver2} {
1169 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver1 - x y z]} {
1170 set ver1 [list $x $y $z]
1172 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver2 - x y z]} {
1173 set ver2 [list $x $y $z]
1177 set v1 [
join $ver1 ""]
1179 set v2 [
join $ver2 ""]
1182 if {[
string is integer $v1] && [
string is integer $v2]} {
1183 set ver1 [
expr {[scan [lindex $ver1 0] %d] * 1000000 + [scan [lindex $ver1 1] %d] * 1000 + [scan [lindex $ver1 2] %d]}]
1184 set ver2 [
expr {[scan [lindex $ver2 0] %d] * 1000000 + [scan [lindex $ver2 1] %d] * 1000 + [scan [lindex $ver2 2] %d]}]
1186 if {$ver1 > $ver2} {
1188 }
elseif {$ver1 == $ver2} {
1194 Msg Warning "Version is not numeric: $ver1, $ver2"
1197 return [
expr {$ret}]
1204 proc CheckExtraFiles {libraries constraints simlibraries} {
1207 lassign [
GetProjectFiles] prjLibraries prjProperties prjSimLibraries prjConstraints
1208 set prj_dir [get_property DIRECTORY [current_project]]
1209 file mkdir "$prj_dir/.hog"
1210 set extra_file_name "$prj_dir/.hog/extra.files"
1211 set new_extra_file [open $extra_file_name "w"]
1213 dict for {prjLib prjFiles} $prjLibraries {
1214 foreach prjFile $prjFiles {
1215 if {[file extension $prjFile] == ".xcix"} {
1216 Msg Warning "IP $prjFile is packed in a .xcix core container. \
1217 This files are not suitable for version control systems. We recommend to use .xci files instead."
1220 if {[file extension $prjFile] == ".xci" && [get_property CORE_CONTAINER [get_files $prjFile]] != ""} {
1221 Msg Info "$prjFile is a virtual IP file in a core container. Ignoring it..."
1225 if {[IsInList $prjFile [DictGet $libraries $prjLib]] == 0} {
1226 if {[file extension $prjFile] == ".bd"} {
1227 # Generating BD products to save md5sum of already modified BD
1228 Msg Info "Generating targets of $prjFile..."
1229 generate_target all [get_files $prjFile]
1231 puts $new_extra_file "$prjFile [Md5Sum $prjFile]"
1232 Msg Info "$prjFile (lib: $prjLib) has been generated by an external script. Adding to $extra_file_name..."
1236 close $new_extra_file
1237 set extra_sim_file "$prj_dir/.hog/extrasim.files"
1238 set new_extra_file [open $extra_sim_file "w"]
1240 dict for {prjSimLib prjSimFiles} $prjSimLibraries {
1241 foreach prjSimFile $prjSimFiles {
1242 if {[IsInList $prjSimFile [DictGet $simlibraries $prjSimLib]] == 0} {
1243 puts $new_extra_file "$prjSimFile [Md5Sum $prjSimFile]"
1244 Msg Info "$prjSimFile (lib: $prjSimLib) has been generated by an external script. Adding to $extra_sim_file..."
1248 close $new_extra_file
1249 set extra_con_file "$prj_dir/.hog/extracon.files"
1250 set new_extra_file [open $extra_con_file "w"]
1252 dict for {prjConLib prjConFiles} $prjConstraints {
1253 foreach prjConFile $prjConFiles {
1254 if {[IsInList $prjConFile [DictGet $constraints $prjConLib]] == 0} {
1255 puts $new_extra_file "$prjConFile [Md5Sum $prjConFile]"
1256 Msg Info "$prjConFile has been generated by an external script. Adding to $extra_con_file..."
1260 close $new_extra_file
1267 proc CheckLatestHogRelease {{repo_path .}} {
1268 if {[
info exists ::env(CHECK_FOR_HOG_UPDATES)] && $::env(CHECK_FOR_HOG_UPDATES) eq "0"} {
1273 if {[
catch {
package require inifile 0.2.3} ERROR] == 0} {
1274 set repo_conf $repo_path/Top/repo.conf
1275 if {[
file exists $repo_conf]} {
1276 set PROPERTIES [
ReadConf $repo_conf]
1277 if {[dict exists $PROPERTIES main]} {
1278 set mainDict [dict get $PROPERTIES main]
1279 if {[dict exists $mainDict CHECK_FOR_HOG_UPDATES]} {
1280 if {[dict get $mainDict CHECK_FOR_HOG_UPDATES] == 0} {
1290 set current_ver [
Git {describe --always}]
1291 Msg Debug "Current version: $current_ver"
1292 set current_sha [
Git "log $current_ver -1 --format=format:%H"]
1293 Msg Debug "Current SHA: $current_sha"
1296 if {[
OS] == "windows"} {
1297 Msg Info "On windows we cannot set a timeout on 'git fetch', hopefully nothing will go wrong..."
1300 Msg Info "Checking for latest Hog release, can take up to 5 seconds..."
1303 set master_ver [
Git "describe origin/master"]
1304 Msg Debug "Master version: $master_ver"
1305 set master_sha [
Git "log $master_ver -1 --format=format:%H"]
1306 Msg Debug "Master SHA: $master_sha"
1307 lassign [
GitRet "merge-base $current_sha $master_sha"] mb_ret merge_base
1309 Msg Warning "Could not run git merge-base (shallow clone?). Skipping Hog update check."
1313 Msg Debug "merge base: $merge_base"
1316 if {$merge_base != $master_sha} {
1318 Msg Info "Version $master_ver has been released (https://gitlab.com/hog-cern/Hog/-/releases/$master_ver)"
1319 Msg Status "You should consider updating Hog submodule with the following instructions:"
1321 Msg Status "cd Hog && git checkout master && git pull"
1323 Msg Status "Also update the ref: in your .gitlab-ci.yml to $master_ver"
1327 Msg Info "Latest official version is $master_ver, nothing to do."
1339 proc CheckYmlRef {repo_path allow_failure} {
1340 if {$allow_failure} {
1341 set MSG_TYPE CriticalWarning
1346 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
1347 Msg CriticalWarning "Cannot find package YAML, skipping consistency check of \"ref\" in gilab-ci.yaml file.\n Error message: $YAMLPACKAGE
1348 You can fix this by installing package \"tcllib\""
1356 if {[
file exists .gitlab-ci.yml]} {
1360 if {[
file exists .gitlab-ci.yml]} {
1361 set fp [open ".gitlab-ci.yml" r]
1362 set file_data [read $fp]
1365 Msg $MSG_TYPE "Cannot open file .gitlab-ci.yml"
1369 set file_data "\n$file_data\n\n"
1371 if {[
catch {::yaml::yaml2dict -stream $file_data} yamlDict]} {
1372 Msg $MSG_TYPE "Parsing $repo_path/.gitlab-ci.yml failed. To fix this, check that yaml syntax is respected, remember not to use tabs."
1376 dict for {dictKey dictValue} $yamlDict {
1377 #looking for Hog include in .gitlab-ci.yml
1379 "$dictKey" == "include" && (
1380 [lsearch [split $dictValue " {}"] "/hog.yml"] != "-1" ||
1381 [lsearch [split $dictValue " {}"] "/hog-dynamic.yml"] != "-1"
1384 set YML_REF [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "ref"] + 1}]]
1385 set YML_NAME [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "file"] + 1}]]
1389 if {$YML_REF == ""} {
1390 Msg Warning "Hog version not specified in the .gitlab-ci.yml. Assuming that master branch is used."
1392 set YML_REF_F [
Git {name-rev --tags --name-only origin/master}]
1395 set YML_REF_F [regsub -all "'" $YML_REF ""]
1398 if {$YML_NAME == ""} {
1399 Msg $MSG_TYPE "Hog included yml file not specified, assuming hog.yml"
1400 set YML_NAME_F hog.yml
1402 set YML_NAME_F [regsub -all "^/" $YML_NAME ""]
1405 lappend YML_FILES $YML_NAME_F
1411 if {[
catch {::yaml::yaml2dict -file $YML_NAME_F} yamlDict]} {
1412 Msg $MSG_TYPE "Parsing $YML_NAME_F failed."
1416 dict for {dictKey dictValue} $yamlDict {
1417 #looking for included files
1418 if {"$dictKey" == "include"} {
1419 foreach v $dictValue {
1420 lappend YML_FILES [lindex [split $v " "] [expr {[lsearch -dictionary [split $v " "] "local"] + 1}]]
1426 Msg Info "Found the following yml files: $YML_FILES"
1428 set HOGYML_SHA [
GetSHA $YML_FILES]
1429 lassign [
GitRet "log --format=%h -1 --abbrev=7 $YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1431 lassign [
GitRet "log --format=%h -1 --abbrev=7 origin/$YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1433 Msg $MSG_TYPE "Error in project .gitlab-ci.yml. ref: $YML_REF not found"
1434 set EXPECTEDYML_SHA ""
1437 if {!($EXPECTEDYML_SHA eq "")} {
1438 if {$HOGYML_SHA == $EXPECTEDYML_SHA} {
1439 Msg Info "Hog included file $YML_FILES matches with $YML_REF in .gitlab-ci.yml."
1441 Msg $MSG_TYPE "HOG $YML_FILES SHA mismatch.
1442 From Hog submodule: $HOGYML_SHA
1443 From ref in .gitlab-ci.yml: $EXPECTEDYML_SHA
1444 You can fix this in 2 ways: by changing the ref in your repository or by changing the Hog submodule commit"
1447 Msg $MSG_TYPE "One or more of the following files could not be found $YML_FILES in Hog at $YML_REF"
1450 Msg Info ".gitlab-ci.yml not found in $repo_path. Skipping this step"
1471 proc CompareLibDicts {proj_libs list_libs proj_sets list_sets proj_props list_props {severity "CriticalWarning"} {outFile ""} {extraFiles ""}} {
1472 set extra_files $extraFiles
1474 set out_prjlibs $proj_libs
1475 set out_prjprops $proj_props
1477 dict for {prjSet prjLibraries} $proj_sets {
1478 # Check if sets is also in list files
1479 if {[IsInList $prjSet $list_sets]} {
1480 set listLibraries [DictGet $list_sets $prjSet]
1481 # Loop over libraries in fileset
1482 foreach prjLib $prjLibraries {
1483 set prjFiles [DictGet $proj_libs $prjLib]
1484 # Check if library exists in list files
1485 if {[IsInList $prjLib $listLibraries]} {
1486 # Loop over files in library
1487 set listFiles [DictGet $list_libs $prjLib]
1488 foreach prjFile $prjFiles {
1489 set idx [lsearch -exact $listFiles $prjFile]
1490 set listFiles [lreplace $listFiles $idx $idx]
1492 # File is in project but not in list libraries, check if it was generated at creation time...
1493 if {[dict exists $extra_files $prjFile]} {
1494 # File was generated at creation time, checking the md5sum
1495 # Removing the file from the prjFiles list
1496 set idx2 [lsearch -exact $prjFiles $prjFile]
1497 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1498 set new_md5sum [Md5Sum $prjFile]
1499 set old_md5sum [DictGet $extra_files $prjFile]
1500 if {$new_md5sum != $old_md5sum} {
1501 # tclint-disable-next-line line-length
1502 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
1505 set extra_files [dict remove $extra_files $prjFile]
1507 # File is neither in list files nor in extra_files
1508 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1512 # File is both in list files and project, checking properties...
1513 set prjProps [DictGet $proj_props $prjFile]
1514 set listProps [DictGet $list_props $prjFile]
1515 # Check if it is a potential sourced file
1516 if {[IsInList "nosynth" $prjProps] && [IsInList "noimpl" $prjProps] && [IsInList "nosim" $prjProps]} {
1517 # Check if it is sourced
1518 set idx_source [lsearch -exact $listProps "source"]
1519 if {$idx_source >= 0} {
1520 # It is sourced, let's replace the individual properties with source
1521 set idx [lsearch -exact $prjProps "noimpl"]
1522 set prjProps [lreplace $prjProps $idx $idx]
1523 set idx [lsearch -exact $prjProps "nosynth"]
1524 set prjProps [lreplace $prjProps $idx $idx]
1525 set idx [lsearch -exact $prjProps "nosim"]
1526 set prjProps [lreplace $prjProps $idx $idx]
1527 lappend prjProps "source"
1531 foreach prjProp $prjProps {
1532 set idx [lsearch -exact $listProps $prjProp]
1533 set listProps [lreplace $listProps $idx $idx]
1535 MsgAndLog "Property $prjProp of $prjFile was set in project but not in list files" $severity $outFile
1540 foreach listProp $listProps {
1541 if {[string first $listProp "topsim="] == -1 && [string first $listProp "enable"] == -1} {
1542 MsgAndLog "Property $listProp of $prjFile was found in list files but not set in project." $severity $outFile
1547 # Update project prjProps
1548 dict set out_prjprops $prjFile $prjProps
1551 # Loop over remaining files in list libraries
1552 foreach listFile $listFiles {
1553 MsgAndLog "$listFile was found in list files but not in project." $severity $outFile
1557 # Check extra files again...
1558 foreach prjFile $prjFiles {
1559 if {[dict exists $extra_files $prjFile]} {
1560 # File was generated at creation time, checking the md5sum
1561 # Removing the file from the prjFiles list
1562 set idx2 [lsearch -exact $prjFiles $prjFile]
1563 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1564 set new_md5sum [Md5Sum $prjFile]
1565 set old_md5sum [DictGet $extra_files $prjFile]
1566 if {$new_md5sum != $old_md5sum} {
1567 # tclint-disable-next-line line-length
1568 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
1571 set extra_files [dict remove $extra_files $prjFile]
1573 # File is neither in list files nor in extra_files
1574 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1579 # Update prjLibraries
1580 dict set out_prjlibs $prjLib $prjFiles
1583 MsgAndLog "Fileset $prjSet found in project but not in list files" $severity $outFile
1588 return [list $n_diffs $extra_files $out_prjlibs $out_prjprops]
1598 proc CompareVHDL {file1 file2} {
1599 set a [open $file1 r]
1600 set b [open $file2 r]
1602 while {[
gets $a line] != -1} {
1603 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1604 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1610 while {[
gets $b line] != -1} {
1611 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1612 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1621 foreach x $f1 y $f2 {
1623 lappend diff "> $x\n< $y\n\n"
1636 proc Copy {i_dirs o_dir} {
1637 foreach i_dir $i_dirs {
1638 if {[
file isdirectory $i_dir] && [
file isdirectory $o_dir]} {
1639 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]])} {
1640 file delete -force $o_dir/[
file tail $i_dir]
1644 file copy -force $i_dir $o_dir
1659 proc CopyIPbusXMLs {proj_dir path dst {xml_version "0.0.0"} {xml_sha "00000000"} {use_ipbus_sw 0} {generate 0}} {
1660 if {$use_ipbus_sw == 1} {
1661 lassign [
ExecuteRet python3 -c "from __future__ import print_function; from sys import path;print(':'.join(path\[1:\]))"] ret msg
1663 set ::env(PYTHONPATH) $msg
1664 lassign [
ExecuteRet gen_ipbus_addr_decode -h] ret msg
1671 Msg CriticalWarning "Problem while trying to run python: $msg"
1674 set dst [
file normalize $dst]
1676 if {$can_generate == 0} {
1677 if {$generate == 1} {
1678 Msg Error "Cannot generate IPbus address files, IPbus executable gen_ipbus_addr_decode not found or not working: $msg"
1681 Msg Warning "IPbus executable gen_ipbus_addr_decode not found or not working, will not verify IPbus address tables."
1688 set ipb_files [glob -nocomplain $proj_dir/list/*.ipb]
1689 set n_ipb_files [
llength $ipb_files]
1690 if {$n_ipb_files == 0} {
1691 Msg CriticalWarning "No files with .ipb extension found in $proj_dir/list."
1694 set libraries [dict create]
1695 set vhdl_dict [dict create]
1697 foreach ipb_file $ipb_files {
1703 set xmlfiles [dict get $libraries "xml.ipb"]
1705 set xml_list_error 0
1706 foreach xmlfile $xmlfiles {
1707 if {[
file isdirectory $xmlfile]} {
1708 Msg CriticalWarning "Directory $xmlfile listed in xml list file $list_file. Directories are not supported!"
1709 set xml_list_error 1
1712 if {[
file exists $xmlfile]} {
1713 if {[dict exists $vhdl_dict $xmlfile]} {
1714 set vhdl_file [
file normalize [dict get $vhdl_dict $xmlfile]]
1718 lappend vhdls $vhdl_file
1719 set xmlfile [
file normalize $xmlfile]
1720 Msg Info "Copying $xmlfile to $dst and replacing place holders..."
1721 set in [open $xmlfile r]
1723 if {[regexp \/xml\/+(.*)$ $xmlfile XXX out_with_dir]} {
1724 set out_file $dst/$out_with_dir
1725 lappend xmls $out_with_dir
1726 Msg Debug "xml file $xmlfile is contained in a directory called 'xml', so file will be copied to $out_file"
1727 set out_dir [
file dir $out_file]
1728 if {![
file exists $out_dir]} {
1732 set out_file $dst/[
file tail $xmlfile]
1733 lappend xmls [
file tail $xmlfile]
1736 set out [open $out_file w]
1737 while {[
gets $in line] != -1} {
1738 set new_line [regsub {(.*)__VERSION__(.*)} $line "\\1$xml_version\\2"]
1739 set new_line2 [regsub {(.*)__GIT_SHA__(.*)} $new_line "\\1$xml_sha\\2"]
1740 puts $out $new_line2
1745 Msg Warning "XML file $xmlfile not found"
1748 if {${xml_list_error}} {
1749 Msg Error "Invalid files added to $list_file!"
1752 set cnt [
llength $xmls]
1753 Msg Info "$cnt xml file/s copied"
1756 if {$can_generate == 1} {
1759 file mkdir "address_decode"
1761 foreach x $xmls v $vhdls {
1763 set x [
file normalize ../$x]
1764 if {[
file exists $x]} {
1765 lassign [
ExecuteRet gen_ipbus_addr_decode --no-timestamp $x 2>&1] status log
1767 set generated_vhdl ./ipbus_decode_[
file rootname [
file tail $x]].vhd
1768 if {$generate == 1} {
1769 Msg Info "Copying generated VHDL file $generated_vhdl into $v (replacing if necessary)"
1770 file copy -force -- $generated_vhdl $v
1772 if {[
file exists $v]} {
1774 set n [
llength $diff]
1776 Msg CriticalWarning "$v does not correspond to its XML $x, [
expr {$n / 3}] line/s differ:"
1777 Msg Status [
join $diff "\n"]
1778 set diff_file [open ../diff_[
file rootname [
file tail $x]].txt w]
1779 puts $diff_file $diff
1782 Msg Info "[
file tail $x] and $v match."
1785 Msg Warning "VHDL address map file $v not found."
1789 Msg Warning "Address map generation failed for [
file tail $x]: $log"
1792 Msg Warning "Copied XML file $x not found."
1795 Msg Info "Skipped verification of [
file tail $x] as no VHDL file was specified."
1799 file delete -force address_decode
1811 proc DescriptionFromConf {conf_file} {
1812 set f [open $conf_file "r"]
1813 set lines [
split [read $f] "\n"]
1815 set second_line [
lindex $lines 1]
1818 if {![regexp {\#+ *(.+)} $second_line - description]} {
1822 if {[regexp -all {test|Test|TEST} $description]} {
1823 set description "test"
1836 proc DictGet {dictName keyName {default ""}} {
1837 if {[dict exists $dictName $keyName]} {
1838 return [dict get $dictName $keyName]
1849 proc DictSort {dict args} {
1851 foreach key [lsort {*}$args [dict keys $dict]] {
1852 dict set res $key [dict get $dict $key]
1862 proc DoxygenVersion {target_version} {
1863 set ver [
split $target_version "."]
1864 set v [
Execute doxygen --version]
1865 Msg Info "Found Doxygen version: $v"
1866 set current_ver [
split $v ". "]
1867 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
1868 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
1870 return [
expr {$target <= $current}]
1881 proc eos {command {attempt 1}} {
1883 if {![
info exists env(EOS_MGM_URL)]} {
1884 Msg Warning "Environment variable EOS_MGM_URL not set, setting it to default value root://eosuser.cern.ch"
1885 set ::env(EOS_MGM_URL) "root://eosuser.cern.ch"
1888 Msg Warning "The value of attempt should be 1 or more, not $attempt, setting it to 1 as default"
1891 for {
set i 0} {$i < $attempt} {
incr i} {
1892 set ret [
catch {
exec -ignorestderr eos {*}$command} result]
1897 set wait [
expr {1 + int(rand() * 29)}]
1898 Msg Warning "Command $command failed ($i/$attempt): $result, trying again in $wait seconds..."
1899 after [
expr {$wait * 1000}]
1903 return [list $ret $result]
1913 proc Execute {args} {
1917 Msg Error "Command [
join $args] returned error code: $ret"
1931 proc ExecuteRet {args} {
1933 if {[
llength $args] == 0} {
1934 Msg CriticalWarning "No argument given"
1938 set ret [
catch {
exec -ignorestderr {*}$args} result]
1941 return [list $ret $result]
1948 proc ExtractFilesSection {file_data} {
1949 set in_files_section 0
1952 foreach line $file_data {
1953 if {[regexp {^ *\[ *files *\]} $line]} {
1954 set in_files_section 1
1957 if {$in_files_section} {
1958 if {[regexp {^ *\[.*\]} $line]} {
1961 lappend result $line
1966 if {!$in_files_section} {
1980 proc ExtractVersionFromTag {tag} {
1981 if {[regexp {^(?:b(\d+))?v(\d+)\.(\d+).(\d+)(?:-\d+)?$} $tag -> mr M m p]} {
1986 Msg Warning "Repository tag $tag is not in a Hog-compatible format."
1992 return [list $M $m $p $mr]
2002 proc FileCommitted {File} {
2004 set currentDir [
pwd]
2005 cd [
file dirname [
file normalize $File]]
2006 set GitLog [
Git ls-files [
file tail $File]]
2007 if {$GitLog == ""} {
2008 Msg CriticalWarning "File [
file normalize $File] is not in the git repository. Please add it with:\n git add [
file normalize $File]\n"
2019 proc FindCommonGitChild {SHA1 SHA2} {
2021 set commits [
Git {log --oneline --merges}]
2024 foreach line [
split $commits "\n"] {
2025 set commit [
lindex [
split $line] 0]
2029 set ancestor $commit
2042 proc findFiles {basedir pattern} {
2045 set basedir [
string trimright [
file join [
file normalize $basedir] { }]]
2051 foreach fileName [glob -nocomplain -type {f r} -path $basedir $pattern] {
2052 lappend fileList $fileName
2056 foreach dirName [glob -nocomplain -type {d r} -path $basedir *] {
2059 set subDirList [
findFiles $dirName $pattern]
2060 if {[
llength $subDirList] > 0} {
2061 foreach subDirFile $subDirList {
2062 lappend fileList $subDirFile
2073 proc FindFileType {file_name} {
2074 set extension [
file extension $file_name]
2077 set file_extension "USE_SIGNALTAP_FILE"
2080 set file_extension "VHDL_FILE"
2083 set file_extension "VHDL_FILE"
2086 set file_extension "VERILOG_FILE"
2089 set file_extension "SYSTEMVERILOG_FILE"
2092 set file_extension "SDC_FILE"
2095 set file_extension "PDC_FILE"
2098 set file_extension "NDC_FILE"
2101 set file_extension "FDC_FILE"
2104 set file_extension "SOURCE_FILE"
2107 set file_extension "IP_FILE"
2110 set file_extension "QSYS_FILE"
2113 set file_extension "QIP_FILE"
2116 set file_extension "SIP_FILE"
2119 set file_extension "BSF_FILE"
2122 set file_extension "BDF_FILE"
2125 set file_extension "COMMAND_MACRO_FILE"
2128 set file_extension "VQM_FILE"
2131 set file_extension "ERROR"
2132 Msg Error "Unknown file extension $extension"
2135 return $file_extension
2142 proc FindNewestVersion {versions} {
2143 set new_ver 00000000
2144 foreach ver $versions {
2146 if {[
expr 0x$ver > 0x$new_ver]} {
2156 proc FindRepoRoot {start_dir} {
2157 if {[
file pathtype $start_dir] eq "relative"} {
2158 set dir [
file normalize [
file join [
pwd] $start_dir]]
2163 if {[
file exists [
file join $dir Top]] && [
file exists [
file join $dir Projects]]} {
2166 set parent [
file dirname $dir]
2167 if {$parent eq $dir} {
2179 proc FindVhdlVersion {file_name} {
2180 set extension [
file extension $file_name]
2183 set vhdl_version "-hdl_version VHDL_2008"
2186 set vhdl_version "-hdl_version VHDL_2008"
2193 return $vhdl_version
2200 proc FormatGeneric {generic} {
2201 if {[
string is integer "0x$generic"]} {
2202 return [
format "32'h%08X" "0x$generic"]
2205 return [
format "32'h%08X" 0]
2215 proc GenerateBitstream {{run_folder ""} {repo_path .} {njobs 1}} {
2216 Msg Info "Starting write bitstream flow..."
2218 set revision [get_current_revision]
2219 if {[
catch {execute_module -tool asm} result]} {
2220 Msg Error "Result: $result\n"
2221 Msg Error "Generate bitstream failed. See the report file.\n"
2223 Msg Info "Generate bitstream was successful for revision $revision.\n"
2226 Msg Info "Run GENERATEPROGRAMMINGDATA ..."
2227 if {[
catch {run_tool -name {GENERATEPROGRAMMINGDATA}}]} {
2228 Msg Error "GENERATEPROGRAMMINGDATA FAILED!"
2230 Msg Info "GENERATEPROGRAMMINGDATA PASSED."
2232 Msg Info "Sourcing Hog/Tcl/integrated/post-bitstream.tcl"
2233 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
2235 prj_run Export -impl Implementation0 -task Bitgen
2245 proc GenerateQsysSystem {qsysFile commandOpts} {
2247 if {[
file exists $qsysFile] != 0} {
2248 set qsysPath [
file dirname $qsysFile]
2249 set qsysName [
file rootname [
file tail $qsysFile]]
2250 set qsysIPDir "$qsysPath/$qsysName"
2251 set qsysLogFile "$qsysPath/$qsysName.qsys-generate.log"
2254 if {![
info exists ::env(QSYS_ROOTDIR)]} {
2255 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
2256 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
2257 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
2259 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
2262 set qsys_rootdir $::env(QSYS_ROOTDIR)
2265 set cmd "$qsys_rootdir/qsys-generate"
2266 set cmd_options "$qsysFile --output-directory=$qsysIPDir $commandOpts"
2267 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
2268 Msg Info "Executing: $cmd $cmd_options"
2269 Msg Info "Saving logfile in: $qsysLogFile"
2270 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
2271 set makeRet [
lindex [dict get $opt -errorcode] end]
2272 Msg CriticalWarning "$cmd returned with $makeRet"
2275 Msg Error " Could not execute command $cmd"
2279 set qsysIPFileList [
concat \
2280 [glob -nocomplain -directory $qsysIPDir -types f *.ip *.qip] \
2281 [glob -nocomplain -directory "$qsysIPDir/synthesis" -types f *.ip *.qip *.vhd *.vhdl]]
2282 foreach qsysIPFile $qsysIPFileList {
2283 if {[
file exists $qsysIPFile] != 0} {
2285 set_global_assignment -name $qsysIPFileType $qsysIPFile
2287 set IpMd5Sum [
Md5Sum $qsysIPFile]
2289 set fileDir [
file normalize "./hogTmp"]
2290 set fileName "$fileDir/.hogQsys.md5"
2291 if {![
file exists $fileDir]} {
2294 set hogQsysFile [open $fileName "a"]
2295 set fileEntry "$qsysIPFile\t$IpMd5Sum"
2296 puts $hogQsysFile $fileEntry
2301 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
2311 proc GenericToSimulatorString {prop_dict target} {
2313 dict for {theKey theValue} $prop_dict {
2314 set theValue [string trim $theValue]
2323 regexp {([0-9]*)('h)([0-9a-fA-F]*)} $theValue valueHexFull valueNumBits valueHexFlag valueHex
2324 regexp {^([0-9]*)$} $theValue valueIntFull ValueInt
2325 regexp {(?!^\d+$)^.+$} $theValue valueStrFull ValueStr
2326 if {[string tolower $target] == "vivado" || [string tolower $target] == "xsim"} {
2327 if {[string tolower $theValue] == "true" || [string tolower $theValue] == "false"} {
2328 set prj_generics "$prj_generics $theKey=[string tolower $theValue]"
2329 } elseif {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2330 set prj_generics "$prj_generics $theKey=$valueHexFull"
2331 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2332 set prj_generics "$prj_generics $theKey=$ValueInt"
2333 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2334 set prj_generics "$prj_generics $theKey=\"$ValueStr\""
2336 set prj_generics "$prj_generics $theKey=\"$theValue\""
2338 } elseif {[lsearch -exact [GetSimulators] [string tolower $target]] >= 0} {
2339 if {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2341 scan $valueNumBits %d numBits
2343 scan $valueHex %x numHex
2344 binary scan [binary format "I" $numHex] "B*" binval
2345 set numBits [expr {$numBits - 1}]
2346 set numBin [string range $binval end-$numBits end]
2347 set prj_generics "$prj_generics $theKey=\"$numBin\""
2348 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2349 set prj_generics "$prj_generics $theKey=$ValueInt"
2350 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2351 set prj_generics "$prj_generics {$theKey=\"$ValueStr\"}"
2353 set prj_generics "$prj_generics {$theKey=\"$theValue\"}"
2356 Msg Warning "Target : $target not implemented"
2359 return $prj_generics
2367 proc GetConfFiles {proj_dir} {
2368 Msg Debug "GetConfFiles called with proj_dir=$proj_dir"
2369 if {![
file isdirectory $proj_dir]} {
2370 Msg Error "$proj_dir is supposed to be the top project directory"
2373 set conf_file [
file normalize $proj_dir/hog.conf]
2374 set sim_file [
file normalize $proj_dir/sim.conf]
2375 set pre_tcl [
file normalize $proj_dir/pre-creation.tcl]
2376 set post_tcl [
file normalize $proj_dir/post-creation.tcl]
2377 set pre_rtl [
file normalize $proj_dir/pre-rtl.tcl]
2378 set post_rtl [
file normalize $proj_dir/post-rtl.tcl]
2380 return [list $conf_file $sim_file $pre_tcl $post_tcl $pre_rtl $post_rtl]
2389 proc GetCustomCommands {parameters {directory .}} {
2390 set commands_dict [dict create]
2391 set commands_files [glob -nocomplain $directory/*.tcl]
2393 if {[
llength $commands_files] == 0} {
2397 foreach file $commands_files {
2401 if {$custom_cmd eq ""} {
2406 set custom_cmd_name [dict get $custom_cmd NAME]
2408 Msg Debug "Loaded custom command '$custom_cmd_name' from $file"
2411 if {[dict exists $commands_dict $custom_cmd_name]} {
2412 Msg Error "Custom command '$custom_cmd_name' in $file already defined as: \[dict get $commands_dict $custom_cmd_name\]. Skipping."
2417 set custom_cmd_name [
string toupper $custom_cmd_name]
2418 dict set commands_dict $custom_cmd_name $custom_cmd
2421 return $commands_dict
2424 proc SanitizeCustomCommand {cmdDict file parameters} {
2429 foreach k [dict keys $cmdDict] {
2430 set K [
string toupper $k]
2431 dict set normalized $K [dict get $cmdDict $k]
2434 set cmdDict $normalized
2435 if {![dict exists $cmdDict NAME]} {
2436 Msg Error "Custom command in $file missing required key NAME. Skipping."
2439 if {![dict exists $cmdDict SCRIPT]} {
2440 Msg Error "Custom command '$[dict get $cmdDict NAME]' in $file missing SCRIPT. Skipping."
2445 set allowed {NAME DESCRIPTION OPTIONS CUSTOM_OPTIONS SCRIPT IDE NO_EXIT}
2446 foreach k [dict keys $cmdDict] {
2447 if {[lsearch -exact $allowed $k] < 0} {
2448 Msg Warning "Custom command '[dict get $cmdDict NAME]' in $file: unknown key '$k'. Allowed: $allowed. Skipping."
2453 set name [
string trim [dict get $cmdDict NAME]]
2455 Msg Error "Custom command in $file has empty NAME. Skipping."
2459 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]+$} $name]} {
2460 Msg Error "Custom command NAME '$name' (file $file) contains invalid characters."
2464 if {![dict exists $cmdDict DESCRIPTION]} {
2465 dict set cmdDict DESCRIPTION "No description provided."
2469 set hog_parameters {}
2470 foreach p $parameters {
2471 lappend hog_parameters [
lindex $p 0]
2476 if {[dict exists $cmdDict OPTIONS]} {
2477 set raw_opts [dict get $cmdDict OPTIONS]
2478 if {![
llength $raw_opts]} {
2482 foreach item $raw_opts {
2484 foreach p $parameters {
2485 set hog_parameter [
lindex $p 0]
2486 if {$item eq $hog_parameter} {
2487 lappend hog_options $p
2493 Msg Warning "Custom command '$name' in $file: option '$item' not found in Hog parameters. Skipping."
2496 dict set cmdDict OPTIONS $hog_options
2498 dict set cmdDict OPTIONS {}
2504 if {[dict exists $cmdDict CUSTOM_OPTIONS]} {
2505 set raw_opts [dict get $cmdDict CUSTOM_OPTIONS]
2506 if {![
llength $raw_opts]} {
2509 foreach item $raw_opts {
2510 if {[
llength $item] != 2 && [
llength $item] != 3} {
2511 Msg Error "Bad custom option: \[$item\]. Custom command '$name' in $file: \
2512 each CUSTOM_OPTIONS entry must be {option \"help\"} for flags \
2513 and {option \"default_value\" \"help\"} for options with arguments. Skipping command."
2517 if {[
llength $item] == 2} {
2518 lassign $item opt help
2521 lassign $item opt def help
2524 if {[
IsInList $opt $hog_parameters] == 1} {
2525 Msg Warning "Custom command '$name' in $file: option '$opt' already defined in Hog parameters. Skipping."
2531 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]*(\.arg)?$} $opt]} {
2532 Msg Error "Custom command '$name' in $file: invalid option name '$opt'."
2537 Msg Warning "Custom command '$name' option '$opt' has empty help text."
2541 dict set cmdDict CUSTOM_OPTIONS {}
2545 if {[dict exists $cmdDict NO_EXIT]} {
2546 set no_exit [dict get $cmdDict NO_EXIT]
2547 set no_exit [
string tolower [
string trim $no_exit]]
2549 if {$no_exit eq "1" || $no_exit eq "true"} {
2555 dict set cmdDict NO_EXIT $no_exit
2557 dict set cmdDict NO_EXIT 0
2563 proc LoadCustomCommandFile {file parameters} {
2565 set dir [
file dirname $file]
2567 unset -nocomplain ::hog_command
2568 set rc [
catch {source $file} err]
2571 Msg Error "Error sourcing custom command file $file: $err"
2574 if {![
info exists ::hog_command]} {
2575 Msg Warning "File $file did not define ::hog_command. Skipping."
2578 set cmdDict $::hog_command
2580 if {[
catch {dict size $cmdDict}]} {
2581 Msg Error "In $file ::hog_command is not a valid dict. Skipping."
2591 proc GetDateAndTime {commit} {
2592 set clock_seconds [
clock seconds]
2595 set date [
Git "log -1 --format=%cd --date=format:%d%m%Y $commit"]
2596 set timee [
Git "log -1 --format=%cd --date=format:00%H%M%S $commit"]
2598 Msg Warning "Found Git version older than 2.9.3. Using current date and time instead of commit time."
2599 set date [
clock format $clock_seconds -format {%d%m%Y}]
2600 set timee [
clock format $clock_seconds -format {00%H%M%S}]
2602 return [list $date $timee]
2614 proc GetFile {file fileset} {
2617 set Files [get_files -all $file -of_object [get_filesets $fileset]]
2618 set f [
lindex $Files 0]
2626 puts "***DEBUG Hog:GetFile $file"
2635 proc GetFileGenerics {filename {entity ""}} {
2637 if {[
string equal $file_type "VERILOG_FILE"] || [
string equal $file_type "SYSTEMVERILOG_FILE"]} {
2639 }
elseif {[
string equal $file_type "VHDL_FILE"]} {
2642 Msg CriticalWarning "Could not determine extension of top level file."
2651 proc GetGenericsFromConf {proj_dir} {
2652 set generics_dict [dict create]
2653 set top_dir "Top/$proj_dir"
2654 set conf_file "$top_dir/hog.conf"
2656 Msg Debug "GetGenericsFromConf called with proj_dir=$proj_dir, top_dir=$top_dir"
2658 if {[
file exists $conf_file]} {
2660 if {[dict exists $properties generics]} {
2661 set generics_dict [dict get $properties generics]
2664 Msg Warning "File $conf_file not found."
2666 return $generics_dict
2679 proc GetSimSets {project_name repo_path {simsets ""} {ghdl 0} {no_conf 0}} {
2680 set simsets_dict [dict create]
2681 set list_dir "$repo_path/Top/$project_name/list"
2683 if {$simsets != ""} {
2684 foreach s $simsets {
2685 set list_file "$list_dir/$s.sim"
2686 if {[
file exists $list_file]} {
2687 lappend list_files $list_file
2688 }
elseif {$s != "sim_1"} {
2689 Msg CriticalWarning "Simulation set list file $list_file not found."
2694 set list_files [glob -nocomplain -directory $list_dir "*.sim"]
2698 set proj_dir [
file normalize $repo_path/Top/$project_name]
2699 set sim_file [
file normalize $proj_dir/sim.conf]
2701 foreach list_file $list_files {
2702 set file_name [
file tail $list_file]
2703 set simset_name [
file rootname $file_name]
2704 set fp [open $list_file r]
2705 set file_data [read $fp]
2707 set data [
split $file_data "\n"]
2709 set firstline [
lindex $data 0]
2711 if {[regexp {^ *\# *Simulator} $firstline]} {
2712 set simulator_prop [regexp -all -inline {\S+} $firstline]
2713 set simulator [
string tolower [
lindex $simulator_prop end]]
2715 Msg Warning "Simulator not set in $simset_name.sim. \
2716 The first line of $simset_name.sim should be #Simulator <SIMULATOR_NAME>,\
2717 where <SIMULATOR_NAME> can be xsim, questa, modelsim, ghdl, riviera, activehdl,\
2718 ies, or vcs, e.g. #Simulator questa.\
2719 Setting simulator by default to xsim."
2720 set simulator "xsim"
2722 if {$simulator eq "skip_simulation"} {
2723 Msg Info "Skipping simulation for $simset_name"
2726 if {($ghdl == 1 && $simulator != "ghdl") || ($ghdl == 0 && $simulator == "ghdl")} {
2730 set SIM_PROPERTIES ""
2731 if {[
file exists $sim_file] && $no_conf == 0} {
2732 set SIM_PROPERTIES [
ReadConf $sim_file]
2735 set global_sim_props [dict create]
2736 dict set global_sim_props "properties" [
DictGet $SIM_PROPERTIES "sim"]
2737 dict set global_sim_props "generics" [
DictGet $SIM_PROPERTIES "generics"]
2738 dict set global_sim_props "hog" [
DictGet $SIM_PROPERTIES "hog"]
2741 set sim_dict [dict create]
2742 dict set sim_dict "simulator" $simulator
2743 if {[dict exists $SIM_PROPERTIES $simset_name]} {
2744 dict set sim_dict "properties" [
DictGet $SIM_PROPERTIES $simset_name]
2745 dict set sim_dict "generics" [
DictGet $SIM_PROPERTIES "$simset_name:generics"]
2746 dict set sim_dict "hog" [
DictGet $SIM_PROPERTIES "$simset_name:hog"]
2747 }
elseif {$no_conf == 0} {
2749 set conf_dict [
ReadConf $list_file]
2750 set sim_dict [
MergeDict $sim_dict $conf_dict 0]
2752 set sim_dict [
MergeDict $sim_dict $global_sim_props 0]
2753 dict set simsets_dict $simset_name $sim_dict
2755 return $simsets_dict
2763 proc GetSimsetGenericsFromConf {proj_dir} {
2764 set simsets_generics_dict [dict create]
2765 set top_dir "Top/$proj_dir"
2766 set conf_file "$top_dir/sim.conf"
2769 if {[
file exists $conf_file]} {
2772 set simsets_generics_dict [dict filter $properties key *:generics]
2774 Msg Warning "File $conf_file not found."
2776 return $simsets_generics_dict
2787 proc GetGroupName {proj_dir repo_dir} {
2788 if {[regexp {^(.*)/(Top|Projects)/+(.*?)/*$} $proj_dir dummy possible_repo_dir proj_or_top dir]} {
2790 if {[
file normalize $repo_dir] eq [
file normalize $possible_repo_dir]} {
2791 set group [
file dir $dir]
2792 if {$group == "."} {
2797 Msg Warning "Project directory $proj_dir seems to be in $possible_repo_dir which is not a the main Git repository $repo_dir."
2800 Msg Warning "Could not parse project directory $proj_dir"
2813 proc GetHogDescribe {proj_dir {repo_path .}} {
2814 lassign [
GetRepoVersions $proj_dir $repo_path] global_commit global_version
2815 if {$global_commit == 0} {
2817 set new_sha "[
string toupper [
GetSHA]]"
2820 set new_sha [
string toupper $global_commit]
2840 proc GetHogFiles {args} {
2843 if {[
catch {
package require cmdline} ERROR]} {
2844 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
2851 {list_files.arg "" "The file wildcard, if not specified all Hog list files will be looked for."}
2852 {sha_mode "Forwarded to ReadListFile, see there for info."}
2853 {ext_path.arg "" "Path for the external libraries forwarded to ReadListFile."}
2854 {print_log "Forwarded to ReadListFile, see there for info."}
2856 set usage "USAGE: GetHogFiles \[options\] <list path> <repository path>"
2857 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
2861 set list_path [
lindex $args 0]
2862 set repo_path [
lindex $args 1]
2864 set list_files $options(list_files)
2865 set sha_mode $options(sha_mode)
2866 set ext_path $options(ext_path)
2867 set print_log $options(print_log)
2869 if {$sha_mode == 1} {
2870 set sha_mode_opt "-sha_mode"
2875 if {$print_log == 1} {
2876 set print_log_opt "-print_log"
2878 set print_log_opt ""
2882 if {$list_files == ""} {
2883 set list_files {.src,.con,.sim,.ext}
2885 set libraries [dict create]
2886 set properties [dict create]
2887 set list_files [glob -nocomplain -directory $list_path "*{$list_files}"]
2888 set filesets [dict create]
2890 foreach f $list_files {
2891 set ext [
file extension $f]
2892 if {$ext == ".ext"} {
2893 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $ext_path"] l p fs
2895 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $repo_path"] l p fs
2898 set properties [
MergeDict $p $properties]
2899 Msg Debug "list file $f, filesets: $fs"
2901 Msg Debug "Merged filesets $filesets"
2909 set proj_conf [
file normalize [
file join [
file dirname $list_path] hog.conf]]
2910 if {[
file exists $proj_conf]} {
2912 set already_tracked [dict create]
2913 dict for {libname libfiles} $libraries {
2914 foreach lf $libfiles {dict set already_tracked [file normalize $lf] 1}
2916 dict for {comp_name cfg_abs} $hls_configs {
2917 if {[dict exists $already_tracked $cfg_abs]} {
2918 Msg Debug "HLS component '$comp_name': cfg $cfg_abs already tracked via a .src, skipping conf-based GetHogFiles discovery."
2921 set lib_name "${comp_name}.src"
2922 dict lappend libraries $lib_name $cfg_abs
2923 dict set already_tracked $cfg_abs 1
2924 set hls_extras [ExpandHlsConfigFiles $cfg_abs]
2925 Msg Info "HLS component '$comp_name' (from hog.conf): tracking [expr {1 + [llength $hls_extras]}] file(s) via $cfg_abs"
2926 if {$print_log == 1} {
2927 Msg Status "\nhog.conf \[hls:$comp_name\] (auto-discovered)"
2928 set rel_cfg [Relative $repo_path $cfg_abs 1]
2929 if {$rel_cfg eq ""} {set rel_cfg $cfg_abs}
2930 if {[llength $hls_extras] == 0} {
2931 Msg Status "└── $rel_cfg"
2933 Msg Status "├── $rel_cfg"
2934 Msg Status " Inside [file tail $cfg_abs] (HLS auto-expand):"
2937 set n_extras [llength $hls_extras]
2939 foreach hls_extra $hls_extras {
2941 if {[dict exists $already_tracked $hls_extra]} {continue}
2942 dict lappend libraries $lib_name $hls_extra
2943 dict set already_tracked $hls_extra 1
2944 if {$print_log == 1} {
2945 if {$i == $n_extras} {set pad "└──"} else {set pad "├──"}
2946 set rel_extra [Relative [file dirname $cfg_abs] $hls_extra 1]
2947 if {$rel_extra eq ""} {set rel_extra $hls_extra}
2948 Msg Status " $pad $rel_extra"
2954 return [list $libraries $properties $filesets]
2961 proc GetIDECommand {proj_conf {custom_ver ""}} {
2962 if {$custom_ver ne ""} {
2963 set ide_name_and_ver [
string tolower "$custom_ver"]
2964 }
elseif {[
file exists $proj_conf]} {
2965 set ide_name_and_ver [
string tolower [
GetIDEFromConf $proj_conf]]
2967 Msg Error "Configuration file $proj_conf not found."
2970 set ide_name [
lindex [regexp -all -inline {\S+} $ide_name_and_ver] 0]
2972 if {$ide_name eq "vivado" || $ide_name eq "vivado_vitis_classic" || $ide_name eq "vivado_vitis_unified" || $ide_name eq "vitis_unified"} {
2973 set command "vivado"
2975 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2976 set after_tcl_script " -tclargs "
2978 }
elseif {$ide_name eq "planahead"} {
2979 set command "planAhead"
2981 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2982 set after_tcl_script " -tclargs "
2984 }
elseif {$ide_name eq "quartus"} {
2985 set command "quartus_sh"
2987 set before_tcl_script " -t "
2988 set after_tcl_script " "
2990 }
elseif {$ide_name eq "libero"} {
2993 set command "libero"
2994 set before_tcl_script "SCRIPT:"
2995 set after_tcl_script " SCRIPT_ARGS:\""
2997 }
elseif {$ide_name eq "diamond"} {
2998 set command "diamondc"
2999 set before_tcl_script " "
3000 set after_tcl_script " "
3002 }
elseif {$ide_name eq "vitis_classic"} {
3005 set before_tcl_script ""
3006 set after_tcl_script " "
3008 }
elseif {$ide_name eq "ghdl"} {
3010 set before_tcl_script " "
3011 set after_tcl_script " "
3014 Msg Error "IDE: $ide_name not known."
3017 return [list $command $before_tcl_script $after_tcl_script $end_marker]
3023 proc GetIDEFromConf {conf_file} {
3024 set f [open $conf_file "r"]
3027 if {[regexp -all {^\# *(\w*) *(vitis_(?:classic|unified))? *(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)?(_.*)? *$} $line dummy ide vitisflag version patch]} {
3028 if {[
info exists vitisflag] && $vitisflag != ""} {
3029 set ide "${ide}_${vitisflag}"
3032 if {[
info exists version] && $version != ""} {
3038 set ret [list $ide $ver]
3040 Msg CriticalWarning "The first line of hog.conf should be \#<IDE name> <version>, \
3041 where <IDE name>. is quartus, vivado, planahead, libero, diamond or ghdl, \
3042 and <version> the tool version, e.g. \#vivado 2020.2. Will assume vivado."
3043 set ret [list "vivado" "0.0.0"]
3050 proc GetIDEName {} {
3052 return "ISE/PlanAhead"
3070 proc GetIDEVersion {} {
3073 regexp {\d+\.\d+(\.\d+)?} [version -short] ver
3078 regexp {[\.0-9]+} $quartus(version) ver
3081 set ver [get_libero_version]
3084 regexp {\d+\.\d+(\.\d+)?} [sys_install version] ver
3087 regexp {\d+\.\d+(\.\d+)?} [version] ver
3090 set vitis_output [
exec vitis --version 2>@1]
3091 regexp {[Vv]itis\s+v?(\d+\.\d+(?:\.\d+)?)} $vitis_output -> ver
3105 proc GetLinkedFile {link_file} {
3106 if {[
file type $link_file] eq "link"} {
3107 if {[
OS] == "windows"} {
3109 lassign [
ExecuteRet realpath $link_file] ret msg
3110 lassign [
ExecuteRet cygpath -m $msg] ret2 msg2
3111 if {$ret == 0 && $ret2 == 0} {
3113 Msg Debug "Found link file $link_file on Windows, the linked file is: $real_file"
3115 Msg CriticalWarning "[
file normalize $link_file] is a soft link. \
3116 Soft link are not supported on Windows and readlink.exe or cygpath.exe did not work: readlink=$ret: $msg, cygpath=$ret2: $msg2."
3117 set real_file $link_file
3121 set linked_file [
file link $link_file]
3122 set real_file [
file normalize [
file dirname $link_file]/$linked_file]
3125 if {![
file exists $real_file]} {
3126 Msg Warning "$link_file is a broken link, because the linked file: $real_file does not exist."
3129 Msg Warning "$link file is not a soft link"
3130 set real_file $link_file
3143 proc GetMaxThreads {proj_dir} {
3145 if {[
file exists $proj_dir/hog.conf]} {
3147 if {[dict exists $properties parameters]} {
3148 set propDict [dict get $properties parameters]
3149 if {[dict exists $propDict MAX_THREADS]} {
3150 set maxThreads [dict get $propDict MAX_THREADS]
3154 Msg Warning "File $proj_dir/hog.conf not found. Max threads will be set to default value 1"
3167 proc GetModifiedFiles {{repo_path "."} {pattern "."}} {
3170 set ret [
Git "ls-files --modified $pattern"]
3179 proc GetOptions {argv parameters} {
3182 set param_list [list]
3183 set option_list [list]
3185 foreach p $parameters {
3186 lappend param_list [
lindex $p 0]
3190 while {$index < [
llength $argv]} {
3191 set arg [
lindex $argv $index]
3192 if {[
string first - $arg] == 0} {
3193 set option [
string trimleft $arg "-"]
3195 lappend option_list $arg
3196 if {[lsearch -regex $param_list "$option\[.arg]?"] >= 0} {
3197 if {[lsearch -regex $param_list "$option\[.arg]"] >= 0} {
3198 lappend option_list [
lindex $argv $index]
3203 lappend arg_list $arg
3207 Msg Debug "Argv: $argv"
3208 Msg Debug "Options: $option_list"
3209 Msg Debug "Arguments: $arg_list"
3210 return [list $option_list $arg_list]
3228 proc GetProjectFiles {{project_file ""}} {
3229 set libraries [dict create]
3230 set simlibraries [dict create]
3231 set constraints [dict create]
3232 set properties [dict create]
3233 set consets [dict create]
3234 set srcsets [dict create]
3235 set simsets [dict create]
3238 set all_filesets [get_filesets]
3239 set simulator [get_property target_simulator [current_project]]
3240 set top [get_property "top" [current_fileset]]
3242 dict lappend properties $topfile "top=$top"
3244 foreach fs $all_filesets {
3245 if {$fs == "utils_1"} {
3250 set all_files [get_files -quiet -of_objects [get_filesets $fs]]
3251 set fs_type [get_property FILESET_TYPE [get_filesets $fs]]
3253 if {$fs_type == "BlockSrcs"} {
3255 set dict_fs "sources_1"
3259 foreach f $all_files {
3266 if {[
lindex [get_property IS_GENERATED [
GetFile $f $fs]] 0] != 0} {
3271 if {[get_property FILE_TYPE [
GetFile $f $fs]] == "Configuration Files"} {
3277 if {[get_property CORE_CONTAINER [
GetFile $f $fs]] != ""} {
3278 if {[
file extension $f] == ".xcix"} {
3279 set f [get_property CORE_CONTAINER [
GetFile $f $fs]]
3287 if {[get_property SCOPED_TO_REF [
GetFile $f $fs]] != ""} {
3288 dict lappend properties $f "scoped_to_ref=[get_property SCOPED_TO_REF [
GetFile $f $fs]]"
3293 if {[get_property SCOPED_TO_CELLS [
GetFile $f $fs]] != ""} {
3294 dict lappend properties $f "scoped_to_cells=[regsub " " [get_property SCOPED_TO_CELLS [
GetFile $f $fs]] ","]"
3298 if {[
IsInList "PARENT_COMPOSITE_FILE" [list_property [
GetFile $f $fs]]]} {
3303 if {[
file tail $f] == "nocattrs.dat"} {
3308 if {[
file extension $f] != ".coe"} {
3309 set f [
file normalize $f]
3312 set type [get_property FILE_TYPE [
GetFile $f $fs]]
3314 set lib [get_property -quiet LIBRARY [
GetFile $f $fs]]
3317 Msg Debug "File $f Extension [
file extension $f] Type [
lindex $type 0]"
3319 if {[
string equal [
lindex $type 0] "VHDL"] && [
llength $type] == 1} {
3321 }
elseif {[
string equal [
lindex $type 0] "Block"] && [
string equal [
lindex $type 1] "Designs"]} {
3324 }
elseif {[
string equal $type "SystemVerilog"] && [
file extension $f] != ".sv" && [
file extension $f] != ".svp"} {
3325 set prop "SystemVerilog"
3326 }
elseif {[
string equal [
lindex $type 0] "XDC"] && [
file extension $f] != ".xdc"} {
3328 }
elseif {[
string equal $type "Verilog Header"] && [
file extension $f] != ".vh" && [
file extension $f] != ".svh"} {
3329 set prop "verilog_header"
3330 }
elseif {[
string equal $type "Verilog Template"] && [
file extension $f] == ".v" && [
file extension $f] != ".sv"} {
3331 set prop "verilog_template"
3333 set type [
lindex $type 0]
3337 if {![
string equal $prop ""]} {
3338 dict lappend properties $f $prop
3341 if {[
string equal $fs_type "SimulationSrcs"]} {
3343 if {[
string equal $type "VHDL"]} {
3344 set library "${lib}.sim"
3346 set library "others.sim"
3350 dict lappend simsets $dict_fs $library
3353 dict lappend simlibraries $library $f
3354 }
elseif {[
string equal $type "VHDL"]} {
3357 dict lappend srcsets $dict_fs "${lib}.src"
3359 dict lappend libraries "${lib}.src" $f
3360 }
elseif {[
string first "IP" $type] != -1} {
3363 dict lappend srcsets $dict_fs "ips.src"
3365 dict lappend libraries "ips.src" $f
3366 Msg Debug "Appending $f to ips.src"
3367 }
elseif {[
string equal $fs_type "Constrs"]} {
3370 dict lappend consets $dict_fs "sources.con"
3372 dict lappend constraints "sources.con" $f
3376 dict lappend srcsets $dict_fs "others.src"
3378 dict lappend libraries "others.src" $f
3379 Msg Debug "Appending $f to others.src"
3382 if {[
lindex [get_property -quiet used_in_synthesis [
GetFile $f $fs]] 0] == 0} {
3383 dict lappend properties $f "nosynth"
3385 if {[
lindex [get_property -quiet used_in_implementation [
GetFile $f $fs]] 0] == 0} {
3386 dict lappend properties $f "noimpl"
3388 if {[
lindex [get_property -quiet used_in_simulation [
GetFile $f $fs]] 0] == 0} {
3389 dict lappend properties $f "nosim"
3391 if {[
lindex [get_property -quiet IS_MANAGED [
GetFile $f $fs]] 0] == 0 && [
file extension $f] != ".xcix"} {
3392 dict lappend properties $f "locked"
3398 dict lappend properties "Simulator" [get_property target_simulator [current_project]]
3401 set file [open $project_file r]
3402 set in_file_manager 0
3404 while {[
gets $file line] >= 0} {
3406 if {[regexp {^KEY ActiveRoot \"([^\"]+)\"} $line -> value]} {
3407 set top [
string range $value 0 [
expr {[string first "::" $value] - 1}]]
3411 if {[regexp {^LIST FileManager} $line]} {
3412 set in_file_manager 1
3417 if {$in_file_manager && [regexp {^ENDLIST} $line]} {
3422 if {$in_file_manager && [regexp {^VALUE \"([^\"]+)} $line -> value]} {
3425 lassign [
split $value ,] file_path file_type
3428 set library "others"
3429 while {[
gets $file line] >= 0} {
3430 if {$line == "ENDFILE"} {
3433 regexp {^LIBRARY=\"([^\"]+)} $line -> library
3434 regexp {^PARENT=\"([^\"]+)} $line -> parent_file
3436 Msg Debug "Found file ${file_path} in project.."
3437 if {$parent_file == ""} {
3438 if {$file_type == "hdl"} {
3440 if {[
IsInList "${library}.src" [
DictGet $srcsets "sources_1"]] == 0} {
3441 dict lappend srcsets "sources_1" "${library}.src"
3443 dict lappend libraries "${library}.src" $file_path
3447 if {[
GetModuleName $file_path] == [
string tolower $top] && $top != ""} {
3448 Msg Debug "Found top module $top in $file_path"
3449 dict lappend properties $file_path "top=$top"
3451 }
elseif {$file_type == "tb_hdl"} {
3453 dict lappend simsets "sim_1" "${library}.sim"
3455 dict lappend simlibraries "${library}.sim" $file_path
3456 }
elseif {$file_type == "io_pdc" || $file_type == "sdc"} {
3458 dict lappend consets "constrs_1" "sources.con"
3460 dict lappend constraints "sources.con" $file_path
3467 set fileData [read [open $project_file]]
3469 set project_path [
file dirname $project_file]
3472 regsub {<\?xml.*\?>} $fileData "" fileData
3475 regexp {<Implementation.*?>(.*)</Implementation>} $fileData -> implementationContent
3479 set sourceRegex {<Source name="([^"]*?)" type="([^"]*?)" type_short="([^"]*?)".*?>(.*?)</Source>}
3481 set optionsRegex {<Options(.*?)\/>}
3482 regexp $optionsRegex $implementationContent -> prj_options
3483 foreach option $prj_options {
3484 if {[regexp {^top=\"([^\"]+)\"} $option match result]} {
3489 while {[regexp $sourceRegex $implementationContent match name type type_short optionsContent]} {
3490 Msg Debug "Found file ${name} in project..."
3491 set file_path [
file normalize $project_path/$name]
3493 set optionsRegex {<Options(.*?)\/>}
3494 regexp $optionsRegex $optionsContent -> options
3495 set library "others"
3497 foreach option $options {
3498 if {[
string first "System Verilog" $option]} {
3501 if {[regexp {^lib=\"([^\"]+)\"} $option match1 result]} {
3506 if {[regexp {syn_sim="([^"]*?)"} $match match_sim simonly]} {
3511 if {$type_short == "VHDL" || $type_short == "Verilog" || $type_short == "IPX"} {
3512 if {$ext == ".src"} {
3513 if {[
IsInList "${library}${ext}" [
DictGet $srcsets "sources_1"]] == 0} {
3514 dict lappend srcsets "sources_1" "${library}${ext}"
3516 dict lappend libraries "${library}${ext}" $file_path
3517 }
elseif {$ext == ".sim"} {
3519 dict lappend simsets "sim_1" "${library}.sim"
3521 dict lappend simlibraries "${library}.sim" $file_path
3527 Msg Debug "Found top module $top in $file_path"
3528 dict lappend properties $file_path "top=$top"
3530 }
elseif {$type_short == "SDC"} {
3532 dict lappend consets "constrs_1" "sources.con"
3534 dict lappend constraints "sources.con" $file_path
3538 regsub -- $match $implementationContent "" implementationContent
3541 return [list $libraries $properties $simlibraries $constraints $srcsets $simsets $consets]
3548 proc GetProjectFlavour {proj_name} {
3550 set flavour [
string map {. ""} [
file extension $proj_name]]
3551 if {$flavour != ""} {
3552 if {[
string is integer $flavour]} {
3553 Msg Info "Project $proj_name has flavour = $flavour, the generic variable FLAVOUR will be set to $flavour"
3555 Msg Warning "Project name has a unexpected non numeric extension, flavour will be set to -1"
3572 proc GetProjectVersion {proj_dir repo_path {ext_path ""} {sim 0}} {
3573 if {![
file exists $proj_dir]} {
3574 Msg CriticalWarning "$proj_dir not found"
3581 lassign [
GitRet {describe --tags --abbrev=0 --match "v*"}] ret result
3583 Msg CriticalWarning "No Hog versioning tags (v*) in repo"
3590 Msg Warning "Repository is not clean"
3598 Msg Debug "Project version $v_proj, latest tag $v_last"
3600 Msg Debug "The specified project was modified since official version."
3607 Msg Info "The specified project was modified in the latest official version $ret"
3608 }
elseif {$comp == -1} {
3609 Msg Info "The specified project was modified in a past official version $ret"
3625 proc GetRepoVersions {proj_dir repo_path {ext_path ""} {sim 0}} {
3626 if {[
catch {
package require cmdline} ERROR]} {
3627 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
3642 lappend SHAs [
GetSHA {Hog}]
3646 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
3647 Msg Debug "Hog submodule [
pwd] clean."
3648 lassign [
GetVer ./] hog_ver hog_hash
3650 Msg CriticalWarning "Hog submodule [
pwd] not clean, commit hash will be set to 0."
3651 set hog_hash "0000000"
3652 set hog_ver "00000000"
3658 foreach conf_file $conf_files {
3659 if {[
string match "$repo_path/*" $conf_file]} {
3660 set conf_file [
string replace $conf_file 0 [
string length $repo_path]]
3662 lappend project_files $conf_file
3665 lappend project_files Hog
3668 lassign [
GetVer [
join $conf_files]] top_ver top_hash
3669 lappend SHAs $top_hash
3670 lappend versions $top_ver
3677 lassign [
GetHogFiles -list_files "*.src" -sha_mode "$proj_dir/list/" $repo_path] src_files dummy
3678 dict for {f files} $src_files {
3679 # library names have a .src extension in values returned by GetHogFiles
3680 set name [file rootname [file tail $f]]
3681 if {[file ext $f] == ".oth"} {
3684 lassign [GetVer $files] ver hash
3685 # Msg Info "Found source list file $f, version: $ver commit SHA: $hash"
3687 lappend versions $ver
3689 lappend hashes $hash
3691 set relative_files ""
3692 foreach fil $files {
3693 if {[string match "$repo_path/*" $fil]} {
3694 set fil [string replace $fil 0 [string length $repo_path]]
3696 lappend relative_files $fil
3699 lappend project_files $f {*}$relative_files
3705 lassign [
GetHogFiles -list_files "*.con" -sha_mode "$proj_dir/list/" $repo_path] cons_files dummy
3706 dict for {f files} $cons_files {
3707 #library names have a .con extension in values returned by GetHogFiles
3708 set name [file rootname [file tail $f]]
3709 lassign [GetVer $files] ver hash
3710 #Msg Info "Found constraint list file $f, version: $ver commit SHA: $hash"
3712 Msg CriticalWarning "Constraints file $f not found in Git."
3714 lappend cons_hashes $hash
3716 lappend versions $ver
3717 set relative_files ""
3718 foreach fil $files {
3719 if {[string match "$repo_path/*" $fil]} {
3720 set fil [string replace $fil 0 [string length $repo_path]]
3722 lappend relative_files $fil
3724 lappend project_files $f {*}$relative_files
3731 lassign [
GetHogFiles -list_files "*.sim" -sha_mode "$proj_dir/list/" $repo_path] sim_files dummy
3732 dict for {f files} $sim_files {
3733 #library names have a .sim extension in values returned by GetHogFiles
3734 set name [file rootname [file tail $f]]
3735 lassign [GetVer $files] ver hash
3736 #Msg Info "Found simulation list file $f, version: $ver commit SHA: $hash"
3737 lappend sim_hashes $hash
3739 lappend versions $ver
3740 set relative_files ""
3741 foreach fil $files {
3742 if {[string match "$repo_path/*" $fil]} {
3743 set fil [string replace $fil 0 [string length $repo_path]]
3745 lappend relative_files $fil
3747 lappend project_files $f {*}$relative_files
3755 Msg CriticalWarning "No hashes found for constraints files (not in git)"
3758 set cons_hash [
string tolower [
Git "log --format=%h -1 $cons_hashes"]]
3765 set ext_files [glob -nocomplain "$proj_dir/list/*.ext"]
3768 foreach f $ext_files {
3769 set name [
file rootname [
file tail $f]]
3772 lappend ext_names $name
3773 lappend ext_hashes $hash
3776 lappend versions $ext_ver
3777 lappend project_files $f
3780 set file_data [read $fp]
3782 set data [
split $file_data "\n"]
3784 foreach line $data {
3785 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
3787 set file_and_prop [regexp -all -inline {\S+} $line]
3788 set hdlfile [
lindex $file_and_prop 0]
3789 set hdlfile $ext_path/$hdlfile
3790 if {[
file exists $hdlfile]} {
3791 set hash [
lindex $file_and_prop 1]
3792 set current_hash [
Md5Sum $hdlfile]
3793 if {[
string first $hash $current_hash] == -1} {
3794 Msg CriticalWarning "File $hdlfile has a wrong hash. Current checksum: $current_hash, expected: $hash"
3802 if {[
llength [glob -nocomplain $proj_dir/list/*.ipb]] > 0} {
3804 lassign [
GetHogFiles -list_files "*.ipb" -sha_mode "$proj_dir/list/" $repo_path] xml_files dummy
3805 set xml_source_files [dict get $xml_files "xml.ipb"]
3806 lassign [
GetVer $xml_source_files] xml_ver xml_hash
3807 lappend SHAs $xml_hash
3808 lappend versions $xml_ver
3809 set relative_files ""
3810 foreach fil $xml_source_files {
3811 if {[
string match "$repo_path/*" $fil]} {
3812 set fil [
string replace $fil 0 [
string length $repo_path]]
3814 lappend relative_files $fil
3816 lappend project_files {*}[glob $proj_dir/list/*.ipb] {*}$relative_files
3819 Msg Debug "This project does not use IPbus XMLs"
3824 set user_ip_repos ""
3825 set user_ip_repo_hashes ""
3826 set user_ip_repo_vers ""
3828 if {[
file exists [
lindex $conf_files 0]]} {
3829 set PROPERTIES [
ReadConf [
lindex $conf_files 0]]
3830 if {[dict exists $PROPERTIES main]} {
3831 set main [dict get $PROPERTIES main]
3832 dict for {p v} $main {
3833 if {[string tolower $p] == "ip_repo_paths"} {
3835 if {[file isdirectory "$repo_path/$repo"]} {
3836 set repo_file_list [glob -nocomplain "$repo_path/$repo/*"]
3837 if {[llength $repo_file_list] == 0} {
3838 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3840 lappend user_ip_repos "$repo_path/$repo"
3849 foreach repo $user_ip_repos {
3850 if {[
file isdirectory $repo]} {
3851 set repo_file_list [glob -nocomplain "$repo/*"]
3852 if {[
llength $repo_file_list] != 0} {
3853 lassign [
GetVer $repo] ver sha
3854 lappend user_ip_repo_hashes $sha
3855 lappend user_ip_repo_vers $ver
3856 lappend versions $ver
3857 lappend project_files $repo
3859 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3862 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory does not exist."
3877 append dirty [
Git "status --untracked-files=no --porcelain" $project_files]
3881 Msg Debug "Project-relevant files are clean."
3884 Msg CriticalWarning "Project-relevant files not clean, commit hash and version will be set to 0."
3891 while {$found == 0} {
3892 set global_commit [
Git "log --topo-order --format=%h -1 --abbrev=7 $SHAs"]
3897 if {$common_child == 0} {
3898 Msg CriticalWarning "The commit $sha is not an ancestor of the global commit $global_commit, which is OK. \
3899 But $sha and $global_commit do not have any common child, which is NOT OK. \
3900 This is probably do to a REBASE that is forbidden in Hog methodology as it changes git history. \
3901 Hog cannot guarantee the accuracy of the SHAs. \
3902 A way to fix this is to make a commit that touches all the projects in the repositories (e.g. change the Hog version), \
3903 but please do not rebase in the official branches in the future."
3905 Msg Info "The commit $sha is not an ancestor of the global commit $global_commit, adding the first common child $common_child instead..."
3906 lappend SHAs $common_child
3916 set global_commit "0000000"
3917 set global_version "00000000"
3922 set top_hash [
format %+07s $top_hash]
3923 set cons_hash [
format %+07s $cons_hash]
3924 return [list $global_commit $global_version \
3925 $hog_hash $hog_ver $top_hash $top_ver \
3926 $libs $hashes $vers $cons_ver $cons_hash \
3927 $ext_names $ext_hashes $xml_hash $xml_ver \
3928 $user_ip_repos $user_ip_repo_hashes $user_ip_repo_vers]
3937 proc GetSHA {{path ""}} {
3940 lassign [
GitRet {log --format=%h --abbrev=7 -1}] status result
3942 return [
string tolower $result]
3944 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3950 set repo_path [
lindex [
Git {rev-parse --show-toplevel}] 0]
3955 set file_in_module 0
3956 if {[
file exists .gitmodules]} {
3957 lassign [
GitRet "config --file .gitmodules --get-regexp path"] status result
3959 set submodules [
split $result "\n"]
3962 Msg Warning "Something went wrong while trying to find submodules: $result"
3965 foreach mod $submodules {
3966 set module [
lindex $mod 1]
3967 if {[
string first "$repo_path/$module" $f] == 0} {
3969 set file_in_module 1
3970 lappend paths "$module"
3975 if {$file_in_module == 0} {
3981 lassign [
GitRet {log --format=%h --abbrev=7 -1} $paths] status result
3983 return [
string tolower $result]
3985 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3989 return [
string tolower $result]
3993 proc GetSimulators {} {
3994 set SIMULATORS [list "modelsim" "questa" "riviera" "activehdl" "ies" "vcs"]
3999 proc GetTopFile {} {
4001 set compile_order_prop [get_property source_mgmt_mode [current_project]]
4002 if {$compile_order_prop ne "All"} {
4003 Msg CriticalWarning "Compile order is not set to automatic, setting it now..."
4004 set_property source_mgmt_mode All [current_project]
4005 update_compile_order -fileset sources_1
4007 return [
lindex [get_files -quiet -compile_order sources -used_in synthesis -filter {FILE_TYPE =~ "VHDL*" || FILE_TYPE =~ "*Verilog*" }] end]
4008 }
elseif {[
IsISE]} {
4009 debug::design_graph_mgr -create [current_fileset]
4010 debug::design_graph -add_fileset [current_fileset]
4011 debug::design_graph -update_all
4012 return [
lindex [debug::design_graph -get_compile_order] end]
4014 Msg Error "GetTopFile not yet implemented for this IDE"
4019 proc GetTopModule {} {
4021 return [get_property top [current_fileset]]
4023 Msg Error "GetTopModule not yet implemented for this IDE"
4033 proc GetVer {path {force_develop 0}} {
4037 Msg CriticalWarning "Empty SHA found for ${path}. Commit to Git to resolve this warning."
4040 set p [
lindex $path 0]
4041 if {[
file isdirectory $p]} {
4044 cd [
file dirname $p]
4046 set repo_path [
Git {rev-parse --show-toplevel}]
4049 return [list [
GetVerFromSHA $SHA $repo_path $force_develop] $SHA]
4060 proc GetVerFromSHA {SHA repo_path {force_develop 0}} {
4062 Msg CriticalWarning "Empty SHA found"
4065 lassign [
GitRet "tag --sort=creatordate --contain $SHA -l v*.*.* -l b*v*.*.*"] status result
4068 if {[regexp {^ *$} $result]} {
4070 lassign [
GitRet "log --oneline --pretty=\"%d\""] status2 tag_list
4073 set pattern {tag: v\d+\.\d+\.\d+}
4074 set real_tag_list {}
4075 foreach x $tag_list {
4076 set x_untrimmed [regexp -all -inline $pattern $x]
4077 regsub "tag: " $x_untrimmed "" x_trimmed
4078 set tt [
lindex $x_trimmed 0]
4079 if {![
string equal $tt ""]} {
4080 lappend real_tag_list $tt
4084 Msg Debug "Cleaned up list: $real_tag_list."
4086 set sorted_tags [lsort -decreasing -command CompareVersions $real_tag_list]
4088 Msg Debug "Sorted Tag list: $sorted_tags"
4090 set tag [
lindex $sorted_tags 0]
4093 set pattern {v\d+\.\d+\.\d+}
4094 if {![regexp $pattern $tag]} {
4095 Msg CriticalWarning "No Hog version tags found in this repository."
4100 set repo_conf $repo_path/Top/repo.conf
4104 set hotfix_prefix "hotfix/"
4105 set minor_prefix "minor_version/"
4106 set major_prefix "major_version/"
4108 set enable_develop_branch $force_develop
4110 set branch_name [
Git {rev-parse --abbrev-ref HEAD}]
4112 if {[
file exists $repo_conf]} {
4113 set PROPERTIES [
ReadConf $repo_conf]
4115 if {[dict exists $PROPERTIES main]} {
4116 set mainDict [dict get $PROPERTIES main]
4119 if {[dict exists $mainDict ENABLE_DEVELOP_BRANCH]} {
4120 set enable_develop_branch [dict get $mainDict ENABLE_DEVELOP_BRANCH]
4126 if {[dict exists $PROPERTIES prefixes]} {
4127 set prefixDict [dict get $PROPERTIES prefixes]
4129 if {[dict exists $prefixDict HOTFIX]} {
4130 set hotfix_prefix [dict get $prefixDict HOTFIX]
4132 if {[dict exists $prefixDict MINOR_VERSION]} {
4133 set minor_prefix [dict get $prefixDict MINOR_VERSION]
4135 if {[dict exists $prefixDict MAJOR_VERSION]} {
4136 set major_prefix [dict get $prefixDict MAJOR_VERSION]
4142 if {[
string match "HEAD" $branch_name]} {
4143 Msg Debug "Detached HEAD detected - attempting to find branch name"
4148 set log_refs [
Git {show -s --pretty=%D HEAD}]
4149 set branch_list [
split $log_refs ","]
4150 Msg Debug "list of possible branch refs $log_refs"
4156 set match_prefixes [list $hotfix_prefix $minor_prefix $major_prefix]
4157 set prev_branch_name $branch_name
4159 foreach br $branch_list {
4160 foreach pr $match_prefixes {
4161 if {[
string match "$pr*" [
string trim $br]]} {
4162 set branch_name [
string trim $br]
4168 if {!$match_count == 1} {
4169 set branch_name $prev_branch_name
4170 Msg Debug "Branch name not found. Using $branch_name"
4172 Msg Debug "Branch name found: $branch_name"
4176 if {$enable_develop_branch == 1} {
4177 if {[
string match "$hotfix_prefix*" $branch_name]} {
4182 if {[
string match "$major_prefix*" $branch_name]} {
4184 set version_level major
4185 }
elseif {[
string match "$minor_prefix*" $branch_name] || ($enable_develop_branch == 1 && $is_hotfix == 0)} {
4187 set version_level minor
4190 set version_level patch
4194 Msg CriticalWarning "Tag $tag does not contain a Hog compatible version in this repository."
4197 }
elseif {$mr == 0} {
4198 switch $version_level {
4213 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."
4219 set vers [
split $result "\n"]
4220 set ver [
lindex $vers 0]
4222 if {[regexp {^v.*$} $v]} {
4230 Msg CriticalWarning "Error while trying to find tag for $SHA"
4238 set M [
format %02X $M]
4239 set m [
format %02X $m]
4240 set c [
format %04X $c]
4241 }
elseif {$M > -1} {
4243 set M [
format %02X $M]
4244 set m [
format %02X $m]
4245 set c [
format %04X $c]
4247 Msg Warning "Tag does not contain a properly formatted version: $ver"
4248 set M [
format %02X 0]
4249 set m [
format %02X 0]
4250 set c [
format %04X 0]
4263 proc Git {command {files ""}} {
4264 lassign [
GitRet $command $files] ret result
4266 Msg Error "Code $ret returned by git running: $command -- $files"
4277 proc GetModuleName {filename} {
4279 if {![
file exists $filename]} {
4280 Msg CriticalWarning "Error: File $filename does not exist."
4285 set fileId [open $filename r]
4288 set file_content [read $fileId]
4294 if {[
file extension $filename] == ".vhd" || [
file extension $filename] == ".vhdl"} {
4296 set file_content [
string tolower $file_content]
4298 set pattern {(?m)^\s*entity\s+(\S+)\s+is}
4299 }
elseif {[
file extension $filename] == ".v" || [
file extension $filename] == ".sv"} {
4301 set pattern {\n\s*module\s*(\w+)(\s*|\(|\n)}
4303 Msg Debug "File is neither VHDL nor Verilog... Returning empty string..."
4308 if {[regexp $pattern $file_content match module_name]} {
4311 Msg Debug "No module was found in $filename. Returning an empty string..."
4319 proc GetVerilogGenerics {file} {
4320 set fp [open $file r]
4326 foreach line [
split $data "\n"] {
4327 regsub "^\\s*\/\/.*" $line "" line
4328 regsub "(.*)\/\/.*" $line {\1} line
4329 if {![
string equal $line ""]} {
4330 append lines $line " "
4335 regsub -all {/\*.*\*/} $lines "" lines
4338 set punctuation [list]
4339 foreach char [list "(" ")" ";" "," " " "!" "<=" ":=" "=" "\[" "\]"] {
4340 lappend punctuation $char "\000$char\000"
4344 set tokens [
split [
string map $punctuation $lines] \000]
4346 set parameters [dict create]
4355 foreach token $tokens {
4356 set token [
string trim $token]
4357 if {![
string equal "" $token]} {
4358 if {[
string equal [
string tolower $token] "parameter"]} {
4359 set state $PARAM_NAME
4360 }
elseif {[
string equal $token ")"] || [
string equal $token ";"]} {
4362 }
elseif {$state == $PARAM_WIDTH} {
4363 if {[
string equal $token "\]"]} {
4364 set state $PARAM_NAME
4366 }
elseif {$state == $PARAM_VALUE} {
4367 if {[
string equal $token ","]} {
4368 set state $PARAM_NAME
4369 }
elseif {[
string equal $token ";"]} {
4374 }
elseif {$state == $PARAM_NAME} {
4375 if {[
string equal $token "="]} {
4376 set state $PARAM_VALUE
4377 }
elseif {[
string equal $token "\["]} {
4378 set state $PARAM_WIDTH
4379 }
elseif {[
string equal $token ","]} {
4380 set state $PARAM_NAME
4381 }
elseif {[
string equal $token ";"]} {
4383 }
elseif {[
string equal $token ")"]} {
4386 dict set parameters $token "integer"
4399 proc GetVhdlGenerics {file {entity ""}} {
4400 set fp [open $file r]
4406 foreach line [
split $data "\n"] {
4407 regsub "^\\s*--.*" $line "" line
4408 regsub "(.*)--.*" $line {\1} line
4409 if {![
string equal $line ""]} {
4410 append lines $line " "
4415 set generic_block ""
4416 set generics [dict create]
4418 if {1 == [
string equal $entity ""]} {
4419 regexp {(?i).*entity\s+([^\s]+)\s+is} $lines _ entity
4422 set generics_regexp "(?i).*entity\\s+$entity\\s+is\\s+generic\\s*\\((.*)\\)\\s*;\\s*port.*end.*$entity"
4424 if {[regexp $generics_regexp $lines _ generic_block]} {
4426 foreach line [
split $generic_block ";"] {
4428 regexp {(.*):\s*([A-Za-z0-9_]+).*} $line _ generic type
4431 set splits [
split $generic ","]
4432 foreach split $splits {
4433 dict set generics [
string trim $split] [
string trim $type]
4441 proc GHDL {command logfile} {
4442 set ret [
catch {
exec -ignorestderr ghdl {*}$command >>& $logfile} result options]
4447 return [list $ret $result]
4459 proc GitRet {command {files ""}} {
4462 set ret [
catch {
exec -ignorestderr git {*}$command} result]
4464 set ret [
catch {
exec -ignorestderr git {*}$command -- {*}$files} result]
4466 return [list $ret $result]
4474 proc GitVersion {target_version} {
4475 set ver [
split $target_version "."]
4476 set v [
Git --version]
4478 set current_ver [
split [
lindex $v 2] "."]
4479 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
4480 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
4481 return [
expr {$target <= $current}]
4494 proc RcloneFileExists {remote_file config_path} {
4495 if {[
catch {
exec rclone ls $remote_file --config $config_path 2>@1} result opt] == 0} {
4499 set rclone_ret [
lindex [dict get $opt -errorcode] end]
4500 if {$rclone_ret ne "3" && $rclone_ret ne "4"} {
4501 Msg CriticalWarning "Could not check if $remote_file is in the Rclone repository: $result"
4503 Msg Debug "$remote_file not found in the Rclone repository (rclone exit code $rclone_ret)."
4520 proc HandleIP {what_to_do xci_file ip_path repo_path on_eos on_rclone rclone_config_path {gen_dir "."} {force 0}} {
4522 if {!($what_to_do eq "push") && !($what_to_do eq "pull")} {
4523 Msg Error "You must specify push or pull as first argument."
4526 if {[
catch {
package require tar} TARPACKAGE]} {
4527 Msg CriticalWarning "Cannot find package tar. You can fix this by installing package \"tcllib\""
4534 if {!([
file exists $xci_file])} {
4535 Msg CriticalWarning "Could not find $xci_file."
4541 set xci_path [
file dirname $xci_file]
4542 set xci_name [
file tail $xci_file]
4543 set xci_ip_name [
file rootname [
file tail $xci_file]]
4544 set xci_dir_name [
file tail $xci_path]
4545 set gen_path $gen_dir
4547 set hash [
Md5Sum $xci_file]
4548 set file_name $xci_name\_$hash
4552 set scope [get_property -quiet SCOPE [get_ips $xci_ip_name]]
4554 Msg Debug "IP $xci_name is a subcore of a BD file, will not pull/push it from/to the remote directory."
4558 Msg Info "Preparing to $what_to_do IP: $xci_name ($file_name)."
4560 if {$what_to_do eq "push"} {
4563 if {$on_rclone == 1} {
4569 Msg Info "IP already in the Rclone repository, will not copy..."
4571 Msg Info "IP already in the Rclone repository, will forcefully replace..."
4576 }
elseif {$on_eos == 1} {
4577 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4582 Msg Info "IP already in the EOS repository, will not copy..."
4584 Msg Info "IP already in the EOS repository, will forcefully replace..."
4590 if {[
file exists "$ip_path/$file_name.tar"]} {
4592 Msg Info "IP already in the local repository, will not copy..."
4594 Msg Info "IP already in the local repository, will forcefully replace..."
4603 if {$will_copy == 1} {
4605 Msg Info "Looking for generated files in $gen_path..."
4606 set ip_gen_files [glob -nocomplain $gen_path/*]
4610 if {[
llength $ip_gen_files] > 0} {
4611 Msg Info "Found some IP synthesised files matching $xci_ip_name"
4612 if {$will_remove == 1} {
4613 Msg Info "Removing old synthesised directory $ip_path/$file_name.tar..."
4614 if {$on_rclone == 1} {
4615 lassign [
ExecuteRet rclone delete $ip_path/$file_name.tar --config $rclone_config_path] ret result
4617 Msg CriticalWarning "Could not delete file from Rclone: $result"
4619 }
elseif {$on_eos == 1} {
4620 eos "rm -rf $ip_path/$file_name.tar" 5
4622 file delete -force "$ip_path/$file_name.tar"
4626 Msg Info "Creating local archive with IP generated files..."
4629 set at_least_one_long 0
4630 foreach f $ip_gen_files {
4631 set new_f "[
Relative [
file normalize $repo_path] $f]"
4632 set len [
string length $new_f]
4634 Msg Warning "One file in $xci_ip_name is too long ($len chars): $new_f"
4635 set at_least_one_long 1
4637 lappend tar_files $new_f
4640 Msg Debug "Tar files: $tar_files"
4641 if {$at_least_one_long == 1} {
4642 Msg Warning "Using regular tar, please cross your fingers..."
4644 lassign [
ExecuteRet tar -cf $file_name.tar {*}$tar_files] ret_tar result_tar
4645 if {$ret_tar != 0} {
4646 Msg CriticalWarning "Something went wrong when using regular tar. Error message: $result_tar"
4649 if {[
catch { ::tar::create $file_name.tar $tar_files} err]} {
4650 Msg Warning "Tcl built-in tar failed: $err, will try regular tar..."
4652 lassign [
ExecuteRet tar -cf $file_name.tar {*}$tar_files] ret_tar result_tar
4653 if {$ret_tar != 0} {
4654 Msg CriticalWarning "Something went wrong when using regular tar. Error message: $result_tar"
4659 Msg Info "Copying IP generated files for $xci_name..."
4660 if {$on_rclone == 1} {
4661 lassign [
ExecuteRet rclone copyto $file_name.tar $ip_path/$file_name.tar --config $rclone_config_path] ret result
4663 Msg CriticalWarning "Something went wrong when copying the IP files to Rclone. Error message: $result"
4665 }
elseif {$on_eos == 1} {
4666 lassign [
ExecuteRet xrdcp -f -s $file_name.tar $::env(EOS_MGM_URL)//$ip_path/] ret msg
4668 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4671 Copy "$file_name.tar" "$ip_path/"
4673 Msg Info "Removing local archive"
4674 file delete $file_name.tar
4676 Msg Warning "Could not find synthesized files matching $gen_path/$file_name*"
4679 }
elseif {$what_to_do eq "pull"} {
4680 if {$on_rclone == 1} {
4683 Msg Info "Nothing for $xci_name was found in the Rclone repository, cannot pull."
4687 Msg Info "IP $xci_name found in the Rclone repository $ip_path, copying it locally to $repo_path..."
4688 lassign [
ExecuteRet rclone copyto $ip_path/$file_name.tar $file_name.tar --config $rclone_config_path] ret_copy result_copy
4689 if {$ret_copy != 0} {
4690 Msg CriticalWarning "Something went wrong when copying the IP files from Rclone. Error message: $result_copy"
4693 }
elseif {$on_eos == 1} {
4694 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4696 Msg Info "Nothing for $xci_name was found in the EOS repository, cannot pull."
4700 set remote_tar "$::env(EOS_MGM_URL)//$ip_path/$file_name.tar"
4701 Msg Info "IP $xci_name found in the repository $remote_tar, copying it locally to $repo_path..."
4703 lassign [
ExecuteRet xrdcp -f -r -s $remote_tar $repo_path] ret msg
4705 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4709 if {[
file exists "$ip_path/$file_name.tar"]} {
4710 Msg Info "IP $xci_name found in local repository $ip_path/$file_name.tar, copying it locally to $repo_path..."
4711 Copy $ip_path/$file_name.tar $repo_path
4713 Msg Info "Nothing for $xci_name was found in the local IP repository, cannot pull."
4719 if {[
file exists $file_name.tar]} {
4720 remove_files $xci_file
4721 Msg Info "Extracting IP files from archive to $repo_path..."
4722 ::tar::untar $file_name.tar -dir $repo_path -noperms
4723 Msg Info "Removing local archive"
4724 file delete $file_name.tar
4725 add_files -norecurse -fileset sources_1 $xci_file
4738 proc HexVersionToString {version} {
4739 scan [
string range $version 0 1] %x M
4740 scan [
string range $version 2 3] %x m
4741 scan [
string range $version 4 7] %x c
4746 proc ImportTclLib {} {
4749 if {[
info exists env(HOG_TCLLIB_PATH)]} {
4750 lappend auto_path $env(HOG_TCLLIB_PATH)
4753 puts "ERROR: To run Hog with Microsemi Libero SoC or Lattice Diamond, you need to define the HOG_TCLLIB_PATH variable."
4768 proc InitLauncher {script tcl_path parameters commands argv {custom_commands ""}} {
4769 set repo_path [
file normalize "$tcl_path/../.."]
4771 set bin_path [
file normalize "$tcl_path/../../bin"]
4772 set top_path [
file normalize "$tcl_path/../../Top"]
4774 set cmd_lines [
split $commands "\n"]
4776 set command_options [dict create]
4777 set directive_descriptions [dict create]
4778 set directive_names [dict create]
4779 set common_directive_names [dict create]
4780 set directives_with_optional_projects ""
4781 set custom_command ""
4782 set custom_command_options ""
4784 foreach l $cmd_lines {
4786 if {[regexp {\\(.*) \{\#} $l minc d]} {
4787 lappend directives_with_projects $d
4788 set current_directive $d
4791 if {[regexp {^[^#]* allow_empty_proj} $l minc dd]} {
4792 lappend directives_with_optional_projects $current_directive
4796 if {[regexp {\\(.*) \{} $l minc regular_expression]} {
4797 lappend directive_regex $regular_expression
4801 if {[regexp {\#\s*NAME(\*)?:\s*(.*)\s*} $l minc star name]} {
4802 dict set directive_names $name $regular_expression
4804 dict set common_directive_names $name $regular_expression
4807 set directive_names [
DictSort $directive_names]
4808 set common_directive_names [
DictSort $common_directive_names]
4811 if {[regexp {\#\s*DESCRIPTION:\s*(.*)\s*} $l minc x]} {
4812 dict set directive_descriptions $regular_expression $x
4816 if {[regexp {\#\s*OPTIONS:\s*(.*)\s*} $l minc x]} {
4817 dict set command_options $regular_expression [
split [regsub -all {[ \t\n]+} $x {}] ","]
4821 set short_usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nMost common directives (case insensitive):"
4823 dict for {key value} $common_directive_names {
4824 set short_usage "$short_usage\n - $key: [dict get $directive_descriptions $value]"
4827 if {[
string length $custom_commands] > 0} {
4828 Msg Debug "Found custom commands to add to short short_usage."
4829 set short_usage "$short_usage\n\nCustom commands:"
4830 dict for {key command} $custom_commands {
4831 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4832 set short_usage "$short_usage\n - $key: [dict get $command DESCRIPTION]"
4837 set short_usage "$short_usage\n\n\
4838 To see all the available directives, run:\n./Hog/Do HELP\n\n\
4839 To list available options for the chosen directive run:\n\
4840 ./Hog/Do <directive> HELP\n
4843 set usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nDirectives (case insensitive):"
4845 dict for {key value} $directive_names {
4846 set usage "$usage\n - $key: [dict get $directive_descriptions $value]"
4850 if {[
string length $custom_commands] > 0} {
4851 Msg Debug "Found custom commands to add to short usage."
4852 set usage "$usage\n\nCustom commands:"
4853 dict for {key command} $custom_commands {
4854 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4855 set usage "$usage\n - $key: [dict get $command DESCRIPTION]"
4860 set usage "$usage\n\nTo list available options for the chosen directive run:\n./Hog/Do <directive> HELP"
4869 Msg Debug "HogEnv.conf found"
4876 if {[
catch {
package require cmdline} ERROR]} {
4877 Msg Debug "The cmdline Tcl package was not found, sourcing it from Hog..."
4878 source $tcl_path/utils/cmdline.tcl
4881 set argv [regsub -all {(?i) HELP\y} $argv " -help"]
4886 set custom_parameters [list]
4887 dict for {key command} $custom_commands {
4888 set custom_parameters [concat $custom_parameters [dict get $command CUSTOM_OPTIONS]]
4891 lassign [
GetOptions $argv [
concat $custom_parameters $parameters]] option_list arg_list
4893 if {[
IsInList "-all" $option_list]} {
4902 set directive [
string toupper [
lindex $arg_list 0]]
4905 set argument_is_no_project 1
4907 set NO_DIRECTIVE_FOUND 0
4908 switch -regexp -- $directive "$commands"
4910 if {$NO_DIRECTIVE_FOUND == 1} {
4911 if {[
string length $custom_commands] > 0 && [dict exists $custom_commands $directive]} {
4912 set custom_command $directive
4913 set custom_command_dict [
DictGet $custom_commands $directive]
4914 set custom_command_hog_parameters [
DictGet $custom_command_dict OPTIONS]
4915 set custom_command_options [
DictGet $custom_command_dict CUSTOM_OPTIONS]
4916 set custom_command_options [
concat $custom_command_hog_parameters $custom_command_options]
4918 Msg Status "ERROR: Unknown directive $directive.\n\n"
4924 if {[
IsInList $directive $directives_with_projects 1]} {
4925 set argument_is_no_project 0
4929 if {$directive != ""} {
4930 if {[
IsInList $directive $directives_with_projects 1]} {
4931 puts "usage: ./Hog/Do \[OPTIONS\] $directive <project>\n"
4932 }
elseif {[regexp "^COMPSIM(LIB)?$" $directive]} {
4933 puts "usage: ./Hog/Do \[OPTIONS\] $directive <simulator>\n"
4935 puts "usage: ./Hog/Do \[OPTIONS\] $directive \n"
4938 dict for {dir desc} $directive_descriptions {
4939 if {[regexp $dir $directive]} {
4946 if {$custom_command ne ""} {
4947 if {[
llength $custom_command_options] > 0} {
4948 puts "Available options:"
4950 foreach custom_option $custom_command_options {
4951 set n [
llength $custom_option]
4953 lassign $custom_option opt help
4956 }
elseif {$n == 3} {
4957 lassign $custom_option opt def help
4958 puts " -$opt <argument>"
4960 puts " $help (default: $def)"
4965 Msg Warning "Custom option spec has invalid arity (expected 2 or 3): $custom_option"
4970 dict for {dir opts} $command_options {
4971 if {[regexp $dir $directive]} {
4972 puts "Available options:"
4974 foreach par $parameters {
4975 if {$opt == [lindex $par 0]} {
4976 if {[regexp {\.arg$} $opt]} {
4977 set opt_name [regsub {\.arg$} $opt ""]
4978 puts " -$opt_name <argument>"
4982 puts " [lindex $par [llength $par]-1]"
4997 set directive_options ""
4998 set directive_found 0
4999 if {$custom_command ne ""} {
5000 set directive_options $custom_command_options
5001 set directive_found 1
5003 dict for {dir opts} $command_options {
5004 if {[regexp $dir $directive]} {
5005 set directive_options $opts
5006 set directive_found 1
5013 set directive_option_names {}
5014 foreach opt $directive_options {
5015 lappend directive_option_names [regsub {\.arg$} [
lindex $opt 0] ""]
5018 if {$custom_command ne ""} {
5019 set parameters [
concat $parameters $custom_command_options]
5022 if {[
catch {
array set options [
cmdline::getoptions option_list $parameters $usage]} err]} {
5023 Msg Status "\nERROR: Syntax error, probably unknown option.\n\n USAGE: $err"
5028 if {$directive_found == 1} {
5029 set default_option_list {}
5031 foreach {key value} [
array get options] {
5032 if {[
IsInList $key $directive_option_names] || ![
info exists default_options($key)]} {
5035 if {$value ne $default_options($key)} {
5036 Msg Warning "Option -$key is not supported by directive $directive, ignoring it."
5037 set options($key) $default_options($key)
5042 if {[
llength $arg_list] <= $min_n_of_args || [
llength $arg_list] > $max_n_of_args} {
5043 Msg Status "\nERROR: Wrong number of arguments: [
llength $argv]"
5048 set project [
lindex $arg_list 1]
5049 set optional_project [
IsInList $directive $directives_with_optional_projects 1]
5051 if {$argument_is_no_project == 0} {
5053 regsub "^(\./)?Top/" $project "" project
5055 regsub "/? *\$" $project "" project
5057 if {$project eq "" && $optional_project == 1} {
5066 Msg Debug "Option list:"
5068 foreach {key value} [
array get options] {
5069 Msg Debug "$key => $value"
5076 if {$proj_conf != 0} {
5079 lassign [
GetIDECommand $proj_conf] cmd before_tcl_script after_tcl_script end_marker
5080 Msg Info "Project $project uses $cmd IDE"
5083 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
5085 if {$custom_command ne ""} {
5086 if {[dict exists $custom_commands $directive IDE]} {
5087 lassign [
GetIDECommand "" [dict get $custom_commands $directive IDE]] cmd before_tcl_script after_tcl_script end_marker
5088 Msg Info "Custom command: $custom_command uses $cmd IDE"
5089 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
5091 set command "custom_tcl"
5093 }
elseif {$argument_is_no_project == 1} {
5095 Msg Debug "$project will be used as first argument"
5096 }
elseif {$project != ""} {
5099 }
elseif {$min_n_of_args < 0} {
5104 if {$optional_project == 0} {
5116 set project_group [
file dirname $project]
5117 set project_name $project
5118 set project [
file tail $project]
5119 Msg Debug "InitLauncher: project_group=$project_group, project_name=$project_name, project=$project"
5121 return [list $directive $project $project_name $project_group $repo_path $old_path\
5122 $bin_path $top_path $usage $short_usage $command $cmd [
array get options]\
5130 proc IsCommitAncestor {ancestor commit} {
5131 lassign [
GitRet "merge-base --is-ancestor $ancestor $commit"] status result
5140 return [
expr {[info commands sys_install] != ""}]
5145 return [
expr {[info commands get_libero_version] != ""}]
5154 proc IsInList {element list {regex 0} {nocase 0}} {
5158 if {[regexp -nocase $x $element]} {
5162 if {[regexp $x $element]} {
5166 }
elseif {$regex == 0} {
5168 if {[
string tolower $x] eq [
string tolower $element]} {
5172 if {$x eq $element} {
5185 return [
expr {[string first PlanAhead [version]] == 0}]
5193 if {[
catch {
package require ::quartus::flow} result]} {
5206 proc IsRelativePath {path} {
5207 if {[
string index $path 0] == "/" || [
string index $path 0] == "~"} {
5215 proc IsSynplify {} {
5216 return [
expr {[info commands program_version] != ""}]
5221 return [
expr {![IsQuartus] && ![IsXilinx] && ![IsVitisClassic] && ![IsVitisUnified] && ![IsLibero] && ![IsSynplify] && ![IsDiamond]}]
5229 proc IsVerilog {file} {
5230 if {[
file extension $file] == ".v" || [
file extension $file] == ".sv"} {
5242 proc IsVersal {part} {
5243 if {[get_property ARCHITECTURE [get_parts $part]] eq "versal"} {
5253 return [
expr {[string first Vivado [version]] == 0}]
5261 if {[
info commands version] != ""} {
5262 set current_version [version]
5263 if {[
string first PlanAhead $current_version] == 0 || [
string first Vivado $current_version] == 0} {
5265 }
elseif {[
string first xsct $current_version] == 0} {
5268 Msg Warning "This IDE has the version command but it is not PlanAhead or Vivado: $current_version"
5277 proc IsVitisClassic {} {
5278 if {[
info exists globalSettings::vitis_classic]} {
5279 return $globalSettings::vitis_classic
5281 return [
expr {[info commands platform] != ""}]
5285 proc IsVitisUnified {} {
5286 if {[
info exists globalSettings::vitis_unified]} {
5287 return $globalSettings::vitis_unified
5301 proc ExecuteVitisUnifiedCommand {python_script command args {error_prefix "Failed to execute command"} {output_var ""}} {
5302 set cmdlist [list vitis -s $python_script $command]
5304 lappend cmdlist $arg
5306 lappend cmdlist 2>@1
5308 Msg Debug "Executing: vitis -s $python_script $command $args"
5311 set env(PYTHONUNBUFFERED) "1"
5314 if {[
catch {
set pipe [open "|$cmdlist" "r"]} err]} {
5315 Msg Error "$error_prefix: Failed to open pipe: $err"
5319 fconfigure $pipe -buffering line
5320 set script_output ""
5321 set vitis_version ""
5324 set vitis_banner_patterns {
5325 "*Vitis Development Environment*"
5328 "*Copyright*Xilinx*"
5329 "*Copyright*Advanced Micro Devices*"
5330 "*All Rights Reserved*"
5334 while {[
gets $pipe line] >= 0} {
5336 if {[
string match "*Vitis v*" $line]} {
5337 if {[regexp {Vitis\s+v([0-9]+)\.([0-9]+)(?:\.[0-9]+)?} $line -> major minor]} {
5338 set year_last_two [
string range $major end-1 end]
5339 set vitis_version "$year_last_two.$minor"
5345 foreach pattern $vitis_banner_patterns {
5346 if {[
string match $pattern $line]} {
5352 if {![
string match "INFO:*" $line] && ![
string match "WARNING:*" $line] && ![
string match "ERROR:*" $line] && ![
string match "DEBUG:*" $line]} {
5353 if {$vitis_version ne ""} {
5354 set line "INFO: \[Vitis_v$vitis_version\] $line"
5356 set line "INFO: $line"
5360 append script_output "$line\n"
5367 if {[
catch {
close $pipe} err]} {
5368 if {[regexp {exit (\d+)} $err -> exit_code]} {
5369 if {$exit_code != 0} {
5370 Msg Error "$error_prefix (exit code: $exit_code)"
5371 if {$output_var ne ""} {
5372 upvar $output_var output
5373 set output $script_output
5378 Msg Error "$error_prefix: $err"
5379 if {$output_var ne ""} {
5380 upvar $output_var output
5381 set output $script_output
5388 if {$output_var ne ""} {
5389 upvar $output_var output
5390 set output $script_output
5401 proc IsZynq {part} {
5402 if {[regexp {^(xc7z|xczu).*} $part]} {
5409 proc ImportGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
5410 set list_path "$repo_path/Top/$project_name/list"
5411 lassign [
GetHogFiles -list_files {.src,.ext,.sim} -ext_path $ext_path $list_path $repo_path] src_files properties filesets
5416 set properties [
DictGet $simset_dict "properties"]
5417 set options [
DictGet $properties "options"]
5420 set workdir Projects/$project_name/ghdl
5421 file delete -force $workdir
5423 set import_log "$workdir/ghdl-import-${simset_name}.log"
5424 dict for {lib sources} $src_files {
5425 set libname [file rootname $lib]
5426 foreach f $sources {
5427 if {[file extension $f] != ".vhd" && [file extension $f] != ".vhdl"} {
5428 Msg Info "File $f is not a VHDL file, copying it in workfolder..."
5429 file copy -force $f $workdir
5431 set file_path [Relative $repo_path $f]
5432 set import_log_file [open $import_log "a"]
5433 puts "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
5434 puts $import_log_file "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
5435 close $import_log_file
5436 lassign [GHDL "-i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path" $import_log] ret result
5438 Msg CriticalWarning "GHDL import failed for file $f: $result"
5446 proc LaunchGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
5449 set sim_props [
DictGet $simset_dict "properties"]
5450 set options [
DictGet $sim_props "options"]
5451 set runopts [
DictGet $sim_props "run_options"]
5453 dict for {prop_name prop_val} $sim_props {
5454 set prop_name [string toupper $prop_name]
5455 if {$prop_name == "TOP"} {
5456 set top_sim $prop_val
5459 set workdir $repo_path/Projects/$project_name/ghdl
5460 set make_log "$workdir/ghdl-make-${simset_name}.log"
5461 set run_log "$workdir/ghdl-run-${simset_name}.log"
5464 set make_log_file [open $make_log "w"]
5466 puts "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
5467 puts $make_log_file "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
5468 close $make_log_file
5470 lassign [
GHDL "-m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim" $make_log] ret result
5473 Msg Error "GHDL make failed for $top_sim: $result"
5477 set run_log_file [open $run_log "w"]
5478 puts "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
5479 puts $run_log_file "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
5482 lassign [
GHDL "-r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts" $run_log] ret result
5486 Msg Error "GHDL run failed for $top_sim: $result"
5501 proc LaunchImplementation {reset do_create run_folder project_name {repo_path .} {njobs 4} {do_bitstream 0}} {
5502 Msg Info "Starting implementation flow..."
5505 Msg Info "Resetting run before launching implementation..."
5510 if {[
file exist "$run_folder/timing_ok.txt"]} {
5511 file delete "$run_folder/timing_ok.txt"
5513 if {[
file exist "$run_folder/timing_error.txt"]} {
5514 file delete "$run_folder/timing_error.txt"
5518 source $repo_path/Hog/Tcl/integrated/pre-implementation.tcl
5521 if {$do_bitstream == 1} {
5522 launch_runs impl_1 -to_step [
BinaryStepName [get_property PART [current_project]]] -jobs $njobs -dir $run_folder
5524 launch_runs impl_1 -jobs $njobs -dir $run_folder
5529 Msg Info "running post-implementation"
5530 source $repo_path/Hog/Tcl/integrated/post-implementation.tcl
5531 if {$do_bitstream == 1} {
5532 Msg Info "running pre-bitstream"
5533 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5534 Msg Info "running post-bitstream"
5535 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5539 set prog [get_property PROGRESS [get_runs impl_1]]
5540 set status [get_property STATUS [get_runs impl_1]]
5541 Msg Info "Run: impl_1 progress: $prog, status : $status"
5545 set status_file [open "$run_folder/timing.txt" "w"]
5546 puts $status_file "## $project_name Timing summary"
5548 set f [open [
lindex [glob "$run_folder/impl_1/*.twr" 0]]]
5550 while {[
gets $f line] >= 0} {
5551 if {[
string match "Timing summary:" $line]} {
5552 while {[
gets $f line] >= 0} {
5553 if {[
string match "Timing errors:*" $line]} {
5554 set errs [regexp -inline -- {[0-9]+} $line]
5556 if {[
string match "*Footnotes*" $line]} {
5559 puts $status_file "$line"
5568 Msg Info "Time requirements are met"
5569 file rename -force "$run_folder/timing.txt" "$run_folder/timing_ok.txt"
5572 Msg CriticalWarning "Time requirements are NOT met"
5573 file rename -force "$run_folder/timing.txt" "$run_folder/timing_error.txt"
5579 set wns [get_property STATS.WNS [get_runs [current_run]]]
5580 set tns [get_property STATS.TNS [get_runs [current_run]]]
5581 set whs [get_property STATS.WHS [get_runs [current_run]]]
5582 set ths [get_property STATS.THS [get_runs [current_run]]]
5583 set tpws [get_property STATS.TPWS [get_runs [current_run]]]
5585 if {$wns >= 0 && $whs >= 0 && $tpws >= 0} {
5586 Msg Info "Time requirements are met"
5587 set status_file [open "$run_folder/timing_ok.txt" "w"]
5590 Msg CriticalWarning "Time requirements are NOT met"
5591 set status_file [open "$run_folder/timing_error.txt" "w"]
5595 Msg Status "*** Timing summary ***"
5596 Msg Status "WNS: $wns"
5597 Msg Status "TNS: $tns"
5598 Msg Status "WHS: $whs"
5599 Msg Status "THS: $ths"
5600 Msg Status "TPWS: $tpws"
5606 puts $status_file "## $project_name Timing summary"
5608 m add row "| **Parameter** | \"**value (ns)**\" |"
5609 m add row "| --- | --- |"
5610 m add row "| WNS: | $wns |"
5611 m add row "| TNS: | $tns |"
5612 m add row "| WHS: | $whs |"
5613 m add row "| THS: | $ths |"
5614 m add row "| TPWS: | $tpws |"
5616 puts $status_file [m format 2string]
5617 puts $status_file "\n"
5618 if {$timing_ok == 1} {
5619 puts $status_file " Time requirements are met."
5621 puts $status_file "Time requirements are **NOT** met."
5623 puts $status_file "\n\n"
5627 if {$prog ne "100%"} {
5628 Msg Error "Implementation error"
5633 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5634 Msg Info "Git describe set to $describe"
5636 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5641 if {[
file exists $run_folder/versions.txt]} {
5642 file copy -force $run_folder/versions.txt $dst_dir
5644 Msg Warning "No versions file found in $run_folder/versions.txt"
5647 set timing_files [glob -nocomplain "$run_folder/timing_*.txt"]
5648 set timing_file [
file normalize [
lindex $timing_files 0]]
5650 if {[
file exists $timing_file]} {
5651 file copy -force $timing_file $dst_dir/
5653 Msg Warning "No timing file found, not a problem if running locally"
5657 if {[
IsVersal [get_property part [current_project]]]} {
5658 if {[get_property segmented_configuration [current_project]] == 1} {
5659 Msg Info "Versal Segmented configuration detected: exporting XSA file..."
5660 set xsa_name "$dst_dir/[
file tail $project_name]\-$describe.xsa"
5661 write_hw_platform -fixed -force -file $xsa_name
5665 set revision [get_current_revision]
5667 set ic [get_global_assignment -name INCREMENTAL_COMPILATION]
5668 if {$ic ne "" && [
string toupper $ic] ne "OFF"} {
5669 Msg Info "Incremental compilation enabled ($ic), running Partition Merge..."
5670 if {[
catch {execute_module -tool cdb -args "--merge=on"} result]} {
5671 Msg Error "Result: $result\nPartition Merge failed. See the report file.\n"
5675 if {[
catch {execute_module -tool fit} result]} {
5676 Msg Error "Result: $result\n"
5677 Msg Error "Place & Route failed. See the report file.\n"
5679 Msg Info "\nINFO: Place & Route was successful for revision $revision.\n"
5682 if {[
catch {execute_module -tool sta -args "--do_report_timing"} result]} {
5683 Msg Error "Result: $result\n"
5684 Msg Error "Time Quest failed. See the report file.\n"
5686 Msg Info "Time Quest was successfully run for revision $revision.\n"
5689 set panel "Timing Analyzer||Timing Analyzer Summary"
5690 set device [get_report_panel_data -name $panel -col 1 -row_name "Device Name"]
5691 set timing_model [get_report_panel_data -name $panel -col 1 -row_name "Timing Models"]
5692 set delay_model [get_report_panel_data -name $panel -col 1 -row_name "Delay Model"]
5694 Msg Info "*******************************************************************"
5695 Msg Info "Device: $device"
5696 Msg Info "Timing Models: $timing_model"
5697 Msg Info "Delay Model: $delay_model"
5700 Msg Info "*******************************************************************"
5703 Msg Info "Starting implementation flow..."
5704 if {[
catch {run_tool -name {PLACEROUTE}}]} {
5705 Msg Error "PLACEROUTE FAILED!"
5707 Msg Info "PLACEROUTE PASSED."
5711 Msg Info "Run VERIFYTIMING ..."
5712 if {[
catch {run_tool -name {VERIFYTIMING} -script {Hog/Tcl/integrated/libero_timing.tcl}}]} {
5713 Msg CriticalWarning "VERIFYTIMING FAILED!"
5715 Msg Info "VERIFYTIMING PASSED \n"
5721 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5722 Msg Info "Git describe set to $describe"
5724 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5725 file mkdir $dst_dir/reports
5728 if {[
file exists $run_folder/versions.txt]} {
5729 file copy -force $run_folder/versions.txt $dst_dir
5731 Msg Warning "No versions file found in $run_folder/versions.txt"
5734 set timing_file_path [
file normalize "$repo_path/Projects/timing_libero.txt"]
5735 if {[
file exists $timing_file_path]} {
5736 file copy -force $timing_file_path $dst_dir/reports/Timing.txt
5737 set timing_file [open $timing_file_path "r"]
5738 set status_file [open "$dst_dir/timing.txt" "w"]
5739 puts $status_file "## $project_name Timing summary\n\n"
5740 puts $status_file "| | |"
5741 puts $status_file "| --- | --- |"
5742 while {[
gets $timing_file line] >= 0} {
5743 if {[
string match "SUMMARY" $line]} {
5744 while {[
gets $timing_file line] >= 0} {
5745 if {[
string match "END SUMMARY" $line]} {
5748 if {[
string first ":" $line] == -1} {
5751 set out_string "| [
string map {: | } $line] |"
5752 puts $status_file "$out_string"
5757 Msg Warning "No timing file found, not a problem if running locally"
5762 set force_rst "-forceOne"
5764 prj_run Map $force_rst
5765 prj_run PAR $force_rst
5777 proc GenerateBitstreamOnly {project_name {repo_path .}} {
5781 set project_file [
file normalize "$repo_path/Projects/$project_name/$project_name.xpr"]
5782 if {![
file exists $project_file]} {
5783 Msg Error "Project file not found: $project_file. Please create the project first."
5789 set impl_runs [get_runs -quiet impl_1]
5790 if {[
llength $impl_runs] == 0} {
5791 Msg Error "Implementation run 'impl_1' does not exist. Please run implementation first."
5797 set run_dir [get_property DIRECTORY [get_runs impl_1]]
5798 if {![
file exists $run_dir]} {
5799 Msg Error "Implementation run directory not found: $run_dir. Please check your implementation run."
5804 Msg Info "Running pre-bitstream..."
5805 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5807 Msg Info "Writing bitstream for $project_name..."
5810 write_bitstream -force [
file normalize "$run_dir/$top_name.bit"]
5812 Msg Info "Running post-bitstream..."
5813 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5822 proc LaunchSimulation {project_name lib_path simsets {repo_path .} {scripts_only 0} {compile_only 0}} {
5825 set project [
file tail $project_name]
5826 set main_sim_folder [
file normalize "$repo_path/Projects/$project_name/$project.sim/"]
5828 if {$simsets != ""} {
5829 dict for {simset sim_dict} $simsets {
5830 lappend simsets_todo $simset
5832 Msg Info "Will run only the following simulation's sets (if they exist): $simsets_todo"
5835 if {$scripts_only == 1} {
5836 Msg Info "Only generating simulation scripts, not running simulations..."
5839 if {$compile_only == 1} {
5840 Msg Info "Only compiling simulation libraries, not running simulations..."
5845 set sim_dic [dict create]
5847 Msg Info "Retrieving list of simulation sets..."
5848 foreach s [get_filesets] {
5850 set use_simpass_str 0
5853 set type [get_property FILESET_TYPE $s]
5854 if {$type eq "SimulationSrcs"} {
5855 if {$simsets_todo != "" && $s ni $simsets_todo} {
5856 Msg Info "Skipping $s as it was not specified with the -simset option..."
5859 set sim_dict [
DictGet $simsets $s]
5860 set simulator [
DictGet $sim_dict "simulator"]
5861 set_property "target_simulator" $simulator [current_project]
5862 set hog_sim_props [
DictGet $sim_dict "hog"]
5863 dict for {prop_name prop_val} $hog_sim_props {
5864 # If HOG_SIMPASS_STR is set, use the HOG_SIMPASS_STR string to search for in logs, after simulation is done
5865 if {[string toupper $prop_name] == "HOG_SIMPASS_STR" && $prop_val != ""} {
5866 Msg Info "Setting simulation pass string as '$prop_val'"
5867 set use_simpass_str 1
5868 set simpass_str $prop_val
5870 if {[string toupper $prop_name] == "HOG_SILENT_SIM" && $prop_val == 1} {
5871 set quiet_sim " -quiet"
5877 Msg Info "Creating simulation scripts for $s..."
5878 if {[
file exists $repo_path/Top/$project_name/pre-simulation.tcl]} {
5879 Msg Info "Running $repo_path/Top/$project_name/pre-simulation.tcl"
5880 source $repo_path/Top/$project_name/pre-simulation.tcl
5882 if {[
file exists $repo_path/Top/$project_name/pre-$s-simulation.tcl]} {
5883 Msg Info "Running $repo_path/Top/$project_name/pre-$s-simulation.tcl"
5884 source Running $repo_path/Top/$project_name/pre-$s-simulation.tcl
5886 current_fileset -simset $s
5887 set sim_dir $main_sim_folder/$s/behav
5888 set sim_output_logfile $sim_dir/xsim/simulate.log
5889 if {([
string tolower $simulator] eq "xsim")} {
5890 set sim_name "xsim:$s"
5892 set simulation_command "launch_simulation $quiet_sim -simset [get_filesets $s]"
5893 if {[
catch $simulation_command log]} {
5896 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5897 lappend failed $sim_name
5902 if {$use_simpass_str == 1} {
5905 set file_desc [open $sim_output_logfile r]
5906 set log [read $file_desc]
5909 Msg Info "Searching for simulation pass string: '$simpass_str'"
5910 if {[
string first $simpass_str $log] == -1} {
5911 Msg CriticalWarning "Simulation failed for $s, error info: '$simpass_str' NOT found!"
5912 lappend failed $sim_name
5915 lappend success $sim_name
5919 lappend success $sim_name
5923 Msg Info "Simulation library path is set to $lib_path."
5925 if {!([
file exists $lib_path])} {
5926 Msg Warning "Could not find simulation library path: $lib_path, $simulator simulation will not work."
5930 if {$simlib_ok == 1} {
5931 set_property "compxlib.${simulator}_compiled_library_dir" [
file normalize $lib_path] [current_project]
5932 launch_simulation -scripts_only -simset [get_filesets $s]
5933 set top_name [get_property TOP $s]
5934 set sim_script [
file normalize $sim_dir/$simulator/]
5935 Msg Info "Adding simulation script location $sim_script for $s..."
5936 lappend sim_scripts $sim_script
5937 dict append sim_dic $sim_script $s
5939 Msg Error "Cannot run $simulator simulations without a valid library path"
5946 if {[
info exists sim_scripts] && $scripts_only == 0} {
5948 Msg Info "Generating IP simulation targets, if any..."
5950 foreach ip [get_ips] {
5951 generate_target simulation -quiet $ip
5956 Msg Info "====== Starting simulations runs ======"
5959 foreach s $sim_scripts {
5961 set cmd ./compile.sh
5962 Msg Info " ************* Compiling: $s ************* "
5964 set sim_name "comp:[dict get $sim_dic $s]"
5966 Msg CriticalWarning "Compilation failed for $s, error info: $::errorInfo"
5967 lappend failed $sim_name
5969 lappend success $sim_name
5971 Msg Info "###################### Compilation log starts ######################"
5972 Msg Info "\n\n$log\n\n"
5973 Msg Info "###################### Compilation log ends ######################"
5975 if {$compile_only == 1} {
5978 if {[
file exists "./elaborate.sh"]} {
5979 set cmd ./elaborate.sh
5980 Msg Info " ************* Elaborating: $s ************* "
5982 set sim_name "elab:[dict get $sim_dic $s]"
5984 Msg CriticalWarning "Elaboration failed for $s, error info: $::errorInfo"
5985 lappend failed $sim_name
5987 lappend success $sim_name
5989 Msg Info "###################### Elaboration log starts ######################"
5990 Msg Info "\n\n$log\n\n"
5991 Msg Info "###################### Elaboration log ends ######################"
5993 set cmd ./simulate.sh
5994 Msg Info " ************* Simulating: $s ************* "
5999 if {$use_simpass_str == 1} {
6000 if {[
string first $simpass_str $log] == -1} {
6004 Msg Debug "Simulation pass string not set, relying on simulator exit code."
6008 set sim_name "sim:[dict get $sim_dic $s]"
6010 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
6011 lappend failed $sim_name
6013 lappend success $sim_name
6015 Msg Info "###################### Simulation log starts ######################"
6016 Msg Info "\n\n$log\n\n"
6017 Msg Info "###################### Simulation log ends ######################"
6022 if {[
llength $success] > 0} {
6023 set successes [
join $success "\n"]
6024 Msg Info "The following simulation sets were successful:\n\n$successes\n\n"
6027 if {[
llength $failed] > 0} {
6028 set failures [
join $failed "\n"]
6029 Msg Error "The following simulation sets have failed:\n\n$failures\n\n"
6031 }
elseif {[
llength $success] > 0} {
6032 Msg Info "All the [
llength $success] compilations, elaborations and simulations were successful."
6035 Msg Info "Simulation done."
6037 Msg Warning "Simulation is not yet supported for [
GetIDEName]."
6044 proc LaunchRTLAnalysis {repo_path {pre_rtl_file ""} {post_rtl_file ""}} {
6046 if {[
file exists $pre_rtl_file]} {
6047 Msg Info "Found pre-rtl Tcl script $pre_rtl_file, executing it..."
6048 source $pre_rtl_file
6050 Msg Info "Starting RTL Analysis..."
6052 synth_design -rtl -name rtl_1
6053 if {[
file exists $post_rtl_file]} {
6054 Msg Info "Found post-rtl Tcl script $post_rtl_file, executing it..."
6055 source $post_rtl_file
6058 Msg Warning "RTL Analysis is not yet supported for [
GetIDEName]."
6071 proc LaunchSynthesis {reset do_create run_folder project_name {repo_path .} {ext_path ""} {njobs 4}} {
6074 Msg Info "Resetting run before launching synthesis..."
6078 source $repo_path/Hog/Tcl/integrated/pre-synthesis.tcl
6080 launch_runs synth_1 -jobs $njobs -dir $run_folder
6082 set prog [get_property PROGRESS [get_runs synth_1]]
6083 set status [get_property STATUS [get_runs synth_1]]
6084 Msg Info "Run: synth_1 progress: $prog, status : $status"
6091 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6092 Msg Info "Git describe set to $describe"
6095 set xci_file [get_property IP_FILE $ip]
6097 set xci_path [
file dirname $xci_file]
6098 set xci_ip_name [
file rootname [
file tail $xci_file]]
6099 foreach rptfile [glob -nocomplain -directory $xci_path *.rpt] {
6100 file copy $rptfile $repo_path/bin/$project_name-$describe/reports
6104 if {$prog ne "100%"} {
6105 Msg Error "Synthesis error, status is: $status"
6109 set project [
file tail [
file rootname $project_name]]
6111 Msg Info "Number of jobs set to $njobs."
6112 set_global_assignment -name NUM_PARALLEL_PROCESSORS $njobs
6116 set describe [
GetHogDescribe [
file normalize $repo_path/Top/$project_name] $repo_path]
6118 set revision [get_current_revision]
6121 set tool_and_command [
split [get_global_assignment -name PRE_FLOW_SCRIPT_FILE] ":"]
6122 set tool [
lindex $tool_and_command 0]
6123 set pre_flow_script [
lindex $tool_and_command 1]
6124 set cmd "$tool -t $pre_flow_script quartus_map $project $revision"
6130 Msg Warning "Can not execute command $cmd"
6131 Msg Warning "LOG: $log"
6133 Msg Info "Pre flow script executed!"
6134 Msg Info "Pre flow script log: \n$log"
6138 if {![is_project_open]} {
6139 Msg Info "Re-opening project file $project_name..."
6140 project_open $project -current_revision
6144 if {[
catch {execute_module -tool ipg -args "--clean"} result]} {
6145 Msg Error "Result: $result\n"
6146 Msg Error "IP Generation failed. See the report file.\n"
6148 Msg Info "IP Generation was successful for revision $revision.\n"
6152 if {[
catch {execute_module -tool map -args "--parallel"} result]} {
6153 Msg Error "Result: $result\n"
6154 Msg Error "Analysis & Synthesis failed. See the report file.\n"
6156 Msg Info "Analysis & Synthesis was successful for revision $revision.\n"
6160 defvar_set -name RWNETLIST_32_64_MIXED_FLOW -value 0
6162 Msg Info "Run SYNTHESIS..."
6163 if {[
catch {run_tool -name {SYNTHESIZE}}]} {
6164 Msg Error "SYNTHESIZE FAILED!"
6166 Msg Info "SYNTHESIZE PASSED!"
6172 set force_rst "-forceOne"
6174 prj_run Synthesis $force_rst
6175 if {[prj_syn] == "synplify"} {
6176 prj_run Translate $force_rst
6179 Msg Error "Impossible condition. You need to run this in an IDE."
6190 proc LaunchVitisBuild {project_name {repo_path .} {stage "presynth"}} {
6191 set proj_name $project_name
6192 set bin_dir [
file normalize "$repo_path/bin"]
6198 set vitis_workspace [
file normalize "$repo_path/Projects/$project_name/vitis_unified"]
6199 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"]
6201 if {![
ExecuteVitisUnifiedCommand $python_script "app_list" [list $vitis_workspace] "Failed to get app list from Vitis Unified" json_output]} {
6202 Msg Error "Failed to get app list from Vitis Unified"
6205 if {[
catch {
package require json}]} {
6206 Msg Error "JSON package not available for parsing Vitis Unified app list"
6209 set json_output_filtered ""
6210 if {[regexp -lineanchor {\{.*\}} $json_output json_output_filtered]} {
6211 set ws_apps [json::json2dict $json_output_filtered]
6213 set ws_apps [json::json2dict $json_output]
6218 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
6220 Msg Error "Impossible condition. You need to run this in a Vitis Unified or Vitis Classic IDE."
6225 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$proj_name] $repo_path] commit version hog_hash hog_ver top_hash top_ver \
6226 libs hashes vers cons_ver cons_hash ext_names ext_hashes xml_hash xml_ver user_ip_repos user_ip_hashes user_ip_vers
6228 if {$commit == 0} {
set commit $this_commit}
6235 foreach app_name [dict keys $ws_apps] {
6236 app config -name $app_name -set build-config Release
6240 WriteGenerics "vitisbuild" $repo_path $proj_name $date $timee $commit $version $top_hash $top_ver $hog_hash $hog_ver $cons_ver $cons_hash $libs \
6241 $vers $hashes $ext_names $ext_hashes $user_ip_repos $user_ip_vers $user_ip_hashes $flavour $xml_ver $xml_hash
6244 foreach app_name [dict keys $ws_apps] {
6247 if {![
ExecuteVitisUnifiedCommand $python_script "build_app" [list $app_name $vitis_workspace] "Failed to build app $app_name"]} {
6248 Msg Error "Failed to build app $app_name"
6252 app build -name $app_name
6256 if {$stage == "presynth"} {
6257 Msg Info "Done building apps for $project_name..."
6260 if {[
info exists ::globalSettings::vitis_only_pass] && $::globalSettings::vitis_only_pass == 1} {
6261 Msg Info "Skipping bin directory creation in vitis_only mode (post-bitstream.tcl handles artifacts)."
6265 Msg Info "Evaluating Hog describe for $project_name..."
6266 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6267 Msg Info "Hog describe set to: $describe"
6268 set dst_dir [
file normalize "$bin_dir/$proj_name\-$describe"]
6269 if {![
file exists $dst_dir]} {
6270 Msg Info "Creating $dst_dir..."
6274 foreach app_name [dict keys $ws_apps] {
6276 set main_file "$repo_path/Projects/$project_name/vitis_unified/$app_name/build/$app_name.elf"
6278 set main_file "$repo_path/Projects/$project_name/vitis_classic/$app_name/Release/$app_name.elf"
6280 set dst_main [
file normalize "$dst_dir/[
file tail $proj_name]\-$app_name\-$describe.elf"]
6282 if {![
file exists $main_file]} {
6283 Msg Error "No Vitis .elf file found. Perhaps there was an issue building it?"
6287 Msg Info "Copying main binary file $main_file into $dst_main..."
6288 file copy -force $main_file $dst_main
6303 proc LaunchHlsBuild {project_name {repo_path .}} {
6304 set proj_name $project_name
6305 set bin_dir [
file normalize "$repo_path/bin"]
6309 set conf_file [
file normalize "$repo_path/Top/$project_name/hog.conf"]
6310 if {![
file exists $conf_file]} {
6311 Msg Error "Configuration file not found: $conf_file"
6314 set properties [
ReadConf $conf_file]
6315 set hls_components [dict filter $properties key {hls:*}]
6317 if {[dict size $hls_components] == 0} {
6318 Msg Info "No HLS components found for project $project_name"
6322 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/HlsCommands.py"]
6330 set ide_name [
string tolower [
lindex $ide_info 0]]
6331 if {$ide_name eq "vivado_vitis_unified"} {
6332 set is_mixed_project 1
6334 set is_mixed_project 0
6337 dict for {hls_key hls_props} $hls_components {
6338 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {
6341 set component_name [string trim $component_name]
6342 Msg Info "Building HLS component: $component_name"
6344 # Read HLS_CONFIG path from hog.conf (mandatory)
6346 if {[dict exists $hls_props hls_config]} {
6347 set hls_cfg_rel [dict get $hls_props hls_config]
6348 } elseif {[dict exists $hls_props HLS_CONFIG]} {
6349 set hls_cfg_rel [dict get $hls_props HLS_CONFIG]
6351 if {$hls_cfg_rel eq ""} {
6352 Msg Error "HLS component '$component_name' missing HLS_CONFIG in hog.conf"
6356 set cfg_file [file normalize "$repo_path/$hls_cfg_rel"]
6357 if {![file exists $cfg_file]} {
6358 Msg Error "HLS config file not found: $cfg_file (from HLS_CONFIG=$hls_cfg_rel)"
6362 set hls_work_dir [file normalize "$repo_path/Projects/$project_name/vitis_unified/$component_name"]
6363 file mkdir $hls_work_dir
6366 Msg Info "Running C synthesis for HLS component '$component_name'..."
6368 ![ExecuteVitisUnifiedCommand $python_script "synthesis" \
6369 [list $component_name $cfg_file $hls_work_dir] \
6370 "Failed to run C synthesis for $component_name"]
6372 Msg Error "C synthesis failed for HLS component '$component_name'"
6377 Msg Info "Running implementation for HLS component '$component_name'..."
6379 ![ExecuteVitisUnifiedCommand $python_script "impl" \
6380 [list $component_name $cfg_file $hls_work_dir] \
6381 "Failed to run implementation for $component_name"]
6383 Msg Error "Implementation failed for HLS component '$component_name'"
6387 # Export VHDL to source tree if VHDL_OUTPUT is set
6389 if {[dict exists $hls_props vhdl_output]} {
6390 set vhdl_output [dict get $hls_props vhdl_output]
6391 } elseif {[dict exists $hls_props VHDL_OUTPUT]} {
6392 set vhdl_output [dict get $hls_props VHDL_OUTPUT]
6394 if {$vhdl_output ne ""} {
6395 set vhdl_output_dir [file normalize "$repo_path/$vhdl_output"]
6396 Msg Info "Exporting VHDL for '$component_name' to $vhdl_output_dir..."
6398 ![ExecuteVitisUnifiedCommand $python_script "export_rtl" \
6399 [list $component_name $hls_work_dir $vhdl_output_dir vhdl] \
6400 "Failed to export VHDL for $component_name"]
6402 Msg Warning "Could not export VHDL for HLS component '$component_name'"
6406 # Export Verilog to source tree if VERILOG_OUTPUT is set
6407 set verilog_output ""
6408 if {[dict exists $hls_props verilog_output]} {
6409 set verilog_output [dict get $hls_props verilog_output]
6410 } elseif {[dict exists $hls_props VERILOG_OUTPUT]} {
6411 set verilog_output [dict get $hls_props VERILOG_OUTPUT]
6413 if {$verilog_output ne ""} {
6414 set verilog_output_dir [file normalize "$repo_path/$verilog_output"]
6415 Msg Info "Exporting Verilog for '$component_name' to $verilog_output_dir..."
6417 ![ExecuteVitisUnifiedCommand $python_script "export_rtl" \
6418 [list $component_name $hls_work_dir $verilog_output_dir verilog] \
6419 "Failed to export Verilog for $component_name"]
6421 Msg Warning "Could not export Verilog for HLS component '$component_name'"
6425 # Export IP catalog ZIP to source tree if IP_OUTPUT is set
6427 if {[dict exists $hls_props ip_output]} {
6428 set ip_output [dict get $hls_props ip_output]
6429 } elseif {[dict exists $hls_props IP_OUTPUT]} {
6430 set ip_output [dict get $hls_props IP_OUTPUT]
6432 if {$ip_output ne ""} {
6433 set ip_output_dir [file normalize "$repo_path/$ip_output"]
6434 Msg Info "Exporting IP catalog for '$component_name' to $ip_output_dir..."
6436 ![ExecuteVitisUnifiedCommand $python_script "export_ip" \
6437 [list $component_name $hls_work_dir $ip_output_dir] \
6438 "Failed to export IP for $component_name"]
6440 Msg Error "Could not export IP for HLS component '$component_name'. Make sure package.output.format=ip_catalog is set in hls_config.cfg."
6444 # Collect reports into bin/ for CI and release notes
6445 Msg Info "Evaluating Hog describe for $project_name..."
6446 set describe [GetHogDescribe [file normalize ./Top/$project_name] $repo_path]
6447 Msg Info "Hog describe set to: $describe"
6448 set dst_dir [file normalize "$bin_dir/$proj_name\-$describe"]
6449 if {![file exists $dst_dir]} {
6450 Msg Info "Creating $dst_dir..."
6454 # Mixed projects group HLS components under bin/<proj>/vitis_hls/; pure
6455 # vitis_unified projects place them directly under bin/<proj>/
6456 if {$is_mixed_project} {
6457 set hls_out_dir [file normalize "$dst_dir/vitis_hls"]
6459 set hls_out_dir $dst_dir
6462 Msg Info "Collecting HLS reports for component '$component_name'..."
6464 ![ExecuteVitisUnifiedCommand $python_script "collect_reports" \
6465 [list $component_name $hls_work_dir $hls_out_dir] \
6466 "Failed to collect HLS reports for $component_name"]
6468 Msg Warning "No reports found for HLS component '$component_name'"
6471 # Generate markdown summary files (utilization.txt, timing_ok.txt /
6472 # timing_error.txt) under <hls_out_dir>/<component>/. File names mirror the
6473 # Vivado convention so the existing CI logic (release notes assembly and
6474 # timing-failure detection) works for HLS components too
6475 Msg Info "Generating HLS release-notes summary for component '$component_name'..."
6477 ![ExecuteVitisUnifiedCommand $python_script "release_notes" \
6478 [list $component_name $hls_work_dir $hls_out_dir] \
6479 "Failed to generate HLS summary for $component_name"]
6481 Msg Warning "Could not generate HLS summary for component '$component_name'"
6484 Msg Info "HLS component '$component_name' built successfully"
6489 if {!$is_mixed_project && [
info exists dst_dir] && [
file isdirectory $dst_dir]} {
6490 set versions_file [
file normalize "$dst_dir/versions.txt"]
6491 Msg Info "Generating top-level versions.txt for pure-HLS project at $versions_file"
6494 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$project_name] $repo_path] \
6495 commit version hog_hash hog_ver top_hash top_ver \
6496 libs hashes vers cons_ver cons_hash \
6497 ext_names ext_hashes xml_hash xml_ver \
6498 user_ip_repos user_ip_hashes user_ip_vers
6499 if {$commit == 0} {
set commit [
GetSHA]}
6503 set fh [open $versions_file "w"]
6504 puts $fh "## $proj_name Version Table\n"
6505 puts $fh "| **File set** | **Commit SHA** | **Version** |"
6506 puts $fh "| --- | --- | --- |"
6507 puts $fh "| Global | $commit | $version_str |"
6508 puts $fh "| Top Directory | $top_hash | $top_ver_str |"
6509 puts $fh "| Hog | $hog_hash | $hog_ver_str |"
6510 foreach l $libs v $vers h $hashes {
6512 puts $fh "| **Lib:** $l | $h | $v_str |"
6518 Msg Warning "Could not generate top-level versions.txt for pure-HLS project: $err"
6522 Msg Info "Done building HLS components for $project_name"
6538 proc LaunchHlsSimulation {project_name {repo_path .} {hls_simsets {}}} {
6541 set conf_file [
file normalize "$repo_path/Top/$project_name/hog.conf"]
6542 if {![
file exists $conf_file]} {
6543 Msg Error "Configuration file not found: $conf_file"
6546 set properties [
ReadConf $conf_file]
6547 set hls_components [dict filter $properties key {hls:*}]
6549 if {[dict size $hls_components] == 0} {
6550 Msg Info "No HLS components found for project $project_name"
6556 set targeted_mode [
expr {[llength $hls_simsets] > 0}]
6557 set targets [dict create]
6558 foreach entry $hls_simsets {
6559 if {[regexp {^(csim|cosim):(.+)$} $entry -> sim_type comp_name]} {
6560 dict lappend targets $comp_name $sim_type
6562 Msg Error "Invalid HLS simset format '$entry'. Expected 'csim:<component>' or 'cosim:<component>'."
6563 Msg Info "Available HLS components in hog.conf:"
6564 dict for {hls_key hls_props} $hls_components {
6565 if {[regexp {^hls:(.+)$} $hls_key -> cname]} {
6566 Msg Info " - csim:[string trim $cname]"
6567 Msg Info " - cosim:[string trim $cname]"
6574 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/HlsCommands.py"]
6576 dict for {hls_key hls_props} $hls_components {
6577 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {
6580 set component_name [string trim $component_name]
6582 # In targeted mode, skip components not in the request
6583 if {$targeted_mode && ![dict exists $targets $component_name]} {
6587 # Read HLS_CONFIG path from hog.conf (mandatory)
6589 if {[dict exists $hls_props hls_config]} {
6590 set hls_cfg_rel [dict get $hls_props hls_config]
6591 } elseif {[dict exists $hls_props HLS_CONFIG]} {
6592 set hls_cfg_rel [dict get $hls_props HLS_CONFIG]
6594 if {$hls_cfg_rel eq ""} {
6595 Msg Error "HLS component '$component_name' missing HLS_CONFIG in hog.conf"
6599 set cfg_file [file normalize "$repo_path/$hls_cfg_rel"]
6600 if {![file exists $cfg_file]} {
6601 Msg Error "HLS config file not found: $cfg_file (from HLS_CONFIG=$hls_cfg_rel)"
6605 set hls_work_dir [file normalize "$repo_path/Projects/$project_name/vitis_unified/$component_name"]
6606 file mkdir $hls_work_dir
6608 # Determine which simulations to run
6609 if {$targeted_mode} {
6610 # User explicitly requested these sim types via -simset
6611 set sim_types [dict get $targets $component_name]
6612 set run_csim [expr {"csim" in $sim_types}]
6613 set run_cosim [expr {"cosim" in $sim_types}]
6615 # Validate: check if the requested sim is enabled in hog.conf
6616 foreach st $sim_types {
6618 if {[dict exists $hls_props $st]} {
6619 set flag_val [dict get $hls_props $st]
6620 } elseif {[dict exists $hls_props [string toupper $st]]} {
6621 set flag_val [dict get $hls_props [string toupper $st]]
6623 set is_enabled [expr {[string tolower $flag_val] eq "true" || $flag_val eq "1"}]
6625 set upper_st [string toupper $st]
6626 Msg Warning "HLS simulation '$st' requested for '$component_name' but\
6627 $upper_st is not enabled in \[hls:$component_name\] section of\
6628 hog.conf. Running it anyway."
6629 Msg Info "To enable it permanently, add '$upper_st=true' under\
6630 \[hls:$component_name\] in Top/$project_name/hog.conf"
6634 # Default mode: read flags from hog.conf
6637 foreach {key_lower key_upper} {csim CSIM cosim COSIM} {
6639 if {[dict exists $hls_props $key_lower]} {
6640 set val [dict get $hls_props $key_lower]
6641 } elseif {[dict exists $hls_props $key_upper]} {
6642 set val [dict get $hls_props $key_upper]
6644 if {[string tolower $val] eq "true" || $val eq "1"} {
6645 set run_$key_lower 1
6649 if {!$run_csim && !$run_cosim} {
6650 Msg Info "No HLS simulations enabled for '$component_name' (set CSIM=true and/or COSIM=true in \[hls:$component_name\] in hog.conf)"
6655 Msg Info "Running HLS simulations for component: $component_name"
6659 Msg Info "Running C simulation for HLS component '$component_name'..."
6661 ![ExecuteVitisUnifiedCommand $python_script "csim" \
6662 [list $component_name $cfg_file $hls_work_dir] \
6663 "Failed to run C simulation for $component_name"]
6665 Msg Error "C simulation failed for HLS component '$component_name'"
6670 # COSIM (auto-run synthesis if needed)
6672 set syn_done_marker [file normalize "$hls_work_dir/hls/syn"]
6673 if {![file exists $syn_done_marker]} {
6674 Msg Info "C synthesis output not found for '$component_name', running synthesis automatically before co-simulation..."
6676 ![ExecuteVitisUnifiedCommand $python_script "synthesis" \
6677 [list $component_name $cfg_file $hls_work_dir] \
6678 "Failed to run C synthesis for $component_name"]
6680 Msg Error "C synthesis failed for HLS component '$component_name', cannot proceed with co-simulation"
6685 Msg Info "Running C/RTL co-simulation for HLS component '$component_name'..."
6687 ![ExecuteVitisUnifiedCommand $python_script "cosim" \
6688 [list $component_name $cfg_file $hls_work_dir] \
6689 "Failed to run co-simulation for $component_name"]
6691 Msg Error "C/RTL co-simulation failed for HLS component '$component_name'"
6696 Msg Info "HLS simulations completed for '$component_name'"
6699 Msg Info "Done with HLS simulations for $project_name"
6707 proc GetProcFromProps {repo_path props platform} {
6708 if {[dict exists $props "platform:$platform" "BIF"]} {
6709 set bif_file [dict get $props "platform:$platform" "BIF"]
6711 set bif_file "$repo_path/$bif_file"
6715 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
6725 proc GetBifFromProps {repo_path props platform} {
6726 if {[dict exists $props "platform:$platform" "BIF"]} {
6727 set bif_file [dict get $props "platform:$platform" "BIF"]
6729 set bif_file "$repo_path/$bif_file"
6733 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
6742 proc GetPartFromProps {props} {
6743 if {[dict exists $props "main" "PART"]} {
6744 return [
string tolower [dict get $props "main" "PART"]]
6746 Msg Error "Part number not found in properties"
6755 proc GetArchFromPart {part} {
6756 set part [
string tolower $part]
6758 if {[
string match "xczu*" $part] || [
string match "xqzu*" $part]} {
6760 }
elseif {[
string match "xc7z*" $part] || [
string match "xq7z*" $part]} {
6763 [
string match "xcve*" $part] || [
string match "xcvc*" $part] ||
6764 [
string match "xcvp*" $part] || [
string match "xcvm*" $part] ||
6765 [
string match "xqve*" $part] || [
string match "xck26*" $part]
6768 }
elseif {[
string match "xc*" $part] || [
string match "xq*" $part]} {
6772 Msg CriticalWarning "Unknown part number: $part"
6781 proc GetAppsFromProps {props {list_names 0}} {
6782 set prop_apps [dict filter $props key {app:*}]
6783 set apps [dict create]
6784 set app_names [list]
6786 dict for {app_key app_value} $prop_apps {
6787 if {[regexp {^app:(.+)$} $app_key -> app_name]} {
6788 set app_name [string trim [string tolower $app_name]]
6789 # Convert only the keys of the inner dictionary to lowercase
6790 set app_value_lower [dict create]
6791 dict for {key value} $app_value {
6792 dict set app_value_lower [string tolower $key] $value
6794 dict set apps $app_name $app_value_lower
6795 lappend app_names $app_name
6798 if {$list_names eq 1} {
6809 proc GetPlatformsFromProps {props {list_names 0} {lower_case 0}} {
6810 set platforms [dict create]
6811 set platform_names [list]
6812 set prop_platforms [dict filter $props key {platform:*}]
6814 dict for {platform_key platform_value} $prop_platforms {
6815 if {[regexp {^platform:(.+)$} $platform_key -> platform_name]} {
6816 if {$lower_case == 1} {
6817 set platform_name [string trim [string tolower $platform_name]]
6819 set platform_name [string trim $platform_name]
6821 dict set platforms $platform_name $platform_value
6822 lappend platform_names $platform_name
6825 if {$list_names eq 1} {
6826 return $platform_names
6841 proc GenerateBootArtifacts {properties repo_path proj_dir bin_dir proj_name describe bitfile mmi_file} {
6842 set elf_list [glob -nocomplain "$bin_dir/*.elf"]
6846 if {[
llength $elf_list] == 0} {
6847 Msg Warning "No ELF files found in $bin_dir, skipping generation of boot artifacts"
6851 if {![
file exists $bitfile]} {
6852 Msg Warning "Bitfile $bitfile does not exist, skipping generation of boot artifacts"
6856 Msg Info "Generating boot artifacts for $proj_name..."
6857 Msg Info "Found apps: $apps"
6858 Msg Info "Found platforms: $platforms"
6862 foreach elf_file $elf_list {
6863 set elf_name [
file rootname [
file tail $elf_file]]
6864 Msg Info "Found elf name: $elf_name"
6865 Msg Info "Removing $describe from elf"
6868 if {[regexp "^(.+)-(.+)-$describe\$" $elf_name -> project_name elf_app]} {
6869 set elf_app [
string trim [
string tolower $elf_app]]
6870 Msg Info "Found elf_app: $elf_app"
6872 Msg Error "Could not extract app name from elf file: $elf_name"
6875 Msg Info "Removed project name ($project_name) and $describe from elf"
6877 set app_conf [dict get $apps $elf_app]
6878 set plat [dict get $app_conf "platform"]
6879 set app_proc [dict get $app_conf "proc"]
6882 if {[regexp -nocase {microblaze|risc} $app_proc]} {
6883 Msg Info "Detected soft processor ($app_proc) for $elf_app, updating bitstream memory with ELF file..."
6889 if {$proc_cell eq ""} {
6890 set proc_map_file [
file normalize "$proj_dir/vitis_classic/${plat}.PROC_MAP"]
6892 if {[dict exists $proc_map $app_proc]} {
6893 set proc_cell [dict get $proc_map $app_proc]
6894 Msg Info "Processor $app_proc not found in $mmi_file, using $proc_map_file instead."
6896 Msg Error "Could not determine the instance path of processor '$app_proc' for $elf_app, \
6897 neither from $mmi_file nor from $proc_map_file. \
6898 Check that proc= in the \[app:$elf_app\] section of hog.conf matches the processor instance name."
6902 Msg Info "Updating memory at processor cell: $proc_cell"
6904 set update_mem_cmd "updatemem -force -meminfo $mmi_file -data $elf_file -bit $bitfile -proc $proc_cell -out $bitfile"
6905 set ret [
catch {
exec -ignorestderr {*}$update_mem_cmd >@ stdout} result]
6907 Msg Error "Error updating memory for $elf_app: $result"
6909 Msg Info "Done updating memory for $elf_app"
6911 Msg Info "Detected hard processor ($app_proc) for $elf_app. Make sure the .elf file is defined in the platform ($plat)\
6912 .bif file to be included in the bootable binary image (.bin) generation."
6918 foreach plat $platforms {
6920 if {$bif_file != ""} {
6921 Msg Info "Generating bootable binary image (.bin) for $plat"
6923 Msg Info "Architecture: $arch"
6924 Msg Info "BIF file: $bif_file"
6925 if {$arch eq "unknown"} {
6926 Msg CriticalWarning "Skipping bootable image (.bin) generation for $plat: \
6927 could not determine bootgen architecture from the FPGA part."
6930 set bootgen_cmd "bootgen -arch $arch -image $bif_file -o i $bin_dir/$proj_name-$plat-$describe.bin -w on"
6931 set ret [
catch {
exec -ignorestderr {*}$bootgen_cmd >@ stdout} result]
6933 Msg Error "Error generating bootable binary image (.bin) for $plat: $result"
6935 Msg Info "Done generating bootable binary image (.bin) for $plat"
6947 proc GenerateStandaloneXciTargets {} {
6952 foreach xci [get_files -quiet *.xci] {
6953 set xci_file [get_files -quiet $xci]
6954 if {$xci_file eq ""} {
6959 catch {
set parent [get_property -quiet PARENT_COMPOSITE_FILE $xci_file]}
6960 if {$parent ne ""} {
6961 Msg Debug "Skipping [
file tail $xci], it is generated by its parent [
file tail $parent]"
6965 Msg Info "Generating targets for standalone IP [
file tail $xci]..."
6966 if {[
catch {generate_target all $xci_file} err]} {
6967 Msg CriticalWarning "Failed to generate targets for $xci: $err"
6981 proc GetProcInstPathFromMmi {mmi_file app_proc} {
6982 if {![
file exists $mmi_file]} {
6983 Msg Debug "MMI file not found: $mmi_file"
6987 set f [open $mmi_file "r"]
6988 set mmi_content [read $f]
6991 set inst_paths [list]
6992 foreach {match inst_path} [regexp -all -inline {<Processor[^>]*InstPath\s*=\s*"([^"]+)"} $mmi_content] {
6993 if {[lsearch -exact $inst_paths $inst_path] < 0} {
6994 lappend inst_paths $inst_path
6997 Msg Debug "Processor instance paths found in $mmi_file: $inst_paths"
6999 foreach inst_path $inst_paths {
7000 if {[
string equal -nocase [
file tail $inst_path] $app_proc]} {
7006 if {[
llength $inst_paths] == 1} {
7007 set inst_path [
lindex $inst_paths 0]
7008 Msg Info "Processor $app_proc does not match [
file tail $inst_path], \
7009 using the only processor found in the MMI file: $inst_path"
7020 proc ReadProcMap {proc_map_file} {
7021 set proc_map [dict create]
7022 if {[
file exists $proc_map_file]} {
7023 set f [open $proc_map_file "r"]
7024 while {[
gets $f line] >= 0} {
7025 Msg Debug "Line: $line"
7026 if {[regexp {^(\S+)\s+(.+)$} $line -> key value]} {
7027 Msg Debug "Found key: $key, value: $value in proc map file"
7028 dict set proc_map $key $value
7042 proc ListProjects {{repo_path .} {print 1} {ret_conf 0} {silent 0}} {
7043 set top_path [
file normalize $repo_path/Top]
7044 set confs [
findFiles [
file normalize $top_path] hog.conf]
7046 set confs [lsort $confs]
7050 set p [
Relative $top_path [
file dirname $c]]
7053 if {$description eq "test"} {
7054 set description " - Test project"
7055 }
elseif {$description ne ""} {
7056 set description " - $description"
7059 if {$print == 1 || $description ne " - Test project"} {
7061 set g [
file dirname $p]
7064 if {$silent == 0} {
Msg Status ""}
7072 if {$ret_conf == 0} {
7084 proc Md5Sum {file_name} {
7085 if {!([
file exists $file_name])} {
7086 Msg Warning "Could not find $file_name."
7089 if {[
catch {
package require md5 2.0.7} result]} {
7090 Msg Warning "Tcl package md5 version 2.0.7 not found ($result), will use command line..."
7091 set hash [
lindex [
Execute md5sum $file_name] 0]
7093 set file_hash [
string tolower [md5::md5 -hex -file $file_name]]
7107 proc MergeDict {dict0 dict1 {remove_duplicates 1}} {
7108 set outdict [dict merge $dict1 $dict0]
7109 foreach key [dict keys $dict1] {
7110 if {[dict exists $dict0 $key]} {
7111 set temp_list [dict get $dict1 $key]
7112 foreach item $temp_list {
7114 if {[
IsInList $item [
DictGet $outdict $key]] == 0 || $remove_duplicates == 0} {
7116 dict lappend outdict $key $item
7128 proc MoveElementToEnd {inputList element} {
7129 set index [lsearch $inputList $element]
7131 set inputList [
lreplace $inputList $index $index]
7132 lappend inputList $element
7141 proc OpenProject {project_file repo_path} {
7143 open_project $project_file
7145 set project_folder [
file dirname $project_file]
7146 set project [
file tail [
file rootname $project_file]]
7147 if {[
file exists $project_folder]} {
7149 if {![is_project_open]} {
7150 Msg Info "Opening existing project file $project_file..."
7151 project_open $project -current_revision
7154 Msg Error "Project directory not found for $project_file."
7158 Msg Info "Opening existing project file $project_file..."
7160 open_project -file $project_file -do_backup_on_convert 1 -backup_file {./Projects/$project_file.zip}
7162 Msg Info "Opening existing project file $project_file..."
7163 prj_project open $project_file
7165 Msg Error "This IDE is currently not supported by Hog. Exiting!"
7172 return $tcl_platform(platform)
7182 proc ParseJSON {JSON_FILE JSON_KEY} {
7183 set result [
catch {
package require Tcl 8.4} TclFound]
7184 if {"$result" != "0"} {
7185 Msg CriticalWarning "Cannot find Tcl package version equal or higher than 8.4.\n $TclFound\n Exiting"
7189 set result [
catch {
package require json} JsonFound]
7190 if {"$result" != "0"} {
7191 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
7194 set JsonDict [json::json2dict $JSON_FILE]
7195 set result [
catch {dict get $JsonDict $JSON_KEY} RETURNVALUE]
7196 if {"$result" != "0"} {
7197 Msg CriticalWarning "Cannot find $JSON_KEY in $JSON_FILE\n Exiting"
7210 proc ProjectExists {project {repo_path .}} {
7211 set index [lsearch -exact [
ListProjects $repo_path 0] $project]
7217 Msg Warning "Project not found. Available projects in $repo_path are"
7220 Msg Error "Project $project not found in repository $repo_path"
7231 proc ReadConf {file_name} {
7232 if {[
catch {
package require inifile 0.2.3} ERROR]} {
7233 Msg Error "Could not find inifile package version 0.2.3 or higher.\n
7234 To use ghdl, libero or diamond with Hog, you need to install the tcllib package\n
7235 You can install it with 'sudo apt install tcllib' on Debian/Ubuntu or 'sudo dnf install tcllib' on Fedora/RedHat/CentOs."
7239 ::ini::commentchar "#"
7240 set f [::ini::open $file_name]
7241 set properties [dict create]
7242 foreach sec [::ini::sections $f] {
7244 if {$new_sec == "files"} {
7247 set key_pairs [::ini::get $f $sec]
7249 regsub -all {\{\"} $key_pairs "\{" key_pairs
7250 regsub -all {\"\}} $key_pairs "\}" key_pairs
7252 dict set properties $new_sec [dict create {*}$key_pairs]
7265 proc ReadExtraFileList {extra_file_name} {
7266 set extra_file_dict [dict create]
7267 if {[
file exists $extra_file_name]} {
7268 set file [open $extra_file_name "r"]
7269 set file_data [read $file]
7272 set data [
split $file_data "\n"]
7273 foreach line $data {
7274 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
7275 set ip_and_md5 [regexp -all -inline {\S+} $line]
7276 dict lappend extra_file_dict "[
lindex $ip_and_md5 0]" "[
lindex $ip_and_md5 1]"
7280 return $extra_file_dict
7291 proc ExpandHlsConfigFiles {cfg_file} {
7293 if {![
file exists $cfg_file] || ![
file isfile $cfg_file]} {
7296 set cfg_dir [
file normalize [
file dirname $cfg_file]]
7298 if {[
catch {
set fp [open $cfg_file r]} err]} {
7299 Msg Warning "ExpandHlsConfigFiles: cannot open $cfg_file: $err"
7302 set lines [
split [read $fp] "\n"]
7305 foreach line $lines {
7306 if {[regexp {^[\t\s]*$} $line]} {continue}
7307 if {[regexp {^[\t\s]*\#} $line]} {continue}
7308 if {[regexp {^\s*\[.*\]\s*$} $line]} {continue}
7309 if {![regexp {^\s*[^=\s]+\s*=\s*(.+?)\s*$} $line -> value]} {continue}
7311 foreach tok [regexp -all -inline {\S+} $value] {
7312 if {[
file pathtype $tok] eq "absolute"} {
7313 set candidate [
file normalize $tok]
7315 set candidate [
file normalize [
file join $cfg_dir $tok]]
7317 if {[
file isfile $candidate]} {
7318 lappend out $candidate
7319 }
elseif {[
file isdirectory $candidate]} {
7320 set stack [list $candidate]
7321 while {[
llength $stack] > 0} {
7322 set d [
lindex $stack 0]
7323 set stack [
lrange $stack 1 end]
7324 foreach f [glob -nocomplain -directory $d -types f *] {
7325 lappend out [
file normalize $f]
7327 foreach sub [glob -nocomplain -directory $d -types d *] {
7334 return [lsort -unique $out]
7344 proc GetHlsConfigsFromProjConf {conf_file repo_path} {
7345 set out [dict create]
7346 if {![
file exists $conf_file]} {
return $out}
7347 if {[
catch {
set properties [
ReadConf $conf_file]} err]} {
7348 Msg Debug "GetHlsConfigsFromProjConf: cannot parse $conf_file: $err"
7351 set hls_components [dict filter $properties key {hls:*}]
7352 dict for {hls_key hls_props} $hls_components {
7353 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {continue}
7354 set component_name [string trim $component_name]
7356 if {[dict exists $hls_props hls_config]} {
7357 set cfg_rel [dict get $hls_props hls_config]
7358 } elseif {[dict exists $hls_props HLS_CONFIG]} {
7359 set cfg_rel [dict get $hls_props HLS_CONFIG]
7361 if {$cfg_rel eq ""} {continue}
7362 set cfg_abs [file normalize "$repo_path/$cfg_rel"]
7363 if {![file exists $cfg_abs]} {
7364 Msg CriticalWarning "HLS component '$component_name': HLS_CONFIG=$cfg_rel does not exist on disk."
7367 dict set out $component_name $cfg_abs
7389 proc ReadListFile {args} {
7392 if {[
catch {
package require cmdline} ERROR]} {
7393 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
7399 {lib.arg "" "The name of the library files will be added to, if not given will be extracted from the file name."}
7400 {fileset.arg "" "The name of the library, from the main list file"}
7401 {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."}
7402 {print_log "If set, will use PrintFileTree for the VIEW directive"}
7403 {indent.arg "" "Used to indent files with the VIEW directive"}
7406 set usage "USAGE: ReadListFile \[options\] <list file> <path>"
7407 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
7413 set list_file [
lindex $args 0]
7414 set path [
lindex $args 1]
7415 set sha_mode $options(sha_mode)
7416 set lib $options(lib)
7417 set fileset $options(fileset)
7418 set print_log $options(print_log)
7419 set indent $options(indent)
7421 if {$sha_mode == 1} {
7422 set sha_mode_opt "-sha_mode"
7427 if {$print_log == 1} {
7428 set print_log_opt "-print_log"
7430 set print_log_opt ""
7435 set lib [
file rootname [
file tail $list_file]]
7437 set fp [open $list_file r]
7438 set file_data [read $fp]
7440 set list_file_ext [
file extension $list_file]
7441 switch $list_file_ext {
7443 if {$fileset eq ""} {
7449 set fileset "constrs_1"
7452 set fileset "sources_1"
7456 set libraries [dict create]
7457 set filesets [dict create]
7458 set properties [dict create]
7460 set data [
split $file_data "\n"]
7462 set n [
llength $data]
7464 if {$print_log == 1} {
7465 if {$indent eq ""} {
7466 set list_file_rel [
file tail $list_file]
7467 Msg Status "\n$list_file_rel"
7471 Msg Debug "$n lines read from $list_file."
7474 foreach line $data {
7476 if {![regexp {^[\t\s]*$} $line] & ![regexp {^[\t\s]*\#} $line]} {
7477 set file_and_prop [regexp -all -inline {\S+} $line]
7478 set srcfile [
lindex $file_and_prop 0]
7479 set srcfile "$path/$srcfile"
7481 set srcfiles [glob -nocomplain $srcfile]
7484 if {$srcfiles != $srcfile && ![
string equal $srcfiles ""]} {
7485 Msg Debug "Wildcard source expanded from $srcfile to $srcfiles"
7487 if {![
file exists $srcfile]} {
7488 if {$print_log == 0} {
7489 Msg CriticalWarning "File: $srcfile (from list file: $list_file) does not exist."
7495 foreach vhdlfile $srcfiles {
7496 if {[
file exists $vhdlfile]} {
7497 set vhdlfile [
file normalize $vhdlfile]
7498 set extension [
file extension $vhdlfile]
7500 set prop [
lrange $file_and_prop 1 end]
7503 set library [
lindex [regexp -inline {\ylib\s*=\s*(.+?)\y.*} $prop] 1]
7504 if {$library == ""} {
7508 if {$extension == $list_file_ext} {
7511 set ref_path [
lindex [regexp -inline {\ypath\s*=\s*(\S+).*} $prop] 1]
7512 if {$ref_path eq ""} {
7515 set ref_path [
file normalize $path/$ref_path]
7517 Msg Debug "List file $vhdlfile found in list file, recursively opening it using path \"$ref_path\"..."
7518 if {$print_log == 1} {
7519 if {[
file normalize $last_printed] ne [
file normalize $vhdlfile]} {
7520 Msg Status "$indent Inside [
file tail $vhdlfile]:"
7524 lassign [
ReadListFile {*}"-indent \" $indent\" -lib $library -fileset $fileset $sha_mode_opt $print_log_opt $vhdlfile $ref_path"] l p fs
7526 set properties [
MergeDict $p $properties]
7528 }
elseif {[lsearch {.src .sim .con ReadExtraFileList} $extension] >= 0} {
7530 Msg Error "$vhdlfile cannot be included into $list_file, $extension files must be included into $extension files."
7533 regsub -all " *= *" $prop "=" prop
7537 if {[
string first "lib=" $p] == -1} {
7539 set pos [
string first "=" $p]
7543 set prop_name [
string range $p 0 [
expr {$pos - 1}]]
7546 if {$list_file_ext eq ".ipb"} {
7547 dict lappend properties $vhdlfile $path/$p
7549 dict lappend properties $vhdlfile $p
7551 Msg Debug "Adding property $p to $vhdlfile..."
7552 }
elseif {$list_file_ext != ".ipb"} {
7553 Msg Warning "Setting Property $p is not supported for file $vhdlfile or it is already its default. \
7558 if {[lsearch {.xci .ip .bd .xcix} $extension] >= 0} {
7560 set lib_name "ips.src"
7561 }
elseif {[
IsInList $extension {.vhd .vhdl}] || $list_file_ext == ".sim"} {
7563 if {![
IsInList $extension {.vhd .vhdl}]} {
7564 set lib_name "others.sim"
7566 set lib_name "$library$list_file_ext"
7568 }
elseif {$list_file_ext == ".con"} {
7569 set lib_name "sources.con"
7570 }
elseif {$list_file_ext == ".ipb"} {
7571 set lib_name "xml.ipb"
7572 }
elseif {[
IsInList $list_file_ext {.src}] && [
IsInList $extension {.c .cpp .h .hpp}]} {
7574 set lib_name "$library$list_file_ext"
7577 set lib_name "others.src"
7580 Msg Debug "Appending $vhdlfile to $lib_name list..."
7581 dict lappend libraries $lib_name $vhdlfile
7582 if {$sha_mode != 0 && [
file type $vhdlfile] eq "link"} {
7585 dict lappend libraries $lib_name $real_file
7586 Msg Debug "File $vhdlfile is a soft link, also adding the real file: $real_file"
7594 if {$list_file_ext eq ".src" && $extension eq ".cfg"} {
7596 if {$print_log == 1 && [
llength $hls_extras] > 0} {
7597 Msg Status "$indent Inside [
file tail $vhdlfile] (HLS auto-expand):"
7599 set n_extras [
llength $hls_extras]
7601 foreach hls_extra $hls_extras {
7603 if {$hls_extra eq $vhdlfile} {continue}
7604 Msg Debug "HLS cfg expansion: adding $hls_extra (from $vhdlfile) to $lib_name"
7605 if {$print_log == 1} {
7606 if {$i == $n_extras} {
set pad "└──"}
else {
set pad "├──"}
7607 set rel [
Relative [
file dirname $vhdlfile] $hls_extra]
7608 Msg Status "$indent $pad $rel"
7610 dict lappend libraries $lib_name $hls_extra
7611 if {$sha_mode != 0 && [
file type $hls_extra] eq "link"} {
7613 dict lappend libraries $lib_name $real_extra
7614 Msg Debug "HLS expanded file $hls_extra is a soft link, also adding $real_extra"
7621 if {[dict exists $filesets $fileset] == 0} {
7623 Msg Debug "Adding $fileset to the fileset dictionary..."
7624 Msg Debug "Adding library $lib_name to fileset $fileset..."
7625 dict set filesets $fileset $lib_name
7629 Msg Debug "Adding library $lib_name to fileset $fileset..."
7630 dict lappend filesets $fileset $lib_name
7636 Msg CriticalWarning "File $vhdlfile not found."
7642 if {$sha_mode != 0} {
7644 if {$list_file_ext eq ".ipb"} {
7645 set sha_lib "xml.ipb"
7647 set sha_lib $lib$list_file_ext
7649 dict lappend libraries $sha_lib [
file normalize $list_file]
7650 if {[
file type $list_file] eq "link"} {
7653 dict lappend libraries $lib$list_file_ext $real_file
7654 Msg Debug "List file $list_file is a soft link, also adding the real file: $real_file"
7657 return [list $libraries $properties $filesets]
7666 proc Relative {base dst {quiet 0}} {
7667 if {![
string equal [
file pathtype $base] [
file pathtype $dst]]} {
7669 Msg CriticalWarning "Unable to compute relation for paths of different path types: [
file pathtype $base] vs. [
file pathtype $dst], ($base vs. $dst)"
7674 set base [
file normalize [
file join [
pwd] $base]]
7675 set dst [
file normalize [
file join [
pwd] $dst]]
7678 set base [
file split $base]
7679 set dst [
file split $dst]
7681 while {[
string equal [
lindex $dst 0] [
lindex $base 0]]} {
7682 set dst [
lrange $dst 1 end]
7683 set base [
lrange $base 1 end]
7684 if {![
llength $dst]} {break}
7687 set dstlen [
llength $dst]
7688 set baselen [
llength $base]
7690 if {($dstlen == 0) && ($baselen == 0)} {
7693 while {$baselen > 0} {
7694 set dst [
linsert $dst 0 ..]
7697 set dst [
eval [
linsert $dst 0 file join]]
7708 proc RelativeLocal {pathName filePath} {
7709 if {[
string first [
file normalize $pathName] [
file normalize $filePath]] != -1} {
7710 return [
Relative $pathName $filePath]
7721 proc RemoveDuplicates {mydict} {
7722 set new_dict [dict create]
7723 foreach key [dict keys $mydict] {
7724 set values [
DictGet $mydict $key]
7725 foreach value $values {
7726 set idxs [
lreverse [
lreplace [lsearch -exact -all $values $value] 0 0]]
7728 set values [
lreplace $values $idx $idx]
7731 dict set new_dict $key $values
7742 proc ResetRepoFiles {reset_file} {
7743 if {[
file exists $reset_file]} {
7744 Msg Info "Found $reset_file, opening it..."
7745 set fp [open $reset_file r]
7746 set wild_cards [lsearch -all -inline -not -regexp [
split [read $fp] "\n"] "^ *$"]
7748 Msg Info "Found the following files/wild cards to restore if modified: $wild_cards..."
7749 foreach w $wild_cards {
7751 if {[
llength $mod_files] > 0} {
7752 Msg Info "Found modified $w files: $mod_files, will restore them..."
7755 Msg Info "No modified $w files found."
7766 proc RestoreModifiedFiles {{repo_path "."} {pattern "."}} {
7769 set ret [
Git checkout $pattern]
7780 proc SearchHogProjects {dir} {
7781 set projects_list {}
7782 if {[
file exists $dir]} {
7783 if {[
file isdirectory $dir]} {
7784 foreach proj_dir [glob -nocomplain -types d $dir/*] {
7785 if {![regexp {^.*Top/+(.*)$} $proj_dir dummy proj_name]} {
7786 Msg Warning "Could not parse Top directory $dir"
7789 if {[
file exists "$proj_dir/hog.conf"]} {
7790 lappend projects_list $proj_name
7793 lappend projects_list $p
7798 Msg Error "Input $dir is not a directory!"
7801 Msg Error "Directory $dir doesn't exist!"
7803 return $projects_list
7812 proc SetGenericsSimulation {repo_path proj_dir target} {
7813 set top_dir "$repo_path/Top/$proj_dir"
7814 set simsets [get_filesets]
7815 if {$simsets != ""} {
7816 foreach simset $simsets {
7818 if {[get_property FILESET_TYPE $simset] != "SimulationSrcs"} {
7822 set merged_generics_dict [dict create]
7826 set simset_generics [
DictGet $simset_dict "generics"]
7827 set merged_generics_dict [
MergeDict $merged_generics_dict $simset_generics 0]
7830 Msg Debug "TOP = [get_property top [get_filesets sources_1]]"
7831 Msg Debug "GENERICS = [get_property generic [get_filesets sources_1]]"
7833 set_property generic $generic_str [get_filesets $simset]
7834 Msg Info "Setting generics $generic_str for simulator $target\
7835 and simulation file-set $simset..."
7847 proc SetTopProperty {top_module fileset} {
7848 Msg Info "Setting TOP property to $top_module module"
7851 set_property "top" $top_module [get_filesets $fileset]
7854 set_global_assignment -name TOP_LEVEL_ENTITY $top_module
7856 set_root -module $top_module
7858 prj_impl option top $top_module
7863 proc VIVADO_PATH_PROPERTIES {} {
7864 return {"\.*\.TCL\.PRE$" "^.*\.TCL\.POST$" "^RQS_FILES$" "^INCREMENTAL\_CHECKPOINT$" "NOC\_SOLUTION\_FILE"}
7868 proc VITIS_PATH_PROPERTIES {} {
7869 return {"^HW$" "^XPFM$" "^LINKER-SCRIPT$" "^LIBRARIES$" "^LIBRARY-SEARCH-PATH$"}
7879 proc WriteConf {file_name config {comment ""}} {
7880 if {[
catch {
package require inifile 0.2.3} ERROR]} {
7881 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
7885 ::ini::commentchar "#"
7886 set f [::ini::open $file_name w]
7888 foreach sec [dict keys $config] {
7889 set section [dict get $config $sec]
7890 dict for {p v} $section {
7891 if {[string trim $v] == ""} {
7892 Msg Warning "Property $p has empty value. Skipping..."
7895 ::ini::set $f $sec $p $v
7900 if {![
string equal "$comment" ""]} {
7901 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $comment
7902 set hog_header "Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
7903 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $hog_header
7918 proc WriteGenerics {
7919 mode repo_path design date timee
7920 commit version top_hash top_ver hog_hash hog_ver
7921 cons_ver cons_hash libs vers hashes ext_names ext_hashes
7922 user_ip_repos user_ip_vers user_ip_hashes flavour {xml_ver ""} {xml_hash ""}
7924 Msg Info "Passing parameters/generics to project's top module..."
7927 set generic_string [
concat \
7939 if {$xml_hash != "" && $xml_ver != ""} {
7940 lappend generic_string \
7945 foreach l $libs v $vers h $hashes {
7951 set ver [regsub -all {[\.-]} $ver {_}]
7952 set hash [regsub -all {[\.-]} $hash {_}]
7953 lappend generic_string "$ver" "$hash"
7956 foreach e $ext_names h $ext_hashes {
7958 lappend generic_string "$hash"
7961 foreach repo $user_ip_repos v $user_ip_vers h $user_ip_hashes {
7962 set repo_name [
file tail $repo]
7963 set ver "[
string toupper $repo_name]_VER=[
FormatGeneric $v]"
7964 set hash "[
string toupper $repo_name]_SHA=[
FormatGeneric $h]"
7965 set ver [regsub -all {[\.-]} $ver {_}]
7966 set hash [regsub -all {[\.-]} $hash {_}]
7967 lappend generic_string "$ver" "$hash"
7970 if {$flavour != -1} {
7971 lappend generic_string "FLAVOUR=$flavour"
7977 set generic_string "$prj_generics $generic_string"
7983 if {$mode == "create" || [
IsISE]} {
7986 if {[
file exists $top_file]} {
7989 Msg Debug "Found top level generics $generics in $top_file"
7991 set filtered_generic_string ""
7993 foreach generic_to_set [
split [
string trim $generic_string]] {
7994 set key [
lindex [
split $generic_to_set "="] 0]
7995 if {[dict exists $generics $key]} {
7996 Msg Debug "Hog generic $key found in $top_name"
7997 lappend filtered_generic_string "$generic_to_set"
7999 Msg Warning "Generic $key is passed by Hog but is NOT present in $top_name."
8005 set generic_string $filtered_generic_string
8010 set_property generic $generic_string [current_fileset]
8011 Msg Info "Setting parameters/generics..."
8012 Msg Debug "Detailed parameters/generics: $generic_string"
8017 set simulator [get_property target_simulator [current_project]]
8018 if {$mode == "create"} {
8025 Msg Info "Setting Synplify parameters/generics one by one..."
8026 foreach generic $generic_string {
8027 Msg Debug "Setting Synplify generic: $generic"
8028 set_option -hdl_param -set "$generic"
8031 Msg Info "Setting Diamond parameters/generics one by one..."
8032 prj_impl option -impl Implementation0 HDL_PARAM "$generic_string"
8034 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
8036 foreach app_name [dict keys $ws_apps] {
8037 set defined_symbols [app config -name $app_name -get define-compiler-symbols]
8038 foreach generic_to_set [
split [
string trim $generic_string]] {
8039 set key [
lindex [
split $generic_to_set "="] 0]
8040 set value [
lindex [
split $generic_to_set "="] 1]
8041 if {[
string match "32'h*" $value]} {
8042 set value [
string map {"32'h" "0x"} $value]
8045 foreach symbol [
split $defined_symbols ";"] {
8046 if {[
string match "$key=*" $symbol]} {
8047 Msg Debug "Generic $key found in $app_name, removing it..."
8048 app config -name $app_name -remove define-compiler-symbols "$symbol"
8052 Msg Info "Setting Vitis parameters/generics for app $app_name: $key=$value"
8053 app config -name $app_name define-compiler-symbols "$key=$value"
8065 proc WriteGenericsToBdIPs {mode repo_path proj generic_string} {
8066 Msg Debug "Parameters/generics passed to WriteGenericsToIP: $generic_string"
8068 set bd_ip_generics false
8070 if {[dict exists $properties "hog"]} {
8071 set propDict [dict get $properties "hog"]
8072 if {[dict exists $propDict "PASS_GENERICS_TO_BD_IPS"]} {
8073 set bd_ip_generics [dict get $propDict "PASS_GENERICS_TO_BD_IPS"]
8077 if {[
string compare [
string tolower $bd_ip_generics] "false"] == 0} {
8081 if {$mode == "synth"} {
8082 Msg Info "Attempting to apply generics pre-synthesis..."
8083 set PARENT_PRJ [get_property "PARENT.PROJECT_PATH" [current_project]]
8084 set workaround [open "$repo_path/Projects/$proj/.hog/presynth_workaround.tcl" "w"]
8085 puts $workaround "source \[lindex \$argv 0\];"
8086 puts $workaround "open_project \[lindex \$argv 1\];"
8087 puts $workaround "WriteGenericsToBdIPs \[lindex \$argv 2\] \[lindex \$argv 3\] \[lindex \$argv 4\] \[lindex \$argv 5\];"
8088 puts $workaround "close_project"
8092 exec vivado -mode batch -source $repo_path/Projects/$proj/.hog/presynth_workaround.tcl \
8093 -tclargs $repo_path/Hog/Tcl/hog.tcl $PARENT_PRJ \
8094 "childprocess" $repo_path $proj $generic_string
8097 Msg Error "Encountered an error while attempting workaround: $errMsg"
8099 file delete $repo_path/Projects/$proj/.hog/presynth_workaround.tcl
8101 Msg Info "Done applying generics pre-synthesis."
8105 Msg Info "Looking for IPs to add generics to..."
8106 set ips_generic_string ""
8107 foreach generic_to_set [
split [
string trim $generic_string]] {
8108 set key [
lindex [
split $generic_to_set "="] 0]
8109 set value [
lindex [
split $generic_to_set "="] 1]
8110 append ips_generic_string "CONFIG.$key $value "
8114 if {[
string compare [
string tolower $bd_ip_generics] "true"] == 0} {
8117 set ip_regex $bd_ip_generics
8120 set ip_list [get_ips -regex $ip_regex]
8121 Msg Debug "IPs found with regex \{$ip_regex\}: $ip_list"
8123 set regen_targets {}
8125 foreach {ip} $ip_list {
8126 set WARN_ABOUT_IP false
8127 set ip_props [list_property [get_ips $ip]]
8130 if {[lsearch -exact $ip_props "IS_BD_CONTEXT"] == -1} {
8134 if {[get_property "IS_BD_CONTEXT" [get_ips $ip]] eq "1"} {
8135 foreach {ip_prop} $ip_props {
8136 if {[dict exists $ips_generic_string $ip_prop]} {
8137 if {$WARN_ABOUT_IP == false} {
8138 lappend regen_targets [get_property SCOPE [get_ips $ip]]
8139 Msg Warning "The ip \{$ip\} contains generics that are set by Hog.\
8140 If this is IP is apart of a block design, the .bd file may contain stale, unused, values.\
8141 Hog will always apply the most up-to-date values to the IP during synthesis,\
8142 however these values may or may not be reflected in the .bd file."
8143 set WARN_ABOUT_IP true
8148 set xci_path [get_property IP_FILE [get_ips $ip]]
8150 if {[
string equal $generic_format "ERROR"]} {
8151 Msg Warning "Could not find format for generic $ip_prop in IP $ip. Skipping..."
8155 set value_to_set [dict get $ips_generic_string $ip_prop]
8156 switch -exact $generic_format {
8158 if {[
string match "32'h*" $value_to_set]} {
8159 scan [
string map {"32'h" ""} $value_to_set] "%x" value_to_set
8163 set value_to_set [
expr {$value_to_set ? "true" : "false"}]
8166 if {[
string match "32'h*" $value_to_set]} {
8167 binary scan [
binary format H* [
string map {"32'h" ""} $value_to_set]] d value_to_set
8171 if {[
string match "32'h*" $value_to_set]} {
8172 set value_to_set [
string map {"32'h" "0x"} $value_to_set]
8176 set value_to_set [
format "%s" $value_to_set]
8179 Msg Warning "Unknown generic format $generic_format for IP $ip. Will attempt to pass as string..."
8184 Msg Info "The IP \{$ip\} contains: $ip_prop ($generic_format), setting it to $value_to_set."
8185 if {[
catch {set_property -name $ip_prop -value $value_to_set -objects [get_ips $ip]} prop_error]} {
8186 Msg CriticalWarning "Failed to set property $ip_prop to $value_to_set for IP \{$ip\}: $prop_error"
8193 foreach {regen_target} [lsort -unique $regen_targets] {
8194 Msg Info "Regenerating target: $regen_target"
8195 if {[
catch {generate_target -force all [get_files $regen_target]} prop_error]} {
8196 Msg CriticalWarning "Failed to regen targets: $prop_error"
8204 proc GetGenericFormatFromXciXML {generic_name xml_file} {
8205 if {![
file exists $xml_file]} {
8206 Msg Error "Could not find XML file: $xml_file"
8210 set fp [open $xml_file r]
8211 set xci_data [read $fp]
8214 set paramType "string"
8215 set modelparam_regex [
format {^.*\y%s\y.*$} [
string map {"CONFIG." "MODELPARAM_VALUE."} $generic_name]]
8216 set format_regex {format="([^"]+)"}
8218 set line [
lindex [regexp -inline -line $modelparam_regex $xci_data] 0]
8219 Msg Debug "line: $line"
8221 if {[regexp $format_regex $line match format_value]} {
8222 Msg Debug "Extracted: $format_value format from xml"
8223 set paramType $format_value
8225 Msg Debug "No format found, using string"
8234 proc GetGenericFormatFromXci {generic_name xci_file} {
8235 if {![
file exists $xci_file]} {
8236 Msg Error "Could not find XCI file: $xci_file"
8240 set fp [open $xci_file r]
8241 set xci_data [read $fp]
8244 set paramType "string"
8245 if {[
string first "xilinx.com:schema:json_instance:1.0" $xci_data] == -1} {
8246 Msg Debug "XCI format is not JSON, trying XML..."
8247 set xml_file "[
file rootname $xci_file].xml"
8251 set generic_name [
string map {"CONFIG." ""} $generic_name]
8252 set ip_inst [
ParseJSON $xci_data "ip_inst"]
8253 set parameters [dict get $ip_inst parameters]
8254 set component_parameters [dict get $parameters component_parameters]
8255 if {[dict exists $component_parameters $generic_name]} {
8256 set generic_info [dict get $component_parameters $generic_name]
8257 if {[dict exists [
lindex $generic_info 0] format]} {
8258 set paramType [dict get [
lindex $generic_info 0] format]
8259 Msg Debug "Extracted: $paramType format from xci"
8262 Msg Debug "No format found, using string"
8275 proc WriteGitLabCIYAML {proj_name {ci_conf ""}} {
8276 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
8277 Msg CriticalWarning "Cannot find package YAML.\n Error message: $YAMLPACKAGE. \
8278 If you are running on tclsh, you can fix this by installing package \"tcllib\""
8283 if {$ci_conf != ""} {
8285 foreach sec [dict keys $ci_confs] {
8286 if {[
string first : $sec] == -1} {
8287 lappend job_list $sec
8291 set job_list {"generate_project" "simulate_project"}
8295 set out_yaml [huddle create]
8296 foreach job $job_list {
8298 set huddle_tags [huddle list]
8300 set sec_dict [dict create]
8302 if {$ci_confs != ""} {
8303 foreach var [dict keys [dict get $ci_confs $job]] {
8304 if {$var == "tags"} {
8305 set tag_section "tags"
8306 set tags [dict get [dict get $ci_confs $job] $var]
8307 set tags [
split $tags ","]
8309 set tag_list [huddle list $tag]
8310 set huddle_tags [huddle combine $huddle_tags $tag_list]
8313 dict set sec_dict $var [dict get [dict get $ci_confs $job] $var]
8319 set huddle_variables [huddle create "PROJECT_NAME" $proj_name "extends" ".vars"]
8320 if {[dict exists $ci_confs "$job:variables"]} {
8321 set var_dict [dict get $ci_confs $job:variables]
8322 foreach var [dict keys $var_dict] {
8324 set value [dict get $var_dict "$var"]
8325 set var_inner [huddle create "$var" "$value"]
8326 set huddle_variables [huddle combine $huddle_variables $var_inner]
8331 set middle [huddle create "extends" ".$job" "variables" $huddle_variables]
8332 foreach sec [dict keys $sec_dict] {
8333 set value [dict get $sec_dict $sec]
8334 set var_inner [huddle create "$sec" "$value"]
8335 set middle [huddle combine $middle $var_inner]
8337 if {$tag_section != ""} {
8338 set middle2 [huddle create "$tag_section" $huddle_tags]
8339 set middle [huddle combine $middle $middle2]
8342 set outer [huddle create "$job:$proj_name" $middle]
8343 set out_yaml [huddle combine $out_yaml $outer]
8346 return [
string trimleft [yaml::huddle2yaml $out_yaml] "-"]
8356 proc WriteListFiles {libs props list_path repo_path {ext_path ""}} {
8358 foreach lib [dict keys $libs] {
8359 if {[
llength [
DictGet $libs $lib]] > 0} {
8360 set list_file_name $list_path$lib
8361 set list_file [open $list_file_name w]
8362 Msg Info "Writing $list_file_name..."
8363 puts $list_file "#Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
8364 foreach file [
DictGet $libs $lib] {
8366 set prop [
DictGet $props $file]
8370 puts $list_file "$file_path $prop"
8373 set ext_list_file [open "[
file rootname $list_file].ext" a]
8374 puts $ext_list_file "$file_path $prop"
8375 close $ext_list_file
8378 Msg Warning "The path of file $file is not relative to your repository. Please check!"
8394 proc WriteSimListFile {simset libs props simsets list_path repo_path {force 0}} {
8396 set list_file_name $list_path/${simset}.sim
8397 if {$force == 0 && [
file exists $list_file_name]} {
8398 Msg Info "List file $list_file_name already exists, skipping..."
8402 set list_file [open $list_file_name a+]
8405 puts $list_file "\[files\]"
8406 Msg Info "Writing $list_file_name..."
8407 foreach lib [
DictGet $simsets $simset] {
8408 foreach file [
DictGet $libs $lib] {
8410 set prop [
DictGet $props $file]
8414 set lib_name [
file rootname $lib]
8415 if {$lib_name != $simset && [
file extension $file] == ".vhd" && [
file extension $file] == ""} {
8416 lappend prop "lib=$lib_name"
8418 puts $list_file "$file_path $prop"
8421 Msg Warning "The path of file $file is not relative to your repository. Please check!"
8433 proc WriteToFile {File msg} {
8434 set f [open $File a+]
8445 proc WriteUtilizationSummary {input output project_name run} {
8446 set f [open $input "r"]
8447 set o [open $output "a"]
8448 puts $o "## $project_name $run Utilization report\n\n"
8449 struct::matrix util_m
8450 util_m add columns 14
8453 util_m add row "| **Site Type** | **Used** | **Fixed** | **Prohibited** | **Available** | **Util%** |"
8454 util_m add row "| --- | --- | --- | --- | --- | --- |"
8456 util_m add row "| **Site Type** | **Used** | **Fixed** | **Available** | **Util%** |"
8457 util_m add row "| --- | --- | --- | --- | --- |"
8467 while {[
gets $f line] >= 0} {
8468 if {([
string first "| CLB LUTs" $line] >= 0 || [
string first "| Slice LUTs" $line] >= 0) && $luts == 0} {
8469 util_m add row $line
8472 if {([
string first "| CLB Registers" $line] >= 0 || [
string first "| Slice Registers" $line] >= 0) && $regs == 0} {
8473 util_m add row $line
8476 if {[
string first "| Block RAM Tile" $line] >= 0 && $bram == 0} {
8477 util_m add row $line
8480 if {[
string first "URAM " $line] >= 0 && $uram == 0} {
8481 util_m add row $line
8484 if {[
string first "DSPs" $line] >= 0 && $dsps == 0} {
8485 util_m add row $line
8488 if {[
string first "Bonded IOB" $line] >= 0 && $ios == 0} {
8489 util_m add row $line
8496 puts $o [util_m format 2string]
8502 Msg Error "Found Git version older than 2.7.2. Hog will not work as expected, exiting now."
8511 proc GetCurl {{gitlab_url "https://gitlab.com"}} {
8512 if {[
auto_execok curl] == ""} {
8513 Msg Warning "Cannot find a working curl invocation"
8518 set cmd [list curl --silent --show-error]
8520 set cmd [list env -u LD_LIBRARY_PATH curl --silent --show-error]
8523 if {![
catch {
exec {*}$cmd -I $gitlab_url}]} {
8526 Msg Warning "Impossible to contact $gitlab_url"