13 #include "../PassDetail.h"
21 #include "mlir/IR/BuiltinTypes.h"
22 #include "mlir/IR/OperationSupport.h"
23 #include "llvm/ADT/STLExtras.h"
25 using namespace circt;
26 using namespace calyx;
32 struct CalyxRemoveGroupsFromFSM
33 :
public CalyxRemoveGroupsFromFSMBase<CalyxRemoveGroupsFromFSM> {
34 void runOnOperation()
override;
40 LogicalResult outlineMachine();
58 ComponentOp componentOp;
64 DenseMap<StringAttr, Value> groupGoSignals;
68 DenseMap<StringAttr, calyx::WireLibOp> groupDoneWires;
74 auto loc = componentOp.getLoc();
75 for (
auto group : componentOp.getWiresOp().getOps<GroupOp>()) {
76 auto groupGo = group.getGoOp();
79 <<
"This pass does not need `calyx.group_go` operations.";
81 auto groupDone = group.getDoneOp();
83 return emitError(loc) <<
"Group " << group.getSymName()
84 <<
" does not have a `calyx.group_done` operation";
87 auto fsmGroupGo = groupGoSignals.find(group.getSymNameAttr());
88 assert(fsmGroupGo != groupGoSignals.end() &&
89 "Could not find FSM go signal for group");
95 auto doneWireIt = groupDoneWires.find(group.getSymNameAttr());
96 assert(doneWireIt != groupDoneWires.end() &&
97 "Could not find FSM done backedge for group");
98 auto doneWire = doneWireIt->second;
100 b->setInsertionPointToEnd(componentOp.getWiresOp().getBodyBlock());
101 b->create<calyx::AssignOp>(loc, doneWire.getIn(), groupDone.getSrc(),
102 groupDone.getGuard());
111 auto &wiresRegion = componentOp.getWiresOp().getRegion();
112 auto &wireBlocks = wiresRegion.getBlocks();
113 auto lastBlock = wiresRegion.end();
116 wiresRegion.walk([&](GroupOp group) {
117 wireBlocks.splice(lastBlock, group.getRegion().getBlocks());
122 auto firstBlock = wireBlocks.begin();
123 for (
auto it = firstBlock, e = lastBlock; it != e; ++it) {
124 if (it == firstBlock)
126 firstBlock->getOperations().splice(firstBlock->end(), it->getOperations());
130 while (&wiresRegion.front() != &wiresRegion.back())
131 wiresRegion.back().erase();
134 LogicalResult CalyxRemoveGroupsFromFSM::outlineMachine() {
138 llvm::MapVector<Value, SmallVector<Operation *>> referencedValues;
139 machineOp.walk([&](Operation *op) {
140 for (
auto &operand : op->getOpOperands()) {
141 if (auto barg = operand.get().dyn_cast<BlockArgument>()) {
142 if (barg.getOwner()->getParentOp() == machineOp)
146 referencedValues[operand.get()].push_back(op);
148 auto *defOp = operand.get().getDefiningOp();
149 auto machineOpParent = defOp->getParentOfType<MachineOp>();
150 if (machineOpParent && machineOpParent == machineOp)
153 referencedValues[operand.get()].push_back(op);
160 DenseMap<Value, size_t> ssaInputIndices;
161 auto machineOutputTypes = machineOp.getFunctionType().getResults();
162 auto currentInputs = machineOp.getFunctionType().getInputs();
163 llvm::SmallVector<Type> machineInputTypes(currentInputs);
165 for (
auto &[value, users] : referencedValues) {
166 ssaInputIndices[value] = machineOp.getBody().getNumArguments();
167 auto t = value.getType();
168 auto arg = machineOp.getBody().addArgument(t, b->getUnknownLoc());
169 machineInputTypes.push_back(t);
170 for (
auto *user : users) {
171 for (
auto &operand : user->getOpOperands()) {
172 if (operand.get() == value)
178 machineOp.setType(b->getFunctionType(machineInputTypes, machineOutputTypes));
181 machineOp->moveBefore(componentOp);
182 size_t nMachineInputs = machineOp.getBody().getNumArguments();
186 auto groupDoneInputsAttr =
188 auto groupGoOutputsAttr =
190 if (!groupDoneInputsAttr || !groupGoOutputsAttr)
191 return emitError(machineOp.getLoc())
194 <<
" attribute. Was --materialize-calyx-to-fsm run before "
197 b->setInsertionPointToStart(&componentOp.getBody().front());
201 DenseMap<size_t, Value> fsmInputMap;
204 for (
auto &namedAttr : groupDoneInputsAttr.getValue()) {
205 auto name = namedAttr.getName();
206 auto idx = namedAttr.getValue().cast<IntegerAttr>();
207 auto inputIdx = idx.cast<IntegerAttr>().
getInt();
208 if (fsmInputMap.count(inputIdx))
209 return emitError(machineOp.getLoc())
210 <<
"MachineOp has duplicate input index " << idx;
213 b->setInsertionPointToStart(&componentOp.getBody().front());
214 auto groupDoneWire = b->create<calyx::WireLibOp>(
215 componentOp.getLoc(), name.str() +
"_done", b->getI1Type());
216 fsmInputMap[inputIdx] = groupDoneWire.getOut();
217 groupDoneWires[name] = groupDoneWire;
221 auto topLevelGoAttr =
224 return emitError(machineOp.getLoc())
227 fsmInputMap[topLevelGoAttr.getInt()] = componentOp.getGoPort();
229 auto topLevelDoneAttr =
231 if (!topLevelDoneAttr)
232 return emitError(machineOp.getLoc())
237 for (
auto [value, idx] : ssaInputIndices) {
238 if (fsmInputMap.count(idx))
239 return emitError(machineOp.getLoc())
240 <<
"MachineOp has duplicate input index " << idx;
241 fsmInputMap[idx] = value;
244 if (fsmInputMap.size() != nMachineInputs)
245 return emitError(machineOp.getLoc())
246 <<
"MachineOp has " << nMachineInputs
247 <<
" inputs, but only recorded " << fsmInputMap.size()
248 <<
" inputs. This either means that --materialize-calyx-to-fsm "
249 "failed or that there is a mismatch in the MachineOp attributes.";
252 llvm::SmallVector<Value> fsmInputs;
253 for (
size_t idx = 0; idx < nMachineInputs; ++idx) {
254 auto it = fsmInputMap.find(idx);
255 assert(it != fsmInputMap.end() &&
"Missing FSM input index");
256 fsmInputs.push_back(it->second);
260 auto clkPort = componentOp.getClkPort();
261 auto clk = b->create<seq::ToClockOp>(clkPort.getLoc(), clkPort);
262 auto fsmInstance = b->create<fsm::HWInstanceOp>(
263 machineOp.getLoc(), machineOutputTypes, b->getStringAttr(
"controller"),
264 machineOp.getSymNameAttr(), fsmInputs,
clk, componentOp.getResetPort());
267 for (
auto namedAttr : groupGoOutputsAttr.getValue()) {
268 auto name = namedAttr.getName();
269 auto idx = namedAttr.getValue().cast<IntegerAttr>().
getInt();
270 groupGoSignals[name] = fsmInstance.getResult(idx);
274 b->setInsertionPointToEnd(componentOp.getWiresOp().getBodyBlock());
275 b->create<calyx::AssignOp>(machineOp.getLoc(), componentOp.getDonePort(),
276 fsmInstance.getResult(topLevelDoneAttr.getInt()));
281 void CalyxRemoveGroupsFromFSM::runOnOperation() {
282 componentOp = getOperation();
283 auto *ctx = componentOp.getContext();
285 builder.setInsertionPointToStart(&componentOp.getBody().front());
288 bb = &backedgeBuilder;
291 auto machineOps = componentOp.getControlOp().getOps<fsm::MachineOp>();
292 if (std::distance(machineOps.begin(), machineOps.end()) != 1) {
293 emitError(componentOp.getLoc())
294 <<
"Expected exactly one fsm.MachineOp in the control op";
298 machineOp = *machineOps.begin();
309 return std::make_unique<CalyxRemoveGroupsFromFSM>();
assert(baseType &&"element must be base type")
static std::optional< APInt > getInt(Value value)
Helper to convert a value to a constant integer if it is one.
static void modifyGroupOperations(ComponentOp component)
Makes several modifications to the operations of a GroupOp:
void inlineGroups(ComponentOp component)
Inlines each group in the WiresOp.
Instantiate one of these and use it to build typed backedges.
static constexpr std::string_view sGroupDoneInputs
static constexpr std::string_view sFSMTopLevelGoIndex
static constexpr std::string_view sFSMTopLevelDoneIndex
static constexpr std::string_view sGroupGoOutputs
static void updateGroupAssignmentGuards(OpBuilder &builder, GroupOp &group, Op &op)
Updates the guard of each assignment within a group with op.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
std::unique_ptr< mlir::Pass > createRemoveGroupsFromFSMPass()