8位无符号数乘法运算HDL设计实例
原理分析
加减乘除是运算的基础,也是我们在小学课堂里的重点必修课。乘除运算虽然对于我们今天来说还是小菜一碟,让计算机做起来也是九牛一毛不足挂齿,但是要真探究一下计算机是如何完乘除运算的,可还真有些学问和技巧,并不是人脑那么9*9一闪而过81出来了,计算机虽然得到结果的时间可能比人要快上不知道多少个数量级,但它怎么说还是需要一个过程的。
可能不同的CPU内部的运算原理和机制略有差异,我们也无法完全去把这些运算方式搞清楚,这个例程我们就老老实实的用移位累加的方式完成两个8位无符号数的乘法运算。这里先随便举个例子来说明我们的运算原理,例如8位无符号数189和25相乘。
因为计算机只认识0和1,因此一切运算的基础都是0和1,所以我们的运算也必须是基于2进制来进行的。因此,我们首先要完成机制的转换。乘数189对应的2进制数为10111101,被乘数25对应的2进制数为00011001。
按照我们最常用的10进制乘法运算的方式,我们可以得到如图1所示的2进制乘法。在这个运算过程中,我们从被乘数的最低位到最高位依次判断其取值是1还是0,如果是1则对乘数累加,否则不累加(即取0),需要累加的乘数根据当前被乘数位需要进行相应的移位,如被乘数的bit3为1,则乘数相应左移3次(即放大8倍)作为累加数。依据此原理,我们要设计的8位无符号乘法也是通过对被乘数进行逐位判断后累加进行左移的乘数而得到最终的结果。
图1 2进制乘法
在我们的8位无符号乘法运算中,一些基本的接口信号及其功能为:8位无符号数ain和bin是需要进行运算的两个乘数;输出的结果用16位无符号数yout表示;enable信号为运算使能信号;ready信号为运算完成标志位。用户先给ain和bin赋值,然后将enable信号拉高后即开始运算,大约8个时钟周期后运算输出结果,ready信号输出高电平表示运算结果有效,此后如果enable信号被用户拉低则ready信号也随后拉低,表示完成一次运算。接着用户可以给ain和bin赋新的运算值,然后拉高enable信号继续一次新的运算。
Verilog参考实例
以下是代码片段: module mux( clk,rst_n, enable,ain,bin,yout,ready ); input clk; input rst_n; input enable; (1) input[7:0] ain; (2) input[7:0] bin; (3) output reg[15:0] yout; (4) output reg ready; (5) reg[4:0] i; (6) always@(posedge clk) if(!rst_n) begin ready <= 1'b0; yout <= 16'h0000; i <= 4'd0; end else if(enable) begin if(i < 4'd8) i <= i+1'b1; else ; if(i < 4'd7) begin (7) ready <= 1'b0; if(ain) yout <= (yout+{1'b0,bin,7'd0})>>1; (8) else yout <= yout>>1; (9) end else if(i == 4'd7) begin (10) if(ain) yout <= yout+{1'b0,bin,7'd0}; (11) else ; (12) ready <= 1'b1; (13) end else ready <= 1'b0; end else begin i <= 4'd0; yout <= 16'h0000; end endmodule |
仿真验证
这里的验证专门编写了一个小任务,入口参数是给ain和bin的赋值,然后使能enable信号,发起一次运算操作,待ready信号拉高后比对运算结果yout是否正确,打印结果,然后撤销(拉低)enable信号完成当前运算。在initial里面,通过256*256次调用这个小任务,完成对该乘法器的验证。
以下是代码片段: `timescale 1 ns/ 1 ps module mux_vlg_tst(); reg [7:0] ain; reg [7:0] bin; reg clk; reg enable; reg rst_n; wire ready; wire [31:0] yout; reg[8:0] i,j; mux i1 ( .ain(ain), .bin(bin), .clk(clk), .enable(enable), .ready(ready), .rst_n(rst_n), .yout(yout) ); initial begin $display("mux example simulation is running.\n"); rst_n = 0; clk = 0; enable = 0; ain = 8'hzz; bin = 8'hzz; #1000; @(posedge clk); rst_n = 1; for(i=0;i<256;i=i+1) begin (1) for(j=0;j<256;j=j+1) begin (2) mux_ab(i,j); (3) end end $display("mux example simulation is over.All right.\n"); (4) $stop; end always #10 clk = ~clk; task mux_ab; (5) input[7:0] a; input[7:0] b; begin @(posedge clk); #3; ain = a; bin = b; enable = 1; (6) @(posedge ready); (7) @(posedge clk); #3; if(a*b == yout) $display("%3d * %3d = %5d, it is right.",a,b,yout); (8) else begin (9) $display("%3d * %3d = %5d, it is wrong.",a,b,yout); $stop; end @(posedge clk); #3; enable = 0; (10) ain = 8'hzz; bin = 8'hzz; end endtask endmodule |
如图2所示,为当前测试结果,我们看到了最后的“mux example simulation is over.All right.”提示信息,表示测试通过。
图2 mux工程测试结果
用户1721897 2013-11-20 21:12
用户1694070 2013-10-27 21:04
特权大哥,我并没有全看明白你的代码,但是我有个疑问,问什么要检测被乘数是否为1呢?检测乘数不是更简单吗?比如:两个4位数相乘yout=ain*bin。每次判断if(bin[i]),成立那么就把被乘数左移,然后yout=yout+ain<<i