When multiple words in one array were connected to the same nexus as a
single word array, the code generator was sometimes failing to generate
all the necessary aliases. This was highly dependent on the elaboration
order.
This fix should be more robust, but there are currently no tests in the
test suite that cause the compiler to generate whole-array aliases, and
I can't think of a way to make it do so as we don't yet support unpacked
arrays in module ports, so that branch of the code is currently untested.
When loading a signal into into an index register currently the signedness
of the signal is used when deciding whether sign-extension should be
performed or not. But this can lead to incorrect results, instead the
signedness of the expression must be used as that might be different from
the signal. E.g.
```
reg signed [1:0] = 2'b10;
$display(1 << x[1:0]);
```
gives the wrong result without this.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Assignments with an undefined intra-assignment delay should be treated like
assignments with zero delay. For the most part this is implemented
correctly, except for assignments to a part of a vector where the offset
inside the vector is an immediate value. E.g.
```
reg [1:0] x;
integer d = 'x;
...
x[0] <= #d 1'b1
```
Here when loading the delay into the index register flag 4 is updated, but
never cleared afterwards. As a result, if the delay is undefined, the vector
assignment will be skipped. Fix this by making sure flag 4 is always
cleared before the vector assignment instruction.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
For sequences where the tgt-vvp backend generates `%pushi` followed by one
of the operations that have an immediate version replace it with the
immediate version. This is slightly more efficient.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog makes all of the initialisation, condition, and step
components of a for loop optional. We already support this for the
initialisation and step components.
This causes tgt-vvp to use a lower case 'c' instead of an upper case
'C' as the prefix for constant values used to initialise undriven nets.
For use by the following commit.
vvp does not track whether the values stored in a dynamic array or queue
are 2-state or 4-state. Internally the data is always stored as 4-state.
To make sure that the read value is actually 2-state do a cast for 2-state
reads.
E.g. performing an out-of-bounds access on a 2-state dynamic array or queue
will yield a undefined value.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog allows to use assignment patterns to assign values to an
array. E.g. `int a[4] = '{1, 2, 3, 4}`.
Each value is evaluated in the context of the element type of the array.
Nested assignment patterns are supported. E.g. `int a[2][2] = '{'{1, 2},
'{1, 2}};`
Add initial support for array assignment patterns for both continuous as
well as procedural assignments.
For continuous assignments the assignment pattern is synthesized into an
array of nets. Each pin is connected to one of the assignment pattern
values and then the whole net array is connected to target array.
For procedural assignments it is unrolled in the vvp backend. E.g
effectively turning `a = '{1, 2};` into `a[0] = 1; a[1] = 2;`.
Not yet supported are indexed initializers or `default`.
E.g. `int a[10] = '{1:10, default: 20};`
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are some cases where during an assignment the elaboration step can
generate constructs with the right-hand-side expression being wider than
the left-hand-side expression.
To handle this the tgt-vvp backend inserts a %pad operation when necessary.
One place where this is missing is when writing to an string element. Here
instead an assert is triggered requiring the right-hand-side expression to
be 8 bits wide.
E.g.
```
bit [7:0] x;
bit [8:0] y;
string s;
x = y; // tgt-vvp inserts %pad
s[0] = y; // tgt-vvp triggers assert
```
Long term this should be fixed at the elaboration stage and insert the
proper width cast. But for now solve this the same way as other places in
tgt-vvp and insert the %pad operation for string element assignments if the
width does not match.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Currently continue and break are supported in most loops. But not in
do-while loops. Add support for them in do-while loops as well.
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.
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>
The `netdarray_t` type implements the `packed_width()` method by returning
the packed width of the element type. It is the only non-packed type that
implements the method.
This triggers an assert in the vlog95 backend for tasks with dynamic array
typed parameters. And while the vlog95 backend does not support dynamic
array types it should not result in a crash, just an error message.
The only place that relies on the behavior that the packed width of the
element type is returned is in the vvp backend where variable declarations
are generated. Update that code to query the packed width of the element
type instead and then remove the `packed_width()` implementation for the
`netdarray_t` type.
This fixes the assert in the vlog95 backend. But it is also nicer from an
architectural perspective as this brings the type in line with the other
types in terms of behavior.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The C++ API for `ivl_type_t` has a method to query the total width of a
packed type. This is currently not exported to the C API and the tgt-vvp
backend implements similar functionality by querying the individual
dimensions of a type.
Export the `packed_width()` method to the C API. This allows to remove the
custom implementation from the tgt-vvp backend.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
When multiple events are used in a non-blocking event control they need to
be combined into a single event using `event/or`.
The generated `event/or` statement is missing the trailing semicolon and
newline, which results in parser error when vvp tries to run.
E.g.
```
event e, f;
integer x;
x <= @(e or f) 10;
```
Add the missing semicolon and newline to fix this.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
A non-blocking event controlled assignment consists of 3 steps.
* Setup event
* Perform assignment
* Clear event
This works fine if the lvalue is a singular value. If the lvalue is a
concatenation multiple assignments are generated and the event is cleared
after each assignment. As a result only the first assignment is event
controlled. All other assignments will be regular non-blocking assignments.
E.g.
```
reg x, y;
event e;
{x,y} <= @e 2'b11;
$display(x, y); // x will be 1'b1, y will be 1'bx
```
To resolve this the event needs to be cleared after all assignments have
been done. This requires changes to both tgt-vvp and the vvp runtime.
tgt-vvp is updated to only insert a single `%evctl/c` instruction for each
event controlled non-blocking assignment.
The vvp runtime is not updated to implicitly clear the event in the
`%assign/vec4/e` instruction and instead rely on the explicit `%evctl/c`.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
VVP array assignment operations expect the array element index to be in
index register 3.
For array element assignments with a dynamic part select the array index
gets moved into a temporary index register and has to be moved into
register 3 after evaluating the dynamic part select. This is currently not
done non-blocking event control assignments. This causes the write to go to
the wrong array element. It will go to whatever value is in the register 3
from previous operations.
```
reg [3:0] a[1:0];
integer i = 0;
event e;
a[1][i+:2] <= @e 2'b10; // Will write to the wrong array element
->e;
```
Make sure to move the temporary register to register 3.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The basic structure for supporting assignment operators on real arrays
exists in the tgt-vvp backend. But there are a few problems, most
importantly it generates the wrong instruction for loading data from the
real array.
The instruction it uses is `%load/reala`, but that instruction does not
exist, the correct name is `%load/ar`.
In addition to this there are a few minor problems.
* Out-of-bounds access on the array triggers an assert
* Missing `%pop/real` instruction when skipping a write due to
out-of-bounds access
Address these so assignment operators are supported on real array entries.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Access to members in packed struct fields is internally implemented using a
part select.
vvp has a special syntax for passing a part select of a vector to a system
function. This special syntax assumes that the part select is unsigned like
it is for normal Verilog part selects.
As a result passing a signed struct member to a system function will
interpret it as unsigned.
Add a check to make sure that the expression is actually unsigned. If it is
not fall back to evaluating the expression on the vector stack and pass the
value on the stack to the system function.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
For an out-of-bounds assignment operator on an array element an assert is
hit if the element width is great than 32.
Remove the assert and make sure that this case is handled correctly by
using the `%pad/s` instruction to extended the X value to the correct
width.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
If the index of an array access is known to be out-of-bounds during
elaboration it is replaced with 'x. In the tgt-vvp backend that is handling
compressed array assignments there is an assert() that triggers if the
index is an undefined immediate.
There is already an existing code path that is capable of handling
out-of-bounds access. Remove the assert and set the index to ULONG_MAX to
trigger taking the out-of-bound access path.
On this out-of-bounds path the write to the array is skipped. But this
leaves the result on the vector stack. Insert the `%pop/vec4` instruction
to make sure it is removed.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The operator assignment on dynamic part selects uses the `%store/vec4`
instruction to store the value. This instruction will skip the assignment
if flag 4 is set. This is for handling the case where the index is
undefined.
Since the left hand side of the assignment is an arbitrary expression it
can change the flag. The implementation handles this by making a copy of
the flag and restoring it before executing the `%store/vec4` instruction.
The flag is set by the `%ix/vec4` instruction when loading the index
register. But the copy of the flag is made before that and just picks
up the flag that was stored by previous expressions. This can cause
the store to be skipped when it shouldn't.
E.g. in the following code the increment will be skipped. Flag 4 is used
from the `a == 0` comparison, rather than from computing the part select
index.
```
int a = 0;
if (a == 0) begin
a[a+:2] += 1;
end
$display(a); // Will print 0, should print 1
```
Fix this by moving the copy of the flag after the `%ix/vec4` instruction.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The vvp `%load/vec4a` instruction will skip the load if vvp flag 4 is set
and return 'x. This is meant for handling the case where the index is
undefined.
For assignment operators on array entries, when the index is an immediate
value, vvp flag 4 is not cleared before the load instruction. If a previous
instruction set flag 4 it load yield 'x.
E.g. for the following sequence `x[0]` should be `11`, but will be `'x`.
```
integer x[10];
logic a = 1'b0;
x[0] = 10;
if (a == 0) begin
x[0] += 1;
end
```
Properly clear the flag before the load instruction to handle this
correctly.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
When assigning a value to a real typed array entry the vvp `%store/reala`
instruction. This instruction will skip the store if the vvp flag 4 is set.
This is to handle the case where the index is undefined.
When the index into the array is an immediate value the flag 4 is not
cleared. If a previous instruction set flag 4 the store will be skipped.
E.g. for the following r[0] will remain 0.0 since the assignment to it is
skipped.
```
integer a = 0;
real r[1:0];
if (a == 0) begin
r[0] = 1.23;
end
```
Fix this by using the `draw_eval_expr_into_integer()` helper function to
evaluate the index into a word register. The function correctly handles all
the special cases.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Ever since the conversion to use a stack for vectors `draw_eval_bool64()`
has been unused. The last caller of `draw_eval_bool64()` was removed in
commit e4b862f3d1 ("Clean up vector handling dead code.").
Remove `draw_eval_bool64()` and related functions as well.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There is currently a restriction in the vvp code generator backend that
throws an assertion when generating wire access for a multi-dimensional
real array.
But there is nothing special about multi-dimensional arrays. In vvp arrays
are in canonical form. Meaning they only have a single dimension and the
conversion form multi to single dimension is done in the higher layers.
Remove the assert to allow multi-dimensional real arrays.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The `%store/dar/...` and `%store/qdar/...` instructions are used to load a
value into an entry in dynamic array or queue. These instructions will skip
the load if VVP flag 4 is 1.
For assignment pattern initialization these instructions are used to load
the value of the individual assignment pattern expressions into the dynamic
array.
For queues flag 4 is never cleared when generating the code for the
assignment pattern. This means the initialization might be skipped
depending on what value the flag had before.
```
int a = 1;
int q[$];
a = a == 1;
q = {1, 2, 3, 4};
```
For dynamic arrays it is cleared once in the beginning. But each item in
the assignment pattern can be an arbitrary expression. Evaluating the
expression can cause the flag to get overwritten. E.g. the following code
will skip the assignments.
```
int a = 1;
int d[];
d = {a ? 1 : 1, 2, 3, 4};
```
To fix these issues make sure that the flag is cleared after evaluating
each initialization expression and before executing the `%store/...`
instruction.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
A `return` statement in a function gets translated into a vvp `%disable`
instruction. This works fine as long as no recursion is involved. The
`%disable` instruction will stop execution of all active threads of a
particular scope. For recursive functions this means as soon as the inner
most function returns all containing outer function calls get disabled as
well. This results in incorrect behavior.
To make recursive functions using the `return` statement work use the new
vvp `%disable/parent` instruction. This instruction will only disable the
closest thread in the thread hierarchy that matches the target scope.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>