エンコーダー

エンコーダー

 電子回路の中でエンコーダーと呼ばれるものは、多くの種類があって、どんな場面で「エンコーダ」と言っているのか。という前提がなくては、エンコーダがどのような物かはわかりません。それぐらい、エンコーダーという言葉は、一つの物や事象を示す言葉ではなくなっています。

 ここでのエンコーダはFPGAの内部回路として、人間がわかりやすい値を機械(電子回路)がわかりやすい値に変換することを言います。日本語で記述すると「符号器」と言えます。そして、本稿では、エンコーダの設計をVHDLで実際に設計してみます。

 VHDLで設計するにあたり、正解というのは、動作してみて期待通りの動作が確認できれば正解です。なので、どのように設計しても自由なので、今回は2通りの方法で設計してみます。

 まずは、同期設計の上で一般的と思われる方法で、processの中でエンコードの分岐を記述する方法です。

 ソースコードは以下のようになります。

--  TITLE:					"encoder.vhd"
--  MODULE NAME:			
--  PROJECT CODE:			
--  AUTHOR:					 (****@nakaharagiken.com)
--  CREATION DATE:			2020.11.15
--  SOURCE:            		
--  LICENSE:           		Copyright (c) 2020 nakaharagiken All rights reserved. 
--  DESCRIPTION:            エンコーダ
--  NOTES TO USER:			
--  SOURCE CONTROL:			
--  REVISION HISTORY:  	    v0.1
--
--
library IEEE;												-- ライブラリの宣言
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
use IEEE.std_logic_arith.all;

entity encoder is
port (
	clock:				in	std_logic;						-- 入力信号:system clock
	nreset:				in	std_logic;						-- 入力信号:asynchronous reset 				____reset___|~~~normal~~~
	encin:				in	std_logic_vector(7 downto 0);	-- 入力信号
	encout:				out	std_logic_vector(3 downto 0)	-- 出力信号
);
end entity encoder;

architecture rtl of encoder is

---------------------------------------------------------------
-- SIGNAL宣言
---------------------------------------------------------------
signal	rDFF:			std_logic_vector(3 downto 0);

begin

------------------------------------------
--
------------------------------------------
process (clock,nreset) begin
	if nreset = '0' then
		rDFF <= (others => '0');
	elsif clock' event and clock = '1' then
		case encin is
			when	"00000001"	=>	rDFF	<= X"0";
			when	"00000010"	=>	rDFF	<= X"1";
			when	"00000100"	=>	rDFF	<= X"2";
			when	"00001000"	=>	rDFF	<= X"3";
			when	"00010000"	=>	rDFF	<= X"4";
			when	"00100000"	=>	rDFF	<= X"5";
			when	"01000000"	=>	rDFF	<= X"6";
			when	"10000000"	=>	rDFF	<= X"7";
			when others			=>	rDFF	<= X"0";
		end case;
	end if;
end process;

encout <= rDFF;

end architecture rtl;

 process文は40行目からです。encinの値によってcase文で分岐しています。このように分岐をprocess文の中で行ってしまう。という記述は一般的と言えます。最初はわかりにくいかも知れませんが、慣れてくるとこの方が同期設計のタイミングが取りやすくなります。

 process文は順序回路になりますが、順序回路の中でこのように多い分岐を記述する時にはcase文を使うことが多いです。if文でも同じことができますが、3分岐以上になるとcase文を使うようになります。

 もう一つの方法として、ワイヤーで一旦条件分岐をまとめてから、DFFに入力する。という方法を示します。

--  TITLE:					"encoderA.vhd"
--  MODULE NAME:			
--  PROJECT CODE:			
--  AUTHOR:					 (****@nakaharagiken.com)
--  CREATION DATE:			2020.11.15
--  SOURCE:            		
--  LICENSE:           		Copyright (c) 2020 nakaharagiken All rights reserved. 
--  DESCRIPTION:            エンコーダ
--  NOTES TO USER:			
--  SOURCE CONTROL:			
--  REVISION HISTORY:  	    v0.1	
--
--
library IEEE;												-- ライブラリの宣言
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
use IEEE.std_logic_arith.all;

