会社でVHDLは乗算が使えるか聞かれ即答できなかったので調べた
更新履歴
- 2019/07/17 : コメントを受けunsignedとsignedの乗算について修正
VHDLでの乗算の書き方
基本的には、下記の書き方で乗算になる。
これはVerilogHDLでも同じ。
-- yのビット幅はaとbのビット幅を足し合わせたものになる
y <= a * b;
演算パッケージによる違い
IEEE.STD_LOGIC_ARITH
注意する点は利用するパッケージによって、符号が変わることである。
use IEEE.STD_LOGIC_ARITH.ALL;
-- 内部のstd_logic_vectorがunsignedになる
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-- 内部のstd_logic_vectorがsignedになる
use IEEE.STD_LOGIC_SIGNED.ALL;
例えばIEEE.STD_LOGIC_SIGNED
を利用している場合、
上位コンポーネント(モジュール)からunsignedな信号を入力されても、内部的にはsignedとして扱われる。逆も然り
明示的に符号の有無を示したい場合には、unsigned()
とsigned()
関数を利用する。
Y <= unsigned(A) * unsigned(B);
-- または
Y <= signed(A) * signed(B);
ただしsignedとunsignedの乗算の場合は、
unsigned同士の乗算を行い最後に2の補数に変換されるため、更に1bit幅をもたせる必要がある。
(修正:コメントにて@ryo_i6さんに指摘をいただけましたので引用しています)
ieee.std_logic_arithは内部のコードを見る限りは
絶対値の乗算 -> 符号拡張ではなく
unsignedをsignedに符号拡張 -> signedの乗算になっているようですvhdl:ieee.std_logic_arithfunction "*"(L: SIGNED; R: UNSIGNED) return SIGNED is -- pragma label_applies_to plus begin return mult(CONV_SIGNED(L, L'length), CONV_SIGNED(R, R'length+1)); -- pragma label mult end;
IEEE.NUMERIC_STD
IEEE.NUMERIC_STD
パッケージではstd_logic_vectorは整数として扱われる。
また演算の際には、符号を指定する必要がある。
Y <= std_logic_vector(unsigned(A) * unsigned(B));
-- または
Y <= std_logic_vector(signed(A) * signed(B));
IEEE.NUMERIC_STD
ではsignedとunsigned同士の演算が定義されていない。
一応unsigned側をintergerに変換すればできないこともないが、
signedな信号と乗算する際にsignedとして扱われるらしく正直微妙な結果になった。
ならばと思いunsigned側を符号1bit拡張したがsigned側と幅を合わせろ的なエラーになってしまった。
もう少し調べたほうがいいかもしれない。
正直IEEE.NUMERIC_STD
を使った経験がなさすぎてよくわからない。助けて詳しい人
(追記:こちらの件も@ryo_i6さんよりコメントいただきましたので引用させていただきます)
numeric_stdではsigned * unsignedは定義されていないので
std_logic_arithと同じ結果を得たい場合は
以下のような自作関数を用意することになると思います
numeric_std_sample_0library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; function "*" (l: signed; r: unsigned) return signed is begin return l * signed(resize(r, r'length + 1)); end;
ただし、上記の関数では
例えばsigned 4bit * unsigned 4bitとすると結果がsigned 9bitになりますsigned 4bit * unsigned 4bitは必ずsigned 8bitに収まるはずなので
結果の上位1bitは不要ということになります
このことを加味すると以下のような関数のほうが実用上は便利かと思いますnumeric_std_sample_1library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; function "*" (l: signed; r: unsigned) return signed is variable p : signed(l'length + r'length downto 0); begin p := l * signed(resize(r, r'length + 1)); return signed(p(p'length - 2 downto 0)); end;