LoginSignup
1
2

More than 3 years have passed since last update.

VHDLでの乗算

Last updated at Posted at 2019-07-13

会社で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
function "*"(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は整数として扱われる。
また演算の際には、符号を指定する必要がある。

IEEE.NUMERIC_STDでの乗算
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_0
library 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_1
library 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;
1
2
2

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
1
2