はじめに
符号付き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の更新完了後に定数項を加算する点が異なります。
コード(Data Flow Design)
2019/08/25追記
コード修正: 良くなさそうな所の項目を参照
2019/08/26追記
コード修正: もっと直した方が良い所の項目を参照
修正前コード(折りたたみ)
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;
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の範囲外になるため
大きいビット幅でテストしたい場合は別途実装が必要になります。
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;
パッケージ
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;
シミュレーション
シミュレーション波形を一部抜粋すると乗算ができていることがわかります
良くなさそうな所(2019/8/25追記)
実装の中の以下の部分にあるconst
がif generate
などで書けそうな気がしますが
うまい書き方が思いつきませんでした。
良い書き方があれば教えてください。
2019/08/25追記
Twitterでアドバイスをいただきました。ありがとうございます!
https://t.co/NALnNeIZJH
— KIMURA Masaru (@hiyuh) August 24, 2019
もっと直した方が良いトコロが他にあるけど,取り敢えず.
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の初期値が指定されていない.
— KIMURA Masaru (@hiyuh) August 25, 2019
2. i_RSTとwhen othersの辺りが防御的でない.
例えば,stateやcountがぶっ飛ぶとcountが戻って来なくてリセットも効かない.多分idle以外の時にリセットすると変な感じになりそう.
3. partial_productとsumがvariableでキモい.
4. TPで書こう.
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=twoproc, two processhttps://t.co/7L9BvjcGyX
— sarakane (@sarakane6090) August 25, 2019
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に関して追記