41 #include "mlir/IR/Threading.h"
42 #include "mlir/Transforms/DialectConversion.h"
43 #include "llvm/ADT/STLExtras.h"
45 #define DEBUG_TYPE "firrtl-probes-to-signals"
49 #define GEN_PASS_DEF_PROBESTOSIGNALS
50 #include "circt/Dialect/FIRRTL/Passes.h.inc"
54 using namespace circt;
55 using namespace firrtl;
63 class ProbeVisitor :
public FIRRTLVisitor<ProbeVisitor, LogicalResult> {
65 ProbeVisitor(hw::InnerRefNamespace &irn) : irn(irn) {}
68 LogicalResult visit(FModuleLike mod);
79 static FailureOr<Type>
convertType(Type type, Location loc) {
80 auto err = [type, loc](
const Twine &message) {
81 return mlir::emitError(loc, message) <<
", cannot convert type " << type;
83 if (isa<OpenBundleType, OpenVectorType>(type))
84 return err(
"open aggregates not supported");
86 auto refType = dyn_cast<RefType>(type);
90 if (refType.getLayer())
91 return err(
"layer-colored probes not supported");
94 return refType.getType();
98 static FailureOr<Type> mapType(Type type, Location loc) {
102 return *newType ? *newType : type;
106 template <
typename R>
107 static FailureOr<bool> mapRange(R &&range, Location loc,
108 SmallVectorImpl<Type> &newTypes) {
109 newTypes.reserve(llvm::size(range));
111 bool anyConverted =
false;
112 for (
auto type : range) {
113 auto conv = mapType(type, loc);
116 newTypes.emplace_back(*conv);
117 anyConverted |= *conv != type;
123 LogicalResult visitMemoryDebugPortOp(chirrtl::MemoryDebugPortOp op);
127 LogicalResult visitInvalidOp(Operation *op) {
128 if (
auto dbgPortOp = dyn_cast<chirrtl::MemoryDebugPortOp>(op))
129 return visitMemoryDebugPortOp(dbgPortOp);
131 return visitUnhandledOp(op);
133 LogicalResult visitUnhandledOp(Operation *op);
136 LogicalResult visitUnhandledDecl(Operation *op) {
138 if (
auto fop = dyn_cast<Forceable>(op); fop && fop.isForceable())
139 return visitActiveForceableDecl(fop);
140 return visitUnhandledOp(op);
145 LogicalResult visitDecl(MemOp op);
146 LogicalResult visitDecl(WireOp op);
147 LogicalResult visitActiveForceableDecl(Forceable fop);
149 LogicalResult visitInstanceLike(Operation *op);
150 LogicalResult visitDecl(InstanceOp op) {
return visitInstanceLike(op); }
151 LogicalResult visitDecl(InstanceChoiceOp op) {
return visitInstanceLike(op); }
155 LogicalResult visitExpr(RWProbeOp op);
156 LogicalResult visitExpr(RefCastOp op);
157 LogicalResult visitExpr(RefResolveOp op);
158 LogicalResult visitExpr(RefSendOp op);
159 LogicalResult visitExpr(RefSubOp op);
161 LogicalResult visitStmt(RefDefineOp op);
164 LogicalResult visitStmt(RefForceOp op) {
165 return op.emitError(
"force not supported");
167 LogicalResult visitStmt(RefForceInitialOp op) {
168 return op.emitError(
"force_initial not supported");
170 LogicalResult visitStmt(RefReleaseOp op) {
171 return op.emitError(
"release not supported");
173 LogicalResult visitStmt(RefReleaseInitialOp op) {
174 return op.emitError(
"release_initial not supported");
179 DenseMap<Value, Value> probeToHWMap;
182 SmallVector<Forceable> forceables;
185 SmallVector<Operation *> toDelete;
188 hw::InnerRefNamespace &irn;
199 assert(mod->getNumRegions() == 1);
200 auto &blocks = mod->getRegion(0).getBlocks();
201 return !blocks.empty() ? &blocks.front() :
nullptr;
206 LogicalResult ProbeVisitor::visit(FModuleLike mod) {
208 if (
auto internalPaths = mod->getAttrOfType<ArrayAttr>(
"internalPaths"))
209 return mod.emitError(
"cannot convert module with internal path");
214 SmallVector<std::pair<size_t, WireOp>> wires;
216 auto portTypes = mod.getPortTypes();
217 auto portLocs = mod.getPortLocationsAttr().getAsRange<Location>();
218 SmallVector<Attribute> newPortTypes;
220 wires.reserve(portTypes.size());
221 newPortTypes.reserve(portTypes.size());
223 bool portsToChange =
false;
224 for (
auto [idx, typeAttr, loc] : llvm::enumerate(portTypes, portLocs)) {
225 auto type = cast<TypeAttr>(typeAttr);
229 auto newType = *conv;
232 portsToChange =
true;
235 auto builder = OpBuilder::atBlockBegin(block);
236 wires.emplace_back(idx, builder.create<WireOp>(loc, newType));
237 probeToHWMap[block->getArgument(idx)] = wires.back().second.getData();
240 newPortTypes.push_back(type);
246 ->walk<mlir::WalkOrder::PreOrder>(
247 [&](Operation *op) -> WalkResult {
return dispatchVisitor(op); })
253 mod.setPortTypesAttr(
ArrayAttr::get(mod->getContext(), newPortTypes));
257 for (
auto [arg, typeAttr] :
258 llvm::zip_equal(block->getArguments(), newPortTypes))
259 arg.setType(cast<TypeAttr>(typeAttr).getValue());
262 for (
auto [idx, wire] : wires) {
263 auto arg = block->getArgument(idx);
264 wire.getData().replaceAllUsesWith(arg);
271 for (
auto *op : llvm::reverse(toDelete))
275 for (
auto fop : forceables)
285 LogicalResult ProbeVisitor::visitUnhandledOp(Operation *op) {
286 auto checkType = [&](
auto type) ->
bool {
295 op->emitError(
"unhandled operation needs conversion of type ")
296 << type <<
" to " << *newType;
300 return success(llvm::none_of(op->getOperandTypes(), checkType) &&
301 llvm::none_of(op->getResultTypes(), checkType));
308 ProbeVisitor::visitMemoryDebugPortOp(chirrtl::MemoryDebugPortOp op) {
309 auto conv =
convertType(op.getResult().getType(), op.getLoc());
315 auto vectype = type_cast<FVectorType>(type);
320 auto mem = op.getMemory().getDefiningOp<chirrtl::CombMemOp>();
325 for (
auto *portOp : mem.getResult().getUsers()) {
326 for (
auto result : portOp->getResults()) {
327 for (
auto *user : result.getUsers()) {
328 auto accessOp = dyn_cast<chirrtl::MemoryPortAccessOp>(user);
331 auto newClock = accessOp.getClock();
332 if (clock && clock != newClock)
333 return mem.emitOpError(
334 "has different clocks on different ports (this is ambiguous "
335 "when compiling without reference types)");
341 return mem->emitOpError(
342 "does not have an access port to determine a clock connection (this "
343 "is necessary when compiling without reference types)");
346 SmallVector<Value>
data;
347 ImplicitLocOpBuilder builder(op.getLoc(), op);
351 builder.setInsertionPointToEnd(mem->getBlock());
352 Type uintType = builder.getType<UIntType>();
353 for (uint64_t i = 0, e = mem.getType().getNumElements(); i != e; ++i) {
354 auto port = builder.create<chirrtl::MemoryPortOp>(
355 mem.getType().getElementType(),
357 MemDirAttr::Read, builder.getStringAttr(
"memTap_" + Twine(i)),
358 builder.getArrayAttr({}));
359 builder.create<chirrtl::MemoryPortAccessOp>(
361 builder.create<ConstantOp>(uintType, APSInt::getUnsigned(i)), clock);
362 data.push_back(port.getData());
367 mem.getType().getNumElements()));
368 auto vecData = builder.create<VectorCreateOp>(vectype,
data);
375 builder.setInsertionPoint(mem);
376 auto wire = builder.create<WireOp>(vectype);
377 builder.setInsertionPointToEnd(mem->getBlock());
379 probeToHWMap[op.getResult()] = wire.getData();
380 toDelete.push_back(op);
388 LogicalResult ProbeVisitor::visitDecl(MemOp op) {
390 SmallVector<Type> newTypes;
391 auto needsConv = mapRange(op->getResultTypes(), op->getLoc(), newTypes);
392 if (failed(needsConv))
397 return op.emitError(
"memory has unsupported debug port (memtap)");
400 LogicalResult ProbeVisitor::visitDecl(WireOp op) {
401 if (op.isForceable())
402 return visitActiveForceableDecl(op);
404 auto conv =
convertType(op.getDataRaw().getType(), op.getLoc());
412 ImplicitLocOpBuilder builder(op.getLoc(), op);
413 auto cloned = cast<WireOp>(builder.clone(*op));
414 cloned->getOpResults().front().setType(type);
415 probeToHWMap[op.getDataRaw()] = cloned.getData();
416 toDelete.push_back(op);
420 LogicalResult ProbeVisitor::visitActiveForceableDecl(Forceable fop) {
421 assert(fop.isForceable() &&
"must be called on active forceables");
423 auto data = fop.getData();
424 auto conv = mapType(fop.getDataRef().getType(), fop.getLoc());
427 auto newType = *conv;
428 forceables.push_back(fop);
430 assert(newType ==
data.getType().getPassiveType());
431 if (newType !=
data.getType()) {
432 ImplicitLocOpBuilder builder(fop.getLoc(), fop);
433 builder.setInsertionPointAfterValue(data);
434 auto wire = builder.create<WireOp>(newType);
436 data = wire.getData();
438 probeToHWMap[fop.getDataRef()] =
data;
442 LogicalResult ProbeVisitor::visitInstanceLike(Operation *op) {
443 SmallVector<Type> newTypes;
444 auto needsConv = mapRange(op->getResultTypes(), op->getLoc(), newTypes);
445 if (failed(needsConv))
452 ImplicitLocOpBuilder builder(op->getLoc(), op);
453 auto *newInst = builder.clone(*op);
454 for (
auto [oldResult, newResult, newType] :
455 llvm::zip_equal(op->getOpResults(), newInst->getOpResults(), newTypes)) {
456 if (newType == oldResult.getType()) {
457 oldResult.replaceAllUsesWith(newResult);
461 newResult.setType(newType);
462 probeToHWMap[oldResult] = newResult;
465 toDelete.push_back(op);
473 LogicalResult ProbeVisitor::visitStmt(RefDefineOp op) {
478 auto newDest = probeToHWMap.at(op.getDest());
479 auto newSrc = probeToHWMap.at(op.getSrc());
483 assert(!isa<BlockArgument>(newDest));
484 auto *destDefiningOp = newDest.getDefiningOp();
486 if (!newSrc.getParentBlock()->findAncestorOpInBlock(*destDefiningOp)) {
488 auto diag = op.emitError(
"unable to convert to equivalent connect");
489 diag.attachNote(op.getDest().getLoc()) <<
"destination here";
490 diag.attachNote(op.getSrc().getLoc()) <<
"source here";
494 auto *destBlock = newDest.getParentBlock();
495 auto builder = ImplicitLocOpBuilder::atBlockEnd(op.getLoc(), destBlock);
497 toDelete.push_back(op);
501 LogicalResult ProbeVisitor::visitExpr(RWProbeOp op) {
504 auto conv = mapType(op.getType(), op.getLoc());
507 auto newType = *conv;
508 toDelete.push_back(op);
510 auto ist = irn.lookup(op.getTarget());
514 ImplicitLocOpBuilder builder(op.getLoc(), op);
515 builder.setInsertionPointAfterValue(ref.getValue());
517 assert(cast<FIRRTLBaseType>(
data.getType()).getPassiveType() ==
518 op.getType().getType());
519 if (newType !=
data.getType()) {
520 auto wire = builder.create<WireOp>(newType);
522 data = wire.getData();
524 probeToHWMap[op.getResult()] =
data;
528 LogicalResult ProbeVisitor::visitExpr(RefCastOp op) {
529 auto input = probeToHWMap.at(op.getInput());
537 auto conv = mapType(op.getResult().getType(), op.getLoc());
540 auto newType = *conv;
542 ImplicitLocOpBuilder builder(op.getLoc(), op);
543 builder.setInsertionPointAfterValue(input);
544 auto wire = builder.create<WireOp>(newType);
546 probeToHWMap[op.getResult()] = wire.getData();
547 toDelete.push_back(op);
551 LogicalResult ProbeVisitor::visitExpr(RefSendOp op) {
552 auto conv = mapType(op.getResult().getType(), op.getLoc());
555 auto newType = *conv;
556 toDelete.push_back(op);
559 if (newType == op.getBase().getType()) {
560 probeToHWMap[op.getResult()] = op.getBase();
566 assert(newType == op.getBase().getType().getPassiveType());
567 ImplicitLocOpBuilder builder(op.getLoc(), op);
568 builder.setInsertionPointAfterValue(op.getBase());
569 auto wire = builder.create<WireOp>(newType);
570 emitConnect(builder, wire.getData(), op.getBase());
571 probeToHWMap[op.getResult()] = wire.getData();
575 LogicalResult ProbeVisitor::visitExpr(RefResolveOp op) {
577 auto val = probeToHWMap.at(op.getRef());
578 op.replaceAllUsesWith(val);
579 toDelete.push_back(op);
583 LogicalResult ProbeVisitor::visitExpr(RefSubOp op) {
585 auto val = probeToHWMap.at(op.getInput());
587 ImplicitLocOpBuilder builder(op.getLoc(), op);
588 builder.setInsertionPointAfterValue(op.getInput());
591 probeToHWMap[op.getResult()] = newVal;
592 toDelete.push_back(op);
601 struct ProbesToSignalsPass
602 :
public circt::firrtl::impl::ProbesToSignalsBase<ProbesToSignalsPass> {
603 ProbesToSignalsPass() =
default;
604 void runOnOperation()
override;
608 void ProbesToSignalsPass::runOnOperation() {
611 SmallVector<Operation *, 0> ops(getOperation().getOps<FModuleLike>());
613 hw::InnerRefNamespace irn{getAnalysis<SymbolTable>(),
614 getAnalysis<hw::InnerSymbolTableCollection>()};
616 auto result = failableParallelForEach(&getContext(), ops, [&](Operation *op) {
617 ProbeVisitor visitor(irn);
618 return visitor.visit(cast<FModuleLike>(op));
627 return std::make_unique<ProbesToSignalsPass>();
assert(baseType &&"element must be base type")
static FIRRTLBaseType convertType(FIRRTLBaseType type)
Returns null type if no conversion is needed.
static Block * getBodyBlock(FModuleLike mod)
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Forceable replaceWithNewForceability(Forceable op, bool forceable, ::mlir::PatternRewriter *rewriter=nullptr)
Replace a Forceable op with equivalent, changing whether forceable.
FieldRef getFieldRefForTarget(const hw::InnerSymTarget &ist)
Get FieldRef pointing to the specified inner symbol target, which must be valid.
Value getValueByFieldID(ImplicitLocOpBuilder builder, Value value, unsigned fieldID)
This gets the value targeted by a field id.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
std::unique_ptr< mlir::Pass > createProbesToSignalsPass()
This is the pass constructor.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.