定点计数系统实验

定点计数系统实验

计算机中的数据处理方法

现代计算机通常采用分层的存储结构, 需要处理的数据被存放于主存储器(RAM)中, 中央处理器(CPU) 把数据逐个从RAM中读取到CPU的寄存器(Register)中, 然后由算术逻辑单元(ALU)对寄存器中的数据进行计算, 计算的结果又会被写回到寄存器中, 然后再被写出到主存储器里。下图给出了一个简化的计算机中执行整数加法的过程。 两个存放于RAM中的数字, 4 和 9 , 被分别加载到寄存器R1 和 R2 中, 由 算术单元 ALU 执行相加的操作, 加法的结果被输出到寄存器 R3 中, 随后写出到 RAM里。
这里写图片描述
计算机中的加法
使用计算机进行数据处理中的另一个重要概念是字长, 所谓字长, 是指CPU一次运算操作能够处理的最长的二进制位数。 当前的主流处理器字长通常为32甚至为64。 早期的计算机的字长为8, 即 CPU 中寄存器的数据宽度为 8 个比特, ALU 运算单元一次只能完成 8比特宽度的数值计算。 下图示意了寄存器中的两个8比特数值被ALU单元相加生成一个8比特结果。
这里写图片描述
8比特数据加法

整数的有限字长问题

上文例子中 两个加数分别是 110 和 30 , 结果 是 140,这些数字均小于 8比特二进数能够表示的最大数值 255(十进制), 即: 1111 1111 (二进制) = 255(十进制) 。 于是,善于质疑的读者会提出问题, 如果两个加数均不超过255, 但是加法和大于 255 , 将会得到怎样的结果? 比如十进制的 130 和 150 相加, 理论上的结果是280。 但是8比特字长无法承载280这个数据。 实际上CPU在遇到这种情况时,会在8比特的结果寄存器中保留数值 24 (即10进制的280换算成2进制数 1 0001 1000 的低8位), 然后 会将一个特殊的寄存器的某个标志位的值设为1, 通常是中断标志(Interrupt Flag)寄存器的溢出(Overflow)标志位, 这会引发一个 “硬件异常 (hardware exception)”, 导致处理器发生指令跳转并执行异常处理的程序代码。

计算机中的小数

在数值计算中, 我们经常需要使用小数。 虽然在计算机中使用二进制来表示小数系统, 但是其含义和数学中的十进制小数系统是类似的。 下图给出了十进制和二进制小数系统的对比示意。
这里写图片描述
接下来, 计算机系统需要解决如何表示小数点的问题,在数学中,有2种小数的表示方法
1.科学记数法, 例如, 0.37 * 10E2 , 其中0.37称为尾数, 数学上要求其绝对值介于1和10之间。
2.诸如 10.3 , 35.73 这种我们日常用的方法。
以上两种表示方法, 在计算机系统中均有使用, 前者发展成为浮点数格式, 后者发展成定点数表示方法。

IEEE754 浮点数

为了表示小数, 计算机工业制定了 IEEE754标准, 被称为 “浮点数标准”。 为了适应不同的需要, IEEE754标准定义了 16、32 、64 、 128 比特 多种字长的小数表示方法 。 其中最常用的是 32比特字长的小数格式,即,float 单精度浮点。 下图给出了 32比特 单精度浮点数的格式, 各部分数值的含义, 以及一个计算样例
这里写图片描述
从上图中可以看出, 浮点数采用的是类似科学记数法的表示方式, 即整个数据字长被分为 符号位、指数、小数(尾数)三部分。 其中,尾数被折算为一个介于1和2之间的数据, 然后根据折算出来的指数再确定小数点的位置。 由于小数点的位置是随着数据内容动态确定的, 浮点数也因此而得名。 浮点数的优点是能够有效的利用数据字长, 由于采用了指数方法, 这种格式能够有效的表示较大和较小的数值。

浮点数的计算

