VHDLのtype宣言を利用してモジュール間接続をスッキリ書く

# 概要
VHDLにはtype宣言というものがあり，ユーザー定義型を作ることが可能です．C言語で言えば構造体的なものです．

バスではないのでvectorにはできないけど，いつもペアになっている信号が何系統も存在する．．．

そんな時にtype宣言を利用することで，モジュール間の信号接続をかなりスッキリと書くことが可能なので紹介します．

「VHDLのType宣言を使うとモジュール間の接続がスッキリ書けるよ！」といことが主題なので，クロック，リセット等は省略して説明します．

# お題
４系統のモーター制御を例にしたいと思います．

- １つの系統における入出力
　- エンコーダからの入力信号は, `A相,B相`が常にペア
　- モータードライバへの出力は, `パルス/方向`が常にペア


![top.dio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/c2c4cb8c-cf61-82ba-2e83-b4b394b0c6b4.png)

# 普通に書いたVHDL
今回のお題のようなケースではstd_logic_vevtorを使って書くのも違和感があると思いますので，こんな感じにstd_logicで地道に書いたソースになるかと思います．


## TOPモジュール
TOPモジュールのソースです，地道に信号を宣言しているのでモジュールインスタンスもgenerate文でループして作りづらいのでベタにインスタンスを記述しています．


```vhdl:FPGA_TOP.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity FPGA_TOP is
	Port(
	i_ENC1_A	:in  std_logic;--1系のA相
	i_ENC1_B	:in  std_logic;--1系のB相
	i_ENC2_A	:in  std_logic;--2系のA相
	i_ENC2_B	:in  std_logic;--2系のB相
	i_ENC3_A	:in  std_logic;--3系のA相
	i_ENC3_B	:in  std_logic;--3系のB相
	i_ENC4_A	:in  std_logic;--4系のA相
	i_ENC4_B	:in  std_logic;--4系のB相
	
	o_MOT1_STP	:out  std_logic; --1系のパルス
	o_MOT1_DIR	:out  std_logic; --1系の方向
	o_MOT2_STP	:out  std_logic; --2系のパルス
	o_MOT2_DIR	:out  std_logic; --2系の方向
	o_MOT3_STP	:out  std_logic; --3系のパルス
	o_MOT3_DIR	:out  std_logic; --3系の方向
	o_MOT4_STP	:out  std_logic; --4系のパルス
	o_MOT4_DIR	:out  std_logic); --4系の方向
end entity;

architecture RTL of FPGA_TOP is
begin
	MC_inst1:entity work.MotorController
		port map
		(i_ENC_A	=>i_ENC1_A
		,i_ENC_B	=>i_ENC1_B
		,o_MOT_STP	=>o_MOT1_STP
		,o_MOT_DIR	=>o_MOT1_DIR
		);
	
	MC_inst2:entity work.MotorController
		port map
		(i_ENC_A	=>i_ENC2_A
		,i_ENC_B	=>i_ENC2_B
		,o_MOT_STP	=>o_MOT2_STP
		,o_MOT_DIR	=>o_MOT2_DIR
		);
	
	MC_inst3:entity work.MotorController
		port map
		(i_ENC_A	=>i_ENC3_A
		,i_ENC_B	=>i_ENC3_B
		,o_MOT_STP	=>o_MOT3_STP
		,o_MOT_DIR	=>o_MOT3_DIR
		);
	
	MC_inst4:entity work.MotorController
		port map
		(i_ENC_A	=>i_ENC4_A
		,i_ENC_B	=>i_ENC4_B
		,o_MOT_STP	=>o_MOT4_STP
		,o_MOT_DIR	=>o_MOT4_DIR
		);
end architecture;
```

## サブモジュール
中の論理は省略しますので，はINとOUTを繋げて論理合成だけはできるようにしておきます．
```vhdl:MotorController.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;

entity MotorController is
port( 
	i_ENC_A		:in	 std_logic;	--
	i_ENC_B		:in	 std_logic;	--
	o_MOT_STP	:out std_logic;	--
	o_MOT_DIR	:out std_logic);
end entity;

architecture RTL of MotorController is

begin
	o_MOT_STP <= i_ENC_A;
	o_MOT_DIR <= i_ENC_B;
	
end architecture;

```

