The current class end label parser rule gets the data type from the
TYPE_IDENTIFIER, casts that to a class_type_t and gets the name from that.
This code was written when the TYPE_IDENTIFIER only provided the data type.
But these days it provides both the data type and the name. Simplify the
code to get the name directly from the TYPE_IDENTIIFER.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
In order to avoid conflicts in the grammar the lexer distinguishes between
identifiers and type identifiers. To correctly classify an identifier the
lexer needs to know in which scope a token is parsed. E.g. when the parser
encounters a package scope operator it calls `lex_in_package_scope()` to tell
the lexer which scope the following identifier should be classified in.
Currently the `lex_in_package_scope()` is only used when a type identifiers
is parsed, but on for normal identifiers. As a result it is not possible to
reference variables or function from a package scope that have the same
name as a type identifier in the current scope. E.g.
```
package P;
int T;
endpackage
module test;
typedef int T;
initial $display(P::T);
endmodule
```
Another problem is that in expressions both type identifiers and signal
identifiers can be referenced. As a result there are two rules in an
expression that can be reduced
* <PACKAGE_TYPE> :: <TYPE_IDENTIFIER>
* <PACKAGE_TYPE> :: <IDENTIFIER>
The way the rules are formulated at the moment the parser has to use token
lookahead to decide which rule to follow before it can reduce the package
scope operator. As a result the lexer detects the token before
lex_in_package_scope() is called and the identifier does not get evaluated
in the package scope, but in the current scope. Which can cause the
identifier to be misclassified. E.g.
```
package P;
typedef int T;
shortint X;
endpackage
module test;
typedef byte X;
initial $display($bits(P::T));
initial $display($bits(P::X));
endmodule
```
Here `P::T` gets classified as a signal identifier and `P::X` gets
classified as a type identifier.
To solve this introduce a common rule for the package scope operator. Using
the same rule everywhere allows the parser to reduce it unconditionally
without using lookahead.
Note that there are additional problems with resolving the type of a scoped
type identifiers in expressions, but this is a prerequisite to solve that.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The parser currently only allows package scoped functions to be called if
there is at least one argument. But package scoped functions are the same
as normal functions and it is allowed to call them with no arguments. It is
even possible to pass no value for a positional argument, if the positional
argument has a default value.
Update the parser to handle this.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
For function calls when calling the PECallFunction() constructor a copy is
made of the argument expression list. This means the original list should
be deleted within the rule.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
If the type of a port is an array type it currently always gets evaluated
in the scope where the port is declared.
But if the type is a typedef it might be declared in a different scope and
must be evaluated in that scope. E.g. the following will declare an array
port with 10 entries and an element type of a 5 bit vector, while it should
declare one with 4 entries and an element type of a 2 bit vector.
```
localparam A = 2;
localparam B = 4;
typedef [A-1:0] T[B];
module test (
T x
);
localparam A = 5;
localparam B = 10;
endmodule
```
This is in part due to array types being given special handling. This was
necessary before because each base type required slightly different
handling and so the base type had to be extracted from the array type.
This has now been consolidated and all data types are treated the same.
The only exception is the vector type which still needs special handling to
support separate definition of port direction and type.
As a result it is possible to remove the special handling of the array
type. This solves the problem of evaluating the type in the wrong scope.
Some special handling needs to be retained though to be able to
differentiate between array dimensions that are part of a type and array
dimensions that are part of port declaration. This is again necessary to
correctly support separate definition of port direction and type. E.g. in
the example below port `x` and `y` get treated slightly differently, even
though the resulting signals will be identical.
```
typedef logic [7:0] T[1:0];
...
input T x;
input [7:0] y[1:0];
```
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The parser used to have behavior that was dependent on the
`ivl_variable_type_t` of a signal. It also used the `ivl_variable_type_t`
of a signal to decide whether a signal can be re-declared as part of a
non-ANSI port declaration.
Neither of these is done anymore and most of the reference to
`ivl_variable_type_t` can be removed from the parser. The only thing it is
still needed for is to decide whether a vector type is 4-state or 2-state.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
`pform_make_task_ports()` has code very similar to `pform_set_net_range()`.
Use that helper function instead of duplicating the code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The last user of the `make_range_from_width()` was removed in commit
f6042033d0 ("Correctly handle separate port type declaration for
`integer` and `time`").
Remove the function itself as well.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Both Verilog (2005) and SystemVerilog support default port values for
variable output ports. SystemVerilog also supports default port values for
input ports. For port declaration lists it is possible to specify the
default value for port identifier.
E.g.
```
module M (
input integer x, y = 1,
output integer z, w = 2
) ...
```
Currently the parser only supports specifying the default value for the
first identifier in the list. Extend the parser to also allow to specify
the default value for identifiers in the list.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are a few places where it is possible to provide an optional
initializer in the form of `[ = <expression> ]`.
There are currently multiple rules that implement this behavior as well as
few places with duplicated rules, one with and one without the initializer.
Introduce a common helper rule for optional initializers. This allows to
remove some duplicated code from the parser.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
For output port lists with a default value for the first port declaration
all subsequent port declarations are declared as inout ports. This is due
to a small typo when setting the `port_declaration_context` port direction.
Fix this to make sure all ports in the port declaration list are declared
as output ports.
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>
SystemVerilog adds the `var` keyword that can be used to declare a signal
as a variable.
In contexts where a signal is always a variable it is purely optional and
it makes no difference whether it is specified or not. This is in
* for loop variable declarations
* task and function port declarations
For variable declarations as block items when `var` is used it is possible
to omit the explicit data type and use a implicit data type instead. E.g.
all of the following are valid.
```
var x;
var signed y;
var [1:0] z;
```
For module input and output ports the `var` keyword can be used in place of
the net type. It can be combined with either an implicit or explicit data
type.
E.g.
```
input var x
output var [1:0] y
```
inout ports can not be variables and will be reported as an error.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Refactor the variable lifetime parser rules so that instead of having too
rules, one with lifetime and one without, there is a single rule where the
lifetime is an optional element.
This helps to avoid a combinatorial explosion of parser rules once we
add `var` support.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Currently the parser can recover from `integer` or `time` variable
declarations, but not for variables of other types. Refector the parser
rules so that it can recover for all variable types as well as events.
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 lgate struct has its own fields for tracking file and line number,
while everything else that has this information attached inherits from the
LineInfo class.
Make lgate also inherit from LineInfo for consistency.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The `pform_set_data_type()` function is used to set the data type as well
as attributes on a list of signals. Currently the signals are passed as a
list of signal names and then the function looks up the actual signals from
the names.
Refactor the code to directly pass a list of signals. This will allow to
skip the look-up by name.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
`pform_set_reg_idx()` is always called right after `pform_makewire()`. Move
it into the function. This avoids the extra lookup that
`pform_set_reg_idx()` does to get the PWire from the name.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are a few parameters for `pform_makewire()` and related functions
that always get passed the same value.
* port_type is always NetNet::NOT_A_PORT
* attr is always 0
Remove these parameters.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Variable declarations in packages need an explicit data type. Omitting the
data type or using just packed dimensions is not valid syntax. E.g. the
following should not work.
```
package P;
x;
[1:0] y;
endpackage
```
The current implementation does accept this tough. To fix this update the
parser to only allow explicit data types for package variable declarations.
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>
It is possible to call a class method without parenthesis if no arguments
are specified.
At the moment this works when calling a class method by name. But when
using the implicit class handle `this` or `super` it does not work.
Add support for this.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Class constructors can be declared without parenthesis after the `new` when
no arguments are required. Just like for normal function.
In a similar way the base class constructor can also be invoked without
parenthesis after the `new`.
```
class C extends D;
function new;
super.new;
endfunction
endclass
```
Add support for this by making the parenthesis optional in the parser.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Class constructors don't allow for non-ANSI ports. E.g. the following is
not valid.
```
class C;
function new();
input int i;
endfunction
endclass
```
The parser will currently accept this, but otherwise ignore the non-ANSI
port. Modify the parser rules so that this is a syntax error.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are a few places in the grammar that follow the pattern of
`implicit_class_handle '.' hierarchy_identifier` and then splice the two
identifier paths into a single one. Factor this into a common helper rule
to avoid duplicated code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are a few places in the grammar where it is possible to specify a
argument list in parenthesis or nothing. E.g. a task invocation.
```
task t(int a = 10);
endtask
initial begin
// All 3 are valid syntax
t(1);
t();
t;
end
```
Factor this out into a common rule to be able to remove some duplicated
code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
There are a few places in the grammar where it is possible to specify a
task/function port list in parenthesis or nothing. E.g. task and function
prototypes. Factor this out into a common rule to be able to remove some
duplicated code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The base type for an enum type can be a type identifier for a typedef as
long as it resolves to a vector or integer type with at most one packed
dimension. This is described in section 6.19 ("Enumerations") of the LRM
(1800-2017). E.g.
```
typedef bit [3:0] T;
enum T {
A
} e;
```
Add support for this by allowing to specify a type identifier as the base
type for an enum in the parser. During elaboration it is checked whether
the type identifier resolves to a valid enum base type.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
It is possible to declare a new typedef that shadows an existing typedef in
a higher level scope. E.g.
```
typedef int T;
class C;
typedef real T;
endclass
```
In the current implementation this works for scopes that are not class
scopes.
Update the parser to also support this in class scopes by re-using the
existing parser rule that is used for the other scopes.
Reusing the existing rule also adds support for class forward typedes
inside classes.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
It is possible to declare a new typedef that shadows an existing typedef in
a higher level scope. E.g.
```
typedef int T;
module M;
typedef real T;
endmodule
```
In the current implementation this only works as long as the new type is
a not an array type.
Update the parser to allow to specify unpacked dimension when overwriting
a typedef from a different scope.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The parser currently expects `reg output` for UDP registered output. But
the correct syntax is `output reg`. Fix this to accept the correct syntax.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Currently there is a mix of passing line information either as `struct
vlltype` or as a separate `const char *file` and `unsigned lineno`.
For consistency always use the struct vlltype variant.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
The SystemVerilog grammar explicitly allows an empty class item
declaration. The empty class item declaration is just a semicolon and has
no effect.
E.g. the following is legal
```
class C
int x;;;
endclass
```
Add support to the parser to accept empty class item declarations.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
In Verilog module input ports can only have a packed dimensions and a
signed flag, but no explicit data type.
In SystemVerilog an explicit data type can be specified for module input
ports. Such a port is a net, regardless of the data type, unless
explicitly made a variable using the `var` keyword.
This works for the most part in the current implementation, but for some
data types such as `reg` and `integer` the input port is turned into a
variable. And since input port's can't be variables in the current
implementation this results in an error.
Fix this by completely removing the `reg_flag` that is used to indicate
that a certain data type is always a variable. There is no such restriction
on data types for SystemVerilog and for Verilog there are already checks in
place that a input port can only have an implicit (or real) data type.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
While a variable can have any data type the data type for nets is quite
restricted.
The SystemVerilog LRM section 6.7.1 ("Net declarations with built-in net
types") requires that the data type of a wire is either a 4-state packed or
a unpacked struct or unpacked array of 4-state packed types.
As an extension to this iverilog allows real data type for wires as well as
2-state packed types.
Add a check that reports an error if a net with any other type is declared.
In addition in Verilog a net can not have an explicit data type at all. It
can only have a packed dimension and a signed flag. As an extension to this
Icarus also allows wires to be of `real` data type.
Note that in Verilog mode the data type is checked in the parser since only
the parser knows whether the data type is an implicit type (`input reg
[7:0]` and `input [7:0] x` elaborate the same). But for SystemVerilog the
type is checked during elaboration since due to forward typedefs and type
parameters the type is not necessarily known in the parser.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
In SystemVerilog output ports are a variable if either:
* They are explicitly declared a variable (with the `var` keyword)
* There is no explicit net type, but a explicit data type
This is in detail described in section 23.2.2.3 ("Rules for determining port
kind, data type, and direction") of the LRM (1800-2017).
E.g.
```
output x // Net
output [1:0] x // Net
output signed x // Net
output wire x // Net
output wire logic x // Net
output var x // Variable
output logic x // Variable
output var logic x // Variable
output int x // Variable
output real x // Variable
output string x // Variable
output some_typedef x // Variable
```
At the moment the code checks for certain data types and only makes the
output port a variable for those. And it is even different data types
depending on whether the port is declared ANSI or non-ANSI style.
Change this so that if a data type is specified and it is not a implicit
data type (i.e. only ranges or `signed`) then the output is of type
variable.
This ensures consistent and correct behavior.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Task and function item rules are identical. Consolidate them into a single
set of rules to remove some duplicated code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog allows unpacked array dimensions on non-ANSI style task and
function ports.
To support this refactor pform_make_task_ports() to accept a of
pform_port_t, which in addition to the identifier name also allows to
specify per port unpacked dimensions.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Tasks and functions support two types of port declarations. Either ANSI
style, in parenthesis after the task name, or non-ANSI style, as
declaration statements in the task body.
In the current implementation SystemVerilog types are only accept for ANSI
style port declarations, while non-ANSI style only accept Verilog types
(reg, integer, time, real).
Add support for SystemVerilog data types for non-ansi style ports.
This also makes the parsing rules simpler since we can use `data_type` to
match all data types and don't need a explicit rule for each supported data
type.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog allows `parameter` and `localparam` to declare constants
within a class scope. E.g.
```SystemVerilog
class C;
localparam A = 10;
endclass
```
In this context both declare a local parameter that can not be overwritten.
Supporting this can be achieved for the most part by adding a parser
sub-rule in class declaration rule. In addition some extra support code is
needed to mark the parameter as non-overridable.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>