CIRCT  19.0.0git
InferResets.cpp
Go to the documentation of this file.
1 //===- InferResets.cpp - Infer resets and add async reset -------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines the InferResets pass.
10 //
11 //===----------------------------------------------------------------------===//
12 
19 #include "circt/Support/Debug.h"
20 #include "circt/Support/FieldRef.h"
23 #include "mlir/IR/Dominance.h"
24 #include "mlir/IR/ImplicitLocOpBuilder.h"
25 #include "mlir/IR/Threading.h"
26 #include "mlir/Pass/Pass.h"
27 #include "llvm/ADT/EquivalenceClasses.h"
28 #include "llvm/ADT/SetVector.h"
29 #include "llvm/ADT/TinyPtrVector.h"
30 #include "llvm/ADT/TypeSwitch.h"
31 #include "llvm/Support/Debug.h"
32 
33 #define DEBUG_TYPE "infer-resets"
34 
35 namespace circt {
36 namespace firrtl {
37 #define GEN_PASS_DEF_INFERRESETS
38 #include "circt/Dialect/FIRRTL/Passes.h.inc"
39 } // namespace firrtl
40 } // namespace circt
41 
42 using circt::igraph::InstanceOpInterface;
45 using llvm::BumpPtrAllocator;
46 using llvm::MapVector;
47 using llvm::SmallDenseSet;
48 using llvm::SmallSetVector;
49 using mlir::FailureOr;
50 using mlir::InferTypeOpInterface;
51 using mlir::WalkOrder;
52 
53 using namespace circt;
54 using namespace firrtl;
55 
56 //===----------------------------------------------------------------------===//
57 // Utilities
58 //===----------------------------------------------------------------------===//
59 
60 namespace {
61 /// A reset domain.
62 struct ResetDomain {
63  /// Whether this is the root of the reset domain.
64  bool isTop = false;
65  /// The reset signal for this domain. A null value indicates that this domain
66  /// explicitly has no reset.
67  Value reset;
68 
69  // Implementation details for this domain.
70  Value existingValue;
71  std::optional<unsigned> existingPort;
72  StringAttr newPortName;
73 
74  ResetDomain(Value reset) : reset(reset) {}
75 };
76 } // namespace
77 
78 inline bool operator==(const ResetDomain &a, const ResetDomain &b) {
79  return (a.isTop == b.isTop && a.reset == b.reset);
80 }
81 inline bool operator!=(const ResetDomain &a, const ResetDomain &b) {
82  return !(a == b);
83 }
84 
85 /// Return the name and parent module of a reset. The reset value must either be
86 /// a module port or a wire/node operation.
87 static std::pair<StringAttr, FModuleOp> getResetNameAndModule(Value reset) {
88  if (auto arg = dyn_cast<BlockArgument>(reset)) {
89  auto module = cast<FModuleOp>(arg.getParentRegion()->getParentOp());
90  return {module.getPortNameAttr(arg.getArgNumber()), module};
91  } else {
92  auto op = reset.getDefiningOp();
93  return {op->getAttrOfType<StringAttr>("name"),
94  op->getParentOfType<FModuleOp>()};
95  }
96 }
97 
98 /// Return the name of a reset. The reset value must either be a module port or
99 /// a wire/node operation.
100 static inline StringAttr getResetName(Value reset) {
101  return getResetNameAndModule(reset).first;
102 }
103 
104 /// Construct a zero value of the given type using the given builder.
105 static Value createZeroValue(ImplicitLocOpBuilder &builder, FIRRTLBaseType type,
107  // The zero value's type is a const version of `type`.
108  type = type.getConstType(true);
109  auto it = cache.find(type);
110  if (it != cache.end())
111  return it->second;
112  auto nullBit = [&]() {
113  return createZeroValue(
114  builder, UIntType::get(builder.getContext(), 1, /*isConst=*/true),
115  cache);
116  };
117  auto value =
119  .Case<ClockType>([&](auto type) {
120  return builder.create<AsClockPrimOp>(nullBit());
121  })
122  .Case<AsyncResetType>([&](auto type) {
123  return builder.create<AsAsyncResetPrimOp>(nullBit());
124  })
125  .Case<SIntType, UIntType>([&](auto type) {
126  return builder.create<ConstantOp>(
127  type, APInt::getZero(type.getWidth().value_or(1)));
128  })
129  .Case<BundleType>([&](auto type) {
130  auto wireOp = builder.create<WireOp>(type);
131  for (unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
132  auto fieldType = type.getElementTypePreservingConst(i);
133  auto zero = createZeroValue(builder, fieldType, cache);
134  auto acc =
135  builder.create<SubfieldOp>(fieldType, wireOp.getResult(), i);
136  emitConnect(builder, acc, zero);
137  }
138  return wireOp.getResult();
139  })
140  .Case<FVectorType>([&](auto type) {
141  auto wireOp = builder.create<WireOp>(type);
142  auto zero = createZeroValue(
143  builder, type.getElementTypePreservingConst(), cache);
144  for (unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
145  auto acc = builder.create<SubindexOp>(zero.getType(),
146  wireOp.getResult(), i);
147  emitConnect(builder, acc, zero);
148  }
149  return wireOp.getResult();
150  })
151  .Case<ResetType, AnalogType>(
152  [&](auto type) { return builder.create<InvalidValueOp>(type); })
153  .Default([](auto) {
154  llvm_unreachable("switch handles all types");
155  return Value{};
156  });
157  cache.insert({type, value});
158  return value;
159 }
160 
161 /// Construct a null value of the given type using the given builder.
162 static Value createZeroValue(ImplicitLocOpBuilder &builder,
163  FIRRTLBaseType type) {
165  return createZeroValue(builder, type, cache);
166 }
167 
168 /// Helper function that inserts reset multiplexer into all `ConnectOp`s
169 /// with the given target. Looks through `SubfieldOp`, `SubindexOp`,
170 /// and `SubaccessOp`, and inserts multiplexers into connects to
171 /// these subaccesses as well. Modifies the insertion location of the builder.
172 /// Returns true if the `resetValue` was used in any way, false otherwise.
173 static bool insertResetMux(ImplicitLocOpBuilder &builder, Value target,
174  Value reset, Value resetValue) {
175  // Indicates whether the `resetValue` was assigned to in some way. We use this
176  // to erase unused subfield/subindex/subaccess ops on the reset value if they
177  // end up unused.
178  bool resetValueUsed = false;
179 
180  for (auto &use : target.getUses()) {
181  Operation *useOp = use.getOwner();
182  builder.setInsertionPoint(useOp);
183  TypeSwitch<Operation *>(useOp)
184  // Insert a mux on the value connected to the target:
185  // connect(dst, src) -> connect(dst, mux(reset, resetValue, src))
186  .Case<ConnectOp, MatchingConnectOp>([&](auto op) {
187  if (op.getDest() != target)
188  return;
189  LLVM_DEBUG(llvm::dbgs() << " - Insert mux into " << op << "\n");
190  auto muxOp =
191  builder.create<MuxPrimOp>(reset, resetValue, op.getSrc());
192  op.getSrcMutable().assign(muxOp);
193  resetValueUsed = true;
194  })
195  // Look through subfields.
196  .Case<SubfieldOp>([&](auto op) {
197  auto resetSubValue =
198  builder.create<SubfieldOp>(resetValue, op.getFieldIndexAttr());
199  if (insertResetMux(builder, op, reset, resetSubValue))
200  resetValueUsed = true;
201  else
202  resetSubValue.erase();
203  })
204  // Look through subindices.
205  .Case<SubindexOp>([&](auto op) {
206  auto resetSubValue =
207  builder.create<SubindexOp>(resetValue, op.getIndexAttr());
208  if (insertResetMux(builder, op, reset, resetSubValue))
209  resetValueUsed = true;
210  else
211  resetSubValue.erase();
212  })
213  // Look through subaccesses.
214  .Case<SubaccessOp>([&](auto op) {
215  if (op.getInput() != target)
216  return;
217  auto resetSubValue =
218  builder.create<SubaccessOp>(resetValue, op.getIndex());
219  if (insertResetMux(builder, op, reset, resetSubValue))
220  resetValueUsed = true;
221  else
222  resetSubValue.erase();
223  });
224  }
225  return resetValueUsed;
226 }
227 
228 //===----------------------------------------------------------------------===//
229 // Reset Network
230 //===----------------------------------------------------------------------===//
231 
232 namespace {
233 
234 /// A reset signal.
235 ///
236 /// This essentially combines the exact `FieldRef` of the signal in question
237 /// with a type to be used for error reporting and inferring the reset kind.
238 struct ResetSignal {
239  ResetSignal(FieldRef field, FIRRTLBaseType type) : field(field), type(type) {}
240  bool operator<(const ResetSignal &other) const { return field < other.field; }
241  bool operator==(const ResetSignal &other) const {
242  return field == other.field;
243  }
244  bool operator!=(const ResetSignal &other) const { return !(*this == other); }
245 
246  FieldRef field;
247  FIRRTLBaseType type;
248 };
249 
250 /// A connection made to or from a reset network.
251 ///
252 /// These drives are tracked for each reset network, and are used for error
253 /// reporting to the user.
254 struct ResetDrive {
255  /// What's being driven.
256  ResetSignal dst;
257  /// What's driving.
258  ResetSignal src;
259  /// The location to use for diagnostics.
260  Location loc;
261 };
262 
263 /// A list of connections to a reset network.
264 using ResetDrives = SmallVector<ResetDrive, 1>;
265 
266 /// All signals connected together into a reset network.
267 using ResetNetwork = llvm::iterator_range<
268  llvm::EquivalenceClasses<ResetSignal>::member_iterator>;
269 
270 /// Whether a reset is sync or async.
271 enum class ResetKind { Async, Sync };
272 
273 } // namespace
274 
275 namespace llvm {
276 template <>
277 struct DenseMapInfo<ResetSignal> {
278  static inline ResetSignal getEmptyKey() {
279  return ResetSignal{DenseMapInfo<FieldRef>::getEmptyKey(), {}};
280  }
281  static inline ResetSignal getTombstoneKey() {
282  return ResetSignal{DenseMapInfo<FieldRef>::getTombstoneKey(), {}};
283  }
284  static unsigned getHashValue(const ResetSignal &x) {
285  return circt::hash_value(x.field);
286  }
287  static bool isEqual(const ResetSignal &lhs, const ResetSignal &rhs) {
288  return lhs == rhs;
289  }
290 };
291 } // namespace llvm
292 
293 template <typename T>
294 static T &operator<<(T &os, const ResetKind &kind) {
295  switch (kind) {
296  case ResetKind::Async:
297  return os << "async";
298  case ResetKind::Sync:
299  return os << "sync";
300  }
301  return os;
302 }
303 
304 //===----------------------------------------------------------------------===//
305 // Pass Infrastructure
306 //===----------------------------------------------------------------------===//
307 
308 namespace {
309 /// Infer concrete reset types and insert full async reset.
310 ///
311 /// This pass replaces `reset` types in the IR with a concrete `asyncreset` or
312 /// `uint<1>` depending on how the reset is used, and adds async resets to
313 /// registers in modules marked with the corresponding
314 /// `FullAsyncResetAnnotation`.
315 ///
316 /// On a high level, the first stage of the pass that deals with reset inference
317 /// operates as follows:
318 ///
319 /// 1. Build a global graph of the resets in the design by tracing reset signals
320 /// through instances. This uses the `ResetNetwork` utilities and boils down
321 /// to finding groups of values in the IR that are part of the same reset
322 /// network (i.e., somehow attached together through ports, wires, instances,
323 /// and connects). We use LLVM's `EquivalenceClasses` data structure to do
324 /// this efficiently.
325 ///
326 /// 2. Infer the type of each reset network found in step 1 by looking at the
327 /// type of values connected to the network. This results in the network
328 /// being declared a sync (`uint<1>`) or async (`asyncreset`) network. If the
329 /// reset is never driven by a concrete type, an error is emitted.
330 ///
331 /// 3. Walk the IR and update the type of wires and ports with the reset types
332 /// found in step 2. This will replace all `reset` types in the IR with
333 /// a concrete type.
334 ///
335 /// The second stage that deals with the addition of async resets operates as
336 /// follows:
337 ///
338 /// 4. Visit every module in the design and determine if it has an explicit
339 /// reset annotated. Ports of and wires in the module can have a
340 /// `FullAsyncResetAnnotation`, which marks that port or wire as the async
341 /// reset for the module. A module may also carry a
342 /// `IgnoreFullAsyncResetAnnotation`, which marks it as being explicitly not
343 /// in a reset domain. These annotations are sparse; it is very much possible
344 /// that just the top-level module in the design has a full async reset
345 /// annotation. A module can only ever carry one of these annotations, which
346 /// puts it into one of three categories from an async reset inference
347 /// perspective:
348 ///
349 /// a. unambiguously marks a port or wire as the module's async reset
350 /// b. explicitly marks it as not to have any async resets added
351 /// c. inherit reset
352 ///
353 /// 5. For every module in the design, determine the full async reset domain it
354 /// is in. Note that this very narrowly deals with the inference of a
355 /// "default" async reset, which basically goes through the IR and attaches
356 /// all non-reset registers to a default async reset signal. If a module
357 /// carries one of the annotations mentioned in (4), the annotated port or
358 /// wire is used as its reset domain. Otherwise, it inherits the reset domain
359 /// from parent modules. This conceptually involves looking at all the places
360 /// where a module is instantiated, and recursively determining the reset
361 /// domain at the instantiation site. A module can only ever be in one reset
362 /// domain. In case it is inferred to lie in multiple ones, e.g., if it is
363 /// instantiated in different reset domains, an error is emitted. If
364 /// successful, every module is associated with a reset signal, either one of
365 /// its local ports or wires, or a port or wire within one of its parent
366 /// modules.
367 ///
368 /// 6. For every module in the design, determine how async resets shall be
369 /// implemented. This step handles the following distinct cases:
370 ///
371 /// a. Skip a module because it is marked as having no reset domain.
372 /// b. Use a port or wire in the module itself as reset. This is possible
373 /// if the module is at the "top" of its reset domain, which means that
374 /// it itself carried a reset annotation, and the reset value is either
375 /// a port or wire of the module itself.
376 /// c. Route a parent module's reset through a module port and use that
377 /// port as the reset. This happens if the module is *not* at the "top"
378 /// of its reset domain, but rather refers to a value in a parent module
379 /// as its reset.
380 ///
381 /// As a result, a module's reset domain is annotated with the existing local
382 /// value to reuse (port or wire), the index of an existing port to reuse,
383 /// and the name of an additional port to insert into its port list.
384 ///
385 /// 7. For every module in the design, async resets are implemented. This
386 /// determines the local value to use as the reset signal and updates the
387 /// `reg` and `regreset` operations in the design. If the register already
388 /// has an async reset, it is left unchanged. If it has a sync reset, the
389 /// sync reset is moved into a `mux` operation on all `connect`s to the
390 /// register (which the Scala code base called the `RemoveResets` pass).
391 /// Finally the register is replaced with a `regreset` operation, with the
392 /// reset signal determined earlier, and a "zero" value constructed for the
393 /// register's type.
394 ///
395 /// Determining the local reset value is trivial if step 6 found a module to
396 /// be of case a or b. Case c is the non-trivial one, because it requires
397 /// modifying the port list of the module. This is done by first determining
398 /// the name of the reset signal in the parent module, which is either the
399 /// name of the port or wire declaration. We then look for an existing
400 /// `asyncreset` port in the port list and reuse that as reset. If no port
401 /// with that name was found, or the existing port is of the wrong type, a
402 /// new port is inserted into the port list.
403 ///
404 /// TODO: This logic is *very* brittle and error-prone. It may make sense to
405 /// just add an additional port for the inferred async reset in any case,
406 /// with an optimization to use an existing `asyncreset` port if all of the
407 /// module's instantiations have that port connected to the desired signal
408 /// already.
409 ///
410 struct InferResetsPass
411  : public circt::firrtl::impl::InferResetsBase<InferResetsPass> {
412  void runOnOperation() override;
413  void runOnOperationInner();
414 
415  // Copy creates a new empty pass (because ResetMap has no copy constructor).
416  using InferResetsBase::InferResetsBase;
417  InferResetsPass(const InferResetsPass &other) : InferResetsBase(other) {}
418 
419  //===--------------------------------------------------------------------===//
420  // Reset type inference
421 
422  void traceResets(CircuitOp circuit);
423  void traceResets(InstanceOp inst);
424  void traceResets(Value dst, Value src, Location loc);
425  void traceResets(Value value);
426  void traceResets(Type dstType, Value dst, unsigned dstID, Type srcType,
427  Value src, unsigned srcID, Location loc);
428 
429  LogicalResult inferAndUpdateResets();
430  FailureOr<ResetKind> inferReset(ResetNetwork net);
431  LogicalResult updateReset(ResetNetwork net, ResetKind kind);
432  bool updateReset(FieldRef field, FIRRTLBaseType resetType);
433 
434  //===--------------------------------------------------------------------===//
435  // Async reset implementation
436 
437  LogicalResult collectAnnos(CircuitOp circuit);
438  // Collect async reset annotations in the module and return a reset signal.
439  // Return `failure()` if there was an error in the annotation processing.
440  // Return `std::nullopt` if there was no async reset annotation.
441  // Return `nullptr` if there was `ignore` annotation.
442  // Return a non-null Value if the reset was actually provided.
443  FailureOr<std::optional<Value>> collectAnnos(FModuleOp module);
444 
445  LogicalResult buildDomains(CircuitOp circuit);
446  void buildDomains(FModuleOp module, const InstancePath &instPath,
447  Value parentReset, InstanceGraph &instGraph,
448  unsigned indent = 0);
449 
450  void determineImpl();
451  void determineImpl(FModuleOp module, ResetDomain &domain);
452 
453  LogicalResult implementAsyncReset();
454  LogicalResult implementAsyncReset(FModuleOp module, ResetDomain &domain);
455  void implementAsyncReset(Operation *op, FModuleOp module, Value actualReset);
456 
457  LogicalResult verifyNoAbstractReset();
458 
459  //===--------------------------------------------------------------------===//
460  // Utilities
461 
462  /// Get the reset network a signal belongs to.
463  ResetNetwork getResetNetwork(ResetSignal signal) {
464  return llvm::make_range(resetClasses.findLeader(signal),
465  resetClasses.member_end());
466  }
467 
468  /// Get the drives of a reset network.
469  ResetDrives &getResetDrives(ResetNetwork net) {
470  return resetDrives[*net.begin()];
471  }
472 
473  /// Guess the root node of a reset network, such that we have something for
474  /// the user to make sense of.
475  ResetSignal guessRoot(ResetNetwork net);
476  ResetSignal guessRoot(ResetSignal signal) {
477  return guessRoot(getResetNetwork(signal));
478  }
479 
480  //===--------------------------------------------------------------------===//
481  // Analysis data
482 
483  /// A map of all traced reset networks in the circuit.
484  llvm::EquivalenceClasses<ResetSignal> resetClasses;
485 
486  /// A map of all connects to and from a reset.
487  DenseMap<ResetSignal, ResetDrives> resetDrives;
488 
489  /// The annotated reset for a module. A null value indicates that the module
490  /// is explicitly annotated with `ignore`. Otherwise the port/wire/node
491  /// annotated as reset within the module is stored.
492  DenseMap<Operation *, Value> annotatedResets;
493 
494  /// The reset domain for a module. In case of conflicting domain membership,
495  /// the vector for a module contains multiple elements.
496  MapVector<FModuleOp, SmallVector<std::pair<ResetDomain, InstancePath>, 1>>
497  domains;
498 
499  /// Cache of modules symbols
500  InstanceGraph *instanceGraph;
501 
502  /// Cache of instance paths.
503  std::unique_ptr<InstancePathCache> instancePathCache;
504 };
505 } // namespace
506 
507 void InferResetsPass::runOnOperation() {
508  runOnOperationInner();
509  resetClasses = llvm::EquivalenceClasses<ResetSignal>();
510  resetDrives.clear();
511  annotatedResets.clear();
512  domains.clear();
513  instancePathCache.reset(nullptr);
514  markAnalysesPreserved<InstanceGraph>();
515 }
516 
517 void InferResetsPass::runOnOperationInner() {
518  instanceGraph = &getAnalysis<InstanceGraph>();
519  instancePathCache = std::make_unique<InstancePathCache>(*instanceGraph);
520 
521  // Trace the uninferred reset networks throughout the design.
522  traceResets(getOperation());
523 
524  // Infer the type of the traced resets and update the IR.
525  if (failed(inferAndUpdateResets()))
526  return signalPassFailure();
527 
528  // Gather the reset annotations throughout the modules.
529  if (failed(collectAnnos(getOperation())))
530  return signalPassFailure();
531 
532  // Build the reset domains in the design.
533  if (failed(buildDomains(getOperation())))
534  return signalPassFailure();
535 
536  // Determine how each reset shall be implemented.
537  determineImpl();
538 
539  // Implement the async resets.
540  if (failed(implementAsyncReset()))
541  return signalPassFailure();
542 
543  // Require that no Abstract Resets exist on ports in the design.
544  if (failed(verifyNoAbstractReset()))
545  return signalPassFailure();
546 }
547 
548 std::unique_ptr<mlir::Pass> circt::firrtl::createInferResetsPass() {
549  return std::make_unique<InferResetsPass>();
550 }
551 
552 ResetSignal InferResetsPass::guessRoot(ResetNetwork net) {
553  ResetDrives &drives = getResetDrives(net);
554  ResetSignal bestSignal = *net.begin();
555  unsigned bestNumDrives = -1;
556 
557  for (auto signal : net) {
558  // Don't consider `invalidvalue` for reporting as a root.
559  if (isa_and_nonnull<InvalidValueOp>(
560  signal.field.getValue().getDefiningOp()))
561  continue;
562 
563  // Count the number of times this particular signal in the reset network is
564  // assigned to.
565  unsigned numDrives = 0;
566  for (auto &drive : drives)
567  if (drive.dst == signal)
568  ++numDrives;
569 
570  // Keep track of the signal with the lowest number of assigns. These tend to
571  // be the signals further up the reset tree. This will usually resolve to
572  // the root of the reset tree far up in the design hierarchy.
573  if (numDrives < bestNumDrives) {
574  bestNumDrives = numDrives;
575  bestSignal = signal;
576  }
577  }
578  return bestSignal;
579 }
580 
581 //===----------------------------------------------------------------------===//
582 // Custom Field IDs
583 //===----------------------------------------------------------------------===//
584 
585 // The following functions implement custom field IDs specifically for the use
586 // in reset inference. They look much more like tracking fields on types than
587 // individual values. For example, vectors don't carry separate IDs for each of
588 // their elements. Instead they have one set of IDs for the entire vector, since
589 // the element type is uniform across all elements.
590 
591 static unsigned getMaxFieldID(FIRRTLBaseType type) {
593  .Case<BundleType>([](auto type) {
594  unsigned id = 0;
595  for (auto e : type.getElements())
596  id += getMaxFieldID(e.type) + 1;
597  return id;
598  })
599  .Case<FVectorType>(
600  [](auto type) { return getMaxFieldID(type.getElementType()) + 1; })
601  .Default([](auto) { return 0; });
602 }
603 
604 static unsigned getFieldID(BundleType type, unsigned index) {
605  assert(index < type.getNumElements());
606  unsigned id = 1;
607  for (unsigned i = 0; i < index; ++i)
608  id += getMaxFieldID(type.getElementType(i)) + 1;
609  return id;
610 }
611 
612 static unsigned getFieldID(FVectorType type) { return 1; }
613 
614 static unsigned getIndexForFieldID(BundleType type, unsigned fieldID) {
615  assert(type.getNumElements() && "Bundle must have >0 fields");
616  --fieldID;
617  for (const auto &e : llvm::enumerate(type.getElements())) {
618  auto numSubfields = getMaxFieldID(e.value().type) + 1;
619  if (fieldID < numSubfields)
620  return e.index();
621  fieldID -= numSubfields;
622  }
623  assert(false && "field id outside bundle");
624  return 0;
625 }
626 
627 // If a field is pointing to a child of a zero-length vector, it is useless.
628 static bool isUselessVec(FIRRTLBaseType oldType, unsigned fieldID) {
629  if (oldType.isGround()) {
630  assert(fieldID == 0);
631  return false;
632  }
633 
634  // If this is a bundle type, recurse.
635  if (auto bundleType = type_dyn_cast<BundleType>(oldType)) {
636  unsigned index = getIndexForFieldID(bundleType, fieldID);
637  return isUselessVec(bundleType.getElementType(index),
638  fieldID - getFieldID(bundleType, index));
639  }
640 
641  // If this is a vector type, check if it is zero length. Anything in a
642  // zero-length vector is useless.
643  if (auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
644  if (vectorType.getNumElements() == 0)
645  return true;
646  return isUselessVec(vectorType.getElementType(),
647  fieldID - getFieldID(vectorType));
648  }
649 
650  return false;
651 }
652 
653 // If a field is pointing to a child of a zero-length vector, it is useless.
654 static bool isUselessVec(FieldRef field) {
655  return isUselessVec(
656  getBaseType(type_cast<FIRRTLType>(field.getValue().getType())),
657  field.getFieldID());
658 }
659 
660 static bool getDeclName(Value value, SmallString<32> &string) {
661  if (auto arg = dyn_cast<BlockArgument>(value)) {
662  auto module = cast<FModuleOp>(arg.getOwner()->getParentOp());
663  string += module.getPortName(arg.getArgNumber());
664  return true;
665  }
666 
667  auto *op = value.getDefiningOp();
668  return TypeSwitch<Operation *, bool>(op)
669  .Case<InstanceOp, MemOp>([&](auto op) {
670  string += op.getName();
671  string += ".";
672  string +=
673  op.getPortName(cast<OpResult>(value).getResultNumber()).getValue();
674  return true;
675  })
676  .Case<WireOp, NodeOp, RegOp, RegResetOp>([&](auto op) {
677  string += op.getName();
678  return true;
679  })
680  .Default([](auto) { return false; });
681 }
682 
683 static bool getFieldName(const FieldRef &fieldRef, SmallString<32> &string) {
684  SmallString<64> name;
685  auto value = fieldRef.getValue();
686  if (!getDeclName(value, string))
687  return false;
688 
689  auto type = value.getType();
690  auto localID = fieldRef.getFieldID();
691  while (localID) {
692  if (auto bundleType = type_dyn_cast<BundleType>(type)) {
693  auto index = getIndexForFieldID(bundleType, localID);
694  // Add the current field string, and recurse into a subfield.
695  auto &element = bundleType.getElements()[index];
696  if (!string.empty())
697  string += ".";
698  string += element.name.getValue();
699  // Recurse in to the element type.
700  type = element.type;
701  localID = localID - getFieldID(bundleType, index);
702  } else if (auto vecType = type_dyn_cast<FVectorType>(type)) {
703  string += "[]";
704  // Recurse in to the element type.
705  type = vecType.getElementType();
706  localID = localID - getFieldID(vecType);
707  } else {
708  // If we reach here, the field ref is pointing inside some aggregate type
709  // that isn't a bundle or a vector. If the type is a ground type, then the
710  // localID should be 0 at this point, and we should have broken from the
711  // loop.
712  llvm_unreachable("unsupported type");
713  }
714  }
715  return true;
716 }
717 
718 //===----------------------------------------------------------------------===//
719 // Reset Tracing
720 //===----------------------------------------------------------------------===//
721 
722 /// Check whether a type contains a `ResetType`.
723 static bool typeContainsReset(Type type) {
724  return TypeSwitch<Type, bool>(type)
725  .Case<FIRRTLType>([](auto type) {
726  return type.getRecursiveTypeProperties().hasUninferredReset;
727  })
728  .Default([](auto) { return false; });
729 }
730 
731 /// Iterate over a circuit and follow all signals with `ResetType`, aggregating
732 /// them into reset nets. After this function returns, the `resetMap` is
733 /// populated with the reset networks in the circuit, alongside information on
734 /// drivers and their types that contribute to the reset.
735 void InferResetsPass::traceResets(CircuitOp circuit) {
736  LLVM_DEBUG({
737  llvm::dbgs() << "\n";
738  debugHeader("Tracing uninferred resets") << "\n\n";
739  });
740 
741  SmallVector<std::pair<FModuleOp, SmallVector<Operation *>>> moduleToOps;
742 
743  for (auto module : circuit.getOps<FModuleOp>())
744  moduleToOps.push_back({module, {}});
745 
746  hw::InnerRefNamespace irn{getAnalysis<SymbolTable>(),
747  getAnalysis<hw::InnerSymbolTableCollection>()};
748 
749  mlir::parallelForEach(circuit.getContext(), moduleToOps, [](auto &e) {
750  e.first.walk([&](Operation *op) {
751  // We are only interested in operations which are related to abstract
752  // reset.
753  if (llvm::any_of(
754  op->getResultTypes(),
755  [](mlir::Type type) { return typeContainsReset(type); }) ||
756  llvm::any_of(op->getOperandTypes(), typeContainsReset))
757  e.second.push_back(op);
758  });
759  });
760 
761  for (auto &[_, ops] : moduleToOps)
762  for (auto *op : ops) {
763  TypeSwitch<Operation *>(op)
764  .Case<FConnectLike>([&](auto op) {
765  traceResets(op.getDest(), op.getSrc(), op.getLoc());
766  })
767  .Case<InstanceOp>([&](auto op) { traceResets(op); })
768  .Case<RefSendOp>([&](auto op) {
769  // Trace using base types.
770  traceResets(op.getType().getType(), op.getResult(), 0,
771  op.getBase().getType().getPassiveType(), op.getBase(),
772  0, op.getLoc());
773  })
774  .Case<RefResolveOp>([&](auto op) {
775  // Trace using base types.
776  traceResets(op.getType(), op.getResult(), 0,
777  op.getRef().getType().getType(), op.getRef(), 0,
778  op.getLoc());
779  })
780  .Case<Forceable>([&](Forceable op) {
781  if (auto node = dyn_cast<NodeOp>(op.getOperation()))
782  traceResets(node.getResult(), node.getInput(), node.getLoc());
783  // Trace reset into rwprobe. Avoid invalid IR.
784  if (op.isForceable())
785  traceResets(op.getDataType(), op.getData(), 0, op.getDataType(),
786  op.getDataRef(), 0, op.getLoc());
787  })
788  .Case<RWProbeOp>([&](RWProbeOp op) {
789  auto ist = irn.lookup(op.getTarget());
790  assert(ist);
791  auto ref = getFieldRefForTarget(ist);
792  auto baseType = op.getType().getType();
793  traceResets(baseType, op.getResult(), 0, baseType.getPassiveType(),
794  ref.getValue(), ref.getFieldID(), op.getLoc());
795  })
796  .Case<UninferredResetCastOp, ConstCastOp, RefCastOp>([&](auto op) {
797  traceResets(op.getResult(), op.getInput(), op.getLoc());
798  })
799  .Case<InvalidValueOp>([&](auto op) {
800  // Uniquify `InvalidValueOp`s that are contributing to multiple
801  // reset networks. These are tricky to handle because passes
802  // like CSE will generally ensure that there is only a single
803  // `InvalidValueOp` per type. However, a `reset` invalid value
804  // may be connected to two reset networks that end up being
805  // inferred as `asyncreset` and `uint<1>`. In that case, we need
806  // a distinct `InvalidValueOp` for each reset network in order
807  // to assign it the correct type.
808  auto type = op.getType();
809  if (!typeContainsReset(type) || op->hasOneUse() || op->use_empty())
810  return;
811  LLVM_DEBUG(llvm::dbgs() << "Uniquify " << op << "\n");
812  ImplicitLocOpBuilder builder(op->getLoc(), op);
813  for (auto &use :
814  llvm::make_early_inc_range(llvm::drop_begin(op->getUses()))) {
815  // - `make_early_inc_range` since `getUses()` is invalidated
816  // upon
817  // `use.set(...)`.
818  // - `drop_begin` such that the first use can keep the
819  // original op.
820  auto newOp = builder.create<InvalidValueOp>(type);
821  use.set(newOp);
822  }
823  })
824 
825  .Case<SubfieldOp>([&](auto op) {
826  // Associate the input bundle's resets with the output field's
827  // resets.
828  BundleType bundleType = op.getInput().getType();
829  auto index = op.getFieldIndex();
830  traceResets(op.getType(), op.getResult(), 0,
831  bundleType.getElements()[index].type, op.getInput(),
832  getFieldID(bundleType, index), op.getLoc());
833  })
834 
835  .Case<SubindexOp, SubaccessOp>([&](auto op) {
836  // Associate the input vector's resets with the output field's
837  // resets.
838  //
839  // This collapses all elements in vectors into one shared
840  // element which will ensure that reset inference provides a
841  // uniform result for all elements.
842  //
843  // CAVEAT: This may infer reset networks that are too big, since
844  // unrelated resets in the same vector end up looking as if they
845  // were connected. However for the sake of type inference, this
846  // is indistinguishable from them having to share the same type
847  // (namely the vector element type).
848  FVectorType vectorType = op.getInput().getType();
849  traceResets(op.getType(), op.getResult(), 0,
850  vectorType.getElementType(), op.getInput(),
851  getFieldID(vectorType), op.getLoc());
852  })
853 
854  .Case<RefSubOp>([&](RefSubOp op) {
855  // Trace through ref.sub.
856  auto aggType = op.getInput().getType().getType();
857  uint64_t fieldID = TypeSwitch<FIRRTLBaseType, uint64_t>(aggType)
858  .Case<FVectorType>([](auto type) {
859  return getFieldID(type);
860  })
861  .Case<BundleType>([&](auto type) {
862  return getFieldID(type, op.getIndex());
863  });
864  traceResets(op.getType(), op.getResult(), 0,
865  op.getResult().getType(), op.getInput(), fieldID,
866  op.getLoc());
867  });
868  }
869 }
870 
871 /// Trace reset signals through an instance. This essentially associates the
872 /// instance's port values with the target module's port values.
873 void InferResetsPass::traceResets(InstanceOp inst) {
874  // Lookup the referenced module. Nothing to do if its an extmodule.
875  auto module = inst.getReferencedModule<FModuleOp>(*instanceGraph);
876  if (!module)
877  return;
878  LLVM_DEBUG(llvm::dbgs() << "Visiting instance " << inst.getName() << "\n");
879 
880  // Establish a connection between the instance ports and module ports.
881  for (const auto &it : llvm::enumerate(inst.getResults())) {
882  auto dir = module.getPortDirection(it.index());
883  Value dstPort = module.getArgument(it.index());
884  Value srcPort = it.value();
885  if (dir == Direction::Out)
886  std::swap(dstPort, srcPort);
887  traceResets(dstPort, srcPort, it.value().getLoc());
888  }
889 }
890 
891 /// Analyze a connect of one (possibly aggregate) value to another.
892 /// Each drive involving a `ResetType` is recorded.
893 void InferResetsPass::traceResets(Value dst, Value src, Location loc) {
894  // Analyze the actual connection.
895  traceResets(dst.getType(), dst, 0, src.getType(), src, 0, loc);
896 }
897 
898 /// Analyze a connect of one (possibly aggregate) value to another.
899 /// Each drive involving a `ResetType` is recorded.
900 void InferResetsPass::traceResets(Type dstType, Value dst, unsigned dstID,
901  Type srcType, Value src, unsigned srcID,
902  Location loc) {
903  if (auto dstBundle = type_dyn_cast<BundleType>(dstType)) {
904  auto srcBundle = type_cast<BundleType>(srcType);
905  for (unsigned dstIdx = 0, e = dstBundle.getNumElements(); dstIdx < e;
906  ++dstIdx) {
907  auto dstField = dstBundle.getElements()[dstIdx].name;
908  auto srcIdx = srcBundle.getElementIndex(dstField);
909  if (!srcIdx)
910  continue;
911  auto &dstElt = dstBundle.getElements()[dstIdx];
912  auto &srcElt = srcBundle.getElements()[*srcIdx];
913  if (dstElt.isFlip) {
914  traceResets(srcElt.type, src, srcID + getFieldID(srcBundle, *srcIdx),
915  dstElt.type, dst, dstID + getFieldID(dstBundle, dstIdx),
916  loc);
917  } else {
918  traceResets(dstElt.type, dst, dstID + getFieldID(dstBundle, dstIdx),
919  srcElt.type, src, srcID + getFieldID(srcBundle, *srcIdx),
920  loc);
921  }
922  }
923  return;
924  }
925 
926  if (auto dstVector = type_dyn_cast<FVectorType>(dstType)) {
927  auto srcVector = type_cast<FVectorType>(srcType);
928  auto srcElType = srcVector.getElementType();
929  auto dstElType = dstVector.getElementType();
930  // Collapse all elements into one shared element. See comment in traceResets
931  // above for some context. Note that we are directly passing on the field ID
932  // of the vector itself as a stand-in for its element type. This is not
933  // really what `FieldRef` is designed to do, but tends to work since all the
934  // places that need to reason about the resulting weird IDs are inside this
935  // file. Normally you would pick a specific index from the vector, which
936  // would also move the field ID forward by some amount. However, we can't
937  // distinguish individual elements for the sake of type inference *and* we
938  // have to support zero-length vectors for which the only available ID is
939  // the vector itself. Therefore we always just pick the vector itself for
940  // the field ID and make sure in `updateType` that we handle vectors
941  // accordingly.
942  traceResets(dstElType, dst, dstID + getFieldID(dstVector), srcElType, src,
943  srcID + getFieldID(srcVector), loc);
944  return;
945  }
946 
947  // Handle connecting ref's. Other uses trace using base type.
948  if (auto dstRef = type_dyn_cast<RefType>(dstType)) {
949  auto srcRef = type_cast<RefType>(srcType);
950  return traceResets(dstRef.getType(), dst, dstID, srcRef.getType(), src,
951  srcID, loc);
952  }
953 
954  // Handle reset connections.
955  auto dstBase = type_dyn_cast<FIRRTLBaseType>(dstType);
956  auto srcBase = type_dyn_cast<FIRRTLBaseType>(srcType);
957  if (!dstBase || !srcBase)
958  return;
959  if (!type_isa<ResetType>(dstBase) && !type_isa<ResetType>(srcBase))
960  return;
961 
962  FieldRef dstField(dst, dstID);
963  FieldRef srcField(src, srcID);
964  LLVM_DEBUG(llvm::dbgs() << "Visiting driver '" << dstField << "' = '"
965  << srcField << "' (" << dstType << " = " << srcType
966  << ")\n");
967 
968  // Determine the leaders for the dst and src reset networks before we make
969  // the connection. This will allow us to later detect if dst got merged
970  // into src, or src into dst.
971  ResetSignal dstLeader =
972  *resetClasses.findLeader(resetClasses.insert({dstField, dstBase}));
973  ResetSignal srcLeader =
974  *resetClasses.findLeader(resetClasses.insert({srcField, srcBase}));
975 
976  // Unify the two reset networks.
977  ResetSignal unionLeader = *resetClasses.unionSets(dstLeader, srcLeader);
978  assert(unionLeader == dstLeader || unionLeader == srcLeader);
979 
980  // If dst got merged into src, append dst's drives to src's, or vice
981  // versa. Also, remove dst's or src's entry in resetDrives, because they
982  // will never come up as a leader again.
983  if (dstLeader != srcLeader) {
984  auto &unionDrives = resetDrives[unionLeader]; // needed before finds
985  auto mergedDrivesIt =
986  resetDrives.find(unionLeader == dstLeader ? srcLeader : dstLeader);
987  if (mergedDrivesIt != resetDrives.end()) {
988  unionDrives.append(mergedDrivesIt->second);
989  resetDrives.erase(mergedDrivesIt);
990  }
991  }
992 
993  // Keep note of this drive so we can point the user at the right location
994  // in case something goes wrong.
995  resetDrives[unionLeader].push_back(
996  {{dstField, dstBase}, {srcField, srcBase}, loc});
997 }
998 
999 //===----------------------------------------------------------------------===//
1000 // Reset Inference
1001 //===----------------------------------------------------------------------===//
1002 
1003 LogicalResult InferResetsPass::inferAndUpdateResets() {
1004  LLVM_DEBUG({
1005  llvm::dbgs() << "\n";
1006  debugHeader("Infer reset types") << "\n\n";
1007  });
1008  for (auto it = resetClasses.begin(), end = resetClasses.end(); it != end;
1009  ++it) {
1010  if (!it->isLeader())
1011  continue;
1012  ResetNetwork net = llvm::make_range(resetClasses.member_begin(it),
1013  resetClasses.member_end());
1014 
1015  // Infer whether this should be a sync or async reset.
1016  auto kind = inferReset(net);
1017  if (failed(kind))
1018  return failure();
1019 
1020  // Update the types in the IR to match the inferred kind.
1021  if (failed(updateReset(net, *kind)))
1022  return failure();
1023  }
1024  return success();
1025 }
1026 
1027 FailureOr<ResetKind> InferResetsPass::inferReset(ResetNetwork net) {
1028  LLVM_DEBUG(llvm::dbgs() << "Inferring reset network with "
1029  << std::distance(net.begin(), net.end())
1030  << " nodes\n");
1031 
1032  // Go through the nodes and track the involved types.
1033  unsigned asyncDrives = 0;
1034  unsigned syncDrives = 0;
1035  unsigned invalidDrives = 0;
1036  for (ResetSignal signal : net) {
1037  // Keep track of whether this signal contributes a vote for async or sync.
1038  if (type_isa<AsyncResetType>(signal.type))
1039  ++asyncDrives;
1040  else if (type_isa<UIntType>(signal.type))
1041  ++syncDrives;
1042  else if (isUselessVec(signal.field) ||
1043  isa_and_nonnull<InvalidValueOp>(
1044  signal.field.getValue().getDefiningOp()))
1045  ++invalidDrives;
1046  }
1047  LLVM_DEBUG(llvm::dbgs() << "- Found " << asyncDrives << " async, "
1048  << syncDrives << " sync, " << invalidDrives
1049  << " invalid drives\n");
1050 
1051  // Handle the case where we have no votes for either kind.
1052  if (asyncDrives == 0 && syncDrives == 0 && invalidDrives == 0) {
1053  ResetSignal root = guessRoot(net);
1054  auto diag = mlir::emitError(root.field.getValue().getLoc())
1055  << "reset network never driven with concrete type";
1056  for (ResetSignal signal : net)
1057  diag.attachNote(signal.field.getLoc()) << "here: ";
1058  return failure();
1059  }
1060 
1061  // Handle the case where we have votes for both kinds.
1062  if (asyncDrives > 0 && syncDrives > 0) {
1063  ResetSignal root = guessRoot(net);
1064  bool majorityAsync = asyncDrives >= syncDrives;
1065  auto diag = mlir::emitError(root.field.getValue().getLoc())
1066  << "reset network";
1067  SmallString<32> fieldName;
1068  if (getFieldName(root.field, fieldName))
1069  diag << " \"" << fieldName << "\"";
1070  diag << " simultaneously connected to async and sync resets";
1071  diag.attachNote(root.field.getValue().getLoc())
1072  << "majority of connections to this reset are "
1073  << (majorityAsync ? "async" : "sync");
1074  for (auto &drive : getResetDrives(net)) {
1075  if ((type_isa<AsyncResetType>(drive.dst.type) && !majorityAsync) ||
1076  (type_isa<AsyncResetType>(drive.src.type) && !majorityAsync) ||
1077  (type_isa<UIntType>(drive.dst.type) && majorityAsync) ||
1078  (type_isa<UIntType>(drive.src.type) && majorityAsync))
1079  diag.attachNote(drive.loc)
1080  << (type_isa<AsyncResetType>(drive.src.type) ? "async" : "sync")
1081  << " drive here:";
1082  }
1083  return failure();
1084  }
1085 
1086  // At this point we know that the type of the reset is unambiguous. If there
1087  // are any votes for async, we make the reset async. Otherwise we make it
1088  // sync.
1089  auto kind = (asyncDrives ? ResetKind::Async : ResetKind::Sync);
1090  LLVM_DEBUG(llvm::dbgs() << "- Inferred as " << kind << "\n");
1091  return kind;
1092 }
1093 
1094 //===----------------------------------------------------------------------===//
1095 // Reset Updating
1096 //===----------------------------------------------------------------------===//
1097 
1098 LogicalResult InferResetsPass::updateReset(ResetNetwork net, ResetKind kind) {
1099  LLVM_DEBUG(llvm::dbgs() << "Updating reset network with "
1100  << std::distance(net.begin(), net.end())
1101  << " nodes to " << kind << "\n");
1102 
1103  // Determine the final type the reset should have.
1104  FIRRTLBaseType resetType;
1105  if (kind == ResetKind::Async)
1106  resetType = AsyncResetType::get(&getContext());
1107  else
1108  resetType = UIntType::get(&getContext(), 1);
1109 
1110  // Update all those values in the network that cannot be inferred from
1111  // operands. If we change the type of a module port (i.e. BlockArgument), add
1112  // the module to a module worklist since we need to update its function type.
1113  SmallSetVector<Operation *, 16> worklist;
1114  SmallDenseSet<Operation *> moduleWorklist;
1115  SmallDenseSet<std::pair<Operation *, Operation *>> extmoduleWorklist;
1116  for (auto signal : net) {
1117  Value value = signal.field.getValue();
1118  if (!isa<BlockArgument>(value) &&
1119  !isa_and_nonnull<WireOp, RegOp, RegResetOp, InstanceOp, InvalidValueOp,
1120  ConstCastOp, RefCastOp, UninferredResetCastOp,
1121  RWProbeOp>(value.getDefiningOp()))
1122  continue;
1123  if (updateReset(signal.field, resetType)) {
1124  for (auto user : value.getUsers())
1125  worklist.insert(user);
1126  if (auto blockArg = dyn_cast<BlockArgument>(value))
1127  moduleWorklist.insert(blockArg.getOwner()->getParentOp());
1128  else if (auto instOp = value.getDefiningOp<InstanceOp>()) {
1129  if (auto extmodule =
1130  instOp.getReferencedModule<FExtModuleOp>(*instanceGraph))
1131  extmoduleWorklist.insert({extmodule, instOp});
1132  } else if (auto uncast = value.getDefiningOp<UninferredResetCastOp>()) {
1133  uncast.replaceAllUsesWith(uncast.getInput());
1134  uncast.erase();
1135  }
1136  }
1137  }
1138 
1139  // Process the worklist of operations that have their type changed, pushing
1140  // types down the SSA dataflow graph. This is important because we change the
1141  // reset types in aggregates, and then need all the subindex, subfield, and
1142  // subaccess operations to be updated as appropriate.
1143  while (!worklist.empty()) {
1144  auto *wop = worklist.pop_back_val();
1145  SmallVector<Type, 2> types;
1146  if (auto op = dyn_cast<InferTypeOpInterface>(wop)) {
1147  // Determine the new result types.
1148  SmallVector<Type, 2> types;
1149  if (failed(op.inferReturnTypes(op->getContext(), op->getLoc(),
1150  op->getOperands(), op->getAttrDictionary(),
1151  op->getPropertiesStorage(),
1152  op->getRegions(), types)))
1153  return failure();
1154 
1155  // Update the results and add the changed ones to the
1156  // worklist.
1157  for (auto it : llvm::zip(op->getResults(), types)) {
1158  auto newType = std::get<1>(it);
1159  if (std::get<0>(it).getType() == newType)
1160  continue;
1161  std::get<0>(it).setType(newType);
1162  for (auto *user : std::get<0>(it).getUsers())
1163  worklist.insert(user);
1164  }
1165  LLVM_DEBUG(llvm::dbgs() << "- Inferred " << *op << "\n");
1166  } else if (auto uop = dyn_cast<UninferredResetCastOp>(wop)) {
1167  for (auto *user : uop.getResult().getUsers())
1168  worklist.insert(user);
1169  uop.replaceAllUsesWith(uop.getInput());
1170  LLVM_DEBUG(llvm::dbgs() << "- Inferred " << uop << "\n");
1171  uop.erase();
1172  }
1173  }
1174 
1175  // Update module types based on the type of the block arguments.
1176  for (auto *op : moduleWorklist) {
1177  auto module = dyn_cast<FModuleOp>(op);
1178  if (!module)
1179  continue;
1180 
1181  SmallVector<Attribute> argTypes;
1182  argTypes.reserve(module.getNumPorts());
1183  for (auto arg : module.getArguments())
1184  argTypes.push_back(TypeAttr::get(arg.getType()));
1185 
1186  module->setAttr(FModuleLike::getPortTypesAttrName(),
1187  ArrayAttr::get(op->getContext(), argTypes));
1188  LLVM_DEBUG(llvm::dbgs()
1189  << "- Updated type of module '" << module.getName() << "'\n");
1190  }
1191 
1192  // Update extmodule types based on their instantiation.
1193  for (auto pair : extmoduleWorklist) {
1194  auto module = cast<FExtModuleOp>(pair.first);
1195  auto instOp = cast<InstanceOp>(pair.second);
1196 
1197  SmallVector<Attribute> types;
1198  for (auto type : instOp.getResultTypes())
1199  types.push_back(TypeAttr::get(type));
1200 
1201  module->setAttr(FModuleLike::getPortTypesAttrName(),
1202  ArrayAttr::get(module->getContext(), types));
1203  LLVM_DEBUG(llvm::dbgs()
1204  << "- Updated type of extmodule '" << module.getName() << "'\n");
1205  }
1206 
1207  return success();
1208 }
1209 
1210 /// Update the type of a single field within a type.
1211 static FIRRTLBaseType updateType(FIRRTLBaseType oldType, unsigned fieldID,
1212  FIRRTLBaseType fieldType) {
1213  // If this is a ground type, simply replace it, preserving constness.
1214  if (oldType.isGround()) {
1215  assert(fieldID == 0);
1216  return fieldType.getConstType(oldType.isConst());
1217  }
1218 
1219  // If this is a bundle type, update the corresponding field.
1220  if (auto bundleType = type_dyn_cast<BundleType>(oldType)) {
1221  unsigned index = getIndexForFieldID(bundleType, fieldID);
1222  SmallVector<BundleType::BundleElement> fields(bundleType.begin(),
1223  bundleType.end());
1224  fields[index].type = updateType(
1225  fields[index].type, fieldID - getFieldID(bundleType, index), fieldType);
1226  return BundleType::get(oldType.getContext(), fields, bundleType.isConst());
1227  }
1228 
1229  // If this is a vector type, update the element type.
1230  if (auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
1231  auto newType = updateType(vectorType.getElementType(),
1232  fieldID - getFieldID(vectorType), fieldType);
1233  return FVectorType::get(newType, vectorType.getNumElements(),
1234  vectorType.isConst());
1235  }
1236 
1237  llvm_unreachable("unknown aggregate type");
1238  return oldType;
1239 }
1240 
1241 /// Update the reset type of a specific field.
1242 bool InferResetsPass::updateReset(FieldRef field, FIRRTLBaseType resetType) {
1243  // Compute the updated type.
1244  auto oldType = type_cast<FIRRTLType>(field.getValue().getType());
1245  FIRRTLType newType = mapBaseType(oldType, [&](auto base) {
1246  return updateType(base, field.getFieldID(), resetType);
1247  });
1248 
1249  // Update the type if necessary.
1250  if (oldType == newType)
1251  return false;
1252  LLVM_DEBUG(llvm::dbgs() << "- Updating '" << field << "' from " << oldType
1253  << " to " << newType << "\n");
1254  field.getValue().setType(newType);
1255  return true;
1256 }
1257 
1258 //===----------------------------------------------------------------------===//
1259 // Reset Annotations
1260 //===----------------------------------------------------------------------===//
1261 
1262 LogicalResult InferResetsPass::collectAnnos(CircuitOp circuit) {
1263  LLVM_DEBUG({
1264  llvm::dbgs() << "\n";
1265  debugHeader("Gather async reset annotations") << "\n\n";
1266  });
1267  SmallVector<std::pair<FModuleOp, std::optional<Value>>> results;
1268  for (auto module : circuit.getOps<FModuleOp>())
1269  results.push_back({module, {}});
1270  // Collect annotations parallelly.
1271  if (failed(mlir::failableParallelForEach(
1272  circuit.getContext(), results, [&](auto &moduleAndResult) {
1273  auto result = collectAnnos(moduleAndResult.first);
1274  if (failed(result))
1275  return failure();
1276  moduleAndResult.second = *result;
1277  return success();
1278  })))
1279  return failure();
1280 
1281  for (auto [module, reset] : results)
1282  if (reset.has_value())
1283  annotatedResets.insert({module, *reset});
1284  return success();
1285 }
1286 
1287 FailureOr<std::optional<Value>>
1288 InferResetsPass::collectAnnos(FModuleOp module) {
1289  bool anyFailed = false;
1290  SmallSetVector<std::pair<Annotation, Location>, 4> conflictingAnnos;
1291 
1292  // Consume a possible "ignore" annotation on the module itself, which
1293  // explicitly assigns it no reset domain.
1294  bool ignore = false;
1295  AnnotationSet::removeAnnotations(module, [&](Annotation anno) {
1297  ignore = true;
1298  conflictingAnnos.insert({anno, module.getLoc()});
1299  return true;
1300  }
1301  if (anno.isClass(fullAsyncResetAnnoClass)) {
1302  anyFailed = true;
1303  module.emitError("'FullAsyncResetAnnotation' cannot target module; "
1304  "must target port or wire/node instead");
1305  return true;
1306  }
1307  return false;
1308  });
1309  if (anyFailed)
1310  return failure();
1311 
1312  // Consume any reset annotations on module ports.
1313  Value reset;
1314  AnnotationSet::removePortAnnotations(module, [&](unsigned argNum,
1315  Annotation anno) {
1316  Value arg = module.getArgument(argNum);
1317  if (anno.isClass(fullAsyncResetAnnoClass)) {
1318  if (!isa<AsyncResetType>(arg.getType())) {
1319  mlir::emitError(arg.getLoc(), "'FullAsyncResetAnnotation' must "
1320  "target async reset, but targets ")
1321  << arg.getType();
1322  anyFailed = true;
1323  return true;
1324  }
1325  reset = arg;
1326  conflictingAnnos.insert({anno, reset.getLoc()});
1327 
1328  return false;
1329  }
1331  anyFailed = true;
1332  mlir::emitError(arg.getLoc(),
1333  "'IgnoreFullAsyncResetAnnotation' cannot target port; "
1334  "must target module instead");
1335  return true;
1336  }
1337  return false;
1338  });
1339  if (anyFailed)
1340  return failure();
1341 
1342  // Consume any reset annotations on wires in the module body.
1343  module.getBody().walk([&](Operation *op) {
1344  // Reset annotations must target wire/node ops.
1345  if (!isa<WireOp, NodeOp>(op)) {
1346  if (AnnotationSet::hasAnnotation(op, fullAsyncResetAnnoClass,
1348  anyFailed = true;
1349  op->emitError(
1350  "reset annotations must target module, port, or wire/node");
1351  }
1352  return;
1353  }
1354 
1355  // At this point we know that we have a WireOp/NodeOp. Process the reset
1356  // annotations.
1357  AnnotationSet::removeAnnotations(op, [&](Annotation anno) {
1358  if (anno.isClass(fullAsyncResetAnnoClass)) {
1359  auto resultType = op->getResult(0).getType();
1360  if (!isa<AsyncResetType>(resultType)) {
1361  mlir::emitError(op->getLoc(), "'FullAsyncResetAnnotation' must "
1362  "target async reset, but targets ")
1363  << resultType;
1364  anyFailed = true;
1365  return true;
1366  }
1367  reset = op->getResult(0);
1368  conflictingAnnos.insert({anno, reset.getLoc()});
1369  return false;
1370  }
1372  anyFailed = true;
1373  op->emitError(
1374  "'IgnoreFullAsyncResetAnnotation' cannot target wire/node; must "
1375  "target module instead");
1376  return true;
1377  }
1378  return false;
1379  });
1380  });
1381  if (anyFailed)
1382  return failure();
1383 
1384  // If we have found no annotations, there is nothing to do. We just leave
1385  // this module unannotated, which will cause it to inherit a reset domain
1386  // from its instantiation sites.
1387  if (!ignore && !reset) {
1388  LLVM_DEBUG(llvm::dbgs()
1389  << "No reset annotation for " << module.getName() << "\n");
1390  return std::optional<Value>();
1391  }
1392 
1393  // If we have found multiple annotations, emit an error and abort.
1394  if (conflictingAnnos.size() > 1) {
1395  auto diag = module.emitError("multiple reset annotations on module '")
1396  << module.getName() << "'";
1397  for (auto &annoAndLoc : conflictingAnnos)
1398  diag.attachNote(annoAndLoc.second)
1399  << "conflicting " << annoAndLoc.first.getClassAttr() << ":";
1400  return failure();
1401  }
1402 
1403  // Dump some information in debug builds.
1404  LLVM_DEBUG({
1405  llvm::dbgs() << "Annotated reset for " << module.getName() << ": ";
1406  if (ignore)
1407  llvm::dbgs() << "no domain\n";
1408  else if (auto arg = dyn_cast<BlockArgument>(reset))
1409  llvm::dbgs() << "port " << module.getPortName(arg.getArgNumber()) << "\n";
1410  else
1411  llvm::dbgs() << "wire "
1412  << reset.getDefiningOp()->getAttrOfType<StringAttr>("name")
1413  << "\n";
1414  });
1415 
1416  // Store the annotated reset for this module.
1417  assert(ignore || reset);
1418  return std::optional<Value>(reset);
1419 }
1420 
1421 //===----------------------------------------------------------------------===//
1422 // Domain Construction
1423 //===----------------------------------------------------------------------===//
1424 
1425 /// Gather the reset domains present in a circuit. This traverses the instance
1426 /// hierarchy of the design, making instances either live in a new reset
1427 /// domain if so annotated, or inherit their parent's domain. This can go
1428 /// wrong in some cases, mainly when a module is instantiated multiple times
1429 /// within different reset domains.
1430 LogicalResult InferResetsPass::buildDomains(CircuitOp circuit) {
1431  LLVM_DEBUG({
1432  llvm::dbgs() << "\n";
1433  debugHeader("Build async reset domains") << "\n\n";
1434  });
1435 
1436  // Gather the domains.
1437  auto &instGraph = getAnalysis<InstanceGraph>();
1438  auto module = dyn_cast<FModuleOp>(*instGraph.getTopLevelNode()->getModule());
1439  if (!module) {
1440  LLVM_DEBUG(llvm::dbgs()
1441  << "Skipping circuit because main module is no `firrtl.module`");
1442  return success();
1443  }
1444  buildDomains(module, InstancePath{}, Value{}, instGraph);
1445 
1446  // Report any domain conflicts among the modules.
1447  bool anyFailed = false;
1448  for (auto &it : domains) {
1449  auto module = cast<FModuleOp>(it.first);
1450  auto &domainConflicts = it.second;
1451  if (domainConflicts.size() <= 1)
1452  continue;
1453 
1454  anyFailed = true;
1455  SmallDenseSet<Value> printedDomainResets;
1456  auto diag = module.emitError("module '")
1457  << module.getName()
1458  << "' instantiated in different reset domains";
1459  for (auto &it : domainConflicts) {
1460  ResetDomain &domain = it.first;
1461  const auto &path = it.second;
1462  auto inst = path.leaf();
1463  auto loc = path.empty() ? module.getLoc() : inst.getLoc();
1464  auto &note = diag.attachNote(loc);
1465 
1466  // Describe the instance itself.
1467  if (path.empty())
1468  note << "root instance";
1469  else {
1470  note << "instance '";
1471  llvm::interleave(
1472  path,
1473  [&](InstanceOpInterface inst) { note << inst.getInstanceName(); },
1474  [&]() { note << "/"; });
1475  note << "'";
1476  }
1477 
1478  // Describe the reset domain the instance is in.
1479  note << " is in";
1480  if (domain.reset) {
1481  auto nameAndModule = getResetNameAndModule(domain.reset);
1482  note << " reset domain rooted at '" << nameAndModule.first.getValue()
1483  << "' of module '" << nameAndModule.second.getName() << "'";
1484 
1485  // Show where the domain reset is declared (once per reset).
1486  if (printedDomainResets.insert(domain.reset).second) {
1487  diag.attachNote(domain.reset.getLoc())
1488  << "reset domain '" << nameAndModule.first.getValue()
1489  << "' of module '" << nameAndModule.second.getName()
1490  << "' declared here:";
1491  }
1492  } else
1493  note << " no reset domain";
1494  }
1495  }
1496  return failure(anyFailed);
1497 }
1498 
1499 void InferResetsPass::buildDomains(FModuleOp module,
1500  const InstancePath &instPath,
1501  Value parentReset, InstanceGraph &instGraph,
1502  unsigned indent) {
1503  LLVM_DEBUG({
1504  llvm::dbgs().indent(indent * 2) << "Visiting ";
1505  if (instPath.empty())
1506  llvm::dbgs() << "$root";
1507  else
1508  llvm::dbgs() << instPath.leaf().getInstanceName();
1509  llvm::dbgs() << " (" << module.getName() << ")\n";
1510  });
1511 
1512  // Assemble the domain for this module.
1513  ResetDomain domain(parentReset);
1514  auto it = annotatedResets.find(module);
1515  if (it != annotatedResets.end()) {
1516  domain.isTop = true;
1517  domain.reset = it->second;
1518  }
1519 
1520  // Associate the domain with this module. If the module already has an
1521  // associated domain, it must be identical. Otherwise we'll have to report
1522  // the conflicting domains to the user.
1523  auto &entries = domains[module];
1524  if (llvm::all_of(entries,
1525  [&](const auto &entry) { return entry.first != domain; }))
1526  entries.push_back({domain, instPath});
1527 
1528  // Traverse the child instances.
1529  for (auto *record : *instGraph[module]) {
1530  auto submodule = dyn_cast<FModuleOp>(*record->getTarget()->getModule());
1531  if (!submodule)
1532  continue;
1533  auto childPath =
1534  instancePathCache->appendInstance(instPath, record->getInstance());
1535  buildDomains(submodule, childPath, domain.reset, instGraph, indent + 1);
1536  }
1537 }
1538 
1539 /// Determine how the reset for each module shall be implemented.
1540 void InferResetsPass::determineImpl() {
1541  LLVM_DEBUG({
1542  llvm::dbgs() << "\n";
1543  debugHeader("Determine implementation") << "\n\n";
1544  });
1545  for (auto &it : domains) {
1546  auto module = cast<FModuleOp>(it.first);
1547  auto &domain = it.second.back().first;
1548  determineImpl(module, domain);
1549  }
1550 }
1551 
1552 /// Determine how the reset for a module shall be implemented. This function
1553 /// fills in the `existingValue`, `existingPort`, and `newPortName` fields of
1554 /// the given reset domain.
1555 ///
1556 /// Generally it does the following:
1557 /// - If the domain has explicitly no reset ("ignore"), leaves everything
1558 /// empty.
1559 /// - If the domain is the place where the reset is defined ("top"), fills in
1560 /// the existing port/wire/node as reset.
1561 /// - If the module already has a port with the reset's name:
1562 /// - If the type is `asyncreset`, reuses that port.
1563 /// - Otherwise appends a `_N` suffix with increasing N to create a
1564 /// yet-unused
1565 /// port name, and marks that as to be created.
1566 /// - Otherwise indicates that a port with the reset's name should be created.
1567 ///
1568 void InferResetsPass::determineImpl(FModuleOp module, ResetDomain &domain) {
1569  if (!domain.reset)
1570  return; // nothing to do if the module needs no reset
1571  LLVM_DEBUG(llvm::dbgs() << "Planning reset for " << module.getName() << "\n");
1572 
1573  // If this is the root of a reset domain, we don't need to add any ports
1574  // and can just simply reuse the existing values.
1575  if (domain.isTop) {
1576  LLVM_DEBUG(llvm::dbgs() << "- Rooting at local value "
1577  << getResetName(domain.reset) << "\n");
1578  domain.existingValue = domain.reset;
1579  if (auto blockArg = dyn_cast<BlockArgument>(domain.reset))
1580  domain.existingPort = blockArg.getArgNumber();
1581  return;
1582  }
1583 
1584  // Otherwise, check if a port with this name and type already exists and
1585  // reuse that where possible.
1586  auto neededName = getResetName(domain.reset);
1587  auto neededType = domain.reset.getType();
1588  LLVM_DEBUG(llvm::dbgs() << "- Looking for existing port " << neededName
1589  << "\n");
1590  auto portNames = module.getPortNames();
1591  auto ports = llvm::zip(portNames, module.getArguments());
1592  auto portIt = llvm::find_if(
1593  ports, [&](auto port) { return std::get<0>(port) == neededName; });
1594  if (portIt != ports.end() && std::get<1>(*portIt).getType() == neededType) {
1595  LLVM_DEBUG(llvm::dbgs()
1596  << "- Reusing existing port " << neededName << "\n");
1597  domain.existingValue = std::get<1>(*portIt);
1598  domain.existingPort = std::distance(ports.begin(), portIt);
1599  return;
1600  }
1601 
1602  // If we have found a port but the types don't match, pick a new name for
1603  // the reset port.
1604  //
1605  // CAVEAT: The Scala FIRRTL compiler just throws an error in this case. This
1606  // seems unnecessary though, since the compiler can just insert a new reset
1607  // signal as needed.
1608  if (portIt != ports.end()) {
1609  LLVM_DEBUG(llvm::dbgs()
1610  << "- Existing " << neededName << " has incompatible type "
1611  << std::get<1>(*portIt).getType() << "\n");
1612  StringAttr newName;
1613  unsigned suffix = 0;
1614  do {
1615  newName =
1616  StringAttr::get(&getContext(), Twine(neededName.getValue()) +
1617  Twine("_") + Twine(suffix++));
1618  } while (llvm::is_contained(portNames, newName));
1619  LLVM_DEBUG(llvm::dbgs()
1620  << "- Creating uniquified port " << newName << "\n");
1621  domain.newPortName = newName;
1622  return;
1623  }
1624 
1625  // At this point we know that there is no such port, and we can safely
1626  // create one as needed.
1627  LLVM_DEBUG(llvm::dbgs() << "- Creating new port " << neededName << "\n");
1628  domain.newPortName = neededName;
1629 }
1630 
1631 //===----------------------------------------------------------------------===//
1632 // Async Reset Implementation
1633 //===----------------------------------------------------------------------===//
1634 
1635 /// Implement the async resets gathered in the pass' `domains` map.
1636 LogicalResult InferResetsPass::implementAsyncReset() {
1637  LLVM_DEBUG({
1638  llvm::dbgs() << "\n";
1639  debugHeader("Implement async resets") << "\n\n";
1640  });
1641  for (auto &it : domains)
1642  if (failed(implementAsyncReset(cast<FModuleOp>(it.first),
1643  it.second.back().first)))
1644  return failure();
1645  return success();
1646 }
1647 
1648 /// Implement the async resets for a specific module.
1649 ///
1650 /// This will add ports to the module as appropriate, update the register ops
1651 /// in the module, and update any instantiated submodules with their
1652 /// corresponding reset implementation details.
1653 LogicalResult InferResetsPass::implementAsyncReset(FModuleOp module,
1654  ResetDomain &domain) {
1655  LLVM_DEBUG(llvm::dbgs() << "Implementing async reset for " << module.getName()
1656  << "\n");
1657 
1658  // Nothing to do if the module was marked explicitly with no reset domain.
1659  if (!domain.reset) {
1660  LLVM_DEBUG(llvm::dbgs()
1661  << "- Skipping because module explicitly has no domain\n");
1662  return success();
1663  }
1664 
1665  // If needed, add a reset port to the module.
1666  Value actualReset = domain.existingValue;
1667  if (domain.newPortName) {
1668  PortInfo portInfo{domain.newPortName,
1669  AsyncResetType::get(&getContext()),
1670  Direction::In,
1671  {},
1672  domain.reset.getLoc()};
1673  module.insertPorts({{0, portInfo}});
1674  actualReset = module.getArgument(0);
1675  LLVM_DEBUG(llvm::dbgs()
1676  << "- Inserted port " << domain.newPortName << "\n");
1677  }
1678  assert(actualReset);
1679  LLVM_DEBUG({
1680  llvm::dbgs() << "- Using ";
1681  if (auto blockArg = dyn_cast<BlockArgument>(actualReset))
1682  llvm::dbgs() << "port #" << blockArg.getArgNumber() << " ";
1683  else
1684  llvm::dbgs() << "wire/node ";
1685  llvm::dbgs() << getResetName(actualReset) << "\n";
1686  });
1687 
1688  // Gather a list of operations in the module that need to be updated with
1689  // the new reset.
1690  SmallVector<Operation *> opsToUpdate;
1691  module.walk([&](Operation *op) {
1692  if (isa<InstanceOp, RegOp, RegResetOp>(op))
1693  opsToUpdate.push_back(op);
1694  });
1695 
1696  // If the reset is a local wire or node, move it upwards such that it
1697  // dominates all the operations that it will need to attach to. In the case
1698  // of a node this might not be easily possible, so we just spill into a wire
1699  // in that case.
1700  if (!isa<BlockArgument>(actualReset)) {
1701  mlir::DominanceInfo dom(module);
1702  // The first op in `opsToUpdate` is the top-most op in the module, since
1703  // the ops and blocks are traversed in a depth-first, top-to-bottom order
1704  // in `walk`. So we can simply check if the local reset declaration is
1705  // before the first op to find out if we need to move anything.
1706  auto *resetOp = actualReset.getDefiningOp();
1707  if (!opsToUpdate.empty() && !dom.dominates(resetOp, opsToUpdate[0])) {
1708  LLVM_DEBUG(llvm::dbgs()
1709  << "- Reset doesn't dominate all uses, needs to be moved\n");
1710 
1711  // If the node can't be moved because its input doesn't dominate the
1712  // target location, convert it to a wire.
1713  auto nodeOp = dyn_cast<NodeOp>(resetOp);
1714  if (nodeOp && !dom.dominates(nodeOp.getInput(), opsToUpdate[0])) {
1715  LLVM_DEBUG(llvm::dbgs()
1716  << "- Promoting node to wire for move: " << nodeOp << "\n");
1717  auto builder = ImplicitLocOpBuilder::atBlockBegin(nodeOp.getLoc(),
1718  nodeOp->getBlock());
1719  auto wireOp = builder.create<WireOp>(
1720  nodeOp.getResult().getType(), nodeOp.getNameAttr(),
1721  nodeOp.getNameKindAttr(), nodeOp.getAnnotationsAttr(),
1722  nodeOp.getInnerSymAttr(), nodeOp.getForceableAttr());
1723  // Don't delete the node, since it might be in use in worklists.
1724  nodeOp->replaceAllUsesWith(wireOp);
1725  nodeOp->removeAttr(nodeOp.getInnerSymAttrName());
1726  nodeOp.setName("");
1727  // Leave forcable alone, since we cannot remove a result. It will be
1728  // cleaned up in canonicalization since it is dead. As will this node.
1729  nodeOp.setNameKind(NameKindEnum::DroppableName);
1730  nodeOp.setAnnotationsAttr(ArrayAttr::get(builder.getContext(), {}));
1731  builder.setInsertionPointAfter(nodeOp);
1732  emitConnect(builder, wireOp.getResult(), nodeOp.getResult());
1733  resetOp = wireOp;
1734  actualReset = wireOp.getResult();
1735  domain.existingValue = wireOp.getResult();
1736  }
1737 
1738  // Determine the block into which the reset declaration needs to be
1739  // moved.
1740  Block *targetBlock = dom.findNearestCommonDominator(
1741  resetOp->getBlock(), opsToUpdate[0]->getBlock());
1742  LLVM_DEBUG({
1743  if (targetBlock != resetOp->getBlock())
1744  llvm::dbgs() << "- Needs to be moved to different block\n";
1745  });
1746 
1747  // At this point we have to figure out in front of which operation in
1748  // the target block the reset declaration has to be moved. The reset
1749  // declaration and the first op it needs to dominate may be buried
1750  // inside blocks of other operations (e.g. `WhenOp`), so we have to look
1751  // through their parent operations until we find the one that lies
1752  // within the target block.
1753  auto getParentInBlock = [](Operation *op, Block *block) {
1754  while (op && op->getBlock() != block)
1755  op = op->getParentOp();
1756  return op;
1757  };
1758  auto *resetOpInTarget = getParentInBlock(resetOp, targetBlock);
1759  auto *firstOpInTarget = getParentInBlock(opsToUpdate[0], targetBlock);
1760 
1761  // Move the operation upwards. Since there are situations where the
1762  // reset declaration does not dominate the first use, but the `WhenOp`
1763  // it is nested within actually *does* come before that use, we have to
1764  // consider moving the reset declaration in front of its parent op.
1765  if (resetOpInTarget->isBeforeInBlock(firstOpInTarget))
1766  resetOp->moveBefore(resetOpInTarget);
1767  else
1768  resetOp->moveBefore(firstOpInTarget);
1769  }
1770  }
1771 
1772  // Update the operations.
1773  for (auto *op : opsToUpdate)
1774  implementAsyncReset(op, module, actualReset);
1775 
1776  return success();
1777 }
1778 
1779 /// Modify an operation in a module to implement an async reset for that
1780 /// module.
1781 void InferResetsPass::implementAsyncReset(Operation *op, FModuleOp module,
1782  Value actualReset) {
1783  ImplicitLocOpBuilder builder(op->getLoc(), op);
1784 
1785  // Handle instances.
1786  if (auto instOp = dyn_cast<InstanceOp>(op)) {
1787  // Lookup the reset domain of the instantiated module. If there is no
1788  // reset domain associated with that module, or the module is explicitly
1789  // marked as being in no domain, simply skip.
1790  auto refModule = instOp.getReferencedModule<FModuleOp>(*instanceGraph);
1791  if (!refModule)
1792  return;
1793  auto domainIt = domains.find(refModule);
1794  if (domainIt == domains.end())
1795  return;
1796  auto &domain = domainIt->second.back().first;
1797  if (!domain.reset)
1798  return;
1799  LLVM_DEBUG(llvm::dbgs()
1800  << "- Update instance '" << instOp.getName() << "'\n");
1801 
1802  // If needed, add a reset port to the instance.
1803  Value instReset;
1804  if (domain.newPortName) {
1805  LLVM_DEBUG(llvm::dbgs() << " - Adding new result as reset\n");
1806 
1807  auto newInstOp = instOp.cloneAndInsertPorts(
1808  {{/*portIndex=*/0,
1809  {domain.newPortName,
1810  type_cast<FIRRTLBaseType>(actualReset.getType()),
1811  Direction::In}}});
1812  instReset = newInstOp.getResult(0);
1813 
1814  // Update the uses over to the new instance and drop the old instance.
1815  instOp.replaceAllUsesWith(newInstOp.getResults().drop_front());
1816  instanceGraph->replaceInstance(instOp, newInstOp);
1817  instOp->erase();
1818  instOp = newInstOp;
1819  } else if (domain.existingPort.has_value()) {
1820  auto idx = *domain.existingPort;
1821  instReset = instOp.getResult(idx);
1822  LLVM_DEBUG(llvm::dbgs() << " - Using result #" << idx << " as reset\n");
1823  }
1824 
1825  // If there's no reset port on the instance to connect, we're done. This
1826  // can happen if the instantiated module has a reset domain, but that
1827  // domain is e.g. rooted at an internal wire.
1828  if (!instReset)
1829  return;
1830 
1831  // Connect the instance's reset to the actual reset.
1832  assert(instReset && actualReset);
1833  builder.setInsertionPointAfter(instOp);
1834  emitConnect(builder, instReset, actualReset);
1835  return;
1836  }
1837 
1838  // Handle reset-less registers.
1839  if (auto regOp = dyn_cast<RegOp>(op)) {
1840  if (AnnotationSet::removeAnnotations(regOp, excludeMemToRegAnnoClass))
1841  return;
1842 
1843  LLVM_DEBUG(llvm::dbgs() << "- Adding async reset to " << regOp << "\n");
1844  auto zero = createZeroValue(builder, regOp.getResult().getType());
1845  auto newRegOp = builder.create<RegResetOp>(
1846  regOp.getResult().getType(), regOp.getClockVal(), actualReset, zero,
1847  regOp.getNameAttr(), regOp.getNameKindAttr(), regOp.getAnnotations(),
1848  regOp.getInnerSymAttr(), regOp.getForceableAttr());
1849  regOp.getResult().replaceAllUsesWith(newRegOp.getResult());
1850  if (regOp.getForceable())
1851  regOp.getRef().replaceAllUsesWith(newRegOp.getRef());
1852  regOp->erase();
1853  return;
1854  }
1855 
1856  // Handle registers with reset.
1857  if (auto regOp = dyn_cast<RegResetOp>(op)) {
1858  // If the register already has an async reset, leave it untouched.
1859  if (type_isa<AsyncResetType>(regOp.getResetSignal().getType())) {
1860  LLVM_DEBUG(llvm::dbgs()
1861  << "- Skipping (has async reset) " << regOp << "\n");
1862  // The following performs the logic of `CheckResets` in the original
1863  // Scala source code.
1864  if (failed(regOp.verifyInvariants()))
1865  signalPassFailure();
1866  return;
1867  }
1868  LLVM_DEBUG(llvm::dbgs() << "- Updating reset of " << regOp << "\n");
1869 
1870  auto reset = regOp.getResetSignal();
1871  auto value = regOp.getResetValue();
1872 
1873  // If we arrive here, the register has a sync reset. In order to add an
1874  // async reset, we have to move the sync reset into a mux in front of the
1875  // register.
1876  insertResetMux(builder, regOp.getResult(), reset, value);
1877  builder.setInsertionPointAfterValue(regOp.getResult());
1878  auto mux = builder.create<MuxPrimOp>(reset, value, regOp.getResult());
1879  emitConnect(builder, regOp.getResult(), mux);
1880 
1881  // Replace the existing reset with the async reset.
1882  builder.setInsertionPoint(regOp);
1883  auto zero = createZeroValue(builder, regOp.getResult().getType());
1884  regOp.getResetSignalMutable().assign(actualReset);
1885  regOp.getResetValueMutable().assign(zero);
1886  }
1887 }
1888 
1889 LogicalResult InferResetsPass::verifyNoAbstractReset() {
1890  bool hasAbstractResetPorts = false;
1891  for (FModuleLike module :
1892  getOperation().getBodyBlock()->getOps<FModuleLike>()) {
1893  for (PortInfo port : module.getPorts()) {
1894  if (getBaseOfType<ResetType>(port.type)) {
1895  auto diag = emitError(port.loc)
1896  << "a port \"" << port.getName()
1897  << "\" with abstract reset type was unable to be "
1898  "inferred by InferResets (is this a top-level port?)";
1899  diag.attachNote(module->getLoc())
1900  << "the module with this uninferred reset port was defined here";
1901  hasAbstractResetPorts = true;
1902  }
1903  }
1904  }
1905 
1906  if (hasAbstractResetPorts)
1907  return failure();
1908  return success();
1909 }
assert(baseType &&"element must be base type")
static Value createZeroValue(ImplicitLocOpBuilder &builder, FIRRTLBaseType type, SmallDenseMap< FIRRTLBaseType, Value > &cache)
Construct a zero value of the given type using the given builder.
static unsigned getFieldID(BundleType type, unsigned index)
bool operator!=(const ResetDomain &a, const ResetDomain &b)
Definition: InferResets.cpp:81
static std::pair< StringAttr, FModuleOp > getResetNameAndModule(Value reset)
Return the name and parent module of a reset.
Definition: InferResets.cpp:87
static unsigned getIndexForFieldID(BundleType type, unsigned fieldID)
static FIRRTLBaseType updateType(FIRRTLBaseType oldType, unsigned fieldID, FIRRTLBaseType fieldType)
Update the type of a single field within a type.
static bool isUselessVec(FIRRTLBaseType oldType, unsigned fieldID)
bool operator==(const ResetDomain &a, const ResetDomain &b)
Definition: InferResets.cpp:78
static StringAttr getResetName(Value reset)
Return the name of a reset.
static bool insertResetMux(ImplicitLocOpBuilder &builder, Value target, Value reset, Value resetValue)
Helper function that inserts reset multiplexer into all ConnectOps with the given target.
static bool getFieldName(const FieldRef &fieldRef, SmallString< 32 > &string)
static bool typeContainsReset(Type type)
Check whether a type contains a ResetType.
static bool getDeclName(Value value, SmallString< 32 > &string)
static unsigned getMaxFieldID(FIRRTLBaseType type)
static InstancePath empty
static Block * getBodyBlock(FModuleLike mod)
This class represents a reference to a specific field or element of an aggregate value.
Definition: FieldRef.h:28
unsigned getFieldID() const
Get the field ID of this FieldRef, which is a unique identifier mapped to a specific field in a bundl...
Definition: FieldRef.h:59
Value getValue() const
Get the Value which created this location.
Definition: FieldRef.h:37
This class provides a read-only projection of an annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
bool isConst()
Returns true if this is a 'const' type that can only hold compile-time constant values.
FIRRTLBaseType getConstType(bool isConst)
Return a 'const' or non-'const' version of this type.
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
Definition: FIRRTLTypes.h:520
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition: FIRRTLTypes.h:530
This graph tracks modules and where they are instantiated.
An instance path composed of a series of instances.
InstanceOpInterface leaf() const
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
FieldRef getFieldRefForTarget(const hw::InnerSymTarget &ist)
Get FieldRef pointing to the specified inner symbol target, which must be valid.
constexpr const char * excludeMemToRegAnnoClass
igraph::InstancePathCache InstancePathCache
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
Definition: FIRRTLUtils.h:222
FIRRTLType mapBaseType(FIRRTLType type, function_ref< FIRRTLBaseType(FIRRTLBaseType)> fn)
Return a FIRRTLType with its base type component mutated by the given function.
Definition: FIRRTLUtils.h:238
constexpr const char * fullAsyncResetAnnoClass
Annotation that marks a reset (port or wire) and domain.
T & operator<<(T &os, FIRVersion version)
Definition: FIRParser.h:119
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
constexpr const char * ignoreFullAsyncResetAnnoClass
Annotation that marks a module as not belonging to any reset domain.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
Definition: FIRRTLUtils.cpp:24
std::unique_ptr< mlir::Pass > createInferResetsPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
llvm::raw_ostream & debugHeader(llvm::StringRef str, int width=80)
Write a "header"-like string to the debug stream with a certain width.
Definition: Debug.cpp:18
inline ::llvm::hash_code hash_value(const FieldRef &fieldRef)
Get a hash code for a FieldRef.
Definition: FieldRef.h:92
bool operator<(const AppID &a, const AppID &b)
Definition: Manifest.cpp:610
This holds the name and type that describes the module's ports.
static ResetSignal getEmptyKey()
static ResetSignal getTombstoneKey()
static bool isEqual(const ResetSignal &lhs, const ResetSignal &rhs)
static unsigned getHashValue(const ResetSignal &x)