LoginSignup
3
4

More than 3 years have passed since last update.

VHDLでの符号付き2進数の順次演算の乗算回路

Last updated at Posted at 2019-08-20

はじめに

符号付き2進数の乗算の記事で符号付き2進数の乗算についてまとめました。

この結果をもとに今回はこちらのサイトで紹介されている
順次演算の乗算回路の符号付き2進数バージョンを作成します。

論理合成については確認していないため、合成できない記述があるかもしれませんので注意してください。

開発環境

  • Windows10 Pro 64bit
  • Docker for Windows Community Edition Version 2.0.0.3
  • VcXsrv X Server Version "1.20.1.4"
  • ghdl/ext:ls-vunit-gtkwave
    • GHDL : 0.37-dev
    • vunit-hdl : 4.0.9rc0
    • GTKWave Analyzer v3.3.102
    • X11 libraries

実装

入出力仕様

VALIDとREADYを用いたハンドシェイク通信により入力値を取得します。

  • 入力
    • クロック入力 : i_CLK
    • リセット入力 : i_RST
    • 入力値有効 : i_VALID
    • 乗数 : i_MULTIPLIER
    • 被乗数 : i_MULTIPLICAND
  • 出力
    • 入力準備完了 : o_READY
    • 乗算結果 : o_DATA

状態遷移

筆算とコード中の各状態の関係を
4ビット同士の乗算の場合で表現すると以下の通りになります。

実装を簡単にするため定数項の加算を分離しました。
これにより符号無しの乗算回路よりレイテンシが1クロック多くなります。

                              a3    a2    a1    a0  # state = idle   : i_valid = '1'で入力値を読み出し
*)                            b3    b2    b1    b0  # state = idle   : i_valid = '1'で入力値を読み出し
----------------------------------------------------
                            ~p30   p20   p10   p00  # state = calc   : 部分積を加算(count = 3)
                      ~p31   p21   p11   p01        # state = calc   : 部分積を加算(count = 2)
                ~p31   p22   p12   p02              # state = calc   : 部分積を加算(count = 1)
           p33  ~p23  p~13  ~p03                    # state = calc   : 部分積を加算(count = 0)
+)     1                 1                          # state = update : 定数項を加算し出力
----------------------------------------------------
      P7    P6    P5    P4    P3    P2    P1    P0

概要図

上記の筆算を元に概要図を作成すると以下のようになります。
符号無しの場合と比べると
部分積の加算の前に論理反転とセレクタが入っている点と
regの更新完了後に定数項を加算する点が異なります。

SequentialMultiplier.jpg

コード(Data Flow Design)

2019/08/25追記
コード修正: 良くなさそうな所の項目を参照

2019/08/26追記
コード修正: もっと直した方が良い所の項目を参照

修正前コード(折りたたみ)
SequentialMultiplier.vhd(修正前)
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity SequentialMultiplier is
    generic(
        MULTIPLIER_BITS : natural;
        MULTIPLICAND_BITS : natural
    );
    port(
        i_CLK : in std_logic;
        i_RST : in std_logic;
        i_VALID : in std_logic;
        i_MULTIPLIER : in signed(MULTIPLIER_BITS - 1 downto 0);
        i_MULTIPLICAND : in signed(MULTIPLICAND_BITS - 1 downto 0);
        o_READY : out std_logic;
        o_DATA : out signed(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0)
    );
end SequentialMultiplier;

architecture Behavioral of SequentialMultiplier is

    type type_state is (idle, calc, update);

    signal state : type_state;

    signal count : natural range 0 to MULTIPLIER_BITS - 1;

    signal multiplier : unsigned(MULTIPLIER_BITS - 1 downto 0);
    signal multiplicand : unsigned(MULTIPLICAND_BITS - 1 downto 0);
    signal pp : unsigned(MULTIPLICAND_BITS - 1 downto 0);
    signal reg : unsigned(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0);
    signal feedback : unsigned(MULTIPLICAND_BITS - 1 downto 0);

