By default an identifier that has been imported into a package is not
available for imports by other packages. Only imports that have been
exported can be imported again. E.g.
```
package P1;
int x;
endpackage
package P2;
import P1::x;
export P1::x;
endpackage
module test;
import P2::x; // This will only work if x has been exported.
endmodule
```
Exports follow the same syntax as imports and allow both export of specific
identifiers or wildcard export. Export supports the special `*::*` target,
which will export all imported items.
Add support for handling package exports.
There is one special cases that needs to be considered. Usually when using
wildcard imports from multiple packages it is an error if there multiple
import candidates for an identifier. With exports it is possible that there
are multiple candidates through different packets, but they all refer to
the same identifier. In this case it does not create a conflict. E.g.
```
package P1;
int x;
endpackage
package P2;
import P1::x;
export P1::x;
endpackage
package P3;
import P1::*;
import P2::*;
int y = x; // No import conflict
endpackage
```
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog supports type parameters. These are similar to value
parameters, but they allow to pass a type to a module or similar when
instantiating it.
E.g.
```
module A #(parameter type T = int);
endmodule
module B;
A #(.T(real)) i_a;
endmodule
```
Add support for handling type parameters.
For the vlog95 and vhdl backends type parameters, similar to typedefs, get
replaced with their actual value. For modules with non-local type
parameters for each module instance a unique module or architecture is
generated with the actual type.
Querying type parameters through VPI is not yet supported.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Currently typedefs are just a pointer to a data_type_t.
Currently typedefs are implemented by setting the name field of a
data_type_t when a typedef of the type is declared. This works mostly, but
there are some corner cases that can't be supported.
E.g. a typedef of a typedef does not work as it overwrites the name field
of the same data_type_t multiple times.
Forward typedefs can also not be supported since forward typedefs allow to
reference a type before it has been declared.
There are also some problems with type identifier references from a
higher-level scope if there is a type identifier in the current scope with
the same name, but it is declared after the type identifier has been
referenced. E.g. in the following x should be a vector fo width 8, but it
will be a vector of width 4, because while the right type is used it is
elaborated in the wrong scope.
```
localparam A = 8;
typedef logic [A-1:0] T;
module M;
localparam A = 4;
T x;
typedef int T;
endmodule
```
Furthermore typedefs used for the type of ports are elaborated in the wrong
scope.
To handle these corner case issues introduce a data_type_t for typedefs.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The has_parameter_port_list member of the LexicalScope class is not
initialized, which means its default value is undefined. This leads to
random failures where a parameter is marked as non-overridable when it
shouldn't.
Make sure has_parameter_port_list is properly initialized to false.
Fixes: 673b40b78c ("Elaborate `parameter` as non-overridable where required")
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
For modules, programs and interfaces that have a parameter port list, a
parameter declared inside the scope's body is supposed to be elaborated as
a local parameter.
TThis is described in the Verilog LRM (1364-2005) section 4.10.1 ("Module
parameters") and the SystemVerilog LRM (1800-2017) section 6.20.1
("Parameter declaration syntax").
Implement this by marking a parameter declared in such a way as
non-overridable.
Note that a parameter declared within a named block, function or task can
still be overridden, even if the module has a parameter port list.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Parameters declared in certain scopes behave like local parameters and can
not be overridden. Rather than making those parameters a localparam track
whether a parameter can be overridden.
This allows to generate better error messages when trying to override the
parameter.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
During parsing parameters and localparams are kept in a separate list only
to be collected into the same list during elaboration.
Store them in the same list during parsing as well, this allows to remove
some duplicated code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
enums for a scope are stored in a std::set. This means when iterating over
the enums during elaboration it is possible that they are elaborated in a
different order than they have been declared in. This causes problems if
one enum references items of the other enum. E.g.
```
enum {
A
} a;
enum {
B = A
} b;
```
In the current implementation whether this works or not depends on the
pointer values of the enum_type_t for `a` and `b`, which can change between
environments.
To make sure that enums are elaborated in the same order use a std::vector
instead of a std::set.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The IEEE standard specifies that the numbering of generate blocks
restarts at 1 in each new scope, and that the 'else' part of an 'if'
construct is part of the same constuct, so has the same number.
This eliminates some indeterminism in the error messages, which was
causing occasional failures in CI. We don't expect this list to be
very large, so the O(n) insertion time should not be a problem.
Use the common data_type_or_implicit rules to support type
definitions for parameters. This eliminates a bunch of special
rules in parse.y, and opens the door for parameters having
more complex types.
Explicit imports should always conflict with local declarations using
the same name. Wildcard imports only conflict if they are referenced
before a local declaration with the same name.
This also unifies the detection of identifier conflicts.
This implements and enforces the full set of rules for determining
timescales in SystemVerilog. The previous relaxation of the rules
that allowed timescales to be redefined within the compilation unit
scope has been removed. Time unit and precision redeclarations are
now recognised after a nested module declaration.
SystemVerilog allows tasks, functions, and classes to be defined at the
root level or inside packages, so we can't rely on an enclosing module
being present to provide the timescale.
If a static variable declared in a task, function, or block has an
initialisation expression, SystemVerilog requires the declaration to
have an explicit static lifetime. This is supposed to be a compile
error, but for now just output a warning.
Implementing this required adding support in the parser for explicit
lifetimes in variable declarations. For now, just output an error if
the user asks for a lifetime that isn't the default for that scope.
There were also some subtleties related to using enumerations
from typedefs and using them in multiple places. Fix various
bugs related to those issues.
This was temporarily implemented to just copy definitions to the
local scope, but the better method is to create a PEIdent that has
the package attached to it.
Class methods belong in a class scope, not the containing module.
So create a lexical scope that carries tasks and functions and
create a PClass to represent classes.
This gets me to the point where the parser stashes a defined type,
and the lexical analyzer uses the type names to differentiate
IDENTIFIER and TYPE_IDENTIFIER.
The pform propagates the parsed enum base type information
to the elaborator so that the base type can be fully elaborated.
This is necessary to get the types of the enumeration literals
correct.
When enum names are used as r-values in expressions, use their
values. Treat the enum names similar to (but not exactly as)
localparams so that they fit into the rest of the elaboration
flow naturally.
Creation of implicit nets requires knowledge of whether an identifier
has been declared before it is used. Currently implicit nets are
created during elaboration, but by this stage the order of declaration
and use is not known. This patch moves the creation of implicit nets
into the parser stage.
Currently, parameters and localparams declared in tasks, functions,
generate blocks, and named blocks are placed in the parent module
scope. Event declarations in these scopes are not permitted (a
syntax error is reported). This patch corrects this behaviour, so
that all the above declarations are accepted and are placed in the
scope in which they are declared.
Note that the IEEE standard does not permit parameter declarations
in generate blocks. This patch causes the parser to reject such
declarations.
Within generate schemes it is possible to have nested scopes, even
more liberally then outside generate blocks. So see to it that the
scopes properly stack with the generate blocks, and that wires and
behaviors are put in the right scopes.
Named begin/end blocks burried within generate schemes need to be
elaborated. Handle this by remembering to elaborate_scope on the
statements within the generate scheme.
In the process, clean up/regularize some of the member names and
methods.
Normally processes are found in the lexical scope of a module, but
there are special cases where processes (other then task/function
definitions) are in other lexical scopes. The most likely case is
initilizations that are in the lexical scope where the assigned
variable is declared.
In the process, the behaviors list is kept in the base PScope class
instead of the Module or any other derived lexical scope class.
Move the storage of wires (signals) out of the Module class into
the PScope base class, and instead of putting the PWires all into
the Module object, distribute them into the various lexical scopes
(derived from PScope) so that the wire names do not need to carry
scope information.
This required some rewiring of elaboration of signals, and rewriting
of lexical scope handling.
All the pform objects that represent lexical scope now are derived
from the PScope class, and are kept in a lexical_scope table so that
the scope can be managed.
Modules, functions and tasks are named scopes so derive them all
from the PScope base class. These items all take scoped items, so
the eventual plan is to move these items into PScope.