先来看一个非常简单的例子,假定我们有一个输入数组,长度为8,此时我们根据select信号选择对应的输入作为输出。 很简单是不是?SpinalHDL中定义了Vec的结构体,我们完全可以这么来写:
如果能写出来,那么Vec是怎么用的你没什么问题了。那么我们稍变换一下,假定我们有八个Mem,根据Select来输出支持地址对应Mem的数据,该如何去实现呢?看到这里也许你会不假思索的写下了下面这段代码:
代码写的很溜,然而一运行满屏报错,而错误的原因,就是“滥用”了Vec。Vec什么场景下能用? 那么什么场景下能够用Vec呢?我们可以先来看下Vec的定义:
在Vec中,我们所传入的电路类型gen要求集成自Data,而在SpinalHDL中的数据类型定义,其给出了详细的继承关系:
也就意味着我们能往Vec中放置的只能限定于上面的类型(当然我们可以通过Bundle定义更加复杂的数据类型,例如AXI4,也是可以放在Vec中的)。而在上面的电路描述中,其向Vec中放置的是Mem,Mem本质上是一个电路实现模块,而并非一个电路数据类型,故而不能使用Vec来实现我们的需求。知道了不能这么做,那该如何做? 别急,有Scala SpinalHDL是基于Scala的,而所有的电路对象不管是电路数据类型还是电路模块其本质上都是对象,其和普通的软件语言对象没有本质的不同,再次贴上当初令我茅塞顿开的一段话:
像上面的需求,我们想要做的,无非是一个盛放Mem的容器而已,软件怎么写我们就怎么写就可以了,Scala中的Array可不用区分所容纳的究竟是电路模块还是电路数据类型,因此我们完全可以这么来实现:
这里要做解释的是最后一行,其实现意图是通过map从每个Mem中读出指定地址的数据,得到一个Array[UInt]数组,而随后之所以调用toSeq在于我们从Array[UInt]中选择所使用的索引类型是UInt而非Int。这两者有极大的不同,UInt是一种电路数据结构类型而非Scala软件中常规的数据类型。Array是不支持UInt作为索引的。而SpinalHDL对Seq隐式扩展提供了read函数能够接受UInt作为索引读取相应位置的数据类型(会映射成switch电路):
写在最后 相对而言,Array的使用场景是大于Vec的,Vec能容纳的只有SpinalHDL中的电路数据类型,而Array则能盛放一切,小到Bool大到Component均可,故而最开始的电路你甚至可以这么来写:
原作者:玉骐
|