sv.bind op for encoding system verilog bind statements (#1135)

sv.bind op for encoding system verilog bind statements.  Bind is a syntax for an out-of-line instance declaration which happens logically at the end of the target module.  This form refers to an instance in the IR to do the heavy lifting.
This commit is contained in:
Andrew Lenharth 2021-05-28 09:57:19 -05:00 committed by GitHub
parent 35bd44f920
commit 04cdf0a486
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 124 additions and 1 deletions

View File

@ -20,6 +20,9 @@
#include "mlir/Interfaces/SideEffectInterfaces.h"
namespace circt {
namespace hw {
class InstanceOp;
}
namespace sv {
/// Return true if the specified operation is an expression.

View File

@ -10,6 +10,13 @@
//
//===----------------------------------------------------------------------===//
/// Ensure symbol is one of the hw module.* types
def isModuleSymbol : AttrConstraint<
CPred<
"hw::isAnyModule(::mlir::SymbolTable::lookupNearestSymbolFrom("
"&$_op, $_self.cast<::mlir::FlatSymbolRefAttr>().getValue()))"
>, "is module like">;
//===----------------------------------------------------------------------===//
// Control flow like-operations
//===----------------------------------------------------------------------===//
@ -480,3 +487,26 @@ def CoverOp : SVOp<"cover", [ProceduralOp]> {
let results = (outs);
let assemblyFormat = "attr-dict $property `:` type($property)";
}
def BindOp : SVOp<"bind", []> {
let summary = "indirect instantiation statement";
let description = [{
Indirectly instantiate a module in the context of another module. This
operation pairs with rtl.instance which tracks all information except the
emission point for the bind. This requires that the parent module of the
bind exist in the IR. See 23.11 of SV 2017 spec.
}];
let arguments = (ins FlatSymbolRefAttr:$bind);
let results = (outs);
let verifier = "return ::verify$cppClass(*this);";
let assemblyFormat = [{ $bind attr-dict }];
let extraClassDeclaration = [{
/// Lookup the instance for the bind. This returns null on
/// invalid IR.
hw::InstanceOp getReferencedInstance();
}];
}

View File

@ -44,6 +44,8 @@ public:
ReadInterfaceSignalOp,
// Verification statements.
AssertOp, AssumeOp, CoverOp,
// Bind Statements
BindOp,
// Terminators.
TypeDeclTerminatorOp>([&](auto expr) -> ResultType {
return thisCast->visitSV(expr, args...);
@ -115,6 +117,9 @@ public:
HANDLE(AssumeOp, Unhandled);
HANDLE(CoverOp, Unhandled);
// Bind statements.
HANDLE(BindOp, Unhandled);
// Terminators.
HANDLE(TypeDeclTerminatorOp, Unhandled);
#undef HANDLE

View File

@ -44,6 +44,32 @@ LogicalResult sv::verifyInNonProceduralRegion(Operation *op) {
return failure();
}
/// Returns the operation registered with the given symbol name with the regions
/// of 'symbolTableOp'. recurse through nested regions which don't contain the
/// symboltable trait. Returns nullptr if no valid symbol was found.
static Operation *lookupSymbolInNested(Operation *symbolTableOp,
StringRef symbol) {
Region &region = symbolTableOp->getRegion(0);
if (region.empty())
return nullptr;
// Look for a symbol with the given name.
Identifier symbolNameId = Identifier::get(SymbolTable::getSymbolAttrName(),
symbolTableOp->getContext());
for (Block &block : region)
for (Operation &nestedOp : block) {
auto nameAttr = nestedOp.getAttrOfType<StringAttr>(symbolNameId);
if (nameAttr && nameAttr.getValue() == symbol)
return &nestedOp;
if (!nestedOp.hasTrait<OpTrait::SymbolTable>() &&
nestedOp.getNumRegions()) {
if (auto *nop = lookupSymbolInNested(&nestedOp, symbol))
return nop;
}
}
return nullptr;
}
//===----------------------------------------------------------------------===//
// ImplicitSSAName Custom Directive
//===----------------------------------------------------------------------===//
@ -965,6 +991,31 @@ LogicalResult PAssignOp::canonicalize(PAssignOp op, PatternRewriter &rewriter) {
return success();
}
//===----------------------------------------------------------------------===//
// BindOp
//===----------------------------------------------------------------------===//
hw::InstanceOp BindOp::getReferencedInstance() {
auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
if (!topLevelModuleOp)
return nullptr;
/// Lookup the instance for the symbol. This returns null on
/// invalid IR.
auto inst = lookupSymbolInNested(topLevelModuleOp, bind());
return dyn_cast_or_null<hw::InstanceOp>(inst);
}
/// Ensure that the symbol being instantiated exists and is an InterfaceOp.
static LogicalResult verifyBindOp(BindOp op) {
auto inst = op.getReferencedInstance();
if (!inst)
return op.emitError("Referenced instance doesn't exist");
if (!inst->getAttr("doNotPrint"))
return op.emitError("Referenced instance isn't marked as doNotPrint");
return success();
}
//===----------------------------------------------------------------------===//
// TableGen generated logic.
//===----------------------------------------------------------------------===//

View File

@ -160,3 +160,24 @@ hw.module @test1(%arg0: i1, %arg1: i1, %arg8: i8) {
// CHECK-NEXT: hw.output
hw.output
}
//CHECK-LABEL: sv.bind @a1
//CHECK-NEXT: sv.bind @b1
sv.bind @a1
sv.bind @b1
//CHECK-NEXT: hw.module.extern @ExternDestMod(%a: i1, %b: i2)
//CHECK-NEXT: hw.module @InternalDestMod(%a: i1, %b: i2) {
//CHECK-NEXT: hw.output
//CHECK-NEXT: }
hw.module.extern @ExternDestMod(%a: i1, %b: i2)
hw.module @InternalDestMod(%a: i1, %b: i2) {}
//CHECK-NEXT: hw.module @AB(%a: i1, %b: i2) {
//CHECK-NEXT: hw.instance "whatever" sym @a1 @ExternDestMod(%a, %b) {doNotPrint = 1 : i64} : (i1, i2) -> ()
//CHECK-NEXT: hw.instance "yo" sym @b1 @InternalDestMod(%a, %b) {doNotPrint = 1 : i64} : (i1, i2) -> ()
//CHECK-NEXT: hw.output
//CHECK-NEXT: }
hw.module @AB(%a: i1, %b: i2) -> () {
hw.instance "whatever" sym @a1 @ExternDestMod(%a, %b) {doNotPrint=1}: (i1, i2) -> ()
hw.instance "yo" sym @b1 @InternalDestMod(%a, %b) {doNotPrint=1} : (i1, i2) -> ()
hw.output
}

View File

@ -140,4 +140,17 @@ hw.module @Assume(%arg0: i1) {
hw.module @Cover(%arg0: i1) {
// expected-error @+1 {{sv.cover should be in a procedural region}}
sv.cover %arg0: i1
}
}
// -----
// expected-error @+1 {{Referenced instance doesn't exist}}
sv.bind @A
// -----
hw.module.extern @ExternDestMod()
hw.module @InternSrcMod() {
hw.instance "whatever" sym @A @ExternDestMod() : () -> ()
hw.output
}
// expected-error @+1 {{Referenced instance isn't marked as doNotPrint}}
sv.bind @A