对于在性能要求不高的地方,可以使用这种除法
其原理是每一拍除数减去被除数,直到被除数小于除数。减的次数为商,剩下的是余数。
对于a/b,假设a位宽为m,b位宽为n,则需要m拍出结果
算法原理:
对于无符号除法,其商不会超过m,余数一定不会超过n
定义变量
assign a_tmp = {n{1'b0},a}
assign b_tmp = {b,m{1'b0}}
在每个周期开始时,先将a_tmp左移一位,末尾补0,然后与b_tmp比较,是否大于b_tmp,是则a_tmp减去b_tmp将且加上1,否则继续往下执行。上面的移位、比较和减法(视具体情况而定)要执行m次,执行结束后a_tmp的高n位即为余数,低m位即为商
举例:
假设4bit的两数相除 a/b,商和余数最多只有4位 (假设1101/0010也就是13除以2得6余1)
我们先自己做二进制除法,则首先看a的最高位,若比除数小则看前两位,大则减除数,然后看余数,以此类推直到最后看到最低位;而上述算法道理一样,a左移进前四位目的就在于从a本身的最高位开始看起,移4次则是看到最低位为止,期间若比除数大,则减去除数,注意减完以后正是此时所剩的余数。而商呢则加到了这个数的末尾,因为只要比除数大,商就是1,而商0则是直接左移了,因为会自动补0。这里比较巧因为商可以随此时的a继续左移,然后新的商会继续加到末尾。经过比对会发现移4位后左右两边分别就是余数和商。
具体实现:
通常,商的位宽与被除数位宽一致,如图1中所示
module division_cmp #(parameter N = 5,parameter M = 3)( input clk, input rst_n, input cpt_start, input [N-1:0] dividend, //被除数 input [M-1:0] divisor, //除数 output [N-1:0] quotient, //商 output [M-1:0] reminder, //余数 output cpt_end);assign rem_mux = cmp_start ? {N{1'b0}} : cur_rem;localparam CW = log2(N);wire [M-1:0] rem_mux;reg [M-1:0] cur_val;reg [M-1:0] nxt_val;wire cpt_val;reg [CW-1:0] cpt_cnt;wire [N:0] cpt_sub;reg quo_val;wire cpt_en;reg cpt_en_ff;assign rem_mux = cpt_start ? {N{1'b0}} : cur_val;assign cpt_val = dividend[N-cpt_cnt-:1];assign cpt_sub = {rem_mux,cpt_val} - {1'b0,divisor}; //左移1位并相减always @ (*) begin if(~cpt_sub[N]) begin //相减后为正数 nxt_val = cpt_sub[N-1:0]; //取相减后的值 quo_val = 1'b1; //商为1 end else begin //相减后为负数 nxt_val = {rem_mux[N-2:0],cpt_val}; //左移移1位 quo_val = 1'b0; //商为0 endendalways @ (posedge clk or negedge rst_n) begin if(~rst_n)Cur_val <= {M{1'b0}};cpt_en_ff <= 1'b0; elseCur_val <= nxt_val;cpt_en_ff <= cpt_en;end//计数,需要M拍always @ (posedge clk or negedge rst_n) begin if(~rst_n)cpt_cnt <= {CW{1'b0}}; else if(cpt_cnt==N-1)cpt_cnt <= {CW{1'b0}}; else if(cpt_start | (cpt_cnt>{CW{1'b0}})) cpt_cnt <= cpt_cnt + 1'b1;endassign cpt_en = cpt_start | (cpt_cnt>{CW{1'b0}});always @ (posedge clk or negedge rst_n) begin if(~rst_n)Quotient <= {M{1'b0}}; else if(cpt_en)Quotient[N-cpt_cnt-:1] <= quo_val;endassign cpt_end = (!cpt_en) & cpt_en_ff; //下降沿assign reminder = cur_val; endmodule
当商的位宽与被除数位宽不一致时
如图2,说明除数一定大于1,上面的代码也能实现,只是需要拍数较多
此时需要将数据提前写入rem_mux中
module division_cmp #(parameter N = 5,parameter M = 3,parameter P = 4,)( input clk, input rst_n, input cpt_start, input [N-1:0] dividend, //被除数 input [M-1:0] divisor, //除数 output [P-1:0] quotient, //商 output [M-1:0] reminder, //余数 output cpt_end);localparam CW = log2(P);//..............//省略//..............assign rem_mux = cpt_start ? dividend[P+M:P] : cur_val;//..............//省略//..............//计数,需要P拍always @ (posedge clk or negedge rst_n) begin if(~rst_n)cpt_cnt <= {CW{1'b0}}; else if(cpt_cnt==N-1)cpt_cnt <= {CW{1'b0}}; else if(cpt_start | (cpt_cnt>{CW{1'b0}})) cpt_cnt <= cpt_cnt + 1'b1;endendmodule
当遇到图3的情况,也是提前把数据写入rem_mux,但不是完全提前,高位需要补0
module division_cmp #(parameter N = 5,parameter M = 3,parameter P = 4,)( input clk, input rst_n, input cpt_start, input [N-1:0] dividend, //被除数 input [M-1:0] divisor, //除数 output [P-1:0] quotient, //商 output [M-1:0] reminder, //余数 output cpt_end);localparam CW = log2(P);//..............//省略//..............assign rem_mux = cpt_start ? {{P+M-N}{1'b0},dividend[P+M:P]} : cur_val;//..............//省略//..............//计数,需要P拍always @ (posedge clk or negedge rst_n) begin if(~rst_n)cpt_cnt <= {CW{1'b0}}; else if(cpt_cnt==N-1)cpt_cnt <= {CW{1'b0}}; else if(cpt_start | (cpt_cnt>{CW{1'b0}})) cpt_cnt <= cpt_cnt + 1'b1;endendmodule