CIRCT  20.0.0git
ExternalizeRegisters.cpp
Go to the documentation of this file.
1 //===- ExternalizeRegisters.cpp -------------------------------------------===//
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 
10 #include "circt/Dialect/HW/HWOps.h"
14 #include "circt/Support/LLVM.h"
17 #include "mlir/Dialect/Func/IR/FuncOps.h"
18 #include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
19 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
20 #include "mlir/Pass/Pass.h"
21 #include "mlir/Transforms/DialectConversion.h"
22 #include "llvm/ADT/PostOrderIterator.h"
23 #include "llvm/ADT/Twine.h"
24 #include <optional>
25 
26 using namespace mlir;
27 using namespace circt;
28 using namespace hw;
29 using namespace igraph;
30 
31 namespace circt {
32 #define GEN_PASS_DEF_EXTERNALIZEREGISTERS
33 #include "circt/Tools/circt-bmc/Passes.h.inc"
34 } // namespace circt
35 
36 //===----------------------------------------------------------------------===//
37 // Externalize Registers Pass
38 //===----------------------------------------------------------------------===//
39 
40 namespace {
41 struct ExternalizeRegistersPass
42  : public circt::impl::ExternalizeRegistersBase<ExternalizeRegistersPass> {
43  using ExternalizeRegistersBase::ExternalizeRegistersBase;
44  void runOnOperation() override;
45 };
46 } // namespace
47 
48 void ExternalizeRegistersPass::runOnOperation() {
49  auto &instanceGraph = getAnalysis<hw::InstanceGraph>();
50  DenseSet<Operation *> handled;
51  DenseMap<StringAttr, SmallVector<Type>> addedInputs;
52  DenseMap<StringAttr, SmallVector<StringAttr>> addedInputNames;
53  DenseMap<StringAttr, SmallVector<Type>> addedOutputs;
54  DenseMap<StringAttr, SmallVector<StringAttr>> addedOutputNames;
55  DenseMap<StringAttr, SmallVector<Attribute>> initialValues;
56 
57  // Iterate over all instances in the instance graph. This ensures we visit
58  // every module, even private top modules (private and never instantiated).
59  for (auto *startNode : instanceGraph) {
60  if (handled.count(startNode->getModule().getOperation()))
61  continue;
62 
63  // Visit the instance subhierarchy starting at the current module, in a
64  // depth-first manner. This allows us to inline child modules into parents
65  // before we attempt to inline parents into their parents.
66  for (InstanceGraphNode *node : llvm::post_order(startNode)) {
67  if (!handled.insert(node->getModule().getOperation()).second)
68  continue;
69 
70  auto module =
71  dyn_cast_or_null<HWModuleOp>(node->getModule().getOperation());
72  if (!module)
73  continue;
74 
75  unsigned numRegs = 0;
76  bool foundClk = false;
77  for (auto ty : module.getInputTypes()) {
78  if (isa<seq::ClockType>(ty)) {
79  if (foundClk) {
80  module.emitError("modules with multiple clocks not yet supported");
81  return signalPassFailure();
82  }
83  foundClk = true;
84  }
85  }
86  module->walk([&](Operation *op) {
87  if (auto regOp = dyn_cast<seq::CompRegOp>(op)) {
88  if (!isa<BlockArgument>(regOp.getClk())) {
89  regOp.emitError("only clocks directly given as block arguments "
90  "are supported");
91  return signalPassFailure();
92  }
93  if (regOp.getReset()) {
94  regOp.emitError("registers with reset signals not yet supported");
95  return signalPassFailure();
96  }
97  mlir::Attribute initState;
98  if (auto initVal = regOp.getInitialValue()) {
99  // Find the seq.initial op where the initial value is defined and
100  // fetch the operation inside that defines the value
101  auto initialOp =
102  regOp.getInitialValue().getDefiningOp<seq::InitialOp>();
103  if (!initialOp) {
104  regOp.emitError("registers with initial values not directly "
105  "defined by a seq.initial op not yet supported");
106  return signalPassFailure();
107  }
108  auto index = cast<OpResult>(initVal).getResultNumber();
109  auto initValDef =
110  initialOp->getRegion(0).front().getTerminator()->getOperand(
111  index);
112  // If it's defined by a constant op then just fetch the constant
113  // value - otherwise unsupported
114  if (auto constantOp = initValDef.getDefiningOp<hw::ConstantOp>()) {
115  // Fetch value from constant op - leave removing the dead op to
116  // DCE
117  initState = constantOp.getValueAttr();
118  } else {
119  regOp.emitError("registers with initial values not directly "
120  "defined by a hw.constant op in a seq.initial op "
121  "not yet supported");
122  return signalPassFailure();
123  }
124  } else {
125  // If there's no initial value just add a unit attribute to maintain
126  // one-to-one correspondence with module ports
127  initState = mlir::UnitAttr::get(&getContext());
128  }
129  addedInputs[module.getSymNameAttr()].push_back(regOp.getType());
130  addedOutputs[module.getSymNameAttr()].push_back(
131  regOp.getInput().getType());
132  OpBuilder builder(regOp);
133  auto regName = regOp.getName();
134  StringAttr newInputName, newOutputName;
135  if (regName && !regName.value().empty()) {
136  newInputName = builder.getStringAttr(regName.value() + "_state");
137  newOutputName = builder.getStringAttr(regName.value() + "_input");
138  } else {
139  newInputName =
140  builder.getStringAttr("reg_" + Twine(numRegs) + "_state");
141  newOutputName =
142  builder.getStringAttr("reg_" + Twine(numRegs) + "_input");
143  }
144  addedInputNames[module.getSymNameAttr()].push_back(newInputName);
145  addedOutputNames[module.getSymNameAttr()].push_back(newOutputName);
146  initialValues[module.getSymNameAttr()].push_back(initState);
147 
148  regOp.getResult().replaceAllUsesWith(
149  module.appendInput(newInputName, regOp.getType()).second);
150  module.appendOutput(newOutputName, regOp.getInput());
151  regOp->erase();
152  ++numRegs;
153  return;
154  }
155  if (auto instanceOp = dyn_cast<InstanceOp>(op)) {
156  OpBuilder builder(instanceOp);
157  auto newInputs =
158  addedInputs[instanceOp.getModuleNameAttr().getAttr()];
159  auto newInputNames =
160  addedInputNames[instanceOp.getModuleNameAttr().getAttr()];
161  auto newOutputs =
162  addedOutputs[instanceOp.getModuleNameAttr().getAttr()];
163  auto newOutputNames =
164  addedOutputNames[instanceOp.getModuleNameAttr().getAttr()];
165  addedInputs[module.getSymNameAttr()].append(newInputs);
166  addedInputNames[module.getSymNameAttr()].append(newInputNames);
167  addedOutputs[module.getSymNameAttr()].append(newOutputs);
168  addedOutputNames[module.getSymNameAttr()].append(newOutputNames);
169  initialValues[module.getSymNameAttr()].append(
170  initialValues[instanceOp.getModuleNameAttr().getAttr()]);
171  SmallVector<Attribute> argNames(
172  instanceOp.getInputNames().getValue());
173  SmallVector<Attribute> resultNames(
174  instanceOp.getOutputNames().getValue());
175 
176  for (auto [input, name] : zip_equal(newInputs, newInputNames)) {
177  instanceOp.getInputsMutable().append(
178  module.appendInput(name, input).second);
179  argNames.push_back(name);
180  }
181  for (auto outputName : newOutputNames) {
182  resultNames.push_back(outputName);
183  }
184  SmallVector<Type> resTypes(instanceOp->getResultTypes());
185  resTypes.append(newOutputs);
186  auto newInst = builder.create<InstanceOp>(
187  instanceOp.getLoc(), resTypes, instanceOp.getInstanceNameAttr(),
188  instanceOp.getModuleNameAttr(), instanceOp.getInputs(),
189  builder.getArrayAttr(argNames), builder.getArrayAttr(resultNames),
190  instanceOp.getParametersAttr(), instanceOp.getInnerSymAttr());
191  for (auto [output, name] :
192  zip(newInst->getResults().take_back(newOutputs.size()),
193  newOutputNames))
194  module.appendOutput(name, output);
195  numRegs += newInputs.size();
196  instanceOp.replaceAllUsesWith(
197  newInst.getResults().take_front(instanceOp->getNumResults()));
198  instanceGraph.replaceInstance(instanceOp, newInst);
199  instanceOp->erase();
200  return;
201  }
202  });
203 
204  module->setAttr(
205  "num_regs",
206  IntegerAttr::get(IntegerType::get(&getContext(), 32), numRegs));
207 
208  module->setAttr("initial_values",
209  ArrayAttr::get(&getContext(),
210  initialValues[module.getSymNameAttr()]));
211  }
212  }
213 }
This is a Node in the InstanceGraph.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: hw.py:1