赞
踩
本文的目的是阐述如何将算法转化为RTL代码
即所有数据的小数点位置是固定的。通常,将小数点置于最高位之前,就为定点小数or纯小数,将小数点置于最低为之后,就为定点整数or纯整数。
如果小数点在中间,就为浮点数。
注意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中就可以实现。
加减法系统计算过程是 将减法转变为加上其补码,先扩位再计算
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
针对乘法算法实现 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个触发器
RTL基于Paper-Pencil Division Algorithm的除法器IP设计
根据之前的讨论,硬件电路中无法表示小数,所有的定点数都要看作是小数×2^n的结果,而且n的选取不同会导致同一个小数有多种不同的表示。
为了各计算机厂商有一个统一的标准,进而相互兼容,推出IEEE 754标准,用于二进制表示十进制浮点数。该标准现已广泛应用于各种CPU等核,即C++、Java等高级语言的float
、double
数据类型均是按照IEEE 754标准映射到底层硬件的。
该标准仅用于统一浮点数表示方法,在作四则运算比较困难,需要统一指数。
坐标旋转数字计算法(Coordinate Rotation Digital Computer, CORDIC)
如何使用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的数据一般为小数
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。