Spinal Utilities

Overview

SpinalHDL存在大量的零碎的feature,很不起眼但是非常有用。 core 模块和 lib 模块均包含有 util ,这里略微总结不在官方文档内的比较有用的功能。

隐式转换

从Scalal原生类型隐式转换到Spinal的硬件类型,并提供硬件描述相关功能。抑或是对一些原有类型的扩展。

Seq到Vec

import spinal.lib._

class TraversableOncePimped[T <: Data](pimped: Seq[T])

  • 提供类似于Vec使用UInt访问内部数据的功能
  • 提供 reduceBalanceTree
  • 提供 asBits
  • 提供简单的集合查找和计数功能:
    • def sExist(condition: T => Bool): Bool = (pimped map condition).fold(False)(_ || _)
    • def sContains(value: T) : Bool = sExist(_ === value)
    • def sFindFirst(condition: T => Bool) : (Bool,UInt)
    • def sCount(condition: T => Bool): UInt = SetCount((pimped.map(condition)))
    • def sCount(value: T): UInt = sCount(_ === value)

特殊的, Seq[Bool] 还提供 orR, andR, xorR 功能,将内部的Boolean全部与或非起来。

更基本的类 class TraversableOnceAnyPimped[T <: Any](pimped: Seq[T]) 支持 whenIndexed 功能,

1
2
3
4
5
6
7
8
def apply(id : UInt)(gen : (T) => Unit): Unit ={
assert(widthOf(id) == log2Up(pimped.size))
for((e,i) <- pimped.zipWithIndex) {
when(id === i){
gen(e)
}
}
}

Tuple到Bits

import spinal.core._

class TuplePimperBase(product: Product)

提供类似于Verilog位拼接的功能,如:

1
2
3
assign {cout, s} = {a, b, c};
||
(cout, s) := (a, b, c).asBits

字符串转Vec[Bits]

1
val avec = "0123abcd".toVecOfByte

Stream、Flow及Fragment自动转成其payload

需要 import DataCarrier._

不在需要引用 payload 来获得其payload。

时钟域的扩展

提供异步复位同步释放的扩展,可以从异步复位时钟域经由同步器获得新的同步释放时钟域

def withBufferedResetFrom(resetCd : ClockDomain, bufferDepth : Int = BufferCC.defaultDepth.get) : ClockDomain

对Seq的when

  • whenMasked
    1
    2
    3
    whenMasked(avec, amask: Bits) {
    active(sth)
    }
  • whenIndexed
    1
    2
    3
    whenIndexed(avec, aindex: UInt) {
    active(sth)
    }

带优先级的Mux

1
2
3
4
5
6
7
8
9
10
11
object PriorityMux{
def apply[T <: Data](in: Seq[(Bool, T)]): T = {
if (in.size == 1) {
in.head._2
} else {
Mux(in.head._1, in.head._2, apply(in.tail)) //Inttelij right code marked red
}
}
def apply[T <: Data](sel: Seq[Bool], in: Seq[T]): T = apply(sel zip in)
def apply[T <: Data](sel: Bits, in: Seq[T]): T = apply(sel.asBools.zip(in))
}

延迟的标志信号 DelayEvent

可以将一个标志信号延时多个cycle。(对这一flag直接 Delay 不是不行,只是在延迟的cycle数太大时过于浪费寄存器资源)存在两个控制延迟的方式,

  • def apply(event: Bool, cycle: BigInt): Bool
  • def apply(event: Bool, cycle: UInt): Bool

计数器扩展

可增减计数器,多请求计数器。

  • CounterUpDown 提供 decrement 方法减一
  • 多请求:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    object CounterMultiRequest {
    def apply(width: Int, requests : (Bool,(UInt) => UInt)*): UInt = {
    val counter = Reg(UInt(width bit)) init(0)
    var counterNext = cloneOf(counter)
    counterNext := counter
    for((cond,func) <- requests){
    when(cond){
    counterNext \= func(counterNext)
    }
    }
    counter := counterNext
    counter
    }
    }