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"
63class ProbeVisitor :
public FIRRTLVisitor<ProbeVisitor, LogicalResult> {
65 ProbeVisitor(hw::InnerRefNamespace &irn) : irn(irn) {}
68 LogicalResult visit(FModuleLike mod);
70 using FIRRTLVisitor<ProbeVisitor, LogicalResult>::visitDecl;
71 using FIRRTLVisitor<ProbeVisitor, LogicalResult>::visitExpr;
72 using FIRRTLVisitor<ProbeVisitor, LogicalResult>::visitStmt;
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;
206LogicalResult ProbeVisitor::visit(FModuleLike mod) {
210 SmallVector<std::pair<size_t, WireOp>> wires;
212 auto portTypes = mod.getPortTypes();
213 auto portLocs = mod.getPortLocationsAttr().getAsRange<Location>();
214 SmallVector<Attribute> newPortTypes;
216 wires.reserve(portTypes.size());
217 newPortTypes.reserve(portTypes.size());
219 bool portsToChange =
false;
220 for (
auto [idx, typeAttr, loc] : llvm::enumerate(portTypes, portLocs)) {
221 auto type = cast<TypeAttr>(typeAttr);
225 auto newType = *conv;
228 portsToChange =
true;
229 newPortTypes.push_back(TypeAttr::get(newType));
231 auto builder = OpBuilder::atBlockBegin(block);
232 wires.emplace_back(idx, WireOp::create(builder, loc, newType));
233 probeToHWMap[block->getArgument(idx)] = wires.back().second.getData();
236 newPortTypes.push_back(type);
242 ->walk<mlir::WalkOrder::PreOrder>(
243 [&](Operation *op) -> WalkResult {
return dispatchVisitor(op); })
249 mod.setPortTypesAttr(ArrayAttr::get(mod->getContext(), newPortTypes));
253 for (
auto [arg, typeAttr] :
254 llvm::zip_equal(block->getArguments(), newPortTypes))
255 arg.setType(cast<TypeAttr>(typeAttr).getValue());
258 for (
auto [idx, wire] : wires) {
259 auto arg = block->getArgument(idx);
260 wire.getData().replaceAllUsesWith(arg);
267 for (
auto *op : llvm::reverse(toDelete))
271 for (
auto fop : forceables)
281LogicalResult ProbeVisitor::visitUnhandledOp(Operation *op) {
282 auto checkType = [&](
auto type) ->
bool {
291 op->emitError(
"unhandled operation needs conversion of type ")
292 << type <<
" to " << *newType;
296 return success(llvm::none_of(op->getOperandTypes(), checkType) &&
297 llvm::none_of(op->getResultTypes(), checkType));
304ProbeVisitor::visitMemoryDebugPortOp(chirrtl::MemoryDebugPortOp op) {
305 auto conv =
convertType(op.getResult().getType(), op.getLoc());
316 auto mem = op.getMemory().getDefiningOp<chirrtl::CombMemOp>();
321 for (
auto *portOp : mem.getResult().getUsers()) {
322 for (
auto result : portOp->getResults()) {
323 for (
auto *user : result.getUsers()) {
324 auto accessOp = dyn_cast<chirrtl::MemoryPortAccessOp>(user);
327 auto newClock = accessOp.getClock();
328 if (clock && clock != newClock)
329 return mem.emitOpError(
330 "has different clocks on different ports (this is ambiguous "
331 "when compiling without reference types)");
337 return mem->emitOpError(
338 "does not have an access port to determine a clock connection (this "
339 "is necessary when compiling without reference types)");
342 SmallVector<Value>
data;
343 ImplicitLocOpBuilder builder(op.getLoc(), op);
347 builder.setInsertionPointToEnd(mem->getBlock());
348 Type uintType = builder.getType<UIntType>();
349 for (uint64_t i = 0, e = mem.getType().getNumElements(); i != e; ++i) {
350 auto port = chirrtl::MemoryPortOp::create(
351 builder, mem.getType().getElementType(),
352 chirrtl::CMemoryPortType::get(builder.getContext()), mem.getResult(),
353 MemDirAttr::Read, builder.getStringAttr(
"memTap_" + Twine(i)),
354 builder.getArrayAttr({}));
355 chirrtl::MemoryPortAccessOp::create(
356 builder, port.getPort(),
357 ConstantOp::create(builder, uintType, APSInt::getUnsigned(i)), clock);
358 data.push_back(port.getData());
362 assert(vectype == FVectorType::get(mem.getType().getElementType(),
363 mem.getType().getNumElements()));
364 auto vecData = VectorCreateOp::create(builder, vectype,
data);
371 builder.setInsertionPoint(mem);
372 auto wire = WireOp::create(builder, vectype);
373 builder.setInsertionPointToEnd(mem->getBlock());
375 probeToHWMap[op.getResult()] = wire.getData();
376 toDelete.push_back(op);
384LogicalResult ProbeVisitor::visitDecl(MemOp op) {
386 SmallVector<Type> newTypes;
387 auto needsConv = mapRange(op->getResultTypes(), op->getLoc(), newTypes);
388 if (failed(needsConv))
393 return op.emitError(
"memory has unsupported debug port (memtap)");
396LogicalResult ProbeVisitor::visitDecl(WireOp op) {
397 if (op.isForceable())
398 return visitActiveForceableDecl(op);
400 auto conv =
convertType(op.getDataRaw().getType(), op.getLoc());
408 ImplicitLocOpBuilder builder(op.getLoc(), op);
409 auto cloned = cast<WireOp>(builder.clone(*op));
410 cloned->getOpResults().front().setType(type);
411 probeToHWMap[op.getDataRaw()] = cloned.getData();
412 toDelete.push_back(op);
416LogicalResult ProbeVisitor::visitActiveForceableDecl(Forceable fop) {
417 assert(fop.isForceable() &&
"must be called on active forceables");
419 auto data = fop.getData();
420 auto conv = mapType(fop.getDataRef().getType(), fop.getLoc());
423 auto newType = *conv;
424 forceables.push_back(fop);
426 assert(newType ==
data.getType().getPassiveType());
427 if (newType !=
data.getType()) {
428 ImplicitLocOpBuilder builder(fop.getLoc(), fop);
429 builder.setInsertionPointAfterValue(
data);
430 auto wire = WireOp::create(builder, newType);
432 data = wire.getData();
434 probeToHWMap[fop.getDataRef()] =
data;
438LogicalResult ProbeVisitor::visitInstanceLike(Operation *op) {
439 SmallVector<Type> newTypes;
440 auto needsConv = mapRange(op->getResultTypes(), op->getLoc(), newTypes);
441 if (failed(needsConv))
448 ImplicitLocOpBuilder builder(op->getLoc(), op);
449 auto *newInst = builder.clone(*op);
450 for (
auto [oldResult, newResult, newType] :
451 llvm::zip_equal(op->getOpResults(), newInst->getOpResults(), newTypes)) {
452 if (newType == oldResult.getType()) {
453 oldResult.replaceAllUsesWith(newResult);
457 newResult.setType(newType);
458 probeToHWMap[oldResult] = newResult;
461 toDelete.push_back(op);
469LogicalResult ProbeVisitor::visitStmt(RefDefineOp op) {
474 auto newDest = probeToHWMap.at(op.getDest());
475 auto newSrc = probeToHWMap.at(op.getSrc());
479 assert(!isa<BlockArgument>(newDest));
480 auto *destDefiningOp = newDest.getDefiningOp();
482 if (!newSrc.getParentBlock()->findAncestorOpInBlock(*destDefiningOp)) {
484 auto diag = op.emitError(
"unable to convert to equivalent connect");
485 diag.attachNote(op.getDest().getLoc()) <<
"destination here";
486 diag.attachNote(op.getSrc().getLoc()) <<
"source here";
490 auto *destBlock = newDest.getParentBlock();
491 auto builder = ImplicitLocOpBuilder::atBlockEnd(op.getLoc(), destBlock);
493 toDelete.push_back(op);
497LogicalResult ProbeVisitor::visitExpr(RWProbeOp op) {
500 auto conv = mapType(op.getType(), op.getLoc());
503 auto newType = *conv;
504 toDelete.push_back(op);
506 auto ist = irn.
lookup(op.getTarget());
510 ImplicitLocOpBuilder builder(op.getLoc(), op);
511 builder.setInsertionPointAfterValue(ref.getValue());
513 assert(cast<FIRRTLBaseType>(
data.getType()).getPassiveType() ==
514 op.getType().getType());
515 if (newType !=
data.getType()) {
516 auto wire = WireOp::create(builder, newType);
518 data = wire.getData();
520 probeToHWMap[op.getResult()] =
data;
524LogicalResult ProbeVisitor::visitExpr(RefCastOp op) {
525 auto input = probeToHWMap.at(op.getInput());
533 auto conv = mapType(op.getResult().getType(), op.getLoc());
536 auto newType = *conv;
538 ImplicitLocOpBuilder builder(op.getLoc(), op);
539 builder.setInsertionPointAfterValue(input);
540 auto wire = WireOp::create(builder, newType);
542 probeToHWMap[op.getResult()] = wire.getData();
543 toDelete.push_back(op);
547LogicalResult ProbeVisitor::visitExpr(RefSendOp op) {
548 auto conv = mapType(op.getResult().getType(), op.getLoc());
551 auto newType = *conv;
552 toDelete.push_back(op);
555 if (newType == op.getBase().getType()) {
556 probeToHWMap[op.getResult()] = op.getBase();
562 assert(newType == op.getBase().getType().getPassiveType());
563 ImplicitLocOpBuilder builder(op.getLoc(), op);
564 builder.setInsertionPointAfterValue(op.getBase());
565 auto wire = WireOp::create(builder, newType);
566 emitConnect(builder, wire.getData(), op.getBase());
567 probeToHWMap[op.getResult()] = wire.getData();
571LogicalResult ProbeVisitor::visitExpr(RefResolveOp op) {
573 auto val = probeToHWMap.at(op.getRef());
574 op.replaceAllUsesWith(val);
575 toDelete.push_back(op);
579LogicalResult ProbeVisitor::visitExpr(RefSubOp op) {
581 auto val = probeToHWMap.at(op.getInput());
583 ImplicitLocOpBuilder builder(op.getLoc(), op);
584 builder.setInsertionPointAfterValue(op.getInput());
587 probeToHWMap[op.getResult()] = newVal;
588 toDelete.push_back(op);
597struct ProbesToSignalsPass
598 :
public circt::firrtl::impl::ProbesToSignalsBase<ProbesToSignalsPass> {
599 ProbesToSignalsPass() =
default;
600 void runOnOperation()
override;
604void ProbesToSignalsPass::runOnOperation() {
607 SmallVector<Operation *, 0> ops(getOperation().getOps<FModuleLike>());
609 hw::InnerRefNamespace irn{getAnalysis<SymbolTable>(),
610 getAnalysis<hw::InnerSymbolTableCollection>()};
612 auto result = failableParallelForEach(&getContext(), ops, [&](Operation *op) {
613 ProbeVisitor visitor(irn);
614 return visitor.visit(cast<FModuleLike>(op));
assert(baseType &&"element must be base type")
static FIRRTLBaseType convertType(FIRRTLBaseType type)
Returns null type if no conversion is needed.
false return failure()
Parse an optional keyword or string and set instance into 'result'.
static Block * getBodyBlock(FModuleLike mod)
#define CIRCT_DEBUG_SCOPED_PASS_LOGGER(PASS)
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
Forceable replaceWithNewForceability(Forceable op, bool forceable, ::mlir::PatternRewriter *rewriter=nullptr)
Replace a Forceable op with equivalent, changing whether forceable.
BaseTy type_cast(Type type)
FieldRef getFieldRefForTarget(const hw::InnerSymTarget &ist)
Get FieldRef pointing to the specified inner symbol target, which must be valid.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs, bool warnOnTruncation=false)
Emit a connect between two values.
Value getValueByFieldID(ImplicitLocOpBuilder builder, Value value, unsigned fieldID)
This gets the value targeted by a field id.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
InnerSymTarget lookup(hw::InnerRefAttr inner) const
Resolve the InnerRef to its target within this namespace, returning empty target if no such name exis...