9#include "../PassDetails.h" 
   19#include "mlir/Transforms/DialectConversion.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();
 
 
   53class 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;
 
   76class 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) << 
"'";
 
  124void ValidReady::buildInputSignals() {
 
  125  Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
 
  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 = WrapValidReadyOp::create(b, 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,
 
  159void ValidReady::mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
 
  160                                 SmallVectorImpl<Value> &newOperands,
 
  161                                 ArrayRef<Backedge> newResults) {
 
  162  auto unwrap = UnwrapValidReadyOp::create(b, inst->getLoc(),
 
  163                                           inst->getOperand(origPort.argNum),
 
  164                                           newResults[readyPort.argNum]);
 
  165  newOperands[dataPort.argNum] = 
unwrap.getRawOutput();
 
  166  newOperands[validPort.argNum] = 
unwrap.getValid();
 
  169void ValidReady::buildOutputSignals() {
 
  170  Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
 
  178      converter.createNewInput(origPort, readySuffix + inSuffix, i1, readyPort);
 
  181    auto *terminator = body->getTerminator();
 
  182    ImplicitLocOpBuilder b(origPort.loc, terminator);
 
  184    auto unwrap = UnwrapValidReadyOp::create(
 
  185        b, 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,
 
  202void ValidReady::mapOutputSignals(OpBuilder &b, Operation *inst,
 
  204                                  SmallVectorImpl<Value> &newOperands,
 
  205                                  ArrayRef<Backedge> newResults) {
 
  207      WrapValidReadyOp::create(b, inst->getLoc(), newResults[dataPort.argNum],
 
  208                               newResults[validPort.argNum]);
 
  209  inst->getResult(origPort.argNum).replaceAllUsesWith(
wrap.getChanOutput());
 
  210  newOperands[readyPort.argNum] = 
wrap.getReady();
 
  213void FIFO::buildInputSignals() {
 
  214  Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
 
  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 = WrapFIFOOp::create(b, 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,
 
  250void FIFO::mapInputSignals(OpBuilder &b, Operation *inst, Value instValue,
 
  251                           SmallVectorImpl<Value> &newOperands,
 
  252                           ArrayRef<Backedge> newResults) {
 
  254      UnwrapFIFOOp::create(b, inst->getLoc(), inst->getOperand(origPort.argNum),
 
  255                           newResults[rdenPort.argNum]);
 
  256  newOperands[dataPort.argNum] = 
unwrap.getData();
 
  257  newOperands[emptyPort.argNum] = 
unwrap.getEmpty();
 
  260void FIFO::buildOutputSignals() {
 
  261  Type i1 = IntegerType::get(getContext(), 1, IntegerType::Signless);
 
  265  StringRef outSuffix =
 
  272      converter.createNewInput(origPort, rdenSuffix + inSuffix, i1, rdenPort);
 
  275    auto *terminator = body->getTerminator();
 
  276    ImplicitLocOpBuilder b(origPort.loc, terminator);
 
  279        UnwrapFIFOOp::create(b, 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,
 
  292void FIFO::mapOutputSignals(OpBuilder &b, Operation *inst, Value instValue,
 
  293                            SmallVectorImpl<Value> &newOperands,
 
  294                            ArrayRef<Backedge> newResults) {
 
  295  auto wrap = WrapFIFOOp::create(
 
  296      b, 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();
 
  307struct ESIPortsPass : 
public circt::esi::impl::LowerESIPortsBase<ESIPortsPass> {
 
  308  void runOnOperation() 
override;
 
  311  bool updateFunc(HWModuleExternOp mod);
 
  312  void updateInstance(HWModuleExternOp mod, InstanceOp inst);
 
  318void ESIPortsPass::runOnOperation() {
 
  319  ModuleOp top = getOperation();
 
  325  DenseMap<SymbolRefAttr, HWModuleExternOp> externModsMutated;
 
  326  for (
auto mod : top.getOps<HWModuleExternOp>())
 
  329      externModsMutated[FlatSymbolRefAttr::
get(mod)] = mod;
 
  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();
 
  356bool 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);
 
  419  auto newFuncType = FunctionType::get(ctxt, newArgTypes, newResultTypes);
 
  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);
 
 
  471void 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        InterfaceInstanceOp::create(instBuilder, iface.getInterfaceType());
 
  507    nameStringBuffer.clear();
 
  510        StringAttr::get(mod.getContext(),
 
  512    GetModportOp sinkModport =
 
  514    UnwrapSVInterfaceOp::create(instBuilder, 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        InterfaceInstanceOp::create(instBuilder, iface.getInterfaceType());
 
  554    nameStringBuffer.clear();
 
  557        StringAttr::get(mod.getContext(),
 
  559    GetModportOp sourceModport =
 
  562        WrapSVInterfaceOp::create(instBuilder, res.getType(), sourceModport);
 
  565    res.replaceAllUsesWith(newChannel);
 
  566    GetModportOp sinkModport =
 
  569    newOperands.push_back(sinkModport);
 
  573  auto newInst = hw::InstanceOp::create(
 
  574      instBuilder, mod, inst.getInstanceNameAttr(), newOperands,
 
  575      inst.getParameters(), inst.getInnerSymAttr());
 
  579  for (
size_t resNum = 0, numRes = newResults.size(); resNum < numRes;
 
  581    newResults[resNum].replaceAllUsesWith(newInst.getResult(resNum));
 
  587std::unique_ptr<OperationPass<ModuleOp>>
 
  589  return std::make_unique<ESIPortsPass>();
 
 
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 EvaluatorValuePtr unwrap(OMEvaluatorValue c)
 
static InstancePath empty
 
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)
 
void error(Twine message)
 
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.