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