(原创)采用加法器数乘法器实现17位有符号数相乘(Verilog)

时间:2022-11-01 10:22:37

本例程采用加法器数乘法器实现17位有符号数相乘。参考《基于Verilog HDL 的数字系统应用设计》,王钿 ,桌兴旺 编著

 

  
  
  
1 module signed_mult17b_addtree (
2 mul_a,
3 mul_b,
4 mul_out,
5 clk,
6 rst_n,
7 );
8
9   parameter MUL_WIDTH = 17 ;
10   parameter MUL_RESULT = 33 ;
11
12   input [MUL_WIDTH - 1 : 0 ] mul_a;
13   input [MUL_WIDTH - 1 : 0 ] mul_b;
14   input clk;
15   input rst_n;
16
17   output [MUL_RESULT - 1 : 0 ] mul_out;
18
19   reg [MUL_RESULT - 1 : 0 ] mul_out;
20   reg [MUL_RESULT - 1 : 0 ] mul_out_reg;
21   reg msb;
22   reg msb_reg_0;
23   reg msb_reg_1;
24   reg msb_reg_2;
25   reg msb_reg_3;
26   reg [MUL_WIDTH - 1 : 0 ] mul_a_reg;
27   reg [MUL_WIDTH - 1 : 0 ] mul_b_reg;
28
29   reg [MUL_RESULT - 2 : 0 ] stored0;
30   reg [MUL_RESULT - 2 : 0 ] stored1;
31   reg [MUL_RESULT - 2 : 0 ] stored2;
32   reg [MUL_RESULT - 2 : 0 ] stored3;
33   reg [MUL_RESULT - 2 : 0 ] stored4;
34   reg [MUL_RESULT - 2 : 0 ] stored5;
35   reg [MUL_RESULT - 2 : 0 ] stored6;
36   reg [MUL_RESULT - 2 : 0 ] stored7;
37 reg [MUL_RESULT - 2 : 0 ] stored8;
38 reg [MUL_RESULT - 2 : 0 ] stored9;
39 reg [MUL_RESULT - 2 : 0 ] stored10;
40 reg [MUL_RESULT - 2 : 0 ] stored11;
41 reg [MUL_RESULT - 2 : 0 ] stored12;
42 reg [MUL_RESULT - 2 : 0 ] stored13;
43 reg [MUL_RESULT - 2 : 0 ] stored14;
44 reg [MUL_RESULT - 2 : 0 ] stored15;
45
46 reg [MUL_RESULT - 2 : 0 ] add0_0;
47 reg [MUL_RESULT - 2 : 0 ] add0_1;
48 reg [MUL_RESULT - 2 : 0 ] add0_2;
49 reg [MUL_RESULT - 2 : 0 ] add0_3;
50 reg [MUL_RESULT - 2 : 0 ] add0_4;
51 reg [MUL_RESULT - 2 : 0 ] add0_5;
52 reg [MUL_RESULT - 2 : 0 ] add0_6;
53 reg [MUL_RESULT - 2 : 0 ] add0_7;
54
55 reg [MUL_RESULT - 2 : 0 ] add1_0;
56 reg [MUL_RESULT - 2 : 0 ] add1_1;
57 reg [MUL_RESULT - 2 : 0 ] add1_2;
58 reg [MUL_RESULT - 2 : 0 ] add1_3;
59
60 reg [MUL_RESULT - 2 : 0 ] add2_0;
61 reg [MUL_RESULT - 2 : 0 ] add2_1;
62
63 reg [MUL_RESULT - 1 : 0 ] add3_0;
64
65 always @ ( posedge clk or negedge rst_n )
66 begin
67 if ( ! rst_n )
68 begin
69 mul_a_reg <= 17 ' b0;
70 mul_b_reg <= 17 ' b0;
71
72 stored0 <= 32 ' b0;
73 stored1 <= 32 ' b0;
74 stored2 <= 32 ' b0;
75 stored3 <= 32 ' b0;
76 stored4 <= 32 ' b0;
77 stored5 <= 32 ' b0;
78 stored6 <= 32 ' b0;
79 stored7 <= 32 ' b0;
80 stored8 <= 32 ' b0;
81 stored9 <= 32 ' b0;
82 stored10 <= 32 ' b0;
83 stored11 <= 32 ' b0;
84 stored12 <= 32 ' b0;
85 stored13 <= 32 ' b0;
86 stored14 <= 32 ' b0;
87 stored15 <= 32 ' b0;
88
89 add0_0 <= 32 ' b0;
90 add0_1 <= 32 ' b0;
91 add0_2 <= 32 ' b0;
92 add0_3 <= 32 ' b0;
93 add0_4 <= 32 ' b0;
94 add0_5 <= 32 ' b0;
95 add0_6 <= 32 ' b0;
96 add0_7 <= 32 ' b0;
97
98
99 add1_0 <= 32 ' b0;
100 add1_1 <= 32 ' b0;
101 add1_2 <= 32 ' b0;
102 add1_3 <= 32 ' b0;
103
104 add2_0 <= 32 ' b0;
105 add2_1 <= 32 ' b0;
106
107 add3_0 <= 32 ' b0;
108
109 msb <= 1 ' b0;
110 msb_reg_0 <= 1 ' b0;
111 msb_reg_1 <= 1 ' b0;
112 msb_reg_2 <= 1 ' b0;
113 msb_reg_3 <= 1 ' b0;
114
115 mul_out_reg <= 33 ' b0;
116 mul_out <= 33 ' b0;
117
118 end
119 else
120 begin
121 mul_a_reg <= (mul_a[ 16 ] == 0 ) ? mul_a : {mul_a[ 16 ], ~ mul_a[ 15 : 0 ] + 1 ' b1};
122 mul_b_reg <= (mul_b[ 16 ] == 0 ) ? mul_b : {mul_b[ 16 ], ~ mul_b[ 15 : 0 ] + 1 ' b1};
123
124 msb_reg_0 <= mul_a_reg[ 16 ] ^ mul_b_reg[ 16 ];
125 msb_reg_1 <= msb_reg_0;
126 msb_reg_2 <= msb_reg_1;
127 msb_reg_3 <= msb_reg_2;
128 msb <= msb_reg_3;
129
130 stored0 <= mul_b_reg[ 0 ] ? { 16 ' b0,mul_a_reg[15:0]} : 32 ' b0;
131 stored1 <= mul_b_reg[ 1 ] ? { 15 ' b0,mul_a_reg[15:0],1 ' b0} : 32 ' b0;
132 stored2 <= mul_b_reg[ 2 ] ? { 14 ' b0,mul_a_reg[15:0],2 ' b0} : 32 ' b0;
133 stored3 <= mul_b_reg[ 3 ] ? { 13 ' b0,mul_a_reg[15:0],3 ' b0} : 32 ' b0;
134 stored4 <= mul_b_reg[ 4 ] ? { 12 ' b0,mul_a_reg[15:0],4 ' b0} : 32 ' b0;
135 stored5 <= mul_b_reg[ 5 ] ? { 11 ' b0,mul_a_reg[15:0],5 ' b0} : 32 ' b0;
136 stored6 <= mul_b_reg[ 6 ] ? { 10 ' b0,mul_a_reg[15:0],6 ' b0} : 32 ' b0;
137 stored7 <= mul_b_reg[ 7 ] ? { 9 ' b0,mul_a_reg[15:0],7 ' b0} : 32 ' b0;
138 stored8 <= mul_b_reg[ 8 ] ? { 8 ' b0,mul_a_reg[15:0],8 ' b0} : 32 ' b0;
139 stored9 <= mul_b_reg[ 9 ] ? { 7 ' b0,mul_a_reg[15:0],9 ' b0} : 32 ' b0;
140 stored10 <= mul_b_reg[ 10 ] ? { 6 ' b0,mul_a_reg[15:0],10 ' b0} : 32 ' b0;
141 stored11 <= mul_b_reg[ 11 ] ? { 5 ' b0,mul_a_reg[15:0],11 ' b0} : 32 ' b0;
142 stored12 <= mul_b_reg[ 12 ] ? { 4 ' b0,mul_a_reg[15:0],12 ' b0} : 32 ' b0;
143 stored13 <= mul_b_reg[ 13 ] ? { 3 ' b0,mul_a_reg[15:0],13 ' b0} : 32 ' b0;
144 stored14 <= mul_b_reg[ 14 ] ? { 2 ' b0,mul_a_reg[15:0],14 ' b0} : 32 ' b0;
145 stored15 <= mul_b_reg[ 15 ] ? { 1 ' b0,mul_a_reg[15:0],15 ' b0} : 32 ' b0;
146
147 add0_0 <= stored0 + stored1;
148 add0_1 <= stored2 + stored3;
149 add0_2 <= stored4 + stored5;
150 add0_3 <= stored6 + stored7;
151 add0_4 <= stored8 + stored9;
152 add0_5 <= stored10 + stored11;
153 add0_6 <= stored12 + stored13;
154 add0_7 <= stored14 + stored15;
155
156 add1_0 <= add0_0 + add0_1;
157 add1_1 <= add0_2 + add0_3;
158 add1_2 <= add0_4 + add0_5;
159 add1_3 <= add0_6 + add0_7;
160
161 add2_0 <= add1_0 + add1_1;
162 add2_1 <= add1_2 + add1_3;
163
164 add3_0 <= add2_0 + add2_1;
165
166 mul_out_reg <= {msb,add3_0[ 31 : 0 ]};
167 mul_out <= (mul_out_reg[ 32 ] == 0 ) ? mul_out_reg : {mul_out_reg[ 32 ], ~ mul_out_reg[ 31 : 0 ] + 1 ' b1};
168
169 end
170 end
171
172 endmodule
173
174

 

调试小结:本例程采用了5级流水线技术,以至于能在一个时钟周期内完成一次乘法运算输出。参考的范例为4位无符号输入,采用2级流水线技术。

              由于是有符号运算,符号位要单独处理,也就是把两个数的最高位相异或,即可得到乘积的符号位。如:

  
  
  
1 msb_reg_0 <= mul_a_reg[ 16 ] ^ mul_b_reg[ 16 ];

 

              有符号数是以补码形式存放在内存的,所以必须要将补码转换为原码后再进行运算,运算后的结果再根据乘积符号位判断最终结果是正还是负,若为负,还需将运算后的结果还原为补码,之后加入一位符号位,便可得到33位的有符号最终结果。(无论是整数还是小数都适用)

 

  
  
  
1 mul_a_reg <= (mul_a[ 16 ] == 0 ) ? mul_a : {mul_a[ 16 ], ~ mul_a[ 15 : 0 ] + 1 ' b1};
2 mul_b_reg <= (mul_b[ 16 ] == 0 ) ? mul_b : {mul_b[ 16 ], ~ mul_b[ 15 : 0 ] + 1 ' b1};
3 mul_out <= (mul_out_reg[ 32 ] == 0 ) ? mul_out_reg : {mul_out_reg[ 32 ], ~ mul_out_reg[ 31 : 0 ] + 1 ' b1};

 

              由于采用流水线技术,所以有可能会出现时序不一致,该例程出现的问题就是符号位的运算和数据位的运算时序不一致,所以添增了几层赋值运算,确保二则时序一致,不然将会运算出错。

 

  
  
  
1 msb_reg_1 <= msb_reg_0;
2 msb_reg_2 <= msb_reg_1;
3 msb_reg_3 <= msb_reg_2;
4 msb <= msb_reg_3;

 

              本例程只用了一个always,里面有很多的运算,最初,我心里想,我在一个always里面进行那么多的运算,一个时钟周期怎么能处理完呢,如果一个时钟周期处理不完,那么不是有些变量的值会丢失吗,最初没有调试成功,我一直都怀疑是这个原因,所以我就把always里的每一个运算都单独拿出来,放到一个always里面,也就是每个always里只有一个运算,一共写了好几十个always,程序写得相当的长,但问题还是没有解决,然后再一次仔细研究,才发现是符号位运算的时序跟数据位运算不一致,所以就想办法延时符号位运算结果送至最终乘积结果的时间,现在想出的办法是多加几层的赋值,来拖延符号运算结果送至最终乘积结果的时间。这时才发现,原来我一直没有搞明白always是怎样一步一步执行的,现在明白了,时钟触发always语句里面一般都用非阻塞赋值,非阻塞赋值就是第二个赋值语句,不需要等待第一个赋值语句完成后才进行,这样的结果是,第二个赋值语句不能以第一个语句的最新结果参与运算,而是以前一状态的值参与运算。他们几乎是同时进行的,也就是在每个时钟内,always里面的语句都将执行一次,不存在一个时钟内,有些语句还未执行到。最初,我学Verilog的时候就把非阻塞语句和阻塞语句区别开了的,但似乎并没有真正理解并会应用。我为这个,付出了很多很多,现在终于解决了!

 

 改正一处:

原来:

  
  
  
1 mul_out_reg <= {msb,add3_0[ 31 : 0 ]};
2 mul_out <= (mul_out_reg[ 32 ] == 0 ) ? mul_out_reg :
3 {mul_out_reg[ 32 ], ~ mul_out_reg[ 31 : 0 ] + 1 ' b1};

更改为: 

  
  
  
1 mul_out_reg <= (add3_0 == 0 ) ? 33 ' b0 : {msb,add3_0[31:0]};
2 mul_out <= (mul_out_reg == 0 ) ? 33 ' b0 : (mul_out_reg[32]==0)? mul_out_reg :
3 {mul_out_reg[32 ], ~ mul_out_reg[31 : 0 ] + 1 ' b1};
4

 

更改原因:当输入的两个数,一个为0,另一个为负数时,按原来的代码运算乘积并不是0,而是-1,所以需要改正,改正后不会出现这样的错误。