0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于FPGA的高效除法器设计

FPGA研究院 来源:数字站 2025-10-28 14:56 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

来源:数字站

FPGA可以通过除号直接实现除法,但是当除数或被除数位宽较大时,计算会变得缓慢,导致时序约束不能通过。此时可以通过在除法IP中加入流水线来提高最大时钟频率,这种方式提高时钟频率也很有限。如果还不能达到要求,就只能把除法器拆分,来提高系统时钟频率。

其实最简单的方式是使用计数器对除数进行累加,并且把累加的次数寄存,当累加结果大于等于被除数时,此时寄存的累加次数就是商,而被除数减去累加结果就得到余数。

但这种方式存在一种弊端,当除数很小的时候,被除数特别大时,需要经过很多个时钟周期才能计算除结果。比如被除数为100,除数为1,就需要100个时钟左右才能计算出结果,效率无疑是低下的。因此一般不会使用这种方式实现除法器。

01除法器原理

首先观察图1中二进制数10011001除以1010的计算过程,除数与被除数位宽相差4,4位除数(1010)大于被除数的高4位(1001),小于被除数的高5位(10011),所以首先是被除数的高5位减去除数(如图紫色数字),得到01001。然后被除数的第2位到6位数据依旧大于除数,则在次减去除数,就这样依次相减,最后得到商为1111,余数为3。

034429c4-b069-11f0-8c8f-92fbcf53809c.png

03981052-b069-11f0-8c8f-92fbcf53809c.png

图1 二进制除法

其实通过上面讲述,就可以根据这个规律实现除法器了,每次从被除数高位取除数相同数据位宽的数据与除数进行比较,大于等于时直接做减法,商加1。小于时被除数数据位宽向低位移动,被除数每右移动1位,商左移1位,直到被除数移动到最低位时结束运算。

以上描述的方法在FPGA上不太好实现,最好是改进一下,将被除数和除数比较的位进行固定,那样就会好实现很多。

如图2所示,还是前文的8位10011001除以4位1010,通过一个9位的寄存器存储8位被除数,始终比较被除数寄存器的第4到8位数据与除数的大小,如果大于除数,则进行减法运算,否则将被除数左移1位,然后在次进行判断。

首先是5’b01001小于除数4’b1010,则将被除数左移1位。然后被除数高五位数据5’b10010大于除数,则商1,两者相减得到01001。得到数据作为被除数运算位,此时被除数高5位还是比除数小,将被除数左移1位,相当于运算被除数的低位数据,同时也要把商左移1位。

03ebace4-b069-11f0-8c8f-92fbcf53809c.png

图2 除法运算

就如同上图循环判断被除数高5位数据与除数的大小关系,然后对被除数进行移位和减法运算,就能够计算出商和余数。当被除数的最低位数据被移入高5位时,在通过判断被除数高5位数据与除数大小并进行相应减法后,运算结束。也就是说,这种情况下,被除数位宽为8,除数位宽为4,则被除数左移4次后计算结束,余数等于最后被除数的数值右移4位(被除数总共左移的次数)。

这种运算FPGA实现就较为简单,例如被除数位宽为M,除数位宽为N,满足M>=N,只需要比较被除数[M:M-N]与除数的大小,被除数左移M-N次后运算结束,将最终的被除数右移M-N位得到余数。

上述只适用于一般情况,还会出现图3这种,被除数依旧是8位10000001,4位除数为0010,此时如果还拿被除数高5位减去除数,得到的结果仍然大于除数,会导致计算错误。

0445bcf2-b069-11f0-8c8f-92fbcf53809c.png

034429c4-b069-11f0-8c8f-92fbcf53809c.png

图3 除法运算

此时可以把除数进行左移,直到除数的最高位为1或者除数左移后大于被除数的高5位为止,需要考虑除数左移1位,相当于增大2倍,要保持最终结果不变,被除数左移次数需要增加除数左移的次数,并且最后余数右移的次数也会相应增加。所以上述计算变为图4所示。

04ea1716-b069-11f0-8c8f-92fbcf53809c.png

