9#include "../PassDetails.h"
19#include "mlir/Transforms/DialectConversion.h"
23#define GEN_PASS_DEF_LOWERESIPORTS
24#include "circt/Dialect/ESI/ESIPasses.h.inc"
37 auto attr = op->getAttrOfType<StringAttr>(attrName);
39 return attr.getValue();
53class ValidReady :
public ESISignalingStandad {
56 : ESISignalingStandad(converter, origPort), validPort(origPort),
57 readyPort(origPort) {}
59 void mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
60 SmallVectorImpl<Value> &newOperands,
61 ArrayRef<Backedge> newResults)
override;
62 void mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
63 SmallVectorImpl<Value> &newOperands,
64 ArrayRef<Backedge> newResults)
override;
67 void buildInputSignals()
override;
68 void buildOutputSignals()
override;
72 PortInfo validPort, readyPort, dataPort;
76class FIFO :
public ESISignalingStandad {
79 : ESISignalingStandad(converter, origPort) {}
81 void mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
82 SmallVectorImpl<Value> &newOperands,
83 ArrayRef<Backedge> newResults)
override;
84 void mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
85 SmallVectorImpl<Value> &newOperands,
86 ArrayRef<Backedge> newResults)
override;
89 void buildInputSignals()
override;
90 void buildOutputSignals()
override;
94 PortInfo rdenPort, emptyPort, dataPort;
98class ValidOnly :
public ESISignalingStandad {
101 : ESISignalingStandad(converter, origPort), validPort(origPort) {}
103 void mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
104 SmallVectorImpl<Value> &newOperands,
105 ArrayRef<Backedge> newResults)
override;
106 void mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
107 SmallVectorImpl<Value> &newOperands,
108 ArrayRef<Backedge> newResults)
override;
111 void buildInputSignals()
override;
112 void buildOutputSignals()
override;
122 FailureOr<std::unique_ptr<PortConversion>> build(
hw::PortInfo port)
override {
123 return llvm::TypeSwitch<Type, FailureOr<std::unique_ptr<PortConversion>>>(
126 -> FailureOr<std::unique_ptr<PortConversion>> {
128 ChannelSignaling signaling = chanTy.getSignaling();
129 if (signaling == ChannelSignaling::ValidReady)
130 return {std::make_unique<ValidReady>(converter, port)};
132 if (signaling == ChannelSignaling::FIFO)
133 return {std::make_unique<FIFO>(converter, port)};
135 if (signaling == ChannelSignaling::ValidOnly)
136 return {std::make_unique<ValidOnly>(converter, port)};
138 auto error = converter.getModule().emitOpError(
139 "encountered unknown signaling standard on port '")
140 << stringifyEnum(signaling) <<
"'";
149void ValidReady::buildInputSignals() {
150 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
158 Value
data = converter.createNewInput(
159 origPort, inSuffix, cast<esi::ChannelType>(origPort.type).getInner(),
162 converter.createNewInput(origPort, validSuffix + inSuffix, i1, validPort);
166 ImplicitLocOpBuilder
b(origPort.loc, body, body->begin());
169 auto wrap = WrapValidReadyOp::create(b, data, valid);
170 ready =
wrap.getReady();
173 body->getArgument(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
178 StringRef outSuffix =
180 converter.createNewOutput(origPort, readySuffix + outSuffix, i1, ready,
184void ValidReady::mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
185 SmallVectorImpl<Value> &newOperands,
186 ArrayRef<Backedge> newResults) {
187 auto unwrap = UnwrapValidReadyOp::create(b, inst->getLoc(),
188 inst->getOperand(origPort.argNum),
189 newResults[readyPort.argNum]);
190 newOperands[dataPort.argNum] =
unwrap.getRawOutput();
191 newOperands[validPort.argNum] =
unwrap.getValid();
194void ValidReady::buildOutputSignals() {
195 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
203 converter.createNewInput(origPort, readySuffix + inSuffix, i1, readyPort);
206 auto *terminator = body->getTerminator();
207 ImplicitLocOpBuilder
b(origPort.loc, terminator);
209 auto unwrap = UnwrapValidReadyOp::create(
210 b, terminator->getOperand(origPort.argNum), ready);
212 valid =
unwrap.getValid();
216 StringRef outSuffix =
220 converter.createNewOutput(origPort, outSuffix,
221 cast<esi::ChannelType>(origPort.type).getInner(),
223 converter.createNewOutput(origPort, validSuffix + outSuffix, i1, valid,
227void ValidReady::mapOutputSignals(OpBuilder &b, Operation *inst,
229 SmallVectorImpl<Value> &newOperands,
230 ArrayRef<Backedge> newResults) {
232 WrapValidReadyOp::create(b, inst->getLoc(), newResults[dataPort.argNum],
233 newResults[validPort.argNum]);
234 inst->getResult(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
235 newOperands[readyPort.argNum] =
wrap.getReady();
238void FIFO::buildInputSignals() {
239 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
240 auto chanTy = cast<ChannelType>(origPort.type);
248 StringRef outSuffix =
252 Value
data = converter.createNewInput(
253 origPort, inSuffix, cast<esi::ChannelType>(origPort.type).getInner(),
256 converter.createNewInput(origPort, emptySuffix + inSuffix, i1, emptyPort);
260 ImplicitLocOpBuilder
b(origPort.loc, body, body->begin());
263 auto wrap = WrapFIFOOp::create(b, ArrayRef<Type>({chanTy,
b.getI1Type()}),
265 rden =
wrap.getRden();
268 body->getArgument(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
271 converter.createNewOutput(origPort, rdenSuffix + outSuffix, i1, rden,
275void FIFO::mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
276 SmallVectorImpl<Value> &newOperands,
277 ArrayRef<Backedge> newResults) {
279 UnwrapFIFOOp::create(b, inst->getLoc(), inst->getOperand(origPort.argNum),
280 newResults[rdenPort.argNum]);
281 newOperands[dataPort.argNum] =
unwrap.getData();
282 newOperands[emptyPort.argNum] =
unwrap.getEmpty();
285void FIFO::buildOutputSignals() {
286 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
290 StringRef outSuffix =
297 converter.createNewInput(origPort, rdenSuffix + inSuffix, i1, rdenPort);
300 auto *terminator = body->getTerminator();
301 ImplicitLocOpBuilder
b(origPort.loc, terminator);
304 UnwrapFIFOOp::create(b, terminator->getOperand(origPort.argNum), rden);
310 converter.createNewOutput(origPort, outSuffix,
311 cast<esi::ChannelType>(origPort.type).getInner(),
313 converter.createNewOutput(origPort, emptySuffix + outSuffix, i1,
empty,
317void FIFO::mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
318 SmallVectorImpl<Value> &newOperands,
319 ArrayRef<Backedge> newResults) {
320 auto wrap = WrapFIFOOp::create(
321 b, inst->getLoc(), ArrayRef<Type>({origPort.type, b.getI1Type()}),
322 newResults[dataPort.argNum], newResults[emptyPort.argNum]);
323 inst->getResult(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
324 newOperands[rdenPort.argNum] =
wrap.getRden();
331void ValidOnly::buildInputSignals() {
332 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
340 Value
data = converter.createNewInput(
341 origPort, inSuffix, cast<esi::ChannelType>(origPort.type).getInner(),
344 converter.createNewInput(origPort, validSuffix + inSuffix, i1, validPort);
347 ImplicitLocOpBuilder
b(origPort.loc, body, body->begin());
348 auto wrap = WrapValidOnlyOp::create(b, data, valid);
349 body->getArgument(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
354void ValidOnly::mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
355 SmallVectorImpl<Value> &newOperands,
356 ArrayRef<Backedge> newResults) {
357 auto unwrap = UnwrapValidOnlyOp::create(b, inst->getLoc(),
358 inst->getOperand(origPort.argNum));
359 newOperands[dataPort.argNum] =
unwrap.getRawOutput();
360 newOperands[validPort.argNum] =
unwrap.getValid();
363void ValidOnly::buildOutputSignals() {
364 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
368 auto *terminator = body->getTerminator();
369 ImplicitLocOpBuilder
b(origPort.loc, terminator);
372 UnwrapValidOnlyOp::create(b, terminator->getOperand(origPort.argNum));
374 valid =
unwrap.getValid();
378 StringRef outSuffix =
382 converter.createNewOutput(origPort, outSuffix,
383 cast<esi::ChannelType>(origPort.type).getInner(),
385 converter.createNewOutput(origPort, validSuffix + outSuffix, i1, valid,
390void ValidOnly::mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
391 SmallVectorImpl<Value> &newOperands,
392 ArrayRef<Backedge> newResults) {
394 WrapValidOnlyOp::create(b, inst->getLoc(), newResults[dataPort.argNum],
395 newResults[validPort.argNum]);
396 inst->getResult(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
404struct ESIPortsPass :
public circt::esi::impl::LowerESIPortsBase<ESIPortsPass> {
405 void runOnOperation()
override;
408 bool updateFunc(HWModuleExternOp mod);
415void ESIPortsPass::runOnOperation() {
416 ModuleOp top = getOperation();
422 DenseMap<SymbolRefAttr, HWModuleExternOp> externModsMutated;
423 for (
auto mod : top.getOps<HWModuleExternOp>())
426 externModsMutated[FlatSymbolRefAttr::
get(mod)] = mod;
429 top.walk([&externModsMutated,
this](InstanceOp inst) {
430 auto mapIter = externModsMutated.find(inst.getModuleNameAttr());
431 if (mapIter != externModsMutated.end())
437 getAnalysis<circt::hw::InstanceGraph>();
439 for (
auto mod : top.getOps<HWMutableModuleLike>()) {
442 return signalPassFailure();
453bool ESIPortsPass::updateFunc(HWModuleExternOp mod) {
454 auto *ctxt = &getContext();
456 bool updated =
false;
458 SmallVector<Attribute> newArgNames, newResultNames;
459 SmallVector<Location> newArgLocs, newResultLocs;
463 SmallVector<Type, 16> newArgTypes;
464 size_t nextArgNo = 0;
465 for (
auto argTy : mod.getInputTypes()) {
466 auto chanTy = dyn_cast<ChannelType>(argTy);
467 newArgNames.push_back(mod.getInputNameAttr(nextArgNo));
468 newArgLocs.push_back(mod.getInputLoc(nextArgNo));
472 newArgTypes.push_back(argTy);
478 auto iface = build->getOrConstructInterface(chanTy);
486 SmallVector<Type, 8> newResultTypes;
487 SmallVector<DictionaryAttr, 4> newResultAttrs;
488 for (
size_t resNum = 0, numRes = mod.getNumOutputPorts(); resNum < numRes;
490 Type resTy = mod.getOutputTypes()[resNum];
491 auto chanTy = dyn_cast<ChannelType>(resTy);
492 auto resNameAttr = mod.getOutputNameAttr(resNum);
493 auto resLocAttr = mod.getOutputLoc(resNum);
495 newResultTypes.push_back(resTy);
496 newResultNames.push_back(resNameAttr);
497 newResultLocs.push_back(resLocAttr);
503 sv::InterfaceOp iface = build->getOrConstructInterface(chanTy);
505 newArgTypes.push_back(sinkPort);
506 newArgNames.push_back(resNameAttr);
507 newArgLocs.push_back(resLocAttr);
516 auto newFuncType = FunctionType::get(ctxt, newArgTypes, newResultTypes);
519 mod.setHWModuleType(newModType);
520 mod.setInputLocs(newArgLocs);
521 mod.setOutputLocs(newResultLocs);
526 if (BlockArgument arg = dyn_cast<BlockArgument>(operand)) {
527 auto *op = arg.getParentBlock()->getParentOp();
528 if (HWModuleLike mod = dyn_cast_or_null<HWModuleLike>(op))
529 return mod.getInputName(arg.getArgNumber());
531 auto *srcOp = operand.getDefiningOp();
532 if (
auto instOp = dyn_cast<InstanceOp>(srcOp))
533 return instOp.getInstanceName();
535 if (
auto srcName = srcOp->getAttrOfType<StringAttr>(
"name"))
536 return srcName.getValue();
544 llvm::raw_string_ostream s(name);
546 s << llvm::toLower(iface.getSymName()[12]) << iface.getSymName().substr(13);
549 if (operand.hasOneUse()) {
550 Operation *dstOp = *operand.getUsers().begin();
551 if (
auto instOp = dyn_cast<InstanceOp>(dstOp))
552 s <<
"To" << llvm::toUpper(instOp.getInstanceName()[0])
553 << instOp.getInstanceName().substr(1);
554 else if (
auto dstName = dstOp->getAttrOfType<StringAttr>(
"name"))
555 s <<
"To" << dstName.getValue();
560 if (!operName.empty())
561 s <<
"From" << llvm::toUpper(operName[0]) << operName.substr(1);
568void ESIPortsPass::updateInstance(HWModuleExternOp mod, InstanceOp inst) {
570 circt::ImplicitLocOpBuilder instBuilder(inst.getLoc(), inst);
575 SmallVector<Value, 16> newOperands;
578 std::string nameStringBuffer;
579 for (
auto op : inst.getOperands()) {
580 auto instChanTy = dyn_cast<ChannelType>(op.getType());
582 newOperands.push_back(op);
589 auto iface = build->getOrConstructInterface(instChanTy);
591 mod.getInputTypes()[opNum]) {
592 inst.emitOpError(
"ESI ChannelType (operand #")
593 << opNum <<
") doesn't match module!";
595 newOperands.push_back(op);
603 InterfaceInstanceOp::create(instBuilder, iface.getInterfaceType());
604 nameStringBuffer.clear();
607 StringAttr::get(mod.getContext(),
609 GetModportOp sinkModport =
611 UnwrapSVInterfaceOp::create(instBuilder, op, sinkModport);
612 GetModportOp sourceModport =
615 newOperands.push_back(sourceModport);
620 SmallVector<Value, 8> newResults;
621 SmallVector<Type, 8> newResultTypes;
622 for (
size_t resNum = 0, numRes = inst.getNumResults(); resNum < numRes;
624 Value res = inst.getResult(resNum);
625 auto instChanTy = dyn_cast<ChannelType>(res.getType());
627 newResults.push_back(res);
628 newResultTypes.push_back(res.getType());
634 auto iface = build->getOrConstructInterface(instChanTy);
636 mod.getInputTypes()[opNum]) {
637 inst.emitOpError(
"ESI ChannelType (result #")
638 << resNum <<
", operand #" << opNum <<
") doesn't match module!";
640 newResults.push_back(res);
641 newResultTypes.push_back(res.getType());
650 InterfaceInstanceOp::create(instBuilder, iface.getInterfaceType());
651 nameStringBuffer.clear();
654 StringAttr::get(mod.getContext(),
656 GetModportOp sourceModport =
659 WrapSVInterfaceOp::create(instBuilder, res.getType(), sourceModport);
662 res.replaceAllUsesWith(newChannel);
663 GetModportOp sinkModport =
666 newOperands.push_back(sinkModport);
670 auto newInst = hw::InstanceOp::create(
671 instBuilder, mod, inst.getInstanceNameAttr(), newOperands,
672 inst.getParameters(), inst.getInnerSymAttr());
676 for (
size_t resNum = 0, numRes = newResults.size(); resNum < numRes;
678 newResults[resNum].replaceAllUsesWith(newInst.getResult(resNum));
684std::unique_ptr<OperationPass<ModuleOp>>
686 return std::make_unique<ESIPortsPass>();
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
static StringRef getOperandName(Value operand)
static StringRef getStringAttributeOr(Operation *op, StringRef attrName, StringRef def)
static std::string & constructInstanceName(Value operand, sv::InterfaceOp iface, std::string &name)
Create a reasonable name for a SV interface instance.
static LogicalResult updateInstance(const DomainInfo &info, TermAllocator &allocator, DomainTable &table, T op)
static EvaluatorValuePtr unwrap(OMEvaluatorValue c)
static InstancePath empty
Assist the lowering steps for conversions which need to create auxiliary IR.
static constexpr char sinkStr[]
static constexpr char sourceStr[]
HW-specific instance graph with a virtual entry node linking to all publicly visible modules.
PortConversionBuilder(PortConverterImpl &converter)
virtual FailureOr< std::unique_ptr< PortConversion > > build(hw::PortInfo port)
Base class for the port conversion of a particular port.
Channels are the basic communication primitives.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
constexpr StringRef extModPortValidSuffix
Suffix lowered valid ports with this suffix.
constexpr StringRef extModPortRdenSuffix
Suffix lowered read enable ports with this suffix.
constexpr StringRef extModPortReadySuffix
Suffix lowered ready ports with this suffix.
constexpr StringRef extModBundleSignalsAttrName
Name of dialect attribute which governs whether or not to bundle (i.e.
constexpr StringRef extModPortInSuffix
Suffix all lowered input ports with this suffix. Defaults to nothing.
std::unique_ptr< OperationPass< ModuleOp > > createESIPortLoweringPass()
constexpr StringRef extModPortOutSuffix
Suffix all lowered output ports with this suffix. Defaults to nothing.
constexpr StringRef extModPortEmptySuffix
Suffix lowered empty ports with this suffix.
ModuleType fnToMod(Operation *op, ArrayRef< Attribute > inputNames, ArrayRef< Attribute > outputNames)
void error(Twine message)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
This holds the name, type, direction of a module's ports.