CIRCT 24.0.0git
Loading...
Searching...
No Matches
ProbeToSV.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
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"
24
25#define DEBUG_TYPE "lower-probe-to-sv"
26
27namespace circt {
28#define GEN_PASS_DEF_LOWERPROBETOSV
29#include "circt/Conversion/Passes.h.inc"
30} // namespace circt
31
32using namespace circt;
33using namespace mlir;
34
35namespace {
36
37struct ReadResolution {
38 probe::ReadOp read;
39 probe::SendOp send;
40 hw::InstanceOp instance;
41};
42
43/// Return true if `type` is, or recursively contains, a Probe ref.
44bool containsProbeRef(Type type) {
45 bool found = false;
46 type.walk([&](Type nested) {
47 if (isa<probe::RefType>(nested))
48 found = true;
49 });
50 return found;
51}
52
53/// Add a field-zero inner symbol to `op`, or return its existing one.
54StringAttr getOrAddInnerSym(hw::InnerSymbolOpInterface op,
56 hw::HWModuleOp module, StringRef nameHint) {
57 auto oldAttr = op.getInnerSymAttr();
58 if (oldAttr)
59 if (auto name = oldAttr.getSymName())
60 return name;
61
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));
66 if (oldAttr)
67 llvm::append_range(properties, oldAttr.getProps());
68 op.setInnerSymbolAttr(hw::InnerSymAttr::get(op.getContext(), properties));
69 return name;
70}
71
72/// Remove a Probe output from a module and its instances without replacing it
73/// with any physical port.
74class EraseProbeOutput : public hw::PortConversion {
75public:
77
78 void mapInputSignals(OpBuilder &, Operation *, Value,
79 SmallVectorImpl<Value> &, ArrayRef<Backedge>) override {
80 llvm_unreachable("Probe input ports must fail validation");
81 }
82
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");
87 }
88
89private:
90 void buildInputSignals() override {
91 llvm_unreachable("Probe input ports must fail validation");
92 }
93 void buildOutputSignals() override {}
94};
95
96class ProbePortConversionBuilder : public hw::PortConversionBuilder {
97public:
99
100 FailureOr<std::unique_ptr<hw::PortConversion>>
101 build(hw::PortInfo port) override {
102 if (port.isOutput() && isa<probe::RefType>(port.type))
103 return {std::make_unique<EraseProbeOutput>(converter, port)};
104 return PortConversionBuilder::build(port);
105 }
106};
107
108class LowerProbeToSVPass
109 : public circt::impl::LowerProbeToSVBase<LowerProbeToSVPass> {
110public:
111 LowerProbeToSVPass() = default;
112 LowerProbeToSVPass(const LowerProbeToSVPass &pass) : Base(pass) {}
113
114 void runOnOperation() override;
115
116private:
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;
123 };
124
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);
132
133 hw::InnerRefAttr getOrCreateSourceRef(
134 probe::SendOp send,
135 hw::InnerSymbolNamespaceCollection &innerSymbolNamespaces,
136 LoweringState &state);
137 hw::InnerRefAttr getOrCreateInstanceRef(
138 hw::InstanceOp instance,
139 hw::InnerSymbolNamespaceCollection &innerSymbolNamespaces);
140};
141
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 "
152 "ports");
153 if (port.isOutput())
154 ++outputIndex;
155 continue;
156 }
157
158 if (!concreteModule)
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");
164
165 auto output =
166 cast<hw::OutputOp>(concreteModule.getBodyBlock()->getTerminator());
167 auto send = output.getOperand(outputIndex).getDefiningOp<probe::SendOp>();
168 if (!send)
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;
173 ++outputIndex;
174 }
175 if (hasProbeOutput)
176 state.modulesWithProbeOutputs.push_back(cast<hw::HWModuleOp>(*module));
177 return success();
178}
179
180FailureOr<ReadResolution>
181LowerProbeToSVPass::resolveRead(probe::ReadOp read, LoweringState &state) {
182 Value input = read.getInput();
183
184 if (auto send = input.getDefiningOp<probe::SendOp>())
185 return ReadResolution{read, send, {}};
186
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");
193
194 auto *referenced = state.symbolTables.lookupNearestSymbolFrom(
195 instance, instance.getModuleNameAttr());
196 auto childModule = cast<hw::HWModuleOp>(referenced);
197
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};
202}
203
204LogicalResult LowerProbeToSVPass::validateProbeUse(Operation *op) {
205 bool hasProbeValue = llvm::any_of(op->getOperands(), [](Value value) {
206 return containsProbeRef(value.getType());
207 });
208 hasProbeValue |= llvm::any_of(op->getResults(), [](Value value) {
209 return containsProbeRef(value.getType());
210 });
211 if (!hasProbeValue ||
212 isa<probe::SendOp, probe::ReadOp, hw::OutputOp, hw::InstanceOp>(op))
213 return success();
214
215 op->emitOpError(
216 "the Probe dialect only permits Probe refs to flow through "
217 "probe.send, probe.read, hw.output, and direct hw.instance results");
218 return failure();
219}
220
221LogicalResult LowerProbeToSVPass::validate(LoweringState &state) {
222 auto circuit = getOperation();
223
224 for (auto module : circuit.getOps<hw::HWModuleLike>())
225 if (failed(validateModulePorts(module, state)))
226 return failure();
227
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);
235
236 if (failed(validateProbeUse(op)))
237 return WalkResult::interrupt();
238 return WalkResult::advance();
239 });
240 if (walkResult.wasInterrupted())
241 return failure();
242 }
243
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 ")
249 << payloadType;
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 ")
253 << payloadType;
254 }
255
256 for (auto read : readOps) {
257 auto resolution = resolveRead(read, state);
258 if (failed(resolution))
259 return failure();
260 state.resolutions.push_back(*resolution);
261 }
262 return success();
263}
264
265hw::InnerRefAttr LowerProbeToSVPass::getOrCreateSourceRef(
266 probe::SendOp send,
267 hw::InnerSymbolNamespaceCollection &innerSymbolNamespaces,
268 LoweringState &state) {
269 auto [it, inserted] = state.sourceRefs.try_emplace(send.getOperation());
270 if (!inserted)
271 return it->second;
272
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);
280 return it->second;
281}
282
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());
288 auto name = getOrAddInnerSym(innerSym, innerSymbolNamespaces, module,
289 instance.getInstanceName());
290 return hw::InnerRefAttr::get(module.getModuleNameAttr(), name);
291}
292
293LogicalResult LowerProbeToSVPass::rewrite(LoweringState &state) {
294 auto circuit = getOperation();
295 Namespace circuitNamespace;
296 circuitNamespace.add(circuit);
297 hw::HierPathCache pathCache(
298 &circuitNamespace,
299 OpBuilder::InsertPoint(circuit.getBody(), circuit.getBody()->begin()));
300 hw::InnerSymbolNamespaceCollection innerSymbolNamespaces;
301
302 for (auto resolution : state.resolutions) {
303 SmallVector<Attribute> path;
304 if (resolution.instance)
305 path.push_back(
306 getOrCreateInstanceRef(resolution.instance, innerSymbolNamespaces));
307 path.push_back(
308 getOrCreateSourceRef(resolution.send, innerSymbolNamespaces, state));
309
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(""));
317 auto value = sv::ReadInOutOp::create(builder, xmr);
318 resolution.read.replaceAllUsesWith(value.getResult());
319 resolution.read.erase();
320 }
321
322 // Remove Probe output ports and update all corresponding instances.
323 auto &instanceGraph = getAnalysis<hw::InstanceGraph>();
324 for (auto module : state.modulesWithProbeOutputs)
325 if (failed(
326 hw::PortConverter<ProbePortConversionBuilder>(instanceGraph, module)
327 .run()))
328 return failure();
329
330 for (auto send : llvm::reverse(state.sendOps))
331 send.erase();
332 return success();
333}
334
335void LowerProbeToSVPass::runOnOperation() {
336 LoweringState state;
337 if (failed(validate(state)))
338 return signalPassFailure();
339 if (failed(rewrite(state)))
340 signalPassFailure();
341}
342
343} // namespace
assert(baseType &&"element must be base type")
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
void add(mlir::ModuleOp module)
Definition Namespace.h:48
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)
create(value)
Definition sv.py:151
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)
read(addr)
Definition xrt_cosim.py:23
InnerSymbolNamespace & get(Operation *op)
mlir::Type type
Definition HWTypes.h:34