如何理解autobundle和makeios中的flip?

如何解决如何理解autobundle和makeios中的flip?

在 LazyModule.scala 中,函数 AutoBundle() 使用 flipped = true 翻转 dangleIn 中的 Data(bundleIn) 以进行自动IO,而在 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,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,Boolean),Int)) = {
    val ((key,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,""),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,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,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
  }
}

解决方法

这是一段非常复杂的代码(Diplomacy/LazyModule 是一个非常先进的生成器),但是 .flip 是 Chisel 2 API 中的 Flipped(...) 等价于 Chisel 3。它被使用在火箭芯片中通过 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

基本上,在使用现成有效的接口时,您通常需要“翻转”生产者或消费者界面中的指示。

,

如果我能回答这个问题,让我试试:

  1. AutoBudle 部分

对于AutoBundle部分,值得注意的是,AutoBundle类构造器使用的elts实际上来自外交框架生成的Dangles。特定节点的解析悬垂实际上是danglesOut ++ danglesIn。根据评论,danglesOut 实际上是 Seq of dangles which describe the connections from this node output to other nodes inputs.,而 danglesIn 表示 Seq of dangles which describe the connections from this node input from other nodes outputs.。简而言之,这意味着 danglesOut 是输出端口的“悬挂抽象”,而 danglesIn 表示输入端口。另外,请注意 DanglesIn Seq 中的项目的 flipped 字段为真,而 DanglesOut 中的项目的该字段为假。这就是 Dangles 中 flipped 字段的语义含义。它强烈暗示 flipped 在外交语境下表示“输入”,而“非翻转”表示“输出”。

现在,让我们回到 AutoBundle 部分。它使用悬垂数据来创建凿子数据类型。然后使用这个Data类型为对应的LazyModuleImp构造IO,代码如下:

val element = if (flip) data.cloneType.flip() else data.cloneType

这基本上是说如果 flipped 中的 Dangle 字段为真,那么在构建 IO 时附加到此 Data 的方向应该是某种形式的“输入”。好吧,现在您实际上需要深入研究 chisel 库中的 cloneTypebindingdirectin 热点问题: cloneType 被凿子库用来克隆/复制现有的凿子数据对象。为什么?也许是因为在函数式编程语言中更喜欢不变性而不是可变性。通过创建一个精确的副本,您不必修改预先存在的对象,这可能会支持线程安全之类的东西。所以,cloneType 只是 chisel 克隆对象的方式。 def cloneType: this.type 位于根数据中,对于所有 chisel Data 子类型,您必须实现 cloneType 东西来告诉用户他们如何克隆一个新对象。因此,cloneType 高度依赖于实现。许多内置数据子类型(Bits(因此 UInt、SInt、Bool)、Bundle、DecoupledIO 等)都有内置的 cloneType 实现,但是如果您创建从 Record 扩展的新子类型,用户定义的 cloneType 仍然是强制性的。然而,大多数子类型基本上都有像 (new CusType).asInstanceOf[this.type] 这样的实现。 (Bikesheding:我不得不说 this.type 的东西非常复杂,他们真的应该使用 typeclass 或 F-type 来实现克隆的东西。但这可能会破坏兼容性。) 还有一个问题,如果您通读 Data 代码库,会发现一些字段,例如:specifiedDirectiondirectionbindspecifiedDirection 表示用户指定的数据方向。而 direction 表示结合 specifiedDirection 和父约束(输入、输出,如果数据类型是聚合的还包括双向)的实际解析方向。 Binding 有点棘手,这意味着此数据类型是否已绑定到硬件构造函数。例如,如果你定义了 UInt(32.W),它只是一个结构声明,还没有绑定到任何 harewire(他们称之为未绑定类型,或 chiselType)。但是,如果你用Wire()Reg()IO()包裹它,它会将你刚刚声明的数据类型标记为有界类型(硬件类型),有多种形式的绑定({ {1}}、PortBindingOpBindingMemoryPortBindingRegBindingWireBinding 等)

在澄清了上面的 cloneType 内容之后,让我们再次浏览这个 loc:

ChildBinding

val element = if (flip) data.cloneType.flip() else data.cloneType 之后,您可能想知道 cloneType 调用是否保留了 data.cloneTypedirection 信息?就像我之前说的,这是一个非常依赖于实现的问题,您需要逐案检查。然而,大多数实现将只是一个 loc: binding,考虑到绑定过程都发生在包装器调用中((new CusType).asInstanceOf[this.type] 将返回有界类型,但它仍然只是与iodef),因此通常调用 cloneType 后绑定信息将消失。在方向方面,考虑以下代码:

IO(iodef)

class TestBundle extends Record{ val a = Input(UInt(8.W)) val b = Output(UInt(8.W)) val elements =ListMap("a" -> a,"b" -> b) override def cloneType: this.type = new TestBundle().asInstanceOf[this.type] } class TestModule extends Module{ val testBundle = new TestBundle val flippedTestBundle = Flipped(testBundle) val io = IO(flippedTestBundle) val cloneIO = io.cloneType io.a := io.b } 是凿子类型io的有界类型,TestBundle信息已经全部解析(TestBundle->Bidirection,a->Output,b->Input,注意a 和 b 的解析方向被翻转)。但是,当您调用 direction 时,它将返回一个全新的 TestBundleL:io.cloneType。因此,很明显 override def cloneType: this.type = new TestBundle().asInstanceOf[this.type]val flippedTestBundle = Flipped(testBundle) 的所有影响都将被移除。 cloneIO 中唯一剩下的 val io = IO(flippedTestBundle) 信息只是 a(Input) 和 b(Output) 的 direction。我在某处读到 chisel 开发人员不建议在 Bundle 上应用方向性,除非 bundle 本身确实是双向的。 现在,让我们回顾一下这个位置:specifiedDirection。它指示是否输入端口(在外交框架中解决),只需翻转此端口中附加数据的方向即可。如果端口是输出,则保持原样。我认为这是相当直率的吧? 另外值得注意的是,在 Chisel 2 兼容层中,如果父方向未指定,用户指定的方向 val element = if (flip) data.cloneType.flip() else data.cloneType 将解析为 Flipped。 (未指定-> 输出) 我希望这可以回答您关于 AutoBundle 的问题。现在,让我们深入研究 makeIO

  1. makeIO 首先,为什么makeIOs?注意只有InputSourceNodeSinkNode,而其他节点类型没有。在外交基础设施内,makeIOsSourceNode 是 DAG 图的末端。在某些情况下,您希望使用固定 IO(可能用于测试)扩展此外交图。在外交图中,SourceNode只有输出,而SinkNode只有输入,但是如果要扩展图形,则必须对图的Source部分进行输入,而对Sink部分进行输出。见下图:

NetWork Extending

因此,考虑到我已经阐明了上面的 cloneType 和 flip ,您可能很清楚为什么 AutoBundle 和 MakeIos 中的 SinkNode 是相反的。

希望这个答案能让事情更清楚一些。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?