Functions for working with hierarchical identifiers.
Perhaps the most fundamental operation for a hierarchical
identifier is figure out what it refers to. This turns out to be a
surprisingly challenging and nuanced process.
Our top-level routine for following hierarchical identifiers is vl-follow-hidexpr. It is meant to make looking up hierarchical identifiers
convenient despite these complications.
We now describe some of these challenges and how vl-follow-hidexpr
deals with them.
- Datatypes versus Scopes
- Challenge: The same syntax is used to refer to both data structure members
like myinst.opcode and also to scopes like mysubmod.mywire. However,
structures and scopes are very different and need to be traversed in different
- Our Approach: vl-follow-hidexpr follows only the scope-based part of
the HID. However, as one of our return values, we provide the tail of the
hierarchical index that leads into this variable. For instance, in a case like
foo.bar.myinst.opcode where myinst is an instruction_t structure
variable, we will follow only until the declaration of myinst and then we
will return myinst.opcode as the tail.
- Unclear Destination
- Challenge: Depending on the kind of analysis being done, we might want to
continue or stop resolving at certain points. For instance, if we are trying
to size a hierarchical identifier like myinterface.ready, we probably want
to follow through the interface all the way to the ready signal. However,
for light-weight variable use analysis, we may want to stop as soon as we hit
- Our Approach: vl-follow-hidexpr follows the HID as far as possible,
but returns a vl-hidtrace-p that includes not only the final declaration
we arrive at, but also all intermediate points along the way. If you only care
about the final destination (e.g., the ready signal for sizing or similar)
then you can get this final destination from the first vl-hidstep-p in
the trace. But if you also want to know, e.g., that myinterface has been
used, this information can easily be extracted from the rest of the trace.
- Unresolved Parameters
- Challenge: Because of parameters, we may not be able to tell whether the
indices in a hierarchical identifier are valid. For instance, if there is an
array of module instances like mymod myarr [width-1:0] (...) and we are
trying to follow a hierarchical reference like foo.bar.myarr.baz, then
we will not know whether this is valid until we have resolved width.
- In some applications, e.g., for vl-lint, it may be best to allow
these potentially invalid indices. After all, we "know" that this reference
is either invalid or is a reference to baz within mymod. In that
case, we may well wish to assume that the index will be valid and just go on
and find baz.
- However, some other applications have more stringent soundness constraints.
If we are writing transforms that are meant to build conservative, safe, formal
models of the Verilog code, we may instead want to insist that all of the
indices have been resolved and cause an error if this is not the case.
- Our Approach: vl-follow-hidexpr always tries to check that indices
are in bounds and to cause errors when indices are clearly out of bounds. If
we encounter indices that are potentially out of bounds, then we can do one of
- By default, we are permissive and assume the index will be in bounds.
- However, if :strictp is enabled, we will cause an error.
- As a special note: we always require generate array indices to be resolved.
See vl-follow-hidexpr for additional discussion.
- Error reporting
- Challenge: A HID may be invalid in many different ways. Any part of the
HID may try to refer to a name that does not exist, and any index along the HID
might be invalid. If an error occurs, we should provide enough detail to
understand the problem.
- Our Approach: In the case of any error, vl-follow-hidexpr returns a
message. Callers should put this message in the appropriate context so
that the end-user can understand the nature and location of the problem.
- Follow a HID to find the associated declaration.
- Follow a scope expression to find the associated declaration.
- Check array indices against the corresponding array bounds.
- Get the type of a variable of type x after an indexing
operation is applied to it.
- Check an array index against the corresponding array bounds.
- Given a HID expression denoting a variable of the input type, create
a trace showing the type of each field select/indexing operation.
- Report an error while following a HID.
- A single step along the way of a hierarchical identifier.
- Removes any explicit signed indicator from a datatype.
- Given a hid and a suffix, such as a.b.c.d and c.d, return the
hid without the suffix, i.e. a.b.
- Replaces the hidexpr nested inside the scopeexpr.
- Find the block from a generate array corresponding to some index.
- Determines if every index throughout a vl-hidexpr-p is resolved.
- Finds the struct members of x when it is a struct or union.
- Converts a vl-hidindex-p into a string like "bar".
- Finds the hidexpr nested inside the scopeexpr.
- Converts a hierarchical identifier expression into a string like
- Gives the number of packed plus unpacked dimensions on a datatype.
- Looks up a usertype name and returns its definition if successful.
- Determines if every index in a vl-hidindex-p is resolved.
- A list of vl-hidstep structures, typically all of the steps
encountered along a HID.
- A list of vl-selstep-p objects.