In addition to providing positional arguments for task and functions
SystemVerilog allows to bind arguments by name. This is similar to how
module ports can be bound by name.
```
task t(int a, int b); ... endtask
...
t(.b(1), .a(2));
```
Extend the parser and elaboration stage to be able to handle this. During
elaboration the named argument list is transformed into a purely positional
list so that later stages like synthesis do not have to care about the
names.
For system functions and tasks all arguments must be unnamed, otherwise an
error will be reported.
In addition to functions and tasks arguments can also be bound by name for
the various different ways of invoking a class constructor.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Attach line information to named items. This allows to provide better
location information for messages involving named items. The location of
item itself can't always be used, since the item itself might be empty.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are a few different places in the parser that all parse named
expressions in the same way. Consolidate them into a single rule.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The last user of the named_number_t type was removed in commit 2f474358d9
("2f474358d99929ec625a46690d1be6939ed67064"). Remove the type as well.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Commit 3daa2982ac ("Add support for `const` variables") added support for
constant variables, but had a small mistake and did propagate the constant
flag from the parser if the variable is declared with the `var` keyword.
Still allowing to modify those variables. Fix this.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog allows to declare const variables. These variables are
read-only and can not be assigned a value after their declaration. It is
only possible to assign an initial value as an initializer.
E.g.
```
const int x = 10;
x = 20; // Error
```
The LRM requires that for variable declarations with static storage the
initializer is a constant expression with the extension that other const
variables are also allowed. const variables with automatic storage can
be initialized by any expression.
Checking if an expression contains only const variables requires a bit more
work to implement. So for now be more lenient that what the standard
requires and allow arbitrary expressions to initialize const variables even
for those with static storage.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
In Verilog it is possible to declare multiple ports as part of the same
port declaration. Ports declared this way all have same direction, signal
kind and data type. E.g.
```
module M (input [3:0] a, b, c) ...
```
SystemVerilog extends this and allows to override on a per port basis
certain port attributes. E.g. redefine just the data type
```
module test (input [3:0] a, [1:0] b, int c) ...
```
Or to just redefine the port kind
```
module test(input [3:0] a, var b, wire c) ...
```
It is even possible to leave out the direction for the very first port. As
long as at least one other property of the port is specified. In that case
the direction will default to `inout`. E.g.
```
module test(integer a, b, c) ...
```
Furthermore it is possible to specify unpacked dimensions for each of the
ports. E.g.
```
module test(input integer a, b[1:0], c[3:0][1:0]) ...
```
If all port properties are omitted for the first port this indicates the
start of a non-ANSI port list.
Extend the parser to handle this.
If all three direction, port kind and data type are omitted they are
inherited from the previous port. Otherwise
* If the direction is omitted it is inherited from the previous port.
* If the data type is omitted it defaults to logic.
* If the port kind is omitted the behavior depends on the direction.
For output ports with an explicit data type it is a variable, for
all others it is a net.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Add a helper function to the parser that handles module port declaration.
This allows to reduce a bit of duplicated code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
At the moment there are two rules for port declarations. One that allows
the port to be declared as an unpacked array, the other that allows to
specify an initializer expression.
SystemVerilog allows both to be specified in the same port declaration. Add
support for this to the parser.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Implicit named port connections are only supported by SystemVerilog. Add a
check to generate an error when trying to use it in Verilog mode.
Regression test br_gh315 is modified to run in SystemVerilog mode since it
makes use of implicit named port connections.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The implicitly generated identifier for implicit named port connections
gets its file and line information from the optional attributes. If no
attribute list is specified this will just point to the beginning of the
file resulting in incorrect line information.
Use the file and line information from the identifier token instead to fix
this.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
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>
As a quirk of the (System)Verilog grammar a function or task argument list
with no arguments can not be distinguished from a argument list with a
single empty argument. The iverilog parses it as the latter. There are
currently many places in the code base where this is fixed up in slightly
different ways.
Fix this up in the parser in a central way before passing the arguments to
the elaboration stage.
The existing implementation in some cases removes all empty trailing
arguments. While this works to handle the case for zero arguments it also
hides some errors that should be detected. E.g. in the following 3
arguments are passed to a function which only takes two arguments. But no
error is reported since the explicitly specified empty arguments are
removed.
```
function f(integer a, integer b = 2); ... endfunction
f(1,,);
```
In the new implementation the empty argument will only be removed if there
is exactly one empty argument in the argument list.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This includes support at the parser (pform) through enaboration
and the netlist format for the break and continue statements.
Elaboration actually already worked for for-loops, but since the code
generators need more information, this is a rewire of that support to
be explicit about for-loops. This means they are not rewritten as fancy
while loops. The code generators will have to handle that.
Given the elaboration of for-loops now work, write the vvp code generator
support needed to implement it.
Now that for-loops are presented as for-loops to the code generator, the
vlog95 code generator doesn't need to infer them anymore. Generate the code
more directly.
Also update the tests list so that the vlog95_reg tests all pass.
Currently package scoped function calls are supported. Update the parser
and elaboration to also allow method calls on packaged scoped variables.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog allows to use either `super` or `this.super` to access the
base class. Both have the same behavior. Currently only `super` is
supported, also add support for `this.super`.
To support it the parser has to be changed slightly and move the trailing
`.` after the `this` or `super` keywords into the `implicit_class_handle`
parser rule. This is necessary to avoid reduce conflicts in the grammar.
As a side effect `super` can no longer be used as a standalone identifier.
E.g. `return super;` But that's not legal SystemVerilog anyway, so that is
OK.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
One variant did, the other variant didn't. As well as being a trap for
the unwary, this gets in the way of using yywarn/VLwarn for non-fatal
"sorry" messages.
By default when creating a new class object the type of the object is
determined by the type of the target.
SystemVerilog also allows to explicitly specify the type of the object to
be created. The specified class still needs to be assignment compatible
with the target. This is e.g. useful to construct an object of a derived
class of the target. E.g.
```
class B; ... endclass
class C extends B; ... endclass
B b;
b = C::new;
```
Add support for this to the parser as well as handling it during
elaboration.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Up to 1800-2017 the grammar in the LRM allowed an optional lifetime
qualifier for class declarations. Icarus supports this and uses this as the
default lifetime for methods of the class. But the LRM never specified what
this qualifier should do actually actually. Starting with 1800-2023 the
qualifier will be removed from the grammar[1].
Furthermore the LRM states that methods of a class are supposed to have
automatic storage and static storage is forbidden.
This currently works in Icarus for the most part since the liftime attached
to class methods is ignored during elaboration in most places. Where it
does not work is for variable initializers where it results in broken code
being generated and vvp crashes at runtime. E.g.
```
class C;
task t;
int x = 10;
endtask
endclass
```
Keep the optional lifetime qualifier for classes in the grammar for now, to
ensure backwards compatibility in case somebody is actually using it. But
ignore it and print a warning if it is specified.
In addition set the default lifetime for all classes to automatic. This
makes sure that variable initialization in classes works as expected.
[1] https://accellera.mantishub.io/view.php?id=3561
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog has explicit support for calling a function
as a statement. This is allowed when the function call is encapsulated in
`void'(...)`. E.g. `void'(f(1, 2, 3));`
We already support calling function calls as statements without the void
cast and emit a warning when doing so.
Adding support for void casts only requires to update the parser to handle
the void cast and then do not emit the warning if a function is called as
a statement as part of a void cast.
Void casting a task or void function call is not allowed and will generate
an elaboration error.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
It is not allowed to create objects of virtual classes. Currently the
virtual keyword is accepted by the parser, but otherwise ignored.
Keep track of whether a class is virtual and generate an error when the
class new operator is used for a virtual type.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The ivl_variable_type_t in PWire is now only used for passing the base type
for vector types to the elaboration stage. But we can query the base the
from the vector_type_t itself. If the there is no data_type_t set for the
PWire the base type will default to IVL_VT_LOGIC.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
For loops may have empty initialization statements. In that case some things
can't be done, such as loop unrolling or synthesis, but otherwise it is a
valid thing to do. So generate the correct code in this case.
SystemVerilog allows to skip dimensions in a foreach loop by not specifying
an identifier name for the dimensions. E.g. the following will iterate over
the first and last dimensions, but skip the middle dimension.
```
int x[1][2][3];
foreach(x[a,,b]) ...
```
Add support for this to the parser as well as elaboration.
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>
SystemVerilog supports forward type declarations. This allows to declare a
type identifier and use it, e.g. in a signal declaration, before declaring
what the actual type is. The type still needs to be fully defined
eventually in the same scope as its forward type declaration. E.g.
```
typedef T;
T x;
typedef int T;
```
The forward type definition can also contain the kind of the type it is
going to be. E.g struct, union, class, etc. The LRM calls this the basic
type. If the actual type is not of the basic type specified in the forward
type declaration this is an error. E.g.
```
typedef struct T;
typedef int T; // Error, int is not a struct
```
It is legal to have more than one forward type declaration for the same
type name, as long as the basic type is the compatible. It is even legal to
have a forward type declaration after the actual type has already been
declared. E.g.
```
typedef T;
typedef int T;
typedef T;
```
Implement support for forward type definitions as part of the new
typedef_t. The basic type will be attached to the typedef_t.
The compatibility of the basic type for multiple forward type declarations
will be checked in the parser. The compatibility of the basic type to the
actual type will be checked during elaboration, once the actual type is
known.
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>
SystemVerilog supports using compressed assignment operators for the genvar
for loop variable update.
Add support for this in a similar way as increment/decrement operators by
transforming the statement to its uncompressed equivalent. E.g. `x += y`
gets transformed to `x = x + y`.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Currently when referencing a typedef this gets replaced with the
`data_type_t` that the typedef points to. This works for most cases, but
there are some corner cases where it breaks down.
E.g. it is possible to have a scoped type identifier which references a
type defined in a package. For such type identifiers, only the data_type_t
itself is remembered, but not the package scope. This will cause the type
identifier to be elaborated in the wrong scope.
Furthermore type identifiers of vector types used for module or task port
might not be elaborated in the correct scope.
Introduce a new `typeref_t` which has `data_type_t` as a base type and can
be used as the data type for a signal. A new instance of a `typeref_t` is
created when referencing a type identifier. The `typeref_t` remembers both
the data type and the scope of the type identifier.
When elaborating the `typeref_t` the elaboration is passed through to the
referenced `data_type_t`. But special care is taken to lookup the right
scope first.
With the new approach also typedefs of typedefs are supported. This
previously did not work because chained typedefs all reference the same
`data_type_t`, but each typedef sets the `name` field of the `data_type_t`.
So the second typedef overwrites the first typedef and a lookup of the
scope of the first typedef by name will fail as it will return the scope of
the second typedef.
This refactoring also allows to define clear ownership of a data_type_t
instance. This e.g. means that an array type owns its base type and the
base type can be freed when the array type itself is freed. The same is
true for signals and class properties, they now own their data type and the
data type can be freed when the signal or property is freed.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>