begin

    process(i_CLK)
    begin
        if rising_edge(i_CLK) then
            if i_RST = '1' then
                state <= idle;
            else
                case state is
                    when idle =>
                        if i_VALID = '1' then
                            state <= calc;
                        end if;
                    when calc =>
                        if count = 0 then
                            state <= update;
                        end if;
                    when update =>
                        state <= idle;
                    when others =>
                        state <= idle;
                end case;
            end if;
        end if;
    end process;

    o_READY <= '1' when state = idle else '0';

    process(i_CLK)
    begin
        if rising_edge(i_CLK) then
            case state is
                when idle =>
                    count <= MULTIPLIER_BITS - 1;
                when calc =>
                    if count = 0 then
                        count <= MULTIPLIER_BITS - 1;
                    else
                        count <= count - 1;
                    end if;
                when others => null;
            end case;
        end if;
    end process;

    process(i_CLK)
    begin
        if rising_edge(i_CLK) then
            case state is
                when idle =>
                    multiplier <= unsigned(i_MULTIPLIER);
                when calc =>
                    multiplier <= '0' & multiplier(MULTIPLIER_BITS - 1 downto 1);
                when others => null;
            end case;
        end if;
    end process;

    process(i_CLK)
    begin
        if rising_edge(i_CLK) then
            case state is
                when idle =>
                    multiplicand <= unsigned(i_MULTIPLICAND);
                when
                    others => null;
            end case;
        end if;
    end process;

    partial_product_gen : for i in MULTIPLICAND_BITS - 1 downto 0 generate
        pp(i) <= multiplicand(i) and multiplier(0);
    end generate;

    feedback <= reg(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto MULTIPLIER_BITS);

    process(i_CLK)
        variable partial_product : unsigned(MULTIPLICAND_BITS - 1 downto 0);
        variable sum : unsigned(MULTIPLICAND_BITS downto 0);
    begin
        if rising_edge(i_CLK) then
            case state is
                when idle =>
                    reg <= (others => '0');
                when calc =>
                    if count = 0 then
                        partial_product := pp(MULTIPLICAND_BITS - 1) & not pp(MULTIPLICAND_BITS - 2 downto 0);
                    else
                        partial_product := not pp(MULTIPLICAND_BITS - 1) & pp(MULTIPLICAND_BITS - 2 downto 0);
                    end if;
                    sum := resize(partial_product, MULTIPLICAND_BITS + 1) + resize(feedback, MULTIPLICAND_BITS + 1);
                    reg <= sum & reg(MULTIPLIER_BITS - 1 downto 1);
                when others =>
                    null;
            end case;
        end if;
    end process;

    process(i_CLK)
        pure function f_const
        return unsigned is
            variable var : unsigned(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0) := (others => '0');
        begin
            var(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1) := '1';
            if (MULTIPLICAND_BITS = MULTIPLIER_BITS) then
                var(MULTIPLICAND_BITS) := '1';
            else
                var(MULTIPLICAND_BITS - 1) := '1';
                var(MULTIPLIER_BITS   - 1) := '1';
            end if;
            return var;
        end function f_const;
        constant const : unsigned(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0) := f_const;
    begin
        if rising_edge(i_CLK) then
            if i_RST = '1' then
                o_DATA <= (others => '0');
            else
                case state is
                    when update => o_DATA <= signed(reg + const);
                    when others => null;
                end case;
            end if;
        end if;
    end process;

end Behavioral;

SequentialMultiplier.vhd(修正後)
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity SequentialMultiplier is
    generic (
        MULTIPLIER_BITS   : natural;
        MULTIPLICAND_BITS : natural
    );
    port (
        i_CLK          : in std_logic;
        i_RST          : in std_logic;
        i_VALID        : in std_logic;
        i_MULTIPLIER   : in signed(MULTIPLIER_BITS - 1 downto 0);
        i_MULTIPLICAND : in signed(MULTIPLICAND_BITS - 1 downto 0);
        o_READY        : out std_logic;
        o_DATA         : out signed(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0)
    );