034429c4-b069-11f0-8c8f-92fbcf53809c.png

图4 除法运算

首先除数根据条件需要左移2位都不会大于被除数的高5位,且不会溢出,所以除数就先左移2位,然后就是判断被除数高5位是否大于除数,大于除数就进行减法运算且商加1,减法运算结果小于除数取代被除数参与运算的数据位。如果被除数高5位小于除数,则被除数和商一起左移,然后在次判断。最终当被除数左移次数达到除数左移次数加被除数与除数位宽之差时计算结束。02除法器实现

经过上述分析,首先当输入数据有效时,需要判断除数和被除数是否为0,其中一个为0,则输出均为0,且除数为0时,应该报错。该模块采用一个状态机作为架构,包括空闲状态,左移除数,除法运算三个状态。假设除数位宽L_DIVR,被除数位宽L_DIVN,且L_DIVN>=L_DIVR。

模块初始处于空闲状态,只有上游模块把开始计算信号拉高且被除数与除数均不为0时,状态机跳转到左移除数状态。此时需要把除数和被除数存入对应寄存器,把除数左移次数计数器、被除数左移次数计数器、商和余数暂存器清零。

开始信号只有在状态机处于空闲状态下才会有效,其余时间不会接收上游模块的除数、被除数等数据。

在左移除数的状态下,如果除数最高位为0,且除数左移1位比被除数寄存器的高L_DIVR+1位小,那么将除数左移1位,除数左移次数计数器加1。否则状态机跳转到除法运算状态。

状态机处于除法运算状态时,需要计算被除数高L_DIVR+1减去除数是否大于0。如果大于等于0,则将减法结果作为被除数的高L_DIVR+1位,商的最低位赋值为1,也就是加1。如果小于0且被除数左移次数小于除数左移次数加L_DIVN-L_DIVR时,把被除数和商均左移1位,被除数左移次数计数器加1。

如果被除数左移次数等于除数左移次数加L_DIVN-L_DIVR,则状态机跳转到空闲状态,将被除数右移除数左移次数加L_DIVN-L_DIVR得到余数。

状态机从除法状态跳转到空闲状态时,把商、余数进行输出。


以下代码是模块端口信号:

//当输入除数为0时,error信号拉高,且商和余数为0;
//当ready信号为低电平时,不能将开始信号start拉高,此时拉高start信号会被忽略。
modulediv#(
 parameter    L_DIVN   = 8    ,//被除数的位宽;
 parameter    L_DIVR   = 4    //除数的位宽;
)(
input               clk    ,//时钟信号;
input               rst_n   ,//复位信号,低电平有效;


inputstart    ,//开始计算信号,高电平有效,必须在ready信号为高电平时输入才有效。
input    [L_DIVN -1:0]   dividend  ,//被除数输入;
input    [L_DIVR -1:0]   divisor   ,//除数输入;


 output reg            ready   ,//高电平表示此模块空闲。
 output reg            error   ,//高电平表示输入除数为0,输入数据错误。
output reg            quotient_vld ,//商和余数输出有效指示信号,高电平有效;
 output reg   [L_DIVR -1:0] remainder  ,//余数,余数的大小不会超过除数大小。
output reg   [L_DIVN -1:0] quotient   //商。
);

以下是状态机状态编码和中间信号的定义,以及通过自动计算位宽函数取计算计数器的位宽。

 localparam     L_CNT    =  clogb2(L_DIVN) ;//利用函数自动计算移位次数计数器的位宽。
 localparam    IDLE    = 3'b001   ;//状态机空闲状态的编码;
 localparam    ADIVR   = 3'b010   ;//状态机移动除数状态的编码;
 localparam    DIV    = 3'b100   ;//状态机进行减法计算和移动被除数状态的编码;


 reg          vld    ;//
 reg     [2:0]    state_c   ;//状态机的现态;
 reg     [2:0]    state_n   ;//状态机的次态;
 reg     [L_DIVN :0]  dividend_r  ;//保存被除数;
 reg     [L_DIVR -1:0] divisor_r   ;//保存除数。
 reg     [L_DIVN -1:0] quotient_r   ;//保存商。
 reg     [L_CNT -1:0]  shift_dividend ;//用于记录被除数左移的次数。
 reg     [L_CNT -1:0]  shift_divisor ;//用于记录除数左移的次数。


 wire     [L_DIVR :0]   comparison  ;//被除数的高位减去除数。
 wire           max    ;//高电平表示被除数左移次数已经用完,除法运算基本结束,可能还需要进行一次减法运算。


