hw.probe for late binding to nameless values (#2443)

This provides a way to refer to values without pre-binding a name. Thus the implementation of the device holding a value is unspecified, simply constrained such that at emission time, a namable entity will exist with the value in it. This will be used for bind and XMRs and in verbatim name-substitutions.
This commit is contained in:
Andrew Lenharth 2022-01-12 08:46:27 -06:00 committed by GitHub
parent 8cc1a1a514
commit 67a8d24ce8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 55 additions and 8 deletions

View File

@ -211,6 +211,6 @@ def ProbeOp : FIRRTLOp<"probe"> {
let arguments = (ins SymbolNameAttr:$inner_sym, Variadic<FIRRTLType>:$operands);
let results = (outs);
let assemblyFormat = "$inner_sym `(` $operands `)` attr-dict `:` type($operands)";
let assemblyFormat = "$inner_sym attr-dict ( `,` $operands^ `:` type($operands))?";
}

View File

@ -154,10 +154,10 @@ public:
auto *thisCast = static_cast<ConcreteType *>(this);
return TypeSwitch<Operation *, ResultType>(op)
.template Case<AttachOp, ConnectOp, PartialConnectOp, ForceOp, PrintFOp,
SkipOp, StopOp, WhenOp, AssertOp, AssumeOp, CoverOp>(
[&](auto opNode) -> ResultType {
return thisCast->visitStmt(opNode, args...);
})
SkipOp, StopOp, WhenOp, AssertOp, AssumeOp, CoverOp,
ProbeOp>([&](auto opNode) -> ResultType {
return thisCast->visitStmt(opNode, args...);
})
.Default([&](auto expr) -> ResultType {
return thisCast->visitInvalidStmt(op, args...);
});
@ -191,6 +191,8 @@ public:
HANDLE(AssertOp);
HANDLE(AssumeOp);
HANDLE(CoverOp);
HANDLE(ProbeOp);
#undef HANDLE
};

View File

@ -458,3 +458,20 @@ def GlobalRefOp : HWOp<"globalRef", [IsolatedFromAbove, Symbol]> {
let verifier = "return this->verifyGlobalRef();";
}
def ProbeOp : HWOp<"probe", []> {
let summary = "Probe values for use in remote references";
let description = [{
Captures values without binding to any accidental name. This allows
capturing names holding values of interest while allowing the name to
resolved only at emission time.
}];
let arguments = (ins SymbolNameAttr:$inner_sym,
Variadic<AnyType>:$operands);
let results = (outs);
let assemblyFormat = "$inner_sym attr-dict (`,` $operands^ `:` type($operands))?";
}

View File

@ -80,7 +80,7 @@ public:
ResultType dispatchStmtVisitor(Operation *op, ExtraArgs... args) {
auto *thisCast = static_cast<ConcreteType *>(this);
return TypeSwitch<Operation *, ResultType>(op)
.template Case<OutputOp, InstanceOp, TypeScopeOp, TypedeclOp>(
.template Case<ProbeOp, OutputOp, InstanceOp, TypeScopeOp, TypedeclOp>(
[&](auto expr) -> ResultType {
return thisCast->visitStmt(expr, args...);
})
@ -118,6 +118,7 @@ public:
}
// Basic nodes.
HANDLE(ProbeOp, Unhandled);
HANDLE(OutputOp, Unhandled);
HANDLE(InstanceOp, Unhandled);
HANDLE(TypeScopeOp, Unhandled);

View File

@ -2399,6 +2399,7 @@ private:
LogicalResult visitSV(ReleaseOp op);
LogicalResult visitSV(AliasOp op);
LogicalResult visitSV(InterfaceInstanceOp op);
LogicalResult visitStmt(ProbeOp op);
LogicalResult visitStmt(OutputOp op);
LogicalResult visitStmt(InstanceOp op);
LogicalResult visitStmt(TypeScopeOp op);
@ -3405,6 +3406,10 @@ LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
return success();
}
// Probes only exist to provide naming to values. They are handled in
// the naming prepass.
LogicalResult StmtEmitter::visitStmt(ProbeOp op) { return success(); }
// This may be called in the top-level, not just in an hw.module. Thus we can't
// use the name map to find expression names for arguments to the instance, nor
// do we need to emit subexpressions. Prepare pass, which has run for all

