CIRCT  18.0.0git
LowerToHW.cpp
Go to the documentation of this file.
1 //===- LowerToHW.cpp - FIRRTL to HW/SV Lowering Pass ----------------------===//
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 //
9 // This is the main FIRRTL to HW/SV Lowering Pass Implementation.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "../PassDetail.h"
26 #include "circt/Dialect/HW/HWOps.h"
30 #include "circt/Dialect/SV/SVOps.h"
35 #include "mlir/IR/BuiltinOps.h"
36 #include "mlir/IR/BuiltinTypes.h"
37 #include "mlir/IR/ImplicitLocOpBuilder.h"
38 #include "mlir/IR/Threading.h"
39 #include "mlir/Pass/Pass.h"
40 #include "llvm/ADT/StringSet.h"
41 #include "llvm/ADT/TinyPtrVector.h"
42 #include "llvm/Support/Debug.h"
43 #include "llvm/Support/FormatVariadic.h"
44 #include "llvm/Support/Parallel.h"
45 
46 #define DEBUG_TYPE "lower-to-hw"
47 
48 using namespace circt;
49 using namespace firrtl;
50 using circt::comb::ICmpPredicate;
51 
52 /// Attribute that indicates that the module hierarchy starting at the
53 /// annotated module should be dumped to a file.
54 static const char moduleHierarchyFileAttrName[] = "firrtl.moduleHierarchyFile";
55 
56 /// Return true if the specified type is a sized FIRRTL type (Int or Analog)
57 /// with zero bits.
58 static bool isZeroBitFIRRTLType(Type type) {
59  auto ftype = dyn_cast<FIRRTLBaseType>(type);
60  return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
61 }
62 
63 // Return a single source value in the operands of the given attach op if
64 // exists.
65 static Value getSingleNonInstanceOperand(AttachOp op) {
66  Value singleSource;
67  for (auto operand : op.getAttached()) {
68  if (isZeroBitFIRRTLType(operand.getType()) ||
69  operand.getDefiningOp<InstanceOp>())
70  continue;
71  // If it is used by other than attach op or there is already a source
72  // value, bail out.
73  if (!operand.hasOneUse() || singleSource)
74  return {};
75  singleSource = operand;
76  }
77  return singleSource;
78 }
79 
80 /// This verifies that the target operation has been lowered to a legal
81 /// operation. This checks that the operation recursively has no FIRRTL
82 /// operations or types.
83 static LogicalResult verifyOpLegality(Operation *op) {
84  auto checkTypes = [](Operation *op) -> WalkResult {
85  // Check that this operation is not a FIRRTL op.
86  if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
87  return op->emitError("Found unhandled FIRRTL operation '")
88  << op->getName() << "'";
89 
90  // Helper to check a TypeRange for any FIRRTL types.
91  auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
92  if (llvm::any_of(types, [](Type type) {
93  return isa<FIRRTLDialect>(type.getDialect());
94  }))
95  return op->emitOpError("found unhandled FIRRTL type");
96  return success();
97  };
98 
99  // Check operand and result types.
100  if (failed(checkTypeRange(op->getOperandTypes())) ||
101  failed(checkTypeRange(op->getResultTypes())))
102  return WalkResult::interrupt();
103 
104  // Check the block argument types.
105  for (auto &region : op->getRegions())
106  for (auto &block : region)
107  if (failed(checkTypeRange(block.getArgumentTypes())))
108  return WalkResult::interrupt();
109 
110  // Continue to the next operation.
111  return WalkResult::advance();
112  };
113 
114  if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
115  return failure();
116  return success();
117 }
118 
119 /// Given two FIRRTL integer types, return the widest one.
120 static IntType getWidestIntType(Type t1, Type t2) {
121  auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
122  return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
123 }
124 
125 /// Cast a value to a desired target type. This will insert struct casts and
126 /// unrealized conversion casts as necessary.
127 static Value castToFIRRTLType(Value val, Type type,
128  ImplicitLocOpBuilder &builder) {
129  // Use HWStructCastOp for a bundle type.
130  if (BundleType bundle = dyn_cast<BundleType>(type))
131  val = builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
132 
133  if (type != val.getType())
134  val = builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(
135  0);
136 
137  return val;
138 }
139 
140 /// Cast from a FIRRTL type (potentially with a flip) to a standard type.
141 static Value castFromFIRRTLType(Value val, Type type,
142  ImplicitLocOpBuilder &builder) {
143 
144  if (hw::StructType structTy = type.dyn_cast<hw::StructType>()) {
145  // Strip off Flip type if needed.
146  val =
147  builder
148  .create<mlir::UnrealizedConversionCastOp>(
149  type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
150  .getResult(0);
151  val = builder.createOrFold<HWStructCastOp>(type, val);
152  return val;
153  }
154 
155  val =
156  builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(0);
157 
158  return val;
159 }
160 
161 /// Move a ExtractTestCode related annotation from annotations to an attribute.
162 static void moveVerifAnno(ModuleOp top, AnnotationSet &annos,
163  StringRef annoClass, StringRef attrBase) {
164  auto anno = annos.getAnnotation(annoClass);
165  auto ctx = top.getContext();
166  if (!anno)
167  return;
168  if (auto dir = anno.getMember<StringAttr>("directory")) {
169  SmallVector<NamedAttribute> old;
170  for (auto i : top->getAttrs())
171  old.push_back(i);
172  old.emplace_back(
173  StringAttr::get(ctx, attrBase),
174  hw::OutputFileAttr::getAsDirectory(ctx, dir.getValue(), true, true));
175  top->setAttrs(old);
176  }
177  if (auto file = anno.getMember<StringAttr>("filename")) {
178  SmallVector<NamedAttribute> old;
179  for (auto i : top->getAttrs())
180  old.push_back(i);
181  old.emplace_back(StringAttr::get(ctx, attrBase + ".bindfile"),
182  hw::OutputFileAttr::getFromFilename(
183  ctx, file.getValue(), /*excludeFromFileList=*/true));
184  top->setAttrs(old);
185  }
186 }
187 
188 static unsigned getBitWidthFromVectorSize(unsigned size) {
189  return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
190 }
191 
192 // Try moving a name from an firrtl expression to a hw expression as a name
193 // hint. Dont' overwrite an existing name.
194 static void tryCopyName(Operation *dst, Operation *src) {
195  if (auto attr = src->getAttrOfType<StringAttr>("name"))
196  if (!dst->hasAttr("sv.namehint") && !dst->hasAttr("name"))
197  dst->setAttr("sv.namehint", attr);
198 }
199 
200 //===----------------------------------------------------------------------===//
201 // firrtl.module Lowering Pass
202 //===----------------------------------------------------------------------===//
203 namespace {
204 
205 struct FIRRTLModuleLowering;
206 
207 /// This is state shared across the parallel module lowering logic.
208 struct CircuitLoweringState {
209  std::atomic<bool> used_PRINTF_COND{false};
210  std::atomic<bool> used_ASSERT_VERBOSE_COND{false};
211  std::atomic<bool> used_STOP_COND{false};
212 
213  std::atomic<bool> used_RANDOMIZE_REG_INIT{false},
214  used_RANDOMIZE_MEM_INIT{false};
215  std::atomic<bool> used_RANDOMIZE_GARBAGE_ASSIGN{false};
216 
217  CircuitLoweringState(CircuitOp circuitOp, bool enableAnnotationWarning,
218  bool emitChiselAssertsAsSVA,
219  InstanceGraph *instanceGraph, NLATable *nlaTable)
220  : circuitOp(circuitOp), instanceGraph(instanceGraph),
221  enableAnnotationWarning(enableAnnotationWarning),
222  emitChiselAssertsAsSVA(emitChiselAssertsAsSVA), nlaTable(nlaTable) {
223  auto *context = circuitOp.getContext();
224 
225  // Get the testbench output directory.
226  if (auto tbAnno =
227  AnnotationSet(circuitOp).getAnnotation(testBenchDirAnnoClass)) {
228  auto dirName = tbAnno.getMember<StringAttr>("dirname");
229  testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
230  context, dirName.getValue(), false, true);
231  }
232 
233  for (auto &op : *circuitOp.getBodyBlock()) {
234  if (auto module = dyn_cast<FModuleLike>(op))
236  dut = module;
237  }
238 
239  // Figure out which module is the DUT and TestHarness. If there is no
240  // module marked as the DUT, the top module is the DUT. If the DUT and the
241  // test harness are the same, then there is no test harness.
242  testHarness = instanceGraph->getTopLevelModule();
243  if (!dut) {
244  dut = testHarness;
245  testHarness = nullptr;
246  } else if (dut == testHarness) {
247  testHarness = nullptr;
248  }
249  }
250 
251  Operation *getNewModule(Operation *oldModule) {
252  auto it = oldToNewModuleMap.find(oldModule);
253  return it != oldToNewModuleMap.end() ? it->second : nullptr;
254  }
255 
256  // Process remaining annotations and emit warnings on unprocessed annotations
257  // still remaining in the annoSet.
258  void processRemainingAnnotations(Operation *op, const AnnotationSet &annoSet);
259 
260  CircuitOp circuitOp;
261 
262  // Safely add a BindOp to global mutable state. This will acquire a lock to
263  // do this safely.
264  void addBind(sv::BindOp op) {
265  std::lock_guard<std::mutex> lock(bindsMutex);
266  binds.push_back(op);
267  }
268 
269  /// For a given Type Alias, return the corresponding AliasType. Create and
270  /// record the AliasType, if it doesn't exist.
271  hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
272  Location typeLoc) {
273 
274  auto hwAlias = typeAliases.getTypedecl(firAliasType);
275  if (hwAlias)
276  return hwAlias;
277  assert(!typeAliases.isFrozen() &&
278  "type aliases cannot be generated after its frozen");
279  return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
280  }
281 
282  FModuleLike getDut() { return dut; }
283  FModuleLike getTestHarness() { return testHarness; }
284 
285  // Return true if this module is the DUT or is instantiated by the DUT.
286  // Returns false if the module is not instantiated by the DUT.
287  bool isInDUT(igraph::ModuleOpInterface child) {
288  if (auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
289  return getInstanceGraph()->isAncestor(child, parent);
290  return dut == child;
291  }
292 
293  hw::OutputFileAttr getTestBenchDirectory() { return testBenchDirectory; }
294 
295  // Return true if this module is instantiated by the Test Harness. Returns
296  // false if the module is not instantiated by the Test Harness or if the Test
297  // Harness is not known.
298  bool isInTestHarness(igraph::ModuleOpInterface mod) { return !isInDUT(mod); }
299 
300  InstanceGraph *getInstanceGraph() { return instanceGraph; }
301 
302  /// Given a type, return the corresponding lowered type for the HW dialect.
303  /// A wrapper to the FIRRTLUtils::lowerType, required to ensure safe addition
304  /// of TypeScopeOp for all the TypeDecls.
305  Type lowerType(Type type, Location loc) {
306  return ::lowerType(type, loc,
307  [&](Type rawType, BaseTypeAliasType firrtlType,
308  Location typeLoc) -> hw::TypeAliasType {
309  return getTypeAlias(rawType, firrtlType, typeLoc);
310  });
311  }
312 
313 private:
314  friend struct FIRRTLModuleLowering;
315  friend struct FIRRTLLowering;
316  CircuitLoweringState(const CircuitLoweringState &) = delete;
317  void operator=(const CircuitLoweringState &) = delete;
318 
319  DenseMap<Operation *, Operation *> oldToNewModuleMap;
320 
321  /// Cache of module symbols. We need to test hirarchy-based properties to
322  /// lower annotaitons.
323  InstanceGraph *instanceGraph;
324 
325  // Record the set of remaining annotation classes. This is used to warn only
326  // once about any annotation class.
327  StringSet<> pendingAnnotations;
328  const bool enableAnnotationWarning;
329  std::mutex annotationPrintingMtx;
330 
331  const bool emitChiselAssertsAsSVA;
332 
333  // Records any sv::BindOps that are found during the course of execution.
334  // This is unsafe to access directly and should only be used through addBind.
335  SmallVector<sv::BindOp> binds;
336 
337  // Control access to binds.
338  std::mutex bindsMutex;
339 
340  // The design-under-test (DUT), if it is found. This will be set if a
341  // "sifive.enterprise.firrtl.MarkDUTAnnotation" exists.
342  FModuleLike dut;
343 
344  // If there is a module marked as the DUT and it is not the top level module,
345  // this will be set.
346  FModuleLike testHarness;
347 
348  // If there is a testbench output directory, this will be set.
349  hw::OutputFileAttr testBenchDirectory;
350 
351  /// A mapping of instances to their forced instantiation names (if
352  /// applicable).
353  DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
354 
355  /// Cached nla table analysis.
356  NLATable *nlaTable = nullptr;
357 
358  /// FIRRTL::BaseTypeAliasType is lowered to hw::TypeAliasType, which requires
359  /// TypedeclOp inside a single global TypeScopeOp. This structure
360  /// maintains a map of FIRRTL alias types to HW alias type, which is populated
361  /// in the sequential phase and accessed during the read-only phase when its
362  /// frozen.
363  /// This structure ensures that
364  /// all TypeAliases are lowered as a prepass, before lowering all the modules
365  /// in parallel. Lowering of TypeAliases must be done sequentially to ensure
366  /// deteministic TypeDecls inside the global TypeScopeOp.
367  struct RecordTypeAlias {
368 
369  RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
370 
371  hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias) const {
372  auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
373  if (iter != firrtlTypeToAliasTypeMap.end())
374  return iter->second;
375  return {};
376  }
377 
378  bool isFrozen() { return frozen; }
379 
380  void freeze() { frozen = true; }
381 
382  hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
383  Location typeLoc) {
384  assert(!frozen && "Record already frozen, cannot be updated");
385 
386  if (!typeScope) {
387  auto b = ImplicitLocOpBuilder::atBlockBegin(
388  circuitOp.getLoc(),
389  &circuitOp->getParentRegion()->getBlocks().back());
390  typeScope = b.create<hw::TypeScopeOp>(
391  b.getStringAttr(circuitOp.getName() + "__TYPESCOPE_"));
392  typeScope.getBodyRegion().push_back(new Block());
393  }
394  auto typeName = firAlias.getName();
395  // Get a unique typedecl name.
396  // The bundleName can conflict with other symbols, but must be unique
397  // within the TypeScopeOp.
398  typeName =
399  StringAttr::get(typeName.getContext(),
400  typeDeclNamespace.newName(typeName.getValue()));
401 
402  auto typeScopeBuilder =
403  ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
404  auto typeDecl = typeScopeBuilder.create<hw::TypedeclOp>(typeLoc, typeName,
405  rawType, nullptr);
406  auto hwAlias = hw::TypeAliasType::get(
407  SymbolRefAttr::get(typeScope.getSymNameAttr(),
408  {FlatSymbolRefAttr::get(typeDecl)}),
409  rawType);
410  auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
411  assert(insert.second && "Entry already exists, insert failed");
412  return insert.first->second;
413  }
414 
415  private:
416  bool frozen = false;
417  /// Global typescope for all the typedecls in this module.
418  hw::TypeScopeOp typeScope;
419 
420  /// Map of FIRRTL type to the lowered AliasType.
421  DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
422 
423  /// Set to keep track of unique typedecl names.
424  Namespace typeDeclNamespace;
425 
426  CircuitOp circuitOp;
427  };
428 
429  RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
430 };
431 
432 void CircuitLoweringState::processRemainingAnnotations(
433  Operation *op, const AnnotationSet &annoSet) {
434  if (!enableAnnotationWarning || annoSet.empty())
435  return;
436  std::lock_guard<std::mutex> lock(annotationPrintingMtx);
437 
438  for (auto a : annoSet) {
439  auto inserted = pendingAnnotations.insert(a.getClass());
440  if (!inserted.second)
441  continue;
442 
443  // The following annotations are okay to be silently dropped at this point.
444  // This can occur for example if an annotation marks something in the IR as
445  // not to be processed by a pass, but that pass hasn't run anyway.
446  if (a.isClass(
447  // If the class is `circt.nonlocal`, it's not really an annotation,
448  // but part of a path specifier for another annotation which is
449  // non-local. We can ignore these path specifiers since there will
450  // be a warning produced for the real annotation.
451  "circt.nonlocal",
452  // The following are either consumed by a pass running before
453  // LowerToHW, or they have no effect if the pass doesn't run at all.
454  // If the accompanying pass runs on the HW dialect, then LowerToHW
455  // should have consumed and processed these into an attribute on the
456  // output.
458  // The following are inspected (but not consumed) by FIRRTL/GCT
459  // passes that have all run by now. Since no one is responsible for
460  // consuming these, they will linger around and can be ignored.
464  // This annotation is used to mark which external modules are
465  // imported blackboxes from the BlackBoxReader pass.
467  // This annotation is used by several GrandCentral passes.
469  // The following will be handled while lowering the verification
470  // ops.
473  // The following will be handled after lowering FModule ops, since
474  // they are still needed on the circuit until after lowering
475  // FModules.
478  continue;
479 
480  mlir::emitWarning(op->getLoc(), "unprocessed annotation:'" + a.getClass() +
481  "' still remaining after LowerToHW");
482  }
483 }
484 } // end anonymous namespace
485 
486 namespace {
487 struct FIRRTLModuleLowering : public LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
488 
489  void runOnOperation() override;
490  void setDisableMemRandomization() { disableMemRandomization = true; }
491  void setDisableRegRandomization() { disableRegRandomization = true; }
492  void setEnableAnnotationWarning() { enableAnnotationWarning = true; }
493  void setEmitChiselAssertAsSVA() { emitChiselAssertsAsSVA = true; }
494 
495 private:
496  void lowerFileHeader(CircuitOp op, CircuitLoweringState &loweringState);
497  LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
498  SmallVectorImpl<hw::PortInfo> &ports,
499  Operation *moduleOp, StringRef moduleName,
500  CircuitLoweringState &loweringState);
501  bool handleForceNameAnnos(FModuleLike oldModule, AnnotationSet &annos,
502  CircuitLoweringState &loweringState);
503  hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
504  CircuitLoweringState &loweringState);
505  hw::HWModuleExternOp lowerExtModule(FExtModuleOp oldModule,
506  Block *topLevelModule,
507  CircuitLoweringState &loweringState);
508  hw::HWModuleExternOp lowerMemModule(FMemModuleOp oldModule,
509  Block *topLevelModule,
510  CircuitLoweringState &loweringState);
511 
512  LogicalResult
513  lowerModulePortsAndMoveBody(FModuleOp oldModule, hw::HWModuleOp newModule,
514  CircuitLoweringState &loweringState);
515  LogicalResult lowerModuleOperations(hw::HWModuleOp module,
516  CircuitLoweringState &loweringState);
517 };
518 
519 } // end anonymous namespace
520 
521 /// This is the pass constructor.
522 std::unique_ptr<mlir::Pass> circt::createLowerFIRRTLToHWPass(
523  bool enableAnnotationWarning, bool emitChiselAssertsAsSVA,
524  bool disableMemRandomization, bool disableRegRandomization) {
525  auto pass = std::make_unique<FIRRTLModuleLowering>();
526  if (enableAnnotationWarning)
527  pass->setEnableAnnotationWarning();
528  if (emitChiselAssertsAsSVA)
529  pass->setEmitChiselAssertAsSVA();
530  if (disableMemRandomization)
531  pass->setDisableMemRandomization();
532  if (disableRegRandomization)
533  pass->setDisableRegRandomization();
534  return pass;
535 }
536 
537 /// Run on the firrtl.circuit operation, lowering any firrtl.module operations
538 /// it contains.
539 void FIRRTLModuleLowering::runOnOperation() {
540 
541  // We run on the top level modules in the IR blob. Start by finding the
542  // firrtl.circuit within it. If there is none, then there is nothing to do.
543  auto *topLevelModule = getOperation().getBody();
544 
545  // Find the single firrtl.circuit in the module.
546  CircuitOp circuit;
547  for (auto &op : *topLevelModule) {
548  if ((circuit = dyn_cast<CircuitOp>(&op)))
549  break;
550  }
551 
552  if (!circuit)
553  return;
554 
555  auto *circuitBody = circuit.getBodyBlock();
556 
557  // Keep track of the mapping from old to new modules. The result may be null
558  // if lowering failed.
559  CircuitLoweringState state(
560  circuit, enableAnnotationWarning, emitChiselAssertsAsSVA,
561  &getAnalysis<InstanceGraph>(), &getAnalysis<NLATable>());
562 
563  SmallVector<hw::HWModuleOp, 32> modulesToProcess;
564 
565  AnnotationSet circuitAnno(circuit);
566  moveVerifAnno(getOperation(), circuitAnno, extractAssertAnnoClass,
567  "firrtl.extract.assert");
568  moveVerifAnno(getOperation(), circuitAnno, extractAssumeAnnoClass,
569  "firrtl.extract.assume");
570  moveVerifAnno(getOperation(), circuitAnno, extractCoverageAnnoClass,
571  "firrtl.extract.cover");
572  circuitAnno.removeAnnotationsWithClass(
574 
575  state.processRemainingAnnotations(circuit, circuitAnno);
576  // Iterate through each operation in the circuit body, transforming any
577  // FModule's we come across. If any module fails to lower, return early.
578  for (auto &op : make_early_inc_range(circuitBody->getOperations())) {
579  auto result =
580  TypeSwitch<Operation *, LogicalResult>(&op)
581  .Case<FModuleOp>([&](auto module) {
582  auto loweredMod = lowerModule(module, topLevelModule, state);
583  if (!loweredMod)
584  return failure();
585 
586  state.oldToNewModuleMap[&op] = loweredMod;
587  modulesToProcess.push_back(loweredMod);
588  // Lower all the alias types.
589  module.walk([&](Operation *op) {
590  for (auto res : op->getResults()) {
591  if (auto aliasType =
592  type_dyn_cast<BaseTypeAliasType>(res.getType()))
593  state.lowerType(aliasType, op->getLoc());
594  }
595  });
596  return lowerModulePortsAndMoveBody(module, loweredMod, state);
597  })
598  .Case<FExtModuleOp>([&](auto extModule) {
599  auto loweredMod =
600  lowerExtModule(extModule, topLevelModule, state);
601  if (!loweredMod)
602  return failure();
603  state.oldToNewModuleMap[&op] = loweredMod;
604  return success();
605  })
606  .Case<FMemModuleOp>([&](auto memModule) {
607  auto loweredMod =
608  lowerMemModule(memModule, topLevelModule, state);
609  if (!loweredMod)
610  return failure();
611  state.oldToNewModuleMap[&op] = loweredMod;
612  return success();
613  })
614  .Default([&](Operation *op) {
615  // We don't know what this op is. If it has no illegal FIRRTL
616  // types, we can forward the operation. Otherwise, we emit an
617  // error and drop the operation from the circuit.
618  if (succeeded(verifyOpLegality(op)))
619  op->moveBefore(topLevelModule, topLevelModule->end());
620  else
621  return failure();
622  return success();
623  });
624  if (failed(result))
625  return signalPassFailure();
626  }
627  // Ensure no more TypeDecl can be added to the global TypeScope.
628  state.typeAliases.freeze();
629  // Handle the creation of the module hierarchy metadata.
630 
631  // Collect the two sets of hierarchy files from the circuit. Some of them will
632  // be rooted at the test harness, the others will be rooted at the DUT.
633  SmallVector<Attribute> dutHierarchyFiles;
634  SmallVector<Attribute> testHarnessHierarchyFiles;
635  circuitAnno.removeAnnotations([&](Annotation annotation) {
636  if (annotation.isClass(moduleHierAnnoClass)) {
637  auto file = hw::OutputFileAttr::getFromFilename(
638  &getContext(),
639  annotation.getMember<StringAttr>("filename").getValue(),
640  /*excludeFromFileList=*/true);
641  dutHierarchyFiles.push_back(file);
642  return true;
643  }
644  if (annotation.isClass(testHarnessHierAnnoClass)) {
645  auto file = hw::OutputFileAttr::getFromFilename(
646  &getContext(),
647  annotation.getMember<StringAttr>("filename").getValue(),
648  /*excludeFromFileList=*/true);
649  // If there is no testHarness, we print the hiearchy for this file
650  // starting at the DUT.
651  if (state.getTestHarness())
652  testHarnessHierarchyFiles.push_back(file);
653  else
654  dutHierarchyFiles.push_back(file);
655  return true;
656  }
657  return false;
658  });
659  // Attach the lowered form of these annotations.
660  if (!dutHierarchyFiles.empty())
661  state.oldToNewModuleMap[state.getDut()]->setAttr(
663  ArrayAttr::get(&getContext(), dutHierarchyFiles));
664  if (!testHarnessHierarchyFiles.empty())
665  state.oldToNewModuleMap[state.getTestHarness()]->setAttr(
667  ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
668 
669  // Finally, lower all operations.
670  auto result = mlir::failableParallelForEachN(
671  &getContext(), 0, modulesToProcess.size(), [&](auto index) {
672  return lowerModuleOperations(modulesToProcess[index], state);
673  });
674 
675  // If any module bodies failed to lower, return early.
676  if (failed(result))
677  return signalPassFailure();
678 
679  // Move binds from inside modules to outside modules.
680  for (auto bind : state.binds) {
681  bind->moveBefore(bind->getParentOfType<hw::HWModuleOp>());
682  }
683 
684  // Finally delete all the old modules.
685  for (auto oldNew : state.oldToNewModuleMap)
686  oldNew.first->erase();
687 
688  // Emit all the macros and preprocessor gunk at the start of the file.
689  lowerFileHeader(circuit, state);
690 
691  // Now that the modules are moved over, remove the Circuit.
692  circuit.erase();
693 }
694 
695 /// Emit the file header that defines a bunch of macros.
696 void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
697  CircuitLoweringState &state) {
698  // Intentionally pass an UnknownLoc here so we don't get line number
699  // comments on the output of this boilerplate in generated Verilog.
700  ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), op);
701 
702  StringSet<> emittedDecls;
703 
704  auto emitDecl = [&](StringRef name, ArrayAttr args) {
705  if (emittedDecls.count(name))
706  return;
707  emittedDecls.insert(name);
708  OpBuilder::InsertionGuard guard(b);
709  b.setInsertionPointAfter(op);
710  b.create<sv::MacroDeclOp>(name, args, StringAttr());
711  };
712 
713  // TODO: We could have an operation for macros and uses of them, and
714  // even turn them into symbols so we can DCE unused macro definitions.
715  auto emitDefine = [&](StringRef name, StringRef body, ArrayAttr args = {}) {
716  emitDecl(name, args);
717  b.create<sv::MacroDefOp>(name, body);
718  };
719 
720  // Helper function to emit a "#ifdef guard" with a `define in the then and
721  // optionally in the else branch.
722  auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
723  StringRef defineTrue = "",
724  StringRef defineFalse = StringRef()) {
725  if (!defineFalse.data()) {
726  assert(defineTrue.data() && "didn't define anything");
727  b.create<sv::IfDefOp>(guard, [&]() { emitDefine(defName, defineTrue); });
728  } else {
729  b.create<sv::IfDefOp>(
730  guard,
731  [&]() {
732  if (defineTrue.data())
733  emitDefine(defName, defineTrue);
734  },
735  [&]() { emitDefine(defName, defineFalse); });
736  }
737  };
738 
739  // Helper function to emit #ifndef guard.
740  auto emitGuard = [&](const char *guard, llvm::function_ref<void(void)> body) {
741  b.create<sv::IfDefOp>(
742  guard, []() {}, body);
743  };
744 
745  bool needsRandomizeRegInit =
746  state.used_RANDOMIZE_REG_INIT && !disableRegRandomization;
747  bool needsRandomizeMemInit =
748  state.used_RANDOMIZE_MEM_INIT && !disableMemRandomization;
749 
750  // If none of the macros are needed, then don't emit any header at all, not
751  // even the header comment.
752  if (!state.used_RANDOMIZE_GARBAGE_ASSIGN && !needsRandomizeRegInit &&
753  !needsRandomizeMemInit && !state.used_PRINTF_COND &&
754  !state.used_ASSERT_VERBOSE_COND && !state.used_STOP_COND)
755  return;
756 
757  b.create<sv::VerbatimOp>(
758  "// Standard header to adapt well known macros to our needs.");
759 
760  bool needRandom = false;
761  if (state.used_RANDOMIZE_GARBAGE_ASSIGN) {
762  emitGuard("RANDOMIZE", [&]() {
763  emitGuardedDefine("RANDOMIZE_GARBAGE_ASSIGN", "RANDOMIZE");
764  });
765  needRandom = true;
766  }
767  if (needsRandomizeRegInit) {
768  emitGuard("RANDOMIZE",
769  [&]() { emitGuardedDefine("RANDOMIZE_REG_INIT", "RANDOMIZE"); });
770  needRandom = true;
771  }
772  if (needsRandomizeMemInit) {
773  emitGuard("RANDOMIZE",
774  [&]() { emitGuardedDefine("RANDOMIZE_MEM_INIT", "RANDOMIZE"); });
775  needRandom = true;
776  }
777 
778  if (needRandom) {
779  b.create<sv::VerbatimOp>(
780  "\n// RANDOM may be set to an expression that produces a 32-bit "
781  "random unsigned value.");
782  emitGuardedDefine("RANDOM", "RANDOM", StringRef(), "$random");
783  }
784 
785  if (state.used_PRINTF_COND) {
786  b.create<sv::VerbatimOp>(
787  "\n// Users can define 'PRINTF_COND' to add an extra gate to "
788  "prints.");
789  emitGuard("PRINTF_COND_", [&]() {
790  emitGuardedDefine("PRINTF_COND", "PRINTF_COND_", "(`PRINTF_COND)", "1");
791  });
792  }
793 
794  if (state.used_ASSERT_VERBOSE_COND) {
795  b.create<sv::VerbatimOp>(
796  "\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
797  "gate to assert error printing.");
798  emitGuard("ASSERT_VERBOSE_COND_", [&]() {
799  emitGuardedDefine("ASSERT_VERBOSE_COND", "ASSERT_VERBOSE_COND_",
800  "(`ASSERT_VERBOSE_COND)", "1");
801  });
802  }
803 
804  if (state.used_STOP_COND) {
805  b.create<sv::VerbatimOp>(
806  "\n// Users can define 'STOP_COND' to add an extra gate "
807  "to stop conditions.");
808  emitGuard("STOP_COND_", [&]() {
809  emitGuardedDefine("STOP_COND", "STOP_COND_", "(`STOP_COND)", "1");
810  });
811  }
812 
813  if (needRandom) {
814  b.create<sv::VerbatimOp>(
815  "\n// Users can define INIT_RANDOM as general code that gets "
816  "injected "
817  "into the\n// initializer block for modules with registers.");
818  emitGuardedDefine("INIT_RANDOM", "INIT_RANDOM", StringRef(), "");
819 
820  b.create<sv::VerbatimOp>(
821  "\n// If using random initialization, you can also define "
822  "RANDOMIZE_DELAY to\n// customize the delay used, otherwise 0.002 "
823  "is used.");
824  emitGuardedDefine("RANDOMIZE_DELAY", "RANDOMIZE_DELAY", StringRef(),
825  "0.002");
826 
827  b.create<sv::VerbatimOp>(
828  "\n// Define INIT_RANDOM_PROLOG_ for use in our modules below.");
829  emitGuard("INIT_RANDOM_PROLOG_", [&]() {
830  b.create<sv::IfDefOp>(
831  "RANDOMIZE",
832  [&]() {
833  emitGuardedDefine("VERILATOR", "INIT_RANDOM_PROLOG_",
834  "`INIT_RANDOM",
835  "`INIT_RANDOM #`RANDOMIZE_DELAY begin end");
836  },
837  [&]() { emitDefine("INIT_RANDOM_PROLOG_", ""); });
838  });
839 
840  b.create<sv::VerbatimOp>("\n// Include register initializers in init "
841  "blocks unless synthesis is set");
842  emitGuard("SYNTHESIS", [&] {
843  emitGuardedDefine("ENABLE_INITIAL_REG_", "ENABLE_INITIAL_REG_",
844  StringRef(), "");
845  });
846 
847  b.create<sv::VerbatimOp>("\n// Include rmemory initializers in init "
848  "blocks unless synthesis is set");
849  emitGuard("SYNTHESIS", [&] {
850  emitGuardedDefine("ENABLE_INITIAL_MEM_", "ENABLE_INITIAL_MEM_",
851  StringRef(), "");
852  });
853  }
854 
855  if (state.used_RANDOMIZE_GARBAGE_ASSIGN) {
856  b.create<sv::VerbatimOp>(
857  "\n// RANDOMIZE_GARBAGE_ASSIGN enable range checks for mem "
858  "assignments.");
859  emitGuard("RANDOMIZE_GARBAGE_ASSIGN_BOUND_CHECK", [&]() {
860  b.create<sv::IfDefOp>(
861  "RANDOMIZE_GARBAGE_ASSIGN",
862  [&]() {
863  StringRef args[] = {"INDEX", "VALUE", "SIZE"};
864  emitDefine("RANDOMIZE_GARBAGE_ASSIGN_BOUND_CHECK",
865  " ((INDEX) < (SIZE) ? (VALUE) : {`RANDOM})",
866  b.getStrArrayAttr(ArrayRef(args)));
867  },
868  [&]() {
869  StringRef args[] = {"INDEX", "VALUE", "SIZE"};
870  emitDefine("RANDOMIZE_GARBAGE_ASSIGN_BOUND_CHECK", "(VALUE)",
871  b.getStrArrayAttr(args));
872  });
873  });
874  }
875 
876  // Blank line to separate the header from the modules.
877  b.create<sv::VerbatimOp>("");
878 }
879 
880 LogicalResult
881 FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
882  SmallVectorImpl<hw::PortInfo> &ports,
883  Operation *moduleOp, StringRef moduleName,
884  CircuitLoweringState &loweringState) {
885  ports.reserve(firrtlPorts.size());
886  size_t numArgs = 0;
887  size_t numResults = 0;
888  for (auto firrtlPort : firrtlPorts) {
889  hw::PortInfo hwPort;
890  hwPort.name = firrtlPort.name;
891  hwPort.type = loweringState.lowerType(firrtlPort.type, firrtlPort.loc);
892  if (firrtlPort.sym)
893  if (firrtlPort.sym.size() > 1 ||
894  (firrtlPort.sym.size() == 1 && !firrtlPort.sym.getSymName()))
895  return emitError(firrtlPort.loc)
896  << "cannot lower aggregate port " << firrtlPort.name
897  << " with field sensitive symbols, HW dialect does not support "
898  "per field symbols yet.";
899  hwPort.sym = firrtlPort.sym;
900  bool hadDontTouch = firrtlPort.annotations.removeDontTouch();
901  if (hadDontTouch && !hwPort.sym) {
902  if (hwPort.type.isInteger(0)) {
903  if (enableAnnotationWarning) {
904  mlir::emitWarning(firrtlPort.loc)
905  << "zero width port " << hwPort.name
906  << " has dontTouch annotation, removing anyway";
907  }
908  continue;
909  }
911  moduleOp->getContext(),
912  Twine("__") + moduleName + Twine("__") + firrtlPort.name.strref()));
913  }
914 
915  // We can't lower all types, so make sure to cleanly reject them.
916  if (!hwPort.type) {
917  moduleOp->emitError("cannot lower this port type to HW");
918  return failure();
919  }
920 
921  // If this is a zero bit port, just drop it. It doesn't matter if it is
922  // input, output, or inout. We don't want these at the HW level.
923  if (hwPort.type.isInteger(0)) {
924  if (hwPort.sym && !hwPort.sym.empty()) {
925  return mlir::emitError(firrtlPort.loc)
926  << "zero width port " << hwPort.name
927  << " is referenced by name [" << hwPort.sym
928  << "] (e.g. in an XMR) but must be removed";
929  }
930  continue;
931  }
932 
933  // Figure out the direction of the port.
934  if (firrtlPort.isOutput()) {
936  hwPort.argNum = numResults++;
937  } else if (firrtlPort.isInput()) {
939  hwPort.argNum = numArgs++;
940  } else {
941  // If the port is an inout bundle or contains an analog type, then it is
942  // implicitly inout.
943  hwPort.type = hw::InOutType::get(hwPort.type);
945  hwPort.argNum = numArgs++;
946  }
947  hwPort.loc = firrtlPort.loc;
948  ports.push_back(hwPort);
949  loweringState.processRemainingAnnotations(moduleOp, firrtlPort.annotations);
950  }
951  return success();
952 }
953 
954 /// Map the parameter specifier on the specified extmodule into the HWModule
955 /// representation for parameters. If `ignoreValues` is true, all the values
956 /// are dropped.
957 static ArrayAttr getHWParameters(FExtModuleOp module, bool ignoreValues) {
958  auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
959  return cast<ParamDeclAttr>(a);
960  });
961  if (params.empty())
962  return {};
963 
964  Builder builder(module);
965 
966  // Map the attributes over from firrtl attributes to HW attributes
967  // directly. MLIR's DictionaryAttr always stores keys in the dictionary
968  // in sorted order which is nicely stable.
969  SmallVector<Attribute> newParams;
970  for (const ParamDeclAttr &entry : params) {
971  auto name = entry.getName();
972  auto type = entry.getType();
973  auto value = ignoreValues ? Attribute() : entry.getValue();
974  auto paramAttr =
975  hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
976  newParams.push_back(paramAttr);
977  }
978  return builder.getArrayAttr(newParams);
979 }
980 
981 bool FIRRTLModuleLowering::handleForceNameAnnos(
982  FModuleLike oldModule, AnnotationSet &annos,
983  CircuitLoweringState &loweringState) {
984  bool failed = false;
985  // Remove ForceNameAnnotations by generating verilogNames on instances.
986  annos.removeAnnotations([&](Annotation anno) {
987  if (!anno.isClass(forceNameAnnoClass))
988  return false;
989 
990  auto sym = anno.getMember<FlatSymbolRefAttr>("circt.nonlocal");
991  // This must be a non-local annotation due to how the Chisel API is
992  // implemented.
993  //
994  // TODO: handle this in some sensible way based on what the SFC does with
995  // a local annotation.
996  if (!sym) {
997  auto diag = oldModule.emitOpError()
998  << "contains a '" << forceNameAnnoClass
999  << "' that is not a non-local annotation";
1000  diag.attachNote() << "the erroneous annotation is '" << anno.getDict()
1001  << "'\n";
1002  failed = true;
1003  return false;
1004  }
1005 
1006  auto nla = loweringState.nlaTable->getNLA(sym.getAttr());
1007  // The non-local anchor must exist.
1008  //
1009  // TODO: handle this with annotation verification.
1010  if (!nla) {
1011  auto diag = oldModule.emitOpError()
1012  << "contains a '" << forceNameAnnoClass
1013  << "' whose non-local symbol, '" << sym
1014  << "' does not exist in the circuit";
1015  diag.attachNote() << "the erroneous annotation is '" << anno.getDict();
1016  failed = true;
1017  return false;
1018  }
1019 
1020  // Add the forced name to global state (keyed by a pseudo-inner name ref).
1021  // Error out if this key is alredy in use.
1022  //
1023  // TODO: this error behavior can be relaxed to always overwrite with the
1024  // new forced name (the bug-compatible behavior of the Chisel
1025  // implementation) or fixed to duplicate modules such that the naming can
1026  // be applied.
1027  auto inst =
1028  cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1029  auto inserted = loweringState.instanceForceNames.insert(
1030  {{inst.getModule(), inst.getName()}, anno.getMember("name")});
1031  if (!inserted.second &&
1032  (anno.getMember("name") != (inserted.first->second))) {
1033  auto diag = oldModule.emitError()
1034  << "contained multiple '" << forceNameAnnoClass
1035  << "' with different names: " << inserted.first->second
1036  << " was not " << anno.getMember("name");
1037  diag.attachNote() << "the erroneous annotation is '" << anno.getDict()
1038  << "'";
1039  failed = true;
1040  return false;
1041  }
1042  return true;
1043  });
1044  return failed;
1045 }
1046 
1047 hw::HWModuleExternOp
1048 FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1049  Block *topLevelModule,
1050  CircuitLoweringState &loweringState) {
1051  // Map the ports over, lowering their types as we go.
1052  SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1053  SmallVector<hw::PortInfo, 8> ports;
1054  if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1055  loweringState)))
1056  return {};
1057 
1058  StringRef verilogName;
1059  if (auto defName = oldModule.getDefname())
1060  verilogName = defName.value();
1061 
1062  // Build the new hw.module op.
1063  auto builder = OpBuilder::atBlockEnd(topLevelModule);
1064  auto nameAttr = builder.getStringAttr(oldModule.getName());
1065  // Map over parameters if present. Drop all values as we do so, so there are
1066  // no known default values in the extmodule. This ensures that the
1067  // hw.instance will print all the parameters when generating verilog.
1068  auto parameters = getHWParameters(oldModule, /*ignoreValues=*/true);
1069  auto newModule = builder.create<hw::HWModuleExternOp>(
1070  oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1071  SymbolTable::setSymbolVisibility(newModule,
1072  SymbolTable::getSymbolVisibility(oldModule));
1073 
1074  bool hasOutputPort =
1075  llvm::any_of(firrtlPorts, [&](auto p) { return p.isOutput(); });
1076  if (!hasOutputPort &&
1078  loweringState.isInDUT(oldModule))
1079  newModule->setAttr("firrtl.extract.cover.extra", builder.getUnitAttr());
1080 
1081  AnnotationSet annos(oldModule);
1082  if (handleForceNameAnnos(oldModule, annos, loweringState))
1083  return {};
1084 
1085  loweringState.processRemainingAnnotations(oldModule, annos);
1086  return newModule;
1087 }
1088 
1089 hw::HWModuleExternOp
1090 FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1091  Block *topLevelModule,
1092  CircuitLoweringState &loweringState) {
1093  // Map the ports over, lowering their types as we go.
1094  SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1095  SmallVector<hw::PortInfo, 8> ports;
1096  if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1097  loweringState)))
1098  return {};
1099 
1100  // Build the new hw.module op.
1101  auto builder = OpBuilder::atBlockEnd(topLevelModule);
1102  auto newModule = builder.create<hw::HWModuleExternOp>(
1103  oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1104  oldModule.getModuleNameAttr());
1105  loweringState.processRemainingAnnotations(oldModule,
1106  AnnotationSet(oldModule));
1107  return newModule;
1108 }
1109 
1110 /// Run on each firrtl.module, creating a basic hw.module for the firrtl module.
1111 hw::HWModuleOp
1112 FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1113  CircuitLoweringState &loweringState) {
1114  // Map the ports over, lowering their types as we go.
1115  SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1116  SmallVector<hw::PortInfo, 8> ports;
1117  if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1118  loweringState)))
1119  return {};
1120 
1121  // Build the new hw.module op.
1122  auto builder = OpBuilder::atBlockEnd(topLevelModule);
1123  auto nameAttr = builder.getStringAttr(oldModule.getName());
1124  auto newModule =
1125  builder.create<hw::HWModuleOp>(oldModule.getLoc(), nameAttr, ports);
1126  if (auto outputFile = oldModule->getAttr("output_file"))
1127  newModule->setAttr("output_file", outputFile);
1128  if (auto comment = oldModule->getAttrOfType<StringAttr>("comment"))
1129  newModule.setCommentAttr(comment);
1130 
1131  // Move SV attributes.
1132  if (auto svAttrs = sv::getSVAttributes(oldModule))
1133  sv::setSVAttributes(newModule, svAttrs);
1134 
1135  // Pass along the number of random initialization bits needed for this module.
1136  if (auto randomWidth = oldModule->getAttr("firrtl.random_init_width"))
1137  newModule->setAttr("firrtl.random_init_width", randomWidth);
1138 
1139  // If the circuit has an entry point, set all other modules private.
1140  // Otherwise, mark all modules as public.
1141  SymbolTable::setSymbolVisibility(newModule,
1142  SymbolTable::getSymbolVisibility(oldModule));
1143 
1144  // Transform module annotations
1145  AnnotationSet annos(oldModule);
1146 
1148  newModule->setAttr("firrtl.extract.cover.extra", builder.getUnitAttr());
1149 
1150  // If this is in the test harness, make sure it goes to the test directory.
1151  if (auto testBenchDir = loweringState.getTestBenchDirectory())
1152  if (loweringState.isInTestHarness(oldModule)) {
1153  newModule->setAttr("output_file", testBenchDir);
1154  newModule->setAttr("firrtl.extract.do_not_extract",
1155  builder.getUnitAttr());
1156  newModule.setCommentAttr(
1157  builder.getStringAttr("VCS coverage exclude_file"));
1158  }
1159 
1160  if (handleForceNameAnnos(oldModule, annos, loweringState))
1161  return {};
1162 
1163  loweringState.processRemainingAnnotations(oldModule, annos);
1164  return newModule;
1165 }
1166 
1167 /// Given a value of analog type, check to see the only use of it is an
1168 /// attach. If so, remove the attach and return the value being attached to
1169 /// it, converted to an HW inout type. If this isn't a situation we can
1170 /// handle, just return null.
1171 static Value tryEliminatingAttachesToAnalogValue(Value value,
1172  Operation *insertPoint) {
1173  if (!value.hasOneUse())
1174  return {};
1175 
1176  auto attach = dyn_cast<AttachOp>(*value.user_begin());
1177  if (!attach || attach.getNumOperands() != 2)
1178  return {};
1179 
1180  // Don't optimize zero bit analogs.
1181  auto loweredType = lowerType(value.getType());
1182  if (loweredType.isInteger(0))
1183  return {};
1184 
1185  // Check to see if the attached value dominates the insertion point. If
1186  // not, just fail.
1187  auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1188  auto *op = attachedValue.getDefiningOp();
1189  if (op && op->getBlock() == insertPoint->getBlock() &&
1190  !op->isBeforeInBlock(insertPoint))
1191  return {};
1192 
1193  attach.erase();
1194 
1195  ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1196  return castFromFIRRTLType(attachedValue, hw::InOutType::get(loweredType),
1197  builder);
1198 }
1199 
1200 /// Given a value of flip type, check to see if all of the uses of it are
1201 /// connects. If so, remove the connects and return the value being connected
1202 /// to it, converted to an HW type. If this isn't a situation we can handle,
1203 /// just return null.
1204 ///
1205 /// This can happen when there are no connects to the value. The 'mergePoint'
1206 /// location is where a 'hw.merge' operation should be inserted if needed.
1207 static Value
1208 tryEliminatingConnectsToValue(Value flipValue, Operation *insertPoint,
1209  CircuitLoweringState &loweringState) {
1210  // Handle analog's separately.
1211  if (type_isa<AnalogType>(flipValue.getType()))
1212  return tryEliminatingAttachesToAnalogValue(flipValue, insertPoint);
1213 
1214  Operation *connectOp = nullptr;
1215  for (auto &use : flipValue.getUses()) {
1216  // We only know how to deal with connects where this value is the
1217  // destination.
1218  if (use.getOperandNumber() != 0)
1219  return {};
1220  if (!isa<ConnectOp, StrictConnectOp>(use.getOwner()))
1221  return {};
1222 
1223  // We only support things with a single connect.
1224  if (connectOp)
1225  return {};
1226  connectOp = use.getOwner();
1227  }
1228 
1229  // We don't have an HW equivalent of "poison" so just don't special case
1230  // the case where there are no connects other uses of an output.
1231  if (!connectOp)
1232  return {}; // TODO: Emit an sv.constant here since it is unconnected.
1233 
1234  // Don't special case zero-bit results.
1235  auto loweredType =
1236  loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1237  if (loweredType.isInteger(0))
1238  return {};
1239 
1240  // Convert each connect into an extended version of its operand being
1241  // output.
1242  ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1243 
1244  auto connectSrc = connectOp->getOperand(1);
1245 
1246  // Directly forward foreign types.
1247  if (!connectSrc.getType().isa<FIRRTLType>()) {
1248  connectOp->erase();
1249  return connectSrc;
1250  }
1251 
1252  // Convert fliped sources to passive sources.
1253  if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1254  connectSrc = builder
1255  .create<mlir::UnrealizedConversionCastOp>(
1256  type_cast<FIRRTLBaseType>(connectSrc.getType())
1257  .getPassiveType(),
1258  connectSrc)
1259  .getResult(0);
1260 
1261  // We know it must be the destination operand due to the types, but the
1262  // source may not match the destination width.
1263  auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1264 
1265  if (destTy != connectSrc.getType() &&
1266  (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1267  isa<BaseTypeAliasType>(destTy))) {
1268  connectSrc =
1269  builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1270  }
1271  if (!destTy.isGround()) {
1272  // If types are not ground type and they don't match, we give up.
1273  if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1274  return {};
1275  } else if (destTy.getBitWidthOrSentinel() !=
1276  type_cast<FIRRTLBaseType>(connectSrc.getType())
1277  .getBitWidthOrSentinel()) {
1278  // The only type mismatchs we care about is due to integer width
1279  // differences.
1280  auto destWidth = destTy.getBitWidthOrSentinel();
1281  assert(destWidth != -1 && "must know integer widths");
1282  connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1283  }
1284 
1285  // Remove the connect and use its source as the value for the output.
1286  connectOp->erase();
1287 
1288  // Convert from FIRRTL type to builtin type.
1289  return castFromFIRRTLType(connectSrc, loweredType, builder);
1290 }
1291 
1292 static SmallVector<SubfieldOp> getAllFieldAccesses(Value structValue,
1293  StringRef field) {
1294  SmallVector<SubfieldOp> accesses;
1295  for (auto *op : structValue.getUsers()) {
1296  assert(isa<SubfieldOp>(op));
1297  auto fieldAccess = cast<SubfieldOp>(op);
1298  auto elemIndex =
1299  fieldAccess.getInput().getType().get().getElementIndex(field);
1300  if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1301  accesses.push_back(fieldAccess);
1302  }
1303  return accesses;
1304 }
1305 
1306 /// Now that we have the operations for the hw.module's corresponding to the
1307 /// firrtl.module's, we can go through and move the bodies over, updating the
1308 /// ports and output op.
1309 LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1310  FModuleOp oldModule, hw::HWModuleOp newModule,
1311  CircuitLoweringState &loweringState) {
1312  ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1313 
1314  // Use a placeholder instruction be a cursor that indicates where we want to
1315  // move the new function body to. This is important because we insert some
1316  // ops at the start of the function and some at the end, and the body is
1317  // currently empty to avoid iterator invalidation.
1318  auto cursor = bodyBuilder.create<hw::ConstantOp>(APInt(1, 1));
1319  bodyBuilder.setInsertionPoint(cursor);
1320 
1321  // Insert argument casts, and re-vector users in the old body to use them.
1322  SmallVector<PortInfo> ports = oldModule.getPorts();
1323  assert(oldModule.getBody().getNumArguments() == ports.size() &&
1324  "port count mismatch");
1325 
1326  size_t nextNewArg = 0;
1327  size_t firrtlArg = 0;
1328  SmallVector<Value, 4> outputs;
1329 
1330  // This is the terminator in the new module.
1331  auto outputOp = newModule.getBodyBlock()->getTerminator();
1332  ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1333 
1334  for (auto &port : ports) {
1335  // Inputs and outputs are both modeled as arguments in the FIRRTL level.
1336  auto oldArg = oldModule.getBody().getArgument(firrtlArg++);
1337 
1338  bool isZeroWidth =
1339  type_isa<FIRRTLBaseType>(port.type) &&
1340  type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1341 
1342  if (!port.isOutput() && !isZeroWidth) {
1343  // Inputs and InOuts are modeled as arguments in the result, so we can
1344  // just map them over. We model zero bit outputs as inouts.
1345  Value newArg = newModule.getBody().getArgument(nextNewArg++);
1346 
1347  // Cast the argument to the old type, reintroducing sign information in
1348  // the hw.module body.
1349  newArg = castToFIRRTLType(newArg, oldArg.getType(), bodyBuilder);
1350  // Switch all uses of the old operands to the new ones.
1351  oldArg.replaceAllUsesWith(newArg);
1352  continue;
1353  }
1354 
1355  // We lower zero width inout and outputs to a wire that isn't connected to
1356  // anything outside the module. Inputs are lowered to zero.
1357  if (isZeroWidth && port.isInput()) {
1358  Value newArg = bodyBuilder
1359  .create<WireOp>(port.type, "." + port.getName().str() +
1360  ".0width_input")
1361  .getResult();
1362  oldArg.replaceAllUsesWith(newArg);
1363  continue;
1364  }
1365 
1366  if (auto value =
1367  tryEliminatingConnectsToValue(oldArg, outputOp, loweringState)) {
1368  // If we were able to find the value being connected to the output,
1369  // directly use it!
1370  outputs.push_back(value);
1371  assert(oldArg.use_empty() && "should have removed all uses of oldArg");
1372  continue;
1373  }
1374 
1375  // Outputs need a temporary wire so they can be connect'd to, which we
1376  // then return.
1377  auto newArg = bodyBuilder.create<WireOp>(
1378  port.type, "." + port.getName().str() + ".output");
1379 
1380  // Switch all uses of the old operands to the new ones.
1381  oldArg.replaceAllUsesWith(newArg.getResult());
1382 
1383  // Don't output zero bit results or inouts.
1384  auto resultHWType = loweringState.lowerType(port.type, port.loc);
1385  if (!resultHWType.isInteger(0)) {
1386  auto output =
1387  castFromFIRRTLType(newArg.getResult(), resultHWType, outputBuilder);
1388  auto idx = newModule.getNumInputPorts() + outputs.size();
1389  outputs.push_back(output);
1390 
1391  // If output port has symbol, move it to this wire.
1392  if (auto sym = newModule.getPortSymbolAttr(idx)) {
1393  newArg.setInnerSymAttr(sym);
1394  newModule.setPortSymbolAttr(idx, {});
1395  }
1396  }
1397  }
1398 
1399  // Update the hw.output terminator with the list of outputs we have.
1400  outputOp->setOperands(outputs);
1401 
1402  // Finally splice the body over, don't move the old terminator over though.
1403  auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1404  auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1405  newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1406  oldBlockInstList.begin(), oldBlockInstList.end());
1407 
1408  // We are done with our cursor op.
1409  cursor.erase();
1410 
1411  return success();
1412 }
1413 
1414 //===----------------------------------------------------------------------===//
1415 // Module Body Lowering Pass
1416 //===----------------------------------------------------------------------===//
1417 
1418 namespace {
1419 
1420 struct FIRRTLLowering : public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1421 
1422  FIRRTLLowering(hw::HWModuleOp module, CircuitLoweringState &circuitState)
1423  : theModule(module), circuitState(circuitState),
1424  builder(module.getLoc(), module.getContext()), moduleNamespace(module),
1425  backedgeBuilder(builder, module.getLoc()) {}
1426 
1427  LogicalResult run();
1428 
1429  // Helpers.
1430  Value getOrCreateClockConstant(seq::ClockConst clock);
1431  Value getOrCreateIntConstant(const APInt &value);
1432  Value getOrCreateIntConstant(unsigned numBits, uint64_t val,
1433  bool isSigned = false) {
1434  return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1435  }
1436  Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1437  Value getOrCreateXConstant(unsigned numBits);
1438  Value getOrCreateZConstant(Type type);
1439  Value getPossiblyInoutLoweredValue(Value value);
1440  Value getLoweredValue(Value value);
1441  Value getLoweredNonClockValue(Value value);
1442  Value getLoweredAndExtendedValue(Value value, Type destType);
1443  Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1444  LogicalResult setLowering(Value orig, Value result);
1445  LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1446  template <typename ResultOpType, typename... CtorArgTypes>
1447  LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1448  template <typename ResultOpType, typename... CtorArgTypes>
1449  LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1450  Backedge createBackedge(Location loc, Type type);
1451  Backedge createBackedge(Value orig, Type type);
1452  bool updateIfBackedge(Value dest, Value src);
1453 
1454  /// Returns true if the lowered operation requires an inner symbol on it.
1455  bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1457  return true;
1458  if (!hasDroppableName(op))
1459  return true;
1460  if (auto forceable = dyn_cast<Forceable>(op.getOperation()))
1461  if (forceable.isForceable())
1462  return true;
1463  return false;
1464  }
1465 
1466  /// Gets the lowered InnerSymAttr of this operation. If the operation is
1467  /// DontTouched, has a non-droppable name, or is forceable, then we will
1468  /// ensure that the InnerSymAttr has a symbol with fieldID zero.
1469  hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1470  auto attr = op.getInnerSymAttr();
1471  // TODO: we should be checking for symbol collisions here and renaming as
1472  // neccessary. As well, we should record the renamings in a map so that we
1473  // can update any InnerRefAttrs that we find.
1474  if (requiresInnerSymbol(op))
1475  std::tie(attr, std::ignore) = getOrAddInnerSym(
1476  op.getContext(), attr, 0,
1477  [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
1478  return attr;
1479  }
1480 
1481  void runWithInsertionPointAtEndOfBlock(std::function<void(void)> fn,
1482  Region &region);
1483 
1484  /// Return a read value for the specified inout value, auto-uniquing them.
1485  Value getReadValue(Value v);
1486  /// Return an `i1` value for the specified value, auto-uniqueing them.
1487  Value getNonClockValue(Value v);
1488 
1489  void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1490  ::ResetType resetStyle, sv::EventControl resetEdge,
1491  Value reset, std::function<void(void)> body = {},
1492  std::function<void(void)> resetBody = {});
1493  void addToAlwaysBlock(Value clock, std::function<void(void)> body = {}) {
1494  addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, ::ResetType(),
1495  sv::EventControl(), Value(), body,
1496  std::function<void(void)>());
1497  }
1498 
1499  void addToIfDefBlock(StringRef cond, std::function<void(void)> thenCtor,
1500  std::function<void(void)> elseCtor = {});
1501  void addToInitialBlock(std::function<void(void)> body);
1502  void addIfProceduralBlock(Value cond, std::function<void(void)> thenCtor,
1503  std::function<void(void)> elseCtor = {});
1504  Value getExtOrTruncAggregateValue(Value array, FIRRTLBaseType sourceType,
1505  FIRRTLBaseType destType,
1506  bool allowTruncate);
1507  Value createArrayIndexing(Value array, Value index);
1508  Value createValueWithMuxAnnotation(Operation *op, bool isMux2);
1509 
1513 
1514  // Lowering hooks.
1515  enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1516  UnloweredOpResult handleUnloweredOp(Operation *op);
1517  LogicalResult visitExpr(ConstantOp op);
1518  LogicalResult visitExpr(SpecialConstantOp op);
1519  LogicalResult visitExpr(SubindexOp op);
1520  LogicalResult visitExpr(SubaccessOp op);
1521  LogicalResult visitExpr(SubfieldOp op);
1522  LogicalResult visitExpr(VectorCreateOp op);
1523  LogicalResult visitExpr(BundleCreateOp op);
1524  LogicalResult visitExpr(FEnumCreateOp op);
1525  LogicalResult visitExpr(AggregateConstantOp op);
1526  LogicalResult visitExpr(IsTagOp op);
1527  LogicalResult visitExpr(SubtagOp op);
1528  LogicalResult visitUnhandledOp(Operation *op) { return failure(); }
1529  LogicalResult visitInvalidOp(Operation *op) { return failure(); }
1530 
1531  // Declarations.
1532  LogicalResult visitDecl(WireOp op);
1533  LogicalResult visitDecl(NodeOp op);
1534  LogicalResult visitDecl(RegOp op);
1535  LogicalResult visitDecl(RegResetOp op);
1536  LogicalResult visitDecl(MemOp op);
1537  LogicalResult visitDecl(InstanceOp op);
1538  LogicalResult visitDecl(VerbatimWireOp op);
1539 
1540  // Unary Ops.
1541  LogicalResult lowerNoopCast(Operation *op);
1542  LogicalResult visitExpr(AsSIntPrimOp op);
1543  LogicalResult visitExpr(AsUIntPrimOp op);
1544  LogicalResult visitExpr(AsClockPrimOp op);
1545  LogicalResult visitExpr(AsAsyncResetPrimOp op) { return lowerNoopCast(op); }
1546 
1547  LogicalResult visitExpr(HWStructCastOp op);
1548  LogicalResult visitExpr(BitCastOp op);
1549  LogicalResult visitExpr(mlir::UnrealizedConversionCastOp op);
1550  LogicalResult visitExpr(CvtPrimOp op);
1551  LogicalResult visitExpr(NotPrimOp op);
1552  LogicalResult visitExpr(NegPrimOp op);
1553  LogicalResult visitExpr(PadPrimOp op);
1554  LogicalResult visitExpr(XorRPrimOp op);
1555  LogicalResult visitExpr(AndRPrimOp op);
1556  LogicalResult visitExpr(OrRPrimOp op);
1557 
1558  // Binary Ops.
1559  template <typename ResultUnsignedOpType,
1560  typename ResultSignedOpType = ResultUnsignedOpType>
1561  LogicalResult lowerBinOp(Operation *op);
1562  template <typename ResultOpType>
1563  LogicalResult lowerBinOpToVariadic(Operation *op);
1564 
1565  template <typename ResultOpType>
1566  LogicalResult lowerElementwiseLogicalOp(Operation *op);
1567 
1568  LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1569  ICmpPredicate unsignedOp);
1570  template <typename SignedOp, typename UnsignedOp>
1571  LogicalResult lowerDivLikeOp(Operation *op);
1572 
1573  LogicalResult visitExpr(CatPrimOp op);
1574 
1575  LogicalResult visitExpr(AndPrimOp op) {
1576  return lowerBinOpToVariadic<comb::AndOp>(op);
1577  }
1578  LogicalResult visitExpr(OrPrimOp op) {
1579  return lowerBinOpToVariadic<comb::OrOp>(op);
1580  }
1581  LogicalResult visitExpr(XorPrimOp op) {
1582  return lowerBinOpToVariadic<comb::XorOp>(op);
1583  }
1584  LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1585  return lowerElementwiseLogicalOp<comb::OrOp>(op);
1586  }
1587  LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1588  return lowerElementwiseLogicalOp<comb::AndOp>(op);
1589  }
1590  LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1591  return lowerElementwiseLogicalOp<comb::XorOp>(op);
1592  }
1593  LogicalResult visitExpr(AddPrimOp op) {
1594  return lowerBinOpToVariadic<comb::AddOp>(op);
1595  }
1596  LogicalResult visitExpr(EQPrimOp op) {
1597  return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1598  }
1599  LogicalResult visitExpr(NEQPrimOp op) {
1600  return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1601  }
1602  LogicalResult visitExpr(LTPrimOp op) {
1603  return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1604  }
1605  LogicalResult visitExpr(LEQPrimOp op) {
1606  return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1607  }
1608  LogicalResult visitExpr(GTPrimOp op) {
1609  return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1610  }
1611  LogicalResult visitExpr(GEQPrimOp op) {
1612  return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1613  }
1614 
1615  LogicalResult visitExpr(SubPrimOp op) { return lowerBinOp<comb::SubOp>(op); }
1616  LogicalResult visitExpr(MulPrimOp op) {
1617  return lowerBinOpToVariadic<comb::MulOp>(op);
1618  }
1619  LogicalResult visitExpr(DivPrimOp op) {
1620  return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1621  }
1622  LogicalResult visitExpr(RemPrimOp op) {
1623  return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1624  }
1625 
1626  // Verif Operations
1627  LogicalResult visitExpr(IsXIntrinsicOp op);
1628  LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1629  LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1630  LogicalResult visitExpr(SizeOfIntrinsicOp op);
1631  LogicalResult visitExpr(ClockGateIntrinsicOp op);
1632  LogicalResult visitExpr(LTLAndIntrinsicOp op);
1633  LogicalResult visitExpr(LTLOrIntrinsicOp op);
1634  LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1635  LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1636  LogicalResult visitExpr(LTLNotIntrinsicOp op);
1637  LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1638  LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1639  LogicalResult visitExpr(LTLClockIntrinsicOp op);
1640  LogicalResult visitExpr(LTLDisableIntrinsicOp op);
1641  LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1642  LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1643  LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1644  LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1645 
1646  // Other Operations
1647  LogicalResult visitExpr(BitsPrimOp op);
1648  LogicalResult visitExpr(InvalidValueOp op);
1649  LogicalResult visitExpr(HeadPrimOp op);
1650  LogicalResult visitExpr(ShlPrimOp op);
1651  LogicalResult visitExpr(ShrPrimOp op);
1652  LogicalResult visitExpr(DShlPrimOp op) {
1653  return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1654  }
1655  LogicalResult visitExpr(DShrPrimOp op) {
1656  return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1657  }
1658  LogicalResult visitExpr(DShlwPrimOp op) {
1659  return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1660  }
1661  LogicalResult visitExpr(TailPrimOp op);
1662  LogicalResult visitExpr(MuxPrimOp op);
1663  LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1664  LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1665  LogicalResult visitExpr(MultibitMuxOp op);
1666  LogicalResult visitExpr(VerbatimExprOp op);
1667  LogicalResult visitExpr(XMRRefOp op);
1668  LogicalResult visitExpr(XMRDerefOp op);
1669 
1670  // Statements
1671  LogicalResult lowerVerificationStatement(
1672  Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1673  Value enable, StringAttr messageAttr, ValueRange operands,
1674  StringAttr nameAttr, bool isConcurrent, EventControl eventControl);
1675 
1676  LogicalResult visitStmt(SkipOp op);
1677 
1678  FailureOr<bool> lowerConnect(Value dest, Value srcVal);
1679  LogicalResult visitStmt(ConnectOp op);
1680  LogicalResult visitStmt(StrictConnectOp op);
1681  LogicalResult visitStmt(ForceOp op);
1682  LogicalResult visitStmt(PrintFOp op);
1683  LogicalResult visitStmt(StopOp op);
1684  LogicalResult visitStmt(AssertOp op);
1685  LogicalResult visitStmt(AssumeOp op);
1686  LogicalResult visitStmt(CoverOp op);
1687  LogicalResult visitStmt(AttachOp op);
1688  LogicalResult visitStmt(RefForceOp op);
1689  LogicalResult visitStmt(RefForceInitialOp op);
1690  LogicalResult visitStmt(RefReleaseOp op);
1691  LogicalResult visitStmt(RefReleaseInitialOp op);
1692 
1693  FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
1694  FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
1695  FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
1696 
1697  LogicalResult fixupLTLOps();
1698 
1699  Type lowerType(Type type) {
1700  return circuitState.lowerType(type, builder.getLoc());
1701  }
1702 
1703 private:
1704  /// The module we're lowering into.
1705  hw::HWModuleOp theModule;
1706 
1707  /// Global state.
1708  CircuitLoweringState &circuitState;
1709 
1710  /// This builder is set to the right location for each visit call.
1711  ImplicitLocOpBuilder builder;
1712 
1713  /// Each value lowered (e.g. operation result) is kept track in this map.
1714  /// The key should have a FIRRTL type, the result will have an HW dialect
1715  /// type.
1716  DenseMap<Value, Value> valueMapping;
1717 
1718  /// Mapping from clock values to corresponding non-clock values converted
1719  /// via a deduped `seq.from_clock` op.
1720  DenseMap<Value, Value> fromClockMapping;
1721 
1722  /// This keeps track of constants that we have created so we can reuse them.
1723  /// This is populated by the getOrCreateIntConstant method.
1724  DenseMap<Attribute, Value> hwConstantMap;
1725  DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1726 
1727  /// This keeps track of constant X that we have created so we can reuse them.
1728  /// This is populated by the getOrCreateXConstant method.
1729  DenseMap<unsigned, Value> hwConstantXMap;
1730  DenseMap<Type, Value> hwConstantZMap;
1731 
1732  /// We auto-unique "ReadInOut" ops from wires and regs, enabling
1733  /// optimizations and CSEs of the read values to be more obvious. This
1734  /// caches a known ReadInOutOp for the given value and is managed by
1735  /// `getReadValue(v)`.
1736  DenseMap<Value, Value> readInOutCreated;
1737 
1738  // We auto-unique graph-level blocks to reduce the amount of generated
1739  // code and ensure that side effects are properly ordered in FIRRTL.
1740  using AlwaysKeyType = std::tuple<Block *, sv::EventControl, Value,
1741  ::ResetType, sv::EventControl, Value>;
1743  alwaysBlocks;
1744  llvm::SmallDenseMap<std::pair<Block *, Attribute>, sv::IfDefOp> ifdefBlocks;
1746 
1747  /// A namespace that can be used to generate new symbol names that are unique
1748  /// within this module.
1749  hw::InnerSymbolNamespace moduleNamespace;
1750 
1751  /// A backedge builder to directly materialize values during the lowering
1752  /// without requiring temporary wires.
1753  BackedgeBuilder backedgeBuilder;
1754  /// Currently unresolved backedges. More precisely, a mapping from the
1755  /// backedge value to the value it will be replaced with. We use a MapVector
1756  /// so that a combinational cycles of backedges, the one backedge that gets
1757  /// replaced with an undriven wire is consistent.
1758  llvm::MapVector<Value, Value> backedges;
1759 
1760  /// A collection of values generated by the lowering process that may have
1761  /// become obsolete through subsequent parts of the lowering. This covers the
1762  /// values of wires that may be overridden by subsequent connects; or
1763  /// subaccesses that appear only as destination of a connect, and thus gets
1764  /// obsoleted by the connect directly updating the wire or register.
1765  DenseSet<Operation *> maybeUnusedValues;
1766 
1767  void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1768  void maybeUnused(Value value) {
1769  if (auto *op = value.getDefiningOp())
1770  maybeUnused(op);
1771  }
1772 
1773  /// A worklist of LTL operations that don't have their final type yet. The
1774  /// FIRRTL intrinsics for LTL ops all use `uint<1>` types, but the actual LTL
1775  /// ops themselves have more precise `!ltl.sequence` and `!ltl.property`
1776  /// types. After all LTL ops have been lowered, this worklist is used to
1777  /// compute their actual types (re-inferring return types) and push the
1778  /// updated types to their users. This also drops any `hw.wire`s in between
1779  /// the LTL ops, which were necessary to go from the def-before-use FIRRTL
1780  /// dialect to the graph-like HW dialect.
1781  SetVector<Operation *> ltlOpFixupWorklist;
1782 };
1783 } // end anonymous namespace
1784 
1785 LogicalResult FIRRTLModuleLowering::lowerModuleOperations(
1786  hw::HWModuleOp module, CircuitLoweringState &loweringState) {
1787  return FIRRTLLowering(module, loweringState).run();
1788 }
1789 
1790 // This is the main entrypoint for the lowering pass.
1791 LogicalResult FIRRTLLowering::run() {
1792  // FIRRTL FModule is a single block because FIRRTL ops are a DAG. Walk
1793  // through each operation, lowering each in turn if we can, introducing
1794  // casts if we cannot.
1795  auto &body = theModule.getBody();
1796 
1797  SmallVector<Operation *, 16> opsToRemove;
1798 
1799  // Iterate through each operation in the module body, attempting to lower
1800  // each of them. We maintain 'builder' for each invocation.
1801  for (auto &op : body.front().getOperations()) {
1802  builder.setInsertionPoint(&op);
1803  builder.setLoc(op.getLoc());
1804  auto done = succeeded(dispatchVisitor(&op));
1805  circuitState.processRemainingAnnotations(&op, AnnotationSet(&op));
1806  if (done)
1807  opsToRemove.push_back(&op);
1808  else {
1809  switch (handleUnloweredOp(&op)) {
1810  case AlreadyLowered:
1811  break; // Something like hw.output, which is already lowered.
1812  case NowLowered: // Something handleUnloweredOp removed.
1813  opsToRemove.push_back(&op);
1814  break;
1815  case LoweringFailure:
1816  backedgeBuilder.abandon();
1817  return failure();
1818  }
1819  }
1820  }
1821 
1822  // Replace all backedges with uses of their regular values. We process them
1823  // after the module body since the lowering table is too hard to keep up to
1824  // date. Multiple operations may be lowered to the same backedge when values
1825  // are folded, which means we would have to scan the entire lowering table to
1826  // safely replace a backedge.
1827  for (auto &[backedge, value] : backedges) {
1828  SmallVector<Location> driverLocs;
1829  // In the case where we have backedges connected to other backedges, we have
1830  // to find the value that actually drives the group.
1831  while (true) {
1832  // If we find the original backedge we have some undriven logic or
1833  // a combinatorial loop. Bail out and provide information on the nodes.
1834  if (backedge == value) {
1835  Location edgeLoc = backedge.getLoc();
1836  if (driverLocs.empty()) {
1837  mlir::emitError(edgeLoc, "sink does not have a driver");
1838  } else {
1839  auto diag = mlir::emitError(edgeLoc, "sink in combinational loop");
1840  for (auto loc : driverLocs)
1841  diag.attachNote(loc) << "through driver here";
1842  }
1843  backedgeBuilder.abandon();
1844  return failure();
1845  }
1846  // If the value is not another backedge, we have found the driver.
1847  auto it = backedges.find(value);
1848  if (it == backedges.end())
1849  break;
1850  // Find what is driving the next backedge.
1851  driverLocs.push_back(value.getLoc());
1852  value = it->second;
1853  }
1854  if (auto *defOp = backedge.getDefiningOp())
1855  maybeUnusedValues.erase(defOp);
1856  backedge.replaceAllUsesWith(value);
1857  }
1858 
1859  // Now that all of the operations that can be lowered are, remove th
1860  // original values. We know that any lowered operations will be dead (if
1861  // removed in reverse order) at this point - any users of them from
1862  // unremapped operations will be changed to use the newly lowered ops.
1863  while (!opsToRemove.empty()) {
1864  assert(opsToRemove.back()->use_empty() &&
1865  "Should remove ops in reverse order of visitation");
1866  maybeUnusedValues.erase(opsToRemove.back());
1867  opsToRemove.pop_back_val()->erase();
1868  }
1869 
1870  // Prune operations that may have become unused throughout the lowering.
1871  while (!maybeUnusedValues.empty()) {
1872  auto it = maybeUnusedValues.begin();
1873  auto *op = *it;
1874  maybeUnusedValues.erase(it);
1875  if (!isOpTriviallyDead(op))
1876  continue;
1877  for (auto operand : op->getOperands())
1878  if (auto *defOp = operand.getDefiningOp())
1879  maybeUnusedValues.insert(defOp);
1880  op->erase();
1881  }
1882 
1883  // Determine the actual types of lowered LTL operations and remove any
1884  // intermediate wires among them.
1885  if (failed(fixupLTLOps()))
1886  return failure();
1887 
1888  return backedgeBuilder.clearOrEmitError();
1889 }
1890 
1891 //===----------------------------------------------------------------------===//
1892 // Helpers
1893 //===----------------------------------------------------------------------===//
1894 
1895 /// Create uniqued constant clocks.
1896 Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
1897  auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
1898 
1899  auto &entry = hwConstantMap[attr];
1900  if (entry)
1901  return entry;
1902 
1903  OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1904  entry = entryBuilder.create<seq::ConstClockOp>(builder.getLoc(), attr);
1905  return entry;
1906 }
1907 
1908 /// Check to see if we've already lowered the specified constant. If so,
1909 /// return it. Otherwise create it and put it in the entry block for reuse.
1910 Value FIRRTLLowering::getOrCreateIntConstant(const APInt &value) {
1911  auto attr = builder.getIntegerAttr(
1912  builder.getIntegerType(value.getBitWidth()), value);
1913 
1914  auto &entry = hwConstantMap[attr];
1915  if (entry)
1916  return entry;
1917 
1918  OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1919  entry = entryBuilder.create<hw::ConstantOp>(builder.getLoc(), attr);
1920  return entry;
1921 }
1922 
1923 /// Check to see if we've already created the specified aggregate constant
1924 /// attribute. If so, return it. Otherwise create it.
1925 Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
1926  Type type) {
1927  // Base case.
1928  if (hw::type_isa<IntegerType>(type))
1929  return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
1930 
1931  auto cache = hwAggregateConstantMap.lookup({value, type});
1932  if (cache)
1933  return cache;
1934 
1935  // Recursively construct elements.
1936  SmallVector<Attribute> values;
1937  for (auto e : llvm::enumerate(cast<ArrayAttr>(value))) {
1938  Type subType;
1939  if (auto array = hw::type_dyn_cast<hw::ArrayType>(type))
1940  subType = array.getElementType();
1941  else if (auto structType = hw::type_dyn_cast<hw::StructType>(type))
1942  subType = structType.getElements()[e.index()].type;
1943  else
1944  assert(false && "type must be either array or struct");
1945 
1946  values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
1947  }
1948 
1949  // FIRRTL and HW have a different operand ordering for arrays.
1950  if (hw::type_isa<hw::ArrayType>(type))
1951  std::reverse(values.begin(), values.end());
1952 
1953  auto &entry = hwAggregateConstantMap[{value, type}];
1954  entry = builder.getArrayAttr(values);
1955  return entry;
1956 }
1957 
1958 /// Zero bit operands end up looking like failures from getLoweredValue. This
1959 /// helper function invokes the closure specified if the operand was actually
1960 /// zero bit, or returns failure() if it was some other kind of failure.
1961 static LogicalResult handleZeroBit(Value failedOperand,
1962  std::function<LogicalResult()> fn) {
1963  assert(failedOperand && "Should be called on the failed operand");
1964  if (!isZeroBitFIRRTLType(failedOperand.getType()))
1965  return failure();
1966  return fn();
1967 }
1968 
1969 /// Check to see if we've already lowered the specified constant. If so,
1970 /// return it. Otherwise create it and put it in the entry block for reuse.
1971 Value FIRRTLLowering::getOrCreateXConstant(unsigned numBits) {
1972 
1973  auto &entry = hwConstantXMap[numBits];
1974  if (entry)
1975  return entry;
1976 
1977  OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1978  entry = entryBuilder.create<sv::ConstantXOp>(
1979  builder.getLoc(), entryBuilder.getIntegerType(numBits));
1980  return entry;
1981 }
1982 
1983 Value FIRRTLLowering::getOrCreateZConstant(Type type) {
1984  auto &entry = hwConstantZMap[type];
1985  if (!entry) {
1986  OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1987  entry = entryBuilder.create<sv::ConstantZOp>(builder.getLoc(), type);
1988  }
1989  return entry;
1990 }
1991 
1992 /// Return the lowered HW value corresponding to the specified original value.
1993 /// This returns a null value for FIRRTL values that haven't be lowered, e.g.
1994 /// unknown width integers. This returns hw::inout type values if present, it
1995 /// does not implicitly read from them.
1996 Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
1997  // Block arguments are considered lowered.
1998  if (value.isa<BlockArgument>())
1999  return value;
2000 
2001  // If we lowered this value, then return the lowered value, otherwise fail.
2002  if (auto lowering = valueMapping.lookup(value)) {
2003  assert(!lowering.getType().isa<FIRRTLType>() &&
2004  "Lowered value should be a non-FIRRTL value");
2005  return lowering;
2006  }
2007  return Value();
2008 }
2009 
2010 /// Return the lowered value corresponding to the specified original value.
2011 /// This returns a null value for FIRRTL values that cannot be lowered, e.g.
2012 /// unknown width integers.
2013 Value FIRRTLLowering::getLoweredValue(Value value) {
2014  auto result = getPossiblyInoutLoweredValue(value);
2015  if (!result)
2016  return result;
2017 
2018  // If we got an inout value, implicitly read it. FIRRTL allows direct use
2019  // of wires and other things that lower to inout type.
2020  if (result.getType().isa<hw::InOutType>())
2021  return getReadValue(result);
2022 
2023  return result;
2024 }
2025 
2026 /// Return the lowered value, converting `seq.clock` to `i1.
2027 Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2028  auto result = getLoweredValue(value);
2029  if (!result)
2030  return result;
2031 
2032  if (hw::type_isa<seq::ClockType>(result.getType()))
2033  return getNonClockValue(result);
2034 
2035  return result;
2036 }
2037 
2038 /// Return the lowered aggregate value whose type is converted into
2039 /// `destType`. We have to care about the extension/truncation/signedness of
2040 /// each element.
2041 Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2042  FIRRTLBaseType sourceType,
2043  FIRRTLBaseType destType,
2044  bool allowTruncate) {
2045  SmallVector<Value> resultBuffer;
2046 
2047  // Helper function to cast each element of array to dest type.
2048  auto cast = [&](Value value, FIRRTLBaseType sourceType,
2049  FIRRTLBaseType destType) {
2050  auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2051  auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2052  auto resultType = builder.getIntegerType(destWidth);
2053 
2054  if (srcWidth == destWidth)
2055  return value;
2056 
2057  if (srcWidth > destWidth) {
2058  if (allowTruncate)
2059  return builder.createOrFold<comb::ExtractOp>(resultType, value, 0);
2060 
2061  builder.emitError("operand should not be a truncation");
2062  return Value();
2063  }
2064 
2065  if (firrtl::type_cast<IntType>(sourceType).isSigned())
2066  return comb::createOrFoldSExt(value, resultType, builder);
2067  auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2068  return builder.createOrFold<comb::ConcatOp>(zero, value);
2069  };
2070 
2071  // This recursive function constructs the output array.
2072  std::function<LogicalResult(Value, FIRRTLBaseType, FIRRTLBaseType)> recurse =
2073  [&](Value src, FIRRTLBaseType srcType,
2074  FIRRTLBaseType destType) -> LogicalResult {
2075  return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2076  .Case<FVectorType>([&](auto srcVectorType) {
2077  auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2078  unsigned size = resultBuffer.size();
2079  unsigned indexWidth =
2080  getBitWidthFromVectorSize(srcVectorType.getNumElements());
2081  for (size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2082  destVectorType.getNumElements());
2083  i != e; ++i) {
2084  auto iIdx = getOrCreateIntConstant(indexWidth, i);
2085  auto arrayIndex = builder.create<hw::ArrayGetOp>(src, iIdx);
2086  if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2087  destVectorType.getElementType())))
2088  return failure();
2089  }
2090  SmallVector<Value> temp(resultBuffer.begin() + size,
2091  resultBuffer.end());
2092  auto array = builder.createOrFold<hw::ArrayCreateOp>(temp);
2093  resultBuffer.resize(size);
2094  resultBuffer.push_back(array);
2095  return success();
2096  })
2097  .Case<BundleType>([&](BundleType srcStructType) {
2098  auto destStructType = firrtl::type_cast<BundleType>(destType);
2099  unsigned size = resultBuffer.size();
2100 
2101  // TODO: We don't support partial connects for bundles for now.
2102  if (destStructType.getNumElements() != srcStructType.getNumElements())
2103  return failure();
2104 
2105  for (auto elem : llvm::enumerate(destStructType)) {
2106  auto structExtract =
2107  builder.create<hw::StructExtractOp>(src, elem.value().name);
2108  if (failed(recurse(structExtract,
2109  srcStructType.getElementType(elem.index()),
2110  destStructType.getElementType(elem.index()))))
2111  return failure();
2112  }
2113  SmallVector<Value> temp(resultBuffer.begin() + size,
2114  resultBuffer.end());
2115  auto newStruct = builder.createOrFold<hw::StructCreateOp>(
2116  lowerType(destStructType), temp);
2117  resultBuffer.resize(size);
2118  resultBuffer.push_back(newStruct);
2119  return success();
2120  })
2121  .Case<IntType>([&](auto) {
2122  if (auto result = cast(src, srcType, destType)) {
2123  resultBuffer.push_back(result);
2124  return success();
2125  }
2126  return failure();
2127  })
2128  .Default([&](auto) { return failure(); });
2129  };
2130 
2131  if (failed(recurse(array, sourceType, destType)))
2132  return Value();
2133 
2134  assert(resultBuffer.size() == 1 &&
2135  "resultBuffer must only contain a result array if `success` is true");
2136  return resultBuffer[0];
2137 }
2138 
2139 /// Return the lowered value corresponding to the specified original value and
2140 /// then extend it to match the width of destType if needed.
2141 ///
2142 /// This returns a null value for FIRRTL values that cannot be lowered, e.g.
2143 /// unknown width integers.
2144 Value FIRRTLLowering::getLoweredAndExtendedValue(Value value, Type destType) {
2145  assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2146  type_isa<FIRRTLBaseType>(destType) &&
2147  "input/output value should be FIRRTL");
2148 
2149  // We only know how to extend integer types with known width.
2150  auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2151  if (destWidth == -1)
2152  return {};
2153 
2154  auto result = getLoweredValue(value);
2155  if (!result) {
2156  // If this was a zero bit operand being extended, then produce a zero of
2157  // the right result type. If it is just a failure, fail.
2158  if (!isZeroBitFIRRTLType(value.getType()))
2159  return {};
2160  // Zero bit results have to be returned as null. The caller can handle
2161  // this if they want to.
2162  if (destWidth == 0)
2163  return {};
2164  // Otherwise, FIRRTL semantics is that an extension from a zero bit value
2165  // always produces a zero value in the destination width.
2166  return getOrCreateIntConstant(destWidth, 0);
2167  }
2168 
2169  if (destWidth ==
2170  cast<FIRRTLBaseType>(value.getType()).getBitWidthOrSentinel()) {
2171  // Lookup the lowered type of dest.
2172  auto loweredDstType = lowerType(destType);
2173  if (result.getType() != loweredDstType &&
2174  (isa<hw::TypeAliasType>(result.getType()) ||
2175  isa<hw::TypeAliasType>(loweredDstType))) {
2176  return builder.createOrFold<hw::BitcastOp>(loweredDstType, result);
2177  }
2178  }
2179  // Aggregates values
2180  if (result.getType().isa<hw::ArrayType, hw::StructType>()) {
2181  // Types already match.
2182  if (destType == value.getType())
2183  return result;
2184 
2185  return getExtOrTruncAggregateValue(
2186  result, type_cast<FIRRTLBaseType>(value.getType()),
2187  type_cast<FIRRTLBaseType>(destType),
2188  /* allowTruncate */ false);
2189  }
2190 
2191  if (result.getType().isa<seq::ClockType>()) {
2192  // Types already match.
2193  if (destType == value.getType())
2194  return result;
2195  builder.emitError("cannot use clock type as an integer");
2196  return {};
2197  }
2198 
2199  auto intResultType = dyn_cast<IntegerType>(result.getType());
2200  if (!intResultType) {
2201  builder.emitError("operand of type ")
2202  << result.getType() << " cannot be used as an integer";
2203  return {};
2204  }
2205 
2206  auto srcWidth = intResultType.getWidth();
2207  if (srcWidth == unsigned(destWidth))
2208  return result;
2209 
2210  if (srcWidth > unsigned(destWidth)) {
2211  builder.emitError("operand should not be a truncation");
2212  return {};
2213  }
2214 
2215  auto resultType = builder.getIntegerType(destWidth);
2216 
2217  // Extension follows the sign of the source value, not the destination.
2218  auto valueFIRType =
2219  type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2220  if (type_cast<IntType>(valueFIRType).isSigned())
2221  return comb::createOrFoldSExt(result, resultType, builder);
2222 
2223  auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2224  return builder.createOrFold<comb::ConcatOp>(zero, result);
2225 }
2226 
2227 /// Return the lowered value corresponding to the specified original value and
2228 /// then extended or truncated to match the width of destType if needed.
2229 ///
2230 /// This returns a null value for FIRRTL values that cannot be lowered, e.g.
2231 /// unknown width integers.
2232 Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2233  assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2234  type_isa<FIRRTLBaseType>(destType) &&
2235  "input/output value should be FIRRTL");
2236 
2237  // We only know how to adjust integer types with known width.
2238  auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2239  if (destWidth == -1)
2240  return {};
2241 
2242  auto result = getLoweredValue(value);
2243  if (!result) {
2244  // If this was a zero bit operand being extended, then produce a zero of
2245  // the right result type. If it is just a failure, fail.
2246  if (!isZeroBitFIRRTLType(value.getType()))
2247  return {};
2248  // Zero bit results have to be returned as null. The caller can handle
2249  // this if they want to.
2250  if (destWidth == 0)
2251  return {};
2252  // Otherwise, FIRRTL semantics is that an extension from a zero bit value
2253  // always produces a zero value in the destination width.
2254  return getOrCreateIntConstant(destWidth, 0);
2255  }
2256 
2257  // Aggregates values
2258  if (result.getType().isa<hw::ArrayType, hw::StructType>()) {
2259  // Types already match.
2260  if (destType == value.getType())
2261  return result;
2262 
2263  return getExtOrTruncAggregateValue(
2264  result, type_cast<FIRRTLBaseType>(value.getType()),
2265  type_cast<FIRRTLBaseType>(destType),
2266  /* allowTruncate */ true);
2267  }
2268 
2269  auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2270  if (srcWidth == unsigned(destWidth))
2271  return result;
2272 
2273  if (destWidth == 0)
2274  return {};
2275 
2276  if (srcWidth > unsigned(destWidth)) {
2277  auto resultType = builder.getIntegerType(destWidth);
2278  return builder.createOrFold<comb::ExtractOp>(resultType, result, 0);
2279  }
2280 
2281  auto resultType = builder.getIntegerType(destWidth);
2282 
2283  // Extension follows the sign of the source value, not the destination.
2284  auto valueFIRType =
2285  type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2286  if (type_cast<IntType>(valueFIRType).isSigned())
2287  return comb::createOrFoldSExt(result, resultType, builder);
2288 
2289  auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2290  return builder.createOrFold<comb::ConcatOp>(zero, result);
2291 }
2292 
2293 /// Set the lowered value of 'orig' to 'result', remembering this in a map.
2294 /// This always returns success() to make it more convenient in lowering code.
2295 ///
2296 /// Note that result may be null here if we're lowering orig to a zero-bit
2297 /// value.
2298 ///
2299 LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2300  if (auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2301  assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2302  "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2303 
2304 #ifndef NDEBUG
2305  auto baseType = getBaseType(origType);
2306  auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2307 
2308  // Caller should pass null value iff this was a zero bit value.
2309  if (srcWidth != -1) {
2310  if (result)
2311  assert((srcWidth != 0) &&
2312  "Lowering produced value for zero width source");
2313  else
2314  assert((srcWidth == 0) &&
2315  "Lowering produced null value but source wasn't zero width");
2316  }
2317 #endif
2318  } else {
2319  assert(result && "Lowering of foreign type produced null value");
2320  }
2321 
2322  auto &slot = valueMapping[orig];
2323  assert(!slot && "value lowered multiple times");
2324  slot = result;
2325  return success();
2326 }
2327 
2328 /// Set the lowering for a value to the specified result. This came from a
2329 /// possible folding, so check to see if we need to handle a constant.
2330 LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2331  Value result) {
2332  // If this is a constant, check to see if we have it in our unique mapping:
2333  // it could have come from folding an operation.
2334  if (auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2335  auto &entry = hwConstantMap[cst.getValueAttr()];
2336  if (entry == cst) {
2337  // We're already using an entry in the constant map, nothing to do.
2338  } else if (entry) {
2339  // We already had this constant, reuse the one we have instead of the
2340  // one we just folded.
2341  result = entry;
2342  cst->erase();
2343  } else {
2344  // This is a new constant. Remember it!
2345  entry = cst;
2346  cst->moveBefore(&theModule.getBodyBlock()->front());
2347  }
2348  }
2349 
2350  return setLowering(orig, result);
2351 }
2352 
2353 /// Create a new operation with type ResultOpType and arguments CtorArgTypes,
2354 /// then call setLowering with its result.
2355 template <typename ResultOpType, typename... CtorArgTypes>
2356 LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2357  CtorArgTypes... args) {
2358  auto result = builder.createOrFold<ResultOpType>(args...);
2359  if (auto *op = result.getDefiningOp())
2360  tryCopyName(op, orig);
2361  return setPossiblyFoldedLowering(orig->getResult(0), result);
2362 }
2363 
2364 /// Create a new LTL operation with type ResultOpType and arguments
2365 /// CtorArgTypes, then call setLowering with its result. Also add the operation
2366 /// to the worklist of LTL ops that need to have their types fixed-up after the
2367 /// lowering.
2368 template <typename ResultOpType, typename... CtorArgTypes>
2369 LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2370  CtorArgTypes... args) {
2371  auto result = builder.createOrFold<ResultOpType>(args...);
2372  if (auto *op = result.getDefiningOp())
2373  ltlOpFixupWorklist.insert(op);
2374  return setPossiblyFoldedLowering(orig->getResult(0), result);
2375 }
2376 
2377 /// Creates a backedge of the specified result type. A backedge represents a
2378 /// placeholder to be filled in later by a lowered value. If the backedge is not
2379 /// updated with a real value by the end of the pass, it will be replaced with
2380 /// an undriven wire. Backedges are allowed to be updated to other backedges.
2381 /// If a chain of backedges forms a combinational loop, they will be replaced
2382 /// with an undriven wire.
2383 Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2384  auto backedge = backedgeBuilder.get(type, loc);
2385  backedges.insert({backedge, backedge});
2386  return backedge;
2387 }
2388 
2389 /// Sets the lowering for a value to a backedge of the specified result type.
2390 /// This is useful for lowering types which cannot pass through a wire, or to
2391 /// directly materialize values in operations that violate the SSA dominance
2392 /// constraint.
2393 Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2394  auto backedge = createBackedge(orig.getLoc(), type);
2395  (void)setLowering(orig, backedge);
2396  return backedge;
2397 }
2398 
2399 /// If the `from` value is in fact a backedge, record that the backedge will
2400 /// be replaced by the value. Return true if the destination is a backedge.
2401 bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2402  auto backedgeIt = backedges.find(dest);
2403  if (backedgeIt == backedges.end())
2404  return false;
2405  backedgeIt->second = src;
2406  return true;
2407 }
2408 
2409 /// Switch the insertion point of the current builder to the end of the
2410 /// specified block and run the closure. This correctly handles the case
2411 /// where the closure is null, but the caller needs to make sure the block
2412 /// exists.
2413 void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2414  std::function<void(void)> fn, Region &region) {
2415  if (!fn)
2416  return;
2417 
2418  auto oldIP = builder.saveInsertionPoint();
2419 
2420  builder.setInsertionPointToEnd(&region.front());
2421  fn();
2422  builder.restoreInsertionPoint(oldIP);
2423 }
2424 
2425 /// Return a read value for the specified inout operation, auto-uniquing them.
2426 Value FIRRTLLowering::getReadValue(Value v) {
2427  Value result = readInOutCreated.lookup(v);
2428  if (result)
2429  return result;
2430 
2431  // Make sure to put the read value at the correct scope so it dominates all
2432  // future uses.
2433  auto oldIP = builder.saveInsertionPoint();
2434  if (auto *vOp = v.getDefiningOp()) {
2435  builder.setInsertionPointAfter(vOp);
2436  } else {
2437  // For reads of ports, just set the insertion point at the top of the
2438  // module.
2439  builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2440  }
2441 
2442  // Instead of creating `ReadInOutOp` for `ArrayIndexInOutOp`, create
2443  // `ArrayGetOp` for root arrays.
2444  if (auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2445  result = getReadValue(arrayIndexInout.getInput());
2446  result = builder.createOrFold<hw::ArrayGetOp>(result,
2447  arrayIndexInout.getIndex());
2448  } else {
2449  // Otherwise, create a read inout operation.
2450  result = builder.createOrFold<sv::ReadInOutOp>(v);
2451  }
2452  builder.restoreInsertionPoint(oldIP);
2453  readInOutCreated.insert({v, result});
2454  return result;
2455 }
2456 
2457 Value FIRRTLLowering::getNonClockValue(Value v) {
2458  auto it = fromClockMapping.try_emplace(v, Value{});
2459  if (it.second) {
2460  ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
2461  builder.setInsertionPointAfterValue(v);
2462  it.first->second = builder.create<seq::FromClockOp>(v);
2463  }
2464  return it.first->second;
2465 }
2466 
2467 void FIRRTLLowering::addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
2468  ::ResetType resetStyle,
2469  sv::EventControl resetEdge, Value reset,
2470  std::function<void(void)> body,
2471  std::function<void(void)> resetBody) {
2472  AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
2473  resetStyle, resetEdge, reset};
2474  sv::AlwaysOp alwaysOp;
2475  sv::IfOp insideIfOp;
2476  std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2477 
2478  if (!alwaysOp) {
2479  if (reset) {
2480  assert(resetStyle != ::ResetType::NoReset);
2481  // Here, we want to create the folloing structure with sv.always and
2482  // sv.if. If `reset` is async, we need to add `reset` to a sensitivity
2483  // list.
2484  //
2485  // sv.always @(clockEdge or reset) {
2486  // sv.if (reset) {
2487  // resetBody
2488  // } else {
2489  // body
2490  // }
2491  // }
2492 
2493  auto createIfOp = [&]() {
2494  // It is weird but intended. Here we want to create an empty sv.if
2495  // with an else block.
2496  insideIfOp = builder.create<sv::IfOp>(
2497  reset, []() {}, []() {});
2498  };
2499  if (resetStyle == ::ResetType::AsyncReset) {
2500  sv::EventControl events[] = {clockEdge, resetEdge};
2501  Value clocks[] = {clock, reset};
2502 
2503  alwaysOp = builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2504  if (resetEdge == sv::EventControl::AtNegEdge)
2505  llvm_unreachable("negative edge for reset is not expected");
2506  createIfOp();
2507  });
2508  } else {
2509  alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2510  }
2511  } else {
2512  assert(!resetBody);
2513  alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock);
2514  insideIfOp = nullptr;
2515  }
2516  alwaysBlocks[key] = {alwaysOp, insideIfOp};
2517  }
2518 
2519  if (reset) {
2520  assert(insideIfOp && "reset body must be initialized before");
2521  runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2522  runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2523  } else {
2524  runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2525  }
2526 
2527  // Move the earlier always block(s) down to where the last would have been
2528  // inserted. This ensures that any values used by the always blocks are
2529  // defined ahead of the uses, which leads to better generated Verilog.
2530  alwaysOp->moveBefore(builder.getInsertionBlock(),
2531  builder.getInsertionPoint());
2532 }
2533 
2534 void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2535  std::function<void(void)> thenCtor,
2536  std::function<void(void)> elseCtor) {
2537  auto condAttr = builder.getStringAttr(cond);
2538  auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
2539  if (op) {
2540  runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2541  runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2542 
2543  // Move the earlier #ifdef block(s) down to where the last would have been
2544  // inserted. This ensures that any values used by the #ifdef blocks are
2545  // defined ahead of the uses, which leads to better generated Verilog.
2546  op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2547  } else {
2548  ifdefBlocks[{builder.getBlock(), condAttr}] =
2549  builder.create<sv::IfDefOp>(condAttr, thenCtor, elseCtor);
2550  }
2551 }
2552 
2553 void FIRRTLLowering::addToInitialBlock(std::function<void(void)> body) {
2554  auto op = initialBlocks.lookup(builder.getBlock());
2555  if (op) {
2556  runWithInsertionPointAtEndOfBlock(body, op.getBody());
2557 
2558  // Move the earlier initial block(s) down to where the last would have
2559  // been inserted. This ensures that any values used by the initial blocks
2560  // are defined ahead of the uses, which leads to better generated Verilog.
2561  op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2562  } else {
2563  initialBlocks[builder.getBlock()] = builder.create<sv::InitialOp>(body);
2564  }
2565 }
2566 
2567 void FIRRTLLowering::addIfProceduralBlock(Value cond,
2568  std::function<void(void)> thenCtor,
2569  std::function<void(void)> elseCtor) {
2570  // Check to see if we already have an if on this condition immediately
2571  // before the insertion point. If so, extend it.
2572  auto insertIt = builder.getInsertionPoint();
2573  if (insertIt != builder.getBlock()->begin())
2574  if (auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
2575  if (ifOp.getCond() == cond) {
2576  runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
2577  runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
2578  return;
2579  }
2580  }
2581 
2582  builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
2583 }
2584 
2585 //===----------------------------------------------------------------------===//
2586 // Special Operations
2587 //===----------------------------------------------------------------------===//
2588 
2589 /// Handle the case where an operation wasn't lowered. When this happens, the
2590 /// operands should just be unlowered non-FIRRTL values. If the operand was
2591 /// not lowered then leave it alone, otherwise we have a problem with
2592 /// lowering.
2593 ///
2594 FIRRTLLowering::UnloweredOpResult
2595 FIRRTLLowering::handleUnloweredOp(Operation *op) {
2596  // Simply pass through non-FIRRTL operations and consider them already
2597  // lowered. This allows us to handled partially lowered inputs, and also allow
2598  // other FIRRTL operations to spawn additional already-lowered operations,
2599  // like `hw.output`.
2600  if (!isa<FIRRTLDialect>(op->getDialect())) {
2601  for (auto &operand : op->getOpOperands())
2602  if (auto lowered = getPossiblyInoutLoweredValue(operand.get()))
2603  operand.set(lowered);
2604  for (auto result : op->getResults())
2605  (void)setLowering(result, result);
2606  return AlreadyLowered;
2607  }
2608 
2609  // Ok, at least one operand got lowered, so this operation is using a FIRRTL
2610  // value, but wasn't itself lowered. This is because the lowering is
2611  // incomplete. This is either a bug or incomplete implementation.
2612  //
2613  // There is one aspect of incompleteness we intentionally expect: we allow
2614  // primitive operations that produce a zero bit result to be ignored by the
2615  // lowering logic. They don't have side effects, and handling this corner
2616  // case just complicates each of the lowering hooks. Instead, we just handle
2617  // them all right here.
2618  if (op->getNumResults() == 1) {
2619  auto resultType = op->getResult(0).getType();
2620  if (type_isa<FIRRTLBaseType>(resultType) &&
2621  isZeroBitFIRRTLType(resultType) &&
2622  (isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
2623  // Zero bit values lower to the null Value.
2624  (void)setLowering(op->getResult(0), Value());
2625  return NowLowered;
2626  }
2627  }
2628  op->emitOpError("LowerToHW couldn't handle this operation");
2629  return LoweringFailure;
2630 }
2631 
2632 LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
2633  // Zero width values must be lowered to nothing.
2634  if (isZeroBitFIRRTLType(op.getType()))
2635  return setLowering(op, Value());
2636 
2637  return setLowering(op, getOrCreateIntConstant(op.getValue()));
2638 }
2639 
2640 LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
2641  Value cst;
2642  if (op.getType().isa<ClockType>()) {
2643  cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
2644  : seq::ClockConst::Low);
2645  } else {
2646  cst = getOrCreateIntConstant(APInt(/*bitWidth*/ 1, op.getValue()));
2647  }
2648  return setLowering(op, cst);
2649 }
2650 
2651 FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
2652  auto iIdx = getOrCreateIntConstant(
2654  firrtl::type_cast<FVectorType>(op.getInput().getType())
2655  .getNumElements()),
2656  op.getIndex());
2657 
2658  // If the input has an inout type, we need to lower to ArrayIndexInOutOp;
2659  // otherwise hw::ArrayGetOp.
2660  Value result;
2661  if (input.getType().isa<sv::InOutType>())
2662  result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
2663  else
2664  result = builder.createOrFold<hw::ArrayGetOp>(input, iIdx);
2665  tryCopyName(result.getDefiningOp(), op);
2666  return result;
2667 }
2668 
2669 FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
2670  Value valueIdx = getLoweredAndExtOrTruncValue(
2671  op.getIndex(),
2672  UIntType::get(op->getContext(),
2674  firrtl::type_cast<FVectorType>(op.getInput().getType())
2675  .getNumElements())));
2676  if (!valueIdx) {
2677  op->emitError() << "input lowering failed";
2678  return failure();
2679  }
2680 
2681  // If the input has an inout type, we need to lower to ArrayIndexInOutOp;
2682  // otherwise, lower the op to array indexing.
2683  Value result;
2684  if (input.getType().isa<sv::InOutType>())
2685  result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
2686  else
2687  result = createArrayIndexing(input, valueIdx);
2688  tryCopyName(result.getDefiningOp(), op);
2689  return result;
2690 }
2691 
2692 FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
2693  auto resultType = lowerType(op->getResult(0).getType());
2694  if (!resultType || !input) {
2695  op->emitError() << "subfield type lowering failed";
2696  return failure();
2697  }
2698 
2699  // If the input has an inout type, we need to lower to StructFieldInOutOp;
2700  // otherwise, StructExtractOp.
2701  auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
2702  .getElementName(op.getFieldIndex());
2703  Value result;
2704  if (input.getType().isa<sv::InOutType>())
2705  result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
2706  else
2707  result = builder.createOrFold<hw::StructExtractOp>(input, field);
2708  tryCopyName(result.getDefiningOp(), op);
2709  return result;
2710 }
2711 
2712 LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
2713  if (isZeroBitFIRRTLType(op.getType()))
2714  return setLowering(op, Value());
2715 
2716  auto input = getPossiblyInoutLoweredValue(op.getInput());
2717  if (!input)
2718  return op.emitError() << "input lowering failed";
2719 
2720  auto result = lowerSubindex(op, input);
2721  if (failed(result))
2722  return failure();
2723  return setLowering(op, *result);
2724 }
2725 
2726 LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
2727  if (isZeroBitFIRRTLType(op.getType()))
2728  return setLowering(op, Value());
2729 
2730  auto input = getPossiblyInoutLoweredValue(op.getInput());
2731  if (!input)
2732  return op.emitError() << "input lowering failed";
2733 
2734  auto result = lowerSubaccess(op, input);
2735  if (failed(result))
2736  return failure();
2737  return setLowering(op, *result);
2738 }
2739 
2740 LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
2741  // firrtl.mem lowering lowers some SubfieldOps. Zero-width can leave
2742  // invalid subfield accesses
2743  if (getLoweredValue(op) || !op.getInput())
2744  return success();
2745 
2746  if (isZeroBitFIRRTLType(op.getType()))
2747  return setLowering(op, Value());
2748 
2749  auto input = getPossiblyInoutLoweredValue(op.getInput());
2750  if (!input)
2751  return op.emitError() << "input lowering failed";
2752 
2753  auto result = lowerSubfield(op, input);
2754  if (failed(result))
2755  return failure();
2756  return setLowering(op, *result);
2757 }
2758 
2759 LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
2760  auto resultType = lowerType(op.getResult().getType());
2761  SmallVector<Value> operands;
2762  // NOTE: The operand order must be inverted.
2763  for (auto oper : llvm::reverse(op.getOperands())) {
2764  auto val = getLoweredValue(oper);
2765  if (!val)
2766  return failure();
2767  operands.push_back(val);
2768  }
2769  return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
2770 }
2771 
2772 LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
2773  auto resultType = lowerType(op.getResult().getType());
2774  SmallVector<Value> operands;
2775  for (auto oper : op.getOperands()) {
2776  auto val = getLoweredValue(oper);
2777  if (!val)
2778  return failure();
2779  operands.push_back(val);
2780  }
2781  return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
2782 }
2783 
2784 LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
2785  // Zero width values must be lowered to nothing.
2786  if (isZeroBitFIRRTLType(op.getType()))
2787  return setLowering(op, Value());
2788 
2789  auto input = getLoweredValue(op.getInput());
2790  auto tagName = op.getFieldNameAttr();
2791  auto type = lowerType(op.getType());
2792 
2793  if (auto structType = dyn_cast<hw::StructType>(type)) {
2794  auto enumType = structType.getFieldType("tag");
2795  auto enumAttr = hw::EnumFieldAttr::get(op.getLoc(), tagName, enumType);
2796  auto enumOp = builder.create<hw::EnumConstantOp>(enumAttr);
2797  auto unionType = structType.getFieldType("body");
2798  auto unionOp = builder.create<hw::UnionCreateOp>(unionType, tagName, input);
2799  SmallVector<Value> operands = {enumOp.getResult(), unionOp.getResult()};
2800  return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
2801  }
2802 
2803  return setLoweringTo<hw::EnumConstantOp>(
2804  op, hw::EnumFieldAttr::get(op.getLoc(), tagName, type));
2805 }
2806 
2807 LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
2808  auto resultType = lowerType(op.getResult().getType());
2809  auto attr =
2810  getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
2811 
2812  return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
2813  cast<ArrayAttr>(attr));
2814 }
2815 
2816 LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
2817  auto tagName = op.getFieldNameAttr();
2818  auto lhs = getLoweredValue(op.getInput());
2819  if (isa<hw::StructType>(lhs.getType()))
2820  lhs = builder.create<hw::StructExtractOp>(lhs, "tag");
2821  auto enumField = hw::EnumFieldAttr::get(op.getLoc(), tagName, lhs.getType());
2822  auto rhs = builder.create<hw::EnumConstantOp>(enumField);
2823  return setLoweringTo<hw::EnumCmpOp>(op, lhs, rhs);
2824 }
2825 
2826 LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
2827  // Zero width values must be lowered to nothing.
2828  if (isZeroBitFIRRTLType(op.getType()))
2829  return setLowering(op, Value());
2830 
2831  auto tagName = op.getFieldNameAttr();
2832  auto input = getLoweredValue(op.getInput());
2833  auto field = builder.create<hw::StructExtractOp>(input, "body");
2834  return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
2835 }
2836 
2837 //===----------------------------------------------------------------------===//
2838 // Declarations
2839 //===----------------------------------------------------------------------===//
2840 
2841 LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
2842  auto origResultType = op.getResult().getType();
2843 
2844  // Foreign types lower to a backedge that needs to be resolved by a later
2845  // connect op.
2846  if (!type_isa<FIRRTLType>(origResultType)) {
2847  createBackedge(op.getResult(), origResultType);
2848  return success();
2849  }
2850 
2851  auto resultType = lowerType(origResultType);
2852  if (!resultType)
2853  return failure();
2854 
2855  if (resultType.isInteger(0))
2856  return setLowering(op.getResult(), Value());
2857 
2858  // Name attr is required on sv.wire but optional on firrtl.wire.
2859  auto innerSym = lowerInnerSymbol(op);
2860  auto name = op.getNameAttr();
2861  // This is not a temporary wire created by the compiler, so attach a symbol
2862  // name.
2863  auto wire = builder.create<hw::WireOp>(
2864  op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
2865 
2866  if (auto svAttrs = sv::getSVAttributes(op))
2867  sv::setSVAttributes(wire, svAttrs);
2868 
2869  return setLowering(op.getResult(), wire);
2870 }
2871 
2872 LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
2873  auto resultTy = lowerType(op.getType());
2874  if (!resultTy)
2875  return failure();
2876  resultTy = sv::InOutType::get(op.getContext(), resultTy);
2877 
2878  SmallVector<Value, 4> operands;
2879  operands.reserve(op.getSubstitutions().size());
2880  for (auto operand : op.getSubstitutions()) {
2881  auto lowered = getLoweredValue(operand);
2882  if (!lowered)
2883  return failure();
2884  operands.push_back(lowered);
2885  }
2886 
2887  ArrayAttr symbols = op.getSymbolsAttr();
2888  if (!symbols)
2889  symbols = ArrayAttr::get(op.getContext(), {});
2890 
2891  return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
2892  operands, symbols);
2893 }
2894 
2895 LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
2896  auto operand = getLoweredValue(op.getInput());
2897  if (!operand)
2898  return handleZeroBit(
2899  op.getInput(), [&]() { return setLowering(op.getResult(), Value()); });
2900 
2901  // Node operations are logical noops, but may carry annotations or be
2902  // referred to through an inner name. If a don't touch is present, ensure
2903  // that we have a symbol name so we can keep the node as a wire.
2904  auto name = op.getNameAttr();
2905  auto innerSym = lowerInnerSymbol(op);
2906 
2907  if (innerSym)
2908  operand = builder.create<hw::WireOp>(operand, name, innerSym);
2909 
2910  // Move SV attributes.
2911  if (auto svAttrs = sv::getSVAttributes(op)) {
2912  if (!innerSym)
2913  operand = builder.create<hw::WireOp>(operand, name);
2914  sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
2915  }
2916 
2917  return setLowering(op.getResult(), operand);
2918 }
2919 
2920 LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
2921  auto resultType = lowerType(op.getResult().getType());
2922  if (!resultType)
2923  return failure();
2924  if (resultType.isInteger(0))
2925  return setLowering(op.getResult(), Value());
2926 
2927  Value clockVal = getLoweredValue(op.getClockVal());
2928  if (!clockVal)
2929  return failure();
2930 
2931  // Create a reg op, wiring itself to its input.
2932  auto innerSym = lowerInnerSymbol(op);
2933  Backedge inputEdge = backedgeBuilder.get(resultType);
2934  auto reg = builder.create<seq::FirRegOp>(inputEdge, clockVal,
2935  op.getNameAttr(), innerSym);
2936 
2937  // Pass along the start and end random initialization bits for this register.
2938  if (auto randomRegister = op->getAttr("firrtl.random_init_register"))
2939  reg->setAttr("firrtl.random_init_register", randomRegister);
2940  if (auto randomStart = op->getAttr("firrtl.random_init_start"))
2941  reg->setAttr("firrtl.random_init_start", randomStart);
2942  if (auto randomEnd = op->getAttr("firrtl.random_init_end"))
2943  reg->setAttr("firrtl.random_init_end", randomEnd);
2944 
2945  // Move SV attributes.
2946  if (auto svAttrs = sv::getSVAttributes(op))
2947  sv::setSVAttributes(reg, svAttrs);
2948 
2949  inputEdge.setValue(reg);
2950  circuitState.used_RANDOMIZE_REG_INIT = true;
2951  (void)setLowering(op.getResult(), reg);
2952  return success();
2953 }
2954 
2955 LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
2956  auto resultType = lowerType(op.getResult().getType());
2957  if (!resultType)
2958  return failure();
2959  if (resultType.isInteger(0))
2960  return setLowering(op.getResult(), Value());
2961 
2962  Value clockVal = getLoweredValue(op.getClockVal());
2963  Value resetSignal = getLoweredValue(op.getResetSignal());
2964  // Reset values may be narrower than the register. Extend appropriately.
2965  Value resetValue = getLoweredAndExtOrTruncValue(
2966  op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
2967 
2968  if (!clockVal || !resetSignal || !resetValue)
2969  return failure();
2970 
2971  // Create a reg op, wiring itself to its input.
2972  auto innerSym = lowerInnerSymbol(op);
2973  bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
2974  Backedge inputEdge = backedgeBuilder.get(resultType);
2975  auto reg =
2976  builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
2977  resetSignal, resetValue, innerSym, isAsync);
2978 
2979  // Pass along the start and end random initialization bits for this register.
2980  if (auto randomRegister = op->getAttr("firrtl.random_init_register"))
2981  reg->setAttr("firrtl.random_init_register", randomRegister);
2982  if (auto randomStart = op->getAttr("firrtl.random_init_start"))
2983  reg->setAttr("firrtl.random_init_start", randomStart);
2984  if (auto randomEnd = op->getAttr("firrtl.random_init_end"))
2985  reg->setAttr("firrtl.random_init_end", randomEnd);
2986 
2987  // Move SV attributes.
2988  if (auto svAttrs = sv::getSVAttributes(op))
2989  sv::setSVAttributes(reg, svAttrs);
2990 
2991  inputEdge.setValue(reg);
2992  circuitState.used_RANDOMIZE_REG_INIT = true;
2993  (void)setLowering(op.getResult(), reg);
2994 
2995  return success();
2996 }
2997 
2998 LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
2999  // TODO: Remove this restriction and preserve aggregates in
3000  // memories.
3001  if (type_isa<BundleType>(op.getDataType()))
3002  return op.emitOpError(
3003  "should have already been lowered from a ground type to an aggregate "
3004  "type using the LowerTypes pass. Use "
3005  "'firtool --lower-types' or 'circt-opt "
3006  "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3007  "to run this.");
3008 
3009  FirMemory memSummary = op.getSummary();
3010 
3011  // Create the memory declaration.
3012  auto memType = seq::FirMemType::get(
3013  op.getContext(), memSummary.depth, memSummary.dataWidth,
3014  memSummary.isMasked ? std::optional<uint32_t>(memSummary.maskBits)
3015  : std::optional<uint32_t>());
3016 
3017  seq::FirMemInitAttr memInit;
3018  if (auto init = op.getInitAttr())
3019  memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3020  init.getIsBinary(), init.getIsInline());
3021 
3022  auto memDecl = builder.create<seq::FirMemOp>(
3023  memType, memSummary.readLatency, memSummary.writeLatency,
3024  memSummary.readUnderWrite, memSummary.writeUnderWrite, op.getNameAttr(),
3025  op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3026 
3027  // If the module is outside the DUT, set the appropriate output directory for
3028  // the memory.
3029  if (!circuitState.isInDUT(theModule))
3030  if (auto testBenchDir = circuitState.getTestBenchDirectory())
3031  memDecl.setOutputFileAttr(testBenchDir);
3032 
3033  // Memories return multiple structs, one for each port, which means we
3034  // have two layers of type to split apart.
3035  for (size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3036 
3037  auto addOutput = [&](StringRef field, size_t width, Value value) {
3038  for (auto &a : getAllFieldAccesses(op.getResult(i), field)) {
3039  if (width > 0)
3040  (void)setLowering(a, value);
3041  else
3042  a->eraseOperand(0);
3043  }
3044  };
3045 
3046  auto addInput = [&](StringRef field, Value backedge) {
3047  for (auto a : getAllFieldAccesses(op.getResult(i), field)) {
3048  if (a.getType()
3049  .cast<FIRRTLBaseType>()
3050  .getPassiveType()
3051  .getBitWidthOrSentinel() > 0)
3052  (void)setLowering(a, backedge);
3053  else
3054  a->eraseOperand(0);
3055  }
3056  };
3057 
3058  auto addInputPort = [&](StringRef field, size_t width) -> Value {
3059  // If the memory is 0-width, do not materialize any connections to it.
3060  // However, `seq.firmem` now requires a 1-bit input, so materialize
3061  // a dummy x value to provide it with.
3062  Value backedge, portValue;
3063  if (width == 0) {
3064  portValue = getOrCreateXConstant(1);
3065  } else {
3066  auto portType = IntegerType::get(op.getContext(), width);
3067  backedge = portValue = createBackedge(builder.getLoc(), portType);
3068  }
3069  addInput(field, backedge);
3070  return portValue;
3071  };
3072 
3073  auto addClock = [&](StringRef field) -> Value {
3074  Type clockTy = seq::ClockType::get(op.getContext());
3075  Value portValue = createBackedge(builder.getLoc(), clockTy);
3076  addInput(field, portValue);
3077  return portValue;
3078  };
3079 
3080  auto memportKind = op.getPortKind(i);
3081  if (memportKind == MemOp::PortKind::Read) {
3082  auto addr = addInputPort("addr", op.getAddrBits());
3083  auto en = addInputPort("en", 1);
3084  auto clk = addClock("clk");
3085  auto data = builder.create<seq::FirMemReadOp>(memDecl, addr, clk, en);
3086  addOutput("data", memSummary.dataWidth, data);
3087  } else if (memportKind == MemOp::PortKind::ReadWrite) {
3088  auto addr = addInputPort("addr", op.getAddrBits());
3089  auto en = addInputPort("en", 1);
3090  auto clk = addClock("clk");
3091  // If maskBits =1, then And the mask field with enable, and update the
3092  // enable. Else keep mask port.
3093  auto mode = addInputPort("wmode", 1);
3094  if (!memSummary.isMasked)
3095  mode = builder.createOrFold<comb::AndOp>(mode, addInputPort("wmask", 1),
3096  true);
3097  auto wdata = addInputPort("wdata", memSummary.dataWidth);
3098  // Ignore mask port, if maskBits =1
3099  Value mask;
3100  if (memSummary.isMasked)
3101  mask = addInputPort("wmask", memSummary.maskBits);
3102  auto rdata = builder.create<seq::FirMemReadWriteOp>(
3103  memDecl, addr, clk, en, wdata, mode, mask);
3104  addOutput("rdata", memSummary.dataWidth, rdata);
3105  } else {
3106  auto addr = addInputPort("addr", op.getAddrBits());
3107  // If maskBits =1, then And the mask field with enable, and update the
3108  // enable. Else keep mask port.
3109  auto en = addInputPort("en", 1);
3110  if (!memSummary.isMasked)
3111  en = builder.createOrFold<comb::AndOp>(en, addInputPort("mask", 1),
3112  true);
3113  auto clk = addClock("clk");
3114  auto data = addInputPort("data", memSummary.dataWidth);
3115  // Ignore mask port, if maskBits =1
3116  Value mask;
3117  if (memSummary.isMasked)
3118  mask = addInputPort("mask", memSummary.maskBits);
3119  builder.create<seq::FirMemWriteOp>(memDecl, addr, clk, en, data, mask);
3120  }
3121  }
3122 
3123  circuitState.used_RANDOMIZE_MEM_INIT = true;
3124  return success();
3125 }
3126 
3127 LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3128  Operation *oldModule =
3129  circuitState.getInstanceGraph()->getReferencedModule(oldInstance);
3130  auto newModule = circuitState.getNewModule(oldModule);
3131  if (!newModule) {
3132  oldInstance->emitOpError("could not find module [")
3133  << oldInstance.getModuleName() << "] referenced by instance";
3134  return failure();
3135  }
3136 
3137  // If this is a referenced to a parameterized extmodule, then bring the
3138  // parameters over to this instance.
3139  ArrayAttr parameters;
3140  if (auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3141  parameters = getHWParameters(oldExtModule, /*ignoreValues=*/false);
3142 
3143  // Decode information about the input and output ports on the referenced
3144  // module.
3145  SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3146 
3147  // Build an index from the name attribute to an index into portInfo, so we
3148  // can do efficient lookups.
3149  llvm::SmallDenseMap<Attribute, unsigned> portIndicesByName;
3150  for (unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3151  portIndicesByName[portInfo[portIdx].name] = portIdx;
3152 
3153  // Ok, get ready to create the new instance operation. We need to prepare
3154  // input operands.
3155  SmallVector<Value, 8> operands;
3156  for (size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3157  auto &port = portInfo[portIndex];
3158  auto portType = lowerType(port.type);
3159  if (!portType) {
3160  oldInstance->emitOpError("could not lower type of port ") << port.name;
3161  return failure();
3162  }
3163 
3164  // Drop zero bit input/inout ports.
3165  if (portType.isInteger(0))
3166  continue;
3167 
3168  // We wire outputs up after creating the instance.
3169  if (port.isOutput())
3170  continue;
3171 
3172  auto portResult = oldInstance.getResult(portIndex);
3173  assert(portResult && "invalid IR, couldn't find port");
3174 
3175  // Replace the input port with a backedge. If it turns out that this port
3176  // is never driven, an uninitialized wire will be materialized at the end.
3177  if (port.isInput()) {
3178  operands.push_back(createBackedge(portResult, portType));
3179  continue;
3180  }
3181 
3182  // If the result has an analog type and is used only by attach op, try
3183  // eliminating a temporary wire by directly using an attached value.
3184  if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3185  if (auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3186  if (auto source = getSingleNonInstanceOperand(attach)) {
3187  auto loweredResult = getPossiblyInoutLoweredValue(source);
3188  operands.push_back(loweredResult);
3189  (void)setLowering(portResult, loweredResult);
3190  continue;
3191  }
3192  }
3193  }
3194 
3195  // Create a wire for each inout operand, so there is something to connect
3196  // to. The instance becomes the sole driver of this wire.
3197  auto wire = builder.create<sv::WireOp>(
3198  portType, "." + port.getName().str() + ".wire");
3199 
3200  // Know that the argument FIRRTL value is equal to this wire, allowing
3201  // connects to it to be lowered.
3202  (void)setLowering(portResult, wire);
3203 
3204  operands.push_back(wire);
3205  }
3206 
3207  // If this instance is destined to be lowered to a bind, generate a symbol
3208  // for it and generate a bind op. Enter the bind into global
3209  // CircuitLoweringState so that this can be moved outside of module once
3210  // we're guaranteed to not be a parallel context.
3211  auto innerSym = oldInstance.getInnerSymAttr();
3212  if (oldInstance.getLowerToBind()) {
3213  if (!innerSym)
3214  innerSym = hw::InnerSymAttr::get(
3215  builder.getStringAttr("__" + oldInstance.getName() + "__"));
3216  auto bindOp = builder.create<sv::BindOp>(theModule.getNameAttr(),
3217  innerSym.getSymName());
3218  // If the lowered op already had output file information, then use that.
3219  // Otherwise, generate some default bind information.
3220  if (auto outputFile = oldInstance->getAttr("output_file"))
3221  bindOp->setAttr("output_file", outputFile);
3222  // Add the bind to the circuit state. This will be moved outside of the
3223  // encapsulating module after all modules have been processed in parallel.
3224  circuitState.addBind(bindOp);
3225  }
3226 
3227  // Create the new hw.instance operation.
3228  auto newInstance = builder.create<hw::InstanceOp>(
3229  newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3230 
3231  if (oldInstance.getLowerToBind())
3232  newInstance->setAttr("doNotPrint", builder.getBoolAttr(true));
3233 
3234  if (newInstance.getInnerSymAttr())
3235  if (auto forceName = circuitState.instanceForceNames.lookup(
3236  {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3237  newInstance.getInnerNameAttr()}))
3238  newInstance->setAttr("hw.verilogName", forceName);
3239 
3240  // Now that we have the new hw.instance, we need to remap all of the users
3241  // of the outputs/results to the values returned by the instance.
3242  unsigned resultNo = 0;
3243  for (size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3244  auto &port = portInfo[portIndex];
3245  if (!port.isOutput() || isZeroBitFIRRTLType(port.type))
3246  continue;
3247 
3248  Value resultVal = newInstance.getResult(resultNo);
3249 
3250  auto oldPortResult = oldInstance.getResult(portIndex);
3251  (void)setLowering(oldPortResult, resultVal);
3252  ++resultNo;
3253  }
3254  return success();
3255 }
3256 
3257 //===----------------------------------------------------------------------===//
3258 // Unary Operations
3259 //===----------------------------------------------------------------------===//
3260 
3261 // Lower a cast that is a noop at the HW level.
3262 LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3263  auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3264  if (!operand)
3265  return failure();
3266 
3267  // Noop cast.
3268  return setLowering(op->getResult(0), operand);
3269 }
3270 
3271 LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3272  if (isa<ClockType>(op.getInput().getType()))
3273  return setLowering(op->getResult(0),
3274  getLoweredNonClockValue(op.getInput()));
3275  return lowerNoopCast(op);
3276 }
3277 
3278 LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3279  if (isa<ClockType>(op.getInput().getType()))
3280  return setLowering(op->getResult(0),
3281  getLoweredNonClockValue(op.getInput()));
3282  return lowerNoopCast(op);
3283 }
3284 
3285 LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3286  return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3287 }
3288 
3289 LogicalResult FIRRTLLowering::visitExpr(mlir::UnrealizedConversionCastOp op) {
3290  // General lowering for non-unary casts.
3291  if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3292  return failure();
3293 
3294  auto operand = op.getOperand(0);
3295  auto result = op.getResult(0);
3296 
3297  // FIRRTL -> FIRRTL
3298  if (type_isa<FIRRTLType>(operand.getType()) &&
3299  type_isa<FIRRTLType>(result.getType()))
3300  return lowerNoopCast(op);
3301 
3302  // other -> FIRRTL
3303  // other -> other
3304  if (!type_isa<FIRRTLType>(operand.getType())) {
3305  if (type_isa<FIRRTLType>(result.getType()))
3306  return setLowering(result, getPossiblyInoutLoweredValue(operand));
3307  return failure(); // general foreign op lowering for other -> other
3308  }
3309 
3310  // FIRRTL -> other
3311  // Otherwise must be a conversion from FIRRTL type to standard type.
3312  auto lowered_result = getLoweredValue(operand);
3313  if (!lowered_result) {
3314  // If this is a conversion from a zero bit HW type to firrtl value, then
3315  // we want to successfully lower this to a null Value.
3316  if (operand.getType().isSignlessInteger(0)) {
3317  return setLowering(result, Value());
3318  }
3319  return failure();
3320  }
3321 
3322  // We lower builtin.unrealized_conversion_cast converting from a firrtl type
3323  // to a standard type into the lowered operand.
3324  result.replaceAllUsesWith(lowered_result);
3325  return success();
3326 }
3327 
3328 LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3329  // Conversions from hw struct types to FIRRTL types are lowered as the
3330  // input operand.
3331  if (auto opStructType = op.getOperand().getType().dyn_cast<hw::StructType>())
3332  return setLowering(op, op.getOperand());
3333 
3334  // Otherwise must be a conversion from FIRRTL bundle type to hw struct
3335  // type.
3336  auto result = getLoweredValue(op.getOperand());
3337  if (!result)
3338  return failure();
3339 
3340  // We lower firrtl.stdStructCast converting from a firrtl bundle to an hw
3341  // struct type into the lowered operand.
3342  op.replaceAllUsesWith(result);
3343  return success();
3344 }
3345 
3346 LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3347  auto operand = getLoweredValue(op.getOperand());
3348  if (!operand)
3349  return failure();
3350  auto resultType = lowerType(op.getType());
3351  if (!resultType)
3352  return failure();
3353 
3354  return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3355 }
3356 
3357 LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3358  auto operand = getLoweredValue(op.getOperand());
3359  if (!operand) {
3360  return handleZeroBit(op.getOperand(), [&]() {
3361  // Unsigned zero bit to Signed is 1b0.
3362  if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3363  return setLowering(op, getOrCreateIntConstant(1, 0));
3364  // Signed->Signed is a zero bit value.
3365  return setLowering(op, Value());
3366  });
3367  }
3368 
3369  // Signed to signed is a noop.
3370  if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3371  return setLowering(op, operand);
3372 
3373  // Otherwise prepend a zero bit.
3374  auto zero = getOrCreateIntConstant(1, 0);
3375  return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3376 }
3377 
3378 LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3379  auto operand = getLoweredValue(op.getInput());
3380  if (!operand)
3381  return failure();
3382  // ~x ---> x ^ 0xFF
3383  auto allOnes = getOrCreateIntConstant(
3384  APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3385  return setLoweringTo<comb::XorOp>(op, operand, allOnes, true);
3386 }
3387 
3388 LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3389  // FIRRTL negate always adds a bit.
3390  // -x ---> 0-sext(x) or 0-zext(x)
3391  auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3392  if (!operand)
3393  return failure();
3394 
3395  auto resultType = lowerType(op.getType());
3396 
3397  auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3398  return setLoweringTo<comb::SubOp>(op, zero, operand, true);
3399 }
3400 
3401 // Pad is a noop or extension operation.
3402 LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3403  auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3404  if (!operand)
3405  return failure();
3406  return setLowering(op, operand);
3407 }
3408 
3409 LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3410  auto operand = getLoweredValue(op.getInput());
3411  if (!operand) {
3412  return handleZeroBit(op.getInput(), [&]() {
3413  return setLowering(op, getOrCreateIntConstant(1, 0));
3414  });
3415  return failure();
3416  }
3417 
3418  return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
3419  true);
3420 }
3421 
3422 LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3423  auto operand = getLoweredValue(op.getInput());
3424  if (!operand) {
3425  return handleZeroBit(op.getInput(), [&]() {
3426  return setLowering(op, getOrCreateIntConstant(1, 1));
3427  });
3428  }
3429 
3430  // Lower AndR to == -1
3431  return setLoweringTo<comb::ICmpOp>(
3432  op, ICmpPredicate::eq, operand,
3433  getOrCreateIntConstant(
3434  APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3435  true);
3436 }
3437 
3438 LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3439  auto operand = getLoweredValue(op.getInput());
3440  if (!operand) {
3441  return handleZeroBit(op.getInput(), [&]() {
3442  return setLowering(op, getOrCreateIntConstant(1, 0));
3443  });
3444  return failure();
3445  }
3446 
3447  // Lower OrR to != 0
3448  return setLoweringTo<comb::ICmpOp>(
3449  op, ICmpPredicate::ne, operand,
3450  getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3451  true);
3452 }
3453 
3454 //===----------------------------------------------------------------------===//
3455 // Binary Operations
3456 //===----------------------------------------------------------------------===//
3457 
3458 template <typename ResultOpType>
3459 LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3460  auto resultType = op->getResult(0).getType();
3461  auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3462  auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3463  if (!lhs || !rhs)
3464  return failure();
3465 
3466  return setLoweringTo<ResultOpType>(op, lhs, rhs, true);
3467 }
3468 
3469 /// Element-wise logical operations can be lowered into bitcast and normal comb
3470 /// operations. Eventually we might want to introduce elementwise operations
3471 /// into HW/SV level as well.
3472 template <typename ResultOpType>
3473 LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3474  auto resultType = op->getResult(0).getType();
3475  auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3476  auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3477 
3478  if (!lhs || !rhs)
3479  return failure();
3480  auto bitwidth = firrtl::getBitWidth(type_cast<FIRRTLBaseType>(resultType));
3481 
3482  if (!bitwidth)
3483  return failure();
3484 
3485  // TODO: Introduce elementwise operations to HW dialect instead of abusing
3486  // bitcast operations.
3487  auto intType = builder.getIntegerType(*bitwidth);
3488  auto retType = lhs.getType();
3489  lhs = builder.createOrFold<hw::BitcastOp>(intType, lhs);
3490  rhs = builder.createOrFold<hw::BitcastOp>(intType, rhs);
3491  auto result = builder.createOrFold<ResultOpType>(lhs, rhs, /*twoState=*/true);
3492  return setLoweringTo<hw::BitcastOp>(op, retType, result);
3493 }
3494 
3495 /// lowerBinOp extends each operand to the destination type, then performs the
3496 /// specified binary operator.
3497 template <typename ResultUnsignedOpType, typename ResultSignedOpType>
3498 LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
3499  // Extend the two operands to match the destination type.
3500  auto resultType = op->getResult(0).getType();
3501  auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3502  auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3503  if (!lhs || !rhs)
3504  return failure();
3505 
3506  // Emit the result operation.
3507  if (type_cast<IntType>(resultType).isSigned())
3508  return setLoweringTo<ResultSignedOpType>(op, lhs, rhs, true);
3509  return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs, true);
3510 }
3511 
3512 /// lowerCmpOp extends each operand to the longest type, then performs the
3513 /// specified binary operator.
3514 LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
3515  ICmpPredicate unsignedOp) {
3516  // Extend the two operands to match the longest type.
3517  auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
3518  auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
3519  if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
3520  return failure();
3521 
3522  auto cmpType = getWidestIntType(lhsIntType, rhsIntType);
3523  if (cmpType.getWidth() == 0) // Handle 0-width inputs by promoting to 1 bit.
3524  cmpType = UIntType::get(builder.getContext(), 1);
3525  auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
3526  auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
3527  if (!lhs || !rhs)
3528  return failure();
3529 
3530  // Emit the result operation.
3531  Type resultType = builder.getIntegerType(1);
3532  return setLoweringTo<comb::ICmpOp>(
3533  op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
3534  true);
3535 }
3536 
3537 /// Lower a divide or dynamic shift, where the operation has to be performed
3538 /// in the widest type of the result and two inputs then truncated down.
3539 template <typename SignedOp, typename UnsignedOp>
3540 LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
3541  // hw has equal types for these, firrtl doesn't. The type of the firrtl
3542  // RHS may be wider than the LHS, and we cannot truncate off the high bits
3543  // (because an overlarge amount is supposed to shift in sign or zero bits).
3544  auto opType = type_cast<IntType>(op->getResult(0).getType());
3545  if (opType.getWidth() == 0)
3546  return setLowering(op->getResult(0), Value());
3547 
3548  auto resultType = getWidestIntType(opType, op->getOperand(1).getType());
3549  resultType = getWidestIntType(resultType, op->getOperand(0).getType());
3550  auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3551  auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3552  if (!lhs || !rhs)
3553  return failure();
3554 
3555  Value result;
3556  if (opType.isSigned())
3557  result = builder.createOrFold<SignedOp>(lhs, rhs, true);
3558  else
3559  result = builder.createOrFold<UnsignedOp>(lhs, rhs, true);
3560 
3561  tryCopyName(result.getDefiningOp(), op);
3562 
3563  if (resultType == opType)
3564  return setLowering(op->getResult(0), result);
3565  return setLoweringTo<comb::ExtractOp>(op, lowerType(opType), result, 0);
3566 }
3567 
3568 LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
3569  auto lhs = getLoweredValue(op.getLhs());
3570  auto rhs = getLoweredValue(op.getRhs());
3571  if (!lhs) {
3572  return handleZeroBit(op.getLhs(), [&]() {
3573  if (rhs) // cat(0bit, x) --> x
3574  return setLowering(op, rhs);
3575  // cat(0bit, 0bit) --> 0bit
3576  return handleZeroBit(op.getRhs(),
3577  [&]() { return setLowering(op, Value()); });
3578  });
3579  }
3580 
3581  if (!rhs) // cat(x, 0bit) --> x
3582  return handleZeroBit(op.getRhs(), [&]() { return setLowering(op, lhs); });
3583 
3584  return setLoweringTo<comb::ConcatOp>(op, lhs, rhs);
3585 }
3586 
3587 //===----------------------------------------------------------------------===//
3588 // Verif Operations
3589 //===----------------------------------------------------------------------===//
3590 
3591 LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
3592  auto input = getLoweredNonClockValue(op.getArg());
3593  if (!input)
3594  return failure();
3595 
3596  return setLoweringTo<comb::ICmpOp>(
3597  op, ICmpPredicate::ceq, input,
3598  getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()), true);
3599 }
3600 
3601 LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
3602  auto resultType = builder.getIntegerType(1);
3603  auto str = builder.create<sv::ConstantStrOp>(op.getFormatString());
3604  auto reg =
3605  builder.create<sv::RegOp>(resultType, builder.getStringAttr("_pargs"));
3606  addToInitialBlock([&]() {
3607  auto call = builder.create<sv::SystemFunctionOp>(
3608  resultType, "test$plusargs", ArrayRef<Value>{str});
3609  builder.create<sv::BPAssignOp>(reg, call);
3610  });
3611  return setLoweringTo<sv::ReadInOutOp>(op, reg);
3612 }
3613 
3614 LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
3615  auto resultType = builder.getIntegerType(1);
3616  auto type = lowerType(op.getResult().getType());
3617  if (!type)
3618  return failure();
3619  auto regv =
3620  builder.create<sv::RegOp>(type, builder.getStringAttr("_pargs_v_"));
3621  auto regf =
3622  builder.create<sv::RegOp>(resultType, builder.getStringAttr("_pargs_f"));
3623  builder.create<sv::IfDefOp>(
3624  "SYNTHESIS",
3625  [&]() {
3626  auto cst0 = getOrCreateIntConstant(1, 0);
3627  builder.create<sv::AssignOp>(regf, cst0);
3628  },
3629  [&]() {
3630  addToInitialBlock([&]() {
3631  auto zero32 = getOrCreateIntConstant(32, 0);
3632  auto tmpResultType = builder.getIntegerType(32);
3633  auto str = builder.create<sv::ConstantStrOp>(op.getFormatString());
3634  auto call = builder.create<sv::SystemFunctionOp>(
3635  tmpResultType, "value$plusargs", ArrayRef<Value>{str, regv});
3636  auto truevalue = builder.create<comb::ICmpOp>(ICmpPredicate::ne, call,
3637  zero32, true);
3638  builder.create<sv::BPAssignOp>(regf, truevalue);
3639  });
3640  });
3641  auto readf = builder.create<sv::ReadInOutOp>(regf);
3642  auto readv = builder.create<sv::ReadInOutOp>(regv);
3643 
3644  (void)setLowering(op.getResult(), readv);
3645  return setLowering(op.getFound(), readf);
3646 }
3647 
3648 LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
3649  op.emitError("SizeOf should have been resolved.");
3650  return failure();
3651 }
3652 
3653 LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
3654  Value testEnable;
3655  if (op.getTestEnable())
3656  testEnable = getLoweredValue(op.getTestEnable());
3657  return setLoweringTo<seq::ClockGateOp>(
3658  op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
3659  testEnable, /*inner_sym=*/hw::InnerSymAttr{});
3660 }
3661 
3662 LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
3663  return setLoweringToLTL<ltl::AndOp>(
3664  op,
3665  ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3666 }
3667 
3668 LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
3669  return setLoweringToLTL<ltl::OrOp>(
3670  op,
3671  ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3672 }
3673 
3674 LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
3675  return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
3676  op.getDelayAttr(), op.getLengthAttr());
3677 }
3678 
3679 LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
3680  return setLoweringToLTL<ltl::ConcatOp>(
3681  op,
3682  ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3683 }
3684 
3685 LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
3686  return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
3687 }
3688 
3689 LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
3690  return setLoweringToLTL<ltl::ImplicationOp>(
3691  op,
3692  ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3693 }
3694 
3695 LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
3696  return setLoweringToLTL<ltl::EventuallyOp>(op,
3697  getLoweredValue(op.getInput()));
3698 }
3699 
3700 LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
3701  return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
3702  ltl::ClockEdge::Pos,
3703  getLoweredNonClockValue(op.getClock()));
3704 }
3705 
3706 LogicalResult FIRRTLLowering::visitExpr(LTLDisableIntrinsicOp op) {
3707  return setLoweringToLTL<ltl::DisableOp>(
3708  op,
3709  ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3710 }
3711 
3712 LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
3713  builder.create<verif::AssertOp>(getLoweredValue(op.getProperty()),
3714  op.getLabelAttr());
3715  return success();
3716 }
3717 
3718 LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
3719  builder.create<verif::AssumeOp>(getLoweredValue(op.getProperty()),
3720  op.getLabelAttr());
3721  return success();
3722 }
3723 
3724 LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
3725  builder.create<verif::CoverOp>(getLoweredValue(op.getProperty()),
3726  op.getLabelAttr());
3727  return success();
3728 }
3729 
3730 LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
3731  auto clock = getLoweredNonClockValue(op.getClock());
3732  auto reset = getLoweredValue(op.getReset());
3733  if (!clock || !reset)
3734  return failure();
3735  auto resetType = op.getReset().getType();
3736  auto uintResetType = dyn_cast<UIntType>(resetType);
3737  auto isSync = uintResetType && uintResetType.getWidth() == 1;
3738  auto isAsync = isa<AsyncResetType>(resetType);
3739  if (!isAsync && !isSync) {
3740  auto d = op.emitError("uninferred reset passed to 'has_been_reset'; "
3741  "requires sync or async reset");
3742  d.attachNote() << "reset is of type " << resetType
3743  << ", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
3744  return failure();
3745  }
3746  return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
3747 }
3748 
3749 //===----------------------------------------------------------------------===//
3750 // Other Operations
3751 //===----------------------------------------------------------------------===//
3752 
3753 LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
3754  auto input = getLoweredValue(op.getInput());
3755  if (!input)
3756  return failure();
3757 
3758  Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
3759  return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
3760 }
3761 
3762 LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
3763  auto resultTy = lowerType(op.getType());
3764  if (!resultTy)
3765  return failure();
3766 
3767  // Values of analog type always need to be lowered to something with inout
3768  // type. We do that by lowering to a wire and return that. As with the
3769  // SFC, we do not connect anything to this, because it is bidirectional.
3770  if (type_isa<AnalogType>(op.getType()))
3771  // This is a locally visible, private wire created by the compiler, so do
3772  // not attach a symbol name.
3773  return setLoweringTo<sv::WireOp>(op, resultTy, ".invalid_analog");
3774 
3775  // We don't allow aggregate values which contain values of analog types.
3776  if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
3777  return failure();
3778 
3779  // We lower invalid to 0. TODO: the FIRRTL spec mentions something about
3780  // lowering it to a random value, we should see if this is what we need to
3781  // do.
3782  if (auto bitwidth =
3783  firrtl::getBitWidth(type_cast<FIRRTLBaseType>(op.getType()))) {
3784  if (*bitwidth == 0) // Let the caller handle zero width values.
3785  return failure();
3786 
3787  auto constant = getOrCreateIntConstant(*bitwidth, 0);
3788  // If the result is an aggregate value, we have to bitcast the constant.
3789  if (!type_isa<IntegerType>(resultTy))
3790  constant = builder.create<hw::BitcastOp>(resultTy, constant);
3791  return setLowering(op, constant);
3792  }
3793 
3794  // Invalid for bundles isn't supported.
3795  op.emitOpError("unsupported type");
3796  return failure();
3797 }
3798 
3799 LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
3800  auto input = getLoweredValue(op.getInput());
3801  if (!input)
3802  return failure();
3803  auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3804  if (op.getAmount() == 0)
3805  return setLowering(op, Value());
3806  Type resultType = builder.getIntegerType(op.getAmount());
3807  return setLoweringTo<comb::ExtractOp>(op, resultType, input,
3808  inWidth - op.getAmount());
3809 }
3810 
3811 LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
3812  auto input = getLoweredValue(op.getInput());
3813  if (!input) {
3814  return handleZeroBit(op.getInput(), [&]() {
3815  if (op.getAmount() == 0)
3816  return failure();
3817  return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
3818  });
3819  }
3820 
3821  // Handle the degenerate case.
3822  if (op.getAmount() == 0)
3823  return setLowering(op, input);
3824 
3825  auto zero = getOrCreateIntConstant(op.getAmount(), 0);
3826  return setLoweringTo<comb::ConcatOp>(op, input, zero);
3827 }
3828 
3829 LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
3830  auto input = getLoweredValue(op.getInput());
3831  if (!input)
3832  return failure();
3833 
3834  // Handle the special degenerate cases.
3835  auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3836  auto shiftAmount = op.getAmount();
3837  if (shiftAmount >= inWidth) {
3838  // Unsigned shift by full width returns a single-bit zero.
3839  if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
3840  return setLowering(op, getOrCreateIntConstant(1, 0));
3841 
3842  // Signed shift by full width is equivalent to extracting the sign bit.
3843  shiftAmount = inWidth - 1;
3844  }
3845 
3846  Type resultType = builder.getIntegerType(inWidth - shiftAmount);
3847  return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
3848 }
3849 
3850 LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
3851  auto input = getLoweredValue(op.getInput());
3852  if (!input)
3853  return failure();
3854 
3855  auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3856  if (inWidth == op.getAmount())
3857  return setLowering(op, Value());
3858  Type resultType = builder.getIntegerType(inWidth - op.getAmount());
3859  return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
3860 }
3861 
3862 LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
3863  auto cond = getLoweredValue(op.getSel());
3864  auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
3865  auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
3866  if (!cond || !ifTrue || !ifFalse)
3867  return failure();
3868 
3869  if (op.getType().isa<ClockType>())
3870  return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
3871  return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
3872  true);
3873 }
3874 
3875 LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
3876  auto cond = getLoweredValue(op.getSel());
3877  auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
3878  auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
3879  if (!cond || !ifTrue || !ifFalse)
3880  return failure();
3881 
3882  auto val = builder.create<comb::MuxOp>(ifTrue.getType(), cond, ifTrue,
3883  ifFalse, true);
3884  return setLowering(op, createValueWithMuxAnnotation(val, true));
3885 }
3886 
3887 LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
3888  auto sel = getLoweredValue(op.getSel());
3889  auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
3890  auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
3891  auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
3892  auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
3893  if (!sel || !v3 || !v2 || !v1 || !v0)
3894  return failure();
3895  Value array[] = {v3, v2, v1, v0};
3896  auto create = builder.create<hw::ArrayCreateOp>(array);
3897  auto val = builder.create<hw::ArrayGetOp>(create, sel);
3898  return setLowering(op, createValueWithMuxAnnotation(val, false));
3899 }
3900 
3901 // Construct a value with vendor specific pragmas to utilize MUX cells.
3902 // Specifically we annotate pragmas in the following form.
3903 //
3904 // For an array indexing:
3905 // ```
3906 // wire GEN;
3907 // /* synopsys infer_mux_override */
3908 // assign GEN = array[index] /* cadence map_to_mux */;
3909 // ```
3910 //
3911 // For a mux:
3912 // ```
3913 // wire GEN;
3914 // /* synopsys infer_mux_override */
3915 // assign GEN = sel ? /* cadence map_to_mux */ high : low;
3916 // ```
3917 Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op, bool isMux2) {
3918  assert(op->getNumResults() == 1 && "only expect a single result");
3919  auto val = op->getResult(0);
3920  auto valWire = builder.create<sv::WireOp>(val.getType());
3921  // Use SV attributes to annotate pragmas.
3923  op, sv::SVAttributeAttr::get(builder.getContext(), "cadence map_to_mux",
3924  /*emitAsComment=*/true));
3925 
3926  // For operands, create temporary wires with optimization blockers(inner
3927  // symbols) so that the AST structure will never be destoyed in the later
3928  // pipeline.
3929  {
3930  OpBuilder::InsertionGuard guard(builder);
3931  builder.setInsertionPoint(op);
3932  StringRef namehint = isMux2 ? "mux2cell_in" : "mux4cell_in";
3933  for (auto [idx, operand] : llvm::enumerate(op->getOperands())) {
3934  auto [innerSym, _] = getOrAddInnerSym(
3935  op->getContext(), /*attr=*/nullptr, 0,
3936  [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
3937  auto wire =
3938  builder.create<hw::WireOp>(operand, namehint + Twine(idx), innerSym);
3939  op->setOperand(idx, wire);
3940  }
3941  }
3942 
3943  auto assignOp = builder.create<sv::AssignOp>(valWire, val);
3944  sv::setSVAttributes(assignOp,
3945  sv::SVAttributeAttr::get(builder.getContext(),
3946  "synopsys infer_mux_override",
3947  /*emitAsComment=*/true));
3948  return builder.create<sv::ReadInOutOp>(valWire);
3949 }
3950 
3951 Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
3952 
3953  auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
3954  // Extend to power of 2. FIRRTL semantics say out-of-bounds access result in
3955  // an indeterminate value. Existing chisel code depends on this behavior
3956  // being "return index 0". Ideally, we would tail extend the array to improve
3957  // optimization.
3958  if (!llvm::isPowerOf2_64(size)) {
3959  auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
3960  auto extValue = builder.create<hw::ArrayGetOp>(array, extElem);
3961  SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
3962  auto ext = builder.create<hw::ArrayCreateOp>(temp);
3963  Value temp2[] = {ext.getResult(), array};
3964  array = builder.create<hw::ArrayConcatOp>(temp2);
3965  }
3966 
3967  Value inBoundsRead = builder.create<hw::ArrayGetOp>(array, index);
3968 
3969  return inBoundsRead;
3970 }
3971 
3972 LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
3973  // Lower and resize to the index width.
3974  auto index = getLoweredAndExtOrTruncValue(
3975  op.getIndex(),
3976  UIntType::get(op.getContext(),
3977  getBitWidthFromVectorSize(op.getInputs().size())));
3978 
3979  if (!index)
3980  return failure();
3981  SmallVector<Value> loweredInputs;
3982  loweredInputs.reserve(op.getInputs().size());
3983  for (auto input : op.getInputs()) {
3984  auto lowered = getLoweredAndExtendedValue(input, op.getType());
3985  if (!lowered)
3986  return failure();
3987  loweredInputs.push_back(lowered);
3988  }
3989 
3990  Value array = builder.create<hw::ArrayCreateOp>(loweredInputs);
3991  return setLowering(op, createArrayIndexing(array, index));
3992 }
3993 
3994 LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
3995  auto resultTy = lowerType(op.getType());
3996  if (!resultTy)
3997  return failure();
3998 
3999  SmallVector<Value, 4> operands;
4000  operands.reserve(op.getSubstitutions().size());
4001  for (auto operand : op.getSubstitutions()) {
4002  auto lowered = getLoweredValue(operand);
4003  if (!lowered)
4004  return failure();
4005  operands.push_back(lowered);
4006  }
4007 
4008  ArrayAttr symbols = op.getSymbolsAttr();
4009  if (!symbols)
4010  symbols = ArrayAttr::get(op.getContext(), {});
4011 
4012  return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4013  operands, symbols);
4014 }
4015 
4016 LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4017  // This XMR is accessed solely by FIRRTL statements that mutate the probe.
4018  // To avoid the use of clock wires, create an `i1` wire and ensure that
4019  // all connections are also of the `i1` type.
4020  Type baseType = op.getType().getType();
4021 
4022  Type xmrType;
4023  if (isa<ClockType>(baseType))
4024  xmrType = builder.getIntegerType(1);
4025  else
4026  xmrType = lowerType(baseType);
4027 
4028  return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4029  op.getRef(), op.getVerbatimSuffixAttr());
4030 }
4031 
4032 LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4033  // When an XMR targets a clock wire, replace it with an `i1` wire, but
4034  // introduce a clock-typed read op into the design afterwards.
4035  Type xmrType;
4036  if (isa<ClockType>(op.getType()))
4037  xmrType = builder.getIntegerType(1);
4038  else
4039  xmrType = lowerType(op.getType());
4040 
4041  auto xmr = builder.create<sv::XMRRefOp>(
4042  sv::InOutType::get(xmrType), op.getRef(), op.getVerbatimSuffixAttr());
4043  auto readXmr = getReadValue(xmr);
4044  if (!isa<ClockType>(op.getType()))
4045  return setLowering(op, readXmr);
4046  return setLoweringTo<seq::ToClockOp>(op, readXmr);
4047 }
4048 
4049 //===----------------------------------------------------------------------===//
4050 // Statements
4051 //===----------------------------------------------------------------------===//
4052 
4053 LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4054  // Nothing! We could emit an comment as a verbatim op if there were a
4055  // reason to.
4056  return success();
4057 }
4058 
4059 /// Resolve a connection to `destVal`, an `hw::WireOp` or `seq::FirRegOp`, by
4060 /// updating the input operand to be `srcVal`. Returns true if the update was
4061 /// made and the connection can be considered lowered. Returns false if the
4062 /// destination isn't a wire or register with an input operand to be updated.
4063 /// Returns failure if the destination is a subaccess operation. These should be
4064 /// transposed to the right-hand-side by a pre-pass.
4065 FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4066  auto srcType = srcVal.getType();
4067  auto dstType = destVal.getType();
4068  if (srcType != dstType &&
4069  (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4070  srcVal = builder.create<hw::BitcastOp>(destVal.getType(), srcVal);
4071  }
4072  return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4073  .Case<hw::WireOp>([&](auto op) {
4074  maybeUnused(op.getInput());
4075  op.getInputMutable().assign(srcVal);
4076  return true;
4077  })
4078  .Case<seq::FirRegOp>([&](auto op) {
4079  maybeUnused(op.getNext());
4080  op.getNextMutable().assign(srcVal);
4081  return true;
4082  })
4083  .Case<hw::StructExtractOp, hw::ArrayGetOp>([](auto op) {
4084  // NOTE: msvc thinks `return op.emitOpError(...);` is ambiguous. So
4085  // return `failure()` separately.
4086  op.emitOpError("used as connect destination");
4087  return failure();
4088  })
4089  .Default([](auto) { return false; });
4090 }
4091 
4092 LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4093  auto dest = op.getDest();
4094  // The source can be a smaller integer, extend it as appropriate if so.
4095  auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4096  auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4097  if (!srcVal)
4098  return handleZeroBit(op.getSrc(), []() { return success(); });
4099 
4100  auto destVal = getPossiblyInoutLoweredValue(dest);
4101  if (!destVal)
4102  return failure();
4103 
4104  auto result = lowerConnect(destVal, srcVal);
4105  if (failed(result))
4106  return failure();
4107  if (*result)
4108  return success();
4109 
4110  // If this connect is driving a value that is currently a backedge, record
4111  // that the source is the value of the backedge.
4112  if (updateIfBackedge(destVal, srcVal))
4113  return success();
4114 
4115  if (!destVal.getType().isa<hw::InOutType>())
4116  return op.emitError("destination isn't an inout type");
4117 
4118  builder.create<sv::AssignOp>(destVal, srcVal);
4119  return success();
4120 }
4121 
4122 LogicalResult FIRRTLLowering::visitStmt(StrictConnectOp op) {
4123  auto dest = op.getDest();
4124  auto srcVal = getLoweredValue(op.getSrc());
4125  if (!srcVal)
4126  return handleZeroBit(op.getSrc(), []() { return success(); });
4127 
4128  auto destVal = getPossiblyInoutLoweredValue(dest);
4129  if (!destVal)
4130  return failure();
4131 
4132  auto result = lowerConnect(destVal, srcVal);
4133  if (failed(result))
4134  return failure();
4135  if (*result)
4136  return success();
4137 
4138  // If this connect is driving a value that is currently a backedge, record
4139  // that the source is the value of the backedge.
4140  if (updateIfBackedge(destVal, srcVal))
4141  return success();
4142 
4143  if (!destVal.getType().isa<hw::InOutType>())
4144  return op.emitError("destination isn't an inout type");
4145 
4146  builder.create<sv::AssignOp>(destVal, srcVal);
4147  return success();
4148 }
4149 
4150 LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4151  auto srcVal = getLoweredValue(op.getSrc());
4152  if (!srcVal)
4153  return failure();
4154 
4155  auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4156  if (!destVal)
4157  return failure();
4158 
4159  if (!destVal.getType().isa<hw::InOutType>())
4160  return op.emitError("destination isn't an inout type");
4161 
4162  // #ifndef SYNTHESIS
4163  addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4164  addToInitialBlock([&]() { builder.create<sv::ForceOp>(destVal, srcVal); });
4165  });
4166  return success();
4167 }
4168 
4169 LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4170  auto src = getLoweredNonClockValue(op.getSrc());
4171  auto clock = getLoweredNonClockValue(op.getClock());
4172  auto pred = getLoweredValue(op.getPredicate());
4173  if (!src || !clock || !pred)
4174  return failure();
4175 
4176  auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4177  if (!destVal)
4178  return failure();
4179 
4180  // #ifndef SYNTHESIS
4181  addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4182  addToAlwaysBlock(clock, [&]() {
4183  addIfProceduralBlock(
4184  pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4185  });
4186  });
4187  return success();
4188 }
4189 LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4190  auto src = getLoweredNonClockValue(op.getSrc());
4191  auto pred = getLoweredValue(op.getPredicate());
4192  if (!src || !pred)
4193  return failure();
4194 
4195  auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4196  if (!destVal)
4197  return failure();
4198 
4199  // #ifndef SYNTHESIS
4200  addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4201  addToInitialBlock([&]() {
4202  addIfProceduralBlock(
4203  pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4204  });
4205  });
4206  return success();
4207 }
4208 LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4209  auto clock = getLoweredNonClockValue(op.getClock());
4210  auto pred = getLoweredValue(op.getPredicate());
4211  if (!clock || !pred)
4212  return failure();
4213 
4214  auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4215  if (!destVal)
4216  return failure();
4217 
4218  // #ifndef SYNTHESIS
4219  addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4220  addToAlwaysBlock(clock, [&]() {
4221  addIfProceduralBlock(pred,
4222  [&]() { builder.create<sv::ReleaseOp>(destVal); });
4223  });
4224  });
4225  return success();
4226 }
4227 LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4228  auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4229  auto pred = getLoweredValue(op.getPredicate());
4230  if (!destVal || !pred)
4231  return failure();
4232 
4233  // #ifndef SYNTHESIS
4234  addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4235  addToInitialBlock([&]() {
4236  addIfProceduralBlock(pred,
4237  [&]() { builder.create<sv::ReleaseOp>(destVal); });
4238  });
4239  });
4240  return success();
4241 }
4242 
4243 // Printf is a macro op that lowers to an sv.ifdef.procedural, an sv.if,
4244 // and an sv.fwrite all nested together.
4245 LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
4246  auto clock = getLoweredNonClockValue(op.getClock());
4247  auto cond = getLoweredValue(op.getCond());
4248  if (!clock || !cond)
4249  return failure();
4250 
4251  SmallVector<Value, 4> operands;
4252  operands.reserve(op.getSubstitutions().size());
4253  for (auto operand : op.getSubstitutions()) {
4254  operands.push_back(getLoweredValue(operand));
4255  if (!operands.back()) {
4256  // If this is a zero bit operand, just pass a one bit zero.
4257  if (!isZeroBitFIRRTLType(operand.getType()))
4258  return failure();
4259  operands.back() = getOrCreateIntConstant(1, 0);
4260  }
4261  }
4262 
4263  // Emit an "#ifndef SYNTHESIS" guard into the always block.
4264  addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4265  addToAlwaysBlock(clock, [&]() {
4266  circuitState.used_PRINTF_COND = true;
4267 
4268  // Emit an "sv.if '`PRINTF_COND_ & cond' into the #ifndef.
4269  Value ifCond =
4270  builder.create<sv::MacroRefExprOp>(cond.getType(), "PRINTF_COND_");
4271  ifCond = builder.createOrFold<comb::AndOp>(ifCond, cond, true);
4272 
4273  addIfProceduralBlock(ifCond, [&]() {
4274  // Emit the sv.fwrite, writing to stderr by default.
4275  Value fdStderr = builder.create<hw::ConstantOp>(APInt(32, 0x80000002));
4276  builder.create<sv::FWriteOp>(fdStderr, op.getFormatString(), operands);
4277  });
4278  });
4279  });
4280 
4281  return success();
4282 }
4283 
4284 // Stop lowers into a nested series of behavioral statements plus $fatal
4285 // or $finish.
4286 LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4287  auto clock = getLoweredNonClockValue(op.getClock());
4288  auto cond = getLoweredValue(op.getCond());
4289  if (!clock || !cond)
4290  return failure();
4291 
4292  // Emit an "#ifndef SYNTHESIS" guard into the always block.
4293  addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4294  // Emit this into an "sv.always posedge" body.
4295  addToAlwaysBlock(clock, [&]() {
4296  circuitState.used_STOP_COND = true;
4297 
4298  // Emit an "sv.if '`STOP_COND_ & cond' into the #ifndef.
4299  Value ifCond =
4300  builder.create<sv::MacroRefExprOp>(cond.getType(), "STOP_COND_");
4301  ifCond = builder.createOrFold<comb::AndOp>(ifCond, cond, true);
4302  addIfProceduralBlock(ifCond, [&]() {
4303  // Emit the sv.fatal or sv.finish.
4304  if (op.getExitCode())
4305  builder.create<sv::FatalOp>();
4306  else
4307  builder.create<sv::FinishOp>();
4308  });
4309  });
4310  });
4311 
4312  return success();
4313 }
4314 
4315 /// Helper function to build an immediate assert operation based on the
4316 /// original FIRRTL operation name. This reduces code duplication in
4317 /// `lowerVerificationStatement`.
4318 template <typename... Args>
4319 static Operation *buildImmediateVerifOp(ImplicitLocOpBuilder &builder,
4320  StringRef opName, Args &&...args) {
4321  if (opName == "assert")
4322  return builder.create<sv::AssertOp>(std::forward<Args>(args)...);
4323  if (opName == "assume")
4324  return builder.create<sv::AssumeOp>(std::forward<Args>(args)...);
4325  if (opName == "cover")
4326  return builder.create<sv::CoverOp>(std::forward<Args>(args)...);
4327  llvm_unreachable("unknown verification op");
4328 }
4329 
4330 /// Helper function to build a concurrent assert operation based on the
4331 /// original FIRRTL operation name. This reduces code duplication in
4332 /// `lowerVerificationStatement`.
4333 template <typename... Args>
4334 static Operation *buildConcurrentVerifOp(ImplicitLocOpBuilder &builder,
4335  StringRef opName, Args &&...args) {
4336  if (opName == "assert")
4337  return builder.create<sv::AssertConcurrentOp>(std::forward<Args>(args)...);
4338  if (opName == "assume")
4339  return builder.create<sv::AssumeConcurrentOp>(std::forward<Args>(args)...);
4340  if (opName == "cover")
4341  return builder.create<sv::CoverConcurrentOp>(std::forward<Args>(args)...);
4342  llvm_unreachable("unknown verification op");
4343 }
4344 
4345 /// Template for lowering verification statements from type A to
4346 /// type B.
4347 ///
4348 /// For example, lowering the "foo" op to the "bar" op would start
4349 /// with:
4350 ///
4351 /// foo(clock, condition, enable, "message")
4352 ///
4353 /// This becomes a Verilog clocking block with the "bar" op guarded
4354 /// by an if enable:
4355 ///
4356 /// always @(posedge clock) begin
4357 /// if (enable) begin
4358 /// bar(condition);
4359 /// end
4360 /// end
4361 /// The above can also be reduced into a concurrent verification statement
4362 /// sv.assert.concurrent posedge %clock (condition && enable)
4363 LogicalResult FIRRTLLowering::lowerVerificationStatement(
4364  Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
4365  Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
4366  StringAttr opNameAttr, bool isConcurrent, EventControl opEventControl) {
4367  StringRef opName = op->getName().stripDialect();
4368 
4369  // The attribute holding the compile guards
4370  ArrayRef<Attribute> guards{};
4371  if (auto guardsAttr = op->template getAttrOfType<ArrayAttr>("guards"))
4372  guards = guardsAttr.getValue();
4373 
4374  auto isAssert = opName == "assert";
4375  auto isCover = opName == "cover";
4376 
4377  // TODO : Need to figure out if there is a cleaner way to get the string which
4378  // indicates the assert is UNR only. Or better - not rely on this at all -
4379  // ideally there should have been some other attribute which indicated that
4380  // this assert for UNR only.
4381  auto isUnrOnlyAssert = llvm::any_of(guards, [](Attribute attr) {
4382  StringAttr strAttr = dyn_cast<StringAttr>(attr);
4383  return strAttr && strAttr.getValue() == "USE_UNR_ONLY_CONSTRAINTS";
4384  });
4385 
4386  auto clock = getLoweredNonClockValue(opClock);
4387  auto enable = getLoweredValue(opEnable);
4388  auto predicate = getLoweredValue(opPredicate);
4389  if (!clock || !enable || !predicate)
4390  return failure();
4391 
4392  StringAttr label;
4393  if (opNameAttr && !opNameAttr.getValue().empty())
4394  label = opNameAttr;
4395  StringAttr prefixedLabel;
4396  if (label)
4397  prefixedLabel =
4398  StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
4399 
4400  StringAttr message;
4401  SmallVector<Value> messageOps;
4402  if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
4403  message = opMessageAttr;
4404  for (auto operand : opOperands) {
4405  auto loweredValue = getLoweredValue(operand);
4406  if (!loweredValue) {
4407  // If this is a zero bit operand, just pass a one bit zero.
4408  if (!isZeroBitFIRRTLType(operand.getType()))
4409  return failure();
4410  loweredValue = getOrCreateIntConstant(1, 0);
4411  }
4412  // Wrap any message ops in $sampled() to guarantee that these will print
4413  // with the same value as when the assertion triggers. (See SystemVerilog
4414  // 2017 spec section 16.9.3 for more information.) The custom
4415  // "ifElseFatal" variant is special cased because this isn't actually a
4416  // concurrent assertion.
4417  auto format = op->getAttrOfType<StringAttr>("format");
4418  if (isConcurrent && (!format || format.getValue() != "ifElseFatal" ||
4419  circuitState.emitChiselAssertsAsSVA))
4420  loweredValue = builder.create<sv::SampledOp>(loweredValue);
4421  messageOps.push_back(loweredValue);
4422  }
4423  }
4424 
4425  auto emit = [&]() {
4426  // Handle the purely procedural flavor of the operation.
4427  if (!isConcurrent && !circuitState.emitChiselAssertsAsSVA) {
4428  auto deferImmediate = circt::sv::DeferAssertAttr::get(
4429  builder.getContext(), circt::sv::DeferAssert::Immediate);
4430  addToAlwaysBlock(clock, [&]() {
4431  addIfProceduralBlock(enable, [&]() {
4432  buildImmediateVerifOp(builder, opName, predicate, deferImmediate,
4433  prefixedLabel, message, messageOps);
4434  });
4435  });
4436  return;
4437  }
4438 
4439  auto boolType = IntegerType::get(builder.getContext(), 1);
4440 
4441  // Handle the `ifElseFatal` format, which does not emit an SVA but
4442  // rather a process that uses $error and $fatal to perform the checks.
4443  // TODO: This should *not* be part of the op, but rather a lowering
4444  // option that the user of this pass can choose.
4445  auto format = op->template getAttrOfType<StringAttr>("format");
4446  if (format && (format.getValue() == "ifElseFatal" &&
4447  !circuitState.emitChiselAssertsAsSVA)) {
4448  predicate = comb::createOrFoldNot(predicate, builder, /*twoState=*/true);
4449  predicate = builder.createOrFold<comb::AndOp>(enable, predicate, true);
4450  addToIfDefBlock("SYNTHESIS", {}, [&]() {
4451  addToAlwaysBlock(clock, [&]() {
4452  addIfProceduralBlock(predicate, [&]() {
4453  circuitState.used_ASSERT_VERBOSE_COND = true;
4454  circuitState.used_STOP_COND = true;
4455  addIfProceduralBlock(
4456  builder.create<sv::MacroRefExprOp>(boolType,
4457  "ASSERT_VERBOSE_COND_"),
4458  [&]() { builder.create<sv::ErrorOp>(message, messageOps); });
4459  addIfProceduralBlock(
4460  builder.create<sv::MacroRefExprOp>(boolType, "STOP_COND_"),
4461  [&]() { builder.create<sv::FatalOp>(); });
4462  });
4463  });
4464  });
4465  return;
4466  }
4467 
4468  // Formulate the `enable -> predicate` as `!enable | predicate`.
4469  // Except for covers, combine them: enable & predicate
4470  if (!isCover) {
4471  auto notEnable =
4472  comb::createOrFoldNot(enable, builder, /*twoState=*/true);
4473  predicate = builder.createOrFold<comb::OrOp>(notEnable, predicate, true);
4474  } else {
4475  predicate = builder.createOrFold<comb::AndOp>(enable, predicate, true);
4476  }
4477 
4478  // Handle the regular SVA case.
4479  sv::EventControl event;
4480  switch (opEventControl) {
4481  case EventControl::AtPosEdge:
4482  event = circt::sv::EventControl::AtPosEdge;
4483  break;
4484  case EventControl::AtEdge:
4485  event = circt::sv::EventControl::AtEdge;
4486  break;
4487  case EventControl::AtNegEdge:
4488  event = circt::sv::EventControl::AtNegEdge;
4489  break;
4490  }
4491 
4493  builder, opName,
4494  circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
4495  predicate, prefixedLabel, message, messageOps);
4496 
4497  // Assertions gain a companion `assume` behind a
4498  // `USE_PROPERTY_AS_CONSTRAINT` guard.
4499  if (isAssert) {
4500  StringAttr assumeLabel;
4501  if (label)
4502  assumeLabel = StringAttr::get(builder.getContext(),
4503  "assume__" + label.getValue());
4504  addToIfDefBlock("USE_PROPERTY_AS_CONSTRAINT", [&]() {
4505  if (!isUnrOnlyAssert) {
4506  builder.create<sv::AssumeConcurrentOp>(
4507  circt::sv::EventControlAttr::get(builder.getContext(), event),
4508  clock, predicate, assumeLabel);
4509  } else {
4510  builder.create<sv::AlwaysOp>(
4511  ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate), [&]() {
4512  buildImmediateVerifOp(builder, "assume", predicate,
4514  builder.getContext(),
4515  circt::sv::DeferAssert::Immediate),
4516  assumeLabel);
4517  });
4518  }
4519  });
4520  }
4521  };
4522 
4523  // Wrap the verification statement up in the optional preprocessor
4524  // guards. This is a bit awkward since we want to translate an array of
4525  // guards into a recursive call to `addToIfDefBlock`.
4526  bool anyFailed = false;
4527  std::function<void()> emitWrapped = [&]() {
4528  if (guards.empty()) {
4529  emit();
4530  return;
4531  }
4532  auto guard = guards[0].dyn_cast<StringAttr>();
4533  if (!guard) {
4534  op->emitOpError("elements in `guards` array must be `StringAttr`");
4535  anyFailed = true;
4536  return;
4537  }
4538  guards = guards.drop_front();
4539  addToIfDefBlock(guard.getValue(), emitWrapped);
4540  };
4541  emitWrapped();
4542  if (anyFailed)
4543  return failure();
4544  return success();
4545 }
4546 
4547 // Lower an assert to SystemVerilog.
4548 LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
4549  return lowerVerificationStatement(
4550  op, "assert__", op.getClock(), op.getPredicate(), op.getEnable(),
4551  op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4552  op.getIsConcurrent(), op.getEventControl());
4553 }
4554 
4555 // Lower an assume to SystemVerilog.
4556 LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
4557  return lowerVerificationStatement(
4558  op, "assume__", op.getClock(), op.getPredicate(), op.getEnable(),
4559  op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4560  op.getIsConcurrent(), op.getEventControl());
4561 }
4562 
4563 // Lower a cover to SystemVerilog.
4564 LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
4565  return lowerVerificationStatement(
4566  op, "cover__", op.getClock(), op.getPredicate(), op.getEnable(),
4567  op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4568  op.getIsConcurrent(), op.getEventControl());
4569 }
4570 
4571 LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
4572  // Don't emit anything for a zero or one operand attach.
4573  if (op.getAttached().size() < 2)
4574  return success();
4575 
4576  SmallVector<Value, 4> inoutValues;
4577  for (auto v : op.getAttached()) {
4578  inoutValues.push_back(getPossiblyInoutLoweredValue(v));
4579  if (!inoutValues.back()) {
4580  // Ignore zero bit values.
4581  if (!isZeroBitFIRRTLType(v.getType()))
4582  return failure();
4583  inoutValues.pop_back();
4584  continue;
4585  }
4586 
4587  if (!inoutValues.back().getType().isa<hw::InOutType>())
4588  return op.emitError("operand isn't an inout type");
4589  }
4590 
4591  if (inoutValues.size() < 2)
4592  return success();
4593 
4594  // If the op has a single source value, the value is used as a lowering result
4595  // of other values. Therefore we can delete the attach op here.
4597  return success();
4598 
4599  // If all operands of the attach are internal to this module (none of them
4600  // are ports), then they can all be replaced with a single wire, and we can
4601  // delete the attach op.
4602  bool isAttachInternalOnly =
4603  llvm::none_of(inoutValues, [](auto v) { return isa<BlockArgument>(v); });
4604 
4605  if (isAttachInternalOnly) {
4606  auto v0 = inoutValues.front();
4607  for (auto v : inoutValues) {
4608  if (v == v0)
4609  continue;
4610  v.replaceAllUsesWith(v0);
4611  }
4612  return success();
4613  }
4614 
4615  // If the attach operands contain a port, then we can't do anything to
4616  // simplify the attach operation.
4617  addToIfDefBlock(
4618  "SYNTHESIS",
4619  // If we're doing synthesis, we emit an all-pairs assign complex.
4620  [&]() {
4621  SmallVector<Value, 4> values;
4622  for (size_t i = 0, e = inoutValues.size(); i != e; ++i)
4623  values.push_back(getReadValue(inoutValues[i]));
4624 
4625  for (size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
4626  for (size_t i2 = 0; i2 != e; ++i2)
4627  if (i1 != i2)
4628  builder.create<sv::AssignOp>(inoutValues[i1], values[i2]);
4629  }
4630  },
4631  // In the non-synthesis case, we emit a SystemVerilog alias
4632  // statement.
4633  [&]() {
4634  builder.create<sv::IfDefOp>(
4635  "verilator",
4636  [&]() {
4637  builder.create<sv::VerbatimOp>(
4638  "`error \"Verilator does not support alias and thus "
4639  "cannot "
4640  "arbitrarily connect bidirectional wires and ports\"");
4641  },
4642  [&]() { builder.create<sv::AliasOp>(inoutValues); });
4643  });
4644 
4645  return success();
4646 }
4647 
4648 LogicalResult FIRRTLLowering::fixupLTLOps() {
4649  if (ltlOpFixupWorklist.empty())
4650  return success();
4651  LLVM_DEBUG(llvm::dbgs() << "Fixing up " << ltlOpFixupWorklist.size()
4652  << " LTL ops\n");
4653 
4654  // Add wire users into the worklist.
4655  for (unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
4656  for (auto *user : ltlOpFixupWorklist[i]->getUsers())
4657  if (isa<hw::WireOp>(user))
4658  ltlOpFixupWorklist.insert(user);
4659 
4660  // Re-infer LTL op types and remove wires.
4661  while (!ltlOpFixupWorklist.empty()) {
4662  auto *op = ltlOpFixupWorklist.pop_back_val();
4663 
4664  // Update the operation's return type by re-running type inference.
4665  if (auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
4666  LLVM_DEBUG(llvm::dbgs() << "- Update " << *op << "\n");
4667  SmallVector<Type, 2> types;
4668  auto result = opIntf.inferReturnTypes(
4669  op->getContext(), op->getLoc(), op->getOperands(),
4670  op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
4671  types);
4672  if (failed(result))
4673  return failure();
4674  assert(types.size() == op->getNumResults());
4675 
4676  // Update the result types and add the dependent ops into the worklist if
4677  // the type changed.
4678  for (auto [result, type] : llvm::zip(op->getResults(), types)) {
4679  if (result.getType() == type)
4680  continue;
4681  LLVM_DEBUG(llvm::dbgs()
4682  << " - Result #" << result.getResultNumber() << " from "
4683  << result.getType() << " to " << type << "\n");
4684  result.setType(type);
4685  for (auto *user : result.getUsers())
4686  if (user != op)
4687  ltlOpFixupWorklist.insert(user);
4688  }
4689  }
4690 
4691  // Remove LTL-typed wires.
4692  if (auto wireOp = dyn_cast<hw::WireOp>(op)) {
4693  if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
4694  wireOp.replaceAllUsesWith(wireOp.getInput());
4695  LLVM_DEBUG(llvm::dbgs() << "- Remove " << wireOp << "\n");
4696  if (wireOp.use_empty())
4697  wireOp.erase();
4698  }
4699  continue;
4700  }
4701 
4702  // Ensure that the operation has no users outside of LTL operations.
4703  SmallPtrSet<Operation *, 4> usersReported;
4704  for (auto *user : op->getUsers()) {
4705  if (!usersReported.insert(user).second)
4706  continue;
4707  if (isa<ltl::LTLDialect, verif::VerifDialect>(user->getDialect()))
4708  continue;
4709  if (isa<hw::WireOp>(user))
4710  continue;
4711  auto d = op->emitError(
4712  "verification operation used in a non-verification context");
4713  d.attachNote(user->getLoc())
4714  << "leaking outside verification context here";
4715  return d;
4716  }
4717  }
4718 
4719  return success();
4720 }
assert(baseType &&"element must be base type")
int32_t width
Definition: FIRRTL.cpp:27
@ Input
Definition: HW.h:32
@ Output
Definition: HW.h:32
@ InOut
Definition: HW.h:32
static unsigned getBitWidthFromVectorSize(unsigned size)
Definition: LowerToHW.cpp:188
static void moveVerifAnno(ModuleOp top, AnnotationSet &annos, StringRef annoClass, StringRef attrBase)
Move a ExtractTestCode related annotation from annotations to an attribute.
Definition: LowerToHW.cpp:162
static Operation * buildImmediateVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build an immediate assert operation based on the original FIRRTL operation name.
Definition: LowerToHW.cpp:4319
static LogicalResult handleZeroBit(Value failedOperand, std::function< LogicalResult()> fn)
Zero bit operands end up looking like failures from getLoweredValue.
Definition: LowerToHW.cpp:1961
static Value castToFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast a value to a desired target type.
Definition: LowerToHW.cpp:127
static Operation * buildConcurrentVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build a concurrent assert operation based on the original FIRRTL operation name.
Definition: LowerToHW.cpp:4334
static ArrayAttr getHWParameters(FExtModuleOp module, bool ignoreValues)
Map the parameter specifier on the specified extmodule into the HWModule representation for parameter...
Definition: LowerToHW.cpp:957
static bool isZeroBitFIRRTLType(Type type)
Return true if the specified type is a sized FIRRTL type (Int or Analog) with zero bits.
Definition: LowerToHW.cpp:58
static Value tryEliminatingAttachesToAnalogValue(Value value, Operation *insertPoint)
Given a value of analog type, check to see the only use of it is an attach.
Definition: LowerToHW.cpp:1171
static const char moduleHierarchyFileAttrName[]
Attribute that indicates that the module hierarchy starting at the annotated module should be dumped ...
Definition: LowerToHW.cpp:54
static SmallVector< SubfieldOp > getAllFieldAccesses(Value structValue, StringRef field)
Definition: LowerToHW.cpp:1292
static void tryCopyName(Operation *dst, Operation *src)
Definition: LowerToHW.cpp:194
static LogicalResult verifyOpLegality(Operation *op)
This verifies that the target operation has been lowered to a legal operation.
Definition: LowerToHW.cpp:83
static Value castFromFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast from a FIRRTL type (potentially with a flip) to a standard type.
Definition: LowerToHW.cpp:141
static Value tryEliminatingConnectsToValue(Value flipValue, Operation *insertPoint, CircuitLoweringState &loweringState)
Given a value of flip type, check to see if all of the uses of it are connects.
Definition: LowerToHW.cpp:1208
static Value getSingleNonInstanceOperand(AttachOp op)
Definition: LowerToHW.cpp:65
static IntType getWidestIntType(Type t1, Type t2)
Given two FIRRTL integer types, return the widest one.
Definition: LowerToHW.cpp:120
llvm::SmallVector< StringAttr > outputs
Builder builder
static int64_t size(hw::ArrayType mType, capnp::schema::Field::Reader cField)
Returns the expected size of an array (capnp list) in 64-bit words.
Definition: Schema.cpp:193
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition: Namespace.h:29
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
This is the common base class between SIntType and UIntType.
Definition: FIRRTLTypes.h:291
This table tracks nlas and what modules participate in them.
Definition: NLATable.h:29
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
Definition: CombOps.cpp:48
Value createOrFoldSExt(Location loc, Value value, Type destTy, OpBuilder &builder)
Create a sign extension operation from a value of integer type to an equal or larger integer type.
Definition: CombOps.cpp:25
constexpr const char * extractCoverageAnnoClass
constexpr const char * elaborationArtefactsDirectoryAnnoClass
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
Definition: FIRRTLUtils.h:208
constexpr const char * extractGrandCentralClass
constexpr const char * blackBoxAnnoClass
constexpr const char * dontObfuscateModuleAnnoClass
constexpr const char * metadataDirectoryAttrName
constexpr const char * testBenchDirAnnoClass
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.
constexpr const char * dutAnnoClass
constexpr const char * forceNameAnnoClass
constexpr const char * noDedupAnnoClass
mlir::Type getPassiveType(mlir::Type anyBaseFIRRTLType)
constexpr const char * subCircuitsTargetDirectoryAnnoClass
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
constexpr const char * extractAssertAnnoClass
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * blackBoxTargetDirAnnoClass
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
Definition: FIRRTLOps.cpp:3395
constexpr const char * extractAssumeAnnoClass
constexpr const char * scalaClassAnnoClass
constexpr const char * testHarnessHierAnnoClass
constexpr const char * moduleHierAnnoClass
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
circt::hw::InOutType InOutType
Definition: SVTypes.h:25
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
std::unique_ptr< mlir::Pass > createLowerFIRRTLToHWPass(bool enableAnnotationWarning=false, bool emitChiselAssertsAsSVA=false, bool disableMemRandomization=false, bool disableRegRandomization=false)
This is the pass constructor.
Definition: LowerToHW.cpp:522
mlir::raw_indented_ostream & dbgs()
Definition: Utility.h:28
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:16