9 #include "../PassDetails.h"
19 #include "mlir/Transforms/DialectConversion.h"
23 #define GEN_PASS_DEF_LOWERESIPORTS
24 #include "circt/Dialect/ESI/ESIPasses.h.inc"
28 using namespace circt;
37 auto attr = op->getAttrOfType<StringAttr>(attrName);
39 return attr.getValue();
53 class 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;
76 class 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;
100 FailureOr<std::unique_ptr<PortConversion>> build(hw::PortInfo port)
override {
101 return llvm::TypeSwitch<Type, FailureOr<std::unique_ptr<PortConversion>>>(
104 -> FailureOr<std::unique_ptr<PortConversion>> {
106 ChannelSignaling signaling = chanTy.getSignaling();
107 if (signaling == ChannelSignaling::ValidReady)
108 return {std::make_unique<ValidReady>(converter, port)};
110 if (signaling == ChannelSignaling::FIFO)
111 return {std::make_unique<FIFO>(converter, port)};
113 auto error = converter.getModule().emitOpError(
114 "encountered unknown signaling standard on port '")
115 << stringifyEnum(signaling) <<
"'";
116 error.attachNote(port.loc);
124 void ValidReady::buildInputSignals() {
133 Value
data = converter.createNewInput(
134 origPort, inSuffix, cast<esi::ChannelType>(origPort.type).getInner(),
137 converter.createNewInput(origPort, validSuffix + inSuffix, i1, validPort);
141 ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
144 auto wrap = b.create<WrapValidReadyOp>(
data, valid);
145 ready =
wrap.getReady();
148 body->getArgument(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
153 StringRef outSuffix =
155 converter.createNewOutput(origPort, readySuffix + outSuffix, i1, ready,
159 void ValidReady::mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
160 SmallVectorImpl<Value> &newOperands,
161 ArrayRef<Backedge> newResults) {
162 auto unwrap = b.create<UnwrapValidReadyOp>(inst->getLoc(),
163 inst->getOperand(origPort.argNum),
164 newResults[readyPort.argNum]);
165 newOperands[dataPort.argNum] =
unwrap.getRawOutput();
166 newOperands[validPort.argNum] =
unwrap.getValid();
169 void ValidReady::buildOutputSignals() {
178 converter.createNewInput(origPort, readySuffix + inSuffix, i1, readyPort);
181 auto *terminator = body->getTerminator();
182 ImplicitLocOpBuilder b(origPort.loc, terminator);
184 auto unwrap = b.create<UnwrapValidReadyOp>(
185 terminator->getOperand(origPort.argNum), ready);
187 valid =
unwrap.getValid();
191 StringRef outSuffix =
195 converter.createNewOutput(origPort, outSuffix,
196 cast<esi::ChannelType>(origPort.type).getInner(),
198 converter.createNewOutput(origPort, validSuffix + outSuffix, i1, valid,
202 void ValidReady::mapOutputSignals(OpBuilder &b, Operation *inst,
204 SmallVectorImpl<Value> &newOperands,
205 ArrayRef<Backedge> newResults) {
207 b.create<WrapValidReadyOp>(inst->getLoc(), newResults[dataPort.argNum],
208 newResults[validPort.argNum]);
209 inst->getResult(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
210 newOperands[readyPort.argNum] =
wrap.getReady();
213 void FIFO::buildInputSignals() {
215 auto chanTy = cast<ChannelType>(origPort.type);
223 StringRef outSuffix =
227 Value
data = converter.createNewInput(
228 origPort, inSuffix, cast<esi::ChannelType>(origPort.type).getInner(),
231 converter.createNewInput(origPort, emptySuffix + inSuffix, i1, emptyPort);
235 ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
238 auto wrap = b.create<WrapFIFOOp>(ArrayRef<Type>({chanTy, b.getI1Type()}),
240 rden =
wrap.getRden();
243 body->getArgument(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
246 converter.createNewOutput(origPort, rdenSuffix + outSuffix, i1, rden,
250 void FIFO::mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
251 SmallVectorImpl<Value> &newOperands,
252 ArrayRef<Backedge> newResults) {
254 b.create<UnwrapFIFOOp>(inst->getLoc(), inst->getOperand(origPort.argNum),
255 newResults[rdenPort.argNum]);
256 newOperands[dataPort.argNum] =
unwrap.getData();
257 newOperands[emptyPort.argNum] =
unwrap.getEmpty();
260 void FIFO::buildOutputSignals() {
265 StringRef outSuffix =
272 converter.createNewInput(origPort, rdenSuffix + inSuffix, i1, rdenPort);
275 auto *terminator = body->getTerminator();
276 ImplicitLocOpBuilder b(origPort.loc, terminator);
279 b.create<UnwrapFIFOOp>(terminator->getOperand(origPort.argNum), rden);
285 converter.createNewOutput(origPort, outSuffix,
286 cast<esi::ChannelType>(origPort.type).getInner(),
288 converter.createNewOutput(origPort, emptySuffix + outSuffix, i1,
empty,
292 void FIFO::mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
293 SmallVectorImpl<Value> &newOperands,
294 ArrayRef<Backedge> newResults) {
295 auto wrap = b.create<WrapFIFOOp>(
296 inst->getLoc(), ArrayRef<Type>({origPort.type, b.getI1Type()}),
297 newResults[dataPort.argNum], newResults[emptyPort.argNum]);
298 inst->getResult(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
299 newOperands[rdenPort.argNum] =
wrap.getRden();
307 struct ESIPortsPass :
public circt::esi::impl::LowerESIPortsBase<ESIPortsPass> {
308 void runOnOperation()
override;
311 bool updateFunc(HWModuleExternOp mod);
312 void updateInstance(HWModuleExternOp mod, InstanceOp inst);
318 void ESIPortsPass::runOnOperation() {
319 ModuleOp top = getOperation();
325 DenseMap<SymbolRefAttr, HWModuleExternOp> externModsMutated;
326 for (
auto mod : top.getOps<HWModuleExternOp>())
332 top.walk([&externModsMutated,
this](InstanceOp inst) {
333 auto mapIter = externModsMutated.find(inst.getModuleNameAttr());
334 if (mapIter != externModsMutated.end())
335 updateInstance(mapIter->second, inst);
340 getAnalysis<circt::hw::InstanceGraph>();
342 for (
auto mod : top.getOps<HWMutableModuleLike>()) {
345 return signalPassFailure();
356 bool ESIPortsPass::updateFunc(HWModuleExternOp mod) {
357 auto *
ctxt = &getContext();
359 bool updated =
false;
361 SmallVector<Attribute> newArgNames, newResultNames;
362 SmallVector<Location> newArgLocs, newResultLocs;
366 SmallVector<Type, 16> newArgTypes;
367 size_t nextArgNo = 0;
368 for (
auto argTy : mod.getInputTypes()) {
369 auto chanTy = dyn_cast<ChannelType>(argTy);
370 newArgNames.push_back(mod.getInputNameAttr(nextArgNo));
371 newArgLocs.push_back(mod.getInputLoc(nextArgNo));
375 newArgTypes.push_back(argTy);
381 auto iface = build->getOrConstructInterface(chanTy);
389 SmallVector<Type, 8> newResultTypes;
390 SmallVector<DictionaryAttr, 4> newResultAttrs;
391 for (
size_t resNum = 0, numRes = mod.getNumOutputPorts(); resNum < numRes;
393 Type resTy = mod.getOutputTypes()[resNum];
394 auto chanTy = dyn_cast<ChannelType>(resTy);
395 auto resNameAttr = mod.getOutputNameAttr(resNum);
396 auto resLocAttr = mod.getOutputLoc(resNum);
398 newResultTypes.push_back(resTy);
399 newResultNames.push_back(resNameAttr);
400 newResultLocs.push_back(resLocAttr);
406 sv::InterfaceOp iface = build->getOrConstructInterface(chanTy);
408 newArgTypes.push_back(sinkPort);
409 newArgNames.push_back(resNameAttr);
410 newArgLocs.push_back(resLocAttr);
422 mod.setHWModuleType(newModType);
423 mod.setInputLocs(newArgLocs);
424 mod.setOutputLocs(newResultLocs);
429 if (BlockArgument arg = dyn_cast<BlockArgument>(operand)) {
430 auto *op = arg.getParentBlock()->getParentOp();
431 if (HWModuleLike mod = dyn_cast_or_null<HWModuleLike>(op))
432 return mod.getInputName(arg.getArgNumber());
434 auto *srcOp = operand.getDefiningOp();
435 if (
auto instOp = dyn_cast<InstanceOp>(srcOp))
436 return instOp.getInstanceName();
438 if (
auto srcName = srcOp->getAttrOfType<StringAttr>(
"name"))
439 return srcName.getValue();
447 llvm::raw_string_ostream s(name);
449 s << llvm::toLower(iface.getSymName()[12]) << iface.getSymName().substr(13);
452 if (operand.hasOneUse()) {
453 Operation *dstOp = *operand.getUsers().begin();
454 if (
auto instOp = dyn_cast<InstanceOp>(dstOp))
455 s <<
"To" << llvm::toUpper(instOp.getInstanceName()[0])
456 << instOp.getInstanceName().substr(1);
457 else if (
auto dstName = dstOp->getAttrOfType<StringAttr>(
"name"))
458 s <<
"To" << dstName.getValue();
463 if (!operName.empty())
464 s <<
"From" << llvm::toUpper(operName[0]) << operName.substr(1);
471 void ESIPortsPass::updateInstance(HWModuleExternOp mod, InstanceOp inst) {
473 circt::ImplicitLocOpBuilder instBuilder(inst.getLoc(), inst);
478 SmallVector<Value, 16> newOperands;
481 std::string nameStringBuffer;
482 for (
auto op : inst.getOperands()) {
483 auto instChanTy = dyn_cast<ChannelType>(op.getType());
485 newOperands.push_back(op);
492 auto iface = build->getOrConstructInterface(instChanTy);
494 mod.getInputTypes()[opNum]) {
495 inst.emitOpError(
"ESI ChannelType (operand #")
496 << opNum <<
") doesn't match module!";
498 newOperands.push_back(op);
506 instBuilder.create<InterfaceInstanceOp>(iface.getInterfaceType());
507 nameStringBuffer.clear();
512 GetModportOp sinkModport =
514 instBuilder.create<UnwrapSVInterfaceOp>(op, sinkModport);
515 GetModportOp sourceModport =
518 newOperands.push_back(sourceModport);
523 SmallVector<Value, 8> newResults;
524 SmallVector<Type, 8> newResultTypes;
525 for (
size_t resNum = 0, numRes = inst.getNumResults(); resNum < numRes;
527 Value res = inst.getResult(resNum);
528 auto instChanTy = dyn_cast<ChannelType>(res.getType());
530 newResults.push_back(res);
531 newResultTypes.push_back(res.getType());
537 auto iface = build->getOrConstructInterface(instChanTy);
539 mod.getInputTypes()[opNum]) {
540 inst.emitOpError(
"ESI ChannelType (result #")
541 << resNum <<
", operand #" << opNum <<
") doesn't match module!";
543 newResults.push_back(res);
544 newResultTypes.push_back(res.getType());
553 instBuilder.create<InterfaceInstanceOp>(iface.getInterfaceType());
554 nameStringBuffer.clear();
559 GetModportOp sourceModport =
562 instBuilder.create<WrapSVInterfaceOp>(res.getType(), sourceModport);
565 res.replaceAllUsesWith(newChannel);
566 GetModportOp sinkModport =
569 newOperands.push_back(sinkModport);
573 auto newInst = instBuilder.create<hw::InstanceOp>(
574 mod, inst.getInstanceNameAttr(), newOperands, inst.getParameters(),
575 inst.getInnerSymAttr());
579 for (
size_t resNum = 0, numRes = newResults.size(); resNum < numRes;
581 newResults[resNum].replaceAllUsesWith(newInst.getResult(resNum));
587 std::unique_ptr<OperationPass<ModuleOp>>
589 return std::make_unique<ESIPortsPass>();
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
static std::string & constructInstanceName(Value operand, sv::InterfaceOp iface, std::string &name)
Create a reasonable name for a SV interface instance.
static StringRef getOperandName(Value operand)
static StringRef getStringAttributeOr(Operation *op, StringRef attrName, StringRef def)
static InstancePath empty
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.
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)
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.