mirror of https://github.com/llvm/circt.git
[Moore] Support unconnected behavior (#7202)
This commit is contained in:
parent
1f6c29fb64
commit
560257cd4c
|
@ -11,6 +11,35 @@ The main goal of the `moore` dialect is to provide a set of operations and types
|
|||
|
||||
In contrast, the `sv` dialect is geared towards emission of SystemVerilog text, and is focused on providing a good lowering target to allow for emission. The `moore` and `sv` dialect may eventually converge into a single dialect. As we are building out the Verilog frontend capabilities of CIRCT it is valuable to have a separate ingestion dialect, such that we do not have to make disruptive changes to the load-bearing `sv` dialect used in production.
|
||||
|
||||
## LRM Rules
|
||||
### Unconnection rules
|
||||
The SystemVerilog LRM defines unconnected behavior while leaving ports unconnected.
|
||||
|
||||
| Port Type | Unconnected Behavior |
|
||||
| ---------------- | -------------------------- |
|
||||
| Input (Net) | High-impedance value ('Z) |
|
||||
| Input (Variable) | Default initial value |
|
||||
| Output | No effect on Simulation |
|
||||
| Inout (Net) | High-impedance value ('Z) |
|
||||
| Inout (Variable) | Default initial value |
|
||||
| Ref | Cannot be left unconnected |
|
||||
| Interconnect | Cannot be left unconnected |
|
||||
| Interface | Cannot be left unconnected |
|
||||
|
||||
For variables that do not have a specified initializer. It has a default rule to initialize data value according to its type:
|
||||
|
||||
| Type | Default initial value |
|
||||
| --------------------------- | ------------------------------- |
|
||||
| 4-state integral | 'X |
|
||||
| 2-state integral | '0 |
|
||||
| **real**, **shortreal** | 0.0 |
|
||||
| Enumeration | Base type default initial value |
|
||||
| **string** | "" (empty string) |
|
||||
| **event** | New event |
|
||||
| **class** | **null** |
|
||||
| **interface class** | **null** |
|
||||
| **chandle (Opaque handle)** | **null** |
|
||||
| **virtual interface** | **null** |
|
||||
|
||||
## Types
|
||||
|
||||
|
@ -19,7 +48,7 @@ In contrast, the `sv` dialect is geared towards emission of SystemVerilog text,
|
|||
The `moore.iN` and `moore.lN` types represent a two-valued or four-valued simple bit vector of width `N`.
|
||||
|
||||
| Verilog | Moore Dialect |
|
||||
|------------|---------------|
|
||||
| ---------- | ------------- |
|
||||
| `bit` | `!moore.i1` |
|
||||
| `logic` | `!moore.l1` |
|
||||
| `reg` | `!moore.l1` |
|
||||
|
|
|
@ -120,9 +120,53 @@ struct MemberVisitor {
|
|||
|
||||
for (const auto *con : instNode.getPortConnections()) {
|
||||
const auto *expr = con->getExpression();
|
||||
if (!expr)
|
||||
return mlir::emitError(loc)
|
||||
<< "unconnected port `" << con->port.name << "` not supported";
|
||||
|
||||
// Handle unconnected behavior. The expression is null if it have no
|
||||
// connection for the port.
|
||||
if (!expr) {
|
||||
auto *port = con->port.as_if<PortSymbol>();
|
||||
switch (port->direction) {
|
||||
case slang::ast::ArgumentDirection::In: {
|
||||
auto refType = moore::RefType::get(
|
||||
cast<moore::UnpackedType>(context.convertType(port->getType())));
|
||||
|
||||
if (const auto *net =
|
||||
port->internalSymbol->as_if<slang::ast::NetSymbol>()) {
|
||||
auto netOp = builder.create<moore::NetOp>(
|
||||
loc, refType, StringAttr::get(builder.getContext(), net->name),
|
||||
convertNetKind(net->netType.netKind), nullptr);
|
||||
auto readOp = builder.create<moore::ReadOp>(
|
||||
loc, refType.getNestedType(), netOp);
|
||||
portValues.insert({port, readOp});
|
||||
} else if (const auto *var =
|
||||
port->internalSymbol
|
||||
->as_if<slang::ast::VariableSymbol>()) {
|
||||
auto varOp = builder.create<moore::VariableOp>(
|
||||
loc, refType, StringAttr::get(builder.getContext(), var->name),
|
||||
nullptr);
|
||||
auto readOp = builder.create<moore::ReadOp>(
|
||||
loc, refType.getNestedType(), varOp);
|
||||
portValues.insert({port, readOp});
|
||||
} else {
|
||||
return mlir::emitError(loc)
|
||||
<< "unsupported internal symbol for unconnected port `"
|
||||
<< port->name << "`";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// No need to express unconnected behavior for output port, skip to the
|
||||
// next iteration of the loop.
|
||||
case slang::ast::ArgumentDirection::Out:
|
||||
continue;
|
||||
|
||||
// TODO: Mark Inout port as unsupported and it will be supported later.
|
||||
default:
|
||||
return mlir::emitError(loc)
|
||||
<< "unsupported port `" << port->name << "` ("
|
||||
<< slang::ast::toString(port->kind) << ")";
|
||||
}
|
||||
}
|
||||
|
||||
// Unpack the `<expr> = EmptyArgument` pattern emitted by Slang for
|
||||
// output and inout ports.
|
||||
|
@ -185,7 +229,6 @@ struct MemberVisitor {
|
|||
|
||||
for (auto &port : moduleLowering->ports) {
|
||||
auto value = portValues.lookup(&port.ast);
|
||||
assert(value && "no prepared value for port");
|
||||
if (port.ast.direction == ArgumentDirection::Out)
|
||||
outputValues.push_back(value);
|
||||
else
|
||||
|
@ -202,7 +245,8 @@ struct MemberVisitor {
|
|||
|
||||
// Assign output values from the instance to the connected expression.
|
||||
for (auto [lvalue, output] : llvm::zip(outputValues, inst.getOutputs()))
|
||||
builder.create<moore::ContinuousAssignOp>(loc, lvalue, output);
|
||||
if (lvalue)
|
||||
builder.create<moore::ContinuousAssignOp>(loc, lvalue, output);
|
||||
|
||||
return success();
|
||||
}
|
||||
|
|
|
@ -1054,6 +1054,23 @@ module PortsTop;
|
|||
// CHECK-NEXT: moore.assign [[V1]], [[V1_VALUE]]
|
||||
// CHECK-NEXT: moore.assign [[C1]], [[C1_VALUE]]
|
||||
MultiPorts p3(x3, y3, z3, w3);
|
||||
|
||||
wire x4, y4;
|
||||
// CHECK: %a = moore.net wire : <l1>
|
||||
// CHECK: [[A_VALUE:%.+]] = moore.read %a : l1
|
||||
// CHECK: [[X4:%.+]] = moore.read %x4 : l1
|
||||
// CHECK: %c = moore.variable : <l1>
|
||||
// CHECK: [[C_VALUE:%.+]] = moore.read %c : l1
|
||||
// CHECK: [[D_VALUE:%.+]], [[E_VALUE:%.+]] = moore.instance "p4" @PortsUnconnected(
|
||||
// CHECK-SAME: a: [[A_VALUE]]: !moore.l1
|
||||
// CHECK-SAME: b: [[X4]]: !moore.l1
|
||||
// CHECK-SAME: c: [[C_VALUE]]: !moore.l1
|
||||
// CHECK-SAME: ) -> (
|
||||
// CHECK-SAME: d: !moore.l1
|
||||
// CHECK-SAME: e: !moore.l1
|
||||
// CHECK-SAME: )
|
||||
// CHECK: moore.assign %y4, [[D_VALUE]] : l1
|
||||
PortsUnconnected p4(.a(), .b(x4), .c(), .d(y4), .e());
|
||||
endmodule
|
||||
|
||||
// CHECK-LABEL: moore.module @PortsAnsi
|
||||
|
@ -1084,14 +1101,14 @@ module PortsAnsi(
|
|||
endmodule
|
||||
|
||||
// CHECK-LABEL: moore.module @PortsNonAnsi
|
||||
// CHECK-SAME: in %a : !moore.l1
|
||||
// CHECK-SAME: out b : !moore.l1
|
||||
// CHECK-SAME: in %c : !moore.ref<l1>
|
||||
// CHECK-SAME: in %d : !moore.ref<l1>
|
||||
module PortsNonAnsi(a, b, c, d);
|
||||
// CHECK-SAME: in %a : !moore.l1
|
||||
input a;
|
||||
// CHECK-SAME: out b : !moore.l1
|
||||
output b;
|
||||
// CHECK-SAME: in %c : !moore.ref<l1>
|
||||
inout c;
|
||||
// CHECK-SAME: in %d : !moore.ref<l1>
|
||||
ref logic d;
|
||||
endmodule
|
||||
|
||||
|
@ -1167,6 +1184,34 @@ module MultiPorts(
|
|||
// CHECK: moore.output [[V1_READ]], [[C1_READ]]
|
||||
endmodule
|
||||
|
||||
// CHECK-LABEL: moore.module @PortsUnconnected
|
||||
module PortsUnconnected(
|
||||
// CHECK-SAME: in %a : !moore.l1
|
||||
input a,
|
||||
// CHECK-SAME: in %b : !moore.l1
|
||||
input b,
|
||||
// CHECK-SAME: in %c : !moore.l1
|
||||
input logic c,
|
||||
// CHECK-SAME: out d : !moore.l1
|
||||
output d,
|
||||
// CHECK-SAME: out e : !moore.l1
|
||||
output e
|
||||
);
|
||||
// Internal nets and variables created by Slang for each port.
|
||||
// CHECK: [[A_INT:%.+]] = moore.net name "a" wire : <l1>
|
||||
// CHECK: [[B_INT:%.+]] = moore.net name "b" wire : <l1>
|
||||
// CHECK: [[C_INT:%.+]] = moore.variable name "c" : <l1>
|
||||
// CHECK: [[D_INT:%.+]] = moore.net wire : <l1>
|
||||
// CHECK: [[E_INT:%.+]] = moore.net wire : <l1>
|
||||
|
||||
// Mapping ports to local declarations.
|
||||
// CHECK: moore.assign [[A_INT]], %a : l1
|
||||
// CHECK: moore.assign [[B_INT]], %b : l1
|
||||
// CHECK: [[D_READ:%.+]] = moore.read [[D_INT]] : l1
|
||||
// CHECK: [[E_READ:%.+]] = moore.read [[E_INT]] : l1
|
||||
// CHECK: moore.output [[D_READ]], [[E_READ]] : !moore.l1, !moore.l1
|
||||
endmodule
|
||||
|
||||
// CHECK-LABEL: moore.module @EventControl(in %clk : !moore.l1)
|
||||
module EventControl(input clk);
|
||||
// CHECK: %clk_0 = moore.net name "clk" wire : <l1>
|
||||
|
|
Loading…
Reference in New Issue