Verilog实现32位除法器
实现乘法器有多种思路,本次作业采用的是不恢复余数的方式。下面是对两种参考思路的介绍。
基于移位、减法的恢复余数除法器
对于32位无符号除法,可将被除数a转换成高32位为0低32位是a的数temp_a,在每个周期开始时 temp_a 向左移动一位,最后一位补零,然后判断 temp_a 的高 32 位是否大于等于除数b,如是则 temp_a 的高32位减去b并且加1,得到的值赋给 temp_a,如果不是则直接进入下一步,执行结束后 temp_a 的高 32 位即为余数,低 32 位即为商。对于 32 位有符号除法,可先将有符号数转换成无符号数除法,根据被除数和除数的符号判断商的符号,被除数是负数时余数为负,否则为正。
不恢复余数除法器
不恢复余数即不管相减结果是正还是负,都把它写入 reg_r,若为负,下次迭代不是从中减去除数而是加上除数。
Verilog实现
无符号除法器时钟信号下降沿时检查 start 信号,有效时开始执行,执行除法指令时,busy 标志位置 1。在执行除法指令时,任何情况下不产生算数异常,当除数为 0 时,运算结果未知,对除法器除数为 0 和溢出情况的发生通过汇编指令中其他指令进行检查和处理。无符号运算 Verilog 代码如下:
`timescale 1ns / 1ps
module DIVU(
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 //除法器忙标志位
);
reg [4:0] count;
reg [31:0] reg_q;
reg [31:0] reg_r;
reg [31:0] reg_b;
reg r_sign;
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 @(negedge clock or posedge reset)
begin
if (reset == 1)
begin
count <= 5'b0;
busy <= 0;
end
else
begin
if (start&&(!busy))
begin //初始化
reg_r <= 32'b0;
r_sign <= 0;
reg_q <= dividend;
reg_b <= divisor;
count <= 5'b0;
busy <= 1'b1;
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 + 5'b1; //计数器加一
if (count == 5'b11111) busy <= 0;
end
end
end
endmodule
有符号运算中基本和无符号除法器类似,需要注意余数应与被除数符号一致,Verilog 代码如下:
`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 //除法器忙标志位
);
reg [4:0] count;
reg [31:0] reg_q;
reg [31:0] reg_r;
reg [31:0] reg_b;
reg q_sign;
reg r_sign;
reg a_sign;
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 r = a_sign? (-(r_sign? reg_r+reg_b:reg_r)):(r_sign? reg_r+reg_b:reg_r);
assign q = q_sign? -reg_q:reg_q;
always@(negedge clock or posedge reset)
begin
if(reset)
begin count<=0;busy<=0;end
else if(start&&(!busy))
begin
count<=0;reg_q<=dividend[31]?-dividend:dividend;
reg_r<=0;
reg_b<=divisor[31]?-divisor:divisor;
r_sign<=0;busy<=1'b1;
q_sign<=dividend[31]^divisor[31];
a_sign<=dividend[31];
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
endmodule