AFix(AFix是最新版Spinal新增的数据结构,不知为何该板块有些代码无法通过编译)

一、描述(Description)

AFix(Auto-ranging Fixed-Point), 是一类定点数能在进行定点数操作的过程中跟踪其表示范围的变化。

注意:这部分代码的绝大多数都仍在开发中, API和函数都可能随着版本变化, 欢迎大家提出反馈意见

二、声明(Declaration)

AFix能用bit大小或指数来创建:

AFix.U(12 bits)         //U12.0
AFix.UQ(8 bits, 4 bits) //U8.4
AFix.U(8 exp, 12 bits)  //U8.4
AFix.U(8 exp, -4 exp)   //U8.4
AFix.U(8 exp, 4 exp)    //U8.-4

AFix.S(12 bits)         //S11 + sign
AFix.SQ(8 bits, 4 bits) //S8.4 + sign
AFix.S(8 exp, 12 bits)  //S8.3 + sign
AFix.S(8 exp, -4 exp)   //S8.4 + sign

例如:

AFix.U(12 bits)的范围是0到4095.

AFix.SQ(8 bits, 4 bits)的范围是-4096(-256)到4095(255.9375)

AFix.U(8 exp, 4 exp)的范围所0到256.

定制化的AFix范围能通过直接初始化类来创建。

class AFix(val maxValue: BigInt, val minValue: BigInt, val exp: ExpNumber)

new AFix(4096, 0, 0 exp)        //[0 to 4096, 2^0]
new AFix(256, -256, -2 exp)     //[-256 to 256, 2^-2]
new AFix(16, 8, 2 exp)          //[8 to 16, 2^2]

maxValueminValue存储着可表示的支持的整数值, 这些值代表乘以2^exp之后的真实定点值。

AFix.U(2 exp, -1 exp)能表示:0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5

AFix.U(2 exp, -2 exp)能表示:-2.0, -1.75, -1.5, -1.25, -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75

指数可以大于0并且能代表大于1的值。

AFix.S(2 exp, 1 exp)能代表:-4, 2, 0, 2

AFix(8, 16, 2 exp)能代表:32, 36, 40, 44, 48, 52, 56, 60, 65

备注:AFix会用5 bits来存储这个类型, 正如它的maxValue存储16

三、数学操作(Mathematical Operations)

AFix在硬件层面支持加法(+), 减法(-), 和乘法(*)。除法(/), 和取模(%), 也都提供了但在硬件描述中不建议使用。

AFix值的运算就好像一般的Int一样, 有符号和无符号数都是可执行的, 且有符号与无符号数之间没有数据类型的区别。

//整数和分数的扩充
val a = AFix.U(4 bits)          // [   0 (  0.)     to  15 (15.  )]  4 bits, 2^0
val b = AFix.UQ(2 bits, 2 bits) // [   0 (  0.)     to  15 ( 3.75)]  4 bits, 2^-2
val c = a + b                   // [   0 (  0.)     to  77 (19.25)]  7 bits, 2^-2
val d = new AFix(-4, 8, -2 exp) // [-  4 (- 1.25)   to   8 ( 2.00)]  5 bits, 2^-2
val e = c * d                   // [-308 (-19.3125) to 616 (38.50)] 11 bits, 2^-4

//整数不扩充
val aa = new AFix(8, 16, -4) // [8 to 16] 5 bits, 2^-4
val bb = new AFix(1, 15, -4) // [1 to 15] 4 bits, 2^-4
val cc = aa + bb                 // [9 to 31] 5 bits, 2^-4

AFix支持运算且不用表示数扩展范围, 这种特点是通过选择每个输入的一致的最大最小范围来实现的。

+|无扩展加, -|无扩展减。

四、不相等操作(Inequality Operators)

AFix支持标准比较操作

A === B
A =\= B
A < B
A <= B
A > B
A >= B

注意:在编译时超出区间范围的操作会被优化掉!

五、Bit移位(Bitshifting)

AFix支持小数点和bit移位。

<<把小数点向左移动, 移动的距离加到指数上。>>把小数点向右移动, 移动的距离从指数中减去。<<|把bits左移, 添加bits 0。>>|把bits右移, 移除bits

六、近似和饱和(Saturation and Rounding)

AFix实现了饱和和所有普遍的近似方法。

饱和可以饱和到AFix值的可支持的值, 有很多辅助函数考虑了指数。

val a = new AFix(63, 0, -2 exp) // [0 to 63, 2^-2]
a.sat(63, 0)                    // [0 to 63, 2^-2]
a.sat(63, 0, -3 exp)            // [0 to 31, 2^-2]
a.sat(new AFix(31, 0, -1 exp))  // [0 to 31, 2^-2]

AFix近似模式:

// The following require exp < 0
.floor() or .truncate()
.ceil()
.floorToZero()
.ceilToInf()
// The following require exp < -1
.roundHalfUp()
.roundHalfDown()
.roundHalfToZero()
.roundHalfToInf()
.roundHalfToEven()
.roundHalfToOdd()

这些近似模式的数学上的表述可以查看Wiki

所有这些模式都会产生一个指数部分为0的AFix。如果近似到不同的指数需要考虑移位或者用truncated赋值。

七、赋值(Assignment)

AFix会在赋值的时候自动地检查范围和精度。默认的, 把AFix赋值给另一个有更小的范围或精度的AFix会产生错误。

.truncated函数用来控制如何给更小的数值类型赋值。

def truncated(
    saturation  : Boolean = false,
    overflow    : Boolean = true,
    rounding    : RoundType = RoundType.FLOOR)

def saturated(): AFix = this.truncated(saturation = true, overflow = false)

RoundType

RoundType.FLOOR
RoundType.CEIL
RoundType.FLOORTOZERO
RoundType.CEILTOINF
RoundType.ROUNDUP
RoundType.ROUNDDOWN
RoundType.ROUNDTOZERO
RoundType.ROUNDTOINF
RoundType.ROUNDTOEVEN
RoundType.ROUNDTOODD

saturation标志在赋值数据类型的时候增加逻辑产生饱和。

overflow标志在近似后不检查范围区间直接赋值。

当用精度不同的数据去赋值的时候都需要近似处理。