一.试商法思想
同数学除法计算,1.先将被除数的最高位数与除数比较,即两者做差判断。
2.若差大于等于0,则商的最高位为1,将差与被除数下一位组合,重复算商至被除数最后一位。
3.当差小于0时,就将被除数的这一位与下一最高位组合,重复算商至被除数的最后一位。
举个栗子
12÷5。你得先把1和5比较吧,1比5小商个零,这个咱通常都省略不写,再把2落下来和1组合除5,12比5大商2,12与10做差得余2
在二进制里也一样,比如101/11,被除数的最高位1比除数11小,就把第2位0和1组合与除数11比大小,10比11小就再把1与10组合和除数11比大小,再用被除数减除数,商1,所得差为余数。
注:32位除法,至少需要32个时钟周期得到结果
二.DIV模块添加
1.DIV模块主要部分是一个有4个状态的状态机
DivFree:除法模块空闲
DivByZero:除数为零
DivOn:除法在进行
DivEnd:除法结束
状态转换图
start_i是否开始运算
annul_i是否取消运算(0有效)
opdata1_i为被除数 opdata2_i为除数
cnt计数时钟周期
2.div模块实现
代码很长,建议一次观看完保证完整性୧⍤⃝🌷
module div(
input wire clk,
input wire rst,
input wire signed_div_i,
input wire[31:0] opdata1_i,//被除数
input wire[31:0] opdata2_i,//除数
input wire start_i,//是否开始
input wire annul_i,//是否取消
output reg[63:0] result_o,//高32位为余数HI,低32位为商LO
output reg ready_o //对前面连接的EX模块发是否结束了除法的信号
);
wire[32:0] div_temp;//存对应位相减的差
reg[5:0] cnt;//记时钟周期,32时结束除法
reg[64:0] dividend;//低32位存商,高32位存余数,第32位空.
//低33-1位先存为被除数。作差,差小于零商给第0位0,被除数左移再做差;
//差大于零商给第0位1,被除数左移,差存入被除数少此位的左边。
reg[1:0] state;//记四个状态
reg[31:0] divsor;//除数(我也不理解为啥不直接用temp_op2 ?_?,
//或许是考虑除数temp_op2不为0时方便直接固定差的算法,可直接用差)
reg[31:0] temp_op1;
reg[31:0] temp_op2;//看是否取补码后的操作数
assign div_temp={1'b0,divdend[63:32]}-{1'b0,divisor};//先定好差的固定算法
always @ (posedge clk)begin
if(rst==1'b1)begin //复位初始化
state<='DivFree;
ready_o<='DivResultNotReady;
result_o<={'Zeroword,'ZeroWord};
end
else begin
case(state)
'DivFree:
begin if(start=='DivStart&&annul_i==1'b0) //除法开始且不取消
begin if(opdata2_i==32'h00000000) begin
state<='DivZero; end //除数为0转'DivZero状态
else
begin state<='DivOn; //除法正常进行状态。1.让计时器清零 2.负
//操作数取反码 3.设好被除数的某位与除数做差
cnt<=6'b000000;
if(signed_div_i==1'b1&&opdata1_i[31]==1'b1)
begin temp_op1=~opdata1_i+1; end
else
begin temp_op1=opdata1_i; end
if(signed_div_i==1'b1&&opdata2_i[31]==1'b1)
begin temp_op1=~opdata2_i+1; end
else
begin temp_op1=opdata2_i; end
dividend<={32'h00000000,32'h00000000};
dividend[32:1]<=temp_op1;//差为dividend[63:32]与除数divisor相减。 //将被除数先放入dividend[32:1]中,
//那么首次相减时就可先使被除数的最高位与除数比较。
divisor<=temp_op2;
end
end
else //除法未开始或被取消,不进行除法运算
begin ready_o<='DivResultNotReady;
result_o<={32'h00000000,32'h00000000};
end
end
'DivByZero: //除数为0
begin dividend<={32'h00000000,32'h00000000};//商0余0
//一般将商和余数都存在dividend中,在最后'DivEnd状态再将dividend赋给结果
state<='DivEnd;
end
'DivOn:
//运算未取消且未结束,差大于0和小于等于0对应不同情况,让时钟加1
//运算未取消但结束,看是否符号运算,看商和余数是否取补码,状态转'DivEnd结束,时钟清零
//运算取消,状态转'DivFree
begin if(annul_i==1'b0)
begin if(cnt!=6'b100000) //cnt计数时钟不是32,除法未进行完
begin if(div_temp[32]==1'b1) //差为负,dividend被除数左移,未参与运算的最高位加入
//商0,即在左移后的最后一位补0
begin dividend<={dividend[63:0],1'b0}; end
else //差为正,将差与未参与运算的最高位组合
begin dividend<={div_temp[31:0],dividend[31:0],1'b1}; end //差,除数,商 作为新的结果
cnt<=cnt+1;
end
else //运算未取消但结束
begin if((signed_div_i==1'b1)&&((opdata1_i[31]^opdata2_i[31]==1'b1)) //被除数与除数是否异号,异号商为负
begin dividend[31:0]<={~dividend[31:0]+1}; end
if((signed_div_i==1'b1)&&((opdata1_i[31]^dividend[64])==1'b1) //这个余数我不理解 -_-
begin dividend[64:33]<={~dividend[64:33]+1}; end
state<='DivEnd;
cnt<=6'b000000;
end
end
else
begin state<='DivFree; end
end
'DivEnd:
begin result_o<={dividend[64:33],dividend[31:0]}; //结果赋值,高32位存余数,低32位存商
ready_o<='DivResultReady; //向EX模块输出运算结束信号
if(start_i=='DivStop) //EX模块接收到上述运算结束信号时,送来运算停止信号。DIV模块到DivFree状态
begin state<='DivFree;
ready_o<='DivResultNotReady;
result_o<={32'h00000000,32'h00000000};
end
end
endcase
end
end
endmodule
最后祝大家中秋快乐,祝老师教师节快乐! ~_<