【VHDL】ビットシフトを利用した乗算回路

FPGAで乗算を実現する場合、基本的には専用リソースとして用意されているDSPを使うかと思います。しかし固定値の乗算で、かつ乗算値が小さい場合には、シフト演算を利用することでDSPを使用しないで実現できます。ちょっとした掛け算をするだけなのにベンダIPをインスタンス化するのも面倒ですので、たまにですがこの方法を使うことがあります。

シフト演算とは?

1ビットシフトの例を示します。ここでは論理シフト(符号を考えない)のみを扱います。

論理シフト シフト演算

青枠は8ビットデータを10進数で表した場合の数です。ここで注目すべきは、左シフトの場合は2で掛けた値。右シフトの場合は2で割った値になっている点です。

一度左シフトした値を再度左シフトすればさらに2倍。これは2ビット左シフトした場合に4倍になっているのと同等です。このことからわかる通り、2のべき乗を掛ける、もしくは割る操作はビットシフトのみで行えることが分かります。シフトするだけで計算したことになるので、シフト”演算”と呼ばれるのでしょうか。

シフト演算で乗算の実現

では、シフト演算だけで3をかけたい、という場合にはどうすればよいでしょう?シフト演算は2のべき乗を表せる(1も含む)ので、2のべき乗だけで3を表せばよいことが分かります。

3=2+1

つまり、「入力値そのまま」+「入力値を1ビット左シフト」で3をかけたことと同等となります。

もう一つ例を挙げて、7はどうでしょう?

7=4+2+1

で表現できます。しかし、加算回数は少ない方が回路的には有利です。そのため以下の方がいいでしょう。

7=8-1

このように、引き算を使用すると処理が楽になる場合もあります。

乗算なので、左ビットシフトのみを使用しています。

ソースコード

以上をVHDLで実現する場合のコードを記述してみました。補足説明としては、8ビット×4ビットの例なので、答えは12ビットになります。シフトすることで数値が捨てられてしまわないよう、内部でシフトする時点で答えのビット数に合わせてあります。

シフト演算モジュール(VHDL)

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity shift_mul is
    port    (
        srstn_i : in    std_logic;
        clk_i   : in    std_logic;

        sig_i   : in    std_logic_vector (7 downto 0);
        mul_i   : in    std_logic_vector (3 downto 0);
        sig_o   : out   std_logic_vector (11 downto 0)
    );
end shift_mul;

architecture rtl of shift_mul is

signal sig_x1   : std_logic_vector (11 downto 0);
signal sig_x2   : std_logic_vector (11 downto 0);
signal sig_x4   : std_logic_vector (11 downto 0);
signal sig_x8   : std_logic_vector (11 downto 0);
signal sig_x16  : std_logic_vector (11 downto 0);
signal mul      : std_logic_vector (11 downto 0);

begin

-- 入力信号を左シフトして2のべき乗をかけた信号を生成
sig_x1  <= (11 downto  8 => '0') & sig_i;
sig_x2  <= (11 downto  9 => '0') & sig_i & (0 downto 0 => '0');
sig_x4  <= (11 downto 10 => '0') & sig_i & (1 downto 0 => '0');
sig_x8  <= (11 downto 11 => '0') & sig_i & (2 downto 0 => '0');
sig_x16 <= sig_i & (3 downto 0 => '0');