end SequentialMultiplier;

architecture Behavioral of SequentialMultiplier is

    type type_state is (idle, calc, update);

    signal state : type_state := idle;

    signal count : natural range 0 to MULTIPLIER_BITS - 1 := MULTIPLIER_BITS - 1;

    signal multiplier : unsigned(MULTIPLIER_BITS - 1 downto 0) := (others => '0');
    signal multiplicand : unsigned(MULTIPLICAND_BITS - 1 downto 0) := (others => '0');
    signal pp : unsigned(MULTIPLICAND_BITS - 1 downto 0);
    signal partial_product : unsigned(MULTIPLICAND_BITS - 1 downto 0);
    signal sum : unsigned(MULTIPLICAND_BITS downto 0);
    signal reg : unsigned(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0) := (others => '0');
    signal feedback : unsigned(MULTIPLICAND_BITS - 1 downto 0);

    signal data_out : signed(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0) := (others => '0');

begin

    process (i_CLK)
    begin
        if rising_edge(i_CLK) then
            if i_RST = '1' then
                state <= idle;
            else
                case state is
                    when idle =>
                        if i_VALID = '1' then
                            state <= calc;
                        end if;
                    when calc =>
                        if count = 0 then
                            state <= update;
                        end if;
                    when update =>
                        state <= idle;
                    when others =>
                        state <= idle;
                end case;
            end if;
        end if;
    end process;

    o_READY <= '1' when state = idle else '0';

    process (i_CLK)
    begin
        if rising_edge(i_CLK) then
            if i_RST = '1' then
                count <= MULTIPLIER_BITS - 1;
            else
                case state is
                    when idle =>
                        count <= MULTIPLIER_BITS - 1;
                    when calc =>
                        if count = 0 then
                            count <= MULTIPLIER_BITS - 1;
                        else
                            count <= count - 1;
                        end if;
                    when others =>
                        count <= MULTIPLIER_BITS - 1;
                end case;
            end if;
        end if;
    end process;

    process (i_CLK)
    begin
        if rising_edge(i_CLK) then
            if i_RST = '1' then
                multiplier <= (others => '0');
            else
                case state is
                    when idle =>
                        multiplier <= unsigned(i_MULTIPLIER);
                    when calc =>
                        multiplier <= '0' & multiplier(MULTIPLIER_BITS - 1 downto 1);
                    when others =>
                        multiplier <= (others => '0');
                end case;
            end if;
        end if;
    end process;

    process (i_CLK)
    begin
        if rising_edge(i_CLK) then
            if i_RST = '1' then
                multiplicand <= (others => '0');
            else
                case state is
                    when idle =>
                        multiplicand <= unsigned(i_MULTIPLICAND);
                    when calc =>
                        multiplicand <= multiplicand;
                    when others =>
                        multiplicand <= (others => '0');
                end case;
            end if;
        end if;
    end process;

    pp_gen : for i in MULTIPLICAND_BITS - 1 downto 0 generate
        pp(i) <= multiplicand(i) and multiplier(0);
    end generate;

    process (count, pp)
    begin
        if count = 0 then
            partial_product <= pp(MULTIPLICAND_BITS - 1) & not pp(MULTIPLICAND_BITS - 2 downto 0);
        else
            partial_product <= not pp(MULTIPLICAND_BITS - 1) & pp(MULTIPLICAND_BITS - 2 downto 0);
        end if;
    end process;

    feedback <= reg(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto MULTIPLIER_BITS);

    sum <= resize(partial_product, MULTIPLICAND_BITS + 1) + resize(feedback, MULTIPLICAND_BITS + 1);

    process (i_CLK)
    begin
        if rising_edge(i_CLK) then
            if i_RST = '1' then
                reg <= (others => '0');
            else
                case state is
                    when idle =>
                        reg <= (others => '0');
                    when calc =>
                        reg <= sum & reg(MULTIPLIER_BITS - 1 downto 1);
                    when others =>
                        reg <= (others => '0');
                end case;
            end if;
        end if;
    end process;

    process (i_CLK)
        function f_const
            return unsigned is
            variable var : unsigned(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0) := (others => '0');
        begin
            var(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1) := '1';
            if (MULTIPLICAND_BITS = MULTIPLIER_BITS) then
                var(MULTIPLICAND_BITS) := '1';
            else
                var(MULTIPLICAND_BITS - 1) := '1';
                var(MULTIPLIER_BITS - 1) := '1';
            end if;
            return var;
        end function;
        constant const : unsigned(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0) := f_const;
    begin
        if rising_edge(i_CLK) then
            if i_RST = '1' then
                data_out <= (others => '0');
            else
                case state is
                    when idle | calc =>
                        data_out <= data_out;
                    when update =>
                        data_out <= signed(reg + const);
                    when others =>
                        data_out <= (others => '0');
                end case;
            end if;
        end if;
    end process;

    o_DATA <= data_out;

