9 #include "../PassDetails.h"
19 #include "mlir/Transforms/DialectConversion.h"
21 using namespace circt;
30 auto attr = op->getAttrOfType<StringAttr>(attrName);
32 return attr.getValue();
46 class ValidReady :
public ESISignalingStandad {
49 : ESISignalingStandad(converter, origPort), validPort(origPort),
50 readyPort(origPort) {}
52 void mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
53 SmallVectorImpl<Value> &newOperands,
54 ArrayRef<Backedge> newResults)
override;
55 void mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
56 SmallVectorImpl<Value> &newOperands,
57 ArrayRef<Backedge> newResults)
override;
60 void buildInputSignals()
override;
61 void buildOutputSignals()
override;
65 PortInfo validPort, readyPort, dataPort;
69 class FIFO :
public ESISignalingStandad {
72 : ESISignalingStandad(converter, origPort) {}
74 void mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
75 SmallVectorImpl<Value> &newOperands,
76 ArrayRef<Backedge> newResults)
override;
77 void mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
78 SmallVectorImpl<Value> &newOperands,
79 ArrayRef<Backedge> newResults)
override;
82 void buildInputSignals()
override;
83 void buildOutputSignals()
override;
87 PortInfo rdenPort, emptyPort, dataPort;
92 using PortConversionBuilder::PortConversionBuilder;
94 return llvm::TypeSwitch<Type, FailureOr<std::unique_ptr<PortConversion>>>(
96 .Case([&](esi::ChannelType chanTy)
99 ChannelSignaling signaling = chanTy.getSignaling();
100 if (signaling == ChannelSignaling::ValidReady)
101 return {std::make_unique<ValidReady>(converter, port)};
103 if (signaling == ChannelSignaling::FIFO0)
104 return {std::make_unique<FIFO>(converter, port)};
106 auto error = converter.getModule().emitOpError(
107 "encountered unknown signaling standard on port '")
108 << stringifyEnum(signaling) <<
"'";
109 error.attachNote(port.loc);
112 .Default([&](
auto) {
return PortConversionBuilder::build(port); });
117 void ValidReady::buildInputSignals() {
126 Value
data = converter.createNewInput(
127 origPort, inSuffix, cast<esi::ChannelType>(origPort.type).getInner(),
130 converter.createNewInput(origPort, validSuffix + inSuffix, i1, validPort);
134 ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
137 auto wrap = b.create<WrapValidReadyOp>(
data, valid);
138 ready =
wrap.getReady();
141 body->getArgument(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
146 StringRef outSuffix =
148 converter.createNewOutput(origPort, readySuffix + outSuffix, i1, ready,
152 void ValidReady::mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
153 SmallVectorImpl<Value> &newOperands,
154 ArrayRef<Backedge> newResults) {
155 auto unwrap = b.create<UnwrapValidReadyOp>(inst->getLoc(),
156 inst->getOperand(origPort.argNum),
157 newResults[readyPort.argNum]);
158 newOperands[dataPort.argNum] =
unwrap.getRawOutput();
159 newOperands[validPort.argNum] =
unwrap.getValid();
162 void ValidReady::buildOutputSignals() {
171 converter.createNewInput(origPort, readySuffix + inSuffix, i1, readyPort);
174 auto *terminator = body->getTerminator();
175 ImplicitLocOpBuilder b(origPort.loc, terminator);
177 auto unwrap = b.create<UnwrapValidReadyOp>(
178 terminator->getOperand(origPort.argNum), ready);
180 valid =
unwrap.getValid();
184 StringRef outSuffix =
188 converter.createNewOutput(origPort, outSuffix,
189 origPort.type.cast<esi::ChannelType>().getInner(),
191 converter.createNewOutput(origPort, validSuffix + outSuffix, i1, valid,
195 void ValidReady::mapOutputSignals(OpBuilder &b, Operation *inst,
197 SmallVectorImpl<Value> &newOperands,
198 ArrayRef<Backedge> newResults) {
200 b.create<WrapValidReadyOp>(inst->getLoc(), newResults[dataPort.argNum],
201 newResults[validPort.argNum]);
202 inst->getResult(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
203 newOperands[readyPort.argNum] =
wrap.getReady();
206 void FIFO::buildInputSignals() {
208 auto chanTy = origPort.type.cast<ChannelType>();
216 StringRef outSuffix =
220 Value
data = converter.createNewInput(
221 origPort, inSuffix, origPort.type.cast<esi::ChannelType>().getInner(),
224 converter.createNewInput(origPort, emptySuffix + inSuffix, i1, emptyPort);
228 ImplicitLocOpBuilder b(origPort.loc, body, body->begin());
231 auto wrap = b.create<WrapFIFOOp>(ArrayRef<Type>({chanTy, b.getI1Type()}),
233 rden =
wrap.getRden();
236 body->getArgument(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
239 converter.createNewOutput(origPort, rdenSuffix + outSuffix, i1, rden,
243 void FIFO::mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
244 SmallVectorImpl<Value> &newOperands,
245 ArrayRef<Backedge> newResults) {
247 b.create<UnwrapFIFOOp>(inst->getLoc(), inst->getOperand(origPort.argNum),
248 newResults[rdenPort.argNum]);
249 newOperands[dataPort.argNum] =
unwrap.getData();
250 newOperands[emptyPort.argNum] =
unwrap.getEmpty();
253 void FIFO::buildOutputSignals() {
258 StringRef outSuffix =
265 converter.createNewInput(origPort, rdenSuffix + inSuffix, i1, rdenPort);
268 auto *terminator = body->getTerminator();
269 ImplicitLocOpBuilder b(origPort.loc, terminator);
272 b.create<UnwrapFIFOOp>(terminator->getOperand(origPort.argNum), rden);
278 converter.createNewOutput(origPort, outSuffix,
279 origPort.type.cast<esi::ChannelType>().getInner(),
281 converter.createNewOutput(origPort, emptySuffix + outSuffix, i1,
empty,
285 void FIFO::mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
286 SmallVectorImpl<Value> &newOperands,
287 ArrayRef<Backedge> newResults) {
288 auto wrap = b.create<WrapFIFOOp>(
289 inst->getLoc(), ArrayRef<Type>({origPort.type, b.getI1Type()}),
290 newResults[dataPort.argNum], newResults[emptyPort.argNum]);
291 inst->getResult(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
292 newOperands[rdenPort.argNum] =
wrap.getRden();
300 struct ESIPortsPass :
public LowerESIPortsBase<ESIPortsPass> {
301 void runOnOperation()
override;
304 bool updateFunc(HWModuleExternOp mod);
305 void updateInstance(HWModuleExternOp mod, InstanceOp inst);
311 void ESIPortsPass::runOnOperation() {
312 ModuleOp top = getOperation();
318 DenseMap<SymbolRefAttr, HWModuleExternOp> externModsMutated;
319 for (
auto mod : top.getOps<HWModuleExternOp>())
325 top.walk([&externModsMutated,
this](InstanceOp inst) {
326 auto mapIter = externModsMutated.find(inst.getModuleNameAttr());
327 if (mapIter != externModsMutated.end())
328 updateInstance(mapIter->second, inst);
333 getAnalysis<circt::hw::InstanceGraph>();
335 for (
auto mod : top.getOps<HWMutableModuleLike>()) {
338 return signalPassFailure();
349 bool ESIPortsPass::updateFunc(HWModuleExternOp mod) {
350 auto *ctxt = &getContext();
352 bool updated =
false;
354 SmallVector<Attribute> newArgNames, newResultNames;
355 SmallVector<Location> newArgLocs, newResultLocs;
359 SmallVector<Type, 16> newArgTypes;
360 size_t nextArgNo = 0;
361 for (
auto argTy : mod.getInputTypes()) {
362 auto chanTy = argTy.dyn_cast<ChannelType>();
363 newArgNames.push_back(mod.getInputNameAttr(nextArgNo));
364 newArgLocs.push_back(mod.getInputLoc(nextArgNo));
368 newArgTypes.push_back(argTy);
374 auto iface = build->getOrConstructInterface(chanTy);
382 SmallVector<Type, 8> newResultTypes;
383 SmallVector<DictionaryAttr, 4> newResultAttrs;
384 for (
size_t resNum = 0, numRes = mod.getNumOutputPorts(); resNum < numRes;
386 Type resTy = mod.getOutputTypes()[resNum];
387 auto chanTy = resTy.dyn_cast<ChannelType>();
388 auto resNameAttr = mod.getOutputNameAttr(resNum);
389 auto resLocAttr = mod.getOutputLoc(resNum);
391 newResultTypes.push_back(resTy);
392 newResultNames.push_back(resNameAttr);
393 newResultLocs.push_back(resLocAttr);
399 sv::InterfaceOp iface = build->getOrConstructInterface(chanTy);
401 newArgTypes.push_back(sinkPort);
402 newArgNames.push_back(resNameAttr);
403 newArgLocs.push_back(resLocAttr);
415 mod.setHWModuleType(newModType);
416 mod.setInputLocs(newArgLocs);
417 mod.setOutputLocs(newResultLocs);
422 if (BlockArgument arg = operand.dyn_cast<BlockArgument>()) {
423 auto *op = arg.getParentBlock()->getParentOp();
424 if (HWModuleLike mod = dyn_cast_or_null<HWModuleLike>(op))
425 return mod.getInputName(arg.getArgNumber());
427 auto *srcOp = operand.getDefiningOp();
428 if (
auto instOp = dyn_cast<InstanceOp>(srcOp))
429 return instOp.getInstanceName();
431 if (
auto srcName = srcOp->getAttrOfType<StringAttr>(
"name"))
432 return srcName.getValue();
440 llvm::raw_string_ostream s(name);
442 s << llvm::toLower(iface.getSymName()[12]) << iface.getSymName().substr(13);
445 if (operand.hasOneUse()) {
446 Operation *dstOp = *operand.getUsers().begin();
447 if (
auto instOp = dyn_cast<InstanceOp>(dstOp))
448 s <<
"To" << llvm::toUpper(instOp.getInstanceName()[0])
449 << instOp.getInstanceName().substr(1);
450 else if (
auto dstName = dstOp->getAttrOfType<StringAttr>(
"name"))
451 s <<
"To" << dstName.getValue();
456 if (!operName.empty())
457 s <<
"From" << llvm::toUpper(operName[0]) << operName.substr(1);
464 void ESIPortsPass::updateInstance(HWModuleExternOp mod, InstanceOp inst) {
466 circt::ImplicitLocOpBuilder instBuilder(inst.getLoc(), inst);
471 SmallVector<Value, 16> newOperands;
474 std::string nameStringBuffer;
475 for (
auto op : inst.getOperands()) {
476 auto instChanTy = op.getType().dyn_cast<ChannelType>();
478 newOperands.push_back(op);
485 auto iface = build->getOrConstructInterface(instChanTy);
487 mod.getInputTypes()[opNum]) {
488 inst.emitOpError(
"ESI ChannelType (operand #")
489 << opNum <<
") doesn't match module!";
491 newOperands.push_back(op);
499 instBuilder.create<InterfaceInstanceOp>(iface.getInterfaceType());
500 nameStringBuffer.clear();
505 GetModportOp sinkModport =
507 instBuilder.create<UnwrapSVInterfaceOp>(op, sinkModport);
508 GetModportOp sourceModport =
511 newOperands.push_back(sourceModport);
516 SmallVector<Value, 8> newResults;
517 SmallVector<Type, 8> newResultTypes;
518 for (
size_t resNum = 0, numRes = inst.getNumResults(); resNum < numRes;
520 Value res = inst.getResult(resNum);
521 auto instChanTy = res.getType().dyn_cast<ChannelType>();
523 newResults.push_back(res);
524 newResultTypes.push_back(res.getType());
530 auto iface = build->getOrConstructInterface(instChanTy);
532 mod.getInputTypes()[opNum]) {
533 inst.emitOpError(
"ESI ChannelType (result #")
534 << resNum <<
", operand #" << opNum <<
") doesn't match module!";
536 newResults.push_back(res);
537 newResultTypes.push_back(res.getType());
546 instBuilder.create<InterfaceInstanceOp>(iface.getInterfaceType());
547 nameStringBuffer.clear();
552 GetModportOp sourceModport =
555 instBuilder.create<WrapSVInterfaceOp>(res.getType(), sourceModport);
558 res.replaceAllUsesWith(newChannel);
559 GetModportOp sinkModport =
562 newOperands.push_back(sinkModport);
566 InstanceOp newInst = instBuilder.create<InstanceOp>(
567 mod, inst.getInstanceNameAttr(), newOperands, inst.getParameters(),
568 inst.getInnerSymAttr());
572 for (
size_t resNum = 0, numRes = newResults.size(); resNum < numRes;
574 newResults[resNum].replaceAllUsesWith(newInst.getResult(resNum));
580 std::unique_ptr<OperationPass<ModuleOp>>
582 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.
Base class for the port conversion of a particular port.
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)
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
This holds the name, type, direction of a module's ports.