-- シフト信号の組み合わせで乗算を実現
process (clk_i) begin
    if (clk_i'event and clk_i='1') then
        if (srstn_i = '0') then
            mul <= (others=>'0');
        else
            case mul_i is
                when x"0"   => mul  <= (others=>'0');               -- x0は0
                when x"1"   => mul  <= sig_x1;                      -- x1=そのまま出力
                when x"2"   => mul  <= sig_x2;                      -- x2=1bit左シフト
                when x"3"   => mul  <= sig_x2 + sig_x1;             -- x3=x(2+1)
                when x"4"   => mul  <= sig_x4;                      -- x4=2bit左シフト
                when x"5"   => mul  <= sig_x4 + sig_x1;             -- x5=x(4+1)
                when x"6"   => mul  <= sig_x4 + sig_x2;             -- x6=x(4+2)
                when x"7"   => mul  <= sig_x8 - sig_x1;             -- x7=x(8-1)
                when x"8"   => mul  <= sig_x8;                      -- x8=3bit左シフト
                when x"9"   => mul  <= sig_x8 + sig_x1;             -- x9=x(8+1)
                when x"a"   => mul  <= sig_x8 + sig_x2;             -- x10=x(8+2)
                when x"b"   => mul  <= sig_x8 + sig_x2 + sig_x1;    -- x11=x(8+2+1)
                when x"c"   => mul  <= sig_x16 - sig_x4;            -- x12=x(16-4)
                when x"d"   => mul  <= sig_x16 - sig_x2 - sig_x1;   -- x13=x(16-2-1)
                when x"e"   => mul  <= sig_x16 - sig_x2;            -- x14=x(16-2)
                when x"f"   => mul  <= sig_x16 - sig_x1;            -- x15=x(16-1)
                when others => mul  <= (others=>'0');
            end case;
        end if;
    end if;
end process;

sig_o   <= mul;

end rtl;

テストベンチ

テスト内容は以下を順番に行っています。

  • 1に0~15を乗算
  • 2に0~15を乗算
  • 255に15を乗算
  • library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_unsigned.all;
    use ieee.std_logic_arith.all;
    
    entity testbench is
    end testbench;
    
    architecture rtl of testbench is
    
    component shift_mul is
        port    (
            srstn_i : in    std_logic;
            clk_i   : in    std_logic;
    
            sig_i   : in    std_logic_vector (7 downto 0);
            mul_i   : in    std_logic_vector (3 downto 0);
            sig_o   : out   std_logic_vector (11 downto 0)
        );
    end component;
    
    signal srstn_i : std_logic := '0';
    signal clk_i   : std_logic := '0';
    signal sig_i   : std_logic_vector (7 downto 0) := (others=>'0');
    signal mul_i   : std_logic_vector (3 downto 0) := (others=>'0');
    signal sig_o   : std_logic_vector (11 downto 0) := (others=>'0');
    
    begin
    
    -- クロック生成
    process begin
        clk_i   <= '0';
        wait for 5 ns;
        clk_i   <= '1';
        wait for 5 ns;
    end process;
    -- 同期リセット生成
    process begin
        srstn_i <= '0';
        wait for 99 ns;
        wait until (clk_i'event and clk_i='1');
        srstn_i <= '1';
        wait;
    end process;
    
    -- 入力生成
    process begin
        -- リセット解除後1クロック待機
        wait until (srstn_i = '1');
        wait until (clk_i'event and clk_i='1');
        -- 入力信号1に対して0~15を順番に乗算
        for i in 0 to 15 loop
            sig_i   <= x"01";
            mul_i   <= conv_std_logic_vector(i, 4);
            wait until (clk_i'event and clk_i='1');
        end loop;
    
        wait until (clk_i'event and clk_i='1');
        
        -- 入力信号2に対して0~15を順番に乗算
        for i in 0 to 15 loop
            sig_i   <= x"02";
            mul_i   <= conv_std_logic_vector(i, 4);
            wait until (clk_i'event and clk_i='1');
        end loop;
    
        wait until (clk_i'event and clk_i='1');
    
        -- 入力信号256に対して15を乗算
        sig_i   <= x"ff";
        mul_i   <= conv_std_logic_vector(15, 4);
    
        wait;
    end process;
    
    u_shift_mul : shift_mul
        port map    (
            srstn_i => srstn_i  ,--: in    std_logic;
            clk_i   => clk_i    ,--: in    std_logic;
    
            sig_i   => sig_i    ,--: in    std_logic_vector (7 downto 0);
            mul_i   => mul_i    ,--: in    std_logic_vector (3 downto 0);
            sig_o   => sig_o    --: out   std_logic_vector (11 downto 0)
        );
    
    end rtl;

    シミュレーション結果

    波形は次の通りです。期待する結果が得られたことが分かります。

    シフト演算 乗算 シミュレーション

    シェアする

    • このエントリーをはてなブックマークに追加

    フォローする