赞
踩
在数字信号处理种复数乘法去使用的非常多,今天分享一个自己设计的复数乘法器,并将设计参数化,放入自己的代码库,供有需要时直接使用。相比于官方提供的封闭的IP核,自己设计的IP核虽然性能比不过,但是更灵活,方便进行个性化修改。FPGA其实就像搭积木一样,只要自己的代码库够丰富,设计只会越来越轻松!今天学习了在testbench中自动化对比仿真的技巧,对比错误将信息打印出来,方便回到波形中去查看,对比通过输出pass。
两个复数相乘有:
C
A
⋅
C
B
=
(
a
+
b
j
)
(
c
+
d
j
)
C_A \cdot C_B = (a + bj)(c + dj)
CA⋅CB=(a+bj)(c+dj)
=
(
a
c
−
b
d
)
+
(
a
d
+
b
c
)
j
=(ac - bd) + (ad + bc)j
=(ac−bd)+(ad+bc)j
那么,
R
e
=
a
c
−
b
d
Re = ac - bd
Re=ac−bd
I
m
=
a
d
+
b
c
Im = ad + bc
Im=ad+bc
如果直接计算需要使用四个乘法器,外加两个加法器。为了减少资源使用:
令
m
1
=
d
(
a
−
b
)
m_1 = d(a-b)
m1=d(a−b)
m
2
=
a
(
c
−
d
)
m_2 = a(c-d)
m2=a(c−d)
m
3
=
b
(
c
+
d
)
m_3 = b(c+d)
m3=b(c+d)
代入上面式子可得
R
e
=
a
c
−
b
d
=
m
1
+
m
2
Re = ac - bd = m_1 + m_2
Re=ac−bd=m1+m2
I
m
=
a
d
+
b
c
=
m
1
+
m
3
Im = ad + bc = m_1 + m_3
Im=ad+bc=m1+m3
这样变换之后改为使用3个乘法器,5个加法器,相比减少了1个乘法器的使用,虽然多使用了3个加法器,但是3个加法器所消耗的资源远远不及一个乘法器。
电路结构如下:
将设计参数化,形成自己的IP核,参考如下:
module complexX #( parameter WIDTH1 = 16, parameter WIDTH2 = 16 )( input clk, //输入(a+bj)(c+dj) input signed [WIDTH1-1:0] a, input signed [WIDTH1-1:0] b, input signed [WIDTH2-1:0] c, input signed [WIDTH2-1:0] d, output signed [WIDTH1+WIDTH2-1:0] Re, output signed [WIDTH1+WIDTH2-1:0] Im ); //m1 = d(a-b) //m2 = a(c-d) //m3 = b(c+d) //Re = ac - bd = m1 + m2 //Im = ad + bc = m1 + m3 wire [WIDTH1:0] aSb; wire [WIDTH2:0] cSd; wire [WIDTH2:0] cAd; reg [WIDTH1+WIDTH2-1:0] m1 = 'd0; reg [WIDTH1+WIDTH2-1:0] m2 = 'd0; reg [WIDTH1+WIDTH2-1:0] m3 = 'd0; assign aSb = $signed(a) - $signed(b); assign cSd = $signed(c) - $signed(d); assign cAd = $signed(c) + $signed(d); always@(posedge clk)begin m1 <= $signed(d) * $signed(aSb); m2 <= $signed(a) * $signed(cSd); m3 <= $signed(b) * $signed(cAd); end assign Re = $signed(m1) + $signed(m2); assign Im = $signed(m1) + $signed(m3); endmodule
编写仿真代码,遍历范围内的数据对乘法器进行验证。在testbench中有一些技巧可以提高仿真效率,自动化对比仿真,对比错误将信息打印出来再回到波形中去查看,对比通过输出pass。因为遍历16位宽的数据仿真时间太长,这里将参数改为4位。
module complexX_tb; parameter T = 10; parameter WIDTH1 = 4; parameter WIDTH2 = 4; reg clk ; reg signed [WIDTH1 -1 : 0] a,b ; reg signed [WIDTH2 -1 : 0] c,d ; wire signed [WIDTH1+WIDTH2-1 : 0] Re,Im ; complexX #(.WIDTH1(WIDTH1), .WIDTH2(WIDTH2)) u_complexX( .clk(clk), .a (a ), .b (b ), .c (c ), .d (d ), .Re (Re ), .Im (Im ) ); always #(T/2) clk = ~clk; reg signed [WIDTH1 -1 : 0] i; reg signed [WIDTH2 -1 : 0] j; initial begin clk = 1'b0; #(10*T); for(i = -$pow(2,WIDTH1-1); i < $pow(2,WIDTH1-1)-1; i= i+1)begin for(j = -$pow(2,WIDTH2-1); j < $pow(2,WIDTH2-1)-1; j = j+1)begin a = $signed(i); b = $signed(j); c = $signed(i); d = $signed(j); #(T); if ((Re != ((a * c) - (b * d)))||(Im != ((a * d) + (b * c))) ) begin $display("***ERROR at time = %0d ***", $time); $display("a =%d, b =%d, c =%d, d =%d, Re =%d, Im =%d",a, b, c, d, Re, Im); $stop; end #(T); end end $display("****** Testbench Successfully completed! ****** "); $display("*** ###### ### ##### ##### *** "); $display("*** # # ## ## # # *** "); $display("*** # # # # # # *** "); $display("*** ######## ####### ##### ##### *** "); $display("*** # # # # # *** "); $display("*** # # # # # *** "); $display("*** # # # ##### ##### *** "); $stop; end endmodule
如果仿真出现计算错误,会立即停止仿真,并显示下图所示的信息:
如果仿真完全正确,则会打印如下信息:
最终仿真结果如下图所示:
学习FPGA的时候很多常用的模块可以将其参数化,形成自己的ip,以后方便调用。做FPGA设计是一个逐渐积累的过程。相比于官方提供的封闭的IP核,自己设计的IP核虽然性能比不过,但是更灵活,方便进行个性化修改。FPGA其实就像搭积木一样,只要自己的代码库够丰富,设计只会越来越轻松!点击下面链接查看合集
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。