实用工具
介绍
许多工具和实用程序都存在于 spinal.lib 中,但有些工具和实用程序已经存在于 SpinalHDL Core 中。
语法 |
返回类型 |
描述 |
---|---|---|
|
Int |
返回 Bits/UInt/SInt 信号的位宽 |
|
Int |
返回表示 |
|
Boolean |
如果 |
|
BigInt |
返回第一个 |
|
位 |
连接所有参数,从 MSB 到 LSB,请参阅 Cat |
|
位 |
连接参数, 从 LSB 到 MSB, 参见 Cat |
Cat
如上所述, Cat
有两个版本。两个版本都连接了它们包含的信号,但有细微的差别:
Cat(x: Data*)
使用任意数量的硬件信号作为参数。它模拟了其他HDL且MSB变成了结果Bits
最左端的参数,最右端是 LSB . 换种说法:输入按照参数顺序拼接.Cat(x: Iterable[Data])
接受包含硬件信号的单个 Scala 可迭代集合(Seq / Set / List / …)。此版本将列表的第一个元素放入 LSB,最后一个元素放入 MSB。
差异主要在于这样的约定: Bits
是从最高索引到最低索引写入的,而列表是从索引 0 开始写入到最高索引的。 所有约定中,Cat
将索引 0 放置在 LSB 处。
val bit0, bit1, bit2 = Bool()
val first = Cat(bit2, bit1, bit0)
// is equivalent to
val signals = List(bit0, bit1, bit2)
val second = Cat(signals)
克隆硬件数据类型
您可以使用 cloneOf(x)
函数克隆给定的硬件数据类型。它将返回相同 Scala 类型和参数的新实例。
例如:
def plusOne(value : UInt) : UInt = {
// Will provide new instance of a UInt with the same width as ``value``
val temp = cloneOf(value)
temp := value + 1
return temp
}
// treePlusOne will become a 8 bits value
val treePlusOne = plusOne(U(3, 8 bits))
您可以在 硬件类型页面 上获取有关如何管理硬件数据类型的更多信息。
备注
如果你在 Bundle
上使用 cloneOf
函数,这个 Bundle
应该是一个 case class
,否则应该在内部重写clone函数。
// An example of a regular 'class' with 'override def clone()' function
class MyBundle(ppp : Int) extends Bundle {
val a = UInt(ppp bits)
override def clone = new MyBundle(ppp)
}
val x = new MyBundle(3)
val typeDef = HardType(new MyBundle(3))
val y = typeDef()
cloneOf(x) // Need clone method, else it errors
cloneOf(y) // Is ok
将数据类型作为构造函数参数传递
许多可重用硬件需要通过数据类型进行参数化。例如,如果您想定义 FIFO 或移位寄存器,则需要一个参数来指定组件所需的有效负载类型。
有两种类似的方法可以做到这一点。
老办法
老方法的一个很好的例子是 ShiftRegister
组件的定义:
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))
}
// ...
}
以下是实例化该组件的方法:
val shiftReg = ShiftRegister(Bits(32 bits), depth = 8)
如您所见,原始硬件类型直接作为构造参数传递。每次你想创建这种硬件数据类型的新实例时,你需要使用 cloneOf(...)
函数。以这种方式做事并不是超级安全,因为很容易忘记使用 cloneOf
。
安全的方法
安全的传递数据类型参数方法,示例如下:
case class ShiftRegister[T <: Data](dataType: HardType[T], depth: Int) extends Component {
val io = new Bundle {
val input = in (dataType())
val output = out(dataType())
}
// ...
}
以下是实例化组件的方法(与之前完全相同):
val shiftReg = ShiftRegister(Bits(32 bits), depth = 8)
请注意,上述示例中使用了一个 HardType
包装器,它包装了原始数据类型 T
,这种做法比“旧方法”更容易使用。因为要创建硬件数据类型的新实例,只需调用 HardType
的 apply
函数(或者换句话说,在类型名后添加括号)。
此外,从用户的角度来看,这种机制是完全透明的,因为硬件数据类型可以隐式转换为 HardType
。
频率和时间
SpinalHDL 有专用语法来定义频率和时间值:
val frequency = 100 MHz // infers type TimeNumber
val timeoutLimit = 3 ms // infers type HertzNumber
val period = 100 us // infers type TimeNumber
val periodCycles = frequency * period // infers type BigDecimal
val timeoutCycles = frequency * timeoutLimit // infers type BigDecimal
TimeNumber
:fs
、ps
、ns
、us
、ms
、sec
、mn
、hr
HertzNumber
:Hz
, KHz
, MHz
, GHz
, THz
TimeNumber
和 HertzNumber
是基于 PhysicalNumber
类,它使用 scala BigDecimal
来存储数字。
二进制前缀
SpinalHDL 允许根据 IEC 使用二进制前缀表示法定义整数。
val memSize = 512 MiB // infers type BigInt
val dpRamSize = 4 KiB // infers type BigInt
可以使用以下二进制前缀表示法:
二进制前缀 |
值 |
---|---|
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 |
当然,BigInt 可以以字节为单位进行打印。例如,BigInt(1024).byteUnit
.
val memSize = 512 MiB
println(memSize)
>> 536870912
println(memSize.byteUnit)
>> 512MiB
val dpRamSize = BigInt("123456789", 16)
println(dpRamSize.byteUnit())
>> 4GiB+564MiB+345KiB+905Byte
println((32.MiB + 12.KiB + 223).byteUnit())
>> 32MiB+12KiB+223Byte
println((32.MiB + 12.KiB + 223).byteUnit(ceil = true))
>> 33~MiB