CIRCT  18.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  for (auto &op : llvm::make_early_inc_range(getOperation().getOps())) {
48  // Populate invalidOps for later handling.
49  if (auto inv = dyn_cast<InvalidValueOp>(op)) {
50  invalidOps.push_back(inv);
51  continue;
52  }
53  auto reg = dyn_cast<RegResetOp>(op);
54  if (!reg)
55  continue;
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  continue;
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  continue;
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  continue;
100  return signalPassFailure();
101  }
102 
103  // Convert all invalid values to zero.
104  for (auto inv : invalidOps) {
105  // Delete invalids which have no uses.
106  if (inv->getUses().empty()) {
107  inv->erase();
108  madeModifications = true;
109  continue;
110  }
111  ImplicitLocOpBuilder builder(inv.getLoc(), inv);
112  Value replacement =
114  .Case<ClockType, AsyncResetType, ResetType>(
115  [&](auto type) -> Value {
116  return builder.create<SpecialConstantOp>(
117  type, builder.getBoolAttr(false));
118  })
119  .Case<IntType>([&](IntType type) -> Value {
120  return builder.create<ConstantOp>(type, getIntZerosAttr(type));
121  })
122  .Case<BundleType, FVectorType>([&](auto type) -> Value {
123  auto width = circt::firrtl::getBitWidth(type);
124  assert(width && "width must be inferred");
125  auto zero = builder.create<ConstantOp>(APSInt(*width));
126  return builder.create<BitCastOp>(type, zero);
127  })
128  .Default([&](auto) {
129  llvm_unreachable("all types are supported");
130  return Value();
131  });
132  inv.replaceAllUsesWith(replacement);
133  inv.erase();
134  madeModifications = true;
135  }
136 
137  if (!madeModifications)
138  return markAllAnalysesPreserved();
139 }
140 
141 std::unique_ptr<mlir::Pass> circt::firrtl::createSFCCompatPass() {
142  return std::make_unique<SFCCompatPass>();
143 }
assert(baseType &&"element must be base type")
int32_t width
Definition: FIRRTL.cpp:27
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:515
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition: FIRRTLTypes.h:525
This is the common base class between SIntType and UIntType.
Definition: FIRRTLTypes.h:291
std::unique_ptr< mlir::Pass > createSFCCompatPass()
Definition: SFCCompat.cpp:141
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