Verilog设计定点纯小数乘除法器

一、定点纯小数乘除法器

1.原理分析

1.1 补码乘法

        Booth算法,由校正法推导而出,对乘数X和被乘数Y的符号没有分类要求。

        特点:被乘数和部分积均采用双符号位,作为数值位参与计算。取一附加位添加到乘数末尾,该附加位初始值Yn+1 = 0。每次观察乘数的两位,规则如下:

当运算至最后一位时(即最后一次判断),部分积不右移。最后的部分积作为乘积,同样借助双符号位判断是否溢出。

        例题:

1.1 补码除法

        补码交替加减法,其步骤分为:求余数,判商值,移位,求余数...,如此重复,直到求出商值(达到要求精度)。

        关键点:第一步求余数

        中间步骤求新余数,判断商值

        最后一步估值减小误差,有“末位恒置1法”和“0舍1入法”,前者硬件实现比较简单。

        商值结果溢出判断

        例题:

2.代码解析

设计文件

算法分析:当S为0时,C=A*B。从例题可知,两个N位数补码(含一位符号位)相乘,要重复N次操作(判断YnYn+1;部分积加值;部分积移位)可得到最终结果。但是最后一次操作没有移位操作,需要单独处理,思路是使用C_reg寄存器储存操作后移位前的结果,用C_shift变量存储操作后移位后的结果,作为取C_reg的值的部分作为输出C。

当S为1时,C=A/B。从例题可知,两个N位数补码(含一位符号位)相除,欲得到N位结果(一位符号位,N-1位小数位),要重复N次操作(判断被除数(余数)和除数的符号位;商值移位;余数加值,商值判断)得到结果。但第一次操作没有商值判断和余数移位;最后一步没有余数加值。且重复N次操作后,还要做末位置1操作。

为方便共享硬件,除法的除数和被除数均用两位符号位。乘法需要循环8次,故除法结果取7位结果(1位符号位,6位小数位),使用7次操作(判断被除数(余数)和除数的符号位;商值移位;余数加值,商值判断)和一次末位置1操作。而除法的商值移位,可以借助count完成。最后一步没有余数加值可以直接忽略这个要求,因为不影响结果。但第一次操作没有商值判断和余数移位,商值同样放置在变量中,但最终不取;余数移位则用一个选择器和B_shift实现。

//输入:1_符号 7_小数 
//输出:1_符号 14_小数
//输入输出均为补码表示 
//S:0 乘法  1 除法
//C:A*B A/B
//ready 结果有效
//start 开始运算
//乘法为Booth算法
//除法为加减交替法
module ALU_fixed(
    input clk,
    input rst_n,
    input start,
    input S,
    input signed [7:0] A,
    input signed [7:0] B,
    output signed [14:0] C,
    output error,
    output ready
    );

    reg signed [8:0] A_reg;
    reg signed [8:0] B_reg;
    reg signed [15:0] C_reg;
    wire signed [15:0] C_shift;
    wire signed [8:0] A_shift;
    wire signed [8:0] A_not;
    wire signed [8:0] B_not;

    reg busy,busy_D;
    reg [2:0] count;
    always @(posedge clk , negedge rst_n) begin
        if (!rst_n) begin
            A_reg <= 0;
            B_reg <= 0;
            C_reg <= 0;
        end else if(S == 0) begin
             A_reg <= {A[7],A};
            if(busy) begin
                    case (B_reg[1:0])
                        2'b00:begin
                            C_reg <= C_shift;
                        end
                        2'b01:begin
                            C_reg[15:7] <= C_shift[15:7] + A_reg;
                            C_reg[6:0] <= C_shift[6:0];
                        end
                        2'b10:begin
                            C_reg[15:7] <= C_shift[15:7] + A_not;
                            C_reg[6:0] <= C_shift[6:0];
                        end
                        2'b11:begin
                            C_reg <= C_shift;
                        end
                    endcase
                    B_reg <= B_reg >> 1;
                end
            else begin
                B_reg <= {B,1'b0};
                C_reg <= 0;
            end
        end
        else begin
            if(busy) begin
                if(count == 3'd7)
                    C_reg[4'd15 - count] = 1;
                else
                    if (A_reg[8] ^ B_reg[8]) begin
                        A_reg = A_shift + B_reg;
                        C_reg[4'd15 - count] = 0;
                    end
                    else begin
                        A_reg = A_shift + B_not;
                        C_reg[4'd15 - count] = 1;
                    end
                end
            else begin
                A_reg <= {A[7],A};
                B_reg <= {B[7],B};
                C_reg <= 0;
            end
        end 
    end

    always @(posedge clk , negedge rst_n) begin
        if (!rst_n) begin
            busy <= 0;
            busy_D <= 0;
        end else begin
            busy_D <= busy;
            if(start == 1)
                busy <= 1;
            else if(count == 3'd7)
                busy <= 0;
            else
                busy <= busy;
        end
    end

    always @(posedge clk , negedge rst_n) begin
        if (!rst_n) begin
            count <= 0;
        end else begin
            if(busy)
                count <= count + 3'd1;
            else
                count <= 0;
        end
    end
    assign error = (S == 0) ? (C_reg[15]^C_reg[14]) : (A[7] ^ B[7] ^ C[14]);
    assign C_shift = C_reg >>> 1;
    assign A_shift = (count == 0) ? A_reg : A_reg << 1;
    assign A_not = ~A_reg + 1'b1;
    assign B_not = ~B_reg + 1'b1;
    assign ready = busy_D & ~busy;
    assign C = C_reg[14:0];
endmodule

 仿真文件


module ALU_fixed_tb(

    );
    reg clk,rst_n,start,S;
    reg [7:0] A,B;
    wire [14:0] C;
    wire ready,error;

    ALU_fixed ALU1(
        .clk(clk),
        .rst_n(rst_n),
        .start(start),
        .S(S),
        .A(A),
        .B(B),
        .C(C),
        .error(error),
        .ready(ready)
    );


    always #1 clk = ~clk; 
    initial begin
        clk = 0;
        rst_n = 0;
        A = 0;
        B = 0;
        start = 0;
        #3
        rst_n = 1;
        #4
        S = 0;
        A = 8'b0_1101000;
        B = 8'b0_1011000;
        start = 1;
        #2
        start = 0;
        #20
        A = 8'b1_0101000;
        B = 8'b1_0011000;
        start = 1;
        #2
        start = 0;
        #20
        S = 1;
        A = 8'b0_1001000;
        B = 8'b0_1101000;
        start = 1;
        #2
        start = 0;
        #20
        A = 8'b1_0111000;
        B = 8'b0_0000001;
        start = 1;
        #2
        start = 0;
        #20
        $stop;
    end
endmodule

3.结果

乘法:0.8125*0.6875=0.55859375

除法:0.5625/0.8125=0.692307,模块结果为0.703125,误差为0.010818,算法误差为(0,2^(-6)=0.0156],在误差范围内。当然,由于使用的是末位置1法,对一些比较明显的结果如-0.5,-1并不能得到准确的结果,得到的是误差范围内的结果。

溢出判断(纯小数乘法不溢出,纯小数除法会溢出)

4.收获

        本文算法参考《计算机组成原理》,更多细节自己去跑代码。

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值