end Behavioral;


テスト

すべての入力値パターンについてテストするコードになります。

MULTIPLIER_BITSやMULTIPLICAND_BITSを大きくすると
計算量が膨大になるので注意してください。

また、MULTIPLIER_BITSかMULTIPLICAND_BITSを32以上にすると
for loopの範囲がintegerの範囲外になるため
大きいビット幅でテストしたい場合は別途実装が必要になります。

tb_SequentialMultiplier_0.vhd
library vunit_lib;
context vunit_lib.vunit_context;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.pkg_mult.all;

entity tb_SequentialMultiplier_0 is
    generic (
        runner_cfg : string := runner_cfg_default
    );
end tb_SequentialMultiplier_0;

architecture behavior of tb_SequentialMultiplier_0 is

    -- Parameters
    constant MULTIPLIER_BITS : natural := 8;
    constant MULTIPLICAND_BITS : natural := 8;

    -- Inputs
    signal i_CLK : std_logic := '0';
    signal i_RST : std_logic := '0';
    signal i_VALID : std_logic := '0';
    signal i_MULTIPLIER : signed(MULTIPLIER_BITS - 1 downto 0) := (others => '0');
    signal i_MULTIPLICAND : signed(MULTIPLICAND_BITS - 1 downto 0) := (others => '0');

    -- Outputs
    signal o_READY : std_logic;
    signal o_DATA : signed(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0) := (others => '0');

    -- Clock period definitions
    constant CLK_PERIOD : time := 20 ns;

