八位右移位乘法器


八位右移位乘法器

虛假的右移位

其實移位總是相對的,所以右移還是有左移的成分。

左移位乘法器很好理解,因為在列豎式的時候就能看明白,符合我們的常規思維:

image-20201110164456963

也就是說,每一次乘法之后,只要把對應的部分積左移相應的位數,再相加,就可以得到最終的結果,這個過程像極了小學二年級的乘法課,非常的直觀了,這里不做贅述~

那么什么是右移呢?我聽到有同學說,把乘數和被乘數換一下就是了,像這樣:

image-20201110170450444

運算順序從右往左,理論上結果是正確的,就是看起來有點別扭,我們把它轉過來:

image-20201110171651354

這下應該看明白了吧,無非是最后的結果再加移位一次,從而實現一個“虛假的右移位”

真正的右移位

真正的右移位,其實是針對小數計算而言的,我們計算整數乘法的時候,習慣性的左移,根本原因是我們習慣了整數的小數點在最右側,只要不超過那個小數點,就可以直接相加得結果。

而小數計算,因為小數點在左邊的緣故,在計算機當中,就可以根據小數點位置不變的思想,來將部分積右移,從而實現一個右移乘法器。這種思想所成的乘法器,也成為定點乘法器

以二進制小數計算“0.1101 x 0.1011”為例:

image-20201110171651354

由圖可以看出,最后一位的結果並不參與移位相加的運算,什么意思呢?

我們再畫詳細點:

image-20201110192743010

計算時,每次得到的部分積,都會與上一次得到的部分積相加,而每次相加結果的最后一位不參與運算,可直接右移轉存,則部分積占用的空間顯然比左移運算小多了。這樣說,想必應該足夠明白了吧?

設計文件(方式一)

知道了上述原理,我們來寫一個右移位的乘法器,邏輯很清晰,每次根據乘數位是否為0進行判斷,如果是0,則部分積=前一步的部分積+0,如果是1,則部分積=前一步的部分積+被乘數,同時每次將部分積的最低位存儲至結果的低位中,最后一次的部分積則為結果的高位。

實現代碼如下:

module mul_8_1(result,mul1,mul2);
    input wire[7:0] mul1;
    input wire[7:0] mul2;
    output reg[15:0] res;
    reg [7:0] A;        //部分積
    integer i;
    reg [7:0] temp_x;   //乘法運算時,被乘數與乘數最后一位的積的最后一位不參與最后的加法運算,將其右移另存。
    always @ (*)
    begin
        A=0;
        for (i=0;i<8;i=i+1)
            begin
                if(mul2[i]) A=A+{1'b0,mul1};      //部分積移位前可能會比乘數多一位
                else A=A+9'b000000000;
                temp_x[i]=A[0];
                A=A>>1;
            end
            res={A,temp_x};
    end
endmodule

RTL級電路綜合中卻很少使用for循環語句。主要原因就是for循環會被綜合器展開為所有變量情況的執行語句,每個變量獨立占用寄存器資源,每條執行語句並不能有效地復用硬件邏輯資源,造成巨大的資源浪費。也就是說,for語句循環幾次,就是將相同的電路復制幾次。循環次數越多,綜合電路占用面積越大,綜合就越慢。

所以我們稍微改改寫法,就可以解決for循環了。

綜合電路

23個元件,32IO口,202根線

image-20201110195053083

設計文件(方式二)

所幸八位的乘法器也並不算復雜,可以寫一個並行計算的部分積。這樣也就解決了for循環的問題。

`define size 8
module mul_8_1(
    input wire [`size - 1:0] mul1,mul2,
    output wire [2*`size - 1:0] res
    );
    wire [`size : 0] A[0:`size-1];
    wire [`size -1 : 0] low_pro;

    assign A[0] = mul2[0] ? {1'b0,mul1} : 9'd0;
    assign A[1] = mul2[1] ? ((A[0]>>1) + {1'b0,mul1}) : ((A[0]>>1) + 9'd0);
    assign A[2] = mul2[2] ? ((A[1]>>1) + {1'b0,mul1}) : ((A[1]>>1) + 9'd0);
    assign A[3] = mul2[3] ? ((A[2]>>1) + {1'b0,mul1}) : ((A[2]>>1) + 9'd0);
    assign A[4] = mul2[4] ? ((A[3]>>1) + {1'b0,mul1}) : ((A[3]>>1) + 9'd0);
    assign A[5] = mul2[5] ? ((A[4]>>1) + {1'b0,mul1}) : ((A[4]>>1) + 9'd0);
    assign A[6] = mul2[6] ? ((A[5]>>1) + {1'b0,mul1}) : ((A[5]>>1) + 9'd0);
    assign A[7] = mul2[7] ? ((A[6]>>1) + {1'b0,mul1}) : ((A[6]>>1) + 9'd0);

    assign low_pro[0] = A[0][0];
    assign low_pro[1] = A[1][0];
    assign low_pro[2] = A[2][0];
    assign low_pro[3] = A[3][0];
    assign low_pro[4] = A[4][0];
    assign low_pro[5] = A[5][0];
    assign low_pro[6] = A[6][0];
    assign low_pro[7] = A[7][0];

    assign res = {(A[7] >> 1),low_pro};
endmodule

綜合電路

23個元件,32IO口,224根線,和使用for循環的方式相比,多了幾根線??!

好吧果然是我的能力問題,有見解的朋友們請務必說一下,我想學!

image-20201110194747053

測試文件

由於兩個都是乘法器,輸入和輸出端口都一樣,則可以用同樣的測試文件進行測試:

module mul_tb(
    );
    reg [7:0] mul1,mul2;
    wire [15:0] res;
    reg clk;
    initial begin
        mul1 <= 8'd7;
        mul2 <= 8'd3;
        clk <= 0;
    end

    always # 10 clk = ~clk;
    always @ (posedge clk) begin
        mul2 <= mul2 + 1'b1;
    end

    mul_8_1 try(.mul1(mul1),.mul2(mul2),.res(res),);
endmodule

仿真波形

調成無符號十進制數進行觀察,如圖所示,7x7=497x8=56,結果正確。

image-20201110200652246


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM