19 #include "llvm/Support/Debug.h"
20 #include "llvm/Support/Mutex.h"
21 #include "llvm/Support/RWMutex.h"
23 #define DEBUG_TYPE "firrtl-lower-layers"
25 using namespace circt;
26 using namespace firrtl;
33 if (
auto result = dyn_cast<OpResult>(value))
34 return op->isAncestor(result.getOwner());
35 auto argument = cast<BlockArgument>(value);
36 return op->isAncestor(argument.getOwner()->getParentOp());
42 enum class ConnectKind {
61 DenseMap<hw::InnerRefAttr, std::pair<hw::InnerSymAttr, StringAttr>>;
67 static void appendName(StringRef name, SmallString<32> &output,
68 bool toLower =
false) {
76 auto i = output.size() - name.size();
77 output[i] = llvm::toLower(output[i]);
80 static void appendName(SymbolRefAttr name, SmallString<32> &output,
81 bool toLower =
false) {
82 appendName(name.getRootReference(), output, toLower);
83 for (
auto nested : name.getNestedReferences())
84 appendName(nested.getValue(), output, toLower);
90 SymbolRefAttr layerName) {
91 SmallString<32> result;
100 SmallString<32> result;
108 SymbolRefAttr layerName) {
109 SmallString<32> result;
110 result.append(
"layers");
113 result.append(
".sv");
124 FModuleOp buildNewModule(OpBuilder &
builder, Location location,
125 Twine namehint, SmallVectorImpl<PortInfo> &ports);
128 InnerRefMap runOnModuleLike(FModuleLike moduleLike);
131 void runOnModuleBody(FModuleOp moduleOp,
InnerRefMap &innerRefMap);
135 void removeLayersFromPorts(FModuleLike moduleLike);
138 void removeLayersFromValue(Value value);
142 void removeLayersFromRefCast(RefCastOp cast);
146 void maybeSetOutputDir(Operation *op);
150 void setOutputFile(Operation *op,
const Twine &filename);
153 void runOnOperation()
override;
178 SmallVectorImpl<PortInfo> &ports) {
179 llvm::sys::SmartScopedLock<true> instrumentationLock(*circuitMutex);
180 FModuleOp newModule =
builder.create<FModuleOp>(
181 location,
builder.getStringAttr(namehint),
184 maybeSetOutputDir(newModule);
185 SymbolTable::setSymbolVisibility(newModule, SymbolTable::Visibility::Private);
190 auto type = dyn_cast<RefType>(value.getType());
191 if (!type || !type.getLayer())
193 value.setType(type.removeLayer());
197 auto result = cast.getResult();
198 auto oldType = result.getType();
199 if (oldType.getLayer()) {
200 auto input = cast.getInput();
201 auto srcType = input.getType();
202 auto newType = oldType.removeLayer();
203 if (newType == srcType) {
204 result.replaceAllUsesWith(input);
208 result.setType(newType);
213 auto oldTypeAttrs = moduleLike.getPortTypesAttr();
214 SmallVector<Attribute> newTypeAttrs;
215 newTypeAttrs.reserve(oldTypeAttrs.size());
216 bool changed =
false;
218 for (
auto typeAttr : oldTypeAttrs.getAsRange<TypeAttr>()) {
219 if (
auto refType = dyn_cast<RefType>(typeAttr.getValue())) {
220 if (refType.getLayer()) {
225 newTypeAttrs.push_back(typeAttr);
231 moduleLike->setAttr(FModuleLike::getPortTypesAttrName(),
234 if (
auto moduleOp = dyn_cast<FModuleOp>(moduleLike.getOperation())) {
235 for (
auto arg : moduleOp.getBodyBlock()->getArguments())
236 removeLayersFromValue(arg);
242 llvm::dbgs() <<
"Module: " << moduleLike.getModuleName() <<
"\n";
243 llvm::dbgs() <<
" Examining Layer Blocks:\n";
248 TypeSwitch<Operation *, void>(moduleLike.getOperation())
249 .Case<FModuleOp>([&](
auto op) {
251 removeLayersFromPorts(op);
252 runOnModuleBody(op, innerRefMap);
254 .Case<FExtModuleOp, FIntModuleOp, FMemModuleOp>([&](
auto op) {
256 removeLayersFromPorts(op);
258 .Case<ClassOp, ExtClassOp>([](
auto) {})
259 .Default([](
auto) {
assert(0 &&
"unknown module-like op"); });
266 CircuitOp circuitOp = moduleOp->getParentOfType<CircuitOp>();
267 StringRef circuitName = circuitOp.getName();
268 hw::InnerSymbolNamespace ns(moduleOp);
273 DenseMap<InstanceOp, FModuleOp> createdInstances;
286 moduleOp.walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
288 if (
auto wire = dyn_cast<WireOp>(op)) {
289 removeLayersFromValue(wire.getResult());
290 return WalkResult::advance();
292 if (
auto sub = dyn_cast<RefSubOp>(op)) {
293 removeLayersFromValue(sub.getResult());
294 return WalkResult::advance();
296 if (
auto instance = dyn_cast<InstanceOp>(op)) {
297 instance.setLayers({});
298 for (
auto result : instance.getResults())
299 removeLayersFromValue(result);
300 return WalkResult::advance();
302 if (
auto cast = dyn_cast<RefCastOp>(op)) {
303 removeLayersFromRefCast(cast);
304 return WalkResult::advance();
307 auto layerBlock = dyn_cast<LayerBlockOp>(op);
309 return WalkResult::advance();
311 Block *body = layerBlock.getBody(0);
316 SmallVector<PortInfo> ports;
319 SmallVector<ConnectInfo> connectValues;
322 auto createInputPort = [&](Value operand, Location loc) {
328 auto type = operand.getType();
329 if (
auto refType = dyn_cast<RefType>(type))
330 type = refType.getType();
338 Value replacement = body->addArgument(type, loc);
339 if (isa<RefType>(operand.getType())) {
340 OpBuilder::InsertionGuard guard(
builder);
341 builder.setInsertionPointToStart(body);
342 replacement =
builder.create<RefSendOp>(loc, replacement);
344 operand.replaceUsesWithIf(replacement, [&](OpOperand &use) {
345 auto *user = use.getOwner();
346 if (!layerBlock->isAncestor(user))
348 if (
auto connectLike = dyn_cast<FConnectLike>(user)) {
349 if (use.getOperandNumber() == 0)
355 connectValues.push_back({operand, ConnectKind::NonRef});
361 auto getPortLoc = [&](Value port) -> Location {
363 if (
auto *destOp = port.getDefiningOp())
364 if (
auto instOp = dyn_cast<InstanceOp>(destOp)) {
365 auto modOpIt = createdInstances.find(instOp);
366 if (modOpIt != createdInstances.end()) {
367 auto portNum = port.cast<OpResult>().getResultNumber();
368 loc = modOpIt->getSecond().getPortLocation(portNum);
376 auto createOutputPort = [&](Value dest, Value src) {
377 auto loc = getPortLoc(dest);
378 auto portNum = ports.size();
382 if (
auto oldRef = dyn_cast<RefType>(dest.getType()))
386 type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType(),
391 Value replacement = body->addArgument(refType, loc);
392 if (isa<RefType>(dest.getType())) {
393 dest.replaceUsesWithIf(replacement, [&](OpOperand &use) {
394 auto *user = use.getOwner();
395 if (!layerBlock->isAncestor(user))
397 if (
auto connectLike = dyn_cast<FConnectLike>(user)) {
398 if (use.getOperandNumber() == 0)
403 connectValues.push_back({dest, ConnectKind::Ref});
406 connectValues.push_back({dest, ConnectKind::NonRef});
407 OpBuilder::InsertionGuard guard(
builder);
408 builder.setInsertionPointAfterValue(src);
410 loc, body->getArgument(portNum),
411 builder.create<RefSendOp>(loc, src)->getResult(0));
414 SmallVector<hw::InnerSymAttr> innerSyms;
415 for (
auto &op : llvm::make_early_inc_range(*body)) {
418 if (
auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
419 if (
auto innerSym = symOp.getInnerSymAttr())
420 innerSyms.push_back(innerSym);
432 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
434 if (!createdInstances.contains(instOp))
439 <<
" Found instance created from nested layer block:\n"
440 <<
" module: " << instOp.getModuleName() <<
"\n"
441 <<
" instance: " << instOp.getName() <<
"\n";
443 instOp->moveBefore(layerBlock);
447 if (
auto refSend = dyn_cast<RefSendOp>(op)) {
448 auto src = refSend.getBase();
450 createInputPort(src, op.getLoc());
454 if (
auto refCast = dyn_cast<RefCastOp>(op)) {
456 createInputPort(refCast.getInput(), op.getLoc());
460 if (
auto connect = dyn_cast<FConnectLike>(op)) {
463 auto srcInLayerBlock =
isAncestor(layerBlock, src);
464 auto dstInLayerBlock =
isAncestor(layerBlock, dst);
465 if (!srcInLayerBlock && !dstInLayerBlock) {
466 connect->moveBefore(layerBlock);
470 if (!srcInLayerBlock) {
471 createInputPort(src, op.getLoc());
475 if (!dstInLayerBlock) {
476 createOutputPort(dst, src);
477 if (!dst.getType().isa<RefType>())
498 if (
auto refResolve = dyn_cast<RefResolveOp>(op))
499 if (refResolve.getResult().hasOneUse() &&
500 refResolve.getRef().getParentBlock() != body)
501 if (
auto connect = dyn_cast<StrictConnectOp>(
502 *refResolve.getResult().getUsers().begin()))
503 if (
connect.getDest().getParentBlock() != body) {
504 refResolve->moveBefore(layerBlock);
509 for (
auto operand : op.getOperands()) {
511 createInputPort(operand, op.getLoc());
516 FModuleOp newModule = buildNewModule(
builder, layerBlock.getLoc(),
517 moduleNames.lookup(layerBlock), ports);
518 SymbolTable::setSymbolVisibility(newModule,
519 SymbolTable::Visibility::Private);
520 newModule.getBody().takeBody(layerBlock.getRegion());
523 llvm::dbgs() <<
" New Module: " << moduleNames.lookup(layerBlock)
525 llvm::dbgs() <<
" ports:\n";
526 for (
size_t i = 0, e = ports.size(); i != e; ++i) {
527 auto port = ports[i];
528 auto value = connectValues[i];
529 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
530 <<
" type: " << port.type <<
"\n"
531 <<
" direction: " << port.direction <<
"\n"
532 <<
" value: " << value.value <<
"\n"
534 << (value.kind == ConnectKind::NonRef ?
"NonRef" :
"Ref")
544 builder.setInsertionPointAfter(layerBlock);
546 auto instanceOp =
builder.create<InstanceOp>(
547 layerBlock.getLoc(), newModule,
549 instanceName, NameKindEnum::DroppableName,
550 ArrayRef<Attribute>{},
551 ArrayRef<Attribute>{},
true,
553 (innerSyms.empty() ? hw::InnerSymAttr{}
555 ns.newName(instanceName)))));
558 setOutputFile(instanceOp, fileName);
560 createdInstances.try_emplace(instanceOp, newModule);
562 LLVM_DEBUG(llvm::dbgs() <<
" moved inner refs:\n");
563 for (hw::InnerSymAttr innerSym : innerSyms) {
565 innerSym.getSymName());
566 auto splice = std::make_pair(instanceOp.getInnerSymAttr(),
567 newModule.getModuleNameAttr());
568 innerRefMap.insert({oldInnerRef, splice});
569 LLVM_DEBUG(llvm::dbgs() <<
" - ref: " << oldInnerRef <<
"\n"
570 <<
" splice: " << splice.first <<
", "
571 << splice.second <<
"\n";);
575 assert(ports.size() == connectValues.size() &&
576 "the number of instance ports and values to connect to them must be "
578 for (
unsigned portNum = 0, e = newModule.getNumPorts(); portNum < e;
580 OpBuilder::InsertionGuard guard(
builder);
581 builder.setInsertionPointAfterValue(instanceOp.getResult(portNum));
583 auto src = connectValues[portNum].value;
584 if (isa<RefType>(src.getType()))
585 src =
builder.create<RefResolveOp>(
586 newModule.getPortLocationAttr(portNum), src);
587 builder.create<StrictConnectOp>(newModule.getPortLocationAttr(portNum),
588 instanceOp.getResult(portNum), src);
589 }
else if (instanceOp.getResult(portNum).getType().isa<RefType>() &&
590 connectValues[portNum].kind == ConnectKind::Ref)
591 builder.create<RefDefineOp>(getPortLoc(connectValues[portNum].value),
592 connectValues[portNum].value,
593 instanceOp.getResult(portNum));
595 builder.create<StrictConnectOp>(
596 getPortLoc(connectValues[portNum].value),
597 connectValues[portNum].value,
598 builder.create<RefResolveOp>(newModule.getPortLocationAttr(portNum),
599 instanceOp.getResult(portNum)));
603 return WalkResult::advance();
610 if (!testBenchDir.empty() && dut)
611 op->setAttr(
"output_file", hw::OutputFileAttr::getAsDirectory(
612 op->getContext(), testBenchDir,
617 if (!testBenchDir.empty() && dut) {
618 op->setAttr(
"output_file", hw::OutputFileAttr::getFromDirectoryAndFilename(
619 op->getContext(), testBenchDir, filename,
624 op->setAttr(
"output_file", hw::OutputFileAttr::getFromFilename(
625 op->getContext(), filename,
633 llvm::dbgs() <<
"==----- Running LowerLayers "
634 "-------------------------------------------------===\n");
635 CircuitOp circuitOp = getOperation();
638 llvm::sys::SmartMutex<true> mutex;
639 circuitMutex = &mutex;
645 auto dir = anno.getMember<StringAttr>(
"dirname");
646 assert(dir &&
"invalid test bench annotation");
647 testBenchDir = dir.getValue();
654 WalkResult result = circuitOp->walk([&](FModuleOp moduleOp) {
656 return WalkResult::interrupt();
657 moduleOp->walk([&](LayerBlockOp layerBlockOp) {
659 layerBlockOp.getLayerName());
660 moduleNames.insert({layerBlockOp, ns.
newName(name)});
662 return WalkResult::advance();
665 if (result.wasInterrupted())
666 return signalPassFailure();
668 auto mergeMaps = [](
auto &&a,
auto &&b) {
671 return std::forward<decltype(a)>(a);
675 SmallVector<FModuleLike> modules(
676 circuitOp.getBodyBlock()->getOps<FModuleLike>());
679 [
this](FModuleLike mod) { return runOnModuleLike(mod); });
688 for (hw::HierPathOp hierPathOp : circuitOp.getOps<hw::HierPathOp>()) {
689 SmallVector<Attribute> newNamepath;
690 bool modified =
false;
691 for (
auto attr : hierPathOp.getNamepath()) {
692 hw::InnerRefAttr innerRef = dyn_cast<hw::InnerRefAttr>(attr);
694 newNamepath.push_back(attr);
697 auto it = innerRefMap.find(innerRef);
698 if (it == innerRefMap.end()) {
699 newNamepath.push_back(attr);
703 auto &[inst, mod] = it->getSecond();
704 newNamepath.push_back(
710 hierPathOp.setNamepathAttr(
727 SmallVector<std::pair<LayerOp, StringAttr>> layers;
728 StringRef circuitName = circuitOp.getName();
729 circuitOp.walk<mlir::WalkOrder::PreOrder>([&](LayerOp layerOp) {
730 auto parentOp = layerOp->getParentOfType<LayerOp>();
731 while (parentOp && parentOp != layers.back().first)
733 builder.setInsertionPointToStart(circuitOp.getBodyBlock());
736 SmallString<32> prefix(
"layers_");
737 prefix.append(circuitName);
739 for (
auto [layer, _] : layers) {
740 prefix.append(layer.getSymName());
743 prefix.append(layerOp.getSymName());
745 SmallString<128> includes;
746 for (
auto [_, strAttr] : layers) {
747 includes.append(strAttr);
748 includes.append(
"\n");
752 setOutputFile(
builder.create<sv::VerbatimOp>(
753 layerOp.getLoc(), includes +
"`ifndef " + prefix +
"\n" +
754 "`define " + prefix),
758 builder.setInsertionPointToEnd(circuitOp.getBodyBlock());
760 builder.create<sv::VerbatimOp>(layerOp.getLoc(),
"`endif // " + prefix),
763 if (!layerOp.getBody().getOps<LayerOp>().empty())
765 {layerOp,
builder.getStringAttr(
"`include \"" + prefix +
".sv\"")});
770 llvm::make_early_inc_range(circuitOp.getBodyBlock()->getOps<LayerOp>()))
775 return std::make_unique<LowerLayersPass>();
assert(baseType &&"element must be base type")
static SmallString< 32 > instanceNameForLayer(SymbolRefAttr layerName)
For a layerblock @A::@B::@C, the generated instance is called a_b_c.
static void appendName(StringRef name, SmallString< 32 > &output, bool toLower=false)
static SmallString< 32 > fileNameForLayer(StringRef circuitName, SymbolRefAttr layerName)
For all layerblocks @A::@B::@C in a circuit called Circuit, the output filename is layers_Circuit_A_B...
static SmallString< 32 > moduleNameForLayer(StringRef moduleName, SymbolRefAttr layerName)
For a layer @A::@B::@C in module Module, the generated module is called Module_A_B_C.
static bool isAncestor(Operation *op, Value value)
DenseMap< hw::InnerRefAttr, std::pair< hw::InnerSymAttr, StringAttr > > InnerRefMap
void setOutputFile(Operation *op, const Twine &filename)
Set an output file attribute pointing at the provided filename.
void runOnOperation() override
Entry point for the function.
void removeLayersFromPorts(FModuleLike moduleLike)
Update the module's port types to remove any explicit layer requirements from any probe types.
void runOnModuleBody(FModuleOp moduleOp, InnerRefMap &innerRefMap)
Extract layerblocks and strip probe colors from all ops under the module.
FModuleOp dut
The design-under-test (DUT) as indicated by the presence of a "sifive.enterprise.firrtl....
DenseMap< LayerBlockOp, StringRef > moduleNames
A map of layer blocks to module name that should be created for it.
InnerRefMap runOnModuleLike(FModuleLike moduleLike)
Strip layer colors from the module's interface.
void removeLayersFromRefCast(RefCastOp cast)
Remove any layers from the result of the cast.
void removeLayersFromValue(Value value)
Update the value's type to remove any layers from any probe types.
void maybeSetOutputDir(Operation *op)
Set an output file attribute pointing at the testbench directory if a testbench directory is known to...
StringRef testBenchDir
The directory for verification collateral as indicated by the presence of a "sifive....
FModuleOp buildNewModule(OpBuilder &builder, Location location, Twine namehint, SmallVectorImpl< PortInfo > &ports)
Safely build a new module with a given namehint.
llvm::sys::SmartMutex< true > * circuitMutex
Indicates exclusive access to modify the circuitNamespace and the circuit.
This class represents a reference to a specific field or element of an aggregate value.
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
def connect(destination, source)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
LogicalResult extractDUT(FModuleOp mod, FModuleOp &dut)
Utility that searches for a MarkDUTAnnotation on a specific module, mod, and tries to update a design...
constexpr const char * testBenchDirAnnoClass
std::unique_ptr< mlir::Pass > createLowerLayersPass()
static ResultTy transformReduce(MLIRContext *context, IterTy begin, IterTy end, ResultTy init, ReduceFuncTy reduce, TransformFuncTy transform)
Wrapper for llvm::parallelTransformReduce that performs the transform_reduce serially when MLIR multi...
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
The namespace of a CircuitOp, generally inhabited by modules.