//自动计算计数器位宽函数。
function integerclogb2(input integer depth);begin
if(depth ==0)
   clogb2 =1;
elseif(depth !=0)
for(clogb2=0; depth>0; clogb2=clogb2+1)
    depth=depth >>1;
  end
 endfunction


max用来判断被除数左移次数是否等于除数于被除数位宽差加除数左移次数。而comparison在除数左移状态时,需要计算被除数高位与除数左移后相减的结果,在其余状态下,计算被除数高位与除数相减的结果。

//max为高电平表示被除数左移的次数等于除数左移次数加上被除数与除数的位宽差;
 assign max = (shift_dividend == (L_DIVN - L_DIVR) + shift_divisor);


//用来判断除数和被除数第一次做减法的高位两者的大小,当被除数高位大于等于除数时,comparison最高位为0,反之为1。
//comparison的计算结果还能表示被除数高位与除数减法运算的结果。
//在移动除数时,判断的是除数左移一位后与被除数高位的大小关系,进而判断能不能把除数进行左移。
 assign comparison = ((divisor[L_DIVR-1] ==0) && ((state_c == ADIVR))) ?
    dividend_r[L_DIVN : L_DIVN - L_DIVR] - {divisor_r[L_DIVR-2:0],1'b0} :
    dividend_r[L_DIVN : L_DIVN - L_DIVR] - divisor_r;//计算被除数高位减去除数,如果计算结果最高位为0,表示被除数高位大于等于除数,如果等于1表示被除数高位小于除数。

下面是状态机跳转代码。

//状态机次态到现态的转换;
 always@(posedge clkornegedge rst_n)begin
if(rst_n==1'b0)begin//初始值为空闲状态;
   state_c <= IDLE;
    end
else begin//状态机次态到现态的转换;
      state_c <= state_n;
    end
  end


//状态机的次态变化。
  always@(*)begin
case(state_c)
IDLE : begin//如果开始计算信号为高电平且除数和被除数均不等于0。
if(start & (dividend != 0) & (divisor != 0))begin
          state_n = ADIVR;
        end
else begin//如果开始条件无效或者除数、被除数为0,则继续处于空闲状态。
          state_n = state_c;
        end
      end
ADIVR : begin//如果除数的最高位为高电平或者除数左移一位大于被除数的高位,则跳转到除法运算状态;
if(divisor_r[L_DIVR-1] | comparison[L_DIVR])begin
          state_n = DIV;
        end
else begin
          state_n = state_c;
        end
      end
DIV : begin
if(max)begin//如果被除数移动次数达到最大值,则状态机回到空闲状态,计算完成。
          state_n = IDLE;
        end
else begin
          state_n = state_c;
        end
      end
default : begin//状态机跳转到空闲状态;
        state_n = IDLE;
      end
    endcase
  end


下面是除数和被除数、商的计算过程,与前文描述一致,不再赘述。

//对被除数进行移位或进行减法运算。
//初始时需要加载除数和被除数,然后需要判断除数和被除数的高位,确定除数是否需要移位。
//然后根据除数和被除数高位的大小,确认被除数是移位还是与除数进行减法运算,注意被除数移动时,为了保证结果不变,商也会左移一位。
//如果被除数高位与除数进行减法运算,则商的最低位变为1,好比此时商1进行的减法运算。经减法结果赋值到被除数对应位。
 always@(posedge clkornegedge rst_n)begin
if(rst_n==1'b0)begin//初始值为0;
   divisor_r <= 0;
      dividend_r <= 0;
      quotient_r <= 0;
      shift_divisor <= 0;
      shift_dividend <= 0;
    end//状态机处于加载状态时,将除数和被除数加载到对应寄存器,开始计算;
elseif(state_c == IDLE && start && (dividend != 0) & (divisor != 0))begin
      dividend_r <= dividend;//加载被除数到寄存器;
      divisor_r <= divisor;//加载除数到寄存器;
      quotient_r <= 0;//将商清零;
      shift_dividend <= 0;//将移位的被除数寄存器清零;
      shift_divisor <= 0; //将移位的除数寄存器清零;
    end//状态机处于除数左移状态,且除数左移后小于等于被除数高位且除数最高位为0。
elseif(state_c == ADIVR && (~comparison[L_DIVR]) && (~divisor_r[L_DIVR-1]))begin
      divisor_r <= divisor_r << 1;//将除数左移1位;
      shift_divisor <= shift_divisor + 1;//除数总共被左移的次数加1;
    end
elseif(state_c == DIV)begin//该状态需要完成被除数移位和减法运算。
if(comparison[L_DIVR] && (~max))begin//当除数大于被除数高位时,被除数需要移位。
        dividend_r <= dividend_r << 1;//将被除数左移1位;
        quotient_r <= quotient_r << 1;//同时把商左移1位;
        shift_dividend <= shift_dividend + 1;//被除数总共被左移的次数加1;
      end
elseif(~comparison[L_DIVR])begin//当除数小于等于被除数高位时,被除数高位减去除数作为新的被除数高位。
        dividend_r[L_DIVN : L_DIVN - L_DIVR] <= comparison;//减法结果赋值给被除数进行减法运算的相应位。
        quotient_r[0] <= 1;//因为做了一次减法,则商加1。
      end
    end
  end



参考代码如下所示:

//注意此模块默认被除数的位宽大于等于除数的位宽。
//当quotient_vld信号为高电平且error为低电平时,输出的数据是除法计算的正确结果。
//当输入除数为0时,error信号拉高,且商和余数为0;
//当ready信号为低电平时,不能将开始信号start拉高,此时拉高start信号会被忽略。
module div#(
 parameter    L_DIVN   = 8    ,//被除数的位宽;
 parameter    L_DIVR   = 4//除数的位宽;
)(
 input         clk    ,//时钟信号;
 input          rst_n   ,//复位信号,低电平有效;


 input          start    ,//开始计算信号,高电平有效,必须在ready信号为高电平时输入才有效。
 input    [L_DIVN -1:0] dividend  ,//被除数输入;
 input    [L_DIVR -1:0] divisor   ,//除数输入;


 output  reg        ready   ,//高电平表示此模块空闲。
 output  reg        error   ,//高电平表示输入除数为0,输入数据错误。
 output reg        quotient_vld ,//商和余数输出有效指示信号,高电平有效;
 output reg   [L_DIVR -1:0] remainder  ,//余数,余数的大小不会超过除数大小。
 output reg   [L_DIVN -1:0] quotient   //商。
); 
 localparam     L_CNT    =  clogb2(L_DIVN) ;//利用函数自动计算移位次数计数器的位宽。
 localparam    IDLE    = 3'b001   ;//状态机空闲状态的编码;
 localparam    ADIVR   = 3'b010   ;//状态机移动除数状态的编码;
 localparam    DIV    = 3'b100   ;//状态机进行减法计算和移动被除数状态的编码;


 reg          vld    ;//
 reg     [2:0]    state_c   ;//状态机的现态;
 reg     [2:0]    state_n   ;//状态机的次态;
 reg     [L_DIVN :0]  dividend_r  ;//保存被除数;
 reg     [L_DIVR -1:0] divisor_r   ;//保存除数。
 reg     [L_DIVN -1:0] quotient_r   ;//保存商。
 reg     [L_CNT -1:0]  shift_dividend ;//用于记录被除数左移的次数。
 reg     [L_CNT -1:0]  shift_divisor ;//用于记录除数左移的次数。


 wire     [L_DIVR :0]   comparison  ;//被除数的高位减去除数。
 wire           max    ;//高电平表示被除数左移次数已经用完,除法运算基本结束,可能还需要进行一次减法运算。


//自动计算计数器位宽函数。
function integerclogb2(input integer depth);begin
if(depth ==0)
   clogb2 =1;
elseif(depth !=0)
for(clogb2=0; depth>0; clogb2=clogb2+1)
    depth=depth >>1;
  end
 endfunction


//max为高电平表示被除数左移的次数等于除数左移次数加上被除数与除数的位宽差;
 assign max = (shift_dividend == (L_DIVN - L_DIVR) + shift_divisor);


//用来判断除数和被除数第一次做减法的高位两者的大小,当被除数高位大于等于除数时,comparison最高位为0,反之为1。
//comparison的计算结果还能表示被除数高位与除数减法运算的结果。
//在移动除数时,判断的是除数左移一位后与被除数高位的大小关系,进而判断能不能把除数进行左移。
 assign comparison = ((divisor[L_DIVR-1] ==0) && ((state_c == ADIVR))) ?
    dividend_r[L_DIVN : L_DIVN - L_DIVR] - {divisor_r[L_DIVR-2:0],1'b0} :
    dividend_r[L_DIVN : L_DIVN - L_DIVR] - divisor_r;//计算被除数高位减去除数,如果计算结果最高位为0,表示被除数高位大于等于除数,如果等于1表示被除数高位小于除数。


//状态机次态到现态的转换;
 always@(posedge clkornegedge rst_n)begin
if(rst_n==1'b0)begin//初始值为空闲状态;
   state_c <= IDLE;
    end
else begin//状态机次态到现态的转换;
      state_c <= state_n;
    end
  end


//状态机的次态变化。
  always@(*)begin
case(state_c)
IDLE : begin//如果开始计算信号为高电平且除数和被除数均不等于0。
if(start & (dividend != 0) & (divisor != 0))begin
          state_n = ADIVR;
        end
else begin//如果开始条件无效或者除数、被除数为0,则继续处于空闲状态。
          state_n = state_c;
        end
      end
ADIVR : begin//如果除数的最高位为高电平或者除数左移一位大于被除数的高位,则跳转到除法运算状态;
if(divisor_r[L_DIVR-1] | comparison[L_DIVR])begin
          state_n = DIV;
        end
else begin
          state_n = state_c;
        end
      end
DIV : begin
if(max)begin//如果被除数移动次数达到最大值,则状态机回到空闲状态,计算完成。
          state_n = IDLE;
        end
else begin
          state_n = state_c;
        end
      end
default : begin//状态机跳转到空闲状态;
        state_n = IDLE;
      end
    endcase
  end


//对被除数进行移位或进行减法运算。
//初始时需要加载除数和被除数,然后需要判断除数和被除数的高位,确定除数是否需要移位。
//然后根据除数和被除数高位的大小,确认被除数是移位还是与除数进行减法运算,注意被除数移动时,为了保证结果不变,商也会左移一位。
//如果被除数高位与除数进行减法运算,则商的最低位变为1,好比此时商1进行的减法运算。经减法结果赋值到被除数对应位。
  always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin//初始值为0;
      divisor_r <= 0;
      dividend_r <= 0;
      quotient_r <= 0;
      shift_divisor <= 0;
      shift_dividend <= 0;
    end//状态机处于加载状态时,将除数和被除数加载到对应寄存器,开始计算;
elseif(state_c == IDLE && start && (dividend != 0) & (divisor != 0))begin
      dividend_r <= dividend;//加载被除数到寄存器;
      divisor_r <= divisor;//加载除数到寄存器;
      quotient_r <= 0;//将商清零;
      shift_dividend <= 0;//将移位的被除数寄存器清零;
      shift_divisor <= 0; //将移位的除数寄存器清零;
    end//状态机处于除数左移状态,且除数左移后小于等于被除数高位且除数最高位为0。
elseif(state_c == ADIVR && (~comparison[L_DIVR]) && (~divisor_r[L_DIVR-1]))begin
      divisor_r <= divisor_r << 1;//将除数左移1位;
      shift_divisor <= shift_divisor + 1;//除数总共被左移的次数加1;
    end
elseif(state_c == DIV)begin//该状态需要完成被除数移位和减法运算。
if(comparison[L_DIVR] && (~max))begin//当除数大于被除数高位时,被除数需要移位。
        dividend_r <= dividend_r << 1;//将被除数左移1位;
        quotient_r <= quotient_r << 1;//同时把商左移1位;
        shift_dividend <= shift_dividend + 1;//被除数总共被左移的次数加1;
      end
elseif(~comparison[L_DIVR])begin//当除数小于等于被除数高位时,被除数高位减去除数作为新的被除数高位。
        dividend_r[L_DIVN : L_DIVN - L_DIVR] <= comparison;//减法结果赋值给被除数进行减法运算的相应位。
        quotient_r[0] <= 1;//因为做了一次减法,则商加1。
      end
    end
  end


//生成状态机从计算除结果的状态跳转到空闲状态的指示信号,用于辅助设计输出有效指示信号。
  always@(posedge clk)begin
    vld <= (state_c == DIV) && (state_n == IDLE);
  end


//生成商、余数及有效指示信号;
  always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin//初始值为0;
      quotient <= 0;
      remainder <= 0;
      quotient_vld <= 1'b0;
    end//如果开始计算时,发现除数或者被除数为0,则商和余数均输出0,且将输出有效信号拉高。
elseif(state_c == IDLE && start && ((dividend== 0) || (divisor==0)))begin
      quotient <= 0;
      remainder <= 0;
      quotient_vld <= 1'b1;
    end
elseif(vld)begin//当计算完成时。
      quotient <= quotient_r;//把计算得到的商输出。
      quotient_vld <= 1'b1;//把商有效是指信号拉高。
//移动剩余部分以补偿对齐变化,计算得到余数;
      remainder <= (dividend_r[L_DIVN - 1 : 0]) >> shift_dividend;
  end
elsebegin
   quotient_vld <= 1'b0;
    end
  end


//当输入除数为0时,将错误指示信号拉高,其余时间均为低电平。
  always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin//初始值为0;
      error <= 1'b0;
    end
elseif(state_c == IDLE && start)begin
if(divisor==0)//开始计算时,如果除数为0,把错误指示信号拉高。
        error <= 1'b1;
else//开始计算时,如果除数不为0,把错误指示信号拉低。
        error <= 1'b0;
    end
  end


//状态机处于空闲且不处于复位状态;
  always@(*)begin
if(start || state_c != IDLE || vld)
      ready = 1'b0;
else
      ready = 1'b1;
  end


endmodule

03
除法器仿真


对应的TestBench文件如下所示:

`timescale1ns/1ns
moduletest();
  localparam L_DIVN     = 8       ;//被除数的位宽;
  localparam L_DIVR     = 4       ;//除数的位宽;
  localparam CYCLE      = 20       ;//时钟周期;


  reg               clk       ;//时钟信号;
  reg               rst_n      ;//复位信号,低电平有效;
  reg               start      ;//开始计算信号,高电平有效,
  reg     [L_DIVN -1:0]  dividend    ;//被除数输入;
  reg     [L_DIVR -1:0]  divisor     ;//除数输入;


  wire    [L_DIVN -1:0]  quotient    ;//商。
  wire    [L_DIVR -1:0]  remainder    ;//余数,余数的大小不会超过除数大小。
  wire              quotient_vld  ;//商和余数输出有效指示信号,高电平有效;
  wire              ready      ;//高电平表示此模块空闲。
  wire              error      ;//高电平表示输入除数为0,输入数据错误。


  reg               quotient_error ;
  reg               rem_error    ;


//例化待测试的除法器模块;
  div#(
    .L_DIVN     ( L_DIVN    ),//被除数的位宽;
    .L_DIVR     ( L_DIVR    )//除数的位宽;
  )
  u_div(
    .clk      ( clk      ),//时钟信号;
    .rst_n     ( rst_n     ),//复位信号,低电平有效;
    .start     ( start     ),//开始计算信号,高电平有效,
    .dividend    ( dividend   ),//被除数输入;
    .divisor    ( divisor    ),//除数输入;
    .quotient    ( quotient   ),//商。
    .remainder   ( remainder   ),//余数,余数的大小不会超过除数大小。
    .ready     ( ready     ),//高电平表示此模块空闲。
    .error     ( error     ),//高电平表示输入除数为0,输入数据错误。
    .quotient_vld  ( quotient_vld )//商和余数输出有效指示信号,高电平有效;
  );


//生成时钟信号;
  initial begin
    clk =0;
    forever#(CYCLE/2) clk = ~clk;
  end


//生成复位信号;
  initial begin
    rst_n =1;
#2;
    rst_n =0;//开始时复位5个时钟;
#(5*CYCLE);
    rst_n =1;
  end


  initial begin
#1;
    dividend =121; divisor =13;start=0;
#(8*CYCLE);
    repeat(20)begin//循环进行20次运算;
#(5*CYCLE);
      dividend = {$random};//产生随机数作为被除数;
      divisor = {$random} ;//产生随机数作为除数;
      start =1;
#(CYCLE);
      start =0;
      @(negedge quotient_vld);
#1;
#(3*CYCLE);
    end
#(5*CYCLE);
    $stop;//停止仿真;
  end


//对模块输出的数据进行判断。
  always@(posedge clkornegedge rst_n)begin
if(rst_n==1'b0)begin//初始值为0;
      quotient_error <= 1'b0;
            rem_error <= 1'b0;
        end
        else if(quotient_vld && (divisor!=0))begin
            quotient_error <= ((dividend / divisor) != quotient);
            rem_error <= ((dividend % divisor) != remainder);
        end
    end


endmodule



仿真结果如下所示,商和余数的错误指示信号均为低电平,表示除法运算的结果没有什么问题。

058e6802-b069-11f0-8c8f-92fbcf53809c.png

图5 仿真结果

模块内部信号的详细仿真如图6、7所示,具体含义与前文介绍一致,就不再赘述了。

05eac66a-b069-11f0-8c8f-92fbcf53809c.png

图6 除法模块详细仿真

063f32d6-b069-11f0-8c8f-92fbcf53809c.png

图7 除法模块详细仿真

04总结

使用该模块需要注意几点:

1、被除数位宽必须大于等于除数位宽。

2、当error为高电平时,表示输入除数为0,此时输出的商和余数均为0且无效。 3、只有当quotient_vld为高电平时,模块输出的商和余数才是有效的。

4、只有当模块处于空闲(ready为高电平)时,外部输入的start和除数、被除数才能有效。 如果需要源文件,在公众号后台回复“基于FPGA的除法器”(不包括引号)即可。由于我只使用vscode和modelsim对工程进行仿真,所以没有vivado和quartus工程。下面视频就是通过vscode调用modelsim进行仿真的视频。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1656

    文章

    22305

    浏览量

    630776
  • 计数器
    +关注

    关注

    32

    文章

    2306

    浏览量

    97650
  • 除法器
    +关注

    关注

    2

    文章

    15

    浏览量

    14112
  • 状态机
    +关注

    关注

    2

    文章

    497

    浏览量

    28883

原文标题:基于FPGA的高效除法器

文章出处:【微信号:FPGA研究院,微信公众号:FPGA研究院】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    基于FPGA除法器纯逻辑设计案例

    除法运算。很多人觉得不就是除法嘛,直接打上/即可,但是,FPGA是不能正确综合这个除法器的,综合的结果只是一个固定数值,而不像其他微处理器。可以这么说,用
    的头像 发表于 06-17 10:17 8237次阅读
    基于<b class='flag-5'>FPGA</b>的<b class='flag-5'>除法器</b>纯逻辑设计案例

    除法器工作原理介绍

    二进制除法器的本质是多次减法,直到余数小于除数为止。对应的两个N bit二进制数的除法算法如下。 1、设置2N bit寄存器A的低N位存放被除数,设置2N bit寄存器B的高N位存放除数,设置N
    发表于 10-21 08:32

    如何对蜂鸟e203内核乘除法器进行优化

    控制逻辑:除法器控制逻辑设计合理与否直接影响到除法器的性能表现,因此可以优化除法器控制逻辑,如增加硬件控制逻辑、优化状态转移逻辑等,可以有效提高除法器的效率。 采用更
    发表于 10-24 06:47

    ise的除法器modelsim仿不了?

    `大虾们,小女子最近调程序的时候用到了ise ip core的除法器,但是调用modelsim仿真的时候发现木有进行除法啊,单独写了个除法器也还是用不了,这是什么情况呢?(vhdl写的程序哈)`
    发表于 06-15 11:52

    基于FPGA除法器

    定点除法器的输出是商和余数的形式,但是我想让他表示成小数的形式(因为最后要送到数码管显示),该怎么装换?求大神,给点思路也可以!总共是8位显示,而整数部分和小数部分的位数不定?怎么设计
    发表于 05-15 20:01

    高速硬件除法器

    这是一个高速硬件除法器,要求画出此硬件的除法器的工作流程图。说明其工作原理特别是高速原理。要求有仿真时序波形图并说出说明在fpga上验证器硬件功能。
    发表于 12-17 09:10

    除法器的设计资料分享

    4.3 实例九 除法器设计4.3.1. 本章导读要求掌握除法器原理,并根据原理设计除法器模块以及设计对应的测试模块,最后在 Robei可视化仿真软件经行功能实现和仿真验证。设计原理这个除法器
    发表于 11-12 07:03

    并行除法器 ,并行除法器结构原理是什么?

    并行除法器 ,并行除法器结构原理是什么?   1.可控加法/减法(CAS)单元    和阵列乘法器非常相似,阵列式除法器也是一种并行运算部件,采用大规模集成
    发表于 04-13 10:46 1.6w次阅读

    除法器对数运算电路的应用

    除法器对数运算电路的应用 由对数电路实现除法运算的数学原理是:
    发表于 04-24 16:07 3078次阅读
    <b class='flag-5'>除法器</b>对数运算电路的应用

    AD734:10 MHz四象限乘法器/除法器数据表

    AD734:10 MHz四象限乘法器/除法器数据表
    发表于 05-15 10:18 12次下载
    AD734:10 MHz四象限乘<b class='flag-5'>法器</b>/<b class='flag-5'>除法器</b>数据表

    简化合成器的有源乘法器除法器

    简化合成器的有源乘法器除法器
    发表于 05-16 17:15 9次下载
    简化合成器的有源乘<b class='flag-5'>法器</b>和<b class='flag-5'>除法器</b>

    实例九— 除法器设计

    4.3 实例九 除法器设计4.3.1. 本章导读要求掌握除法器原理,并根据原理设计除法器模块以及设计对应的测试模块,最后在 Robei可视化仿真软件经行功能实现和仿真验证。设计原理这个除法器
    发表于 11-07 10:51 18次下载
    实例九— <b class='flag-5'>除法器</b>设计

    FPGA常用运算模块-除法器

    本文是本系列的第四篇,本文主要介绍FPGA常用运算模块-除法器,xilinx提供了相关的IP以便于用户进行开发使用。
    的头像 发表于 05-22 16:20 5380次阅读
    <b class='flag-5'>FPGA</b>常用运算模块-<b class='flag-5'>除法器</b>

    FPGA基于线性迭代法的除法器设计

    FPGA实现除法的方法有几种,比如直接用/来进行除法运算,调用IP核进行除法运算,但这两种方式都有个共同的问题——都是黑盒子,在进行时序违例处理时,往往不好操作,比如想打打拍改善下时序
    的头像 发表于 07-04 10:03 2132次阅读
    <b class='flag-5'>FPGA</b>基于线性迭代法的<b class='flag-5'>除法器</b>设计

    使用IAR IDE仿真RL78内置硬件乘法器除法器注意事项

    使用IAR IDE仿真RL78内置硬件乘法器除法器注意事项
    的头像 发表于 10-30 17:04 2143次阅读
    使用IAR IDE仿真RL78内置硬件乘<b class='flag-5'>法器</b>和<b class='flag-5'>除法器</b>注意事项