If a net or variable is referenced in another net or variable declaration
or in a value parameter definition (e.g. when using the $bits function)
and hasn't already been elaborated, we need to elaborate it early. So
during the scope elaboration phase, add placeholders in each NetScope
object to record the PWire objects that are yet to be elaborated. This
allows the symbol_search() function to find the unelaborated objects
and to trigger early elaboration.
Add a flag in the PWire object to indicate when we are elaborating it.
This allows us to detect circular references and avoid an infinite loop.
This fixes issue #483, issue #575, and issue #1097.
When using the Icarus extension for wire types, only logic (4-state)
wires may have multiple drivers (as documented in extensions.rst).
Other types of wire should be treated as unresolved wires.
When the Icarus extension is disabled, only 4-state wires are
allowed (IEEE 1800-2017 section 6.7.1).
Most places in the code use a std::vector for array dimensions.
The only exception is the constructor of NetNet, which uses
a `std::list` to pass the unpacked dimensions. But to store the
unpacked dimensions it also uses a `std::vector`.
There does not seem to be a good reason why the constructor
has to take a `std::list`, so switch it also to `std::vector`.
This allows to simplify the code and remove some special handling
for `std::list<netrange_t>`.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
`std::vector<netrange_t>` is used for signal array dimensions. As such it is
used in quite a few places.
Add a typedef that can be used as a shorthand to refer to it. This helps to
keep lines where this is used from growing to overly long.
The new type is called `netranges_t`.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are two `NetNet` constructors, one for arrays and one for non-arrays.
There are a few places where the array constructor is used for non-arrays,
but with an empty unpacked dimensions list. Switch this over to using the
non-array constructor.
This slightly reduces boiler-plate code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
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>
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>
`ivl_assert()` is similar to `assert()` except that it will also include
source file and line information about the expression for which the assert
was triggered.
Use `ivl_assert()` instead of `assert()` where the line information is
available. This will generate better bug reports and make it easier to
diagnose why an assert is triggered.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
In SystemVerilog identifiers can usually have an additional package scope
in which they should be resolved. At the moment there are many places in
the code base that handle the resolution of the package scope.
Add a common data type for package scoped paths as well as a
symbol_search() variant that works on package scoped identifiers. This
allows to handle package scope resolution in a central place.
Having the code in a central place makes it easier to ensure consistent and
correct behavior. E.g. there are currently some corner case bugs that are
common to all implementations. With the common implementation it only has
to be fixed in one place.
It will also make it easier to eventually implement class scoped
identifiers.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
It was common practice in the past to just declare a port direction
and declare it as a vector in a subsequent type declaration. Versions
of the standard up to and including 1364-2005 include an example that
does this (e.g. 1364-2005 section 12.3.7). Users may have old or
third-party code that they can't or don't want to modify, so allow
the warning to be suppressed by including it in the anachronisms
category.
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>
Elaborating uses a multi stage approach. Currently non-static class
properties are elaborated during the scope elaboration phase. This can
cause problems if the type of a property is declared in a different scope.
In that case it is possible that the scope in which the type is defined has
not been elaborated yet and the type is not available. E.g.
```
package P;
typedef int T;
endpackage
class C;
P::T x;
endclass
```
Another area where this is problematic is when a class has a property of a
another class that has a forward declaration. In this case the type of the
forward declared class, which is created when the scope is elaborated, is
not available when the scope of the class that is using it is elaborated.
E.g.
```
typedef class B;
class A;
B b;
endclass
class B;
endclass
```
To avoid this elaborate the properties during the signal elaboration phase.
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>
It is currently possible to declare an unpacked array with multiple
dimensions. But trying to declare an unpacked array that has another
unpacked array as a base type will result in undefined behavior. E.g.
```
typedef int T1[1:0];
typedef T1 T2[3:0];
T2 x[7:0];
```
To support this recursively unwrap the data type and add the unpacked
dimensions to the signal until the base type no longer is a unpacked array
type.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
If the type of a port is an array type it currently always gets evaluated
in the scope where the port is declared.
But if the type is a typedef it might be declared in a different scope and
must be evaluated in that scope. E.g. the following will declare an array
port with 10 entries and an element type of a 5 bit vector, while it should
declare one with 4 entries and an element type of a 2 bit vector.
```
localparam A = 2;
localparam B = 4;
typedef [A-1:0] T[B];
module test (
T x
);
localparam A = 5;
localparam B = 10;
endmodule
```
This is in part due to array types being given special handling. This was
necessary before because each base type required slightly different
handling and so the base type had to be extracted from the array type.
This has now been consolidated and all data types are treated the same.
The only exception is the vector type which still needs special handling to
support separate definition of port direction and type.
As a result it is possible to remove the special handling of the array
type. This solves the problem of evaluating the type in the wrong scope.
Some special handling needs to be retained though to be able to
differentiate between array dimensions that are part of a type and array
dimensions that are part of port declaration. This is again necessary to
correctly support separate definition of port direction and type. E.g. in
the example below port `x` and `y` get treated slightly differently, even
though the resulting signals will be identical.
```
typedef logic [7:0] T[1:0];
...
input T x;
input [7:0] y[1:0];
```
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
(System)Verilog allows to declare the port direction separate from the
signal declaration. E.g.
```
output x;
integer x;
```
But this is only allowed if the port declaration
* does not have an explicit net type
* does not have an explicit data type
* is a non-ANSI style declaration
For all other cases of port declarations the signal is considered fully
defined and it is not allowed to have a separate signal declaration.
In addition the declared packed dimensions need to match between the port
and signal declaration.
In the current implementation there are a few cases where this is not
handled correctly.
1) It is possible to declare non-ANSI task ports with the same name over
and over again, if it was declared as a signal before the port.
```
task t;
string x;
input logic x;
output real x;
endtask
```
2) It is possible to re-declare non-ANSI input ports of a module that have
a data type, but no explicit net type.
```
module M;
input integer x;
wire integer x;
endmodule
```
3) It is possible to re-declare a ANSI port if it has an implicit data type.
```
module M(output [1:0] x);
reg [1:0] x;
endmodule
```
4) It is possible to declare a vector signal for a scalar non-ANSI task
port.
```
task t;
input x;
reg [7:0] x;
```
To handle all of these correctly refactor signal declaration and lookup a
bit.
The PWire class that represents a signal already has two flags `port_set_`
and `net_set_`. These flags indicate whether a signal has already been used
in a port or signal declaration. A port declaration that includes an
explicit data type is considered both a port and signal declaration.
Use these flags to decide whether it is possible to extend an existing
declaration. E.g. when creating a port without an explicit data type and a
PWire by that name already exists and the `port_set_` flag is not set
extend the existing PWire. On the other hand if the `port_set_` flag is
already set report an error.
Similar for signals but with the `net_set_` flag.
For port declarations with an explicit data type or ANSI style port
declarations it is always an error if a PWire by that name already exists.
This is for both module and task/function ports.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog allows input ports to be variables. If something is connected
to the input port it will be converted to an unresolved wire.
This is handled the same as having a continuous assignment on a
SystemVerilog varibale.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The custom `svector` class is essentially a subset of `std::vector`. There
is no inherent advantage to using `svector`. Both have the same memory
footprint.
`svector` was designed to be of static size, but there are a few places in
the parser where it has to grow at runtime. Handling this becomes a bit
easier by switching to `std::vector` since it is possible to use its
methods which take care of resizing the vector.
This also allows to remove the unused parameter of the `lgate` struct
constructor, which was only needed for compatibility with `svector`.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are currently two implementations for elaborating unpacked array
types. One that is used when elaborating a signal with an unpacked array
type and one that is used everywhere else using the elaborate_type()
infrastructure.
The elaborate_type() implementation is less complete and for example does
not support bounded queue types.
Consolidate both into a single implementation to reduce duplicated code and
get consistent behavior. This for example makes sure that the maximum queue
size is respected when used as a function return type.
Nested data structures of arrays, dynamic arrays or queues are not yet
supported. In the current implementation when encountering such a type an
assert will be triggered and the application crashes. In the new
implementation an error message will be printed without crashing the
application.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Currently a `vector_type_t` with the base type set to `IVL_VT_REAL` is used as
the data type for real type signals. But there is also the `real_type_t` data
type, which is used as the data type for function return types and class
properties.
Move signals also over to using `real_type_t`. This ensures consistent
behavior between all sorts of constructs with a data type, makes sure that
`vector_type_t` is only used for vector types.
It also allows to eventually differentiate between `real` and `shortreal`
at the elaboration stage. Currently this information is discarded by the
parser.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
When using non-ANSI style port declarations it is possible to declare the
port direction and the data type for the port in separate statements. E.g.
```
input x;
reg x;
```
When using packed array dimensions they must match for both declarations.
E.g.
```
input [3:0] x;
reg [3:0] x;
```
But this only applies for vector types, i.e. the packed dimension is
explicitly declared. It does not apply to the `integer` and `time` types,
which have an implicit packed dimension.
The current implementation requires that even for `integer` and `time`
types the implicit dimension needs to be explicitly declared in the port
direction. E.g. the following will result in a elaboration error
complaining about a packed dimension mismatch.
```
module test;
output x;
integer x;
endmodule
```
Currently the parser creates a vector_type_t for `time` and `integer`. This
means that e.g. `time` and `reg [63:0]` are indistinguishable during
elaboration, even though they require different behavior.
To fix let the atom2_type_t handle `integer` and `time`. Since it no longer
exclusively handles 2-state types, rename it to atom_type_t.
This also fixes a problem with the vlog95 target unit tests. The vlog95
target translates
```
module test(output integer x);
endmodule
```
to
```
module test(x);
output x;
integer x;
endmodule
```
which then fails when being elaborated again. There were some regression
tests that were failing because of this that will now pass.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
When using non-ANSI style port declarations it is possible to declare the
port direction and the data type for the port in separate statements. E.g.
```
input x;
reg x;
```
When using packed array dimensions they must match for both declarations.
E.g.
```
input [3:0] x;
reg [3:0] x;
```
But this only applies to vector types, i.e. the packed dimension is
explicitly declared. It does not apply to the atom2 types which have an
implicit packed dimension.
The current implementation requires that even for atom2 types the implicit
dimension needs to be explicitly declared in the port direction. E.g. the
following will result in a elaboration error complaining about a packed
dimension mismatch.
```
module test;
output x;
byte x;
endmodule
```
Currently atom2_type_t's are deconstructed into base type, range and
signdness in the parser. That data is then passed to the signal
elaboration, which will then construct a netvector_t from it. This makes it
impossible to e.g. differentiate between `bit signed [31:0]` and `int`
during elaboration.
Instead of breaking the data type apart pass it as the data_type_t of the
signal and use the elaborate_type() method in the signal elaboration to
generate the netvector_t.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The base type of an array type needs to be elaborated in the scope where
the array type is declared. At the moment it is elaborated in the scope
where the signal is declared, which can cause incorrect results.
E.g. in the example below the width of the array base type would be 4
instead of 8.
```
localparam A = 8;
typedef reg [A-1:0] T[1:0];
module test;
localparam A = 4;
T x;
endmodule
```
If an unpacked array type is specified use the scope of the array type as
the default scope for the base type. Note that the base type can still be a
typedef in a different scope than the array scope, but we need to start
searching for it in the array scope.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
When using non-ANSI style port declarations it is possible to have both a
port and net or variable declaration for the same signal. In this case the
range specification for the two declarations have to match.
In the current implementation if the range specifications do not match an
error is reported and no signal is created. This generates follow up errors
about the signal not being declared when it is used.
In some cases it even causes the application to crash. E.g. the task
elaboration expects the port signal to exist. If it does not it will crash.
To avoid this still create the signal, even when an error is detected. Use
the range specification of the net or variable in this case. Overall
elaboration will still fail due to the error, but the application will not
crash.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Currently only dynamic arrays and queues of atom2 and vector types are
supported. Add support for packed arrays. Since these three types are
essentially handled the same internally supporting this only requires to
allow to elaborate a packed array base type.
Factor out type elaboration into a helper function that can be shared
between the elaboration of the base type of a dynamic array or queue and
the type for other signals. This gives consistent behavior and will also
make it easier to support additional base types for dynamic arrays or
queues.
Note that it is not yet possible to index elements of packed array dynamic
arrays or queues. But neither is it possible to do a bit select for vector
or atom2 type dynamic arrays or queues yet. Supporting this needs some
additional work.
There is one test that declares a queue of a struct type, but doesn't use
it since it is not supported. With this change a error will generated when
trying to declare a queue of a struct. So update that test to not declare
the variable so it does not fail.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Each data_type_t has a elaborate_type() method that returns the
corresponding ivl_data_t for that type.
Make use of that in PWire::elaborate_sig(). This removes duplicated code
and ensures consistent behavior between the different places where types
are elaborated.
This will also make it easier to add new data type that are going to be
elaborated this way.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The logic that decides whether a vector is scalar or not incorrectly flags
all variables that are declared in packages as scalar. As a result it is
not possible to do a part select on a vector declared in a package.
Rather than having an independent scalar flag consider a vector as scalar
if it does not have any packed dimensions.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
While a variable can have any data type the data type for nets is quite
restricted.
The SystemVerilog LRM section 6.7.1 ("Net declarations with built-in net
types") requires that the data type of a wire is either a 4-state packed or
a unpacked struct or unpacked array of 4-state packed types.
As an extension to this iverilog allows real data type for wires as well as
2-state packed types.
Add a check that reports an error if a net with any other type is declared.
In addition in Verilog a net can not have an explicit data type at all. It
can only have a packed dimension and a signed flag. As an extension to this
Icarus also allows wires to be of `real` data type.
Note that in Verilog mode the data type is checked in the parser since only
the parser knows whether the data type is an implicit type (`input reg
[7:0]` and `input [7:0] x` elaborate the same). But for SystemVerilog the
type is checked during elaboration since due to forward typedefs and type
parameters the type is not necessarily known in the parser.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
For signals that are declared in a block string_type_t is already used to
pass the type information to the signal elaboration.
But for task ports it is passed as IVL_VT_STRING. Switch this over to also
passing the type information as a data_type_t.
This allows to remove the special handling for IVL_VT_STRING in the signal
elaboration.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Currently it is only possible to declare packed array variables with a
struct type as the element type.
Add support for packed arrays of other packed types. This includes packed
arrays of enums and vectors as well as packed arrays of packed arrays.
Since packed arrays of packed types are already supported for class members
the infrastructure for elaborating all types of packed arrays exists.
It just needs to be called when elaborating a signal.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are too many ad hoc handlers of symbol_search partial results.
Rewrite symbol_search to clean up things like partial results and
member/method detections. Use this reworked symbol_search function
to rewrite expression elaborate for the PECallFunction expressions.
In the rare cases where DARRAY signals are in the network, handle
them by creating the proper ivl_nexus_t node. This also implements
the receive of vvp_object_t objects bu vvp_fun_anyedge_sa. This
together makes it possible for IVL_VT_DQUEUE objects to be in
wait lists.
This fixes#412
This.new is not allowed.
super.new beyond the first statement is not allowed.
And while I'm at it, clean up the use of "@" and "#" in
the code as tokens for this and super.
The compiler elaborates types on the fly as they are used. For user-
defined types (typedefs) we must do the elaboration in the scope where
the type was declared, not in the scope where it is used.
When strings are arguments to functions/tasks, that doesn't suddenly
make them implicitly scalar. Strings are vectors and should be treated
that was, even if they are IMPLICIT_REG.