CIRCT  19.0.0git
SFCCompat.cpp
Go to the documentation of this file.
1 //===- SFCCompat.cpp - SFC Compatible Pass ----------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //===----------------------------------------------------------------------===//
7 //
8 // This pass makes a number of updates to the circuit that are required to match
9 // the behavior of the Scala FIRRTL Compiler (SFC). This pass removes invalid
10 // values from the circuit. This is a combination of the Scala FIRRTL
11 // Compiler's RemoveRests pass and RemoveValidIf. This is done to remove two
12 // "interpretations" of invalid. Namely: (1) registers that are initialized to
13 // an invalid value (module scoped and looking through wires and connects only)
14 // are converted to an unitialized register and (2) invalid values are converted
15 // to zero (after rule 1 is applied). Additionally, this pass checks and
16 // disallows async reset registers that are not driven with a constant when
17 // looking through wires, connects, and nodes.
18 //
19 //===----------------------------------------------------------------------===//
20 
21 #include "PassDetails.h"
26 #include "mlir/IR/ImplicitLocOpBuilder.h"
27 #include "llvm/ADT/APSInt.h"
28 #include "llvm/ADT/TypeSwitch.h"
29 #include "llvm/Support/Debug.h"
30 
31 #define DEBUG_TYPE "firrtl-remove-resets"
32 
33 using namespace circt;
34 using namespace firrtl;
35 
36 struct SFCCompatPass : public SFCCompatBase<SFCCompatPass> {
37  void runOnOperation() override;
38 };
39 
41  LLVM_DEBUG(
42  llvm::dbgs() << "==----- Running SFCCompat "
43  "---------------------------------------------------===\n"
44  << "Module: '" << getOperation().getName() << "'\n";);
45 
46  bool madeModifications = false;
47  SmallVector<InvalidValueOp> invalidOps;
48 
49  bool fullAsyncResetExists = false;
51  getOperation(), [&](unsigned argNum, Annotation anno) {
53  return false;
54  return fullAsyncResetExists = true;
55  });
56 
57  auto result = getOperation()->walk([&](Operation *op) {
58  // Populate invalidOps for later handling.
59  if (auto inv = dyn_cast<InvalidValueOp>(op)) {
60  invalidOps.push_back(inv);
61  return WalkResult::advance();
62  }
63  auto reg = dyn_cast<RegResetOp>(op);
64  if (!reg)
65  return WalkResult::advance();
66 
67  // If the `RegResetOp` has an invalidated initialization and we
68  // are not running FART, then replace it with a `RegOp`.
69  if (!fullAsyncResetExists &&
70  walkDrivers(reg.getResetValue(), true, false, false,
71  [](FieldRef dst, FieldRef src) {
72  return src.isa<InvalidValueOp>();
73  })) {
74  ImplicitLocOpBuilder builder(reg.getLoc(), reg);
75  RegOp newReg = builder.create<RegOp>(
76  reg.getResult().getType(), reg.getClockVal(), reg.getNameAttr(),
77  reg.getNameKindAttr(), reg.getAnnotationsAttr(),
78  reg.getInnerSymAttr(), reg.getForceableAttr());
79  reg.replaceAllUsesWith(newReg);
80  reg.erase();
81  madeModifications = true;
82  return WalkResult::advance();
83  }
84 
85  // If the `RegResetOp` has an asynchronous reset and the reset value is not
86  // a module-scoped constant when looking through wires and nodes, then
87  // generate an error. This implements the SFC's CheckResets pass.
88  if (!isa<AsyncResetType>(reg.getResetSignal().getType()))
89  return WalkResult::advance();
90  if (walkDrivers(
91  reg.getResetValue(), true, true, true,
92  [&](FieldRef dst, FieldRef src) {
93  if (src.isa<ConstantOp, InvalidValueOp, SpecialConstantOp,
94  AggregateConstantOp>())
95  return true;
96  auto diag = emitError(reg.getLoc());
97  auto [fieldName, rootKnown] = getFieldName(dst);
98  diag << "register " << reg.getNameAttr()
99  << " has an async reset, but its reset value";
100  if (rootKnown)
101  diag << " \"" << fieldName << "\"";
102  diag << " is not driven with a constant value through wires, "
103  "nodes, or connects";
104  std::tie(fieldName, rootKnown) = getFieldName(src);
105  diag.attachNote(src.getLoc())
106  << "reset driver is "
107  << (rootKnown ? ("\"" + fieldName + "\"") : "here");
108  return false;
109  }))
110  return WalkResult::advance();
111  return WalkResult::interrupt();
112  });
113 
114  if (result.wasInterrupted())
115  return signalPassFailure();
116 
117  // Convert all invalid values to zero.
118  for (auto inv : invalidOps) {
119  // Delete invalids which have no uses.
120  if (inv->getUses().empty()) {
121  inv->erase();
122  madeModifications = true;
123  continue;
124  }
125  ImplicitLocOpBuilder builder(inv.getLoc(), inv);
126  Value replacement =
128  .Case<ClockType, AsyncResetType, ResetType>(
129  [&](auto type) -> Value {
130  return builder.create<SpecialConstantOp>(
131  type, builder.getBoolAttr(false));
132  })
133  .Case<IntType>([&](IntType type) -> Value {
134  return builder.create<ConstantOp>(type, getIntZerosAttr(type));
135  })
136  .Case<BundleType, FVectorType>([&](auto type) -> Value {
137  auto width = circt::firrtl::getBitWidth(type);
138  assert(width && "width must be inferred");
139  auto zero = builder.create<ConstantOp>(APSInt(*width));
140  return builder.create<BitCastOp>(type, zero);
141  })
142  .Default([&](auto) {
143  llvm_unreachable("all types are supported");
144  return Value();
145  });
146  inv.replaceAllUsesWith(replacement);
147  inv.erase();
148  madeModifications = true;
149  }
150 
151  if (!madeModifications)
152  return markAllAnalysesPreserved();
153 }
154 
155 std::unique_ptr<mlir::Pass> circt::firrtl::createSFCCompatPass() {
156  return std::make_unique<SFCCompatPass>();
157 }
assert(baseType &&"element must be base type")
int32_t width
Definition: FIRRTL.cpp:36
Builder builder
This class represents a reference to a specific field or element of an aggregate value.
Definition: FieldRef.h:28
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
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.
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 is the common base class between SIntType and UIntType.
Definition: FIRRTLTypes.h:294
constexpr const char * fullAsyncResetAnnoClass
Annotation that marks a reset (port or wire) and domain.
std::unique_ptr< mlir::Pass > createSFCCompatPass()
Definition: SFCCompat.cpp:155
bool walkDrivers(FIRRTLBaseValue value, bool lookThroughWires, bool lookThroughNodes, bool lookThroughCasts, WalkDriverCallback callback)
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
IntegerAttr getIntZerosAttr(Type type)
Utility for generating a constant zero attribute.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:20
void runOnOperation() override
Definition: SFCCompat.cpp:40