CIRCT 24.0.0git
Loading...
Searching...
No Matches
ESILowerPorts.cpp
Go to the documentation of this file.
1//===- ESILowerPorts.cpp - Lower ESI ports pass ----------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "../PassDetails.h"
10
15#include "circt/Support/LLVM.h"
16
17#include "mlir/Transforms/DialectConversion.h"
18
19#include "llvm/Support/MathExtras.h"
20
21namespace circt {
22namespace esi {
23#define GEN_PASS_DEF_LOWERESIPORTS
24#include "circt/Dialect/ESI/ESIPasses.h.inc"
25} // namespace esi
26} // namespace circt
27
28using namespace circt;
29using namespace circt::esi;
30using namespace circt::esi::detail;
31using namespace circt::hw;
32
33// Returns either the string dialect attr stored in 'op' going by the name
34// 'attrName' or 'def' if the attribute doesn't exist in 'op'.
35inline static StringRef getStringAttributeOr(Operation *op, StringRef attrName,
36 StringRef def) {
37 auto attr = op->getAttrOfType<StringAttr>(attrName);
38 if (attr)
39 return attr.getValue();
40 return def;
41}
42
43namespace {
44
45/// Per-channel ESI signaling protocol policies (ValidReady, FIFO, ValidOnly).
46/// These are stateless policy types with only static methods; they are never
47/// instantiated. They are used as the policy type parameter of the
48/// channel-port lowering containers below, which share their logic between
49/// scalar channel ports and arrays of channel ports.
50///
51/// Each protocol has one or more control signals associated with the channel
52/// data to, at minimum, indicate the validity of the data. The protocol defines
53/// how these signals are named and whether there is a backpressure handshake
54/// signal.
55///
56/// Each policy provides:
57/// - getValiditySuffix(module): suffix for the forward validity signal.
58/// - hasBackpressure: compile-time flag, true iff the protocol has a
59/// backpressure handshake signal.
60/// - getBackpressureSuffix(module): suffix for the backpressure handshake
61/// signal (only defined when 'hasBackpressure' is true).
62/// - wrap(b, loc, chanTy, data, validity): recreate a channel of type 'chanTy'
63/// from its 'data' and 'validity' signals; returns {channel, backpressure}
64/// (the backpressure value is null if the protocol has no backpressure
65/// signal).
66/// - unwrap(b, loc, chan, backpressure): break 'chan' into its {data,
67/// validity} signals ('backpressure' is unused for protocols without a
68/// backpressure signal).
69///
70/// Note: if we add a credit control signaling protocol in the future, this will
71/// have to be re-thought.
72
73/// ValidReady: 'valid' validity, 'ready' backpressure handshake.
74struct ValidReadyProtocol {
75 static constexpr bool hasBackpressure = true;
76 static StringRef getValiditySuffix(Operation *module) {
77 return getStringAttributeOr(module, extModPortValidSuffix, "_valid");
78 }
79 static StringRef getBackpressureSuffix(Operation *module) {
80 return getStringAttributeOr(module, extModPortReadySuffix, "_ready");
81 }
82 static std::pair<Value, Value> wrap(OpBuilder &b, Location loc,
83 ChannelType chanTy, Value data,
84 Value validity) {
85 assert(chanTy.getInner() == data.getType() &&
86 "channel inner type must match the data type");
87 auto wrap =
88 WrapValidReadyOp::create(b, loc, chanTy, b.getI1Type(), data, validity);
89 return {wrap.getChanOutput(), wrap.getReady()};
90 }
91 static std::pair<Value, Value> unwrap(OpBuilder &b, Location loc, Value chan,
92 Value backpressure) {
93 auto unwrap = UnwrapValidReadyOp::create(b, loc, chan, backpressure);
94 return {unwrap.getRawOutput(), unwrap.getValid()};
95 }
96};
97
98/// FIFO: 'empty' validity (empty == !valid), 'rden' (read-enable)
99/// "backpressure" handshake.
100struct FIFOProtocol {
101 static constexpr bool hasBackpressure = true;
102 static StringRef getValiditySuffix(Operation *module) {
103 return getStringAttributeOr(module, extModPortEmptySuffix, "_empty");
104 }
105 static StringRef getBackpressureSuffix(Operation *module) {
106 return getStringAttributeOr(module, extModPortRdenSuffix, "_rden");
107 }
108 static std::pair<Value, Value> wrap(OpBuilder &b, Location loc,
109 ChannelType chanTy, Value data,
110 Value validity) {
111 auto wrap = WrapFIFOOp::create(
112 b, loc, ArrayRef<Type>({chanTy, b.getI1Type()}), data, validity);
113 return {wrap.getChanOutput(), wrap.getRden()};
114 }
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);
118 return {unwrap.getData(), unwrap.getEmpty()};
119 }
120};
121
122/// ValidOnly: forward 'valid' validity, no backpressure handshake.
123struct ValidOnlyProtocol {
124 static constexpr bool hasBackpressure = false;
125 static StringRef getValiditySuffix(Operation *module) {
126 return getStringAttributeOr(module, extModPortValidSuffix, "_valid");
127 }
128 static std::pair<Value, Value> wrap(OpBuilder &b, Location loc,
129 ChannelType chanTy, Value data,
130 Value validity) {
131 auto wrap =
132 WrapValidOnlyOp::create(b, loc, data, validity, chanTy.getDataDelay());
133 return {wrap.getChanOutput(), Value()};
134 }
135 static std::pair<Value, Value> unwrap(OpBuilder &b, Location loc, Value chan,
136 Value /*backpressure*/) {
137 auto unwrap = UnwrapValidOnlyOp::create(b, loc, chan);
138 return {unwrap.getRawOutput(), unwrap.getValid()};
139 }
140};
141
142/// Return true if 'type' contains an ESI channel nested inside an aggregate.
143/// Used to detect (and reject) ports which embed channels in a way this pass
144/// cannot lower (e.g. arrays of arrays of channels, or structs of channels).
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);
149 }
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);
153 });
154 return false;
155}
156
157/// Lower a single ESI channel port into its constituent wire-level signals
158/// using the 'Protocol' signaling policy.
159template <typename Protocol>
160class ScalarChannelPort : public PortConversion {
161public:
163
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;
170
171private:
172 void buildInputSignals() override;
173 void buildOutputSignals() override;
174
175 // Port info for the lowered signals. 'backpressurePort' is only valid when
176 // 'Protocol::hasBackpressure' is true.
177 PortInfo dataPort, validityPort, backpressurePort;
178};
179
180/// Lower a port which is an array of ESI channels into arrays of the
181/// constituent wire-level signals (one array element per channel) using the
182/// 'Protocol' signaling policy.
183template <typename Protocol>
184class ArrayChannelPort : public PortConversion {
185public:
187
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;
194
195private:
196 void buildInputSignals() override;
197 void buildOutputSignals() override;
198
199 // Port info for the lowered signal arrays. 'backpressurePort' is only valid
200 // when 'Protocol::hasBackpressure' is true.
201 PortInfo dataPort, validityPort, backpressurePort;
202};
203
204// Emit an error about an unknown signaling standard on 'port'.
205static FailureOr<std::unique_ptr<PortConversion>>
206emitUnknownSignaling(Operation *op, hw::PortInfo port,
207 ChannelSignaling signaling) {
208 auto error =
209 op->emitOpError("encountered unknown signaling standard on port '")
210 << stringifyEnum(signaling) << "'";
211 error.attachNote(port.loc);
212 return error;
213}
214
215/// Instantiate the channel-port lowering container 'PortKind' (e.g.
216/// ScalarChannelPort or ArrayChannelPort) with the protocol policy selected by
217/// 'signaling'. Returns failure for an unknown signaling standard.
218template <template <typename> class PortKind>
219static FailureOr<std::unique_ptr<PortConversion>>
220buildChannelPort(PortConverterImpl &converter, hw::PortInfo port,
221 ChannelSignaling signaling) {
222 switch (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)};
229 }
230 return emitUnknownSignaling(converter.getModule(), port, signaling);
231}
232
233class ESIPortConversionBuilder : public PortConversionBuilder {
234public:
236 FailureOr<std::unique_ptr<PortConversion>> build(hw::PortInfo port) override {
237 return llvm::TypeSwitch<Type, FailureOr<std::unique_ptr<PortConversion>>>(
238 port.type)
239 .Case([&](esi::ChannelType chanTy)
240 -> FailureOr<std::unique_ptr<PortConversion>> {
241 return buildChannelPort<ScalarChannelPort>(converter, port,
242 chanTy.getSignaling());
243 })
244 .Case([&](hw::ArrayType arrTy)
245 -> FailureOr<std::unique_ptr<PortConversion>> {
246 auto chanTy = dyn_cast<esi::ChannelType>(arrTy.getElementType());
247 if (!chanTy) {
248 // Channels nested deeper than a top-level array of channels are
249 // not supported.
250 if (containsChannel(arrTy)) {
251 auto error = converter.getModule().emitOpError(
252 "cannot lower port containing channels nested inside an "
253 "aggregate other than a single array of channels");
254 error.attachNote(port.loc);
255 return error;
256 }
257 return PortConversionBuilder::build(port);
258 }
259 // The lowering decomposes the array element-by-element, so the size
260 // must be a known, non-zero constant. A parametric size makes
261 // `getNumElements()` return -1 and a zero size would assert in
262 // hw.array_create; emit a diagnostic rather than crash.
263 if (!isa<IntegerAttr>(arrTy.getSizeAttr()) ||
264 arrTy.getNumElements() == 0)
265 return converter.getModule()
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());
271 })
272 .Default([&](auto) { return PortConversionBuilder::build(port); });
273 }
274};
275} // namespace
276
277/// Extract element 'idx' from the array-typed value 'array'.
278static Value getArrayElement(OpBuilder &b, Location loc, Value array,
279 size_t idx) {
280 auto arrTy = cast<hw::ArrayType>(array.getType());
281 IntegerType idxType = b.getIntegerType(
282 std::max(1u, llvm::Log2_64_Ceil(arrTy.getNumElements())));
283 Value idxVal = hw::ConstantOp::create(b, loc, idxType, idx);
284 return hw::ArrayGetOp::create(b, loc, array, idxVal);
285}
286
287/// Pack a list of element values (with 'elements[i]' destined for array index
288/// 'i') into an hw.array. hw.array_create takes its operands in reverse order
289/// (operand 0 becomes the highest index), so the list is reversed to keep
290/// 'elements[i]' at array index 'i'.
291static Value packArray(OpBuilder &b, Location loc, ArrayRef<Value> elements) {
292 SmallVector<Value> reversed(elements.rbegin(), elements.rend());
293 return hw::ArrayCreateOp::create(b, loc, reversed);
294}
295
296//===----------------------------------------------------------------------===//
297// ScalarChannelPort
298//===----------------------------------------------------------------------===//
299
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);
305
306 StringRef inSuffix = getStringAttributeOr(module, extModPortInSuffix, "");
307 StringRef outSuffix = getStringAttributeOr(module, extModPortOutSuffix, "");
308
309 // The data and forward validity signals come into the module alongside the
310 // data.
311 Value data =
312 converter.createNewInput(origPort, inSuffix, chanTy.getInner(), dataPort);
313 Value validity = converter.createNewInput(
314 origPort, Protocol::getValiditySuffix(module) + inSuffix, i1,
315 validityPort);
316
317 Value backpressure;
318 if (body) {
319 ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
320 // Recreate the original channel value from the lowered signals. (A later
321 // pass takes care of eliminating the ESI ops.)
322 auto [chan, bp] = Protocol::wrap(b, b.getLoc(), chanTy, data, validity);
323 backpressure = bp;
324 body->getArgument(origPort.argNum).replaceAllUsesWith(chan);
325 }
326
327 // The backpressure handshake signal (if any) leaves the module.
328 if constexpr (Protocol::hasBackpressure)
329 converter.createNewOutput(
330 origPort, Protocol::getBackpressureSuffix(module) + outSuffix, i1,
331 backpressure, backpressurePort);
332}
333
334template <typename Protocol>
335void ScalarChannelPort<Protocol>::mapInputSignals(
336 OpBuilder &b, Operation *inst, Value, SmallVectorImpl<Value> &newOperands,
337 ArrayRef<Backedge> newResults) {
338 Value backpressure;
339 if constexpr (Protocol::hasBackpressure)
340 backpressure = newResults[backpressurePort.argNum];
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;
345}
346
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);
352
353 StringRef inSuffix = getStringAttributeOr(module, extModPortInSuffix, "");
354 StringRef outSuffix = getStringAttributeOr(module, extModPortOutSuffix, "");
355
356 // The backpressure handshake signal (if any) comes into the module.
357 Value backpressure;
358 if constexpr (Protocol::hasBackpressure)
359 backpressure = converter.createNewInput(
360 origPort, Protocol::getBackpressureSuffix(module) + inSuffix, i1,
361 backpressurePort);
362
363 Value data, validity;
364 if (body) {
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;
371 }
372
373 // The data and forward validity signals leave the module.
374 converter.createNewOutput(origPort, outSuffix, chanTy.getInner(), data,
375 dataPort);
376 converter.createNewOutput(origPort,
377 Protocol::getValiditySuffix(module) + outSuffix, i1,
378 validity, validityPort);
379}
380
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);
386 auto [chan, backpressure] =
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)
391 newOperands[backpressurePort.argNum] = backpressure;
392}
393
394//===----------------------------------------------------------------------===//
395// ArrayChannelPort
396//===----------------------------------------------------------------------===//
397
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);
407
408 StringRef inSuffix = getStringAttributeOr(module, extModPortInSuffix, "");
409 StringRef outSuffix = getStringAttributeOr(module, extModPortOutSuffix, "");
410
411 // The data and forward validity signal arrays come into the module alongside
412 // the data.
413 Value dataArr =
414 converter.createNewInput(origPort, inSuffix, dataArrTy, dataPort);
415 Value validityArr = converter.createNewInput(
416 origPort, Protocol::getValiditySuffix(module) + inSuffix, validityArrTy,
417 validityPort);
418
419 Value backpressureArr;
420 if (body) {
421 ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
422 // Recreate the original array of channels by wrapping each element's
423 // lowered signals back into a channel.
424 SmallVector<Value> chans, backpressures;
425 for (size_t i = 0; i < numElems; ++i) {
426 Value data = getArrayElement(b, b.getLoc(), dataArr, i);
427 Value validity = getArrayElement(b, b.getLoc(), validityArr, 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);
432 }
433 body->getArgument(origPort.argNum)
434 .replaceAllUsesWith(packArray(b, b.getLoc(), chans));
435 if (!backpressures.empty())
436 backpressureArr = packArray(b, b.getLoc(), backpressures);
437 }
438
439 // The backpressure handshake signal array (if any) leaves the module.
440 if constexpr (Protocol::hasBackpressure)
441 converter.createNewOutput(
442 origPort, Protocol::getBackpressureSuffix(module) + outSuffix,
443 validityArrTy, backpressureArr, backpressurePort);
444}
445
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();
453
454 Value backpressureArr;
455 if constexpr (Protocol::hasBackpressure)
456 backpressureArr = newResults[backpressurePort.argNum];
457
458 // Unwrap each channel element into its data and validity signals.
459 SmallVector<Value> datas, validities;
460 for (size_t i = 0; i < numElems; ++i) {
461 Value chan = getArrayElement(b, loc, chanArr, i);
462 Value backpressure;
463 if constexpr (Protocol::hasBackpressure)
464 backpressure = getArrayElement(b, loc, backpressureArr, i);
465 auto [data, validity] = Protocol::unwrap(b, loc, chan, backpressure);
466 datas.push_back(data);
467 validities.push_back(validity);
468 }
469 newOperands[dataPort.argNum] = packArray(b, loc, datas);
470 newOperands[validityPort.argNum] = packArray(b, loc, validities);
471}
472
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);
482
483 StringRef inSuffix = getStringAttributeOr(module, extModPortInSuffix, "");
484 StringRef outSuffix = getStringAttributeOr(module, extModPortOutSuffix, "");
485
486 // The backpressure handshake signal array (if any) comes into the module.
487 Value backpressureArr;
488 if constexpr (Protocol::hasBackpressure)
489 backpressureArr = converter.createNewInput(
490 origPort, Protocol::getBackpressureSuffix(module) + inSuffix,
491 validityArrTy, backpressurePort);
492
493 Value dataArr, validityArr;
494 if (body) {
495 auto *terminator = body->getTerminator();
496 ImplicitLocOpBuilder b(origPort.loc, terminator);
497 Value chanArr = terminator->getOperand(origPort.argNum);
498 // Unwrap each channel element into its data and validity signals.
499 SmallVector<Value> datas, validities;
500 for (size_t i = 0; i < numElems; ++i) {
501 Value chan = getArrayElement(b, b.getLoc(), chanArr, i);
502 Value backpressure;
503 if constexpr (Protocol::hasBackpressure)
504 backpressure = getArrayElement(b, b.getLoc(), backpressureArr, i);
505 auto [data, validity] =
506 Protocol::unwrap(b, b.getLoc(), chan, backpressure);
507 datas.push_back(data);
508 validities.push_back(validity);
509 }
510 dataArr = packArray(b, b.getLoc(), datas);
511 validityArr = packArray(b, b.getLoc(), validities);
512 }
513
514 // The data and forward validity signal arrays leave the module.
515 converter.createNewOutput(origPort, outSuffix, dataArrTy, dataArr, dataPort);
516 converter.createNewOutput(origPort,
517 Protocol::getValiditySuffix(module) + outSuffix,
518 validityArrTy, validityArr, validityPort);
519}
520
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();
529
530 Value dataArr = newResults[dataPort.argNum];
531 Value validityArr = newResults[validityPort.argNum];
532
533 // Wrap each element's data and validity signals back into a channel.
534 SmallVector<Value> chans, backpressures;
535 for (size_t i = 0; i < numElems; ++i) {
536 Value data = getArrayElement(b, loc, dataArr, i);
537 Value validity = getArrayElement(b, loc, validityArr, i);
538 auto [chan, bp] = Protocol::wrap(b, loc, chanTy, data, validity);
539 chans.push_back(chan);
540 if (bp)
541 backpressures.push_back(bp);
542 }
543 inst->getResult(origPort.argNum).replaceAllUsesWith(packArray(b, loc, chans));
544 if constexpr (Protocol::hasBackpressure)
545 newOperands[backpressurePort.argNum] = packArray(b, loc, backpressures);
546}
547
548namespace {
549/// Lower ESI channel ports to their constituent wire-level signals.
550struct ESIPortsPass : public circt::esi::impl::LowerESIPortsBase<ESIPortsPass> {
551 void runOnOperation() override;
552};
553} // anonymous namespace
554
555/// Iterate through the `hw.module[.extern]`s and lower their ports.
556void ESIPortsPass::runOnOperation() {
557 ModuleOp top = getOperation();
558 // Find all modules and run port conversion on them.
559 circt::hw::InstanceGraph &instanceGraph =
560 getAnalysis<circt::hw::InstanceGraph>();
561
562 for (auto mod : top.getOps<HWMutableModuleLike>()) {
563 if (failed(
564 PortConverter<ESIPortConversionBuilder>(instanceGraph, mod).run()))
565 return signalPassFailure();
566 }
567}
568
569std::unique_ptr<OperationPass<ModuleOp>>
571 return std::make_unique<ESIPortsPass>();
572}
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)
Definition OM.cpp:111
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
Definition Types.h:129
create(elements, Type result_type=None)
Definition hw.py:486
create(array_value, idx)
Definition hw.py:453
create(data_type, value)
Definition hw.py:433
constexpr StringRef extModPortValidSuffix
Suffix lowered valid ports with this suffix.
Definition ESIDialect.h:46
constexpr StringRef extModPortRdenSuffix
Suffix lowered read enable ports with this suffix.
Definition ESIDialect.h:52
constexpr StringRef extModPortReadySuffix
Suffix lowered ready ports with this suffix.
Definition ESIDialect.h:49
constexpr StringRef extModPortInSuffix
Suffix all lowered input ports with this suffix. Defaults to nothing.
Definition ESIDialect.h:41
std::unique_ptr< OperationPass< ModuleOp > > createESIPortLoweringPass()
constexpr StringRef extModPortOutSuffix
Suffix all lowered output ports with this suffix. Defaults to nothing.
Definition ESIDialect.h:43
constexpr StringRef extModPortEmptySuffix
Suffix lowered empty ports with this suffix.
Definition ESIDialect.h:55
void error(Twine message)
Definition LSPUtils.cpp:16
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, List[str] cmdline_args=sys.argv)
mlir::Type type
Definition HWTypes.h:34
This holds the name, type, direction of a module's ports.