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