9#include "../PassDetails.h"
19#include "mlir/Transforms/DialectConversion.h"
21#include "llvm/Support/MathExtras.h"
25#define GEN_PASS_DEF_LOWERESIPORTS
26#include "circt/Dialect/ESI/ESIPasses.h.inc"
39 auto attr = op->getAttrOfType<StringAttr>(attrName);
41 return attr.getValue();
76struct ValidReadyProtocol {
77 static constexpr bool hasBackpressure =
true;
78 static StringRef getValiditySuffix(Operation *module) {
81 static StringRef getBackpressureSuffix(Operation *module) {
84 static std::pair<Value, Value>
wrap(OpBuilder &b, Location loc,
85 ChannelType chanTy, Value data,
87 auto wrap = WrapValidReadyOp::create(b, loc, data, validity);
88 return {
wrap.getChanOutput(),
wrap.getReady()};
90 static std::pair<Value, Value>
unwrap(OpBuilder &b, Location loc, Value chan,
92 auto unwrap = UnwrapValidReadyOp::create(b, loc, chan, backpressure);
100 static constexpr bool hasBackpressure =
true;
101 static StringRef getValiditySuffix(Operation *module) {
104 static StringRef getBackpressureSuffix(Operation *module) {
107 static std::pair<Value, Value>
wrap(OpBuilder &b, Location loc,
108 ChannelType chanTy, Value data,
110 auto wrap = WrapFIFOOp::create(
111 b, loc, ArrayRef<Type>({chanTy, b.getI1Type()}), data, validity);
112 return {
wrap.getChanOutput(),
wrap.getRden()};
114 static std::pair<Value, Value>
unwrap(OpBuilder &b, Location loc, Value chan,
115 Value backpressure) {
116 auto unwrap = UnwrapFIFOOp::create(b, loc, chan, backpressure);
122struct ValidOnlyProtocol {
123 static constexpr bool hasBackpressure =
false;
124 static StringRef getValiditySuffix(Operation *module) {
127 static std::pair<Value, Value>
wrap(OpBuilder &b, Location loc,
128 ChannelType chanTy, Value data,
130 auto wrap = WrapValidOnlyOp::create(b, loc, data, validity);
131 return {
wrap.getChanOutput(), Value()};
133 static std::pair<Value, Value>
unwrap(OpBuilder &b, Location loc, Value chan,
135 auto unwrap = UnwrapValidOnlyOp::create(b, loc, chan);
143static bool containsChannel(Type type) {
144 if (
auto arr = dyn_cast<hw::ArrayType>(type)) {
145 Type elem = arr.getElementType();
146 return isa<esi::ChannelType>(elem) || containsChannel(elem);
148 if (
auto str = dyn_cast<hw::StructType>(type))
149 return llvm::any_of(str.getElements(), [](
const auto &field) {
150 return isa<esi::ChannelType>(field.type) || containsChannel(field.type);
157template <
typename Protocol>
163 SmallVectorImpl<Value> &newOperands,
164 ArrayRef<Backedge> newResults)
override;
166 SmallVectorImpl<Value> &newOperands,
167 ArrayRef<Backedge> newResults)
override;
175 PortInfo dataPort, validityPort, backpressurePort;
181template <
typename Protocol>
187 SmallVectorImpl<Value> &newOperands,
188 ArrayRef<Backedge> newResults)
override;
190 SmallVectorImpl<Value> &newOperands,
191 ArrayRef<Backedge> newResults)
override;
199 PortInfo dataPort, validityPort, backpressurePort;
203static FailureOr<std::unique_ptr<PortConversion>>
205 ChannelSignaling signaling) {
207 op->emitOpError(
"encountered unknown signaling standard on port '")
208 << stringifyEnum(signaling) <<
"'";
216template <
template <
typename>
class PortKind>
217static FailureOr<std::unique_ptr<PortConversion>>
219 ChannelSignaling signaling) {
221 case ChannelSignaling::ValidReady:
222 return {std::make_unique<PortKind<ValidReadyProtocol>>(converter, port)};
223 case ChannelSignaling::FIFO:
224 return {std::make_unique<PortKind<FIFOProtocol>>(converter, port)};
225 case ChannelSignaling::ValidOnly:
226 return {std::make_unique<PortKind<ValidOnlyProtocol>>(converter, port)};
228 return emitUnknownSignaling(converter.
getModule(), port, signaling);
234 FailureOr<std::unique_ptr<PortConversion>> build(
hw::PortInfo port)
override {
235 return llvm::TypeSwitch<Type, FailureOr<std::unique_ptr<PortConversion>>>(
238 -> FailureOr<std::unique_ptr<PortConversion>> {
239 return buildChannelPort<ScalarChannelPort>(converter, port,
240 chanTy.getSignaling());
242 .Case([&](hw::ArrayType arrTy)
243 -> FailureOr<std::unique_ptr<PortConversion>> {
244 auto chanTy = dyn_cast<esi::ChannelType>(arrTy.getElementType());
248 if (containsChannel(arrTy)) {
250 "cannot lower port containing channels nested inside an "
251 "aggregate other than a single array of channels");
261 if (!isa<IntegerAttr>(arrTy.getSizeAttr()) ||
262 arrTy.getNumElements() == 0)
264 .emitOpError(
"cannot lower array-of-channels port with a "
265 "non-constant or zero-length size")
266 .attachNote(port.
loc);
267 return buildChannelPort<ArrayChannelPort>(converter, port,
268 chanTy.getSignaling());
278 auto arrTy = cast<hw::ArrayType>(array.getType());
279 IntegerType idxType = b.getIntegerType(
280 std::max(1u, llvm::Log2_64_Ceil(arrTy.getNumElements())));
289static Value
packArray(OpBuilder &b, Location loc, ArrayRef<Value> elements) {
290 SmallVector<Value> reversed(elements.rbegin(), elements.rend());
298template <
typename Protocol>
299void ScalarChannelPort<Protocol>::buildInputSignals() {
300 Operation *
module = converter.getModule();
301 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
302 auto chanTy = cast<ChannelType>(origPort.type);
310 converter.
createNewInput(origPort, inSuffix, chanTy.getInner(), dataPort);
312 origPort, Protocol::getValiditySuffix(module) + inSuffix, i1,
317 ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
320 auto [chan, bp] = Protocol::wrap(b, b.getLoc(), chanTy, data, validity);
322 body->getArgument(origPort.argNum).replaceAllUsesWith(chan);
326 if constexpr (Protocol::hasBackpressure)
328 origPort, Protocol::getBackpressureSuffix(module) + outSuffix, i1,
329 backpressure, backpressurePort);
332template <
typename Protocol>
333void ScalarChannelPort<Protocol>::mapInputSignals(
334 OpBuilder &b, Operation *inst, Value, SmallVectorImpl<Value> &newOperands,
335 ArrayRef<Backedge> newResults) {
337 if constexpr (Protocol::hasBackpressure)
338 backpressure = newResults[backpressurePort.argNum];
339 auto [
data, validity] = Protocol::unwrap(
340 b, inst->getLoc(), inst->getOperand(origPort.argNum), backpressure);
341 newOperands[dataPort.argNum] =
data;
342 newOperands[validityPort.argNum] = validity;
345template <
typename Protocol>
346void ScalarChannelPort<Protocol>::buildOutputSignals() {
347 Operation *
module = converter.getModule();
348 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
349 auto chanTy = cast<ChannelType>(origPort.type);
356 if constexpr (Protocol::hasBackpressure)
358 origPort, Protocol::getBackpressureSuffix(module) + inSuffix, i1,
361 Value
data, validity;
363 auto *terminator = body->getTerminator();
364 ImplicitLocOpBuilder b(origPort.loc, terminator);
365 auto unwrapped = Protocol::unwrap(
366 b, b.getLoc(), terminator->getOperand(origPort.argNum), backpressure);
367 data = unwrapped.first;
368 validity = unwrapped.second;
372 converter.
createNewOutput(origPort, outSuffix, chanTy.getInner(), data,
375 Protocol::getValiditySuffix(module) + outSuffix, i1,
376 validity, validityPort);
379template <
typename Protocol>
380void ScalarChannelPort<Protocol>::mapOutputSignals(
381 OpBuilder &b, Operation *inst, Value, SmallVectorImpl<Value> &newOperands,
382 ArrayRef<Backedge> newResults) {
383 auto chanTy = cast<ChannelType>(origPort.type);
384 auto [chan, backpressure] =
385 Protocol::wrap(b, inst->getLoc(), chanTy, newResults[dataPort.argNum],
386 newResults[validityPort.argNum]);
387 inst->getResult(origPort.argNum).replaceAllUsesWith(chan);
388 if constexpr (Protocol::hasBackpressure)
389 newOperands[backpressurePort.argNum] = backpressure;
396template <
typename Protocol>
397void ArrayChannelPort<Protocol>::buildInputSignals() {
398 Operation *
module = converter.getModule();
399 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
400 auto arrTy = cast<hw::ArrayType>(origPort.type);
401 auto chanTy = cast<ChannelType>(arrTy.getElementType());
402 size_t numElems = arrTy.getNumElements();
403 auto dataArrTy = hw::ArrayType::get(chanTy.getInner(), numElems);
404 auto validityArrTy = hw::ArrayType::get(i1, numElems);
412 converter.
createNewInput(origPort, inSuffix, dataArrTy, dataPort);
414 origPort, Protocol::getValiditySuffix(module) + inSuffix, validityArrTy,
417 Value backpressureArr;
419 ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
422 SmallVector<Value> chans, backpressures;
423 for (
size_t i = 0; i < numElems; ++i) {
426 auto [chan, bp] = Protocol::wrap(b, b.getLoc(), chanTy, data, validity);
427 chans.push_back(chan);
428 if constexpr (Protocol::hasBackpressure)
429 backpressures.push_back(bp);
431 body->getArgument(origPort.argNum)
432 .replaceAllUsesWith(
packArray(b, b.getLoc(), chans));
433 if (!backpressures.empty())
434 backpressureArr =
packArray(b, b.getLoc(), backpressures);
438 if constexpr (Protocol::hasBackpressure)
440 origPort, Protocol::getBackpressureSuffix(module) + outSuffix,
441 validityArrTy, backpressureArr, backpressurePort);
444template <
typename Protocol>
445void ArrayChannelPort<Protocol>::mapInputSignals(
446 OpBuilder &b, Operation *inst, Value, SmallVectorImpl<Value> &newOperands,
447 ArrayRef<Backedge> newResults) {
448 Location loc = inst->getLoc();
449 Value chanArr = inst->getOperand(origPort.argNum);
450 size_t numElems = cast<hw::ArrayType>(origPort.type).getNumElements();
452 Value backpressureArr;
453 if constexpr (Protocol::hasBackpressure)
454 backpressureArr = newResults[backpressurePort.argNum];
457 SmallVector<Value> datas, validities;
458 for (
size_t i = 0; i < numElems; ++i) {
461 if constexpr (Protocol::hasBackpressure)
463 auto [
data, validity] = Protocol::unwrap(b, loc, chan, backpressure);
464 datas.push_back(data);
465 validities.push_back(validity);
467 newOperands[dataPort.argNum] =
packArray(b, loc, datas);
468 newOperands[validityPort.argNum] =
packArray(b, loc, validities);
471template <
typename Protocol>
472void ArrayChannelPort<Protocol>::buildOutputSignals() {
473 Operation *
module = converter.getModule();
474 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
475 auto arrTy = cast<hw::ArrayType>(origPort.type);
476 auto chanTy = cast<ChannelType>(arrTy.getElementType());
477 size_t numElems = arrTy.getNumElements();
478 auto dataArrTy = hw::ArrayType::get(chanTy.getInner(), numElems);
479 auto validityArrTy = hw::ArrayType::get(i1, numElems);
485 Value backpressureArr;
486 if constexpr (Protocol::hasBackpressure)
488 origPort, Protocol::getBackpressureSuffix(module) + inSuffix,
489 validityArrTy, backpressurePort);
491 Value dataArr, validityArr;
493 auto *terminator = body->getTerminator();
494 ImplicitLocOpBuilder b(origPort.loc, terminator);
495 Value chanArr = terminator->getOperand(origPort.argNum);
497 SmallVector<Value> datas, validities;
498 for (
size_t i = 0; i < numElems; ++i) {
501 if constexpr (Protocol::hasBackpressure)
503 auto [
data, validity] =
504 Protocol::unwrap(b, b.getLoc(), chan, backpressure);
505 datas.push_back(data);
506 validities.push_back(validity);
508 dataArr =
packArray(b, b.getLoc(), datas);
509 validityArr =
packArray(b, b.getLoc(), validities);
513 converter.
createNewOutput(origPort, outSuffix, dataArrTy, dataArr, dataPort);
515 Protocol::getValiditySuffix(module) + outSuffix,
516 validityArrTy, validityArr, validityPort);
519template <
typename Protocol>
520void ArrayChannelPort<Protocol>::mapOutputSignals(
521 OpBuilder &b, Operation *inst, Value, SmallVectorImpl<Value> &newOperands,
522 ArrayRef<Backedge> newResults) {
523 Location loc = inst->getLoc();
524 auto arrTy = cast<hw::ArrayType>(origPort.type);
525 auto chanTy = cast<ChannelType>(arrTy.getElementType());
526 size_t numElems = arrTy.getNumElements();
528 Value dataArr = newResults[dataPort.argNum];
529 Value validityArr = newResults[validityPort.argNum];
532 SmallVector<Value> chans, backpressures;
533 for (
size_t i = 0; i < numElems; ++i) {
536 auto [chan, bp] = Protocol::wrap(b, loc, chanTy, data, validity);
537 chans.push_back(chan);
539 backpressures.push_back(bp);
541 inst->getResult(origPort.argNum).replaceAllUsesWith(
packArray(b, loc, chans));
542 if constexpr (Protocol::hasBackpressure)
543 newOperands[backpressurePort.argNum] =
packArray(b, loc, backpressures);
551struct ESIPortsPass :
public circt::esi::impl::LowerESIPortsBase<ESIPortsPass> {
552 void runOnOperation()
override;
555 bool updateFunc(HWModuleExternOp mod);
556 void updateInstance(HWModuleExternOp mod, InstanceOp inst);
562void ESIPortsPass::runOnOperation() {
563 ModuleOp top = getOperation();
569 DenseMap<SymbolRefAttr, HWModuleExternOp> externModsMutated;
570 for (
auto mod : top.getOps<HWModuleExternOp>())
573 externModsMutated[FlatSymbolRefAttr::
get(mod)] = mod;
576 top.walk([&externModsMutated,
this](InstanceOp inst) {
577 auto mapIter = externModsMutated.find(inst.getModuleNameAttr());
578 if (mapIter != externModsMutated.end())
579 updateInstance(mapIter->second, inst);
584 getAnalysis<circt::hw::InstanceGraph>();
586 for (
auto mod : top.getOps<HWMutableModuleLike>()) {
589 return signalPassFailure();
600bool ESIPortsPass::updateFunc(HWModuleExternOp mod) {
601 auto *ctxt = &getContext();
603 bool updated =
false;
605 SmallVector<Attribute> newArgNames, newResultNames;
606 SmallVector<Location> newArgLocs, newResultLocs;
610 SmallVector<Type, 16> newArgTypes;
611 size_t nextArgNo = 0;
612 for (
auto argTy : mod.getInputTypes()) {
613 auto chanTy = dyn_cast<ChannelType>(argTy);
614 newArgNames.push_back(mod.getInputNameAttr(nextArgNo));
615 newArgLocs.push_back(mod.getInputLoc(nextArgNo));
619 newArgTypes.push_back(argTy);
625 auto iface = build->getOrConstructInterface(chanTy);
633 SmallVector<Type, 8> newResultTypes;
634 SmallVector<DictionaryAttr, 4> newResultAttrs;
635 for (
size_t resNum = 0, numRes = mod.getNumOutputPorts(); resNum < numRes;
637 Type resTy = mod.getOutputTypes()[resNum];
638 auto chanTy = dyn_cast<ChannelType>(resTy);
639 auto resNameAttr = mod.getOutputNameAttr(resNum);
640 auto resLocAttr = mod.getOutputLoc(resNum);
642 newResultTypes.push_back(resTy);
643 newResultNames.push_back(resNameAttr);
644 newResultLocs.push_back(resLocAttr);
650 sv::InterfaceOp iface = build->getOrConstructInterface(chanTy);
652 newArgTypes.push_back(sinkPort);
653 newArgNames.push_back(resNameAttr);
654 newArgLocs.push_back(resLocAttr);
663 auto newFuncType = FunctionType::get(ctxt, newArgTypes, newResultTypes);
666 mod.setHWModuleType(newModType);
667 mod.setInputLocs(newArgLocs);
668 mod.setOutputLocs(newResultLocs);
673 if (BlockArgument arg = dyn_cast<BlockArgument>(operand)) {
674 auto *op = arg.getParentBlock()->getParentOp();
675 if (HWModuleLike mod = dyn_cast_or_null<HWModuleLike>(op))
676 return mod.getInputName(arg.getArgNumber());
678 auto *srcOp = operand.getDefiningOp();
679 if (
auto instOp = dyn_cast<InstanceOp>(srcOp))
680 return instOp.getInstanceName();
682 if (
auto srcName = srcOp->getAttrOfType<StringAttr>(
"name"))
683 return srcName.getValue();
691 llvm::raw_string_ostream s(name);
693 s << llvm::toLower(iface.getSymName()[12]) << iface.getSymName().substr(13);
696 if (operand.hasOneUse()) {
697 Operation *dstOp = *operand.getUsers().begin();
698 if (
auto instOp = dyn_cast<InstanceOp>(dstOp))
699 s <<
"To" << llvm::toUpper(instOp.getInstanceName()[0])
700 << instOp.getInstanceName().substr(1);
701 else if (
auto dstName = dstOp->getAttrOfType<StringAttr>(
"name"))
702 s <<
"To" << dstName.getValue();
707 if (!operName.empty())
708 s <<
"From" << llvm::toUpper(operName[0]) << operName.substr(1);
715void ESIPortsPass::updateInstance(HWModuleExternOp mod, InstanceOp inst) {
717 circt::ImplicitLocOpBuilder instBuilder(inst.getLoc(), inst);
722 SmallVector<Value, 16> newOperands;
725 std::string nameStringBuffer;
726 for (
auto op : inst.getOperands()) {
727 auto instChanTy = dyn_cast<ChannelType>(op.getType());
729 newOperands.push_back(op);
736 auto iface = build->getOrConstructInterface(instChanTy);
738 mod.getInputTypes()[opNum]) {
739 inst.emitOpError(
"ESI ChannelType (operand #")
740 << opNum <<
") doesn't match module!";
742 newOperands.push_back(op);
750 InterfaceInstanceOp::create(instBuilder, iface.getInterfaceType());
751 nameStringBuffer.clear();
754 StringAttr::get(mod.getContext(),
756 GetModportOp sinkModport =
758 UnwrapSVInterfaceOp::create(instBuilder, op, sinkModport);
759 GetModportOp sourceModport =
762 newOperands.push_back(sourceModport);
767 SmallVector<Value, 8> newResults;
768 SmallVector<Type, 8> newResultTypes;
769 for (
size_t resNum = 0, numRes = inst.getNumResults(); resNum < numRes;
771 Value res = inst.getResult(resNum);
772 auto instChanTy = dyn_cast<ChannelType>(res.getType());
774 newResults.push_back(res);
775 newResultTypes.push_back(res.getType());
781 auto iface = build->getOrConstructInterface(instChanTy);
783 mod.getInputTypes()[opNum]) {
784 inst.emitOpError(
"ESI ChannelType (result #")
785 << resNum <<
", operand #" << opNum <<
") doesn't match module!";
787 newResults.push_back(res);
788 newResultTypes.push_back(res.getType());
797 InterfaceInstanceOp::create(instBuilder, iface.getInterfaceType());
798 nameStringBuffer.clear();
801 StringAttr::get(mod.getContext(),
803 GetModportOp sourceModport =
806 WrapSVInterfaceOp::create(instBuilder, res.getType(), sourceModport);
809 res.replaceAllUsesWith(newChannel);
810 GetModportOp sinkModport =
813 newOperands.push_back(sinkModport);
817 auto newInst = hw::InstanceOp::create(
818 instBuilder, mod, inst.getInstanceNameAttr(), newOperands,
819 inst.getParameters(), inst.getInnerSymAttr());
823 for (
size_t resNum = 0, numRes = newResults.size(); resNum < numRes;
825 newResults[resNum].replaceAllUsesWith(newInst.getResult(resNum));
831std::unique_ptr<OperationPass<ModuleOp>>
833 return std::make_unique<ESIPortsPass>();
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
static StringRef getOperandName(Value operand)
static Value packArray(OpBuilder &b, Location loc, ArrayRef< Value > elements)
Pack a list of element values (with 'elements[i]' destined for array index 'i') into an hw....
static Value getArrayElement(OpBuilder &b, Location loc, Value array, size_t idx)
Extract element 'idx' from the array-typed value 'array'.
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 EvaluatorValuePtr unwrap(OMEvaluatorValue c)
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.
virtual void buildInputSignals()=0
virtual void mapInputSignals(OpBuilder &b, Operation *inst, Value instValue, SmallVectorImpl< Value > &newOperands, ArrayRef< Backedge > newResults)=0
Update an instance port to the new port information.
PortConversion(PortConverterImpl &converter, hw::PortInfo origPort)
virtual void mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue, SmallVectorImpl< Value > &newOperands, ArrayRef< Backedge > newResults)=0
virtual void buildOutputSignals()=0
void createNewOutput(hw::PortInfo origPort, const Twine &suffix, Type type, Value output, hw::PortInfo &newPort)
Same as above.
hw::HWMutableModuleLike getModule() const
Value createNewInput(hw::PortInfo origPort, const Twine &suffix, Type type, hw::PortInfo &newPort)
These two methods take care of allocating new ports in the correct place based on the position of 'or...
Channels are the basic communication primitives.
create(elements, Type result_type=None)
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, List[str] cmdline_args=sys.argv)
This holds the name, type, direction of a module's ports.