CIRCT  20.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 
25 #include "mlir/IR/ImplicitLocOpBuilder.h"
26 #include "mlir/Pass/Pass.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 namespace circt {
34 namespace firrtl {
35 #define GEN_PASS_DEF_SFCCOMPAT
36 #include "circt/Dialect/FIRRTL/Passes.h.inc"
37 } // namespace firrtl
38 } // namespace circt
39 
40 using namespace circt;
41 using namespace firrtl;
42 
44  : public circt::firrtl::impl::SFCCompatBase<SFCCompatPass> {
45  void runOnOperation() override;
46 };
47 
49  LLVM_DEBUG(
50  llvm::dbgs() << "==----- Running SFCCompat "
51  "---------------------------------------------------===\n"
52  << "Module: '" << getOperation().getName() << "'\n";);
53 
54  bool madeModifications = false;
55  SmallVector<InvalidValueOp> invalidOps;
56 
57  auto fullResetAttr = StringAttr::get(&getContext(), fullResetAnnoClass);
58  auto isFullResetAnno = [fullResetAttr](Annotation anno) {
59  auto annoClassAttr = anno.getClassAttr();
60  return annoClassAttr == fullResetAttr;
61  };
62  bool fullResetExists = AnnotationSet::removePortAnnotations(
63  getOperation(),
64  [&](unsigned argNum, Annotation anno) { return isFullResetAnno(anno); });
65  getOperation()->walk([isFullResetAnno, &fullResetExists](Operation *op) {
66  fullResetExists |= AnnotationSet::removeAnnotations(op, isFullResetAnno);
67  });
68  madeModifications |= fullResetExists;
69 
70  auto result = getOperation()->walk([&](Operation *op) {
71  // Populate invalidOps for later handling.
72  if (auto inv = dyn_cast<InvalidValueOp>(op)) {
73  invalidOps.push_back(inv);
74  return WalkResult::advance();
75  }
76  auto reg = dyn_cast<RegResetOp>(op);
77  if (!reg)
78  return WalkResult::advance();
79 
80  // If the `RegResetOp` has an invalidated initialization and we
81  // are not running FART, then replace it with a `RegOp`.
82  if (!fullResetExists && walkDrivers(reg.getResetValue(), true, true, false,
83  [](FieldRef dst, FieldRef src) {
84  return src.isa<InvalidValueOp>();
85  })) {
86  ImplicitLocOpBuilder builder(reg.getLoc(), reg);
87  RegOp newReg = builder.create<RegOp>(
88  reg.getResult().getType(), reg.getClockVal(), reg.getNameAttr(),
89  reg.getNameKindAttr(), reg.getAnnotationsAttr(),
90  reg.getInnerSymAttr(), reg.getForceableAttr());
91  reg.replaceAllUsesWith(newReg);
92  reg.erase();
93  madeModifications = true;
94  return WalkResult::advance();
95  }
96 
97  // If the `RegResetOp` has an asynchronous reset and the reset value is not
98  // a module-scoped constant when looking through wires and nodes, then
99  // generate an error. This implements the SFC's CheckResets pass.
100  if (!isa<AsyncResetType>(reg.getResetSignal().getType()))
101  return WalkResult::advance();
102  if (walkDrivers(
103  reg.getResetValue(), true, true, true,
104  [&](FieldRef dst, FieldRef src) {
105  if (src.isa<ConstantOp, InvalidValueOp, SpecialConstantOp,
106  AggregateConstantOp>())
107  return true;
108  auto diag = emitError(reg.getLoc());
109  auto [fieldName, rootKnown] = getFieldName(dst);
110  diag << "register " << reg.getNameAttr()
111  << " has an async reset, but its reset value";
112  if (rootKnown)
113  diag << " \"" << fieldName << "\"";
114  diag << " is not driven with a constant value through wires, "
115  "nodes, or connects";
116  std::tie(fieldName, rootKnown) = getFieldName(src);
117  diag.attachNote(src.getLoc())
118  << "reset driver is "
119  << (rootKnown ? ("\"" + fieldName + "\"") : "here");
120  return false;
121  }))
122  return WalkResult::advance();
123  return WalkResult::interrupt();
124  });
125 
126  if (result.wasInterrupted())
127  return signalPassFailure();
128 
129  // Convert all invalid values to zero.
130  for (auto inv : invalidOps) {
131  // Delete invalids which have no uses.
132  if (inv->getUses().empty()) {
133  inv->erase();
134  madeModifications = true;
135  continue;
136  }
137  ImplicitLocOpBuilder builder(inv.getLoc(), inv);
138  Value replacement =
140  .Case<ClockType, AsyncResetType, ResetType>(
141  [&](auto type) -> Value {
142  return builder.create<SpecialConstantOp>(
143  type, builder.getBoolAttr(false));
144  })
145  .Case<IntType>([&](IntType type) -> Value {
146  return builder.create<ConstantOp>(type, getIntZerosAttr(type));
147  })
148  .Case<BundleType, FVectorType>([&](auto type) -> Value {
149  auto width = circt::firrtl::getBitWidth(type);
150  assert(width && "width must be inferred");
151  auto zero = builder.create<ConstantOp>(APSInt(*width));
152  return builder.create<BitCastOp>(type, zero);
153  })
154  .Default([&](auto) {
155  llvm_unreachable("all types are supported");
156  return Value();
157  });
158  inv.replaceAllUsesWith(replacement);
159  inv.erase();
160  madeModifications = true;
161  }
162 
163  if (!madeModifications)
164  return markAllAnalysesPreserved();
165 }
166 
167 std::unique_ptr<mlir::Pass> circt::firrtl::createSFCCompatPass() {
168  return std::make_unique<SFCCompatPass>();
169 }
assert(baseType &&"element must be base type")
int32_t width
Definition: FIRRTL.cpp:36
This class represents a reference to a specific field or element of an aggregate value.
Definition: FieldRef.h:28
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
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.
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 is the common base class between SIntType and UIntType.
Definition: FIRRTLTypes.h:296
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
constexpr const char * fullResetAnnoClass
Annotation that marks a reset (port or wire) and domain.
std::unique_ptr< mlir::Pass > createSFCCompatPass()
Definition: SFCCompat.cpp:167
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:21
void runOnOperation() override
Definition: SFCCompat.cpp:48