以十进制为例(乘法)
浮点数乘法只需要对尾数做乘法,以及对指数做加法如:
•计算 : 2.5 E3 * 6 E2 = (2.5 * 6) E (3 + 2) = 15 E5 = 1.5 E6
浮点数做加法较为困难, 因为需要先调整加数的指数, 把运算的两个加数的小数点对齐, 运算完毕后还需要再次调整指数数据, 以把尾数的计算的结果进行规范化, 使得尾数部分介于1和2之间。
以十进制为例,
•计算 : 1.7 E3 + 1.1 E2 = 1.7 E3 + 0.11 E3 = 1.81 E3
•计算 : 1.7 E3 + 9.8 E3 = 11.5 E3 = 1.15 E4
从上面的例子可以看出, 当加数的指数不一致时, 需要把小的数据向大的数据看齐。 当尾数相加产生了数量级变化时, 需要调整指数, 重新把尾数调整回规定的范围。

定点数加法

在下文中, 定点数格式的表示方法被记为SI(N)F(M),S表示为有符号数, N比特整数位, M比特小数位。 例如:SI9F7表示:有符号数,9比特整数位,7比特小数位。
两个定点数相加,设其定点格式分别为 SI(N1)F(M1) 和 SI(N2)F(M2), 设 N1 > N2, M1 > M2。
•首先需要进行数据对齐, 即两个定点数的小数字长, 整数字长分别一致
•即,在小数字长较小的数据低位补零, 在整数字长较小的数据高位符号扩展
•字长调整后, 两个数据的字长均为 SI(N1)F(M1)
•为避免溢出, 还需将整数字长扩充1位
•加法结果的字长格式为 SI(N1+1)F(M1)
•对于变量字长无法扩充的情况,则为了避免溢出, 将加数进行算术右移,抛弃小数部分,从而为整数部分留出更多空间。
注:所谓算术右移是指对高位数据进行符号扩展的右移操作, 而逻辑右移操作仅对高位补零。 不同的编程语言中算术右移的实现方式不同, 请参考相关资料

浮点数转换为定点数

理论模型中的各种数据均为浮点类型, 有时需要把理论模型中的数据变量转化为定点小数类型, 例如,一个数字滤波器中的滤波系数常数,最简单的浮点转为定点的方法是乘以缩放因子,即:
•设浮点类型数据变量为 V_f ,需要转化为小数字长为FWL的 定点数 V_i
•记: 缩放因子 SCALE = 2的FWL次幂, 即 2 ^ FWL 或 (1 << FWL)
•则 V_i 等于 int(V_f * SCALE),其中 int()表示取整操作
•如果希望提升数据精度, 则可以再取整时加入舍入操作
•即根据数据的符号, 在对数据取整之前, 加上或者减去 0.5

定点数转化为浮点数

当需要把定点计算的结果代入回理论计算模型时, 需要将其转换回浮点格式, 则有
•设 定点数 V_i 其 小数字长为FWL, 要转化为浮点类型数据变量 V_f
•记: 缩放因子 SCALE = 2的FWL次幂, 即 2 ^ FWL 或 (1 << FWL)
•则有 V_f = float(V_i) / SCALE
•其中 float() 表示将数据转化为浮点格式, 其中的 “/” 号 表示浮点除法

C 语言的变量类型及实验

变量类型字长

明确处理器对应的变量字长, 通常32位处理器上变量类型的字长配置如下
•int 表示 32比特(4字节)有符号整数
•unsigned int 表示 32比特(4字节)无符号整数
•short 表示 16比特(2字节)有符号整数
•unsigned short 表示 16比特(2字节)无符号整数
•char 表示 8比特(1字节)有符号整数
•unsigned char 表示 8比特(1字节)无符号整数
•float 表示 32位(4字节)单精度浮点数
•double表示 表示 64位(8字节)双精度浮点数

定点加法

两个定点数相加时,需要将小数部分字长调整一致,例如
•3个 char 类型变量 A B C
•变量A 为 SI2F6, 变量B 为 SI3F5, 变量C 为 SI4F4,
•则 C = (A >> 2)+(B >> 1),肯定不会溢出
•如果希望提高计算精度,则需要增加结果变量C的字长
•声明 short C ,然后对齐 A、B的小数点
•C = A + (B << 1),此时 C 为 SI10F6格式

