17#include "mlir/Transforms/DialectConversion.h"
19#include "llvm/Support/MathExtras.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();
74struct ValidReadyProtocol {
75 static constexpr bool hasBackpressure =
true;
76 static StringRef getValiditySuffix(Operation *module) {
79 static StringRef getBackpressureSuffix(Operation *module) {
82 static std::pair<Value, Value>
wrap(OpBuilder &b, Location loc,
83 ChannelType chanTy, Value data,
86 "channel inner type must match the data type");
88 WrapValidReadyOp::create(b, loc, chanTy, b.getI1Type(), data, validity);
89 return {
wrap.getChanOutput(),
wrap.getReady()};
91 static std::pair<Value, Value>
unwrap(OpBuilder &b, Location loc, Value chan,
93 auto unwrap = UnwrapValidReadyOp::create(b, loc, chan, backpressure);
101 static constexpr bool hasBackpressure =
true;
102 static StringRef getValiditySuffix(Operation *module) {
105 static StringRef getBackpressureSuffix(Operation *module) {
108 static std::pair<Value, Value>
wrap(OpBuilder &b, Location loc,
109 ChannelType chanTy, Value data,
111 auto wrap = WrapFIFOOp::create(
112 b, loc, ArrayRef<Type>({chanTy, b.getI1Type()}),
data, validity);
113 return {
wrap.getChanOutput(),
wrap.getRden()};
115 static std::pair<Value, Value>
unwrap(OpBuilder &b, Location loc, Value chan,
116 Value backpressure) {
117 auto unwrap = UnwrapFIFOOp::create(b, loc, chan, backpressure);
123struct ValidOnlyProtocol {
124 static constexpr bool hasBackpressure =
false;
125 static StringRef getValiditySuffix(Operation *module) {
128 static std::pair<Value, Value>
wrap(OpBuilder &b, Location loc,
129 ChannelType chanTy, Value data,
132 WrapValidOnlyOp::create(b, loc, data, validity, chanTy.getDataDelay());
133 return {
wrap.getChanOutput(), Value()};
135 static std::pair<Value, Value>
unwrap(OpBuilder &b, Location loc, Value chan,
137 auto unwrap = UnwrapValidOnlyOp::create(b, loc, chan);
145static bool containsChannel(Type type) {
146 if (
auto arr = dyn_cast<hw::ArrayType>(type)) {
147 Type elem = arr.getElementType();
148 return isa<esi::ChannelType>(elem) || containsChannel(elem);
150 if (
auto str = dyn_cast<hw::StructType>(type))
151 return llvm::any_of(str.getElements(), [](
const auto &field) {
152 return isa<esi::ChannelType>(field.type) || containsChannel(field.type);
159template <
typename Protocol>
164 void mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
165 SmallVectorImpl<Value> &newOperands,
166 ArrayRef<Backedge> newResults)
override;
167 void mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
168 SmallVectorImpl<Value> &newOperands,
169 ArrayRef<Backedge> newResults)
override;
172 void buildInputSignals()
override;
173 void buildOutputSignals()
override;
177 PortInfo dataPort, validityPort, backpressurePort;
183template <
typename Protocol>
188 void mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
189 SmallVectorImpl<Value> &newOperands,
190 ArrayRef<Backedge> newResults)
override;
191 void mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
192 SmallVectorImpl<Value> &newOperands,
193 ArrayRef<Backedge> newResults)
override;
196 void buildInputSignals()
override;
197 void buildOutputSignals()
override;
201 PortInfo dataPort, validityPort, backpressurePort;
205static FailureOr<std::unique_ptr<PortConversion>>
207 ChannelSignaling signaling) {
209 op->emitOpError(
"encountered unknown signaling standard on port '")
210 << stringifyEnum(signaling) <<
"'";
218template <
template <
typename>
class PortKind>
219static FailureOr<std::unique_ptr<PortConversion>>
221 ChannelSignaling signaling) {
223 case ChannelSignaling::ValidReady:
224 return {std::make_unique<PortKind<ValidReadyProtocol>>(converter, port)};
225 case ChannelSignaling::FIFO:
226 return {std::make_unique<PortKind<FIFOProtocol>>(converter, port)};
227 case ChannelSignaling::ValidOnly:
228 return {std::make_unique<PortKind<ValidOnlyProtocol>>(converter, port)};
230 return emitUnknownSignaling(converter.
getModule(), port, signaling);
236 FailureOr<std::unique_ptr<PortConversion>> build(hw::PortInfo port)
override {
237 return llvm::TypeSwitch<Type, FailureOr<std::unique_ptr<PortConversion>>>(
239 .Case([&](esi::ChannelType chanTy)
240 -> FailureOr<std::unique_ptr<PortConversion>> {
241 return buildChannelPort<ScalarChannelPort>(converter, port,
242 chanTy.getSignaling());
244 .Case([&](hw::ArrayType arrTy)
245 -> FailureOr<std::unique_ptr<PortConversion>> {
246 auto chanTy = dyn_cast<esi::ChannelType>(arrTy.getElementType());
250 if (containsChannel(arrTy)) {
252 "cannot lower port containing channels nested inside an "
253 "aggregate other than a single array of channels");
263 if (!isa<IntegerAttr>(arrTy.getSizeAttr()) ||
264 arrTy.getNumElements() == 0)
266 .emitOpError(
"cannot lower array-of-channels port with a "
267 "non-constant or zero-length size")
268 .attachNote(port.
loc);
269 return buildChannelPort<ArrayChannelPort>(converter, port,
270 chanTy.getSignaling());
280 auto arrTy = cast<hw::ArrayType>(array.getType());
281 IntegerType idxType = b.getIntegerType(
282 std::max(1u, llvm::Log2_64_Ceil(arrTy.getNumElements())));
291static Value
packArray(OpBuilder &b, Location loc, ArrayRef<Value> elements) {
292 SmallVector<Value> reversed(elements.rbegin(), elements.rend());
300template <
typename Protocol>
301void ScalarChannelPort<Protocol>::buildInputSignals() {
302 Operation *module = converter.
getModule();
303 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
304 auto chanTy = cast<ChannelType>(origPort.type);
314 origPort, Protocol::getValiditySuffix(module) + inSuffix, i1,
319 ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
322 auto [chan, bp] = Protocol::wrap(b, b.getLoc(), chanTy, data, validity);
324 body->getArgument(origPort.argNum).replaceAllUsesWith(chan);
328 if constexpr (Protocol::hasBackpressure)
330 origPort, Protocol::getBackpressureSuffix(module) + outSuffix, i1,
334template <
typename Protocol>
335void ScalarChannelPort<Protocol>::mapInputSignals(
336 OpBuilder &b, Operation *inst, Value, SmallVectorImpl<Value> &newOperands,
337 ArrayRef<Backedge> newResults) {
339 if constexpr (Protocol::hasBackpressure)
341 auto [
data, validity] = Protocol::unwrap(
342 b, inst->getLoc(), inst->getOperand(origPort.argNum),
backpressure);
343 newOperands[dataPort.argNum] =
data;
344 newOperands[validityPort.argNum] = validity;
347template <
typename Protocol>
348void ScalarChannelPort<Protocol>::buildOutputSignals() {
349 Operation *module = converter.
getModule();
350 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
351 auto chanTy = cast<ChannelType>(origPort.type);
358 if constexpr (Protocol::hasBackpressure)
360 origPort, Protocol::getBackpressureSuffix(module) + inSuffix, i1,
363 Value
data, validity;
365 auto *terminator = body->getTerminator();
366 ImplicitLocOpBuilder b(origPort.loc, terminator);
367 auto unwrapped = Protocol::unwrap(
368 b, b.getLoc(), terminator->getOperand(origPort.argNum),
backpressure);
369 data = unwrapped.first;
370 validity = unwrapped.second;
377 Protocol::getValiditySuffix(module) + outSuffix, i1,
378 validity, validityPort);
381template <
typename Protocol>
382void ScalarChannelPort<Protocol>::mapOutputSignals(
383 OpBuilder &b, Operation *inst, Value, SmallVectorImpl<Value> &newOperands,
384 ArrayRef<Backedge> newResults) {
385 auto chanTy = cast<ChannelType>(origPort.type);
387 Protocol::wrap(b, inst->getLoc(), chanTy, newResults[dataPort.argNum],
388 newResults[validityPort.argNum]);
389 inst->getResult(origPort.argNum).replaceAllUsesWith(chan);
390 if constexpr (Protocol::hasBackpressure)
398template <
typename Protocol>
399void ArrayChannelPort<Protocol>::buildInputSignals() {
400 Operation *module = converter.
getModule();
401 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
402 auto arrTy = cast<hw::ArrayType>(origPort.type);
403 auto chanTy = cast<ChannelType>(arrTy.getElementType());
404 size_t numElems = arrTy.getNumElements();
405 auto dataArrTy = hw::ArrayType::get(chanTy.
getInner(), numElems);
406 auto validityArrTy = hw::ArrayType::get(i1, numElems);
414 converter.
createNewInput(origPort, inSuffix, dataArrTy, dataPort);
416 origPort, Protocol::getValiditySuffix(module) + inSuffix, validityArrTy,
419 Value backpressureArr;
421 ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
424 SmallVector<Value> chans, backpressures;
425 for (
size_t i = 0; i < numElems; ++i) {
428 auto [chan, bp] = Protocol::wrap(b, b.getLoc(), chanTy, data, validity);
429 chans.push_back(chan);
430 if constexpr (Protocol::hasBackpressure)
431 backpressures.push_back(bp);
433 body->getArgument(origPort.argNum)
434 .replaceAllUsesWith(
packArray(b, b.getLoc(), chans));
435 if (!backpressures.empty())
436 backpressureArr =
packArray(b, b.getLoc(), backpressures);
440 if constexpr (Protocol::hasBackpressure)
442 origPort, Protocol::getBackpressureSuffix(module) + outSuffix,
443 validityArrTy, backpressureArr, backpressurePort);
446template <
typename Protocol>
447void ArrayChannelPort<Protocol>::mapInputSignals(
448 OpBuilder &b, Operation *inst, Value, SmallVectorImpl<Value> &newOperands,
449 ArrayRef<Backedge> newResults) {
450 Location loc = inst->getLoc();
451 Value chanArr = inst->getOperand(origPort.argNum);
452 size_t numElems = cast<hw::ArrayType>(origPort.type).getNumElements();
454 Value backpressureArr;
455 if constexpr (Protocol::hasBackpressure)
456 backpressureArr = newResults[backpressurePort.argNum];
459 SmallVector<Value> datas, validities;
460 for (
size_t i = 0; i < numElems; ++i) {
463 if constexpr (Protocol::hasBackpressure)
466 datas.push_back(data);
467 validities.push_back(validity);
469 newOperands[dataPort.argNum] =
packArray(b, loc, datas);
470 newOperands[validityPort.argNum] =
packArray(b, loc, validities);
473template <
typename Protocol>
474void ArrayChannelPort<Protocol>::buildOutputSignals() {
475 Operation *module = converter.
getModule();
476 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
477 auto arrTy = cast<hw::ArrayType>(origPort.type);
478 auto chanTy = cast<ChannelType>(arrTy.getElementType());
479 size_t numElems = arrTy.getNumElements();
480 auto dataArrTy = hw::ArrayType::get(chanTy.
getInner(), numElems);
481 auto validityArrTy = hw::ArrayType::get(i1, numElems);
487 Value backpressureArr;
488 if constexpr (Protocol::hasBackpressure)
490 origPort, Protocol::getBackpressureSuffix(module) + inSuffix,
491 validityArrTy, backpressurePort);
493 Value dataArr, validityArr;
495 auto *terminator = body->getTerminator();
496 ImplicitLocOpBuilder b(origPort.loc, terminator);
497 Value chanArr = terminator->getOperand(origPort.argNum);
499 SmallVector<Value> datas, validities;
500 for (
size_t i = 0; i < numElems; ++i) {
503 if constexpr (Protocol::hasBackpressure)
505 auto [
data, validity] =
507 datas.push_back(data);
508 validities.push_back(validity);
510 dataArr =
packArray(b, b.getLoc(), datas);
511 validityArr =
packArray(b, b.getLoc(), validities);
515 converter.
createNewOutput(origPort, outSuffix, dataArrTy, dataArr, dataPort);
517 Protocol::getValiditySuffix(module) + outSuffix,
518 validityArrTy, validityArr, validityPort);
521template <
typename Protocol>
522void ArrayChannelPort<Protocol>::mapOutputSignals(
523 OpBuilder &b, Operation *inst, Value, SmallVectorImpl<Value> &newOperands,
524 ArrayRef<Backedge> newResults) {
525 Location loc = inst->getLoc();
526 auto arrTy = cast<hw::ArrayType>(origPort.type);
527 auto chanTy = cast<ChannelType>(arrTy.getElementType());
528 size_t numElems = arrTy.getNumElements();
530 Value dataArr = newResults[dataPort.argNum];
531 Value validityArr = newResults[validityPort.argNum];
534 SmallVector<Value> chans, backpressures;
535 for (
size_t i = 0; i < numElems; ++i) {
538 auto [chan, bp] = Protocol::wrap(b, loc, chanTy, data, validity);
539 chans.push_back(chan);
541 backpressures.push_back(bp);
543 inst->getResult(origPort.argNum).replaceAllUsesWith(
packArray(b, loc, chans));
544 if constexpr (Protocol::hasBackpressure)
545 newOperands[backpressurePort.argNum] =
packArray(b, loc, backpressures);
550struct ESIPortsPass :
public circt::esi::impl::LowerESIPortsBase<ESIPortsPass> {
551 void runOnOperation()
override;
556void ESIPortsPass::runOnOperation() {
557 ModuleOp top = getOperation();
559 circt::hw::InstanceGraph &instanceGraph =
560 getAnalysis<circt::hw::InstanceGraph>();
562 for (
auto mod : top.getOps<HWMutableModuleLike>()) {
564 PortConverter<ESIPortConversionBuilder>(instanceGraph, mod).
run()))
565 return signalPassFailure();
569std::unique_ptr<OperationPass<ModuleOp>>
571 return std::make_unique<ESIPortsPass>();
assert(baseType &&"element must be base type")
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
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 EvaluatorValuePtr unwrap(OMEvaluatorValue c)
PortConversionBuilder(PortConverterImpl &converter)
virtual FailureOr< std::unique_ptr< PortConversion > > build(hw::PortInfo port)
Base class for the port conversion of a particular port.
PortConversion(PortConverterImpl &converter, hw::PortInfo origPort)
void createNewOutput(hw::PortInfo origPort, const Twine &suffix, Type type, Value output, hw::PortInfo &newPort, PortAttrPolicy attrPolicy=PortAttrPolicy::Drop)
Same as above.
Value createNewInput(hw::PortInfo origPort, const Twine &suffix, Type type, hw::PortInfo &newPort, PortAttrPolicy attrPolicy=PortAttrPolicy::Drop)
These two methods take care of allocating new ports in the correct place based on the position of 'or...
hw::HWMutableModuleLike getModule() const
const Type * getInner() const
create(elements, Type result_type=None)
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 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.
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.