FPGA搭积木之复数乘法器以及ModelSim自动化仿真的技巧

发布于:2024-04-30 ⋅ 阅读:(19) ⋅ 点赞:(0)

1.前言

  在数字信号处理种复数乘法去使用的非常多,今天分享一个自己设计的复数乘法器,并将设计参数化,放入自己的代码库,供有需要时直接使用。相比于官方提供的封闭的IP核,自己设计的IP核虽然性能比不过,但是更灵活,方便进行个性化修改。FPGA其实就像搭积木一样,只要自己的代码库够丰富,设计只会越来越轻松!今天学习了在testbench中自动化对比仿真的技巧,对比错误将信息打印出来,方便回到波形中去查看,对比通过输出pass。

2.原理

  两个复数相乘有:
C A ⋅ C B = ( a + b j ) ( c + d j ) C_A \cdot C_B = (a + bj)(c + dj) CACB=(a+bj)(c+dj) = ( a c − b d ) + ( a d + b c ) j =(ac - bd) + (ad + bc)j =(acbd)+(ad+bc)j
  那么,
R e = a c − b d Re = ac - bd Re=acbd
I m = a d + b c Im = ad + bc Im=ad+bc
  如果直接计算需要使用四个乘法器,外加两个加法器。为了减少资源使用:

  令
m 1 = d ( a − b ) m_1 = d(a-b) m1=d(ab)
m 2 = a ( c − d ) m_2 = a(c-d) m2=a(cd)
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=acbd=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个加法器所消耗的资源远远不及一个乘法器。

  电路结构如下:

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

4.仿真

  编写仿真代码,遍历范围内的数据对乘法器进行验证。在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其实就像搭积木一样,只要自己的代码库够丰富,设计只会越来越轻松!点击下面链接查看合集

此合集持续分享一些笔者自己设计的可复用硬件模块点击进入:FPGA搭积木


网站公告

今日签到

点亮在社区的每一天
去签到