CIRCT 20.0.0git
Loading...
Searching...
No Matches
MaterializeFSM.cpp
Go to the documentation of this file.
1//===- MaterializeCalyxToFSM.cpp - FSM Materialization 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//
9// Contains the definitions of the FSM materialization pass.
10//
11//===----------------------------------------------------------------------===//
12
16#include "circt/Support/LLVM.h"
17#include "mlir/IR/BuiltinTypes.h"
18#include "mlir/IR/OperationSupport.h"
19#include "mlir/Pass/Pass.h"
20#include "llvm/ADT/STLExtras.h"
21
22namespace circt {
23#define GEN_PASS_DEF_MATERIALIZECALYXTOFSM
24#include "circt/Conversion/Passes.h.inc"
25} // namespace circt
26
27using namespace circt;
28using namespace calyx;
29using namespace mlir;
30using namespace fsm;
31
32namespace {
33
34struct MaterializeCalyxToFSMPass
35 : public circt::impl::MaterializeCalyxToFSMBase<MaterializeCalyxToFSMPass> {
36 void runOnOperation() override;
37
38 /// Assigns the 'fsm.output' operation of the provided 'state' to enabled the
39 /// set of provided groups. If 'topLevelDone' is set, also asserts the
40 /// top-level done signal.
41 void assignStateOutputOperands(OpBuilder &b, StateOp stateOp,
42 bool topLevelDone = false) {
43 SmallVector<Value> outputOperands;
44 auto &enabledGroups = stateEnables[stateOp];
45 for (StringAttr group : referencedGroups)
46 outputOperands.push_back(
47 getOrCreateConstant(b, APInt(1, enabledGroups.contains(group))));
48
49 assert(outputOperands.size() == machineOp.getNumArguments() - 1 &&
50 "Expected exactly one value for each uniquely referenced group in "
51 "this machine");
52 outputOperands.push_back(getOrCreateConstant(b, APInt(1, topLevelDone)));
53 stateOp.ensureOutput(b);
54 auto outputOp = stateOp.getOutputOp();
55 outputOp->setOperands(outputOperands);
56 }
57
58 /// Extends every `fsm.return` guard in the transitions of this state to also
59 /// include the provided set of 'doneGuards'. 'doneGuards' is passed by value
60 /// to allow the caller to provide additional done guards apart from group
61 /// enable-generated guards.
62 void assignStateTransitionGuard(OpBuilder &b, StateOp stateOp,
63 SmallVector<Value> doneGuards = {}) {
64 auto &enabledGroups = stateEnables[stateOp];
65 for (auto groupIt : llvm::enumerate(referencedGroups))
66 if (enabledGroups.contains(groupIt.value()))
67 doneGuards.push_back(machineOp.getArgument(groupIt.index()));
68
69 for (auto transition :
70 stateOp.getTransitions().getOps<fsm::TransitionOp>()) {
71
72 if (!transition.hasGuard() && doneGuards.empty())
73 continue;
74 transition.ensureGuard(b);
75 auto guardOp = transition.getGuardReturn();
76 llvm::SmallVector<Value> guards;
77 llvm::append_range(guards, doneGuards);
78 if (guardOp.getNumOperands() != 0)
79 guards.push_back(guardOp.getOperand());
80
81 if (guards.empty())
82 continue;
83
84 b.setInsertionPoint(guardOp);
85 Value guardConjunction;
86 if (guards.size() == 1)
87 guardConjunction = guards.front();
88 else
89 guardConjunction =
90 b.create<comb::AndOp>(transition.getLoc(), guards, false);
91 guardOp.setOperand(guardConjunction);
92 }
93 }
94
95 Value getOrCreateConstant(OpBuilder &b, APInt value) {
96 auto it = constants.find(value);
97 if (it != constants.end())
98 return it->second;
99
100 OpBuilder::InsertionGuard g(b);
101 b.setInsertionPointToStart(&machineOp.getBody().front());
102 auto constantOp = b.create<hw::ConstantOp>(machineOp.getLoc(), value);
103 constants[value] = constantOp;
104 return constantOp;
105 }
106
107 /// Maintain a set of all groups referenced within this fsm.machine.
108 /// Use a SetVector to ensure a deterministic ordering - strong assumptions
109 /// are placed on the order of referenced groups wrt. the top-level I/O
110 /// created for the group done/go signals.
111 SetVector<StringAttr> referencedGroups;
112
113 /// Maintain a relation between states and the groups which they enable.
114 DenseMap<fsm::StateOp, DenseSet<StringAttr>> stateEnables;
115
116 /// A handle to the machine under transformation.
117 MachineOp machineOp;
118
119 /// Constant cache.
120 DenseMap<APInt, Value> constants;
121
122 OpBuilder *b;
123
124 FSMStateNode *entryState;
125 FSMStateNode *exitState;
126
127 // Walks the machine and gathers the set of referenced groups and SSA values.
128 void walkMachine();
129
130 // Creates the top-level group go/done I/O for the machine.
131 void materializeGroupIO();
132
133 // Add attributes to the machine op to indicate which in/out ports are
134 // associated with group activations and which are additional inputs to the
135 // FSM.
136 void assignAttributes();
137};
138
139} // end anonymous namespace
140
141void MaterializeCalyxToFSMPass::walkMachine() {
142 // Walk the states of the machine and gather the relation between states and
143 // the groups which they enable as well as the set of all enabled states.
144 for (auto stateOp : machineOp.getOps<fsm::StateOp>()) {
145 for (auto enableOp : llvm::make_early_inc_range(
146 stateOp.getOutput().getOps<calyx::EnableOp>())) {
147 auto groupName = enableOp.getGroupNameAttr().getAttr();
148 stateEnables[stateOp].insert(groupName);
149 referencedGroups.insert(groupName);
150 // Erase the enable op now that we've recorded the information.
151 enableOp.erase();
152 }
153 }
154}
155
156void MaterializeCalyxToFSMPass::materializeGroupIO() {
157 // Materialize the top-level I/O ports of the fsm.machine. We add an in- and
158 // output for every unique group referenced within the machine, as well as an
159 // additional in- and output to represent the top-level "go" input and "done"
160 // output ports.
161 SmallVector<Type> ioTypes = SmallVector<Type>(
162 referencedGroups.size() + /*top-level go/done*/ 1, b->getI1Type());
163 size_t nGroups = ioTypes.size() - 1;
164 machineOp.setType(b->getFunctionType(ioTypes, ioTypes));
165 assert(machineOp.getBody().getNumArguments() == 0 &&
166 "expected no inputs to the FSM");
167 machineOp.getBody().addArguments(
168 ioTypes, SmallVector<Location, 4>(ioTypes.size(), b->getUnknownLoc()));
169
170 // Build output assignments and transition guards in every state. We here
171 // assume that the ordering of states in referencedGroups is fixed and
172 // deterministic, since it is used as an analogue for port I/O ordering.
173 for (auto stateOp : machineOp.getOps<fsm::StateOp>()) {
174 assignStateOutputOperands(*b, stateOp,
175 /*topLevelDone=*/false);
176 assignStateTransitionGuard(*b, stateOp);
177 }
178
179 // Assign top-level go guard in the transition state.
180 size_t topLevelGoIdx = nGroups;
181 assignStateTransitionGuard(*b, entryState->getState(),
182 {machineOp.getArgument(topLevelGoIdx)});
183
184 // Assign top-level done in the exit state.
185 assignStateOutputOperands(*b, exitState->getState(),
186 /*topLevelDone=*/true);
187}
188
189void MaterializeCalyxToFSMPass::assignAttributes() {
190 // sGroupDoneInputs is a mapping from group name to the index of the
191 // corresponding done input port.
192 llvm::SmallVector<NamedAttribute> groupDoneInputs;
193 for (size_t i = 0; i < referencedGroups.size(); ++i)
194 groupDoneInputs.push_back({referencedGroups[i], b->getI64IntegerAttr(i)});
195 machineOp->setAttr(calyxToFSM::sGroupDoneInputs,
196 b->getDictionaryAttr(groupDoneInputs));
197
198 // sGroupGoOutputs is a mapping from group name to the index of the
199 // corresponding go output port.
200 llvm::SmallVector<NamedAttribute> groupGoOutputs;
201 for (size_t i = 0; i < referencedGroups.size(); ++i)
202 groupGoOutputs.push_back({referencedGroups[i], b->getI64IntegerAttr(i)});
203 machineOp->setAttr(calyxToFSM::sGroupGoOutputs,
204 b->getDictionaryAttr(groupGoOutputs));
205
206 // Assign top level go/done attributes
207 machineOp->setAttr(calyxToFSM::sFSMTopLevelGoIndex,
208 b->getI64IntegerAttr(referencedGroups.size()));
209 machineOp->setAttr(calyxToFSM::sFSMTopLevelDoneIndex,
210 b->getI64IntegerAttr(referencedGroups.size()));
211}
212
213void MaterializeCalyxToFSMPass::runOnOperation() {
214 ComponentOp component = getOperation();
215 auto *ctx = &getContext();
216 auto builder = OpBuilder(ctx);
217 b = &builder;
218 auto controlOp = component.getControlOp();
219 machineOp =
220 dyn_cast_or_null<fsm::MachineOp>(controlOp.getBodyBlock()->front());
221 if (!machineOp) {
222 controlOp.emitOpError()
223 << "expected an 'fsm.machine' operation as the top-level operation "
224 "within the control region of this component.";
225 signalPassFailure();
226 return;
227 }
228
229 // Ensure a well-formed FSM.
230 auto graph = FSMGraph(machineOp);
231 entryState = graph.lookup(b->getStringAttr(calyxToFSM::sEntryStateName));
232 exitState = graph.lookup(b->getStringAttr(calyxToFSM::sExitStateName));
233
234 if (!(entryState && exitState)) {
235 machineOp.emitOpError()
236 << "Expected an '" << calyxToFSM::sEntryStateName << "' and '"
237 << calyxToFSM::sExitStateName << "' state to be present in the FSM.";
238 signalPassFailure();
239 return;
240 }
241
242 walkMachine();
243 materializeGroupIO();
244 assignAttributes();
245}
246
247std::unique_ptr<mlir::Pass> circt::createMaterializeCalyxToFSMPass() {
248 return std::make_unique<MaterializeCalyxToFSMPass>();
249}
assert(baseType &&"element must be base type")
static constexpr std::string_view sGroupDoneInputs
Definition CalyxToFSM.h:37
static constexpr std::string_view sFSMTopLevelGoIndex
Definition CalyxToFSM.h:42
static constexpr std::string_view sExitStateName
Definition CalyxToFSM.h:36
static constexpr std::string_view sFSMTopLevelDoneIndex
Definition CalyxToFSM.h:44
static constexpr std::string_view sEntryStateName
Definition CalyxToFSM.h:35
static constexpr std::string_view sGroupGoOutputs
Definition CalyxToFSM.h:39
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createMaterializeCalyxToFSMPass()
Definition fsm.py:1