17 if {![
info exists tcl_path]} {
18 set tcl_path [
file normalize "[
file dirname [
info script]]"]
20 source "$tcl_path/utils/Logger.tcl"
24 set CI_STAGES {"generate_project" "simulate_project"}
25 set CI_PROPS {"-synth_only"}
34 proc AddFile {file fileset} {
36 add_files -norecurse -fileset $fileset $file
46 proc AddHogFiles {libraries properties filesets} {
47 Msg Info "Adding source files to project..."
48 Msg Debug "Filesets: $filesets"
49 Msg Debug "Libraries: $libraries"
50 Msg Debug "Properties: $properties"
53 set synth_conf_command "organize_tool_files -tool {SYNTHESIZE} -input_type {constraint}"
55 set timing_conf_command "organize_tool_files -tool {VERIFYTIMING} -input_type {constraint}"
57 set place_conf_command "organize_tool_files -tool {PLACEROUTE} -input_type {constraint}"
61 foreach fileset [dict keys $filesets] {
62 Msg Debug "Fileset: $fileset"
65 if {[
string equal [get_filesets -quiet $fileset] ""]} {
67 create_fileset -simset $fileset
70 current_fileset -simset [get_filesets $fileset]
71 set simulation [get_filesets $fileset]
73 set_property -name {$simulator.compile.vhdl_syntax} -value {2008} -objects $simulation
75 set_property SOURCE_SET sources_1 $simulation
79 set libs_in_fileset [
DictGet $filesets $fileset]
80 if {[
IsInList "ips.src" $libs_in_fileset]} {
87 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
88 dict for {app_name app_config} $ws_apps {
89 set app_lib [string tolower "app_$app_name\.src"]
90 if {![IsInList $app_lib $libs_in_fileset 0 1]} {
91 Msg Warning "App '$app_name' exists in workspace but no corresponding sourcefile '$app_lib' found. \
92 Make sure you have a list file with the correct naming convention: \[app_<app_name>\.src\]"
98 foreach lib $libs_in_fileset {
99 Msg Debug "lib: $lib \n"
100 set lib_files [
DictGet $libraries $lib]
101 Msg Debug "Files in $lib: $lib_files"
102 set rootlib [
file rootname [
file tail $lib]]
103 set ext [
file extension $lib]
104 Msg Debug "lib: $lib ext: $ext fileset: $fileset"
108 if {[
string match "app_*" [
string tolower $lib]]} {
111 Msg Debug "Adding $lib to $fileset"
112 add_files -norecurse -fileset $fileset $lib_files
114 foreach f $lib_files {
115 set file_obj [get_files -of_objects [get_filesets $fileset] [list "*$f"]]
117 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
118 set_property -name "library" -value $rootlib -objects $file_obj
122 set props [
DictGet $properties $f]
123 if {[
file extension $f] == ".vhd" || [
file extension $f] == ".vhdl"} {
125 if {[lsearch -inline -regexp $props "93"] < 0} {
128 if {[lsearch -inline -regexp $props "2008"] >= 0} {
129 set vhdl_year "VHDL 2008"
130 }
elseif {[lsearch -inline -regexp $props "2019"] >= 0} {
132 set vhdl_year "VHDL 2019"
134 Msg CriticalWarning "VHDL 2019 is not supported\
135 in Vivado version older than 2023.2.\
136 Using VHDL 2008, but this might not work."
137 set vhdl_year "VHDL 2008"
141 set vhdl_year "VHDL 2008"
143 Msg Debug "File type for $f is $vhdl_year"
144 set_property -name "file_type" -value $vhdl_year -objects $file_obj
147 Msg Debug "Filetype is VHDL 93 for $f"
152 if {[lsearch -inline -regexp $props "SystemVerilog"] > 0} {
155 set_property -name "file_type" -value "SystemVerilog" -objects $file_obj
156 Msg Debug "Filetype is SystemVerilog for $f"
158 Msg Warning "Xilinx PlanAhead/ISE does not support SystemVerilog.\
159 Property not set for $f"
164 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
166 Msg Info "Setting $top as top module for file set $fileset..."
167 set globalSettings::synth_top_module $top
172 if {[lsearch -inline -regexp $props "verilog_header"] >= 0} {
173 Msg Debug "Setting verilog header type for $f..."
174 set_property file_type {Verilog Header} [get_files $f]
175 }
elseif {[lsearch -inline -regexp $props "verilog_template"] >= 0} {
177 Msg Debug "Setting verilog template type for $f..."
178 set_property file_type {Verilog Template} [get_files $f]
179 }
elseif {[lsearch -inline -regexp $props "verilog"] >= 0} {
181 Msg Debug "Setting verilog type for $f..."
182 set_property file_type {Verilog} [get_files $f]
186 if {[lsearch -inline -regexp $props "nosynth"] >= 0} {
187 Msg Debug "Setting not used in synthesis for $f..."
188 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
192 if {[lsearch -inline -regexp $props "noimpl"] >= 0} {
193 Msg Debug "Setting not used in implementation for $f..."
194 set_property -name "used_in_implementation" -value "false" -objects $file_obj
198 if {[lsearch -inline -regexp $props "nosim"] >= 0} {
199 Msg Debug "Setting not used in simulation for $f..."
200 set_property -name "used_in_simulation" -value "false" -objects $file_obj
205 set top_sim [
lindex [regexp -inline {\ytopsim\s*=\s*(.+?)\y.*} $props] 1]
206 if {$top_sim != ""} {
207 Msg Warning "Setting the simulation top module with the topsim property is deprecated.\
208 Please set this property in the \[properties\] section of your .sim list file,
209 or in the \[$fileset\] section of your sim.conf,\
210 by adding the following line.\ntop=$top_sim"
214 set sim_runtime [
lindex [regexp -inline {\yruntime\s*=\s*(.+?)\y.*} $props] 1]
215 if {$sim_runtime != ""} {
216 Msg Warning "Setting the simulation runtime using the runtime= property is deprecated.\
217 Please set this property in the \[properties\] section of your .sim list file,\
218 or in the \[$fileset\] section of your sim.conf,\
219 by adding the following line.\n<simulator_name>.simulate.runtime=$sim_runtime"
223 if {[lsearch -inline -regexp $props "wavefile"] >= 0} {
224 Msg Warning "Setting a wave do file using the wavefile property is deprecated.\
225 Set this property in the sim.conf file under the \[$fileset\] section,\
226 or in the \[properties\] section of the .sim list file,\
227 by adding the following line .\n<simulator_name>.simulate.custom_wave_do=[
file tail $f]"
231 if {[lsearch -inline -regexp $props "dofile"] >= 0} {
232 Msg Warning "Setting a wave do file using the dofile property is deprecated.\
233 Set this property in the sim.conf file under the \[$fileset\] section,\
234 or in the \[properties\] section of the .sim list file,\
235 by adding the following line .\n<simulator_name>.simulate.custom_do=[
file tail $f]"
239 if {[lsearch -inline -regexp $props "locked"] >= 0 && $ext == ".ip"} {
240 Msg Info "Locking IP $f..."
241 set_property IS_MANAGED 0 [get_files $f]
245 if {[
file extension $f] == ".bd"} {
246 Msg Info "Generating Target for [
file tail $f],\
247 please remember to commit the (possible) changed file."
248 generate_target all [get_files $f]
253 if {[
file extension $f] == ".tcl" && $ext != ".con"} {
254 if {[lsearch -inline -regexp $props "source"] >= 0} {
255 Msg Info "Sourcing Tcl script $f,\
256 and setting it not used in synthesis, implementation and simulation..."
258 set_property -name "used_in_synthesis" -value "false" -objects $file_obj
259 set_property -name "used_in_implementation" -value "false" -objects $file_obj
260 set_property -name "used_in_simulation" -value "false" -objects $file_obj
265 set ref [
lindex [regexp -inline {\yscoped_to_ref\s*=\s*(.+?)\y.*} $props] 1]
266 set cell [
lindex [regexp -inline {\yscoped_to_cells\s*=\s*(.+?)\y.*} $props] 1]
267 if {([
file extension $f] == ".tcl" || [
file extension $f] == ".xdc") && $ext == ".con"} {
269 set_property SCOPED_TO_REF $ref $file_obj
272 set_property SCOPED_TO_CELLS $cell $file_obj
276 Msg Info "[
llength $lib_files] file/s added to library $rootlib..."
279 if {$ext == ".sim"} {
280 Msg Warning "Simulation files not supported in Quartus Prime mode... Skipping $lib"
282 if {![is_project_open]} {
283 Msg Error "Project is closed"
285 foreach cur_file $lib_files {
289 set props [
DictGet $properties $cur_file]
292 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
294 Msg Info "Setting $top as top module for file set $fileset..."
295 set globalSettings::synth_top_module $top
298 if {[
string first "VHDL" $file_type] != -1} {
299 if {[
string first "1987" $props] != -1} {
300 set hdl_version "VHDL_1987"
301 }
elseif {[
string first "1993" $props] != -1} {
302 set hdl_version "VHDL_1993"
303 }
elseif {[
string first "2008" $props] != -1} {
304 set hdl_version "VHDL_2008"
306 set hdl_version "default"
308 if {$hdl_version == "default"} {
309 set_global_assignment -name $file_type $cur_file -library $rootlib
311 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version -library $rootlib
313 }
elseif {[
string first "SYSTEMVERILOG" $file_type] != -1} {
315 if {[
string first "2005" $props] != -1} {
316 set hdl_version "systemverilog_2005"
317 }
elseif {[
string first "2009" $props] != -1} {
318 set hdl_version "systemverilog_2009"
320 set hdl_version "default"
322 if {$hdl_version == "default"} {
323 set_global_assignment -name $file_type $cur_file
325 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
327 }
elseif {[
string first "VERILOG" $file_type] != -1} {
329 if {[
string first "1995" $props] != -1} {
330 set hdl_version "verilog_1995"
331 }
elseif {[
string first "2001" $props] != -1} {
332 set hdl_version "verilog_2001"
334 set hdl_version "default"
336 if {$hdl_version == "default"} {
337 set_global_assignment -name $file_type $cur_file
339 set_global_assignment -name $file_type $cur_file -hdl_version $hdl_version
341 }
elseif {[
string first "SOURCE" $file_type] != -1 || [
string first "COMMAND_MACRO" $file_type] != -1} {
342 set_global_assignment -name $file_type $cur_file
343 if {$ext == ".con"} {
345 }
elseif {$ext == ".src"} {
347 if {[
string first "qsys" $props] != -1} {
350 regsub -all {\{||qsys||\}} $props $emptyString props
352 set qsysPath [
file dirname $cur_file]
353 set qsysName "[
file rootname [
file tail $cur_file]].qsys"
354 set qsysFile "$qsysPath/$qsysName"
355 set qsysLogFile "$qsysPath/[
file rootname [
file tail $cur_file]].qsys-script.log"
358 if {![
info exists ::env(QSYS_ROOTDIR)]} {
359 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
360 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
361 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
363 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
366 set qsys_rootdir $::env(QSYS_ROOTDIR)
369 set cmd "$qsys_rootdir/qsys-script"
370 set cmd_options " --script=$cur_file"
371 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
372 Msg Info "Executing: $cmd $cmd_options"
373 Msg Info "Saving logfile in: $qsysLogFile"
374 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
375 set makeRet [
lindex [dict get $opt -errorcode] end]
376 Msg CriticalWarning "$cmd returned with $makeRet"
379 Msg Error " Could not execute command $cmd"
383 if {[
file exists $qsysName] != 0} {
384 file rename -force $qsysName $qsysFile
386 set qsysMd5Sum [
Md5Sum $qsysFile]
388 set fileDir [
file normalize "./hogTmp"]
389 set fileName "$fileDir/.hogQsys.md5"
390 if {![
file exists $fileDir]} {
393 set hogQsysFile [open $fileName "a"]
394 set fileEntry "$qsysFile\t$qsysMd5Sum"
395 puts $hogQsysFile $fileEntry
398 Msg ERROR "Error while moving the generated qsys file to final location: $qsysName not found!"
400 if {[
file exists $qsysFile] != 0} {
401 if {[
string first "noadd" $props] == -1} {
403 set_global_assignment -name $qsysFileType $qsysFile
405 regsub -all {noadd} $props $emptyString props
407 if {[
string first "nogenerate" $props] == -1} {
411 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
415 }
elseif {[
string first "QSYS" $file_type] != -1} {
417 regsub -all {\{||\}} $props $emptyString props
418 if {[
string first "noadd" $props] == -1} {
419 set_global_assignment -name $file_type $cur_file
421 regsub -all {noadd} $props $emptyString props
425 if {[
string first "nogenerate" $props] == -1} {
429 set_global_assignment -name $file_type $cur_file -library $rootlib
434 if {$ext == ".con"} {
435 set vld_exts {.sdc .pin .dcf .gcf .pdc .ndc .fdc .crt .vcd }
436 foreach con_file $lib_files {
438 set con_ext [
file extension $con_file]
439 if {[
IsInList [
file extension $con_file] $vld_exts]} {
440 set option [
string map {. -} $con_ext]
441 set option [
string map {fdc net_fdc} $option]
442 set option [
string map {pdc io_pdc} $option]
443 create_links -convert_EDN_to_HDL 0 -library {work} $option $con_file
445 set props [
DictGet $properties $con_file]
447 if {$con_ext == ".sdc"} {
448 if {[lsearch $props "notiming"] >= 0} {
449 Msg Info "Excluding $con_file from timing verification..."
451 Msg Info "Adding $con_file to time verification"
452 append timing_conf_command " -file $con_file"
456 if {[lsearch $props "nosynth"] >= 0} {
457 Msg Info "Excluding $con_file from synthesis..."
459 Msg Info "Adding $con_file to synthesis"
460 append synth_conf_command " -file $con_file"
465 if {$con_ext == ".pdc" || $con_ext == ".sdc"} {
466 if {[lsearch $props "noplace"] >= 0} {
467 Msg Info "Excluding $con_file from place and route..."
469 Msg Info "Adding $con_file to place and route"
470 append place_conf_command " -file $con_file"
475 Msg CriticalWarning "Constraint file $con_file does not have a valid extension. Allowed extensions are: \n $vld_exts"
478 }
elseif {$ext == ".src"} {
479 foreach f $lib_files {
480 Msg Debug "Adding source $f to library $rootlib..."
481 create_links -library $rootlib -hdl_source $f
483 }
elseif {$ext == ".sim"} {
484 Msg Debug "Adding stimulus file $f to library..."
485 create_links -library $rootlib -stimulus $f
487 build_design_hierarchy
488 foreach cur_file $lib_files {
492 set props [
DictGet $properties $cur_file]
495 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
497 Msg Info "Setting $top as top module for file set $rootlib..."
498 set globalSettings::synth_top_module "${top}::$rootlib"
503 if {$ext == ".src" || $ext == ".con" || $ext == ".ext"} {
504 foreach f $lib_files {
505 Msg Debug "Diamond: adding source file $f to library $rootlib..."
506 prj_src add -work $rootlib $f
507 set props [
DictGet $properties $f]
509 set top [
lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $props] 1]
511 Msg Info "Setting $top as top module for the project..."
512 set globalSettings::synth_top_module $top
516 if {[lsearch -inline -regexp $props "enable"] >= 0} {
517 Msg Debug "Setting $f as active Logic Preference file"
521 }
elseif {$ext == ".sim"} {
522 foreach f $lib_files {
523 Msg Debug "Diamond Adding simulation file $f to library $rootlib..."
524 prj_src add -work $rootlib -simulate_only $f
532 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
534 foreach app_name [dict keys $ws_apps] {
535 foreach f $lib_files {
536 if {[
string tolower $rootlib] != [
string tolower "app_$app_name"]} {
540 Msg Info "Adding source file $f from lib: $lib to vitis app \[$app_name\]..."
541 set proj_f_path [regsub "^$globalSettings::repo_path" $f ""]
542 set proj_f_path [regsub "[
file tail $f]$" $proj_f_path ""]
543 Msg Debug "Project_f_path is $proj_f_path"
545 importsources -name $app_name -soft-link -path $f -target $proj_f_path
555 if {[
DictGet $filesets "sim_1"] == ""} {
556 delete_fileset -quiet [get_filesets -quiet "sim_1"]
562 if {$synth_conf == 1} {
563 Msg Info $synth_conf_command
564 eval $synth_conf_command
566 if {$timing_conf == 1} {
567 Msg Info $timing_conf_command
568 eval $timing_conf_command
570 if {$place_conf == 1} {
571 Msg Info $place_conf_command
572 eval $place_conf_command
578 proc ALLOWED_PROPS {} {
579 return [dict create ".vhd" [list "93" "nosynth" "noimpl" "nosim" "1987" "1993" "2008" "2019"] \
580 ".vhdl" [list "93" "nosynth" "noimpl" "nosim" "1987" "1993" "2008" "2019"] \
581 ".bd" [list "nosim"] \
582 ".v" [list "SystemVerilog" "verilog_header" "nosynth" "noimpl" "nosim" "1995" "2001"] \
583 ".sv" [list "verilog" "verilog_header" "nosynth" "noimpl" "nosim" "2005" "2009"] \
584 ".do" [list "nosim"] \
585 ".udo" [list "nosim"] \
586 ".xci" [list "nosynth" "noimpl" "nosim" "locked"] \
587 ".xdc" [list "nosynth" "noimpl"] \
588 ".tcl" [list "nosynth" "noimpl" "nosim" "source" "qsys" "noadd"\
589 "--block-symbol-file" "--clear-output-directory" "--example-design"\
590 "--export-qsys-script" "--family" "--greybox" "--ipxact"\
591 "--jvm-max-heap-size" "--parallel" "--part" "--search-path"\
592 "--simulation" "--synthesis" "--testbench" "--testbench-simulation"\
593 "--upgrade-ip-cores" "--upgrade-variation-file"
595 ".qsys" [list "nogenerate" "noadd" "--block-symbol-file" "--clear-output-directory" "--example-design"\
596 "--export-qsys-script" "--family" "--greybox" "--ipxact" "--jvm-max-heap-size" "--parallel"\
597 "--part" "--search-path" "--simulation" "--synthesis" "--testbench" "--testbench-simulation"\
598 "--upgrade-ip-cores" "--upgrade-variation-file"
600 ".sdc" [list "notiming" "nosynth" "noplace"] \
601 ".elf" [list "scoped_to_ref" "scoped_to_cells" "nosim" "noimpl"] \
602 ".pdc" [list "nosynth" "noplace"] \
603 ".lpf" [list "enable"]]
614 proc BinaryStepName {part} {
616 return "WRITE_DEVICE_IMAGE"
620 return "WRITE_BITSTREAM"
629 proc CheckSyntax {project_name repo_path {project_file ""}} {
631 set syntax [check_syntax -return_string]
632 if {[
string first "CRITICAL" $syntax] != -1} {
637 lassign [
GetHogFiles -list_files "*.src" "$repo_path/Top/$project_name/list/" $repo_path] src_files dummy
638 dict for {lib files} $src_files {
640 set file_extension [file extension $f]
641 if {$file_extension == ".vhd" || $file_extension == ".vhdl" || $file_extension == ".v" || $file_extension == ".sv"} {
642 if {[catch {execute_module -tool map -args "--analyze_file=$f"} result]} {
643 Msg Error "\nResult: $result\n"
644 Msg Error "Check syntax failed.\n"
647 Msg Info "Check syntax was successful for $f.\n"
649 Msg Warning "Found syntax error in file $f:\n $result\n"
656 lassign [
GetProjectFiles $project_file] prjLibraries prjProperties prjSimLibraries prjConstraints prjSrcSets prjSimSets prjConSets
657 dict for {lib sources} $prjLibraries {
658 if {[file extension $lib] == ".src"} {
660 Msg Info "Checking Syntax of $f"
666 Msg Info "The Checking Syntax is not supported by this IDE. Skipping..."
671 proc CloseProject {} {
693 proc CompareVersions {ver1 ver2} {
702 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver1 - x y z]} {
703 set ver1 [list $x $y $z]
705 if {[regexp {v(\d+)\.(\d+)\.(\d+)} $ver2 - x y z]} {
706 set ver2 [list $x $y $z]
710 set v1 [
join $ver1 ""]
712 set v2 [
join $ver2 ""]
715 if {[
string is integer $v1] && [
string is integer $v2]} {
716 set ver1 [
expr {[scan [lindex $ver1 0] %d] * 1000000 + [scan [lindex $ver1 1] %d] * 1000 + [scan [lindex $ver1 2] %d]}]
717 set ver2 [
expr {[scan [lindex $ver2 0] %d] * 1000000 + [scan [lindex $ver2 1] %d] * 1000 + [scan [lindex $ver2 2] %d]}]
721 }
elseif {$ver1 == $ver2} {
727 Msg Warning "Version is not numeric: $ver1, $ver2"
737 proc CheckExtraFiles {libraries constraints simlibraries} {
740 lassign [
GetProjectFiles] prjLibraries prjProperties prjSimLibraries prjConstraints
741 set prj_dir [get_property DIRECTORY [current_project]]
742 file mkdir "$prj_dir/.hog"
743 set extra_file_name "$prj_dir/.hog/extra.files"
744 set new_extra_file [open $extra_file_name "w"]
746 dict for {prjLib prjFiles} $prjLibraries {
747 foreach prjFile $prjFiles {
748 if {[file extension $prjFile] == ".xcix"} {
749 Msg Warning "IP $prjFile is packed in a .xcix core container. \
750 This files are not suitable for version control systems. We recommend to use .xci files instead."
753 if {[file extension $prjFile] == ".xci" && [get_property CORE_CONTAINER [get_files $prjFile]] != ""} {
754 Msg Info "$prjFile is a virtual IP file in a core container. Ignoring it..."
758 if {[IsInList $prjFile [DictGet $libraries $prjLib]] == 0} {
759 if {[file extension $prjFile] == ".bd"} {
760 # Generating BD products to save md5sum of already modified BD
761 Msg Info "Generating targets of $prjFile..."
762 generate_target all [get_files $prjFile]
764 puts $new_extra_file "$prjFile [Md5Sum $prjFile]"
765 Msg Info "$prjFile (lib: $prjLib) has been generated by an external script. Adding to $extra_file_name..."
769 close $new_extra_file
770 set extra_sim_file "$prj_dir/.hog/extrasim.files"
771 set new_extra_file [open $extra_sim_file "w"]
773 dict for {prjSimLib prjSimFiles} $prjSimLibraries {
774 foreach prjSimFile $prjSimFiles {
775 if {[IsInList $prjSimFile [DictGet $simlibraries $prjSimLib]] == 0} {
776 puts $new_extra_file "$prjSimFile [Md5Sum $prjSimFile]"
777 Msg Info "$prjSimFile (lib: $prjSimLib) has been generated by an external script. Adding to $extra_sim_file..."
781 close $new_extra_file
782 set extra_con_file "$prj_dir/.hog/extracon.files"
783 set new_extra_file [open $extra_con_file "w"]
785 dict for {prjConLib prjConFiles} $prjConstraints {
786 foreach prjConFile $prjConFiles {
787 if {[IsInList $prjConFile [DictGet $constraints $prjConLib]] == 0} {
788 puts $new_extra_file "$prjConFile [Md5Sum $prjConFile]"
789 Msg Info "$prjConFile has been generated by an external script. Adding to $extra_con_file..."
793 close $new_extra_file
800 proc CheckLatestHogRelease {{repo_path .}} {
803 set current_ver [
Git {describe --always}]
804 Msg Debug "Current version: $current_ver"
805 set current_sha [
Git "log $current_ver -1 --format=format:%H"]
806 Msg Debug "Current SHA: $current_sha"
809 if {[
OS] == "windows"} {
810 Msg Info "On windows we cannot set a timeout on 'git fetch', hopefully nothing will go wrong..."
813 Msg Info "Checking for latest Hog release, can take up to 5 seconds..."
816 set master_ver [
Git "describe origin/master"]
817 Msg Debug "Master version: $master_ver"
818 set master_sha [
Git "log $master_ver -1 --format=format:%H"]
819 Msg Debug "Master SHA: $master_sha"
820 set merge_base [
Git "merge-base $current_sha $master_sha"]
821 Msg Debug "merge base: $merge_base"
824 if {$merge_base != $master_sha} {
826 Msg Info "Version $master_ver has been released (https://gitlab.com/hog-cern/Hog/-/releases/$master_ver)"
827 Msg Status "You should consider updating Hog submodule with the following instructions:"
829 Msg Status "cd Hog && git checkout master && git pull"
831 Msg Status "Also update the ref: in your .gitlab-ci.yml to $master_ver"
835 Msg Info "Latest official version is $master_ver, nothing to do."
847 proc CheckYmlRef {repo_path allow_failure} {
848 if {$allow_failure} {
849 set MSG_TYPE CriticalWarning
854 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
855 Msg CriticalWarning "Cannot find package YAML, skipping consistency check of \"ref\" in gilab-ci.yaml file.\n Error message: $YAMLPACKAGE
856 You can fix this by installing package \"tcllib\""
864 if {[
file exists .gitlab-ci.yml]} {
868 if {[
file exists .gitlab-ci.yml]} {
869 set fp [open ".gitlab-ci.yml" r]
870 set file_data [read $fp]
873 Msg $MSG_TYPE "Cannot open file .gitlab-ci.yml"
877 set file_data "\n$file_data\n\n"
879 if {[
catch {::yaml::yaml2dict -stream $file_data} yamlDict]} {
880 Msg $MSG_TYPE "Parsing $repo_path/.gitlab-ci.yml failed. To fix this, check that yaml syntax is respected, remember not to use tabs."
884 dict for {dictKey dictValue} $yamlDict {
885 #looking for Hog include in .gitlab-ci.yml
886 if {"$dictKey" == "include" && (
887 [lsearch [split $dictValue " {}"] "/hog.yml"] != "-1" ||
888 [lsearch [split $dictValue " {}"] "/hog-dynamic.yml"] != "-1"
890 set YML_REF [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "ref"] + 1}]]
891 set YML_NAME [lindex [split $dictValue " {}"] [expr {[lsearch -dictionary [split $dictValue " {}"] "file"] + 1}]]
895 if {$YML_REF == ""} {
896 Msg Warning "Hog version not specified in the .gitlab-ci.yml. Assuming that master branch is used."
898 set YML_REF_F [
Git {name-rev --tags --name-only origin/master}]
901 set YML_REF_F [regsub -all "'" $YML_REF ""]
904 if {$YML_NAME == ""} {
905 Msg $MSG_TYPE "Hog included yml file not specified, assuming hog.yml"
906 set YML_NAME_F hog.yml
908 set YML_NAME_F [regsub -all "^/" $YML_NAME ""]
911 lappend YML_FILES $YML_NAME_F
917 if {[
catch {::yaml::yaml2dict -file $YML_NAME_F} yamlDict]} {
918 Msg $MSG_TYPE "Parsing $YML_NAME_F failed."
922 dict for {dictKey dictValue} $yamlDict {
923 #looking for included files
924 if {"$dictKey" == "include"} {
925 foreach v $dictValue {
926 lappend YML_FILES [lindex [split $v " "] [expr {[lsearch -dictionary [split $v " "] "local"] + 1}]]
932 Msg Info "Found the following yml files: $YML_FILES"
934 set HOGYML_SHA [
GetSHA $YML_FILES]
935 lassign [
GitRet "log --format=%h -1 --abbrev=7 $YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
937 lassign [
GitRet "log --format=%h -1 --abbrev=7 origin/$YML_REF_F" $YML_FILES] ret EXPECTEDYML_SHA
939 Msg $MSG_TYPE "Error in project .gitlab-ci.yml. ref: $YML_REF not found"
940 set EXPECTEDYML_SHA ""
943 if {!($EXPECTEDYML_SHA eq "")} {
944 if {$HOGYML_SHA == $EXPECTEDYML_SHA} {
945 Msg Info "Hog included file $YML_FILES matches with $YML_REF in .gitlab-ci.yml."
947 Msg $MSG_TYPE "HOG $YML_FILES SHA mismatch.
948 From Hog submodule: $HOGYML_SHA
949 From ref in .gitlab-ci.yml: $EXPECTEDYML_SHA
950 You can fix this in 2 ways: by changing the ref in your repository or by changing the Hog submodule commit"
953 Msg $MSG_TYPE "One or more of the following files could not be found $YML_FILES in Hog at $YML_REF"
956 Msg Info ".gitlab-ci.yml not found in $repo_path. Skipping this step"
977 proc CompareLibDicts {proj_libs list_libs proj_sets list_sets proj_props list_props {severity "CriticalWarning"} {outFile ""} {extraFiles ""}} {
978 set extra_files $extraFiles
980 set out_prjlibs $proj_libs
981 set out_prjprops $proj_props
983 dict for {prjSet prjLibraries} $proj_sets {
984 # Check if sets is also in list files
985 if {[IsInList $prjSet $list_sets]} {
986 set listLibraries [DictGet $list_sets $prjSet]
987 # Loop over libraries in fileset
988 foreach prjLib $prjLibraries {
989 set prjFiles [DictGet $proj_libs $prjLib]
990 # Check if library exists in list files
991 if {[IsInList $prjLib $listLibraries]} {
992 # Loop over files in library
993 set listFiles [DictGet $list_libs $prjLib]
994 foreach prjFile $prjFiles {
995 set idx [lsearch -exact $listFiles $prjFile]
996 set listFiles [lreplace $listFiles $idx $idx]
998 # File is in project but not in list libraries, check if it was generated at creation time...
999 if {[dict exists $extra_files $prjFile]} {
1000 # File was generated at creation time, checking the md5sum
1001 # Removing the file from the prjFiles list
1002 set idx2 [lsearch -exact $prjFiles $prjFile]
1003 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1004 set new_md5sum [Md5Sum $prjFile]
1005 set old_md5sum [DictGet $extra_files $prjFile]
1006 if {$new_md5sum != $old_md5sum} {
1007 # tclint-disable-next-line line-length
1008 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
1011 set extra_files [dict remove $extra_files $prjFile]
1013 # File is neither in list files nor in extra_files
1014 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1018 # File is both in list files and project, checking properties...
1019 set prjProps [DictGet $proj_props $prjFile]
1020 set listProps [DictGet $list_props $prjFile]
1021 # Check if it is a potential sourced file
1022 if {[IsInList "nosynth" $prjProps] && [IsInList "noimpl" $prjProps] && [IsInList "nosim" $prjProps]} {
1023 # Check if it is sourced
1024 set idx_source [lsearch -exact $listProps "source"]
1025 if {$idx_source >= 0} {
1026 # It is sourced, let's replace the individual properties with source
1027 set idx [lsearch -exact $prjProps "noimpl"]
1028 set prjProps [lreplace $prjProps $idx $idx]
1029 set idx [lsearch -exact $prjProps "nosynth"]
1030 set prjProps [lreplace $prjProps $idx $idx]
1031 set idx [lsearch -exact $prjProps "nosim"]
1032 set prjProps [lreplace $prjProps $idx $idx]
1033 lappend prjProps "source"
1037 foreach prjProp $prjProps {
1038 set idx [lsearch -exact $listProps $prjProp]
1039 set listProps [lreplace $listProps $idx $idx]
1041 MsgAndLog "Property $prjProp of $prjFile was set in project but not in list files" $severity $outFile
1046 foreach listProp $listProps {
1047 if {[string first $listProp "topsim="] == -1 && [string first $listProp "enable"] == -1} {
1048 MsgAndLog "Property $listProp of $prjFile was found in list files but not set in project." $severity $outFile
1053 # Update project prjProps
1054 dict set out_prjprops $prjFile $prjProps
1057 # Loop over remaining files in list libraries
1058 foreach listFile $listFiles {
1059 MsgAndLog "$listFile was found in list files but not in project." $severity $outFile
1063 # Check extra files again...
1064 foreach prjFile $prjFiles {
1065 if {[dict exists $extra_files $prjFile]} {
1066 # File was generated at creation time, checking the md5sum
1067 # Removing the file from the prjFiles list
1068 set idx2 [lsearch -exact $prjFiles $prjFile]
1069 set prjFiles [lreplace $prjFiles $idx2 $idx2]
1070 set new_md5sum [Md5Sum $prjFile]
1071 set old_md5sum [DictGet $extra_files $prjFile]
1072 if {$new_md5sum != $old_md5sum} {
1073 # tclint-disable-next-line line-length
1074 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
1077 set extra_files [dict remove $extra_files $prjFile]
1079 # File is neither in list files nor in extra_files
1080 MsgAndLog "$prjFile was found in project but not in list files or .hog/extra.files" $severity $outFile
1085 # Update prjLibraries
1086 dict set out_prjlibs $prjLib $prjFiles
1089 MsgAndLog "Fileset $prjSet found in project but not in list files" $severity $outFile
1094 return [list $n_diffs $extra_files $out_prjlibs $out_prjprops]
1104 proc CompareVHDL {file1 file2} {
1105 set a [open $file1 r]
1106 set b [open $file2 r]
1108 while {[
gets $a line] != -1} {
1109 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1110 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1116 while {[
gets $b line] != -1} {
1117 set line [regsub {^[\t\s]*(.*)?\s*} $line "\\1"]
1118 if {![regexp {^$} $line] & ![regexp {^--} $line]} {
1127 foreach x $f1 y $f2 {
1129 lappend diff "> $x\n< $y\n\n"
1142 proc Copy {i_dirs o_dir} {
1143 foreach i_dir $i_dirs {
1144 if {[
file isdirectory $i_dir] && [
file isdirectory $o_dir]} {
1145 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]])} {
1146 file delete -force $o_dir/[
file tail $i_dir]
1150 file copy -force $i_dir $o_dir
1165 proc CopyIPbusXMLs {proj_dir path dst {xml_version "0.0.0"} {xml_sha "00000000"} {use_ipbus_sw 0} {generate 0}} {
1166 if {$use_ipbus_sw == 1} {
1167 lassign [
ExecuteRet python3 -c "from __future__ import print_function; from sys import path;print(':'.join(path\[1:\]))"] ret msg
1169 set ::env(PYTHONPATH) $msg
1170 lassign [
ExecuteRet gen_ipbus_addr_decode -h] ret msg
1177 Msg CriticalWarning "Problem while trying to run python: $msg"
1180 set dst [
file normalize $dst]
1182 if {$can_generate == 0} {
1183 if {$generate == 1} {
1184 Msg Error "Cannot generate IPbus address files, IPbus executable gen_ipbus_addr_decode not found or not working: $msg"
1187 Msg Warning "IPbus executable gen_ipbus_addr_decode not found or not working, will not verify IPbus address tables."
1194 set ipb_files [glob -nocomplain $proj_dir/list/*.ipb]
1195 set n_ipb_files [
llength $ipb_files]
1196 if {$n_ipb_files == 0} {
1197 Msg CriticalWarning "No files with .ipb extension found in $proj_dir/list."
1200 set libraries [dict create]
1201 set vhdl_dict [dict create]
1203 foreach ipb_file $ipb_files {
1209 set xmlfiles [dict get $libraries "xml.ipb"]
1211 set xml_list_error 0
1212 foreach xmlfile $xmlfiles {
1213 if {[
file isdirectory $xmlfile]} {
1214 Msg CriticalWarning "Directory $xmlfile listed in xml list file $list_file. Directories are not supported!"
1215 set xml_list_error 1
1218 if {[
file exists $xmlfile]} {
1219 if {[dict exists $vhdl_dict $xmlfile]} {
1220 set vhdl_file [
file normalize $path/[dict get $vhdl_dict $xmlfile]]
1224 lappend vhdls $vhdl_file
1225 set xmlfile [
file normalize $xmlfile]
1226 Msg Info "Copying $xmlfile to $dst and replacing place holders..."
1227 set in [open $xmlfile r]
1228 set out [open $dst/[
file tail $xmlfile] w]
1230 while {[
gets $in line] != -1} {
1231 set new_line [regsub {(.*)__VERSION__(.*)} $line "\\1$xml_version\\2"]
1232 set new_line2 [regsub {(.*)__GIT_SHA__(.*)} $new_line "\\1$xml_sha\\2"]
1233 puts $out $new_line2
1237 lappend xmls [
file tail $xmlfile]
1239 Msg Warning "XML file $xmlfile not found"
1242 if {${xml_list_error}} {
1243 Msg Error "Invalid files added to $list_file!"
1246 set cnt [
llength $xmls]
1247 Msg Info "$cnt xml file/s copied"
1250 if {$can_generate == 1} {
1253 file mkdir "address_decode"
1255 foreach x $xmls v $vhdls {
1257 set x [
file normalize ../$x]
1258 if {[
file exists $x]} {
1259 lassign [
ExecuteRet gen_ipbus_addr_decode --no-timestamp $x 2>&1] status log
1261 set generated_vhdl ./ipbus_decode_[
file rootname [
file tail $x]].vhd
1262 if {$generate == 1} {
1263 Msg Info "Copying generated VHDL file $generated_vhdl into $v (replacing if necessary)"
1264 file copy -force -- $generated_vhdl $v
1266 if {[
file exists $v]} {
1268 set n [
llength $diff]
1270 Msg CriticalWarning "$v does not correspond to its XML $x, [
expr {$n / 3}] line/s differ:"
1271 Msg Status [
join $diff "\n"]
1272 set diff_file [open ../diff_[
file rootname [
file tail $x]].txt w]
1273 puts $diff_file $diff
1276 Msg Info "[
file tail $x] and $v match."
1279 Msg Warning "VHDL address map file $v not found."
1283 Msg Warning "Address map generation failed for [
file tail $x]: $log"
1286 Msg Warning "Copied XML file $x not found."
1289 Msg Info "Skipped verification of [
file tail $x] as no VHDL file was specified."
1293 file delete -force address_decode
1305 proc DescriptionFromConf {conf_file} {
1306 set f [open $conf_file "r"]
1307 set lines [
split [read $f] "\n"]
1309 set second_line [
lindex $lines 1]
1312 if {![regexp {\#+ *(.+)} $second_line - description]} {
1316 if {[regexp -all {test|Test|TEST} $description]} {
1317 set description "test"
1330 proc DictGet {dictName keyName {default ""}} {
1331 if {[dict exists $dictName $keyName]} {
1332 return [dict get $dictName $keyName]
1343 proc DictSort {dict args} {
1345 foreach key [lsort {*}$args [dict keys $dict]] {
1346 dict set res $key [dict get $dict $key]
1356 proc DoxygenVersion {target_version} {
1357 set ver [
split $target_version "."]
1358 set v [
Execute doxygen --version]
1359 Msg Info "Found Doxygen version: $v"
1360 set current_ver [
split $v ". "]
1361 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
1362 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
1364 return [
expr {$target <= $current}]
1375 proc eos {command {attempt 1}} {
1377 if {![
info exists env(EOS_MGM_URL)]} {
1378 Msg Warning "Environment variable EOS_MGM_URL not set, setting it to default value root://eosuser.cern.ch"
1379 set ::env(EOS_MGM_URL) "root://eosuser.cern.ch"
1382 Msg Warning "The value of attempt should be 1 or more, not $attempt, setting it to 1 as default"
1385 for {
set i 0} {$i < $attempt} {
incr i} {
1386 set ret [
catch {
exec -ignorestderr eos {*}$command} result]
1391 set wait [
expr {1 + int(rand() * 29)}]
1392 Msg Warning "Command $command failed ($i/$attempt): $result, trying again in $wait seconds..."
1393 after [
expr {$wait * 1000}]
1397 return [list $ret $result]
1407 proc Execute {args} {
1411 Msg Error "Command [
join $args] returned error code: $ret"
1425 proc ExecuteRet {args} {
1427 if {[
llength $args] == 0} {
1428 Msg CriticalWarning "No argument given"
1432 set ret [
catch {
exec -ignorestderr {*}$args} result]
1435 return [list $ret $result]
1442 proc ExtractFilesSection {file_data} {
1443 set in_files_section 0
1446 foreach line $file_data {
1447 if {[regexp {^ *\[ *files *\]} $line]} {
1448 set in_files_section 1
1451 if {$in_files_section} {
1452 if {[regexp {^ *\[.*\]} $line]} {
1455 lappend result $line
1460 if {!$in_files_section} {
1474 proc ExtractVersionFromTag {tag} {
1475 if {[regexp {^(?:b(\d+))?v(\d+)\.(\d+).(\d+)(?:-\d+)?$} $tag -> mr M m p]} {
1480 Msg Warning "Repository tag $tag is not in a Hog-compatible format."
1486 return [list $M $m $p $mr]
1496 proc FileCommitted {File} {
1498 set currentDir [
pwd]
1499 cd [
file dirname [
file normalize $File]]
1500 set GitLog [
Git ls-files [
file tail $File]]
1501 if {$GitLog == ""} {
1502 Msg CriticalWarning "File [
file normalize $File] is not in the git repository. Please add it with:\n git add [
file normalize $File]\n"
1513 proc FindCommonGitChild {SHA1 SHA2} {
1515 set commits [
Git {log --oneline --merges}]
1518 foreach line [
split $commits "\n"] {
1519 set commit [
lindex [
split $line] 0]
1523 set ancestor $commit
1536 proc findFiles {basedir pattern} {
1539 set basedir [
string trimright [
file join [
file normalize $basedir] { }]]
1545 foreach fileName [glob -nocomplain -type {f r} -path $basedir $pattern] {
1546 lappend fileList $fileName
1550 foreach dirName [glob -nocomplain -type {d r} -path $basedir *] {
1553 set subDirList [
findFiles $dirName $pattern]
1554 if {[
llength $subDirList] > 0} {
1555 foreach subDirFile $subDirList {
1556 lappend fileList $subDirFile
1567 proc FindFileType {file_name} {
1568 set extension [
file extension $file_name]
1571 set file_extension "USE_SIGNALTAP_FILE"
1574 set file_extension "VHDL_FILE"
1577 set file_extension "VHDL_FILE"
1580 set file_extension "VERILOG_FILE"
1583 set file_extension "SYSTEMVERILOG_FILE"
1586 set file_extension "SDC_FILE"
1589 set file_extension "PDC_FILE"
1592 set file_extension "NDC_FILE"
1595 set file_extension "FDC_FILE"
1598 set file_extension "SOURCE_FILE"
1601 set file_extension "IP_FILE"
1604 set file_extension "QSYS_FILE"
1607 set file_extension "QIP_FILE"
1610 set file_extension "SIP_FILE"
1613 set file_extension "BSF_FILE"
1616 set file_extension "BDF_FILE"
1619 set file_extension "COMMAND_MACRO_FILE"
1622 set file_extension "VQM_FILE"
1625 set file_extension "ERROR"
1626 Msg Error "Unknown file extension $extension"
1629 return $file_extension
1636 proc FindNewestVersion {versions} {
1637 set new_ver 00000000
1638 foreach ver $versions {
1640 if {[
expr 0x$ver > 0x$new_ver]} {
1652 proc FindVhdlVersion {file_name} {
1653 set extension [
file extension $file_name]
1656 set vhdl_version "-hdl_version VHDL_2008"
1659 set vhdl_version "-hdl_version VHDL_2008"
1666 return $vhdl_version
1673 proc FormatGeneric {generic} {
1674 if {[
string is integer "0x$generic"]} {
1675 return [
format "32'h%08X" "0x$generic"]
1678 return [
format "32'h%08X" 0]
1688 proc GenerateBitstream {{run_folder ""} {repo_path .} {njobs 1}} {
1689 Msg Info "Starting write bitstream flow..."
1691 set revision [get_current_revision]
1692 if {[
catch {execute_module -tool asm} result]} {
1693 Msg Error "Result: $result\n"
1694 Msg Error "Generate bitstream failed. See the report file.\n"
1696 Msg Info "Generate bitstream was successful for revision $revision.\n"
1699 Msg Info "Run GENERATEPROGRAMMINGDATA ..."
1700 if {[
catch {run_tool -name {GENERATEPROGRAMMINGDATA}}]} {
1701 Msg Error "GENERATEPROGRAMMINGDATA FAILED!"
1703 Msg Info "GENERATEPROGRAMMINGDATA PASSED."
1705 Msg Info "Sourcing Hog/Tcl/integrated/post-bitstream.tcl"
1706 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
1708 prj_run Export -impl Implementation0 -task Bitgen
1718 proc GenerateQsysSystem {qsysFile commandOpts} {
1719 if {[
file exists $qsysFile] != 0} {
1720 set qsysPath [
file dirname $qsysFile]
1721 set qsysName [
file rootname [
file tail $qsysFile]]
1722 set qsysIPDir "$qsysPath/$qsysName"
1723 set qsysLogFile "$qsysPath/$qsysName.qsys-generate.log"
1726 if {![
info exists ::env(QSYS_ROOTDIR)]} {
1727 if {[
info exists ::env(QUARTUS_ROOTDIR)]} {
1728 set qsys_rootdir "$::env(QUARTUS_ROOTDIR)/sopc_builder/bin"
1729 Msg Warning "The QSYS_ROOTDIR environment variable is not set! I will use $qsys_rootdir"
1731 Msg CriticalWarning "The QUARTUS_ROOTDIR environment variable is not set! Assuming all quartus executables are contained in your PATH!"
1734 set qsys_rootdir $::env(QSYS_ROOTDIR)
1737 set cmd "$qsys_rootdir/qsys-generate"
1738 set cmd_options "$qsysFile --output-directory=$qsysIPDir $commandOpts"
1739 if {![
catch {"exec $cmd -version"}] || [
lindex $::errorCode 0] eq "NONE"} {
1740 Msg Info "Executing: $cmd $cmd_options"
1741 Msg Info "Saving logfile in: $qsysLogFile"
1742 if {[
catch {
eval exec -ignorestderr "$cmd $cmd_options >>& $qsysLogFile"} ret opt]} {
1743 set makeRet [
lindex [dict get $opt -errorcode] end]
1744 Msg CriticalWarning "$cmd returned with $makeRet"
1747 Msg Error " Could not execute command $cmd"
1751 set qsysIPFileList [
concat \
1752 [glob -nocomplain -directory $qsysIPDir -types f *.ip *.qip] \
1753 [glob -nocomplain -directory "$qsysIPDir/synthesis" -types f *.ip *.qip *.vhd *.vhdl]
1755 foreach qsysIPFile $qsysIPFileList {
1756 if {[
file exists $qsysIPFile] != 0} {
1758 set_global_assignment -name $qsysIPFileType $qsysIPFile
1760 set IpMd5Sum [
Md5Sum $qsysIPFile]
1762 set fileDir [
file normalize "./hogTmp"]
1763 set fileName "$fileDir/.hogQsys.md5"
1764 if {![
file exists $fileDir]} {
1767 set hogQsysFile [open $fileName "a"]
1768 set fileEntry "$qsysIPFile\t$IpMd5Sum"
1769 puts $hogQsysFile $fileEntry
1774 Msg ERROR "Error while generating ip variations from qsys: $qsysFile not found!"
1784 proc GenericToSimulatorString {prop_dict target} {
1786 dict for {theKey theValue} $prop_dict {
1795 regexp {([0-9]*)('h)([0-9a-fA-F]*)} $theValue valueHexFull valueNumBits valueHexFlag valueHex
1796 regexp {^([0-9]*)$} $theValue valueIntFull ValueInt
1797 regexp {(?!^\d+$)^.+$} $theValue valueStrFull ValueStr
1798 if {[string tolower $target] == "vivado" || [string tolower $target] == "xsim"} {
1799 if {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
1800 set prj_generics "$prj_generics $theKey=$valueHexFull"
1801 } elseif {$valueIntFull != "" && $ValueInt != ""} {
1802 set prj_generics "$prj_generics $theKey=$ValueInt"
1803 } elseif {$valueStrFull != "" && $ValueStr != ""} {
1804 set prj_generics "$prj_generics $theKey=\"$ValueStr\""
1806 set prj_generics "$prj_generics $theKey=\"$theValue\""
1808 } elseif {[lsearch -exact [GetSimulators] [string tolower $target]] >= 0} {
1809 if {$valueNumBits != "" && $valueHexFlag != "" && $valueHex != ""} {
1811 scan $valueNumBits %d numBits
1813 scan $valueHex %x numHex
1814 binary scan [binary format "I" $numHex] "B*" binval
1815 set numBits [expr {$numBits - 1}]
1816 set numBin [string range $binval end-$numBits end]
1817 set prj_generics "$prj_generics $theKey=\"$numBin\""
1818 } elseif {$valueIntFull != "" && $ValueInt != ""} {
1819 set prj_generics "$prj_generics $theKey=$ValueInt"
1820 } elseif {$valueStrFull != "" && $ValueStr != ""} {
1821 set prj_generics "$prj_generics {$theKey=\"$ValueStr\"}"
1823 set prj_generics "$prj_generics {$theKey=\"$theValue\"}"
1826 Msg Warning "Target : $target not implemented"
1829 return $prj_generics
1837 proc GetConfFiles {proj_dir} {
1838 Msg Debug "GetConfFiles called with proj_dir=$proj_dir"
1839 if {![
file isdirectory $proj_dir]} {
1840 Msg Error "$proj_dir is supposed to be the top project directory"
1843 set conf_file [
file normalize $proj_dir/hog.conf]
1844 set sim_file [
file normalize $proj_dir/sim.conf]
1845 set pre_tcl [
file normalize $proj_dir/pre-creation.tcl]
1846 set post_tcl [
file normalize $proj_dir/post-creation.tcl]
1848 return [list $conf_file $sim_file $pre_tcl $post_tcl]
1857 proc GetCustomCommands {{directory .} {ret_commands 0}} {
1858 set commands_dict [dict create]
1859 set commands_files [glob -nocomplain $directory/*.tcl]
1860 set commands_string ""
1862 if {[
llength $commands_files] == 0} {
1866 if {$ret_commands == 0} {
1867 append commands_string "\nCustom Commands:\n"
1870 foreach file $commands_files {
1871 set base_name [
string toupper [
file rootname [
file tail $file]]]
1872 if {$ret_commands == 1} {
1873 append commands_string "
1875 Msg Info \"Running custom script: $file\"
1877 Msg Info \"Done running custom script...\"
1882 set f [open $file r]
1883 set first_line [
gets $f]
1885 if {[regexp -nocase "^#\s*$base_name:\s*(.*)" $first_line full_match script_des]} {
1886 append commands_string " - $base_name: $script_des\n"
1888 append commands_string " - $base_name: runs $file\n"
1892 return $commands_string
1898 proc GetDateAndTime {commit} {
1899 set clock_seconds [
clock seconds]
1902 set date [
Git "log -1 --format=%cd --date=format:%d%m%Y $commit"]
1903 set timee [
Git "log -1 --format=%cd --date=format:00%H%M%S $commit"]
1905 Msg Warning "Found Git version older than 2.9.3. Using current date and time instead of commit time."
1906 set date [
clock format $clock_seconds -format {%d%m%Y}]
1907 set timee [
clock format $clock_seconds -format {00%H%M%S}]
1909 return [list $date $timee]
1921 proc GetFile {file fileset} {
1924 set Files [get_files -all $file -of_object [get_filesets $fileset]]
1925 set f [
lindex $Files 0]
1933 puts "***DEBUG Hog:GetFile $file"
1942 proc GetFileGenerics {filename {entity ""}} {
1944 if {[
string equal $file_type "VERILOG_FILE"] || [
string equal $file_type "SYSTEMVERILOG_FILE"]} {
1946 }
elseif {[
string equal $file_type "VHDL_FILE"]} {
1949 Msg CriticalWarning "Could not determine extension of top level file."
1958 proc GetGenericsFromConf {proj_dir} {
1959 set generics_dict [dict create]
1960 set top_dir "Top/$proj_dir"
1961 set conf_file "$top_dir/hog.conf"
1963 Msg Debug "GetGenericsFromConf called with proj_dir=$proj_dir, top_dir=$top_dir"
1965 if {[
file exists $conf_file]} {
1967 if {[dict exists $properties generics]} {
1968 set generics_dict [dict get $properties generics]
1971 Msg Warning "File $conf_file not found."
1973 return $generics_dict
1986 proc GetSimSets {project_name repo_path {simsets ""} {ghdl 0} {no_conf 0}} {
1987 set simsets_dict [dict create]
1988 set list_dir "$repo_path/Top/$project_name/list"
1990 if {$simsets != ""} {
1991 foreach s $simsets {
1992 set list_file "$list_dir/$s.sim"
1993 if {[
file exists $list_file]} {
1994 lappend list_files $list_file
1995 }
elseif {$s != "sim_1"} {
1996 Msg CriticalWarning "Simulation set list file $list_file not found."
2001 set list_files [glob -nocomplain -directory $list_dir "*.sim"]
2005 set proj_dir [
file normalize $repo_path/Top/$project_name]
2006 set sim_file [
file normalize $proj_dir/sim.conf]
2008 foreach list_file $list_files {
2009 set file_name [
file tail $list_file]
2010 set simset_name [
file rootname $file_name]
2011 set fp [open $list_file r]
2012 set file_data [read $fp]
2014 set data [
split $file_data "\n"]
2016 set firstline [
lindex $data 0]
2018 if {[regexp {^ *\# *Simulator} $firstline]} {
2019 set simulator_prop [regexp -all -inline {\S+} $firstline]
2020 set simulator [
string tolower [
lindex $simulator_prop end]]
2022 Msg Warning "Simulator not set in $simset_name.sim. \
2023 The first line of $simset_name.sim should be #Simulator <SIMULATOR_NAME>,\
2024 where <SIMULATOR_NAME> can be xsim, questa, modelsim, ghdl, riviera, activehdl,\
2025 ies, or vcs, e.g. #Simulator questa.\
2026 Setting simulator by default to xsim."
2027 set simulator "xsim"
2029 if {$simulator eq "skip_simulation"} {
2030 Msg Info "Skipping simulation for $simset_name"
2033 if {($ghdl == 1 && $simulator != "ghdl") || ($ghdl == 0 && $simulator == "ghdl")} {
2037 set SIM_PROPERTIES ""
2038 if {[
file exists $sim_file] && $no_conf == 0} {
2039 set SIM_PROPERTIES [
ReadConf $sim_file]
2042 set global_sim_props [dict create]
2043 dict set global_sim_props "properties" [
DictGet $SIM_PROPERTIES "sim"]
2044 dict set global_sim_props "generics" [
DictGet $SIM_PROPERTIES "generics"]
2045 dict set global_sim_props "hog" [
DictGet $SIM_PROPERTIES "hog"]
2048 set sim_dict [dict create]
2049 dict set sim_dict "simulator" $simulator
2050 if {[dict exists $SIM_PROPERTIES $simset_name]} {
2051 dict set sim_dict "properties" [
DictGet $SIM_PROPERTIES $simset_name]
2052 dict set sim_dict "generics" [
DictGet $SIM_PROPERTIES "$simset_name:generics"]
2053 dict set sim_dict "hog" [
DictGet $SIM_PROPERTIES "$simset_name:hog"]
2054 }
elseif {$no_conf == 0} {
2056 set conf_dict [
ReadConf $list_file]
2057 set sim_dict [
MergeDict $sim_dict $conf_dict 0]
2059 set sim_dict [
MergeDict $sim_dict $global_sim_props 0]
2060 dict set simsets_dict $simset_name $sim_dict
2062 return $simsets_dict
2070 proc GetSimsetGenericsFromConf {proj_dir} {
2071 set simsets_generics_dict [dict create]
2072 set top_dir "Top/$proj_dir"
2073 set conf_file "$top_dir/sim.conf"
2076 if {[
file exists $conf_file]} {
2079 set simsets_generics_dict [dict filter $properties key *:generics]
2081 Msg Warning "File $conf_file not found."
2083 return $simsets_generics_dict
2094 proc GetGroupName {proj_dir repo_dir} {
2095 if {[regexp {^(.*)/(Top|Projects)/+(.*?)/*$} $proj_dir dummy possible_repo_dir proj_or_top dir]} {
2097 if {[
file normalize $repo_dir] eq [
file normalize $possible_repo_dir]} {
2098 set group [
file dir $dir]
2099 if {$group == "."} {
2104 Msg Warning "Project directory $proj_dir seems to be in $possible_repo_dir which is not a the main Git repository $repo_dir."
2107 Msg Warning "Could not parse project directory $proj_dir"
2120 proc GetHogDescribe {sha {repo_path .}} {
2123 set new_sha "[
string toupper [
GetSHA]]"
2126 set new_sha [
string toupper $sha]
2146 proc GetHogFiles {args} {
2149 if {[
catch {
package require cmdline} ERROR]} {
2150 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
2157 {list_files.arg "" "The file wildcard, if not specified all Hog list files will be looked for."}
2158 {sha_mode "Forwarded to ReadListFile, see there for info."}
2159 {ext_path.arg "" "Path for the external libraries forwarded to ReadListFile."}
2160 {print_log "Forwarded to ReadListFile, see there for info."}
2162 set usage "USAGE: GetHogFiles \[options\] <list path> <repository path>"
2163 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
2167 set list_path [
lindex $args 0]
2168 set repo_path [
lindex $args 1]
2170 set list_files $options(list_files)
2171 set sha_mode $options(sha_mode)
2172 set ext_path $options(ext_path)
2173 set print_log $options(print_log)
2175 if {$sha_mode == 1} {
2176 set sha_mode_opt "-sha_mode"
2181 if {$print_log == 1} {
2182 set print_log_opt "-print_log"
2184 set print_log_opt ""
2188 if {$list_files == ""} {
2189 set list_files {.src,.con,.sim,.ext}
2191 set libraries [dict create]
2192 set properties [dict create]
2193 set list_files [glob -nocomplain -directory $list_path "*{$list_files}"]
2194 set filesets [dict create]
2196 foreach f $list_files {
2197 set ext [
file extension $f]
2198 if {$ext == ".ext"} {
2199 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $ext_path"] l p fs
2201 lassign [
ReadListFile {*}"$sha_mode_opt $print_log_opt $f $repo_path"] l p fs
2204 set properties [
MergeDict $p $properties]
2205 Msg Debug "list file $f, filesets: $fs"
2207 Msg Debug "Merged filesets $filesets"
2209 return [list $libraries $properties $filesets]
2215 proc GetIDECommand {proj_conf} {
2217 if {[
file exists $proj_conf]} {
2218 set ide_name_and_ver [
string tolower [
GetIDEFromConf $proj_conf]]
2219 set ide_name [
lindex [regexp -all -inline {\S+} $ide_name_and_ver] 0]
2221 if {$ide_name eq "vivado" || $ide_name eq "vivado_vitis_classic"} {
2222 set command "vivado"
2224 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2225 set after_tcl_script " -tclargs "
2227 }
elseif {$ide_name eq "planahead"} {
2228 set command "planAhead"
2230 set before_tcl_script " -nojournal -nolog -mode batch -notrace -source "
2231 set after_tcl_script " -tclargs "
2233 }
elseif {$ide_name eq "quartus"} {
2234 set command "quartus_sh"
2236 set before_tcl_script " -t "
2237 set after_tcl_script " "
2239 }
elseif {$ide_name eq "libero"} {
2242 set command "libero"
2243 set before_tcl_script "SCRIPT:"
2244 set after_tcl_script " SCRIPT_ARGS:\""
2246 }
elseif {$ide_name eq "diamond"} {
2247 set command "diamondc"
2248 set before_tcl_script " "
2249 set after_tcl_script " "
2251 }
elseif {$ide_name eq "vitis_classic"} {
2254 set before_tcl_script ""
2255 set after_tcl_script " "
2257 }
elseif {$ide_name eq "ghdl"} {
2259 set before_tcl_script " "
2260 set after_tcl_script " "
2263 Msg Error "IDE: $ide_name not known."
2266 Msg Error "Configuration file $proj_conf not found."
2269 return [list $command $before_tcl_script $after_tcl_script $end_marker]
2275 proc GetIDEFromConf {conf_file} {
2276 set f [open $conf_file "r"]
2279 if {[regexp -all {^\# *(\w*) *(vitis_classic)? *(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)?(_.*)? *$} $line dummy ide vitisflag version patch]} {
2280 if {[
info exists vitisflag] && $vitisflag != ""} {
2281 set ide "${ide}_${vitisflag}"
2284 if {[
info exists version] && $version != ""} {
2290 set ret [list $ide $ver]
2292 Msg CriticalWarning "The first line of hog.conf should be \#<IDE name> <version>, \
2293 where <IDE name>. is quartus, vivado, planahead, libero, diamond or ghdl, \
2294 and <version> the tool version, e.g. \#vivado 2020.2. Will assume vivado."
2295 set ret [list "vivado" "0.0.0"]
2302 proc GetIDEName {} {
2304 return "ISE/PlanAhead"
2322 proc GetIDEVersion {} {
2325 regexp {\d+\.\d+(\.\d+)?} [version -short] ver
2330 regexp {[\.0-9]+} $quartus(version) ver
2333 set ver [get_libero_version]
2335 regexp {\d+\.\d+(\.\d+)?} [sys_install version] ver
2337 regexp {\d+\.\d+(\.\d+)?} [version] ver
2349 proc GetLinkedFile {link_file} {
2350 if {[
file type $link_file] eq "link"} {
2351 if {[
OS] == "windows"} {
2353 lassign [
ExecuteRet realpath $link_file] ret msg
2354 lassign [
ExecuteRet cygpath -m $msg] ret2 msg2
2355 if {$ret == 0 && $ret2 == 0} {
2357 Msg Debug "Found link file $link_file on Windows, the linked file is: $real_file"
2359 Msg CriticalWarning "[
file normalize $link_file] is a soft link. \
2360 Soft link are not supported on Windows and readlink.exe or cygpath.exe did not work: readlink=$ret: $msg, cygpath=$ret2: $msg2."
2361 set real_file $link_file
2365 set linked_file [
file link $link_file]
2366 set real_file [
file normalize [
file dirname $link_file]/$linked_file]
2369 if {![
file exists $real_file]} {
2370 Msg Warning "$link_file is a broken link, because the linked file: $real_file does not exist."
2373 Msg Warning "$link file is not a soft link"
2374 set real_file $link_file
2387 proc GetMaxThreads {proj_dir} {
2389 if {[
file exists $proj_dir/hog.conf]} {
2391 if {[dict exists $properties parameters]} {
2392 set propDict [dict get $properties parameters]
2393 if {[dict exists $propDict MAX_THREADS]} {
2394 set maxThreads [dict get $propDict MAX_THREADS]
2398 Msg Warning "File $proj_dir/hog.conf not found. Max threads will be set to default value 1"
2411 proc GetModifiedFiles {{repo_path "."} {pattern "."}} {
2414 set ret [
Git "ls-files --modified $pattern"]
2423 proc GetOptions {argv parameters} {
2426 set param_list [list]
2427 set option_list [list]
2429 foreach p $parameters {
2430 lappend param_list [
lindex $p 0]
2434 while {$index < [
llength $argv]} {
2435 set arg [
lindex $argv $index]
2436 if {[
string first - $arg] == 0} {
2437 set option [
string trimleft $arg "-"]
2439 lappend option_list $arg
2440 if {[lsearch $param_list ${option}*] >= 0 && [
string first ".arg" [lsearch -inline $param_list ${option}*]] >= 0} {
2441 lappend option_list [
lindex $argv $index]
2445 lappend arg_list $arg
2449 Msg Debug "Argv: $argv"
2450 Msg Debug "Options: $option_list"
2451 Msg Debug "Arguments: $arg_list"
2452 return [list $option_list $arg_list]
2470 proc GetProjectFiles {{project_file ""}} {
2471 set libraries [dict create]
2472 set simlibraries [dict create]
2473 set constraints [dict create]
2474 set properties [dict create]
2475 set consets [dict create]
2476 set srcsets [dict create]
2477 set simsets [dict create]
2480 set all_filesets [get_filesets]
2481 set simulator [get_property target_simulator [current_project]]
2482 set top [get_property "top" [current_fileset]]
2484 dict lappend properties $topfile "top=$top"
2486 foreach fs $all_filesets {
2487 if {$fs == "utils_1"} {
2492 set all_files [get_files -quiet -of_objects [get_filesets $fs]]
2493 set fs_type [get_property FILESET_TYPE [get_filesets $fs]]
2495 if {$fs_type == "BlockSrcs"} {
2497 set dict_fs "sources_1"
2501 foreach f $all_files {
2508 if {[
lindex [get_property IS_GENERATED [
GetFile $f $fs]] 0] != 0} {
2513 if {[get_property FILE_TYPE [
GetFile $f $fs]] == "Configuration Files"} {
2519 if {[get_property CORE_CONTAINER [
GetFile $f $fs]] != ""} {
2520 if {[
file extension $f] == ".xcix"} {
2521 set f [get_property CORE_CONTAINER [
GetFile $f $fs]]
2529 if {[get_property SCOPED_TO_REF [
GetFile $f $fs]] != ""} {
2530 dict lappend properties $f "scoped_to_ref=[get_property SCOPED_TO_REF [
GetFile $f $fs]]"
2535 if {[get_property SCOPED_TO_CELLS [
GetFile $f $fs]] != ""} {
2536 dict lappend properties $f "scoped_to_cells=[get_property SCOPED_TO_CELLS [
GetFile $f $fs]]"
2540 if {[
IsInList "PARENT_COMPOSITE_FILE" [list_property [
GetFile $f $fs]]]} {
2545 if {[
file tail $f] == "nocattrs.dat"} {
2550 if {[
file extension $f] != ".coe"} {
2551 set f [
file normalize $f]
2554 set type [get_property FILE_TYPE [
GetFile $f $fs]]
2556 set lib [get_property -quiet LIBRARY [
GetFile $f $fs]]
2559 Msg Debug "File $f Extension [
file extension $f] Type [
lindex $type 0]"
2561 if {[
string equal [
lindex $type 0] "VHDL"] && [
llength $type] == 1} {
2563 }
elseif {[
string equal [
lindex $type 0] "Block"] && [
string equal [
lindex $type 1] "Designs"]} {
2566 }
elseif {[
string equal $type "SystemVerilog"] && [
file extension $f] != ".sv"} {
2567 set prop "SystemVerilog"
2568 }
elseif {[
string equal [
lindex $type 0] "XDC"] && [
file extension $f] != ".xdc"} {
2570 }
elseif {[
string equal $type "Verilog Header"] && [
file extension $f] != ".vh" && [
file extension $f] != ".svh"} {
2571 set prop "verilog_header"
2572 }
elseif {[
string equal $type "Verilog Template"] && [
file extension $f] == ".v" && [
file extension $f] != ".sv"} {
2573 set prop "verilog_template"
2575 set type [
lindex $type 0]
2579 if {![
string equal $prop ""]} {
2580 dict lappend properties $f $prop
2583 if {[
string equal $fs_type "SimulationSrcs"]} {
2585 if {[
string equal $type "VHDL"]} {
2586 set library "${lib}.sim"
2588 set library "others.sim"
2592 dict lappend simsets $dict_fs $library
2595 dict lappend simlibraries $library $f
2596 }
elseif {[
string equal $type "VHDL"]} {
2599 dict lappend srcsets $dict_fs "${lib}.src"
2601 dict lappend libraries "${lib}.src" $f
2602 }
elseif {[
string first "IP" $type] != -1} {
2605 dict lappend srcsets $dict_fs "ips.src"
2607 dict lappend libraries "ips.src" $f
2608 Msg Debug "Appending $f to ips.src"
2609 }
elseif {[
string equal $fs_type "Constrs"]} {
2612 dict lappend consets $dict_fs "sources.con"
2614 dict lappend constraints "sources.con" $f
2618 dict lappend srcsets $dict_fs "others.src"
2620 dict lappend libraries "others.src" $f
2621 Msg Debug "Appending $f to others.src"
2624 if {[
lindex [get_property -quiet used_in_synthesis [
GetFile $f $fs]] 0] == 0} {
2625 dict lappend properties $f "nosynth"
2627 if {[
lindex [get_property -quiet used_in_implementation [
GetFile $f $fs]] 0] == 0} {
2628 dict lappend properties $f "noimpl"
2630 if {[
lindex [get_property -quiet used_in_simulation [
GetFile $f $fs]] 0] == 0} {
2631 dict lappend properties $f "nosim"
2633 if {[
lindex [get_property -quiet IS_MANAGED [
GetFile $f $fs]] 0] == 0 && [
file extension $f] != ".xcix"} {
2634 dict lappend properties $f "locked"
2640 dict lappend properties "Simulator" [get_property target_simulator [current_project]]
2643 set file [open $project_file r]
2644 set in_file_manager 0
2646 while {[
gets $file line] >= 0} {
2648 if {[regexp {^KEY ActiveRoot \"([^\"]+)\"} $line -> value]} {
2649 set top [
string range $value 0 [
expr {[string first "::" $value] - 1}]]
2653 if {[regexp {^LIST FileManager} $line]} {
2654 set in_file_manager 1
2659 if {$in_file_manager && [regexp {^ENDLIST} $line]} {
2664 if {$in_file_manager && [regexp {^VALUE \"([^\"]+)} $line -> value]} {
2667 lassign [
split $value ,] file_path file_type
2670 set library "others"
2671 while {[
gets $file line] >= 0} {
2672 if {$line == "ENDFILE"} {
2675 regexp {^LIBRARY=\"([^\"]+)} $line -> library
2676 regexp {^PARENT=\"([^\"]+)} $line -> parent_file
2678 Msg Debug "Found file ${file_path} in project.."
2679 if {$parent_file == ""} {
2680 if {$file_type == "hdl"} {
2682 if {[
IsInList "${library}.src" [
DictGet $srcsets "sources_1"]] == 0} {
2683 dict lappend srcsets "sources_1" "${library}.src"
2685 dict lappend libraries "${library}.src" $file_path
2689 if {[
GetModuleName $file_path] == [
string tolower $top] && $top != ""} {
2690 Msg Debug "Found top module $top in $file_path"
2691 dict lappend properties $file_path "top=$top"
2693 }
elseif {$file_type == "tb_hdl"} {
2695 dict lappend simsets "sim_1" "${library}.sim"
2697 dict lappend simlibraries "${library}.sim" $file_path
2698 }
elseif {$file_type == "io_pdc" || $file_type == "sdc"} {
2700 dict lappend consets "constrs_1" "sources.con"
2702 dict lappend constraints "sources.con" $file_path
2709 set fileData [read [open $project_file]]
2711 set project_path [
file dirname $project_file]
2714 regsub {<\?xml.*\?>} $fileData "" fileData
2717 regexp {<Implementation.*?>(.*)</Implementation>} $fileData -> implementationContent
2721 set sourceRegex {<Source name="([^"]*?)" type="([^"]*?)" type_short="([^"]*?)".*?>(.*?)</Source>}
2723 set optionsRegex {<Options(.*?)\/>}
2724 regexp $optionsRegex $implementationContent -> prj_options
2725 foreach option $prj_options {
2726 if {[regexp {^top=\"([^\"]+)\"} $option match result]} {
2731 while {[regexp $sourceRegex $implementationContent match name type type_short optionsContent]} {
2732 Msg Debug "Found file ${name} in project..."
2733 set file_path [
file normalize $project_path/$name]
2735 set optionsRegex {<Options(.*?)\/>}
2736 regexp $optionsRegex $optionsContent -> options
2737 set library "others"
2739 foreach option $options {
2740 if {[
string first "System Verilog" $option]} {
2743 if {[regexp {^lib=\"([^\"]+)\"} $option match1 result]} {
2748 if {[regexp {syn_sim="([^"]*?)"} $match match_sim simonly]} {
2753 if {$type_short == "VHDL" || $type_short == "Verilog" || $type_short == "IPX"} {
2754 if {$ext == ".src"} {
2755 if {[
IsInList "${library}${ext}" [
DictGet $srcsets "sources_1"]] == 0} {
2756 dict lappend srcsets "sources_1" "${library}${ext}"
2758 dict lappend libraries "${library}${ext}" $file_path
2759 }
elseif {$ext == ".sim"} {
2761 dict lappend simsets "sim_1" "${library}.sim"
2763 dict lappend simlibraries "${library}.sim" $file_path
2769 Msg Debug "Found top module $top in $file_path"
2770 dict lappend properties $file_path "top=$top"
2772 }
elseif {$type_short == "SDC"} {
2774 dict lappend consets "constrs_1" "sources.con"
2776 dict lappend constraints "sources.con" $file_path
2780 regsub -- $match $implementationContent "" implementationContent
2783 return [list $libraries $properties $simlibraries $constraints $srcsets $simsets $consets]
2790 proc GetProjectFlavour {proj_name} {
2792 set flavour [
string map {. ""} [
file extension $proj_name]]
2793 if {$flavour != ""} {
2794 if {[
string is integer $flavour]} {
2795 Msg Info "Project $proj_name has flavour = $flavour, the generic variable FLAVOUR will be set to $flavour"
2797 Msg Warning "Project name has a unexpected non numeric extension, flavour will be set to -1"
2814 proc GetProjectVersion {proj_dir repo_path {ext_path ""} {sim 0}} {
2815 if {![
file exists $proj_dir]} {
2816 Msg CriticalWarning "$proj_dir not found"
2826 Msg Warning "Repository is not clean"
2834 Msg Debug "Project version $v_proj, latest tag $v_last"
2836 Msg Info "The specified project was modified since official version."
2843 Msg Info "The specified project was modified in the latest official version $ret"
2844 }
elseif {$comp == -1} {
2845 Msg Info "The specified project was modified in a past official version $ret"
2861 proc GetRepoVersions {proj_dir repo_path {ext_path ""} {sim 0}} {
2862 if {[
catch {
package require cmdline} ERROR]} {
2863 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
2878 lappend SHAs [
GetSHA {Hog}]
2882 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
2883 Msg Info "Hog submodule [
pwd] clean."
2884 lassign [
GetVer ./] hog_ver hog_hash
2886 Msg CriticalWarning "Hog submodule [
pwd] not clean, commit hash will be set to 0."
2887 set hog_hash "0000000"
2888 set hog_ver "00000000"
2893 if {[
Git {status --untracked-files=no --porcelain}] eq ""} {
2894 Msg Info "Git working directory [
pwd] clean."
2897 Msg CriticalWarning "Git working directory [
pwd] not clean, commit hash, and version will be set to 0."
2902 lassign [
GetVer [
join $conf_files]] top_ver top_hash
2903 lappend SHAs $top_hash
2904 lappend versions $top_ver
2911 lassign [
GetHogFiles -list_files "*.src" -sha_mode "./list/" $repo_path] src_files dummy
2912 dict for {f files} $src_files {
2913 # library names have a .src extension in values returned by GetHogFiles
2914 set name [file rootname [file tail $f]]
2915 if {[file ext $f] == ".oth"} {
2918 lassign [GetVer $files] ver hash
2919 # Msg Info "Found source list file $f, version: $ver commit SHA: $hash"
2921 lappend versions $ver
2923 lappend hashes $hash
2930 lassign [
GetHogFiles -list_files "*.con" -sha_mode "./list/" $repo_path] cons_files dummy
2931 dict for {f files} $cons_files {
2932 #library names have a .con extension in values returned by GetHogFiles
2933 set name [file rootname [file tail $f]]
2934 lassign [GetVer $files] ver hash
2935 #Msg Info "Found constraint list file $f, version: $ver commit SHA: $hash"
2937 Msg CriticalWarning "Constraints file $f not found in Git."
2939 lappend cons_hashes $hash
2941 lappend versions $ver
2948 lassign [
GetHogFiles -list_files "*.sim" -sha_mode "./list/" $repo_path] sim_files dummy
2949 dict for {f files} $sim_files {
2950 #library names have a .sim extension in values returned by GetHogFiles
2951 set name [file rootname [file tail $f]]
2952 lassign [GetVer $files] ver hash
2953 #Msg Info "Found simulation list file $f, version: $ver commit SHA: $hash"
2954 lappend sim_hashes $hash
2956 lappend versions $ver
2962 if {"{}" eq $cons_hashes} {
2964 Msg CriticalWarning "No hashes found for constraints files (not in git)"
2967 set cons_hash [
string tolower [
Git "log --format=%h -1 $cons_hashes"]]
2974 set ext_files [glob -nocomplain "./list/*.ext"]
2977 foreach f $ext_files {
2978 set name [
file rootname [
file tail $f]]
2981 lappend ext_names $name
2982 lappend ext_hashes $hash
2985 lappend versions $ext_ver
2988 set file_data [read $fp]
2990 set data [
split $file_data "\n"]
2992 foreach line $data {
2993 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
2995 set file_and_prop [regexp -all -inline {\S+} $line]
2996 set hdlfile [
lindex $file_and_prop 0]
2997 set hdlfile $ext_path/$hdlfile
2998 if {[
file exists $hdlfile]} {
2999 set hash [
lindex $file_and_prop 1]
3000 set current_hash [
Md5Sum $hdlfile]
3001 if {[
string first $hash $current_hash] == -1} {
3002 Msg CriticalWarning "File $hdlfile has a wrong hash. Current checksum: $current_hash, expected: $hash"
3010 if {[
llength [glob -nocomplain ./list/*.ipb]] > 0} {
3012 lassign [
GetHogFiles -list_files "*.ipb" -sha_mode "./list/" $repo_path] xml_files dummy
3013 lassign [
GetVer [dict get $xml_files "xml.ipb"]] xml_ver xml_hash
3014 lappend SHAs $xml_hash
3015 lappend versions $xml_ver
3019 Msg Info "This project does not use IPbus XMLs"
3024 set user_ip_repos ""
3025 set user_ip_repo_hashes ""
3026 set user_ip_repo_vers ""
3028 if {[
file exists [
lindex $conf_files 0]]} {
3029 set PROPERTIES [
ReadConf [
lindex $conf_files 0]]
3030 if {[dict exists $PROPERTIES main]} {
3031 set main [dict get $PROPERTIES main]
3032 dict for {p v} $main {
3033 if {[string tolower $p] == "ip_repo_paths"} {
3035 if {[file isdirectory "$repo_path/$repo"]} {
3036 set repo_file_list [glob -nocomplain "$repo_path/$repo/*"]
3037 if {[llength $repo_file_list] == 0} {
3038 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3040 lappend user_ip_repos "$repo_path/$repo"
3049 foreach repo $user_ip_repos {
3050 if {[
file isdirectory $repo]} {
3051 set repo_file_list [glob -nocomplain "$repo/*"]
3052 if {[
llength $repo_file_list] != 0} {
3053 lassign [
GetVer $repo] ver sha
3054 lappend user_ip_repo_hashes $sha
3055 lappend user_ip_repo_vers $ver
3056 lappend versions $ver
3058 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory is empty."
3061 Msg Warning "IP_REPO_PATHS property set to $repo in hog.conf but directory does not exist."
3070 while {$found == 0} {
3071 set global_commit [
Git "log --format=%h -1 --abbrev=7 $SHAs"]
3076 if {$common_child == 0} {
3077 Msg CriticalWarning "The commit $sha is not an ancestor of the global commit $global_commit, which is OK. \
3078 But $sha and $global_commit do not have any common child, which is NOT OK. \
3079 This is probably do to a REBASE that is forbidden in Hog methodology as it changes git history. \
3080 Hog cannot guarantee the accuracy of the SHAs. \
3081 A way to fix this is to make a commit that touches all the projects in the repositories (e.g. change the Hog version), \
3082 but please do not rebase in the official branches in the future."
3084 Msg Info "The commit $sha is not an ancestor of the global commit $global_commit, adding the first common child $common_child instead..."
3085 lappend SHAs $common_child
3095 set global_commit "0000000"
3096 set global_version "00000000"
3101 set top_hash [
format %+07s $top_hash]
3102 set cons_hash [
format %+07s $cons_hash]
3103 return [list $global_commit $global_version \
3104 $hog_hash $hog_ver $top_hash $top_ver \
3105 $libs $hashes $vers $cons_ver $cons_hash \
3106 $ext_names $ext_hashes $xml_hash $xml_ver \
3107 $user_ip_repos $user_ip_repo_hashes $user_ip_repo_vers]
3116 proc GetSHA {{path ""}} {
3118 lassign [
GitRet {log --format=%h --abbrev=7 -1}] status result
3120 return [
string tolower $result]
3122 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3128 set repo_path [
lindex [
Git {rev-parse --show-toplevel}] 0]
3132 set file_in_module 0
3133 if {[
file exists $repo_path/.gitmodules]} {
3134 lassign [
GitRet "config --file $repo_path/.gitmodules --get-regexp path"] status result
3136 set submodules [
split $result "\n"]
3139 Msg Warning "Something went wrong while trying to find submodules: $result"
3142 foreach mod $submodules {
3143 set module [
lindex $mod 1]
3144 if {[
string first "$repo_path/$module" $f] == 0} {
3146 set file_in_module 1
3147 lappend paths "$repo_path/$module"
3152 if {$file_in_module == 0} {
3158 lassign [
GitRet {log --format=%h --abbrev=7 -1} $paths] status result
3160 return [
string tolower $result]
3162 Msg Error "Something went wrong while finding the latest SHA. Does the repository have a commit?"
3165 return [
string tolower $result]
3169 proc GetSimulators {} {
3170 set SIMULATORS [list "modelsim" "questa" "riviera" "activehdl" "ies" "vcs"]
3175 proc GetTopFile {} {
3177 set compile_order_prop [get_property source_mgmt_mode [current_project]]
3178 if {$compile_order_prop ne "All"} {
3179 Msg CriticalWarning "Compile order is not set to automatic, setting it now..."
3180 set_property source_mgmt_mode All [current_project]
3181 update_compile_order -fileset sources_1
3183 return [
lindex [get_files -quiet -compile_order sources -used_in synthesis -filter {FILE_TYPE =~ "VHDL*" || FILE_TYPE =~ "*Verilog*" }] end]
3184 }
elseif {[
IsISE]} {
3185 debug::design_graph_mgr -create [current_fileset]
3186 debug::design_graph -add_fileset [current_fileset]
3187 debug::design_graph -update_all
3188 return [
lindex [debug::design_graph -get_compile_order] end]
3190 Msg Error "GetTopFile not yet implemented for this IDE"
3195 proc GetTopModule {} {
3197 return [get_property top [current_fileset]]
3199 Msg Error "GetTopModule not yet implemented for this IDE"
3209 proc GetVer {path {force_develop 0}} {
3213 Msg CriticalWarning "Empty SHA found for ${path}. Commit to Git to resolve this warning."
3216 set p [
lindex $path 0]
3217 if {[
file isdirectory $p]} {
3220 cd [
file dirname $p]
3222 set repo_path [
Git {rev-parse --show-toplevel}]
3225 return [list [
GetVerFromSHA $SHA $repo_path $force_develop] $SHA]
3236 proc GetVerFromSHA {SHA repo_path {force_develop 0}} {
3238 Msg CriticalWarning "Empty SHA found"
3241 lassign [
GitRet "tag --sort=creatordate --contain $SHA -l v*.*.* -l b*v*.*.*"] status result
3244 if {[regexp {^ *$} $result]} {
3246 lassign [
GitRet "log --oneline --pretty=\"%d\""] status2 tag_list
3249 set pattern {tag: v\d+\.\d+\.\d+}
3250 set real_tag_list {}
3251 foreach x $tag_list {
3252 set x_untrimmed [regexp -all -inline $pattern $x]
3253 regsub "tag: " $x_untrimmed "" x_trimmed
3254 set tt [
lindex $x_trimmed 0]
3255 if {![
string equal $tt ""]} {
3256 lappend real_tag_list $tt
3260 Msg Debug "Cleaned up list: $real_tag_list."
3262 set sorted_tags [lsort -decreasing -command CompareVersions $real_tag_list]
3264 Msg Debug "Sorted Tag list: $sorted_tags"
3266 set tag [
lindex $sorted_tags 0]
3269 set pattern {v\d+\.\d+\.\d+}
3270 if {![regexp $pattern $tag]} {
3271 Msg CriticalWarning "No Hog version tags found in this repository."
3276 set repo_conf $repo_path/Top/repo.conf
3280 set hotfix_prefix "hotfix/"
3281 set minor_prefix "minor_version/"
3282 set major_prefix "major_version/"
3284 set enable_develop_branch $force_develop
3286 set branch_name [
Git {rev-parse --abbrev-ref HEAD}]
3288 if {[
file exists $repo_conf]} {
3289 set PROPERTIES [
ReadConf $repo_conf]
3291 if {[dict exists $PROPERTIES main]} {
3292 set mainDict [dict get $PROPERTIES main]
3295 if {[dict exists $mainDict ENABLE_DEVELOP_BRANCH]} {
3296 set enable_develop_branch [dict get $mainDict ENABLE_DEVELOP_BRANCH]
3302 if {[dict exists $PROPERTIES prefixes]} {
3303 set prefixDict [dict get $PROPERTIES prefixes]
3305 if {[dict exists $prefixDict HOTFIX]} {
3306 set hotfix_prefix [dict get $prefixDict HOTFIX]
3308 if {[dict exists $prefixDict MINOR_VERSION]} {
3309 set minor_prefix [dict get $prefixDict MINOR_VERSION]
3311 if {[dict exists $prefixDict MAJOR_VERSION]} {
3312 set major_prefix [dict get $prefixDict MAJOR_VERSION]
3318 if {$enable_develop_branch == 1} {
3319 if {[
string match "$hotfix_prefix*" $branch_name]} {
3324 if {[
string match "$major_prefix*" $branch_name]} {
3326 set version_level major
3327 }
elseif {[
string match "$minor_prefix*" $branch_name] || ($enable_develop_branch == 1 && $is_hotfix == 0)} {
3329 set version_level minor
3332 set version_level patch
3336 Msg CriticalWarning "Tag $tag does not contain a Hog compatible version in this repository."
3339 }
elseif {$mr == 0} {
3340 switch $version_level {
3355 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."
3361 set vers [
split $result "\n"]
3362 set ver [
lindex $vers 0]
3364 if {[regexp {^v.*$} $v]} {
3372 Msg CriticalWarning "Error while trying to find tag for $SHA"
3380 set M [
format %02X $M]
3381 set m [
format %02X $m]
3382 set c [
format %04X $c]
3383 }
elseif {$M > -1} {
3385 set M [
format %02X $M]
3386 set m [
format %02X $m]
3387 set c [
format %04X $c]
3389 Msg Warning "Tag does not contain a properly formatted version: $ver"
3390 set M [
format %02X 0]
3391 set m [
format %02X 0]
3392 set c [
format %04X 0]
3405 proc Git {command {files ""}} {
3406 lassign [
GitRet $command $files] ret result
3408 Msg Error "Code $ret returned by git running: $command -- $files"
3419 proc GetModuleName {filename} {
3421 if {![
file exists $filename]} {
3422 Msg CriticalWarning "Error: File $filename does not exist."
3427 set fileId [open $filename r]
3430 set file_content [read $fileId]
3436 if {[
file extension $filename] == ".vhd" || [
file extension $filename] == ".vhdl"} {
3438 set file_content [
string tolower $file_content]
3440 set pattern {(?m)^\s*entity\s+(\S+)\s+is}
3441 }
elseif {[
file extension $filename] == ".v" || [
file extension $filename] == ".sv"} {
3443 set pattern {\n\s*module\s*(\w+)(\s*|\(|\n)}
3445 Msg Debug "File is neither VHDL nor Verilog... Returning empty string..."
3450 if {[regexp $pattern $file_content match module_name]} {
3453 Msg Debug "No module was found in $filename. Returning an empty string..."
3461 proc GetVerilogGenerics {file} {
3462 set fp [open $file r]
3468 foreach line [
split $data "\n"] {
3469 regsub "^\\s*\/\/.*" $line "" line
3470 regsub "(.*)\/\/.*" $line {\1} line
3471 if {![
string equal $line ""]} {
3472 append lines $line " "
3477 regsub -all {/\*.*\*/} $lines "" lines
3480 set punctuation [list]
3481 foreach char [list "(" ")" ";" "," " " "!" "<=" ":=" "=" "\[" "\]"] {
3482 lappend punctuation $char "\000$char\000"
3486 set tokens [
split [
string map $punctuation $lines] \000]
3488 set parameters [dict create]
3497 foreach token $tokens {
3498 set token [
string trim $token]
3499 if {![
string equal "" $token]} {
3500 if {[
string equal [
string tolower $token] "parameter"]} {
3501 set state $PARAM_NAME
3502 }
elseif {[
string equal $token ")"] || [
string equal $token ";"]} {
3504 }
elseif {$state == $PARAM_WIDTH} {
3505 if {[
string equal $token "\]"]} {
3506 set state $PARAM_NAME
3508 }
elseif {$state == $PARAM_VALUE} {
3509 if {[
string equal $token ","]} {
3510 set state $PARAM_NAME
3511 }
elseif {[
string equal $token ";"]} {
3516 }
elseif {$state == $PARAM_NAME} {
3517 if {[
string equal $token "="]} {
3518 set state $PARAM_VALUE
3519 }
elseif {[
string equal $token "\["]} {
3520 set state $PARAM_WIDTH
3521 }
elseif {[
string equal $token ","]} {
3522 set state $PARAM_NAME
3523 }
elseif {[
string equal $token ";"]} {
3525 }
elseif {[
string equal $token ")"]} {
3528 dict set parameters $token "integer"
3541 proc GetVhdlGenerics {file {entity ""}} {
3542 set fp [open $file r]
3548 foreach line [
split $data "\n"] {
3549 regsub "^\\s*--.*" $line "" line
3550 regsub "(.*)--.*" $line {\1} line
3551 if {![
string equal $line ""]} {
3552 append lines $line " "
3557 set generic_block ""
3558 set generics [dict create]
3560 if {1 == [
string equal $entity ""]} {
3561 regexp {(?i).*entity\s+([^\s]+)\s+is} $lines _ entity
3564 set generics_regexp "(?i).*entity\\s+$entity\\s+is\\s+generic\\s*\\((.*)\\)\\s*;\\s*port.*end.*$entity"
3566 if {[regexp $generics_regexp $lines _ generic_block]} {
3568 foreach line [
split $generic_block ";"] {
3570 regexp {(.*):\s*([A-Za-z0-9_]+).*} $line _ generic type
3573 set splits [
split $generic ","]
3574 foreach split $splits {
3575 dict set generics [
string trim $split] [
string trim $type]
3583 proc GHDL {command logfile} {
3584 set ret [
catch {
exec -ignorestderr ghdl {*}$command >>& $logfile} result options]
3589 return [list $ret $result]
3601 proc GitRet {command {files ""}} {
3604 set ret [
catch {
exec -ignorestderr git {*}$command} result]
3606 set ret [
catch {
exec -ignorestderr git {*}$command -- {*}$files} result]
3608 return [list $ret $result]
3616 proc GitVersion {target_version} {
3617 set ver [
split $target_version "."]
3618 set v [
Git --version]
3620 set current_ver [
split [
lindex $v 2] "."]
3621 set target [
expr {[lindex $ver 0] * 100000 + [lindex $ver 1] * 100 + [lindex $ver 2]}]
3622 set current [
expr {[lindex $current_ver 0] * 100000 + [lindex $current_ver 1] * 100 + [lindex $current_ver 2]}]
3623 return [
expr {$target <= $current}]
3637 proc HandleIP {what_to_do xci_file ip_path repo_path {gen_dir "."} {force 0}} {
3638 if {!($what_to_do eq "push") && !($what_to_do eq "pull")} {
3639 Msg Error "You must specify push or pull as first argument."
3642 if {[
catch {
package require tar} TARPACKAGE]} {
3643 Msg CriticalWarning "Cannot find package tar. You can fix this by installing package \"tcllib\""
3652 if {[
string first "/eos/" $ip_path] == 0} {
3660 lassign [
eos "ls $ip_path"] ret result
3662 Msg CriticalWarning "Could not run ls for for EOS path: $ip_path (error: $result). \
3663 Either the drectory does not exist or there are (temporary) problem with EOS."
3667 Msg Info "IP remote directory path, on EOS, is set to: $ip_path"
3673 if {!([
file exists $xci_file])} {
3674 Msg CriticalWarning "Could not find $xci_file."
3680 set xci_path [
file dirname $xci_file]
3681 set xci_name [
file tail $xci_file]
3682 set xci_ip_name [
file rootname [
file tail $xci_file]]
3683 set xci_dir_name [
file tail $xci_path]
3684 set gen_path $gen_dir
3686 set hash [
Md5Sum $xci_file]
3687 set file_name $xci_name\_$hash
3689 Msg Info "Preparing to $what_to_do IP: $xci_name..."
3691 if {$what_to_do eq "push"} {
3695 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
3700 Msg Info "IP already in the EOS repository, will not copy..."
3702 Msg Info "IP already in the EOS repository, will forcefully replace..."
3708 if {[
file exists "$ip_path/$file_name.tar"]} {
3710 Msg Info "IP already in the local repository, will not copy..."
3712 Msg Info "IP already in the local repository, will forcefully replace..."
3721 if {$will_copy == 1} {
3723 Msg Info "Looking for generated files in $gen_path..."
3724 set ip_gen_files [glob -nocomplain $gen_path/*]
3728 if {[
llength $ip_gen_files] > 0} {
3729 Msg Info "Found some IP synthesised files matching $xci_ip_name"
3730 if {$will_remove == 1} {
3731 Msg Info "Removing old synthesised directory $ip_path/$file_name.tar..."
3733 eos "rm -rf $ip_path/$file_name.tar" 5
3735 file delete -force "$ip_path/$file_name.tar"
3739 Msg Info "Creating local archive with IP generated files..."
3741 foreach f $ip_gen_files {
3742 if {$first_file == 0} {
3743 ::tar::create $file_name.tar "[
Relative [
file normalize $repo_path] $f]"
3746 ::tar::add $file_name.tar "[
Relative [
file normalize $repo_path] $f]"
3750 Msg Info "Copying IP generated files for $xci_name..."
3752 lassign [
ExecuteRet xrdcp -f -s $file_name.tar $::env(EOS_MGM_URL)//$ip_path/] ret msg
3754 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
3757 Copy "$file_name.tar" "$ip_path/"
3759 Msg Info "Removing local archive"
3760 file delete $file_name.tar
3762 Msg Warning "Could not find synthesized files matching $gen_path/$file_name*"
3765 }
elseif {$what_to_do eq "pull"} {
3767 lassign [
eos "ls $ip_path/$file_name.tar"] ret result
3769 Msg Info "Nothing for $xci_name was found in the EOS repository, cannot pull."
3773 set remote_tar "$::env(EOS_MGM_URL)//$ip_path/$file_name.tar"
3774 Msg Info "IP $xci_name found in the repository $remote_tar, copying it locally to $repo_path..."
3776 lassign [
ExecuteRet xrdcp -f -r -s $remote_tar $repo_path] ret msg
3778 Msg CriticalWarning "Something went wrong when copying the IP files to EOS. Error message: $msg"
3782 if {[
file exists "$ip_path/$file_name.tar"]} {
3783 Msg Info "IP $xci_name found in local repository $ip_path/$file_name.tar, copying it locally to $repo_path..."
3784 Copy $ip_path/$file_name.tar $repo_path
3786 Msg Info "Nothing for $xci_name was found in the local IP repository, cannot pull."
3792 if {[
file exists $file_name.tar]} {
3793 remove_files $xci_file
3794 Msg Info "Extracting IP files from archive to $repo_path..."
3795 ::tar::untar $file_name.tar -dir $repo_path -noperms
3796 Msg Info "Removing local archive"
3797 file delete $file_name.tar
3798 add_files -norecurse -fileset sources_1 $xci_file
3811 proc HexVersionToString {version} {
3812 scan [
string range $version 0 1] %x M
3813 scan [
string range $version 2 3] %x m
3814 scan [
string range $version 4 7] %x c
3819 proc ImportTclLib {} {
3821 if {[
info exists env(HOG_TCLLIB_PATH)]} {
3822 lappend auto_path $env(HOG_TCLLIB_PATH)
3825 puts "ERROR: To run Hog with Microsemi Libero SoC or Lattice Diamond, you need to define the HOG_TCLLIB_PATH variable."
3840 proc InitLauncher {script tcl_path parameters commands argv {custom_commands ""}} {
3841 set repo_path [
file normalize "$tcl_path/../.."]
3843 set bin_path [
file normalize "$tcl_path/../../bin"]
3844 set top_path [
file normalize "$tcl_path/../../Top"]
3846 set cmd_lines [
split $commands "\n"]
3848 set command_options [dict create]
3849 set directive_descriptions [dict create]
3850 set directive_names [dict create]
3851 set common_directive_names [dict create]
3853 foreach l $cmd_lines {
3855 if {[regexp {\\(.*) \{\#} $l minc d]} {
3856 lappend directives_with_projects $d
3860 if {[regexp {\\(.*) \{} $l minc regular_expression]} {
3861 lappend directive_regex $regular_expression
3865 if {[regexp {\#\s*NAME(\*)?:\s*(.*)\s*} $l minc star name]} {
3866 dict set directive_names $name $regular_expression
3868 dict set common_directive_names $name $regular_expression
3871 set directive_names [
DictSort $directive_names]
3872 set common_directive_names [
DictSort $common_directive_names]
3875 if {[regexp {\#\s*DESCRIPTION:\s*(.*)\s*} $l minc x]} {
3876 dict set directive_descriptions $regular_expression $x
3880 if {[regexp {\#\s*OPTIONS:\s*(.*)\s*} $l minc x]} {
3881 dict set command_options $regular_expression [
split [regsub -all {[ \t\n]+} $x {}] ","]
3885 set short_usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nMost common directives (case insensitive):"
3887 dict for {key value} $common_directive_names {
3888 set short_usage "$short_usage\n - $key: [dict get $directive_descriptions $value]"
3891 set short_usage "$short_usage\n\n\
3892 To see all the available directives, run:\n./Hog/Do HELP\n\n\
3893 To list available options for the chosen directive run:\n\
3894 ./Hog/Do <directive> HELP\n
3897 set usage "usage: ./Hog/Do \[OPTIONS\] <directive> \[project\]\n\nDirectives (case insensitive):"
3899 dict for {key value} $directive_names {
3900 set usage "$usage\n - $key: [dict get $directive_descriptions $value]"
3903 set usage "$usage\n$custom_commands"
3905 set usage "$usage\n\nTo list available options for the chosen directive run:\n./Hog/Do <directive> HELP"
3914 Msg Debug "HogEnv.conf found"
3922 if {[
catch {
package require cmdline} ERROR]} {
3923 Msg Debug "The cmdline Tcl package was not found, sourcing it from Hog..."
3924 source $tcl_path/utils/cmdline.tcl
3927 set argv [regsub -all {(?i) HELP\y} $argv " -help"]
3929 lassign [
GetOptions $argv $parameters] option_list arg_list
3931 if {[
IsInList "-all" $option_list]} {
3940 set directive [
string toupper [
lindex $arg_list 0]]
3943 set argument_is_no_project 1
3945 switch -regexp -- $directive "$commands"
3947 if {[
IsInList $directive $directives_with_projects 1]} {
3948 set argument_is_no_project 0
3952 if {$directive != ""} {
3953 if {[
IsInList $directive $directives_with_projects 1]} {
3954 puts "usage: ./Hog/Do \[OPTIONS\] $directive <project>\n"
3955 }
elseif {[regexp "^COMPSIM(LIB)?$" $directive]} {
3956 puts "usage: ./Hog/Do \[OPTIONS\] $directive <simulator>\n"
3958 puts "usage: ./Hog/Do \[OPTIONS\] $directive \n"
3961 dict for {dir desc} $directive_descriptions {
3962 if {[regexp $dir $directive]} {
3968 dict for {dir opts} $command_options {
3969 if {[regexp $dir $directive]} {
3970 puts "Available options:"
3972 foreach par $parameters {
3973 if {$opt == [lindex $par 0]} {
3974 if {[regexp {\.arg$} $opt]} {
3975 set opt_name [regsub {\.arg$} $opt ""]
3976 puts " -$opt_name <argument>"
3980 puts " [lindex $par [llength $par]-1]"
3994 if {[
catch {
array set options [
cmdline::getoptions option_list $parameters $usage]} err]} {
3995 Msg Status "\nERROR: Syntax error, probably unknown option.\n\n USAGE: $err"
3999 if {[
llength $arg_list] <= $min_n_of_args || [
llength $arg_list] > $max_n_of_args} {
4000 Msg Status "\nERROR: Wrong number of arguments: [
llength $argv]"
4005 set project [
lindex $arg_list 1]
4007 if {$argument_is_no_project == 0} {
4009 regsub "^(\./)?Top/" $project "" project
4011 regsub "/? *\$" $project "" project
4017 Msg Debug "Option list:"
4018 foreach {key value} [
array get options] {
4019 Msg Debug "$key => $value"
4026 if {$proj_conf != 0} {
4029 lassign [
GetIDECommand $proj_conf] cmd before_tcl_script after_tcl_script end_marker
4030 Msg Info "Project $project uses $cmd IDE"
4033 set command "$cmd $before_tcl_script$script$after_tcl_script$argv$end_marker"
4035 if {$argument_is_no_project == 1} {
4037 Msg Debug "$project will be used as first argument"
4038 }
elseif {$project != ""} {
4041 }
elseif {$min_n_of_args < 0} {
4054 set project_group [
file dirname $project]
4055 set project_name $project
4056 set project [
file tail $project]
4057 Msg Debug "InitLauncher: project_group=$project_group, project_name=$project_name, project=$project"
4059 return [list $directive $project $project_name $project_group $repo_path $old_path $bin_path $top_path $usage $short_usage $command $cmd [
array get options]]
4066 proc IsCommitAncestor {ancestor commit} {
4067 lassign [
GitRet "merge-base --is-ancestor $ancestor $commit"] status result
4076 return [
expr {[info commands sys_install] != ""}]
4081 return [
expr {[info commands get_libero_version] != ""}]
4090 proc IsInList {element list {regex 0} {nocase 0}} {
4094 if {[regexp -nocase $x $element]} {
4098 if {[regexp $x $element]} {
4102 }
elseif {$regex == 0} {
4104 if {[
string tolower $x] eq [
string tolower $element]} {
4108 if {$x eq $element} {
4121 return [
expr {[string first PlanAhead [version]] == 0}]
4129 if {[
catch {
package require ::quartus::flow} result]} {
4142 proc IsRelativePath {path} {
4143 if {[
string index $path 0] == "/" || [
string index $path 0] == "~"} {
4151 proc IsSynplify {} {
4152 return [
expr {[info commands program_version] != ""}]
4157 return [
expr {![IsQuartus] && ![IsXilinx] && ![IsVitisClassic] && ![IsLibero] && ![IsSynplify] && ![IsDiamond]}]
4165 proc IsVersal {part} {
4166 if {[get_property ARCHITECTURE [get_parts $part]] eq "versal"} {
4176 return [
expr {[string first Vivado [version]] == 0}]
4184 if {[
info commands version] != ""} {
4185 set current_version [version]
4186 if {[
string first PlanAhead $current_version] == 0 || [
string first Vivado $current_version] == 0} {
4188 }
elseif {[
string first xsct $current_version] == 0} {
4191 Msg Warning "This IDE has the version command but it is not PlanAhead or Vivado: $current_version"
4200 proc IsVitisClassic {} {
4201 return [
expr {[info commands platform] != ""}]
4209 proc IsZynq {part} {
4210 if {[regexp {^(xc7z|xczu).*} $part]} {
4217 proc ImportGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
4218 set list_path "$repo_path/Top/$project_name/list"
4219 lassign [
GetHogFiles -list_files {.src,.ext,.sim} -ext_path $ext_path $list_path $repo_path] src_files properties filesets
4224 set properties [
DictGet $simset_dict "properties"]
4225 set options [
DictGet $properties "options"]
4228 set workdir Projects/$project_name/ghdl
4229 file delete -force $workdir
4231 set import_log "$workdir/ghdl-import-${simset_name}.log"
4232 dict for {lib sources} $src_files {
4233 set libname [file rootname $lib]
4234 foreach f $sources {
4235 if {[file extension $f] != ".vhd" && [file extension $f] != ".vhdl"} {
4236 Msg Info "File $f is not a VHDL file, copying it in workfolder..."
4237 file copy -force $f $workdir
4239 set file_path [Relative $repo_path $f]
4240 set import_log_file [open $import_log "a"]
4241 puts "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
4242 puts $import_log_file "ghdl -i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path"
4243 close $import_log_file
4244 lassign [GHDL "-i --work=$libname --workdir=$workdir -fsynopsys --ieee=standard $options $file_path" $import_log] ret result
4246 Msg CriticalWarning "GHDL import failed for file $f: $result"
4255 proc LaunchGHDL {project_name repo_path simset_name simset_dict {ext_path ""}} {
4258 set sim_props [
DictGet $simset_dict "properties"]
4259 set options [
DictGet $sim_props "options"]
4260 set runopts [
DictGet $sim_props "run_options"]
4262 dict for {prop_name prop_val} $sim_props {
4263 set prop_name [string toupper $prop_name]
4264 if {$prop_name == "TOP"} {
4265 set top_sim $prop_val
4268 set workdir $repo_path/Projects/$project_name/ghdl
4269 set make_log "$workdir/ghdl-make-${simset_name}.log"
4270 set run_log "$workdir/ghdl-run-${simset_name}.log"
4273 set make_log_file [open $make_log "w"]
4275 puts "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
4276 puts $make_log_file "ghdl -m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim"
4277 close $make_log_file
4279 lassign [
GHDL "-m --work=$simset_name -fsynopsys --ieee=standard $options $top_sim" $make_log] ret result
4282 Msg Error "GHDL make failed for $top_sim: $result"
4286 set run_log_file [open $run_log "w"]
4287 puts "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
4288 puts $run_log_file "ghdl -r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts"
4291 lassign [
GHDL "-r --work=$simset_name -fsynopsys --ieee=standard $options $top_sim $runopts" $run_log] ret result
4295 Msg Error "GHDL run failed for $top_sim: $result"
4310 proc LaunchImplementation {reset do_create run_folder project_name {repo_path .} {njobs 4} {do_bitstream 0}} {
4311 Msg Info "Starting implementation flow..."
4313 if {$reset == 1 && $do_create == 0} {
4314 Msg Info "Resetting run before launching implementation..."
4319 source $repo_path/Hog/Tcl/integrated/pre-implementation.tcl
4322 if {$do_bitstream == 1} {
4323 launch_runs impl_1 -to_step [
BinaryStepName [get_property PART [current_project]]] -jobs $njobs -dir $run_folder
4325 launch_runs impl_1 -jobs $njobs -dir $run_folder
4330 Msg Info "running post-implementation"
4331 source $repo_path/Hog/Tcl/integrated/post-implementation.tcl
4332 if {$do_bitstream == 1} {
4333 Msg Info "running pre-bitstream"
4334 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
4335 Msg Info "running post-bitstream"
4336 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
4340 set prog [get_property PROGRESS [get_runs impl_1]]
4341 set status [get_property STATUS [get_runs impl_1]]
4342 Msg Info "Run: impl_1 progress: $prog, status : $status"
4346 set status_file [open "$run_folder/timing.txt" "w"]
4347 puts $status_file "## $project_name Timing summary"
4349 set f [open [
lindex [glob "$run_folder/impl_1/*.twr" 0]]]
4351 while {[
gets $f line] >= 0} {
4352 if {[
string match "Timing summary:" $line]} {
4353 while {[
gets $f line] >= 0} {
4354 if {[
string match "Timing errors:*" $line]} {
4355 set errs [regexp -inline -- {[0-9]+} $line]
4357 if {[
string match "*Footnotes*" $line]} {
4360 puts $status_file "$line"
4369 Msg Info "Time requirements are met"
4370 file rename -force "$run_folder/timing.txt" "$run_folder/timing_ok.txt"
4373 Msg CriticalWarning "Time requirements are NOT met"
4374 file rename -force "$run_folder/timing.txt" "$run_folder/timing_error.txt"
4380 set wns [get_property STATS.WNS [get_runs [current_run]]]
4381 set tns [get_property STATS.TNS [get_runs [current_run]]]
4382 set whs [get_property STATS.WHS [get_runs [current_run]]]
4383 set ths [get_property STATS.THS [get_runs [current_run]]]
4384 set tpws [get_property STATS.TPWS [get_runs [current_run]]]
4386 if {$wns >= 0 && $whs >= 0 && $tpws >= 0} {
4387 Msg Info "Time requirements are met"
4388 set status_file [open "$run_folder/timing_ok.txt" "w"]
4391 Msg CriticalWarning "Time requirements are NOT met"
4392 set status_file [open "$run_folder/timing_error.txt" "w"]
4396 Msg Status "*** Timing summary ***"
4397 Msg Status "WNS: $wns"
4398 Msg Status "TNS: $tns"
4399 Msg Status "WHS: $whs"
4400 Msg Status "THS: $ths"
4401 Msg Status "TPWS: $tpws"
4407 puts $status_file "## $project_name Timing summary"
4409 m add row "| **Parameter** | \"**value (ns)**\" |"
4410 m add row "| --- | --- |"
4411 m add row "| WNS: | $wns |"
4412 m add row "| TNS: | $tns |"
4413 m add row "| WHS: | $whs |"
4414 m add row "| THS: | $ths |"
4415 m add row "| TPWS: | $tpws |"
4417 puts $status_file [m format 2string]
4418 puts $status_file "\n"
4419 if {$timing_ok == 1} {
4420 puts $status_file " Time requirements are met."
4422 puts $status_file "Time requirements are **NOT** met."
4424 puts $status_file "\n\n"
4428 if {$prog ne "100%"} {
4429 Msg Error "Implementation error"
4434 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
4436 Msg Info "Git describe set to $describe"
4438 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
4443 if {[
file exists $run_folder/versions.txt]} {
4444 file copy -force $run_folder/versions.txt $dst_dir
4446 Msg Warning "No versions file found in $run_folder/versions.txt"
4449 set timing_files [glob -nocomplain "$run_folder/timing_*.txt"]
4450 set timing_file [
file normalize [
lindex $timing_files 0]]
4452 if {[
file exists $timing_file]} {
4453 file copy -force $timing_file $dst_dir/
4455 Msg Warning "No timing file found, not a problem if running locally"
4459 if {[
IsVersal [get_property part [current_project]]]} {
4460 if {[get_property segmented_configuration [current_project]] == 1} {
4461 Msg Info "Versal Segmented configuration detected: exporting XSA file..."
4462 set xsa_name "$dst_dir/[
file tail $project_name]\-$describe.xsa"
4463 write_hw_platform -fixed -force -file $xsa_name
4467 set revision [get_current_revision]
4469 if {[
catch {execute_module -tool fit} result]} {
4470 Msg Error "Result: $result\n"
4471 Msg Error "Place & Route failed. See the report file.\n"
4473 Msg Info "\nINFO: Place & Route was successful for revision $revision.\n"
4476 if {[
catch {execute_module -tool sta -args "--do_report_timing"} result]} {
4477 Msg Error "Result: $result\n"
4478 Msg Error "Time Quest failed. See the report file.\n"
4480 Msg Info "Time Quest was successfully run for revision $revision.\n"
4483 set panel "Timing Analyzer||Timing Analyzer Summary"
4484 set device [get_report_panel_data -name $panel -col 1 -row_name "Device Name"]
4485 set timing_model [get_report_panel_data -name $panel -col 1 -row_name "Timing Models"]
4486 set delay_model [get_report_panel_data -name $panel -col 1 -row_name "Delay Model"]
4488 Msg Info "*******************************************************************"
4489 Msg Info "Device: $device"
4490 Msg Info "Timing Models: $timing_model"
4491 Msg Info "Delay Model: $delay_model"
4494 Msg Info "*******************************************************************"
4497 Msg Info "Starting implementation flow..."
4498 if {[
catch {run_tool -name {PLACEROUTE}}]} {
4499 Msg Error "PLACEROUTE FAILED!"
4501 Msg Info "PLACEROUTE PASSED."
4505 Msg Info "Run VERIFYTIMING ..."
4506 if {[
catch {run_tool -name {VERIFYTIMING} -script {Hog/Tcl/integrated/libero_timing.tcl}}]} {
4507 Msg CriticalWarning "VERIFYTIMING FAILED!"
4509 Msg Info "VERIFYTIMING PASSED \n"
4515 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
4517 Msg Info "Git describe set to $describe"
4519 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
4520 file mkdir $dst_dir/reports
4523 if {[
file exists $run_folder/versions.txt]} {
4524 file copy -force $run_folder/versions.txt $dst_dir
4526 Msg Warning "No versions file found in $run_folder/versions.txt"
4529 set timing_file_path [
file normalize "$repo_path/Projects/timing_libero.txt"]
4530 if {[
file exists $timing_file_path]} {
4531 file copy -force $timing_file_path $dst_dir/reports/Timing.txt
4532 set timing_file [open $timing_file_path "r"]
4533 set status_file [open "$dst_dir/timing.txt" "w"]
4534 puts $status_file "## $project_name Timing summary\n\n"
4535 puts $status_file "| | |"
4536 puts $status_file "| --- | --- |"
4537 while {[
gets $timing_file line] >= 0} {
4538 if {[
string match "SUMMARY" $line]} {
4539 while {[
gets $timing_file line] >= 0} {
4540 if {[
string match "END SUMMARY" $line]} {
4543 if {[
string first ":" $line] == -1} {
4546 set out_string "| [
string map {: | } $line] |"
4547 puts $status_file "$out_string"
4552 Msg Warning "No timing file found, not a problem if running locally"
4557 set force_rst "-forceOne"
4559 prj_run Map $force_rst
4560 prj_run PAR $force_rst
4572 proc GenerateBitstreamOnly {project_name {repo_path .}} {
4574 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
4576 set dst_dir [
file normalize "$repo_path/bin/$project_name\-$describe"]
4578 cd Projects/$project_name/$project_name.runs/impl_1
4579 Msg Info "Running pre-bitstream..."
4580 source $repo_path/Hog/Tcl/integrated/pre-bitstream.tcl
4582 Msg Info "Writing bitstream for $project_name..."
4584 write_bitstream -force $dst_dir/$project_name-$describe.bit
4586 Msg Info "Running post-bitstream..."
4587 source $repo_path/Hog/Tcl/integrated/post-bitstream.tcl
4596 proc LaunchSimulation {project_name lib_path simsets {repo_path .} {scripts_only 0} {compile_only 0}} {
4599 set project [
file tail $project_name]
4600 set main_sim_folder [
file normalize "$repo_path/Projects/$project_name/$project.sim/"]
4602 if {$simsets != ""} {
4603 dict for {simset sim_dict} $simsets {
4604 lappend simsets_todo $simset
4606 Msg Info "Will run only the following simulation's sets (if they exist): $simsets_todo"
4609 if {$scripts_only == 1} {
4610 Msg Info "Only generating simulation scripts, not running simulations..."
4613 if {$compile_only == 1} {
4614 Msg Info "Only compiling simulation libraries, not running simulations..."
4619 set sim_dic [dict create]
4621 Msg Info "Retrieving list of simulation sets..."
4622 foreach s [get_filesets] {
4624 set use_simpass_str 0
4627 set type [get_property FILESET_TYPE $s]
4628 if {$type eq "SimulationSrcs"} {
4629 if {$simsets_todo != "" && $s ni $simsets_todo} {
4630 Msg Info "Skipping $s as it was not specified with the -simset option..."
4633 set sim_dict [
DictGet $simsets $s]
4634 set simulator [
DictGet $sim_dict "simulator"]
4635 set_property "target_simulator" $simulator [current_project]
4636 set hog_sim_props [
DictGet $sim_dict "hog"]
4637 dict for {prop_name prop_val} $hog_sim_props {
4638 # If HOG_SIMPASS_STR is set, use the HOG_SIMPASS_STR string to search for in logs, after simulation is done
4639 if {[string toupper $prop_name] == "HOG_SIMPASS_STR" && $prop_val != ""} {
4640 Msg Info "Setting simulation pass string as '$prop_val'"
4641 set use_simpass_str 1
4642 set simpass_str $prop_val
4644 if {[string toupper $prop_name] == "HOG_SILENT_SIM" && $prop_val == 1} {
4645 set quiet_sim " -quiet"
4651 Msg Info "Creating simulation scripts for $s..."
4652 if {[
file exists $repo_path/Top/$project_name/pre-simulation.tcl]} {
4653 Msg Info "Running $repo_path/Top/$project_name/pre-simulation.tcl"
4654 source $repo_path/Top/$project_name/pre-simulation.tcl
4656 if {[
file exists $repo_path/Top/$project_name/pre-$s-simulation.tcl]} {
4657 Msg Info "Running $repo_path/Top/$project_name/pre-$s-simulation.tcl"
4658 source Running $repo_path/Top/$project_name/pre-$s-simulation.tcl
4660 current_fileset -simset $s
4661 set sim_dir $main_sim_folder/$s/behav
4662 set sim_output_logfile $sim_dir/xsim/simulate.log
4663 if {([
string tolower $simulator] eq "xsim")} {
4664 set sim_name "xsim:$s"
4666 set simulation_command "launch_simulation $quiet_sim -simset [get_filesets $s]"
4667 if {[
catch $simulation_command log]} {
4670 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
4671 lappend failed $sim_name
4676 if {$use_simpass_str == 1} {
4679 set file_desc [open $sim_output_logfile r]
4680 set log [read $file_desc]
4683 Msg Info "Searching for simulation pass string: '$simpass_str'"
4684 if {[
string first $simpass_str $log] == -1} {
4685 Msg CriticalWarning "Simulation failed for $s, error info: '$simpass_str' NOT found!"
4686 lappend failed $sim_name
4689 lappend success $sim_name
4693 lappend success $sim_name
4697 Msg Info "Simulation library path is set to $lib_path."
4699 if {!([
file exists $lib_path])} {
4700 Msg Warning "Could not find simulation library path: $lib_path, $simulator simulation will not work."
4704 if {$simlib_ok == 1} {
4705 set_property "compxlib.${simulator}_compiled_library_dir" [
file normalize $lib_path] [current_project]
4706 launch_simulation -scripts_only -simset [get_filesets $s]
4707 set top_name [get_property TOP $s]
4708 set sim_script [
file normalize $sim_dir/$simulator/]
4709 Msg Info "Adding simulation script location $sim_script for $s..."
4710 lappend sim_scripts $sim_script
4711 dict append sim_dic $sim_script $s
4713 Msg Error "Cannot run $simulator simulations without a valid library path"
4720 if {[
info exists sim_scripts] && $scripts_only == 0} {
4722 Msg Info "Generating IP simulation targets, if any..."
4724 foreach ip [get_ips] {
4725 generate_target simulation -quiet $ip
4730 Msg Info "====== Starting simulations runs ======"
4733 foreach s $sim_scripts {
4735 set cmd ./compile.sh
4736 Msg Info " ************* Compiling: $s ************* "
4738 set sim_name "comp:[dict get $sim_dic $s]"
4740 Msg CriticalWarning "Compilation failed for $s, error info: $::errorInfo"
4741 lappend failed $sim_name
4743 lappend success $sim_name
4745 Msg Info "###################### Compilation log starts ######################"
4746 Msg Info "\n\n$log\n\n"
4747 Msg Info "###################### Compilation log ends ######################"
4749 if {$compile_only == 1} {
4752 if {[
file exists "./elaborate.sh"] } {
4753 set cmd ./elaborate.sh
4754 Msg Info " ************* Elaborating: $s ************* "
4756 set sim_name "elab:[dict get $sim_dic $s]"
4758 Msg CriticalWarning "Elaboration failed for $s, error info: $::errorInfo"
4759 lappend failed $sim_name
4761 lappend success $sim_name
4763 Msg Info "###################### Elaboration log starts ######################"
4764 Msg Info "\n\n$log\n\n"
4765 Msg Info "###################### Elaboration log ends ######################"
4767 set cmd ./simulate.sh
4768 Msg Info " ************* Simulating: $s ************* "
4773 if {$use_simpass_str == 1} {
4774 if {[
string first $simpass_str $log] == -1} {
4778 Msg Debug "Simulation pass string not set, relying on simulator exit code."
4782 set sim_name "sim:[dict get $sim_dic $s]"
4784 Msg CriticalWarning "Simulation failed for $s, error info: $::errorInfo"
4785 lappend failed $sim_name
4787 lappend success $sim_name
4789 Msg Info "###################### Simulation log starts ######################"
4790 Msg Info "\n\n$log\n\n"
4791 Msg Info "###################### Simulation log ends ######################"
4796 if {[
llength $success] > 0} {
4797 set successes [
join $success "\n"]
4798 Msg Info "The following simulation sets were successful:\n\n$successes\n\n"
4801 if {[
llength $failed] > 0} {
4802 set failures [
join $failed "\n"]
4803 Msg Error "The following simulation sets have failed:\n\n$failures\n\n"
4805 }
elseif {[
llength $success] > 0} {
4806 Msg Info "All the [
llength $success] compilations, elaborations and simulations were successful."
4809 Msg Info "Simulation done."
4811 Msg Warning "Simulation is not yet supported for [
GetIDEName]."
4822 proc LaunchRTLAnalysis {} {
4824 Msg Info "Starting RTL Analysis..."
4825 synth_design -rtl -name rtl_1
4838 proc LaunchSynthesis {reset do_create run_folder project_name {repo_path .} {ext_path ""} {njobs 4}} {
4840 if {$reset == 1 && $do_create == 0} {
4841 Msg Info "Resetting run before launching synthesis..."
4845 source $repo_path/Hog/Tcl/integrated/pre-synthesis.tcl
4847 launch_runs synth_1 -jobs $njobs -dir $run_folder
4849 set prog [get_property PROGRESS [get_runs synth_1]]
4850 set status [get_property STATUS [get_runs synth_1]]
4851 Msg Info "Run: synth_1 progress: $prog, status : $status"
4858 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path $ext_path] sha
4860 Msg Info "Git describe set to $describe"
4863 set xci_file [get_property IP_FILE $ip]
4865 set xci_path [
file dirname $xci_file]
4866 set xci_ip_name [
file rootname [
file tail $xci_file]]
4867 foreach rptfile [glob -nocomplain -directory $xci_path *.rpt] {
4868 file copy $rptfile $repo_path/bin/$project_name-$describe/reports
4872 if {$prog ne "100%"} {
4873 Msg Error "Synthesis error, status is: $status"
4877 set project [
file tail [
file rootname $project_name]]
4879 Msg Info "Number of jobs set to $njobs."
4880 set_global_assignment -name NUM_PARALLEL_PROCESSORS $njobs
4884 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$project_name] $repo_path] sha
4887 set revision [get_current_revision]
4890 set tool_and_command [
split [get_global_assignment -name PRE_FLOW_SCRIPT_FILE] ":"]
4891 set tool [
lindex $tool_and_command 0]
4892 set pre_flow_script [
lindex $tool_and_command 1]
4893 set cmd "$tool -t $pre_flow_script quartus_map $project $revision"
4899 Msg Warning "Can not execute command $cmd"
4900 Msg Warning "LOG: $log"
4902 Msg Info "Pre flow script executed!"
4906 if {![is_project_open]} {
4907 Msg Info "Re-opening project file $project_name..."
4908 project_open $project -current_revision
4912 if {[
catch {execute_module -tool ipg -args "--clean"} result]} {
4913 Msg Error "Result: $result\n"
4914 Msg Error "IP Generation failed. See the report file.\n"
4916 Msg Info "IP Generation was successful for revision $revision.\n"
4920 if {[
catch {execute_module -tool map -args "--parallel"} result]} {
4921 Msg Error "Result: $result\n"
4922 Msg Error "Analysis & Synthesis failed. See the report file.\n"
4924 Msg Info "Analysis & Synthesis was successful for revision $revision.\n"
4928 defvar_set -name RWNETLIST_32_64_MIXED_FLOW -value 0
4930 Msg Info "Run SYNTHESIS..."
4931 if {[
catch {run_tool -name {SYNTHESIZE}}]} {
4932 Msg Error "SYNTHESIZE FAILED!"
4934 Msg Info "SYNTHESIZE PASSED!"
4940 set force_rst "-forceOne"
4942 prj_run Synthesis $force_rst
4943 if {[prj_syn] == "synplify"} {
4944 prj_run Translate $force_rst
4947 Msg Error "Impossible condition. You need to run this in an IDE."
4958 proc LaunchVitisBuild {project_name {repo_path .} {stage "presynth"}} {
4959 set proj_name $project_name
4960 set bin_dir [
file normalize "$repo_path/bin"]
4964 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
4965 lassign [
GetRepoVersions [
file normalize $repo_path/Top/$proj_name] $repo_path] commit version hog_hash hog_ver top_hash top_ver \
4966 libs hashes vers cons_ver cons_hash ext_names ext_hashes xml_hash xml_ver user_ip_repos user_ip_hashes user_ip_vers
4968 if {$commit == 0 } {
set commit $this_commit}
4972 foreach app_name [dict keys $ws_apps] {
4973 app config -name $app_name -set build-config Release
4976 WriteGenerics "vitisbuild" $repo_path $proj_name $date $timee $commit $version $top_hash $top_ver $hog_hash $hog_ver $cons_ver $cons_hash $libs \
4977 $vers $hashes $ext_names $ext_hashes $user_ip_repos $user_ip_vers $user_ip_hashes $flavour $xml_ver $xml_hash
4978 foreach app_name [dict keys $ws_apps] { app build -name $app_name}
4980 if {$stage == "presynth"} {
4981 Msg Info "Done building apps for $project_name..."
4985 Msg Info "Evaluating Git sha for $project_name..."
4986 lassign [
GetRepoVersions [
file normalize ./Top/$project_name] $repo_path] sha
4989 Msg Info "Hog describe set to: $describe"
4990 set dst_dir [
file normalize "$bin_dir/$proj_name\-$describe"]
4991 if {![
file exists $dst_dir]} {
4992 Msg Info "Creating $dst_dir..."
4996 foreach app_name [dict keys $ws_apps] {
4997 set main_file "$repo_path/Projects/$project_name/vitis_classic/$app_name/Release/$app_name.elf"
4998 set dst_main [
file normalize "$dst_dir/[
file tail $proj_name]\-$app_name\-$describe.elf"]
5000 if {![
file exists $main_file]} {
5001 Msg Error "No Vitis .elf file found. Perhaps there was an issue building it?"
5005 Msg Info "Copying main binary file $main_file into $dst_main..."
5006 file copy -force $main_file $dst_main
5015 proc GetProcFromProps {repo_path props platform} {
5016 if {[dict exists $props "platform:$platform" "BIF"]} {
5017 set bif_file [dict get $props "platform:$platform" "BIF"]
5019 set bif_file "$repo_path/$bif_file"
5023 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
5033 proc GetBifFromProps {repo_path props platform} {
5034 if {[dict exists $props "platform:$platform" "BIF"]} {
5035 set bif_file [dict get $props "platform:$platform" "BIF"]
5037 set bif_file "$repo_path/$bif_file"
5041 Msg CriticalWarning "BIF file not found in platform ($platform) properties, skipping bootable image (.bin) generation"
5050 proc GetPartFromProps {props} {
5051 if {[dict exists $props "main" "PART"]} {
5052 return [
string tolower [dict get $props "main" "PART"]]
5054 Msg Error "Part number not found in properties"
5063 proc GetArchFromPart {part} {
5065 if {[
string match "xczu*" $part]} {
5067 }
elseif {[
string match "xc7z*" $part]} {
5069 }
elseif {[
string match "xck26*" $part]} {
5072 Msg CriticalWarning "Unknown part number: $part"
5081 proc GetAppsFromProps {props {list_names 0}} {
5082 set prop_apps [dict filter $props key {app:*}]
5083 set apps [dict create]
5084 set app_names [list]
5086 dict for {app_key app_value} $prop_apps {
5087 if {[regexp {^app:(.+)$} $app_key -> app_name]} {
5088 set app_name [string trim [string tolower $app_name]]
5089 # Convert only the keys of the inner dictionary to lowercase
5090 set app_value_lower [dict create]
5091 dict for {key value} $app_value {
5092 dict set app_value_lower [string tolower $key] $value
5094 dict set apps $app_name $app_value_lower
5095 lappend app_names $app_name
5098 if {$list_names eq 1} {
5109 proc GetPlatformsFromProps {props {list_names 0} {lower_case 0}} {
5110 set platforms [dict create]
5111 set platform_names [list]
5112 set prop_platforms [dict filter $props key {platform:*}]
5114 dict for {platform_key platform_value} $prop_platforms {
5115 if {[regexp {^platform:(.+)$} $platform_key -> platform_name]} {
5116 if {$lower_case == 1} {
5117 set platform_name [string trim [string tolower $platform_name]]
5119 set platform_name [string trim $platform_name]
5121 dict set platforms $platform_name $platform_value
5122 lappend platform_names $platform_name
5125 if {$list_names eq 1} {
5126 return $platform_names
5141 proc GenerateBootArtifacts {properties repo_path proj_dir bin_dir proj_name describe bitfile mmi_file} {
5142 set elf_list [glob -nocomplain "$bin_dir/*.elf"]
5146 if {[
llength $elf_list] == 0} {
5147 Msg Warning "No ELF files found in $bin_dir, skipping generation of boot artifacts"
5151 if {![
file exists $bitfile]} {
5152 Msg Warning "Bitfile $bitfile does not exist, skipping generation of boot artifacts"
5156 Msg Info "Generating boot artifacts for $proj_name..."
5157 Msg Info "Found apps: $apps"
5158 Msg Info "Found platforms: $platforms"
5162 foreach elf_file $elf_list {
5163 set elf_name [
file rootname [
file tail $elf_file]]
5164 Msg Info "Found elf name: $elf_name"
5165 Msg Info "Removing $describe from elf"
5168 if {[regexp "^(.+)-(.+)-$describe\$" $elf_name -> project_name elf_app]} {
5169 set elf_app [
string trim [
string tolower $elf_app]]
5170 Msg Info "Found elf_app: $elf_app"
5172 Msg Error "Could not extract app name from elf file: $elf_name"
5175 Msg Info "Removed project name ($project_name) and $describe from elf"
5177 set app_conf [dict get $apps $elf_app]
5178 set plat [dict get $app_conf "platform"]
5179 set app_proc [dict get $app_conf "proc"]
5182 if {[regexp -nocase {microblaze|risc} $app_proc]} {
5183 Msg Info "Detected soft processor ($app_proc) for $elf_app, updating bitstream memory with ELF file..."
5186 if {[dict size $proc_map] == 0} {
5187 Msg Error "Failed to read map from $proc_map_file"
5190 Msg Info "Found processor map: $proc_map"
5192 set proc_cell [
lindex [
split [dict get $proc_map $app_proc] ":"] 1]
5193 Msg Info "Updating memory at processor cell: $proc_cell"
5195 set update_mem_cmd "updatemem -force -meminfo $mmi_file -data $elf_file -bit $bitfile -proc $proc_cell -out $bitfile"
5196 set ret [
catch {
exec -ignorestderr {*}$update_mem_cmd >@ stdout} result]
5198 Msg Error "Error updating memory for $elf_app: $result"
5200 Msg Info "Done updating memory for $elf_app"
5203 Msg Info "Detected hard processor ($app_proc) for $elf_app. Make sure the .elf file is defined in the platform ($plat)\
5204 .bif file to be included in the bootable binary image (.bin) generation."
5210 foreach plat $platforms {
5212 if {$bif_file != ""} {
5213 Msg Info "Generating bootable binary image (.bin) for $plat"
5215 Msg Info "Architecture: $arch"
5216 Msg Info "BIF file: $bif_file"
5217 set bootgen_cmd "bootgen -arch $arch -image $bif_file -o i $bin_dir/$proj_name-$plat-$describe.bin -w on"
5218 set ret [
catch {
exec -ignorestderr {*}$bootgen_cmd >@ stdout} result]
5220 Msg Error "Error generating bootable binary image (.bin) for $elf_app: $result"
5222 Msg Info "Done generating bootable binary image (.bin) for $plat"
5231 proc ReadProcMap {proc_map_file} {
5232 set proc_map [dict create]
5233 if {[
file exists $proc_map_file]} {
5234 set f [open $proc_map_file "r"]
5235 while {[
gets $f line] >= 0} {
5236 Msg Debug "Line: $line"
5237 if {[regexp {^(\S+)\s+(.+)$} $line -> key value]} {
5238 Msg Debug "Found key: $key, value: $value in proc map file"
5239 dict set proc_map $key $value
5253 proc ListProjects {{repo_path .} {print 1} {ret_conf 0}} {
5254 set top_path [
file normalize $repo_path/Top]
5255 set confs [
findFiles [
file normalize $top_path] hog.conf]
5257 set confs [lsort $confs]
5261 set p [
Relative $top_path [
file dirname $c]]
5264 if {$description eq "test"} {
5265 set description " - Test project"
5266 }
elseif {$description ne ""} {
5267 set description " - $description"
5270 if {$print == 1 || $description ne " - Test project"} {
5272 set g [
file dirname $p]
5283 if {$ret_conf == 0} {
5295 proc Md5Sum {file_name} {
5296 if {!([
file exists $file_name])} {
5297 Msg Warning "Could not find $file_name."
5300 if {[
catch {
package require md5 2.0.7} result]} {
5301 Msg Warning "Tcl package md5 version 2.0.7 not found ($result), will use command line..."
5302 set hash [
lindex [
Execute md5sum $file_name] 0]
5304 set file_hash [
string tolower [md5::md5 -hex -file $file_name]]
5318 proc MergeDict {dict0 dict1 {remove_duplicates 1}} {
5319 set outdict [dict merge $dict1 $dict0]
5320 foreach key [dict keys $dict1] {
5321 if {[dict exists $dict0 $key]} {
5322 set temp_list [dict get $dict1 $key]
5323 foreach item $temp_list {
5325 if {[
IsInList $item [
DictGet $outdict $key]] == 0 || $remove_duplicates == 0} {
5327 dict lappend outdict $key $item
5339 proc MoveElementToEnd {inputList element} {
5340 set index [lsearch $inputList $element]
5342 set inputList [
lreplace $inputList $index $index]
5343 lappend inputList $element
5352 proc OpenProject {project_file repo_path} {
5354 open_project $project_file
5356 set project_folder [
file dirname $project_file]
5357 set project [
file tail [
file rootname $project_file]]
5358 if {[
file exists $project_folder]} {
5360 if {![is_project_open]} {
5361 Msg Info "Opening existing project file $project_file..."
5362 project_open $project -current_revision
5365 Msg Error "Project directory not found for $project_file."
5369 Msg Info "Opening existing project file $project_file..."
5371 open_project -file $project_file -do_backup_on_convert 1 -backup_file {./Projects/$project_file.zip}
5373 Msg Info "Opening existing project file $project_file..."
5374 prj_project open $project_file
5376 Msg Error "This IDE is currently not supported by Hog. Exiting!"
5383 return $tcl_platform(platform)
5393 proc ParseJSON {JSON_FILE JSON_KEY} {
5394 set result [
catch {
package require Tcl 8.4} TclFound]
5395 if {"$result" != "0"} {
5396 Msg CriticalWarning "Cannot find Tcl package version equal or higher than 8.4.\n $TclFound\n Exiting"
5400 set result [
catch {
package require json} JsonFound]
5401 if {"$result" != "0"} {
5402 Msg CriticalWarning "Cannot find JSON package equal or higher than 1.0.\n $JsonFound\n Exiting"
5405 set JsonDict [json::json2dict $JSON_FILE]
5406 set result [
catch {dict get $JsonDict $JSON_KEY} RETURNVALUE]
5407 if {"$result" != "0"} {
5408 Msg CriticalWarning "Cannot find $JSON_KEY in $JSON_FILE\n Exiting"
5421 proc ProjectExists {project {repo_path .}} {
5422 set index [lsearch -exact [
ListProjects $repo_path 0] $project]
5428 Msg Error "Project $project not found in repository $repo_path"
5439 proc ReadConf {file_name} {
5440 if {[
catch {
package require inifile 0.2.3} ERROR]} {
5441 Msg Error "Could not find inifile package version 0.2.3 or higher.\n
5442 To use ghdl, libero or diamond with Hog, you need to install the tcllib package\n
5443 You can install it with 'sudo apt install tcllib' on Debian/Ubuntu or 'sudo dnf install tcllib' on Fedora/RedHat/CentOs."
5447 ::ini::commentchar "#"
5448 set f [::ini::open $file_name]
5449 set properties [dict create]
5450 foreach sec [::ini::sections $f] {
5452 if {$new_sec == "files"} {
5455 set key_pairs [::ini::get $f $sec]
5457 regsub -all {\{\"} $key_pairs "\{" key_pairs
5458 regsub -all {\"\}} $key_pairs "\}" key_pairs
5460 dict set properties $new_sec [dict create {*}$key_pairs]
5473 proc ReadExtraFileList {extra_file_name} {
5474 set extra_file_dict [dict create]
5475 if {[
file exists $extra_file_name]} {
5476 set file [open $extra_file_name "r"]
5477 set file_data [read $file]
5480 set data [
split $file_data "\n"]
5481 foreach line $data {
5482 if {![regexp {^ *$} $line] & ![regexp {^ *\#} $line]} {
5483 set ip_and_md5 [regexp -all -inline {\S+} $line]
5484 dict lappend extra_file_dict "[
lindex $ip_and_md5 0]" "[
lindex $ip_and_md5 1]"
5488 return $extra_file_dict
5508 proc ReadListFile {args} {
5511 if {[
catch {
package require cmdline} ERROR]} {
5512 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
5518 {lib.arg "" "The name of the library files will be added to, if not given will be extracted from the file name."}
5519 {fileset.arg "" "The name of the library, from the main list file"}
5520 {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."}
5521 {print_log "If set, will use PrintFileTree for the VIEW directive"}
5522 {indent.arg "" "Used to indent files with the VIEW directive"}
5525 set usage "USAGE: ReadListFile \[options\] <list file> <path>"
5526 if {[
catch {
array set options [
cmdline::getoptions args $parameters $usage]}] || [
llength $args] != 2} {
5532 set list_file [
lindex $args 0]
5533 set path [
lindex $args 1]
5534 set sha_mode $options(sha_mode)
5535 set lib $options(lib)
5536 set fileset $options(fileset)
5537 set print_log $options(print_log)
5538 set indent $options(indent)
5540 if {$sha_mode == 1} {
5541 set sha_mode_opt "-sha_mode"
5546 if {$print_log == 1} {
5547 set print_log_opt "-print_log"
5549 set print_log_opt ""
5554 set lib [
file rootname [
file tail $list_file]]
5556 set fp [open $list_file r]
5557 set file_data [read $fp]
5559 set list_file_ext [
file extension $list_file]
5560 switch $list_file_ext {
5562 if {$fileset eq ""} {
5568 set fileset "constrs_1"
5571 set fileset "sources_1"
5575 set libraries [dict create]
5576 set filesets [dict create]
5577 set properties [dict create]
5579 set data [
split $file_data "\n"]
5581 set n [
llength $data]
5583 if {$print_log == 1} {
5584 if {$indent eq ""} {
5585 set list_file_rel [
file tail $list_file]
5586 Msg Status "\n$list_file_rel"
5590 Msg Debug "$n lines read from $list_file."
5593 foreach line $data {
5595 if {![regexp {^[\t\s]*$} $line] & ![regexp {^[\t\s]*\#} $line]} {
5596 set file_and_prop [regexp -all -inline {\S+} $line]
5597 set srcfile [
lindex $file_and_prop 0]
5598 set srcfile "$path/$srcfile"
5600 set srcfiles [glob -nocomplain $srcfile]
5603 if {$srcfiles != $srcfile && ![
string equal $srcfiles ""]} {
5604 Msg Debug "Wildcard source expanded from $srcfile to $srcfiles"
5606 if {![
file exists $srcfile]} {
5607 if {$print_log == 0} {
5608 Msg CriticalWarning "File: $srcfile (from list file: $list_file) does not exist."
5614 foreach vhdlfile $srcfiles {
5615 if {[
file exists $vhdlfile]} {
5616 set vhdlfile [
file normalize $vhdlfile]
5617 set extension [
file extension $vhdlfile]
5619 set prop [
lrange $file_and_prop 1 end]
5622 set library [
lindex [regexp -inline {\ylib\s*=\s*(.+?)\y.*} $prop] 1]
5623 if {$library == ""} {
5627 if {$extension == $list_file_ext} {
5630 set ref_path [
lindex [regexp -inline {\ypath\s*=\s*(\S+).*} $prop] 1]
5631 if {$ref_path eq ""} {
5634 set ref_path [
file normalize $path/$ref_path]
5636 Msg Debug "List file $vhdlfile found in list file, recursively opening it using path \"$ref_path\"..."
5637 if {$print_log == 1} {
5638 if {[
file normalize $last_printed] ne [
file normalize $vhdlfile]} {
5639 Msg Status "$indent Inside [
file tail $vhdlfile]:"
5643 lassign [
ReadListFile {*}"-indent \" $indent\" -lib $library -fileset $fileset $sha_mode_opt $print_log_opt $vhdlfile $ref_path"] l p fs
5645 set properties [
MergeDict $p $properties]
5647 }
elseif {[lsearch {.src .sim .con ReadExtraFileList} $extension] >= 0} {
5649 Msg Error "$vhdlfile cannot be included into $list_file, $extension files must be included into $extension files."
5652 regsub -all " *= *" $prop "=" prop
5656 if {[
string first "lib=" $p] == -1} {
5658 set pos [
string first "=" $p]
5662 set prop_name [
string range $p 0 [
expr {$pos - 1}]]
5665 dict lappend properties $vhdlfile $p
5666 Msg Debug "Adding property $p to $vhdlfile..."
5667 }
elseif {$list_file_ext != ".ipb"} {
5668 Msg Warning "Setting Property $p is not supported for file $vhdlfile or it is already its default. \
5673 if {[lsearch {.xci .ip .bd .xcix} $extension] >= 0} {
5675 set lib_name "ips.src"
5676 }
elseif {[
IsInList $extension {.vhd .vhdl}] || $list_file_ext == ".sim"} {
5678 if {![
IsInList $extension {.vhd .vhdl}]} {
5679 set lib_name "others.sim"
5681 set lib_name "$library$list_file_ext"
5683 }
elseif {$list_file_ext == ".con"} {
5684 set lib_name "sources.con"
5685 }
elseif {$list_file_ext == ".ipb"} {
5686 set lib_name "xml.ipb"
5687 }
elseif { [
IsInList $list_file_ext {.src}] && [
IsInList $extension {.c .cpp .h .hpp}] } {
5689 set lib_name "$library$list_file_ext"
5692 set lib_name "others.src"
5695 Msg Debug "Appending $vhdlfile to $lib_name list..."
5696 dict lappend libraries $lib_name $vhdlfile
5697 if {$sha_mode != 0 && [
file type $vhdlfile] eq "link"} {
5700 dict lappend libraries $lib_name $real_file
5701 Msg Debug "File $vhdlfile is a soft link, also adding the real file: $real_file"
5706 if {[dict exists $filesets $fileset] == 0} {
5708 Msg Debug "Adding $fileset to the fileset dictionary..."
5709 Msg Debug "Adding library $lib_name to fileset $fileset..."
5710 dict set filesets $fileset $lib_name
5714 Msg Debug "Adding library $lib_name to fileset $fileset..."
5715 dict lappend filesets $fileset $lib_name
5721 Msg CriticalWarning "File $vhdlfile not found."
5727 if {$sha_mode != 0} {
5729 if {$list_file_ext eq ".ipb"} {
5730 set sha_lib "xml.ipb"
5732 set sha_lib $lib$list_file_ext
5734 dict lappend libraries $sha_lib [
file normalize $list_file]
5735 if {[
file type $list_file] eq "link"} {
5738 dict lappend libraries $lib$list_file_ext $real_file
5739 Msg Debug "List file $list_file is a soft link, also adding the real file: $real_file"
5742 return [list $libraries $properties $filesets]
5750 proc Relative {base dst} {
5751 if {![
string equal [
file pathtype $base] [
file pathtype $dst]]} {
5752 Msg CriticalWarning "Unable to compute relation for paths of different path types: [
file pathtype $base] vs. [
file pathtype $dst], ($base vs. $dst)"
5756 set base [
file normalize [
file join [
pwd] $base]]
5757 set dst [
file normalize [
file join [
pwd] $dst]]
5760 set base [
file split $base]
5761 set dst [
file split $dst]
5763 while {[
string equal [
lindex $dst 0] [
lindex $base 0]]} {
5764 set dst [
lrange $dst 1 end]
5765 set base [
lrange $base 1 end]
5766 if {![
llength $dst]} {break}
5769 set dstlen [
llength $dst]
5770 set baselen [
llength $base]
5772 if {($dstlen == 0) && ($baselen == 0)} {
5775 while {$baselen > 0} {
5776 set dst [
linsert $dst 0 ..]
5779 set dst [
eval [
linsert $dst 0 file join]]
5790 proc RelativeLocal {pathName filePath} {
5791 if {[
string first [
file normalize $pathName] [
file normalize $filePath]] != -1} {
5792 return [
Relative $pathName $filePath]
5803 proc RemoveDuplicates {mydict} {
5804 set new_dict [dict create]
5805 foreach key [dict keys $mydict] {
5806 set values [
DictGet $mydict $key]
5807 foreach value $values {
5808 set idxs [
lreverse [
lreplace [lsearch -exact -all $values $value] 0 0]]
5810 set values [
lreplace $values $idx $idx]
5813 dict set new_dict $key $values
5824 proc ResetRepoFiles {reset_file} {
5825 if {[
file exists $reset_file]} {
5826 Msg Info "Found $reset_file, opening it..."
5827 set fp [open $reset_file r]
5828 set wild_cards [lsearch -all -inline -not -regexp [
split [read $fp] "\n"] "^ *$"]
5830 Msg Info "Found the following files/wild cards to restore if modified: $wild_cards..."
5831 foreach w $wild_cards {
5833 if {[
llength $mod_files] > 0} {
5834 Msg Info "Found modified $w files: $mod_files, will restore them..."
5837 Msg Info "No modified $w files found."
5848 proc RestoreModifiedFiles {{repo_path "."} {pattern "."}} {
5851 set ret [
Git checkout $pattern]
5862 proc SearchHogProjects {dir} {
5863 set projects_list {}
5864 if {[
file exists $dir]} {
5865 if {[
file isdirectory $dir]} {
5866 foreach proj_dir [glob -nocomplain -types d $dir/*] {
5867 if {![regexp {^.*Top/+(.*)$} $proj_dir dummy proj_name]} {
5868 Msg Warning "Could not parse Top directory $dir"
5871 if {[
file exists "$proj_dir/hog.conf"]} {
5872 lappend projects_list $proj_name
5875 lappend projects_list $p
5880 Msg Error "Input $dir is not a directory!"
5883 Msg Error "Directory $dir doesn't exist!"
5885 return $projects_list
5894 proc SetGenericsSimulation {repo_path proj_dir target} {
5895 set top_dir "$repo_path/Top/$proj_dir"
5896 set simsets [get_filesets]
5897 if {$simsets != ""} {
5898 foreach simset $simsets {
5900 if {[get_property FILESET_TYPE $simset] != "SimulationSrcs"} {
5904 set merged_generics_dict [dict create]
5908 set simset_generics [
DictGet $simset_dict "generics"]
5909 set merged_generics_dict [
MergeDict $merged_generics_dict $simset_generics 0]
5911 set_property generic $generic_str [get_filesets $simset]
5912 Msg Debug "Setting generics $generic_str for simulator $target\
5913 and simulation file-set $simset..."
5925 proc SetTopProperty {top_module fileset} {
5926 Msg Info "Setting TOP property to $top_module module"
5929 set_property "top" $top_module [get_filesets $fileset]
5932 set_global_assignment -name TOP_LEVEL_ENTITY $top_module
5934 set_root -module $top_module
5936 prj_impl option top $top_module
5941 proc VIVADO_PATH_PROPERTIES {} {
5942 return {"\.*\.TCL\.PRE$" "^.*\.TCL\.POST$" "^RQS_FILES$" "^INCREMENTAL\_CHECKPOINT$" "NOC\_SOLUTION\_FILE"}
5946 proc VITIS_PATH_PROPERTIES {} {
5947 return {"^HW$" "^XPFM$" "^LINKER-SCRIPT$" "^LIBRARIES$" "^LIBRARY-SEARCH-PATH$"}
5957 proc WriteConf {file_name config {comment ""}} {
5958 if {[
catch {
package require inifile 0.2.3} ERROR]} {
5959 puts "$ERROR\n If you are running this script on tclsh, you can fix this by installing 'tcllib'"
5963 ::ini::commentchar "#"
5964 set f [::ini::open $file_name w]
5966 foreach sec [dict keys $config] {
5967 set section [dict get $config $sec]
5968 dict for {p v} $section {
5969 if {[string trim $v] == ""} {
5970 Msg Warning "Property $p has empty value. Skipping..."
5973 ::ini::set $f $sec $p $v
5978 if {![
string equal "$comment" ""]} {
5979 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $comment
5980 set hog_header "Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
5981 ::ini::comment $f [
lindex [::ini::sections $f] 0] "" $hog_header
5996 proc WriteGenerics {mode repo_path design date timee\
5997 commit version top_hash top_ver hog_hash hog_ver \
5998 cons_ver cons_hash libs vers hashes ext_names ext_hashes \
5999 user_ip_repos user_ip_vers user_ip_hashes flavour {xml_ver ""} {xml_hash ""}} {
6000 Msg Info "Passing parameters/generics to project's top module..."
6003 set generic_string [
concat \
6015 if {$xml_hash != "" && $xml_ver != ""} {
6016 lappend generic_string \
6021 foreach l $libs v $vers h $hashes {
6024 lappend generic_string "$ver" "$hash"
6027 foreach e $ext_names h $ext_hashes {
6029 lappend generic_string "$hash"
6032 foreach repo $user_ip_repos v $user_ip_vers h $user_ip_hashes {
6033 set repo_name [
file tail $repo]
6034 set ver "[
string toupper $repo_name]_VER=[
FormatGeneric $v]"
6035 set hash "[
string toupper $repo_name]_SHA=[
FormatGeneric $h]"
6036 lappend generic_string "$ver" "$hash"
6039 if {$flavour != -1} {
6040 lappend generic_string "FLAVOUR=$flavour"
6046 set generic_string "$prj_generics $generic_string"
6052 if {$mode == "create" || [
IsISE]} {
6055 if {[
file exists $top_file]} {
6058 Msg Debug "Found top level generics $generics in $top_file"
6060 set filtered_generic_string ""
6062 foreach generic_to_set [
split [
string trim $generic_string]] {
6063 set key [
lindex [
split $generic_to_set "="] 0]
6064 if {[dict exists $generics $key]} {
6065 Msg Debug "Hog generic $key found in $top_name"
6066 lappend filtered_generic_string "$generic_to_set"
6068 Msg Warning "Generic $key is passed by Hog but is NOT present in $top_name."
6074 set generic_string $filtered_generic_string
6079 set_property generic $generic_string [current_fileset]
6080 Msg Info "Setting parameters/generics..."
6081 Msg Debug "Detailed parameters/generics: $generic_string"
6086 set simulator [get_property target_simulator [current_project]]
6087 if {$mode == "create"} {
6094 Msg Info "Setting Synplify parameters/generics one by one..."
6095 foreach generic $generic_string {
6096 Msg Debug "Setting Synplify generic: $generic"
6097 set_option -hdl_param -set "$generic"
6100 Msg Info "Setting Diamond parameters/generics one by one..."
6101 prj_impl option -impl Implementation0 HDL_PARAM "$generic_string"
6103 if {[
catch {
set ws_apps [app list -dict]}]} {
set ws_apps ""}
6105 foreach app_name [dict keys $ws_apps] {
6106 set defined_symbols [app config -name $app_name -get define-compiler-symbols]
6107 foreach generic_to_set [
split [
string trim $generic_string]] {
6108 set key [
lindex [
split $generic_to_set "="] 0]
6109 set value [
lindex [
split $generic_to_set "="] 1]
6110 if {[
string match "32'h*" $value]} {
6111 set value [
string map {"32'h" "0x"} $value]
6114 foreach symbol [
split $defined_symbols ";"] {
6115 if {[
string match "$key=*" $symbol]} {
6116 Msg Debug "Generic $key found in $app_name, removing it..."
6117 app config -name $app_name -remove define-compiler-symbols "$symbol"
6121 Msg Info "Setting Vitis parameters/generics for app $app_name: $key=$value"
6122 app config -name $app_name define-compiler-symbols "$key=$value"
6134 proc WriteGenericsToBdIPs {mode repo_path proj generic_string} {
6135 Msg Debug "Parameters/generics passed to WriteGenericsToIP: $generic_string"
6137 set bd_ip_generics false
6139 if {[dict exists $properties "hog"]} {
6140 set propDict [dict get $properties "hog"]
6141 if {[dict exists $propDict "PASS_GENERICS_TO_BD_IPS"]} {
6142 set bd_ip_generics [dict get $propDict "PASS_GENERICS_TO_BD_IPS"]
6146 if {[
string compare [
string tolower $bd_ip_generics] "false"] == 0} {
6150 if {$mode == "synth"} {
6151 Msg Info "Attempting to apply generics pre-synthesis..."
6152 set PARENT_PRJ [get_property "PARENT.PROJECT_PATH" [current_project]]
6153 set workaround [open "$repo_path/Projects/$proj/.hog/presynth_workaround.tcl" "w"]
6154 puts $workaround "source \[lindex \$argv 0\];"
6155 puts $workaround "open_project \[lindex \$argv 1\];"
6156 puts $workaround "WriteGenericsToBdIPs \[lindex \$argv 2\] \[lindex \$argv 3\] \[lindex \$argv 4\] \[lindex \$argv 5\];"
6157 puts $workaround "close_project"
6161 exec vivado -mode batch -source $repo_path/Projects/$proj/.hog/presynth_workaround.tcl \
6162 -tclargs $repo_path/Hog/Tcl/hog.tcl $PARENT_PRJ \
6163 "childprocess" $repo_path $proj $generic_string
6166 Msg Error "Encountered an error while attempting workaround: $errMsg"
6168 file delete $repo_path/Projects/$proj/.hog/presynth_workaround.tcl
6170 Msg Info "Done applying generics pre-synthesis."
6174 Msg Info "Looking for IPs to add generics to..."
6175 set ips_generic_string ""
6176 foreach generic_to_set [
split [
string trim $generic_string]] {
6177 set key [
lindex [
split $generic_to_set "="] 0]
6178 set value [
lindex [
split $generic_to_set "="] 1]
6179 append ips_generic_string "CONFIG.$key $value "
6183 if {[
string compare [
string tolower $bd_ip_generics] "true"] == 0} {
6186 set ip_regex $bd_ip_generics
6189 set ip_list [get_ips -regex $ip_regex]
6190 Msg Debug "IPs found with regex \{$ip_regex\}: $ip_list"
6192 set regen_targets {}
6194 foreach {ip} $ip_list {
6195 set WARN_ABOUT_IP false
6196 set ip_props [list_property [get_ips $ip]]
6199 if {[lsearch -exact $ip_props "IS_BD_CONTEXT"] == -1} {
6203 if {[get_property "IS_BD_CONTEXT" [get_ips $ip]] eq "1"} {
6204 foreach {ip_prop} $ip_props {
6205 if {[dict exists $ips_generic_string $ip_prop]} {
6206 if {$WARN_ABOUT_IP == false} {
6207 lappend regen_targets [get_property SCOPE [get_ips $ip]]
6208 Msg Warning "The ip \{$ip\} contains generics that are set by Hog.\
6209 If this is IP is apart of a block design, the .bd file may contain stale, unused, values.\
6210 Hog will always apply the most up-to-date values to the IP during synthesis,\
6211 however these values may or may not be reflected in the .bd file."
6212 set WARN_ABOUT_IP true
6217 set xci_path [get_property IP_FILE [get_ips $ip]]
6219 if {[
string equal $generic_format "ERROR"]} {
6220 Msg Warning "Could not find format for generic $ip_prop in IP $ip. Skipping..."
6224 set value_to_set [dict get $ips_generic_string $ip_prop]
6225 switch -exact $generic_format {
6227 if {[
string match "32'h*" $value_to_set]} {
6228 scan [
string map {"32'h" ""} $value_to_set] "%x" value_to_set
6232 set value_to_set [
expr {$value_to_set ? "true" : "false"}]
6235 if {[
string match "32'h*" $value_to_set]} {
6236 binary scan [
binary format H* [
string map {"32'h" ""} $value_to_set]] d value_to_set
6240 if {[
string match "32'h*" $value_to_set]} {
6241 set value_to_set [
string map {"32'h" "0x"} $value_to_set]
6245 set value_to_set [
format "%s" $value_to_set]
6248 Msg Warning "Unknown generic format $generic_format for IP $ip. Will attempt to pass as string..."
6253 Msg Info "The IP \{$ip\} contains: $ip_prop ($generic_format), setting it to $value_to_set."
6254 if {[
catch {set_property -name $ip_prop -value $value_to_set -objects [get_ips $ip]} prop_error]} {
6255 Msg CriticalWarning "Failed to set property $ip_prop to $value_to_set for IP \{$ip\}: $prop_error"
6262 foreach {regen_target} [lsort -unique $regen_targets] {
6263 Msg Info "Regenerating target: $regen_target"
6264 if {[
catch {generate_target -force all [get_files $regen_target]} prop_error]} {
6265 Msg CriticalWarning "Failed to regen targets: $prop_error"
6273 proc GetGenericFormatFromXciXML {generic_name xml_file} {
6274 if {![
file exists $xml_file]} {
6275 Msg Error "Could not find XML file: $xml_file"
6279 set fp [open $xml_file r]
6280 set xci_data [read $fp]
6283 set paramType "string"
6284 set modelparam_regex [
format {^.*\y%s\y.*$} [
string map {"CONFIG." "MODELPARAM_VALUE."} $generic_name]]
6285 set format_regex {format="([^"]+)"}
6287 set line [
lindex [regexp -inline -line $modelparam_regex $xci_data] 0]
6288 Msg Debug "line: $line"
6290 if {[regexp $format_regex $line match format_value]} {
6291 Msg Debug "Extracted: $format_value format from xml"
6292 set paramType $format_value
6294 Msg Debug "No format found, using string"
6303 proc GetGenericFormatFromXci {generic_name xci_file} {
6304 if {![
file exists $xci_file]} {
6305 Msg Error "Could not find XCI file: $xci_file"
6309 set fp [open $xci_file r]
6310 set xci_data [read $fp]
6313 set paramType "string"
6314 if {[
string first "xilinx.com:schema:json_instance:1.0" $xci_data] == -1} {
6315 Msg Debug "XCI format is not JSON, trying XML..."
6316 set xml_file "[
file rootname $xci_file].xml"
6320 set generic_name [
string map {"CONFIG." ""} $generic_name]
6321 set ip_inst [
ParseJSON $xci_data "ip_inst"]
6322 set parameters [dict get $ip_inst parameters]
6323 set component_parameters [dict get $parameters component_parameters]
6324 if {[dict exists $component_parameters $generic_name]} {
6325 set generic_info [dict get $component_parameters $generic_name]
6326 if {[dict exists [
lindex $generic_info 0] format]} {
6327 set paramType [dict get [
lindex $generic_info 0] format]
6328 Msg Debug "Extracted: $paramType format from xci"
6331 Msg Debug "No format found, using string"
6344 proc WriteGitLabCIYAML {proj_name {ci_conf ""}} {
6345 if {[
catch {
package require yaml 0.3.3} YAMLPACKAGE]} {
6346 Msg CriticalWarning "Cannot find package YAML.\n Error message: $YAMLPACKAGE. \
6347 If you are running on tclsh, you can fix this by installing package \"tcllib\""
6352 if {$ci_conf != ""} {
6354 foreach sec [dict keys $ci_confs] {
6355 if {[
string first : $sec] == -1} {
6356 lappend job_list $sec
6360 set job_list {"generate_project" "simulate_project"}
6364 set out_yaml [huddle create]
6365 foreach job $job_list {
6367 set huddle_tags [huddle list]
6369 set sec_dict [dict create]
6371 if {$ci_confs != ""} {
6372 foreach var [dict keys [dict get $ci_confs $job]] {
6373 if {$var == "tags"} {
6374 set tag_section "tags"
6375 set tags [dict get [dict get $ci_confs $job] $var]
6376 set tags [
split $tags ","]
6378 set tag_list [huddle list $tag]
6379 set huddle_tags [huddle combine $huddle_tags $tag_list]
6382 dict set sec_dict $var [dict get [dict get $ci_confs $job] $var]
6388 set huddle_variables [huddle create "PROJECT_NAME" $proj_name "extends" ".vars"]
6389 if {[dict exists $ci_confs "$job:variables"]} {
6390 set var_dict [dict get $ci_confs $job:variables]
6391 foreach var [dict keys $var_dict] {
6393 set value [dict get $var_dict "$var"]
6394 set var_inner [huddle create "$var" "$value"]
6395 set huddle_variables [huddle combine $huddle_variables $var_inner]
6400 set middle [huddle create "extends" ".$job" "variables" $huddle_variables]
6401 foreach sec [dict keys $sec_dict] {
6402 set value [dict get $sec_dict $sec]
6403 set var_inner [huddle create "$sec" "$value"]
6404 set middle [huddle combine $middle $var_inner]
6406 if {$tag_section != ""} {
6407 set middle2 [huddle create "$tag_section" $huddle_tags]
6408 set middle [huddle combine $middle $middle2]
6411 set outer [huddle create "$job:$proj_name" $middle]
6412 set out_yaml [huddle combine $out_yaml $outer]
6415 return [
string trimleft [yaml::huddle2yaml $out_yaml] "-"]
6425 proc WriteListFiles {libs props list_path repo_path {ext_path ""}} {
6427 foreach lib [dict keys $libs] {
6428 if {[
llength [
DictGet $libs $lib]] > 0} {
6429 set list_file_name $list_path$lib
6430 set list_file [open $list_file_name w]
6431 Msg Info "Writing $list_file_name..."
6432 puts $list_file "#Generated by Hog on [
clock format [
clock seconds] -format "%Y-%m-%d %H:%M:%S"]"
6433 foreach file [
DictGet $libs $lib] {
6435 set prop [
DictGet $props $file]
6439 puts $list_file "$file_path $prop"
6442 set ext_list_file [open "[
file rootname $list_file].ext" a]
6443 puts $ext_list_file "$file_path $prop"
6444 close $ext_list_file
6447 Msg Warning "The path of file $file is not relative to your repository. Please check!"
6463 proc WriteSimListFile {simset libs props simsets list_path repo_path {force 0}} {
6465 set list_file_name $list_path/${simset}.sim
6466 if {$force == 0 && [
file exists $list_file_name]} {
6467 Msg Info "List file $list_file_name already exists, skipping..."
6471 set list_file [open $list_file_name a+]
6474 puts $list_file "\[files\]"
6475 Msg Info "Writing $list_file_name..."
6476 foreach lib [
DictGet $simsets $simset] {
6477 foreach file [
DictGet $libs $lib] {
6479 set prop [
DictGet $props $file]
6483 set lib_name [
file rootname $lib]
6484 if {$lib_name != $simset && [
file extension $file] == ".vhd" && [
file extension $file] == ""} {
6485 lappend prop "lib=$lib_name"
6487 puts $list_file "$file_path $prop"
6490 Msg Warning "The path of file $file is not relative to your repository. Please check!"
6502 proc WriteToFile {File msg} {
6503 set f [open $File a+]
6514 proc WriteUtilizationSummary {input output project_name run} {
6515 set f [open $input "r"]
6516 set o [open $output "a"]
6517 puts $o "## $project_name $run Utilization report\n\n"
6518 struct::matrix util_m
6519 util_m add columns 14
6522 util_m add row "| **Site Type** | **Used** | **Fixed** | **Prohibited** | **Available** | **Util%** |"
6523 util_m add row "| --- | --- | --- | --- | --- | --- |"
6525 util_m add row "| **Site Type** | **Used** | **Fixed** | **Available** | **Util%** |"
6526 util_m add row "| --- | --- | --- | --- | --- |"
6536 while {[
gets $f line] >= 0} {
6537 if {([
string first "| CLB LUTs" $line] >= 0 || [
string first "| Slice LUTs" $line] >= 0) && $luts == 0} {
6538 util_m add row $line
6541 if {([
string first "| CLB Registers" $line] >= 0 || [
string first "| Slice Registers" $line] >= 0) && $regs == 0} {
6542 util_m add row $line
6545 if {[
string first "| Block RAM Tile" $line] >= 0 && $bram == 0} {
6546 util_m add row $line
6549 if {[
string first "URAM " $line] >= 0 && $uram == 0} {
6550 util_m add row $line
6553 if {[
string first "DSPs" $line] >= 0 && $dsps == 0} {
6554 util_m add row $line
6557 if {[
string first "Bonded IOB" $line] >= 0 && $ios == 0} {
6558 util_m add row $line
6565 puts $o [util_m format 2string]
6571 Msg Error "Found Git version older than 2.7.2. Hog will not work as expected, exiting now."