Add regression tests for the following types partial writes for both
2-state and 4-state vectors.
* Non-blocking
* Blocking
* Blocking event control
Check that all in-bounds partial writes, partial out-of-bounds and
full out-of-bounds all works as expected.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Writes to 2-state arrays currently only support full writes. If the write
is a partial write it will trigger an assert. E.g.
```
int a[3:0]
int i = -1;
a[i+:8] = 8'h0; // Triggers assert
```
Add support for partial writes by doing a read-modify-write in the same way
as for 4-state arrays.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The `%store/vec4a` instruction does not handle partial of full
out-of-bounds writes to a vector array element. Trying to do so will
trigger an assert. E.g.
```
integer a[3:0];
integer i = -10;
a[0][i+:8] = 8'h0; // Triggers assert
```
For fully out-of-bounds writes the write should be skipped, for partial
out-of-bounds writes the value needs to be resized to be within the bounds
of the vector. Use the `resize_rval_vec()` helper function to implement
this.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are a few functions that handle implement different kinds of vector
writes that have to handle that the assigned value partially or completely
out-of-bounds.
Each function has similar, but not identical, code for this, sometimes with
small bugs for corner cases.
Add a helper function that takes care of handling of updating the width and
offset of the assigned value if necessary.
This ensure consistent and correct behavior and allow to remove some
duplicated code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The `%assign/vec4/a/{d,e}` instructions, when checking for a full
out-of-bounds write on the low side, uses the target signal width, while it
should use the assigned value width.
This can lead to a fully out-of-bounds write to be assumed to be a partial
out-of-bounds access, which will trigger an assert later on.
E.g.
```
integer a[1:0];
integer i = -4;
a[0][i+:4] <= 4'h0; // Triggers assert
```
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Check that multiple events can be used in a non-blocking event control
assignment. The assignment should happen if either of the events trigger.
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>
Check that a non-blocking event control assignment works as expected to a
lvalue concatenation. All values that are part of the concatenation should
only be assigned after the event triggers.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Check that non-blocking event control assignments works on an array part
select if the part select index is not an immediate value.
This is a copy of the nb_ec_array_pv test, but using variable indices
instead of immediate values.
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>
Both the `%concati/vec4` and `%pushi/vec4` instructions need to construct a
vector from the immediate value encoded in the instruction. Currently both
these instructions have a custom implementation for that.
Remove the custom implementations from those functions and use the
`get_immediate_rval()` helper function. This removes a bit of duplicated
code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The `%cmp/ws` and `%cmp/wu` instructions compare two index registers. They
are currently unused. Since the index registers are not used for data there
is not really a need to compare them. Values can be compared before loading
them into an index register.
So remove these two instructions.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The `%mov/wu` instruction moves data from one index register to another.
The instruction is not used. It also does the same as `%ix/mov`. So remove
it.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The `recv_vec{4,8}_pv()` functions are used to implement a partial write to
a vector. As parameters they take both the value and the width of the
value.
All callers of of these functions pass `val.size()` or a variation thereof
as the width of the value. And all implementations that do anything with
the data have an assert that `val.size() == wid`.
Remove the `wid` parameter from these functions and just use `val.size()`
directly where needed. This allows to simplify the interface and also
to remove the asserts.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Check that assignment operators on real array entries are supported.
Also check that
* out-of-bounds indices work as expected
* it works after a comparison that set vvp flag 4 to 0
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>
Check that the signedness of a struct member is properly handled when being
passed to a system function.
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>
Check that an assignment operator on an out-of-bounds array element works
as expected. The out-of-bounds access should leave the array unmodified,
but the right-hand side must be evaluated regardless.
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>
Parameters with real values are possible in Verilog, but not in the VCD
format, so lie a little and call them "real" objects. Otherwise, we can
treat them like constants and it works out, at least for gtkwave.
Check that for the following operations the load or store is not skipped
after a operation that sets vvp flag 4.
* Assignment to immediate indexed real array entry
* Assignment operator on immediate indexed vector array entry
* Assignment operator on dynamic vector part select
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>
Writing parameters into VCD files makes the values available to waveform
tools. This can be done easily enough by writing out a $dumpadd section
at the beginning of the file that sets the parameter values. We don't need
to track the values over change, because by definition they do not change.
This changes the typical vcd output as well, so a few of the regression tests
need to be adjusted to account for this.
Also, while tracking this down, found and fixed the vvp/README.txt documention
for the .param/x records.
Check that out-of-bounds access on a dynamic array or queue works and
returns the correct value.
* 2-state vectors: '0 with the element width
* 4-state vectors: 'x with the element width
* reals: 0.0
* strings: ""
Note that the 2-state test currently still fails as out-of-bounds access on
a 2-state vector incorrectly returns 'x.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Commit e1870acfac ("Return the correct value when a queue or darray
references an undefined element") added support for specifying the width of
the vector elements stored in a queue or dynamic array, so that an
out-of-bounds access can create a word with the right width.
To get the element width of a queue or dynamic array it uses the
`packed_width()` method. But this method is only implemented for
`netqueue_t` and returns always 1 for dynamic arrays. As a result
out-of-bounds access on a dynamic array will push a vector of the wrong
width onto the stack if the vector element is wider than 1 bit. This will
usually trigger an assert in vvp.
Fix this by moving the `packed_width()` method implementation from
`netqueue_t` to its base class `netdarray_t` so that it works for all types
of dynamic arrays.
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>
The get_signed() method for the netqueue_t class is identical to that of
its base class netdarray_t.
Remove the redundant re-implementation of the method.
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>