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}"
70 if {![
IsVivado] || ([
info exists globalSettings::vitis_only_pass] && $globalSettings::vitis_only_pass == 1)} {
74 set ws_apps [
GetVitisApps "$globalSettings::build_dir/vitis_unified" \
75 "$globalSettings::repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"]
79 foreach fileset [dict keys $filesets] {
80 Msg Debug "Fileset: $fileset"
83 if {[
string equal [get_filesets -quiet $fileset] ""]} {
85 create_fileset -simset $fileset
88 current_fileset -simset [get_filesets $fileset]
89 set simulation [get_filesets $fileset]
91 set_property -name {$simulator.compile.vhdl_syntax} -value {2008} -objects $simulation
93 set_property SOURCE_SET sources_1 $simulation
97 set libs_in_fileset [
DictGet $filesets $fileset]
98 if {[
IsInList "ips.src" $libs_in_fileset]} {
103 if {$ws_apps ne ""} {
104 dict for {app_name app_config} $ws_apps {
105 set app_lib [string tolower "app_$app_name\.src"]
106 if {![IsInList $app_lib $libs_in_fileset 0 1]} {
107 Msg Warning "App '$app_name' exists in workspace but no corresponding sourcefile '$app_lib' found. \
108 Make sure you have a list file with the correct naming convention: \[app_<app_name>\.src\]"
116 set app_files_dict [dict create]
120 set reapply_targets [dict create]
121 foreach lib $libs_in_fileset {
122 Msg Debug "lib: $lib \n"
123 set lib_files [
DictGet $libraries $lib]
124 Msg Debug "Files in $lib: $lib_files"
125 set rootlib [
file rootname [
file tail $lib]]
126 set ext [
file extension $lib]
127 Msg Debug "lib: $lib ext: $ext fileset: $fileset"
129 if {[
IsXilinx] && !([
info exists globalSettings::vitis_only_pass] && $globalSettings::vitis_only_pass == 1)} {
131 if {[
string match "app_*" [
string tolower $lib]]} {
134 Msg Debug "Adding $lib to $fileset"
135 add_files -norecurse -fileset $fileset $lib_files
137 foreach f $lib_files {
138 set file_obj [get_files -of_objects [get_filesets $fileset] [list "*$f"]]
140 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
141 set_property -name "library" -value $rootlib -objects $file_obj
145 set props [
DictGet $properties $f]
146 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
148 if {[lsearch -inline -regexp $props "93"] < 0} {
151 if {[lsearch -inline -regexp $props "2008"] >= 0} {
152 set vhdl_year "VHDL 2008"
153 }
elseif {[lsearch -inline -regexp $props "2019"] >= 0} {
155 set vhdl_year "VHDL 2019"
157 Msg CriticalWarning "VHDL 2019 is not supported\
158 in Vivado version older than 2023.2.\
159 Using VHDL 2008, but this might not work."
160 set vhdl_year "VHDL 2008"
164 set vhdl_year "VHDL 2008"
166 Msg Debug "File type for $f is $vhdl_year"
167 set_property -name "file_type" -value $vhdl_year -objects $file_obj
170 Msg Debug "Filetype is VHDL 93 for $f"
175 if {[lsearch -inline -regexp $props "SystemVerilog"] > 0} {
178 set_property -name "file_type" -value "SystemVerilog" -objects $file_obj
179 Msg Debug "Filetype is SystemVerilog for $f"
181 Msg Warning "Xilinx PlanAhead/ISE does not support SystemVerilog.\
182 Property not set for $f"
187 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
189 Msg Info "Setting $top as top module for file set $fileset..."
190 set globalSettings::synth_top_module $top
194 if {[lsearch -inline -regexp $props "verilog_header"] >= 0} {
195 Msg Debug "Setting verilog header type for $f..."
196 set_property file_type {Verilog Header} [get_files $f]
197 }
elseif {[lsearch -inline -regexp $props "verilog_template"] >= 0} {
199 Msg Debug "Setting verilog template type for $f..."
200 set_property file_type {Verilog Template} [get_files $f]
201 }
elseif {[lsearch -inline -regexp $props "verilog"] >= 0} {
203 Msg Debug "Setting verilog type for $f..."
204 set_property file_type {Verilog} [get_files $f]
208 if {[lsearch -inline -regexp $props "nosynth"] >= 0} {
209 Msg Debug "Setting not used in synthesis for $f..."
210 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
214 if {[lsearch -inline -regexp $props "noimpl"] >= 0} {
215 Msg Debug "Setting not used in implementation for $f..."
216 set_property -name "used_in_implementation" -value "false" -objects $file_obj
220 if {[lsearch -inline -regexp $props "nosim"] >= 0} {
221 Msg Debug "Setting not used in simulation for $f..."
222 set_property -name "used_in_simulation" -value "false" -objects $file_obj
227 set top_sim [
lindex [regexp -inline {\ytopsim\s*=\s*(.+?)\y.*} $props] 1]
228 if {$top_sim != ""} {
229 Msg Warning "Setting the simulation top module with the topsim property is deprecated.\
230 Please set this property in the \[properties\] section of your .sim list file,
231 or in the \[$fileset\] section of your sim.conf,\
232 by adding the following line.\ntop=$top_sim"
236 set sim_runtime [
lindex [regexp -inline {\yruntime\s*=\s*(.+?)\y.*} $props] 1]
237 if {$sim_runtime != ""} {
238 Msg Warning "Setting the simulation runtime using the runtime= property is deprecated.\
239 Please set this property in the \[properties\] section of your .sim list file,\
240 or in the \[$fileset\] section of your sim.conf,\
241 by adding the following line.\n<simulator_name>.simulate.runtime=$sim_runtime"
245 if {[lsearch -inline -regexp $props "wavefile"] >= 0} {
246 Msg Warning "Setting a wave do file using the wavefile property is deprecated.\
247 Set this property in the sim.conf file under the \[$fileset\] section,\
248 or in the \[properties\] section of the .sim list file,\
249 by adding the following line .\n<simulator_name>.simulate.custom_wave_do=[
file tail $f]"
253 if {[lsearch -inline -regexp $props "dofile"] >= 0} {
254 Msg Warning "Setting a wave do file using the dofile property is deprecated.\
255 Set this property in the sim.conf file under the \[$fileset\] section,\
256 or in the \[properties\] section of the .sim list file,\
257 by adding the following line .\n<simulator_name>.simulate.custom_do=[
file tail $f]"
261 if {[lsearch -inline -regexp $props "locked"] >= 0 && $ext == ".ip"} {
262 Msg Info "Locking IP $f..."
263 set_property IS_MANAGED 0 [get_files $f]
267 if {[
file extension $f] == ".bd"} {
268 Msg Info "Generating Target for [
file tail $f],\
269 please remember to commit the (possible) changed file."
270 generate_target all [get_files $f]
273 dict for {fo pd} $reapply_targets {
274 set ref [dict get $pd ref]
275 set cell [dict get $pd cell]
276 Msg Debug "Reapplying ref: $ref and cell:$cell to $fo"
277 set_property SCOPED_TO_REF $ref $fo
278 set_property SCOPED_TO_CELLS [split $cell ","] $fo
283 if {[
file extension $f] == ".tcl" && $ext != ".con"} {
284 if {[lsearch -inline -regexp $props "source"] >= 0} {
285 Msg Info "Sourcing Tcl script $f,\
286 and setting it not used in synthesis, implementation and simulation..."
288 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
289 set_property -name "used_in_implementation" -value "false" -objects $file_obj
290 set_property -name "used_in_simulation" -value "false" -objects $file_obj
295 set ref [
lindex [regexp -inline {\yscoped_to_ref\s*=\s*([^ ]+)} $props] 1]
296 set cell [
lindex [regexp -inline {\yscoped_to_cells\s*=\s*([^ ]+)} $props] 1]
297 if {[
file extension $f] == ".elf" || (([
file extension $f] == ".tcl" || [
file extension $f] == ".xdc") && $ext == ".con")} {
298 if {[
file extension $f] == ".elf"} {dict set reapply_targets $file_obj [dict create ref $ref cell $cell]}
300 set_property SCOPED_TO_REF $ref $file_obj
303 set_property SCOPED_TO_CELLS [
split $cell ","] $file_obj
307 Msg Info "[
llength $lib_files] file/s added to library $rootlib..."
310 if {$ext == ".sim"} {
311 Msg Warning "Simulation files not supported in Quartus Prime mode... Skipping $lib"
313 if {![is_project_open]} {
314 Msg Error "Project is closed"
316 foreach cur_file $lib_files {
320 set props [
DictGet $properties $cur_file]
323 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
325 Msg Info "Setting $top as top module for file set $fileset..."
326 set globalSettings::synth_top_module $top
329 if {[
string first "VHDL" $file_type] != -1} {
330 if {[
string first "1987" $props] != -1} {
331 set hdl_version "VHDL_1987"
332 }
elseif {[
string first "1993" $props] != -1} {
333 set hdl_version "VHDL_1993"
334 }
elseif {[
string first "2008" $props] != -1} {
335 set hdl_version "VHDL_2008"
337 set hdl_version "default"
339 if {$hdl_version == "default"} {
340 set_global_assignment -name $file_type $cur_file -library $rootlib
342 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version -library $rootlib
344 }
elseif {[
string first "SYSTEMVERILOG" $file_type] != -1} {
346 if {[
string first "2005" $props] != -1} {
347 set hdl_version "systemverilog_2005"
348 }
elseif {[
string first "2009" $props] != -1} {
349 set hdl_version "systemverilog_2009"
351 set hdl_version "default"
353 if {$hdl_version == "default"} {
354 set_global_assignment -name $file_type $cur_file
356 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
358 }
elseif {[
string first "VERILOG" $file_type] != -1} {
360 if {[
string first "1995" $props] != -1} {
361 set hdl_version "verilog_1995"
362 }
elseif {[
string first "2001" $props] != -1} {
363 set hdl_version "verilog_2001"
365 set hdl_version "default"
367 if {$hdl_version == "default"} {
368 set_global_assignment -name $file_type $cur_file
370 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
372 }
elseif {[
string first "SOURCE" $file_type] != -1 || [
string first "COMMAND_MACRO" $file_type] != -1} {
373 set_global_assignment -name $file_type $cur_file
374 if {$ext == ".con"} {
376 }
elseif {$ext == ".src"} {
378 if {[
string first "qsys" $props] != -1} {
381 regsub -all {\{||qsys||\}} $props $emptyString props
383 set qsysPath [
file dirname $cur_file]
384 set qsysName "[
file rootname [
file tail $cur_file]].qsys"
385 set qsysFile "$qsysPath/$qsysName"
386 set qsysLogFile "$qsysPath/[
file rootname [
file tail $cur_file]].qsys-script.log"
389 if {![
info exists ::env(QSYS_ROOTDIR)]} {
390 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
391 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
392 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
394 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
397 set qsys_rootdir $::env(QSYS_ROOTDIR)
400 set cmd "$qsys_rootdir/qsys-script"
401 set cmd_options " --script=$cur_file"
402 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
403 Msg Info "Executing: $cmd $cmd_options"
404 Msg Info "Saving logfile in: $qsysLogFile"
405 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
406 set makeRet [
lindex [dict get $opt -errorcode] end]
407 Msg CriticalWarning "$cmd returned with $makeRet"
410 Msg Error " Could not execute command $cmd"
414 if {[
file exists $qsysName] != 0} {
415 file rename -force $qsysName $qsysFile
417 set qsysMd5Sum [
Md5Sum $qsysFile]
419 set fileDir [
file normalize "./hogTmp"]
420 set fileName "$fileDir/.hogQsys.md5"
421 if {![
file exists $fileDir]} {
424 set hogQsysFile [open $fileName "a"]
425 set fileEntry "$qsysFile\t$qsysMd5Sum"
426 puts $hogQsysFile $fileEntry
429 Msg ERROR "Error while moving the generated qsys file to final location: $qsysName not found!"
431 if {[
file exists $qsysFile] != 0} {
432 if {[
string first "noadd" $props] == -1} {
434 set_global_assignment -name $qsysFileType $qsysFile
436 regsub -all {noadd} $props $emptyString props
438 if {[
string first "nogenerate" $props] == -1} {
442 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
446 }
elseif {[
string first "QSYS" $file_type] != -1} {
448 regsub -all {\{||\}} $props $emptyString props
449 if {[
string first "noadd" $props] == -1} {
450 set_global_assignment -name $file_type $cur_file
452 regsub -all {noadd} $props $emptyString props
456 if {[
string first "nogenerate" $props] == -1} {
460 set_global_assignment -name $file_type $cur_file -library $rootlib
465 if {$ext == ".con"} {
466 set vld_exts {.sdc .pin .dcf .gcf .pdc .ndc .fdc .crt .vcd }
467 foreach con_file $lib_files {
469 set con_ext [
file extension $con_file]
470 if {[
IsInList [
file extension $con_file] $vld_exts]} {
471 set option [
string map {. -} $con_ext]
472 set option [
string map {fdc net_fdc} $option]
473 set option [
string map {pdc io_pdc} $option]
474 create_links -convert_EDN_to_HDL 0 -library {work} $option $con_file
476 set props [
DictGet $properties $con_file]
478 if {$con_ext == ".sdc"} {
479 if {[lsearch $props "notiming"] >= 0} {
480 Msg Info "Excluding $con_file from timing verification..."
482 Msg Info "Adding $con_file to time verification"
483 append timing_conf_command " -file $con_file"
487 if {[lsearch $props "nosynth"] >= 0} {
488 Msg Info "Excluding $con_file from synthesis..."
490 Msg Info "Adding $con_file to synthesis"
491 append synth_conf_command " -file $con_file"
496 if {$con_ext == ".pdc" || $con_ext == ".sdc"} {
497 if {[lsearch $props "noplace"] >= 0} {
498 Msg Info "Excluding $con_file from place and route..."
500 Msg Info "Adding $con_file to place and route"
501 append place_conf_command " -file $con_file"
506 Msg CriticalWarning "Constraint file $con_file does not have a valid extension. Allowed extensions are: \n $vld_exts"
509 }
elseif {$ext == ".src"} {
510 foreach f $lib_files {
511 Msg Debug "Adding source $f to library $rootlib..."
512 create_links -library $rootlib -hdl_source $f
514 }
elseif {$ext == ".sim"} {
515 Msg Debug "Adding stimulus file $f to library..."
516 create_links -library $rootlib -stimulus $f
518 build_design_hierarchy
519 foreach cur_file $lib_files {
523 set props [
DictGet $properties $cur_file]
526 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
528 Msg Info "Setting $top as top module for file set $rootlib..."
529 set globalSettings::synth_top_module "${top}::$rootlib"
534 if {$ext == ".src" || $ext == ".con" || $ext == ".ext"} {
535 foreach f $lib_files {
536 Msg Debug "Diamond: adding source file $f to library $rootlib..."
537 prj_src add -work $rootlib $f
538 set props [
DictGet $properties $f]
540 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
542 Msg Info "Setting $top as top module for the project..."
543 set globalSettings::synth_top_module $top
547 if {[lsearch -inline -regexp $props "enable"] >= 0} {
548 Msg Debug "Setting $f as active Logic Preference file"
552 }
elseif {$ext == ".sim"} {
553 foreach f $lib_files {
554 Msg Debug "Diamond Adding simulation file $f to library $rootlib..."
555 prj_src add -work $rootlib -simulate_only $f
561 foreach app_name [dict keys $ws_apps] {
562 foreach f $lib_files {
563 if {[
string tolower $rootlib] != [
string tolower "app_$app_name"]} {
567 Msg Info "Adding source file $f from lib: $lib to vitis app \[$app_name\]..."
568 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
569 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
570 Msg Debug "Project_f_path is $proj_f_path"
572 importsources -name $app_name -soft-link -path $f -target $proj_f_path
576 Msg Debug "Vitis Unified: Collecting files for apps from library $rootlib..."
580 if {[dict size $ws_apps] == 0} {
581 Msg Debug "No apps found in workspace, skipping file collection"
583 Msg Debug "Found [dict size $ws_apps] app(s) in workspace"
584 Msg Debug "Processing library: $rootlib with [
llength $lib_files] file(s)"
585 foreach app_name [dict keys $ws_apps] {
586 set expected_lib [
string tolower "app_$app_name"]
587 Msg Debug "Checking files for app: $app_name (looking for lib: $expected_lib, current lib: [
string tolower $rootlib])"
588 foreach f $lib_files {
589 if {[
string tolower $rootlib] != $expected_lib} {
592 Msg Debug "File $f matches app $app_name"
593 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
594 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
595 set proj_f_path [
string trimleft $proj_f_path "/"]
597 if {![dict exists $app_files_dict $app_name]} {
598 dict set app_files_dict $app_name files [list]
599 dict set app_files_dict $app_name target $proj_f_path
600 Msg Debug "Initialized app_files_dict for $app_name with target: $proj_f_path"
603 set current_files [dict get $app_files_dict $app_name files]
604 lappend current_files $f
605 dict set app_files_dict $app_name files $current_files
606 set current_count [
llength [dict get $app_files_dict $app_name files]]
607 Msg Debug "Added file $f to app_files_dict for $app_name, current count: $current_count"
617 set python_script "$globalSettings::repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"
618 set vitis_workspace "$globalSettings::build_dir/vitis_unified"
622 set ::env(HOG_VITIS_VER) $vitis_version
623 Msg Debug "Vitis version: $vitis_version (set in HOG_VITIS_VER environment variable)"
625 dict for {app_name app_data} $app_files_dict {
626 set files_list [dict get $app_data files]
627 set target_path [dict get $app_data target]
629 if {[llength $files_list] > 0} {
630 Msg Info "Adding [llength $files_list] source file(s) to vitis app \[$app_name\]..."
631 Msg Debug "Files: $files_list"
632 Msg Debug "Target path: $target_path"
634 # Convert Tcl list to JSON array for Python
637 foreach f $files_list {
639 append files_json ", "
641 # Escape backslashes and quotes in file paths
642 set escaped_f [string map {\\ \\\\ \" \\\"} $f]
643 append files_json "\"$escaped_f\""
646 append files_json "\]"
648 Msg Debug "JSON string: $files_json"
650 # The file list contains spaces: pass it in the environment, as a command
651 # line argument it would be split by the Windows shell that runs vitis.bat
652 set ::env(HOG_VITIS_APP_FILES) $files_json
653 set error_msg "Failed to add files to app $app_name"
655 ![ExecuteVitisUnifiedCommand $python_script "add_app_files" \
656 [list $app_name $vitis_workspace $target_path] \
659 Msg Error "Failed to add files to Vitis Unified app '$app_name'"
663 Msg Warning "No files to add for app '$app_name'"
670 if {[
DictGet $filesets "sim_1"] == ""} {
671 delete_fileset -quiet [get_filesets -quiet "sim_1"]
677 if {$synth_conf == 1} {
678 Msg Info $synth_conf_command
679 eval $synth_conf_command
681 if {$timing_conf == 1} {
682 Msg Info $timing_conf_command
683 eval $timing_conf_command
685 if {$place_conf == 1} {
686 Msg Info $place_conf_command
687 eval $place_conf_command
693 proc ALLOWED_PROPS {} {
694 return [dict create ".vhd" [list "93" "nosynth" "noimpl" "nosim" "1987" "1993" "2008" "2019"] \
695 ".vhdl" [list "93" "nosynth" "noimpl" "nosim" "1987" "1993" "2008" "2019"] \
696 ".bd" [list "nosim"] \
697 ".v" [list "SystemVerilog" "verilog_header" "nosynth" "noimpl" "nosim" "1995" "2001"] \
698 ".sv" [list "verilog" "verilog_header" "nosynth" "noimpl" "nosim" "2005" "2009"] \
699 ".svp" [list "verilog" "verilog_header" "nosynth" "noimpl" "nosim" "2005" "2009"] \
700 ".do" [list "nosim"] \
701 ".udo" [list "nosim"] \
702 ".xci" [list "nosynth" "noimpl" "nosim" "locked"] \
703 ".xdc" [list "nosynth" "noimpl" "scoped_to_ref" "scoped_to_cells"] \
704 ".tcl" [list "nosynth" "noimpl" "nosim" "scoped_to_ref" "scoped_to_cells" "source" "qsys" "noadd" \
705 "--block-symbol-file" "--clear-output-directory" "--example-design" \
706 "--export-qsys-script" "--family" "--greybox" "--ipxact" \
707 "--jvm-max-heap-size" "--parallel" "--part" "--search-path" \
708 "--simulation" "--synthesis" "--testbench" "--testbench-simulation" \
709 "--upgrade-ip-cores" "--upgrade-variation-file"] \
710 ".qsys" [list "nogenerate" "noadd" "--block-symbol-file" "--clear-output-directory" "--example-design" \
711 "--export-qsys-script" "--family" "--greybox" "--ipxact" "--jvm-max-heap-size" "--parallel" \
712 "--part" "--search-path" "--simulation" "--synthesis" "--testbench" "--testbench-simulation" \
713 "--upgrade-ip-cores" "--upgrade-variation-file"] \
714 ".sdc" [list "notiming" "nosynth" "noplace"] \
715 ".elf" [list "scoped_to_ref" "scoped_to_cells" "nosim" "noimpl"] \
716 ".pdc" [list "nosynth" "noplace"] \
717 ".lpf" [list "enable"]]
728 proc BinaryStepName {part} {
730 return "WRITE_DEVICE_IMAGE"
734 return "WRITE_BITSTREAM"
741 set essential_vars [dict create \
742 "HOG_USER" "NOT defined. This variable is essential for git to work properly. \
743 It should be set to the username for your service account (a valid git account)." \
744 "HOG_EMAIL" "NOT defined. This variable is essential for git to work properly. It should be set to your service's account email." \
745 "HOG_PUSH_TOKEN" "NOT defined. This variable is essential for git to work properly. \
746 It should be set to a Gitlab/GitHub API token for your service account."]
749 dict for {var msg} $essential_vars {
750 if {![info exists env($var)]} {
751 Msg CriticalWarning "Essential environment variable $var is $msg"
754 Msg Info "Found environment variable $var."
758 set additional_vars [dict create \
759 "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." \
760 "HOG_TARGET_BRANCH" "NOT defined. Default branch for merge is \"master\"" \
761 "HOG_CREATE_OFFICIAL_RELEASE" "NOT defined. \
762 Set this variable to '1' to make Hog create an official release in GitHub/Gitlab with the binaries generated in the CI." \
763 "HOG_USE_DOXYGEN" "NOT defined. \
764 Set this variable to 1 to make Hog-CI run Doxygen and copy the official documentation over when you merge to the official branch."]
767 ([
info exists env(HOG_OFFICIAL_BIN_EOS_PATH)] && $env(HOG_OFFICIAL_BIN_EOS_PATH) ne "") ||
768 ([
info exists env(HOG_OFFICIAL_BIN_PATH)] && [
string match "/eos/*" $env(HOG_OFFICIAL_BIN_PATH)])
770 Msg Info "Official binary path points to EOS. Checking EOS environment variables for uploads..."
771 if {[
info exists env(HOG_OFFICIAL_BIN_PATH)]} {
772 Msg CriticalWarning "Variable HOG_OFFICIAL_BIN_EOS_PATH is defined. \
773 From Hog2026.2 this variable will be deprecated. Please, use HOG_OFFICIAL_BIN_PATH instead."
775 if {![
info exists env(EOS_PASSWORD)]} {
776 if {![
info exists env(HOG_PASSWORD)]} {
777 Msg Warning "Neither EOS_PASSWORD nor HOG_PASSWORD environment variable is defined. \
778 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."
780 Msg Info "HOG_PASSWORD environment variable is defined and will be used as password for EOS uploads. \
781 If you want to use a different password for EOS uploads, please set the EOS_PASSWORD environment variable."
784 Msg Info "EOS_PASSWORD environment variable is defined and will be used as password for EOS uploads."
787 if {![
info exists env(EOS_USER)]} {
788 Msg Info "EOS_USER environment variable is not defined. Assuming EOS username is the same as HOG_USER."
790 Msg Info "EOS_USER environment variable is defined and will be used as username for EOS uploads."
793 if {![
info exists env(EOS_MGM_URL)]} {
794 Msg Info "EOS_MGM_URL environment variable is not defined. Assuming default value of root://eosuser.cern.ch."
796 Msg Info "EOS_MGM_URL environment variable is defined and will be used as MGM URL for EOS uploads."
798 }
elseif {[
info exists env(HOG_OFFICIAL_BIN_PATH)]} {
799 Msg Info "Variable HOG_OFFICIAL_BIN_PATH is defined. Hog will copy the official binary files to the path defined in this variable. \
800 Please make sure this path is correct and has enough space to store the binaries."
802 Msg Info "No official binary path defined. Hog will not be able to upload binaries."
807 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
815 proc CheckEnv {project_name ide} {
818 set essential_commands [dict create "git" "--version" "$ide" "-version"]
819 set additional_commands [dict create \
823 "rclone" "--version"]
825 set additional_vars [dict create \
826 "HOG_PATH" "NOT defined. Hog might work as long as all the necessary executable are in the PATH variable." \
827 "HOG_XIL_LICENSE" "NOT defined. If this variable is not set to the license servers separated by comas, \
828 you need some alternative way of getting your Xilinx license (for example a license file on the machine)." \
829 "LM_LICENSE_FILE" "NOT defined. This variable should be set the Quartus/Libero license servers separated by semicolon. \
830 If not, you need an alternative way of getting your Quartus/Libero license." \
831 "HOG_LD_LIBRARY_PATH" "NOT defined. Hog might work as long as all the necessary library are found." \
832 "HOG_SIMULATION_LIB_PATH" "NOT defined. Hog-CI will not be able to run simulations using third-party simulators." \
833 "HOG_CHECK_PROJVER" "NOT defined. Hog will NOT check the CI project version. \
834 Set this variable to '1' if you want Hog to check the CI project version before creating the HDL project in Create_Project stage. \
835 If the project has not been changed with respect to the target branch, the CI will skip this project" \
836 "HOG_CHECK_SYNTAX" "NOT defined. Hog will NOT check the syntax. \
837 Set this variable to '1' if you want Hog to check the syntax after creating the HDL project in Create_Project stage." \
838 "HOG_NO_BITSTREAM" "NOT defined. Hog-CI will run the implementation up to the write_bitstream stage and create bit files." \
839 "HOG_NO_RESET_BD" "NOT defined or not equal to 1. Hog will reset .bd files (if any) before starting synthesis." \
840 "HOG_IP_PATH" "NOT defined. Hog-CI will NOT use an EOS/LOCAL IP repository to speed up the IP synthesis." \
841 "HOG_RESET_FILES" "NOT defined. Hog-CI will NOT reset any files." \
842 "HOG_NJOBS" "NOT defined. Hog-CI will build IPs with default number of jobs (4)." \
843 "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"]
844 Msg Info "Checking environment to run Hog-CI for project $project_name with IDE $ide..."
846 Msg Info "Checking essential commands..."
847 dict for {cmd ver} $essential_commands {
848 if {[catch {exec which $cmd}]} {
849 Msg CriticalWarning "$cmd executable not found. Hog-CI cannot run!"
852 Msg Info "Found executable $cmd."
853 if {$cmd == "ghdl"} {
854 Msg Info [exec $cmd --version]
855 } elseif {$cmd != "diamond"} {
856 Msg Info [exec $cmd $ver]
861 Msg Info "Checking additional commands..."
862 dict for {cmd ver} $additional_commands {
863 if {[catch {exec which $cmd}]} {
864 Msg Warning "$cmd executable not found."
866 Msg Info "Found executable $cmd."
868 Msg Info [exec $cmd $ver]
873 if {$ide == "libero"} {
874 Msg Info "Checking essential environment variables..."
876 if {![
info exists env(HOG_TCLLIB_PATH)]} {
877 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."
880 Msg Info "HOG_TCLLIB_PATH is set. Hog-CI can run with Libero."
884 Msg Info "Checking additional environment variables..."
885 dict for {var msg} $additional_vars {
886 if {![info exists env($var)]} {
887 Msg Info "Environment variable $var is $msg"
889 Msg Info "Found environment variable $var."
894 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
907 proc CheckRemoteIpPath {repo_path ip_path} {
915 if {[regexp {^[^/]+:} $ip_path]} {
919 lassign [
ExecuteRet rclone --version] rclone_ret rclone_ver
920 if {$rclone_ret != 0} {
921 Msg Warning "Rclone path specified but rclone not found or failed: $rclone_ver"
925 Msg Info "IP remote directory path, on Rclone, is set to: $ip_path"
927 if {[
info exists env(HOG_RCLONE_CONFIG)]} {
928 Msg Info "Using rclone config from environment variable HOG_RCLONE_CONFIG: $env(HOG_RCLONE_CONFIG)"
929 set config_path $env(HOG_RCLONE_CONFIG)
931 set config_path "/dev/null"
932 Msg Info "Environment variable HOG_RCLONE_CONFIG not set, using rclone environmental variables..."
935 set remote_name "[
lindex [
split $ip_path ":"] 0]:"
936 lassign [
ExecuteRet rclone listremotes --config $config_path] rclone_list_ret remotes
937 if {$rclone_list_ret != 0} {
938 Msg Warning "Could not list rclone remotes: $remotes"
942 if {![
IsInList $remote_name $remotes]} {
943 Msg Warning "Rclone remote $remote_name not found among available remotes: $remotes"
949 }
elseif {[
string first "/eos/" $ip_path] == 0} {
952 if {!([
info exists ::env(ENABLE_EOS)] && $::env(ENABLE_EOS) == 1)} {
953 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."
958 if {[
file isdirectory $ip_path]} {
959 Msg Info "Eos is mounted in the current machine. Treating it as a normal directory..."
962 lassign [
eos "ls $ip_path"] ret result
964 Msg Warning "Could not run ls for for EOS path: $ip_path (error: $result). \
965 Either the drectory does not exist or there are (temporary) problem with EOS."
969 Msg Info "IP remote directory path, on EOS, is set to: $ip_path"
977 return [list 1 $on_eos $on_rclone $config_path]
980 proc CheckProjVer {repo_path project {sim 0} {ext_path ""}} {
984 Msg Info "Will check also the version of the simulation files..."
988 if {[
info exists env(HOG_PUSH_TOKEN)] && [
info exist env(CI_PROJECT_ID)] && [
info exist env(CI_API_V4_URL)]} {
989 set token $env(HOG_PUSH_TOKEN)
990 set api_url $env(CI_API_V4_URL)
991 set project_id $env(CI_PROJECT_ID)
993 set curl_cmd [
GetCurl $api_url]
994 if {[
info exist env(CI_JOB_NAME)] && $env(CI_JOB_NAME) == "check_branch_state"} {
1003 set project_dir $repo_path/Top/$project
1006 Msg Info "$project was modified, continuing with the CI..."
1007 if {$ci_run == 1 && $curl_cmd != 0 && ![
IsQuartus] && ![
IsISE]} {
1008 Msg Info "Checking if the project has been already built in a previous CI run..."
1010 if {$sha == [
GetSHA $repo_path]} {
1011 Msg Info "Project was modified in the current commit, Hog will proceed with the build workflow."
1014 Msg Info "Checking if project $project has been built in a previous CI run with sha $sha..."
1015 set result [
catch {
package require json} JsonFound]
1016 if {"$result" != "0"} {
1017 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
1020 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/$project_id/pipelines"] ret content
1021 set pipeline_dict [json::json2dict $content]
1022 if {[
llength $pipeline_dict] > 0} {
1023 foreach pip $pipeline_dict {
1024 set pip_sha [
DictGet $pip sha]
1025 set source [
DictGet $pip source]
1026 if {$source == "merge_request_event" && [
string first $sha $pip_sha] != -1} {
1027 Msg Info "Found pipeline with sha $pip_sha for project $project"
1028 set pipeline_id [
DictGet $pip id]
1030 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/${project_id}/pipelines/${pipeline_id}/jobs?pagination=keyset&per_page=100"] ret2 content2
1031 set jobs_dict [json::json2dict $content2]
1032 if {[
llength $jobs_dict] > 0} {
1033 foreach job $jobs_dict {
1034 set job_name [
DictGet $job name]
1036 set artifacts [
DictGet $job artifacts_file]
1037 set status [
DictGet $job status]
1038 set current_job_name $env(CI_JOB_NAME)
1039 if {$current_job_name == $job_name && $status == "success"} {
1041 lassign [
ExecuteRet {*}$curl_cmd --location --output artifacts.zip --header "PRIVATE-TOKEN: $token" --url "$api_url/projects/$project_id/jobs/$job_id/artifacts"] ret3 content3
1043 Msg CriticalWarning "Cannot download artifacts for job $job_name with id $job_id"
1046 lassign [
ExecuteRet unzip -o $repo_path/artifacts.zip] ret_zip
1047 if {$ret_zip != 0} {
1050 Msg Info "Artifacts for job $job_name with id $job_id downloaded and unzipped."
1051 file mkdir $repo_path/Projects/$project
1052 set fp [open "$repo_path/Projects/$project/skip.me" w+]
1064 }
elseif {$ver != -1} {
1065 Msg Info "$project was not modified since version: $ver."
1066 file mkdir $repo_path/Projects/$project
1067 set fp [open "$repo_path/Projects/$project/skip.me" w+]
1071 Msg Error "Impossible to check the project version. Most likely the repository is not clean. Please, commit your changes before running this command."
1082 proc CheckSyntax {project_name repo_path {project_file ""}} {
1084 update_compile_order
1085 set syntax [check_syntax -return_string]
1086 if {[
string first "CRITICAL" $syntax] != -1} {
1091 lassign [
GetHogFiles -list_files "*.src" "$repo_path/Top/$project_name/list/" $repo_path] src_files dummy
1092 dict for {lib files} $src_files {
1094 set file_extension [file extension $f]
1095 if {$file_extension == ".vhd" || $file_extension == ".vhdl" || $file_extension == ".v" || $file_extension == ".sv"} {
1096 if {[catch {execute_module -tool map -args "--analyze_file=$f"} result]} {
1097 Msg Error "\nResult: $result\n"
1098 Msg Error "Check syntax failed.\n"
1100 if {$result == ""} {
1101 Msg Info "Check syntax was successful for $f.\n"
1103 Msg Warning "Found syntax error in file $f:\n $result\n"
1110 lassign [
GetProjectFiles $project_file] prjLibraries prjProperties prjSimLibraries prjConstraints prjSrcSets prjSimSets prjConSets
1111 dict for {lib sources} $prjLibraries {
1112 if {[file extension $lib] == ".src"} {
1113 foreach f $sources {
1114 Msg Info "Checking Syntax of $f"
1120 Msg Info "The Checking Syntax is not supported by this IDE. Skipping..."
1125 proc CloseProject {} {
1147 proc CompareVersions {ver1 ver2} {
1156 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver1 - x y z]} {
1157 set ver1 [list $x $y $z]
1159 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver2 - x y z]} {
1160 set ver2 [list $x $y $z]
1164 set v1 [
join $ver1 ""]
1166 set v2 [
join $ver2 ""]
1169 if {[
string is integer $v1] && [
string is integer $v2]} {
1170 set ver1 [
expr {[scan [lindex $ver1 0] %d] * 1000000 + [scan [lindex $ver1 1] %d] * 1000 + [scan [lindex $ver1 2] %d]}]
1171 set ver2 [
expr {[scan [lindex $ver2 0] %d] * 1000000 + [scan [lindex $ver2 1] %d] * 1000 + [scan [lindex $ver2 2] %d]}]
1173 if {$ver1 > $ver2} {
1175 }
elseif {$ver1 == $ver2} {
1181 Msg Warning "Version is not numeric: $ver1, $ver2"
1184 return [
expr {$ret}]
1191 proc CheckExtraFiles {libraries constraints simlibraries} {
1194 lassign [
GetProjectFiles] prjLibraries prjProperties prjSimLibraries prjConstraints
1195 set prj_dir [get_property DIRECTORY [current_project]]
1196 file mkdir "$prj_dir/.hog"
1197 set extra_file_name "$prj_dir/.hog/extra.files"
1198 set new_extra_file [open $extra_file_name "w"]
1200 dict for {prjLib prjFiles} $prjLibraries {
1201 foreach prjFile $prjFiles {
1202 if {[file extension $prjFile] == ".xcix"} {
1203 Msg Warning "IP $prjFile is packed in a .xcix core container. \
1204 This files are not suitable for version control systems. We recommend to use .xci files instead."
1207 if {[file extension $prjFile] == ".xci" && [get_property CORE_CONTAINER [get_files $prjFile]] != ""} {
1208 Msg Info "$prjFile is a virtual IP file in a core container. Ignoring it..."
1212 if {[IsInList $prjFile [DictGet $libraries $prjLib]] == 0} {
1213 if {[file extension $prjFile] == ".bd"} {
1214 # Generating BD products to save md5sum of already modified BD
1215 Msg Info "Generating targets of $prjFile..."
1216 generate_target all [get_files $prjFile]
1218 puts $new_extra_file "$prjFile [Md5Sum $prjFile]"
1219 Msg Info "$prjFile (lib: $prjLib) has been generated by an external script. Adding to $extra_file_name..."
1223 close $new_extra_file
1224 set extra_sim_file "$prj_dir/.hog/extrasim.files"
1225 set new_extra_file [open $extra_sim_file "w"]
1227 dict for {prjSimLib prjSimFiles} $prjSimLibraries {
1228 foreach prjSimFile $prjSimFiles {
1229 if {[IsInList $prjSimFile [DictGet $simlibraries $prjSimLib]] == 0} {
1230 puts $new_extra_file "$prjSimFile [Md5Sum $prjSimFile]"
1231 Msg Info "$prjSimFile (lib: $prjSimLib) has been generated by an external script. Adding to $extra_sim_file..."
1235 close $new_extra_file
1236 set extra_con_file "$prj_dir/.hog/extracon.files"
1237 set new_extra_file [open $extra_con_file "w"]
1239 dict for {prjConLib prjConFiles} $prjConstraints {
1240 foreach prjConFile $prjConFiles {
1241 if {[IsInList $prjConFile [DictGet $constraints $prjConLib]] == 0} {
1242 puts $new_extra_file "$prjConFile [Md5Sum $prjConFile]"
1243 Msg Info "$prjConFile has been generated by an external script. Adding to $extra_con_file..."
1247 close $new_extra_file
1254 proc CheckLatestHogRelease {{repo_path .}} {
1255 if {[
info exists ::env(CHECK_FOR_HOG_UPDATES)] && $::env(CHECK_FOR_HOG_UPDATES) eq "0"} {
1260 if {[
catch {
package require inifile 0.2.3} ERROR] == 0} {
1261 set repo_conf $repo_path/Top/repo.conf
1262 if {[
file exists $repo_conf]} {
1263 set PROPERTIES [
ReadConf $repo_conf]
1264 if {[dict exists $PROPERTIES main]} {
1265 set mainDict [dict get $PROPERTIES main]
1266 if {[dict exists $mainDict CHECK_FOR_HOG_UPDATES]} {
1267 if {[dict get $mainDict CHECK_FOR_HOG_UPDATES] == 0} {
1277 set current_ver [
Git {describe --always}]
1278 Msg Debug "Current version: $current_ver"
1279 set current_sha [
Git "log $current_ver -1 --format=format:%H"]
1280 Msg Debug "Current SHA: $current_sha"
1283 if {[
OS] == "windows"} {
1284 Msg Info "On windows we cannot set a timeout on 'git fetch', hopefully nothing will go wrong..."
1287 Msg Info "Checking for latest Hog release, can take up to 5 seconds..."
1290 set master_ver [
Git "describe origin/master"]
1291 Msg Debug "Master version: $master_ver"
1292 set master_sha [
Git "log $master_ver -1 --format=format:%H"]
1293 Msg Debug "Master SHA: $master_sha"
1294 lassign [
GitRet "merge-base $current_sha $master_sha"] mb_ret merge_base
1296 Msg Warning "Could not run git merge-base (shallow clone?). Skipping Hog update check."
1300 Msg Debug "merge base: $merge_base"
1303 if {$merge_base != $master_sha} {
1305 Msg Info "Version $master_ver has been released (https://gitlab.com/hog-cern/Hog/-/releases/$master_ver)"
1306 Msg Status "You should consider updating Hog submodule with the following instructions:"
1308 Msg Status "cd Hog && git checkout master && git pull"
1310 Msg Status "Also update the ref: in your .gitlab-ci.yml to $master_ver"
1314 Msg Info "Latest official version is $master_ver, nothing to do."
1326 proc CheckYmlRef {repo_path allow_failure} {
1327 if {$allow_failure} {
1328 set MSG_TYPE CriticalWarning
1333 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
1334 Msg CriticalWarning "Cannot find package YAML, skipping consistency check of \"ref\" in gilab-ci.yaml file.\n Error message: $YAMLPACKAGE
1335 You can fix this by installing package \"tcllib\""
1343 if {[
file exists .gitlab-ci.yml]} {
1347 if {[
file exists .gitlab-ci.yml]} {
1348 set fp [open ".gitlab-ci.yml" r]
1349 set file_data [read $fp]
1352 Msg $MSG_TYPE "Cannot open file .gitlab-ci.yml"
1356 set file_data "\n$file_data\n\n"
1358 if {[
catch {::yaml::yaml2dict -stream $file_data} yamlDict]} {
1359 Msg $MSG_TYPE "Parsing $repo_path/.gitlab-ci.yml failed. To fix this, check that yaml syntax is respected, remember not to use tabs."
1363 dict for {dictKey dictValue} $yamlDict {
1364 #looking for Hog include in .gitlab-ci.yml
1366 "$dictKey" == "include" && (
1367 [lsearch [split $dictValue " {}"] "/hog.yml"] != "-1" ||
1368 [lsearch [split $dictValue " {}"] "/hog-dynamic.yml"] != "-1"
1371 set YML_REF [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "ref"] + 1}]]
1372 set YML_NAME [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "file"] + 1}]]
1376 if {$YML_REF == ""} {
1377 Msg Warning "Hog version not specified in the .gitlab-ci.yml. Assuming that master branch is used."
1379 set YML_REF_F [
Git {name-rev --tags --name-only origin/master}]
1382 set YML_REF_F [regsub -all "'" $YML_REF ""]
1385 if {$YML_NAME == ""} {
1386 Msg $MSG_TYPE "Hog included yml file not specified, assuming hog.yml"
1387 set YML_NAME_F hog.yml
1389 set YML_NAME_F [regsub -all "^/" $YML_NAME ""]
1392 lappend YML_FILES $YML_NAME_F
1398 if {[
catch {::yaml::yaml2dict -file $YML_NAME_F} yamlDict]} {
1399 Msg $MSG_TYPE "Parsing $YML_NAME_F failed."
1403 dict for {dictKey dictValue} $yamlDict {
1404 #looking for included files
1405 if {"$dictKey" == "include"} {
1406 foreach v $dictValue {
1407 lappend YML_FILES [lindex [split $v " "] [expr {[lsearch -dictionary [split $v " "] "local"] + 1}]]
1413 Msg Info "Found the following yml files: $YML_FILES"
1415 set HOGYML_SHA [
GetSHA $YML_FILES]
1416 lassign [
GitRet "log --format=%h -1 --abbrev=7 $YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1418 lassign [
GitRet "log --format=%h -1 --abbrev=7 origin/$YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1420 Msg $MSG_TYPE "Error in project .gitlab-ci.yml. ref: $YML_REF not found"
1421 set EXPECTEDYML_SHA ""
1424 if {!($EXPECTEDYML_SHA eq "")} {
1425 if {$HOGYML_SHA == $EXPECTEDYML_SHA} {
1426 Msg Info "Hog included file $YML_FILES matches with $YML_REF in .gitlab-ci.yml."
1428 Msg $MSG_TYPE "HOG $YML_FILES SHA mismatch.
1429 From Hog submodule: $HOGYML_SHA
1430 From ref in .gitlab-ci.yml: $EXPECTEDYML_SHA
1431 You can fix this in 2 ways: by changing the ref in your repository or by changing the Hog submodule commit"
1434 Msg $MSG_TYPE "One or more of the following files could not be found $YML_FILES in Hog at $YML_REF"
1437 Msg Info ".gitlab-ci.yml not found in $repo_path. Skipping this step"
1458 proc CompareLibDicts {proj_libs list_libs proj_sets list_sets proj_props list_props {severity "CriticalWarning"} {outFile ""} {extraFiles ""}} {
1459 set extra_files $extraFiles
1461 set out_prjlibs $proj_libs
1462 set out_prjprops $proj_props
1464 dict for {prjSet prjLibraries} $proj_sets {
1465 # Check if sets is also in list files
1466 if {[IsInList $prjSet $list_sets]} {
1467 set listLibraries [DictGet $list_sets $prjSet]
1468 # Loop over libraries in fileset
1469 foreach prjLib $prjLibraries {
1470 set prjFiles [DictGet $proj_libs $prjLib]
1471 # Check if library exists in list files
1472 if {[IsInList $prjLib $listLibraries]} {
1473 # Loop over files in library
1474 set listFiles [DictGet $list_libs $prjLib]
1475 foreach prjFile $prjFiles {
1476 set idx [lsearch -exact $listFiles $prjFile]
1477 set listFiles [lreplace $listFiles $idx $idx]
1479 # File is in project but not in list libraries, check if it was generated at creation time...
1480 if {[dict exists $extra_files $prjFile]} {
1481 # File was generated at creation time, checking the md5sum
1482 # Removing the file from the prjFiles list
1483 set idx2 [lsearch -exact $prjFiles $prjFile]
1484 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1485 set new_md5sum [Md5Sum $prjFile]
1486 set old_md5sum [DictGet $extra_files $prjFile]
1487 if {$new_md5sum != $old_md5sum} {
1488 # tclint-disable-next-line line-length
1489 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
1492 set extra_files [dict remove $extra_files $prjFile]
1494 # File is neither in list files nor in extra_files
1495 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1499 # File is both in list files and project, checking properties...
1500 set prjProps [DictGet $proj_props $prjFile]
1501 set listProps [DictGet $list_props $prjFile]
1502 # Check if it is a potential sourced file
1503 if {[IsInList "nosynth" $prjProps] && [IsInList "noimpl" $prjProps] && [IsInList "nosim" $prjProps]} {
1504 # Check if it is sourced
1505 set idx_source [lsearch -exact $listProps "source"]
1506 if {$idx_source >= 0} {
1507 # It is sourced, let's replace the individual properties with source
1508 set idx [lsearch -exact $prjProps "noimpl"]
1509 set prjProps [lreplace $prjProps $idx $idx]
1510 set idx [lsearch -exact $prjProps "nosynth"]
1511 set prjProps [lreplace $prjProps $idx $idx]
1512 set idx [lsearch -exact $prjProps "nosim"]
1513 set prjProps [lreplace $prjProps $idx $idx]
1514 lappend prjProps "source"
1518 foreach prjProp $prjProps {
1519 set idx [lsearch -exact $listProps $prjProp]
1520 set listProps [lreplace $listProps $idx $idx]
1522 MsgAndLog "Property $prjProp of $prjFile was set in project but not in list files" $severity $outFile
1527 foreach listProp $listProps {
1528 if {[string first $listProp "topsim="] == -1 && [string first $listProp "enable"] == -1} {
1529 MsgAndLog "Property $listProp of $prjFile was found in list files but not set in project." $severity $outFile
1534 # Update project prjProps
1535 dict set out_prjprops $prjFile $prjProps
1538 # Loop over remaining files in list libraries
1539 foreach listFile $listFiles {
1540 MsgAndLog "$listFile was found in list files but not in project." $severity $outFile
1544 # Check extra files again...
1545 foreach prjFile $prjFiles {
1546 if {[dict exists $extra_files $prjFile]} {
1547 # File was generated at creation time, checking the md5sum
1548 # Removing the file from the prjFiles list
1549 set idx2 [lsearch -exact $prjFiles $prjFile]
1550 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1551 set new_md5sum [Md5Sum $prjFile]
1552 set old_md5sum [DictGet $extra_files $prjFile]
1553 if {$new_md5sum != $old_md5sum} {
1554 # tclint-disable-next-line line-length
1555 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
1558 set extra_files [dict remove $extra_files $prjFile]
1560 # File is neither in list files nor in extra_files
1561 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1566 # Update prjLibraries
1567 dict set out_prjlibs $prjLib $prjFiles
1570 MsgAndLog "Fileset $prjSet found in project but not in list files" $severity $outFile
1575 return [list $n_diffs $extra_files $out_prjlibs $out_prjprops]
1585 proc CompareVHDL {file1 file2} {
1586 set a [open $file1 r]
1587 set b [open $file2 r]
1589 while {[
gets $a line] != -1} {
1590 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1591 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1597 while {[
gets $b line] != -1} {
1598 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1599 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1608 foreach x $f1 y $f2 {
1610 lappend diff "> $x\n< $y\n\n"
1623 proc Copy {i_dirs o_dir} {
1624 foreach i_dir $i_dirs {
1625 if {[
file isdirectory $i_dir] && [
file isdirectory $o_dir]} {
1626 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]])} {
1627 file delete -force $o_dir/[
file tail $i_dir]
1631 file copy -force $i_dir $o_dir
1646 proc CopyIPbusXMLs {proj_dir path dst {xml_version "0.0.0"} {xml_sha "00000000"} {use_ipbus_sw 0} {generate 0}} {
1647 if {$use_ipbus_sw == 1} {
1648 lassign [
ExecuteRet python3 -c "from __future__ import print_function; from sys import path;print(':'.join(path\[1:\]))"] ret msg
1650 set ::env(PYTHONPATH) $msg
1651 lassign [
ExecuteRet gen_ipbus_addr_decode -h] ret msg
1658 Msg CriticalWarning "Problem while trying to run python: $msg"
1661 set dst [
file normalize $dst]
1663 if {$can_generate == 0} {
1664 if {$generate == 1} {
1665 Msg Error "Cannot generate IPbus address files, IPbus executable gen_ipbus_addr_decode not found or not working: $msg"
1668 Msg Warning "IPbus executable gen_ipbus_addr_decode not found or not working, will not verify IPbus address tables."
1675 set ipb_files [glob -nocomplain $proj_dir/list/*.ipb]
1676 set n_ipb_files [
llength $ipb_files]
1677 if {$n_ipb_files == 0} {
1678 Msg CriticalWarning "No files with .ipb extension found in $proj_dir/list."
1681 set libraries [dict create]
1682 set vhdl_dict [dict create]
1684 foreach ipb_file $ipb_files {
1690 set xmlfiles [dict get $libraries "xml.ipb"]
1692 set xml_list_error 0
1693 foreach xmlfile $xmlfiles {
1694 if {[
file isdirectory $xmlfile]} {
1695 Msg CriticalWarning "Directory $xmlfile listed in xml list file $list_file. Directories are not supported!"
1696 set xml_list_error 1
1699 if {[
file exists $xmlfile]} {
1700 if {[dict exists $vhdl_dict $xmlfile]} {
1701 set vhdl_file [
file normalize [dict get $vhdl_dict $xmlfile]]
1705 lappend vhdls $vhdl_file
1706 set xmlfile [
file normalize $xmlfile]
1707 Msg Info "Copying $xmlfile to $dst and replacing place holders..."
1708 set in [open $xmlfile r]
1710 if {[regexp \/xml\/+(.*)$ $xmlfile XXX out_with_dir]} {
1711 set out_file $dst/$out_with_dir
1712 lappend xmls $out_with_dir
1713 Msg Debug "xml file $xmlfile is contained in a directory called 'xml', so file will be copied to $out_file"
1714 set out_dir [
file dir $out_file]
1715 if {![
file exists $out_dir]} {
1719 set out_file $dst/[
file tail $xmlfile]
1720 lappend xmls [
file tail $xmlfile]
1723 set out [open $out_file w]
1724 while {[
gets $in line] != -1} {
1725 set new_line [regsub {(.*)__VERSION__(.*)} $line "\\1$xml_version\\2"]
1726 set new_line2 [regsub {(.*)__GIT_SHA__(.*)} $new_line "\\1$xml_sha\\2"]
1727 puts $out $new_line2
1732 Msg Warning "XML file $xmlfile not found"
1735 if {${xml_list_error}} {
1736 Msg Error "Invalid files added to $list_file!"
1739 set cnt [
llength $xmls]
1740 Msg Info "$cnt xml file/s copied"
1743 if {$can_generate == 1} {
1746 file mkdir "address_decode"
1748 foreach x $xmls v $vhdls {
1750 set x [
file normalize ../$x]
1751 if {[
file exists $x]} {
1752 lassign [
ExecuteRet gen_ipbus_addr_decode --no-timestamp $x 2>&1] status log
1754 set generated_vhdl ./ipbus_decode_[
file rootname [
file tail $x]].vhd
1755 if {$generate == 1} {
1756 Msg Info "Copying generated VHDL file $generated_vhdl into $v (replacing if necessary)"
1757 file copy -force -- $generated_vhdl $v
1759 if {[
file exists $v]} {
1761 set n [
llength $diff]
1763 Msg CriticalWarning "$v does not correspond to its XML $x, [
expr {$n / 3}] line/s differ:"
1764 Msg Status [
join $diff "\n"]
1765 set diff_file [open ../diff_[
file rootname [
file tail $x]].txt w]
1766 puts $diff_file $diff
1769 Msg Info "[
file tail $x] and $v match."
1772 Msg Warning "VHDL address map file $v not found."
1776 Msg Warning "Address map generation failed for [
file tail $x]: $log"
1779 Msg Warning "Copied XML file $x not found."
1782 Msg Info "Skipped verification of [
file tail $x] as no VHDL file was specified."
1786 file delete -force address_decode
1798 proc DescriptionFromConf {conf_file} {
1799 set f [open $conf_file "r"]
1800 set lines [
split [read $f] "\n"]
1802 set second_line [
lindex $lines 1]
1805 if {![regexp {\#+ *(.+)} $second_line - description]} {
1809 if {[regexp -all {test|Test|TEST} $description]} {
1810 set description "test"
1823 proc DictGet {dictName keyName {default ""}} {
1824 if {[dict exists $dictName $keyName]} {
1825 return [dict get $dictName $keyName]
1836 proc DictSort {dict args} {
1838 foreach key [lsort {*}$args [dict keys $dict]] {
1839 dict set res $key [dict get $dict $key]
1849 proc DoxygenVersion {target_version} {
1850 set ver [
split $target_version "."]
1851 set v [
Execute doxygen --version]
1852 Msg Info "Found Doxygen version: $v"
1853 set current_ver [
split $v ". "]
1854 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
1855 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
1857 return [
expr {$target <= $current}]
1868 proc eos {command {attempt 1}} {
1870 if {![
info exists env(EOS_MGM_URL)]} {
1871 Msg Warning "Environment variable EOS_MGM_URL not set, setting it to default value root://eosuser.cern.ch"
1872 set ::env(EOS_MGM_URL) "root://eosuser.cern.ch"
1875 Msg Warning "The value of attempt should be 1 or more, not $attempt, setting it to 1 as default"
1878 for {
set i 0} {$i < $attempt} {
incr i} {
1879 set ret [
catch {
exec -ignorestderr eos {*}$command} result]
1884 set wait [
expr {1 + int(rand() * 29)}]
1885 Msg Warning "Command $command failed ($i/$attempt): $result, trying again in $wait seconds..."
1886 after [
expr {$wait * 1000}]
1890 return [list $ret $result]
1900 proc Execute {args} {
1904 Msg Error "Command [
join $args] returned error code: $ret"
1918 proc ExecuteRet {args} {
1920 if {[
llength $args] == 0} {
1921 Msg CriticalWarning "No argument given"
1925 set ret [
catch {
exec -ignorestderr {*}$args} result]
1928 return [list $ret $result]
1935 proc ExtractFilesSection {file_data} {
1936 set in_files_section 0
1939 foreach line $file_data {
1940 if {[regexp {^ *\[ *files *\]} $line]} {
1941 set in_files_section 1
1944 if {$in_files_section} {
1945 if {[regexp {^ *\[.*\]} $line]} {
1948 lappend result $line
1953 if {!$in_files_section} {
1967 proc ExtractVersionFromTag {tag} {
1968 if {[regexp {^(?:b(\d+))?v(\d+)\.(\d+).(\d+)(?:-\d+)?$} $tag -> mr M m p]} {
1973 Msg Warning "Repository tag $tag is not in a Hog-compatible format."
1979 return [list $M $m $p $mr]
1989 proc FileCommitted {File} {
1991 set currentDir [
pwd]
1992 cd [
file dirname [
file normalize $File]]
1993 set GitLog [
Git ls-files [
file tail $File]]
1994 if {$GitLog == ""} {
1995 Msg CriticalWarning "File [
file normalize $File] is not in the git repository. Please add it with:\n git add [
file normalize $File]\n"
2006 proc FindCommonGitChild {SHA1 SHA2} {
2008 set commits [
Git {log --oneline --merges}]
2011 foreach line [
split $commits "\n"] {
2012 set commit [
lindex [
split $line] 0]
2016 set ancestor $commit
2029 proc findFiles {basedir pattern} {
2032 set basedir [
string trimright [
file join [
file normalize $basedir] { }]]
2038 foreach fileName [glob -nocomplain -type {f r} -path $basedir $pattern] {
2039 lappend fileList $fileName
2043 foreach dirName [glob -nocomplain -type {d r} -path $basedir *] {
2046 set subDirList [
findFiles $dirName $pattern]
2047 if {[
llength $subDirList] > 0} {
2048 foreach subDirFile $subDirList {
2049 lappend fileList $subDirFile
2060 proc FindFileType {file_name} {
2061 set extension [
file extension $file_name]
2064 set file_extension "USE_SIGNALTAP_FILE"
2067 set file_extension "VHDL_FILE"
2070 set file_extension "VHDL_FILE"
2073 set file_extension "VERILOG_FILE"
2076 set file_extension "SYSTEMVERILOG_FILE"
2079 set file_extension "SDC_FILE"
2082 set file_extension "PDC_FILE"
2085 set file_extension "NDC_FILE"
2088 set file_extension "FDC_FILE"
2091 set file_extension "SOURCE_FILE"
2094 set file_extension "IP_FILE"
2097 set file_extension "QSYS_FILE"
2100 set file_extension "QIP_FILE"
2103 set file_extension "SIP_FILE"
2106 set file_extension "BSF_FILE"
2109 set file_extension "BDF_FILE"
2112 set file_extension "COMMAND_MACRO_FILE"
2115 set file_extension "VQM_FILE"
2118 set file_extension "ERROR"
2119 Msg Error "Unknown file extension $extension"
2122 return $file_extension
2129 proc FindNewestVersion {versions} {
2130 set new_ver 00000000
2131 foreach ver $versions {
2133 if {[
expr 0x$ver > 0x$new_ver]} {
2143 proc FindRepoRoot {start_dir} {
2144 if {[
file pathtype $start_dir] eq "relative"} {
2145 set dir [
file normalize [
file join [
pwd] $start_dir]]
2150 if {[
file exists [
file join $dir Top]] && [
file exists [
file join $dir Projects]]} {
2153 set parent [
file dirname $dir]
2154 if {$parent eq $dir} {
2166 proc FindVhdlVersion {file_name} {
2167 set extension [
file extension $file_name]
2170 set vhdl_version "-hdl_version VHDL_2008"
2173 set vhdl_version "-hdl_version VHDL_2008"
2180 return $vhdl_version
2187 proc FormatGeneric {generic} {
2188 if {[
string is integer "0x$generic"]} {
2189 return [
format "32'h%08X" "0x$generic"]
2192 return [
format "32'h%08X" 0]
2202 proc GenerateBitstream {{run_folder ""} {repo_path .} {njobs 1}} {
2203 Msg Info "Starting write bitstream flow..."
2205 set revision [get_current_revision]
2206 if {[
catch {execute_module -tool asm} result]} {
2207 Msg Error "Result: $result\n"
2208 Msg Error "Generate bitstream failed. See the report file.\n"
2210 Msg Info "Generate bitstream was successful for revision $revision.\n"
2213 Msg Info "Run GENERATEPROGRAMMINGDATA ..."
2214 if {[
catch {run_tool -name {GENERATEPROGRAMMINGDATA}}]} {
2215 Msg Error "GENERATEPROGRAMMINGDATA FAILED!"
2217 Msg Info "GENERATEPROGRAMMINGDATA PASSED."
2219 Msg Info "Sourcing Hog/Tcl/integrated/post-bitstream.tcl"
2220 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
2222 prj_run Export -impl Implementation0 -task Bitgen
2232 proc GenerateQsysSystem {qsysFile commandOpts} {
2234 if {[
file exists $qsysFile] != 0} {
2235 set qsysPath [
file dirname $qsysFile]
2236 set qsysName [
file rootname [
file tail $qsysFile]]
2237 set qsysIPDir "$qsysPath/$qsysName"
2238 set qsysLogFile "$qsysPath/$qsysName.qsys-generate.log"
2241 if {![
info exists ::env(QSYS_ROOTDIR)]} {
2242 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
2243 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
2244 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
2246 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
2249 set qsys_rootdir $::env(QSYS_ROOTDIR)
2252 set cmd "$qsys_rootdir/qsys-generate"
2253 set cmd_options "$qsysFile --output-directory=$qsysIPDir $commandOpts"
2254 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
2255 Msg Info "Executing: $cmd $cmd_options"
2256 Msg Info "Saving logfile in: $qsysLogFile"
2257 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
2258 set makeRet [
lindex [dict get $opt -errorcode] end]
2259 Msg CriticalWarning "$cmd returned with $makeRet"
2262 Msg Error " Could not execute command $cmd"
2266 set qsysIPFileList [
concat \
2267 [glob -nocomplain -directory $qsysIPDir -types f *.ip *.qip] \
2268 [glob -nocomplain -directory "$qsysIPDir/synthesis" -types f *.ip *.qip *.vhd *.vhdl]]
2269 foreach qsysIPFile $qsysIPFileList {
2270 if {[
file exists $qsysIPFile] != 0} {
2272 set_global_assignment -name $qsysIPFileType $qsysIPFile
2274 set IpMd5Sum [
Md5Sum $qsysIPFile]
2276 set fileDir [
file normalize "./hogTmp"]
2277 set fileName "$fileDir/.hogQsys.md5"
2278 if {![
file exists $fileDir]} {
2281 set hogQsysFile [open $fileName "a"]
2282 set fileEntry "$qsysIPFile\t$IpMd5Sum"
2283 puts $hogQsysFile $fileEntry
2288 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
2298 proc GenericToSimulatorString {prop_dict target} {
2300 dict for {theKey theValue} $prop_dict {
2301 set theValue [string trim $theValue]
2310 regexp {([0-9]*)('h)([0-9a-fA-F]*)} $theValue valueHexFull valueNumBits valueHexFlag valueHex
2311 regexp {^([0-9]*)$} $theValue valueIntFull ValueInt
2312 regexp {(?!^\d+$)^.+$} $theValue valueStrFull ValueStr
2313 if {[string tolower $target] == "vivado" || [string tolower $target] == "xsim"} {
2314 if {[string tolower $theValue] == "true" || [string tolower $theValue] == "false"} {
2315 set prj_generics "$prj_generics $theKey=[string tolower $theValue]"
2316 } elseif {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2317 set prj_generics "$prj_generics $theKey=$valueHexFull"
2318 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2319 set prj_generics "$prj_generics $theKey=$ValueInt"
2320 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2321 set prj_generics "$prj_generics $theKey=\"$ValueStr\""
2323 set prj_generics "$prj_generics $theKey=\"$theValue\""
2325 } elseif {[lsearch -exact [GetSimulators] [string tolower $target]] >= 0} {
2326 if {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2328 scan $valueNumBits %d numBits
2330 scan $valueHex %x numHex
2331 binary scan [binary format "I" $numHex] "B*" binval
2332 set numBits [expr {$numBits - 1}]
2333 set numBin [string range $binval end-$numBits end]
2334 set prj_generics "$prj_generics $theKey=\"$numBin\""
2335 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2336 set prj_generics "$prj_generics $theKey=$ValueInt"
2337 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2338 set prj_generics "$prj_generics {$theKey=\"$ValueStr\"}"
2340 set prj_generics "$prj_generics {$theKey=\"$theValue\"}"
2343 Msg Warning "Target : $target not implemented"
2346 return $prj_generics
2354 proc GetConfFiles {proj_dir} {
2355 Msg Debug "GetConfFiles called with proj_dir=$proj_dir"
2356 if {![
file isdirectory $proj_dir]} {
2357 Msg Error "$proj_dir is supposed to be the top project directory"
2360 set conf_file [
file normalize $proj_dir/hog.conf]
2361 set sim_file [
file normalize $proj_dir/sim.conf]
2362 set pre_tcl [
file normalize $proj_dir/pre-creation.tcl]
2363 set post_tcl [
file normalize $proj_dir/post-creation.tcl]
2364 set pre_rtl [
file normalize $proj_dir/pre-rtl.tcl]
2365 set post_rtl [
file normalize $proj_dir/post-rtl.tcl]
2367 return [list $conf_file $sim_file $pre_tcl $post_tcl $pre_rtl $post_rtl]
2376 proc GetCustomCommands {parameters {directory .}} {
2377 set commands_dict [dict create]
2378 set commands_files [glob -nocomplain $directory/*.tcl]
2380 if {[
llength $commands_files] == 0} {
2384 foreach file $commands_files {
2388 if {$custom_cmd eq ""} {
2393 set custom_cmd_name [dict get $custom_cmd NAME]
2395 Msg Debug "Loaded custom command '$custom_cmd_name' from $file"
2398 if {[dict exists $commands_dict $custom_cmd_name]} {
2399 Msg Error "Custom command '$custom_cmd_name' in $file already defined as: \[dict get $commands_dict $custom_cmd_name\]. Skipping."
2404 set custom_cmd_name [
string toupper $custom_cmd_name]
2405 dict set commands_dict $custom_cmd_name $custom_cmd
2408 return $commands_dict
2411 proc SanitizeCustomCommand {cmdDict file parameters} {
2416 foreach k [dict keys $cmdDict] {
2417 set K [
string toupper $k]
2418 dict set normalized $K [dict get $cmdDict $k]
2421 set cmdDict $normalized
2422 if {![dict exists $cmdDict NAME]} {
2423 Msg Error "Custom command in $file missing required key NAME. Skipping."
2426 if {![dict exists $cmdDict SCRIPT]} {
2427 Msg Error "Custom command '$[dict get $cmdDict NAME]' in $file missing SCRIPT. Skipping."
2432 set allowed {NAME DESCRIPTION OPTIONS CUSTOM_OPTIONS SCRIPT IDE NO_EXIT}
2433 foreach k [dict keys $cmdDict] {
2434 if {[lsearch -exact $allowed $k] < 0} {
2435 Msg Warning "Custom command '[dict get $cmdDict NAME]' in $file: unknown key '$k'. Allowed: $allowed. Skipping."
2440 set name [
string trim [dict get $cmdDict NAME]]
2442 Msg Error "Custom command in $file has empty NAME. Skipping."
2446 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]+$} $name]} {
2447 Msg Error "Custom command NAME '$name' (file $file) contains invalid characters."
2451 if {![dict exists $cmdDict DESCRIPTION]} {
2452 dict set cmdDict DESCRIPTION "No description provided."
2456 set hog_parameters {}
2457 foreach p $parameters {
2458 lappend hog_parameters [
lindex $p 0]
2463 if {[dict exists $cmdDict OPTIONS]} {
2464 set raw_opts [dict get $cmdDict OPTIONS]
2465 if {![
llength $raw_opts]} {
2469 foreach item $raw_opts {
2471 foreach p $parameters {
2472 set hog_parameter [
lindex $p 0]
2473 if {$item eq $hog_parameter} {
2474 lappend hog_options $p
2480 Msg Warning "Custom command '$name' in $file: option '$item' not found in Hog parameters. Skipping."
2483 dict set cmdDict OPTIONS $hog_options
2485 dict set cmdDict OPTIONS {}
2491 if {[dict exists $cmdDict CUSTOM_OPTIONS]} {
2492 set raw_opts [dict get $cmdDict CUSTOM_OPTIONS]
2493 if {![
llength $raw_opts]} {
2496 foreach item $raw_opts {
2497 if {[
llength $item] != 2 && [
llength $item] != 3} {
2498 Msg Error "Bad custom option: \[$item\]. Custom command '$name' in $file: \
2499 each CUSTOM_OPTIONS entry must be {option \"help\"} for flags \
2500 and {option \"default_value\" \"help\"} for options with arguments. Skipping command."
2504 if {[
llength $item] == 2} {
2505 lassign $item opt help
2508 lassign $item opt def help
2511 if {[
IsInList $opt $hog_parameters] == 1} {
2512 Msg Warning "Custom command '$name' in $file: option '$opt' already defined in Hog parameters. Skipping."
2518 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]*(\.arg)?$} $opt]} {
2519 Msg Error "Custom command '$name' in $file: invalid option name '$opt'."
2524 Msg Warning "Custom command '$name' option '$opt' has empty help text."
2528 dict set cmdDict CUSTOM_OPTIONS {}
2532 if {[dict exists $cmdDict NO_EXIT]} {
2533 set no_exit [dict get $cmdDict NO_EXIT]
2534 set no_exit [
string tolower [
string trim $no_exit]]
2536 if {$no_exit eq "1" || $no_exit eq "true"} {
2542 dict set cmdDict NO_EXIT $no_exit
2544 dict set cmdDict NO_EXIT 0
2550 proc LoadCustomCommandFile {file parameters} {
2552 set dir [
file dirname $file]
2554 unset -nocomplain ::hog_command
2555 set rc [
catch {source $file} err]
2558 Msg Error "Error sourcing custom command file $file: $err"
2561 if {![
info exists ::hog_command]} {
2562 Msg Warning "File $file did not define ::hog_command. Skipping."
2565 set cmdDict $::hog_command
2567 if {[
catch {dict size $cmdDict}]} {
2568 Msg Error "In $file ::hog_command is not a valid dict. Skipping."
2578 proc GetDateAndTime {commit} {
2579 set clock_seconds [
clock seconds]
2582 set date [
Git "log -1 --format=%cd --date=format:%d%m%Y $commit"]
2583 set timee [
Git "log -1 --format=%cd --date=format:00%H%M%S $commit"]
2585 Msg Warning "Found Git version older than 2.9.3. Using current date and time instead of commit time."
2586 set date [
clock format $clock_seconds -format {%d%m%Y}]
2587 set timee [
clock format $clock_seconds -format {00%H%M%S}]
2589 return [list $date $timee]
2601 proc GetFile {file fileset} {
2604 set Files [get_files -all $file -of_object [get_filesets $fileset]]
2605 set f [
lindex $Files 0]
2613 puts "***DEBUG Hog:GetFile $file"
2622 proc GetFileGenerics {filename {entity ""}} {
2624 if {[
string equal $file_type "VERILOG_FILE"] || [
string equal $file_type "SYSTEMVERILOG_FILE"]} {
2626 }
elseif {[
string equal $file_type "VHDL_FILE"]} {
2629 Msg CriticalWarning "Could not determine extension of top level file."
2638 proc GetGenericsFromConf {proj_dir} {
2639 set generics_dict [dict create]
2640 set top_dir "Top/$proj_dir"
2641 set conf_file "$top_dir/hog.conf"
2643 Msg Debug "GetGenericsFromConf called with proj_dir=$proj_dir, top_dir=$top_dir"
2645 if {[
file exists $conf_file]} {
2647 if {[dict exists $properties generics]} {
2648 set generics_dict [dict get $properties generics]
2651 Msg Warning "File $conf_file not found."
2653 return $generics_dict
2666 proc GetSimSets {project_name repo_path {simsets ""} {ghdl 0} {no_conf 0}} {
2667 set simsets_dict [dict create]
2668 set list_dir "$repo_path/Top/$project_name/list"
2670 if {$simsets != ""} {
2671 foreach s $simsets {
2672 set list_file "$list_dir/$s.sim"
2673 if {[
file exists $list_file]} {
2674 lappend list_files $list_file
2675 }
elseif {$s != "sim_1"} {
2676 Msg CriticalWarning "Simulation set list file $list_file not found."
2681 set list_files [glob -nocomplain -directory $list_dir "*.sim"]
2685 set proj_dir [
file normalize $repo_path/Top/$project_name]
2686 set sim_file [
file normalize $proj_dir/sim.conf]
2688 foreach list_file $list_files {
2689 set file_name [
file tail $list_file]
2690 set simset_name [
file rootname $file_name]
2691 set fp [open $list_file r]
2692 set file_data [read $fp]
2694 set data [
split $file_data "\n"]
2696 set firstline [
lindex $data 0]
2698 if {[regexp {^ *\# *Simulator} $firstline]} {
2699 set simulator_prop [regexp -all -inline {\S+} $firstline]
2700 set simulator [
string tolower [
lindex $simulator_prop end]]
2702 Msg Warning "Simulator not set in $simset_name.sim. \
2703 The first line of $simset_name.sim should be #Simulator <SIMULATOR_NAME>,\
2704 where <SIMULATOR_NAME> can be xsim, questa, modelsim, ghdl, riviera, activehdl,\
2705 ies, or vcs, e.g. #Simulator questa.\
2706 Setting simulator by default to xsim."
2707 set simulator "xsim"
2709 if {$simulator eq "skip_simulation"} {
2710 Msg Info "Skipping simulation for $simset_name"
2713 if {($ghdl == 1 && $simulator != "ghdl") || ($ghdl == 0 && $simulator == "ghdl")} {
2717 set SIM_PROPERTIES ""
2718 if {[
file exists $sim_file] && $no_conf == 0} {
2719 set SIM_PROPERTIES [
ReadConf $sim_file]
2722 set global_sim_props [dict create]
2723 dict set global_sim_props "properties" [
DictGet $SIM_PROPERTIES "sim"]
2724 dict set global_sim_props "generics" [
DictGet $SIM_PROPERTIES "generics"]
2725 dict set global_sim_props "hog" [
DictGet $SIM_PROPERTIES "hog"]
2728 set sim_dict [dict create]
2729 dict set sim_dict "simulator" $simulator
2730 if {[dict exists $SIM_PROPERTIES $simset_name]} {
2731 dict set sim_dict "properties" [
DictGet $SIM_PROPERTIES $simset_name]
2732 dict set sim_dict "generics" [
DictGet $SIM_PROPERTIES "$simset_name:generics"]
2733 dict set sim_dict "hog" [
DictGet $SIM_PROPERTIES "$simset_name:hog"]
2734 }
elseif {$no_conf == 0} {
2736 set conf_dict [
ReadConf $list_file]
2737 set sim_dict [
MergeDict $sim_dict $conf_dict 0]
2739 set sim_dict [
MergeDict $sim_dict $global_sim_props 0]
2740 dict set simsets_dict $simset_name $sim_dict
2742 return $simsets_dict
2750 proc GetSimsetGenericsFromConf {proj_dir} {
2751 set simsets_generics_dict [dict create]
2752 set top_dir "Top/$proj_dir"
2753 set conf_file "$top_dir/sim.conf"
2756 if {[
file exists $conf_file]} {
2759 set simsets_generics_dict [dict filter $properties key *:generics]
2761 Msg Warning "File $conf_file not found."
2763 return $simsets_generics_dict
2774 proc GetGroupName {proj_dir repo_dir} {
2775 if {[regexp {^(.*)/(Top|Projects)/+(.*?)/*$} $proj_dir dummy possible_repo_dir proj_or_top dir]} {
2777 if {[
file normalize $repo_dir] eq [
file normalize $possible_repo_dir]} {
2778 set group [
file dir $dir]
2779 if {$group == "."} {
2784 Msg Warning "Project directory $proj_dir seems to be in $possible_repo_dir which is not a the main Git repository $repo_dir."
2787 Msg Warning "Could not parse project directory $proj_dir"
2800 proc GetHogDescribe {proj_dir {repo_path .}} {
2801 lassign [
GetRepoVersions $proj_dir $repo_path] global_commit global_version
2802 if {$global_commit == 0} {
2804 set new_sha "[
string toupper [
GetSHA]]"
2807 set new_sha [
string toupper $global_commit]
2827 proc GetHogFiles {args} {
2830 if {[
catch {
package require cmdline} ERROR]} {
2831 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
2838 {list_files.arg "" "The file wildcard, if not specified all Hog list files will be looked for."}
2839 {sha_mode "Forwarded to ReadListFile, see there for info."}
2840 {ext_path.arg "" "Path for the external libraries forwarded to ReadListFile."}
2841 {print_log "Forwarded to ReadListFile, see there for info."}
2843 set usage "USAGE: GetHogFiles \[options\] <list path> <repository path>"
2844 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
2848 set list_path [
lindex $args 0]
2849 set repo_path [
lindex $args 1]
2851 set list_files $options(list_files)
2852 set sha_mode $options(sha_mode)
2853 set ext_path $options(ext_path)
2854 set print_log $options(print_log)
2856 if {$sha_mode == 1} {
2857 set sha_mode_opt "-sha_mode"
2862 if {$print_log == 1} {
2863 set print_log_opt "-print_log"
2865 set print_log_opt ""
2869 if {$list_files == ""} {
2870 set list_files {.src,.con,.sim,.ext}
2872 set libraries [dict create]
2873 set properties [dict create]
2874 set list_files [glob -nocomplain -directory $list_path "*{$list_files}"]
2875 set filesets [dict create]
2877 foreach f $list_files {
2878 set ext [
file extension $f]
2879 if {$ext == ".ext"} {
2880 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $ext_path"] l p fs
2882 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $repo_path"] l p fs
2885 set properties [
MergeDict $p $properties]
2886 Msg Debug "list file $f, filesets: $fs"
2888 Msg Debug "Merged filesets $filesets"
2896 set proj_conf [
file normalize [
file join [
file dirname $list_path] hog.conf]]
2897 if {[
file exists $proj_conf]} {
2899 set already_tracked [dict create]
2900 dict for {libname libfiles} $libraries {
2901 foreach lf $libfiles {dict set already_tracked [file normalize $lf] 1}
2903 dict for {comp_name cfg_abs} $hls_configs {
2904 if {[dict exists $already_tracked $cfg_abs]} {
2905 Msg Debug "HLS component '$comp_name': cfg $cfg_abs already tracked via a .src, skipping conf-based GetHogFiles discovery."
2908 set lib_name "${comp_name}.src"
2909 dict lappend libraries $lib_name $cfg_abs
2910 dict set already_tracked $cfg_abs 1
2911 set hls_extras [ExpandHlsConfigFiles $cfg_abs]
2912 Msg Info "HLS component '$comp_name' (from hog.conf): tracking [expr {1 + [llength $hls_extras]}] file(s) via $cfg_abs"
2913 if {$print_log == 1} {
2914 Msg Status "\nhog.conf \[hls:$comp_name\] (auto-discovered)"
2915 set rel_cfg [Relative $repo_path $cfg_abs 1]
2916 if {$rel_cfg eq ""} {set rel_cfg $cfg_abs}
2917 if {[llength $hls_extras] == 0} {
2918 Msg Status "└── $rel_cfg"
2920 Msg Status "├── $rel_cfg"
2921 Msg Status " Inside [file tail $cfg_abs] (HLS auto-expand):"
2924 set n_extras [llength $hls_extras]
2926 foreach hls_extra $hls_extras {
2928 if {[dict exists $already_tracked $hls_extra]} {continue}
2929 dict lappend libraries $lib_name $hls_extra
2930 dict set already_tracked $hls_extra 1
2931 if {$print_log == 1} {
2932 if {$i == $n_extras} {set pad "└──"} else {set pad "├──"}
2933 set rel_extra [Relative [file dirname $cfg_abs] $hls_extra 1]
2934 if {$rel_extra eq ""} {set rel_extra $hls_extra}
2935 Msg Status " $pad $rel_extra"
2941 return [list $libraries $properties $filesets]
2948 proc GetIDECommand {proj_conf {custom_ver ""}} {
2949 if {$custom_ver ne ""} {
2950 set ide_name_and_ver [
string tolower "$custom_ver"]
2951 }
elseif {[
file exists $proj_conf]} {
2952 set ide_name_and_ver [
string tolower [
GetIDEFromConf $proj_conf]]
2954 Msg Error "Configuration file $proj_conf not found."
2957 set ide_name [
lindex [regexp -all -inline {\S+} $ide_name_and_ver] 0]
2959 if {$ide_name eq "vivado" || $ide_name eq "vivado_vitis_classic" || $ide_name eq "vivado_vitis_unified" || $ide_name eq "vitis_unified"} {
2960 set command "vivado"
2962 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2963 set after_tcl_script " -tclargs "
2965 }
elseif {$ide_name eq "planahead"} {
2966 set command "planAhead"
2968 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2969 set after_tcl_script " -tclargs "
2971 }
elseif {$ide_name eq "quartus"} {
2972 set command "quartus_sh"
2974 set before_tcl_script " -t "
2975 set after_tcl_script " "
2977 }
elseif {$ide_name eq "libero"} {
2980 set command "libero"
2981 set before_tcl_script "SCRIPT:"
2982 set after_tcl_script " SCRIPT_ARGS:\""
2984 }
elseif {$ide_name eq "diamond"} {
2985 set command "diamondc"
2986 set before_tcl_script " "
2987 set after_tcl_script " "
2989 }
elseif {$ide_name eq "vitis_classic"} {
2992 set before_tcl_script ""
2993 set after_tcl_script " "
2995 }
elseif {$ide_name eq "ghdl"} {
2997 set before_tcl_script " "
2998 set after_tcl_script " "
3001 Msg Error "IDE: $ide_name not known."
3004 return [list $command $before_tcl_script $after_tcl_script $end_marker]
3010 proc GetIDEFromConf {conf_file} {
3011 set f [open $conf_file "r"]
3014 if {[regexp -all {^\# *(\w*) *(vitis_(?:classic|unified))? *(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)?(_.*)? *$} $line dummy ide vitisflag version patch]} {
3015 if {[
info exists vitisflag] && $vitisflag != ""} {
3016 set ide "${ide}_${vitisflag}"
3019 if {[
info exists version] && $version != ""} {
3025 set ret [list $ide $ver]
3027 Msg CriticalWarning "The first line of hog.conf should be \#<IDE name> <version>, \
3028 where <IDE name>. is quartus, vivado, planahead, libero, diamond or ghdl, \
3029 and <version> the tool version, e.g. \#vivado 2020.2. Will assume vivado."
3030 set ret [list "vivado" "0.0.0"]
3037 proc GetIDEName {} {
3039 return "ISE/PlanAhead"
3057 proc GetIDEVersion {} {
3060 regexp {\d+\.\d+(\.\d+)?} [version -short] ver
3065 regexp {[\.0-9]+} $quartus(version) ver
3068 set ver [get_libero_version]
3071 regexp {\d+\.\d+(\.\d+)?} [sys_install version] ver
3074 regexp {\d+\.\d+(\.\d+)?} [version] ver
3077 set vitis_output [
exec vitis --version 2>@1]
3078 regexp {[Vv]itis\s+v?(\d+\.\d+(?:\.\d+)?)} $vitis_output -> ver
3092 proc GetLinkedFile {link_file} {
3093 if {[
file type $link_file] eq "link"} {
3094 if {[
OS] == "windows"} {
3096 lassign [
ExecuteRet realpath $link_file] ret msg
3097 lassign [
ExecuteRet cygpath -m $msg] ret2 msg2
3098 if {$ret == 0 && $ret2 == 0} {
3100 Msg Debug "Found link file $link_file on Windows, the linked file is: $real_file"
3102 Msg CriticalWarning "[
file normalize $link_file] is a soft link. \
3103 Soft link are not supported on Windows and readlink.exe or cygpath.exe did not work: readlink=$ret: $msg, cygpath=$ret2: $msg2."
3104 set real_file $link_file
3108 set linked_file [
file link $link_file]
3109 set real_file [
file normalize [
file dirname $link_file]/$linked_file]
3112 if {![
file exists $real_file]} {
3113 Msg Warning "$link_file is a broken link, because the linked file: $real_file does not exist."
3116 Msg Warning "$link file is not a soft link"
3117 set real_file $link_file
3130 proc GetMaxThreads {proj_dir} {
3132 if {[
file exists $proj_dir/hog.conf]} {
3134 if {[dict exists $properties parameters]} {
3135 set propDict [dict get $properties parameters]
3136 if {[dict exists $propDict MAX_THREADS]} {
3137 set maxThreads [dict get $propDict MAX_THREADS]
3141 Msg Warning "File $proj_dir/hog.conf not found. Max threads will be set to default value 1"
3154 proc GetModifiedFiles {{repo_path "."} {pattern "."}} {
3157 set ret [
Git "ls-files --modified $pattern"]
3166 proc GetOptions {argv parameters} {
3169 set param_list [list]
3170 set option_list [list]
3172 foreach p $parameters {
3173 lappend param_list [
lindex $p 0]
3177 while {$index < [
llength $argv]} {
3178 set arg [
lindex $argv $index]
3179 if {[
string first - $arg] == 0} {
3180 set option [
string trimleft $arg "-"]
3182 lappend option_list $arg
3183 if {[lsearch -regex $param_list "$option\[.arg]?"] >= 0} {
3184 if {[lsearch -regex $param_list "$option\[.arg]"] >= 0} {
3185 lappend option_list [
lindex $argv $index]
3190 lappend arg_list $arg
3194 Msg Debug "Argv: $argv"
3195 Msg Debug "Options: $option_list"
3196 Msg Debug "Arguments: $arg_list"
3197 return [list $option_list $arg_list]
3215 proc GetProjectFiles {{project_file ""}} {
3216 set libraries [dict create]
3217 set simlibraries [dict create]
3218 set constraints [dict create]
3219 set properties [dict create]
3220 set consets [dict create]
3221 set srcsets [dict create]
3222 set simsets [dict create]
3225 set all_filesets [get_filesets]
3226 set simulator [get_property target_simulator [current_project]]
3227 set top [get_property "top" [current_fileset]]
3229 dict lappend properties $topfile "top=$top"
3231 foreach fs $all_filesets {
3232 if {$fs == "utils_1"} {
3237 set all_files [get_files -quiet -of_objects [get_filesets $fs]]
3238 set fs_type [get_property FILESET_TYPE [get_filesets $fs]]
3240 if {$fs_type == "BlockSrcs"} {
3242 set dict_fs "sources_1"
3246 foreach f $all_files {
3253 if {[
lindex [get_property IS_GENERATED [
GetFile $f $fs]] 0] != 0} {
3258 if {[get_property FILE_TYPE [
GetFile $f $fs]] == "Configuration Files"} {
3264 if {[get_property CORE_CONTAINER [
GetFile $f $fs]] != ""} {
3265 if {[
file extension $f] == ".xcix"} {
3266 set f [get_property CORE_CONTAINER [
GetFile $f $fs]]
3274 if {[get_property SCOPED_TO_REF [
GetFile $f $fs]] != ""} {
3275 dict lappend properties $f "scoped_to_ref=[get_property SCOPED_TO_REF [
GetFile $f $fs]]"
3280 if {[get_property SCOPED_TO_CELLS [
GetFile $f $fs]] != ""} {
3281 dict lappend properties $f "scoped_to_cells=[regsub " " [get_property SCOPED_TO_CELLS [
GetFile $f $fs]] ","]"
3285 if {[
IsInList "PARENT_COMPOSITE_FILE" [list_property [
GetFile $f $fs]]]} {
3290 if {[
file tail $f] == "nocattrs.dat"} {
3295 if {[
file extension $f] != ".coe"} {
3296 set f [
file normalize $f]
3299 set type [get_property FILE_TYPE [
GetFile $f $fs]]
3301 set lib [get_property -quiet LIBRARY [
GetFile $f $fs]]
3304 Msg Debug "File $f Extension [
file extension $f] Type [
lindex $type 0]"
3306 if {[
string equal [
lindex $type 0] "VHDL"] && [
llength $type] == 1} {
3308 }
elseif {[
string equal [
lindex $type 0] "VHDL"] && [
string equal [
lindex $type 1] "2019"]} {
3314 }
elseif {[
string equal [
lindex $type 0] "Block"] && [
string equal [
lindex $type 1] "Designs"]} {
3317 }
elseif {[
string equal $type "SystemVerilog"] && [
file extension $f] != ".sv" && [
file extension $f] != ".svp"} {
3318 set prop "SystemVerilog"
3319 }
elseif {[
string equal [
lindex $type 0] "XDC"] && [
file extension $f] != ".xdc"} {
3321 }
elseif {[
string equal $type "Verilog Header"] && [
file extension $f] != ".vh" && [
file extension $f] != ".svh"} {
3322 set prop "verilog_header"
3323 }
elseif {[
string equal $type "Verilog Template"] && [
file extension $f] == ".v" && [
file extension $f] != ".sv"} {
3324 set prop "verilog_template"
3326 set type [
lindex $type 0]
3330 if {![
string equal $prop ""]} {
3331 dict lappend properties $f $prop
3334 if {[
string equal $fs_type "SimulationSrcs"]} {
3336 if {[
string equal $type "VHDL"]} {
3337 set library "${lib}.sim"
3339 set library "others.sim"
3343 dict lappend simsets $dict_fs $library
3346 dict lappend simlibraries $library $f
3347 }
elseif {[
string equal $type "VHDL"]} {
3350 dict lappend srcsets $dict_fs "${lib}.src"
3352 dict lappend libraries "${lib}.src" $f
3353 }
elseif {[
string first "IP" $type] != -1} {
3356 dict lappend srcsets $dict_fs "ips.src"
3358 dict lappend libraries "ips.src" $f
3359 Msg Debug "Appending $f to ips.src"
3360 }
elseif {[
string equal $fs_type "Constrs"]} {
3363 dict lappend consets $dict_fs "sources.con"
3365 dict lappend constraints "sources.con" $f
3369 dict lappend srcsets $dict_fs "others.src"
3371 dict lappend libraries "others.src" $f
3372 Msg Debug "Appending $f to others.src"
3375 if {[
lindex [get_property -quiet used_in_synthesis [
GetFile $f $fs]] 0] == 0} {
3376 dict lappend properties $f "nosynth"
3378 if {[
lindex [get_property -quiet used_in_implementation [
GetFile $f $fs]] 0] == 0} {
3379 dict lappend properties $f "noimpl"
3381 if {[
lindex [get_property -quiet used_in_simulation [
GetFile $f $fs]] 0] == 0} {
3382 dict lappend properties $f "nosim"
3384 if {[
lindex [get_property -quiet IS_MANAGED [
GetFile $f $fs]] 0] == 0 && [
file extension $f] != ".xcix"} {
3385 dict lappend properties $f "locked"
3391 dict lappend properties "Simulator" [get_property target_simulator [current_project]]
3394 set file [open $project_file r]
3395 set in_file_manager 0
3397 while {[
gets $file line] >= 0} {
3399 if {[regexp {^KEY ActiveRoot \"([^\"]+)\"} $line -> value]} {
3400 set top [
string range $value 0 [
expr {[string first "::" $value] - 1}]]
3404 if {[regexp {^LIST FileManager} $line]} {
3405 set in_file_manager 1
3410 if {$in_file_manager && [regexp {^ENDLIST} $line]} {
3415 if {$in_file_manager && [regexp {^VALUE \"([^\"]+)} $line -> value]} {
3418 lassign [
split $value ,] file_path file_type
3421 set library "others"
3422 while {[
gets $file line] >= 0} {
3423 if {$line == "ENDFILE"} {
3426 regexp {^LIBRARY=\"([^\"]+)} $line -> library
3427 regexp {^PARENT=\"([^\"]+)} $line -> parent_file
3429 Msg Debug "Found file ${file_path} in project.."
3430 if {$parent_file == ""} {
3431 if {$file_type == "hdl"} {
3433 if {[
IsInList "${library}.src" [
DictGet $srcsets "sources_1"]] == 0} {
3434 dict lappend srcsets "sources_1" "${library}.src"
3436 dict lappend libraries "${library}.src" $file_path
3440 if {[
GetModuleName $file_path] == [
string tolower $top] && $top != ""} {
3441 Msg Debug "Found top module $top in $file_path"
3442 dict lappend properties $file_path "top=$top"
3444 }
elseif {$file_type == "tb_hdl"} {
3446 dict lappend simsets "sim_1" "${library}.sim"
3448 dict lappend simlibraries "${library}.sim" $file_path
3449 }
elseif {$file_type == "io_pdc" || $file_type == "sdc"} {
3451 dict lappend consets "constrs_1" "sources.con"
3453 dict lappend constraints "sources.con" $file_path
3460 set fileData [read [open $project_file]]
3462 set project_path [
file dirname $project_file]
3465 regsub {<\?xml.*\?>} $fileData "" fileData
3468 regexp {<Implementation.*?>(.*)</Implementation>} $fileData -> implementationContent
3472 set sourceRegex {<Source name="([^"]*?)" type="([^"]*?)" type_short="([^"]*?)".*?>(.*?)</Source>}
3474 set optionsRegex {<Options(.*?)\/>}
3475 regexp $optionsRegex $implementationContent -> prj_options
3476 foreach option $prj_options {
3477 if {[regexp {^top=\"([^\"]+)\"} $option match result]} {
3482 while {[regexp $sourceRegex $implementationContent match name type type_short optionsContent]} {
3483 Msg Debug "Found file ${name} in project..."
3484 set file_path [
file normalize $project_path/$name]
3486 set optionsRegex {<Options(.*?)\/>}
3487 regexp $optionsRegex $optionsContent -> options
3488 set library "others"
3490 foreach option $options {
3491 if {[
string first "System Verilog" $option]} {
3494 if {[regexp {^lib=\"([^\"]+)\"} $option match1 result]} {
3499 if {[regexp {syn_sim="([^"]*?)"} $match match_sim simonly]} {
3504 if {$type_short == "VHDL" || $type_short == "Verilog" || $type_short == "IPX"} {
3505 if {$ext == ".src"} {
3506 if {[
IsInList "${library}${ext}" [
DictGet $srcsets "sources_1"]] == 0} {
3507 dict lappend srcsets "sources_1" "${library}${ext}"
3509 dict lappend libraries "${library}${ext}" $file_path
3510 }
elseif {$ext == ".sim"} {
3512 dict lappend simsets "sim_1" "${library}.sim"
3514 dict lappend simlibraries "${library}.sim" $file_path
3520 Msg Debug "Found top module $top in $file_path"
3521 dict lappend properties $file_path "top=$top"
3523 }
elseif {$type_short == "SDC"} {
3525 dict lappend consets "constrs_1" "sources.con"
3527 dict lappend constraints "sources.con" $file_path
3531 regsub -- $match $implementationContent "" implementationContent
3534 return [list $libraries $properties $simlibraries $constraints $srcsets $simsets $consets]
3541 proc GetProjectFlavour {proj_name} {
3543 set flavour [
string map {. ""} [
file extension $proj_name]]
3544 if {$flavour != ""} {
3545 if {[
string is integer $flavour]} {
3546 Msg Info "Project $proj_name has flavour = $flavour, the generic variable FLAVOUR will be set to $flavour"
3548 Msg Warning "Project name has a unexpected non numeric extension, flavour will be set to -1"
3565 proc GetProjectVersion {proj_dir repo_path {ext_path ""} {sim 0}} {
3566 if {![
file exists $proj_dir]} {
3567 Msg CriticalWarning "$proj_dir not found"
3574 lassign [
GitRet {describe --tags --abbrev=0 --match "v*"}] ret result
3576 Msg CriticalWarning "No Hog versioning tags (v*) in repo"
3583 Msg Warning "Repository is not clean"
3591 Msg Debug "Project version $v_proj, latest tag $v_last"
3593 Msg Debug "The specified project was modified since official version."
3600 Msg Info "The specified project was modified in the latest official version $ret"
3601 }
elseif {$comp == -1} {
3602 Msg Info "The specified project was modified in a past official version $ret"
3618 proc GetRepoVersions {proj_dir repo_path {ext_path ""} {sim 0}} {
3619 if {[
catch {
package require cmdline} ERROR]} {
3620 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
3635 lappend SHAs [
GetSHA {Hog}]
3639 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
3640 Msg Debug "Hog submodule [
pwd] clean."
3641 lassign [
GetVer ./] hog_ver hog_hash
3643 Msg CriticalWarning "Hog submodule [
pwd] not clean, commit hash will be set to 0."
3644 set hog_hash "0000000"
3645 set hog_ver "00000000"
3651 foreach conf_file $conf_files {
3652 if {[
string match "$repo_path/*" $conf_file]} {
3653 set conf_file [
string replace $conf_file 0 [
string length $repo_path]]
3655 lappend project_files $conf_file
3658 lappend project_files Hog
3661 lassign [
GetVer [
join $conf_files]] top_ver top_hash
3662 lappend SHAs $top_hash
3663 lappend versions $top_ver
3670 lassign [
GetHogFiles -list_files "*.src" -sha_mode "$proj_dir/list/" $repo_path] src_files dummy
3671 dict for {f files} $src_files {
3672 # library names have a .src extension in values returned by GetHogFiles
3673 set name [file rootname [file tail $f]]
3674 if {[file ext $f] == ".oth"} {
3677 lassign [GetVer $files] ver hash
3678 # Msg Info "Found source list file $f, version: $ver commit SHA: $hash"
3680 lappend versions $ver
3682 lappend hashes $hash
3684 set relative_files ""
3685 foreach fil $files {
3686 if {[string match "$repo_path/*" $fil]} {
3687 set fil [string replace $fil 0 [string length $repo_path]]
3689 lappend relative_files $fil
3692 lappend project_files $f {*}$relative_files
3698 lassign [
GetHogFiles -list_files "*.con" -sha_mode "$proj_dir/list/" $repo_path] cons_files dummy
3699 dict for {f files} $cons_files {
3700 #library names have a .con extension in values returned by GetHogFiles
3701 set name [file rootname [file tail $f]]
3702 lassign [GetVer $files] ver hash
3703 #Msg Info "Found constraint list file $f, version: $ver commit SHA: $hash"
3705 Msg CriticalWarning "Constraints file $f not found in Git."
3707 lappend cons_hashes $hash
3709 lappend versions $ver
3710 set relative_files ""
3711 foreach fil $files {
3712 if {[string match "$repo_path/*" $fil]} {
3713 set fil [string replace $fil 0 [string length $repo_path]]
3715 lappend relative_files $fil
3717 lappend project_files $f {*}$relative_files
3724 lassign [
GetHogFiles -list_files "*.sim" -sha_mode "$proj_dir/list/" $repo_path] sim_files dummy
3725 dict for {f files} $sim_files {
3726 #library names have a .sim extension in values returned by GetHogFiles
3727 set name [file rootname [file tail $f]]
3728 lassign [GetVer $files] ver hash
3729 #Msg Info "Found simulation list file $f, version: $ver commit SHA: $hash"
3730 lappend sim_hashes $hash
3732 lappend versions $ver
3733 set relative_files ""
3734 foreach fil $files {
3735 if {[string match "$repo_path/*" $fil]} {
3736 set fil [string replace $fil 0 [string length $repo_path]]
3738 lappend relative_files $fil
3740 lappend project_files $f {*}$relative_files
3748 Msg CriticalWarning "No hashes found for constraints files (not in git)"
3751 set cons_hash [
string tolower [
Git "log --format=%h -1 $cons_hashes"]]
3758 set ext_files [glob -nocomplain "$proj_dir/list/*.ext"]
3761 foreach f $ext_files {
3762 set name [
file rootname [
file tail $f]]
3765 lappend ext_names $name
3766 lappend ext_hashes $hash
3769 lappend versions $ext_ver
3770 lappend project_files $f
3773 set file_data [read $fp]
3775 set data [
split $file_data "\n"]
3777 foreach line $data {
3778 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
3780 set file_and_prop [regexp -all -inline {\S+} $line]
3781 set hdlfile [
lindex $file_and_prop 0]
3782 set hdlfile $ext_path/$hdlfile
3783 if {[
file exists $hdlfile]} {
3784 set hash [
lindex $file_and_prop 1]
3785 set current_hash [
Md5Sum $hdlfile]
3786 if {[
string first $hash $current_hash] == -1} {
3787 Msg CriticalWarning "File $hdlfile has a wrong hash. Current checksum: $current_hash, expected: $hash"
3795 if {[
llength [glob -nocomplain $proj_dir/list/*.ipb]] > 0} {
3797 lassign [
GetHogFiles -list_files "*.ipb" -sha_mode "$proj_dir/list/" $repo_path] xml_files dummy
3798 set xml_source_files [dict get $xml_files "xml.ipb"]
3799 lassign [
GetVer $xml_source_files] xml_ver xml_hash
3800 lappend SHAs $xml_hash
3801 lappend versions $xml_ver
3802 set relative_files ""
3803 foreach fil $xml_source_files {
3804 if {[
string match "$repo_path/*" $fil]} {
3805 set fil [
string replace $fil 0 [
string length $repo_path]]
3807 lappend relative_files $fil
3809 lappend project_files {*}[glob $proj_dir/list/*.ipb] {*}$relative_files
3812 Msg Debug "This project does not use IPbus XMLs"
3817 set user_ip_repos ""
3818 set user_ip_repo_hashes ""
3819 set user_ip_repo_vers ""
3821 if {[
file exists [
lindex $conf_files 0]]} {
3822 set PROPERTIES [
ReadConf [
lindex $conf_files 0]]
3823 if {[dict exists $PROPERTIES main]} {
3824 set main [dict get $PROPERTIES main]
3825 dict for {p v} $main {
3826 if {[string tolower $p] == "ip_repo_paths"} {
3828 if {[file isdirectory "$repo_path/$repo"]} {
3829 set repo_file_list [glob -nocomplain "$repo_path/$repo/*"]
3830 if {[llength $repo_file_list] == 0} {
3831 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3833 lappend user_ip_repos "$repo_path/$repo"
3842 foreach repo $user_ip_repos {
3843 if {[
file isdirectory $repo]} {
3844 set repo_file_list [glob -nocomplain "$repo/*"]
3845 if {[
llength $repo_file_list] != 0} {
3846 lassign [
GetVer $repo] ver sha
3847 lappend user_ip_repo_hashes $sha
3848 lappend user_ip_repo_vers $ver
3849 lappend versions $ver
3850 lappend project_files $repo
3852 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3855 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory does not exist."
3870 append dirty [
Git "status --untracked-files=no --porcelain" $project_files]
3874 Msg Debug "Project-relevant files are clean."
3877 Msg CriticalWarning "Project-relevant files not clean, commit hash and version will be set to 0."
3884 while {$found == 0} {
3885 set global_commit [
Git "log --topo-order --format=%h -1 --abbrev=7 $SHAs"]
3890 if {$common_child == 0} {
3891 Msg CriticalWarning "The commit $sha is not an ancestor of the global commit $global_commit, which is OK. \
3892 But $sha and $global_commit do not have any common child, which is NOT OK. \
3893 This is probably do to a REBASE that is forbidden in Hog methodology as it changes git history. \
3894 Hog cannot guarantee the accuracy of the SHAs. \
3895 A way to fix this is to make a commit that touches all the projects in the repositories (e.g. change the Hog version), \
3896 but please do not rebase in the official branches in the future."
3898 Msg Info "The commit $sha is not an ancestor of the global commit $global_commit, adding the first common child $common_child instead..."
3899 lappend SHAs $common_child
3909 set global_commit "0000000"
3910 set global_version "00000000"
3915 set top_hash [
format %+07s $top_hash]
3916 set cons_hash [
format %+07s $cons_hash]
3917 return [list $global_commit $global_version \
3918 $hog_hash $hog_ver $top_hash $top_ver \
3919 $libs $hashes $vers $cons_ver $cons_hash \
3920 $ext_names $ext_hashes $xml_hash $xml_ver \
3921 $user_ip_repos $user_ip_repo_hashes $user_ip_repo_vers]
3930 proc GetSHA {{path ""}} {
3933 lassign [
GitRet {log --format=%h --abbrev=7 -1}] status result
3935 return [
string tolower $result]
3937 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3943 set repo_path [
lindex [
Git {rev-parse --show-toplevel}] 0]
3948 set file_in_module 0
3949 if {[
file exists .gitmodules]} {
3950 lassign [
GitRet "config --file .gitmodules --get-regexp path"] status result
3952 set submodules [
split $result "\n"]
3955 Msg Warning "Something went wrong while trying to find submodules: $result"
3958 foreach mod $submodules {
3959 set module [
lindex $mod 1]
3960 if {[
string first "$repo_path/$module" $f] == 0} {
3962 set file_in_module 1
3963 lappend paths "$module"
3968 if {$file_in_module == 0} {
3974 lassign [
GitRet {log --format=%h --abbrev=7 -1} $paths] status result
3976 return [
string tolower $result]
3978 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3982 return [
string tolower $result]
3986 proc GetSimulators {} {
3987 set SIMULATORS [list "modelsim" "questa" "riviera" "activehdl" "ies" "vcs"]
3992 proc GetTopFile {} {
3994 set compile_order_prop [get_property source_mgmt_mode [current_project]]
3995 if {$compile_order_prop ne "All"} {
3996 Msg CriticalWarning "Compile order is not set to automatic, setting it now..."
3997 set_property source_mgmt_mode All [current_project]
3998 update_compile_order -fileset sources_1
4000 return [
lindex [get_files -quiet -compile_order sources -used_in synthesis -filter {FILE_TYPE =~ "VHDL*" || FILE_TYPE =~ "*Verilog*" }] end]
4001 }
elseif {[
IsISE]} {
4002 debug::design_graph_mgr -create [current_fileset]
4003 debug::design_graph -add_fileset [current_fileset]
4004 debug::design_graph -update_all
4005 return [
lindex [debug::design_graph -get_compile_order] end]
4007 Msg Error "GetTopFile not yet implemented for this IDE"
4012 proc GetTopModule {} {
4014 return [get_property top [current_fileset]]
4016 Msg Error "GetTopModule not yet implemented for this IDE"
4026 proc GetVer {path {force_develop 0}} {
4030 Msg CriticalWarning "Empty SHA found for ${path}. Commit to Git to resolve this warning."
4033 set p [
lindex $path 0]
4034 if {[
file isdirectory $p]} {
4037 cd [
file dirname $p]
4039 set repo_path [
Git {rev-parse --show-toplevel}]
4042 return [list [
GetVerFromSHA $SHA $repo_path $force_develop] $SHA]
4053 proc GetVerFromSHA {SHA repo_path {force_develop 0}} {
4055 Msg CriticalWarning "Empty SHA found"
4058 lassign [
GitRet "tag --sort=creatordate --contain $SHA -l v*.*.* -l b*v*.*.*"] status result
4061 if {[regexp {^ *$} $result]} {
4063 lassign [
GitRet "log --oneline --pretty=\"%d\""] status2 tag_list
4066 set pattern {tag: v\d+\.\d+\.\d+}
4067 set real_tag_list {}
4068 foreach x $tag_list {
4069 set x_untrimmed [regexp -all -inline $pattern $x]
4070 regsub "tag: " $x_untrimmed "" x_trimmed
4071 set tt [
lindex $x_trimmed 0]
4072 if {![
string equal $tt ""]} {
4073 lappend real_tag_list $tt
4077 Msg Debug "Cleaned up list: $real_tag_list."
4079 set sorted_tags [lsort -decreasing -command CompareVersions $real_tag_list]
4081 Msg Debug "Sorted Tag list: $sorted_tags"
4083 set tag [
lindex $sorted_tags 0]
4086 set pattern {v\d+\.\d+\.\d+}
4087 if {![regexp $pattern $tag]} {
4088 Msg CriticalWarning "No Hog version tags found in this repository."
4093 set repo_conf $repo_path/Top/repo.conf
4097 set hotfix_prefix "hotfix/"
4098 set minor_prefix "minor_version/"
4099 set major_prefix "major_version/"
4101 set enable_develop_branch $force_develop
4103 set branch_name [
Git {rev-parse --abbrev-ref HEAD}]
4105 if {[
file exists $repo_conf]} {
4106 set PROPERTIES [
ReadConf $repo_conf]
4108 if {[dict exists $PROPERTIES main]} {
4109 set mainDict [dict get $PROPERTIES main]
4112 if {[dict exists $mainDict ENABLE_DEVELOP_BRANCH]} {
4113 set enable_develop_branch [dict get $mainDict ENABLE_DEVELOP_BRANCH]
4119 if {[dict exists $PROPERTIES prefixes]} {
4120 set prefixDict [dict get $PROPERTIES prefixes]
4122 if {[dict exists $prefixDict HOTFIX]} {
4123 set hotfix_prefix [dict get $prefixDict HOTFIX]
4125 if {[dict exists $prefixDict MINOR_VERSION]} {
4126 set minor_prefix [dict get $prefixDict MINOR_VERSION]
4128 if {[dict exists $prefixDict MAJOR_VERSION]} {
4129 set major_prefix [dict get $prefixDict MAJOR_VERSION]
4135 if {[
string match "HEAD" $branch_name]} {
4136 Msg Debug "Detached HEAD detected - attempting to find branch name"
4141 set log_refs [
Git {show -s --pretty=%D HEAD}]
4142 set branch_list [
split $log_refs ","]
4143 Msg Debug "list of possible branch refs $log_refs"
4149 set match_prefixes [list $hotfix_prefix $minor_prefix $major_prefix]
4150 set prev_branch_name $branch_name
4152 foreach br $branch_list {
4153 foreach pr $match_prefixes {
4154 if {[
string match "$pr*" [
string trim $br]]} {
4155 set branch_name [
string trim $br]
4161 if {!$match_count == 1} {
4162 set branch_name $prev_branch_name
4163 Msg Debug "Branch name not found. Using $branch_name"
4165 Msg Debug "Branch name found: $branch_name"
4169 if {$enable_develop_branch == 1} {
4170 if {[
string match "$hotfix_prefix*" $branch_name]} {
4175 if {[
string match "$major_prefix*" $branch_name]} {
4177 set version_level major
4178 }
elseif {[
string match "$minor_prefix*" $branch_name] || ($enable_develop_branch == 1 && $is_hotfix == 0)} {
4180 set version_level minor
4183 set version_level patch
4187 Msg CriticalWarning "Tag $tag does not contain a Hog compatible version in this repository."
4190 }
elseif {$mr == 0} {
4191 switch $version_level {
4206 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."
4212 set vers [
split $result "\n"]
4213 set ver [
lindex $vers 0]
4215 if {[regexp {^v.*$} $v]} {
4223 Msg CriticalWarning "Error while trying to find tag for $SHA"
4231 set M [
format %02X $M]
4232 set m [
format %02X $m]
4233 set c [
format %04X $c]
4234 }
elseif {$M > -1} {
4236 set M [
format %02X $M]
4237 set m [
format %02X $m]
4238 set c [
format %04X $c]
4240 Msg Warning "Tag does not contain a properly formatted version: $ver"
4241 set M [
format %02X 0]
4242 set m [
format %02X 0]
4243 set c [
format %04X 0]
4256 proc Git {command {files ""}} {
4257 lassign [
GitRet $command $files] ret result
4259 Msg Error "Code $ret returned by git running: $command -- $files"
4270 proc GetModuleName {filename} {
4272 if {![
file exists $filename]} {
4273 Msg CriticalWarning "Error: File $filename does not exist."
4278 set fileId [open $filename r]
4281 set file_content [read $fileId]
4287 if {[
file extension $filename] == ".vhd" || [
file extension $filename] == ".vhdl"} {
4289 set file_content [
string tolower $file_content]
4291 set pattern {(?m)^\s*entity\s+(\S+)\s+is}
4292 }
elseif {[
file extension $filename] == ".v" || [
file extension $filename] == ".sv"} {
4294 set pattern {\n\s*module\s*(\w+)(\s*|\(|\n)}
4296 Msg Debug "File is neither VHDL nor Verilog... Returning empty string..."
4301 if {[regexp $pattern $file_content match module_name]} {
4304 Msg Debug "No module was found in $filename. Returning an empty string..."
4312 proc GetVerilogGenerics {file} {
4313 set fp [open $file r]
4319 foreach line [
split $data "\n"] {
4320 regsub "^\\s*\/\/.*" $line "" line
4321 regsub "(.*)\/\/.*" $line {\1} line
4322 if {![
string equal $line ""]} {
4323 append lines $line " "
4328 regsub -all {/\*.*\*/} $lines "" lines
4331 set punctuation [list]
4332 foreach char [list "(" ")" ";" "," " " "!" "<=" ":=" "=" "\[" "\]"] {
4333 lappend punctuation $char "\000$char\000"
4337 set tokens [
split [
string map $punctuation $lines] \000]
4339 set parameters [dict create]
4348 foreach token $tokens {
4349 set token [
string trim $token]
4350 if {![
string equal "" $token]} {
4351 if {[
string equal [
string tolower $token] "parameter"]} {
4352 set state $PARAM_NAME
4353 }
elseif {[
string equal $token ")"] || [
string equal $token ";"]} {
4355 }
elseif {$state == $PARAM_WIDTH} {
4356 if {[
string equal $token "\]"]} {
4357 set state $PARAM_NAME
4359 }
elseif {$state == $PARAM_VALUE} {
4360 if {[
string equal $token ","]} {
4361 set state $PARAM_NAME
4362 }
elseif {[
string equal $token ";"]} {
4367 }
elseif {$state == $PARAM_NAME} {
4368 if {[
string equal $token "="]} {
4369 set state $PARAM_VALUE
4370 }
elseif {[
string equal $token "\["]} {
4371 set state $PARAM_WIDTH
4372 }
elseif {[
string equal $token ","]} {
4373 set state $PARAM_NAME
4374 }
elseif {[
string equal $token ";"]} {
4376 }
elseif {[
string equal $token ")"]} {
4379 dict set parameters $token "integer"
4392 proc GetVhdlGenerics {file {entity ""}} {
4393 set fp [open $file r]
4399 foreach line [
split $data "\n"] {
4400 regsub "^\\s*--.*" $line "" line
4401 regsub "(.*)--.*" $line {\1} line
4402 if {![
string equal $line ""]} {
4403 append lines $line " "
4408 set generic_block ""
4409 set generics [dict create]
4411 if {1 == [
string equal $entity ""]} {
4412 regexp {(?i).*entity\s+([^\s]+)\s+is} $lines _ entity
4415 set generics_regexp "(?i).*entity\\s+$entity\\s+is\\s+generic\\s*\\((.*)\\)\\s*;\\s*port.*end.*$entity"
4417 if {[regexp $generics_regexp $lines _ generic_block]} {
4419 foreach line [
split $generic_block ";"] {
4421 regexp {(.*):\s*([A-Za-z0-9_]+).*} $line _ generic type
4424 set splits [
split $generic ","]
4425 foreach split $splits {
4426 dict set generics [
string trim $split] [
string trim $type]
4434 proc GHDL {command logfile} {
4435 set ret [
catch {
exec -ignorestderr ghdl {*}$command >>& $logfile} result options]
4440 return [list $ret $result]
4452 proc GitRet {command {files ""}} {
4455 set ret [
catch {
exec -ignorestderr git {*}$command} result]
4457 set ret [
catch {
exec -ignorestderr git {*}$command -- {*}$files} result]
4459 return [list $ret $result]
4467 proc GitVersion {target_version} {
4468 set ver [
split $target_version "."]
4469 set v [
Git --version]
4471 set current_ver [
split [
lindex $v 2] "."]
4472 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
4473 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
4474 return [
expr {$target <= $current}]
4487 proc RcloneFileExists {remote_file config_path} {
4488 if {[
catch {
exec rclone ls $remote_file --config $config_path 2>@1} result opt] == 0} {
4492 set rclone_ret [
lindex [dict get $opt -errorcode] end]
4493 if {$rclone_ret ne "3" && $rclone_ret ne "4"} {
4494 Msg CriticalWarning "Could not check if $remote_file is in the Rclone repository: $result"
4496 Msg Debug "$remote_file not found in the Rclone repository (rclone exit code $rclone_ret)."
4513 proc HandleIP {what_to_do xci_file ip_path repo_path on_eos on_rclone rclone_config_path {gen_dir "."} {force 0}} {
4515 if {!($what_to_do eq "push") && !($what_to_do eq "pull")} {
4516 Msg Error "You must specify push or pull as first argument."
4519 if {[
catch {
package require tar} TARPACKAGE]} {
4520 Msg CriticalWarning "Cannot find package tar. You can fix this by installing package \"tcllib\""
4527 if {!([
file exists $xci_file])} {
4528 Msg CriticalWarning "Could not find $xci_file."
4534 set xci_path [
file dirname $xci_file]
4535 set xci_name [
file tail $xci_file]
4536 set xci_ip_name [
file rootname [
file tail $xci_file]]
4537 set xci_dir_name [
file tail $xci_path]
4538 set gen_path $gen_dir
4540 set hash [
Md5Sum $xci_file]
4541 set file_name $xci_name\_$hash
4545 set scope [get_property -quiet SCOPE [get_ips $xci_ip_name]]
4547 Msg Debug "IP $xci_name is a subcore of a BD file, will not pull/push it from/to the remote directory."
4551 Msg Info "Preparing to $what_to_do IP: $xci_name ($file_name)."
4553 if {$what_to_do eq "push"} {
4556 if {$on_rclone == 1} {
4562 Msg Info "IP already in the Rclone repository, will not copy..."
4564 Msg Info "IP already in the Rclone repository, will forcefully replace..."
4569 }
elseif {$on_eos == 1} {
4570 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4575 Msg Info "IP already in the EOS repository, will not copy..."
4577 Msg Info "IP already in the EOS repository, will forcefully replace..."
4583 if {[
file exists "$ip_path/$file_name.tar"]} {
4585 Msg Info "IP already in the local repository, will not copy..."
4587 Msg Info "IP already in the local repository, will forcefully replace..."
4596 if {$will_copy == 1} {
4598 Msg Info "Looking for generated files in $gen_path..."
4599 set ip_gen_files [glob -nocomplain $gen_path/*]
4603 if {[
llength $ip_gen_files] > 0} {
4604 Msg Info "Found some IP synthesised files matching $xci_ip_name"
4605 if {$will_remove == 1} {
4606 Msg Info "Removing old synthesised directory $ip_path/$file_name.tar..."
4607 if {$on_rclone == 1} {
4608 lassign [
ExecuteRet rclone delete $ip_path/$file_name.tar --config $rclone_config_path] ret result
4610 Msg CriticalWarning "Could not delete file from Rclone: $result"
4612 }
elseif {$on_eos == 1} {
4613 eos "rm -rf $ip_path/$file_name.tar" 5
4615 file delete -force "$ip_path/$file_name.tar"
4619 Msg Info "Creating local archive with IP generated files..."
4622 set at_least_one_long 0
4623 foreach f $ip_gen_files {
4624 set new_f "[
Relative [
file normalize $repo_path] $f]"
4625 set len [
string length $new_f]
4627 Msg Warning "One file in $xci_ip_name is too long ($len chars): $new_f"
4628 set at_least_one_long 1
4630 lappend tar_files $new_f
4633 Msg Debug "Tar files: $tar_files"
4634 if {$at_least_one_long == 1} {
4635 Msg Warning "Using regular tar, please cross your fingers..."
4637 lassign [
ExecuteRet tar -cf $file_name.tar {*}$tar_files] ret_tar result_tar
4638 if {$ret_tar != 0} {
4639 Msg CriticalWarning "Something went wrong when using regular tar. Error message: $result_tar"
4642 if {[
catch { ::tar::create $file_name.tar $tar_files} err]} {
4643 Msg Warning "Tcl built-in tar failed: $err, will try regular tar..."
4645 lassign [
ExecuteRet tar -cf $file_name.tar {*}$tar_files] ret_tar result_tar
4646 if {$ret_tar != 0} {
4647 Msg CriticalWarning "Something went wrong when using regular tar. Error message: $result_tar"
4652 Msg Info "Copying IP generated files for $xci_name..."
4653 if {$on_rclone == 1} {
4654 lassign [
ExecuteRet rclone copyto $file_name.tar $ip_path/$file_name.tar --config $rclone_config_path] ret result
4656 Msg CriticalWarning "Something went wrong when copying the IP files to Rclone. Error message: $result"
4658 }
elseif {$on_eos == 1} {
4659 lassign [
ExecuteRet xrdcp -f -s $file_name.tar $::env(EOS_MGM_URL)//$ip_path/] ret msg
4661 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4664 Copy "$file_name.tar" "$ip_path/"
4666 Msg Info "Removing local archive"
4667 file delete $file_name.tar
4669 Msg Warning "Could not find synthesized files matching $gen_path/$file_name*"
4672 }
elseif {$what_to_do eq "pull"} {
4673 if {$on_rclone == 1} {
4676 Msg Info "Nothing for $xci_name was found in the Rclone repository, cannot pull."
4680 Msg Info "IP $xci_name found in the Rclone repository $ip_path, copying it locally to $repo_path..."
4681 lassign [
ExecuteRet rclone copyto $ip_path/$file_name.tar $file_name.tar --config $rclone_config_path] ret_copy result_copy
4682 if {$ret_copy != 0} {
4683 Msg CriticalWarning "Something went wrong when copying the IP files from Rclone. Error message: $result_copy"
4686 }
elseif {$on_eos == 1} {
4687 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4689 Msg Info "Nothing for $xci_name was found in the EOS repository, cannot pull."
4693 set remote_tar "$::env(EOS_MGM_URL)//$ip_path/$file_name.tar"
4694 Msg Info "IP $xci_name found in the repository $remote_tar, copying it locally to $repo_path..."
4696 lassign [
ExecuteRet xrdcp -f -r -s $remote_tar $repo_path] ret msg
4698 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4702 if {[
file exists "$ip_path/$file_name.tar"]} {
4703 Msg Info "IP $xci_name found in local repository $ip_path/$file_name.tar, copying it locally to $repo_path..."
4704 Copy $ip_path/$file_name.tar $repo_path
4706 Msg Info "Nothing for $xci_name was found in the local IP repository, cannot pull."
4712 if {[
file exists $file_name.tar]} {
4713 remove_files $xci_file
4714 Msg Info "Extracting IP files from archive to $repo_path..."
4715 ::tar::untar $file_name.tar -dir $repo_path -noperms
4716 Msg Info "Removing local archive"
4717 file delete $file_name.tar
4718 add_files -norecurse -fileset sources_1 $xci_file
4731 proc HexVersionToString {version} {
4732 scan [
string range $version 0 1] %x M
4733 scan [
string range $version 2 3] %x m
4734 scan [
string range $version 4 7] %x c
4739 proc ImportTclLib {} {
4742 if {[
info exists env(HOG_TCLLIB_PATH)]} {
4743 lappend auto_path $env(HOG_TCLLIB_PATH)
4746 puts "ERROR: To run Hog with Microsemi Libero SoC or Lattice Diamond, you need to define the HOG_TCLLIB_PATH variable."
4761 proc InitLauncher {script tcl_path parameters commands argv {custom_commands ""}} {
4762 set repo_path [
file normalize "$tcl_path/../.."]
4764 set bin_path [
file normalize "$tcl_path/../../bin"]
4765 set top_path [
file normalize "$tcl_path/../../Top"]
4767 set cmd_lines [
split $commands "\n"]
4769 set command_options [dict create]
4770 set directive_descriptions [dict create]
4771 set directive_names [dict create]
4772 set common_directive_names [dict create]
4773 set directives_with_optional_projects ""
4774 set custom_command ""
4775 set custom_command_options ""
4777 foreach l $cmd_lines {
4779 if {[regexp {\\(.*) \{\#} $l minc d]} {
4780 lappend directives_with_projects $d
4781 set current_directive $d
4784 if {[regexp {^[^#]* allow_empty_proj} $l minc dd]} {
4785 lappend directives_with_optional_projects $current_directive
4789 if {[regexp {\\(.*) \{} $l minc regular_expression]} {
4790 lappend directive_regex $regular_expression
4794 if {[regexp {\#\s*NAME(\*)?:\s*(.*)\s*} $l minc star name]} {
4795 dict set directive_names $name $regular_expression
4797 dict set common_directive_names $name $regular_expression
4800 set directive_names [
DictSort $directive_names]
4801 set common_directive_names [
DictSort $common_directive_names]
4804 if {[regexp {\#\s*DESCRIPTION:\s*(.*)\s*} $l minc x]} {
4805 dict set directive_descriptions $regular_expression $x
4809 if {[regexp {\#\s*OPTIONS:\s*(.*)\s*} $l minc x]} {
4810 dict set command_options $regular_expression [
split [regsub -all {[ \t\n]+} $x {}] ","]
4814 set short_usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nMost common directives (case insensitive):"
4816 dict for {key value} $common_directive_names {
4817 set short_usage "$short_usage\n - $key: [dict get $directive_descriptions $value]"
4820 if {[
string length $custom_commands] > 0} {
4821 Msg Debug "Found custom commands to add to short short_usage."
4822 set short_usage "$short_usage\n\nCustom commands:"
4823 dict for {key command} $custom_commands {
4824 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4825 set short_usage "$short_usage\n - $key: [dict get $command DESCRIPTION]"
4830 set short_usage "$short_usage\n\n\
4831 To see all the available directives, run:\n./Hog/Do HELP\n\n\
4832 To list available options for the chosen directive run:\n\
4833 ./Hog/Do <directive> HELP\n
4836 set usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nDirectives (case insensitive):"
4838 dict for {key value} $directive_names {
4839 set usage "$usage\n - $key: [dict get $directive_descriptions $value]"
4843 if {[
string length $custom_commands] > 0} {
4844 Msg Debug "Found custom commands to add to short usage."
4845 set usage "$usage\n\nCustom commands:"
4846 dict for {key command} $custom_commands {
4847 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4848 set usage "$usage\n - $key: [dict get $command DESCRIPTION]"
4853 set usage "$usage\n\nTo list available options for the chosen directive run:\n./Hog/Do <directive> HELP"
4862 Msg Debug "HogEnv.conf found"
4869 if {[
catch {
package require cmdline} ERROR]} {
4870 Msg Debug "The cmdline Tcl package was not found, sourcing it from Hog..."
4871 source $tcl_path/utils/cmdline.tcl
4874 set argv [regsub -all {(?i) HELP\y} $argv " -help"]
4879 set custom_parameters [list]
4880 dict for {key command} $custom_commands {
4881 set custom_parameters [concat $custom_parameters [dict get $command CUSTOM_OPTIONS]]
4884 lassign [
GetOptions $argv [
concat $custom_parameters $parameters]] option_list arg_list
4886 if {[
IsInList "-all" $option_list]} {
4895 set directive [
string toupper [
lindex $arg_list 0]]
4898 set argument_is_no_project 1
4900 set NO_DIRECTIVE_FOUND 0
4901 switch -regexp -- $directive "$commands"
4903 if {$NO_DIRECTIVE_FOUND == 1} {
4904 if {[
string length $custom_commands] > 0 && [dict exists $custom_commands $directive]} {
4905 set custom_command $directive
4906 set custom_command_dict [
DictGet $custom_commands $directive]
4907 set custom_command_hog_parameters [
DictGet $custom_command_dict OPTIONS]
4908 set custom_command_options [
DictGet $custom_command_dict CUSTOM_OPTIONS]
4909 set custom_command_options [
concat $custom_command_hog_parameters $custom_command_options]
4911 Msg Status "ERROR: Unknown directive $directive.\n\n"
4917 if {[
IsInList $directive $directives_with_projects 1]} {
4918 set argument_is_no_project 0
4922 if {$directive != ""} {
4923 if {[
IsInList $directive $directives_with_projects 1]} {
4924 puts "usage: ./Hog/Do \[OPTIONS\] $directive <project>\n"
4925 }
elseif {[regexp "^COMPSIM(LIB)?$" $directive]} {
4926 puts "usage: ./Hog/Do \[OPTIONS\] $directive <simulator>\n"
4928 puts "usage: ./Hog/Do \[OPTIONS\] $directive \n"
4931 dict for {dir desc} $directive_descriptions {
4932 if {[regexp $dir $directive]} {
4939 if {$custom_command ne ""} {
4940 if {[
llength $custom_command_options] > 0} {
4941 puts "Available options:"
4943 foreach custom_option $custom_command_options {
4944 set n [
llength $custom_option]
4946 lassign $custom_option opt help
4949 }
elseif {$n == 3} {
4950 lassign $custom_option opt def help
4951 puts " -$opt <argument>"
4953 puts " $help (default: $def)"
4958 Msg Warning "Custom option spec has invalid arity (expected 2 or 3): $custom_option"
4963 dict for {dir opts} $command_options {
4964 if {[regexp $dir $directive]} {
4965 puts "Available options:"
4967 foreach par $parameters {
4968 if {$opt == [lindex $par 0]} {
4969 if {[regexp {\.arg$} $opt]} {
4970 set opt_name [regsub {\.arg$} $opt ""]
4971 puts " -$opt_name <argument>"
4975 puts " [lindex $par [llength $par]-1]"
4990 set directive_options ""
4991 set directive_found 0
4992 if {$custom_command ne ""} {
4993 set directive_options $custom_command_options
4994 set directive_found 1
4996 dict for {dir opts} $command_options {
4997 if {[regexp $dir $directive]} {
4998 set directive_options $opts
4999 set directive_found 1
5006 set directive_option_names {}
5007 foreach opt $directive_options {
5008 lappend directive_option_names [regsub {\.arg$} [
lindex $opt 0] ""]
5011 if {$custom_command ne ""} {
5012 set parameters [
concat $parameters $custom_command_options]
5015 if {[
catch {
array set options [
cmdline::getoptions option_list $parameters $usage]} err]} {
5016 Msg Status "\nERROR: Syntax error, probably unknown option.\n\n USAGE: $err"
5021 if {$directive_found == 1} {
5022 set default_option_list {}
5024 foreach {key value} [
array get options] {
5025 if {[
IsInList $key $directive_option_names] || ![
info exists default_options($key)]} {
5028 if {$value ne $default_options($key)} {
5029 Msg Warning "Option -$key is not supported by directive $directive, ignoring it."
5030 set options($key) $default_options($key)
5035 if {[
llength $arg_list] <= $min_n_of_args || [
llength $arg_list] > $max_n_of_args} {
5036 Msg Status "\nERROR: Wrong number of arguments: [
llength $argv]"
5041 set project [
lindex $arg_list 1]
5042 set optional_project [
IsInList $directive $directives_with_optional_projects 1]
5044 if {$argument_is_no_project == 0} {
5046 regsub "^(\./)?Top/" $project "" project
5048 regsub "/? *\$" $project "" project
5050 if {$project eq "" && $optional_project == 1} {
5059 Msg Debug "Option list:"
5061 foreach {key value} [
array get options] {
5062 Msg Debug "$key => $value"
5069 if {$proj_conf != 0} {
5072 lassign [
GetIDECommand $proj_conf] cmd before_tcl_script after_tcl_script end_marker
5073 Msg Info "Project $project uses $cmd IDE"
5076 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
5078 if {$custom_command ne ""} {
5079 if {[dict exists $custom_commands $directive IDE]} {
5080 lassign [
GetIDECommand "" [dict get $custom_commands $directive IDE]] cmd before_tcl_script after_tcl_script end_marker
5081 Msg Info "Custom command: $custom_command uses $cmd IDE"
5082 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
5084 set command "custom_tcl"
5086 }
elseif {$argument_is_no_project == 1} {
5088 Msg Debug "$project will be used as first argument"
5089 }
elseif {$project != ""} {
5092 }
elseif {$min_n_of_args < 0} {
5097 if {$optional_project == 0} {
5109 set project_group [
file dirname $project]
5110 set project_name $project
5111 set project [
file tail $project]
5112 Msg Debug "InitLauncher: project_group=$project_group, project_name=$project_name, project=$project"
5114 return [list $directive $project $project_name $project_group $repo_path $old_path\
5115 $bin_path $top_path $usage $short_usage $command $cmd [
array get options]\
5123 proc IsCommitAncestor {ancestor commit} {
5124 lassign [
GitRet "merge-base --is-ancestor $ancestor $commit"] status result
5133 return [
expr {[info commands sys_install] != ""}]
5138 return [
expr {[info commands get_libero_version] != ""}]
5147 proc IsInList {element list {regex 0} {nocase 0}} {
5151 if {[regexp -nocase $x $element]} {
5155 if {[regexp $x $element]} {
5159 }
elseif {$regex == 0} {
5161 if {[
string tolower $x] eq [
string tolower $element]} {
5165 if {$x eq $element} {
5178 return [
expr {[string first PlanAhead [version]] == 0}]
5186 if {[
catch {
package require ::quartus::flow} result]} {
5199 proc IsRelativePath {path} {
5200 if {[
string index $path 0] == "~"} {
5203 return [
expr {[file pathtype $path] eq "relative"}]
5207 proc IsSynplify {} {
5208 return [
expr {[info commands program_version] != ""}]
5213 return [
expr {![IsQuartus] && ![IsXilinx] && ![IsVitisClassic] && ![IsVitisUnified] && ![IsLibero] && ![IsSynplify] && ![IsDiamond]}]
5221 proc IsVerilog {file} {
5222 if {[
file extension $file] == ".v" || [
file extension $file] == ".sv"} {
5234 proc IsVersal {part} {
5235 if {[get_property ARCHITECTURE [get_parts $part]] eq "versal"} {
5245 return [
expr {[string first Vivado [version]] == 0}]
5253 if {[
info commands version] != ""} {
5254 set current_version [version]
5255 if {[
string first PlanAhead $current_version] == 0 || [
string first Vivado $current_version] == 0} {
5257 }
elseif {[
string first xsct $current_version] == 0} {
5260 Msg Warning "This IDE has the version command but it is not PlanAhead or Vivado: $current_version"
5269 proc IsVitisClassic {} {
5270 if {[
info exists globalSettings::vitis_classic]} {
5271 return $globalSettings::vitis_classic
5273 return [
expr {[info commands platform] != ""}]
5277 proc IsVitisUnified {} {
5278 if {[
info exists globalSettings::vitis_unified]} {
5279 return $globalSettings::vitis_unified
5293 proc ExecuteVitisUnifiedCommand {python_script command args {error_prefix "Failed to execute command"} {output_var ""}} {
5294 set cmdlist [list vitis -s $python_script $command]
5296 lappend cmdlist $arg
5298 lappend cmdlist 2>@1
5300 Msg Debug "Executing: vitis -s $python_script $command $args"
5303 set ::env(PYTHONUNBUFFERED) "1"
5306 if {[
catch {
set pipe [open "|$cmdlist" "r"]} err]} {
5307 Msg Error "$error_prefix: Failed to open pipe: $err"
5311 fconfigure $pipe -buffering line
5312 set script_output ""
5313 set vitis_version ""
5316 set vitis_banner_patterns {
5317 "*Vitis Development Environment*"
5320 "*Copyright*Xilinx*"
5321 "*Copyright*Advanced Micro Devices*"
5322 "*All Rights Reserved*"
5326 while {[
gets $pipe line] >= 0} {
5328 if {[
string match "*Vitis v*" $line]} {
5329 if {[regexp {Vitis\s+v([0-9]+)\.([0-9]+)(?:\.[0-9]+)?} $line -> major minor]} {
5330 set year_last_two [
string range $major end-1 end]
5331 set vitis_version "$year_last_two.$minor"
5337 foreach pattern $vitis_banner_patterns {
5338 if {[
string match $pattern $line]} {
5344 if {![
string match "INFO:*" $line] && ![
string match "WARNING:*" $line] && ![
string match "ERROR:*" $line] && ![
string match "DEBUG:*" $line]} {
5345 if {$vitis_version ne ""} {
5346 set line "INFO: \[Vitis_v$vitis_version\] $line"
5348 set line "INFO: $line"
5352 append script_output "$line\n"
5359 if {[
catch {
close $pipe} err]} {
5360 if {[regexp {exit (\d+)} $err -> exit_code]} {
5361 if {$exit_code != 0} {
5362 Msg Error "$error_prefix (exit code: $exit_code)"
5363 if {$output_var ne ""} {
5364 upvar $output_var output
5365 set output $script_output
5370 Msg Error "$error_prefix: $err"
5371 if {$output_var ne ""} {
5372 upvar $output_var output
5373 set output $script_output
5380 if {$output_var ne ""} {
5381 upvar $output_var output
5382 set output $script_output
5398 proc GetVitisApps {{vitis_workspace ""} {python_script ""}} {
5401 if {[
catch {
set ws_apps [app list -dict]}]} {
5414 "Failed to get app list from Vitis Unified workspace $vitis_workspace" json_output]
5419 if {[
catch {
package require json}]} {
5420 Msg Warning "JSON package not available for parsing Vitis Unified app list"
5424 set json_output_filtered ""
5425 if {[regexp -lineanchor {\{.*\}} $json_output json_output_filtered]} {
5426 return [json::json2dict $json_output_filtered]
5428 return [json::json2dict $json_output]
5436 proc IsZynq {part} {
5437 if {[regexp {^(xc7z|xczu).*} $part]} {
5444 proc ImportGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
5445 set list_path "$repo_path/Top/$project_name/list"
5446 lassign [
GetHogFiles -list_files {.src,.ext,.sim} -ext_path $ext_path $list_path $repo_path] src_files properties filesets
5451 set properties [
DictGet $simset_dict "properties"]
5452 set options [
DictGet $properties "options"]
5455 set workdir Projects/$project_name/ghdl
5456 file delete -force $workdir
5458 set import_log "$workdir/ghdl-import-${simset_name}.log"
5459 dict for {lib sources} $src_files {
5460 set libname [file rootname $lib]
5461 foreach f $sources {
5462 if {[file extension $f] != ".vhd" && [file extension $f] != ".vhdl"} {
5463 Msg Info "File $f is not a VHDL file, copying it in workfolder..."
5464 file copy -force $f $workdir
5466 set file_path [Relative $repo_path $f]
5467 set import_log_file [open $import_log "a"]
5468 puts "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
5469 puts $import_log_file "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
5470 close $import_log_file
5471 lassign [GHDL "-i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path" $import_log] ret result
5473 Msg CriticalWarning "GHDL import failed for file $f: $result"
5481 proc LaunchGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
5484 set sim_props [
DictGet $simset_dict "properties"]
5485 set options [
DictGet $sim_props "options"]
5486 set runopts [
DictGet $sim_props "run_options"]
5488 dict for {prop_name prop_val} $sim_props {
5489 set prop_name [string toupper $prop_name]
5490 if {$prop_name == "TOP"} {
5491 set top_sim $prop_val
5494 set workdir $repo_path/Projects/$project_name/ghdl
5495 set make_log "$workdir/ghdl-make-${simset_name}.log"
5496 set run_log "$workdir/ghdl-run-${simset_name}.log"
5499 set make_log_file [open $make_log "w"]
5501 puts "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
5502 puts $make_log_file "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
5503 close $make_log_file
5505 lassign [
GHDL "-m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim" $make_log] ret result
5508 Msg Error "GHDL make failed for $top_sim: $result"
5512 set run_log_file [open $run_log "w"]
5513 puts "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
5514 puts $run_log_file "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
5517 lassign [
GHDL "-r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts" $run_log] ret result
5521 Msg Error "GHDL run failed for $top_sim: $result"
5536 proc LaunchImplementation {reset do_create run_folder project_name {repo_path .} {njobs 4} {do_bitstream 0}} {
5537 Msg Info "Starting implementation flow..."
5540 Msg Info "Resetting run before launching implementation..."
5545 if {[
file exist "$run_folder/timing_ok.txt"]} {
5546 file delete "$run_folder/timing_ok.txt"
5548 if {[
file exist "$run_folder/timing_error.txt"]} {
5549 file delete "$run_folder/timing_error.txt"
5553 source $repo_path/Hog/Tcl/integrated/pre-implementation.tcl
5556 if {$do_bitstream == 1} {
5557 launch_runs impl_1 -to_step [
BinaryStepName [get_property PART [current_project]]] -jobs $njobs -dir $run_folder
5559 launch_runs impl_1 -jobs $njobs -dir $run_folder
5564 Msg Info "running post-implementation"
5565 source $repo_path/Hog/Tcl/integrated/post-implementation.tcl
5566 if {$do_bitstream == 1} {
5567 Msg Info "running pre-bitstream"
5568 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5569 Msg Info "running post-bitstream"
5570 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5574 set prog [get_property PROGRESS [get_runs impl_1]]
5575 set status [get_property STATUS [get_runs impl_1]]
5576 Msg Info "Run: impl_1 progress: $prog, status : $status"
5580 set status_file [open "$run_folder/timing.txt" "w"]
5581 puts $status_file "## $project_name Timing summary"
5583 set f [open [
lindex [glob "$run_folder/impl_1/*.twr" 0]]]
5585 while {[
gets $f line] >= 0} {
5586 if {[
string match "Timing summary:" $line]} {
5587 while {[
gets $f line] >= 0} {
5588 if {[
string match "Timing errors:*" $line]} {
5589 set errs [regexp -inline -- {[0-9]+} $line]
5591 if {[
string match "*Footnotes*" $line]} {
5594 puts $status_file "$line"
5603 Msg Info "Time requirements are met"
5604 file rename -force "$run_folder/timing.txt" "$run_folder/timing_ok.txt"
5607 Msg CriticalWarning "Time requirements are NOT met"
5608 file rename -force "$run_folder/timing.txt" "$run_folder/timing_error.txt"
5614 set wns [get_property STATS.WNS [get_runs [current_run]]]
5615 set tns [get_property STATS.TNS [get_runs [current_run]]]
5616 set whs [get_property STATS.WHS [get_runs [current_run]]]
5617 set ths [get_property STATS.THS [get_runs [current_run]]]
5618 set tpws [get_property STATS.TPWS [get_runs [current_run]]]
5620 if {$wns >= 0 && $whs >= 0 && $tpws >= 0} {
5621 Msg Info "Time requirements are met"
5622 set status_file [open "$run_folder/timing_ok.txt" "w"]
5625 Msg CriticalWarning "Time requirements are NOT met"
5626 set status_file [open "$run_folder/timing_error.txt" "w"]
5630 Msg Status "*** Timing summary ***"
5631 Msg Status "WNS: $wns"
5632 Msg Status "TNS: $tns"
5633 Msg Status "WHS: $whs"
5634 Msg Status "THS: $ths"
5635 Msg Status "TPWS: $tpws"
5641 puts $status_file "## $project_name Timing summary"
5643 m add row "| **Parameter** | \"**value (ns)**\" |"
5644 m add row "| --- | --- |"
5645 m add row "| WNS: | $wns |"
5646 m add row "| TNS: | $tns |"
5647 m add row "| WHS: | $whs |"
5648 m add row "| THS: | $ths |"
5649 m add row "| TPWS: | $tpws |"
5651 puts $status_file [m format 2string]
5652 puts $status_file "\n"
5653 if {$timing_ok == 1} {
5654 puts $status_file " Time requirements are met."
5656 puts $status_file "Time requirements are **NOT** met."
5658 puts $status_file "\n\n"
5662 if {$prog ne "100%"} {
5663 Msg Error "Implementation error"
5668 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5669 Msg Info "Git describe set to $describe"
5671 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5676 if {[
file exists $run_folder/versions.txt]} {
5677 file copy -force $run_folder/versions.txt $dst_dir
5679 Msg Warning "No versions file found in $run_folder/versions.txt"
5682 set timing_files [glob -nocomplain "$run_folder/timing_*.txt"]
5683 set timing_file [
file normalize [
lindex $timing_files 0]]
5685 if {[
file exists $timing_file]} {
5686 file copy -force $timing_file $dst_dir/
5688 Msg Warning "No timing file found, not a problem if running locally"
5692 if {[
IsVersal [get_property part [current_project]]]} {
5693 if {[get_property segmented_configuration [current_project]] == 1} {
5694 Msg Info "Versal Segmented configuration detected: exporting XSA file..."
5695 set xsa_name "$dst_dir/[
file tail $project_name]\-$describe.xsa"
5696 write_hw_platform -fixed -force -file $xsa_name
5700 set revision [get_current_revision]
5702 set ic [get_global_assignment -name INCREMENTAL_COMPILATION]
5703 if {$ic ne "" && [
string toupper $ic] ne "OFF"} {
5704 Msg Info "Incremental compilation enabled ($ic), running Partition Merge..."
5705 if {[
catch {execute_module -tool cdb -args "--merge=on"} result]} {
5706 Msg Error "Result: $result\nPartition Merge failed. See the report file.\n"
5710 if {[
catch {execute_module -tool fit} result]} {
5711 Msg Error "Result: $result\n"
5712 Msg Error "Place & Route failed. See the report file.\n"
5714 Msg Info "\nINFO: Place & Route was successful for revision $revision.\n"
5717 if {[
catch {execute_module -tool sta -args "--do_report_timing"} result]} {
5718 Msg Error "Result: $result\n"
5719 Msg Error "Time Quest failed. See the report file.\n"
5721 Msg Info "Time Quest was successfully run for revision $revision.\n"
5724 set panel "Timing Analyzer||Timing Analyzer Summary"
5725 set device [get_report_panel_data -name $panel -col 1 -row_name "Device Name"]
5726 set timing_model [get_report_panel_data -name $panel -col 1 -row_name "Timing Models"]
5727 set delay_model [get_report_panel_data -name $panel -col 1 -row_name "Delay Model"]
5729 Msg Info "*******************************************************************"
5730 Msg Info "Device: $device"
5731 Msg Info "Timing Models: $timing_model"
5732 Msg Info "Delay Model: $delay_model"
5735 Msg Info "*******************************************************************"
5738 Msg Info "Starting implementation flow..."
5739 if {[
catch {run_tool -name {PLACEROUTE}}]} {
5740 Msg Error "PLACEROUTE FAILED!"
5742 Msg Info "PLACEROUTE PASSED."
5746 Msg Info "Run VERIFYTIMING ..."
5747 if {[
catch {run_tool -name {VERIFYTIMING} -script {Hog/Tcl/integrated/libero_timing.tcl}}]} {
5748 Msg CriticalWarning "VERIFYTIMING FAILED!"
5750 Msg Info "VERIFYTIMING PASSED \n"
5756 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5757 Msg Info "Git describe set to $describe"
5759 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5760 file mkdir $dst_dir/reports
5763 if {[
file exists $run_folder/versions.txt]} {
5764 file copy -force $run_folder/versions.txt $dst_dir
5766 Msg Warning "No versions file found in $run_folder/versions.txt"
5769 set timing_file_path [
file normalize "$repo_path/Projects/timing_libero.txt"]
5770 if {[
file exists $timing_file_path]} {
5771 file copy -force $timing_file_path $dst_dir/reports/Timing.txt
5772 set timing_file [open $timing_file_path "r"]
5773 set status_file [open "$dst_dir/timing.txt" "w"]
5774 puts $status_file "## $project_name Timing summary\n\n"
5775 puts $status_file "| | |"
5776 puts $status_file "| --- | --- |"
5777 while {[
gets $timing_file line] >= 0} {
5778 if {[
string match "SUMMARY" $line]} {
5779 while {[
gets $timing_file line] >= 0} {
5780 if {[
string match "END SUMMARY" $line]} {
5783 if {[
string first ":" $line] == -1} {
5786 set out_string "| [
string map {: | } $line] |"
5787 puts $status_file "$out_string"
5792 Msg Warning "No timing file found, not a problem if running locally"
5797 set force_rst "-forceOne"
5799 prj_run Map $force_rst
5800 prj_run PAR $force_rst
5812 proc GenerateBitstreamOnly {project_name {repo_path .}} {
5816 set project_file [
file normalize "$repo_path/Projects/$project_name/$project_name.xpr"]
5817 if {![
file exists $project_file]} {
5818 Msg Error "Project file not found: $project_file. Please create the project first."
5824 set impl_runs [get_runs -quiet impl_1]
5825 if {[
llength $impl_runs] == 0} {
5826 Msg Error "Implementation run 'impl_1' does not exist. Please run implementation first."
5832 set run_dir [get_property DIRECTORY [get_runs impl_1]]
5833 if {![
file exists $run_dir]} {
5834 Msg Error "Implementation run directory not found: $run_dir. Please check your implementation run."
5839 Msg Info "Running pre-bitstream..."
5840 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5842 Msg Info "Writing bitstream for $project_name..."
5845 write_bitstream -force [
file normalize "$run_dir/$top_name.bit"]
5847 Msg Info "Running post-bitstream..."
5848 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5857 proc LaunchSimulation {project_name lib_path simsets {repo_path .} {scripts_only 0} {compile_only 0}} {
5860 set project [
file tail $project_name]
5861 set main_sim_folder [
file normalize "$repo_path/Projects/$project_name/$project.sim/"]
5863 if {$simsets != ""} {
5864 dict for {simset sim_dict} $simsets {
5865 lappend simsets_todo $simset
5867 Msg Info "Will run only the following simulation's sets (if they exist): $simsets_todo"
5870 if {$scripts_only == 1} {
5871 Msg Info "Only generating simulation scripts, not running simulations..."
5874 if {$compile_only == 1} {
5875 Msg Info "Only compiling simulation libraries, not running simulations..."
5880 set sim_dic [dict create]
5882 Msg Info "Retrieving list of simulation sets..."
5883 foreach s [get_filesets] {
5885 set use_simpass_str 0
5888 set type [get_property FILESET_TYPE $s]
5889 if {$type eq "SimulationSrcs"} {
5890 if {$simsets_todo != "" && $s ni $simsets_todo} {
5891 Msg Info "Skipping $s as it was not specified with the -simset option..."
5894 set sim_dict [
DictGet $simsets $s]
5895 set simulator [
DictGet $sim_dict "simulator"]
5896 set_property "target_simulator" $simulator [current_project]
5897 set hog_sim_props [
DictGet $sim_dict "hog"]
5898 dict for {prop_name prop_val} $hog_sim_props {
5899 # If HOG_SIMPASS_STR is set, use the HOG_SIMPASS_STR string to search for in logs, after simulation is done
5900 if {[string toupper $prop_name] == "HOG_SIMPASS_STR" && $prop_val != ""} {
5901 Msg Info "Setting simulation pass string as '$prop_val'"
5902 set use_simpass_str 1
5903 set simpass_str $prop_val
5905 if {[string toupper $prop_name] == "HOG_SILENT_SIM" && $prop_val == 1} {
5906 set quiet_sim " -quiet"
5912 Msg Info "Creating simulation scripts for $s..."
5913 if {[
file exists $repo_path/Top/$project_name/pre-simulation.tcl]} {
5914 Msg Info "Running $repo_path/Top/$project_name/pre-simulation.tcl"
5915 source $repo_path/Top/$project_name/pre-simulation.tcl
5917 if {[
file exists $repo_path/Top/$project_name/pre-$s-simulation.tcl]} {
5918 Msg Info "Running $repo_path/Top/$project_name/pre-$s-simulation.tcl"
5919 source $repo_path/Top/$project_name/pre-$s-simulation.tcl
5921 current_fileset -simset $s
5922 set sim_dir $main_sim_folder/$s/behav
5923 set sim_output_logfile $sim_dir/xsim/simulate.log
5924 if {([
string tolower $simulator] eq "xsim")} {
5925 set sim_name "xsim:$s"
5927 set simulation_command "launch_simulation $quiet_sim -simset [get_filesets $s]"
5928 if {[
catch $simulation_command log]} {
5931 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5932 lappend failed $sim_name
5937 if {$use_simpass_str == 1} {
5940 set file_desc [open $sim_output_logfile r]
5941 set log [read $file_desc]
5944 Msg Info "Searching for simulation pass string: '$simpass_str'"
5945 if {[
string first $simpass_str $log] == -1} {
5946 Msg CriticalWarning "Simulation failed for $s, error info: '$simpass_str' NOT found!"
5947 lappend failed $sim_name
5950 lappend success $sim_name
5954 lappend success $sim_name
5958 Msg Info "Simulation library path is set to $lib_path."
5960 if {!([
file exists $lib_path])} {
5961 Msg Warning "Could not find simulation library path: $lib_path, $simulator simulation will not work."
5965 if {$simlib_ok == 1} {
5966 set_property "compxlib.${simulator}_compiled_library_dir" [
file normalize $lib_path] [current_project]
5967 launch_simulation -scripts_only -simset [get_filesets $s]
5968 set top_name [get_property TOP $s]
5969 set sim_script [
file normalize $sim_dir/$simulator/]
5970 Msg Info "Adding simulation script location $sim_script for $s..."
5971 lappend sim_scripts $sim_script
5972 dict append sim_dic $sim_script $s
5974 Msg Error "Cannot run $simulator simulations without a valid library path"
5981 if {[
info exists sim_scripts] && $scripts_only == 0} {
5983 Msg Info "Generating IP simulation targets, if any..."
5985 foreach ip [get_ips] {
5986 generate_target simulation -quiet $ip
5991 Msg Info "====== Starting simulations runs ======"
5994 foreach s $sim_scripts {
5996 set cmd ./compile.sh
5997 Msg Info " ************* Compiling: $s ************* "
5999 set sim_name "comp:[dict get $sim_dic $s]"
6001 Msg CriticalWarning "Compilation failed for $s, error info: $::errorInfo"
6002 lappend failed $sim_name
6004 lappend success $sim_name
6006 Msg Info "###################### Compilation log starts ######################"
6007 Msg Info "\n\n$log\n\n"
6008 Msg Info "###################### Compilation log ends ######################"
6010 if {$compile_only == 1} {
6013 if {[
file exists "./elaborate.sh"]} {
6014 set cmd ./elaborate.sh
6015 Msg Info " ************* Elaborating: $s ************* "
6017 set sim_name "elab:[dict get $sim_dic $s]"
6019 Msg CriticalWarning "Elaboration failed for $s, error info: $::errorInfo"
6020 lappend failed $sim_name
6022 lappend success $sim_name
6024 Msg Info "###################### Elaboration log starts ######################"
6025 Msg Info "\n\n$log\n\n"
6026 Msg Info "###################### Elaboration log ends ######################"
6028 set cmd ./simulate.sh
6029 Msg Info " ************* Simulating: $s ************* "
6034 if {$use_simpass_str == 1} {
6035 if {[
string first $simpass_str $log] == -1} {
6039 Msg Debug "Simulation pass string not set, relying on simulator exit code."
6043 set sim_name "sim:[dict get $sim_dic $s]"
6045 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
6046 lappend failed $sim_name
6048 lappend success $sim_name
6050 Msg Info "###################### Simulation log starts ######################"
6051 Msg Info "\n\n$log\n\n"
6052 Msg Info "###################### Simulation log ends ######################"
6057 if {[
llength $success] > 0} {
6058 set successes [
join $success "\n"]
6059 Msg Info "The following simulation sets were successful:\n\n$successes\n\n"
6062 if {[
llength $failed] > 0} {
6063 set failures [
join $failed "\n"]
6064 Msg Error "The following simulation sets have failed:\n\n$failures\n\n"
6066 }
elseif {[
llength $success] > 0} {
6067 Msg Info "All the [
llength $success] compilations, elaborations and simulations were successful."
6070 Msg Info "Simulation done."
6072 Msg Warning "Simulation is not yet supported for [
GetIDEName]."
6079 proc LaunchRTLAnalysis {repo_path {pre_rtl_file ""} {post_rtl_file ""}} {
6081 if {[
file exists $pre_rtl_file]} {
6082 Msg Info "Found pre-rtl Tcl script $pre_rtl_file, executing it..."
6083 source $pre_rtl_file
6085 Msg Info "Starting RTL Analysis..."
6087 synth_design -rtl -name rtl_1
6088 if {[
file exists $post_rtl_file]} {
6089 Msg Info "Found post-rtl Tcl script $post_rtl_file, executing it..."
6090 source $post_rtl_file
6093 Msg Warning "RTL Analysis is not yet supported for [
GetIDEName]."
6106 proc LaunchSynthesis {reset do_create run_folder project_name {repo_path .} {ext_path ""} {njobs 4}} {
6109 Msg Info "Resetting run before launching synthesis..."
6113 source $repo_path/Hog/Tcl/integrated/pre-synthesis.tcl
6115 launch_runs synth_1 -jobs $njobs -dir $run_folder
6117 set prog [get_property PROGRESS [get_runs synth_1]]
6118 set status [get_property STATUS [get_runs synth_1]]
6119 Msg Info "Run: synth_1 progress: $prog, status : $status"
6126 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6127 Msg Info "Git describe set to $describe"
6130 set xci_file [get_property IP_FILE $ip]
6132 set xci_path [
file dirname $xci_file]
6133 set xci_ip_name [
file rootname [
file tail $xci_file]]
6134 foreach rptfile [glob -nocomplain -directory $xci_path *.rpt] {
6135 file copy $rptfile $repo_path/bin/$project_name-$describe/reports
6139 if {$prog ne "100%"} {
6140 Msg Error "Synthesis error, status is: $status"
6144 set project [
file tail [
file rootname $project_name]]
6146 Msg Info "Number of jobs set to $njobs."
6147 set_global_assignment -name NUM_PARALLEL_PROCESSORS $njobs
6151 set describe [
GetHogDescribe [
file normalize $repo_path/Top/$project_name] $repo_path]
6153 set revision [get_current_revision]
6156 set tool_and_command [
split [get_global_assignment -name PRE_FLOW_SCRIPT_FILE] ":"]
6157 set tool [
lindex $tool_and_command 0]
6158 set pre_flow_script [
lindex $tool_and_command 1]
6159 set cmd "$tool -t $pre_flow_script quartus_map $project $revision"
6165 Msg Warning "Can not execute command $cmd"
6166 Msg Warning "LOG: $log"
6168 Msg Info "Pre flow script executed!"
6169 Msg Info "Pre flow script log: \n$log"
6173 if {![is_project_open]} {
6174 Msg Info "Re-opening project file $project_name..."
6175 project_open $project -current_revision
6179 if {[
catch {execute_module -tool ipg -args "--clean"} result]} {
6180 Msg Error "Result: $result\n"
6181 Msg Error "IP Generation failed. See the report file.\n"
6183 Msg Info "IP Generation was successful for revision $revision.\n"
6187 if {[
catch {execute_module -tool map -args "--parallel"} result]} {
6188 Msg Error "Result: $result\n"
6189 Msg Error "Analysis & Synthesis failed. See the report file.\n"
6191 Msg Info "Analysis & Synthesis was successful for revision $revision.\n"
6195 defvar_set -name RWNETLIST_32_64_MIXED_FLOW -value 0
6197 Msg Info "Run SYNTHESIS..."
6198 if {[
catch {run_tool -name {SYNTHESIZE}}]} {
6199 Msg Error "SYNTHESIZE FAILED!"
6201 Msg Info "SYNTHESIZE PASSED!"
6207 set force_rst "-forceOne"
6209 prj_run Synthesis $force_rst
6210 if {[prj_syn] == "synplify"} {
6211 prj_run Translate $force_rst
6214 Msg Error "Impossible condition. You need to run this in an IDE."
6225 proc LaunchVitisBuild {project_name {repo_path .} {stage "presynth"}} {
6226 set proj_name $project_name
6227 set bin_dir [
file normalize "$repo_path/bin"]
6233 set vitis_workspace [
file normalize "$repo_path/Projects/$project_name/vitis_unified"]
6234 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"]
6235 set ws_apps [
GetVitisApps $vitis_workspace $python_script]
6239 Msg Error "Impossible condition. You need to run this in a Vitis Unified or Vitis Classic IDE."
6244 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$proj_name] $repo_path] commit version hog_hash hog_ver top_hash top_ver \
6245 libs hashes vers cons_ver cons_hash ext_names ext_hashes xml_hash xml_ver user_ip_repos user_ip_hashes user_ip_vers
6247 if {$commit == 0} {
set commit $this_commit}
6254 foreach app_name [dict keys $ws_apps] {
6255 app config -name $app_name -set build-config Release
6259 WriteGenerics "vitisbuild" $repo_path $proj_name $date $timee $commit $version $top_hash $top_ver $hog_hash $hog_ver $cons_ver $cons_hash $libs \
6260 $vers $hashes $ext_names $ext_hashes $user_ip_repos $user_ip_vers $user_ip_hashes $flavour $xml_ver $xml_hash
6263 foreach app_name [dict keys $ws_apps] {
6266 if {![
ExecuteVitisUnifiedCommand $python_script "build_app" [list $app_name $vitis_workspace] "Failed to build app $app_name"]} {
6267 Msg Error "Failed to build app $app_name"
6271 app build -name $app_name
6275 if {$stage == "presynth"} {
6276 Msg Info "Done building apps for $project_name..."
6279 if {[
info exists ::globalSettings::vitis_only_pass] && $::globalSettings::vitis_only_pass == 1} {
6280 Msg Info "Skipping bin directory creation in vitis_only mode (post-bitstream.tcl handles artifacts)."
6284 Msg Info "Evaluating Hog describe for $project_name..."
6285 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
6286 Msg Info "Hog describe set to: $describe"
6287 set dst_dir [
file normalize "$bin_dir/$proj_name\-$describe"]
6288 if {![
file exists $dst_dir]} {
6289 Msg Info "Creating $dst_dir..."
6293 foreach app_name [dict keys $ws_apps] {
6295 set main_file "$repo_path/Projects/$project_name/vitis_unified/$app_name/build/$app_name.elf"
6297 set main_file "$repo_path/Projects/$project_name/vitis_classic/$app_name/Release/$app_name.elf"
6299 set dst_main [
file normalize "$dst_dir/[
file tail $proj_name]\-$app_name\-$describe.elf"]
6301 if {![
file exists $main_file]} {
6302 Msg Error "No Vitis .elf file found. Perhaps there was an issue building it?"
6306 Msg Info "Copying main binary file $main_file into $dst_main..."
6307 file copy -force $main_file $dst_main
6322 proc LaunchHlsBuild {project_name {repo_path .}} {
6323 set proj_name $project_name
6324 set bin_dir [
file normalize "$repo_path/bin"]
6328 set conf_file [
file normalize "$repo_path/Top/$project_name/hog.conf"]
6329 if {![
file exists $conf_file]} {
6330 Msg Error "Configuration file not found: $conf_file"
6333 set properties [
ReadConf $conf_file]
6334 set hls_components [dict filter $properties key {hls:*}]
6336 if {[dict size $hls_components] == 0} {
6337 Msg Info "No HLS components found for project $project_name"
6341 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/HlsCommands.py"]
6349 set ide_name [
string tolower [
lindex $ide_info 0]]
6350 if {$ide_name eq "vivado_vitis_unified"} {
6351 set is_mixed_project 1
6353 set is_mixed_project 0
6356 dict for {hls_key hls_props} $hls_components {
6357 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {
6360 set component_name [string trim $component_name]
6361 Msg Info "Building HLS component: $component_name"
6363 # Read HLS_CONFIG path from hog.conf (mandatory)
6365 if {[dict exists $hls_props hls_config]} {
6366 set hls_cfg_rel [dict get $hls_props hls_config]
6367 } elseif {[dict exists $hls_props HLS_CONFIG]} {
6368 set hls_cfg_rel [dict get $hls_props HLS_CONFIG]
6370 if {$hls_cfg_rel eq ""} {
6371 Msg Error "HLS component '$component_name' missing HLS_CONFIG in hog.conf"
6375 set cfg_file [file normalize "$repo_path/$hls_cfg_rel"]
6376 if {![file exists $cfg_file]} {
6377 Msg Error "HLS config file not found: $cfg_file (from HLS_CONFIG=$hls_cfg_rel)"
6381 set hls_work_dir [file normalize "$repo_path/Projects/$project_name/vitis_unified/$component_name"]
6382 file mkdir $hls_work_dir
6385 Msg Info "Running C synthesis for HLS component '$component_name'..."
6387 ![ExecuteVitisUnifiedCommand $python_script "synthesis" \
6388 [list $component_name $cfg_file $hls_work_dir] \
6389 "Failed to run C synthesis for $component_name"]
6391 Msg Error "C synthesis failed for HLS component '$component_name'"
6396 Msg Info "Running implementation for HLS component '$component_name'..."
6398 ![ExecuteVitisUnifiedCommand $python_script "impl" \
6399 [list $component_name $cfg_file $hls_work_dir] \
6400 "Failed to run implementation for $component_name"]
6402 Msg Error "Implementation failed for HLS component '$component_name'"
6406 # Export VHDL to source tree if VHDL_OUTPUT is set
6408 if {[dict exists $hls_props vhdl_output]} {
6409 set vhdl_output [dict get $hls_props vhdl_output]
6410 } elseif {[dict exists $hls_props VHDL_OUTPUT]} {
6411 set vhdl_output [dict get $hls_props VHDL_OUTPUT]
6413 if {$vhdl_output ne ""} {
6414 set vhdl_output_dir [file normalize "$repo_path/$vhdl_output"]
6415 Msg Info "Exporting VHDL for '$component_name' to $vhdl_output_dir..."
6417 ![ExecuteVitisUnifiedCommand $python_script "export_rtl" \
6418 [list $component_name $hls_work_dir $vhdl_output_dir vhdl] \
6419 "Failed to export VHDL for $component_name"]
6421 Msg Warning "Could not export VHDL for HLS component '$component_name'"
6425 # Export Verilog to source tree if VERILOG_OUTPUT is set
6426 set verilog_output ""
6427 if {[dict exists $hls_props verilog_output]} {
6428 set verilog_output [dict get $hls_props verilog_output]
6429 } elseif {[dict exists $hls_props VERILOG_OUTPUT]} {
6430 set verilog_output [dict get $hls_props VERILOG_OUTPUT]
6432 if {$verilog_output ne ""} {
6433 set verilog_output_dir [file normalize "$repo_path/$verilog_output"]
6434 Msg Info "Exporting Verilog for '$component_name' to $verilog_output_dir..."
6436 ![ExecuteVitisUnifiedCommand $python_script "export_rtl" \
6437 [list $component_name $hls_work_dir $verilog_output_dir verilog] \
6438 "Failed to export Verilog for $component_name"]
6440 Msg Warning "Could not export Verilog for HLS component '$component_name'"
6444 # Export IP catalog ZIP to source tree if IP_OUTPUT is set
6446 if {[dict exists $hls_props ip_output]} {
6447 set ip_output [dict get $hls_props ip_output]
6448 } elseif {[dict exists $hls_props IP_OUTPUT]} {
6449 set ip_output [dict get $hls_props IP_OUTPUT]
6451 if {$ip_output ne ""} {
6452 set ip_output_dir [file normalize "$repo_path/$ip_output"]
6453 Msg Info "Exporting IP catalog for '$component_name' to $ip_output_dir..."
6455 ![ExecuteVitisUnifiedCommand $python_script "export_ip" \
6456 [list $component_name $hls_work_dir $ip_output_dir] \
6457 "Failed to export IP for $component_name"]
6459 Msg Error "Could not export IP for HLS component '$component_name'. Make sure package.output.format=ip_catalog is set in hls_config.cfg."
6463 # Collect reports into bin/ for CI and release notes
6464 Msg Info "Evaluating Hog describe for $project_name..."
6465 set describe [GetHogDescribe [file normalize ./Top/$project_name] $repo_path]
6466 Msg Info "Hog describe set to: $describe"
6467 set dst_dir [file normalize "$bin_dir/$proj_name\-$describe"]
6468 if {![file exists $dst_dir]} {
6469 Msg Info "Creating $dst_dir..."
6473 # Mixed projects group HLS components under bin/<proj>/vitis_hls/; pure
6474 # vitis_unified projects place them directly under bin/<proj>/
6475 if {$is_mixed_project} {
6476 set hls_out_dir [file normalize "$dst_dir/vitis_hls"]
6478 set hls_out_dir $dst_dir
6481 Msg Info "Collecting HLS reports for component '$component_name'..."
6483 ![ExecuteVitisUnifiedCommand $python_script "collect_reports" \
6484 [list $component_name $hls_work_dir $hls_out_dir] \
6485 "Failed to collect HLS reports for $component_name"]
6487 Msg Warning "No reports found for HLS component '$component_name'"
6490 # Generate markdown summary files (utilization.txt, timing_ok.txt /
6491 # timing_error.txt) under <hls_out_dir>/<component>/. File names mirror the
6492 # Vivado convention so the existing CI logic (release notes assembly and
6493 # timing-failure detection) works for HLS components too
6494 Msg Info "Generating HLS release-notes summary for component '$component_name'..."
6496 ![ExecuteVitisUnifiedCommand $python_script "release_notes" \
6497 [list $component_name $hls_work_dir $hls_out_dir] \
6498 "Failed to generate HLS summary for $component_name"]
6500 Msg Warning "Could not generate HLS summary for component '$component_name'"
6503 Msg Info "HLS component '$component_name' built successfully"
6508 if {!$is_mixed_project && [
info exists dst_dir] && [
file isdirectory $dst_dir]} {
6509 set versions_file [
file normalize "$dst_dir/versions.txt"]
6510 Msg Info "Generating top-level versions.txt for pure-HLS project at $versions_file"
6513 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$project_name] $repo_path] \
6514 commit version hog_hash hog_ver top_hash top_ver \
6515 libs hashes vers cons_ver cons_hash \
6516 ext_names ext_hashes xml_hash xml_ver \
6517 user_ip_repos user_ip_hashes user_ip_vers
6518 if {$commit == 0} {
set commit [
GetSHA]}
6522 set fh [open $versions_file "w"]
6523 puts $fh "## $proj_name Version Table\n"
6524 puts $fh "| **File set** | **Commit SHA** | **Version** |"
6525 puts $fh "| --- | --- | --- |"
6526 puts $fh "| Global | $commit | $version_str |"
6527 puts $fh "| Top Directory | $top_hash | $top_ver_str |"
6528 puts $fh "| Hog | $hog_hash | $hog_ver_str |"
6529 foreach l $libs v $vers h $hashes {
6531 puts $fh "| **Lib:** $l | $h | $v_str |"
6537 Msg Warning "Could not generate top-level versions.txt for pure-HLS project: $err"
6541 Msg Info "Done building HLS components for $project_name"
6557 proc LaunchHlsSimulation {project_name {repo_path .} {hls_simsets {}}} {
6560 set conf_file [
file normalize "$repo_path/Top/$project_name/hog.conf"]
6561 if {![
file exists $conf_file]} {
6562 Msg Error "Configuration file not found: $conf_file"
6565 set properties [
ReadConf $conf_file]
6566 set hls_components [dict filter $properties key {hls:*}]
6568 if {[dict size $hls_components] == 0} {
6569 Msg Info "No HLS components found for project $project_name"
6575 set targeted_mode [
expr {[llength $hls_simsets] > 0}]
6576 set targets [dict create]
6577 foreach entry $hls_simsets {
6578 if {[regexp {^(csim|cosim):(.+)$} $entry -> sim_type comp_name]} {
6579 dict lappend targets $comp_name $sim_type
6581 Msg Error "Invalid HLS simset format '$entry'. Expected 'csim:<component>' or 'cosim:<component>'."
6582 Msg Info "Available HLS components in hog.conf:"
6583 dict for {hls_key hls_props} $hls_components {
6584 if {[regexp {^hls:(.+)$} $hls_key -> cname]} {
6585 Msg Info " - csim:[string trim $cname]"
6586 Msg Info " - cosim:[string trim $cname]"
6593 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/HlsCommands.py"]
6595 dict for {hls_key hls_props} $hls_components {
6596 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {
6599 set component_name [string trim $component_name]
6601 # In targeted mode, skip components not in the request
6602 if {$targeted_mode && ![dict exists $targets $component_name]} {
6606 # Read HLS_CONFIG path from hog.conf (mandatory)
6608 if {[dict exists $hls_props hls_config]} {
6609 set hls_cfg_rel [dict get $hls_props hls_config]
6610 } elseif {[dict exists $hls_props HLS_CONFIG]} {
6611 set hls_cfg_rel [dict get $hls_props HLS_CONFIG]
6613 if {$hls_cfg_rel eq ""} {
6614 Msg Error "HLS component '$component_name' missing HLS_CONFIG in hog.conf"
6618 set cfg_file [file normalize "$repo_path/$hls_cfg_rel"]
6619 if {![file exists $cfg_file]} {
6620 Msg Error "HLS config file not found: $cfg_file (from HLS_CONFIG=$hls_cfg_rel)"
6624 set hls_work_dir [file normalize "$repo_path/Projects/$project_name/vitis_unified/$component_name"]
6625 file mkdir $hls_work_dir
6627 # Determine which simulations to run
6628 if {$targeted_mode} {
6629 # User explicitly requested these sim types via -simset
6630 set sim_types [dict get $targets $component_name]
6631 set run_csim [expr {"csim" in $sim_types}]
6632 set run_cosim [expr {"cosim" in $sim_types}]
6634 # Validate: check if the requested sim is enabled in hog.conf
6635 foreach st $sim_types {
6637 if {[dict exists $hls_props $st]} {
6638 set flag_val [dict get $hls_props $st]
6639 } elseif {[dict exists $hls_props [string toupper $st]]} {
6640 set flag_val [dict get $hls_props [string toupper $st]]
6642 set is_enabled [expr {[string tolower $flag_val] eq "true" || $flag_val eq "1"}]
6644 set upper_st [string toupper $st]
6645 Msg Warning "HLS simulation '$st' requested for '$component_name' but\
6646 $upper_st is not enabled in \[hls:$component_name\] section of\
6647 hog.conf. Running it anyway."
6648 Msg Info "To enable it permanently, add '$upper_st=true' under\
6649 \[hls:$component_name\] in Top/$project_name/hog.conf"
6653 # Default mode: read flags from hog.conf
6656 foreach {key_lower key_upper} {csim CSIM cosim COSIM} {
6658 if {[dict exists $hls_props $key_lower]} {
6659 set val [dict get $hls_props $key_lower]
6660 } elseif {[dict exists $hls_props $key_upper]} {
6661 set val [dict get $hls_props $key_upper]
6663 if {[string tolower $val] eq "true" || $val eq "1"} {
6664 set run_$key_lower 1
6668 if {!$run_csim && !$run_cosim} {
6669 Msg Info "No HLS simulations enabled for '$component_name' (set CSIM=true and/or COSIM=true in \[hls:$component_name\] in hog.conf)"
6674 Msg Info "Running HLS simulations for component: $component_name"
6678 Msg Info "Running C simulation for HLS component '$component_name'..."
6680 ![ExecuteVitisUnifiedCommand $python_script "csim" \
6681 [list $component_name $cfg_file $hls_work_dir] \
6682 "Failed to run C simulation for $component_name"]
6684 Msg Error "C simulation failed for HLS component '$component_name'"
6689 # COSIM (auto-run synthesis if needed)
6691 set syn_done_marker [file normalize "$hls_work_dir/hls/syn"]
6692 if {![file exists $syn_done_marker]} {
6693 Msg Info "C synthesis output not found for '$component_name', running synthesis automatically before co-simulation..."
6695 ![ExecuteVitisUnifiedCommand $python_script "synthesis" \
6696 [list $component_name $cfg_file $hls_work_dir] \
6697 "Failed to run C synthesis for $component_name"]
6699 Msg Error "C synthesis failed for HLS component '$component_name', cannot proceed with co-simulation"
6704 Msg Info "Running C/RTL co-simulation for HLS component '$component_name'..."
6706 ![ExecuteVitisUnifiedCommand $python_script "cosim" \
6707 [list $component_name $cfg_file $hls_work_dir] \
6708 "Failed to run co-simulation for $component_name"]
6710 Msg Error "C/RTL co-simulation failed for HLS component '$component_name'"
6715 Msg Info "HLS simulations completed for '$component_name'"
6718 Msg Info "Done with HLS simulations for $project_name"
6726 proc GetProcFromProps {repo_path props platform} {
6727 if {[dict exists $props "platform:$platform" "BIF"]} {
6728 set bif_file [dict get $props "platform:$platform" "BIF"]
6730 set bif_file "$repo_path/$bif_file"
6734 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
6744 proc GetBifFromProps {repo_path props platform} {
6745 if {[dict exists $props "platform:$platform" "BIF"]} {
6746 set bif_file [dict get $props "platform:$platform" "BIF"]
6748 set bif_file "$repo_path/$bif_file"
6752 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
6761 proc GetPartFromProps {props} {
6762 if {[dict exists $props "main" "PART"]} {
6763 return [
string tolower [dict get $props "main" "PART"]]
6765 Msg Error "Part number not found in properties"
6774 proc GetArchFromPart {part} {
6775 set part [
string tolower $part]
6777 if {[
string match "xczu*" $part] || [
string match "xqzu*" $part]} {
6779 }
elseif {[
string match "xc7z*" $part] || [
string match "xq7z*" $part]} {
6782 [
string match "xcve*" $part] || [
string match "xcvc*" $part] ||
6783 [
string match "xcvp*" $part] || [
string match "xcvm*" $part] ||
6784 [
string match "xqve*" $part] || [
string match "xck26*" $part]
6787 }
elseif {[
string match "xc*" $part] || [
string match "xq*" $part]} {
6791 Msg CriticalWarning "Unknown part number: $part"
6800 proc GetAppsFromProps {props {list_names 0}} {
6801 set prop_apps [dict filter $props key {app:*}]
6802 set apps [dict create]
6803 set app_names [list]
6805 dict for {app_key app_value} $prop_apps {
6806 if {[regexp {^app:(.+)$} $app_key -> app_name]} {
6807 set app_name [string trim [string tolower $app_name]]
6808 # Convert only the keys of the inner dictionary to lowercase
6809 set app_value_lower [dict create]
6810 dict for {key value} $app_value {
6811 dict set app_value_lower [string tolower $key] $value
6813 dict set apps $app_name $app_value_lower
6814 lappend app_names $app_name
6817 if {$list_names eq 1} {
6828 proc GetPlatformsFromProps {props {list_names 0} {lower_case 0}} {
6829 set platforms [dict create]
6830 set platform_names [list]
6831 set prop_platforms [dict filter $props key {platform:*}]
6833 dict for {platform_key platform_value} $prop_platforms {
6834 if {[regexp {^platform:(.+)$} $platform_key -> platform_name]} {
6835 if {$lower_case == 1} {
6836 set platform_name [string trim [string tolower $platform_name]]
6838 set platform_name [string trim $platform_name]
6840 dict set platforms $platform_name $platform_value
6841 lappend platform_names $platform_name
6844 if {$list_names eq 1} {
6845 return $platform_names
6860 proc GenerateBootArtifacts {properties repo_path proj_dir bin_dir proj_name describe bitfile mmi_file} {
6861 set elf_list [glob -nocomplain "$bin_dir/*.elf"]
6865 if {[
llength $elf_list] == 0} {
6866 Msg Warning "No ELF files found in $bin_dir, skipping generation of boot artifacts"
6870 if {![
file exists $bitfile]} {
6871 Msg Warning "Bitfile $bitfile does not exist, skipping generation of boot artifacts"
6875 Msg Info "Generating boot artifacts for $proj_name..."
6876 Msg Info "Found apps: $apps"
6877 Msg Info "Found platforms: $platforms"
6881 foreach elf_file $elf_list {
6882 set elf_name [
file rootname [
file tail $elf_file]]
6883 Msg Info "Found elf name: $elf_name"
6884 Msg Info "Removing $describe from elf"
6887 if {[regexp "^(.+)-(.+)-$describe\$" $elf_name -> project_name elf_app]} {
6888 set elf_app [
string trim [
string tolower $elf_app]]
6889 Msg Info "Found elf_app: $elf_app"
6891 Msg Error "Could not extract app name from elf file: $elf_name"
6894 Msg Info "Removed project name ($project_name) and $describe from elf"
6896 set app_conf [dict get $apps $elf_app]
6897 set plat [dict get $app_conf "platform"]
6898 set app_proc [dict get $app_conf "proc"]
6901 if {[regexp -nocase {microblaze|risc} $app_proc]} {
6902 Msg Info "Detected soft processor ($app_proc) for $elf_app, updating bitstream memory with ELF file..."
6908 if {$proc_cell eq ""} {
6909 set proc_map_file [
file normalize "$proj_dir/vitis_classic/${plat}.PROC_MAP"]
6911 if {[dict exists $proc_map $app_proc]} {
6912 set proc_cell [dict get $proc_map $app_proc]
6913 Msg Info "Processor $app_proc not found in $mmi_file, using $proc_map_file instead."
6915 Msg Error "Could not determine the instance path of processor '$app_proc' for $elf_app, \
6916 neither from $mmi_file nor from $proc_map_file. \
6917 Check that proc= in the \[app:$elf_app\] section of hog.conf matches the processor instance name."
6921 Msg Info "Updating memory at processor cell: $proc_cell"
6923 set update_mem_cmd "updatemem -force -meminfo $mmi_file -data $elf_file -bit $bitfile -proc $proc_cell -out $bitfile"
6924 set ret [
catch {
exec -ignorestderr {*}$update_mem_cmd >@ stdout} result]
6926 Msg Error "Error updating memory for $elf_app: $result"
6928 Msg Info "Done updating memory for $elf_app"
6930 Msg Info "Detected hard processor ($app_proc) for $elf_app. Make sure the .elf file is defined in the platform ($plat)\
6931 .bif file to be included in the bootable binary image (.bin) generation."
6937 foreach plat $platforms {
6939 if {$bif_file != ""} {
6940 Msg Info "Generating bootable binary image (.bin) for $plat"
6942 Msg Info "Architecture: $arch"
6943 Msg Info "BIF file: $bif_file"
6944 if {$arch eq "unknown"} {
6945 Msg CriticalWarning "Skipping bootable image (.bin) generation for $plat: \
6946 could not determine bootgen architecture from the FPGA part."
6949 set bootgen_cmd "bootgen -arch $arch -image $bif_file -o i $bin_dir/$proj_name-$plat-$describe.bin -w on"
6950 set ret [
catch {
exec -ignorestderr {*}$bootgen_cmd >@ stdout} result]
6952 Msg Error "Error generating bootable binary image (.bin) for $plat: $result"
6954 Msg Info "Done generating bootable binary image (.bin) for $plat"
6966 proc GenerateStandaloneXciTargets {} {
6971 foreach xci [get_files -quiet *.xci] {
6972 set xci_file [get_files -quiet $xci]
6973 if {$xci_file eq ""} {
6978 catch {
set parent [get_property -quiet PARENT_COMPOSITE_FILE $xci_file]}
6979 if {$parent ne ""} {
6980 Msg Debug "Skipping [
file tail $xci], it is generated by its parent [
file tail $parent]"
6984 Msg Info "Generating targets for standalone IP [
file tail $xci]..."
6985 if {[
catch {generate_target all $xci_file} err]} {
6986 Msg CriticalWarning "Failed to generate targets for $xci: $err"
7000 proc GetProcInstPathFromMmi {mmi_file app_proc} {
7001 if {![
file exists $mmi_file]} {
7002 Msg Debug "MMI file not found: $mmi_file"
7006 set f [open $mmi_file "r"]
7007 set mmi_content [read $f]
7010 set inst_paths [list]
7011 foreach {match inst_path} [regexp -all -inline {<Processor[^>]*InstPath\s*=\s*"([^"]+)"} $mmi_content] {
7012 if {[lsearch -exact $inst_paths $inst_path] < 0} {
7013 lappend inst_paths $inst_path
7016 Msg Debug "Processor instance paths found in $mmi_file: $inst_paths"
7018 foreach inst_path $inst_paths {
7019 if {[
string equal -nocase [
file tail $inst_path] $app_proc]} {
7025 if {[
llength $inst_paths] == 1} {
7026 set inst_path [
lindex $inst_paths 0]
7027 Msg Info "Processor $app_proc does not match [
file tail $inst_path], \
7028 using the only processor found in the MMI file: $inst_path"
7039 proc ReadProcMap {proc_map_file} {
7040 set proc_map [dict create]
7041 if {[
file exists $proc_map_file]} {
7042 set f [open $proc_map_file "r"]
7043 while {[
gets $f line] >= 0} {
7044 Msg Debug "Line: $line"
7045 if {[regexp {^(\S+)\s+(.+)$} $line -> key value]} {
7046 Msg Debug "Found key: $key, value: $value in proc map file"
7047 dict set proc_map $key $value
7061 proc ListProjects {{repo_path .} {print 1} {ret_conf 0} {silent 0}} {
7062 set top_path [
file normalize $repo_path/Top]
7063 set confs [
findFiles [
file normalize $top_path] hog.conf]
7065 set confs [lsort $confs]
7069 set p [
Relative $top_path [
file dirname $c]]
7072 if {$description eq "test"} {
7073 set description " - Test project"
7074 }
elseif {$description ne ""} {
7075 set description " - $description"
7078 if {$print == 1 || $description ne " - Test project"} {
7080 set g [
file dirname $p]
7083 if {$silent == 0} {
Msg Status ""}
7091 if {$ret_conf == 0} {
7103 proc Md5Sum {file_name} {
7104 if {!([
file exists $file_name])} {
7105 Msg Warning "Could not find $file_name."
7108 if {[
catch {
package require md5 2.0.7} result]} {
7109 Msg Warning "Tcl package md5 version 2.0.7 not found ($result), will use command line..."
7110 set hash [
lindex [
Execute md5sum $file_name] 0]
7112 set file_hash [
string tolower [md5::md5 -hex -file $file_name]]
7126 proc MergeDict {dict0 dict1 {remove_duplicates 1}} {
7127 set outdict [dict merge $dict1 $dict0]
7128 foreach key [dict keys $dict1] {
7129 if {[dict exists $dict0 $key]} {
7130 set temp_list [dict get $dict1 $key]
7131 foreach item $temp_list {
7133 if {[
IsInList $item [
DictGet $outdict $key]] == 0 || $remove_duplicates == 0} {
7135 dict lappend outdict $key $item
7147 proc MoveElementToEnd {inputList element} {
7148 set index [lsearch $inputList $element]
7150 set inputList [
lreplace $inputList $index $index]
7151 lappend inputList $element
7160 proc OpenProject {project_file repo_path} {
7162 open_project $project_file
7164 set project_folder [
file dirname $project_file]
7165 set project [
file tail [
file rootname $project_file]]
7166 if {[
file exists $project_folder]} {
7168 if {![is_project_open]} {
7169 Msg Info "Opening existing project file $project_file..."
7170 project_open $project -current_revision
7173 Msg Error "Project directory not found for $project_file."
7177 Msg Info "Opening existing project file $project_file..."
7179 open_project -file $project_file -do_backup_on_convert 1 -backup_file {./Projects/$project_file.zip}
7181 Msg Info "Opening existing project file $project_file..."
7182 prj_project open $project_file
7184 Msg Error "This IDE is currently not supported by Hog. Exiting!"
7191 return $tcl_platform(platform)
7201 proc ParseJSON {JSON_FILE JSON_KEY} {
7202 set result [
catch {
package require Tcl 8.4} TclFound]
7203 if {"$result" != "0"} {
7204 Msg CriticalWarning "Cannot find Tcl package version equal or higher than 8.4.\n $TclFound\n Exiting"
7208 set result [
catch {
package require json} JsonFound]
7209 if {"$result" != "0"} {
7210 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
7213 set JsonDict [json::json2dict $JSON_FILE]
7214 set result [
catch {dict get $JsonDict $JSON_KEY} RETURNVALUE]
7215 if {"$result" != "0"} {
7216 Msg CriticalWarning "Cannot find $JSON_KEY in $JSON_FILE\n Exiting"
7229 proc ProjectExists {project {repo_path .}} {
7230 set index [lsearch -exact [
ListProjects $repo_path 0] $project]
7236 Msg Warning "Project not found. Available projects in $repo_path are"
7239 Msg Error "Project $project not found in repository $repo_path"
7250 proc ReadConf {file_name} {
7251 if {[
catch {
package require inifile 0.2.3} ERROR]} {
7252 Msg Error "Could not find inifile package version 0.2.3 or higher.\n
7253 To use ghdl, libero or diamond with Hog, you need to install the tcllib package\n
7254 You can install it with 'sudo apt install tcllib' on Debian/Ubuntu or 'sudo dnf install tcllib' on Fedora/RedHat/CentOs."
7258 ::ini::commentchar "#"
7259 set f [::ini::open $file_name]
7260 set properties [dict create]
7261 foreach sec [::ini::sections $f] {
7263 if {$new_sec == "files"} {
7266 set key_pairs [::ini::get $f $sec]
7268 regsub -all {\{\"} $key_pairs "\{" key_pairs
7269 regsub -all {\"\}} $key_pairs "\}" key_pairs
7271 dict set properties $new_sec [dict create {*}$key_pairs]
7284 proc ReadExtraFileList {extra_file_name} {
7285 set extra_file_dict [dict create]
7286 if {[
file exists $extra_file_name]} {
7287 set file [open $extra_file_name "r"]
7288 set file_data [read $file]
7291 set data [
split $file_data "\n"]
7292 foreach line $data {
7293 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
7294 set ip_and_md5 [regexp -all -inline {\S+} $line]
7295 dict lappend extra_file_dict "[
lindex $ip_and_md5 0]" "[
lindex $ip_and_md5 1]"
7299 return $extra_file_dict
7310 proc ExpandHlsConfigFiles {cfg_file} {
7312 if {![
file exists $cfg_file] || ![
file isfile $cfg_file]} {
7315 set cfg_dir [
file normalize [
file dirname $cfg_file]]
7317 if {[
catch {
set fp [open $cfg_file r]} err]} {
7318 Msg Warning "ExpandHlsConfigFiles: cannot open $cfg_file: $err"
7321 set lines [
split [read $fp] "\n"]
7324 foreach line $lines {
7325 if {[regexp {^[\t\s]*$} $line]} {continue}
7326 if {[regexp {^[\t\s]*\#} $line]} {continue}
7327 if {[regexp {^\s*\[.*\]\s*$} $line]} {continue}
7328 if {![regexp {^\s*[^=\s]+\s*=\s*(.+?)\s*$} $line -> value]} {continue}
7330 foreach tok [regexp -all -inline {\S+} $value] {
7331 if {[
file pathtype $tok] eq "absolute"} {
7332 set candidate [
file normalize $tok]
7334 set candidate [
file normalize [
file join $cfg_dir $tok]]
7336 if {[
file isfile $candidate]} {
7337 lappend out $candidate
7338 }
elseif {[
file isdirectory $candidate]} {
7339 set stack [list $candidate]
7340 while {[
llength $stack] > 0} {
7341 set d [
lindex $stack 0]
7342 set stack [
lrange $stack 1 end]
7343 foreach f [glob -nocomplain -directory $d -types f *] {
7344 lappend out [
file normalize $f]
7346 foreach sub [glob -nocomplain -directory $d -types d *] {
7353 return [lsort -unique $out]
7363 proc GetHlsConfigsFromProjConf {conf_file repo_path} {
7364 set out [dict create]
7365 if {![
file exists $conf_file]} {
return $out}
7366 if {[
catch {
set properties [
ReadConf $conf_file]} err]} {
7367 Msg Debug "GetHlsConfigsFromProjConf: cannot parse $conf_file: $err"
7370 set hls_components [dict filter $properties key {hls:*}]
7371 dict for {hls_key hls_props} $hls_components {
7372 if {![regexp {^hls:(.+)$} $hls_key -> component_name]} {continue}
7373 set component_name [string trim $component_name]
7375 if {[dict exists $hls_props hls_config]} {
7376 set cfg_rel [dict get $hls_props hls_config]
7377 } elseif {[dict exists $hls_props HLS_CONFIG]} {
7378 set cfg_rel [dict get $hls_props HLS_CONFIG]
7380 if {$cfg_rel eq ""} {continue}
7381 set cfg_abs [file normalize "$repo_path/$cfg_rel"]
7382 if {![file exists $cfg_abs]} {
7383 Msg CriticalWarning "HLS component '$component_name': HLS_CONFIG=$cfg_rel does not exist on disk."
7386 dict set out $component_name $cfg_abs
7408 proc ReadListFile {args} {
7411 if {[
catch {
package require cmdline} ERROR]} {
7412 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
7418 {lib.arg "" "The name of the library files will be added to, if not given will be extracted from the file name."}
7419 {fileset.arg "" "The name of the library, from the main list file"}
7420 {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."}
7421 {print_log "If set, will use PrintFileTree for the VIEW directive"}
7422 {indent.arg "" "Used to indent files with the VIEW directive"}
7425 set usage "USAGE: ReadListFile \[options\] <list file> <path>"
7426 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
7432 set list_file [
lindex $args 0]
7433 set path [
lindex $args 1]
7434 set sha_mode $options(sha_mode)
7435 set lib $options(lib)
7436 set fileset $options(fileset)
7437 set print_log $options(print_log)
7438 set indent $options(indent)
7440 if {$sha_mode == 1} {
7441 set sha_mode_opt "-sha_mode"
7446 if {$print_log == 1} {
7447 set print_log_opt "-print_log"
7449 set print_log_opt ""
7454 set lib [
file rootname [
file tail $list_file]]
7456 set fp [open $list_file r]
7457 set file_data [read $fp]
7459 set list_file_ext [
file extension $list_file]
7460 switch $list_file_ext {
7462 if {$fileset eq ""} {
7468 set fileset "constrs_1"
7471 set fileset "sources_1"
7475 set libraries [dict create]
7476 set filesets [dict create]
7477 set properties [dict create]
7479 set data [
split $file_data "\n"]
7481 set n [
llength $data]
7483 if {$print_log == 1} {
7484 if {$indent eq ""} {
7485 set list_file_rel [
file tail $list_file]
7486 Msg Status "\n$list_file_rel"
7490 Msg Debug "$n lines read from $list_file."
7493 foreach line $data {
7495 if {![regexp {^[\t\s]*$} $line] & ![regexp {^[\t\s]*\#} $line]} {
7496 set file_and_prop [regexp -all -inline {\S+} $line]
7497 set srcfile [
lindex $file_and_prop 0]
7498 set srcfile "$path/$srcfile"
7500 set srcfiles [glob -nocomplain $srcfile]
7503 if {$srcfiles != $srcfile && ![
string equal $srcfiles ""]} {
7504 Msg Debug "Wildcard source expanded from $srcfile to $srcfiles"
7506 if {![
file exists $srcfile]} {
7507 if {$print_log == 0} {
7508 Msg CriticalWarning "File: $srcfile (from list file: $list_file) does not exist."
7514 foreach vhdlfile $srcfiles {
7515 if {[
file exists $vhdlfile]} {
7516 set vhdlfile [
file normalize $vhdlfile]
7517 set extension [
file extension $vhdlfile]
7519 set prop [
lrange $file_and_prop 1 end]
7522 set library [
lindex [regexp -inline {\ylib\s*=\s*(.+?)\y.*} $prop] 1]
7523 if {$library == ""} {
7527 if {$extension == $list_file_ext} {
7530 set ref_path [
lindex [regexp -inline {\ypath\s*=\s*(\S+).*} $prop] 1]
7531 if {$ref_path eq ""} {
7534 set ref_path [
file normalize $path/$ref_path]
7536 Msg Debug "List file $vhdlfile found in list file, recursively opening it using path \"$ref_path\"..."
7537 if {$print_log == 1} {
7538 if {[
file normalize $last_printed] ne [
file normalize $vhdlfile]} {
7539 Msg Status "$indent Inside [
file tail $vhdlfile]:"
7543 lassign [
ReadListFile {*}"-indent \" $indent\" -lib $library -fileset $fileset $sha_mode_opt $print_log_opt $vhdlfile $ref_path"] l p fs
7545 set properties [
MergeDict $p $properties]
7547 }
elseif {[lsearch {.src .sim .con ReadExtraFileList} $extension] >= 0} {
7549 Msg Error "$vhdlfile cannot be included into $list_file, $extension files must be included into $extension files."
7552 regsub -all " *= *" $prop "=" prop
7556 if {[
string first "lib=" $p] == -1} {
7558 set pos [
string first "=" $p]
7562 set prop_name [
string range $p 0 [
expr {$pos - 1}]]
7565 if {$list_file_ext eq ".ipb"} {
7566 dict lappend properties $vhdlfile $path/$p
7568 dict lappend properties $vhdlfile $p
7570 Msg Debug "Adding property $p to $vhdlfile..."
7571 }
elseif {$list_file_ext != ".ipb"} {
7572 Msg Warning "Setting Property $p is not supported for file $vhdlfile or it is already its default. \
7577 if {[lsearch {.xci .ip .bd .xcix} $extension] >= 0} {
7579 set lib_name "ips.src"
7580 }
elseif {[
IsInList $extension {.vhd .vhdl}] || $list_file_ext == ".sim"} {
7582 if {![
IsInList $extension {.vhd .vhdl}]} {
7583 set lib_name "others.sim"
7585 set lib_name "$library$list_file_ext"
7587 }
elseif {$list_file_ext == ".con"} {
7588 set lib_name "sources.con"
7589 }
elseif {$list_file_ext == ".ipb"} {
7590 set lib_name "xml.ipb"
7591 }
elseif {[
IsInList $list_file_ext {.src}] && [
IsInList $extension {.c .cpp .h .hpp}]} {
7593 set lib_name "$library$list_file_ext"
7596 set lib_name "others.src"
7599 Msg Debug "Appending $vhdlfile to $lib_name list..."
7600 dict lappend libraries $lib_name $vhdlfile
7601 if {$sha_mode != 0 && [
file type $vhdlfile] eq "link"} {
7604 dict lappend libraries $lib_name $real_file
7605 Msg Debug "File $vhdlfile is a soft link, also adding the real file: $real_file"
7613 if {$list_file_ext eq ".src" && $extension eq ".cfg"} {
7615 if {$print_log == 1 && [
llength $hls_extras] > 0} {
7616 Msg Status "$indent Inside [
file tail $vhdlfile] (HLS auto-expand):"
7618 set n_extras [
llength $hls_extras]
7620 foreach hls_extra $hls_extras {
7622 if {$hls_extra eq $vhdlfile} {continue}
7623 Msg Debug "HLS cfg expansion: adding $hls_extra (from $vhdlfile) to $lib_name"
7624 if {$print_log == 1} {
7625 if {$i == $n_extras} {
set pad "└──"}
else {
set pad "├──"}
7626 set rel [
Relative [
file dirname $vhdlfile] $hls_extra]
7627 Msg Status "$indent $pad $rel"
7629 dict lappend libraries $lib_name $hls_extra
7630 if {$sha_mode != 0 && [
file type $hls_extra] eq "link"} {
7632 dict lappend libraries $lib_name $real_extra
7633 Msg Debug "HLS expanded file $hls_extra is a soft link, also adding $real_extra"
7640 if {[dict exists $filesets $fileset] == 0} {
7642 Msg Debug "Adding $fileset to the fileset dictionary..."
7643 Msg Debug "Adding library $lib_name to fileset $fileset..."
7644 dict set filesets $fileset $lib_name
7648 Msg Debug "Adding library $lib_name to fileset $fileset..."
7649 dict lappend filesets $fileset $lib_name
7655 Msg CriticalWarning "File $vhdlfile not found."
7661 if {$sha_mode != 0} {
7663 if {$list_file_ext eq ".ipb"} {
7664 set sha_lib "xml.ipb"
7666 set sha_lib $lib$list_file_ext
7668 dict lappend libraries $sha_lib [
file normalize $list_file]
7669 if {[
file type $list_file] eq "link"} {
7672 dict lappend libraries $lib$list_file_ext $real_file
7673 Msg Debug "List file $list_file is a soft link, also adding the real file: $real_file"
7676 return [list $libraries $properties $filesets]
7685 proc Relative {base dst {quiet 0}} {
7686 if {![
string equal [
file pathtype $base] [
file pathtype $dst]]} {
7688 Msg CriticalWarning "Unable to compute relation for paths of different path types: [
file pathtype $base] vs. [
file pathtype $dst], ($base vs. $dst)"
7693 set base [
file normalize [
file join [
pwd] $base]]
7694 set dst [
file normalize [
file join [
pwd] $dst]]
7697 set base [
file split $base]
7698 set dst [
file split $dst]
7700 while {[
string equal [
lindex $dst 0] [
lindex $base 0]]} {
7701 set dst [
lrange $dst 1 end]
7702 set base [
lrange $base 1 end]
7703 if {![
llength $dst]} {break}
7706 set dstlen [
llength $dst]
7707 set baselen [
llength $base]
7709 if {($dstlen == 0) && ($baselen == 0)} {
7712 while {$baselen > 0} {
7713 set dst [
linsert $dst 0 ..]
7716 set dst [
eval [
linsert $dst 0 file join]]
7727 proc RelativeLocal {pathName filePath} {
7728 if {[
string first [
file normalize $pathName] [
file normalize $filePath]] != -1} {
7729 return [
Relative $pathName $filePath]
7740 proc RemoveDuplicates {mydict} {
7741 set new_dict [dict create]
7742 foreach key [dict keys $mydict] {
7743 set values [
DictGet $mydict $key]
7744 foreach value $values {
7745 set idxs [
lreverse [
lreplace [lsearch -exact -all $values $value] 0 0]]
7747 set values [
lreplace $values $idx $idx]
7750 dict set new_dict $key $values
7761 proc ResetRepoFiles {reset_file} {
7762 if {[
file exists $reset_file]} {
7763 Msg Info "Found $reset_file, opening it..."
7764 set fp [open $reset_file r]
7765 set wild_cards [lsearch -all -inline -not -regexp [
split [read $fp] "\n"] "^ *$"]
7767 Msg Info "Found the following files/wild cards to restore if modified: $wild_cards..."
7768 foreach w $wild_cards {
7770 if {[
llength $mod_files] > 0} {
7771 Msg Info "Found modified $w files: $mod_files, will restore them..."
7774 Msg Info "No modified $w files found."
7785 proc RestoreModifiedFiles {{repo_path "."} {pattern "."}} {
7788 set ret [
Git checkout $pattern]
7799 proc SearchHogProjects {dir} {
7800 set projects_list {}
7801 if {[
file exists $dir]} {
7802 if {[
file isdirectory $dir]} {
7803 foreach proj_dir [glob -nocomplain -types d $dir/*] {
7804 if {![regexp {^.*Top/+(.*)$} $proj_dir dummy proj_name]} {
7805 Msg Warning "Could not parse Top directory $dir"
7808 if {[
file exists "$proj_dir/hog.conf"]} {
7809 lappend projects_list $proj_name
7812 lappend projects_list $p
7817 Msg Error "Input $dir is not a directory!"
7820 Msg Error "Directory $dir doesn't exist!"
7822 return $projects_list
7831 proc SetGenericsSimulation {repo_path proj_dir target} {
7832 set top_dir "$repo_path/Top/$proj_dir"
7833 set simsets [get_filesets]
7834 if {$simsets != ""} {
7835 foreach simset $simsets {
7837 if {[get_property FILESET_TYPE $simset] != "SimulationSrcs"} {
7841 set merged_generics_dict [dict create]
7845 set simset_generics [
DictGet $simset_dict "generics"]
7846 set merged_generics_dict [
MergeDict $merged_generics_dict $simset_generics 0]
7849 Msg Debug "TOP = [get_property top [get_filesets sources_1]]"
7850 Msg Debug "GENERICS = [get_property generic [get_filesets sources_1]]"
7852 set_property generic $generic_str [get_filesets $simset]
7853 Msg Info "Setting generics $generic_str for simulator $target\
7854 and simulation file-set $simset..."
7866 proc SetTopProperty {top_module fileset} {
7867 Msg Info "Setting TOP property to $top_module module"
7870 set_property "top" $top_module [get_filesets $fileset]
7873 set_global_assignment -name TOP_LEVEL_ENTITY $top_module
7875 set_root -module $top_module
7877 prj_impl option top $top_module
7882 proc VIVADO_PATH_PROPERTIES {} {
7883 return {"\.*\.TCL\.PRE$" "^.*\.TCL\.POST$" "^RQS_FILES$" "^INCREMENTAL\_CHECKPOINT$" "NOC\_SOLUTION\_FILE"}
7887 proc VITIS_PATH_PROPERTIES {} {
7888 return {"^HW$" "^XPFM$" "^LINKER-SCRIPT$" "^LIBRARIES$" "^LIBRARY-SEARCH-PATH$"}
7898 proc WriteConf {file_name config {comment ""}} {
7899 if {[
catch {
package require inifile 0.2.3} ERROR]} {
7900 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
7904 ::ini::commentchar "#"
7905 set f [::ini::open $file_name w]
7907 foreach sec [dict keys $config] {
7908 set section [dict get $config $sec]
7909 dict for {p v} $section {
7910 if {[string trim $v] == ""} {
7911 Msg Warning "Property $p has empty value. Skipping..."
7914 ::ini::set $f $sec $p $v
7919 if {![
string equal "$comment" ""]} {
7920 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $comment
7921 set hog_header "Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
7922 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $hog_header
7937 proc WriteGenerics {
7938 mode repo_path design date timee
7939 commit version top_hash top_ver hog_hash hog_ver
7940 cons_ver cons_hash libs vers hashes ext_names ext_hashes
7941 user_ip_repos user_ip_vers user_ip_hashes flavour {xml_ver ""} {xml_hash ""}
7943 Msg Info "Passing parameters/generics to project's top module..."
7946 set generic_string [
concat \
7958 if {$xml_hash != "" && $xml_ver != ""} {
7959 lappend generic_string \
7964 foreach l $libs v $vers h $hashes {
7970 set ver [regsub -all {[\.-]} $ver {_}]
7971 set hash [regsub -all {[\.-]} $hash {_}]
7972 lappend generic_string "$ver" "$hash"
7975 foreach e $ext_names h $ext_hashes {
7977 lappend generic_string "$hash"
7980 foreach repo $user_ip_repos v $user_ip_vers h $user_ip_hashes {
7981 set repo_name [
file tail $repo]
7982 set ver "[
string toupper $repo_name]_VER=[
FormatGeneric $v]"
7983 set hash "[
string toupper $repo_name]_SHA=[
FormatGeneric $h]"
7984 set ver [regsub -all {[\.-]} $ver {_}]
7985 set hash [regsub -all {[\.-]} $hash {_}]
7986 lappend generic_string "$ver" "$hash"
7989 if {$flavour != -1} {
7990 lappend generic_string "FLAVOUR=$flavour"
7996 set generic_string "$prj_generics $generic_string"
8002 if {$mode == "create" || [
IsISE]} {
8005 if {[
file exists $top_file]} {
8008 Msg Debug "Found top level generics $generics in $top_file"
8010 set filtered_generic_string ""
8012 foreach generic_to_set [
split [
string trim $generic_string]] {
8013 set key [
lindex [
split $generic_to_set "="] 0]
8014 if {[dict exists $generics $key]} {
8015 Msg Debug "Hog generic $key found in $top_name"
8016 lappend filtered_generic_string "$generic_to_set"
8018 Msg Warning "Generic $key is passed by Hog but is NOT present in $top_name."
8024 set generic_string $filtered_generic_string
8029 set_property generic $generic_string [current_fileset]
8030 Msg Info "Setting parameters/generics..."
8031 Msg Debug "Detailed parameters/generics: $generic_string"
8036 set simulator [get_property target_simulator [current_project]]
8037 if {$mode == "create"} {
8044 Msg Info "Setting Synplify parameters/generics one by one..."
8045 foreach generic $generic_string {
8046 Msg Debug "Setting Synplify generic: $generic"
8047 set_option -hdl_param -set "$generic"
8050 Msg Info "Setting Diamond parameters/generics one by one..."
8051 prj_impl option -impl Implementation0 HDL_PARAM "$generic_string"
8053 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
8055 foreach app_name [dict keys $ws_apps] {
8056 set defined_symbols [app config -name $app_name -get define-compiler-symbols]
8057 foreach generic_to_set [
split [
string trim $generic_string]] {
8058 set key [
lindex [
split $generic_to_set "="] 0]
8059 set value [
lindex [
split $generic_to_set "="] 1]
8060 if {[
string match "32'h*" $value]} {
8061 set value [
string map {"32'h" "0x"} $value]
8064 foreach symbol [
split $defined_symbols ";"] {
8065 if {[
string match "$key=*" $symbol]} {
8066 Msg Debug "Generic $key found in $app_name, removing it..."
8067 app config -name $app_name -remove define-compiler-symbols "$symbol"
8071 Msg Info "Setting Vitis parameters/generics for app $app_name: $key=$value"
8072 app config -name $app_name define-compiler-symbols "$key=$value"
8084 proc WriteGenericsToBdIPs {mode repo_path proj generic_string} {
8085 Msg Debug "Parameters/generics passed to WriteGenericsToIP: $generic_string"
8087 set bd_ip_generics false
8089 if {[dict exists $properties "hog"]} {
8090 set propDict [dict get $properties "hog"]
8091 if {[dict exists $propDict "PASS_GENERICS_TO_BD_IPS"]} {
8092 set bd_ip_generics [dict get $propDict "PASS_GENERICS_TO_BD_IPS"]
8096 if {[
string compare [
string tolower $bd_ip_generics] "false"] == 0} {
8100 if {$mode == "synth"} {
8101 Msg Info "Attempting to apply generics pre-synthesis..."
8102 set PARENT_PRJ [get_property "PARENT.PROJECT_PATH" [current_project]]
8103 set workaround [open "$repo_path/Projects/$proj/.hog/presynth_workaround.tcl" "w"]
8104 puts $workaround "source \[lindex \$argv 0\];"
8105 puts $workaround "open_project \[lindex \$argv 1\];"
8106 puts $workaround "WriteGenericsToBdIPs \[lindex \$argv 2\] \[lindex \$argv 3\] \[lindex \$argv 4\] \[lindex \$argv 5\];"
8107 puts $workaround "close_project"
8111 exec vivado -mode batch -source $repo_path/Projects/$proj/.hog/presynth_workaround.tcl \
8112 -tclargs $repo_path/Hog/Tcl/hog.tcl $PARENT_PRJ \
8113 "childprocess" $repo_path $proj $generic_string
8116 Msg Error "Encountered an error while attempting workaround: $errMsg"
8118 file delete $repo_path/Projects/$proj/.hog/presynth_workaround.tcl
8120 Msg Info "Done applying generics pre-synthesis."
8124 Msg Info "Looking for IPs to add generics to..."
8125 set ips_generic_string ""
8126 foreach generic_to_set [
split [
string trim $generic_string]] {
8127 set key [
lindex [
split $generic_to_set "="] 0]
8128 set value [
lindex [
split $generic_to_set "="] 1]
8129 append ips_generic_string "CONFIG.$key $value "
8133 if {[
string compare [
string tolower $bd_ip_generics] "true"] == 0} {
8136 set ip_regex $bd_ip_generics
8139 set ip_list [get_ips -regex $ip_regex]
8140 Msg Debug "IPs found with regex \{$ip_regex\}: $ip_list"
8142 set regen_targets {}
8144 foreach {ip} $ip_list {
8145 set WARN_ABOUT_IP false
8146 set ip_props [list_property [get_ips $ip]]
8149 if {[lsearch -exact $ip_props "IS_BD_CONTEXT"] == -1} {
8153 if {[get_property "IS_BD_CONTEXT" [get_ips $ip]] eq "1"} {
8154 foreach {ip_prop} $ip_props {
8155 if {[dict exists $ips_generic_string $ip_prop]} {
8156 if {$WARN_ABOUT_IP == false} {
8157 lappend regen_targets [get_property SCOPE [get_ips $ip]]
8158 Msg Warning "The ip \{$ip\} contains generics that are set by Hog.\
8159 If this is IP is apart of a block design, the .bd file may contain stale, unused, values.\
8160 Hog will always apply the most up-to-date values to the IP during synthesis,\
8161 however these values may or may not be reflected in the .bd file."
8162 set WARN_ABOUT_IP true
8167 set xci_path [get_property IP_FILE [get_ips $ip]]
8169 if {[
string equal $generic_format "ERROR"]} {
8170 Msg Warning "Could not find format for generic $ip_prop in IP $ip. Skipping..."
8174 set value_to_set [dict get $ips_generic_string $ip_prop]
8175 switch -exact $generic_format {
8177 if {[
string match "32'h*" $value_to_set]} {
8178 scan [
string map {"32'h" ""} $value_to_set] "%x" value_to_set
8182 set value_to_set [
expr {$value_to_set ? "true" : "false"}]
8185 if {[
string match "32'h*" $value_to_set]} {
8186 binary scan [
binary format H* [
string map {"32'h" ""} $value_to_set]] d value_to_set
8190 if {[
string match "32'h*" $value_to_set]} {
8191 set value_to_set [
string map {"32'h" "0x"} $value_to_set]
8195 set value_to_set [
format "%s" $value_to_set]
8198 Msg Warning "Unknown generic format $generic_format for IP $ip. Will attempt to pass as string..."
8203 Msg Info "The IP \{$ip\} contains: $ip_prop ($generic_format), setting it to $value_to_set."
8204 if {[
catch {set_property -name $ip_prop -value $value_to_set -objects [get_ips $ip]} prop_error]} {
8205 Msg CriticalWarning "Failed to set property $ip_prop to $value_to_set for IP \{$ip\}: $prop_error"
8212 foreach {regen_target} [lsort -unique $regen_targets] {
8213 Msg Info "Regenerating target: $regen_target"
8214 if {[
catch {generate_target -force all [get_files $regen_target]} prop_error]} {
8215 Msg CriticalWarning "Failed to regen targets: $prop_error"
8223 proc GetGenericFormatFromXciXML {generic_name xml_file} {
8224 if {![
file exists $xml_file]} {
8225 Msg Error "Could not find XML file: $xml_file"
8229 set fp [open $xml_file r]
8230 set xci_data [read $fp]
8233 set paramType "string"
8234 set modelparam_regex [
format {^.*\y%s\y.*$} [
string map {"CONFIG." "MODELPARAM_VALUE."} $generic_name]]
8235 set format_regex {format="([^"]+)"}
8237 set line [
lindex [regexp -inline -line $modelparam_regex $xci_data] 0]
8238 Msg Debug "line: $line"
8240 if {[regexp $format_regex $line match format_value]} {
8241 Msg Debug "Extracted: $format_value format from xml"
8242 set paramType $format_value
8244 Msg Debug "No format found, using string"
8253 proc GetGenericFormatFromXci {generic_name xci_file} {
8254 if {![
file exists $xci_file]} {
8255 Msg Error "Could not find XCI file: $xci_file"
8259 set fp [open $xci_file r]
8260 set xci_data [read $fp]
8263 set paramType "string"
8264 if {[
string first "xilinx.com:schema:json_instance:1.0" $xci_data] == -1} {
8265 Msg Debug "XCI format is not JSON, trying XML..."
8266 set xml_file "[
file rootname $xci_file].xml"
8270 set generic_name [
string map {"CONFIG." ""} $generic_name]
8271 set ip_inst [
ParseJSON $xci_data "ip_inst"]
8272 set parameters [dict get $ip_inst parameters]
8273 set component_parameters [dict get $parameters component_parameters]
8274 if {[dict exists $component_parameters $generic_name]} {
8275 set generic_info [dict get $component_parameters $generic_name]
8276 if {[dict exists [
lindex $generic_info 0] format]} {
8277 set paramType [dict get [
lindex $generic_info 0] format]
8278 Msg Debug "Extracted: $paramType format from xci"
8281 Msg Debug "No format found, using string"
8294 proc WriteGitLabCIYAML {proj_name {ci_conf ""}} {
8295 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
8296 Msg CriticalWarning "Cannot find package YAML.\n Error message: $YAMLPACKAGE. \
8297 If you are running on tclsh, you can fix this by installing package \"tcllib\""
8302 if {$ci_conf != ""} {
8304 foreach sec [dict keys $ci_confs] {
8305 if {[
string first : $sec] == -1} {
8306 lappend job_list $sec
8310 set job_list {"generate_project" "simulate_project"}
8314 set out_yaml [huddle create]
8315 foreach job $job_list {
8317 set huddle_tags [huddle list]
8319 set sec_dict [dict create]
8321 if {$ci_confs != ""} {
8322 foreach var [dict keys [dict get $ci_confs $job]] {
8323 if {$var == "tags"} {
8324 set tag_section "tags"
8325 set tags [dict get [dict get $ci_confs $job] $var]
8326 set tags [
split $tags ","]
8328 set tag_list [huddle list $tag]
8329 set huddle_tags [huddle combine $huddle_tags $tag_list]
8332 dict set sec_dict $var [dict get [dict get $ci_confs $job] $var]
8338 set huddle_variables [huddle create "PROJECT_NAME" $proj_name "extends" ".vars"]
8339 if {[dict exists $ci_confs "$job:variables"]} {
8340 set var_dict [dict get $ci_confs $job:variables]
8341 foreach var [dict keys $var_dict] {
8343 set value [dict get $var_dict "$var"]
8344 set var_inner [huddle create "$var" "$value"]
8345 set huddle_variables [huddle combine $huddle_variables $var_inner]
8350 set middle [huddle create "extends" ".$job" "variables" $huddle_variables]
8351 foreach sec [dict keys $sec_dict] {
8352 set value [dict get $sec_dict $sec]
8353 set var_inner [huddle create "$sec" "$value"]
8354 set middle [huddle combine $middle $var_inner]
8356 if {$tag_section != ""} {
8357 set middle2 [huddle create "$tag_section" $huddle_tags]
8358 set middle [huddle combine $middle $middle2]
8361 set outer [huddle create "$job:$proj_name" $middle]
8362 set out_yaml [huddle combine $out_yaml $outer]
8365 return [
string trimleft [yaml::huddle2yaml $out_yaml] "-"]
8375 proc WriteListFiles {libs props list_path repo_path {ext_path ""}} {
8377 foreach lib [dict keys $libs] {
8378 if {[
llength [
DictGet $libs $lib]] > 0} {
8379 set list_file_name $list_path$lib
8380 set list_file [open $list_file_name w]
8381 Msg Info "Writing $list_file_name..."
8382 puts $list_file "#Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
8383 foreach file [
DictGet $libs $lib] {
8385 set prop [
DictGet $props $file]
8389 puts $list_file "$file_path $prop"
8392 set ext_list_file [open "[
file rootname $list_file].ext" a]
8393 puts $ext_list_file "$file_path $prop"
8394 close $ext_list_file
8397 Msg Warning "The path of file $file is not relative to your repository. Please check!"
8413 proc WriteSimListFile {simset libs props simsets list_path repo_path {force 0}} {
8415 set list_file_name $list_path/${simset}.sim
8416 if {$force == 0 && [
file exists $list_file_name]} {
8417 Msg Info "List file $list_file_name already exists, skipping..."
8421 set list_file [open $list_file_name a+]
8424 puts $list_file "\[files\]"
8425 Msg Info "Writing $list_file_name..."
8426 foreach lib [
DictGet $simsets $simset] {
8427 foreach file [
DictGet $libs $lib] {
8429 set prop [
DictGet $props $file]
8433 set lib_name [
file rootname $lib]
8434 if {$lib_name != $simset && [
file extension $file] == ".vhd" && [
file extension $file] == ""} {
8435 lappend prop "lib=$lib_name"
8437 puts $list_file "$file_path $prop"
8440 Msg Warning "The path of file $file is not relative to your repository. Please check!"
8452 proc WriteToFile {File msg} {
8453 set f [open $File a+]
8464 proc WriteUtilizationSummary {input output project_name run} {
8465 set f [open $input "r"]
8466 set o [open $output "a"]
8467 puts $o "## $project_name $run Utilization report\n\n"
8468 struct::matrix util_m
8469 util_m add columns 14
8472 util_m add row "| **Site Type** | **Used** | **Fixed** | **Prohibited** | **Available** | **Util%** |"
8473 util_m add row "| --- | --- | --- | --- | --- | --- |"
8475 util_m add row "| **Site Type** | **Used** | **Fixed** | **Available** | **Util%** |"
8476 util_m add row "| --- | --- | --- | --- | --- |"
8486 while {[
gets $f line] >= 0} {
8487 if {([
string first "| CLB LUTs" $line] >= 0 || [
string first "| Slice LUTs" $line] >= 0) && $luts == 0} {
8488 util_m add row $line
8491 if {([
string first "| CLB Registers" $line] >= 0 || [
string first "| Slice Registers" $line] >= 0) && $regs == 0} {
8492 util_m add row $line
8495 if {[
string first "| Block RAM Tile" $line] >= 0 && $bram == 0} {
8496 util_m add row $line
8499 if {[
string first "URAM " $line] >= 0 && $uram == 0} {
8500 util_m add row $line
8503 if {[
string first "DSPs" $line] >= 0 && $dsps == 0} {
8504 util_m add row $line
8507 if {[
string first "Bonded IOB" $line] >= 0 && $ios == 0} {
8508 util_m add row $line
8515 puts $o [util_m format 2string]
8521 Msg Error "Found Git version older than 2.7.2. Hog will not work as expected, exiting now."
8530 proc GetCurl {{gitlab_url "https://gitlab.com"}} {
8531 if {[
auto_execok curl] == ""} {
8532 Msg Warning "Cannot find a working curl invocation"
8537 set cmd [list curl --silent --show-error]
8539 set cmd [list env -u LD_LIBRARY_PATH curl --silent --show-error]
8542 if {![
catch {
exec {*}$cmd -I $gitlab_url}]} {
8545 Msg Warning "Impossible to contact $gitlab_url"