## 工具(Utils) ### 一、 总览 | 句式 | 返回
类型 | 描述 | | :-----------------------------------------------: | :----------: | :--------------------------------------------------------------------------------------: | | `widthOf(x : BitVector)` | Int | 返回一个类型为Bits/UInt/SInt的信号的位宽 | | `log2Up(x : BigInt)` | Int | 返回表示输入状态`x`所需要的位数 | | `isPow2(x : BigInt)` | Boolean | 当`x`为2的指数时, 返回`True` | | `roundUp(`
`that : BigInt,`
` by : BigInt)` | BigInt | 返回参数`by`乘参数`that`的结果 | | `Cat(x : Data*)` | Bits | 连接所有的参数, 其中第一个参数会放置在最高有效位(MSB), 最后一个参数放置在最低有效位(LSB) | ### 二、克隆硬件数据类型(Cloning hardware datatypes) 用户可以通过`cloneOf(x)`函数克隆给定的硬件数据类型。该函数将会返回一个拥有相同Scala类型和参数的新实例。例如: ```Scala def plusOne(value : UInt) : UInt = { // 将会生成一个和`value`同位宽的UInt类型 val temp = cloneOf(value) temp := value + 1 return temp } // treePlusOne 将会成为一个8比特的值 val treePlusOne = plusOne(U(3, 8 bits)) ``` > **注意!:若对一个`Bundle`类型使用`cloneOf(x)`函数功能, 需要确保该`Bundle`是一个`case`类, 否则需要在`Bundle`内部重写克隆函数。** ### 三、传递数据类型作为结构参数(Passing a datatype as construction parameter) 许多可重用的硬件要求能够被一些数据类型所参数化。例如如果用户想要定义一个FIFO或者移位寄存器, 则需要一个参数来定义该硬件所需的负载。 1. 传统方式 以定义一个`ShiftRegister`元件为例, 介绍传统的传递数据类型方式如下: ```Scala case class ShiftRegister[T <: Data](dataType: T, depth: Int) extends Component { val io = new Bundle { val input = in (cloneOf(dataType)) val output = out(cloneOf(dataType)) //需要在Case Class内部重写`cloneof`函数 } } ``` 实例化该元件的代码为: ```Scala val shiftReg = ShiftRegister(Bits(32 bits), depth = 8) ``` 在上述例子中, 硬件的数据类型(位宽)直接作为构造参数传递。因此每次需要例化或是用该datatype时, 都需要使用`cloneOf`函数对其datatype进行定义。这种方式并不安全因为常`cloneOf`函数会被遗忘。 2. 更安全的方式 以更安全的方式来传递数据类型的例子如下: ```Scala case class ShiftRegister[T <: Data](dataType: HardType[T], depth: Int) extends Component { val io = new Bundle { val input = in (dataType()) val output = out(dataType()) } } ``` 实例化该元件的代码为: ```Scala val shiftReg = ShiftRegister(Bits(32 bits), depth = 8) ``` 在上述的更安全的方法中, 使用了`HardType`去封装数据类型`T`, 可以看成是`T`的蓝图(blueprint)。这种方式相比于传统方式更加简单, 因为每次例化使用该datatype时, 只需要调用`HardType`的`apply`函数即可。此外, 从用户的角度来看, 这种机制是完全透明的, 因为硬件数据类型可以隐式转换为`HardType`。 ### 四、频率与时间(Frequency and time) SpinalHDL有专门的语法来定义频率和时间值: ```Scala val frequency = 100 MHz val timeoutLimit = 3 ms val period = 100 us val periodCycles = frequency * period val timeoutCycles = frequency * timeoutLimit ``` 对一个时间定义, 可以利用如下后缀得到一个`TimeNumber`: `fs, ps, ns, us, ms, sec, mn, hr`; 可以利用如下后缀得到一个`HertzNumber`: `Hz, KHz, MHz, GHz, THz`。 `TimeNumber`和`HertzNumber`是基于`PhysicalNumber`类, 该类在Scala中利用`BigDecimal`存储数据。 ### 五、二进制前缀(Binary prefix) SpinalHDL允许根据IEC, 来使用二进制前缀表示法定义整数。 ```Scala val memSize = 512 MiB val dpRamSize = 4 KiB ``` 可使用的所有二进制前缀如下: | 二进制前缀 | 表示值 | | :-----------: | :-------------------------: | | `Byte, Bytes` | 1 | | `KiB` | 1024 == 1 << 10 | | `MiB` | 10242 == 1 << 20 | | `GiB` | 10243 == 1 << 30 | | `TiB` | 10244 == 1 << 40 | | `PiB` | 10245 == 1 << 50 | | `EiB` | 10246 == 1 << 60 | | `ZiB` | 10247 == 1 << 70 | | `YiB` | 10248 == 1 << 80 |