FPGA除法实现

本文主要通过两种方法实现乘法器,一种是调用IP核方式,另一种是使用移位和减法的方式。

一、调用divider generator IP核

赛灵思pg151介绍了除法器IP的使用,简单介绍:

除法器的实现主要有三种方式:LUTMult、Radix-2、High Radix。

1、LUTMult:使用除数的有限精度倒数组成的简单查找表进行查找,然后乘以被除数。

A.使用DSP、块RAM和少量FPGA逻辑实现。

B.仅支持输出余数,不支持输出分数。

C.推荐处理位宽低于12bits的数据

2、Radix-2:小学迭代除法计算方式,计算商和余数

A.使用FPGA逻辑实现。

B.支持输出余数和分数两种。

C.一般使用此种。

3、High Radix:在进行迭代除法之前,进行预缩放处理,可同时生成商多个bits数据

A.使用DPS、块RAM实现。

B.支持输出余数和分数两种。

C.推荐处理位宽高于16bits的数据。

调用该IP核实,AXIS4_stream options—flow control中,Non blocking与blocking区别

Non blocking:非阻塞,此时IP核的S_AXIS_DIVISOR与S_AXIS_DIVIDEND不往外输出axis_ready信号,流水线输出,不判断外部模块是否接收数据

Blocking:阻塞,此时IP核S_AXIS_DIVISOR与S_AXIS_DIVIDEND输出axis_ready信号,控制外部是否可以输入除数与被除数。

二、IP核的仿真验证

IP核参数配置:

module testbench(

    );

reg clk=1'b0;

reg [7:0]clk_cnt = 8'd0;

wire [23:0]m_axis_dout_tdata;

reg [15:0] dividend = 16'd0;//被除数

wire [7:0]divisor;

wire [15:0] quotient;//商

wire [7:0] remainder;//余数

always

begin

    #5 clk <= ~clk;

end

always@(posedge clk)

begin

    clk_cnt <= clk_cnt + 1'b1;

end

always@(posedge clk)