entity encoderA is
port (
	clock:				in	std_logic;						-- 入力信号:system clock
	nreset:				in	std_logic;						-- 入力信号:asynchronous reset 				____reset___|~~~normal~~~
	encin:				in	std_logic_vector(7 downto 0);	-- 入力信号
	encout:				out	std_logic_vector(3 downto 0)	-- 出力信号
);
end entity encoderA;

architecture rtl of encoderA is

---------------------------------------------------------------
-- SIGNAL宣言
---------------------------------------------------------------
signal	rDFF:			std_logic_vector(3 downto 0);
signal	wDFF:			std_logic_vector(3 downto 0);

begin

wDFF <= 	X"0" when (encin = "00000001") else
			X"1" when (encin = "00000010") else
			X"2" when (encin = "00000100") else
			X"3" when (encin = "00001000") else
			X"4" when (encin = "00010000") else
			X"5" when (encin = "00100000") else
			X"6" when (encin = "01000000") else
			X"7" when (encin = "10000000") else
			X"0";

------------------------------------------
--
------------------------------------------
process (clock,nreset) begin
	if nreset = '0' then
		rDFF <= (others => '0');
	elsif clock' event and clock = '1' then
		rDFF <= wDFF;
	end if;
end process;

encout <= rDFF;

end architecture rtl;

 結果はどちらも同じです。51行目のprocess文はこちらの方が簡単になっています。条件分岐は38行目のようにwhenで記述しています。process文を使わない、組み合わせ回路での条件分岐はこのようにwhen文を使うことが多いです。

 2通りの方法でどちらが正解という事はなく、どちらも正解です。個人の好みにもよるでしょう。しかしながら、process文の中で条件分岐する方をぜひ身に着けてください。これは、ソースコードの見通しにも関係してきますが、ソースを眺めた時に、作者が何を同期設計したかったのかが、ソースコードから読み取りやすいのがprocess文の中での条件分岐です。

 エンコーダそのものの解説をします。上記2つのソースコードを眺めると同じようにバイナリのコードをHex値に変換しているだけ。という事に気がつくと思います。エンコーダというのは、何かの入力があって、それをある規則に従って変換する回路です。

 ですから、一見難しそうなエンコーダである、QPSKのエンコーダなどでも、ある値を別のコード体系に変換する。という意味では同じです。

 エンコーダを検索して、このページに来た方は、エンコーダの正解が書いてない。と思われると思います。エンコーダはあまりに多岐にわたっているので、どうすれば正解というのが記載できません。

 ただし、言えるのはwhen文やcase文で値を変換すれば良い。という事はどれも同じです。かなり複雑なエンコーダでもcase文で分岐できないような数にはならないと思います。ですから、面倒がらずに一つ一つcase文で書いてみてください。

 複雑なエンコーダになればなるほど、組み合わせ回路部分が複雑になりますので、遅延の予想が立てにくくなります。したがって、process文で答えをラッチするようにします。

 本ページのソースコードでは、イネーブル信号を使わないで、クロック同期としましたが、イネーブル信号をporocess文に入れて、clock同期設計できるようになれば、初心者は脱したといえると思います。

 最後にenable信号を挿入したprocess文を掲載しておきます。

------------------------------------------
--
------------------------------------------
process (clock,nreset) begin
	if nreset = '0' then
		rDFF <= (others => '0');
	elsif clock' event and clock = '1' then
		if (enable = '1') then
			case encin is
				when	"00000001"	=>	rDFF	<= X"0";
				when	"00000010"	=>	rDFF	<= X"1";
				when	"00000100"	=>	rDFF	<= X"2";
				when	"00001000"	=>	rDFF	<= X"3";
				when	"00010000"	=>	rDFF	<= X"4";
				when	"00100000"	=>	rDFF	<= X"5";
				when	"01000000"	=>	rDFF	<= X"6";
				when	"10000000"	=>	rDFF	<= X"7";
				when others			=>	rDFF	<= X"0";
			end case;
		end if;
	end if;
end process;