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"
25 #include "mlir/IR/ImplicitLocOpBuilder.h"
26 #include "llvm/ADT/APSInt.h"
27 #include "llvm/ADT/TypeSwitch.h"
28 #include "llvm/Support/Debug.h"
29 
30 #define DEBUG_TYPE "firrtl-remove-resets"
31 
32 using namespace circt;
33 using namespace firrtl;
34 
35 struct SFCCompatPass : public SFCCompatBase<SFCCompatPass> {
36  void runOnOperation() override;
37 };
38 
40  LLVM_DEBUG(
41  llvm::dbgs() << "==----- Running SFCCompat "
42  "---------------------------------------------------===\n"
43  << "Module: '" << getOperation().getName() << "'\n";);
44 
45  bool madeModifications = false;
46  SmallVector<InvalidValueOp> invalidOps;
47  auto result = getOperation()->walk([&](Operation *op) {
48  // Populate invalidOps for later handling.
49  if (auto inv = dyn_cast<InvalidValueOp>(op)) {
50  invalidOps.push_back(inv);
51  return WalkResult::advance();
52  }
53  auto reg = dyn_cast<RegResetOp>(op);
54  if (!reg)
55  return WalkResult::advance();
56 
57  // If the `RegResetOp` has an invalidated initialization, then replace it
58  // with a `RegOp`.
59  if (walkDrivers(reg.getResetValue(), true, false, false,
60  [](FieldRef dst, FieldRef src) {
61  return src.isa<InvalidValueOp>();
62  })) {
63  ImplicitLocOpBuilder builder(reg.getLoc(), reg);
64  RegOp newReg = builder.create<RegOp>(
65  reg.getResult().getType(), reg.getClockVal(), reg.getNameAttr(),
66  reg.getNameKindAttr(), reg.getAnnotationsAttr(),
67  reg.getInnerSymAttr(), reg.getForceableAttr());
68  reg.replaceAllUsesWith(newReg);
69  reg.erase();
70  madeModifications = true;
71  return WalkResult::advance();
72  }
73 
74  // If the `RegResetOp` has an asynchronous reset and the reset value is not
75  // a module-scoped constant when looking through wires and nodes, then
76  // generate an error. This implements the SFC's CheckResets pass.
77  if (!isa<AsyncResetType>(reg.getResetSignal().getType()))
78  return WalkResult::advance();
79  if (walkDrivers(
80  reg.getResetValue(), true, true, true,
81  [&](FieldRef dst, FieldRef src) {
82  if (src.isa<ConstantOp, InvalidValueOp, SpecialConstantOp,
83  AggregateConstantOp>())
84  return true;
85  auto diag = emitError(reg.getLoc());
86  auto [fieldName, rootKnown] = getFieldName(dst);
87  diag << "register " << reg.getNameAttr()
88  << " has an async reset, but its reset value";
89  if (rootKnown)
90  diag << " \"" << fieldName << "\"";
91  diag << " is not driven with a constant value through wires, "
92  "nodes, or connects";
93  std::tie(fieldName, rootKnown) = getFieldName(src);
94  diag.attachNote(src.getLoc())
95  << "reset driver is "
96  << (rootKnown ? ("\"" + fieldName + "\"") : "here");
97  return false;
98  }))
99  return WalkResult::advance();
100  return WalkResult::interrupt();
101  });
102 
103  if (result.wasInterrupted())
104  return signalPassFailure();
105 
106  // Convert all invalid values to zero.
107  for (auto inv : invalidOps) {
108  // Delete invalids which have no uses.
109  if (inv->getUses().empty()) {
110  inv->erase();
111  madeModifications = true;
112  continue;
113  }
114  ImplicitLocOpBuilder builder(inv.getLoc(), inv);
115  Value replacement =
117  .Case<ClockType, AsyncResetType, ResetType>(
118  [&](auto type) -> Value {
119  return builder.create<SpecialConstantOp>(
120  type, builder.getBoolAttr(false));
121  })
122  .Case<IntType>([&](IntType type) -> Value {
123  return builder.create<ConstantOp>(type, getIntZerosAttr(type));
124  })
125  .Case<BundleType, FVectorType>([&](auto type) -> Value {
126  auto width = circt::firrtl::getBitWidth(type);
127  assert(width && "width must be inferred");
128  auto zero = builder.create<ConstantOp>(APSInt(*width));
129  return builder.create<BitCastOp>(type, zero);
130  })
131  .Default([&](auto) {
132  llvm_unreachable("all types are supported");
133  return Value();
134  });
135  inv.replaceAllUsesWith(replacement);
136  inv.erase();
137  madeModifications = true;
138  }
139 
140  if (!madeModifications)
141  return markAllAnalysesPreserved();
142 }
143 
144 std::unique_ptr<mlir::Pass> circt::firrtl::createSFCCompatPass() {
145  return std::make_unique<SFCCompatPass>();
146 }
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
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
std::unique_ptr< mlir::Pass > createSFCCompatPass()
Definition: SFCCompat.cpp:144
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.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21
mlir::raw_indented_ostream & dbgs()
Definition: Utility.h:28
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:20
void runOnOperation() override
Definition: SFCCompat.cpp:39