17 #include "mlir/IR/BuiltinTypes.h"
18 #include "mlir/IR/OperationSupport.h"
19 #include "mlir/IR/PatternMatch.h"
20 #include "llvm/ADT/TypeSwitch.h"
24 #define GEN_PASS_DEF_COMPILECONTROL
25 #include "circt/Dialect/Calyx/CalyxPasses.h.inc"
29 using namespace circt;
30 using namespace calyx;
36 APInt apNumStates(64, numStates);
37 size_t log2 = apNumStates.ceilLogBase2();
38 return log2 > 1 ? log2 : 1;
44 void dispatch(Operation *op, ComponentOp component) {
45 TypeSwitch<Operation *>(op)
46 .template Case<SeqOp, EnableOp>(
47 [&](
auto opNode) { visit(opNode, component); })
49 op->emitError() <<
"Operation '" << op->getName()
50 <<
"' not supported for control compilation";
55 void visit(SeqOp seqOp, ComponentOp &component);
56 void visit(EnableOp, ComponentOp &) {
71 auto wires = component.getWiresOp();
72 Block *wiresBody = wires.getBodyBlock();
74 auto &seqOps =
seq.getBodyBlock()->getOperations();
75 if (!llvm::all_of(seqOps, [](
auto &&op) {
return isa<EnableOp>(op); })) {
76 seq.emitOpError(
"should only contain EnableOps in this pass.");
84 OpBuilder builder(component->getRegion(0));
87 Value fsmIn = fsmRegister.getIn();
88 Value fsmWriteEn = fsmRegister.getWriteEn();
89 Value fsmOut = fsmRegister.getOut();
91 builder.setInsertionPointToStart(wiresBody);
92 auto oneConstant =
createConstant(wires.getLoc(), builder, component, 1, 1);
95 builder.setInsertionPointToEnd(wiresBody);
97 builder.create<GroupOp>(wires->getLoc(), builder.getStringAttr(
"seq"));
100 auto &symTable = am.getChildAnalysis<SymbolTable>(wires);
101 symTable.insert(seqGroup);
104 SmallVector<Attribute, 8> compiledGroups;
106 seq.walk([&](EnableOp enable) {
107 StringRef groupName = enable.getGroupName();
108 compiledGroups.push_back(
110 auto groupOp = symTable.lookup<GroupOp>(groupName);
112 builder.setInsertionPoint(groupOp);
113 auto fsmCurrentState =
createConstant(wires->getLoc(), builder, component,
114 fsmBitWidth, fsmIndex);
118 auto guard = groupOp.getDoneOp().getGuard();
119 Value source = groupOp.getDoneOp().getSrc();
120 auto doneOpValue = !guard ? source
122 wires->getLoc(), guard, source,
false);
129 builder.create<comb::ICmpOp>(wires->getLoc(), comb::ICmpPredicate::eq,
130 fsmOut, fsmCurrentState,
false);
133 builder.create<
comb::AndOp>(wires->getLoc(), eqCmp, notDone,
false);
137 builder.setInsertionPoint(seqGroup);
138 auto groupDoneGuard =
139 builder.create<
comb::AndOp>(wires->getLoc(), eqCmp, doneOpValue,
false);
142 auto goOp = groupOp.getGoOp();
143 assert(goOp &&
"The Go Insertion pass should be run before this.");
144 goOp->setOperands({oneConstant, groupGoGuard});
147 fsmNextState =
createConstant(wires->getLoc(), builder, component,
148 fsmBitWidth, fsmIndex + 1);
149 builder.setInsertionPointToEnd(seqGroup.getBodyBlock());
150 builder.create<AssignOp>(wires->getLoc(), fsmIn, fsmNextState,
152 builder.create<AssignOp>(wires->getLoc(), fsmWriteEn, oneConstant,
160 builder.setInsertionPoint(seqGroup);
161 auto isFinalState = builder.create<comb::ICmpOp>(
162 wires->getLoc(), comb::ICmpPredicate::eq, fsmOut, fsmNextState,
false);
165 builder.setInsertionPointToEnd(seqGroup.getBodyBlock());
166 builder.create<GroupDoneOp>(seqGroup->getLoc(), oneConstant, isFinalState);
170 builder.setInsertionPointToEnd(wiresBody);
172 createConstant(wires->getLoc(), builder, component, fsmBitWidth, 0);
173 builder.create<AssignOp>(wires->getLoc(), fsmIn, zeroConstant, isFinalState);
174 builder.create<AssignOp>(wires->getLoc(), fsmWriteEn, oneConstant,
178 builder.setInsertionPoint(
seq);
179 builder.create<EnableOp>(
180 seq->getLoc(), seqGroup.getSymName(),
188 struct CompileControlPass
189 :
public circt::calyx::impl::CompileControlBase<CompileControlPass> {
190 void runOnOperation()
override;
195 void CompileControlPass::runOnOperation() {
196 ComponentOp component = getOperation();
198 component.getControlOp().walk(
199 [&](Operation *op) { compileControlVisitor.dispatch(op, component); });
203 component.getWiresOp().walk([&](UndefinedOp op) { op->erase(); });
207 return std::make_unique<CompileControlPass>();
assert(baseType &&"element must be base type")
static size_t getNecessaryBitWidth(size_t numStates)
Given some number of states, returns the necessary bit width TODO(Calyx): Probably a better built-in ...
void visit(EnableOp, ComponentOp &)
CompileControlVisitor(AnalysisManager am)
void visit(SeqOp seqOp, ComponentOp &component)
Generates a latency-insensitive FSM to realize a sequential operation.
void dispatch(Operation *op, ComponentOp component)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
std::unique_ptr< mlir::Pass > createCompileControlPass()
hw::ConstantOp createConstant(Location loc, OpBuilder &builder, ComponentOp component, size_t width, size_t value)
A helper function to create constants in the HW dialect.
calyx::RegisterOp createRegister(Location loc, OpBuilder &builder, ComponentOp component, size_t width, Twine prefix)
Creates a RegisterOp, with input and output port bit widths defined by width.
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.