begin

    if(clk_cnt == 8'hFF)    //间隔256个时钟,被除数加1

        dividend <= dividend + 1'b1;

end

assign divisor = 8'd11;

divid_ip divid_ip (

  .aclk(clk),                                      // input wire aclk

  .s_axis_divisor_tvalid(1'b1),    // input wire s_axis_divisor_tvalid

  .s_axis_divisor_tdata(divisor),      // input wire [7 : 0] s_axis_divisor_tdata

  .s_axis_dividend_tvalid(1'b1),  // input wire s_axis_dividend_tvalid

  .s_axis_dividend_tdata(dividend),    // input wire [15 : 0] s_axis_dividend_tdata

  .m_axis_dout_tvalid(),          // output wire m_axis_dout_tvalid

  .m_axis_dout_tdata(m_axis_dout_tdata)            // output wire [23 : 0] m_axis_dout_tdata

);

assign    quotient =  m_axis_dout_tdata[23:8];

assign    remainder =  m_axis_dout_tdata[7:0];

endmodule

仿真结果如下: 

三、FPGA逻辑实现除法器

定义:

        被除数:dividend[M-1:0]

        除数:divisor[N-1:0]

计算可得:

        商:quotient[M-1:0]   位宽为M,与被除数相同

        余数:remainder[N-1:0]  位宽为N,与除数相同

                

先行举例说明:

        被除数:dividend[7:0]  = 8’d255 = 8’b1111_1111

        除数: divisor[3:0]    = 4’d11 = 4’b1011

表达如图:

                                

 第一次运算:      被除数最高位为1’b1,小于4’b1011,因此商最高位置为0;

                                        

第二次运算:被除数高2位为2’b11,小于4’b1011,因此商次高位置为0;                                ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        

第三次运算:被除数高3位为3’b111,小于4’b1011,因此商的高第3位为0

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        

第四次运算:被除数高4位为4’b1111,大于4’b1011,因此将商的高第4位,为1,将4’b1111减去4’b1011,结果3’b100用于接下来的运算;

 

 第五次运算:将上一次运算的结果3’b100拼接商被除数高第5位数,组合成4’b1001。其小于4’b1011,因此商的高第5位为0;

 

 第六次运算:将上一次运算的结果4’b1001拼接被除数高第6位数,组合成4’b10011。其大于4’b1011,相减后结果为4’b1000,将商第6位置为1;

 

第七次运算:将上一次运算的结果4’b1000拼接被除数高第7位数,组合成4’b10001。其大于4’b1011,相减后结果为3’b110,将商第7位置为1;

 

第八次运算:将上一次运算的结果3’b110拼接被除数高第8位数,组合成4’b1101。其大于4’b1011,相减后结果为2’b10,将商第8位置为1;

 

最终得出,255÷11=23余2。

分析可知,商的位宽与被除数位宽一致,余数的位宽与除数的位宽一致。

四、FPGA逻辑实现除法器,并仿真验证

module divid_gen(
    input clk,
    input calculate_en,
    input [7:0] dividend,
    input [3:0] divisor,
    output reg [7:0] quotient,
    output reg [3:0] remainder
    );

reg [3:0] shift_cnt;//被除数移位计算计数器

reg [7:0] dividend_reg; //锁存dividend寄存器
reg [3:0] divisor_reg;//锁存divisor寄存器

reg [7:0] quotient_reg;//商寄存器,用于缓存每次结算的商结果
reg [7:0] remainder_reg;//余数寄存器,用于缓存每次计算的余数结果

reg [1:0] state,next_state;
parameter IDLE_STATE = 2'b00;//空闲态
parameter CALC_STATE = 2'b01;//计算态
parameter END_STATE = 2'b10;//结束态

always@(posedge clk)
begin
    state <= next_state;
end

always@( * )
begin
    case(state)
        IDLE_STATE : begin
            if(calculate_en)
                next_state <= CALC_STATE;
            else
                next_state <= IDLE_STATE;
        end
        CALC_STATE : begin
            if(shift_cnt >= 8'd8) //移位8次,移位结束,8为被除数的数据宽度
                next_state <= END_STATE;
            else
                next_state <= CALC_STATE;
        end
        END_STATE : begin
                next_state <= IDLE_STATE;
        end
        default : begin
                next_state <= IDLE_STATE;
        end
    endcase
end

always@(posedge clk)
begin
    if((state == IDLE_STATE) & (calculate_en == 1'b1)) begin
        dividend_reg <= dividend;
        divisor_reg <= divisor;
    end
end

always@(posedge clk)
begin
    if(state == IDLE_STATE)
        shift_cnt <= 4'b0;
    else if(state == CALC_STATE)
        shift_cnt <= shift_cnt + 1'b1;
end

always@(posedge clk)
begin
    case(shift_cnt)
        4'd0: begin //第一次运算
            if(dividend_reg[7] >= divisor) begin
                quotient_reg[7] <= 1'b1;
                remainder_reg[7:6] <= {(dividend_reg[7] - divisor),dividend_reg[6]};
            end   
            else begin
                quotient_reg[7] <= 1'b0;
                remainder_reg[7:6] <= dividend_reg[7:6];
            end
        end
        4'd1: begin //第二次运算
            if(remainder_reg[7:6] >= divisor) begin
                quotient_reg[6] <= 1'b1;
                remainder_reg[7:5] <= {(remainder_reg[7:6] - divisor),dividend_reg[5]};
            end   
            else begin
                quotient_reg[6] <= 1'b0;
                remainder_reg[7:5] <= {remainder_reg[7:6],dividend_reg[5]};
            end
        end
        4'd2: begin //第三次运算
            if(remainder_reg[7:5] >= divisor) begin
                quotient_reg[5] <= 1'b1;
                remainder_reg[7:4] <= {(remainder_reg[7:5] - divisor),dividend_reg[4]};
            end   
            else begin
                quotient_reg[5] <= 1'b0;
                remainder_reg[7:4] <= {remainder_reg[7:5],dividend_reg[4]};
            end
        end
        4'd3: begin //第四次运算
            if(remainder_reg[7:4] >= divisor) begin
                quotient_reg[4] <= 1'b1;
                remainder_reg[7:3] <= {(remainder_reg[7:4] - divisor),dividend_reg[3]};
            end   
            else begin
                quotient_reg[4] <= 1'b0;
                remainder_reg[7:3] <= {remainder_reg[7:4],dividend_reg[3]};
            end
        end
        4'd4: begin //第五次运算
            if(remainder_reg[7:3] >= divisor) begin
                quotient_reg[3] <= 1'b1;
                remainder_reg[7:2] <= {(remainder_reg[7:3] - divisor),dividend_reg[2]};
            end   
            else begin
                quotient_reg[3] <= 1'b0;
                remainder_reg[7:2] <= {remainder_reg[7:3],dividend_reg[2]};
            end
        end
        4'd5: begin //第六次运算
            if(remainder_reg[7:2] >= divisor) begin
                quotient_reg[2] <= 1'b1;
                remainder_reg[7:1] <= {(remainder_reg[7:2] - divisor),dividend_reg[1]};
            end   
            else begin
                quotient_reg[2] <= 1'b0;
                remainder_reg[7:1] <= {remainder_reg[7:2],dividend_reg[1]};
            end
        end
        4'd6: begin //第七次运算
            if(remainder_reg[7:1] >= divisor) begin
                quotient_reg[1] <= 1'b1;
                remainder_reg[7:0] <= {(remainder_reg[7:1] - divisor),dividend_reg[0]};
            end   
            else begin
                quotient_reg[1] <= 1'b0;
                remainder_reg[7:0] <= {remainder_reg[7:1],dividend_reg[0]};
            end
        end
        4'd7: begin //第八次运算
            if(remainder_reg[7:0] >= divisor) begin
                quotient_reg[0] <= 1'b1;
                remainder_reg[7:0] <= (remainder_reg[7:0] - divisor);
            end   
            else begin
                quotient_reg[0] <= 1'b0;
                remainder_reg[7:0] <= remainder_reg[7:0];
            end
        end
        default :;
    endcase
end

always@(posedge clk)
begin
    if(state == END_STATE) begin
        quotient <= quotient_reg;
        remainder <= remainder_reg[4:0];
    end
end
    
endmodule
 

 

除法器使用位移和相减实现,同样的,乘法器也可以使用位移与加法实现。更高效率的乘法与除法运算,复杂度较高,还是推荐使用IP核实现。

  • 10
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
流水线除法是一种通过将除法运算划分为多个子操作,并在每个时钟周期内进行部分计算的方式,可以提高除法运算的效率。在流水线除法中,将被除数、除数和商的位数分别划分为多个段,每个段在不同的时钟周期内进行计算,最终得到除法的结果。这种方式能够实现高速的除法运算,并广泛应用于计算机领域。 FPGA(可编程逻辑门阵列)是一种集成电路器件,它具有可编程的逻辑门和可配置的存储元件,可以根据用户的需求进行自定义的电路设计和实现FPGA广泛应用于数字电路设计、信号处理、通信系统等领域。 在FPGA实现流水线除法有许多优势。首先,FPGA具有可编程性,可以方便地进行除法计算的逻辑设计。其次,FPGA内部结构的并行处理能力使得流水线除法可以在多个时钟周期内并行运算,提高除法的计算速度。此外,FPGA的可重构性使得在设计中可以灵活地优化流水线除法的结构和功能,以达到更好的性能和资源利用率。 然而,在实现流水线除法的过程中也面临一些挑战。首先是时序约束的问题,由于流水线除法存在多个时钟周期的计算,需要特别注意时钟信号的延时和同步问题,以确保计算结果的准确性和稳定性。此外,流水线除法还需要合理划分各个子操作的计算逻辑,保证各个子段的平衡和协调,以达到最高的计算效率。 总之,流水线除法结合了流水线技术和除法运算,是一种高效的除法计算方式。在FPGA实现流水线除法可以充分利用FPGA的可编程性、并行处理能力和可重构性,提高除法运算的效率,并适用于多种应用领域。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值