CIRCT  19.0.0git
LowerLayers.cpp
Go to the documentation of this file.
1 //===- LowerLayers.cpp - Lower Layers by Convention -------------*- C++ -*-===//
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 // This pass lowers FIRRTL layers based on their specified convention.
9 //
10 //===----------------------------------------------------------------------===//
11 
12 #include "PassDetails.h"
18 #include "circt/Dialect/SV/SVOps.h"
19 #include "llvm/Support/Debug.h"
20 #include "llvm/Support/Mutex.h"
21 #include "llvm/Support/RWMutex.h"
22 
23 #define DEBUG_TYPE "firrtl-lower-layers"
24 
25 using namespace circt;
26 using namespace firrtl;
27 
28 //===----------------------------------------------------------------------===//
29 // Helpers
30 //===----------------------------------------------------------------------===//
31 
32 static bool isAncestor(Operation *op, Value value) {
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());
37 }
38 
39 namespace {
40 
41 /// Indicates the kind of reference that was captured.
42 enum class ConnectKind {
43  /// A normal captured value. This is a read of a value outside the
44  /// layerblock.
45  NonRef,
46  /// A reference. This is a destination of a ref define.
47  Ref
48 };
49 
50 struct ConnectInfo {
51  Value value;
52  ConnectKind kind;
53 };
54 
55 } // namespace
56 
57 // A mapping of an old InnerRefAttr to the new inner symbol and module name that
58 // need to be spliced into the old InnerRefAttr. This is used to fix
59 // hierarchical path operations after layers are converted to modules.
60 using InnerRefMap =
61  DenseMap<hw::InnerRefAttr, std::pair<hw::InnerSymAttr, StringAttr>>;
62 
63 //===----------------------------------------------------------------------===//
64 // Naming Helpers
65 //===----------------------------------------------------------------------===//
66 
67 static void appendName(StringRef name, SmallString<32> &output,
68  bool toLower = false) {
69  if (name.empty())
70  return;
71  if (!output.empty())
72  output.append("_");
73  output.append(name);
74  if (!toLower)
75  return;
76  auto i = output.size() - name.size();
77  output[i] = llvm::toLower(output[i]);
78 }
79 
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);
85 }
86 
87 /// For a layer `@A::@B::@C` in module Module,
88 /// the generated module is called `Module_A_B_C`.
89 static SmallString<32> moduleNameForLayer(StringRef moduleName,
90  SymbolRefAttr layerName) {
91  SmallString<32> result;
92  appendName(moduleName, result);
93  appendName(layerName, result);
94  return result;
95 }
96 
97 /// For a layerblock `@A::@B::@C`,
98 /// the generated instance is called `a_b_c`.
99 static SmallString<32> instanceNameForLayer(SymbolRefAttr layerName) {
100  SmallString<32> result;
101  appendName(layerName, result, /*toLower=*/true);
102  return result;
103 }
104 
105 /// For all layerblocks `@A::@B::@C` in a circuit called Circuit,
106 /// the output filename is `layers_Circuit_A_B_C.sv`.
107 static SmallString<32> fileNameForLayer(StringRef circuitName,
108  SymbolRefAttr layerName) {
109  SmallString<32> result;
110  result.append("layers");
111  appendName(circuitName, result);
112  appendName(layerName, result);
113  result.append(".sv");
114  return result;
115 }
116 
117 //===----------------------------------------------------------------------===//
118 // LowerLayersPass
119 //===----------------------------------------------------------------------===//
120 
121 class LowerLayersPass : public LowerLayersBase<LowerLayersPass> {
122  /// Safely build a new module with a given namehint. This handles geting a
123  /// lock to modify the top-level circuit.
124  FModuleOp buildNewModule(OpBuilder &builder, Location location,
125  Twine namehint, SmallVectorImpl<PortInfo> &ports);
126 
127  /// Strip layer colors from the module's interface.
128  InnerRefMap runOnModuleLike(FModuleLike moduleLike);
129 
130  /// Extract layerblocks and strip probe colors from all ops under the module.
131  void runOnModuleBody(FModuleOp moduleOp, InnerRefMap &innerRefMap);
132 
133  /// Update the module's port types to remove any explicit layer requirements
134  /// from any probe types.
135  void removeLayersFromPorts(FModuleLike moduleLike);
136 
137  /// Update the value's type to remove any layers from any probe types.
138  void removeLayersFromValue(Value value);
139 
140  /// Remove any layers from the result of the cast. If the cast becomes a nop,
141  /// remove the cast itself from the IR.
142  void removeLayersFromRefCast(RefCastOp cast);
143 
144  /// Set an output file attribute pointing at the testbench directory if a
145  /// testbench directory is known to the pass.
146  void maybeSetOutputDir(Operation *op);
147 
148  /// Set an output file attribute pointing at the provided filename. Prepend
149  /// the testbench directory if one is known to the pass.
150  void setOutputFile(Operation *op, const Twine &filename);
151 
152  /// Entry point for the function.
153  void runOnOperation() override;
154 
155  /// Indicates exclusive access to modify the circuitNamespace and the circuit.
156  llvm::sys::SmartMutex<true> *circuitMutex;
157 
158  /// A map of layer blocks to module name that should be created for it.
159  DenseMap<LayerBlockOp, StringRef> moduleNames;
160 
161  /// The design-under-test (DUT) as indicated by the presence of a
162  /// "sifive.enterprise.firrtl.MarkDUTAnnotation". This will be null if no
163  /// annotation is present.
164  FModuleOp dut;
165 
166  /// The directory for verification collateral as indicated by the presence of
167  /// a "sifive.enterprise.firrtl.TestBenchDirAnnotation". Empty if not
168  /// specified.
169  StringRef testBenchDir;
170 };
171 
172 /// Multi-process safe function to build a module in the circuit and return it.
173 /// The name provided is only a namehint for the module---a unique name will be
174 /// generated if there are conflicts with the namehint in the circuit-level
175 /// namespace.
176 FModuleOp LowerLayersPass::buildNewModule(OpBuilder &builder, Location location,
177  Twine namehint,
178  SmallVectorImpl<PortInfo> &ports) {
179  llvm::sys::SmartScopedLock<true> instrumentationLock(*circuitMutex);
180  FModuleOp newModule = builder.create<FModuleOp>(
181  location, builder.getStringAttr(namehint),
182  ConventionAttr::get(builder.getContext(), Convention::Internal), ports,
183  ArrayAttr{});
184  maybeSetOutputDir(newModule);
185  SymbolTable::setSymbolVisibility(newModule, SymbolTable::Visibility::Private);
186  return newModule;
187 }
188 
190  auto type = dyn_cast<RefType>(value.getType());
191  if (!type || !type.getLayer())
192  return;
193  value.setType(type.removeLayer());
194 }
195 
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);
205  cast->erase();
206  return;
207  }
208  result.setType(newType);
209  }
210 }
211 
212 void LowerLayersPass::removeLayersFromPorts(FModuleLike moduleLike) {
213  auto oldTypeAttrs = moduleLike.getPortTypesAttr();
214  SmallVector<Attribute> newTypeAttrs;
215  newTypeAttrs.reserve(oldTypeAttrs.size());
216  bool changed = false;
217 
218  for (auto typeAttr : oldTypeAttrs.getAsRange<TypeAttr>()) {
219  if (auto refType = dyn_cast<RefType>(typeAttr.getValue())) {
220  if (refType.getLayer()) {
221  typeAttr = TypeAttr::get(refType.removeLayer());
222  changed = true;
223  }
224  }
225  newTypeAttrs.push_back(typeAttr);
226  }
227 
228  if (!changed)
229  return;
230 
231  moduleLike->setAttr(FModuleLike::getPortTypesAttrName(),
232  ArrayAttr::get(moduleLike.getContext(), newTypeAttrs));
233 
234  if (auto moduleOp = dyn_cast<FModuleOp>(moduleLike.getOperation())) {
235  for (auto arg : moduleOp.getBodyBlock()->getArguments())
236  removeLayersFromValue(arg);
237  }
238 }
239 
241  LLVM_DEBUG({
242  llvm::dbgs() << "Module: " << moduleLike.getModuleName() << "\n";
243  llvm::dbgs() << " Examining Layer Blocks:\n";
244  });
245 
246  // Strip away layers from the interface of the module-like op.
247  InnerRefMap innerRefMap;
248  TypeSwitch<Operation *, void>(moduleLike.getOperation())
249  .Case<FModuleOp>([&](auto op) {
250  op.setLayers({});
251  removeLayersFromPorts(op);
252  runOnModuleBody(op, innerRefMap);
253  })
254  .Case<FExtModuleOp, FIntModuleOp, FMemModuleOp>([&](auto op) {
255  op.setLayers({});
256  removeLayersFromPorts(op);
257  })
258  .Case<ClassOp, ExtClassOp>([](auto) {})
259  .Default([](auto) { assert(0 && "unknown module-like op"); });
260 
261  return innerRefMap;
262 }
263 
264 void LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
265  InnerRefMap &innerRefMap) {
266  CircuitOp circuitOp = moduleOp->getParentOfType<CircuitOp>();
267  StringRef circuitName = circuitOp.getName();
268  hw::InnerSymbolNamespace ns(moduleOp);
269 
270  // A map of instance ops to modules that this pass creates. This is used to
271  // check if this was an instance that we created and to do fast module
272  // dereferencing (avoiding a symbol table).
273  DenseMap<InstanceOp, FModuleOp> createdInstances;
274 
275  // Post-order traversal that expands a layer block into its parent. For each
276  // layer block found do the following:
277  //
278  // 1. Create and connect one ref-type output port for each value defined in
279  // this layer block that drives an instance marked lowerToBind and move
280  // this instance outside the layer block.
281  // 2. Create one input port for each value captured by this layer block.
282  // 3. Create a new module for this layer block and move the (mutated) body of
283  // this layer block to the new module.
284  // 4. Instantiate the new module outside the layer block and hook it up.
285  // 5. Erase the layer block.
286  moduleOp.walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
287  // Strip layer requirements from any op that might represent a probe.
288  if (auto wire = dyn_cast<WireOp>(op)) {
289  removeLayersFromValue(wire.getResult());
290  return WalkResult::advance();
291  }
292  if (auto sub = dyn_cast<RefSubOp>(op)) {
293  removeLayersFromValue(sub.getResult());
294  return WalkResult::advance();
295  }
296  if (auto instance = dyn_cast<InstanceOp>(op)) {
297  instance.setLayers({});
298  for (auto result : instance.getResults())
299  removeLayersFromValue(result);
300  return WalkResult::advance();
301  }
302  if (auto cast = dyn_cast<RefCastOp>(op)) {
303  removeLayersFromRefCast(cast);
304  return WalkResult::advance();
305  }
306 
307  auto layerBlock = dyn_cast<LayerBlockOp>(op);
308  if (!layerBlock)
309  return WalkResult::advance();
310 
311  Block *body = layerBlock.getBody(0);
312  OpBuilder builder(moduleOp);
313 
314  // Ports that need to be created for the module derived from this layer
315  // block.
316  SmallVector<PortInfo> ports;
317 
318  // Connection that need to be made to the instance of the derived module.
319  SmallVector<ConnectInfo> connectValues;
320 
321  // Create an input port for an operand that is captured from outside.
322  auto createInputPort = [&](Value operand, Location loc) {
323  const auto &[portName, _] = getFieldName(FieldRef(operand, 0), true);
324 
325  // If the value is a ref, we must resolve the ref inside the parent,
326  // passing the input as a value instead of a ref. Inside the layer, we
327  // convert (ref.send) the value back into a ref.
328  auto type = operand.getType();
329  if (auto refType = dyn_cast<RefType>(type))
330  type = refType.getType();
331 
332  ports.push_back({builder.getStringAttr(portName), type, Direction::In,
333  /*sym=*/{},
334  /*loc=*/loc});
335  // Update the layer block's body with arguments as we will swap this body
336  // into the module when we create it. If this is a ref type, then add a
337  // refsend to convert from the non-ref type input port.
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);
343  }
344  operand.replaceUsesWithIf(replacement, [&](OpOperand &use) {
345  auto *user = use.getOwner();
346  if (!layerBlock->isAncestor(user))
347  return false;
348  if (auto connectLike = dyn_cast<FConnectLike>(user)) {
349  if (use.getOperandNumber() == 0)
350  return false;
351  }
352  return true;
353  });
354 
355  connectValues.push_back({operand, ConnectKind::NonRef});
356  };
357 
358  // Set the location intelligently. Use the location of the capture if this
359  // is a port created for forwarding from a parent layer block to a nested
360  // layer block. Otherwise, use unknown.
361  auto getPortLoc = [&](Value port) -> Location {
362  Location loc = UnknownLoc::get(port.getContext());
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);
369  }
370  }
371  return loc;
372  };
373 
374  // Create an output probe port port and adds a ref.define/ref.send to
375  // drive the port if this was not already capturing a ref type.
376  auto createOutputPort = [&](Value dest, Value src) {
377  auto loc = getPortLoc(dest);
378  auto portNum = ports.size();
379  const auto &[portName, _] = getFieldName(FieldRef(dest, 0), true);
380 
381  RefType refType;
382  if (auto oldRef = dyn_cast<RefType>(dest.getType()))
383  refType = oldRef;
384  else
385  refType = RefType::get(
386  type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType(),
387  /*forceable=*/false);
388 
389  ports.push_back({builder.getStringAttr(portName), refType, Direction::Out,
390  /*sym=*/{}, /*loc=*/loc});
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))
396  return false;
397  if (auto connectLike = dyn_cast<FConnectLike>(user)) {
398  if (use.getOperandNumber() == 0)
399  return true;
400  }
401  return false;
402  });
403  connectValues.push_back({dest, ConnectKind::Ref});
404  return;
405  }
406  connectValues.push_back({dest, ConnectKind::NonRef});
407  OpBuilder::InsertionGuard guard(builder);
408  builder.setInsertionPointAfterValue(src);
409  builder.create<RefDefineOp>(
410  loc, body->getArgument(portNum),
411  builder.create<RefSendOp>(loc, src)->getResult(0));
412  };
413 
414  SmallVector<hw::InnerSymAttr> innerSyms;
415  for (auto &op : llvm::make_early_inc_range(*body)) {
416  // Record any operations inside the layer block which have inner symbols.
417  // Theses may have symbol users which need to be updated.
418  if (auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
419  if (auto innerSym = symOp.getInnerSymAttr())
420  innerSyms.push_back(innerSym);
421 
422  // Handle instance ops that were created from nested layer blocks. These
423  // ops need to be moved outside the layer block to avoid nested binds.
424  // Nested binds are illegal in the SystemVerilog specification (and
425  // checked by FIRRTL verification).
426  //
427  // For each value defined in this layer block which drives a port of one
428  // of these instances, create an output reference type port on the
429  // to-be-created module and drive it with the value. Move the instance
430  // outside the layer block. We will hook it up later once we replace the
431  // layer block with an instance.
432  if (auto instOp = dyn_cast<InstanceOp>(op)) {
433  // Ignore instances which this pass did not create.
434  if (!createdInstances.contains(instOp))
435  continue;
436 
437  LLVM_DEBUG({
438  llvm::dbgs()
439  << " Found instance created from nested layer block:\n"
440  << " module: " << instOp.getModuleName() << "\n"
441  << " instance: " << instOp.getName() << "\n";
442  });
443  instOp->moveBefore(layerBlock);
444  continue;
445  }
446 
447  if (auto refSend = dyn_cast<RefSendOp>(op)) {
448  auto src = refSend.getBase();
449  if (!isAncestor(layerBlock, src))
450  createInputPort(src, op.getLoc());
451  continue;
452  }
453 
454  if (auto refCast = dyn_cast<RefCastOp>(op)) {
455  if (!isAncestor(layerBlock, refCast))
456  createInputPort(refCast.getInput(), op.getLoc());
457  continue;
458  }
459 
460  if (auto connect = dyn_cast<FConnectLike>(op)) {
461  auto src = connect.getSrc();
462  auto dst = connect.getDest();
463  auto srcInLayerBlock = isAncestor(layerBlock, src);
464  auto dstInLayerBlock = isAncestor(layerBlock, dst);
465  if (!srcInLayerBlock && !dstInLayerBlock) {
466  connect->moveBefore(layerBlock);
467  continue;
468  }
469  // Create an input port.
470  if (!srcInLayerBlock) {
471  createInputPort(src, op.getLoc());
472  continue;
473  }
474  // Create an output port.
475  if (!dstInLayerBlock) {
476  createOutputPort(dst, src);
477  if (!dst.getType().isa<RefType>())
478  connect.erase();
479  continue;
480  }
481  // Source and destination in layer block. Nothing to do.
482  continue;
483  }
484 
485  // Pre-emptively de-squiggle connections that we are creating. This will
486  // later be cleaned up by the de-squiggling pass. However, there is no
487  // point in creating deeply squiggled connections if we don't have to.
488  //
489  // This pattern matches the following structure. Move the ref.resolve
490  // outside the layer block. The strictconnect will be moved outside in
491  // the next loop iteration:
492  // %0 = ...
493  // %1 = ...
494  // firrtl.layerblock {
495  // %2 = ref.resolve %0
496  // firrtl.strictconnect %1, %2
497  // }
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);
505  continue;
506  }
507 
508  // For any other ops, create input ports for any captured operands.
509  for (auto operand : op.getOperands()) {
510  if (!isAncestor(layerBlock, operand))
511  createInputPort(operand, op.getLoc());
512  }
513  }
514 
515  // Create the new module. This grabs a lock to modify the circuit.
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());
521 
522  LLVM_DEBUG({
523  llvm::dbgs() << " New Module: " << moduleNames.lookup(layerBlock)
524  << "\n";
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"
533  << " kind: "
534  << (value.kind == ConnectKind::NonRef ? "NonRef" : "Ref")
535  << "\n";
536  }
537  });
538 
539  // Replace the original layer block with an instance. Hook up the instance.
540  // Intentionally create instance with probe ports which do not have an
541  // associated layer. This is illegal IR that will be made legal by the end
542  // of the pass. This is done to avoid having to revisit and rewrite each
543  // instance everytime it is moved into a parent layer.
544  builder.setInsertionPointAfter(layerBlock);
545  auto instanceName = instanceNameForLayer(layerBlock.getLayerName());
546  auto instanceOp = builder.create<InstanceOp>(
547  layerBlock.getLoc(), /*moduleName=*/newModule,
548  /*name=*/
549  instanceName, NameKindEnum::DroppableName,
550  /*annotations=*/ArrayRef<Attribute>{},
551  /*portAnnotations=*/ArrayRef<Attribute>{}, /*lowerToBind=*/true,
552  /*innerSym=*/
553  (innerSyms.empty() ? hw::InnerSymAttr{}
554  : hw::InnerSymAttr::get(builder.getStringAttr(
555  ns.newName(instanceName)))));
556 
557  auto fileName = fileNameForLayer(circuitName, layerBlock.getLayerName());
558  setOutputFile(instanceOp, fileName);
559 
560  createdInstances.try_emplace(instanceOp, newModule);
561 
562  LLVM_DEBUG(llvm::dbgs() << " moved inner refs:\n");
563  for (hw::InnerSymAttr innerSym : innerSyms) {
564  auto oldInnerRef = hw::InnerRefAttr::get(moduleOp.getModuleNameAttr(),
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";);
572  }
573 
574  // Connect instance ports to values.
575  assert(ports.size() == connectValues.size() &&
576  "the number of instance ports and values to connect to them must be "
577  "equal");
578  for (unsigned portNum = 0, e = newModule.getNumPorts(); portNum < e;
579  ++portNum) {
580  OpBuilder::InsertionGuard guard(builder);
581  builder.setInsertionPointAfterValue(instanceOp.getResult(portNum));
582  if (instanceOp.getPortDirection(portNum) == Direction::In) {
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));
594  else
595  builder.create<StrictConnectOp>(
596  getPortLoc(connectValues[portNum].value),
597  connectValues[portNum].value,
598  builder.create<RefResolveOp>(newModule.getPortLocationAttr(portNum),
599  instanceOp.getResult(portNum)));
600  }
601  layerBlock.erase();
602 
603  return WalkResult::advance();
604  });
605 }
606 
607 /// Set an output file attribute on the provided operation if the pass has a
608 /// non-empty test bench directory.
610  if (!testBenchDir.empty() && dut)
611  op->setAttr("output_file", hw::OutputFileAttr::getAsDirectory(
612  op->getContext(), testBenchDir,
613  /*excludeFromFileList=*/true));
614 }
615 
616 void LowerLayersPass::setOutputFile(Operation *op, const Twine &filename) {
617  if (!testBenchDir.empty() && dut) {
618  op->setAttr("output_file", hw::OutputFileAttr::getFromDirectoryAndFilename(
619  op->getContext(), testBenchDir, filename,
620  /*excludeFromFileList=*/true));
621 
622  return;
623  }
624  op->setAttr("output_file", hw::OutputFileAttr::getFromFilename(
625  op->getContext(), filename,
626  /*excludeFromFileList=*/true));
627 }
628 
629 /// Process a circuit to remove all layer blocks in each module and top-level
630 /// layer definition.
632  LLVM_DEBUG(
633  llvm::dbgs() << "==----- Running LowerLayers "
634  "-------------------------------------------------===\n");
635  CircuitOp circuitOp = getOperation();
636 
637  // Initialize members which cannot be initialized automatically.
638  llvm::sys::SmartMutex<true> mutex;
639  circuitMutex = &mutex;
640 
641  // Extract information from circuit-level annotations.
642  for (auto anno : AnnotationSet(circuitOp)) {
643  if (!anno.isClass(testBenchDirAnnoClass))
644  continue;
645  auto dir = anno.getMember<StringAttr>("dirname");
646  assert(dir && "invalid test bench annotation");
647  testBenchDir = dir.getValue();
648  }
649 
650  // Determine names for all modules that will be created. Do this serially to
651  // avoid non-determinism from creating these in the parallel region. While
652  // walking modules, set the DUT if it is so marked.
653  CircuitNamespace ns(circuitOp);
654  WalkResult result = circuitOp->walk([&](FModuleOp moduleOp) {
655  if (failed(extractDUT(moduleOp, dut)))
656  return WalkResult::interrupt();
657  moduleOp->walk([&](LayerBlockOp layerBlockOp) {
658  auto name = moduleNameForLayer(moduleOp.getModuleName(),
659  layerBlockOp.getLayerName());
660  moduleNames.insert({layerBlockOp, ns.newName(name)});
661  });
662  return WalkResult::advance();
663  });
664 
665  if (result.wasInterrupted())
666  return signalPassFailure();
667 
668  auto mergeMaps = [](auto &&a, auto &&b) {
669  for (auto bb : b)
670  a.insert(bb);
671  return std::forward<decltype(a)>(a);
672  };
673 
674  // Lower the layer blocks of each module.
675  SmallVector<FModuleLike> modules(
676  circuitOp.getBodyBlock()->getOps<FModuleLike>());
677  auto innerRefMap =
678  transformReduce(circuitOp.getContext(), modules, InnerRefMap{}, mergeMaps,
679  [this](FModuleLike mod) { return runOnModuleLike(mod); });
680 
681  // Rewrite any hw::HierPathOps which have namepaths that contain rewritting
682  // inner refs.
683  //
684  // TODO: This unnecessarily computes a new namepath for every hw::HierPathOp
685  // even if that namepath is not used. It would be better to only build the
686  // new namepath when a change is needed, e.g., by recording updates to the
687  // namepath.
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);
693  if (!innerRef) {
694  newNamepath.push_back(attr);
695  continue;
696  }
697  auto it = innerRefMap.find(innerRef);
698  if (it == innerRefMap.end()) {
699  newNamepath.push_back(attr);
700  continue;
701  }
702 
703  auto &[inst, mod] = it->getSecond();
704  newNamepath.push_back(
705  hw::InnerRefAttr::get(innerRef.getModule(), inst.getSymName()));
706  newNamepath.push_back(hw::InnerRefAttr::get(mod, innerRef.getName()));
707  modified = true;
708  }
709  if (modified)
710  hierPathOp.setNamepathAttr(
711  ArrayAttr::get(circuitOp.getContext(), newNamepath));
712  }
713 
714  // Generate the header and footer of each bindings file. The body will be
715  // populated later when binds are exported to Verilog. This produces text
716  // like:
717  //
718  // `include "layers_A.sv"
719  // `include "layers_A_B.sv"
720  // `ifndef layers_A_B_C
721  // `define layers_A_B_C
722  // <body>
723  // `endif // layers_A_B_C
724  //
725  // TODO: This would be better handled without the use of verbatim ops.
726  OpBuilder builder(circuitOp);
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)
732  layers.pop_back();
733  builder.setInsertionPointToStart(circuitOp.getBodyBlock());
734 
735  // Save the "layers_CIRCUIT_GROUP" string as this is reused a bunch.
736  SmallString<32> prefix("layers_");
737  prefix.append(circuitName);
738  prefix.append("_");
739  for (auto [layer, _] : layers) {
740  prefix.append(layer.getSymName());
741  prefix.append("_");
742  }
743  prefix.append(layerOp.getSymName());
744 
745  SmallString<128> includes;
746  for (auto [_, strAttr] : layers) {
747  includes.append(strAttr);
748  includes.append("\n");
749  }
750 
751  // Write header to a verbatim.
752  setOutputFile(builder.create<sv::VerbatimOp>(
753  layerOp.getLoc(), includes + "`ifndef " + prefix + "\n" +
754  "`define " + prefix),
755  prefix + ".sv");
756 
757  // Write footer to a verbatim.
758  builder.setInsertionPointToEnd(circuitOp.getBodyBlock());
759  setOutputFile(
760  builder.create<sv::VerbatimOp>(layerOp.getLoc(), "`endif // " + prefix),
761  prefix + ".sv");
762 
763  if (!layerOp.getBody().getOps<LayerOp>().empty())
764  layers.push_back(
765  {layerOp, builder.getStringAttr("`include \"" + prefix + ".sv\"")});
766  });
767 
768  // All layers definitions can now be deleted.
769  for (auto layerOp :
770  llvm::make_early_inc_range(circuitOp.getBodyBlock()->getOps<LayerOp>()))
771  layerOp.erase();
772 }
773 
774 std::unique_ptr<mlir::Pass> circt::firrtl::createLowerLayersPass() {
775  return std::make_unique<LowerLayersPass>();
776 }
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.
Definition: LowerLayers.cpp:99
static void appendName(StringRef name, SmallString< 32 > &output, bool toLower=false)
Definition: LowerLayers.cpp:67
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.
Definition: LowerLayers.cpp:89
static bool isAncestor(Operation *op, Value value)
Definition: LowerLayers.cpp:32
DenseMap< hw::InnerRefAttr, std::pair< hw::InnerSymAttr, StringAttr > > InnerRefMap
Definition: LowerLayers.cpp:61
Builder builder
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.
Definition: FieldRef.h:28
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
Definition: Namespace.h:63
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
def connect(destination, source)
Definition: support.py:37
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
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...
Definition: FIRRTLUtils.h:289
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.
Definition: DebugAnalysis.h:21
The namespace of a CircuitOp, generally inhabited by modules.
Definition: Namespace.h:24