Basically did
set(CMAKE_CXX_CLANG_TIDY local/clang-tidy -checks=-*,llvm-use-new-mlir-op-builder -fix)
and then fixed cases where temporary OpBuilders were used (as passed in
by reference now).
Use the names of surrounding generate blocks as a prefix for variable
and instance names. This makes the names match up more closely with how
existing EDA tools would label variables and instances embededded in
generate blocks.
Consider the following Verilog input:
```systemverilog
module Top;
int x;
begin : foo
int y;
for (genvar i = 2; i < 6; ++i) begin : bar
int z;
end
end
endmodule
```
The ImportVerilog conversion pass will now produce the following
variables:
```mlir
%x = moore.variable : <i32> // before: %x
%foo.y = moore.variable : <i32> // before: %y
%foo.bar_2.z = moore.variable : <i32> // before: %z
%foo.bar_3.z = moore.variable : <i32> // before: %z_0
%foo.bar_4.z = moore.variable : <i32> // before: %z_1
%foo.bar_5.z = moore.variable : <i32> // before: %z_2
```
This closely matches names like `foo.bar[2].z` or `foo_bar_2_z` that
would be generated by common EDA tools. Ideally we would be able to use
a name like `foo.bar[2].z` directly, but ExportVerilog currently does
not support escaped identifiers, causing the name to be sanitized to
something like `foo_bar5B25D_z`.
By incorporating for-generate loop indices and block names into the
variable name, logical equivalence checking should become easier and the
signal names in Arcilator waveforms will match user expectations more
closely.
Fixes#8679.
Support `sequence` and `property` declarations. Slang already handles
the references and variable ports. Make visiting of `AssertionInstance`
legal. Those are part of `Expressions` of the `Simple` case. Adapt this
visitor to handle the varied return types.
Ignore `ForwardingTypedefSymbol` AST nodes, since Slang already deals
with these when it does type checking. We don't need them to show up in
the final IR.
Add the `--always-at-star-as-comb` option to circt-verilog and add a
corresponding option to ImportVerilog. When this option is set, detect
`always @*` in the Verilog AST and lower it as a `always_comb`. This is
a common pattern in synthesizers and simulators, since the traiditonal
`always @*` in Verilog does not accurately describe the behaviour of
combinational logic.
Enable this option by default, which seems to be a common behaviour
among existing tools.
* [ImportVerilog] Add HierarchicalNames.cpp to support hierarchical names.
* [ImportVerilog] Distinguish hierarchical names into upward and downward.
Based on the SystemVerilog IEEE Std 1800-2017 § 23.6 Hierarchical names. Hierarchical names are separated into upward and downward. For example:
module Top;
int x;
SubA subA();
assign x = subA.a; // upward: The Sub module's variable is used at the Top module.
endmodule
module SubA;
int a;
assign a = Top.x; // downward: The Top module's variable is used at the Sub module.
endmodule
Therefore, we mark upward as outputs and downward as inputs, meanwhile, all hierarchical names are marked as RefType.
Unsupported cases:
However, we don't support hierarchical names invoked by two irrelevant modules at the same level. For example:
module A;
int a = B.b;
endmodule
module B;
int b = A.a;
endmodule
And we also don't support hierarchical names existing in the repeat modules. For example:
module Bar;
SubC subC1();
SubC subC2();
int u = subC1.a;
assign subC2.b = u;
endmodule
module SubC();
int a, b;
endmodule
Co-authored-by: Fabian Schuiki <fabian@schuiki.ch>
Adds shadow variables for SV function arguments.
Fixes: #7431
Adjusts two test cases test/Conversion/ImportVerilog/basic.sv and test/Conversion/ImportVerilog/builtins.sv
Adds two test functions in test/Conversion/ImportVerilog/builtins.sv, which will crash in the former circt-verilog tool.
---------
Co-authored-by: Max Zhou <max@Maxs-MBP.lan>
In certain corner cases, Slang does not insert the necessary conversion
AST nodes around instance ports. This can be problematic if an input or
output port is connected to a cast-compatible type (e.g. `i32` and
`array<1 x i32>`). To fix this, always insert conversions if the types
of a port and the connected value differ.
Simply skip the elaboration system tasks `$info`, `$warning`, `$error`,
and `$fatal` outside procedural code. Slang already processes these
tasks and generates the corresponding diagnostics for us.
Remove the `NamedConstantOp` and replace its uses with `VariableOp` from
the debug dialect.
The op was originally added to track the value of constant parameters,
localparams, and specparams in the IR. In ImportVerilog, such parameters
would generate a corresponding `named_constant` op and all references to
the parameter by name would be replaced with the `named_constant`'s
result.
This doesn't really work well for parameters defined outside a module,
such as in packages or at the root of the Verilog source file. (Modules
are isolated from above, preventing the use of `named_constant`s from
outside the module.) Therefore expressions would generally fall back to
materializing constants directly where they were used.
Since the named constant ops are only there to track a constant value in
the IR for the user's debugging convenience, using the debug dialect
directly feels a lot more appropriate.
Instead of using the SCF dialect and its `scf.if` and `scf.while`
operations for control flow, switch over to the CF dialect and its
`cf.br` and `cf.cond_br`. This allows us to support SystemVerilog's
`continue`, `break`, and `return` statements which don't map well to the
Structured Control Flow dialect.
Ignore type parameter AST nodes since these are already handled during
Slang's type checking pass. Also tweak the diagnostics on unsupported
SV constructs to be a bit more descriptive.
Add missing conversions for cases where the Slang AST has expressions
operating directly on packed structs/arrays, but the Moore IR expects
a conversion to a simple bit vector type.
Also clean up a few things and remove an invalid `struct_inject`
conversion.
Divide the extract into a dynamic extract named moore.dyn_extract and a constant extract named moore.extract, the former has a variable as its low bit like arr[x], but the latter has a constant value as its low bit like arr[1].
Co-authored-by: Fabian Schuiki <fabian@schuiki.ch>
Add basic support for function definitions and calls to such functions.
Input function arguments are currently passed into the function by
value, while output, inout, ref, and const ref arguments are passed by
reference at the moment. Later on we may want to be more faithful to the
SV spec and make output and inout arguments also pass by value.
Calls to void functions are slightly strange in that they don't really
have a result value, but the `convertExpression` function really wants
a valid `Value` to return. To work around this, these calls generate an
additional `!moore.void` value using `unrealized_conversion_cast`, which
gets deleted again by the statement conversion code.
Add support for enum variants in expressions. Until now, ImportVerilog
would emit a "unknown name" diagnostic when an enum variant is used in
an expression. This is due to the lowering of named value expressions
simply checking whether an MLIR value is present in the `valueSymbols`
table for the referenced symbol name. This works for variables which
create dedicated declarations, but not for enum variants which
potentially live outside the current module's scope.
Instead, ImportVerilog will now check if the named value is a constant,
for example a parameter or an enum variant, and materialize that
constant directly as a `moore.constant` op. This will also be able to
deal with parameters declared inside of packages, for which the table of
value symbols of the current module does not contain any value. (Local
parameters inside the module generate a `moore.named_constant` op that
is present in the table.)
For the new concept of Moore dialect, some operations will be defined as memory-related operations. Modeling memref Dialect and LLVM dialect, the operation relationship is as follows:
ReadOp and blockingAssignOp are related to loadOp and storeOp.
VariableOp is related to allocaOp.
However, the operations mentioned below are for basic types. This PR will support nested types in the following way:
VariableOp with nested types is still related to allocaOp (will be replaced with structCreateOp and UnionCreateOp).
structExtractRefOp is related to storeOp.
structExtractOp is related to loadOp.
To implement this:
Since these operations will be lowered to the hw dialect, the design largely refers to the hw dialect.
Add the trait DestructurableAllocationOpInterface for VariableOp.
Add the trait DestructurableAccessorOpInterface for structExtractOp and structExtractRefOp.
Implement the DestructurableTypeInterface for structLikeType and the reftype of structLikeType.
For local variables:
Use the SROA (Scalar Replacement of Aggregates) Pass to destructure all nested-type variables into basic-type variables.
Use the Mem2Reg (Memory to Register) Pass to replace variables imported by SROA with constants.
For global/module-level variables:
When importing Verilog, use structInjectOp rather than blockingAssignOp, because structExtractRefOp has the Destructurable trait, but global variables should not be destructured.
structInjectOp means creating a new struct with new values and other old values.
Use the canonicalizer Pass to fold duplicate injecting same field operations.
Use the canonicalizer Pass to explicitly show new struct creation.
Use the canonicalizer Pass to send source values directly and remove structExtractOp.
Also, remove some unnecessary spaces in other code.
What's more:
Verify that the input of nested-type-related operations should match the field type defined.
To do:
Update the use of struct SSA values referring to the latest structInjectOp SSA values.
Design the method for union types.
Add and support the dbg dialect to keep local variables visible after SROA & Mem2Reg.
Co-authored-by: Théo Degioanni <degiotheo@gmail.com>
Co-authored-by: Fabian Schuiki <fabian@schuiki.ch>
Add handling for package definitions and accept empty packages for the
time being. Later on we'll want to support parameters, variables,
functions, and other things that can appear in packages. These require
a mechanism to refer to things outside of an `SVModuleOp` though, which
does not yet exist.
Use `upper_bound` to pick an insertion point for modules. This ensures
that multiple parametrizations of a module appear in the order in which
they were instantiated, instead of the reverse order.
We can append the appropriate symbol visibility to svmoduleOp according
to the structures of instanceSymbol provided by the slang front-end. slang
provides a function to get the root of the design. Calling this method
could get all top-level instanceSymbols and help determine which ones
should be tagged. Note that the visibility attribute now used does not
contain the `nested`.
* [mooretocore] Support namedconstant
Signed-off-by: mingzheTerapines <mingzhe.zhang@terapines.com>
* Limit type to integertype.
* add mooretocore support
* remove include
* remove verify of hwops
* remove include
* Test added.
* Add test for MooreToCore
* Support any attribute
* Modify test.
* Add symbol for wireOp
* Remove useless include
* Add more test and error cases
* slang-tidy
* some Imporvement
* Improved version
* little change
* Imporve
---------
Signed-off-by: mingzheTerapines <mingzhe.zhang@terapines.com>
[Moore] Modify the tests related to the RefType.
Add a wrapper type(moore::RefType) to work for VariableOp/NetOp, ReadOp, and AssignOpBase, and it can wrap any SystemVerilog types. We added RefType in order to introduce Mem2Reg into moore to eliminate the local temporary variables. Meanwhile, we added a new op named moore.extract_ref which is the copy of moore.extract that explicitly works on the ref type. And moore.concat_ref is the copy of moore.concat. Later, we will tweak union/struct ops like moore.struct_create in order to support the ref type.
moore.variable/moore.net will return a result with ref<T>, it's similar to memref.alloca.
Change moore::ReadLValueOp to moore::ReadOp, it's similar to memref.load. Initially, we need moore.read_lvalue only to service for assigmen operations(like +=, *=, etc). However, moore. read is not only for assignment operations but also to take a value with RefType(ref) and return the current value with the nested type(T). For example, a = b + c, moore.read will take b and c(both are ref<T>), and then return a value with the nested type(T).
We think the MooreLValueType and VariableDeclOp(52a71a6) that had been removed eventually found their proper site.
We also divide convertExpression() into convertLvalueExpression() for the LHS of assignments and convertRvalueExpression() for the RHS of assignments. The former will return a value with RefType like ref<i32>, but the latter returns a value without RefType like i32.
Co-authored-by: Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
Add module types and support for ports to the `moore.module` and
`moore.instance` operations. Also add a `moore.output` operation as a
terminator for modules, similar to the HW dialect. Extend ImportVerilog
to generate ports.
The ports on SVModuleOp follow a similar strategy as in HWModuleOp: all
input, inout, and ref ports are passed to the module as inputs and are
carried inside the module as block arguments, while output ports are
assigned by the OutputOp terminator. The Moore dialect reuses the
`hw::ModuleType` but does not use the `inout` direction. Instead, inout
ports will be represented as inputs with a `net<T>` wrapper type, while
ref ports will be wrapped as `ref<T>`.
Instances work identically to the HW dialect: input, inout, and ref port
connections are carried as operands, while output ports are represented
as results.
This commit also adds module and instance port support to the
ImportVerilog conversion. Regular ports are mapped to corresponding
ports on the module with the appropriate direction. Multi-ports, which
are a weird quirk of SystemVerilog that allow multiple ports to be
grouped up and presented to the outside as a single named port, are
split up into individual module ports. This is necessary since this
group can contain ports of different directions.
Inside a module Slang automatically generates local net or variable
declarations for all ports. The user may specify these declarations
themselves when using non-ANSI port lists, which Slang handles properly.
ImportVerilog inserts appropriate continuous assignments to drive the
actual input port value onto the local net or variable declaration, and
to drive the local declaration's value onto the actual output port of
the module. This properly adapts from SystemVerilog's assignable and
connectable ports that feel like nets or variables, to the Moore
dialect's by-value passing of inputs and outputs.
Instances in Slang have expressions connected to their ports. Input
ports lower this expression and directly use the result as an operand.
Output ports lower this expression and drive it through a continuous
assignment inserted after the instance, with the instance's
corresponding result on the right-hand side of the assignment.
Once we have a `ref<T>` type, and later potentially also a `net<T>`
type, the port lowering shall be revisited to ensure that inout and ref
ports are mapped to net and ref types, respectively. The lowering of
expressions connected to ports requires more care to ensure that they
are appropriately lowered to lvalues or rvalues, as needed by the port
direction. A `moore.short_circuit` operation or similar would help to
connect inout ports to the local net declarations in a module, and to
capture `alias` statements.
---------
Co-authored-by: cepheus <buyun.xu@terapines.com>
Co-authored-by: Hailong Sun <hailong.sun@terapines.com>
* [Moore] Add the netOp for net declaration in SV
NetOp is for variable declaration. Net Types defines different types of net connections in SV. There are twelve built-in net types defined
`supply0`, `supply1`, `tri`, `triand`, `trior`, `trireg`, `tri0`, `tri1`, `uwire`, `wire`, `wand`, `wor`, and three special net types:
`interconnect`, `userdefined`, `unknown`. The special ones are marked as unsupported because we have no plan to support them currently.
Corresponding test cases have been added to the parser/print and ImportVerilog pass tests. Add two expected failed netkind test cases -
`interconnect` & `user-defined`.
---------
Co-authored-by: Fabian Schuiki <fabian@schuiki.ch>
Co-authored-by: Hailong Sun <hailong.sun@terapines.com>
Extend the `ImportVerilog` conversion to support most of the basic
expressions that commonly appear in SystemVerilog input files. Also add
the correpsonding expression ops to the Moore dialect, and finally get
rid of the old MIR expressions file which is now obsolete.
Thanks @hailongSun2000 and @albertethon for doing a lot of the leg work
to get expression support in!
Co-authored-by: Hailong Sun <hailong.sun@terapines.com>
Co-authored-by: ShiZuoye <albertethon@163.com>
Co-authored-by: Martin Erhart <maerhart@outlook.com>
Add continuous, blocking, and non-blocking assignments to the Moore
dialect. These represent the corresponding constructs in SystemVerilog.
Add a `ScopedHashTable` to the ImportVerilog conversion context and add
variable declarations to it. This allows expression conversion to
resolve name expressions to the MLIR value that was generated for the
corresponding declaration. The Slang AST node pointer itself is used as
a lookup key, since the AST already has all names resolved.
Add a basic expression handling mechanism to the ImportVerilog
conversion. A new `convertExpression` entry point calls an `ExprVisitor`
to convert expression nodes in the Slang AST to the corresponding MLIR
operations. This currently only supports name expressions, which simply
looks up the converted MLIR value for whatever the name was resolved to,
and blocking and non-blocking assignments.
Extend the module conversion to properly handle continuous assignments
at the module level.
Thanks @albertethon, @hailongSun2000, and @angelzzzzz for the help to
get this started.
Co-authored-by: ShiZuoye <albertethon@163.com>
Co-authored-by: Hailong Sun <hailong.sun@terapines.com>
Co-authored-by: Anqi Yu <anqi.yu@terapines.com>
Add the `moore.procedure` operation to represent `initial`, `final`,
`always`, `always_comb`, `always_latch`, and `always_ff` procedures.
Extend the ImportVerilog conversion to map the corresponding Slang AST
nodes to these new operations.
These procedures expect a valid body statement or statement block. In
order to write basic tests, and in preparation for statement conversion
in the future, add the `Statements.cpp` file with a `StmtVisitor` that
handles the conversion of Verilog statements to MLIR ops. A new
`convertStatement` function on the conversion context acts as entry
point to convert statements, similar to `convertModule*` or
`convertType`. We only support a few basic statements as of now, mainly
stray semicolons, nested blocks, and variable declarations. This commit
explicitly does not handle variable lifetime yet, which is a topic for
a future refinement PR.
Thanks @hailongSun2000 for doing the heavy lifting here and getting this
going.
Co-authored-by: Hailong Sun <hailong.sun@terapines.com>
Implement the conversion from SystemVerilog types in Slang's AST to the
corresponding MLIR types in the Moore dialect. To facilitate testing,
add a basic `moore.variable` op and convert module-level variable
declarations to this new op. This requires converting the variable's
type. We may choose to have variables return a special lvalue or pointer
wrapper type in the future once such a need arises to clearly demarcate
variable reading and assignment.
Since we don't support expressions yet, variables with an initializer
expression cause an error for the time being.
Extend the `ImportVerilog` conversion to support empty SystemVerilog
module definitions and instances of such modules.
To do this, add a `ImportVerilogInternals.h` header that defines a
`Context` helper struct to aid in the conversion. Similar to other
parsers and translations, the context has a builder, tracks operations,
and offers entry points to convert different nodes of the Slang AST, and
handles diagnostics and location conversion.
An annoying quirk of Slang is the fact that the top-level declarations
in an SV file are reordered and rearranged. All instantiable nodes, such
as modules, programs, or interfaces, are moved into a separate list of
top-level instances which is unordered. To allow for better testing with
FileCheck and to produce more predictable output, the helper `Context`
tracks an ordered `std::map` of the created top-level MLIR ops, indexed
by their Slang location in the input. This map is used to determine the
insertion point for new modules as the AST is converted, such that the
resulting MLIR ops are essentially in source file order.
This new conversion discards any module ports, and any module members
except for instances and stray semicolons. This is already sufficient to
create module hierarchies, and also to define modules nested in other
modules, which Slang already helpfully resolves to an instance and
outlined module definition.
This commit also adds corresponding `moore.module` and `moore.instance`
operations to capture the empty modules and instances. These will be
extended in the future to deal with all the quirks of Verilog ingestion.
Most of the module hierarchy conversion is in a new `Structure.cpp` file
in the `ImportVerilog` conversion, in preparation for future additions
of statement, expression, and type conversion in separate files.