[Moore] Introduce a new operation - netOp for net declaration (#6884)

* [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>
This commit is contained in:
cepheus 2024-04-22 13:08:54 +08:00 committed by GitHub
parent 77a0808750
commit 61a18fe046
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 202 additions and 8 deletions

View File

@ -146,7 +146,60 @@ def VariableOp : MooreOp<"variable", [
let arguments = (ins StrAttr:$name, Optional<UnpackedType>:$initial);
let results = (outs Res<UnpackedType, "", [MemAlloc]>:$result);
let assemblyFormat = [{
`` custom<ImplicitSSAName>($name) ($initial^)? attr-dict
custom<ImplicitSSAName>($name) ($initial^)? attr-dict
`:` type($result)
}];
}
def NetKindAttr : I32EnumAttr<"NetKind", "Net type kind", [
I32EnumAttrCase<"Supply0", 0, "supply0">,
I32EnumAttrCase<"Supply1", 1, "supply1">,
I32EnumAttrCase<"Tri", 2, "tri">,
I32EnumAttrCase<"TriAnd", 3, "triand">,
I32EnumAttrCase<"TriOr", 4, "trior">,
I32EnumAttrCase<"TriReg", 5, "trireg">,
I32EnumAttrCase<"Tri0", 6, "tri0">,
I32EnumAttrCase<"Tri1", 7, "tri1">,
I32EnumAttrCase<"UWire", 8, "uwire">,
I32EnumAttrCase<"Wire", 9, "wire">,
I32EnumAttrCase<"WAnd", 10, "wand">,
I32EnumAttrCase<"WOr", 11, "wor">,
I32EnumAttrCase<"Interconnect", 12, "interconnect">,
I32EnumAttrCase<"UserDefined", 13, "userdefined">,
I32EnumAttrCase<"Unknown", 14, "unknown">,
]> {
let cppNamespace = "::circt::moore";
}
def NetOp : MooreOp<"net", [
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
OptionalTypesMatchWith<"assigned value and variable types match",
"result", "assignment", "$_self">,
]> {
let summary = "A net declaration";
let description = [{
The `moore.net' operation is a net declaration. Net types defines different types
of net connection in SV. There are twelve built-in net types defined in the official
standard construct of the operation:
`supply0`, `supply1`, `tri`, `triand`, `trior`, `trireg`, `tri0`, `tri1`, `uwire`,
`wire`, `wand`, `wor`.
Optional assignment argument allows net operation to be initialized with specific
values as soon as it is created. Only one net declaration assignment can be made for
a particular net. See IEEE 1800-2017 § 10.3.1 "The net declaration assignment" for
the differences between net declaration assignments and continuous assign statements.
It has some features that are not supported: declaring an interconnect net and using
user-defined types in the net operation.
See IEEE 1800-2017 § 6.7 "Net declarations".
}];
let arguments = (ins
StrAttr:$name,
NetKindAttr:$kind,
Optional<UnpackedType>:$assignment
);
let results = (outs UnpackedType:$result);
let assemblyFormat = [{
``custom<ImplicitSSAName>($name) $kind ($assignment^)? attr-dict
`:` type($result)
}];
}

View File

@ -35,6 +35,42 @@ convertProcedureKind(slang::ast::ProceduralBlockKind kind) {
llvm_unreachable("all procedure kinds handled");
}
static moore::NetKind convertNetKind(slang::ast::NetType::NetKind kind) {
switch (kind) {
case slang::ast::NetType::Supply0:
return moore::NetKind::Supply0;
case slang::ast::NetType::Supply1:
return moore::NetKind::Supply1;
case slang::ast::NetType::Tri:
return moore::NetKind::Tri;
case slang::ast::NetType::TriAnd:
return moore::NetKind::TriAnd;
case slang::ast::NetType::TriOr:
return moore::NetKind::TriOr;
case slang::ast::NetType::TriReg:
return moore::NetKind::TriReg;
case slang::ast::NetType::Tri0:
return moore::NetKind::Tri0;
case slang::ast::NetType::Tri1:
return moore::NetKind::Tri1;
case slang::ast::NetType::UWire:
return moore::NetKind::UWire;
case slang::ast::NetType::Wire:
return moore::NetKind::Wire;
case slang::ast::NetType::WAnd:
return moore::NetKind::WAnd;
case slang::ast::NetType::WOr:
return moore::NetKind::WOr;
case slang::ast::NetType::Interconnect:
return moore::NetKind::Interconnect;
case slang::ast::NetType::UserDefined:
return moore::NetKind::UserDefined;
case slang::ast::NetType::Unknown:
return moore::NetKind::Unknown;
}
llvm_unreachable("all net kinds handled");
}
namespace {
struct MemberVisitor {
Context &context;
@ -73,8 +109,8 @@ struct MemberVisitor {
// Handle variables.
LogicalResult visit(const slang::ast::VariableSymbol &varNode) {
auto type = context.convertType(*varNode.getDeclaredType());
if (!type)
auto loweredType = context.convertType(*varNode.getDeclaredType());
if (!loweredType)
return failure();
Value initial;
@ -83,16 +119,44 @@ struct MemberVisitor {
if (!initial)
return failure();
if (initial.getType() != type)
initial = builder.create<moore::ConversionOp>(loc, type, initial);
if (initial.getType() != loweredType)
initial =
builder.create<moore::ConversionOp>(loc, loweredType, initial);
}
auto varOp = builder.create<moore::VariableOp>(
loc, type, builder.getStringAttr(varNode.name), initial);
loc, loweredType, builder.getStringAttr(varNode.name), initial);
context.valueSymbols.insert(&varNode, varOp);
return success();
}
// Handle nets.
LogicalResult visit(const slang::ast::NetSymbol &netNode) {
auto loweredType = context.convertType(*netNode.getDeclaredType());
if (!loweredType)
return failure();
Value assignment;
if (netNode.getInitializer()) {
assignment = context.convertExpression(*netNode.getInitializer());
if (!assignment)
return failure();
}
auto netkind = convertNetKind(netNode.netType.netKind);
if (netkind == moore::NetKind::Interconnect ||
netkind == moore::NetKind::UserDefined ||
netkind == moore::NetKind::Unknown)
return mlir::emitError(loc, "unsupported net kind `")
<< netNode.netType.name << "`";
auto netOp = builder.create<moore::NetOp>(
loc, loweredType, builder.getStringAttr(netNode.name), netkind,
assignment);
context.valueSymbols.insert(&netNode, netOp);
return success();
}
// Handle continuous assignments.
LogicalResult visit(const slang::ast::ContinuousAssignSymbol &assignNode) {
if (const auto *delay = assignNode.getDelay()) {
@ -129,8 +193,8 @@ struct MemberVisitor {
// Ignore statement block symbols. These get generated by Slang for blocks
// with variables and other declarations. For example, having an initial
// procedure with a variable declaration, such as `initial begin int x; end`,
// will create the procedure with a block and variable declaration as
// procedure with a variable declaration, such as `initial begin int x;
// end`, will create the procedure with a block and variable declaration as
// expected, but will also create a `StatementBlockSymbol` with just the
// variable layout _next to_ the initial procedure.
LogicalResult visit(const slang::ast::StatementBlockSymbol &) {

View File

@ -45,6 +45,14 @@ void VariableOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
setNameFn(getResult(), getName());
}
//===----------------------------------------------------------------------===//
// NetOp
//===----------------------------------------------------------------------===//
void NetOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
setNameFn(getResult(), getName());
}
//===----------------------------------------------------------------------===//
// ConstantOp
//===----------------------------------------------------------------------===//

View File

@ -46,6 +46,33 @@ module Basic;
int v1;
int v2 = v1;
// CHECK: %w0 = moore.net wire : !moore.logic
// CHECK: %w1 = moore.net wire %w0 : !moore.logic
wire w0;
wire w1 = w0;
// CHECK: %w2 = moore.net uwire %w0 : !moore.logic
uwire w2 = w0;
// CHECK: %w3 = moore.net tri %w0 : !moore.logic
tri w3 = w0;
// CHECK: %w4 = moore.net triand %w0 : !moore.logic
triand w4 = w0;
// CHECK: %w5 = moore.net trior %w0 : !moore.logic
trior w5 = w0;
// CHECK: %w6 = moore.net wand %w0 : !moore.logic
wand w6 = w0;
// CHECK: %w7 = moore.net wor %w0 : !moore.logic
wor w7 = w0;
// CHECK: %w8 = moore.net trireg %w0 : !moore.logic
trireg w8 = w0;
// CHECK: %w9 = moore.net tri0 %w0 : !moore.logic
tri0 w9 = w0;
// CHECK: %w10 = moore.net tri1 %w0 : !moore.logic
tri1 w10 = w0;
// CHECK: %w11 = moore.net supply0 : !moore.logic
supply0 w11;
// CHECK: %w12 = moore.net supply1 : !moore.logic
supply1 w12;
// CHECK: %b1 = moore.variable : !moore.packed<range<bit, 0:0>>
// CHECK: [[TMP:%.+]] = moore.conversion %b1 : !moore.packed<range<bit, 0:0>> -> !moore.bit
// CHECK: %b2 = moore.variable [[TMP]] : !moore.bit

View File

@ -44,6 +44,21 @@ endmodule
// -----
module Foo;
// expected-error @below {{unsupported construct}}
nettype real x;
endmodule
// -----
module Foo;
// expected-error @+2 {{unsupported type}}
// expected-note @+1 {{untyped}}
interconnect x;
endmodule
// -----
// expected-error @below {{unsupported construct}}
package Foo;
endpackage

View File

@ -10,6 +10,33 @@ moore.module @Foo {
// CHECK: [[TMP:%.+]] = moore.variable name "v1" %v2 : !moore.bit
moore.variable name "v1" %v2 : !moore.bit
// CHECK: %w0 = moore.net wire : !moore.logic
%w0 = moore.net wire : !moore.logic
// CHECK: %w1 = moore.net wire %w0 : !moore.logic
%w1 = moore.net wire %w0 : !moore.logic
// CHECK: %w2 = moore.net uwire %w0 : !moore.logic
%w2 = moore.net uwire %w0 : !moore.logic
// CHECK: %w3 = moore.net tri %w0 : !moore.logic
%w3 = moore.net tri %w0 : !moore.logic
// CHECK: %w4 = moore.net triand %w0 : !moore.logic
%w4 = moore.net triand %w0 : !moore.logic
// CHECK: %w5 = moore.net trior %w0 : !moore.logic
%w5 = moore.net trior %w0 : !moore.logic
// CHECK: %w6 = moore.net wand %w0 : !moore.logic
%w6 = moore.net wand %w0 : !moore.logic
// CHECK: %w7 = moore.net wor %w0 : !moore.logic
%w7 = moore.net wor %w0 : !moore.logic
// CHECK: %w8 = moore.net trireg %w0 : !moore.logic
%w8 = moore.net trireg %w0 : !moore.logic
// CHECK: %w9 = moore.net tri0 %w0 : !moore.logic
%w9 = moore.net tri0 %w0 : !moore.logic
// CHECK: %w10 = moore.net tri1 %w0 : !moore.logic
%w10 = moore.net tri1 %w0 : !moore.logic
// CHECK: %w11 = moore.net supply0 : !moore.logic
%w11 = moore.net supply0 : !moore.logic
// CHECK: %w12 = moore.net supply1 : !moore.logic
%w12 = moore.net supply1 : !moore.logic
// CHECK: moore.procedure initial {
// CHECK: moore.procedure final {
// CHECK: moore.procedure always {