在进行数字信号处理的时候,计算是必不可少的,通常情况下,能够不用乘法器和除法器就不用乘除法器,可以采用移位和加减法的方式来完成计算。但在一些特殊情况下,希望采用乘除法,这时候在FPGA当中就需要专用的IP了。乘除法在FPGA当中实现起来是比较困难的一件事情。若直接在verilog 代码中使用了乘法或者除法,其实最终对应到电路中,要么是采用大量的block ram来实现,要么是占用DSP资源。这种情况下,对资源的占用是拿捏不准确。因此需要使用专用的乘除法器来实现乘除法。
在进行数字信号处理的时候,这三个码首先得搞清楚了。例如
数据 | 源码 | 反码 | 补码 |
---|---|---|---|
65 | 01000001 | 01000001 | 01000001 |
-65 | 11000001 | 10111110 | 10111111 |
正数的源码补码,反码都是它本身。但是负数就需要注意了,关于如何来计算源码反码补码,其实也比较简单。
首先是正数:例如100,那么它的二进制表示就是8’b0110_0100。
如果是负数:例如-100,那么他的二进制补码的表示就是 8’b1001_1100
在FPGA当中,数据都是以补码的形式存在的,知道了这个,就能够很好对数据进行运算了。
由于在FPGA当中,数据都是以补码的形式存在的,因此对数据进行求绝对值的时候,就比较好操作了。如果一个数是正数,那么这个数据绝对值就是它本身。如果一个数是负数,那么直接按位取反再加一就可以了。
数据类型 | 求绝对值方法 |
---|---|
正数 | 绝对值就是数据本身 |
负数 | 按位取反再加一 |
还是上面的例子,-100的补码是8’1001_1100
它的绝对值是100。也就是8’b0110_0100。也就是由负数的补码,按位取反(包含符号位)再加一得到。
在进行数据位宽的扩展的时候,对于有符号数的处理,得格外的小心,最高位一定不能给搞忘了。
举个简单的例子:
用8bit表示一个正数100
现在用16bit来表示这个数,现在多了8bit,因此多出的高位的8bit,需要用原始数据的最高位来填充,也就是最高位全部填充0。
若用8bit数据表示一个负数 -100;
现在用16bit来表示这个数,现在多了8bit,因此多出的高位的8bit,需要用原始数据的最高位来填充,也就是最高位全部填充1。
前面写的都是整数,在FPGA当中如何来表示小数呢。这里就涉及到一个量化的问题,就是用一个整数来表示小数。在FPGA中常用的就是定点数来表示小数。所谓定点就是指小数点的位置是固定的。比如一个数用8位表示,符号位1位,整数位1位,小数位4位。那么如何使用定点数来表示一个这样的数呢。
比如4.5,按照上面的方式进行定点化。
4.5 × 2 4 = 72 4.5\times 2^{4}= 72 4.5×24=72
可以看到定点化之后的结果是72。其中符号位1位,整数位3位,小数位4位。
负数的定点化和正数类似。比如:
− 4.5 × 2 4 = − 72 -4.5\times 2^{4}= -72 −4.5×24=−72
个人在识别负数的时候,有个个人的小喜好。那就是符号位为1表示这个数是负数,那么最大的负数表示范围就是-2N-1(N是数据的位宽),那么用补码表示这个数据的时候,就是最大的负数表示范围加上后面的数。就比如这个 -72。最大的负数表示范围是-128,再加上后面的符号位后面的数就是结果。(歪门邪道,不可信)
其实可以看作: − 72 = − 2 7 + 2 5 + 2 4 + 2 3 -72 = -2^{7} + 2^{5}+2^{4}+2^{3} −72=−27+25+24+23
在使用定点数的时候,不可避免地会引入量化误差。这个量化误差的精度是由需要量化的小数的位宽决定的。比如一个8bit数,最高位符号位,3位整数位,4位小数位。那么量化位宽就是4位。因此量化的最小精度是1/24,也就是0.0625。
在这里举一个简单的例子。
对于1.0625在不同情况下,进行定点数的转化。
case(1) : 8bit数,最高位为符号位,3位整数位,4位小数位
case(2): 8bit数,最高位为符号位,4位位整数, 3位小数位
对于第一种情况:量化的结果为: 1.0625 × 2 4 = 17 1.0625\times 2^{4} = 17 1.0625×24=17
能够完整地表达出原始的数据,1.0625
对于第二种情况:量化的结果为: 1.0625 × 2 3 = 8.5 ≅ 8 1.0625\times 2^{3} = 8.5\cong 8 1.0625×23=8.5≅8
此时由于量化,导致量化后的数据相较于原始数据有了误差,能表达的数据是1。
对比上面两种情况可以看出,当小数位为3位的时候,对1.0625进行量化的时候,就出现了量化的误差。这个就是由量化的位宽(量化精度)引起的。
负数的定点化其实和前面正数的定点化是类似的。只是最终需要取补码。
例如: 最高位为符号位,3位整数位,4位小数位
− 1.0625 × 2 4 = − 17 -1.0625\times 2^{4} = -17 −1.0625×24=−17
其实和前面一样的,最高位符号位为1,最大负数表示范围:-8。因此
− 8 + 2 2 + 2 1 + 2 − 1 + 2 − 2 + 2 − 3 + 2 − 4 = − 1.0625 -8+2^{2}+2^{1}+2^{-1}+2^{-2}+2^{-3}+2^{-4}= -1.0625 −8+22+21+2−1+2−2+2−3+2−4=−1.0625(歪门邪道,且不可信233333333)
IP的配置界面如下啦:
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2020 All rights reserved
// -----------------------------------------------------------------------------
// Author : WCC [email protected]
// File : tb_divider_sim
// Create : 2020-12-24
// Revise : 2020-
// Editor : Vscode, tab size (4)
// Functions : divider test
//
// -----------------------------------------------------------------------------
`timescale 1ns / 1ps
module tb_divider_sim();
reg clk ;
reg signed [15:0] divisor ;
reg signed [15:0] dividend ;
reg data_in_tvalid ;
wire div_valid ;
wire signed [31:0] div_data ;
div_gen_0 inst_divider (
.aclk(clk), // input wire aclk
.s_axis_divisor_tvalid(data_in_tvalid), // input wire s_axis_divisor_tvalid
.s_axis_divisor_tdata(divisor), // input wire [15 : 0] s_axis_divisor_tdata
.s_axis_dividend_tvalid(data_in_tvalid),// input wire s_axis_dividend_tvalid
.s_axis_dividend_tdata(dividend), // input wire [15 : 0] s_axis_dividend_tdata
.m_axis_dout_tvalid(div_valid), // output wire m_axis_dout_tvalid
.m_axis_dout_tdata(div_data) // output wire [31 : 0] m_axis_dout_tdata
);
initial begin
clk = 0;
forever #(10) clk = ~clk;
end
initial begin
data_in_tvalid = 0;
dividend = 0;
divisor = 0;
repeat(100)@(posedge clk);
data_in_tvalid = 1;
divisor = 16'd5;
dividend = 16'd25;
repeat(30)@(posedge clk);
divisor = 16'd5;
dividend = -16'd25;
repeat(30)@(posedge clk);
divisor = 16'd5;
dividend = 16'd12;
repeat(30)@(posedge clk);
divisor = 16'd5;
dividend = -16'd12;
repeat(30)@(posedge clk);
divisor = 16'd5;
dividend = -16'd4;
repeat(30)@(posedge clk);
divisor = 16'd5;
dividend = -16'd4;
end
endmodule
从输入的激励来看,前两个是能够被整除的,后面几个是不能被整除的。
这里看一下不能被整除的这几个。
可以看到除法器IP的使用,需要指定除法器的基于类型,除数和被除数的位宽,得到结果的余数类型需要指定。然后还需要注意的是,除法器使用的接口是axis接口的,只有在valid有效的时候,才会开始计算。
同时也还要注意到,除法器得到结果是需要很长的latency的。这个latency可以设置。合适的才是最好的