14 #include "../PassDetail.h"
19 #include "mlir/IR/Builders.h"
20 #include "llvm/ADT/TypeSwitch.h"
23 using namespace circt;
24 using namespace pipeline;
30 OpBuilder &
builder,
bool clockGateRegs)
32 clockGateRegs(clockGateRegs) {
33 parentClk = pipeline.getClock();
34 parentRst = pipeline.getReset();
35 parentModule = pipeline->getParentOfType<hw::HWModuleOp>();
39 virtual LogicalResult
run() = 0;
55 llvm::SmallVector<Value>
regs;
67 llvm::ArrayRef<Attribute> inputNames = {}) = 0;
70 llvm::ArrayRef<Attribute> registerNames,
71 size_t stageIndex = -1) {
73 auto *terminator = stage->getTerminator();
76 for (
auto &op : llvm::make_early_inc_range(*stage)) {
77 if (&op == terminator)
80 if (
auto latencyOp = dyn_cast<LatencyOp>(op)) {
85 Block *latencyOpBody = latencyOp.getBodyBlock();
87 llvm::make_early_inc_range(latencyOpBody->without_terminator()))
88 innerOp.moveBefore(
builder.getInsertionBlock(),
90 latencyOp.replaceAllUsesWith(
91 latencyOpBody->getTerminator()->getOperands());
94 op.moveBefore(
builder.getInsertionBlock(),
builder.getInsertionPoint());
98 auto loc = terminator->getLoc();
100 auto getOrSetNotStalled = [&]() {
109 StageKind stageKind = pipeline.getStageKind(stageIndex);
111 StringAttr validSignalName =
112 builder.getStringAttr(getStagePrefix(stageIndex).strref() +
"_valid");
114 case StageKind::Continuous:
116 case StageKind::NonStallable:
119 case StageKind::Stallable:
121 builder.create<comb::AndOp>(loc, args.
enable, getOrSetNotStalled());
122 stageValid.getDefiningOp()->setAttr(
"sv.namehint", validSignalName);
124 case StageKind::Runoff:
125 assert(args.
lnsEn &&
"Expected an LNS signal if this was a runoff stage");
126 stageValid =
builder.create<comb::AndOp>(
128 builder.create<comb::OrOp>(loc, args.
lnsEn, getOrSetNotStalled()));
129 stageValid.getDefiningOp()->setAttr(
"sv.namehint", validSignalName);
134 auto stageOp = dyn_cast<StageOp>(terminator);
136 assert(isa<ReturnOp>(terminator) &&
"expected ReturnOp");
141 rets.
valid = stageValid;
145 assert(registerNames.size() == stageOp.getRegisters().size() &&
146 "register names and registers must be the same size");
148 bool isStallablePipeline = stageKind != StageKind::Continuous;
149 Value notStalledClockGate;
150 if (this->clockGateRegs) {
152 notStalledClockGate =
builder.create<seq::ClockGateOp>(
153 loc, args.
clock, stageValid, Value(),
157 for (
auto it : llvm::enumerate(stageOp.getRegisters())) {
158 auto regIdx = it.index();
159 auto regIn = it.value();
161 StringAttr regName = registerNames[regIdx].cast<StringAttr>();
163 if (this->clockGateRegs) {
165 Value currClockGate = notStalledClockGate;
166 for (
auto hierClockGateEnable : stageOp.getClockGatesForReg(regIdx)) {
168 currClockGate =
builder.create<seq::ClockGateOp>(
169 loc, currClockGate, hierClockGateEnable,
173 dataReg =
builder.create<seq::CompRegOp>(stageOp->getLoc(), regIn,
174 currClockGate, regName);
179 if (isStallablePipeline) {
180 dataReg =
builder.create<seq::CompRegClockEnabledOp>(
181 stageOp->getLoc(), regIn, args.
clock, stageValid, regName);
183 dataReg =
builder.create<seq::CompRegOp>(stageOp->getLoc(), regIn,
184 args.
clock, regName);
187 rets.
regs.push_back(dataReg);
190 rets.
valid = stageValid;
191 if (stageKind == StageKind::NonStallable)
211 bool withPipelinePrefix,
213 StringAttr pipelineName;
214 if (withPipelinePrefix)
215 pipelineName = getPipelineBaseName();
217 if (
auto stageOp = dyn_cast<StageOp>(stageTerminator)) {
219 std::string assignedRegName, assignedOutName, assignedInName;
220 for (
size_t regi = 0; regi < stageOp.getRegisters().
size(); ++regi) {
221 if (
auto regName = stageOp.getRegisterName(regi)) {
222 assignedRegName = regName.str();
223 assignedOutName = assignedRegName +
"_out";
224 assignedInName = assignedRegName +
"_in";
227 (
"stage" + Twine(stageIndex) +
"_reg" + Twine(regi)).str();
228 assignedOutName = (
"out" + Twine(regi)).str();
229 assignedInName = (
"in" + Twine(regi)).str();
233 assignedRegName = pipelineName.str() +
"_" + assignedRegName;
234 assignedOutName = pipelineName.str() +
"_" + assignedOutName;
235 assignedInName = pipelineName.str() +
"_" + assignedInName;
238 egressNames.
regNames.push_back(
builder.getStringAttr(assignedRegName));
239 egressNames.
outNames.push_back(
builder.getStringAttr(assignedOutName));
240 egressNames.
inNames.push_back(
builder.getStringAttr(assignedInName));
244 for (
size_t passi = 0; passi < stageOp.getPassthroughs().size();
246 if (
auto passName = stageOp.getPassthroughName(passi)) {
247 assignedOutName = (passName.strref() +
"_out").str();
248 assignedInName = (passName.strref() +
"_in").str();
250 assignedOutName = (
"pass" + Twine(passi)).str();
251 assignedInName = (
"pass" + Twine(passi)).str();
255 assignedOutName = pipelineName.str() +
"_" + assignedOutName;
256 assignedInName = pipelineName.str() +
"_" + assignedInName;
259 egressNames.
outNames.push_back(
builder.getStringAttr(assignedOutName));
260 egressNames.
inNames.push_back(
builder.getStringAttr(assignedInName));
265 llvm::copy(pipeline.getOutputNames().getAsRange<StringAttr>(),
266 std::back_inserter(egressNames.
outNames));
277 if (
auto nameAttr = pipeline.getNameAttr())
279 return StringAttr::get(pipeline.getContext(),
"p" + Twine(pipelineID));
309 return builder.getStringAttr(pipelineName.strref() +
"_stage" +
313 LogicalResult
run()
override {
314 pipelineName = getPipelineBaseName();
317 for (
auto [outer, inner] :
318 llvm::zip(pipeline.getInputs(), pipeline.getInnerInputs()))
319 inner.replaceAllUsesWith(outer);
323 builder.setInsertionPoint(pipeline);
325 args.
data = pipeline.getInnerInputs();
326 args.
enable = pipeline.getGo();
327 args.
clock = pipeline.getClock();
328 args.
reset = pipeline.getReset();
329 args.
stall = pipeline.getStall();
330 if (failed(lowerStage(pipeline.getEntryStage(), args, 0)))
334 pipeline.getInnerClock().replaceAllUsesWith(pipeline.getClock());
335 pipeline.getInnerReset().replaceAllUsesWith(pipeline.getReset());
336 if (
auto stall = pipeline.getStall())
337 pipeline.getInnerStall().replaceAllUsesWith(stall);
346 llvm::ArrayRef<Attribute> = {})
override {
347 OpBuilder::InsertionGuard guard(
builder);
348 Operation *terminator = stage->getTerminator();
349 Location loc = terminator->getLoc();
351 if (stage != pipeline.getEntryStage()) {
353 for (
auto [vInput, vArg] :
354 llvm::zip(pipeline.getStageDataArgs(stage), args.
data))
355 vInput.replaceAllUsesWith(vArg);
364 StageKind stageKind = pipeline.getStageKind(stageIndex);
366 if (stageIndex == 0) {
367 stageEnabled = args.
enable;
369 auto stageRegPrefix = getStagePrefix(stageIndex);
370 auto enableRegName = (stageRegPrefix.strref() +
"_enable").str();
371 Value enableRegResetVal =
372 builder.create<hw::ConstantOp>(loc, APInt(1, 0,
false)).getResult();
375 case StageKind::Continuous:
377 case StageKind::NonStallable:
378 stageEnabled =
builder.create<seq::CompRegOp>(
382 case StageKind::Stallable:
383 stageEnabled =
builder.create<seq::CompRegClockEnabledOp>(
386 enableRegResetVal, enableRegName);
388 case StageKind::Runoff:
390 "Expected an LNS signal if this was a runoff stage");
391 stageEnabled =
builder.create<seq::CompRegClockEnabledOp>(
396 args.
reset, enableRegResetVal, enableRegName);
402 args.
enable = stageEnabled;
403 pipeline.getStageEnableSignal(stage).replaceAllUsesWith(stageEnabled);
406 auto nextStage = dyn_cast<StageOp>(terminator);
407 StageEgressNames egressNames;
409 getStageEgressNames(stageIndex, nextStage,
413 builder.setInsertionPoint(pipeline);
414 StageReturns stageRets =
415 emitStageBody(stage, args, egressNames.regNames, stageIndex);
419 SmallVector<Value> nextStageArgs;
420 llvm::append_range(nextStageArgs, stageRets.regs);
421 llvm::append_range(nextStageArgs, stageRets.passthroughs);
422 args.
enable = stageRets.valid;
423 if (stageRets.lnsEn) {
426 args.
lnsEn = stageRets.lnsEn;
428 args.
data = nextStageArgs;
429 return lowerStage(nextStage.getNextStage(), args, stageIndex + 1);
433 auto returnOp = cast<pipeline::ReturnOp>(stage->getTerminator());
434 llvm::SmallVector<Value> pipelineReturns;
435 llvm::append_range(pipelineReturns, returnOp.getInputs());
437 pipelineReturns.push_back(stageRets.valid);
438 pipeline.replaceAllUsesWith(pipelineReturns);
448 struct PipelineToHWPass :
public PipelineToHWBase<PipelineToHWPass> {
449 void runOnOperation()
override;
455 void runOnHWModule(hw::HWModuleOp mod);
458 void PipelineToHWPass::runOnOperation() {
459 for (
auto hwMod : getOperation().getOps<hw::HWModuleOp>())
460 runOnHWModule(hwMod);
463 void PipelineToHWPass::runOnHWModule(hw::HWModuleOp mod) {
464 OpBuilder
builder(&getContext());
469 size_t pipelinesSeen = 0;
471 llvm::make_early_inc_range(mod.getOps<ScheduledPipelineOp>())) {
485 return std::make_unique<PipelineToHWPass>();
assert(baseType &&"element must be base type")
static int64_t size(hw::ArrayType mType, capnp::schema::Field::Reader cField)
Returns the expected size of an array (capnp list) in 64-bit words.
StringAttr getStagePrefix(size_t stageIdx) override
LogicalResult run() override
FailureOr< StageReturns > lowerStage(Block *stage, StageArgs args, size_t stageIndex, llvm::ArrayRef< Attribute >={}) override
NOLINTNEXTLINE(misc-no-recursion)
virtual StringAttr getStagePrefix(size_t stageIdx)=0
StageReturns emitStageBody(Block *stage, StageArgs args, llvm::ArrayRef< Attribute > registerNames, size_t stageIndex=-1)
virtual ~PipelineLowering()=default
virtual LogicalResult run()=0
ScheduledPipelineOp pipeline
PipelineLowering(size_t pipelineID, ScheduledPipelineOp pipeline, OpBuilder &builder, bool clockGateRegs)
StringAttr getPipelineBaseName()
hw::HWModuleOp parentModule
void getStageEgressNames(size_t stageIndex, Operation *stageTerminator, bool withPipelinePrefix, StageEgressNames &egressNames)
virtual FailureOr< StageReturns > lowerStage(Block *stage, StageArgs args, size_t stageIndex, llvm::ArrayRef< Attribute > inputNames={})=0
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
std::unique_ptr< mlir::Pass > createPipelineToHWPass()
Create an SCF to Calyx conversion pass.
llvm::SmallVector< Attribute > inNames
llvm::SmallVector< Attribute > outNames
llvm::SmallVector< Attribute > regNames
llvm::SmallVector< Value > passthroughs
llvm::SmallVector< Value > regs