This is legal if the procedural and continuous assignments target
different words.
NOTE: This is not fully compliant with the standard, because vvp
does not know that the nets were originally declared as variables,
so initialises to 'bz instead of 'bx and does not handle release
correctly.
Internally we convert SystemVerilog variables that have a continuous
assignment into unresolved wires. But from a user's perspective they
are still variables, so we should refer to them as such in error
messages. This new flag lets us distinguish between such variables
and nets that were declared as uwires.
The condition expression may require the loop variable width to be
expanded. The compiler wraps the NetESignal with a NetESelect to
do this, so we need to handle that when checking that the condition
expression uses the loop variable.
Fixes issue #687 and issue #1004.
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>
The `netrange_width()` helper function computes the total width of a set of
ranges. There are a few places where this is currently open-coded and
`netrange_width()` can be used. This removes a bit of duplicated code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are a few places in the code where a std::list is copied to a
std::vector by iterating through the list and copying each element over to
the vector. The std::vector type has a iterator based constructor that can
do the same.
Update the code to use it instead. This removes a bit of boilerplate code
and also makes it easier to update the code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
`$bits()` for array types is supposed to return the full size of the array
in bits. This currently works for data types that are passed to `$bits()`,
but not for array typed identifiers.
E.g.
```
typedef int T[1:0];
T x;
$display($bits(T)); // -> 64
$display(x); // -> 32
```
Since the `$bits()` implementation uses the expr_width of an expression
include the size of the unpacked dimensions in that for array identifiers
and array slices. Strictly speaking an array identifier does not have an
expression width, but this would be its expression with if it were for
example bitstream cast to a vector.
Special care needs to be take to not trying to pad array identifier
expressions.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Types for array signals are currently handled as a special case. The type
that is associated with the signal is not the array type itself but rather
the element type.
There is a fair amount of existing code that depends on this behavior so it
is not trivial to change this.
But there are certain constructs such as assignment patterns or array
concatenation where the array type itself is required.
Add a new `NetNet::array_type()` method that will return the array type if
the signal is an array. This will allow to query the array type when
needed.
`NetAssign_::net_type()` is updated to use this new method to return the
array type if the assigned signal is an array.
Long term the special handling of arrays for signals should be removed.
This will for example allow to unify the handling of arrays for signals,
class properties and struct members.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The are many binary operations where if the two operands are 2-state the
result is guaranteed to be 2-state.
This is true for all arithmetic operation with the exception of division
where division by 0 will always result in 'x even if the inputs are both
2-state.
The same is true for all binary bitwise operators as well as the binary
logical operators.
Having the expression type be 2-state avoids some unnecessary %cast2
instructions that would otherwise get inserted when assigning the result to
a 2-state variable.
E.g without this change the following will result in
```
int a, b, c;
b = a + b;
```
will result in
```
%load/vec4 ...;
%load/vec4 ...;
%add;
%cast2;
%store/vec4 ...;
```
For binary comparison operators this is already handled.
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>
There are a few NetExpr subclasses where the data type of the expression
is known, but it not attached to the NetExpr and only kept as a private
member in the subclass.
Attaching the type directly to the NetExpr allows to query it externally
and implement better type checking.
It also allows to remove a bit of duplicated code in the subclasses and
rely on the default implementation in the NetExpr base class.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The signedness of an expression can change depending on its context. E.g.
for an arithmetic operation with one unsigned operand all operands are
treated as unsigned.
For methods, both on built-in SystemVerilog types as well as user defined
classes, this is currently not considered. This can lead to incorrect behavior
if the value is sign extended.
E.g. the following will print 4294967295 rather than 65535.
```
shortint q[$];
q.push_back(-1);
$display(q.pop_front() + 32'h0);
```
Furthermore the return value is not expanded to the width of its context.
This can cause vvp to crash with an exception when it expects a vector on
the stack to have a certain width.
E.g.
```
int d[];
longint x;
bit a = 1'b1;
x = a ? d.size() : 64'h0;
```
Solve both of this by using `pad_to_width()` on the method return value if
it is a vectorable type. Since any function call, not just methods, needs
to have this done to its return value insert this on the common path.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are a couple of different NetNet constructors for different data
types. They are all very similar, consolidate them into a single
constructor taking a ivl_type_t.
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>
Currently only the netvector_t type implements the get_scalar() method. To
check whether a type is scalar it is first cast to netvector_t and then the
method is called.
But there are other types, such as areal that can also be scalar. To
support indicating that a real type is scalar add a virtual get_scalar()
method to ivl_type_s, which is the base class for all types.
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>
Void functions can be used in always_comb, but since the compiler
uses the check_synth() method to generate some warnings, make sure
that function is implemented for functions as well as tasks.
NetNet::get_isint() is never used anywhere, remove it. The information
whether a signal is an integer is always directly queried from the signal
data type.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Parameter expressions need to remember the scope they have been declared in
so that the code generator backends can insert the right parameter
reference, rather than a constant value.
Currently the scope is stored as a non-const reference. But that is not
needed. Mark the scope reference as const so NetEConstParam and
NetECRealParam can be created when only a const scope reference is
available.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
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
If the condition expression is 2-state, the result won't be blended, so
the result will be a valid enum value if both true and false expressions
return the same enum type.
Synthesis could only handle relatively simple conditional constructs.
This rework aims to make it handle anything the user can throw at it
(or output a sensible message as to why it can't).