## 論理合成
一応Latticeのツールで論理合成できることの確認し，合成できることを確認しました．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/0a297194-0d65-2859-26e8-323e3db7e451.png)

# type宣言を使ってスッキリさせたVHDL

では，本題のtype宣言の使い方を紹介します．

## ユーザー定義型の書き方
以下のようにpkgTYPEDEFというパッケージを作ります．（pkgTYPEDEFは任意の名称に変更可能です）
この中でエンコーダーパルスのA相とB相をまとめた`typEncoder`という型を定義します，さらに`typEncoder`型をベクターにした`typEncoder_Vector`型を定義します．

```vhdl:pkgTYPEDEF.vhd
library ieee;
use ieee.std_logic_1164.all;
use	ieee.std_logic_arith.all;

package pkgTYPEDEF is
	----------------------------------------------
	--ユーザー定義型
	----------------------------------------------
	--エンコーダパルス信号
	type typEncoder is record
		A		:std_logic	;--A相
		B		:std_logic	;--B相
	end record;

	--モーター制御信号
	type typMotor is record
		STP		:std_logic	;--ステップ信号
		DIR		:std_logic	;--方向信号
	end record;
	
	----------------------------------------------
	-- ユーザー定義型をベクターにした型の定義
	----------------------------------------------
	type typEncoder_Vector
		is array(integer range <>) of typEncoder;
	
	type typMotor_Vector
		is array(integer range <>) of typMotor;

end package;

```

## TOPモジュール
これが，ユーザー定義型を使った場合のTOPモジュールのソースです．
かなり記述行が圧縮されたと思いませんかっ！！
vectorにしているのでgenerate文も適用しやすくなります．

```vhdl:FPGA_TOP.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
use work.pkgTYPEDEF.all;

entity FPGA_TOP is
	Port(
	i_ENCODER	:in  typEncoder_Vector(0 to 3);	--エンコーダ信号（A相，B相）
	o_MOTOR		:out typMotor_Vector(0 to 3)); --モーター制御信号（パルス,方向）
end entity;

architecture RTL of FPGA_TOP is
begin
	GEN_MC:for i in 0 to 3 generate	
		MC_inst:entity work.MotorController
			port map
			(i_ENCODER	=>i_ENCODER(i)
			,o_MOTOR	=>o_MOTOR(i)
			);
	end generate;
end architecture;
```
## サブモジュール
一応サブモジュール
```vhdl:MotorController.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
use work.pkgTYPEDEF.all;

entity MotorController is
port( 
	i_ENCODER	:in	 typEncoder;	--
	o_MOTOR	:out typMotor);
end entity;

architecture RTL of MotorController is

begin
	o_MOTOR.STP <= i_ENCODER.A;
	o_MOTOR.DIR <= i_ENCODER.B;
	
end architecture;
```

## 論理合成
論理合成の結果です．
ソースは短縮しましたが，等価な回路に論理合成できています！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/70d0bc28-c555-af6e-4ab7-ac78b4aa57a3.png)
ベクターのインデックスが先に来て，ドットで区切ってユーザー定義型のメンバが続いています．

# 追記
Xilinxはドットがアンダーバーに変わったような，そうでなかったような・・・
細かくは覚えてませんが，今回紹介したような書き方でXilinxのデバイスも使っていたので問題なく合成できるはずです．

シミュレーションでは，QuestaとModelsimでは問題なく動いていました．
typeでまとめた信号はグルーピングされて折り畳み/展開が出来ますので，信号波形を並べる際にも見やすいです．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/f20729f3-da5b-3966-add9-abad8b33a7b7.png)

他にも，画像処理系で「データ，イネーブル，ラインスタート，ラインエンド」のように常に一緒にある信号をグルーピングしたときは，かなりスッキリとした記述ができました．