CIRCT 20.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
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
26using namespace mlir;
27using namespace circt;
28using namespace hw;
29using namespace igraph;
30
31namespace 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
40namespace {
41struct ExternalizeRegistersPass
42 : public circt::impl::ExternalizeRegistersBase<ExternalizeRegistersPass> {
43 using ExternalizeRegistersBase::ExternalizeRegistersBase;
44 void runOnOperation() override;
45};
46} // namespace
47
48void 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 constant op that defines the reset value in an initial
100 // block (if it exists)
101 if (!initVal.getDefiningOp<seq::InitialOp>()) {
102 regOp.emitError("registers with initial values not directly "
103 "defined by a seq.initial op not yet supported");
104 return signalPassFailure();
105 }
106 if (auto constantOp = circt::seq::unwrapImmutableValue(initVal)
107 .getDefiningOp<hw::ConstantOp>()) {
108 // Fetch value from constant op - leave removing the dead op to
109 // DCE
110 initState = constantOp.getValueAttr();
111 } else {
112 regOp.emitError("registers with initial values not directly "
113 "defined by a hw.constant op in a seq.initial op "
114 "not yet supported");
115 return signalPassFailure();
116 }
117 } else {
118 // If there's no initial value just add a unit attribute to maintain
119 // one-to-one correspondence with module ports
120 initState = mlir::UnitAttr::get(&getContext());
121 }
122 addedInputs[module.getSymNameAttr()].push_back(regOp.getType());
123 addedOutputs[module.getSymNameAttr()].push_back(
124 regOp.getInput().getType());
125 OpBuilder builder(regOp);
126 auto regName = regOp.getName();
127 StringAttr newInputName, newOutputName;
128 if (regName && !regName.value().empty()) {
129 newInputName = builder.getStringAttr(regName.value() + "_state");
130 newOutputName = builder.getStringAttr(regName.value() + "_input");
131 } else {
132 newInputName =
133 builder.getStringAttr("reg_" + Twine(numRegs) + "_state");
134 newOutputName =
135 builder.getStringAttr("reg_" + Twine(numRegs) + "_input");
136 }
137 addedInputNames[module.getSymNameAttr()].push_back(newInputName);
138 addedOutputNames[module.getSymNameAttr()].push_back(newOutputName);
139 initialValues[module.getSymNameAttr()].push_back(initState);
140
141 regOp.getResult().replaceAllUsesWith(
142 module.appendInput(newInputName, regOp.getType()).second);
143 module.appendOutput(newOutputName, regOp.getInput());
144 regOp->erase();
145 ++numRegs;
146 return;
147 }
148 if (auto instanceOp = dyn_cast<InstanceOp>(op)) {
149 OpBuilder builder(instanceOp);
150 auto newInputs =
151 addedInputs[instanceOp.getModuleNameAttr().getAttr()];
152 auto newInputNames =
153 addedInputNames[instanceOp.getModuleNameAttr().getAttr()];
154 auto newOutputs =
155 addedOutputs[instanceOp.getModuleNameAttr().getAttr()];
156 auto newOutputNames =
157 addedOutputNames[instanceOp.getModuleNameAttr().getAttr()];
158 addedInputs[module.getSymNameAttr()].append(newInputs);
159 addedInputNames[module.getSymNameAttr()].append(newInputNames);
160 addedOutputs[module.getSymNameAttr()].append(newOutputs);
161 addedOutputNames[module.getSymNameAttr()].append(newOutputNames);
162 initialValues[module.getSymNameAttr()].append(
163 initialValues[instanceOp.getModuleNameAttr().getAttr()]);
164 SmallVector<Attribute> argNames(
165 instanceOp.getInputNames().getValue());
166 SmallVector<Attribute> resultNames(
167 instanceOp.getOutputNames().getValue());
168
169 for (auto [input, name] : zip_equal(newInputs, newInputNames)) {
170 instanceOp.getInputsMutable().append(
171 module.appendInput(name, input).second);
172 argNames.push_back(name);
173 }
174 for (auto outputName : newOutputNames) {
175 resultNames.push_back(outputName);
176 }
177 SmallVector<Type> resTypes(instanceOp->getResultTypes());
178 resTypes.append(newOutputs);
179 auto newInst = builder.create<InstanceOp>(
180 instanceOp.getLoc(), resTypes, instanceOp.getInstanceNameAttr(),
181 instanceOp.getModuleNameAttr(), instanceOp.getInputs(),
182 builder.getArrayAttr(argNames), builder.getArrayAttr(resultNames),
183 instanceOp.getParametersAttr(), instanceOp.getInnerSymAttr(),
184 instanceOp.getDoNotPrintAttr());
185 for (auto [output, name] :
186 zip(newInst->getResults().take_back(newOutputs.size()),
187 newOutputNames))
188 module.appendOutput(name, output);
189 numRegs += newInputs.size();
190 instanceOp.replaceAllUsesWith(
191 newInst.getResults().take_front(instanceOp->getNumResults()));
192 instanceGraph.replaceInstance(instanceOp, newInst);
193 instanceOp->erase();
194 return;
195 }
196 });
197
198 module->setAttr(
199 "num_regs",
200 IntegerAttr::get(IntegerType::get(&getContext(), 32), numRegs));
201
202 module->setAttr("initial_values",
203 ArrayAttr::get(&getContext(),
204 initialValues[module.getSymNameAttr()]));
205 }
206 }
207}
This is a Node in the InstanceGraph.
Value unwrapImmutableValue(mlir::TypedValue< seq::ImmutableType > immutableVal)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition hw.py:1