CIRCT 23.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
16#include "circt/Support/LLVM.h"
18
19#include "mlir/Transforms/DialectConversion.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// Base class for all ESI signaling standards, which handles potentially funky
46// attribute-based naming conventions.
47struct ESISignalingStandad : public PortConversion {
48 ESISignalingStandad(PortConverterImpl &converter, hw::PortInfo origPort)
49 : PortConversion(converter, origPort) {}
50};
51
52/// Implement the Valid/Ready signaling standard.
53class ValidReady : public ESISignalingStandad {
54public:
55 ValidReady(PortConverterImpl &converter, hw::PortInfo origPort)
56 : ESISignalingStandad(converter, origPort), validPort(origPort),
57 readyPort(origPort) {}
58
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;
65
66private:
67 void buildInputSignals() override;
68 void buildOutputSignals() override;
69
70 // Keep around information about the port numbers of the relevant ports and
71 // use that later to update the instances.
72 PortInfo validPort, readyPort, dataPort;
73};
74
75/// Implement the FIFO signaling standard.
76class FIFO : public ESISignalingStandad {
77public:
78 FIFO(PortConverterImpl &converter, hw::PortInfo origPort)
79 : ESISignalingStandad(converter, origPort) {}
80
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;
87
88private:
89 void buildInputSignals() override;
90 void buildOutputSignals() override;
91
92 // Keep around information about the port numbers of the relevant ports and
93 // use that later to update the instances.
94 PortInfo rdenPort, emptyPort, dataPort;
95};
96
97/// Implement the Valid-only signaling standard (no backpressure).
98class ValidOnly : public ESISignalingStandad {
99public:
100 ValidOnly(PortConverterImpl &converter, hw::PortInfo origPort)
101 : ESISignalingStandad(converter, origPort), validPort(origPort) {}
102
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;
109
110private:
111 void buildInputSignals() override;
112 void buildOutputSignals() override;
113
114 // Keep around information about the port numbers of the relevant ports and
115 // use that later to update the instances.
116 PortInfo validPort, dataPort;
117};
118
119class ESIPortConversionBuilder : public PortConversionBuilder {
120public:
122 FailureOr<std::unique_ptr<PortConversion>> build(hw::PortInfo port) override {
123 return llvm::TypeSwitch<Type, FailureOr<std::unique_ptr<PortConversion>>>(
124 port.type)
125 .Case([&](esi::ChannelType chanTy)
126 -> FailureOr<std::unique_ptr<PortConversion>> {
127 // Determine which ESI signaling standard is specified.
128 ChannelSignaling signaling = chanTy.getSignaling();
129 if (signaling == ChannelSignaling::ValidReady)
130 return {std::make_unique<ValidReady>(converter, port)};
131
132 if (signaling == ChannelSignaling::FIFO)
133 return {std::make_unique<FIFO>(converter, port)};
134
135 if (signaling == ChannelSignaling::ValidOnly)
136 return {std::make_unique<ValidOnly>(converter, port)};
137
138 auto error = converter.getModule().emitOpError(
139 "encountered unknown signaling standard on port '")
140 << stringifyEnum(signaling) << "'";
141 error.attachNote(port.loc);
142 return error;
143 })
144 .Default([&](auto) { return PortConversionBuilder::build(port); });
145 }
146};
147} // namespace
148
149void ValidReady::buildInputSignals() {
150 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
151
152 StringRef inSuffix =
153 getStringAttributeOr(converter.getModule(), extModPortInSuffix, "");
154 StringRef validSuffix(getStringAttributeOr(converter.getModule(),
155 extModPortValidSuffix, "_valid"));
156
157 // When we find one, add a data and valid signal to the new args.
158 Value data = converter.createNewInput(
159 origPort, inSuffix, cast<esi::ChannelType>(origPort.type).getInner(),
160 dataPort);
161 Value valid =
162 converter.createNewInput(origPort, validSuffix + inSuffix, i1, validPort);
163
164 Value ready;
165 if (body) {
166 ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
167 // Build the ESI wrap operation to translate the lowered signals to what
168 // they were. (A later pass takes care of eliminating the ESI ops.)
169 auto wrap = WrapValidReadyOp::create(b, data, valid);
170 ready = wrap.getReady();
171 // Replace uses of the old ESI port argument with the new one from the
172 // wrap.
173 body->getArgument(origPort.argNum).replaceAllUsesWith(wrap.getChanOutput());
174 }
175
176 StringRef readySuffix = getStringAttributeOr(converter.getModule(),
177 extModPortReadySuffix, "_ready");
178 StringRef outSuffix =
179 getStringAttributeOr(converter.getModule(), extModPortOutSuffix, "");
180 converter.createNewOutput(origPort, readySuffix + outSuffix, i1, ready,
181 readyPort);
182}
183
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();
192}
193
194void ValidReady::buildOutputSignals() {
195 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
196
197 StringRef readySuffix = getStringAttributeOr(converter.getModule(),
198 extModPortReadySuffix, "_ready");
199 StringRef inSuffix =
200 getStringAttributeOr(converter.getModule(), extModPortInSuffix, "");
201
202 Value ready =
203 converter.createNewInput(origPort, readySuffix + inSuffix, i1, readyPort);
204 Value data, valid;
205 if (body) {
206 auto *terminator = body->getTerminator();
207 ImplicitLocOpBuilder b(origPort.loc, terminator);
208
209 auto unwrap = UnwrapValidReadyOp::create(
210 b, terminator->getOperand(origPort.argNum), ready);
211 data = unwrap.getRawOutput();
212 valid = unwrap.getValid();
213 }
214
215 // New outputs.
216 StringRef outSuffix =
217 getStringAttributeOr(converter.getModule(), extModPortOutSuffix, "");
218 StringRef validSuffix = getStringAttributeOr(converter.getModule(),
219 extModPortValidSuffix, "_valid");
220 converter.createNewOutput(origPort, outSuffix,
221 cast<esi::ChannelType>(origPort.type).getInner(),
222 data, dataPort);
223 converter.createNewOutput(origPort, validSuffix + outSuffix, i1, valid,
224 validPort);
225}
226
227void ValidReady::mapOutputSignals(OpBuilder &b, Operation *inst,
228 Value instValue,
229 SmallVectorImpl<Value> &newOperands,
230 ArrayRef<Backedge> newResults) {
231 auto wrap =
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();
236}
237
238void FIFO::buildInputSignals() {
239 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
240 auto chanTy = cast<ChannelType>(origPort.type);
241
242 StringRef rdenSuffix(getStringAttributeOr(converter.getModule(),
243 extModPortRdenSuffix, "_rden"));
244 StringRef emptySuffix(getStringAttributeOr(converter.getModule(),
245 extModPortEmptySuffix, "_empty"));
246 StringRef inSuffix =
247 getStringAttributeOr(converter.getModule(), extModPortInSuffix, "");
248 StringRef outSuffix =
249 getStringAttributeOr(converter.getModule(), extModPortOutSuffix, "");
250
251 // When we find one, add a data and valid signal to the new args.
252 Value data = converter.createNewInput(
253 origPort, inSuffix, cast<esi::ChannelType>(origPort.type).getInner(),
254 dataPort);
255 Value empty =
256 converter.createNewInput(origPort, emptySuffix + inSuffix, i1, emptyPort);
257
258 Value rden;
259 if (body) {
260 ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
261 // Build the ESI wrap operation to translate the lowered signals to what
262 // they were. (A later pass takes care of eliminating the ESI ops.)
263 auto wrap = WrapFIFOOp::create(b, ArrayRef<Type>({chanTy, b.getI1Type()}),
264 data, empty);
265 rden = wrap.getRden();
266 // Replace uses of the old ESI port argument with the new one from the
267 // wrap.
268 body->getArgument(origPort.argNum).replaceAllUsesWith(wrap.getChanOutput());
269 }
270
271 converter.createNewOutput(origPort, rdenSuffix + outSuffix, i1, rden,
272 rdenPort);
273}
274
275void FIFO::mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
276 SmallVectorImpl<Value> &newOperands,
277 ArrayRef<Backedge> newResults) {
278 auto unwrap =
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();
283}
284
285void FIFO::buildOutputSignals() {
286 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
287
288 StringRef inSuffix =
289 getStringAttributeOr(converter.getModule(), extModPortInSuffix, "");
290 StringRef outSuffix =
291 getStringAttributeOr(converter.getModule(), extModPortOutSuffix, "");
292 StringRef rdenSuffix(getStringAttributeOr(converter.getModule(),
293 extModPortRdenSuffix, "_rden"));
294 StringRef emptySuffix(getStringAttributeOr(converter.getModule(),
295 extModPortEmptySuffix, "_empty"));
296 Value rden =
297 converter.createNewInput(origPort, rdenSuffix + inSuffix, i1, rdenPort);
298 Value data, empty;
299 if (body) {
300 auto *terminator = body->getTerminator();
301 ImplicitLocOpBuilder b(origPort.loc, terminator);
302
303 auto unwrap =
304 UnwrapFIFOOp::create(b, terminator->getOperand(origPort.argNum), rden);
305 data = unwrap.getData();
306 empty = unwrap.getEmpty();
307 }
308
309 // New outputs.
310 converter.createNewOutput(origPort, outSuffix,
311 cast<esi::ChannelType>(origPort.type).getInner(),
312 data, dataPort);
313 converter.createNewOutput(origPort, emptySuffix + outSuffix, i1, empty,
314 emptyPort);
315}
316
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();
325}
326
327//===----------------------------------------------------------------------===//
328// ValidOnly signaling standard implementation.
329//===----------------------------------------------------------------------===//
330
331void ValidOnly::buildInputSignals() {
332 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
333
334 StringRef inSuffix =
335 getStringAttributeOr(converter.getModule(), extModPortInSuffix, "");
336 StringRef validSuffix(getStringAttributeOr(converter.getModule(),
337 extModPortValidSuffix, "_valid"));
338
339 // When we find one, add a data and valid signal to the new args.
340 Value data = converter.createNewInput(
341 origPort, inSuffix, cast<esi::ChannelType>(origPort.type).getInner(),
342 dataPort);
343 Value valid =
344 converter.createNewInput(origPort, validSuffix + inSuffix, i1, validPort);
345
346 if (body) {
347 ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
348 auto wrap = WrapValidOnlyOp::create(b, data, valid);
349 body->getArgument(origPort.argNum).replaceAllUsesWith(wrap.getChanOutput());
350 }
351 // No ready/rden output port for ValidOnly.
352}
353
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();
361}
362
363void ValidOnly::buildOutputSignals() {
364 Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
365
366 Value data, valid;
367 if (body) {
368 auto *terminator = body->getTerminator();
369 ImplicitLocOpBuilder b(origPort.loc, terminator);
370
371 auto unwrap =
372 UnwrapValidOnlyOp::create(b, terminator->getOperand(origPort.argNum));
373 data = unwrap.getRawOutput();
374 valid = unwrap.getValid();
375 }
376
377 // New outputs.
378 StringRef outSuffix =
379 getStringAttributeOr(converter.getModule(), extModPortOutSuffix, "");
380 StringRef validSuffix = getStringAttributeOr(converter.getModule(),
381 extModPortValidSuffix, "_valid");
382 converter.createNewOutput(origPort, outSuffix,
383 cast<esi::ChannelType>(origPort.type).getInner(),
384 data, dataPort);
385 converter.createNewOutput(origPort, validSuffix + outSuffix, i1, valid,
386 validPort);
387 // No ready/rden input port for ValidOnly.
388}
389
390void ValidOnly::mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
391 SmallVectorImpl<Value> &newOperands,
392 ArrayRef<Backedge> newResults) {
393 auto wrap =
394 WrapValidOnlyOp::create(b, inst->getLoc(), newResults[dataPort.argNum],
395 newResults[validPort.argNum]);
396 inst->getResult(origPort.argNum).replaceAllUsesWith(wrap.getChanOutput());
397}
398
399namespace {
400/// Convert all the ESI ports on modules to some lower construct. SV
401/// interfaces for now on external modules, ready/valid to modules defined
402/// internally. In the future, it may be possible to select a different
403/// format.
404struct ESIPortsPass : public circt::esi::impl::LowerESIPortsBase<ESIPortsPass> {
405 void runOnOperation() override;
406
407private:
408 bool updateFunc(HWModuleExternOp mod);
409 void updateInstance(HWModuleExternOp mod, InstanceOp inst);
410 ESIHWBuilder *build;
411};
412} // anonymous namespace
413
414/// Iterate through the `hw.module[.extern]`s and lower their ports.
415void ESIPortsPass::runOnOperation() {
416 ModuleOp top = getOperation();
417 ESIHWBuilder b(top);
418 build = &b;
419
420 // Find all externmodules and try to modify them. Remember the modified
421 // ones.
422 DenseMap<SymbolRefAttr, HWModuleExternOp> externModsMutated;
423 for (auto mod : top.getOps<HWModuleExternOp>())
424 if (mod->hasAttrOfType<UnitAttr>(extModBundleSignalsAttrName) &&
425 updateFunc(mod))
426 externModsMutated[FlatSymbolRefAttr::get(mod)] = mod;
427
428 // Find all instances and update them.
429 top.walk([&externModsMutated, this](InstanceOp inst) {
430 auto mapIter = externModsMutated.find(inst.getModuleNameAttr());
431 if (mapIter != externModsMutated.end())
432 updateInstance(mapIter->second, inst);
433 });
434
435 // Find all modules and run port conversion on them.
436 circt::hw::InstanceGraph &instanceGraph =
437 getAnalysis<circt::hw::InstanceGraph>();
438
439 for (auto mod : top.getOps<HWMutableModuleLike>()) {
440 if (failed(
441 PortConverter<ESIPortConversionBuilder>(instanceGraph, mod).run()))
442 return signalPassFailure();
443 }
444
445 build = nullptr;
446}
447
448/// Convert all input and output ChannelTypes into SV Interfaces. For inputs,
449/// just switch the type to `ModportType`. For outputs, append a `ModportType`
450/// to the inputs and remove the output channel from the results. Returns true
451/// if 'mod' was updated. Delay updating the instances to amortize the IR walk
452/// over all the module updates.
453bool ESIPortsPass::updateFunc(HWModuleExternOp mod) {
454 auto *ctxt = &getContext();
455
456 bool updated = false;
457
458 SmallVector<Attribute> newArgNames, newResultNames;
459 SmallVector<Location> newArgLocs, newResultLocs;
460
461 // Reconstruct the list of operand types, changing the type whenever an ESI
462 // port is found.
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));
469 nextArgNo++;
470
471 if (!chanTy) {
472 newArgTypes.push_back(argTy);
473 continue;
474 }
475
476 // When we find one, construct an interface, and add the 'source' modport
477 // to the type list.
478 auto iface = build->getOrConstructInterface(chanTy);
479 newArgTypes.push_back(iface.getModportType(ESIHWBuilder::sourceStr));
480 updated = true;
481 }
482
483 // Iterate through the results and append to one of the two below lists. The
484 // first for non-ESI-ports. The second, ports which have been re-located to
485 // an operand.
486 SmallVector<Type, 8> newResultTypes;
487 SmallVector<DictionaryAttr, 4> newResultAttrs;
488 for (size_t resNum = 0, numRes = mod.getNumOutputPorts(); resNum < numRes;
489 ++resNum) {
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);
494 if (!chanTy) {
495 newResultTypes.push_back(resTy);
496 newResultNames.push_back(resNameAttr);
497 newResultLocs.push_back(resLocAttr);
498 continue;
499 }
500
501 // When we find one, construct an interface, and add the 'sink' modport to
502 // the type list.
503 sv::InterfaceOp iface = build->getOrConstructInterface(chanTy);
504 sv::ModportType sinkPort = iface.getModportType(ESIHWBuilder::sinkStr);
505 newArgTypes.push_back(sinkPort);
506 newArgNames.push_back(resNameAttr);
507 newArgLocs.push_back(resLocAttr);
508 updated = true;
509 }
510
511 mod->removeAttr(extModBundleSignalsAttrName);
512 if (!updated)
513 return false;
514
515 // Set the new types.
516 auto newFuncType = FunctionType::get(ctxt, newArgTypes, newResultTypes);
517 auto newModType =
518 hw::detail::fnToMod(newFuncType, newArgNames, newResultNames);
519 mod.setHWModuleType(newModType);
520 mod.setInputLocs(newArgLocs);
521 mod.setOutputLocs(newResultLocs);
522 return true;
523}
524
525static StringRef getOperandName(Value operand) {
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());
530 } else {
531 auto *srcOp = operand.getDefiningOp();
532 if (auto instOp = dyn_cast<InstanceOp>(srcOp))
533 return instOp.getInstanceName();
534
535 if (auto srcName = srcOp->getAttrOfType<StringAttr>("name"))
536 return srcName.getValue();
537 }
538 return "";
539}
540
541/// Create a reasonable name for a SV interface instance.
542static std::string &constructInstanceName(Value operand, sv::InterfaceOp iface,
543 std::string &name) {
544 llvm::raw_string_ostream s(name);
545 // Drop the "IValidReady_" part of the interface name.
546 s << llvm::toLower(iface.getSymName()[12]) << iface.getSymName().substr(13);
547
548 // Indicate to where the source is connected.
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();
556 }
557
558 // Indicate to where the sink is connected.
559 StringRef operName = getOperandName(operand);
560 if (!operName.empty())
561 s << "From" << llvm::toUpper(operName[0]) << operName.substr(1);
562 return s.str();
563}
564
565/// Update an instance of an updated module by adding `esi.(un)wrap.iface`
566/// around the instance. Create a new instance at the end from the lists built
567/// up before.
568void ESIPortsPass::updateInstance(HWModuleExternOp mod, InstanceOp inst) {
569 using namespace circt::sv;
570 circt::ImplicitLocOpBuilder instBuilder(inst.getLoc(), inst);
571
572 // op counter for error reporting purposes.
573 size_t opNum = 0;
574 // List of new operands.
575 SmallVector<Value, 16> newOperands;
576
577 // Fill the new operand list with old plain operands and mutated ones.
578 std::string nameStringBuffer; // raw_string_ostream uses std::string.
579 for (auto op : inst.getOperands()) {
580 auto instChanTy = dyn_cast<ChannelType>(op.getType());
581 if (!instChanTy) {
582 newOperands.push_back(op);
583 ++opNum;
584 continue;
585 }
586
587 // Get the interface from the cache, and make sure it's the same one as
588 // being used in the module.
589 auto iface = build->getOrConstructInterface(instChanTy);
590 if (iface.getModportType(ESIHWBuilder::sourceStr) !=
591 mod.getInputTypes()[opNum]) {
592 inst.emitOpError("ESI ChannelType (operand #")
593 << opNum << ") doesn't match module!";
594 ++opNum;
595 newOperands.push_back(op);
596 continue;
597 }
598 ++opNum;
599
600 // Build a gasket by instantiating an interface, connecting one end to an
601 // `esi.unwrap.iface` and the other end to the instance.
602 auto ifaceInst =
603 InterfaceInstanceOp::create(instBuilder, iface.getInterfaceType());
604 nameStringBuffer.clear();
605 ifaceInst->setAttr(
606 "name",
607 StringAttr::get(mod.getContext(),
608 constructInstanceName(op, iface, nameStringBuffer)));
609 GetModportOp sinkModport =
610 GetModportOp::create(instBuilder, ifaceInst, ESIHWBuilder::sinkStr);
611 UnwrapSVInterfaceOp::create(instBuilder, op, sinkModport);
612 GetModportOp sourceModport =
613 GetModportOp::create(instBuilder, ifaceInst, ESIHWBuilder::sourceStr);
614 // Finally, add the correct modport to the list of operands.
615 newOperands.push_back(sourceModport);
616 }
617
618 // Go through the results and get both a list of the plain old values being
619 // produced and their types.
620 SmallVector<Value, 8> newResults;
621 SmallVector<Type, 8> newResultTypes;
622 for (size_t resNum = 0, numRes = inst.getNumResults(); resNum < numRes;
623 ++resNum) {
624 Value res = inst.getResult(resNum);
625 auto instChanTy = dyn_cast<ChannelType>(res.getType());
626 if (!instChanTy) {
627 newResults.push_back(res);
628 newResultTypes.push_back(res.getType());
629 continue;
630 }
631
632 // Get the interface from the cache, and make sure it's the same one as
633 // being used in the module.
634 auto iface = build->getOrConstructInterface(instChanTy);
635 if (iface.getModportType(ESIHWBuilder::sinkStr) !=
636 mod.getInputTypes()[opNum]) {
637 inst.emitOpError("ESI ChannelType (result #")
638 << resNum << ", operand #" << opNum << ") doesn't match module!";
639 ++opNum;
640 newResults.push_back(res);
641 newResultTypes.push_back(res.getType());
642 continue;
643 }
644 ++opNum;
645
646 // Build a gasket by instantiating an interface, connecting one end to an
647 // `esi.wrap.iface` and the other end to the instance. Append it to the
648 // operand list.
649 auto ifaceInst =
650 InterfaceInstanceOp::create(instBuilder, iface.getInterfaceType());
651 nameStringBuffer.clear();
652 ifaceInst->setAttr(
653 "name",
654 StringAttr::get(mod.getContext(),
655 constructInstanceName(res, iface, nameStringBuffer)));
656 GetModportOp sourceModport =
657 GetModportOp::create(instBuilder, ifaceInst, ESIHWBuilder::sourceStr);
658 auto newChannel =
659 WrapSVInterfaceOp::create(instBuilder, res.getType(), sourceModport);
660 // Connect all the old users of the output channel with the newly
661 // wrapped replacement channel.
662 res.replaceAllUsesWith(newChannel);
663 GetModportOp sinkModport =
664 GetModportOp::create(instBuilder, ifaceInst, ESIHWBuilder::sinkStr);
665 // And add the modport on the other side to the new operand list.
666 newOperands.push_back(sinkModport);
667 }
668
669 // Create the new instance!
670 auto newInst = hw::InstanceOp::create(
671 instBuilder, mod, inst.getInstanceNameAttr(), newOperands,
672 inst.getParameters(), inst.getInnerSymAttr());
673
674 // Go through the old list of non-ESI result values, and replace them with
675 // the new non-ESI results.
676 for (size_t resNum = 0, numRes = newResults.size(); resNum < numRes;
677 ++resNum) {
678 newResults[resNum].replaceAllUsesWith(newInst.getResult(resNum));
679 }
680 // Erase the old instance!
681 inst.erase();
682}
683
684std::unique_ptr<OperationPass<ModuleOp>>
686 return std::make_unique<ESIPortsPass>();
687}
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)
Definition OM.cpp:111
static InstancePath empty
Assist the lowering steps for conversions which need to create auxiliary IR.
Definition PassDetails.h:56
static constexpr char sinkStr[]
Definition PassDetails.h:78
static constexpr char sourceStr[]
Definition PassDetails.h:77
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.
Definition Types.h:120
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:55
constexpr StringRef extModPortValidSuffix
Suffix lowered valid ports with this suffix.
Definition ESIDialect.h:51
constexpr StringRef extModPortRdenSuffix
Suffix lowered read enable ports with this suffix.
Definition ESIDialect.h:57
constexpr StringRef extModPortReadySuffix
Suffix lowered ready ports with this suffix.
Definition ESIDialect.h:54
constexpr StringRef extModBundleSignalsAttrName
Name of dialect attribute which governs whether or not to bundle (i.e.
Definition ESIDialect.h:39
constexpr StringRef extModPortInSuffix
Suffix all lowered input ports with this suffix. Defaults to nothing.
Definition ESIDialect.h:46
std::unique_ptr< OperationPass< ModuleOp > > createESIPortLoweringPass()
constexpr StringRef extModPortOutSuffix
Suffix all lowered output ports with this suffix. Defaults to nothing.
Definition ESIDialect.h:48
constexpr StringRef extModPortEmptySuffix
Suffix lowered empty ports with this suffix.
Definition ESIDialect.h:60
ModuleType fnToMod(Operation *op, ArrayRef< Attribute > inputNames, ArrayRef< Attribute > outputNames)
Definition HWTypes.cpp:1052
void error(Twine message)
Definition LSPUtils.cpp:16
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition esi.py:1
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
Definition codegen.py:445
mlir::Type type
Definition HWTypes.h:31
This holds the name, type, direction of a module's ports.