begin

    SequentialMultiplier_inst : SequentialMultiplier
    generic map(
        MULTIPLIER_BITS => MULTIPLIER_BITS,
        MULTIPLICAND_BITS => MULTIPLICAND_BITS
    )
    port map(
        i_CLK => i_CLK,
        i_RST => i_RST,
        i_VALID => i_VALID,
        i_MULTIPLIER => i_MULTIPLIER,
        i_MULTIPLICAND => i_MULTIPLICAND,
        o_READY => o_READY,
        o_DATA => o_DATA
    );

    -- Clock process definitions
    CLK_process : process
    begin
        i_CLK <= '0';
        wait for CLK_PERIOD / 2;
        i_CLK <= '1';
        wait for CLK_PERIOD / 2;
    end process;

    test_bench : process
    begin
        test_runner_setup(runner, runner_cfg);
        while test_suite loop

            -- initialize
            i_RST <= '1';
            wait for CLK_PERIOD;
            i_RST <= '0';
            wait for CLK_PERIOD;

            if run("mult") then

                for j in 2 ** (MULTIPLICAND_BITS - 1) - 1 downto - (2 ** (MULTIPLICAND_BITS - 1)) loop
                    for i in 2 ** (MULTIPLIER_BITS - 1) - 1 downto - (2 ** (MULTIPLIER_BITS - 1)) loop
                        i_VALID <= '1';
                        i_MULTIPLIER <= to_signed(i, i_MULTIPLIER'length);
                        i_MULTIPLICAND <= to_signed(j, i_MULTIPLICAND'length);
                        wait for CLK_PERIOD;
                        check_equal(o_READY, '0', "o_READY is not on" );
                        i_VALID <= '0';
                        wait for CLK_PERIOD * (MULTIPLIER_BITS + 1);
                        check_equal(o_READY, '1', "o_READY is not off" );
                        check_equal(o_DATA, to_signed(i * j, o_DATA'length), "o_DATA when multiplier = " & to_string(i) & ", multiplicand = " & to_string(j));
                    end loop;
                end loop;

            end if;

            wait for CLK_PERIOD;

        end loop;
        test_runner_cleanup(runner);
    end process;

end;

パッケージ

pkg_mult.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

package pkg_mult is

    component SequentialMultiplier
        generic(
            MULTIPLIER_BITS : natural;
            MULTIPLICAND_BITS : natural
        );
        port(
            i_CLK : in std_logic;
            i_RST : in std_logic;
            i_VALID : in std_logic;
            i_MULTIPLIER : in signed(MULTIPLIER_BITS - 1 downto 0);
            i_MULTIPLICAND : in signed(MULTIPLICAND_BITS - 1 downto 0);
            o_READY : out std_logic;
            o_DATA : out signed(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0)
        );
    end component;

end pkg_mult;

シミュレーション

シミュレーション波形を一部抜粋すると乗算ができていることがわかります

シミュレーション結果.png

良くなさそうな所(2019/8/25追記)

実装の中の以下の部分にあるconstif generateなどで書けそうな気がしますが
うまい書き方が思いつきませんでした。
良い書き方があれば教えてください。

2019/08/25追記
Twitterでアドバイスをいただきました。ありがとうございます!

functionで処理を記述することでconstを定数として宣言できるようになっています。

また、var(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1) := '1';
if文によらないのでif文の外に出されています。

(修正前)
    process(i_CLK)
        variable const : unsigned(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0);
    begin
        if rising_edge(i_CLK) then
            if i_RST = '1' then
                o_DATA <= (others => '0');
            else
                case state is
                    when update =>
                        const := (others => '0');
                        if MULTIPLICAND_BITS = MULTIPLIER_BITS then
                            const(2 * MULTIPLICAND_BITS - 1) := '1';
                            const(MULTIPLICAND_BITS) := '1';
                        else
                            const(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1) := '1';
                            const(MULTIPLICAND_BITS - 1) := '1';
                            const(MULTIPLIER_BITS - 1) := '1';
                        end if;
                        data_out <= signed(reg + const);
                    when others => null;
                end case;
            end if;
        end if;
    end process;

    o_DATA <= data_out;

修正後
    process(i_CLK)
        pure function f_const
        return unsigned is
            variable var : unsigned(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0) := (others => '0');
        begin
            var(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1) := '1';
            if (MULTIPLICAND_BITS = MULTIPLIER_BITS) then
                var(MULTIPLICAND_BITS) := '1';
            else
                var(MULTIPLICAND_BITS - 1) := '1';
                var(MULTIPLIER_BITS   - 1) := '1';
            end if;
            return var;
        end function f_const;
        constant const : unsigned(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0) := f_const;
    begin
        if rising_edge(i_CLK) then
            if i_RST = '1' then
                o_DATA <= (others => '0');
            else
                case state is
                    when update => data_out <= signed(reg + const);
                    when others => null;
                end case;
            end if;
        end if;
    end process;

    o_DATA <= data_out;

もっと直した方が良いトコロ(2019/8/26追記)

2019/08/26追記
他の部分についても教えていただきました。ありがとうございます!

1. FFになるsignalの初期値が指定されていない

FFになる変数に初期値を指定しました。

修正コードでもFFにならない変数に初期値を指定していませんが、
指定ミスに繋がりそうなので全ての変数を初期化しておくほうが本当は良さそうです。

    type type_state is (idle, calc, update);

    signal state : type_state := idle;

    signal count : natural range 0 to MULTIPLIER_BITS - 1 := MULTIPLIER_BITS - 1;

    signal multiplier : unsigned(MULTIPLIER_BITS - 1 downto 0) := (others => '0');
    signal multiplicand : unsigned(MULTIPLICAND_BITS - 1 downto 0) := (others => '0');
    signal pp : unsigned(MULTIPLICAND_BITS - 1 downto 0);
    signal partial_product : unsigned(MULTIPLICAND_BITS - 1 downto 0);
    signal sum : unsigned(MULTIPLICAND_BITS downto 0);
    signal reg : unsigned(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0) := (others => '0');
    signal feedback : unsigned(MULTIPLICAND_BITS - 1 downto 0);

    signal data_out : signed(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0) := (others => '0');

2. i_RSTとwhen othersの辺りが防御的でない.

最小限の箇所だけi_RSTを書いたりwhen others => nullを使っていましたが
バグの元になるため修正しました。

3. partial_productとsumがvariableでキモい.

たしかに言われてみるとおかしいですね。
variableをsignalにして組み合わせ回路として分離しました。

修正前
    partial_product_gen : for i in MULTIPLICAND_BITS - 1 downto 0 generate
        pp(i) <= multiplicand(i) and multiplier(0);
    end generate;

    feedback <= reg(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto MULTIPLIER_BITS);

    process(i_CLK)
        variable partial_product : unsigned(MULTIPLICAND_BITS - 1 downto 0);
        variable sum : unsigned(MULTIPLICAND_BITS downto 0);
    begin
        if rising_edge(i_CLK) then
            case state is
                when idle =>
                    reg <= (others => '0');
                when calc =>
                    if count = 0 then
                        partial_product := pp(MULTIPLICAND_BITS - 1) & not pp(MULTIPLICAND_BITS - 2 downto 0);
                    else
                        partial_product := not pp(MULTIPLICAND_BITS - 1) & pp(MULTIPLICAND_BITS - 2 downto 0);
                    end if;
                    sum := resize(partial_product, MULTIPLICAND_BITS + 1) + resize(feedback, MULTIPLICAND_BITS + 1);
                    reg <= sum & reg(MULTIPLIER_BITS - 1 downto 1);
                when others =>
                    null;
            end case;
        end if;
    end process;

修正後
    signal partial_product : unsigned(MULTIPLICAND_BITS - 1 downto 0);
    signal sum : unsigned(MULTIPLICAND_BITS downto 0);

    ....

    pp_gen : for i in MULTIPLICAND_BITS - 1 downto 0 generate
        pp(i) <= multiplicand(i) and multiplier(0);
    end generate;

    process (count, pp)
    begin
        if count = 0 then
            partial_product <= pp(MULTIPLICAND_BITS - 1) & not pp(MULTIPLICAND_BITS - 2 downto 0);
        else
            partial_product <= not pp(MULTIPLICAND_BITS - 1) & pp(MULTIPLICAND_BITS - 2 downto 0);
        end if;
    end process;

    feedback <= reg(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto MULTIPLIER_BITS);

    sum <= resize(partial_product, MULTIPLICAND_BITS + 1) + resize(feedback, MULTIPLICAND_BITS + 1);

    process (i_CLK)
    begin
        if rising_edge(i_CLK) then
            if i_RST = '1' then
                reg <= (others => '0');
            else
                case state is
                    when idle =>
                        reg <= (others => '0');
                    when calc =>
                        reg <= sum & reg(MULTIPLIER_BITS - 1 downto 1);
                    when others =>
                        reg <= (others => '0');
                end case;
            end if;
        end if;
    end process;

4. TPで書こう

概要

TPってなんの略だ?となっていましたが
別の方が空リプを飛ばしてくれていました。ありがとうございます!

TPとはtwo-processのことだそうです。

  • すべてのポートおよびシグナル宣言でレコードタイプを使用する
  • エンティティごとに2つのプロセスのみを使用する
  • 高レベルの順次ステートメントを使用してアルゴリズムをコーディングする

これらを行うことで以下のような効果を得られるそうです

  • 均一なアルゴリズムエンコーディングを提供する
  • 抽象化レベルを上げる
  • 読みやすさを改善する
  • シーケンシャルロジックを明確に識別する
  • デバッグの簡素化
  • シミュレーション速度の改善
  • 合成とシミュレーションの両方に1つのモデルを提供する

コード(two-process)

combinationalのprocess文を上から読んでいけるので理解しやすいということのようです。

初めて書くため、おかしい所が多いと思いますのでご指摘あればよろしくお願いします。

今回はrecordタイプにgenericの値を反映させる方法がわからなかったので
portにはrecord typeを仕様できていません(後述)

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

package pkg_mult is

    component SequentialMultiplier
        generic (
            MULTIPLIER_BITS   : natural;
            MULTIPLICAND_BITS : natural
        );
        port (
            i_CLK          : in std_logic;
            i_RST          : in std_logic;
            i_VALID        : in std_logic;
            i_MULTIPLIER   : in signed(MULTIPLIER_BITS - 1 downto 0);
            i_MULTIPLICAND : in signed(MULTIPLICAND_BITS - 1 downto 0);
            o_READY        : out std_logic;
            o_DATA         : out signed(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0)
        );
    end component;

end package;

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity SequentialMultiplier is
    generic (
        MULTIPLIER_BITS   : natural;
        MULTIPLICAND_BITS : natural
    );
    port (
        i_CLK          : in std_logic;
        i_RST          : in std_logic;
        i_VALID        : in std_logic;
        i_MULTIPLIER   : in signed(MULTIPLIER_BITS - 1 downto 0);
        i_MULTIPLICAND : in signed(MULTIPLICAND_BITS - 1 downto 0);
        o_READY        : out std_logic;
        o_DATA         : out signed(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0)
    );
end SequentialMultiplier;

architecture two_process of SequentialMultiplier is

    type type_state is (idle, calc, update);

    type type_reg is record
        state : type_state;
        count : natural range 0 to MULTIPLIER_BITS - 1;
        multiplier : unsigned(MULTIPLIER_BITS - 1 downto 0);
        multiplicand : unsigned(MULTIPLICAND_BITS - 1 downto 0);
        data : unsigned(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0);
        data_out : signed(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0);
    end record;

    signal r, rin : type_reg := (
        state => idle,
        count => MULTIPLIER_BITS - 1,
        multiplier => (others => '0'),
        multiplicand => (others => '0'),
        data => (others => '0'),
        data_out => (others => '0')
    );

    type type_comb is record
        pp : unsigned(MULTIPLICAND_BITS - 1 downto 0);
        partial_product : unsigned(MULTIPLICAND_BITS - 1 downto 0);
        sum : unsigned(MULTIPLICAND_BITS downto 0);
        feedback : unsigned(MULTIPLICAND_BITS - 1 downto 0);
    end record;

    function f_const
        return unsigned is
        variable var : unsigned(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0) := (others => '0');
    begin
        var(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1) := '1';
        if (MULTIPLICAND_BITS = MULTIPLIER_BITS) then
            var(MULTIPLICAND_BITS) := '1';
        else
            var(MULTIPLICAND_BITS - 1) := '1';
            var(MULTIPLIER_BITS - 1) := '1';
        end if;
        return var;
    end function;

    constant const : unsigned(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0) := f_const;

begin

    combinational : process (i_RST, i_VALID, i_MULTIPLIER, i_MULTIPLICAND, r)
        variable v_reg : type_reg;
        variable v_comb : type_comb;
    begin

        v_reg := r;

        case r.state is

            when idle =>

                if i_VALID = '1' then
                    v_reg.state := calc;
                end if;

                v_reg.count := MULTIPLIER_BITS - 1;
                v_reg.multiplier := unsigned(i_MULTIPLIER);
                v_reg.multiplicand := unsigned(i_MULTIPLICAND);
                v_reg.data := (others => '0');

            when calc =>

                if r.count = 0 then
                    v_reg.count := MULTIPLIER_BITS - 1;
                    v_reg.state := update;
                else
                    v_reg.count := r.count - 1;
                end if;

                v_reg.multiplier := '0' & r.multiplier(MULTIPLIER_BITS - 1 downto 1);

                for i in MULTIPLICAND_BITS - 1 downto 0 loop
                    v_comb.pp(i) := r.multiplicand(i) and r.multiplier(0);
                end loop;

                if r.count = 0 then
                    v_comb.partial_product := v_comb.pp(MULTIPLICAND_BITS - 1) & not v_comb.pp(MULTIPLICAND_BITS - 2 downto 0);
                else
                    v_comb.partial_product := not v_comb.pp(MULTIPLICAND_BITS - 1) & v_comb.pp(MULTIPLICAND_BITS - 2 downto 0);
                end if;

                v_comb.feedback := r.data(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto MULTIPLIER_BITS);

                v_comb.sum := resize(v_comb.partial_product, MULTIPLICAND_BITS + 1) + resize(v_comb.feedback, MULTIPLICAND_BITS + 1);

                v_reg.data := v_comb.sum & r.data(MULTIPLIER_BITS - 1 downto 1);

            when update =>

                v_reg.data_out := signed(r.data + const);
                v_reg.state := idle;

            when others =>

                v_reg.state := idle;
                v_reg.count := MULTIPLIER_BITS - 1;
                v_reg.multiplier := (others => '0');
                v_reg.multiplicand := (others => '0');
                v_reg.data := (others => '0');
                v_reg.data_out := (others => '0');

        end case;

        if i_RST = '1' then

            v_reg.state := idle;
            v_reg.count := MULTIPLIER_BITS - 1;
            v_reg.multiplier := (others => '0');
            v_reg.multiplicand := (others => '0');
            v_reg.data := (others => '0');
            v_reg.data_out := (others => '0');

        end if;

        rin <= v_reg;
        o_READY <= '1' when r.state = idle else '0';
        o_DATA <= r.data_out;

    end process;

    sequential : process (i_CLK)
    begin
        if rising_edge(i_CLK) then
            r <= rin;
        end if;
    end process;

end two_process;

よくわからなかったところ

two-processの効果を最大限得るためにはport宣言部でも
下記のようにrecord型を使わなければなりませんが
genericのパラメータの反映のさせ方がわからなかったので断念しています。
良い方法があれば教えてください。

package pkg_mult is

    type type_SequentialMultiplier_input is record
        VALID : std_logic;
        MULTIPLIER   : signed(MULTIPLIER_BITS - 1 downto 0);
        MULTIPLICAND : signed(MULTIPLICAND_BITS - 1 downto 0);
    end record;

    type type_SequentialMultiplier_output is record
        READY      : std_logic;
        MULTIPLIER : signed(MULTIPLIER_BITS - 1 downto 0);
        o_DATA     : signed(MULTIPLICAND_BITS + MULTIPLIER_BITS - 1 downto 0)
    end record;

    component SequentialMultiplier
        generic (
            MULTIPLIER_BITS   : natural;
            MULTIPLICAND_BITS : natural
        );
        port (
            i_CLK : in std_logic;
            i_RST : in std_logic;
            i_D   : in type_SequentialMultiplier_input;
            o_Q   : out type_SequentialMultiplier_output
        );
    end component;

end package;

...

参考

http://www.kumikomi.net/archives/2010/07/ep21suc2.php?page=5
https://www.gaisler.com/doc/vhdl2proc.pdf

更新履歴

2019/08/20 投稿
2019/08/25 Twitterからのアドバイスによるコード更新、typo修正
2019/08/26 Twitterからのアドバイスによるコード更新、two-processに関して追記

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4