Functions for recognizing and following well-formed hierarchical identifiers, scoped identifiers, and indexing expressions.
SystemVerilog provides a very rich syntax for referring to objects in different scopes and throughout the module hierarchy. To deal with this rich syntax, we will need a bit of jargon. These terms are well-defined notions in VL, but may not necessarily be found or used in the same way in the Verilog/SystemVerilog standards.
Identifiers. We say the following expressions are just identifiers. Note that the Verilog/SystemVerilog standards sometimes distinguish between plain and escaped identifiers. While our lexer needs to understand these as different notions, internally there is no difference.
foo \foo$bar
Note that any indexing/selection operations after an identifier is
not part of the identifier. For instance,
Hierarchical identifiers. Identifiers can be chained together, perhaps with indexing, to form hierarchical identifiers. Here are examples of hierarchical identifiers:
foo // any ID is a HID \foo$bar foo.bar // fancier HIDs foo.bar.baz foo.bar[3].baz // Verilog-2005 or SystemVerilog-2012 foo.bar[3][4].baz // SystemVerilog-2012
Hierarchical identifiers may have internal indexing expressions. However,
any subsequent indexing/selection operations are not part of the HID
itself. For instance, we say that
Scope expressions. Hierarchical identifiers can be prefixed with scoping operations, e.g., for packages. Here are some examples of scope expressions:
foo // any ID is a scope expression \foo$bar foo.bar // any HID is a scope expression foo.bar.baz foo.bar[3].baz foo.bar[3][4].baz mypkg::foo // fancier scope expressions mypkg::foo.bar $unit::foo::bar.baz[3].beep
As with ordinary identifiers and hierarchical identifiers, scope expressions
do not have any indexing/selection operators. For example,
Index expressions. Scope expressions can be indexed into by some number of individual bit/array-indexing operations. Here are some examples of index expressions:
foo // any ID is an index expression \foo$bar foo.bar // any HID is an index expression foo.bar.baz foo.bar[3].baz foo.bar[3][4].baz mypkg::foo // any scope expression is an index expression mypkg::foo.bar $unit::foo::bar.baz[3].beep foo[3] // fancier index expressions foo[3][4][5] foo.bar[3] mypkg::foo[3][4][5] $unit::foo::bar.baz[3].beep[2][1][0]
Note that an index expression does not have any part/range selects in
it. That is, an expression like
Note that part/range selection operations, like
VL internally represents hierarchical identifiers as compound vl-expr-p objects. To understand the structure, consider a very complex index expression such as:
ape::bat::cat.dog[3][2][1].elf[7][6][5]
We expect to represent this sort of expression by nesting operations as suggested by the parentheses below. This arrangement matches the jargon above.
Indexing is the outermost operation: ((( ape::bat::cat.dog[3][2][1].elf )[7] )[6] )[5] ------------------------------ ------------------- a scopexpr recursive indexing Followed by scoping: ape::(bat:: (cat.dog[3][2][1].elf) ) --------------------- -------------------- recursive scoping a hidexpr Followed by hierarchy: cat . ((dog [3][2][1]) . elf) ----------------------- sub hidexpr With hierarchical indexing going from outermost to innermost: ((dog [3])[2])[1] ----------------- a hidindex
Where each
The low-level vl-expr-p representation is not very strongly typed and
permits nonsensical expressions like
Instead, in abstract-hids, we set up wrapper functions that provide an interface for working with hierarchical identifier expressions at a somewhat higher level. These wrapper functions include stronger recognizers that ensure that an expression is a well-formed hierarchical identifier, scope expression, or index expression that meets our usual expectations. It also provides convenient accessor functions for traversing well-formed expressions.
For many kinds of transformation and analysis, the fundamental operation on hierarchical, scoped, or indexed expressions is to follow them to what they refer to. To do this correctly requires an detailed understanding of both the concepts above and also scopestacks for looking up identifiers.
Due to this complexity, most code throughout VL should never try to follow hierarchical identifiers on its own. Instead, most code should be make use of the high-level functions described in following-hids.