VHDLの基本を忘れてしまった python ユーザーの備忘録

# はじめに

ソフトウェア関連が主な研究や仕事で使う状況で，またにVHDLで開発をしないと，，という場合で，一度は一通り学習した経験がある人向けです．FPGAの専門家ではないので，間違いなど遠慮なくご指摘ください．

## エディタ

python ユーザーであれば，sublime text で VHDL が読めたらよいな，，と思うかもしれませんが，できるようです．

- [FPGAの部屋 : Sublime Text3 エディタ](https://marsee101.blog.fc2.com/blog-entry-2635.html)

の通りにやったら私の環境(Mac Mojave)では動きました．

## 基本事項のおさらい

- [VHDL入門編](https://www.macnica.co.jp/business/semiconductor/articles/pdf/VHDL_Trial_Text_r1__1.pdf)
- [VHDL実践講座](https://www.ed.tus.ac.jp/~jte401/files/VHDL_2006.pdf)

## VHDLのシミュレータ

手前味噌ですが，

- [GHDLとgtkwaveを用いたVHDL開発環境とMacとWindows10で構築してみた](https://qiita.com/yamadasuzaku/items/69509547e605a2115c25) 

にまとめてみました．他にも，Vivado や ModelSim などもあります．

### 演算の基本

- 論理演算子(andやorなど) は bit のみ使用可
- 算術演算子 (+,-,*,/など)は integer のみで使用可

異なるデータタイプで算術演算や論理演算を行う場合は注意が必要．例えば，トリガーをかけたい場合は，パルス信号に引き算を行い，その正負で判断するわけだが，それは算術演算で行う．AD変換後の生データは整数値では入ってこないはずで，どこかで整数に変換する必要があり，conv_integerやto_integerが必要になる．

というのが昔の一般論で，今では，std_logic_vector に対して + などをダイレクトに使うが，いくつか注意事項があるので，細かいことを下記などを参照されたい．

- [std_logic_vector型と整数型の変換](http://bfin.sakura.ne.jp/?p=701) 
- [加算演算子の使用法](http://arch.cs.kumamoto-u.ac.jp/project/kite/3days/dic/VHDL/add_op.html)
- [HongoWiki VHDLで加算(和)をとるときに注意すること](http://ytkyk.info/wiki/vhdl/VHDLでベクター同士の加算をするときに注意すること.html)
- [細かい説明(英語)](http://userweb.eng.gla.ac.uk/scott.roy/DCD3/05_Arithmetic.pdf)

### bit_vector と  std_logic_vector の違い

- [VHLDの『bit_vector』と『std_logic_vector』](https://ameblo.jp/middleisland88/entry-11057770764.html)

 にある通り．bit_vectorは「０」と「１」しか使えないのに対して，std_logic_vectorは「０」「１」以外に [X,L,H,W,Z,U,-] を指定でき，それぞれ [ストロング、弱L、弱H、不定値、ハイインピーダンス、初期化しない、ドントケア項]である．conv_integer は，bit_vector には使えない．

整数に変換する conv_integer という関数を使う場合は，
　 use ieee.std_logic_unsigned.all;
　 use ieee.std_logic_signed.all;
のどちらを冒頭に記述するかで，符号付きか符号なしの conv_integer として働くかが変化することに注意が必要．

- [HongoWiki VHDLコード内で符号付きと符号無しのベクトルを同時に扱いたい](http://ytkyk.info/wiki/vhdl/VHDLコード内で符号付きと符号無しのベクトルを同時に扱いたい.html)

の湯浅君の記事がよくまとまってます．

conv_integer は今は，

- [VHDL で STD_LOGIC_VECTOR を整数に変換する方法](https://japan.xilinx.com/support/answers/45213.html)

にあるように，to_integer を使うようです．

### integer 

| | integer | std_logic_vector |
|---|---|---|
|初期化| <=0 | <=(others=>'0') | 
|値| 整数値 |  "＂で囲んだバイナリ列 | 
|定義方法| integer range 0 to WIDTH-1 | std_logic_vector(WIDTH-1 downto 0) | 

### Process文って何だっけ．

process 文は，「順次処理文」と呼ばれ，受けから順に実行され，同時処理文と対をなす概念である．だけなら，わかりやすいのであるが，「上から順に実行」が misleading というか，何とも誤解を招きやすい日本語に思われる．「上から順に実行」されるのは，「処理する手順」であって，信号が変化するタイミングではない，のである．

信号が変化するタイミング？とは，ここで２種類あるのがまたややこしい，，

``` vhdl:onlysignal
process(A, B, C, D)
begin
 D <= A; 
 X <= B + D;
 D <= C; 
 Y <= B + D; 
end process;
```
の場合は，
X<= B + C;
Y<= B + C;
となる．
process文は上から順に評価されて最後（end process）に同時に値が代入されるため，D<=Aの後に，D<=Cが実行されるので，D<=Cを最終結果としてX<=B+D, Y<=B+Dが実行される．

一方，

``` vhdl:withvariable
process(A, B, C)
begin
 D := A; -- 信号Aを変数Dに代入
 X <= B + D;
 D := C; -- 信号Cを変数Dに代入
 Y <= B + D; 
end process;
```

の場合は，
X <= B + A
X <= B + C 
となる．これは，DにAが代入されてから，それがソフトウェア的なイメージで次のX<=B+Dに使われて，その次の，D:=Cで，DにCが代入されて，Y<=B+Dが実行される，というイメージである．
[オブジェクトクラス]
(http://arch.cs.kumamoto-u.ac.jp/project/kite/3days/dic/VHDL/object.html)，[process文内のsignalとvariable](https://ameblo.jp/middleisland88/entry-11090340548.html)を参照のこと．

直感的には，上から代入されていく variable の方が，順次処理というイメージに近い気がする．信号の割り当て(<=)は，process文の中では，センシティビティーリストのイベント発生によって同時に代入される．そのため，process文は「順次処理」，process文以外は「同時処理」と理解していると，あれどっちだっけ？と思ってしまう．


### 連結


### VHDLのデータの型，配列，型変換

- [データ型の使い方と型変換](http://www.picfun.com/vhdl36.html)

がたいへんよくまとめってるので，これを読みましょう．
VHDLでの配列は，

``` vhdl:
type　データ型名　is　array　範囲　of　元の型名；
```

で定義できる．例えば，std_logic_vector　の型定義はIEEEのstd_logic_1164の中で，

``` vhdl:
type　std_logic_vector　is　array　(Natural range <>)　of　std_logic；
```

と定義されている．Natural range <> の "<>" は要素数が不定ですよ，という意味です．

例えば，自分で type を宣言して，変数を宣言する場合は，

``` VHDL:
   type BIT_VECTOR is array(INTEGER range <>) of BIT;
   variable MY_VECTOR : BIT_VECTOR(5 downto -5);
```

となる．この配列の長さ，先頭，最後などを得るには，下記のような書き方をすればよく，上記の MY_VECTOR : BIT_VECTOR(5 downto -5) の場合には，

| Attribute Expression | Value |
|---|---|
| MY_VECTOR'left | 5 |
| MY_VECTOR'right | -5 |
| MY_VECTOR'high | 5 |
| MY_VECTOR'low | 5 |
| MY_VECTOR'length | 11 |
| MY_VECTOR'range | (5 down to -5) |
| MY_VECTOR'reverse_range | (-5 to 5) |

となる．詳細は，[Array Types](http://www.yangchangwoo.com/podongii_X2/html/technote/TOOL/MANUAL/15i_doc/fndtn/vhd/vhd4_4.htm) を参照されたい．

## VHDLでの定数

初期化が必要で，初期値の代入は ":=" で行う．

``` vhdl:
constant　const　：　std_logic　：=　"0";
```

### component の使用方法

component文が何?って場合は，VHDLで下位ブロックを呼び出す方法は２種類あって，下記がとてもわかりやすいので，まずは一読ください．

- [VHDLで下位ブロックを呼び出す2つの方法](https://qiita.com/ikwzm/items/21bbcfe21f97cbd29b8a)

要するに，一つは，library名.entity名で指定して呼び出す方法と，もう一つが component 文を使って呼び出す方法で，どっちを使っても良いが，一長一短ある．

component文はモジュールのパラメータや入出力を書き下すため，モジュールの実体(entity文)とcomponent文で呼び出す際の２箇所で全く同じインターフェースが記述されるため，同じものは２回かかない，というソフトウェアの書き方に反して，２重管理となってしまう．(逆に言えば，冗長な分だけ，依存関係は vivado が勝手に階層構造を解釈してくれるので便利，ということかな
component 文は，別ファイルに書かれたモジュールを呼び出すもので，pythonでimportして呼び出す感覚に近いかもしれない．)


### generic の使用方法

[generic](http://vhdl.renerta.com/source/vhd00034.htm) は，component 文でインターフェースの定義を書き下したあとで，architecture と begin の中に，

``` vhdl:
  component ad_iobuf
  generic (
    DATA_WIDTH : Integer : 16);
  );
  port (
    dio_t : in    std_logic_vector(DATA_WIDTH-1 downto 0);
    dio_i : in    std_logic_vector(DATA_WIDTH-1 downto 0);
    dio_o : out   std_logic_vector(DATA_WIDTH-1 downto 0);
    dio_p : inout std_logic_vector(DATA_WIDTH-1 downto 0)
  );
  end component;
```

のように記載する．これは，python で言うところの，def ad_iobuf(data_width=16) のように，初期値を与えて定義された関数，の状態である．つまり，component 宣言はオブジェクトや関数の定義と同じなので，一回だけでよい．

次に，component ad_iobuf で定義された ad_iobuf がどう使われるのかを見てみよう．architecture と begin の end の中で，"i_iobuf : ad_iobuf" で ad_iobuf というコンポーネントを呼び出し，DATA_WIDTH を 9 として ad_iobuf のインスタンスとして i_iobuf を生成する．もう一つ，DATA_WIDTH を 17 として i_iobuf_bd を生成する．つまり，名前 : コンポーネント名，で実態を生成できる．

``` vhdl:
  i_iobuf : ad_iobuf
  generic map(
    DATA_WIDTH => 9
  ) port map (
    dio_t => dio_sig_t,
    dio_i => dio_sig_i,
    dio_o => dio_sig_o,
    dio_p => dio_sig_p
  );

  
  i_iobuf_bd : ad_iobuf
    generic map(
      DATA_WIDTH => 17
      )
    port map (
      dio_t => gpio_t(16 downto 0),
      dio_i => gpio_o(16 downto 0),
      dio_o => gpio_i(16 downto 0),
      dio_p => gpio_bd
      );
```


上記を雰囲気を python で捉えるならば，ad_iobuf をクラスを思って，

``` python:
i_iobuf = ad_iobuf(dio_sig_t, dio_sig_i, dio_sig_o, dio_sig_p, DATA_WIDTH=9)
i_iobuf_bd = ad_iobuf(gpio_t(16 downto 0), gpio_o(16 downto 0),  gpio_i(16 downto 0), gpio_bd, DATA_WIDTH=9) 
```

という感じ．


### record タイプ

C言語でいう構造体で，複数の変数が一体となってしか登場しないものをまとめておくと，コードの量もへり，可読性もよくなるようである．

- [Records - VHDL Example](https://www.nandland.com/vhdl/examples/example-record.html)
- [VHDL TIPS 「レコードタイプの使用法」](http://blog.pld.jp/archives/50972867.html)

など参照．

使い方は，

``` vhdl:
library ieee;
use ieee.std_logic_1164.all;
 
package example_record_pkg is
 
  -- Outputs from the FIFO.
  type t_FROM_FIFO is record
    wr_full  : std_logic;                -- FIFO Full Flag
    rd_empty : std_logic;                -- FIFO Empty Flag
    rd_dv    : std_logic;
    rd_data  : std_logic_vector(7 downto 0);
  end record t_FROM_FIFO;  
 
  -- Inputs to the FIFO.
  type t_TO_FIFO is record
    wr_en    : std_logic;
    wr_data  : std_logic_vector(7 downto 0);
    rd_en    : std_logic;
  end record t_TO_FIFO;
 
  constant c_FROM_FIFO_INIT : t_FROM_FIFO := (wr_full => '0',
                                              rd_empty => '1',
                                              rd_dv => '0',
                                              rd_data => (others => '0'));
 
  constant c_TO_FIFO_INIT : t_TO_FIFO := (wr_en => '0',
                                          wr_data => (others => '0'),
                                          rd_en => '0');
   
   
end package example_record_pkg;
```

などである．

### package 文

C言語でいうところの，定義の書かれたヘッダーファイル，という感じ．

- [Package File - VHDL Example](https://www.nandland.com/vhdl/examples/example-package.html)

``` VHDL:
-- Package Declaration Section
package example_package is
 
  constant c_PIXELS : integer := 65536;
 
  type t_FROM_FIFO is record
    wr_full  : std_logic;
    rd_empty : std_logic;
  end record t_FROM_FIFO;  
   
  component example_component is
    port (
      i_data  : in  std_logic;
      o_rsult : out std_logic); 
  end component example_component;
 
  function Bitwise_AND (
    i_vector : in std_logic_vector(3 downto 0))
    return std_logic;
   
end package example_package;
 
-- Package Body Section
package body example_package is
 
  function Bitwise_AND (
    i_vector : in std_logic_vector(3 downto 0)
    )
    return std_logic is
  begin
    return (i_vector (0) and i_vector (1) and i_vector (2) and i_vector (3));
  end;
 
end package body example_package;
```

ただし，[VHDLでのpackageの使用方法](https://marsee101.blog.fc2.com/blog-entry-1060.html) を見る限り，ヘッダーファイルの依存関係でハマりそうなので，複雑な入れ子関係などは避けた方が無難なようである．