The NetAssign_:net_type() function return the type of lvalue expression.
But it only does so for a limited amount of cases.
Refactor the function so that it works for the general case and always
returns the data type, if the data type of the lvalue expression is known.
This will allow to implement better type checking and other constructs such
as pattern assignments that require to know the type of the lvalue.
It also allows to remove some duplicated code in other methods of
NetAssign_ that want to lookup the type.
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 current NetExpr::enumeration() always returns a nullptr.
The NetExpr class has a ivl_type_t member that represents
the type of the expression.
Provide a default implementation of NetExpr::enumeration() that
casts this type to the netenum_t type. This will allow
to share this implementation between subclasses and remove
a bit of duplicated code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Check that class properties can be shadowed by local symbols in class
methods and also check that a package scoped identifier with the same name
as class property can be accessed in a class method.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are currently two mechanisms for handling class properties. One that
is used when a class property is accessed through an object and other when
a class property is used freestanding in a class method.
Both are very similar, but there are some small differences. E.g. one
supports arrays, the other supports nested properties.
```
class B;
int x;
endclass
class C;
B b;
B ba[2];
task t;
ba[0] = new; // Does work
this.ba[0] = new; // Does not work
b.x = 10; // Does not work
this.b.x = 10; // Does work
endtask
```
There is another problem where free standing properties take precedence
over local variables. E.g.
```
class C;
int x = 1;
task t();
int x = 2;
$display(x); // Should print 2, will print 1
endtask
endclass
```
The class property elaboration also ignores the package scope of the
identifier resulting in access to a class property being elaborated if
there is a property of the same name as the scoped identifier. E.g.
```
package P;
int x = 2;
endpackage
class C;
int x = 1;
task t;
$display(P::x); // Should print 2, will print 1
endtask
endclass
```
Consolidate the two implementation to use the same code path. This is
mainly done by letting the symbol search return a result for free standing
properties as if the property had been specified on the `this` object. I.e.
`prop` and `this.prop` will return the same result from the symbol search.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are no users of the old symbol_search that need the cls_val result.
Remove it as a output parameter of the function.
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.
Check that it is possible to call a method on a package scoped identifier.
Both for built-in types as well as class objects.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Currently package scoped function calls are supported. Update the parser
and elaboration to also allow method calls on packaged scoped variables.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Check that an error is reported when trying to access an imported
identifier through a hierarchical name.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Imported identifiers should only be visible in the scope they have been
imported too. They should not be accessible through hierarchical names into
that scope. This is defined in section 26.3 ("Referencing data in
packages") of the LRM (1800-2017).
Modify the symbol search to not look at imports if the name is part of a
hierarchical path.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The github CI VM has multiple CPUs. 2 for Linux and Windows, 3 for macOS.
Make use of parallel build to speed up the CI tests a bit.
For Windows the `makepkg-mingw` command already schedules a parallel
build, so no changes are made to the Windows build.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Check that null-bytes get removed when reading a value through the VPI API
as a vpiStringVal. Also check that null-bytes are not removed from string
literals when string literals are read through the VPI API as a non
vpiStringVal.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The vlog95 backend currently strips null-bytes from strings in structural
elements. E.g. `assign y = "a\000b"` gets translated to `assign y = "ab"`.
This changes the behavior of the generated output compared to the input.
Don't ignore the null-bytes to make sure the behavior stays the same.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
When a string literal is used in a context where it needs to be wider than
it is it will get left-padded with null-bytes. When the vlog95 backend
emits the string literal it will strip the leading null-bytes as it results
in much more legible code.
Unfortunately there are some corner cases where this results in a change of
behavior of the generated code compared to the original. E.g. if the
context that caused the width expansion has been removed by optimization.
`$display(0 ? "Yes" : "No")` should print " No" due to width expansion, but
when running through the vlog95 backend it will print "No".
Another scenario where there is a change in behavior is when a null byte
was explicitly added at the front of a string literal. E.g. $bits("\000ab")
should print 24, but will print 16 when running through the vlog95 backend.
To mitigate this remove the stripping of the leading null-bytes from the
vlog95 backend. This results in slightly less legible code being generated
in some cases, but makes sure that the code is always correct.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Currently when reading a number literal through the VPI API as a
vpiStringVal all null-bytes in the literal get ignored. This behavior is
different from when reading a signal through the VPI API as a vpiStringVal.
The latter will only ignore leading null-bytes and replace other null-bytes
with a space. E.g. the following two will print different values.
```
$display("%s", "a\000b"); // -> " ab"
reg [23:0] x = "a\000b";
$display("%s", x); // -> "a b"
```
For consistency modify the number literal formatting code so that it has
the same behavior as the signal value formatting code and only replaces
leading null-bytes.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The VPI API for string literals does not correctly handle the case where a
null-byte ('\0') appears in the string literal. It uses strlen() to
calculate the length of the literal, which will give the wrong result if
there is a null-byte in the string literal. Instead of using strlen() use
the stored length to fix this.
In addition when formatting a string literal as a string ignore any
null-bytes. The LRM is not entirely clear what should happen to null-bytes
when formatting a value as a string. But the behavior of ignoring the
null-bytes is consistent with the rules of SystemVerilog for converting a
string literal to a SV string.
This problem can occur when a string literal gets null-byte left-padded due
to width of its context of its expression, but then optimization removes
part of the expression and only leaves the padded string literal.
E.g.
```
$display(0 ? "Yes" : "No");
```
will be transformed into
```
$display("\000No");
```
There is also one subtle change in behavior associated with this. The empty
string ("") is supposed to be equivalent to 8'h00. So e.g.
`$display(":%s:", "")` should print ": :" since the width of the empty
string is 1 byte and the %s modifier prints a string with the width of the
value, left-padding with spaces if necessary. The current implementation
will print "::" though. This change requires to update the marco_with_args
gold file.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Check that when assigning or casting a string literal or vector to a SV
string type that null-bytes are removed.
Also check that writing a null-byte to an element of a string variable is
ignored.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The `%pushi/str` and `%concati/str` instructions should remove null-bytes
from the string literal when converting it to a string. This is defined in
section 6.16 ("String data type") of the LRM (1800-2017).
This is already handled correctly when converting a vector from the stack
to a SV string, just not when converting a string literal to SV string.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Make sure the package scope is considered when elaborating identifiers for
continuous unpacked array assignments.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Classes are allowed to access properties of the base class. This also
includes static properties. Currently when looking up a static property
only those of the class itself are considered. Extend this to also consider
properties of the base classes.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Check that an error is reported, rather than crashing, when trying to do a
package scoped function call when the function does not exist in the
package or is not a function.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Currently a package scoped function call will result in an assert if the
function does not exist in the package scope.
For non-package scoped function calls instead a proper error is reported.
Refactor the code to share the same code paths between package scoped and
non-package scoped function calls. This makes sure that errors are reported
in both cases. It also makes the code slightly smaller.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Check that indices to package scoped identifiers are evaluated in the scope
where the identifier is accessed and not in the scope where the identifier
is declared.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are a few cases where a member select on a package scoped identifier
is evaluated in the scope of the package rather than the scope where the
identifier is referenced.
This leads to incorrect behavior if a local symbol is used as an index in a
part select of the referenced member select. E.g.
```
package P;
localparam N = 1;
struct packed {
logic [3:0] x;
} s = 4'b0101;
endpackage
module test;
localparam N = 2;
initial $display(P::s.x[N]); // Will print 0, should print 1
endmodule
```
Use the scope where the member select is used, rather than the scope where
the identifier is defined, to fix this.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>