首页 > 解决方案 > 如何理解autobundle()和makeios中的翻转?

问题描述

在 LazyModule.scala 中,函数 AutoBundle() 用 flipped = true 翻转 dangleIn 中的 Data(bundleIn) 以生成 autoIO,而在 Nodes.scala 中,类 sourceNode 中的函数 makeIOs() 翻转 bundleOut 以生成 IO,为什么它们不同?

LazyModule.scala 中的 AutoBundle() 代码:

/** [[AutoBundle]] will construct the [[Bundle]]s for a [[LazyModule]] in [[LazyModuleImpLike.instantiate]],
  *
  * @param elts is a sequence of data containing for each IO port  a tuple of (name, data, flipped), where
  *             name: IO name
  *             data: actual data for connection.
  *             flipped: flip or not in [[makeElements]]
  */
final class AutoBundle(elts: (String, Data, Boolean)*) extends Record {
  // We need to preserve the order of elts, despite grouping by name to disambiguate things.
  val elements: ListMap[String, Data] = ListMap() ++ elts.zipWithIndex.map(makeElements).groupBy(_._1).values.flatMap {
    // If name is unique, it will return a Seq[index -> (name -> data)].
    case Seq((key, element, i)) => Seq(i -> (key -> element))
    // If name is not unique, name will append with j, and return `Seq[index -> (s"${name}_${j}" -> data)]`.
    case seq => seq.zipWithIndex.map { case ((key, element, i), j) => i -> (key + "_" + j -> element) }
  }.toList.sortBy(_._1).map(_._2)
  require(elements.size == elts.size)

  // Trim final "(_[0-9]+)*$" in the name, flip data with flipped.
  private def makeElements(tuple: ((String, Data, Boolean), Int)) = {
    val ((key, data, flip), i) = tuple
    // Trim trailing _0_1_2 stuff so that when we append _# we don't create collisions.
    val regex = new Regex("(_[0-9]+)*$")
    val element = if (flip) data.cloneType.flip() else data.cloneType
    (regex.replaceAllIn(key, ""), element, i)
  }

  override def cloneType: this.type = new AutoBundle(elts: _*).asInstanceOf[this.type]
}

Nodes.scala 中的 makeIOs() 代码:

/** A node which represents a node in the graph which only has outward edges and no inward edges.
  *
  * A [[SourceNode]] cannot appear left of a `:=`, `:*=`, `:=*, or `:*=*`
  * There are no Mixed [[SourceNode]]s, There are no "Mixed" [[SourceNode]]s because each one only has an outward side.
  */
class SourceNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(po: Seq[D])(implicit valName: ValName)
  extends MixedNode(imp, imp)
{
  override def description = "source"
  protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) = {
    def resolveStarInfo: String =
      s"""$context
         |$bindingInfo
         |number of known := bindings to inward nodes: $iKnown
         |number of known := bindings to outward nodes: $oKnown
         |number of binding queries from inward nodes: $iStars
         |number of binding queries from outward nodes: $oStars
         |${po.size} outward parameters: [${po.map(_.toString).mkString(",")}]
         |""".stripMargin
    require(oStars <= 1,
      s"""Diplomacy has detected a problem with your graph:
         |The following node appears right of a :=* $oStars times; at most once is allowed.
         |$resolveStarInfo
         |""".stripMargin)
    require(iStars == 0,
      s"""Diplomacy has detected a problem with your graph:
         |The following node cannot appear left of a :*=
         |$resolveStarInfo
         |""".stripMargin)
    require(iKnown == 0,
      s"""Diplomacy has detected a problem with your graph:
         |The following node cannot appear left of a :=
         |$resolveStarInfo
         |""".stripMargin)
    if (oStars == 0)
      require(po.size == oKnown,
        s"""Diplomacy has detected a problem with your graph:
           |The following node has $oKnown outward bindings connected to it, but ${po.size} sources were specified to the node constructor.
           |Either the number of outward := bindings should be exactly equal to the number of sources, or connect this node on the right-hand side of a :=*
           |$resolveStarInfo
           |""".stripMargin)
    else
      require(po.size >= oKnown,
        s"""Diplomacy has detected a problem with your graph:
           |The following node has $oKnown outward bindings connected to it, but ${po.size} sources were specified to the node constructor.
           |To resolve :=*, size of outward parameters can not be less than bindings.
           |$resolveStarInfo
           |""".stripMargin
      )
    (0, po.size - oKnown)
  }
  protected[diplomacy] def mapParamsD(n: Int, p: Seq[D]): Seq[D] = po
  protected[diplomacy] def mapParamsU(n: Int, p: Seq[U]): Seq[U] = Seq()

  def makeIOs()(implicit valName: ValName): HeterogeneousBag[B] = {
    val bundles = this.out.map(_._1)
    val ios = IO(Flipped(new HeterogeneousBag(bundles.map(_.cloneType))))
    ios.suggestName(valName.name)
    bundles.zip(ios).foreach { case (bundle, io) => bundle <> io }
    ios
  }
}

标签: scalaioflipchiselrocket-chip

解决方案


这是一段相当复杂的代码(外交/LazyModule 是一个非常先进的生成器),但.flip它是 Chisel 3 中的 Chisel 2 API 等效项Flipped(...)。它通过 Chisel 2 兼容层用于火箭芯片(import Chisel._相反到import chisel3._)。

Flipped反转类型的方向。请注意,这是递归的,并且类型可以是双向的(通常在Flipped使用时)。

例如:

class Example extends MultiIOModule {
  // .valid and .bits are inputs, .ready is an output
  val in = IO(Flipped(Decoupled(UInt(8.W))))
  // .valid and .bits are outputs, .ready is an input
  val out = IO(Decoupled(UInt(8.W)))
  
  out <> in
}

(Scastie 链接:https ://scastie.scala-lang.org/F91trxakSFSrlVrzk3VxcA )

基本上,当使用现成有效的接口时,您经常需要“翻转”生产者或消费者接口中的方向。


推荐阅读