Stream、Flow是在电路描述里经常用到的对象。较早时间有小伙伴问关于Stream里Fragment嵌套的问题,当时没有去深入的研究,最近重新review下这个问题,特来做个总结。
》开篇立问
先来看看下面的这个场景:
我们定义如下接口:
这里定义了一个Stream接口,如果我们想引用这里面的data,那么下面两种方法是等价的:
b.data
b.payload.data
估计大多数人不深究的话基本上就采用第一种写法了,简单快捷(我也是)。
》深入一步
显而易见,这里b的定义是一个Stream接口。按照Stream接口的定义,其所包含的信号只有三个:
从直观上来看,那么采用b.data这种形式显然是没有直接的方法或调用关系的。
那么剩下的一种可能就是隐式转换了~
Stream类在定义时其继承关系如下:
这里的隐式转换存在于trait DataCarrier中:
这里为DataCarrier定义了两个隐式转换函数toImplicit和toImplicit2。根据DataCarrier的数据类型,分别返回不同的对象:
如果数据类型是普通类型,则直接返回dataCarrier.payload
如果数据类型是Fragment类型,则会返回dataCarrier.fragment(同样,会调用到toImplicit函数)。
如果你在toImplicit函数上打断点,执行上面的接口定义相关的代码,你就会发现会在toImplicit函数上暂停。也就意味着当我们调用b.data时会调用隐式转换函数toImplicit返回b.payload后再执行b.payload.data~
》乱花渐欲迷人眼
理解了上面的代码,接下来的场景可能让你眼花缭乱了。
先来看下面的这段代码:
上面这段代码是不是觉得fire0和fire1是表达相同的功能?然而,生成的RTL代码会让你怀疑自己:
再来看看一个Fragment嵌套的场景:
一眼看去是不是觉得fire0和fire1的作用是一样的?
然而,生成的RTL代码却是这样的:
给你一分钟,你先品着。等品完之后再来看一个三层Fragment嵌套的代码:
对应的RTL代码:
看看对应的RTL代码是不是和你想的又不一样了?
我们调用函数实现和我们自己实现大相径庭!
先晕一会儿~
》拨云见雾
是否又到了怀疑TM这SpinalHDL有Bug吧……
我也思索了两三天~
回归正题。接下来的内容好好品。这一切的一切均还是在于隐式转换。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !