Refactor the enum rule by adding a enum_base_type rule which handles the
type specific initialization. This allows to keep the non-type specific
parts in a common rule, which makes it easier to modify in future changes.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The test checks that all invalid declarations produce an error and also do
not crash the application.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
When something goes wrong when parsing a struct member, e.g. the type does
not exist, a nullptr is added to the struct member list. This will cause a
crash when iterating over the list.
E.g.
```
struct packed {
logc x;
} s;
```
Add a check so that nullptr members are not added to the list.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
By adding ivtest to the iverilog source tree, it is easier to keep
the regression test synchronized with the source that is being tested.
This should be especially helpful for PRs that add a new feature, and
have a matching ivtest PR with the regression test for that feature.
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>
When creating a signal of a packed array it is checked that the base-type
of the packed array is a packed type.
But this check is only done if the packed array itself is the type of the
signal. Placing the packed array in a struct or class will elaborate fine,
but then crash during simulation.
E.g.
```
typedef real myreal;
struct packed {
myreal [1:0] p;
} s;
```
Move the check from signal creation to type elaboration, so that it is not
possible to create a packed type with a non-packed base-type.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Use netrange_t, which is meant for storing ranges, for storing the range of
a netenum_t.
This makes the integration with the rest of the codebase a bit more
seamlessly and also allow to reuse methods defined for netrange_t such as
the width() method rather than having to reimplement it.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Specifying and enum with an invalid dimension range results in an assert or
segfault. E.g. `enum [$] E { ... }`.
Use the `evaluate_ranges()` function to elaborate the enum dimensions. This
functions has proper error checking and recovery built-in.
In addition verify that there is at most one packed dimension.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Having an enum with a lsb > msb base type, e.g. `enum logic [0:9]` is a
legal construct.
It is handled correctly for the most part already, there is just an assert
that triggers on it. Remove that assert.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
To determine the base type structs and packed arrays call the
figure_packed_base_type() for their sub-types.
This method is not defined for enum or atom2 types and the default
implementation returns IVL_VT_NO_TYPE.
As a result packed arrays of enum or atom2 types and packed structs with
members of enum or atom2 types get elaborated with IVL_VT_NO_TYPE
as the base type.
For example
```
struct packed {
bit signed [31:0] x;
} s1;
```
gets elaborated with a base type of IVL_VT_BOOL, while
```
struct packed {
int x;
} s2;
```
gets elaborated with a base type of IVL_VT_NONE.
To fix this define the figure_packed_base_type() for enum_type_t and
atom2_type_t.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Trying to elaborate a type with invalid packed dimensions currently results
in a crash. E.g. `typedef logic [] T;`
The issue is in the `elaborate_array_ranges()` function which
does not verify that the range specification is valid.
Replace the `elaborate_array_ranges()` with `evaluate_ranges()`, which does
the same thing, but properly checks the range specification and
reports an error if it is invalid.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
If the left-hand side of a logical operator is a constant that causes the
right-hand side to be short-circuited the right-hand side can be discarded
even if it is not constant.
In this case replace the expression by a constant.
E.g.
* `0 && expr` will be replaced by a constant 0.
* `1 || expr` will be replaced by a constant 1.
* `0 -> expr` will be replaced by a constant 1.
Note that it is not possible to replace the expression by a constant if
only the right-hand side is a constant, even when the value of the
expression is constant. The left side still has to be evaluated for side
effects.
E.g. it is known at elaboration that `a++ && 0` will yield 0, but the
increment on `a` has to be executed regardless.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Section 11.4.7 of the SystemVerilog LRM states
```
The && and || operators shall use short circuit evaluation as follows:
- The first operand expression shall always be evaluated.
- For &&, if the first operand value is logically false then the second operand shall not be evaluated.
- For ||, if the first operand value is logically true then the second operand shall not be evaluated.
```
vvp currently evaluates both operands of a logical operator. This works
fine as long as the right-hand side does not have a side effect. But if it
has the result might be incorrect.
E.g. for `a && b++` `b` must not be incremented if `a` evaluates to false.
The Verilog LRM mentions that it is allowed to short circuit any expression
"if the final result of an expression can be determined early". But there
is no requirement to do so.
So the new and the old behavior are both correct implementations in
Verilog.
Use the new behavior in both Verilog and SystemVerilog mode to make sure
the behavior is consistent when an expression has side effects.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The code for generating the logical `and` and `or` operators is identical
except for the final opcode to combine the two results.
Consolidate this into a single function to reduce the code a bit.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
If this assert fires, the "this" pointer we pass to it will be a
null pointer, so will cause a null pointer dereference. We've
tested it is not null earlier, so we don't need the assertion.
When using the latest mingw64 header files, rpcndr.h (which is indiretly
included by windows.h) defines a type named "byte" which collides with a
definition in cpp_type_traits.h (included indirectly by the STL). This is
only a problem if "using namespace std" is declared prior to including
windows.h.
For non-ANSI port declarations that have both a port declaration and
a corresponding variable declaration, the signed attribute may be
attached to either the port declaration or the variable declaration,
or both declarations.
This also ensures the same genvar cannot be used in two nested loops
(issue #533), because the implicit localparam with the same name
shadows the genvar declaration.
This requires us to make a copy of the typedefs map when adding it to
a NetScope object, because the pform data is deleted before we are
finished with it.