17 if {![
info exists tcl_path]} {
18 set tcl_path [
file normalize "[
file dirname [
info script]]"]
21 set hog_path [
file normalize "[
file dirname [
info script]]"]
23 source "$hog_path/utils/Logger.tcl"
27 set CI_STAGES {"generate_project" "simulate_project"}
28 set CI_PROPS {"-synth_only"}
37 proc AddFile {file fileset} {
39 add_files -norecurse -fileset $fileset $file
49 proc AddHogFiles {libraries properties filesets} {
50 Msg Info "Adding source files to project..."
51 Msg Debug "Filesets: $filesets"
52 Msg Debug "Libraries: $libraries"
53 Msg Debug "Properties: $properties"
56 set synth_conf_command "organize_tool_files -tool {SYNTHESIZE} -input_type {constraint}"
58 set timing_conf_command "organize_tool_files -tool {VERIFYTIMING} -input_type {constraint}"
60 set place_conf_command "organize_tool_files -tool {PLACEROUTE} -input_type {constraint}"
64 foreach fileset [dict keys $filesets] {
65 Msg Debug "Fileset: $fileset"
68 if {[
string equal [get_filesets -quiet $fileset] ""]} {
70 create_fileset -simset $fileset
73 current_fileset -simset [get_filesets $fileset]
74 set simulation [get_filesets $fileset]
76 set_property -name {$simulator.compile.vhdl_syntax} -value {2008} -objects $simulation
78 set_property SOURCE_SET sources_1 $simulation
82 set libs_in_fileset [
DictGet $filesets $fileset]
83 if {[
IsInList "ips.src" $libs_in_fileset]} {
92 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
95 set vitis_workspace "$globalSettings::build_dir/vitis_unified"
96 set python_script "$globalSettings::repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"
98 if {![
ExecuteVitisUnifiedCommand $python_script "app_list" [list $vitis_workspace] "Failed to get app list from Vitis Unified" json_output]} {
99 Msg Warning "Failed to get app list from Vitis Unified"
102 if {[
catch {
package require json}]} {
103 Msg Warning "JSON package not available for parsing Vitis Unified app list"
106 set json_output_filtered ""
107 if {[regexp -lineanchor {\{.*\}} $json_output json_output_filtered]} {
108 set ws_apps [json::json2dict $json_output_filtered]
110 set ws_apps [json::json2dict $json_output]
117 if {$ws_apps ne ""} {
118 dict for {app_name app_config} $ws_apps {
119 set app_lib [string tolower "app_$app_name\.src"]
120 if {![IsInList $app_lib $libs_in_fileset 0 1]} {
121 Msg Warning "App '$app_name' exists in workspace but no corresponding sourcefile '$app_lib' found. \
122 Make sure you have a list file with the correct naming convention: \[app_<app_name>\.src\]"
131 set app_files_dict [dict create]
135 foreach lib $libs_in_fileset {
136 Msg Debug "lib: $lib \n"
137 set lib_files [
DictGet $libraries $lib]
138 Msg Debug "Files in $lib: $lib_files"
139 set rootlib [
file rootname [
file tail $lib]]
140 set ext [
file extension $lib]
141 Msg Debug "lib: $lib ext: $ext fileset: $fileset"
143 if {[
IsXilinx] && !([
info exists globalSettings::vitis_only_pass] && $globalSettings::vitis_only_pass == 1)} {
145 if {[
string match "app_*" [
string tolower $lib]]} {
148 Msg Debug "Adding $lib to $fileset"
149 add_files -norecurse -fileset $fileset $lib_files
151 foreach f $lib_files {
152 set file_obj [get_files -of_objects [get_filesets $fileset] [list "*$f"]]
154 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
155 set_property -name "library" -value $rootlib -objects $file_obj
159 set props [
DictGet $properties $f]
160 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
162 if {[lsearch -inline -regexp $props "93"] < 0} {
165 if {[lsearch -inline -regexp $props "2008"] >= 0} {
166 set vhdl_year "VHDL 2008"
167 }
elseif {[lsearch -inline -regexp $props "2019"] >= 0} {
169 set vhdl_year "VHDL 2019"
171 Msg CriticalWarning "VHDL 2019 is not supported\
172 in Vivado version older than 2023.2.\
173 Using VHDL 2008, but this might not work."
174 set vhdl_year "VHDL 2008"
178 set vhdl_year "VHDL 2008"
180 Msg Debug "File type for $f is $vhdl_year"
181 set_property -name "file_type" -value $vhdl_year -objects $file_obj
184 Msg Debug "Filetype is VHDL 93 for $f"
189 if {[lsearch -inline -regexp $props "SystemVerilog"] > 0} {
192 set_property -name "file_type" -value "SystemVerilog" -objects $file_obj
193 Msg Debug "Filetype is SystemVerilog for $f"
195 Msg Warning "Xilinx PlanAhead/ISE does not support SystemVerilog.\
196 Property not set for $f"
201 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
203 Msg Info "Setting $top as top module for file set $fileset..."
204 set globalSettings::synth_top_module $top
208 if {[lsearch -inline -regexp $props "verilog_header"] >= 0} {
209 Msg Debug "Setting verilog header type for $f..."
210 set_property file_type {Verilog Header} [get_files $f]
211 }
elseif {[lsearch -inline -regexp $props "verilog_template"] >= 0} {
213 Msg Debug "Setting verilog template type for $f..."
214 set_property file_type {Verilog Template} [get_files $f]
215 }
elseif {[lsearch -inline -regexp $props "verilog"] >= 0} {
217 Msg Debug "Setting verilog type for $f..."
218 set_property file_type {Verilog} [get_files $f]
222 if {[lsearch -inline -regexp $props "nosynth"] >= 0} {
223 Msg Debug "Setting not used in synthesis for $f..."
224 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
228 if {[lsearch -inline -regexp $props "noimpl"] >= 0} {
229 Msg Debug "Setting not used in implementation for $f..."
230 set_property -name "used_in_implementation" -value "false" -objects $file_obj
234 if {[lsearch -inline -regexp $props "nosim"] >= 0} {
235 Msg Debug "Setting not used in simulation for $f..."
236 set_property -name "used_in_simulation" -value "false" -objects $file_obj
241 set top_sim [
lindex [regexp -inline {\ytopsim\s*=\s*(.+?)\y.*} $props] 1]
242 if {$top_sim != ""} {
243 Msg Warning "Setting the simulation top module with the topsim property is deprecated.\
244 Please set this property in the \[properties\] section of your .sim list file,
245 or in the \[$fileset\] section of your sim.conf,\
246 by adding the following line.\ntop=$top_sim"
250 set sim_runtime [
lindex [regexp -inline {\yruntime\s*=\s*(.+?)\y.*} $props] 1]
251 if {$sim_runtime != ""} {
252 Msg Warning "Setting the simulation runtime using the runtime= property is deprecated.\
253 Please set this property in the \[properties\] section of your .sim list file,\
254 or in the \[$fileset\] section of your sim.conf,\
255 by adding the following line.\n<simulator_name>.simulate.runtime=$sim_runtime"
259 if {[lsearch -inline -regexp $props "wavefile"] >= 0} {
260 Msg Warning "Setting a wave do file using the wavefile property is deprecated.\
261 Set this property in the sim.conf file under the \[$fileset\] section,\
262 or in the \[properties\] section of the .sim list file,\
263 by adding the following line .\n<simulator_name>.simulate.custom_wave_do=[
file tail $f]"
267 if {[lsearch -inline -regexp $props "dofile"] >= 0} {
268 Msg Warning "Setting a wave do file using the dofile property is deprecated.\
269 Set this property in the sim.conf file under the \[$fileset\] section,\
270 or in the \[properties\] section of the .sim list file,\
271 by adding the following line .\n<simulator_name>.simulate.custom_do=[
file tail $f]"
275 if {[lsearch -inline -regexp $props "locked"] >= 0 && $ext == ".ip"} {
276 Msg Info "Locking IP $f..."
277 set_property IS_MANAGED 0 [get_files $f]
281 if {[
file extension $f] == ".bd"} {
282 Msg Info "Generating Target for [
file tail $f],\
283 please remember to commit the (possible) changed file."
284 generate_target all [get_files $f]
288 if {[
file extension $f] == ".tcl" && $ext != ".con"} {
289 if {[lsearch -inline -regexp $props "source"] >= 0} {
290 Msg Info "Sourcing Tcl script $f,\
291 and setting it not used in synthesis, implementation and simulation..."
293 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
294 set_property -name "used_in_implementation" -value "false" -objects $file_obj
295 set_property -name "used_in_simulation" -value "false" -objects $file_obj
300 set ref [
lindex [regexp -inline {\yscoped_to_ref\s*=\s*([^ ]+)} $props] 1]
301 set cell [
lindex [regexp -inline {\yscoped_to_cells\s*=\s*([^ ]+)} $props] 1]
302 if {[
file extension $f] == ".elf" || (([
file extension $f] == ".tcl" || [
file extension $f] == ".xdc") && $ext == ".con")} {
304 set_property SCOPED_TO_REF $ref $file_obj
307 set_property SCOPED_TO_CELLS [
split $cell ","] $file_obj
311 Msg Info "[
llength $lib_files] file/s added to library $rootlib..."
314 if {$ext == ".sim"} {
315 Msg Warning "Simulation files not supported in Quartus Prime mode... Skipping $lib"
317 if {![is_project_open]} {
318 Msg Error "Project is closed"
320 foreach cur_file $lib_files {
324 set props [
DictGet $properties $cur_file]
327 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
329 Msg Info "Setting $top as top module for file set $fileset..."
330 set globalSettings::synth_top_module $top
333 if {[
string first "VHDL" $file_type] != -1} {
334 if {[
string first "1987" $props] != -1} {
335 set hdl_version "VHDL_1987"
336 }
elseif {[
string first "1993" $props] != -1} {
337 set hdl_version "VHDL_1993"
338 }
elseif {[
string first "2008" $props] != -1} {
339 set hdl_version "VHDL_2008"
341 set hdl_version "default"
343 if {$hdl_version == "default"} {
344 set_global_assignment -name $file_type $cur_file -library $rootlib
346 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version -library $rootlib
348 }
elseif {[
string first "SYSTEMVERILOG" $file_type] != -1} {
350 if {[
string first "2005" $props] != -1} {
351 set hdl_version "systemverilog_2005"
352 }
elseif {[
string first "2009" $props] != -1} {
353 set hdl_version "systemverilog_2009"
355 set hdl_version "default"
357 if {$hdl_version == "default"} {
358 set_global_assignment -name $file_type $cur_file
360 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
362 }
elseif {[
string first "VERILOG" $file_type] != -1} {
364 if {[
string first "1995" $props] != -1} {
365 set hdl_version "verilog_1995"
366 }
elseif {[
string first "2001" $props] != -1} {
367 set hdl_version "verilog_2001"
369 set hdl_version "default"
371 if {$hdl_version == "default"} {
372 set_global_assignment -name $file_type $cur_file
374 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
376 }
elseif {[
string first "SOURCE" $file_type] != -1 || [
string first "COMMAND_MACRO" $file_type] != -1} {
377 set_global_assignment -name $file_type $cur_file
378 if {$ext == ".con"} {
380 }
elseif {$ext == ".src"} {
382 if {[
string first "qsys" $props] != -1} {
385 regsub -all {\{||qsys||\}} $props $emptyString props
387 set qsysPath [
file dirname $cur_file]
388 set qsysName "[
file rootname [
file tail $cur_file]].qsys"
389 set qsysFile "$qsysPath/$qsysName"
390 set qsysLogFile "$qsysPath/[
file rootname [
file tail $cur_file]].qsys-script.log"
393 if {![
info exists ::env(QSYS_ROOTDIR)]} {
394 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
395 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
396 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
398 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
401 set qsys_rootdir $::env(QSYS_ROOTDIR)
404 set cmd "$qsys_rootdir/qsys-script"
405 set cmd_options " --script=$cur_file"
406 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
407 Msg Info "Executing: $cmd $cmd_options"
408 Msg Info "Saving logfile in: $qsysLogFile"
409 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
410 set makeRet [
lindex [dict get $opt -errorcode] end]
411 Msg CriticalWarning "$cmd returned with $makeRet"
414 Msg Error " Could not execute command $cmd"
418 if {[
file exists $qsysName] != 0} {
419 file rename -force $qsysName $qsysFile
421 set qsysMd5Sum [
Md5Sum $qsysFile]
423 set fileDir [
file normalize "./hogTmp"]
424 set fileName "$fileDir/.hogQsys.md5"
425 if {![
file exists $fileDir]} {
428 set hogQsysFile [open $fileName "a"]
429 set fileEntry "$qsysFile\t$qsysMd5Sum"
430 puts $hogQsysFile $fileEntry
433 Msg ERROR "Error while moving the generated qsys file to final location: $qsysName not found!"
435 if {[
file exists $qsysFile] != 0} {
436 if {[
string first "noadd" $props] == -1} {
438 set_global_assignment -name $qsysFileType $qsysFile
440 regsub -all {noadd} $props $emptyString props
442 if {[
string first "nogenerate" $props] == -1} {
446 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
450 }
elseif {[
string first "QSYS" $file_type] != -1} {
452 regsub -all {\{||\}} $props $emptyString props
453 if {[
string first "noadd" $props] == -1} {
454 set_global_assignment -name $file_type $cur_file
456 regsub -all {noadd} $props $emptyString props
460 if {[
string first "nogenerate" $props] == -1} {
464 set_global_assignment -name $file_type $cur_file -library $rootlib
469 if {$ext == ".con"} {
470 set vld_exts {.sdc .pin .dcf .gcf .pdc .ndc .fdc .crt .vcd }
471 foreach con_file $lib_files {
473 set con_ext [
file extension $con_file]
474 if {[
IsInList [
file extension $con_file] $vld_exts]} {
475 set option [
string map {. -} $con_ext]
476 set option [
string map {fdc net_fdc} $option]
477 set option [
string map {pdc io_pdc} $option]
478 create_links -convert_EDN_to_HDL 0 -library {work} $option $con_file
480 set props [
DictGet $properties $con_file]
482 if {$con_ext == ".sdc"} {
483 if {[lsearch $props "notiming"] >= 0} {
484 Msg Info "Excluding $con_file from timing verification..."
486 Msg Info "Adding $con_file to time verification"
487 append timing_conf_command " -file $con_file"
491 if {[lsearch $props "nosynth"] >= 0} {
492 Msg Info "Excluding $con_file from synthesis..."
494 Msg Info "Adding $con_file to synthesis"
495 append synth_conf_command " -file $con_file"
500 if {$con_ext == ".pdc" || $con_ext == ".sdc"} {
501 if {[lsearch $props "noplace"] >= 0} {
502 Msg Info "Excluding $con_file from place and route..."
504 Msg Info "Adding $con_file to place and route"
505 append place_conf_command " -file $con_file"
510 Msg CriticalWarning "Constraint file $con_file does not have a valid extension. Allowed extensions are: \n $vld_exts"
513 }
elseif {$ext == ".src"} {
514 foreach f $lib_files {
515 Msg Debug "Adding source $f to library $rootlib..."
516 create_links -library $rootlib -hdl_source $f
518 }
elseif {$ext == ".sim"} {
519 Msg Debug "Adding stimulus file $f to library..."
520 create_links -library $rootlib -stimulus $f
522 build_design_hierarchy
523 foreach cur_file $lib_files {
527 set props [
DictGet $properties $cur_file]
530 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
532 Msg Info "Setting $top as top module for file set $rootlib..."
533 set globalSettings::synth_top_module "${top}::$rootlib"
538 if {$ext == ".src" || $ext == ".con" || $ext == ".ext"} {
539 foreach f $lib_files {
540 Msg Debug "Diamond: adding source file $f to library $rootlib..."
541 prj_src add -work $rootlib $f
542 set props [
DictGet $properties $f]
544 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
546 Msg Info "Setting $top as top module for the project..."
547 set globalSettings::synth_top_module $top
551 if {[lsearch -inline -regexp $props "enable"] >= 0} {
552 Msg Debug "Setting $f as active Logic Preference file"
556 }
elseif {$ext == ".sim"} {
557 foreach f $lib_files {
558 Msg Debug "Diamond Adding simulation file $f to library $rootlib..."
559 prj_src add -work $rootlib -simulate_only $f
566 foreach app_name [dict keys $ws_apps] {
567 foreach f $lib_files {
568 if {[
string tolower $rootlib] != [
string tolower "app_$app_name"]} {
572 Msg Info "Adding source file $f from lib: $lib to vitis app \[$app_name\]..."
573 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
574 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
575 Msg Debug "Project_f_path is $proj_f_path"
577 importsources -name $app_name -soft-link -path $f -target $proj_f_path
581 Msg Debug "Vitis Unified: Collecting files for apps from library $rootlib..."
585 if {[dict size $ws_apps] == 0} {
586 Msg Debug "No apps found in workspace, skipping file collection"
588 Msg Debug "Found [dict size $ws_apps] app(s) in workspace"
589 Msg Debug "Processing library: $rootlib with [
llength $lib_files] file(s)"
590 foreach app_name [dict keys $ws_apps] {
591 set expected_lib [
string tolower "app_$app_name"]
592 Msg Debug "Checking files for app: $app_name (looking for lib: $expected_lib, current lib: [
string tolower $rootlib])"
593 foreach f $lib_files {
594 if {[
string tolower $rootlib] != $expected_lib} {
597 Msg Debug "File $f matches app $app_name"
598 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
599 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
600 set proj_f_path [
string trimleft $proj_f_path "/"]
602 if {![dict exists $app_files_dict $app_name]} {
603 dict set app_files_dict $app_name files [list]
604 dict set app_files_dict $app_name target $proj_f_path
605 Msg Debug "Initialized app_files_dict for $app_name with target: $proj_f_path"
608 set current_files [dict get $app_files_dict $app_name files]
609 lappend current_files $f
610 dict set app_files_dict $app_name files $current_files
611 set current_count [
llength [dict get $app_files_dict $app_name files]]
612 Msg Debug "Added file $f to app_files_dict for $app_name, current count: $current_count"
622 set python_script "$globalSettings::repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"
623 set vitis_workspace "$globalSettings::build_dir/vitis_unified"
627 set env(HOG_VITIS_VER) $vitis_version
628 Msg Debug "Vitis version: $vitis_version (set in HOG_VITIS_VER environment variable)"
630 dict for {app_name app_data} $app_files_dict {
631 set files_list [dict get $app_data files]
632 set target_path [dict get $app_data target]
634 if {[llength $files_list] > 0} {
635 Msg Info "Adding [llength $files_list] source file(s) to vitis app \[$app_name\]..."
636 Msg Debug "Files: $files_list"
637 Msg Debug "Target path: $target_path"
639 # Convert Tcl list to JSON array for Python
642 foreach f $files_list {
644 append files_json ", "
646 # Escape backslashes and quotes in file paths
647 set escaped_f [string map {\\ \\\\ \" \\\"} $f]
648 append files_json "\"$escaped_f\""
651 append files_json "\]"
653 Msg Debug "JSON string: $files_json"
655 set error_msg "Failed to add files to app $app_name"
656 if {![ExecuteVitisUnifiedCommand $python_script "add_app_files" \
657 [list $app_name $files_json $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"
711 ".qsys" [list "nogenerate" "noadd" "--block-symbol-file" "--clear-output-directory" "--example-design"\
712 "--export-qsys-script" "--family" "--greybox" "--ipxact" "--jvm-max-heap-size" "--parallel"\
713 "--part" "--search-path" "--simulation" "--synthesis" "--testbench" "--testbench-simulation"\
714 "--upgrade-ip-cores" "--upgrade-variation-file"
716 ".sdc" [list "notiming" "nosynth" "noplace"] \
717 ".elf" [list "scoped_to_ref" "scoped_to_cells" "nosim" "noimpl"] \
718 ".pdc" [list "nosynth" "noplace"] \
719 ".lpf" [list "enable"]]
730 proc BinaryStepName {part} {
732 return "WRITE_DEVICE_IMAGE"
736 return "WRITE_BITSTREAM"
743 set essential_vars [dict create \
744 "HOG_USER" "NOT defined. This variable is essential for git to work properly. \
745 It should be set to the username for your service account (a valid git account)." \
746 "HOG_EMAIL" "NOT defined. This variable is essential for git to work properly. It should be set to your service's account email."\
747 "HOG_PUSH_TOKEN" "NOT defined. This variable is essential for git to work properly. It should be set to a Gitlab/GitHub API token for your service account."
751 dict for {var msg} $essential_vars {
752 if {![info exists env($var)]} {
753 Msg CriticalWarning "Essential environment variable $var is $msg"
756 Msg Info "Found environment variable $var."
760 set additional_vars [dict create \
761 "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." \
762 "HOG_TARGET_BRANCH" "NOT defined. Default branch for merge is \"master\""\
763 "HOG_CREATE_OFFICIAL_RELEASE" "NOT defined. \
764 Set this variable to '1' to make Hog create an official release in GitHub/Gitlab with the binaries generated in the CI."\
765 "HOG_USE_DOXYGEN" "NOT defined. \
766 Set this variable to 1 to make Hog-CI run Doxygen and copy the official documentation over when you merge to the official branch."
769 if {([
info exists env(HOG_OFFICIAL_BIN_EOS_PATH)] && $env(HOG_OFFICIAL_BIN_EOS_PATH) ne "") || \
770 ([
info exists env(HOG_OFFICIAL_BIN_PATH)] && [
string match "/eos/*" $env(HOG_OFFICIAL_BIN_PATH)])} {
771 Msg Info "Official binary path points to EOS. Checking EOS environment variables for uploads..."
772 if {[
info exists env(HOG_OFFICIAL_BIN_PATH)]} {
773 Msg CriticalWarning "Variable HOG_OFFICIAL_BIN_EOS_PATH is defined. \
774 From Hog2026.2 this variable will be deprecated. Please, use HOG_OFFICIAL_BIN_PATH instead."
776 if {![
info exists env(EOS_PASSWORD)]} {
777 if {![
info exists env(HOG_PASSWORD)]} {
778 Msg Warning "Neither EOS_PASSWORD nor HOG_PASSWORD environment variable is defined. \
779 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."
781 Msg Info "HOG_PASSWORD environment variable is defined and will be used as password for EOS uploads. \
782 If you want to use a different password for EOS uploads, please set the EOS_PASSWORD environment variable."
785 Msg Info "EOS_PASSWORD environment variable is defined and will be used as password for EOS uploads."
788 if {![
info exists env(EOS_USER)]} {
789 Msg Info "EOS_USER environment variable is not defined. Assuming EOS username is the same as HOG_USER."
791 Msg Info "EOS_USER environment variable is defined and will be used as username for EOS uploads."
794 if {![
info exists env(EOS_MGM_URL)]} {
795 Msg Info "EOS_MGM_URL environment variable is not defined. Assuming default value of root://eosuser.cern.ch."
797 Msg Info "EOS_MGM_URL environment variable is defined and will be used as MGM URL for EOS uploads."
799 }
elseif {[
info exists env(HOG_OFFICIAL_BIN_PATH)] } {
800 Msg Info "Variable HOG_OFFICIAL_BIN_PATH is defined. Hog will copy the official binary files to the path defined in this variable. \
801 Please make sure this path is correct and has enough space to store the binaries."
803 Msg Info "No official binary path defined. Hog will not be able to upload binaries."
809 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
817 proc CheckEnv {project_name ide} {
820 set essential_commands [dict create "git" "--version" "$ide" "-version"]
821 set additional_commands [dict create \
828 set additional_vars [dict create \
829 "HOG_PATH" "NOT defined. Hog might work as long as all the necessary executable are in the PATH variable."\
830 "HOG_XIL_LICENSE" "NOT defined. If this variable is not set to the license servers separated by comas, \
831 you need some alternative way of getting your Xilinx license (for example a license file on the machine)."\
832 "LM_LICENSE_FILE" "NOT defined. This variable should be set the Quartus/Libero license servers separated by semicolon. \
833 If not, you need an alternative way of getting your Quartus/Libero license."\
834 "HOG_LD_LIBRARY_PATH" "NOT defined. Hog might work as long as all the necessary library are found."\
835 "HOG_SIMULATION_LIB_PATH" "NOT defined. Hog-CI will not be able to run simulations using third-party simulators."\
836 "HOG_CHECK_PROJVER" "NOT defined. Hog will NOT check the CI project version. \
837 Set this variable to '1' if you want Hog to check the CI project version before creating the HDL project in Create_Project stage. \
838 If the project has not been changed with respect to the target branch, the CI will skip this project" \
839 "HOG_CHECK_SYNTAX" "NOT defined. Hog will NOT check the syntax. \
840 Set this variable to '1' if you want Hog to check the syntax after creating the HDL project in Create_Project stage." \
841 "HOG_NO_BITSTREAM" "NOT defined. Hog-CI will run the implementation up to the write_bitstream stage and create bit files." \
842 "HOG_NO_RESET_BD" "NOT defined or not equal to 1. Hog will reset .bd files (if any) before starting synthesis."\
843 "HOG_IP_PATH" "NOT defined. Hog-CI will NOT use an EOS/LOCAL IP repository to speed up the IP synthesis." \
844 "HOG_RESET_FILES" "NOT defined. Hog-CI will NOT reset any files."\
845 "HOG_NJOBS" "NOT defined. Hog-CI will build IPs with default number of jobs (4)."\
846 "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"
848 Msg Info "Checking environment to run Hog-CI for project $project_name with IDE $ide..."
850 Msg Info "Checking essential commands..."
851 dict for {cmd ver} $essential_commands {
852 if {[catch {exec which $cmd}]} {
853 Msg CriticalWarning "$cmd executable not found. Hog-CI cannot run!"
856 Msg Info "Found executable $cmd."
857 if {$cmd == "ghdl"} {
858 Msg Info [exec $cmd --version]
859 } elseif {$cmd != "diamond"} {
860 Msg Info [exec $cmd $ver]
865 Msg Info "Checking additional commands..."
866 dict for {cmd ver} $additional_commands {
867 if {[catch {exec which $cmd}]} {
868 Msg Warning "$cmd executable not found."
870 Msg Info "Found executable $cmd."
872 Msg Info [exec $cmd $ver]
877 if {$ide == "libero"} {
878 Msg Info "Checking essential environment variables..."
880 if {![
info exists env(HOG_TCLLIB_PATH)]} {
881 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."
884 Msg Info "HOG_TCLLIB_PATH is set. Hog-CI can run with Libero."
888 Msg Info "Checking additional environment variables..."
889 dict for {var msg} $additional_vars {
890 if {![info exists env($var)]} {
891 Msg Info "Environment variable $var is $msg"
893 Msg Info "Found environment variable $var."
898 Msg Error "One or more essential environment variables are missing. Hog-CI cannot run!"
906 proc CheckProjVer {repo_path project {sim 0} {ext_path ""}} {
911 Msg Info "Will check also the version of the simulation files..."
915 if {[
info exists env(HOG_PUSH_TOKEN)] && [
info exist env(CI_PROJECT_ID)] && [
info exist env(CI_API_V4_URL)] } {
916 set token $env(HOG_PUSH_TOKEN)
917 set api_url $env(CI_API_V4_URL)
918 set project_id $env(CI_PROJECT_ID)
923 set project_dir $repo_path/Top/$project
926 Msg Info "$project was modified, continuing with the CI..."
928 Msg Info "Checking if the project has been already built in a previous CI run..."
930 if {$sha == [
GetSHA $repo_path]} {
931 Msg Info "Project was modified in the current commit, Hog will proceed with the build workflow."
934 Msg Info "Checking if project $project has been built in a previous CI run with sha $sha..."
935 set result [
catch {
package require json} JsonFound]
936 if {"$result" != "0"} {
937 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
940 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/$project_id/pipelines"] ret content
941 set pipeline_dict [json::json2dict $content]
942 if {[
llength $pipeline_dict] > 0} {
943 foreach pip $pipeline_dict {
945 set source [
DictGet $pip source]
946 if {$source == "merge_request_event" && [
string first $sha $pip_sha] != -1} {
947 Msg Info "Found pipeline with sha $pip_sha for project $project"
948 set pipeline_id [
DictGet $pip id]
950 lassign [
ExecuteRet {*}$curl_cmd --header "PRIVATE-TOKEN: $token" "$api_url/projects/${project_id}/pipelines/${pipeline_id}/jobs?pagination=keyset&per_page=100"] ret2 content2
951 set jobs_dict [json::json2dict $content2]
952 if {[
llength $jobs_dict] > 0} {
953 foreach job $jobs_dict {
954 set job_name [
DictGet $job name]
956 set artifacts [
DictGet $job artifacts_file]
957 set status [
DictGet $job status]
958 set current_job_name $env(CI_JOB_NAME)
959 if {$current_job_name == $job_name && $status == "success"} {
961 lassign [
ExecuteRet {*}$curl_cmd --location --output artifacts.zip --header "PRIVATE-TOKEN: $token" --url "$api_url/projects/$project_id/jobs/$job_id/artifacts"] ret3 content3
963 Msg CriticalWarning "Cannot download artifacts for job $job_name with id $job_id"
966 lassign [
ExecuteRet unzip -o $repo_path/artifacts.zip] ret_zip
970 Msg Info "Artifacts for job $job_name with id $job_id downloaded and unzipped."
971 file mkdir $repo_path/Projects/$project
972 set fp [open "$repo_path/Projects/$project/skip.me" w+]
984 }
elseif {$ver != -1} {
985 Msg Info "$project was not modified since version: $ver, disabling the CI..."
986 file mkdir $repo_path/Projects/$project
987 set fp [open "$repo_path/Projects/$project/skip.me" w+]
991 Msg Error "Impossible to check the project version. Most likely the repository is not clean. Please, commit your changes before running this command."
1001 proc CheckSyntax {project_name repo_path {project_file ""}} {
1003 update_compile_order
1004 set syntax [check_syntax -return_string]
1005 if {[
string first "CRITICAL" $syntax] != -1} {
1010 lassign [
GetHogFiles -list_files "*.src" "$repo_path/Top/$project_name/list/" $repo_path] src_files dummy
1011 dict for {lib files} $src_files {
1013 set file_extension [file extension $f]
1014 if {$file_extension == ".vhd" || $file_extension == ".vhdl" || $file_extension == ".v" || $file_extension == ".sv"} {
1015 if {[catch {execute_module -tool map -args "--analyze_file=$f"} result]} {
1016 Msg Error "\nResult: $result\n"
1017 Msg Error "Check syntax failed.\n"
1019 if {$result == ""} {
1020 Msg Info "Check syntax was successful for $f.\n"
1022 Msg Warning "Found syntax error in file $f:\n $result\n"
1029 lassign [
GetProjectFiles $project_file] prjLibraries prjProperties prjSimLibraries prjConstraints prjSrcSets prjSimSets prjConSets
1030 dict for {lib sources} $prjLibraries {
1031 if {[file extension $lib] == ".src"} {
1032 foreach f $sources {
1033 Msg Info "Checking Syntax of $f"
1039 Msg Info "The Checking Syntax is not supported by this IDE. Skipping..."
1044 proc CloseProject {} {
1066 proc CompareVersions {ver1 ver2} {
1075 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver1 - x y z]} {
1076 set ver1 [list $x $y $z]
1078 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver2 - x y z]} {
1079 set ver2 [list $x $y $z]
1083 set v1 [
join $ver1 ""]
1085 set v2 [
join $ver2 ""]
1088 if {[
string is integer $v1] && [
string is integer $v2]} {
1089 set ver1 [
expr {[scan [lindex $ver1 0] %d] * 1000000 + [scan [lindex $ver1 1] %d] * 1000 + [scan [lindex $ver1 2] %d]}]
1090 set ver2 [
expr {[scan [lindex $ver2 0] %d] * 1000000 + [scan [lindex $ver2 1] %d] * 1000 + [scan [lindex $ver2 2] %d]}]
1092 if {$ver1 > $ver2} {
1094 }
elseif {$ver1 == $ver2} {
1100 Msg Warning "Version is not numeric: $ver1, $ver2"
1103 return [
expr {$ret}]
1110 proc CheckExtraFiles {libraries constraints simlibraries} {
1113 lassign [
GetProjectFiles] prjLibraries prjProperties prjSimLibraries prjConstraints
1114 set prj_dir [get_property DIRECTORY [current_project]]
1115 file mkdir "$prj_dir/.hog"
1116 set extra_file_name "$prj_dir/.hog/extra.files"
1117 set new_extra_file [open $extra_file_name "w"]
1119 dict for {prjLib prjFiles} $prjLibraries {
1120 foreach prjFile $prjFiles {
1121 if {[file extension $prjFile] == ".xcix"} {
1122 Msg Warning "IP $prjFile is packed in a .xcix core container. \
1123 This files are not suitable for version control systems. We recommend to use .xci files instead."
1126 if {[file extension $prjFile] == ".xci" && [get_property CORE_CONTAINER [get_files $prjFile]] != ""} {
1127 Msg Info "$prjFile is a virtual IP file in a core container. Ignoring it..."
1131 if {[IsInList $prjFile [DictGet $libraries $prjLib]] == 0} {
1132 if {[file extension $prjFile] == ".bd"} {
1133 # Generating BD products to save md5sum of already modified BD
1134 Msg Info "Generating targets of $prjFile..."
1135 generate_target all [get_files $prjFile]
1137 puts $new_extra_file "$prjFile [Md5Sum $prjFile]"
1138 Msg Info "$prjFile (lib: $prjLib) has been generated by an external script. Adding to $extra_file_name..."
1142 close $new_extra_file
1143 set extra_sim_file "$prj_dir/.hog/extrasim.files"
1144 set new_extra_file [open $extra_sim_file "w"]
1146 dict for {prjSimLib prjSimFiles} $prjSimLibraries {
1147 foreach prjSimFile $prjSimFiles {
1148 if {[IsInList $prjSimFile [DictGet $simlibraries $prjSimLib]] == 0} {
1149 puts $new_extra_file "$prjSimFile [Md5Sum $prjSimFile]"
1150 Msg Info "$prjSimFile (lib: $prjSimLib) has been generated by an external script. Adding to $extra_sim_file..."
1154 close $new_extra_file
1155 set extra_con_file "$prj_dir/.hog/extracon.files"
1156 set new_extra_file [open $extra_con_file "w"]
1158 dict for {prjConLib prjConFiles} $prjConstraints {
1159 foreach prjConFile $prjConFiles {
1160 if {[IsInList $prjConFile [DictGet $constraints $prjConLib]] == 0} {
1161 puts $new_extra_file "$prjConFile [Md5Sum $prjConFile]"
1162 Msg Info "$prjConFile has been generated by an external script. Adding to $extra_con_file..."
1166 close $new_extra_file
1173 proc CheckLatestHogRelease {{repo_path .}} {
1176 set current_ver [
Git {describe --always}]
1177 Msg Debug "Current version: $current_ver"
1178 set current_sha [
Git "log $current_ver -1 --format=format:%H"]
1179 Msg Debug "Current SHA: $current_sha"
1182 if {[
OS] == "windows"} {
1183 Msg Info "On windows we cannot set a timeout on 'git fetch', hopefully nothing will go wrong..."
1186 Msg Info "Checking for latest Hog release, can take up to 5 seconds..."
1189 set master_ver [
Git "describe origin/master"]
1190 Msg Debug "Master version: $master_ver"
1191 set master_sha [
Git "log $master_ver -1 --format=format:%H"]
1192 Msg Debug "Master SHA: $master_sha"
1193 set merge_base [
Git "merge-base $current_sha $master_sha"]
1194 Msg Debug "merge base: $merge_base"
1197 if {$merge_base != $master_sha} {
1199 Msg Info "Version $master_ver has been released (https://gitlab.com/hog-cern/Hog/-/releases/$master_ver)"
1200 Msg Status "You should consider updating Hog submodule with the following instructions:"
1202 Msg Status "cd Hog && git checkout master && git pull"
1204 Msg Status "Also update the ref: in your .gitlab-ci.yml to $master_ver"
1208 Msg Info "Latest official version is $master_ver, nothing to do."
1220 proc CheckYmlRef {repo_path allow_failure} {
1221 if {$allow_failure} {
1222 set MSG_TYPE CriticalWarning
1227 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
1228 Msg CriticalWarning "Cannot find package YAML, skipping consistency check of \"ref\" in gilab-ci.yaml file.\n Error message: $YAMLPACKAGE
1229 You can fix this by installing package \"tcllib\""
1237 if {[
file exists .gitlab-ci.yml]} {
1241 if {[
file exists .gitlab-ci.yml]} {
1242 set fp [open ".gitlab-ci.yml" r]
1243 set file_data [read $fp]
1246 Msg $MSG_TYPE "Cannot open file .gitlab-ci.yml"
1250 set file_data "\n$file_data\n\n"
1252 if {[
catch {::yaml::yaml2dict -stream $file_data} yamlDict]} {
1253 Msg $MSG_TYPE "Parsing $repo_path/.gitlab-ci.yml failed. To fix this, check that yaml syntax is respected, remember not to use tabs."
1257 dict for {dictKey dictValue} $yamlDict {
1258 #looking for Hog include in .gitlab-ci.yml
1259 if {"$dictKey" == "include" && (
1260 [lsearch [split $dictValue " {}"] "/hog.yml"] != "-1" ||
1261 [lsearch [split $dictValue " {}"] "/hog-dynamic.yml"] != "-1"
1263 set YML_REF [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "ref"] + 1}]]
1264 set YML_NAME [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "file"] + 1}]]
1268 if {$YML_REF == ""} {
1269 Msg Warning "Hog version not specified in the .gitlab-ci.yml. Assuming that master branch is used."
1271 set YML_REF_F [
Git {name-rev --tags --name-only origin/master}]
1274 set YML_REF_F [regsub -all "'" $YML_REF ""]
1277 if {$YML_NAME == ""} {
1278 Msg $MSG_TYPE "Hog included yml file not specified, assuming hog.yml"
1279 set YML_NAME_F hog.yml
1281 set YML_NAME_F [regsub -all "^/" $YML_NAME ""]
1284 lappend YML_FILES $YML_NAME_F
1290 if {[
catch {::yaml::yaml2dict -file $YML_NAME_F} yamlDict]} {
1291 Msg $MSG_TYPE "Parsing $YML_NAME_F failed."
1295 dict for {dictKey dictValue} $yamlDict {
1296 #looking for included files
1297 if {"$dictKey" == "include"} {
1298 foreach v $dictValue {
1299 lappend YML_FILES [lindex [split $v " "] [expr {[lsearch -dictionary [split $v " "] "local"] + 1}]]
1305 Msg Info "Found the following yml files: $YML_FILES"
1307 set HOGYML_SHA [
GetSHA $YML_FILES]
1308 lassign [
GitRet "log --format=%h -1 --abbrev=7 $YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1310 lassign [
GitRet "log --format=%h -1 --abbrev=7 origin/$YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
1312 Msg $MSG_TYPE "Error in project .gitlab-ci.yml. ref: $YML_REF not found"
1313 set EXPECTEDYML_SHA ""
1316 if {!($EXPECTEDYML_SHA eq "")} {
1317 if {$HOGYML_SHA == $EXPECTEDYML_SHA} {
1318 Msg Info "Hog included file $YML_FILES matches with $YML_REF in .gitlab-ci.yml."
1320 Msg $MSG_TYPE "HOG $YML_FILES SHA mismatch.
1321 From Hog submodule: $HOGYML_SHA
1322 From ref in .gitlab-ci.yml: $EXPECTEDYML_SHA
1323 You can fix this in 2 ways: by changing the ref in your repository or by changing the Hog submodule commit"
1326 Msg $MSG_TYPE "One or more of the following files could not be found $YML_FILES in Hog at $YML_REF"
1329 Msg Info ".gitlab-ci.yml not found in $repo_path. Skipping this step"
1350 proc CompareLibDicts {proj_libs list_libs proj_sets list_sets proj_props list_props {severity "CriticalWarning"} {outFile ""} {extraFiles ""}} {
1351 set extra_files $extraFiles
1353 set out_prjlibs $proj_libs
1354 set out_prjprops $proj_props
1356 dict for {prjSet prjLibraries} $proj_sets {
1357 # Check if sets is also in list files
1358 if {[IsInList $prjSet $list_sets]} {
1359 set listLibraries [DictGet $list_sets $prjSet]
1360 # Loop over libraries in fileset
1361 foreach prjLib $prjLibraries {
1362 set prjFiles [DictGet $proj_libs $prjLib]
1363 # Check if library exists in list files
1364 if {[IsInList $prjLib $listLibraries]} {
1365 # Loop over files in library
1366 set listFiles [DictGet $list_libs $prjLib]
1367 foreach prjFile $prjFiles {
1368 set idx [lsearch -exact $listFiles $prjFile]
1369 set listFiles [lreplace $listFiles $idx $idx]
1371 # File is in project but not in list libraries, check if it was generated at creation time...
1372 if {[dict exists $extra_files $prjFile]} {
1373 # File was generated at creation time, checking the md5sum
1374 # Removing the file from the prjFiles list
1375 set idx2 [lsearch -exact $prjFiles $prjFile]
1376 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1377 set new_md5sum [Md5Sum $prjFile]
1378 set old_md5sum [DictGet $extra_files $prjFile]
1379 if {$new_md5sum != $old_md5sum} {
1380 # tclint-disable-next-line line-length
1381 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
1384 set extra_files [dict remove $extra_files $prjFile]
1386 # File is neither in list files nor in extra_files
1387 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1391 # File is both in list files and project, checking properties...
1392 set prjProps [DictGet $proj_props $prjFile]
1393 set listProps [DictGet $list_props $prjFile]
1394 # Check if it is a potential sourced file
1395 if {[IsInList "nosynth" $prjProps] && [IsInList "noimpl" $prjProps] && [IsInList "nosim" $prjProps]} {
1396 # Check if it is sourced
1397 set idx_source [lsearch -exact $listProps "source"]
1398 if {$idx_source >= 0} {
1399 # It is sourced, let's replace the individual properties with source
1400 set idx [lsearch -exact $prjProps "noimpl"]
1401 set prjProps [lreplace $prjProps $idx $idx]
1402 set idx [lsearch -exact $prjProps "nosynth"]
1403 set prjProps [lreplace $prjProps $idx $idx]
1404 set idx [lsearch -exact $prjProps "nosim"]
1405 set prjProps [lreplace $prjProps $idx $idx]
1406 lappend prjProps "source"
1410 foreach prjProp $prjProps {
1411 set idx [lsearch -exact $listProps $prjProp]
1412 set listProps [lreplace $listProps $idx $idx]
1414 MsgAndLog "Property $prjProp of $prjFile was set in project but not in list files" $severity $outFile
1419 foreach listProp $listProps {
1420 if {[string first $listProp "topsim="] == -1 && [string first $listProp "enable"] == -1} {
1421 MsgAndLog "Property $listProp of $prjFile was found in list files but not set in project." $severity $outFile
1426 # Update project prjProps
1427 dict set out_prjprops $prjFile $prjProps
1430 # Loop over remaining files in list libraries
1431 foreach listFile $listFiles {
1432 MsgAndLog "$listFile was found in list files but not in project." $severity $outFile
1436 # Check extra files again...
1437 foreach prjFile $prjFiles {
1438 if {[dict exists $extra_files $prjFile]} {
1439 # File was generated at creation time, checking the md5sum
1440 # Removing the file from the prjFiles list
1441 set idx2 [lsearch -exact $prjFiles $prjFile]
1442 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1443 set new_md5sum [Md5Sum $prjFile]
1444 set old_md5sum [DictGet $extra_files $prjFile]
1445 if {$new_md5sum != $old_md5sum} {
1446 # tclint-disable-next-line line-length
1447 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
1450 set extra_files [dict remove $extra_files $prjFile]
1452 # File is neither in list files nor in extra_files
1453 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1458 # Update prjLibraries
1459 dict set out_prjlibs $prjLib $prjFiles
1462 MsgAndLog "Fileset $prjSet found in project but not in list files" $severity $outFile
1467 return [list $n_diffs $extra_files $out_prjlibs $out_prjprops]
1477 proc CompareVHDL {file1 file2} {
1478 set a [open $file1 r]
1479 set b [open $file2 r]
1481 while {[
gets $a line] != -1} {
1482 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1483 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1489 while {[
gets $b line] != -1} {
1490 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1491 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1500 foreach x $f1 y $f2 {
1502 lappend diff "> $x\n< $y\n\n"
1515 proc Copy {i_dirs o_dir} {
1516 foreach i_dir $i_dirs {
1517 if {[
file isdirectory $i_dir] && [
file isdirectory $o_dir]} {
1518 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]])} {
1519 file delete -force $o_dir/[
file tail $i_dir]
1523 file copy -force $i_dir $o_dir
1538 proc CopyIPbusXMLs {proj_dir path dst {xml_version "0.0.0"} {xml_sha "00000000"} {use_ipbus_sw 0} {generate 0}} {
1539 if {$use_ipbus_sw == 1} {
1540 lassign [
ExecuteRet python3 -c "from __future__ import print_function; from sys import path;print(':'.join(path\[1:\]))"] ret msg
1542 set ::env(PYTHONPATH) $msg
1543 lassign [
ExecuteRet gen_ipbus_addr_decode -h] ret msg
1550 Msg CriticalWarning "Problem while trying to run python: $msg"
1553 set dst [
file normalize $dst]
1555 if {$can_generate == 0} {
1556 if {$generate == 1} {
1557 Msg Error "Cannot generate IPbus address files, IPbus executable gen_ipbus_addr_decode not found or not working: $msg"
1560 Msg Warning "IPbus executable gen_ipbus_addr_decode not found or not working, will not verify IPbus address tables."
1567 set ipb_files [glob -nocomplain $proj_dir/list/*.ipb]
1568 set n_ipb_files [
llength $ipb_files]
1569 if {$n_ipb_files == 0} {
1570 Msg CriticalWarning "No files with .ipb extension found in $proj_dir/list."
1573 set libraries [dict create]
1574 set vhdl_dict [dict create]
1576 foreach ipb_file $ipb_files {
1582 set xmlfiles [dict get $libraries "xml.ipb"]
1584 set xml_list_error 0
1585 foreach xmlfile $xmlfiles {
1586 if {[
file isdirectory $xmlfile]} {
1587 Msg CriticalWarning "Directory $xmlfile listed in xml list file $list_file. Directories are not supported!"
1588 set xml_list_error 1
1591 if {[
file exists $xmlfile]} {
1592 if {[dict exists $vhdl_dict $xmlfile]} {
1593 set vhdl_file [
file normalize $path/[dict get $vhdl_dict $xmlfile]]
1597 lappend vhdls $vhdl_file
1598 set xmlfile [
file normalize $xmlfile]
1599 Msg Info "Copying $xmlfile to $dst and replacing place holders..."
1600 set in [open $xmlfile r]
1601 set out [open $dst/[
file tail $xmlfile] w]
1603 while {[
gets $in line] != -1} {
1604 set new_line [regsub {(.*)__VERSION__(.*)} $line "\\1$xml_version\\2"]
1605 set new_line2 [regsub {(.*)__GIT_SHA__(.*)} $new_line "\\1$xml_sha\\2"]
1606 puts $out $new_line2
1610 lappend xmls [
file tail $xmlfile]
1612 Msg Warning "XML file $xmlfile not found"
1615 if {${xml_list_error}} {
1616 Msg Error "Invalid files added to $list_file!"
1619 set cnt [
llength $xmls]
1620 Msg Info "$cnt xml file/s copied"
1623 if {$can_generate == 1} {
1626 file mkdir "address_decode"
1628 foreach x $xmls v $vhdls {
1630 set x [
file normalize ../$x]
1631 if {[
file exists $x]} {
1632 lassign [
ExecuteRet gen_ipbus_addr_decode --no-timestamp $x 2>&1] status log
1634 set generated_vhdl ./ipbus_decode_[
file rootname [
file tail $x]].vhd
1635 if {$generate == 1} {
1636 Msg Info "Copying generated VHDL file $generated_vhdl into $v (replacing if necessary)"
1637 file copy -force -- $generated_vhdl $v
1639 if {[
file exists $v]} {
1641 set n [
llength $diff]
1643 Msg CriticalWarning "$v does not correspond to its XML $x, [
expr {$n / 3}] line/s differ:"
1644 Msg Status [
join $diff "\n"]
1645 set diff_file [open ../diff_[
file rootname [
file tail $x]].txt w]
1646 puts $diff_file $diff
1649 Msg Info "[
file tail $x] and $v match."
1652 Msg Warning "VHDL address map file $v not found."
1656 Msg Warning "Address map generation failed for [
file tail $x]: $log"
1659 Msg Warning "Copied XML file $x not found."
1662 Msg Info "Skipped verification of [
file tail $x] as no VHDL file was specified."
1666 file delete -force address_decode
1678 proc DescriptionFromConf {conf_file} {
1679 set f [open $conf_file "r"]
1680 set lines [
split [read $f] "\n"]
1682 set second_line [
lindex $lines 1]
1685 if {![regexp {\#+ *(.+)} $second_line - description]} {
1689 if {[regexp -all {test|Test|TEST} $description]} {
1690 set description "test"
1703 proc DictGet {dictName keyName {default ""}} {
1704 if {[dict exists $dictName $keyName]} {
1705 return [dict get $dictName $keyName]
1716 proc DictSort {dict args} {
1718 foreach key [lsort {*}$args [dict keys $dict]] {
1719 dict set res $key [dict get $dict $key]
1729 proc DoxygenVersion {target_version} {
1730 set ver [
split $target_version "."]
1731 set v [
Execute doxygen --version]
1732 Msg Info "Found Doxygen version: $v"
1733 set current_ver [
split $v ". "]
1734 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
1735 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
1737 return [
expr {$target <= $current}]
1748 proc eos {command {attempt 1}} {
1750 if {![
info exists env(EOS_MGM_URL)]} {
1751 Msg Warning "Environment variable EOS_MGM_URL not set, setting it to default value root://eosuser.cern.ch"
1752 set ::env(EOS_MGM_URL) "root://eosuser.cern.ch"
1755 Msg Warning "The value of attempt should be 1 or more, not $attempt, setting it to 1 as default"
1758 for {
set i 0} {$i < $attempt} {
incr i} {
1759 set ret [
catch {
exec -ignorestderr eos {*}$command} result]
1764 set wait [
expr {1 + int(rand() * 29)}]
1765 Msg Warning "Command $command failed ($i/$attempt): $result, trying again in $wait seconds..."
1766 after [
expr {$wait * 1000}]
1770 return [list $ret $result]
1780 proc Execute {args} {
1784 Msg Error "Command [
join $args] returned error code: $ret"
1798 proc ExecuteRet {args} {
1800 if {[
llength $args] == 0} {
1801 Msg CriticalWarning "No argument given"
1805 set ret [
catch {
exec -ignorestderr {*}$args} result]
1808 return [list $ret $result]
1815 proc ExtractFilesSection {file_data} {
1816 set in_files_section 0
1819 foreach line $file_data {
1820 if {[regexp {^ *\[ *files *\]} $line]} {
1821 set in_files_section 1
1824 if {$in_files_section} {
1825 if {[regexp {^ *\[.*\]} $line]} {
1828 lappend result $line
1833 if {!$in_files_section} {
1847 proc ExtractVersionFromTag {tag} {
1848 if {[regexp {^(?:b(\d+))?v(\d+)\.(\d+).(\d+)(?:-\d+)?$} $tag -> mr M m p]} {
1853 Msg Warning "Repository tag $tag is not in a Hog-compatible format."
1859 return [list $M $m $p $mr]
1869 proc FileCommitted {File} {
1871 set currentDir [
pwd]
1872 cd [
file dirname [
file normalize $File]]
1873 set GitLog [
Git ls-files [
file tail $File]]
1874 if {$GitLog == ""} {
1875 Msg CriticalWarning "File [
file normalize $File] is not in the git repository. Please add it with:\n git add [
file normalize $File]\n"
1886 proc FindCommonGitChild {SHA1 SHA2} {
1888 set commits [
Git {log --oneline --merges}]
1891 foreach line [
split $commits "\n"] {
1892 set commit [
lindex [
split $line] 0]
1896 set ancestor $commit
1909 proc findFiles {basedir pattern} {
1912 set basedir [
string trimright [
file join [
file normalize $basedir] { }]]
1918 foreach fileName [glob -nocomplain -type {f r} -path $basedir $pattern] {
1919 lappend fileList $fileName
1923 foreach dirName [glob -nocomplain -type {d r} -path $basedir *] {
1926 set subDirList [
findFiles $dirName $pattern]
1927 if {[
llength $subDirList] > 0} {
1928 foreach subDirFile $subDirList {
1929 lappend fileList $subDirFile
1940 proc FindFileType {file_name} {
1941 set extension [
file extension $file_name]
1944 set file_extension "USE_SIGNALTAP_FILE"
1947 set file_extension "VHDL_FILE"
1950 set file_extension "VHDL_FILE"
1953 set file_extension "VERILOG_FILE"
1956 set file_extension "SYSTEMVERILOG_FILE"
1959 set file_extension "SDC_FILE"
1962 set file_extension "PDC_FILE"
1965 set file_extension "NDC_FILE"
1968 set file_extension "FDC_FILE"
1971 set file_extension "SOURCE_FILE"
1974 set file_extension "IP_FILE"
1977 set file_extension "QSYS_FILE"
1980 set file_extension "QIP_FILE"
1983 set file_extension "SIP_FILE"
1986 set file_extension "BSF_FILE"
1989 set file_extension "BDF_FILE"
1992 set file_extension "COMMAND_MACRO_FILE"
1995 set file_extension "VQM_FILE"
1998 set file_extension "ERROR"
1999 Msg Error "Unknown file extension $extension"
2002 return $file_extension
2009 proc FindNewestVersion {versions} {
2010 set new_ver 00000000
2011 foreach ver $versions {
2013 if {[
expr 0x$ver > 0x$new_ver]} {
2025 proc FindVhdlVersion {file_name} {
2026 set extension [
file extension $file_name]
2029 set vhdl_version "-hdl_version VHDL_2008"
2032 set vhdl_version "-hdl_version VHDL_2008"
2039 return $vhdl_version
2046 proc FormatGeneric {generic} {
2047 if {[
string is integer "0x$generic"]} {
2048 return [
format "32'h%08X" "0x$generic"]
2051 return [
format "32'h%08X" 0]
2061 proc GenerateBitstream {{run_folder ""} {repo_path .} {njobs 1}} {
2062 Msg Info "Starting write bitstream flow..."
2064 set revision [get_current_revision]
2065 if {[
catch {execute_module -tool asm} result]} {
2066 Msg Error "Result: $result\n"
2067 Msg Error "Generate bitstream failed. See the report file.\n"
2069 Msg Info "Generate bitstream was successful for revision $revision.\n"
2072 Msg Info "Run GENERATEPROGRAMMINGDATA ..."
2073 if {[
catch {run_tool -name {GENERATEPROGRAMMINGDATA}}]} {
2074 Msg Error "GENERATEPROGRAMMINGDATA FAILED!"
2076 Msg Info "GENERATEPROGRAMMINGDATA PASSED."
2078 Msg Info "Sourcing Hog/Tcl/integrated/post-bitstream.tcl"
2079 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
2081 prj_run Export -impl Implementation0 -task Bitgen
2091 proc GenerateQsysSystem {qsysFile commandOpts} {
2093 if {[
file exists $qsysFile] != 0} {
2094 set qsysPath [
file dirname $qsysFile]
2095 set qsysName [
file rootname [
file tail $qsysFile]]
2096 set qsysIPDir "$qsysPath/$qsysName"
2097 set qsysLogFile "$qsysPath/$qsysName.qsys-generate.log"
2100 if {![
info exists ::env(QSYS_ROOTDIR)]} {
2101 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
2102 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
2103 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
2105 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
2108 set qsys_rootdir $::env(QSYS_ROOTDIR)
2111 set cmd "$qsys_rootdir/qsys-generate"
2112 set cmd_options "$qsysFile --output-directory=$qsysIPDir $commandOpts"
2113 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
2114 Msg Info "Executing: $cmd $cmd_options"
2115 Msg Info "Saving logfile in: $qsysLogFile"
2116 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
2117 set makeRet [
lindex [dict get $opt -errorcode] end]
2118 Msg CriticalWarning "$cmd returned with $makeRet"
2121 Msg Error " Could not execute command $cmd"
2125 set qsysIPFileList [
concat \
2126 [glob -nocomplain -directory $qsysIPDir -types f *.ip *.qip] \
2127 [glob -nocomplain -directory "$qsysIPDir/synthesis" -types f *.ip *.qip *.vhd *.vhdl]
2129 foreach qsysIPFile $qsysIPFileList {
2130 if {[
file exists $qsysIPFile] != 0} {
2132 set_global_assignment -name $qsysIPFileType $qsysIPFile
2134 set IpMd5Sum [
Md5Sum $qsysIPFile]
2136 set fileDir [
file normalize "./hogTmp"]
2137 set fileName "$fileDir/.hogQsys.md5"
2138 if {![
file exists $fileDir]} {
2141 set hogQsysFile [open $fileName "a"]
2142 set fileEntry "$qsysIPFile\t$IpMd5Sum"
2143 puts $hogQsysFile $fileEntry
2148 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
2158 proc GenericToSimulatorString {prop_dict target} {
2160 dict for {theKey theValue} $prop_dict {
2169 regexp {([0-9]*)('h)([0-9a-fA-F]*)} $theValue valueHexFull valueNumBits valueHexFlag valueHex
2170 regexp {^([0-9]*)$} $theValue valueIntFull ValueInt
2171 regexp {(?!^\d+$)^.+$} $theValue valueStrFull ValueStr
2172 if {[string tolower $target] == "vivado" || [string tolower $target] == "xsim"} {
2173 if {[string tolower $theValue] == "true" || [string tolower $theValue] == "false"} {
2174 set prj_generics "$prj_generics $theKey=$theValue"
2175 } elseif {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2176 set prj_generics "$prj_generics $theKey=$valueHexFull"
2177 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2178 set prj_generics "$prj_generics $theKey=$ValueInt"
2179 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2180 set prj_generics "$prj_generics $theKey=\"$ValueStr\""
2182 set prj_generics "$prj_generics $theKey=\"$theValue\""
2184 } elseif {[lsearch -exact [GetSimulators] [string tolower $target]] >= 0} {
2185 if {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
2187 scan $valueNumBits %d numBits
2189 scan $valueHex %x numHex
2190 binary scan [binary format "I" $numHex] "B*" binval
2191 set numBits [expr {$numBits - 1}]
2192 set numBin [string range $binval end-$numBits end]
2193 set prj_generics "$prj_generics $theKey=\"$numBin\""
2194 } elseif {$valueIntFull != "" && $ValueInt != ""} {
2195 set prj_generics "$prj_generics $theKey=$ValueInt"
2196 } elseif {$valueStrFull != "" && $ValueStr != ""} {
2197 set prj_generics "$prj_generics {$theKey=\"$ValueStr\"}"
2199 set prj_generics "$prj_generics {$theKey=\"$theValue\"}"
2202 Msg Warning "Target : $target not implemented"
2205 return $prj_generics
2213 proc GetConfFiles {proj_dir} {
2214 Msg Debug "GetConfFiles called with proj_dir=$proj_dir"
2215 if {![
file isdirectory $proj_dir]} {
2216 Msg Error "$proj_dir is supposed to be the top project directory"
2219 set conf_file [
file normalize $proj_dir/hog.conf]
2220 set sim_file [
file normalize $proj_dir/sim.conf]
2221 set pre_tcl [
file normalize $proj_dir/pre-creation.tcl]
2222 set post_tcl [
file normalize $proj_dir/post-creation.tcl]
2223 set pre_rtl [
file normalize $proj_dir/pre-rtl.tcl]
2224 set post_rtl [
file normalize $proj_dir/post-rtl.tcl]
2226 return [list $conf_file $sim_file $pre_tcl $post_tcl $pre_rtl $post_rtl]
2235 proc GetCustomCommands {parameters {directory .}} {
2236 set commands_dict [dict create]
2237 set commands_files [glob -nocomplain $directory/*.tcl]
2239 if {[
llength $commands_files] == 0} {
2243 foreach file $commands_files {
2248 if {$custom_cmd eq ""} {
2253 set custom_cmd_name [dict get $custom_cmd NAME]
2255 Msg Debug "Loaded custom command '$custom_cmd_name' from $file"
2258 if {[dict exists $commands_dict $custom_cmd_name]} {
2259 Msg Error "Custom command '$custom_cmd_name' in $file already defined as: \[dict get $commands_dict $custom_cmd_name\]. Skipping."
2265 set custom_cmd_name [
string toupper $custom_cmd_name]
2266 dict set commands_dict $custom_cmd_name $custom_cmd
2269 return $commands_dict
2272 proc SanitizeCustomCommand {cmdDict file parameters} {
2275 foreach k [dict keys $cmdDict] {
2276 set K [
string toupper $k]
2277 dict set normalized $K [dict get $cmdDict $k]
2280 set cmdDict $normalized
2281 if {![dict exists $cmdDict NAME]} {
2282 Msg Error "Custom command in $file missing required key NAME. Skipping."
2285 if {![dict exists $cmdDict SCRIPT]} {
2286 Msg Error "Custom command '$[dict get $cmdDict NAME]' in $file missing SCRIPT. Skipping."
2291 set allowed {NAME DESCRIPTION OPTIONS CUSTOM_OPTIONS SCRIPT IDE NO_EXIT}
2292 foreach k [dict keys $cmdDict] {
2293 if {[lsearch -exact $allowed $k] < 0} {
2294 Msg Warning "Custom command '[dict get $cmdDict NAME]' in $file: unknown key '$k'. Allowed: $allowed. Skipping."
2299 set name [
string trim [dict get $cmdDict NAME]]
2301 Msg Error "Custom command in $file has empty NAME. Skipping."
2305 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]+$} $name]} {
2306 Msg Error "Custom command NAME '$name' (file $file) contains invalid characters."
2310 if {![dict exists $cmdDict DESCRIPTION]} {
2311 dict set cmdDict DESCRIPTION "No description provided."
2315 set hog_parameters {}
2316 foreach p $parameters {
2317 lappend hog_parameters [
lindex $p 0]
2322 if {[dict exists $cmdDict OPTIONS]} {
2323 set raw_opts [dict get $cmdDict OPTIONS]
2324 if {![
llength $raw_opts]} {
2328 foreach item $raw_opts {
2330 foreach p $parameters {
2331 set hog_parameter [
lindex $p 0]
2332 if { $item eq $hog_parameter } {
2333 lappend hog_options $p
2339 Msg Warning "Custom command '$name' in $file: option '$item' not found in Hog parameters. Skipping."
2342 dict set cmdDict OPTIONS $hog_options
2344 dict set cmdDict CUSTOM_OPTIONS {}
2351 if {[dict exists $cmdDict CUSTOM_OPTIONS]} {
2352 set raw_opts [dict get $cmdDict CUSTOM_OPTIONS]
2353 if {![
llength $raw_opts]} {
2356 foreach item $raw_opts {
2358 if {[
llength $item] != 2 && [
llength $item] != 3} {
2359 Msg Error "Bad custom option: \[$item\]. Custom command '$name' in $file: \
2360 each CUSTOM_OPTIONS entry must be {option \"help\"} for flags \
2361 and {option \"default_value\" \"help\"} for options with arguments. Skipping command."
2365 if {[
llength $item] == 2} {
2366 lassign $item opt help
2369 lassign $item opt def help
2372 if { [
IsInList $opt $hog_parameters] == 1 } {
2373 Msg Warning "Custom command '$name' in $file: option '$opt' already defined in Hog parameters. Skipping."
2379 if {![regexp {^[a-zA-Z][a-zA-Z0-9_]*(\.arg)?$} $opt]} {
2380 Msg Error "Custom command '$name' in $file: invalid option name '$opt'."
2385 Msg Warning "Custom command '$name' option '$opt' has empty help text."
2389 dict set cmdDict CUSTOM_OPTIONS {}
2393 if {[dict exists $cmdDict NO_EXIT]} {
2394 set no_exit [dict get $cmdDict NO_EXIT]
2395 set no_exit [
string tolower [
string trim $no_exit]]
2397 if {$no_exit eq "1" || $no_exit eq "true"} {
2403 dict set cmdDict NO_EXIT $no_exit
2405 dict set cmdDict NO_EXIT 0
2411 proc LoadCustomCommandFile {file parameters} {
2413 set dir [
file dirname $file]
2415 unset -nocomplain ::hog_command
2416 set rc [
catch {source $file} err]
2419 Msg Error "Error sourcing custom command file $file: $err"
2422 if {![
info exists ::hog_command]} {
2423 Msg Warning "File $file did not define ::hog_command. Skipping."
2426 set cmdDict $::hog_command
2428 if {[
catch {dict size $cmdDict}]} {
2429 Msg Error "In $file ::hog_command is not a valid dict. Skipping."
2439 proc GetDateAndTime {commit} {
2440 set clock_seconds [
clock seconds]
2443 set date [
Git "log -1 --format=%cd --date=format:%d%m%Y $commit"]
2444 set timee [
Git "log -1 --format=%cd --date=format:00%H%M%S $commit"]
2446 Msg Warning "Found Git version older than 2.9.3. Using current date and time instead of commit time."
2447 set date [
clock format $clock_seconds -format {%d%m%Y}]
2448 set timee [
clock format $clock_seconds -format {00%H%M%S}]
2450 return [list $date $timee]
2462 proc GetFile {file fileset} {
2465 set Files [get_files -all $file -of_object [get_filesets $fileset]]
2466 set f [
lindex $Files 0]
2474 puts "***DEBUG Hog:GetFile $file"
2483 proc GetFileGenerics {filename {entity ""}} {
2485 if {[
string equal $file_type "VERILOG_FILE"] || [
string equal $file_type "SYSTEMVERILOG_FILE"]} {
2487 }
elseif {[
string equal $file_type "VHDL_FILE"]} {
2490 Msg CriticalWarning "Could not determine extension of top level file."
2499 proc GetGenericsFromConf {proj_dir} {
2500 set generics_dict [dict create]
2501 set top_dir "Top/$proj_dir"
2502 set conf_file "$top_dir/hog.conf"
2504 Msg Debug "GetGenericsFromConf called with proj_dir=$proj_dir, top_dir=$top_dir"
2506 if {[
file exists $conf_file]} {
2508 if {[dict exists $properties generics]} {
2509 set generics_dict [dict get $properties generics]
2512 Msg Warning "File $conf_file not found."
2514 return $generics_dict
2527 proc GetSimSets {project_name repo_path {simsets ""} {ghdl 0} {no_conf 0}} {
2528 set simsets_dict [dict create]
2529 set list_dir "$repo_path/Top/$project_name/list"
2531 if {$simsets != ""} {
2532 foreach s $simsets {
2533 set list_file "$list_dir/$s.sim"
2534 if {[
file exists $list_file]} {
2535 lappend list_files $list_file
2536 }
elseif {$s != "sim_1"} {
2537 Msg CriticalWarning "Simulation set list file $list_file not found."
2542 set list_files [glob -nocomplain -directory $list_dir "*.sim"]
2546 set proj_dir [
file normalize $repo_path/Top/$project_name]
2547 set sim_file [
file normalize $proj_dir/sim.conf]
2549 foreach list_file $list_files {
2550 set file_name [
file tail $list_file]
2551 set simset_name [
file rootname $file_name]
2552 set fp [open $list_file r]
2553 set file_data [read $fp]
2555 set data [
split $file_data "\n"]
2557 set firstline [
lindex $data 0]
2559 if {[regexp {^ *\# *Simulator} $firstline]} {
2560 set simulator_prop [regexp -all -inline {\S+} $firstline]
2561 set simulator [
string tolower [
lindex $simulator_prop end]]
2563 Msg Warning "Simulator not set in $simset_name.sim. \
2564 The first line of $simset_name.sim should be #Simulator <SIMULATOR_NAME>,\
2565 where <SIMULATOR_NAME> can be xsim, questa, modelsim, ghdl, riviera, activehdl,\
2566 ies, or vcs, e.g. #Simulator questa.\
2567 Setting simulator by default to xsim."
2568 set simulator "xsim"
2570 if {$simulator eq "skip_simulation"} {
2571 Msg Info "Skipping simulation for $simset_name"
2574 if {($ghdl == 1 && $simulator != "ghdl") || ($ghdl == 0 && $simulator == "ghdl")} {
2578 set SIM_PROPERTIES ""
2579 if {[
file exists $sim_file] && $no_conf == 0} {
2580 set SIM_PROPERTIES [
ReadConf $sim_file]
2583 set global_sim_props [dict create]
2584 dict set global_sim_props "properties" [
DictGet $SIM_PROPERTIES "sim"]
2585 dict set global_sim_props "generics" [
DictGet $SIM_PROPERTIES "generics"]
2586 dict set global_sim_props "hog" [
DictGet $SIM_PROPERTIES "hog"]
2589 set sim_dict [dict create]
2590 dict set sim_dict "simulator" $simulator
2591 if {[dict exists $SIM_PROPERTIES $simset_name]} {
2592 dict set sim_dict "properties" [
DictGet $SIM_PROPERTIES $simset_name]
2593 dict set sim_dict "generics" [
DictGet $SIM_PROPERTIES "$simset_name:generics"]
2594 dict set sim_dict "hog" [
DictGet $SIM_PROPERTIES "$simset_name:hog"]
2595 }
elseif {$no_conf == 0} {
2597 set conf_dict [
ReadConf $list_file]
2598 set sim_dict [
MergeDict $sim_dict $conf_dict 0]
2600 set sim_dict [
MergeDict $sim_dict $global_sim_props 0]
2601 dict set simsets_dict $simset_name $sim_dict
2603 return $simsets_dict
2611 proc GetSimsetGenericsFromConf {proj_dir} {
2612 set simsets_generics_dict [dict create]
2613 set top_dir "Top/$proj_dir"
2614 set conf_file "$top_dir/sim.conf"
2617 if {[
file exists $conf_file]} {
2620 set simsets_generics_dict [dict filter $properties key *:generics]
2622 Msg Warning "File $conf_file not found."
2624 return $simsets_generics_dict
2635 proc GetGroupName {proj_dir repo_dir} {
2636 if {[regexp {^(.*)/(Top|Projects)/+(.*?)/*$} $proj_dir dummy possible_repo_dir proj_or_top dir]} {
2638 if {[
file normalize $repo_dir] eq [
file normalize $possible_repo_dir]} {
2639 set group [
file dir $dir]
2640 if {$group == "."} {
2645 Msg Warning "Project directory $proj_dir seems to be in $possible_repo_dir which is not a the main Git repository $repo_dir."
2648 Msg Warning "Could not parse project directory $proj_dir"
2661 proc GetHogDescribe {proj_dir {repo_path .}} {
2662 lassign [
GetRepoVersions $proj_dir $repo_path] global_commit global_version
2663 if {$global_commit == 0} {
2665 set new_sha "[
string toupper [
GetSHA]]"
2668 set new_sha [
string toupper $global_commit]
2688 proc GetHogFiles {args} {
2691 if {[
catch {
package require cmdline} ERROR]} {
2692 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
2699 {list_files.arg "" "The file wildcard, if not specified all Hog list files will be looked for."}
2700 {sha_mode "Forwarded to ReadListFile, see there for info."}
2701 {ext_path.arg "" "Path for the external libraries forwarded to ReadListFile."}
2702 {print_log "Forwarded to ReadListFile, see there for info."}
2704 set usage "USAGE: GetHogFiles \[options\] <list path> <repository path>"
2705 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
2709 set list_path [
lindex $args 0]
2710 set repo_path [
lindex $args 1]
2712 set list_files $options(list_files)
2713 set sha_mode $options(sha_mode)
2714 set ext_path $options(ext_path)
2715 set print_log $options(print_log)
2717 if {$sha_mode == 1} {
2718 set sha_mode_opt "-sha_mode"
2723 if {$print_log == 1} {
2724 set print_log_opt "-print_log"
2726 set print_log_opt ""
2730 if {$list_files == ""} {
2731 set list_files {.src,.con,.sim,.ext}
2733 set libraries [dict create]
2734 set properties [dict create]
2735 set list_files [glob -nocomplain -directory $list_path "*{$list_files}"]
2736 set filesets [dict create]
2738 foreach f $list_files {
2739 set ext [
file extension $f]
2740 if {$ext == ".ext"} {
2741 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $ext_path"] l p fs
2743 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $repo_path"] l p fs
2746 set properties [
MergeDict $p $properties]
2747 Msg Debug "list file $f, filesets: $fs"
2749 Msg Debug "Merged filesets $filesets"
2751 return [list $libraries $properties $filesets]
2758 proc GetIDECommand {proj_conf {custom_ver ""}} {
2759 if {$custom_ver ne ""} {
2760 set ide_name_and_ver [
string tolower "$custom_ver"]
2761 }
elseif {[
file exists $proj_conf]} {
2762 set ide_name_and_ver [
string tolower [
GetIDEFromConf $proj_conf]]
2764 Msg Error "Configuration file $proj_conf not found."
2767 set ide_name [
lindex [regexp -all -inline {\S+} $ide_name_and_ver] 0]
2769 if {$ide_name eq "vivado" || $ide_name eq "vivado_vitis_classic" || $ide_name eq "vivado_vitis_unified" || $ide_name eq "vitis_unified"} {
2770 set command "vivado"
2772 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2773 set after_tcl_script " -tclargs "
2775 }
elseif {$ide_name eq "planahead"} {
2776 set command "planAhead"
2778 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2779 set after_tcl_script " -tclargs "
2781 }
elseif {$ide_name eq "quartus"} {
2782 set command "quartus_sh"
2784 set before_tcl_script " -t "
2785 set after_tcl_script " "
2787 }
elseif {$ide_name eq "libero"} {
2790 set command "libero"
2791 set before_tcl_script "SCRIPT:"
2792 set after_tcl_script " SCRIPT_ARGS:\""
2794 }
elseif {$ide_name eq "diamond"} {
2795 set command "diamondc"
2796 set before_tcl_script " "
2797 set after_tcl_script " "
2799 }
elseif {$ide_name eq "vitis_classic"} {
2802 set before_tcl_script ""
2803 set after_tcl_script " "
2805 }
elseif {$ide_name eq "ghdl"} {
2807 set before_tcl_script " "
2808 set after_tcl_script " "
2811 Msg Error "IDE: $ide_name not known."
2814 return [list $command $before_tcl_script $after_tcl_script $end_marker]
2820 proc GetIDEFromConf {conf_file} {
2821 set f [open $conf_file "r"]
2824 if {[regexp -all {^\# *(\w*) *(vitis_(?:classic|unified))? *(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)?(_.*)? *$} $line dummy ide vitisflag version patch]} {
2825 if {[
info exists vitisflag] && $vitisflag != ""} {
2826 set ide "${ide}_${vitisflag}"
2829 if {[
info exists version] && $version != ""} {
2835 set ret [list $ide $ver]
2837 Msg CriticalWarning "The first line of hog.conf should be \#<IDE name> <version>, \
2838 where <IDE name>. is quartus, vivado, planahead, libero, diamond or ghdl, \
2839 and <version> the tool version, e.g. \#vivado 2020.2. Will assume vivado."
2840 set ret [list "vivado" "0.0.0"]
2847 proc GetIDEName {} {
2849 return "ISE/PlanAhead"
2867 proc GetIDEVersion {} {
2870 regexp {\d+\.\d+(\.\d+)?} [version -short] ver
2875 regexp {[\.0-9]+} $quartus(version) ver
2878 set ver [get_libero_version]
2881 regexp {\d+\.\d+(\.\d+)?} [sys_install version] ver
2884 regexp {\d+\.\d+(\.\d+)?} [version] ver
2887 set vitis_output [
exec vitis --version 2>@1]
2888 regexp {[Vv]itis\s+v?(\d+\.\d+(?:\.\d+)?)} $vitis_output -> ver
2902 proc GetLinkedFile {link_file} {
2903 if {[
file type $link_file] eq "link"} {
2904 if {[
OS] == "windows"} {
2906 lassign [
ExecuteRet realpath $link_file] ret msg
2907 lassign [
ExecuteRet cygpath -m $msg] ret2 msg2
2908 if {$ret == 0 && $ret2 == 0} {
2910 Msg Debug "Found link file $link_file on Windows, the linked file is: $real_file"
2912 Msg CriticalWarning "[
file normalize $link_file] is a soft link. \
2913 Soft link are not supported on Windows and readlink.exe or cygpath.exe did not work: readlink=$ret: $msg, cygpath=$ret2: $msg2."
2914 set real_file $link_file
2918 set linked_file [
file link $link_file]
2919 set real_file [
file normalize [
file dirname $link_file]/$linked_file]
2922 if {![
file exists $real_file]} {
2923 Msg Warning "$link_file is a broken link, because the linked file: $real_file does not exist."
2926 Msg Warning "$link file is not a soft link"
2927 set real_file $link_file
2940 proc GetMaxThreads {proj_dir} {
2942 if {[
file exists $proj_dir/hog.conf]} {
2944 if {[dict exists $properties parameters]} {
2945 set propDict [dict get $properties parameters]
2946 if {[dict exists $propDict MAX_THREADS]} {
2947 set maxThreads [dict get $propDict MAX_THREADS]
2951 Msg Warning "File $proj_dir/hog.conf not found. Max threads will be set to default value 1"
2964 proc GetModifiedFiles {{repo_path "."} {pattern "."}} {
2967 set ret [
Git "ls-files --modified $pattern"]
2976 proc GetOptions {argv parameters} {
2979 set param_list [list]
2980 set option_list [list]
2982 foreach p $parameters {
2983 lappend param_list [
lindex $p 0]
2987 while {$index < [
llength $argv]} {
2988 set arg [
lindex $argv $index]
2989 if {[
string first - $arg] == 0} {
2990 set option [
string trimleft $arg "-"]
2992 lappend option_list $arg
2993 if {[lsearch -regex $param_list "$option\[.arg]?"] >= 0 } {
2994 if {[lsearch -regex $param_list "$option\[.arg]"] >= 0 } {
2995 lappend option_list [
lindex $argv $index]
3000 lappend arg_list $arg
3004 Msg Debug "Argv: $argv"
3005 Msg Debug "Options: $option_list"
3006 Msg Debug "Arguments: $arg_list"
3007 return [list $option_list $arg_list]
3025 proc GetProjectFiles {{project_file ""}} {
3026 set libraries [dict create]
3027 set simlibraries [dict create]
3028 set constraints [dict create]
3029 set properties [dict create]
3030 set consets [dict create]
3031 set srcsets [dict create]
3032 set simsets [dict create]
3035 set all_filesets [get_filesets]
3036 set simulator [get_property target_simulator [current_project]]
3037 set top [get_property "top" [current_fileset]]
3039 dict lappend properties $topfile "top=$top"
3041 foreach fs $all_filesets {
3042 if {$fs == "utils_1"} {
3047 set all_files [get_files -quiet -of_objects [get_filesets $fs]]
3048 set fs_type [get_property FILESET_TYPE [get_filesets $fs]]
3050 if {$fs_type == "BlockSrcs"} {
3052 set dict_fs "sources_1"
3056 foreach f $all_files {
3063 if {[
lindex [get_property IS_GENERATED [
GetFile $f $fs]] 0] != 0} {
3068 if {[get_property FILE_TYPE [
GetFile $f $fs]] == "Configuration Files"} {
3074 if {[get_property CORE_CONTAINER [
GetFile $f $fs]] != ""} {
3075 if {[
file extension $f] == ".xcix"} {
3076 set f [get_property CORE_CONTAINER [
GetFile $f $fs]]
3084 if {[get_property SCOPED_TO_REF [
GetFile $f $fs]] != ""} {
3085 dict lappend properties $f "scoped_to_ref=[get_property SCOPED_TO_REF [
GetFile $f $fs]]"
3090 if {[get_property SCOPED_TO_CELLS [
GetFile $f $fs]] != ""} {
3091 dict lappend properties $f "scoped_to_cells=[regsub " " [get_property SCOPED_TO_CELLS [
GetFile $f $fs]] ","]"
3095 if {[
IsInList "PARENT_COMPOSITE_FILE" [list_property [
GetFile $f $fs]]]} {
3100 if {[
file tail $f] == "nocattrs.dat"} {
3105 if {[
file extension $f] != ".coe"} {
3106 set f [
file normalize $f]
3109 set type [get_property FILE_TYPE [
GetFile $f $fs]]
3111 set lib [get_property -quiet LIBRARY [
GetFile $f $fs]]
3114 Msg Debug "File $f Extension [
file extension $f] Type [
lindex $type 0]"
3116 if {[
string equal [
lindex $type 0] "VHDL"] && [
llength $type] == 1} {
3118 }
elseif {[
string equal [
lindex $type 0] "Block"] && [
string equal [
lindex $type 1] "Designs"]} {
3121 }
elseif {[
string equal $type "SystemVerilog"] && [
file extension $f] != ".sv" && [
file extension $f] != ".svp"} {
3122 set prop "SystemVerilog"
3123 }
elseif {[
string equal [
lindex $type 0] "XDC"] && [
file extension $f] != ".xdc"} {
3125 }
elseif {[
string equal $type "Verilog Header"] && [
file extension $f] != ".vh" && [
file extension $f] != ".svh"} {
3126 set prop "verilog_header"
3127 }
elseif {[
string equal $type "Verilog Template"] && [
file extension $f] == ".v" && [
file extension $f] != ".sv"} {
3128 set prop "verilog_template"
3130 set type [
lindex $type 0]
3134 if {![
string equal $prop ""]} {
3135 dict lappend properties $f $prop
3138 if {[
string equal $fs_type "SimulationSrcs"]} {
3140 if {[
string equal $type "VHDL"]} {
3141 set library "${lib}.sim"
3143 set library "others.sim"
3147 dict lappend simsets $dict_fs $library
3150 dict lappend simlibraries $library $f
3151 }
elseif {[
string equal $type "VHDL"]} {
3154 dict lappend srcsets $dict_fs "${lib}.src"
3156 dict lappend libraries "${lib}.src" $f
3157 }
elseif {[
string first "IP" $type] != -1} {
3160 dict lappend srcsets $dict_fs "ips.src"
3162 dict lappend libraries "ips.src" $f
3163 Msg Debug "Appending $f to ips.src"
3164 }
elseif {[
string equal $fs_type "Constrs"]} {
3167 dict lappend consets $dict_fs "sources.con"
3169 dict lappend constraints "sources.con" $f
3173 dict lappend srcsets $dict_fs "others.src"
3175 dict lappend libraries "others.src" $f
3176 Msg Debug "Appending $f to others.src"
3179 if {[
lindex [get_property -quiet used_in_synthesis [
GetFile $f $fs]] 0] == 0} {
3180 dict lappend properties $f "nosynth"
3182 if {[
lindex [get_property -quiet used_in_implementation [
GetFile $f $fs]] 0] == 0} {
3183 dict lappend properties $f "noimpl"
3185 if {[
lindex [get_property -quiet used_in_simulation [
GetFile $f $fs]] 0] == 0} {
3186 dict lappend properties $f "nosim"
3188 if {[
lindex [get_property -quiet IS_MANAGED [
GetFile $f $fs]] 0] == 0 && [
file extension $f] != ".xcix"} {
3189 dict lappend properties $f "locked"
3195 dict lappend properties "Simulator" [get_property target_simulator [current_project]]
3198 set file [open $project_file r]
3199 set in_file_manager 0
3201 while {[
gets $file line] >= 0} {
3203 if {[regexp {^KEY ActiveRoot \"([^\"]+)\"} $line -> value]} {
3204 set top [
string range $value 0 [
expr {[string first "::" $value] - 1}]]
3208 if {[regexp {^LIST FileManager} $line]} {
3209 set in_file_manager 1
3214 if {$in_file_manager && [regexp {^ENDLIST} $line]} {
3219 if {$in_file_manager && [regexp {^VALUE \"([^\"]+)} $line -> value]} {
3222 lassign [
split $value ,] file_path file_type
3225 set library "others"
3226 while {[
gets $file line] >= 0} {
3227 if {$line == "ENDFILE"} {
3230 regexp {^LIBRARY=\"([^\"]+)} $line -> library
3231 regexp {^PARENT=\"([^\"]+)} $line -> parent_file
3233 Msg Debug "Found file ${file_path} in project.."
3234 if {$parent_file == ""} {
3235 if {$file_type == "hdl"} {
3237 if {[
IsInList "${library}.src" [
DictGet $srcsets "sources_1"]] == 0} {
3238 dict lappend srcsets "sources_1" "${library}.src"
3240 dict lappend libraries "${library}.src" $file_path
3244 if {[
GetModuleName $file_path] == [
string tolower $top] && $top != ""} {
3245 Msg Debug "Found top module $top in $file_path"
3246 dict lappend properties $file_path "top=$top"
3248 }
elseif {$file_type == "tb_hdl"} {
3250 dict lappend simsets "sim_1" "${library}.sim"
3252 dict lappend simlibraries "${library}.sim" $file_path
3253 }
elseif {$file_type == "io_pdc" || $file_type == "sdc"} {
3255 dict lappend consets "constrs_1" "sources.con"
3257 dict lappend constraints "sources.con" $file_path
3264 set fileData [read [open $project_file]]
3266 set project_path [
file dirname $project_file]
3269 regsub {<\?xml.*\?>} $fileData "" fileData
3272 regexp {<Implementation.*?>(.*)</Implementation>} $fileData -> implementationContent
3276 set sourceRegex {<Source name="([^"]*?)" type="([^"]*?)" type_short="([^"]*?)".*?>(.*?)</Source>}
3278 set optionsRegex {<Options(.*?)\/>}
3279 regexp $optionsRegex $implementationContent -> prj_options
3280 foreach option $prj_options {
3281 if {[regexp {^top=\"([^\"]+)\"} $option match result]} {
3286 while {[regexp $sourceRegex $implementationContent match name type type_short optionsContent]} {
3287 Msg Debug "Found file ${name} in project..."
3288 set file_path [
file normalize $project_path/$name]
3290 set optionsRegex {<Options(.*?)\/>}
3291 regexp $optionsRegex $optionsContent -> options
3292 set library "others"
3294 foreach option $options {
3295 if {[
string first "System Verilog" $option]} {
3298 if {[regexp {^lib=\"([^\"]+)\"} $option match1 result]} {
3303 if {[regexp {syn_sim="([^"]*?)"} $match match_sim simonly]} {
3308 if {$type_short == "VHDL" || $type_short == "Verilog" || $type_short == "IPX"} {
3309 if {$ext == ".src"} {
3310 if {[
IsInList "${library}${ext}" [
DictGet $srcsets "sources_1"]] == 0} {
3311 dict lappend srcsets "sources_1" "${library}${ext}"
3313 dict lappend libraries "${library}${ext}" $file_path
3314 }
elseif {$ext == ".sim"} {
3316 dict lappend simsets "sim_1" "${library}.sim"
3318 dict lappend simlibraries "${library}.sim" $file_path
3324 Msg Debug "Found top module $top in $file_path"
3325 dict lappend properties $file_path "top=$top"
3327 }
elseif {$type_short == "SDC"} {
3329 dict lappend consets "constrs_1" "sources.con"
3331 dict lappend constraints "sources.con" $file_path
3335 regsub -- $match $implementationContent "" implementationContent
3338 return [list $libraries $properties $simlibraries $constraints $srcsets $simsets $consets]
3345 proc GetProjectFlavour {proj_name} {
3347 set flavour [
string map {. ""} [
file extension $proj_name]]
3348 if {$flavour != ""} {
3349 if {[
string is integer $flavour]} {
3350 Msg Info "Project $proj_name has flavour = $flavour, the generic variable FLAVOUR will be set to $flavour"
3352 Msg Warning "Project name has a unexpected non numeric extension, flavour will be set to -1"
3369 proc GetProjectVersion {proj_dir repo_path {ext_path ""} {sim 0}} {
3370 if {![
file exists $proj_dir]} {
3371 Msg CriticalWarning "$proj_dir not found"
3381 Msg Warning "Repository is not clean"
3389 Msg Debug "Project version $v_proj, latest tag $v_last"
3391 Msg Info "The specified project was modified since official version."
3398 Msg Info "The specified project was modified in the latest official version $ret"
3399 }
elseif {$comp == -1} {
3400 Msg Info "The specified project was modified in a past official version $ret"
3416 proc GetRepoVersions {proj_dir repo_path {ext_path ""} {sim 0}} {
3417 if {[
catch {
package require cmdline} ERROR]} {
3418 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
3433 lappend SHAs [
GetSHA {Hog}]
3437 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
3438 Msg Info "Hog submodule [
pwd] clean."
3439 lassign [
GetVer ./] hog_ver hog_hash
3441 Msg CriticalWarning "Hog submodule [
pwd] not clean, commit hash will be set to 0."
3442 set hog_hash "0000000"
3443 set hog_ver "00000000"
3449 set project_files $conf_files
3450 lappend project_files $repo_path/Hog
3453 lassign [
GetVer [
join $conf_files]] top_ver top_hash
3454 lappend SHAs $top_hash
3455 lappend versions $top_ver
3462 lassign [
GetHogFiles -list_files "*.src" -sha_mode "./list/" $repo_path] src_files dummy
3463 dict for {f files} $src_files {
3464 # library names have a .src extension in values returned by GetHogFiles
3465 set name [file rootname [file tail $f]]
3466 if {[file ext $f] == ".oth"} {
3469 lassign [GetVer $files] ver hash
3470 # Msg Info "Found source list file $f, version: $ver commit SHA: $hash"
3472 lappend versions $ver
3474 lappend hashes $hash
3476 lappend project_files $f {*}$files
3482 lassign [
GetHogFiles -list_files "*.con" -sha_mode "./list/" $repo_path] cons_files dummy
3483 dict for {f files} $cons_files {
3484 #library names have a .con extension in values returned by GetHogFiles
3485 set name [file rootname [file tail $f]]
3486 lassign [GetVer $files] ver hash
3487 #Msg Info "Found constraint list file $f, version: $ver commit SHA: $hash"
3489 Msg CriticalWarning "Constraints file $f not found in Git."
3491 lappend cons_hashes $hash
3493 lappend versions $ver
3494 lappend project_files $f {*}$files
3501 lassign [
GetHogFiles -list_files "*.sim" -sha_mode "./list/" $repo_path] sim_files dummy
3502 dict for {f files} $sim_files {
3503 #library names have a .sim extension in values returned by GetHogFiles
3504 set name [file rootname [file tail $f]]
3505 lassign [GetVer $files] ver hash
3506 #Msg Info "Found simulation list file $f, version: $ver commit SHA: $hash"
3507 lappend sim_hashes $hash
3509 lappend versions $ver
3510 lappend project_files $f {*}$files
3518 Msg CriticalWarning "No hashes found for constraints files (not in git)"
3521 set cons_hash [
string tolower [
Git "log --format=%h -1 $cons_hashes"]]
3528 set ext_files [glob -nocomplain "./list/*.ext"]
3531 foreach f $ext_files {
3532 set name [
file rootname [
file tail $f]]
3535 lappend ext_names $name
3536 lappend ext_hashes $hash
3539 lappend versions $ext_ver
3540 lappend project_files $f
3543 set file_data [read $fp]
3545 set data [
split $file_data "\n"]
3547 foreach line $data {
3548 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
3550 set file_and_prop [regexp -all -inline {\S+} $line]
3551 set hdlfile [
lindex $file_and_prop 0]
3552 set hdlfile $ext_path/$hdlfile
3553 if {[
file exists $hdlfile]} {
3554 set hash [
lindex $file_and_prop 1]
3555 set current_hash [
Md5Sum $hdlfile]
3556 if {[
string first $hash $current_hash] == -1} {
3557 Msg CriticalWarning "File $hdlfile has a wrong hash. Current checksum: $current_hash, expected: $hash"
3565 if {[
llength [glob -nocomplain ./list/*.ipb]] > 0} {
3567 lassign [
GetHogFiles -list_files "*.ipb" -sha_mode "./list/" $repo_path] xml_files dummy
3568 set xml_source_files [dict get $xml_files "xml.ipb"]
3569 lassign [
GetVer $xml_source_files] xml_ver xml_hash
3570 lappend SHAs $xml_hash
3571 lappend versions $xml_ver
3572 lappend project_files {*}[glob ./list/*.ipb] {*}$xml_source_files
3576 Msg Info "This project does not use IPbus XMLs"
3581 set user_ip_repos ""
3582 set user_ip_repo_hashes ""
3583 set user_ip_repo_vers ""
3585 if {[
file exists [
lindex $conf_files 0]]} {
3586 set PROPERTIES [
ReadConf [
lindex $conf_files 0]]
3587 if {[dict exists $PROPERTIES main]} {
3588 set main [dict get $PROPERTIES main]
3589 dict for {p v} $main {
3590 if {[string tolower $p] == "ip_repo_paths"} {
3592 if {[file isdirectory "$repo_path/$repo"]} {
3593 set repo_file_list [glob -nocomplain "$repo_path/$repo/*"]
3594 if {[llength $repo_file_list] == 0} {
3595 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3597 lappend user_ip_repos "$repo_path/$repo"
3606 foreach repo $user_ip_repos {
3607 if {[
file isdirectory $repo]} {
3608 set repo_file_list [glob -nocomplain "$repo/*"]
3609 if {[
llength $repo_file_list] != 0} {
3610 lassign [
GetVer $repo] ver sha
3611 lappend user_ip_repo_hashes $sha
3612 lappend user_ip_repo_vers $ver
3613 lappend versions $ver
3614 lappend project_files $repo
3616 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3619 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory does not exist."
3626 if {[
Git "status --untracked-files=no --porcelain" $project_files] eq ""} {
3627 Msg Info "Project-relevant files are clean."
3630 Msg CriticalWarning "Project-relevant files not clean, commit hash and version will be set to 0."
3637 while {$found == 0} {
3638 set global_commit [
Git "log --format=%h -1 --abbrev=7 $SHAs"]
3643 if {$common_child == 0} {
3644 Msg CriticalWarning "The commit $sha is not an ancestor of the global commit $global_commit, which is OK. \
3645 But $sha and $global_commit do not have any common child, which is NOT OK. \
3646 This is probably do to a REBASE that is forbidden in Hog methodology as it changes git history. \
3647 Hog cannot guarantee the accuracy of the SHAs. \
3648 A way to fix this is to make a commit that touches all the projects in the repositories (e.g. change the Hog version), \
3649 but please do not rebase in the official branches in the future."
3651 Msg Info "The commit $sha is not an ancestor of the global commit $global_commit, adding the first common child $common_child instead..."
3652 lappend SHAs $common_child
3662 set global_commit "0000000"
3663 set global_version "00000000"
3668 set top_hash [
format %+07s $top_hash]
3669 set cons_hash [
format %+07s $cons_hash]
3670 return [list $global_commit $global_version \
3671 $hog_hash $hog_ver $top_hash $top_ver \
3672 $libs $hashes $vers $cons_ver $cons_hash \
3673 $ext_names $ext_hashes $xml_hash $xml_ver \
3674 $user_ip_repos $user_ip_repo_hashes $user_ip_repo_vers]
3683 proc GetSHA {{path ""}} {
3685 lassign [
GitRet {log --format=%h --abbrev=7 -1}] status result
3687 return [
string tolower $result]
3689 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3695 set repo_path [
lindex [
Git {rev-parse --show-toplevel}] 0]
3699 set file_in_module 0
3700 if {[
file exists $repo_path/.gitmodules]} {
3701 lassign [
GitRet "config --file $repo_path/.gitmodules --get-regexp path"] status result
3703 set submodules [
split $result "\n"]
3706 Msg Warning "Something went wrong while trying to find submodules: $result"
3709 foreach mod $submodules {
3710 set module [
lindex $mod 1]
3711 if {[
string first "$repo_path/$module" $f] == 0} {
3713 set file_in_module 1
3714 lappend paths "$repo_path/$module"
3719 if {$file_in_module == 0} {
3725 lassign [
GitRet {log --format=%h --abbrev=7 -1} $paths] status result
3727 return [
string tolower $result]
3729 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3732 return [
string tolower $result]
3736 proc GetSimulators {} {
3737 set SIMULATORS [list "modelsim" "questa" "riviera" "activehdl" "ies" "vcs"]
3742 proc GetTopFile {} {
3744 set compile_order_prop [get_property source_mgmt_mode [current_project]]
3745 if {$compile_order_prop ne "All"} {
3746 Msg CriticalWarning "Compile order is not set to automatic, setting it now..."
3747 set_property source_mgmt_mode All [current_project]
3748 update_compile_order -fileset sources_1
3750 return [
lindex [get_files -quiet -compile_order sources -used_in synthesis -filter {FILE_TYPE =~ "VHDL*" || FILE_TYPE =~ "*Verilog*" }] end]
3751 }
elseif {[
IsISE]} {
3752 debug::design_graph_mgr -create [current_fileset]
3753 debug::design_graph -add_fileset [current_fileset]
3754 debug::design_graph -update_all
3755 return [
lindex [debug::design_graph -get_compile_order] end]
3757 Msg Error "GetTopFile not yet implemented for this IDE"
3762 proc GetTopModule {} {
3764 return [get_property top [current_fileset]]
3766 Msg Error "GetTopModule not yet implemented for this IDE"
3776 proc GetVer {path {force_develop 0} {verbose 1}} {
3780 Msg CriticalWarning "Empty SHA found for ${path}. Commit to Git to resolve this warning."
3783 set p [
lindex $path 0]
3784 if {[
file isdirectory $p]} {
3787 cd [
file dirname $p]
3789 set repo_path [
Git {rev-parse --show-toplevel}]
3792 return [list [
GetVerFromSHA $SHA $repo_path $force_develop $verbose] $SHA]
3804 proc GetVerFromSHA {SHA repo_path {force_develop 0} {verbose 1}} {
3806 Msg CriticalWarning "Empty SHA found"
3809 lassign [
GitRet "tag --sort=creatordate --contain $SHA -l v*.*.* -l b*v*.*.*"] status result
3812 if {[regexp {^ *$} $result]} {
3814 lassign [
GitRet "log --oneline --pretty=\"%d\""] status2 tag_list
3817 set pattern {tag: v\d+\.\d+\.\d+}
3818 set real_tag_list {}
3819 foreach x $tag_list {
3820 set x_untrimmed [regexp -all -inline $pattern $x]
3821 regsub "tag: " $x_untrimmed "" x_trimmed
3822 set tt [
lindex $x_trimmed 0]
3823 if {![
string equal $tt ""]} {
3824 lappend real_tag_list $tt
3828 Msg Debug "Cleaned up list: $real_tag_list."
3830 set sorted_tags [lsort -decreasing -command CompareVersions $real_tag_list]
3832 Msg Debug "Sorted Tag list: $sorted_tags"
3834 set tag [
lindex $sorted_tags 0]
3837 set pattern {v\d+\.\d+\.\d+}
3838 if {![regexp $pattern $tag]} {
3839 Msg CriticalWarning "No Hog version tags found in this repository."
3844 set repo_conf $repo_path/Top/repo.conf
3848 set hotfix_prefix "hotfix/"
3849 set minor_prefix "minor_version/"
3850 set major_prefix "major_version/"
3852 set enable_develop_branch $force_develop
3854 set branch_name [
Git {rev-parse --abbrev-ref HEAD}]
3856 if {[
file exists $repo_conf]} {
3857 set PROPERTIES [
ReadConf $repo_conf]
3859 if {[dict exists $PROPERTIES main]} {
3860 set mainDict [dict get $PROPERTIES main]
3863 if {[dict exists $mainDict ENABLE_DEVELOP_BRANCH]} {
3864 set enable_develop_branch [dict get $mainDict ENABLE_DEVELOP_BRANCH]
3870 if {[dict exists $PROPERTIES prefixes]} {
3871 set prefixDict [dict get $PROPERTIES prefixes]
3873 if {[dict exists $prefixDict HOTFIX]} {
3874 set hotfix_prefix [dict get $prefixDict HOTFIX]
3876 if {[dict exists $prefixDict MINOR_VERSION]} {
3877 set minor_prefix [dict get $prefixDict MINOR_VERSION]
3879 if {[dict exists $prefixDict MAJOR_VERSION]} {
3880 set major_prefix [dict get $prefixDict MAJOR_VERSION]
3886 if {[
string match "HEAD" $branch_name]} {
3887 if {$verbose == 1} {
3888 Msg Warning "Detached HEAD detected - attempting to find branch name"
3893 set log_refs [
Git {show -s --pretty=%D HEAD}]
3894 set branch_list [
split $log_refs ","]
3895 Msg Debug "list of possible branch refs $log_refs"
3901 set match_prefixes [list $hotfix_prefix $minor_prefix $major_prefix]
3902 set prev_branch_name $branch_name
3904 foreach br $branch_list {
3905 foreach pr $match_prefixes {
3906 if {[
string match "$pr*" [
string trim $br]]} {
3907 set branch_name [
string trim $br]
3913 if {!$match_count == 1} {
3914 set branch_name $prev_branch_name
3915 if {$verbose == 1} {
3916 Msg Warning "Branch name not found. Using $branch_name"
3919 if {$verbose == 1} {
3920 Msg Info "Branch name found: $branch_name"
3925 if {$enable_develop_branch == 1} {
3926 if {[
string match "$hotfix_prefix*" $branch_name]} {
3931 if {[
string match "$major_prefix*" $branch_name]} {
3933 set version_level major
3934 }
elseif {[
string match "$minor_prefix*" $branch_name] || ($enable_develop_branch == 1 && $is_hotfix == 0)} {
3936 set version_level minor
3939 set version_level patch
3943 Msg CriticalWarning "Tag $tag does not contain a Hog compatible version in this repository."
3946 }
elseif {$mr == 0} {
3947 switch $version_level {
3962 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."
3968 set vers [
split $result "\n"]
3969 set ver [
lindex $vers 0]
3971 if {[regexp {^v.*$} $v]} {
3979 Msg CriticalWarning "Error while trying to find tag for $SHA"
3987 set M [
format %02X $M]
3988 set m [
format %02X $m]
3989 set c [
format %04X $c]
3990 }
elseif {$M > -1} {
3992 set M [
format %02X $M]
3993 set m [
format %02X $m]
3994 set c [
format %04X $c]
3996 Msg Warning "Tag does not contain a properly formatted version: $ver"
3997 set M [
format %02X 0]
3998 set m [
format %02X 0]
3999 set c [
format %04X 0]
4012 proc Git {command {files ""}} {
4013 lassign [
GitRet $command $files] ret result
4015 Msg Error "Code $ret returned by git running: $command -- $files"
4026 proc GetModuleName {filename} {
4028 if {![
file exists $filename]} {
4029 Msg CriticalWarning "Error: File $filename does not exist."
4034 set fileId [open $filename r]
4037 set file_content [read $fileId]
4043 if {[
file extension $filename] == ".vhd" || [
file extension $filename] == ".vhdl"} {
4045 set file_content [
string tolower $file_content]
4047 set pattern {(?m)^\s*entity\s+(\S+)\s+is}
4048 }
elseif {[
file extension $filename] == ".v" || [
file extension $filename] == ".sv"} {
4050 set pattern {\n\s*module\s*(\w+)(\s*|\(|\n)}
4052 Msg Debug "File is neither VHDL nor Verilog... Returning empty string..."
4057 if {[regexp $pattern $file_content match module_name]} {
4060 Msg Debug "No module was found in $filename. Returning an empty string..."
4068 proc GetVerilogGenerics {file} {
4069 set fp [open $file r]
4075 foreach line [
split $data "\n"] {
4076 regsub "^\\s*\/\/.*" $line "" line
4077 regsub "(.*)\/\/.*" $line {\1} line
4078 if {![
string equal $line ""]} {
4079 append lines $line " "
4084 regsub -all {/\*.*\*/} $lines "" lines
4087 set punctuation [list]
4088 foreach char [list "(" ")" ";" "," " " "!" "<=" ":=" "=" "\[" "\]"] {
4089 lappend punctuation $char "\000$char\000"
4093 set tokens [
split [
string map $punctuation $lines] \000]
4095 set parameters [dict create]
4104 foreach token $tokens {
4105 set token [
string trim $token]
4106 if {![
string equal "" $token]} {
4107 if {[
string equal [
string tolower $token] "parameter"]} {
4108 set state $PARAM_NAME
4109 }
elseif {[
string equal $token ")"] || [
string equal $token ";"]} {
4111 }
elseif {$state == $PARAM_WIDTH} {
4112 if {[
string equal $token "\]"]} {
4113 set state $PARAM_NAME
4115 }
elseif {$state == $PARAM_VALUE} {
4116 if {[
string equal $token ","]} {
4117 set state $PARAM_NAME
4118 }
elseif {[
string equal $token ";"]} {
4123 }
elseif {$state == $PARAM_NAME} {
4124 if {[
string equal $token "="]} {
4125 set state $PARAM_VALUE
4126 }
elseif {[
string equal $token "\["]} {
4127 set state $PARAM_WIDTH
4128 }
elseif {[
string equal $token ","]} {
4129 set state $PARAM_NAME
4130 }
elseif {[
string equal $token ";"]} {
4132 }
elseif {[
string equal $token ")"]} {
4135 dict set parameters $token "integer"
4148 proc GetVhdlGenerics {file {entity ""}} {
4149 set fp [open $file r]
4155 foreach line [
split $data "\n"] {
4156 regsub "^\\s*--.*" $line "" line
4157 regsub "(.*)--.*" $line {\1} line
4158 if {![
string equal $line ""]} {
4159 append lines $line " "
4164 set generic_block ""
4165 set generics [dict create]
4167 if {1 == [
string equal $entity ""]} {
4168 regexp {(?i).*entity\s+([^\s]+)\s+is} $lines _ entity
4171 set generics_regexp "(?i).*entity\\s+$entity\\s+is\\s+generic\\s*\\((.*)\\)\\s*;\\s*port.*end.*$entity"
4173 if {[regexp $generics_regexp $lines _ generic_block]} {
4175 foreach line [
split $generic_block ";"] {
4177 regexp {(.*):\s*([A-Za-z0-9_]+).*} $line _ generic type
4180 set splits [
split $generic ","]
4181 foreach split $splits {
4182 dict set generics [
string trim $split] [
string trim $type]
4190 proc GHDL {command logfile} {
4191 set ret [
catch {
exec -ignorestderr ghdl {*}$command >>& $logfile} result options]
4196 return [list $ret $result]
4208 proc GitRet {command {files ""}} {
4211 set ret [
catch {
exec -ignorestderr git {*}$command} result]
4213 set ret [
catch {
exec -ignorestderr git {*}$command -- {*}$files} result]
4215 return [list $ret $result]
4223 proc GitVersion {target_version} {
4224 set ver [
split $target_version "."]
4225 set v [
Git --version]
4227 set current_ver [
split [
lindex $v 2] "."]
4228 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
4229 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
4230 return [
expr {$target <= $current}]
4244 proc HandleIP {what_to_do xci_file ip_path repo_path {gen_dir "."} {force 0}} {
4246 if {!($what_to_do eq "push") && !($what_to_do eq "pull")} {
4247 Msg Error "You must specify push or pull as first argument."
4250 if {[
catch {
package require tar} TARPACKAGE]} {
4251 Msg CriticalWarning "Cannot find package tar. You can fix this by installing package \"tcllib\""
4262 if {[regexp {^[^/]+:} $ip_path]} {
4266 lassign [
ExecuteRet rclone --version] rclone_ret rclone_ver
4267 if {$rclone_ret != 0} {
4268 Msg CriticalWarning "Rclone path specified but rclone not found or failed: $rclone_ver"
4272 Msg Info "IP remote directory path, on Rclone, is set to: $ip_path"
4274 if {[
info exists env(HOG_RCLONE_CONFIG)]} {
4275 Msg Info "Using rclone config from environment variable HOG_RCLONE_CONFIG: $env(HOG_RCLONE_CONFIG)"
4276 set config_path $env(HOG_RCLONE_CONFIG)
4278 set config_path "/dev/null"
4279 Msg Info "Environment variable HOG_RCLONE_CONFIG not set, using rclone environmental variables..."
4282 set remote_name "[
lindex [
split $ip_path ":"] 0]:"
4283 lassign [
ExecuteRet rclone listremotes --config $config_path] rclone_list_ret remotes
4284 if {$rclone_list_ret != 0} {
4285 Msg CriticalWarning "Could not list rclone remotes: $remotes"
4289 if {![
IsInList $remote_name $remotes]} {
4290 Msg CriticalWarning "Rclone remote $remote_name not found among available remotes: $remotes"
4296 }
elseif {[
string first "/eos/" $ip_path] == 0} {
4299 lassign [
eos "ls $ip_path"] ret result
4301 Msg CriticalWarning "Could not run ls for for EOS path: $ip_path (error: $result). \
4302 Either the drectory does not exist or there are (temporary) problem with EOS."
4306 Msg Info "IP remote directory path, on EOS, is set to: $ip_path"
4312 if {!([
file exists $xci_file])} {
4313 Msg CriticalWarning "Could not find $xci_file."
4319 set xci_path [
file dirname $xci_file]
4320 set xci_name [
file tail $xci_file]
4321 set xci_ip_name [
file rootname [
file tail $xci_file]]
4322 set xci_dir_name [
file tail $xci_path]
4323 set gen_path $gen_dir
4325 set hash [
Md5Sum $xci_file]
4326 set file_name $xci_name\_$hash
4328 Msg Info "Preparing to $what_to_do IP: $xci_name..."
4330 if {$what_to_do eq "push"} {
4333 if {$on_rclone == 1} {
4334 lassign [
ExecuteRet rclone ls $ip_path/$file_name.tar --config $config_path] ret result
4339 Msg Info "IP already in the Rclone repository, will not copy..."
4341 Msg Info "IP already in the Rclone repository, will forcefully replace..."
4346 }
elseif {$on_eos == 1} {
4347 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4352 Msg Info "IP already in the EOS repository, will not copy..."
4354 Msg Info "IP already in the EOS repository, will forcefully replace..."
4360 if {[
file exists "$ip_path/$file_name.tar"]} {
4362 Msg Info "IP already in the local repository, will not copy..."
4364 Msg Info "IP already in the local repository, will forcefully replace..."
4373 if {$will_copy == 1} {
4375 Msg Info "Looking for generated files in $gen_path..."
4376 set ip_gen_files [glob -nocomplain $gen_path/*]
4380 if {[
llength $ip_gen_files] > 0} {
4381 Msg Info "Found some IP synthesised files matching $xci_ip_name"
4382 if {$will_remove == 1} {
4383 Msg Info "Removing old synthesised directory $ip_path/$file_name.tar..."
4384 if {$on_rclone == 1} {
4385 lassign [
ExecuteRet rclone delete $ip_path/$file_name.tar --config $config_path] ret result
4387 Msg CriticalWarning "Could not delete file from Rclone: $result"
4389 }
elseif {$on_eos == 1} {
4390 eos "rm -rf $ip_path/$file_name.tar" 5
4392 file delete -force "$ip_path/$file_name.tar"
4396 Msg Info "Creating local archive with IP generated files..."
4399 foreach f $ip_gen_files {
4400 lappend tar_files "[
Relative [
file normalize $repo_path] $f]"
4403 ::tar::create $file_name.tar $tar_files
4405 Msg Info "Copying IP generated files for $xci_name..."
4406 if {$on_rclone == 1} {
4407 lassign [
ExecuteRet rclone copyto $file_name.tar $ip_path/$file_name.tar --config $config_path] ret result
4409 Msg CriticalWarning "Something went wrong when copying the IP files to Rclone. Error message: $result"
4411 }
elseif {$on_eos == 1} {
4412 lassign [
ExecuteRet xrdcp -f -s $file_name.tar $::env(EOS_MGM_URL)//$ip_path/] ret msg
4414 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4417 Copy "$file_name.tar" "$ip_path/"
4419 Msg Info "Removing local archive"
4420 file delete $file_name.tar
4422 Msg Warning "Could not find synthesized files matching $gen_path/$file_name*"
4425 }
elseif {$what_to_do eq "pull"} {
4426 if {$on_rclone == 1} {
4427 lassign [
ExecuteRet rclone ls $ip_path/$file_name.tar --config $config_path] ret result
4429 Msg Info "Nothing for $xci_name was found in the Rclone repository, cannot pull."
4433 Msg Info "IP $xci_name found in the Rclone repository $ip_path, copying it locally to $repo_path..."
4434 lassign [
ExecuteRet rclone copyto $ip_path/$file_name.tar $file_name.tar --config $config_path] ret_copy result_copy
4435 if {$ret_copy != 0} {
4436 Msg CriticalWarning "Something went wrong when copying the IP files from Rclone. Error message: $result_copy"
4439 }
elseif {$on_eos == 1} {
4440 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
4442 Msg Info "Nothing for $xci_name was found in the EOS repository, cannot pull."
4446 set remote_tar "$::env(EOS_MGM_URL)//$ip_path/$file_name.tar"
4447 Msg Info "IP $xci_name found in the repository $remote_tar, copying it locally to $repo_path..."
4449 lassign [
ExecuteRet xrdcp -f -r -s $remote_tar $repo_path] ret msg
4451 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
4455 if {[
file exists "$ip_path/$file_name.tar"]} {
4456 Msg Info "IP $xci_name found in local repository $ip_path/$file_name.tar, copying it locally to $repo_path..."
4457 Copy $ip_path/$file_name.tar $repo_path
4459 Msg Info "Nothing for $xci_name was found in the local IP repository, cannot pull."
4465 if {[
file exists $file_name.tar]} {
4466 remove_files $xci_file
4467 Msg Info "Extracting IP files from archive to $repo_path..."
4468 ::tar::untar $file_name.tar -dir $repo_path -noperms
4469 Msg Info "Removing local archive"
4470 file delete $file_name.tar
4471 add_files -norecurse -fileset sources_1 $xci_file
4484 proc HexVersionToString {version} {
4485 scan [
string range $version 0 1] %x M
4486 scan [
string range $version 2 3] %x m
4487 scan [
string range $version 4 7] %x c
4492 proc ImportTclLib {} {
4495 if {[
info exists env(HOG_TCLLIB_PATH)]} {
4496 lappend auto_path $env(HOG_TCLLIB_PATH)
4499 puts "ERROR: To run Hog with Microsemi Libero SoC or Lattice Diamond, you need to define the HOG_TCLLIB_PATH variable."
4514 proc InitLauncher {script tcl_path parameters commands argv {custom_commands ""}} {
4515 set repo_path [
file normalize "$tcl_path/../.."]
4517 set bin_path [
file normalize "$tcl_path/../../bin"]
4518 set top_path [
file normalize "$tcl_path/../../Top"]
4520 set cmd_lines [
split $commands "\n"]
4522 set command_options [dict create]
4523 set directive_descriptions [dict create]
4524 set directive_names [dict create]
4525 set common_directive_names [dict create]
4526 set custom_command ""
4527 set custom_command_options ""
4529 foreach l $cmd_lines {
4531 if {[regexp {\\(.*) \{\#} $l minc d]} {
4532 lappend directives_with_projects $d
4536 if {[regexp {\\(.*) \{} $l minc regular_expression]} {
4537 lappend directive_regex $regular_expression
4541 if {[regexp {\#\s*NAME(\*)?:\s*(.*)\s*} $l minc star name]} {
4542 dict set directive_names $name $regular_expression
4544 dict set common_directive_names $name $regular_expression
4547 set directive_names [
DictSort $directive_names]
4548 set common_directive_names [
DictSort $common_directive_names]
4551 if {[regexp {\#\s*DESCRIPTION:\s*(.*)\s*} $l minc x]} {
4552 dict set directive_descriptions $regular_expression $x
4556 if {[regexp {\#\s*OPTIONS:\s*(.*)\s*} $l minc x]} {
4557 dict set command_options $regular_expression [
split [regsub -all {[ \t\n]+} $x {}] ","]
4561 set short_usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nMost common directives (case insensitive):"
4563 dict for {key value} $common_directive_names {
4564 set short_usage "$short_usage\n - $key: [dict get $directive_descriptions $value]"
4567 if {[
string length $custom_commands] > 0} {
4568 Msg Debug "Found custom commands to add to short short_usage."
4569 set short_usage "$short_usage\n\nCustom commands:"
4570 dict for {key command} $custom_commands {
4571 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4572 set short_usage "$short_usage\n - $key: [dict get $command DESCRIPTION]"
4578 set short_usage "$short_usage\n\n\
4579 To see all the available directives, run:\n./Hog/Do HELP\n\n\
4580 To list available options for the chosen directive run:\n\
4581 ./Hog/Do <directive> HELP\n
4584 set usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nDirectives (case insensitive):"
4586 dict for {key value} $directive_names {
4587 set usage "$usage\n - $key: [dict get $directive_descriptions $value]"
4591 if {[
string length $custom_commands] > 0} {
4592 Msg Debug "Found custom commands to add to short usage."
4593 set usage "$usage\n\nCustom commands:"
4594 dict for {key command} $custom_commands {
4595 Msg Debug "Adding $key : [dict get $command DESCRIPTION]"
4596 set usage "$usage\n - $key: [dict get $command DESCRIPTION]"
4601 set usage "$usage\n\nTo list available options for the chosen directive run:\n./Hog/Do <directive> HELP"
4610 Msg Debug "HogEnv.conf found"
4618 if {[
catch {
package require cmdline} ERROR]} {
4619 Msg Debug "The cmdline Tcl package was not found, sourcing it from Hog..."
4620 source $tcl_path/utils/cmdline.tcl
4623 set argv [regsub -all {(?i) HELP\y} $argv " -help"]
4628 set custom_parameters [list]
4629 dict for {key command} $custom_commands {
4630 set custom_parameters [concat $custom_parameters [dict get $command CUSTOM_OPTIONS]]
4633 lassign [
GetOptions $argv [
concat $custom_parameters $parameters]] option_list arg_list
4635 if {[
IsInList "-all" $option_list]} {
4644 set directive [
string toupper [
lindex $arg_list 0]]
4647 set argument_is_no_project 1
4649 set NO_DIRECTIVE_FOUND 0
4650 switch -regexp -- $directive "$commands"
4651 if {$NO_DIRECTIVE_FOUND == 1} {
4652 if {[
string length $custom_commands] > 0 && [dict exists $custom_commands $directive]} {
4653 set custom_command $directive
4654 set custom_command_hog_parameters [dict get $custom_commands $directive OPTIONS]
4655 set custom_command_options [dict get $custom_commands $directive CUSTOM_OPTIONS]
4656 set custom_command_options [
concat $custom_command_hog_parameters $custom_command_options]
4658 Msg Status "ERROR: Unknown directive $directive.\n\n"
4664 if {[
IsInList $directive $directives_with_projects 1]} {
4665 set argument_is_no_project 0
4669 if {$directive != ""} {
4670 if {[
IsInList $directive $directives_with_projects 1]} {
4671 puts "usage: ./Hog/Do \[OPTIONS\] $directive <project>\n"
4672 }
elseif {[regexp "^COMPSIM(LIB)?$" $directive]} {
4673 puts "usage: ./Hog/Do \[OPTIONS\] $directive <simulator>\n"
4675 puts "usage: ./Hog/Do \[OPTIONS\] $directive \n"
4678 dict for {dir desc} $directive_descriptions {
4679 if {[regexp $dir $directive]} {
4687 if {$custom_command ne ""} {
4688 if {[
llength $custom_command_options] > 0} {
4689 puts "Available options:"
4691 foreach custom_option $custom_command_options {
4692 set n [
llength $custom_option]
4694 lassign $custom_option opt help
4697 }
elseif {$n == 3} {
4698 lassign $custom_option opt def help
4699 puts " -$opt <argument>"
4701 puts " $help (default: $def)"
4706 Msg Warning "Custom option spec has invalid arity (expected 2 or 3): $custom_option"
4711 dict for {dir opts} $command_options {
4712 if {[regexp $dir $directive]} {
4713 puts "Available options:"
4715 foreach par $parameters {
4716 if {$opt == [lindex $par 0]} {
4717 if {[regexp {\.arg$} $opt]} {
4718 set opt_name [regsub {\.arg$} $opt ""]
4719 puts " -$opt_name <argument>"
4723 puts " [lindex $par [llength $par]-1]"
4737 if {$custom_command ne ""} {
4738 set parameters [
concat $parameters $custom_command_options]
4741 if {[
catch {
array set options [
cmdline::getoptions option_list $parameters $usage]} err]} {
4742 Msg Status "\nERROR: Syntax error, probably unknown option.\n\n USAGE: $err"
4746 if {[
llength $arg_list] <= $min_n_of_args || [
llength $arg_list] > $max_n_of_args} {
4747 Msg Status "\nERROR: Wrong number of arguments: [
llength $argv]"
4752 set project [
lindex $arg_list 1]
4754 if {$argument_is_no_project == 0} {
4756 regsub "^(\./)?Top/" $project "" project
4758 regsub "/? *\$" $project "" project
4764 Msg Debug "Option list:"
4765 foreach {key value} [
array get options] {
4766 Msg Debug "$key => $value"
4773 if {$proj_conf != 0} {
4776 lassign [
GetIDECommand $proj_conf] cmd before_tcl_script after_tcl_script end_marker
4777 Msg Info "Project $project uses $cmd IDE"
4780 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4782 if {$custom_command ne ""} {
4783 if { [dict exists $custom_commands $directive IDE] } {
4784 lassign [
GetIDECommand "" [dict get $custom_commands $directive IDE]] cmd before_tcl_script after_tcl_script end_marker
4785 Msg Info "Custom command: $custom_command uses $cmd IDE"
4786 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4788 set command "custom_tcl"
4790 }
elseif {$argument_is_no_project == 1} {
4792 Msg Debug "$project will be used as first argument"
4793 }
elseif {$project != ""} {
4796 }
elseif {$min_n_of_args < 0} {
4809 set project_group [
file dirname $project]
4810 set project_name $project
4811 set project [
file tail $project]
4812 Msg Debug "InitLauncher: project_group=$project_group, project_name=$project_name, project=$project"
4814 return [list $directive $project $project_name $project_group $repo_path $old_path $bin_path $top_path $usage $short_usage $command $cmd [
array get options]]
4821 proc IsCommitAncestor {ancestor commit} {
4822 lassign [
GitRet "merge-base --is-ancestor $ancestor $commit"] status result
4831 return [
expr {[info commands sys_install] != ""}]
4836 return [
expr {[info commands get_libero_version] != ""}]
4845 proc IsInList {element list {regex 0} {nocase 0}} {
4849 if {[regexp -nocase $x $element]} {
4853 if {[regexp $x $element]} {
4857 }
elseif {$regex == 0} {
4859 if {[
string tolower $x] eq [
string tolower $element]} {
4863 if {$x eq $element} {
4876 return [
expr {[string first PlanAhead [version]] == 0}]
4884 if {[
catch {
package require ::quartus::flow} result]} {
4897 proc IsRelativePath {path} {
4898 if {[
string index $path 0] == "/" || [
string index $path 0] == "~"} {
4906 proc IsSynplify {} {
4907 return [
expr {[info commands program_version] != ""}]
4912 return [
expr {![IsQuartus] && ![IsXilinx] && ![IsVitisClassic] && ![IsVitisUnified] && ![IsLibero] && ![IsSynplify] && ![IsDiamond]}]
4920 proc IsVerilog {file} {
4921 if {[
file extension $file] == ".v" || [
file extension $file] == ".sv"} {
4933 proc IsVersal {part} {
4934 if {[get_property ARCHITECTURE [get_parts $part]] eq "versal"} {
4944 return [
expr {[string first Vivado [version]] == 0}]
4952 if {[
info commands version] != ""} {
4953 set current_version [version]
4954 if {[
string first PlanAhead $current_version] == 0 || [
string first Vivado $current_version] == 0} {
4956 }
elseif {[
string first xsct $current_version] == 0} {
4959 Msg Warning "This IDE has the version command but it is not PlanAhead or Vivado: $current_version"
4968 proc IsVitisClassic {} {
4969 if {[
info exists globalSettings::vitis_classic]} {
4970 return $globalSettings::vitis_classic
4972 return [
expr {[info commands platform] != ""}]
4976 proc IsVitisUnified {} {
4977 if {[
info exists globalSettings::vitis_unified]} {
4978 return $globalSettings::vitis_unified
4992 proc ExecuteVitisUnifiedCommand {python_script command args {error_prefix "Failed to execute command"} {output_var ""}} {
4993 set cmdlist [list vitis -s $python_script $command]
4995 lappend cmdlist $arg
4997 lappend cmdlist 2>@1
4999 Msg Debug "Executing: vitis -s $python_script $command $args"
5002 set env(PYTHONUNBUFFERED) "1"
5005 if {[
catch {
set pipe [open "|$cmdlist" "r"]} err]} {
5006 Msg Error "$error_prefix: Failed to open pipe: $err"
5010 fconfigure $pipe -buffering line
5011 set script_output ""
5012 set vitis_version ""
5015 set vitis_banner_patterns {
5016 "*Vitis Development Environment*"
5019 "*Copyright*Xilinx*"
5020 "*Copyright*Advanced Micro Devices*"
5021 "*All Rights Reserved*"
5025 while {[
gets $pipe line] >= 0} {
5027 if {[
string match "*Vitis v*" $line]} {
5028 if {[regexp {Vitis\s+v([0-9]+)\.([0-9]+)(?:\.[0-9]+)?} $line -> major minor]} {
5029 set year_last_two [
string range $major end-1 end]
5030 set vitis_version "$year_last_two.$minor"
5036 foreach pattern $vitis_banner_patterns {
5037 if {[
string match $pattern $line]} {
5043 if {![
string match "INFO:*" $line] && ![
string match "WARNING:*" $line] && ![
string match "ERROR:*" $line] && ![
string match "DEBUG:*" $line]} {
5044 if {$vitis_version ne ""} {
5045 set line "INFO: \[Vitis_v$vitis_version\] $line"
5047 set line "INFO: $line"
5051 append script_output "$line\n"
5058 if {[
catch {
close $pipe} err]} {
5059 if {[regexp {exit (\d+)} $err -> exit_code]} {
5060 if {$exit_code != 0} {
5061 Msg Error "$error_prefix (exit code: $exit_code)"
5062 if {$output_var ne ""} {
5063 upvar $output_var output
5064 set output $script_output
5069 Msg Error "$error_prefix: $err"
5070 if {$output_var ne ""} {
5071 upvar $output_var output
5072 set output $script_output
5079 if {$output_var ne ""} {
5080 upvar $output_var output
5081 set output $script_output
5092 proc IsZynq {part} {
5093 if {[regexp {^(xc7z|xczu).*} $part]} {
5100 proc ImportGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
5101 set list_path "$repo_path/Top/$project_name/list"
5102 lassign [
GetHogFiles -list_files {.src,.ext,.sim} -ext_path $ext_path $list_path $repo_path] src_files properties filesets
5107 set properties [
DictGet $simset_dict "properties"]
5108 set options [
DictGet $properties "options"]
5111 set workdir Projects/$project_name/ghdl
5112 file delete -force $workdir
5114 set import_log "$workdir/ghdl-import-${simset_name}.log"
5115 dict for {lib sources} $src_files {
5116 set libname [file rootname $lib]
5117 foreach f $sources {
5118 if {[file extension $f] != ".vhd" && [file extension $f] != ".vhdl"} {
5119 Msg Info "File $f is not a VHDL file, copying it in workfolder..."
5120 file copy -force $f $workdir
5122 set file_path [Relative $repo_path $f]
5123 set import_log_file [open $import_log "a"]
5124 puts "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
5125 puts $import_log_file "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
5126 close $import_log_file
5127 lassign [GHDL "-i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path" $import_log] ret result
5129 Msg CriticalWarning "GHDL import failed for file $f: $result"
5138 proc LaunchGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
5141 set sim_props [
DictGet $simset_dict "properties"]
5142 set options [
DictGet $sim_props "options"]
5143 set runopts [
DictGet $sim_props "run_options"]
5145 dict for {prop_name prop_val} $sim_props {
5146 set prop_name [string toupper $prop_name]
5147 if {$prop_name == "TOP"} {
5148 set top_sim $prop_val
5151 set workdir $repo_path/Projects/$project_name/ghdl
5152 set make_log "$workdir/ghdl-make-${simset_name}.log"
5153 set run_log "$workdir/ghdl-run-${simset_name}.log"
5156 set make_log_file [open $make_log "w"]
5158 puts "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
5159 puts $make_log_file "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
5160 close $make_log_file
5162 lassign [
GHDL "-m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim" $make_log] ret result
5165 Msg Error "GHDL make failed for $top_sim: $result"
5169 set run_log_file [open $run_log "w"]
5170 puts "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
5171 puts $run_log_file "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
5174 lassign [
GHDL "-r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts" $run_log] ret result
5178 Msg Error "GHDL run failed for $top_sim: $result"
5193 proc LaunchImplementation {reset do_create run_folder project_name {repo_path .} {njobs 4} {do_bitstream 0}} {
5194 Msg Info "Starting implementation flow..."
5197 Msg Info "Resetting run before launching implementation..."
5202 source $repo_path/Hog/Tcl/integrated/pre-implementation.tcl
5205 if {$do_bitstream == 1} {
5206 launch_runs impl_1 -to_step [
BinaryStepName [get_property PART [current_project]]] -jobs $njobs -dir $run_folder
5208 launch_runs impl_1 -jobs $njobs -dir $run_folder
5213 Msg Info "running post-implementation"
5214 source $repo_path/Hog/Tcl/integrated/post-implementation.tcl
5215 if {$do_bitstream == 1} {
5216 Msg Info "running pre-bitstream"
5217 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5218 Msg Info "running post-bitstream"
5219 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5223 set prog [get_property PROGRESS [get_runs impl_1]]
5224 set status [get_property STATUS [get_runs impl_1]]
5225 Msg Info "Run: impl_1 progress: $prog, status : $status"
5229 set status_file [open "$run_folder/timing.txt" "w"]
5230 puts $status_file "## $project_name Timing summary"
5232 set f [open [
lindex [glob "$run_folder/impl_1/*.twr" 0]]]
5234 while {[
gets $f line] >= 0} {
5235 if {[
string match "Timing summary:" $line]} {
5236 while {[
gets $f line] >= 0} {
5237 if {[
string match "Timing errors:*" $line]} {
5238 set errs [regexp -inline -- {[0-9]+} $line]
5240 if {[
string match "*Footnotes*" $line]} {
5243 puts $status_file "$line"
5252 Msg Info "Time requirements are met"
5253 file rename -force "$run_folder/timing.txt" "$run_folder/timing_ok.txt"
5256 Msg CriticalWarning "Time requirements are NOT met"
5257 file rename -force "$run_folder/timing.txt" "$run_folder/timing_error.txt"
5263 set wns [get_property STATS.WNS [get_runs [current_run]]]
5264 set tns [get_property STATS.TNS [get_runs [current_run]]]
5265 set whs [get_property STATS.WHS [get_runs [current_run]]]
5266 set ths [get_property STATS.THS [get_runs [current_run]]]
5267 set tpws [get_property STATS.TPWS [get_runs [current_run]]]
5269 if {$wns >= 0 && $whs >= 0 && $tpws >= 0} {
5270 Msg Info "Time requirements are met"
5271 set status_file [open "$run_folder/timing_ok.txt" "w"]
5274 Msg CriticalWarning "Time requirements are NOT met"
5275 set status_file [open "$run_folder/timing_error.txt" "w"]
5279 Msg Status "*** Timing summary ***"
5280 Msg Status "WNS: $wns"
5281 Msg Status "TNS: $tns"
5282 Msg Status "WHS: $whs"
5283 Msg Status "THS: $ths"
5284 Msg Status "TPWS: $tpws"
5290 puts $status_file "## $project_name Timing summary"
5292 m add row "| **Parameter** | \"**value (ns)**\" |"
5293 m add row "| --- | --- |"
5294 m add row "| WNS: | $wns |"
5295 m add row "| TNS: | $tns |"
5296 m add row "| WHS: | $whs |"
5297 m add row "| THS: | $ths |"
5298 m add row "| TPWS: | $tpws |"
5300 puts $status_file [m format 2string]
5301 puts $status_file "\n"
5302 if {$timing_ok == 1} {
5303 puts $status_file " Time requirements are met."
5305 puts $status_file "Time requirements are **NOT** met."
5307 puts $status_file "\n\n"
5311 if {$prog ne "100%"} {
5312 Msg Error "Implementation error"
5317 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5318 Msg Info "Git describe set to $describe"
5320 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5325 if {[
file exists $run_folder/versions.txt]} {
5326 file copy -force $run_folder/versions.txt $dst_dir
5328 Msg Warning "No versions file found in $run_folder/versions.txt"
5331 set timing_files [glob -nocomplain "$run_folder/timing_*.txt"]
5332 set timing_file [
file normalize [
lindex $timing_files 0]]
5334 if {[
file exists $timing_file]} {
5335 file copy -force $timing_file $dst_dir/
5337 Msg Warning "No timing file found, not a problem if running locally"
5341 if {[
IsVersal [get_property part [current_project]]]} {
5342 if {[get_property segmented_configuration [current_project]] == 1} {
5343 Msg Info "Versal Segmented configuration detected: exporting XSA file..."
5344 set xsa_name "$dst_dir/[
file tail $project_name]\-$describe.xsa"
5345 write_hw_platform -fixed -force -file $xsa_name
5349 set revision [get_current_revision]
5351 if {[
catch {execute_module -tool fit} result]} {
5352 Msg Error "Result: $result\n"
5353 Msg Error "Place & Route failed. See the report file.\n"
5355 Msg Info "\nINFO: Place & Route was successful for revision $revision.\n"
5358 if {[
catch {execute_module -tool sta -args "--do_report_timing"} result]} {
5359 Msg Error "Result: $result\n"
5360 Msg Error "Time Quest failed. See the report file.\n"
5362 Msg Info "Time Quest was successfully run for revision $revision.\n"
5365 set panel "Timing Analyzer||Timing Analyzer Summary"
5366 set device [get_report_panel_data -name $panel -col 1 -row_name "Device Name"]
5367 set timing_model [get_report_panel_data -name $panel -col 1 -row_name "Timing Models"]
5368 set delay_model [get_report_panel_data -name $panel -col 1 -row_name "Delay Model"]
5370 Msg Info "*******************************************************************"
5371 Msg Info "Device: $device"
5372 Msg Info "Timing Models: $timing_model"
5373 Msg Info "Delay Model: $delay_model"
5376 Msg Info "*******************************************************************"
5379 Msg Info "Starting implementation flow..."
5380 if {[
catch {run_tool -name {PLACEROUTE}}]} {
5381 Msg Error "PLACEROUTE FAILED!"
5383 Msg Info "PLACEROUTE PASSED."
5387 Msg Info "Run VERIFYTIMING ..."
5388 if {[
catch {run_tool -name {VERIFYTIMING} -script {Hog/Tcl/integrated/libero_timing.tcl}}]} {
5389 Msg CriticalWarning "VERIFYTIMING FAILED!"
5391 Msg Info "VERIFYTIMING PASSED \n"
5397 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5398 Msg Info "Git describe set to $describe"
5400 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5401 file mkdir $dst_dir/reports
5404 if {[
file exists $run_folder/versions.txt]} {
5405 file copy -force $run_folder/versions.txt $dst_dir
5407 Msg Warning "No versions file found in $run_folder/versions.txt"
5410 set timing_file_path [
file normalize "$repo_path/Projects/timing_libero.txt"]
5411 if {[
file exists $timing_file_path]} {
5412 file copy -force $timing_file_path $dst_dir/reports/Timing.txt
5413 set timing_file [open $timing_file_path "r"]
5414 set status_file [open "$dst_dir/timing.txt" "w"]
5415 puts $status_file "## $project_name Timing summary\n\n"
5416 puts $status_file "| | |"
5417 puts $status_file "| --- | --- |"
5418 while {[
gets $timing_file line] >= 0} {
5419 if {[
string match "SUMMARY" $line]} {
5420 while {[
gets $timing_file line] >= 0} {
5421 if {[
string match "END SUMMARY" $line]} {
5424 if {[
string first ":" $line] == -1} {
5427 set out_string "| [
string map {: | } $line] |"
5428 puts $status_file "$out_string"
5433 Msg Warning "No timing file found, not a problem if running locally"
5438 set force_rst "-forceOne"
5440 prj_run Map $force_rst
5441 prj_run PAR $force_rst
5453 proc GenerateBitstreamOnly {project_name {repo_path .}} {
5457 set project_file [
file normalize "$repo_path/Projects/$project_name/$project_name.xpr"]
5458 if {![
file exists $project_file]} {
5459 Msg Error "Project file not found: $project_file. Please create the project first."
5466 set impl_runs [get_runs -quiet impl_1]
5467 if {[
llength $impl_runs] == 0} {
5468 Msg Error "Implementation run 'impl_1' does not exist. Please run implementation first."
5472 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5473 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
5475 cd Projects/$project_name/$project_name.runs/impl_1
5476 Msg Info "Running pre-bitstream..."
5477 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
5479 Msg Info "Writing bitstream for $project_name..."
5481 write_bitstream -force $dst_dir/$project_name-$describe.bit
5483 Msg Info "Running post-bitstream..."
5484 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
5493 proc LaunchSimulation {project_name lib_path simsets {repo_path .} {scripts_only 0} {compile_only 0}} {
5496 set project [
file tail $project_name]
5497 set main_sim_folder [
file normalize "$repo_path/Projects/$project_name/$project.sim/"]
5499 if {$simsets != ""} {
5500 dict for {simset sim_dict} $simsets {
5501 lappend simsets_todo $simset
5503 Msg Info "Will run only the following simulation's sets (if they exist): $simsets_todo"
5506 if {$scripts_only == 1} {
5507 Msg Info "Only generating simulation scripts, not running simulations..."
5510 if {$compile_only == 1} {
5511 Msg Info "Only compiling simulation libraries, not running simulations..."
5516 set sim_dic [dict create]
5518 Msg Info "Retrieving list of simulation sets..."
5519 foreach s [get_filesets] {
5521 set use_simpass_str 0
5524 set type [get_property FILESET_TYPE $s]
5525 if {$type eq "SimulationSrcs"} {
5526 if {$simsets_todo != "" && $s ni $simsets_todo} {
5527 Msg Info "Skipping $s as it was not specified with the -simset option..."
5530 set sim_dict [
DictGet $simsets $s]
5531 set simulator [
DictGet $sim_dict "simulator"]
5532 set_property "target_simulator" $simulator [current_project]
5533 set hog_sim_props [
DictGet $sim_dict "hog"]
5534 dict for {prop_name prop_val} $hog_sim_props {
5535 # If HOG_SIMPASS_STR is set, use the HOG_SIMPASS_STR string to search for in logs, after simulation is done
5536 if {[string toupper $prop_name] == "HOG_SIMPASS_STR" && $prop_val != ""} {
5537 Msg Info "Setting simulation pass string as '$prop_val'"
5538 set use_simpass_str 1
5539 set simpass_str $prop_val
5541 if {[string toupper $prop_name] == "HOG_SILENT_SIM" && $prop_val == 1} {
5542 set quiet_sim " -quiet"
5548 Msg Info "Creating simulation scripts for $s..."
5549 if {[
file exists $repo_path/Top/$project_name/pre-simulation.tcl]} {
5550 Msg Info "Running $repo_path/Top/$project_name/pre-simulation.tcl"
5551 source $repo_path/Top/$project_name/pre-simulation.tcl
5553 if {[
file exists $repo_path/Top/$project_name/pre-$s-simulation.tcl]} {
5554 Msg Info "Running $repo_path/Top/$project_name/pre-$s-simulation.tcl"
5555 source Running $repo_path/Top/$project_name/pre-$s-simulation.tcl
5557 current_fileset -simset $s
5558 set sim_dir $main_sim_folder/$s/behav
5559 set sim_output_logfile $sim_dir/xsim/simulate.log
5560 if {([
string tolower $simulator] eq "xsim")} {
5561 set sim_name "xsim:$s"
5563 set simulation_command "launch_simulation $quiet_sim -simset [get_filesets $s]"
5564 if {[
catch $simulation_command log]} {
5567 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5568 lappend failed $sim_name
5573 if {$use_simpass_str == 1} {
5576 set file_desc [open $sim_output_logfile r]
5577 set log [read $file_desc]
5580 Msg Info "Searching for simulation pass string: '$simpass_str'"
5581 if {[
string first $simpass_str $log] == -1} {
5582 Msg CriticalWarning "Simulation failed for $s, error info: '$simpass_str' NOT found!"
5583 lappend failed $sim_name
5586 lappend success $sim_name
5590 lappend success $sim_name
5594 Msg Info "Simulation library path is set to $lib_path."
5596 if {!([
file exists $lib_path])} {
5597 Msg Warning "Could not find simulation library path: $lib_path, $simulator simulation will not work."
5601 if {$simlib_ok == 1} {
5602 set_property "compxlib.${simulator}_compiled_library_dir" [
file normalize $lib_path] [current_project]
5603 launch_simulation -scripts_only -simset [get_filesets $s]
5604 set top_name [get_property TOP $s]
5605 set sim_script [
file normalize $sim_dir/$simulator/]
5606 Msg Info "Adding simulation script location $sim_script for $s..."
5607 lappend sim_scripts $sim_script
5608 dict append sim_dic $sim_script $s
5610 Msg Error "Cannot run $simulator simulations without a valid library path"
5617 if {[
info exists sim_scripts] && $scripts_only == 0} {
5619 Msg Info "Generating IP simulation targets, if any..."
5621 foreach ip [get_ips] {
5622 generate_target simulation -quiet $ip
5627 Msg Info "====== Starting simulations runs ======"
5630 foreach s $sim_scripts {
5632 set cmd ./compile.sh
5633 Msg Info " ************* Compiling: $s ************* "
5635 set sim_name "comp:[dict get $sim_dic $s]"
5637 Msg CriticalWarning "Compilation failed for $s, error info: $::errorInfo"
5638 lappend failed $sim_name
5640 lappend success $sim_name
5642 Msg Info "###################### Compilation log starts ######################"
5643 Msg Info "\n\n$log\n\n"
5644 Msg Info "###################### Compilation log ends ######################"
5646 if {$compile_only == 1} {
5649 if {[
file exists "./elaborate.sh"] } {
5650 set cmd ./elaborate.sh
5651 Msg Info " ************* Elaborating: $s ************* "
5653 set sim_name "elab:[dict get $sim_dic $s]"
5655 Msg CriticalWarning "Elaboration failed for $s, error info: $::errorInfo"
5656 lappend failed $sim_name
5658 lappend success $sim_name
5660 Msg Info "###################### Elaboration log starts ######################"
5661 Msg Info "\n\n$log\n\n"
5662 Msg Info "###################### Elaboration log ends ######################"
5664 set cmd ./simulate.sh
5665 Msg Info " ************* Simulating: $s ************* "
5670 if {$use_simpass_str == 1} {
5671 if {[
string first $simpass_str $log] == -1} {
5675 Msg Debug "Simulation pass string not set, relying on simulator exit code."
5679 set sim_name "sim:[dict get $sim_dic $s]"
5681 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
5682 lappend failed $sim_name
5684 lappend success $sim_name
5686 Msg Info "###################### Simulation log starts ######################"
5687 Msg Info "\n\n$log\n\n"
5688 Msg Info "###################### Simulation log ends ######################"
5693 if {[
llength $success] > 0} {
5694 set successes [
join $success "\n"]
5695 Msg Info "The following simulation sets were successful:\n\n$successes\n\n"
5698 if {[
llength $failed] > 0} {
5699 set failures [
join $failed "\n"]
5700 Msg Error "The following simulation sets have failed:\n\n$failures\n\n"
5702 }
elseif {[
llength $success] > 0} {
5703 Msg Info "All the [
llength $success] compilations, elaborations and simulations were successful."
5706 Msg Info "Simulation done."
5708 Msg Warning "Simulation is not yet supported for [
GetIDEName]."
5715 proc LaunchRTLAnalysis {repo_path {pre_rtl_file ""} {post_rtl_file ""}} {
5717 if {[
file exists $pre_rtl_file]} {
5718 Msg Info "Found pre-rtl Tcl script $pre_rtl_file, executing it..."
5719 source $pre_rtl_file
5721 Msg Info "Starting RTL Analysis..."
5723 synth_design -rtl -name rtl_1
5724 if {[
file exists $post_rtl_file]} {
5725 Msg Info "Found post-rtl Tcl script $post_rtl_file, executing it..."
5726 source $post_rtl_file
5729 Msg Warning "RTL Analysis is not yet supported for [
GetIDEName]."
5742 proc LaunchSynthesis {reset do_create run_folder project_name {repo_path .} {ext_path ""} {njobs 4}} {
5745 Msg Info "Resetting run before launching synthesis..."
5749 source $repo_path/Hog/Tcl/integrated/pre-synthesis.tcl
5751 launch_runs synth_1 -jobs $njobs -dir $run_folder
5753 set prog [get_property PROGRESS [get_runs synth_1]]
5754 set status [get_property STATUS [get_runs synth_1]]
5755 Msg Info "Run: synth_1 progress: $prog, status : $status"
5762 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5763 Msg Info "Git describe set to $describe"
5766 set xci_file [get_property IP_FILE $ip]
5768 set xci_path [
file dirname $xci_file]
5769 set xci_ip_name [
file rootname [
file tail $xci_file]]
5770 foreach rptfile [glob -nocomplain -directory $xci_path *.rpt] {
5771 file copy $rptfile $repo_path/bin/$project_name-$describe/reports
5775 if {$prog ne "100%"} {
5776 Msg Error "Synthesis error, status is: $status"
5780 set project [
file tail [
file rootname $project_name]]
5782 Msg Info "Number of jobs set to $njobs."
5783 set_global_assignment -name NUM_PARALLEL_PROCESSORS $njobs
5787 set describe [
GetHogDescribe [
file normalize $repo_path/Top/$project_name] $repo_path]
5789 set revision [get_current_revision]
5792 set tool_and_command [
split [get_global_assignment -name PRE_FLOW_SCRIPT_FILE] ":"]
5793 set tool [
lindex $tool_and_command 0]
5794 set pre_flow_script [
lindex $tool_and_command 1]
5795 set cmd "$tool -t $pre_flow_script quartus_map $project $revision"
5801 Msg Warning "Can not execute command $cmd"
5802 Msg Warning "LOG: $log"
5804 Msg Info "Pre flow script executed!"
5808 if {![is_project_open]} {
5809 Msg Info "Re-opening project file $project_name..."
5810 project_open $project -current_revision
5814 if {[
catch {execute_module -tool ipg -args "--clean"} result]} {
5815 Msg Error "Result: $result\n"
5816 Msg Error "IP Generation failed. See the report file.\n"
5818 Msg Info "IP Generation was successful for revision $revision.\n"
5822 if {[
catch {execute_module -tool map -args "--parallel"} result]} {
5823 Msg Error "Result: $result\n"
5824 Msg Error "Analysis & Synthesis failed. See the report file.\n"
5826 Msg Info "Analysis & Synthesis was successful for revision $revision.\n"
5830 defvar_set -name RWNETLIST_32_64_MIXED_FLOW -value 0
5832 Msg Info "Run SYNTHESIS..."
5833 if {[
catch {run_tool -name {SYNTHESIZE}}]} {
5834 Msg Error "SYNTHESIZE FAILED!"
5836 Msg Info "SYNTHESIZE PASSED!"
5842 set force_rst "-forceOne"
5844 prj_run Synthesis $force_rst
5845 if {[prj_syn] == "synplify"} {
5846 prj_run Translate $force_rst
5849 Msg Error "Impossible condition. You need to run this in an IDE."
5860 proc LaunchVitisBuild {project_name {repo_path .} {stage "presynth"}} {
5861 set proj_name $project_name
5862 set bin_dir [
file normalize "$repo_path/bin"]
5868 set vitis_workspace [
file normalize "$repo_path/Projects/$project_name/vitis_unified"]
5869 set python_script [
file normalize "$repo_path/Hog/Other/Python/VitisUnified/AppCommands.py"]
5871 if {![
ExecuteVitisUnifiedCommand $python_script "app_list" [list $vitis_workspace] "Failed to get app list from Vitis Unified" json_output]} {
5872 Msg Error "Failed to get app list from Vitis Unified"
5875 if {[
catch {
package require json}]} {
5876 Msg Error "JSON package not available for parsing Vitis Unified app list"
5879 set json_output_filtered ""
5880 if {[regexp -lineanchor {\{.*\}} $json_output json_output_filtered]} {
5881 set ws_apps [json::json2dict $json_output_filtered]
5883 set ws_apps [json::json2dict $json_output]
5888 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
5890 Msg Error "Impossible condition. You need to run this in a Vitis Unified or Vitis Classic IDE."
5895 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$proj_name] $repo_path] commit version hog_hash hog_ver top_hash top_ver \
5896 libs hashes vers cons_ver cons_hash ext_names ext_hashes xml_hash xml_ver user_ip_repos user_ip_hashes user_ip_vers
5898 if {$commit == 0 } {
set commit $this_commit}
5905 foreach app_name [dict keys $ws_apps] {
5906 app config -name $app_name -set build-config Release
5910 WriteGenerics "vitisbuild" $repo_path $proj_name $date $timee $commit $version $top_hash $top_ver $hog_hash $hog_ver $cons_ver $cons_hash $libs \
5911 $vers $hashes $ext_names $ext_hashes $user_ip_repos $user_ip_vers $user_ip_hashes $flavour $xml_ver $xml_hash
5914 foreach app_name [dict keys $ws_apps] {
5917 if {![
ExecuteVitisUnifiedCommand $python_script "build_app" [list $app_name $vitis_workspace] "Failed to build app $app_name"]} {
5918 Msg Error "Failed to build app $app_name"
5922 app build -name $app_name
5926 if {$stage == "presynth"} {
5927 Msg Info "Done building apps for $project_name..."
5930 if {[
info exists ::globalSettings::vitis_only_pass] && $::globalSettings::vitis_only_pass == 1} {
5931 Msg Info "Skipping bin directory creation in vitis_only mode (post-bitstream.tcl handles artifacts)."
5935 Msg Info "Evaluating Hog describe for $project_name..."
5936 set describe [
GetHogDescribe [
file normalize ./Top/$project_name] $repo_path]
5937 Msg Info "Hog describe set to: $describe"
5938 set dst_dir [
file normalize "$bin_dir/$proj_name\-$describe"]
5939 if {![
file exists $dst_dir]} {
5940 Msg Info "Creating $dst_dir..."
5944 foreach app_name [dict keys $ws_apps] {
5946 set main_file "$repo_path/Projects/$project_name/vitis_unified/$app_name/build/$app_name.elf"
5948 set main_file "$repo_path/Projects/$project_name/vitis_classic/$app_name/Release/$app_name.elf"
5950 set dst_main [
file normalize "$dst_dir/[
file tail $proj_name]\-$app_name\-$describe.elf"]
5952 if {![
file exists $main_file]} {
5953 Msg Error "No Vitis .elf file found. Perhaps there was an issue building it?"
5957 Msg Info "Copying main binary file $main_file into $dst_main..."
5958 file copy -force $main_file $dst_main
5967 proc GetProcFromProps {repo_path props platform} {
5968 if {[dict exists $props "platform:$platform" "BIF"]} {
5969 set bif_file [dict get $props "platform:$platform" "BIF"]
5971 set bif_file "$repo_path/$bif_file"
5975 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
5985 proc GetBifFromProps {repo_path props platform} {
5986 if {[dict exists $props "platform:$platform" "BIF"]} {
5987 set bif_file [dict get $props "platform:$platform" "BIF"]
5989 set bif_file "$repo_path/$bif_file"
5993 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
6002 proc GetPartFromProps {props} {
6003 if {[dict exists $props "main" "PART"]} {
6004 return [
string tolower [dict get $props "main" "PART"]]
6006 Msg Error "Part number not found in properties"
6015 proc GetArchFromPart {part} {
6017 if {[
string match "xczu*" $part]} {
6019 }
elseif {[
string match "xc7z*" $part]} {
6021 }
elseif {[
string match "xck26*" $part]} {
6024 Msg CriticalWarning "Unknown part number: $part"
6033 proc GetAppsFromProps {props {list_names 0}} {
6034 set prop_apps [dict filter $props key {app:*}]
6035 set apps [dict create]
6036 set app_names [list]
6038 dict for {app_key app_value} $prop_apps {
6039 if {[regexp {^app:(.+)$} $app_key -> app_name]} {
6040 set app_name [string trim [string tolower $app_name]]
6041 # Convert only the keys of the inner dictionary to lowercase
6042 set app_value_lower [dict create]
6043 dict for {key value} $app_value {
6044 dict set app_value_lower [string tolower $key] $value
6046 dict set apps $app_name $app_value_lower
6047 lappend app_names $app_name
6050 if {$list_names eq 1} {
6061 proc GetPlatformsFromProps {props {list_names 0} {lower_case 0}} {
6062 set platforms [dict create]
6063 set platform_names [list]
6064 set prop_platforms [dict filter $props key {platform:*}]
6066 dict for {platform_key platform_value} $prop_platforms {
6067 if {[regexp {^platform:(.+)$} $platform_key -> platform_name]} {
6068 if {$lower_case == 1} {
6069 set platform_name [string trim [string tolower $platform_name]]
6071 set platform_name [string trim $platform_name]
6073 dict set platforms $platform_name $platform_value
6074 lappend platform_names $platform_name
6077 if {$list_names eq 1} {
6078 return $platform_names
6093 proc GenerateBootArtifacts {properties repo_path proj_dir bin_dir proj_name describe bitfile mmi_file} {
6094 set elf_list [glob -nocomplain "$bin_dir/*.elf"]
6098 if {[
llength $elf_list] == 0} {
6099 Msg Warning "No ELF files found in $bin_dir, skipping generation of boot artifacts"
6103 if {![
file exists $bitfile]} {
6104 Msg Warning "Bitfile $bitfile does not exist, skipping generation of boot artifacts"
6108 Msg Info "Generating boot artifacts for $proj_name..."
6109 Msg Info "Found apps: $apps"
6110 Msg Info "Found platforms: $platforms"
6114 foreach elf_file $elf_list {
6115 set elf_name [
file rootname [
file tail $elf_file]]
6116 Msg Info "Found elf name: $elf_name"
6117 Msg Info "Removing $describe from elf"
6120 if {[regexp "^(.+)-(.+)-$describe\$" $elf_name -> project_name elf_app]} {
6121 set elf_app [
string trim [
string tolower $elf_app]]
6122 Msg Info "Found elf_app: $elf_app"
6124 Msg Error "Could not extract app name from elf file: $elf_name"
6127 Msg Info "Removed project name ($project_name) and $describe from elf"
6129 set app_conf [dict get $apps $elf_app]
6130 set plat [dict get $app_conf "platform"]
6131 set app_proc [dict get $app_conf "proc"]
6134 if {[regexp -nocase {microblaze|risc} $app_proc]} {
6135 Msg Info "Detected soft processor ($app_proc) for $elf_app, updating bitstream memory with ELF file..."
6138 if {[dict size $proc_map] == 0} {
6139 Msg Error "Failed to read map from $proc_map_file"
6142 Msg Info "Found processor map: $proc_map"
6144 set proc_cell [
lindex [
split [dict get $proc_map $app_proc] ":"] 1]
6145 Msg Info "Updating memory at processor cell: $proc_cell"
6147 set update_mem_cmd "updatemem -force -meminfo $mmi_file -data $elf_file -bit $bitfile -proc $proc_cell -out $bitfile"
6148 set ret [
catch {
exec -ignorestderr {*}$update_mem_cmd >@ stdout} result]
6150 Msg Error "Error updating memory for $elf_app: $result"
6152 Msg Info "Done updating memory for $elf_app"
6155 Msg Info "Detected hard processor ($app_proc) for $elf_app. Make sure the .elf file is defined in the platform ($plat)\
6156 .bif file to be included in the bootable binary image (.bin) generation."
6162 foreach plat $platforms {
6164 if {$bif_file != ""} {
6165 Msg Info "Generating bootable binary image (.bin) for $plat"
6167 Msg Info "Architecture: $arch"
6168 Msg Info "BIF file: $bif_file"
6169 set bootgen_cmd "bootgen -arch $arch -image $bif_file -o i $bin_dir/$proj_name-$plat-$describe.bin -w on"
6170 set ret [
catch {
exec -ignorestderr {*}$bootgen_cmd >@ stdout} result]
6172 Msg Error "Error generating bootable binary image (.bin) for $elf_app: $result"
6174 Msg Info "Done generating bootable binary image (.bin) for $plat"
6183 proc ReadProcMap {proc_map_file} {
6184 set proc_map [dict create]
6185 if {[
file exists $proc_map_file]} {
6186 set f [open $proc_map_file "r"]
6187 while {[
gets $f line] >= 0} {
6188 Msg Debug "Line: $line"
6189 if {[regexp {^(\S+)\s+(.+)$} $line -> key value]} {
6190 Msg Debug "Found key: $key, value: $value in proc map file"
6191 dict set proc_map $key $value
6205 proc ListProjects {{repo_path .} {print 1} {ret_conf 0}} {
6206 set top_path [
file normalize $repo_path/Top]
6207 set confs [
findFiles [
file normalize $top_path] hog.conf]
6209 set confs [lsort $confs]
6213 set p [
Relative $top_path [
file dirname $c]]
6216 if {$description eq "test"} {
6217 set description " - Test project"
6218 }
elseif {$description ne ""} {
6219 set description " - $description"
6222 if {$print == 1 || $description ne " - Test project"} {
6224 set g [
file dirname $p]
6235 if {$ret_conf == 0} {
6247 proc Md5Sum {file_name} {
6248 if {!([
file exists $file_name])} {
6249 Msg Warning "Could not find $file_name."
6252 if {[
catch {
package require md5 2.0.7} result]} {
6253 Msg Warning "Tcl package md5 version 2.0.7 not found ($result), will use command line..."
6254 set hash [
lindex [
Execute md5sum $file_name] 0]
6256 set file_hash [
string tolower [md5::md5 -hex -file $file_name]]
6270 proc MergeDict {dict0 dict1 {remove_duplicates 1}} {
6271 set outdict [dict merge $dict1 $dict0]
6272 foreach key [dict keys $dict1] {
6273 if {[dict exists $dict0 $key]} {
6274 set temp_list [dict get $dict1 $key]
6275 foreach item $temp_list {
6277 if {[
IsInList $item [
DictGet $outdict $key]] == 0 || $remove_duplicates == 0} {
6279 dict lappend outdict $key $item
6291 proc MoveElementToEnd {inputList element} {
6292 set index [lsearch $inputList $element]
6294 set inputList [
lreplace $inputList $index $index]
6295 lappend inputList $element
6304 proc OpenProject {project_file repo_path} {
6306 open_project $project_file
6308 set project_folder [
file dirname $project_file]
6309 set project [
file tail [
file rootname $project_file]]
6310 if {[
file exists $project_folder]} {
6312 if {![is_project_open]} {
6313 Msg Info "Opening existing project file $project_file..."
6314 project_open $project -current_revision
6317 Msg Error "Project directory not found for $project_file."
6321 Msg Info "Opening existing project file $project_file..."
6323 open_project -file $project_file -do_backup_on_convert 1 -backup_file {./Projects/$project_file.zip}
6325 Msg Info "Opening existing project file $project_file..."
6326 prj_project open $project_file
6328 Msg Error "This IDE is currently not supported by Hog. Exiting!"
6335 return $tcl_platform(platform)
6345 proc ParseJSON {JSON_FILE JSON_KEY} {
6346 set result [
catch {
package require Tcl 8.4} TclFound]
6347 if {"$result" != "0"} {
6348 Msg CriticalWarning "Cannot find Tcl package version equal or higher than 8.4.\n $TclFound\n Exiting"
6352 set result [
catch {
package require json} JsonFound]
6353 if {"$result" != "0"} {
6354 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
6357 set JsonDict [json::json2dict $JSON_FILE]
6358 set result [
catch {dict get $JsonDict $JSON_KEY} RETURNVALUE]
6359 if {"$result" != "0"} {
6360 Msg CriticalWarning "Cannot find $JSON_KEY in $JSON_FILE\n Exiting"
6373 proc ProjectExists {project {repo_path .}} {
6374 set index [lsearch -exact [
ListProjects $repo_path 0] $project]
6380 Msg Error "Project $project not found in repository $repo_path"
6391 proc ReadConf {file_name} {
6392 if {[
catch {
package require inifile 0.2.3} ERROR]} {
6393 Msg Error "Could not find inifile package version 0.2.3 or higher.\n
6394 To use ghdl, libero or diamond with Hog, you need to install the tcllib package\n
6395 You can install it with 'sudo apt install tcllib' on Debian/Ubuntu or 'sudo dnf install tcllib' on Fedora/RedHat/CentOs."
6399 ::ini::commentchar "#"
6400 set f [::ini::open $file_name]
6401 set properties [dict create]
6402 foreach sec [::ini::sections $f] {
6404 if {$new_sec == "files"} {
6407 set key_pairs [::ini::get $f $sec]
6409 regsub -all {\{\"} $key_pairs "\{" key_pairs
6410 regsub -all {\"\}} $key_pairs "\}" key_pairs
6412 dict set properties $new_sec [dict create {*}$key_pairs]
6425 proc ReadExtraFileList {extra_file_name} {
6426 set extra_file_dict [dict create]
6427 if {[
file exists $extra_file_name]} {
6428 set file [open $extra_file_name "r"]
6429 set file_data [read $file]
6432 set data [
split $file_data "\n"]
6433 foreach line $data {
6434 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
6435 set ip_and_md5 [regexp -all -inline {\S+} $line]
6436 dict lappend extra_file_dict "[
lindex $ip_and_md5 0]" "[
lindex $ip_and_md5 1]"
6440 return $extra_file_dict
6460 proc ReadListFile {args} {
6463 if {[
catch {
package require cmdline} ERROR]} {
6464 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
6470 {lib.arg "" "The name of the library files will be added to, if not given will be extracted from the file name."}
6471 {fileset.arg "" "The name of the library, from the main list file"}
6472 {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."}
6473 {print_log "If set, will use PrintFileTree for the VIEW directive"}
6474 {indent.arg "" "Used to indent files with the VIEW directive"}
6477 set usage "USAGE: ReadListFile \[options\] <list file> <path>"
6478 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
6484 set list_file [
lindex $args 0]
6485 set path [
lindex $args 1]
6486 set sha_mode $options(sha_mode)
6487 set lib $options(lib)
6488 set fileset $options(fileset)
6489 set print_log $options(print_log)
6490 set indent $options(indent)
6492 if {$sha_mode == 1} {
6493 set sha_mode_opt "-sha_mode"
6498 if {$print_log == 1} {
6499 set print_log_opt "-print_log"
6501 set print_log_opt ""
6506 set lib [
file rootname [
file tail $list_file]]
6508 set fp [open $list_file r]
6509 set file_data [read $fp]
6511 set list_file_ext [
file extension $list_file]
6512 switch $list_file_ext {
6514 if {$fileset eq ""} {
6520 set fileset "constrs_1"
6523 set fileset "sources_1"
6527 set libraries [dict create]
6528 set filesets [dict create]
6529 set properties [dict create]
6531 set data [
split $file_data "\n"]
6533 set n [
llength $data]
6535 if {$print_log == 1} {
6536 if {$indent eq ""} {
6537 set list_file_rel [
file tail $list_file]
6538 Msg Status "\n$list_file_rel"
6542 Msg Debug "$n lines read from $list_file."
6545 foreach line $data {
6547 if {![regexp {^[\t\s]*$} $line] & ![regexp {^[\t\s]*\#} $line]} {
6548 set file_and_prop [regexp -all -inline {\S+} $line]
6549 set srcfile [
lindex $file_and_prop 0]
6550 set srcfile "$path/$srcfile"
6552 set srcfiles [glob -nocomplain $srcfile]
6555 if {$srcfiles != $srcfile && ![
string equal $srcfiles ""]} {
6556 Msg Debug "Wildcard source expanded from $srcfile to $srcfiles"
6558 if {![
file exists $srcfile]} {
6559 if {$print_log == 0} {
6560 Msg CriticalWarning "File: $srcfile (from list file: $list_file) does not exist."
6566 foreach vhdlfile $srcfiles {
6567 if {[
file exists $vhdlfile]} {
6568 set vhdlfile [
file normalize $vhdlfile]
6569 set extension [
file extension $vhdlfile]
6571 set prop [
lrange $file_and_prop 1 end]
6574 set library [
lindex [regexp -inline {\ylib\s*=\s*(.+?)\y.*} $prop] 1]
6575 if {$library == ""} {
6579 if {$extension == $list_file_ext} {
6582 set ref_path [
lindex [regexp -inline {\ypath\s*=\s*(\S+).*} $prop] 1]
6583 if {$ref_path eq ""} {
6586 set ref_path [
file normalize $path/$ref_path]
6588 Msg Debug "List file $vhdlfile found in list file, recursively opening it using path \"$ref_path\"..."
6589 if {$print_log == 1} {
6590 if {[
file normalize $last_printed] ne [
file normalize $vhdlfile]} {
6591 Msg Status "$indent Inside [
file tail $vhdlfile]:"
6595 lassign [
ReadListFile {*}"-indent \" $indent\" -lib $library -fileset $fileset $sha_mode_opt $print_log_opt $vhdlfile $ref_path"] l p fs
6597 set properties [
MergeDict $p $properties]
6599 }
elseif {[lsearch {.src .sim .con ReadExtraFileList} $extension] >= 0} {
6601 Msg Error "$vhdlfile cannot be included into $list_file, $extension files must be included into $extension files."
6604 regsub -all " *= *" $prop "=" prop
6608 if {[
string first "lib=" $p] == -1} {
6610 set pos [
string first "=" $p]
6614 set prop_name [
string range $p 0 [
expr {$pos - 1}]]
6617 dict lappend properties $vhdlfile $p
6618 Msg Debug "Adding property $p to $vhdlfile..."
6619 }
elseif {$list_file_ext != ".ipb"} {
6620 Msg Warning "Setting Property $p is not supported for file $vhdlfile or it is already its default. \
6625 if {[lsearch {.xci .ip .bd .xcix} $extension] >= 0} {
6627 set lib_name "ips.src"
6628 }
elseif {[
IsInList $extension {.vhd .vhdl}] || $list_file_ext == ".sim"} {
6630 if {![
IsInList $extension {.vhd .vhdl}]} {
6631 set lib_name "others.sim"
6633 set lib_name "$library$list_file_ext"
6635 }
elseif {$list_file_ext == ".con"} {
6636 set lib_name "sources.con"
6637 }
elseif {$list_file_ext == ".ipb"} {
6638 set lib_name "xml.ipb"
6639 }
elseif { [
IsInList $list_file_ext {.src}] && [
IsInList $extension {.c .cpp .h .hpp}] } {
6641 set lib_name "$library$list_file_ext"
6644 set lib_name "others.src"
6647 Msg Debug "Appending $vhdlfile to $lib_name list..."
6648 dict lappend libraries $lib_name $vhdlfile
6649 if {$sha_mode != 0 && [
file type $vhdlfile] eq "link"} {
6652 dict lappend libraries $lib_name $real_file
6653 Msg Debug "File $vhdlfile is a soft link, also adding the real file: $real_file"
6658 if {[dict exists $filesets $fileset] == 0} {
6660 Msg Debug "Adding $fileset to the fileset dictionary..."
6661 Msg Debug "Adding library $lib_name to fileset $fileset..."
6662 dict set filesets $fileset $lib_name
6666 Msg Debug "Adding library $lib_name to fileset $fileset..."
6667 dict lappend filesets $fileset $lib_name
6673 Msg CriticalWarning "File $vhdlfile not found."
6679 if {$sha_mode != 0} {
6681 if {$list_file_ext eq ".ipb"} {
6682 set sha_lib "xml.ipb"
6684 set sha_lib $lib$list_file_ext
6686 dict lappend libraries $sha_lib [
file normalize $list_file]
6687 if {[
file type $list_file] eq "link"} {
6690 dict lappend libraries $lib$list_file_ext $real_file
6691 Msg Debug "List file $list_file is a soft link, also adding the real file: $real_file"
6694 return [list $libraries $properties $filesets]
6703 proc Relative {base dst {quiet 0}} {
6704 if {![
string equal [
file pathtype $base] [
file pathtype $dst]]} {
6706 Msg CriticalWarning "Unable to compute relation for paths of different path types: [
file pathtype $base] vs. [
file pathtype $dst], ($base vs. $dst)"
6711 set base [
file normalize [
file join [
pwd] $base]]
6712 set dst [
file normalize [
file join [
pwd] $dst]]
6715 set base [
file split $base]
6716 set dst [
file split $dst]
6718 while {[
string equal [
lindex $dst 0] [
lindex $base 0]]} {
6719 set dst [
lrange $dst 1 end]
6720 set base [
lrange $base 1 end]
6721 if {![
llength $dst]} {break}
6724 set dstlen [
llength $dst]
6725 set baselen [
llength $base]
6727 if {($dstlen == 0) && ($baselen == 0)} {
6730 while {$baselen > 0} {
6731 set dst [
linsert $dst 0 ..]
6734 set dst [
eval [
linsert $dst 0 file join]]
6745 proc RelativeLocal {pathName filePath} {
6746 if {[
string first [
file normalize $pathName] [
file normalize $filePath]] != -1} {
6747 return [
Relative $pathName $filePath]
6758 proc RemoveDuplicates {mydict} {
6759 set new_dict [dict create]
6760 foreach key [dict keys $mydict] {
6761 set values [
DictGet $mydict $key]
6762 foreach value $values {
6763 set idxs [
lreverse [
lreplace [lsearch -exact -all $values $value] 0 0]]
6765 set values [
lreplace $values $idx $idx]
6768 dict set new_dict $key $values
6779 proc ResetRepoFiles {reset_file} {
6780 if {[
file exists $reset_file]} {
6781 Msg Info "Found $reset_file, opening it..."
6782 set fp [open $reset_file r]
6783 set wild_cards [lsearch -all -inline -not -regexp [
split [read $fp] "\n"] "^ *$"]
6785 Msg Info "Found the following files/wild cards to restore if modified: $wild_cards..."
6786 foreach w $wild_cards {
6788 if {[
llength $mod_files] > 0} {
6789 Msg Info "Found modified $w files: $mod_files, will restore them..."
6792 Msg Info "No modified $w files found."
6803 proc RestoreModifiedFiles {{repo_path "."} {pattern "."}} {
6806 set ret [
Git checkout $pattern]
6817 proc SearchHogProjects {dir} {
6818 set projects_list {}
6819 if {[
file exists $dir]} {
6820 if {[
file isdirectory $dir]} {
6821 foreach proj_dir [glob -nocomplain -types d $dir/*] {
6822 if {![regexp {^.*Top/+(.*)$} $proj_dir dummy proj_name]} {
6823 Msg Warning "Could not parse Top directory $dir"
6826 if {[
file exists "$proj_dir/hog.conf"]} {
6827 lappend projects_list $proj_name
6830 lappend projects_list $p
6835 Msg Error "Input $dir is not a directory!"
6838 Msg Error "Directory $dir doesn't exist!"
6840 return $projects_list
6849 proc SetGenericsSimulation {repo_path proj_dir target} {
6850 set top_dir "$repo_path/Top/$proj_dir"
6851 set simsets [get_filesets]
6852 if {$simsets != ""} {
6853 foreach simset $simsets {
6855 if {[get_property FILESET_TYPE $simset] != "SimulationSrcs"} {
6859 set merged_generics_dict [dict create]
6863 set simset_generics [
DictGet $simset_dict "generics"]
6864 set merged_generics_dict [
MergeDict $merged_generics_dict $simset_generics 0]
6867 Msg Debug "TOP = [get_property top [get_filesets sources_1]]"
6868 Msg Debug "GENERICS = [get_property generic [get_filesets sources_1]]"
6870 set_property generic $generic_str [get_filesets $simset]
6871 Msg Info "Setting generics $generic_str for simulator $target\
6872 and simulation file-set $simset..."
6884 proc SetTopProperty {top_module fileset} {
6885 Msg Info "Setting TOP property to $top_module module"
6888 set_property "top" $top_module [get_filesets $fileset]
6891 set_global_assignment -name TOP_LEVEL_ENTITY $top_module
6893 set_root -module $top_module
6895 prj_impl option top $top_module
6900 proc VIVADO_PATH_PROPERTIES {} {
6901 return {"\.*\.TCL\.PRE$" "^.*\.TCL\.POST$" "^RQS_FILES$" "^INCREMENTAL\_CHECKPOINT$" "NOC\_SOLUTION\_FILE"}
6905 proc VITIS_PATH_PROPERTIES {} {
6906 return {"^HW$" "^XPFM$" "^LINKER-SCRIPT$" "^LIBRARIES$" "^LIBRARY-SEARCH-PATH$"}
6916 proc WriteConf {file_name config {comment ""}} {
6917 if {[
catch {
package require inifile 0.2.3} ERROR]} {
6918 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
6922 ::ini::commentchar "#"
6923 set f [::ini::open $file_name w]
6925 foreach sec [dict keys $config] {
6926 set section [dict get $config $sec]
6927 dict for {p v} $section {
6928 if {[string trim $v] == ""} {
6929 Msg Warning "Property $p has empty value. Skipping..."
6932 ::ini::set $f $sec $p $v
6937 if {![
string equal "$comment" ""]} {
6938 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $comment
6939 set hog_header "Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
6940 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $hog_header
6955 proc WriteGenerics {mode repo_path design date timee\
6956 commit version top_hash top_ver hog_hash hog_ver \
6957 cons_ver cons_hash libs vers hashes ext_names ext_hashes \
6958 user_ip_repos user_ip_vers user_ip_hashes flavour {xml_ver ""} {xml_hash ""}} {
6959 Msg Info "Passing parameters/generics to project's top module..."
6962 set generic_string [
concat \
6975 if {$xml_hash != "" && $xml_ver != ""} {
6976 lappend generic_string \
6981 foreach l $libs v $vers h $hashes {
6987 set ver [regsub -all {[\.-]} $ver {_}]
6988 set hash [regsub -all {[\.-]} $hash {_}]
6989 lappend generic_string "$ver" "$hash"
6992 foreach e $ext_names h $ext_hashes {
6994 lappend generic_string "$hash"
6997 foreach repo $user_ip_repos v $user_ip_vers h $user_ip_hashes {
6998 set repo_name [
file tail $repo]
6999 set ver "[
string toupper $repo_name]_VER=[
FormatGeneric $v]"
7000 set hash "[
string toupper $repo_name]_SHA=[
FormatGeneric $h]"
7001 set ver [regsub -all {[\.-]} $ver {_}]
7002 set hash [regsub -all {[\.-]} $hash {_}]
7003 lappend generic_string "$ver" "$hash"
7006 if {$flavour != -1} {
7007 lappend generic_string "FLAVOUR=$flavour"
7013 set generic_string "$prj_generics $generic_string"
7019 if {$mode == "create" || [
IsISE]} {
7022 if {[
file exists $top_file]} {
7025 Msg Debug "Found top level generics $generics in $top_file"
7027 set filtered_generic_string ""
7029 foreach generic_to_set [
split [
string trim $generic_string]] {
7030 set key [
lindex [
split $generic_to_set "="] 0]
7031 if {[dict exists $generics $key]} {
7032 Msg Debug "Hog generic $key found in $top_name"
7033 lappend filtered_generic_string "$generic_to_set"
7035 Msg Warning "Generic $key is passed by Hog but is NOT present in $top_name."
7041 set generic_string $filtered_generic_string
7046 set_property generic $generic_string [current_fileset]
7047 Msg Info "Setting parameters/generics..."
7048 Msg Debug "Detailed parameters/generics: $generic_string"
7053 set simulator [get_property target_simulator [current_project]]
7054 if {$mode == "create"} {
7061 Msg Info "Setting Synplify parameters/generics one by one..."
7062 foreach generic $generic_string {
7063 Msg Debug "Setting Synplify generic: $generic"
7064 set_option -hdl_param -set "$generic"
7067 Msg Info "Setting Diamond parameters/generics one by one..."
7068 prj_impl option -impl Implementation0 HDL_PARAM "$generic_string"
7070 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
7072 foreach app_name [dict keys $ws_apps] {
7073 set defined_symbols [app config -name $app_name -get define-compiler-symbols]
7074 foreach generic_to_set [
split [
string trim $generic_string]] {
7075 set key [
lindex [
split $generic_to_set "="] 0]
7076 set value [
lindex [
split $generic_to_set "="] 1]
7077 if {[
string match "32'h*" $value]} {
7078 set value [
string map {"32'h" "0x"} $value]
7081 foreach symbol [
split $defined_symbols ";"] {
7082 if {[
string match "$key=*" $symbol]} {
7083 Msg Debug "Generic $key found in $app_name, removing it..."
7084 app config -name $app_name -remove define-compiler-symbols "$symbol"
7088 Msg Info "Setting Vitis parameters/generics for app $app_name: $key=$value"
7089 app config -name $app_name define-compiler-symbols "$key=$value"
7101 proc WriteGenericsToBdIPs {mode repo_path proj generic_string} {
7102 Msg Debug "Parameters/generics passed to WriteGenericsToIP: $generic_string"
7104 set bd_ip_generics false
7106 if {[dict exists $properties "hog"]} {
7107 set propDict [dict get $properties "hog"]
7108 if {[dict exists $propDict "PASS_GENERICS_TO_BD_IPS"]} {
7109 set bd_ip_generics [dict get $propDict "PASS_GENERICS_TO_BD_IPS"]
7113 if {[
string compare [
string tolower $bd_ip_generics] "false"] == 0} {
7117 if {$mode == "synth"} {
7118 Msg Info "Attempting to apply generics pre-synthesis..."
7119 set PARENT_PRJ [get_property "PARENT.PROJECT_PATH" [current_project]]
7120 set workaround [open "$repo_path/Projects/$proj/.hog/presynth_workaround.tcl" "w"]
7121 puts $workaround "source \[lindex \$argv 0\];"
7122 puts $workaround "open_project \[lindex \$argv 1\];"
7123 puts $workaround "WriteGenericsToBdIPs \[lindex \$argv 2\] \[lindex \$argv 3\] \[lindex \$argv 4\] \[lindex \$argv 5\];"
7124 puts $workaround "close_project"
7128 exec vivado -mode batch -source $repo_path/Projects/$proj/.hog/presynth_workaround.tcl \
7129 -tclargs $repo_path/Hog/Tcl/hog.tcl $PARENT_PRJ \
7130 "childprocess" $repo_path $proj $generic_string
7133 Msg Error "Encountered an error while attempting workaround: $errMsg"
7135 file delete $repo_path/Projects/$proj/.hog/presynth_workaround.tcl
7137 Msg Info "Done applying generics pre-synthesis."
7141 Msg Info "Looking for IPs to add generics to..."
7142 set ips_generic_string ""
7143 foreach generic_to_set [
split [
string trim $generic_string]] {
7144 set key [
lindex [
split $generic_to_set "="] 0]
7145 set value [
lindex [
split $generic_to_set "="] 1]
7146 append ips_generic_string "CONFIG.$key $value "
7150 if {[
string compare [
string tolower $bd_ip_generics] "true"] == 0} {
7153 set ip_regex $bd_ip_generics
7156 set ip_list [get_ips -regex $ip_regex]
7157 Msg Debug "IPs found with regex \{$ip_regex\}: $ip_list"
7159 set regen_targets {}
7161 foreach {ip} $ip_list {
7162 set WARN_ABOUT_IP false
7163 set ip_props [list_property [get_ips $ip]]
7166 if {[lsearch -exact $ip_props "IS_BD_CONTEXT"] == -1} {
7170 if {[get_property "IS_BD_CONTEXT" [get_ips $ip]] eq "1"} {
7171 foreach {ip_prop} $ip_props {
7172 if {[dict exists $ips_generic_string $ip_prop]} {
7173 if {$WARN_ABOUT_IP == false} {
7174 lappend regen_targets [get_property SCOPE [get_ips $ip]]
7175 Msg Warning "The ip \{$ip\} contains generics that are set by Hog.\
7176 If this is IP is apart of a block design, the .bd file may contain stale, unused, values.\
7177 Hog will always apply the most up-to-date values to the IP during synthesis,\
7178 however these values may or may not be reflected in the .bd file."
7179 set WARN_ABOUT_IP true
7184 set xci_path [get_property IP_FILE [get_ips $ip]]
7186 if {[
string equal $generic_format "ERROR"]} {
7187 Msg Warning "Could not find format for generic $ip_prop in IP $ip. Skipping..."
7191 set value_to_set [dict get $ips_generic_string $ip_prop]
7192 switch -exact $generic_format {
7194 if {[
string match "32'h*" $value_to_set]} {
7195 scan [
string map {"32'h" ""} $value_to_set] "%x" value_to_set
7199 set value_to_set [
expr {$value_to_set ? "true" : "false"}]
7202 if {[
string match "32'h*" $value_to_set]} {
7203 binary scan [
binary format H* [
string map {"32'h" ""} $value_to_set]] d value_to_set
7207 if {[
string match "32'h*" $value_to_set]} {
7208 set value_to_set [
string map {"32'h" "0x"} $value_to_set]
7212 set value_to_set [
format "%s" $value_to_set]
7215 Msg Warning "Unknown generic format $generic_format for IP $ip. Will attempt to pass as string..."
7220 Msg Info "The IP \{$ip\} contains: $ip_prop ($generic_format), setting it to $value_to_set."
7221 if {[
catch {set_property -name $ip_prop -value $value_to_set -objects [get_ips $ip]} prop_error]} {
7222 Msg CriticalWarning "Failed to set property $ip_prop to $value_to_set for IP \{$ip\}: $prop_error"
7229 foreach {regen_target} [lsort -unique $regen_targets] {
7230 Msg Info "Regenerating target: $regen_target"
7231 if {[
catch {generate_target -force all [get_files $regen_target]} prop_error]} {
7232 Msg CriticalWarning "Failed to regen targets: $prop_error"
7240 proc GetGenericFormatFromXciXML {generic_name xml_file} {
7241 if {![
file exists $xml_file]} {
7242 Msg Error "Could not find XML file: $xml_file"
7246 set fp [open $xml_file r]
7247 set xci_data [read $fp]
7250 set paramType "string"
7251 set modelparam_regex [
format {^.*\y%s\y.*$} [
string map {"CONFIG." "MODELPARAM_VALUE."} $generic_name]]
7252 set format_regex {format="([^"]+)"}
7254 set line [
lindex [regexp -inline -line $modelparam_regex $xci_data] 0]
7255 Msg Debug "line: $line"
7257 if {[regexp $format_regex $line match format_value]} {
7258 Msg Debug "Extracted: $format_value format from xml"
7259 set paramType $format_value
7261 Msg Debug "No format found, using string"
7270 proc GetGenericFormatFromXci {generic_name xci_file} {
7271 if {![
file exists $xci_file]} {
7272 Msg Error "Could not find XCI file: $xci_file"
7276 set fp [open $xci_file r]
7277 set xci_data [read $fp]
7280 set paramType "string"
7281 if {[
string first "xilinx.com:schema:json_instance:1.0" $xci_data] == -1} {
7282 Msg Debug "XCI format is not JSON, trying XML..."
7283 set xml_file "[
file rootname $xci_file].xml"
7287 set generic_name [
string map {"CONFIG." ""} $generic_name]
7288 set ip_inst [
ParseJSON $xci_data "ip_inst"]
7289 set parameters [dict get $ip_inst parameters]
7290 set component_parameters [dict get $parameters component_parameters]
7291 if {[dict exists $component_parameters $generic_name]} {
7292 set generic_info [dict get $component_parameters $generic_name]
7293 if {[dict exists [
lindex $generic_info 0] format]} {
7294 set paramType [dict get [
lindex $generic_info 0] format]
7295 Msg Debug "Extracted: $paramType format from xci"
7298 Msg Debug "No format found, using string"
7311 proc WriteGitLabCIYAML {proj_name {ci_conf ""}} {
7312 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
7313 Msg CriticalWarning "Cannot find package YAML.\n Error message: $YAMLPACKAGE. \
7314 If you are running on tclsh, you can fix this by installing package \"tcllib\""
7319 if {$ci_conf != ""} {
7321 foreach sec [dict keys $ci_confs] {
7322 if {[
string first : $sec] == -1} {
7323 lappend job_list $sec
7327 set job_list {"generate_project" "simulate_project"}
7331 set out_yaml [huddle create]
7332 foreach job $job_list {
7334 set huddle_tags [huddle list]
7336 set sec_dict [dict create]
7338 if {$ci_confs != ""} {
7339 foreach var [dict keys [dict get $ci_confs $job]] {
7340 if {$var == "tags"} {
7341 set tag_section "tags"
7342 set tags [dict get [dict get $ci_confs $job] $var]
7343 set tags [
split $tags ","]
7345 set tag_list [huddle list $tag]
7346 set huddle_tags [huddle combine $huddle_tags $tag_list]
7349 dict set sec_dict $var [dict get [dict get $ci_confs $job] $var]
7355 set huddle_variables [huddle create "PROJECT_NAME" $proj_name "extends" ".vars"]
7356 if {[dict exists $ci_confs "$job:variables"]} {
7357 set var_dict [dict get $ci_confs $job:variables]
7358 foreach var [dict keys $var_dict] {
7360 set value [dict get $var_dict "$var"]
7361 set var_inner [huddle create "$var" "$value"]
7362 set huddle_variables [huddle combine $huddle_variables $var_inner]
7367 set middle [huddle create "extends" ".$job" "variables" $huddle_variables]
7368 foreach sec [dict keys $sec_dict] {
7369 set value [dict get $sec_dict $sec]
7370 set var_inner [huddle create "$sec" "$value"]
7371 set middle [huddle combine $middle $var_inner]
7373 if {$tag_section != ""} {
7374 set middle2 [huddle create "$tag_section" $huddle_tags]
7375 set middle [huddle combine $middle $middle2]
7378 set outer [huddle create "$job:$proj_name" $middle]
7379 set out_yaml [huddle combine $out_yaml $outer]
7382 return [
string trimleft [yaml::huddle2yaml $out_yaml] "-"]
7392 proc WriteListFiles {libs props list_path repo_path {ext_path ""}} {
7394 foreach lib [dict keys $libs] {
7395 if {[
llength [
DictGet $libs $lib]] > 0} {
7396 set list_file_name $list_path$lib
7397 set list_file [open $list_file_name w]
7398 Msg Info "Writing $list_file_name..."
7399 puts $list_file "#Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
7400 foreach file [
DictGet $libs $lib] {
7402 set prop [
DictGet $props $file]
7406 puts $list_file "$file_path $prop"
7409 set ext_list_file [open "[
file rootname $list_file].ext" a]
7410 puts $ext_list_file "$file_path $prop"
7411 close $ext_list_file
7414 Msg Warning "The path of file $file is not relative to your repository. Please check!"
7430 proc WriteSimListFile {simset libs props simsets list_path repo_path {force 0}} {
7432 set list_file_name $list_path/${simset}.sim
7433 if {$force == 0 && [
file exists $list_file_name]} {
7434 Msg Info "List file $list_file_name already exists, skipping..."
7438 set list_file [open $list_file_name a+]
7441 puts $list_file "\[files\]"
7442 Msg Info "Writing $list_file_name..."
7443 foreach lib [
DictGet $simsets $simset] {
7444 foreach file [
DictGet $libs $lib] {
7446 set prop [
DictGet $props $file]
7450 set lib_name [
file rootname $lib]
7451 if {$lib_name != $simset && [
file extension $file] == ".vhd" && [
file extension $file] == ""} {
7452 lappend prop "lib=$lib_name"
7454 puts $list_file "$file_path $prop"
7457 Msg Warning "The path of file $file is not relative to your repository. Please check!"
7469 proc WriteToFile {File msg} {
7470 set f [open $File a+]
7481 proc WriteUtilizationSummary {input output project_name run} {
7482 set f [open $input "r"]
7483 set o [open $output "a"]
7484 puts $o "## $project_name $run Utilization report\n\n"
7485 struct::matrix util_m
7486 util_m add columns 14
7489 util_m add row "| **Site Type** | **Used** | **Fixed** | **Prohibited** | **Available** | **Util%** |"
7490 util_m add row "| --- | --- | --- | --- | --- | --- |"
7492 util_m add row "| **Site Type** | **Used** | **Fixed** | **Available** | **Util%** |"
7493 util_m add row "| --- | --- | --- | --- | --- |"
7503 while {[
gets $f line] >= 0} {
7504 if {([
string first "| CLB LUTs" $line] >= 0 || [
string first "| Slice LUTs" $line] >= 0) && $luts == 0} {
7505 util_m add row $line
7508 if {([
string first "| CLB Registers" $line] >= 0 || [
string first "| Slice Registers" $line] >= 0) && $regs == 0} {
7509 util_m add row $line
7512 if {[
string first "| Block RAM Tile" $line] >= 0 && $bram == 0} {
7513 util_m add row $line
7516 if {[
string first "URAM " $line] >= 0 && $uram == 0} {
7517 util_m add row $line
7520 if {[
string first "DSPs" $line] >= 0 && $dsps == 0} {
7521 util_m add row $line
7524 if {[
string first "Bonded IOB" $line] >= 0 && $ios == 0} {
7525 util_m add row $line
7532 puts $o [util_m format 2string]
7538 Msg Error "Found Git version older than 2.7.2. Hog will not work as expected, exiting now."
7548 if {![
catch {
exec curl --silent --show-error --version}]} {
7549 if {![
catch {
exec curl --silent --show-error -I https://gitlab.com}]} {
7550 return [list curl --silent --show-error]
7554 if {![
catch {
exec env -u LD_LIBRARY_PATH curl --silent --show-error --version}]} {
7555 if {![
catch {
exec env -u LD_LIBRARY_PATH curl --silent --show-error -I https://gitlab.com}]} {
7556 return [list env -u LD_LIBRARY_PATH curl --silent --show-error]
7560 error "Cannot find a working curl invocation"