CIRCT 22.0.0git
Loading...
Searching...
No Matches
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
15#include "circt/Support/LLVM.h"
18#include "mlir/Dialect/Func/IR/FuncOps.h"
19#include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
20#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
21#include "mlir/Pass/Pass.h"
22#include "mlir/Transforms/DialectConversion.h"
23#include "llvm/ADT/PostOrderIterator.h"
24#include "llvm/ADT/Twine.h"
25#include <optional>
26
27using namespace mlir;
28using namespace circt;
29using namespace hw;
30using namespace igraph;
31
32namespace circt {
33#define GEN_PASS_DEF_EXTERNALIZEREGISTERS
34#include "circt/Tools/circt-bmc/Passes.h.inc"
35} // namespace circt
36
37//===----------------------------------------------------------------------===//
38// Externalize Registers Pass
39//===----------------------------------------------------------------------===//
40
41namespace {
42struct ExternalizeRegistersPass
43 : public circt::impl::ExternalizeRegistersBase<ExternalizeRegistersPass> {
44 using ExternalizeRegistersBase::ExternalizeRegistersBase;
45 void runOnOperation() override;
46
47private:
48 DenseMap<StringAttr, SmallVector<Type>> addedInputs;
49 DenseMap<StringAttr, SmallVector<StringAttr>> addedInputNames;
50 DenseMap<StringAttr, SmallVector<Type>> addedOutputs;
51 DenseMap<StringAttr, SmallVector<StringAttr>> addedOutputNames;
52 DenseMap<StringAttr, SmallVector<Attribute>> initialValues;
53
54 LogicalResult externalizeReg(HWModuleOp module, Operation *op, Twine regName,
55 Value clock, Attribute initState, Value reset,
56 bool isAsync, Value resetValue, Value next);
57};
58} // namespace
59
60void ExternalizeRegistersPass::runOnOperation() {
61 auto &instanceGraph = getAnalysis<hw::InstanceGraph>();
62 DenseSet<Operation *> handled;
63
64 // Iterate over all instances in the instance graph. This ensures we visit
65 // every module, even private top modules (private and never instantiated).
66 for (auto *startNode : instanceGraph) {
67 if (handled.count(startNode->getModule().getOperation()))
68 continue;
69
70 // Visit the instance subhierarchy starting at the current module, in a
71 // depth-first manner. This allows us to inline child modules into parents
72 // before we attempt to inline parents into their parents.
73 for (InstanceGraphNode *node : llvm::post_order(startNode)) {
74 if (!handled.insert(node->getModule().getOperation()).second)
75 continue;
76
77 auto module =
78 dyn_cast_or_null<HWModuleOp>(node->getModule().getOperation());
79 if (!module)
80 continue;
81
82 unsigned numRegs = 0;
83 bool foundClk = false;
84 for (auto ty : module.getInputTypes()) {
85 if (isa<seq::ClockType>(ty)) {
86 if (foundClk) {
87 module.emitError("modules with multiple clocks not yet supported");
88 return signalPassFailure();
89 }
90 foundClk = true;
91 }
92 }
93 module->walk([&](Operation *op) {
94 if (auto regOp = dyn_cast<seq::CompRegOp>(op)) {
95 mlir::Attribute initState = {};
96 if (auto initVal = regOp.getInitialValue()) {
97 // Find the constant op that defines the reset value in an initial
98 // block (if it exists)
99 if (!initVal.getDefiningOp<seq::InitialOp>()) {
100 regOp.emitError("registers with initial values not directly "
101 "defined by a seq.initial op not yet supported");
102 return signalPassFailure();
103 }
104 if (auto constantOp = circt::seq::unwrapImmutableValue(initVal)
105 .getDefiningOp<hw::ConstantOp>()) {
106 // Fetch value from constant op - leave removing the dead op to
107 // DCE
108 initState = constantOp.getValueAttr();
109 } else {
110 regOp.emitError("registers with initial values not directly "
111 "defined by a hw.constant op in a seq.initial op "
112 "not yet supported");
113 return signalPassFailure();
114 }
115 }
116 Twine regName = regOp.getName() && !(regOp.getName().value().empty())
117 ? regOp.getName().value()
118 : "reg_" + Twine(numRegs);
119
120 if (failed(externalizeReg(module, op, regName, regOp.getClk(),
121 initState, regOp.getReset(), false,
122 regOp.getResetValue(), regOp.getInput()))) {
123 return signalPassFailure();
124 }
125 regOp->erase();
126 ++numRegs;
127 return;
128 }
129 if (auto regOp = dyn_cast<seq::FirRegOp>(op)) {
130 mlir::Attribute initState = {};
131 if (auto preset = regOp.getPreset()) {
132 // Get preset value as initState
133 initState = mlir::IntegerAttr::get(
134 mlir::IntegerType::get(&getContext(), preset->getBitWidth()),
135 *preset);
136 }
137 Twine regName = regOp.getName().empty() ? "reg_" + Twine(numRegs)
138 : regOp.getName();
139
140 if (failed(externalizeReg(module, op, regName, regOp.getClk(),
141 initState, regOp.getReset(),
142 regOp.getIsAsync(), regOp.getResetValue(),
143 regOp.getNext()))) {
144 return signalPassFailure();
145 }
146 regOp->erase();
147 ++numRegs;
148 return;
149 }
150 if (auto instanceOp = dyn_cast<InstanceOp>(op)) {
151 OpBuilder builder(instanceOp);
152 auto newInputs =
153 addedInputs[instanceOp.getModuleNameAttr().getAttr()];
154 auto newInputNames =
155 addedInputNames[instanceOp.getModuleNameAttr().getAttr()];
156 auto newOutputs =
157 addedOutputs[instanceOp.getModuleNameAttr().getAttr()];
158 auto newOutputNames =
159 addedOutputNames[instanceOp.getModuleNameAttr().getAttr()];
160 addedInputs[module.getSymNameAttr()].append(newInputs);
161 addedInputNames[module.getSymNameAttr()].append(newInputNames);
162 addedOutputs[module.getSymNameAttr()].append(newOutputs);
163 addedOutputNames[module.getSymNameAttr()].append(newOutputNames);
164 initialValues[module.getSymNameAttr()].append(
165 initialValues[instanceOp.getModuleNameAttr().getAttr()]);
166 SmallVector<Attribute> argNames(
167 instanceOp.getInputNames().getValue());
168 SmallVector<Attribute> resultNames(
169 instanceOp.getOutputNames().getValue());
170
171 for (auto [input, name] : zip_equal(newInputs, newInputNames)) {
172 instanceOp.getInputsMutable().append(
173 module.appendInput(name, input).second);
174 argNames.push_back(name);
175 }
176 for (auto outputName : newOutputNames) {
177 resultNames.push_back(outputName);
178 }
179 SmallVector<Type> resTypes(instanceOp->getResultTypes());
180 resTypes.append(newOutputs);
181 auto newInst = InstanceOp::create(
182 builder, instanceOp.getLoc(), resTypes,
183 instanceOp.getInstanceNameAttr(), instanceOp.getModuleNameAttr(),
184 instanceOp.getInputs(), builder.getArrayAttr(argNames),
185 builder.getArrayAttr(resultNames), instanceOp.getParametersAttr(),
186 instanceOp.getInnerSymAttr(), instanceOp.getDoNotPrintAttr());
187 for (auto [output, name] :
188 zip(newInst->getResults().take_back(newOutputs.size()),
189 newOutputNames))
190 module.appendOutput(name, output);
191 numRegs += newInputs.size();
192 instanceOp.replaceAllUsesWith(
193 newInst.getResults().take_front(instanceOp->getNumResults()));
194 instanceGraph.replaceInstance(instanceOp, newInst);
195 instanceOp->erase();
196 return;
197 }
198 });
199
200 module->setAttr(
201 "num_regs",
202 IntegerAttr::get(IntegerType::get(&getContext(), 32), numRegs));
203
204 module->setAttr("initial_values",
205 ArrayAttr::get(&getContext(),
206 initialValues[module.getSymNameAttr()]));
207 }
208 }
209}
210
211LogicalResult ExternalizeRegistersPass::externalizeReg(
212 HWModuleOp module, Operation *op, Twine regName, Value clock,
213 Attribute initState, Value reset, bool isAsync, Value resetValue,
214 Value next) {
215 if (!isa<BlockArgument>(clock)) {
216 op->emitError("only clocks directly given as block arguments "
217 "are supported");
218 return failure();
219 }
220
221 OpBuilder builder(op);
222 auto result = op->getResult(0);
223 auto regType = result.getType();
224
225 // If there's no initial value just add a unit attribute to maintain
226 // one-to-one correspondence with module ports
227 initialValues[module.getSymNameAttr()].push_back(
228 initState ? initState : mlir::UnitAttr::get(&getContext()));
229
230 StringAttr newInputName(builder.getStringAttr(regName + "_state")),
231 newOutputName(builder.getStringAttr(regName + "_next"));
232 addedInputs[module.getSymNameAttr()].push_back(regType);
233 addedInputNames[module.getSymNameAttr()].push_back(newInputName);
234 addedOutputs[module.getSymNameAttr()].push_back(next.getType());
235 addedOutputNames[module.getSymNameAttr()].push_back(newOutputName);
236
237 // Replace the register with newInput and newOutput
238 auto newInput = module.appendInput(newInputName, regType).second;
239 result.replaceAllUsesWith(newInput);
240 if (reset) {
241 if (isAsync) {
242 // Async reset
243 op->emitError("registers with an async reset are not yet supported");
244 return failure();
245 }
246 // Sync reset
247 auto mux = comb::MuxOp::create(builder, op->getLoc(), regType, reset,
248 resetValue, next);
249 module.appendOutput(newOutputName, mux);
250 } else {
251 // No reset
252 module.appendOutput(newOutputName, next);
253 }
254
255 return success();
256}
This is a Node in the InstanceGraph.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
Value unwrapImmutableValue(mlir::TypedValue< seq::ImmutableType > immutableVal)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition hw.py:1