Hog v9.78.0
hierarchy.tcl
Go to the documentation of this file.
1 #
2 # data structure:
3 # hier_meta {
4 # all_modules {
5 # module_key {
6 # name {}
7 # library {}
8 # type {}
9 # file_path {}
10 # references {} # list of module_keys
11 # properties {}
12 # }
13 # }
14 #
15 # proj_files {
16 # file_path {
17 # file_path {}
18 # ext {}
19 # library {}
20 # properties {}
21 # }
22 # }
23 # }
24 
25 
26 source $tcl_path/utils/hdl_parser.tcl
27 
28 proc PrintOrWrite {output_file msg} {
29  if {$output_file ne ""} {
30  puts $output_file $msg
31  } else {
32  puts $msg
33  }
34 }
35 
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 {}
41  return $hier_meta
42 }
43 
44 proc _compute_file_checksum {file_path} {
45  if {![file exists $file_path]} {
46  return ""
47  }
48 
49  if {[catch {package require md5 2.0.7} result]} {
50  # Fall back to command line md5sum
51  if {[catch {exec md5sum $file_path} output]} {
52  return ""
53  }
54  return [lindex $output 0]
55  } else {
56  return [string tolower [md5::md5 -hex -file $file_path]]
57  }
58 }
59 
60 
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]
64 }
65 
66 proc is_ignored_module {module_key ignore_patterns} {
67  foreach pattern $ignore_patterns {
68  if {[string match $pattern $module_key]} {
69  return 1
70  }
71  }
72  return 0
73 }
74 
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]
81  return $file_info
82 }
83 
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] }
88 
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
91 
92  set key "${mod_library}.${mod_type}.${mod_name}"
93  set mod [dict create]
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"
101 
102  dict set hier_meta all_modules $key $mod
103 }
104 
105 proc _hier_parse_hdl {hier_meta_ref file_info} {
106  upvar 1 $hier_meta_ref hier_meta
107 
108  set top_control 0
109  if {[string first "top_control" $file_info] != -1} {
110  set top_control 1
111  }
112 
113  set top_l0mdt 0
114  if {[string first "top_l0mdt" $file_info] != -1} {
115  set top_l0mdt 1
116  }
117 
118  #Msg Info "Parsing HDL file: [file_info_path $file_info]"
119  set f [file_info_path $file_info]
120  if {![file exists $f]} { return }
121 
122  set library [file_info_library $file_info]
123  set file_properties [file_info_properties $file_info]
124  set ext [string tolower [file extension $f]]
125 
126  Msg Debug "Parsing $f"
127  set discovered_modules [list]
128 
129  set hdl_constructs [parse_hdl_file $f]
130  foreach node $hdl_constructs {
131  Msg Debug "[hdl_node_string $node]"
132  # if {$top_control == 1 || $top_l0mdt == 1} {
133  # puts "Node: "
134  # puts "$node"
135  # }
136  set node_type [dict get $node type]
137  set node_name [dict get $node name]
138 
139  set references [list]
140  foreach component [dict get $node components_declared] {
141  lappend references "unknown.component.[dict get $component name]"
142  }
143 
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]"
150  }
151  }
152 
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]"
157  }
158  }
159 
160  set references [lsort -unique $references]
161  dict set node references $references
162 
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"
167  } else {
168  dict set mod_properties filetype "Verilog"
169  }
170  } elseif {$ext eq ".vhd" || $ext eq ".vhdl"} {
171  set knownYears {93 2008 2019}
172  set year 2008
173  foreach y $knownYears {
174  if {[lsearch -exact $file_properties $y] != -1} {
175  set year $y
176  break
177  }
178  }
179  dict set mod_properties filetype "VHDL$year"
180  }
181 
182  dict set node properties $mod_properties
183  dict set node library $library
184  dict set node color "white"
185 
186  if {$node_type == "vhdl_architecture"} {
187  set key "${library}.${node_type}.[dict get $node entity].${node_name}"
188  } else {
189  set key "${library}.${node_type}.${node_name}"
190  }
191 
192  dict set hier_meta all_modules $key $node
193  lappend discovered_modules $key
194  }
195  return $discovered_modules
196 }
197 
198 proc _hier_parse_ip {hier_meta_ref file_info {include_gen_prods 0}} {
199  upvar 1 $hier_meta_ref hier_meta
200 
201  set f [file_info_path $file_info]
202  if {![file exists $f]} { return }
203 
204  set library [file_info_library $file_info]
205  set name [file rootname [file tail $f]]
206  set mod_properties [dict create]
207  dict set mod_properties filetype "XCI"
208 
209  set output_dir "."
210  if {[catch {open $f r} fid]} {
211  Msg Warning "Warning: Could not open XCI file: $f"
212  } else {
213  set content [read $fid]
214  close $fid
215 
216  if {[regexp {"OUTPUTDIR":\s*\[\s*\{\s*"value":\s*"([^"]+)"} $content -> dir_value]} {
217  set output_dir $dir_value
218  }
219  }
220 
221  set xci_dir [file dirname $f]
222  set resolved_output_dir [file normalize [file join $xci_dir $output_dir]]
223 
224  # Recursively scan for HDL files (max 5 levels deep)
225  set hdl_files [list]
226  set dirs_to_scan [list [list $resolved_output_dir 0]]
227 
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]
234 
235  if {$current_depth >= 5} { continue }
236  if {![file isdirectory $current_dir]} { continue }
237 
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
244  }
245  } elseif {[file isdirectory $entry]} {
246  set dir_name [file tail $entry]
247  # Skip hdl and synth directories
248  if {$dir_name ne "synth"} {
249  lappend dirs_to_scan [list $entry [expr {$current_depth + 1}]]
250  }
251  }
252  }
253  }
254  }
255 
256  set file_properties [file_info_properties $file_info]
257  set all_subs [list]
258 
259  foreach hdl_file $hdl_files {
260  set ext [string tolower [file extension $hdl_file]]
261 
262  # a lot of xci's share submodules, so we can just reuse the ones we already parsed
263  set file_checksum [_compute_file_checksum $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]
268  continue
269  }
270 
271 
272 
273  set hdl_file_info [_create_proj_file_info $hdl_file $library $file_properties]
274  set discovered_modules [list]
275 
276  if {$ext eq ".vhd" || $ext eq ".vhdl" || $ext eq ".v" || $ext eq ".sv"} {
277  set discovered_modules [_hier_parse_hdl hier_meta $hdl_file_info]
278  }
279 
280  if {$file_checksum ne ""} {
281  dict set hier_meta parsed_files_cache $file_checksum $discovered_modules
282  }
283 
284  set all_subs [concat $all_subs $discovered_modules]
285  }
286 
287  set all_subs [lsort -unique $all_subs]
288 
289  if {[llength $all_subs] == 0} {
290  # if no submodules, we probably haven't generated the output, so store xci as component
291  # so other rtl can find it
292  _store_module hier_meta $name $library "component" $f $all_subs $mod_properties
293  }
294  _store_module hier_meta $name $library "xci" $f $all_subs $mod_properties
295 
296 
297 
298 }
299 
300 
301 proc _hier_parse_file {hier_meta_ref file_info {include_gen_prods 0}} {
302  upvar 1 $hier_meta_ref hier_meta
303 
304  set ext [string tolower [file_info_ext $file_info]]
305  if {$ext eq ".vhd" || $ext eq ".vhdl" || $ext eq ".v" || $ext eq ".sv"} {
306  _hier_parse_hdl hier_meta $file_info
307  } elseif {$ext eq ".xci"} {
308  _hier_parse_ip hier_meta $file_info $include_gen_prods
309  } elseif {$ext eq ".bd"} {
310  _hier_parse_bd hier_meta $file_info
311  } else {
312  Msg Warning "Warning: unrecognized file type for [file_info_path $file_info]"
313  }
314 
315 
316 }
317 
318 proc _hier_submodule_append {hier_meta_ref parent_key sub_key} {
319  upvar 1 $hier_meta_ref hier_meta
320 
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
325  }
326 }
327 
328 
329 proc _reference_resolver {hier_meta_ref} {
330  upvar 1 $hier_meta_ref hier_meta
331 
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"}}]
334 
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
340  continue
341  }
342  }
343 
344  dict for {architecture arch_info} $architectures {
345  # if {[string first "top_l0mdt" $architecture] != -1} {
346  # puts $architecture
347  # puts $arch_info
348  # # exit 0
349  # }
350  # if {[string first "top_control" $architecture] != -1} {
351  # puts $architecture
352  # puts $arch_info
353  # # exit 0
354  # }
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
359  continue
360  }
361  }
362 
363 
364  set total_resolved 0
365  set resolution_list [list]
366 
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]
371  set top_control 0
372  if { [string first "top_control" $mod_key] != -1 } {
373  set top_control 1
374  }
375 
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]
382 
383 
384  set found 0
385  set resolved_mod_name ""
386 
387  if { ($library != "unknown" && $library != "work" && $type != "component") || ($library == "unknown" && $type != "component") } {
388  # puts "Keeping reference as-is: $ref"
389  lappend new_references $ref
390  continue;
391  }
392 
393  set ref_lib ""
394  if { $library != "unknown"} {
395  set ref_lib $library
396  } else {
397  set ref_lib $mod_lib
398  }
399 
400 
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]}}]
406  }
407 
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"
412  } else {
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"
417  incr total_resolved
418  break
419  }
420  }
421  }
422 
423  dict set hier_meta all_modules $mod_key references $new_references
424  }
425 
426 
427  return [dict create total $total_resolved resolutions $resolution_list]
428 }
429 
430 proc dfs_sort {hier_meta_ref top_module} {
431  upvar 1 $hier_meta_ref hier_meta
432 
433 
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
438 
439  if {![dict exists $hier_meta all_modules $node]} {
440  return
441  }
442 
443  set mod [dict get $hier_meta all_modules $node]
444  set color [dict get $mod color]
445 
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
450  }
451  return
452  }
453 
454  if {$color eq "black"} {
455  return
456  }
457 
458  dict set mod color "gray"
459  dict set hier_meta all_modules $node $mod
460 
461 
462  set references [dict get $mod references]
463  foreach child $references {
464  _dfs_visit hier_meta $child sorted bad_nodes
465  }
466 
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
470  lappend sorted $node
471  }
472 
473 
474  set sorted [list]
475  set bad_nodes [list]
476 
477  _dfs_visit hier_meta $top_module sorted bad_nodes
478 
479  if {[llength $bad_nodes] > 0} {
480  return [dict create success 0 sorted {} cycles 1 bad_nodes $bad_nodes]
481  }
482 
483  return [dict create success 1 sorted $sorted cycles 0 bad_nodes {}]
484 }
485 
486 
487 proc _debug_string_hier_meta {hier_meta_ref {indent 0}} {
488  upvar 1 $hier_meta_ref hier_meta
489 
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"
496  }
497  append s "\n"
498  }
499 
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"
505  }
506  append s "\n"
507  }
508  return $s
509 }
510 
511 
512 
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}} {
515  set hier_meta [_create_hier_meta]
516 
517  set top_module ""
518 
519  set ignore_list [list]
520 
521  if {$include_ieee == 0} {
522  lappend ignore_list "ieee.*.*"
523  lappend ignore_list "std.*.*"
524  }
525 
526  foreach pat [split $ignore_opt_list ","] {
527  set pat [string trim $pat]
528  if {$pat ne ""} {
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"
531  } else {
532  lappend ignore_list $pat
533  }
534  }
535  }
536 
537  if {$top_module_override ne ""} {
538  set top_module $top_module_override
539  Msg Warning "Using specified top module: $top_module"
540  }
541 
542  dict for {lib files} $listLibraries {
543  set lib [file rootname $lib]
544 
545  foreach f $files {
546  set props ""
547  if {[dict exists $listProperties $f]} {
548 
549  set fprops [dict get $listProperties $f]
550  if {$top_module eq ""} {
551  set top [lindex [regexp -inline {\ytop\s*=\s*(.+?)\y.*} $fprops] 1]
552  if {$top != ""} {
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}"
558  } else {
559  set top_module "${lib}.component.${top}"
560  }
561  }
562  }
563 
564  set props $fprops
565  }
566  dict set hier_meta proj_files $f [_create_proj_file_info $f $lib $props]
567  }
568  }
569 
570  if {$top_module_override eq ""} {
571  Msg Info "Top module from properties: $top_module"
572  }
573 
574  set t_parse [time {
575  dict for {file file_info} [dict get $hier_meta proj_files] {
576  _hier_parse_file hier_meta $file_info
577  }
578  } 1]
579  set parse_us [lindex $t_parse 0]
580  set parse_ms [expr {$parse_us / 1000.0}]
581 
582  Msg Info "Completed initial parsing in $parse_ms ms"
583 
584  set t_resolve [time {
585  set resolve_result [_reference_resolver hier_meta]
586  } 1]
587  set resolve_us [lindex $t_resolve 0]
588  set resolve_ms [expr {$resolve_us / 1000.0}]
589 
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"
593 
594 
595  if {$output_path != ""} {
596  set output_file [open $repo_path/$output_path "w"]
597  } else {
598  set output_file ""
599  puts ""
600  }
601 
602 
603  set sorted_modules [dfs_sort hier_meta $top_module]
604  set bad_nodes [dict get $sorted_modules bad_nodes]
605 
606  #Msg Debug "[_debug_string_hier_meta hier_meta]"
607  set p [dict create]
608  if {$compile_order} {
609  print_compile_order hier_meta [dict get $sorted_modules sorted] $output_file
610  } else {
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} {
615  continue
616  }
617  PrintOrWrite $output_file "Library: $lib"
618  foreach pkg_entry $pkg_list {
619  PrintOrWrite $output_file " Package: $pkg_entry"
620  }
621  }
622  }
623 
624  if {$output_path != ""} {
625  close $output_file
626  }
627 }
628 
629 proc print_compile_order {hier_meta_ref sorted_list {output_file ""}} {
630  upvar 1 $hier_meta_ref hier_meta
631 
632  set groups [list]
633  set curr_file_type ""
634  set curr_files [list]
635 
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]
641 
642  if {$file_type ne $curr_file_type} {
643  if {$curr_file_type ne ""} {
644  lappend groups [list $curr_file_type $curr_files]
645  }
646  set curr_file_type $file_type
647  set curr_files [list $file_path]
648  } else {
649  lappend curr_files $file_path
650  }
651  }
652 
653  if {$curr_file_type ne ""} {
654  lappend groups [list $curr_file_type $curr_files]
655  }
656 
657  foreach group $groups {
658  set type [lindex $group 0]
659  set files [lsort -unique [lindex $group 1]]
660  set output_line "$type \{$files\}"
661  PrintOrWrite $output_file $output_line
662  }
663 }
664 
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
668 
669  set package_dict {}
670 
671  if {[is_ignored_module $module $ignore_list]} {
672  return
673  }
674 
675  if {$stack_ref eq ""} {
676  set stack [list]
677  set last_properties [list]
678  } else {
679  upvar 1 $stack_ref stack
680  upvar 1 $last_properties_ref last_properties
681  }
682 
683 
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]
689  set file_path ""
690  set module_exists 0
691  } else {
692 
693  set mod [dict get $hier_meta all_modules $module]
694  # puts $mod
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]
699  set module_exists 1
700 
701 
702  # for vhdl entities with 1 architecture in the same file, just use that architecture
703  if {$type eq "vhdl_entity" && $module_exists} {
704  set references [dict get $mod references]
705  set arch_refs [list]
706  foreach ref $references {
707  if {[string match "${lib}.vhdl_architecture.${name}.*" $ref]} {
708  lappend arch_refs $ref
709  }
710  }
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]
719  return $package_dict
720  }
721  }
722  }
723  }
724  }
725 
726  set is_circular 0
727  if {[lsearch -exact $stack $module] != -1} {
728  if {[lsearch -exact $bad_nodes $module] != -1} {
729  set is_circular 1
730  }
731  }
732 
733  if {!$is_circular} {
734  lappend stack $module
735  }
736 
737  set indent_str ""
738  for {set i 0} {$i < [llength $last_properties]} {incr i} {
739  if {[lindex $last_properties $i]} {
740  append indent_str " "
741  } else {
742  append indent_str "│ "
743  }
744  }
745 
746  if {$indent > 0} {
747  if {$is_last} {
748  set connector "└─ "
749  } else {
750  set connector "├─ "
751  }
752  } else {
753  set connector ""
754  }
755 
756  if {$light} {
757  set path_str ""
758  } else {
759  set path_str " - ${file_path}"
760  }
761 
762  if {$type == "vhdl_architecture"} {
763  set name "[dict get $mod entity].$name"
764  }
765 
766 
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\]"
772  } else {
773  set msg "${indent_str}${connector}${lib}.${name} (${type})${path_str}"
774  }
775  } else {
776  if {[DictGet $package_dict "$lib"] == ""} {
777  dict set package_dict "$lib" [list]
778  }
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
783  }
784  return $package_dict
785  }
786 
787  PrintOrWrite $output_file $msg
788 
789  if {$is_circular || !$module_exists} {
790  return $package_dict
791  }
792 
793  set references [dict get $mod references]
794  set all_subs [lsort -unique $references]
795 
796  # Filter out ignored modules before processing
797  set filtered_subs [list]
798  foreach sub $all_subs {
799  if {![is_ignored_module $sub $ignore_list]} {
800  lappend filtered_subs $sub
801  }
802  }
803 
804  set num_subs [llength $filtered_subs]
805  set sub_idx 0
806  foreach sub $filtered_subs {
807  incr sub_idx
808  set is_last_child [expr {$sub_idx == $num_subs}]
809 
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]
814  }
815 
816  set stack [lrange $stack 0 end-1]
817  return $package_dict
818 }
819 
820 
821 
822 proc get_rtl_refs {node {name ""}} {
823  set out [dict create]
824 
825  if {$name ne "" && ![catch {dict get $node reference_info} refinfo]} {
826  set rt ""; set rn ""
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
831  }
832  }
833 
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]
838  }
839  }
840 
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]
846  }
847 
848  return $out
849 }
850 
851 proc _hier_parse_bd {hier_meta_ref file_info} {
852  upvar 1 $hier_meta_ref hier_meta
853 
854  set f [file_info_path $file_info]
855 
856  if {![file exists $f]} { return }
857 
858  set library [file_info_library $file_info]
859  set file_properties [file_info_properties $file_info]
860 
861  set name [file rootname [file tail $f]]
862  set mod_properties [dict create]
863  dict set mod_properties filetype "BD"
864 
865  set bd_file [open $f r]
866  set bd_json [read $bd_file]
867  close $bd_file
868  set bd_design $bd_json
869 
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
875  }
876  }
877  set bd_design [join $filtered_lines "\n"]
878 
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
886 
887  set bd_design [string range $bd_design 1 end-1]
888 
889  if {[catch {dict size $bd_design} err]} {
890  Msg Warning "Warning: malformed bd_design in $f, skipping"
891  return {}
892  }
893 
894  set bd_design [lindex $bd_design 1]
895 
896  set unknown_modules {}
897  dict for {m v} [get_rtl_refs $bd_design] {
898  if {[lsearch -exact $unknown_modules $v] == -1} {
899  lappend unknown_modules "unknown.component.$v"
900  }
901  }
902  _store_module hier_meta $name $library component $f $unknown_modules $mod_properties
903 
904 }