Verilog除法器(32位无符号+带符号)
32位无符号除法器设计恢复余数法:恢复余数除法的基本思路是从“部分余数”中减去除数,如果结果为负(不够减),则恢复原来的部分余数,商0.寄存器使用:reg_r 存放被除数areg_b 存放除数breg_r 存放余数(初始清零)具体做法:做减法时,减数是reg_b中的除数,被减数是reg_r中的余数左移一位,最低位由reg_q(被除数)的最高位补充。为了能够判断相减结果的正...
32位无符号除法器设计
恢复余数法:
恢复余数除法的基本思路是从“部分余数”中减去除数,如果结果为负(不够减),则恢复原来的部分余数,商0.
寄存器使用:reg_r 存放被除数a
reg_b 存放除数b
reg_r 存放余数(初始清零)
具体做法:
做减法时,减数是reg_b中的除数,被减数是reg_r中的余数左移一位,最低位由reg_q(被除数)的最高位补充。为了能够判断相减结果的正负,减法器的位数要比除数的位数多出一位。若相减为正,则减法器输出的最高位为0;若相减为负,则减法器输出的最高位为1。
若相减结果为正,把相减结果写入reg_r(部分余数),reg_q的内容左移一位,最低位放入商1。
若相减结果为负,把被减数写入reg_r(恢复之前的余数),reg_q的内容左移一位,最低位放入商0。
循环上述减法,知道被除数全部移除reg_q为止。计算结束后reg_q中内容是商,reg_r中的内容是余数
Tips:
之所以能够一位一位减,是因为若一个数a可以除b,则a左移一位之后(相当于乘2),仍然可除b。
不恢复余数除法
在恢复余数除法算法中,如果部分余数为负,则要恢复原来的余数并左移。设部分余数为R,除数为B。恢复余数相当于R+B,左移相当于(R+B)*2。以上操作完成后进行下一轮的迭代,即从部分余数中减去B。我们有以下的等式:
(R+B)2-B=R2+B
所以不管相减结果是正是负,都把它写入reg_r,若为负,下次迭代不是从中减去除数而是加上除数。
代码如下:
`timescale 1ns / 1ps
module DIVU(
input [31:00] dividend,
input [31:00] divisor,
input start,
input clock,
input reset,
output [31:00] q,
output [31:00] r,
output reg busy
);
wire ready;
reg [5:0] count;
reg [31:00] reg_q;
reg [31:00] reg_r;
reg [31:00] reg_b;
reg busy2,r_sign;
assign ready=~busy&busy2;
wire [32:0] sub_add=r_sign?({reg_r,q[31]}+{1'b0,reg_b}):
({reg_r,q[31]}-{1'b0,reg_b});
assign r=r_sign?reg_r+reg_b:reg_r;
assign q=reg_q;
always @(posedge clock or posedge reset)begin
if(reset)begin
count<=0;
busy<=0;
busy2<=0;
end
else begin
busy2<=busy;
if(start)begin
reg_r<=32'b0;
r_sign<=0;
reg_q<=dividend;
reg_b<=divisor;
count<=0;
busy<=1;
end
else if(busy)begin
reg_r<=sub_add[31:0];
r_sign<=sub_add[32];
reg_q<={reg_q[30:0],~sub_add[32]};
count<=count+1;
if(count==31)busy<=0;
end
end
end
endmodule
32位带符号除法器
带符号数不恢复余数除法
和不带符号不恢复余数思想一致。如果部分余数(第一次为被除数)和除数的符号相同,则减去除数,否者加上除数。这样做的结果导致求出的商为正数,最后还要根据符号对商进行调整。
代码如下:
`timescale 1ns / 1ps
module DIV(
input [31:0] dividend,
input [31:0] divisor,
input start,
input clock,
input reset,
output [31:0] q,
output [31:0] r,
output reg busy
);
wire ready;
reg [5:0] count;
reg [31:00] reg_q;
reg [31:00] reg_r;
reg [31:00] reg_b;
wire [31:00] reg_r2;
reg busy2,r_sign,sign;
assign ready=~busy&busy2;
wire [32:0] sub_add=r_sign?({reg_r,reg_q[31]}+{1'b0,reg_b}):
({reg_r,reg_q[31]}-{1'b0,reg_b});
assign reg_r2=r_sign?reg_r+reg_b:reg_r;
assign r=dividend[31]?(~reg_r2+1):reg_r2;
assign q=(divisor[31]^dividend[31])?(~reg_q+1):reg_q;
always @(posedge clock or posedge reset)begin
if(reset)begin
count<=0;
busy<=0;
busy2<=0;
end
else begin
busy2<=busy;
if(start)begin
reg_r<=32'b0;
r_sign<=0;
if(dividend[31]==1) begin
reg_q<=~dividend+1;
end
else reg_q<=dividend;
if(divisor[31]==1)begin
reg_b<=~divisor+1;
end
else reg_b<=divisor;
count<=0;
busy<=1;
end
else if(busy)begin
reg_r<=sub_add[31:0];
r_sign<=sub_add[32];
reg_q<={reg_q[30:0],~sub_add[32]};
count<=count+1;
if(count==31)busy<=0;
end
end
end
endmodule
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)