View File

@ -1419,6 +1419,7 @@ struct FIRRTLLowering : public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
LogicalResult visitStmt(AssumeOp op);
LogicalResult visitStmt(CoverOp op);
LogicalResult visitStmt(AttachOp op);
LogicalResult visitStmt(ProbeOp op);
private:
/// The module we're lowering into.
@ -3695,3 +3696,21 @@ LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
return success();
}
LogicalResult FIRRTLLowering::visitStmt(ProbeOp op) {
SmallVector<Value, 4> operands;
operands.reserve(op.operands().size());
for (auto operand : op.operands()) {
operands.push_back(getLoweredValue(operand));
if (!operands.back()) {
// If this is a zero bit operand, just pass a one bit zero.
if (!isZeroBitFIRRTLType(operand.getType()))
return failure();
operands.back() = getOrCreateIntConstant(1, 0);
}
}
builder.create<hw::ProbeOp>(op.inner_sym(), operands);
return success();
}

View File

@ -475,6 +475,7 @@ hw.module @bind_rename_port(%.io_req_ready.output: i1, %reset: i1, %clock: i1) {
hw.module @SiFive_MulDiv(%clock: i1, %reset: i1) -> (io_req_ready: i1) {
%false = hw.constant false
hw.instance "InvisibleBind_assert" sym @__ETC_SiFive_MulDiv_assert @bind_rename_port(".io_req_ready.output": %false: i1, reset: %reset: i1, clock: %clock: i1) -> () {doNotPrint = true}
hw.probe @__ETC_SiFive_MulDiv_assert, %false, %reset, %clock: i1,i1,i1
hw.output %false : i1
}

View File

@ -581,6 +581,8 @@ firrtl.circuit "Simple" attributes {annotations = [{class =
}
firrtl.module @bar(in %io_cpu_flush: !firrtl.uint<1>) {
// CHECK: hw.probe @baz, %io_cpu_flush, %io_cpu_flush : i1, i1
firrtl.probe @baz, %io_cpu_flush, %io_cpu_flush : !firrtl.uint<1>, !firrtl.uint<1>
}
// CHECK-LABEL: hw.module @foo

View File

@ -165,8 +165,8 @@ firrtl.module @ProbeTest(in %in1 : !firrtl.uint<2>, in %in2 : !firrtl.uint<3>, o
firrtl.connect %w1, %w2 : !firrtl.uint<4>, !firrtl.uint<4>
firrtl.connect %out3, %in2 : !firrtl.uint<3>, !firrtl.uint<3>
%someNode = firrtl.node %in1 : !firrtl.uint<2>
// CHECK: firrtl.probe @foobar(%in1, %in2, %out3, %w1, %[[TMP3]], %someNode) : !firrtl.uint<2>, !firrtl.uint<3>, !firrtl.uint<3>, !firrtl.uint<4>, !firrtl.uint<4>, !firrtl.uint<2>
firrtl.probe @foobar (%in1, %in2, %out3, %w1, %w2, %someNode) : !firrtl.uint<2>, !firrtl.uint<3>, !firrtl.uint<3>, !firrtl.uint<4>, !firrtl.uint<4>, !firrtl.uint<2>
// CHECK: firrtl.probe @foobar, %in1, %in2, %out3, %w1, %[[TMP3]], %someNode : !firrtl.uint<2>, !firrtl.uint<3>, !firrtl.uint<3>, !firrtl.uint<4>, !firrtl.uint<4>, !firrtl.uint<2>
firrtl.probe @foobar, %in1, %in2, %out3, %w1, %w2, %someNode : !firrtl.uint<2>, !firrtl.uint<3>, !firrtl.uint<3>, !firrtl.uint<4>, !firrtl.uint<4>, !firrtl.uint<2>
}
}