The current `PEIdent::test_width()` method is only able to calculate width
of a path with up to two elements.
For more complex paths it will not be able to calculate the width. E.g.
* Nested struct member access
* function call of a enum member in a struct
To make nested structures work properly walk the whole path tail element
by element updating the type along the way. Also take the indices into
account and update the type if an arrays dimensions have been fully
consumed.
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>
In addition to providing positional arguments for task and functions
SystemVerilog allows to bind arguments by name. This is similar to how
module ports can be bound by name.
```
task t(int a, int b); ... endtask
...
t(.b(1), .a(2));
```
Extend the parser and elaboration stage to be able to handle this. During
elaboration the named argument list is transformed into a purely positional
list so that later stages like synthesis do not have to care about the
names.
For system functions and tasks all arguments must be unnamed, otherwise an
error will be reported.
In addition to functions and tasks arguments can also be bound by name for
the various different ways of invoking a class constructor.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog allows to declare const variables. These variables are
read-only and can not be assigned a value after their declaration. It is
only possible to assign an initial value as an initializer.
E.g.
```
const int x = 10;
x = 20; // Error
```
The LRM requires that for variable declarations with static storage the
initializer is a constant expression with the extension that other const
variables are also allowed. const variables with automatic storage can
be initialized by any expression.
Checking if an expression contains only const variables requires a bit more
work to implement. So for now be more lenient that what the standard
requires and allow arbitrary expressions to initialize const variables even
for those with static storage.
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>
Structs can be initialized by an assignment pattern. E.g.
```
struct packed {
int x;
shortint y;
} S = '{ 1, 2};
```
is the same as
```
struct packed {
int x;
shortint y;
} S;
s.x = 1;
s.y = 2;
```
Add initial support for unnamed struct assignment patterns. Named struct
assignment patterns like
```
struct packed {
int x;
shortint y;
} S = '{x: 1, y: 2};
```
are still unsupported.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog allows to use assignment patterns to assign a value to a
packed array.
This is similar to using a concatenation, with the difference that for
concatenations the values are evaluated in a self-determined context and
for assignment patterns they are evaluated in a context defined by the
element type of the packed array. This means that the value is for example
automatically width expanded or truncated if it does not have the same size
as the element type. Automatic type conversion is also done when allowed. E.g.
```
bit [3:0][3:0] x = '{1'b1, 32'h2, 3.0, "TEST"};
$display("%x", x); // -> 1234
```
Nested assignment patterns are also supported. E.g.
```
bit [1:0][3:0][3:0] x = '{'{1, 2, 3, 4.0}, '{5, 6, 7, 8}};
$display("%x", x); // -> 12345678
```
Add support for using assignment patterns as the right hand side value.
Since the complete type of the target variable is required to correctly
evaluate the assignment pattern it is handled as a special case in
`elab_rval_expression()`. For other types of expressions for packed values
only the total width of the target value is provided to the rvalue
elaboration function.
SystemVerilog also supports assignment patterns for the left hand side in
assignments. This is not yet supported.
Also not yet supported is specifying array elements by index, including
`default`.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
In SystemVerilog identifiers can usually have an additional package scope
in which they should be resolved. At the moment there are many places in
the code base that handle the resolution of the package scope.
Add a common data type for package scoped paths as well as a
symbol_search() variant that works on package scoped identifiers. This
allows to handle package scope resolution in a central place.
Having the code in a central place makes it easier to ensure consistent and
correct behavior. E.g. there are currently some corner case bugs that are
common to all implementations. With the common implementation it only has
to be fixed in one place.
It will also make it easier to eventually implement class scoped
identifiers.
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>
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>
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>
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>
SystemVerilog allows to use the `super` keyword to access properties and
methods of a base class. This is useful if there is for example an
identifier with the same name in the current class as in the base class and
the code wants to access the base class identifier.
To support this a bit of refactoring is required. Currently properties are
internally referenced by name, this does not work if there are multiple
properties of the same. Instead reference properties always by index.
In addition when looking up an identifier that resolves to an object return
both the type and the object itself. This is necessary since both `this`
and `super` resolve to the same object, but each with a different type.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
By default when creating a new class object the type of the object is
determined by the type of the target.
SystemVerilog also allows to explicitly specify the type of the object to
be created. The specified class still needs to be assignment compatible
with the target. This is e.g. useful to construct an object of a derived
class of the target. E.g.
```
class B; ... endclass
class C extends B; ... endclass
B b;
b = C::new;
```
Add support for this to the parser as well as handling it during
elaboration.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
If the base expression of a cast expression has an automatic term then the
cast expression itself has an automatic term.
Make sure this is implemented so that an error is properly reported when
using such an expression in a context where automatic variables are not
allowed.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The last user of the `PExpr::is_the_same()` method was removed in commit
37b60a4c52 ("Clean up interface of the PWire class").
Remove the method.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog supports sign cast where it is possible to change the
signedness of an expression. Syntactical it is similar to width or type
casting, except that the keywords 'signed' or 'unsigned' are used in front
of the cast operator. E.g.
```
logic [3:0] a = 4'b1000;
logic [7:0] b = signed'(a); // b is 8'b11111000;
logic signed [3:0] c = 4'b1000;
logic signed [7:0] d = unsigned'(c); // d is 8'b00001000;
```
As noted by the LRM section 6.24.1 ("Cast operator") applying a sign cast
to an expression is equivalent to calling the $signed() and $unsigned()
system functions on the expression.
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.
This is currently not considered when accessing class properties. This can
lead to incorrect behavior with regards to sign extension.
E.g. the following will print 4294967295 rather than 65535.
```
class C;
shortint x = -1;
endclass
...
C c = new;
$display(c.x + 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.
```
class C;
shortint x = -1;
endclass
...
C c = new;
int x;
bit a = 1'b1;
x = a ? c.x : 64'h0;
```
Solve both of this by using `pad_to_width()` on the property expression if
it is a vectorable type. Since any identifier, not just class properties,
need to have this done insert this on the common path and remove the
`pad_to_width()` call on individual paths.
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>
It is allowed to access a constant declared in a class scope, such as a
enum value or parameter, on an object of that class type. This is described
in section 8.5 ("Object properties and object parameter data") of the LRM
(1800-2017).
E.g.
```
class C
enum { A } e;
endclass
C c = new;
c.e = c.A;
```
Support this by in addition of searching for class properties on the object
also search for constants in the class scope.
A bit of refactoring is needed around the parameter elaboration functions
since they expect a non-const NetScope, but for classes we only have a
const scope available.
The non-const scope is needed to be able to mark specparams as
non-annotatable. Since classes can't have specparams this part is factored
out into a separate function the NetScope parameter for the shared
functions is made const.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Currently explicit cast is supported to atom2 and vector types. packed
struct, packed array and enum are not supported.
An explicit cast to packed type works the same for all packed types though.
Add support for handling also packed structs, packed arrays and enums by
make the code more generic and querying the packed base type from the
ivl_type_t.
To correctly handle enums a bit more work is necessary, which will be done
in a follow up patch.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are too many ad hoc handlers of symbol_search partial results.
Rewrite symbol_search to clean up things like partial results and
member/method detections. Use this reworked symbol_search function
to rewrite expression elaborate for the PECallFunction expressions.
Parameters can have string type and do the usual string stuff,
and also implement some of the string methods on string parameters
so that they evaluate down to constants.
Use the common data_type_or_implicit rules to support type
definitions for parameters. This eliminates a bunch of special
rules in parse.y, and opens the door for parameters having
more complex types.
We don't support evaluating user-defined system functions at compile
time. If possible, defer evaluation until run time. If used in a
constant expression, output a "sorry" message.