定点乘法

两个定点数相乘时,需要确保结果的字长不会溢出, 例如
•2个 char 类型变量 A B 相乘,乘法的全精度结果为16比特
•全精度乘法结果需要使用short类型变量保存
•假设A 为 SI1F7, B为SI1F7, C的类型为short
•则C = A * B , C的定点格式为 SI2F14, (C >> 7)格式为 SI9F7
•如果不希望给结果变量使用更高字长,可以先对乘数进行移位
•例如 A/B/C 均为char类型, 定点格式均为 SI1F7
•则: C = (A >> 3) * (B >> 4) 确定不会溢出,并且 C 仍然为 SI1F7格式

定点-浮点转换

浮点数转换为定点数,需要把浮点数乘以缩放因子然后取整, 例如
•float A_f = 0.9 转换为 SI1F7格式的 char A_i
•定义 SCALE = (1 << 7)
•A_i = (char) (A_f * SCALE)

定点数转换为浮点数,需要把定点数除以缩放因子, 例如
•SI1F7格式的 char A_i 转换为 float A_f
•定义 SCALE = (1 << 7)
•A_f = A_i* 1.0 / SCALE
•其中 “* 1.0 ” 的操作 用于通知编译器进行类型转换, 避免做整数除法

程序代码

#include <stdio.h>
#define FL  7 // fraction length 
#define SCALE_FL     (1<<7)
#define SCALE_FL_2   (1<<(FL*2))//缩放因子
mult_test(){
  float a_f, a_q; char  a_i;
  float b_f, b_q; char  b_i;
  float c_f, c_q; short c_i;
  a_f =  -0.999 ;
  b_f =   0.999 ;
  a_i = a_f * SCALE_FL ;    // fixed point value, S1I0F7
  b_i = b_f * SCALE_FL ;
  a_q = a_i *1.0/ SCALE_FL ;    // quant value
  b_q = b_i *1.0/ SCALE_FL ;
  c_i = a_i * b_i;
  c_q = c_i*1.0 / SCALE_FL_2;
  c_f = a_f * b_f;

  printf("# Mult Test\n");
  printf("# Data Quant Fraction length is %d bit\n", FL);
  printf("# a_f =  %-10f, a_i = %-8d 0x%-8x , a_q = %f\n", a_f, a_i, a_i, a_q);
  printf("# b_f =  %-10f, b_i = %-8d 0x%-8x , b_q = %f\n", b_f, b_i, b_i, b_q);
  printf("# c_f =  %-10f, c_i = %-8d 0x%-8x , c_q = %f\n", c_f, c_i, c_i, c_q);
  printf("#\n");
}
add_test(){
  float a_f, a_q; char  a_i;
  float b_f, b_q; char  b_i;
  float c_f, c_q; short c_i;

  a_f =  -0.999 ;
  b_f =  -0.999 ;
  a_i = a_f * SCALE_FL;    // fixed point value, S1I0F7
  b_i = b_f * SCALE_FL;
  a_q = a_i *1.0/ SCALE_FL;    // quant value
  b_q = b_i *1.0/ SCALE_FL;

  c_i = a_i + b_i;
  c_q = c_i*1.0 / SCALE_FL;
  c_f = a_f + b_f;

  printf("# ADD Test\n");
  printf("# Data Quant Fraction length is %d bit\n", FL);
  printf("# a_f =  %-10f, a_i = %-8d 0x%-8x , a_q = %f\n", a_f, a_i, a_i, a_q);
  printf("# b_f =  %-10f, b_i = %-8d 0x%-8x , b_q = %f\n", b_f, b_i, b_i, b_q);
  printf("# c_f =  %-10f, c_i = %-8d 0x%-8x , c_q = %f\n", c_f, c_i, c_i, c_q);
  printf("#\n");

}

