mirror of https://github.com/llvm/circt.git
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:
parent
35bd44f920
commit
04cdf0a486
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ®ion = 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.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -141,3 +141,16 @@ 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
|
||||
|
|
Loading…
Reference in New Issue