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>
Currently the elaboration function for unpacked array types prints an error
for queues that they are not allowed inside classes. And while this error
gets triggered when declaring a property with a queue type, it also gets
triggered for other places that uses a queue type, e.g. a function return
type. The only exception is signals which uses a different internal code
path when elaborating queue types.
Move the error message, that is class property specific, to the class
property elaboration. This also makes sure that the error messages
references the line where the property is declared and not the line where
the type is declared.
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>
For classes declared inside a module each module instance creates a new
unique class type. These types are not compatible to each other. This is
necessary since module parameters can change the class implementation.
This is defined in section 6.22 ("Type compatibility") of the LRM (1800-2017).
In the current implementation when a class is elaborated the elaborated
type is stored in the class_type_t so it is possible to look up the
elaborated class type. But this class_type_t is shared among elaborated
class types. As a result when creating multiple instances of a module with
a class definition an internal assert is triggered.
To support multiple module instances with class definitions instead of
storing the elaborated type in the type definition look up the type in the
scope in which the type definition is references.
This is similar to how the same problem is solved for enum types.
For packages we still need to remember the elaborated type otherwise scoped
class type references wont work. Since there is only one instance of a
package this doesn't have the same problem as classes in modules.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
An enum data type declared in a module is not compatible between different
instances of the module. The type is unique in each hierarchical instance
scope. The type can for example depend on module parameters which would
result in conflicting definitions. This is defined in section 6.22 ("Type
compatibility") of the LRM (1800-2017).
At the moment enum compatibility is checked by comparing the enum_type_t.
But the enum_type_t is shared among the netenum_t that are created for each
module instance and gives the wrong result.
Since there is exactly one netenum_t created for each enum and each
instantiated scope use this to check if the data type of two enum type
signals is compatible.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
For some data types the value returned by the `elaborate_type()` method is
shared among different signals of that type. E.g. all string or real types
get elaborated to the same ivl_type_s. This means the returned value must
not be modified, otherwise the data type for unrelated signals might get
changed.
To enforce this and protect against accidental breakage make the return
type of the `elaborate_type()` and the related `elaborate_type_raw()`
methods const.
Note that `ivl_type_t` is used for the new return type which is a typedef
for `const ivl_type_s*`.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Void functions can be used in always_comb, but since the compiler
uses the check_synth() method to generate some warnings, make sure
that function is implemented for functions as well as tasks.
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>
Parameters declared in certain scopes behave like local parameters and can
not be overridden. Rather than making those parameters a localparam track
whether a parameter can be overridden.
This allows to generate better error messages when trying to override the
parameter.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
During parsing parameters and localparams are kept in a separate list only
to be collected into the same list during elaboration.
Store them in the same list during parsing as well, this allows to remove
some duplicated code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
A base class can be referenced by scope. E.g. if the base class is in a
package.
```
package P;
class B;
endclass
endpackage
module test;
class C extends P::B;
endlcass
endmodule
```
To support this let the parser accept a scope identifier for the base
class.
A small change is also necessary to how the base class lockup is done
during elaboration. At the moment the code will search for the base class
by name in the current scope. This doesn't work with scoped identifiers.
But we already have a reference to the base class data type, so we don't
have to search for it by name.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Overriding a parameter that does not exist will only generate a warning at
the moment. This can hide programming mistakes such as an typo in a
parameter override.
There is nothing in the LRMs to support that this should only be warning,
so elevate this to an error. This is consistent with how an error is
generated when trying to reference a non-existing port or variable.
The generated error message differentiates between whether the parameter
does not exist at all, or whether it is a localparam.
There are two regression tests that rely on that only a warning is
generated, these have been updated to expect an error.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
enum_type_t inherits from LineInfo, but also has a LineInfo field called
`li`.
When a enum_type_t is created the LineInfo of the object itself is set to
the location where the type is declared.
The `li` field gets set when a signal of the enum_type_t is created to the
location where the signal is created. The `li` field is then used when
elaborating a netenum_t to set the line information on the netenum_t.
This works fine when the enum is directly used to declare a signal, since
the location of the type and signal declaration are the same and there is
only one signal of that type.
But when using a typedef and declaring multiple signals with the same type
the `li` field will be repeatedly set and eventually point to the last
signal declaration of that type.
On the other hand when using or declaring an enum as part of an aggregate
type such as an array, struct or class the line info will never be
set.
This can cause misleading error messages. E.g.
```
typedef enum {
A, B = A
} e_t;
struct packed {
e_t e;
} s;
```
will generate
```
:0: error: Enumeration name B and A have the same value: 32'sd0
```
To fix this use the LineInfo that was assigned to the enum_type_t itself
when it was declared and remove the `li` field.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
enums for a scope are stored in a std::set. This means when iterating over
the enums during elaboration it is possible that they are elaborated in a
different order than they have been declared in. This causes problems if
one enum references items of the other enum. E.g.
```
enum {
A
} a;
enum {
B = A
} b;
```
In the current implementation whether this works or not depends on the
pointer values of the enum_type_t for `a` and `b`, which can change between
environments.
To make sure that enums are elaborated in the same order use a std::vector
instead of a std::set.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Use netrange_t, which is meant for storing ranges, for storing the range of
a netenum_t.
This makes the integration with the rest of the codebase a bit more
seamlessly and also allow to reuse methods defined for netrange_t such as
the width() method rather than having to reimplement it.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Specifying and enum with an invalid dimension range results in an assert or
segfault. E.g. `enum [$] E { ... }`.
Use the `evaluate_ranges()` function to elaborate the enum dimensions. This
functions has proper error checking and recovery built-in.
In addition verify that there is at most one packed dimension.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This also ensures the same genvar cannot be used in two nested loops
(issue #533), because the implicit localparam with the same name
shadows the genvar declaration.
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.
The compiler elaborates types on the fly as they are used. For user-
defined types (typedefs) we must do the elaboration in the scope where
the type was declared, not in the scope where it is used.
Explicit imports should always conflict with local declarations using
the same name. Wildcard imports only conflict if they are referenced
before a local declaration with the same name.
This also unifies the detection of identifier conflicts.
This implements and enforces the full set of rules for determining
timescales in SystemVerilog. The previous relaxation of the rules
that allowed timescales to be redefined within the compilation unit
scope has been removed. Time unit and precision redeclarations are
now recognised after a nested module declaration.
The compilation unit scope is now treated as a specialised form of
package (with an automatically generated name). All items declared
outside a design element are added to the current compilation unit
package. Apart from when searching for a symbol, once we get into
elaboration we can treat these just like any other package.
Two fixes needed:
- when searching for a base class, we need to look in the root scope
if the base class isn't found in the scope hierarchy
- the classes in the root scope need to be stored in an ordered
list, not a map, to ensure they are elaborated in the order they
were declared. Without this, the compiler may try elaborating an
extended class before its base class is known about.
(* my_fancy_attribute *)
foobar1 foobar (clk(clk), rst(rst) ...);
- Modifies PGModule to hold the attribute map (can be verified with pform_dump)
- pform_make_modgate(s) bind the attributes from the parser to the above map
- The attributes from PGModule are inserted into the NetScope of that module
PGModule::elaborate_scope_mod_instances_
- Currently these attributes automatically make it into netlist
- These attributes are accessible via ivl_scope_attr_cnt and ivl_scope_attr_val
from ivl_target.h
SystemVerilog allows tasks, functions, and classes to be defined at the
root level or inside packages, so we can't rely on an enclosing module
being present to provide the timescale.