19#include "mlir/IR/BuiltinOps.h"
20#include "mlir/IR/ImplicitLocOpBuilder.h"
21#include "mlir/IR/SymbolTable.h"
22#include "mlir/Pass/Pass.h"
23#include "llvm/ADT/DenseMap.h"
25#define DEBUG_TYPE "lower-probe-to-sv"
28#define GEN_PASS_DEF_LOWERPROBETOSV
29#include "circt/Conversion/Passes.h.inc"
37struct ReadResolution {
40 hw::InstanceOp instance;
44bool containsProbeRef(Type type) {
46 type.walk([&](Type nested) {
47 if (isa<probe::RefType>(nested))
57 auto oldAttr = op.getInnerSymAttr();
59 if (
auto name = oldAttr.getSymName())
62 auto &ns = namespaces.
get(module);
63 auto name = StringAttr::get(op.getContext(), ns.newName(nameHint));
64 SmallVector<hw::InnerSymPropertiesAttr> properties;
65 properties.push_back(hw::InnerSymPropertiesAttr::get(name));
67 llvm::append_range(properties, oldAttr.getProps());
68 op.setInnerSymbolAttr(hw::InnerSymAttr::get(op.getContext(), properties));
78 void mapInputSignals(OpBuilder &, Operation *, Value,
79 SmallVectorImpl<Value> &, ArrayRef<Backedge>)
override {
80 llvm_unreachable(
"Probe input ports must fail validation");
83 void mapOutputSignals(OpBuilder &, Operation *, Value instanceResult,
84 SmallVectorImpl<Value> &, ArrayRef<Backedge>)
override {
85 assert(instanceResult.use_empty() &&
86 "Probe instance result must have no uses before port conversion");
90 void buildInputSignals()
override {
91 llvm_unreachable(
"Probe input ports must fail validation");
93 void buildOutputSignals()
override {}
100 FailureOr<std::unique_ptr<hw::PortConversion>>
101 build(hw::PortInfo port)
override {
103 return {std::make_unique<EraseProbeOutput>(converter, port)};
108class LowerProbeToSVPass
109 :
public circt::impl::LowerProbeToSVBase<LowerProbeToSVPass> {
111 LowerProbeToSVPass() =
default;
112 LowerProbeToSVPass(
const LowerProbeToSVPass &pass) : Base(pass) {}
114 void runOnOperation()
override;
117 struct LoweringState {
118 SymbolTableCollection symbolTables;
119 SmallVector<ReadResolution> resolutions;
120 SmallVector<hw::HWModuleOp> modulesWithProbeOutputs;
121 SmallVector<probe::SendOp> sendOps;
122 DenseMap<Operation *, hw::InnerRefAttr> sourceRefs;
125 LogicalResult validate(LoweringState &state);
126 FailureOr<ReadResolution> resolveRead(probe::ReadOp read,
127 LoweringState &state);
128 LogicalResult validateModulePorts(hw::HWModuleLike module,
129 LoweringState &state);
130 LogicalResult validateProbeUse(Operation *op);
131 LogicalResult rewrite(LoweringState &state);
133 hw::InnerRefAttr getOrCreateSourceRef(
135 hw::InnerSymbolNamespaceCollection &innerSymbolNamespaces,
136 LoweringState &state);
137 hw::InnerRefAttr getOrCreateInstanceRef(
138 hw::InstanceOp instance,
139 hw::InnerSymbolNamespaceCollection &innerSymbolNamespaces);
142LogicalResult LowerProbeToSVPass::validateModulePorts(hw::HWModuleLike module,
143 LoweringState &state) {
144 unsigned outputIndex = 0;
145 bool hasProbeOutput =
false;
146 auto concreteModule = dyn_cast<hw::HWModuleOp>(*module);
147 for (
auto port : module.getPortList()) {
148 if (!isa<probe::RefType>(port.type)) {
149 if (containsProbeRef(port.type))
150 return module.emitOpError(
151 "the Probe dialect does not support nested Probe refs in module "
159 return module.emitOpError(
160 "Probe refs on external or generated module ports are not supported");
161 if (!module.isPrivate())
162 return module.emitOpError(
163 "Probe output ports on public modules are not supported");
166 cast<hw::OutputOp>(concreteModule.getBodyBlock()->getTerminator());
167 auto send = output.getOperand(outputIndex).getDefiningOp<probe::SendOp>();
169 return output.emitOpError(
170 "the Probe dialect requires a Probe output to be driven directly by "
171 "probe.send; multi-level forwarding is not supported");
172 hasProbeOutput =
true;
176 state.modulesWithProbeOutputs.push_back(cast<hw::HWModuleOp>(*module));
180FailureOr<ReadResolution>
181LowerProbeToSVPass::resolveRead(probe::ReadOp read, LoweringState &state) {
182 Value input =
read.getInput();
184 if (
auto send = input.getDefiningOp<probe::SendOp>())
185 return ReadResolution{
read, send, {}};
187 auto result = cast<OpResult>(input);
188 auto instance = cast<hw::InstanceOp>(result.getOwner());
189 if (instance.getDoNotPrint())
190 return read.emitOpError(
191 "Probe-to-SV lowering cannot create an XMR through an hw.instance "
192 "marked doNotPrint");
194 auto *referenced = state.symbolTables.lookupNearestSymbolFrom(
195 instance, instance.getModuleNameAttr());
196 auto childModule = cast<hw::HWModuleOp>(referenced);
198 auto output = cast<hw::OutputOp>(childModule.getBodyBlock()->getTerminator());
199 auto send = cast<probe::SendOp>(
200 output.getOperand(result.getResultNumber()).getDefiningOp());
201 return ReadResolution{
read, send, instance};
204LogicalResult LowerProbeToSVPass::validateProbeUse(Operation *op) {
205 bool hasProbeValue = llvm::any_of(op->getOperands(), [](Value value) {
206 return containsProbeRef(value.getType());
208 hasProbeValue |= llvm::any_of(op->getResults(), [](Value value) {
209 return containsProbeRef(value.getType());
211 if (!hasProbeValue ||
212 isa<probe::SendOp, probe::ReadOp, hw::OutputOp, hw::InstanceOp>(op))
216 "the Probe dialect only permits Probe refs to flow through "
217 "probe.send, probe.read, hw.output, and direct hw.instance results");
221LogicalResult LowerProbeToSVPass::validate(LoweringState &state) {
222 auto circuit = getOperation();
224 for (
auto module : circuit.getOps<hw::HWModuleLike>())
225 if (failed(validateModulePorts(module, state)))
228 SmallVector<probe::ReadOp> readOps;
229 for (
auto module : circuit.getOps<hw::HWModuleOp>()) {
230 auto walkResult =
module.walk([&](Operation *op) -> WalkResult {
231 if (auto send = dyn_cast<probe::SendOp>(op))
232 state.sendOps.push_back(send);
233 else if (
auto read = dyn_cast<probe::ReadOp>(op))
234 readOps.push_back(read);
236 if (failed(validateProbeUse(op)))
237 return WalkResult::interrupt();
238 return WalkResult::advance();
240 if (walkResult.wasInterrupted())
244 for (
auto send : state.sendOps) {
245 auto payloadType = send.getInput().getType();
246 if (!hw::isHWValueType(payloadType))
247 return send.emitOpError(
248 "Probe-to-SV lowering requires an HW value payload, but got ")
250 if (!sv::isValidNetElementType(payloadType))
251 return send.emitOpError(
"Probe-to-SV lowering requires a payload that "
252 "is valid as an sv.net element type, but got ")
256 for (
auto read : readOps) {
257 auto resolution = resolveRead(read, state);
258 if (failed(resolution))
260 state.resolutions.push_back(*resolution);
265hw::InnerRefAttr LowerProbeToSVPass::getOrCreateSourceRef(
267 hw::InnerSymbolNamespaceCollection &innerSymbolNamespaces,
268 LoweringState &state) {
269 auto [it, inserted] = state.sourceRefs.try_emplace(send.getOperation());
273 auto module = send->getParentOfType<hw::HWModuleOp>();
274 auto &ns = innerSymbolNamespaces.
get(module);
275 auto name = StringAttr::get(&getContext(), ns.newName(
"probe"));
276 ImplicitLocOpBuilder builder(send.getLoc(), send);
277 hw::WireOp::create(builder, send.getInput(), name,
278 hw::InnerSymAttr::get(name));
279 it->second = hw::InnerRefAttr::get(module.getModuleNameAttr(), name);
283hw::InnerRefAttr LowerProbeToSVPass::getOrCreateInstanceRef(
284 hw::InstanceOp instance,
285 hw::InnerSymbolNamespaceCollection &innerSymbolNamespaces) {
286 auto module = instance->getParentOfType<hw::HWModuleOp>();
287 auto innerSym = cast<hw::InnerSymbolOpInterface>(instance.getOperation());
289 instance.getInstanceName());
290 return hw::InnerRefAttr::get(module.getModuleNameAttr(), name);
293LogicalResult LowerProbeToSVPass::rewrite(LoweringState &state) {
294 auto circuit = getOperation();
295 Namespace circuitNamespace;
296 circuitNamespace.
add(circuit);
297 hw::HierPathCache pathCache(
299 OpBuilder::InsertPoint(circuit.getBody(), circuit.getBody()->begin()));
300 hw::InnerSymbolNamespaceCollection innerSymbolNamespaces;
302 for (
auto resolution : state.resolutions) {
303 SmallVector<Attribute> path;
304 if (resolution.instance)
306 getOrCreateInstanceRef(resolution.instance, innerSymbolNamespaces));
308 getOrCreateSourceRef(resolution.send, innerSymbolNamespaces, state));
310 ImplicitLocOpBuilder builder(resolution.read.getLoc(), resolution.read);
311 auto hierPath = pathCache.getOrCreatePath(builder.getArrayAttr(path),
312 resolution.read.getLoc());
313 auto ref = FlatSymbolRefAttr::get(hierPath.getSymNameAttr());
314 auto xmr = sv::XMRRefOp::create(
315 builder, sv::NetType::get(resolution.read.getResult().getType()), ref,
316 builder.getStringAttr(
""));
318 resolution.read.replaceAllUsesWith(value.getResult());
319 resolution.read.erase();
323 auto &instanceGraph = getAnalysis<hw::InstanceGraph>();
324 for (
auto module : state.modulesWithProbeOutputs)
326 hw::PortConverter<ProbePortConversionBuilder>(instanceGraph, module)
330 for (
auto send : llvm::reverse(state.sendOps))
335void LowerProbeToSVPass::runOnOperation() {
337 if (failed(validate(state)))
338 return signalPassFailure();
339 if (failed(rewrite(state)))
assert(baseType &&"element must be base type")
false return failure()
Parse an optional keyword or string and set instance into 'result'.
void add(mlir::ModuleOp module)
PortConversionBuilder(PortConverterImpl &converter)
virtual FailureOr< std::unique_ptr< PortConversion > > build(hw::PortInfo port)
Base class for the port conversion of a particular port.
PortConversion(PortConverterImpl &converter, hw::PortInfo origPort)
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, List[str] cmdline_args=sys.argv)
InnerSymbolNamespace & get(Operation *op)