✎ 编 者 按
Scala里面的隐式转换的好处是灵活,坏处就是太灵活。
StageableKey都是哪儿来的
在pipeline的实现中,有五大要素:pipeline、Connection、Stage、StageableKey、Stageable。我们来看下面的代码:
这里我们所想要实现的是一个四输入按照树形加法器的形式经过两级求和。这是我在看完了pipeline源代码后写下的第一个练习代码。在看了SpinalHDL里面给的pipeline的例子后给我的第一印象就是太灵活了。从最简单的做起,在进行断点调试分析执行流程时,当我将断点打在pip.build时一个问题困扰了我大半个小时。调试信息显示stage0中internal.stageableToData中包含两个元素,而stage1中internal.stageableToData中确显示有四个元素。这让我第一印象就是简直离了个大谱,stage1里我明明也就只调了两次insert函数啊!
老实讲,我的Scala水平也就搞软件的业余水平,如果我是当初初学,那么我就不纠结了,这里还是老是分析下。
insert
老实回到insert函数来仔细扒拉扒拉:
加上括号五行代码我看了十几遍,看了几遍也没毛病啊,这里面定义一个Stageable变量s,并把that赋值给this(s),随后将s返回。调用this(s)会通过调用apply函数创建一个StageableKey。再怎么看是不是也只应该将一个StageableKey压入stageableToData不是~
这就是Scala里面的隐式转换的隐入真的是太灵活了。我在看的时候一直忘记了思考一个问题:在上面的代码中第三行":="左边是Data类型,而右边是Stageable变量类型,在Scala这种强类型语言里,操作符左右两侧变量类型不同能进行操作么?换句话说,就是Data所定义的:=方法所支持的参数列表里应该没有Stageable参数类型。
那么,这里的幺蛾子那就只能是存在隐式转换了。在类Stage中存在下面这些隐式转换:
implicit def stageablePiped[T <: Data](stageable: Stageable[T])(implicit key : StageableOffset = StageableOffsetNone) = Stage.this(stageable, key.value)
implicit def stageablePiped2[T <: Data](stageable: Stageable[T]) = new {
def of(key : Any) = Stage.this.apply(stageable, key)
// def := (value : T)(implicit key : StageableOffset = StageableOffsetNone) = Stage.this(stageable, key.value) := value
}
implicit def stageablePiped3[T <: Data](key: Tuple2[Stageable[T], Any]) = Stage.this(key._1, key._2)
implicit def stageablePipedVec[T <: Data](stageable: Stageable[Vec[T]])(implicit key : StageableOffset = StageableOffsetNone) : Vec[T] = Stage.this(stageable, key.value)
implicit def stageablePipedVec2[T <: Data](stageable: Stageable[Vec[T]]) = new {
def of(key : Any) = Stage.this.apply(stageable, key)
}
implicit def stageablePipedVec3[T <: Data](key: Tuple2[Stageable[Vec[T]], Any]) = Stage.this(key._1, key._2)
这里显然符合条件的就是stageablePiped这个隐式转换了。它将我们传入的that这个Stageable参数调用Stage.this方法给注册进了stageableToData中去。这也就意味着我们在Stage1中调用一次insert将会插入两个StageableKey。而在Stage0中之所以不存在这个问题是因为我们所传入的“that”本身就是一个UInt类型变量,无需再进行隐式转换,其所调用的是这个insert函数:
再回到我们这个代码里面的第18行。两次insert均返回的是Stageable类型变量,而我们这里又做了一次加法。在Stageable里面明显是不存在加法这个方法的。你在IDEA里面如果对加法进行代码跳转,你会发现其调用的是UInt的加法操作。那么无疑这里有存在隐式转换了。即将加法左右两侧的类型均隐式转换成UInt类型。这里还是会调用stageablePiped这个隐式转换,只不过在Stage.this所对应的apply方法里:
由于stageableToData中在调用insert时已经将数据压入,此时不会再生成新的StageableKey,将原有的直接返回即可。
因此,在Stage1中的stageableToData中,一共压入了四个StageableKey。
理解了这些,对于stage1,我们可以换个写法:
这时只会针对stage0.tmp和stage0.tmp1分别调用一次隐式转换得到两个UInt进行相加,故在stage1中的stageableToData中仅包含两个元素。
写在最后
SpinalHDL作者Dolu真的是一个大神,软硬件都玩的真溜~
☆ END ☆
作者:玉骐
原文链接:Spinal FPGA
微信公众号:
推荐阅读
更多SpinalHDL技术干货请关注[Spinal FPGA]欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。