float_check(){

  float v_f = 1.125;
  void   *p = NULL;
  unsigned int v_f_i = 0;
  unsigned int S, E, M;

  printf("# Float Check\n");
  p = &v_f;
  v_f_i = *((unsigned int *) p);
  printf("# v_f = %f, hex val v_f_i = 0x%x \n", v_f, v_f_i );
  S = v_f_i >> 31; E = (v_f_i >> 23)&((1 << 8) - 1); M = v_f_i & (( 1 << 23)-1);
  printf("# S = %d, E = %d, M = 0x%x \n", S, E, M);
  printf("#\n");

}
main(){

  printf("# Fixed point Demo LAB\n\n");
  mult_test();
  add_test();
  float_check();

  printf("# \n");
  printf("# Done, press Enter key to quit\n");
  getch();
}

Verilog 硬件设计语言及实验

有符号数的关键字 signed

Verilog HDL 设计语言和C不同, 它默认的信号变量类型是无符号变量, 对于 端口(input output),线网(wire)和 reg信号变量而言, 如果不声明为 signed 类型, 则电路编译器默认其是 无符号类型的信号变量。 由于 硬件电路的设计阶段需要使用多个工具, 比如仿真阶段使用ModelSim, 电路综合阶段使用 Quartus。 为了保证电路代码在不同EDA工具之间的行为一致性, 为保险起见,进行 加法或乘法运算的 两个信号以及运算的结果信号 ,最好同时声明为 signed 类型信号。
在Verilog代码中,进行字长处理,比如符号扩展和截取数据位时, 位拼接符和位寻址符, 使用的非常频繁, 例如:
•a[7:4] 表示取出信号a的比特7到比特4
•{a[3],a[0]} 表示把信号a的比特3和比特0拼接在一起
•{7{a[3]}} 表示把信号a的比特3重复7次, 作为一个新的信号

verilog实现定点加法

由于 Verilog HDL设计语言可以精确设定比特级别的信号字长, 为了保持不同的EDA工具的行为一致性,有以下建议:
【1】两个加数和结果信号均为signed 类型
【2】定点加法两个加数的小数和整数字长保持一致, 如果不一致, 则先进行补齐
【3】定点加法的结果字长, 其整数字长比加数的整数字长多1, 加法结果的小数字长和加数的小数字长保持一致。
【4】如果HDL代码中有不满足以上条件, 则建议先在设计流程的EDA工具上进行实验, 确保代码在各个工具上行为一致。

verilog实现定点乘法

进行 整数乘法的 两个信号,其全精度结果信号的整数字长和小数字长, 分别等于两个乘数的整数字长之和及小数字长之和。 HDL 代码 可以先计算出 全精度字长的乘积信号, 然后根据精度需要, 截取出所需的小数字长供后级电路使用。

定点数的验证

Verilog 语法支持 real 格式类型数据, 这是一种浮点类型, 该数据通常仅用来仿真验证。 在电路仿真中, 将定点数转换为浮点数来验证结果的正确性。 由于HDL仿真 是一种波形样点级别的仿真, 并不擅长验证带有复杂数学含义的批量数据, 所以复杂数据通常会在仿真的testbench中将数据导出,使用其他工具(比如Matlab)进行数学意义上的验证。

程序代码

module s_add(
  IN1 ,
  IN2 ,
  OUT );

parameter INWL  = 8       ;
parameter OUTWL = INWL + 1;

input  signed [INWL -1:0] IN1, IN2;
output signed [OUTWL-1:0] OUT;

reg   signed [OUTWL-1:0] W_add;

always @ (IN1 or IN2) begin
  W_add = IN1 + IN2;
end

assign OUT = W_add;

endmodule

module s_mult(
  IN1 ,
  IN2 ,
  OUT );

parameter IN1WL  = 8       ;
parameter IN2WL  = 8       ;
parameter OUTWL  = IN1WL + IN2WL;


input signed [IN1WL -1:0] IN1;
input signed [IN2WL -1:0] IN2;
output signed [OUTWL-1:0] OUT;

reg   signed [OUTWL-1:0]  W_p;

always @ (IN1 or IN2) begin
  W_p = IN1 * IN2;
end

assign OUT = W_p;

endmodule
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值