UInt/SInt类型对应于一组bits,可用于有符号/无符号整数运算。
声明整数的语法如下:([] 中的所有内容都是可选项)
val myUInt = UInt(8 bits)
myUInt := U(2,8 bits)
myUInt := U(2)
myUInt := U"0000_0101" // Base per default is binary => 5
myUInt := U"h1A" // Base could be x (base 16)
// h (base 16)
// d (base 10)
// o (base 8)
// b (base 2)
myUInt := U"8’h1A"
myUInt := 2 // You can use a Scala Int as a literal value【注意,直接写数字默认是Int】
val myBool := myUInt === U(7 -> true,(6 downto 0) -> false)
val myBool := myUInt === U(myUInt.range -> true)
// For assignment purposes, you can omit the U/S, which also allows the use of the [default -> ???] feature
myUInt := (default -> true) // Assign myUInt with "11111111"
myUInt := (myUInt.range -> true) // Assign myUInt with "11111111"
myUInt := (7 -> true, default -> false) // Assign myUInt with "10000000"
myUInt := ((4 downto 1) -> true, default -> false) // Assign myUInt with "00011110"
UInt和SInt类型支持以下运算符:
注意:
x rotateLeft y
和x rotateRight y
也是有效的语法。
注意:请注意
x >> 2:T(w(x)-2)
和x >> U(2):T(w(x))
之间的区别。两者的不同在于,在第一种情况下,数字 2 是一个Int
(可以看作是“elaboration integer”),而在第二种情况下它是一个硬件信号。【类比上一节中的 >> y的类型】
val a, b, c = SInt(32 bits)
a := S(5)
b := S(10)
// Bitwise operators
c := ~(a & b) // Inverse(a AND b)
assert(c.getWidth == 32)
// Shift
val arithShift = UInt(8 bits) << 2 // shift left (resulting in 10 bits)
val logicShift = UInt(8 bits) |<< 2 // shift left (resulting in 8 bits)
assert(arithShift.getWidth == 10)
assert(logicShift.getWidth == 8)
// Rotation
val rotated = UInt(8 bits) rotateLeft 3 // left bit rotation
assert(rotated.getWidth == 8)
// Set all bits of b to True when all bits of a are True
when(a.andR) { b.setAll() }
注意:对于SInt左移操作,其提供的算数左移操作将会进行位宽扩展,若想位宽保持不变,可以进行位宽截取(SpinalHDL里提供了非常方便的位宽截取处理方式)。而逻辑左移操作的处理方式与UInt无二差别。
针对加法和减法,SpinalHDL均提供了三种方式实现:
x+y、x-y
: 与Verilog中的加减操作符相同,对于溢出不做处理。x+^y、x-^y
: 为了防止加减法溢出,该方法提供了带位宽扩展的操作处理。x+|y、x-|y
:该操作符对于溢出场景,做截断饱和处理。val a, b, c = UInt(8 bits)
a := U"xf0"
b := U"x0f"
c := a + b
assert(c === U"8’xff")
val d = a +^ b
assert(d === U"9’x0ff")
val e = a +| U"8’x20"
assert(e === U"8’xff")
注意:请注意这里如何进行仿真断言(simulation assertions)(使用
===
),而不是前面示例中的elaboration assertions(使用==
)。
val a = U(5, 8 bits)
val b = U(10, 8 bits)
val c = UInt(2 bits)
when (a > b) {
c := U"10"
} elsewhen (a =/= b) {
c := U"01"
} elsewhen (a === U(0)) {
c.setAll()
} otherwise {
c.clearAll()
}
注意:当以允许“环绕(wraparound)”行为的方式比较UInt值时,意味着当它们超过最大值时,这些值将“环绕”到最小值。可以使用UInt的
wrap
方法作为x.wrap < y
来处理UInt变量x、y,如果在环绕意义下x小于y,则结果为true。【不是太懂????】
将 Bool、Bits 或 SInt 转换为 UInt,可以使用 U(something)。要将东西转换为 SInt,可以使用 S(something)。
// Cast an SInt to Bits
val myBits = mySInt.asBits
// Create a Vector of Bool
val myVec = myUInt.asBools
// Cast a Bits to SInt
val mySInt = S(myBits)
// get the bit at index 4
val myBool = myUInt(4)
// assign bit 1 to True
mySInt(1) := True
// Range
val myUInt_8bits = myUInt_16bits(7 downto 0)
val myUInt_7bits = myUInt_16bits(0 to 6)
val myUInt_6bits = myUInt_16Bits(0 until 6)
mySInt_8bits(3 downto 0) := mySInt_4bits
##
与@@
操作符均为拼接操作符,两者差别在于返回结果类型不同,##
返回类型为Bits
,而@@
返回类型为x变量类型
。myBool := mySInt.lsb // equivalent to mySInt(0)
// Concatenation
val mySInt = mySInt_1 @@ mySInt_1 @@ myBool
val myBits = mySInt_1 ## mySInt_1 ## myBool
// Subdivide
val sel = UInt(2 bits)
val mySIntWord = mySInt_128bits.subdivideIn(32 bits)(sel)
// sel = 3 => mySIntWord = mySInt_128bits(127 downto 96)
// sel = 2 => mySIntWord = mySInt_128bits( 95 downto 64)
// sel = 1 => mySIntWord = mySInt_128bits( 63 downto 32)
// sel = 0 => mySIntWord = mySInt_128bits( 31 downto 0)
// If you want to access in reverse order you can do:【这个实例跟前面的类似!!!】
val myVector = mySInt_128bits.subdivideIn(32 bits).reverse
val mySIntWord = myVector(sel)
// Resize
myUInt_32bits := U"32’x112233344"
myUInt_8bits := myUInt_32bits.resized // automatic resize (myUInt_8bits = 0x44)
val lowest_8bits = myUInt_32bits.resize(8) // resize to 8 bits (myUInt_8bits = 0x44)
// Two's complement
mySInt := myUInt.twoComplement(myBool)
// Absolute value
mySInt_abs := mySInt.abs
对于定点数,我们可以将其分为两部分:
注意:RoundToEven
和RoundToOdd
模式非常特殊,用于一些高精度的大数据统计领域。目前SpinalHDL还不支持这两种模式。
你会发现ROUNDUP,ROUNDDOWN,ROUNDTOZERO,ROUNDTOINF,ROUNDTOEVEN和ROUNTOODD的行为非常相似,其中ROUNDTOINF是最常见的。不同编程语言中舍入的行为可能有所不同。
注意:在SpinalHDL中,ROUNDTOINF是默认的RoundType(round = roundToInf)
val A = SInt(16 bits)
val B = A.roundToInf(6 bits) //默认使用带进位的“align = false”,得到了11位 = 16-6+1
val B = A.roundToInf(6 bits, align = true) // sat 1 carry bit, got 10 bit = 16-6
val B = A.floor(6 bits) // return 10 bit
val B = A.floorToZero(6 bits) // return 10 bit
val B = A.ceil(6 bits) // ceil with carry so return 11 bit
val B = A.ceil(6 bits, align = true) // ceil with carry then sat 1 bit return 10 bit
val B = A.ceilToInf(6 bits)
val B = A.roundUp(6 bits)
val B = A.roundDown(6 bits)
val B = A.roundToInf(6 bits)
val B = A.roundToZero(6 bits)
val B = A.round(6 bits) // SpinalHDL uses roundToInf as the default rounding mode
val B0 = A.roundToInf(6 bits, align = true) // ---+
// |--> equal
val B1 = A.roundToInf(6 bits, align = false).sat(1) // ---+
注意:仅 floor 和 floorToZero 可以在不使用 align 选项的情况下工作;它们不需要进位比特。其他舍入操作默认使用进位比特。
val A = SInt(8 bits)
val B = A.sat(3 bits) // return 5 bits with saturated highest 3 bits
val B = A.sat(3) // equal to sat(3 bits)
val B = A.trim(3 bits) // return 5 bits with the highest 3 bits discarded
val B = A.trim(3 bits) // return 5 bits with the highest 3 bits discarded
val C = A.symmetry // return 8 bits and symmetry as (-128~127 to -127~127)
val C = A.sat(3).symmetry // return 5 bits and symmetry as (-16~15 to -15~15)
在UInt/SInt中提供了两种方法来进行定点数:
在RTL工作中强烈推荐使用 fixTo
,无需像上图中的Way1那样手动处理进位比特对齐和比特宽度计算。
具有自动饱和功能的Factory Fix函数:
val A = SInt(16 bits)
val B = A.fixTo(10 downto 3) // default RoundType.ROUNDTOINF, sym = false
val B = A.fixTo( 8 downto 0, RoundType.ROUNDUP)
val B = A.fixTo( 9 downto 3, RoundType.CEIL, sym = false)
val B = A.fixTo(16 downto 1, RoundType.ROUNDTOINF, sym = true )
val B = A.fixTo(10 downto 3, RoundType.FLOOR) // floor 3 bit, sat 5 bit @ highest
val B = A.fixTo(20 downto 3, RoundType.FLOOR) // floor 3 bit, expand 2 bit @ highest