VHDLのstd_logic_vector型と整数型の間の変換について調べました。調べ終わったあとは何もかもすっきりして澄み切った視界を手に入れたような気分です。
結論から言えば、VHDL言語はstd_logic_vector型と整数型の間の自動変換をしません。
ieee.std_logic_unsigned パッケージとieee.std_logic_signed パッケージ
そもそも違和感を感じたのは、以下のようなプログラムを目にしたことです。std_logic_vector型で作った信号を整数として演算に使用するというものです。この例ではstd_logic_vector型、つまり単なるビットの列が整数に変換され、演算後に整数が再びbitの列に変換されています。そもそもコンピュータでは整数の内部表現はbitの列ですから、このブログの読者はおかしくないと思うかもしれません。
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity unsigned_test is port ( a : in std_logic_vector(7 downto 0); b : in std_logic_vector(7 downto 0); c : out std_logic_vector(7 downto 0) ); end unsigned_test; architecture rtl of unsigned_test is begin c <= a + b; end rtl;
なぜ私がこれを変だと感じたかというと、VHDLの元になったAda言語では、この手の大胆な型変換を許さないからです。Adaではごく一部の算術的な変換(範囲型から基底となっている整数型への変換)などを除くと暗黙の型変換を許しません。それはバグの温床だと考えられるからです。型変換は常にプログラム上で明示的な記述を要求されます。
調べた結果、上の疑問は単に私の勘違いだと言うことがわかりました。
上の式は暗黙の型変換などしていません。単に「std_logic_vector型を引数として受取る演算子”+”」が多重定義されているだけなのです。型変換に見えたのは、多重定義された演算子の一つが、型に応じて選ばれていただけでした。
この多重定義された演算子の定義元は、パッケージ ieee.std_logic_unsigned です。つまり、上のプログラムから use ieee.std_logic_unsigned.all を削ると、コンパイルが通らなくなります。
名前からわかるようにこのパッケージが提供するのは符号無し整数演算です。予想されるようにieee.std_logic_signed パッケージが存在し、そちらは符号付き整数演算を提供しています。
さて、ざっと両パッケージを覗いたところ、signedとunsignedのパッケージを混用するのは面倒のようです。そこで、std_logic_arithが用意されていて、長い間こちらを使っていたようです。
numeric_std パッケージ
std_logic_arithパッケージは符号付き整数演算と符号無し整数演算を提供しますが、型に対する扱いが甘いために、代わりに型に対して厳格なnumeric_stdが提供されています。
numeric_stdパッケージが提供する演算子はstd_logic_vector型を整数型と見なして算術演算を行います。しかしながら、パッケージ内部でsigned型とunsigned型をそれぞれbitの配列として定義しています。そのため、単にstd_logic_vectorを引数として渡しても、多重定義された関数や演算子の実体をコンパイラが特定することが出来ません。
正しくコンパイルするには、引数のbit列型をsignedあるいはunsigned型として明示的に型変換しなければなりません。また、signedあるいはunsignedをbit列に代入することは出来ませんので、きちんとstd_logic_vectorに型変換しなければなりません。
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity numeric_test is port ( a : in std_logic_vector(7 downto 0); b : in std_logic_vector(7 downto 0); c : out std_logic_vector(7 downto 0) ); end numeric_test; architecture rtl of numeric_test is begin c <= std_logic_vector( signed(a) + signed(b) ); end rtl;
まとめ
numeric_stdはbit列に対する算術演算を提供してくれます。厳格な型変換を必要としますが、曖昧な点がないので他のライブラリではなくこちらを使う方が良いでしょう。