目录
本文的目的是阐述如何将算法转化为RTL代码
1. 定点数
即所有数据的小数点位置是固定的。通常,将小数点置于最高位之前,就为定点小数or纯小数,将小数点置于最低为之后,就为定点整数or纯整数。
如果小数点在中间,就为浮点数。
RTL只能整数运算
注意RTL中没有小数点,无法表示小数,只能作整数运算
那涉及小数运算怎么实现呢?RTL可看作:小数扩 2 n 2^n 2n倍成整数后的运算
例如python模型如下
import math as m
def dut (a, b):
a_ext = a * m.pow(2,2)
#...整数运算
return res
相应地RTL模型中如下
module dut(
input clk,
input [4:0] a, // 此处a为 扩4倍后的结果
input [5:0] b, // 此处b为 扩8倍后的结果
output [6:0] res
);
assign a_ext = {a,1'd0}; // 相同量纲,尾添1'd0
//...整数运算
endmodule
这样就将所有的小数运算转化为整数运算了,在RTL中就可以实现。
1.1. 整数
1.2. 加减法
加减法系统计算过程是 将减法转变为加上其补码,先扩位再计算
a + b = [ a ] 补 + [ b ] 补 a + b = [a]_补 + [b]_补 a+b=[a]补+[b]补
a − b = [ a ] 补 + [ − b ] 补 a - b = [a]_补 + [-b]_补 a−b=[a]补+[−b]补
注意扩位是不是有signed
溢出保护
如果被赋值变量的位宽能够覆盖所有取值,就不会出现溢出。否则,就可能出现数据溢出,结果为错值,需要做量化处理。
unsigned 溢出检测和保护RTL例程如下
module u_ovf_chk(
input [3:0] a,
input [3:0] b,
output [1:0] res
);
// max(a) = 4'd15, max(b) = 4'd15, 故max(a+b) = 'd30,因此res必溢出,创建不溢出变量res_ext
wire [4:0] res_ext;
assign res_ext = a + b;
// res_ext[4:2]有1'b1则说明溢出,则res取最大值2'd3
always@(*) begin
if(|res_ext[4:2])
res = 2'b11;
else
res = res_ext[1:0];
end
endmodule
unsigned 溢出检测和保护python例程如下
res = a + b;
if(res >3):
res = 3
如果是有符号数,就要考虑是上溢还是下溢
signed 溢出检测和保护RTL例程如下
module s_ovf_chk(
input signed [4:0] a,
input signed [4:0] b,
output signed [2:0] res
);
// a和b均在-16~15,故a+b在-32~30,因此res必溢出,创建不溢出变量res_ext
wire signed [5:0] res_ext;
assign res_ext = a + b;
// 溢出且res_ext为正数,则为上溢,否则为下溢
always@(*) begin
if(res_ext > 3)
res = 3;
else if(res_ext < -4)
res = -4;
else
res = res_ext[2:0];
end
endmodule
signed 溢出检测和保护RTL例程如下
res = a + b;
if(res>3):
res = 3;
elif(res<-4):
res = -4
1.3. 乘法
针对乘法算法实现 x [ m − 1 : 0 ] × y [ n − 1 : 0 ] x[m-1:0]×y[n-1:0] x[m−1:0]×y[n−1:0] ,介绍几种乘法器的实现方式,三种实现对比如下
实现方式 | 流水线周期 | 操作时间 | 消耗资源数量 |
---|---|---|---|
查找表 | 1个CLK | 1个CLK | |
二叉加法树 | 1个CLK | 可任意配置 | max(m,n)个1bit乘法器,max(m,n)-1个加法器,较少触发器 |
累加器 | 1个CLK | 可任意配置 | max(m,n)个1bit乘法器,max(m,n)-1个加法器,较多触发器 |
补码一位乘(Booth算法) | - | - | - |
查找表
二叉加法树乘法器
4级流水例图如下
累加乘法器
4级流水例图如下。与加法树对比可知,加法树用了11个触发器,阵列加法器用了14个触发器
补码一位乘(Radix-2 Booth)
1.4. 除法
查找表
Paper-Pencil Division Algorithm
RTL基于Paper-Pencil Division Algorithm的除法器IP设计
2. IEEE 754 协议
根据之前的讨论,硬件电路中无法表示小数,所有的定点数都要看作是小数×2^n的结果,而且n的选取不同会导致同一个小数有多种不同的表示。
为了各计算机厂商有一个统一的标准,进而相互兼容,推出IEEE 754标准,用于二进制表示十进制浮点数。该标准现已广泛应用于各种CPU等核,即C++、Java等高级语言的float
、double
数据类型均是按照IEEE 754标准映射到底层硬件的。
该标准仅用于统一浮点数表示方法,在作四则运算比较困难,需要统一指数。
2.1. 单精度浮点float & 双精度浮点double
2.2. IEEE 754 四则运算
2.3. IEEE 754 与定点数 的转化
3. 函数拟合
3.1. 坐标旋转数字计算法(Coordinate Rotation Digital Computer, CORDIC)
坐标旋转数字计算法(Coordinate Rotation Digital Computer, CORDIC)
3.2. 查找表法
如何使用RTL拟合一个函数呢?例如 y = f ( x ) , x ∈ ( a , b ) y=f(x), x\in(a,b) y=f(x),x∈(a,b),可以使用基于查找表的方法。
使用ROM拟合,先将输入信号映射为ROM地址,再将ROM地址映射为 ( a , b ) (a,b) (a,b)内的值,再函数值 y y y映射到ROM值,整个映射过程如下
module fx#(
parameter N = 16,
parameter M
)(
input clk,
input [N-1:0] x_bin,
output [M-1:0] y_bin
);
wire [N-1:0] x_bin_addr;
assign x_bin_addr = x_bin + {1'b1,{(N-1){1'b0}}};
ROM_fx U_ROM_FX( // M × (2^N) ROM
.clk (clk ),
.addr (x_bin_addr ),
.dout (y_bin )
);
endmodule
● N位输入→N位ROM地址
ROM地址与输入位宽相同。若存在负数,那么输入值从 N ′ b 100...00 → N ′ b 011...11 N'b100...00→N'b011...11 N′b100...00→N′b011...11是从最小到最大线性变化,如果直接作为ROM地址,则ROM地址变化不是线性的,所以作转化。
x_bin_addr [N-1:0] = x_bin[N-1:0] + N’b100…00
即
此时x_bin_addr 是 N ′ b 000...00 → N ′ b 111...11 N'b000...00→N'b111...11 N′b000...00→N′b111...11也从最小到最大线性变化的
● ROM地址→x
即用 N ′ b 000...00 → N ′ b 111...11 N'b000...00→N'b111...11 N′b000...00→N′b111...11表示 ( a , b ) (a,b) (a,b)的区间,即采样值。因此有
x = a + b − a 2 N \frac{b-a}{2^N} 2Nb−a · x_bin_addr,即Nbit输入的精度为 b − a 2 N \frac{b-a}{2^N} 2Nb−a
● x→y
即y = f(x) = f(a + b − a 2 N \frac{b-a}{2^N} 2Nb−a · x_bin_addr)
● y→ROM数据
y的数据一般为小数