26 source $tcl_path/utils/hdl_parser.tcl
28 proc PrintOrWrite {output_file msg} {
29 if {$output_file ne ""} {
30 puts $output_file $msg
36 proc _create_hier_meta {} {
37 set hier_meta [dict create]
38 dict set hier_meta all_modules {}
39 dict set hier_meta proj_files {}
40 dict set heir_meta parsed_files_cache {}
44 proc _compute_file_checksum {file_path} {
45 if {![
file exists $file_path]} {
49 if {[
catch {
package require md5 2.0.7} result]} {
51 if {[
catch {
exec md5sum $file_path} output]} {
54 return [
lindex $output 0]
56 return [
string tolower [md5::md5 -hex -file $file_path]]
61 proc is_known_library {hier_meta_ref lib_name} {
62 upvar 1 $hier_meta_ref hier_meta
63 return [dict exists $hier_meta libraries known_libs $lib_name]
66 proc is_ignored_module {module_key ignore_patterns} {
67 foreach pattern $ignore_patterns {
68 if {[
string match $pattern $module_key]} {
75 proc _create_proj_file_info {file_path library properties} {
76 set file_info [dict create]
77 dict set file_info file_path $file_path
78 dict set file_info library $library
79 dict set file_info properties $properties
80 dict set file_info ext [
file extension $file_path]
84 proc file_info_path {file_info} {
return [dict get $file_info file_path]}
85 proc file_info_library {file_info} {
return [dict get $file_info library]}
86 proc file_info_properties {file_info} {
return [dict get $file_info properties]}
87 proc file_info_ext {file_info} {
return [dict get $file_info ext]}
89 proc _store_module {hier_meta_ref mod_name mod_library mod_type file_path references_list props} {
90 upvar 1 $hier_meta_ref hier_meta
92 set key "${mod_library}.${mod_type}.${mod_name}"
94 dict set mod name $mod_name
95 dict set mod library $mod_library
96 dict set mod type $mod_type
97 dict set mod file_path $file_path
98 dict set mod references $references_list
99 dict set mod properties $props
100 dict set mod color "white"
102 dict set hier_meta all_modules $key $mod
105 proc _hier_parse_hdl {hier_meta_ref file_info} {
106 upvar 1 $hier_meta_ref hier_meta
109 if {[
string first "top_control" $file_info] != -1} {
114 if {[
string first "top_l0mdt" $file_info] != -1} {
120 if {![
file exists $f]} {
return}
124 set ext [
string tolower [
file extension $f]]
126 Msg Debug "Parsing $f"
127 set discovered_modules [list]
130 foreach node $hdl_constructs {
136 set node_type [dict get $node type]
137 set node_name [dict get $node name]
139 set references [list]
140 foreach component [dict get $node components_declared] {
141 lappend references "unknown.component.[dict get $component name]"
144 foreach inst [dict get $node instantiations] {
145 if { [dict get $inst type] == "component_inst"} {
146 lappend references "unknown.component.[dict get $inst mod_name]"
147 }
elseif { [dict get $inst type] == "entity_inst"} {
148 set split_name [
split [dict get $inst mod_name] "."]
149 lappend references "[
lindex $split_name 0].component.[
lindex $split_name 1]"
153 foreach lib [dict get $node libraries] {
154 foreach use [dict get $lib uses] {
155 set split_name [
split $use "."]
156 lappend references "[
lindex $split_name 0].vhdl_package.[
lindex $split_name 1]"
160 set references [lsort -unique $references]
161 dict set node references $references
163 set mod_properties [dict create]
164 if {$ext eq ".v" || $ext eq ".sv" || [lsearch -exact $file_properties "SystemVerilog"] != -1} {
165 if {$ext eq ".sv" || [lsearch -exact $file_properties "SystemVerilog"] != -1} {
166 dict set mod_properties filetype "SystemVerilog"
168 dict set mod_properties filetype "Verilog"
170 }
elseif {$ext eq ".vhd" || $ext eq ".vhdl"} {
171 set knownYears {93 2008 2019}
173 foreach y $knownYears {
174 if {[lsearch -exact $file_properties $y] != -1} {
179 dict set mod_properties filetype "VHDL$year"
182 dict set node properties $mod_properties
183 dict set node library $library
184 dict set node color "white"
186 if {$node_type == "vhdl_architecture"} {
187 set key "${library}.${node_type}.[dict get $node entity].${node_name}"
189 set key "${library}.${node_type}.${node_name}"
192 dict set hier_meta all_modules $key $node
193 lappend discovered_modules $key
195 return $discovered_modules
198 proc _hier_parse_ip {hier_meta_ref file_info {include_gen_prods 0}} {
199 upvar 1 $hier_meta_ref hier_meta
202 if {![
file exists $f]} {
return}
205 set name [
file rootname [
file tail $f]]
206 set mod_properties [dict create]
207 dict set mod_properties filetype "XCI"
210 if {[
catch {open $f r} fid]} {
211 Msg Warning "Warning: Could not open XCI file: $f"
213 set content [read $fid]
216 if {[regexp {"OUTPUTDIR":\s*\[\s*\{\s*"value":\s*"([^"]+)"} $content -> dir_value]} {
217 set output_dir $dir_value
221 set xci_dir [
file dirname $f]
222 set resolved_output_dir [
file normalize [
file join $xci_dir $output_dir]]
226 set dirs_to_scan [list [list $resolved_output_dir 0]]
228 if { $include_gen_prods == 1} {
229 while {[
llength $dirs_to_scan] > 0} {
230 set current [
lindex $dirs_to_scan 0]
231 set dirs_to_scan [
lrange $dirs_to_scan 1 end]
232 set current_dir [
lindex $current 0]
233 set current_depth [
lindex $current 1]
235 if {$current_depth >= 5} { continue}
236 if {![
file isdirectory $current_dir]} { continue}
238 set entries [glob -nocomplain -directory $current_dir *]
239 foreach entry $entries {
240 if {[
file isfile $entry]} {
241 set ext [
string tolower [
file extension $entry]]
242 if {$ext eq ".vhd" || $ext eq ".v" || $ext eq ".sv"} {
243 lappend hdl_files $entry
245 }
elseif {[
file isdirectory $entry]} {
246 set dir_name [
file tail $entry]
248 if {$dir_name ne "synth"} {
249 lappend dirs_to_scan [list $entry [
expr {$current_depth + 1}]]
259 foreach hdl_file $hdl_files {
260 set ext [
string tolower [
file extension $hdl_file]]
264 if {$file_checksum ne "" && [dict exists $hier_meta parsed_files_cache $file_checksum]} {
265 Msg Debug "Reusing previously parsed modules for $hdl_file"
266 set discovered_modules [dict get $hier_meta parsed_files_cache $file_checksum]
267 set all_subs [
concat $all_subs $discovered_modules]
274 set discovered_modules [list]
276 if {$ext eq ".vhd" || $ext eq ".vhdl" || $ext eq ".v" || $ext eq ".sv"} {
280 if {$file_checksum ne ""} {
281 dict set hier_meta parsed_files_cache $file_checksum $discovered_modules
284 set all_subs [
concat $all_subs $discovered_modules]
287 set all_subs [lsort -unique $all_subs]
289 if {[
llength $all_subs] == 0} {
292 _store_module hier_meta $name $library "component" $f $all_subs $mod_properties
294 _store_module hier_meta $name $library "xci" $f $all_subs $mod_properties
301 proc _hier_parse_file {hier_meta_ref file_info {include_gen_prods 0}} {
302 upvar 1 $hier_meta_ref hier_meta
305 if {$ext eq ".vhd" || $ext eq ".vhdl" || $ext eq ".v" || $ext eq ".sv"} {
307 }
elseif {$ext eq ".xci"} {
309 }
elseif {$ext eq ".bd"} {
318 proc _hier_submodule_append {hier_meta_ref parent_key sub_key} {
319 upvar 1 $hier_meta_ref hier_meta
321 if {[dict exists $hier_meta all_modules $parent_key]} {
322 set mod [dict get $hier_meta all_modules $parent_key]
323 dict lappend mod references $sub_key
324 dict set hier_meta all_modules $parent_key $mod
329 proc _reference_resolver {hier_meta_ref} {
330 upvar 1 $hier_meta_ref hier_meta
332 set package_bodies [ dict filter [dict get $hier_meta all_modules] script {k v} {expr {[dict get $v type] eq "vhdl_package_body"}}]
333 set architectures [ dict filter [dict get $hier_meta all_modules] script {k v} {expr {[dict get $v type] eq "vhdl_architecture"}}]
335 dict for {package_body body_info} $package_bodies {
336 set entity_key [split $package_body "."]
337 set entity_key "[lindex $entity_key 0].vhdl_package.[lindex $entity_key 2]"
338 if {[dict exists [dict get $hier_meta all_modules] $entity_key]} {
339 _hier_submodule_append hier_meta $entity_key $package_body
344 dict for {architecture arch_info} $architectures {
345 # if {[string first "top_l0mdt" $architecture] != -1} {
350 # if {[string first "top_control" $architecture] != -1} {
355 set entity_key [split $architecture "."]
356 set entity_key "[lindex $entity_key 0].vhdl_entity.[lindex $entity_key 2]"
357 if {[dict exists [dict get $hier_meta all_modules] $entity_key]} {
358 _hier_submodule_append hier_meta $entity_key $architecture
365 set resolution_list [list]
367 dict for {mod_key mod} [dict get $hier_meta all_modules] {
368 set references_data [dict get $mod references]
369 set new_references [list]
370 set mod_lib [dict get $mod library]
372 if { [string first "top_control" $mod_key] != -1 } {
376 foreach ref $references_data {
377 # puts "Processing ref: $ref in mod: $mod_key"
378 set parts [split $ref "."]
379 set library [lindex $parts 0]
380 set type [lindex $parts 1]
381 set name [lindex $parts 2]
385 set resolved_mod_name ""
387 if { ($library != "unknown" && $library != "work" && $type != "component") || ($library == "unknown" && $type != "component") } {
388 # puts "Keeping reference as-is: $ref"
389 lappend new_references $ref
394 if { $library != "unknown"} {
401 set pattern "${library}\.\[vhdl_entity|verilog_module\]\\.$name\$"
402 set matches [ dict filter [dict get $hier_meta all_modules] script {k v} {expr {[regexp $pattern $k]}}]
403 if {[dict size $matches] == "0"} {
404 set pattern ".*\.\[vhdl_entity|verilog_module\]\\.$name\$"
405 set matches [ dict filter [dict get $hier_meta all_modules] script {k v} {expr {[regexp $pattern $k]}}]
408 if {[dict size $matches] == "0"} {
409 lappend new_references $ref
410 Msg Debug "No match found"
411 # puts "Debug: No match found for ref: $ref in mod: $mod_key"
413 dict for {k v} $matches {
414 lappend new_references $k
415 Msg Debug "Mod: $mod_key resolved $ref to $k"
416 # puts "Debug: Mod: $mod_key resolved $ref to $k"
423 dict set hier_meta all_modules $mod_key references $new_references
427 return [dict create total $total_resolved resolutions $resolution_list]
430 proc dfs_sort {hier_meta_ref top_module} {
431 upvar 1 $hier_meta_ref hier_meta
434 proc _dfs_visit {hier_meta_ref node sorted_ref bad_nodes_ref} {
435 upvar 1 $hier_meta_ref hier_meta
436 upvar 1 $sorted_ref sorted
437 upvar 1 $bad_nodes_ref bad_nodes
439 if {![dict exists $hier_meta all_modules $node]} {
443 set mod [dict get $hier_meta all_modules $node]
444 set color [dict get $mod color]
446 if {$color eq "gray"} {
447 Msg Warning "Warning: Circular dependency detected at $node"
448 if {[lsearch -exact $bad_nodes $node] == -1} {
449 lappend bad_nodes $node
454 if {$color eq "black"} {
458 dict set mod color "gray"
459 dict set hier_meta all_modules $node $mod
462 set references [dict get $mod references]
463 foreach child $references {
467 set mod [dict get $hier_meta all_modules $node]
468 dict set mod color "black"
469 dict set hier_meta all_modules $node $mod
477 _dfs_visit hier_meta $top_module sorted bad_nodes
479 if {[
llength $bad_nodes] > 0} {
480 return [dict create success 0 sorted {} cycles 1 bad_nodes $bad_nodes]
483 return [dict create success 1 sorted $sorted cycles 0 bad_nodes {}]
487 proc _debug_string_hier_meta {hier_meta_ref {indent 0}} {
488 upvar 1 $hier_meta_ref hier_meta
490 set ind [
string repeat " " $indent]
491 set s "${ind}=== ALL MODULES ==="
492 dict for {key mod} [dict get $hier_meta all_modules] {
493 append s "\n${ind}$key:"
494 dict for {field value} $mod {
495 append s "\n${ind} $field: $value"
500 append s "\n${ind}=== PROJECT FILES ==="
501 dict for {file finfo} [dict get $hier_meta proj_files] {
502 append s "\n${ind}$file:"
503 dict for {field value} $finfo {
504 append s "\n${ind} $field: $value"
513 proc Hierarchy {listProperties listLibraries repo_path {output_path ""} \
514 {compile_order 0} {light ""} {top_module_override ""} {ignore_opt_list ""} {include_ieee 0} {include_gen_prods 0}} {
519 set ignore_list [list]
521 if {$include_ieee == 0} {
522 lappend ignore_list "ieee.*.*"
523 lappend ignore_list "std.*.*"
526 foreach pat [
split $ignore_opt_list ","] {
527 set pat [
string trim $pat]
529 if {![regexp {^[\w*]+\.[\w*]+\.[\w*]+$} $pat]} {
530 Msg Warning "Warning: ignore pattern '$pat' does not match expected format <lib>.<type>.<name> (wildcards * allowed), ignoring"
532 lappend ignore_list $pat
537 if {$top_module_override ne ""} {
538 set top_module $top_module_override
539 Msg Warning "Using specified top module: $top_module"
542 dict for {lib files} $listLibraries {
543 set lib [file rootname $lib]
547 if {[dict exists $listProperties $f]} {
549 set fprops [dict get $listProperties $f]
550 if {$top_module eq ""} {
551 set top [lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $fprops] 1]
553 set ext [file extension $f]
554 if {$ext eq ".vhd" || $ext eq ".vhdl"} {
555 set top_module "${lib}.vhdl_entity.${top}"
556 } elseif { $ext eq ".v" || $ext eq ".sv"} {
557 set top_module "${lib}.verilog_module.${top}"
559 set top_module "${lib}.component.${top}"
566 dict set hier_meta proj_files $f [_create_proj_file_info $f $lib $props]
570 if {$top_module_override eq ""} {
571 Msg Info "Top module from properties: $top_module"
575 dict for {file file_info} [dict get $hier_meta proj_files] {
576 _hier_parse_file hier_meta $file_info
579 set parse_us [
lindex $t_parse 0]
580 set parse_ms [
expr {$parse_us / 1000.0}]
582 Msg Info "Completed initial parsing in $parse_ms ms"
584 set t_resolve [
time {
585 set resolve_result [_reference_resolver hier_meta]
587 set resolve_us [
lindex $t_resolve 0]
588 set resolve_ms [
expr {$resolve_us / 1000.0}]
590 set total [dict get $resolve_result total]
591 set resolutions [dict get $resolve_result resolutions]
592 Msg Info "Completed reference resolution: $total references resolved in $resolve_ms ms"
595 if {$output_path != ""} {
596 set output_file [open $repo_path/$output_path "w"]
603 set sorted_modules [
dfs_sort hier_meta $top_module]
604 set bad_nodes [dict get $sorted_modules bad_nodes]
608 if {$compile_order} {
611 set p [
print_hierarchy hier_meta $top_module $output_file $ignore_list $bad_nodes $light]
612 PrintOrWrite $output_file "\n\n=====Packages in project:====="
613 dict for {lib pkg_list} $p {
614 if {[llength $pkg_list] == 0} {
617 PrintOrWrite $output_file "Library: $lib"
618 foreach pkg_entry $pkg_list {
619 PrintOrWrite $output_file " Package: $pkg_entry"
624 if {$output_path != ""} {
629 proc print_compile_order {hier_meta_ref sorted_list {output_file ""}} {
630 upvar 1 $hier_meta_ref hier_meta
633 set curr_file_type ""
634 set curr_files [list]
636 foreach mod_key $sorted_list {
637 set mod [dict get $hier_meta all_modules $mod_key]
638 set file_path [dict get $mod file_path]
639 set props [dict get $mod properties]
640 set file_type [dict get $props filetype]
642 if {$file_type ne $curr_file_type} {
643 if {$curr_file_type ne ""} {
644 lappend groups [list $curr_file_type $curr_files]
646 set curr_file_type $file_type
647 set curr_files [list $file_path]
649 lappend curr_files $file_path
653 if {$curr_file_type ne ""} {
654 lappend groups [list $curr_file_type $curr_files]
657 foreach group $groups {
658 set type [
lindex $group 0]
659 set files [lsort -unique [
lindex $group 1]]
660 set output_line "$type \{$files\}"
665 proc print_hierarchy {hier_meta_ref module {output_file ""} {ignore_list ""} \
666 {bad_nodes ""} {light 0} {indent 0} {stack_ref ""} {last_properties_ref ""} {is_last 1}} {
667 upvar 1 $hier_meta_ref hier_meta
675 if {$stack_ref eq ""} {
677 set last_properties [list]
679 upvar 1 $stack_ref stack
680 upvar 1 $last_properties_ref last_properties
684 if {![dict exists $hier_meta all_modules $module]} {
685 set parts [
split $module "."]
686 set lib [
lindex $parts 0]
687 set type [
lindex $parts 1]
688 set name [
lindex $parts 2]
693 set mod [dict get $hier_meta all_modules $module]
695 set name [dict get $mod name]
696 set type [dict get $mod type]
697 set lib [dict get $mod library]
698 set file_path [dict get $mod file_path]
703 if {$type eq "vhdl_entity" && $module_exists} {
704 set references [dict get $mod references]
706 foreach ref $references {
707 if {[
string match "${lib}.vhdl_architecture.${name}.*" $ref]} {
708 lappend arch_refs $ref
711 if {[
llength $arch_refs] == 1} {
712 set arch_key [
lindex $arch_refs 0]
713 if {[dict exists $hier_meta all_modules $arch_key]} {
714 set arch_mod [dict get $hier_meta all_modules $arch_key]
715 set arch_file_path [dict get $arch_mod file_path]
716 if {$arch_file_path eq $file_path} {
717 set p [
print_hierarchy hier_meta $arch_key $output_file $ignore_list $bad_nodes $light $indent stack last_properties $is_last]
718 set package_dict [
MergeDict $p $package_dict]
727 if {[lsearch -exact $stack $module] != -1} {
728 if {[lsearch -exact $bad_nodes $module] != -1} {
734 lappend stack $module
738 for {
set i 0} {$i < [
llength $last_properties]} {
incr i} {
739 if {[
lindex $last_properties $i]} {
740 append indent_str " "
742 append indent_str "│ "
759 set path_str " - ${file_path}"
762 if {$type == "vhdl_architecture"} {
763 set name "[dict get $mod entity].$name"
767 if {[
string first "vhdl_package" $type] == -1} {
768 if {!$module_exists} {
769 set msg "${indent_str}${connector}${lib}.${name} (${type})"
770 }
elseif {$is_circular} {
771 set msg "${indent_str}${connector}${lib}.${name} (${type})${path_str} \[WARNING: circular reference detected\]"
773 set msg "${indent_str}${connector}${lib}.${name} (${type})${path_str}"
776 if {[
DictGet $package_dict "$lib"] == ""} {
777 dict set package_dict "$lib" [list]
779 set package_list [
DictGet $package_dict "$lib"]
780 if {![
IsInList $package_list "${name} ${path_str}"] } {
781 lappend package_list "${name} ${path_str}"
782 dict set package_dict "$lib" $package_list
789 if {$is_circular || !$module_exists} {
793 set references [dict get $mod references]
794 set all_subs [lsort -unique $references]
797 set filtered_subs [list]
798 foreach sub $all_subs {
800 lappend filtered_subs $sub
804 set num_subs [
llength $filtered_subs]
806 foreach sub $filtered_subs {
808 set is_last_child [
expr {$sub_idx == $num_subs}]
810 lappend last_properties $is_last
811 set p [
print_hierarchy hier_meta $sub $output_file $ignore_list $bad_nodes $light [
expr {$indent + 1}] stack last_properties $is_last_child]
812 set package_dict [
MergeDict $p $package_dict]
813 set last_properties [
lrange $last_properties 0 end-1]
816 set stack [
lrange $stack 0 end-1]
822 proc get_rtl_refs {node {name ""}} {
823 set out [dict create]
825 if {$name ne "" && ![
catch {dict get $node reference_info} refinfo]} {
827 catch {
set rt [dict get $refinfo ref_type]}
828 catch {
set rn [dict get $refinfo ref_name]}
829 if {[
string equal $rt "hdl"] && $rn ne ""} {
830 dict set out $name $rn
834 if {![
catch {dict get $node components} comps]} {
835 dict for {cname cnode} $comps {
836 set childMap [get_rtl_refs $cnode $cname]
837 set out [dict merge $out $childMap]
841 dict for {k v} $node {
842 if {$k eq "components" || $k eq "reference_info"} {continue}
843 if {[catch {dict size $v}]} {continue}
844 set childMap [get_rtl_refs $v $k]
845 set out [dict merge $out $childMap]
851 proc _hier_parse_bd {hier_meta_ref file_info} {
852 upvar 1 $hier_meta_ref hier_meta
856 if {![
file exists $f]} {
return}
861 set name [
file rootname [
file tail $f]]
862 set mod_properties [dict create]
863 dict set mod_properties filetype "BD"
865 set bd_file [open $f r]
866 set bd_json [read $bd_file]
868 set bd_design $bd_json
870 set lines [
split $bd_design "\n"]
871 set filtered_lines {}
872 foreach line $lines {
873 if {[
string first "\\" $line] == -1} {
874 lappend filtered_lines $line
877 set bd_design [
join $filtered_lines "\n"]
879 regsub -all {":\s*\{} $bd_design " \{" bd_design
880 regsub -all {:\s*("(?:[^"\\]|\\.)*")} $bd_design { {\1}} bd_design
881 regsub -all {"} $bd_design {} bd_design
882 regsub -all {,} $bd_design {} bd_design
883 regsub -all {:\s* \{} $bd_design {\{} bd_design
884 regsub -all {\[} $bd_design "\{" bd_design
885 regsub -all {\]} $bd_design "\}" bd_design
887 set bd_design [
string range $bd_design 1 end-1]
889 if {[
catch {dict size $bd_design} err]} {
890 Msg Warning "Warning: malformed bd_design in $f, skipping"
894 set bd_design [
lindex $bd_design 1]
896 set unknown_modules {}
898 if {[lsearch -exact $unknown_modules $v] == -1} {
899 lappend unknown_modules "unknown.component.$v"
902 _store_module hier_meta $name $library component $f $unknown_modules $mod_properties