MAX10 FPGAとポケコンPC-G850VSとでUART通信

#1. 概要
三好 健文, 高前田 伸也,『Interface (インターフェース)』, 2009年09月号, CQ出版, pp.116-118に紹介されているUARTモジュールを試す。[『FPGA/PLD入門記事全集』, 2014年, CQ出版](https://shop.cqpub.co.jp/hanbai/books/45/45671.html)にも収録されている。 

負論理にできるようにしたこと、読み取り完了信号`rd`のタイミングを少し変えたこと以外はテキストのままである。

#2. ポケコンPC-G850VS側の準備
[ここ](http://ver0.sakura.ne.jp/g800/index.html#g850term)にあるターミナルソフトウェアをインストールする。今回はg850vterm.ihxをインストールした。インストールできたら、ターミナルを起動し、\[BASIC\]ボタンを押して設定する。

#3. MAX10 FPGA側の準備
下の4つのVHDLをコンパイルして書き込む。    

<details><summary>`トップレベルエンティティを見る/隠す`</summary><div>

```UART.vhd
library ieee;
use ieee.std_logic_1164.all;

entity UART is
	generic(
		F_CLK  : positive  := 48_000_000; -- 評価ボードに載っているクロックをそのまま使う。
		BAUD   : positive  := 9600;
		POL_INV: std_logic := '1' -- 0: 正論理, 1: 負論理
	);
	port(
		clk   : in std_logic;
		aclr_n: in std_logic;

		rx: in  std_logic;
		tx: out std_logic;
		
		rx_data: out std_logic_vector(7 downto 0); -- 受信したデータ
		tx_data: in  std_logic_vector(7 downto 0); -- 送信するデータ
		
		rd: out std_logic; -- 受信完了信号
		wr: in  std_logic; -- 送信命令
		
		tx_ready: out std_logic -- 送信可能か
	);
end entity;

architecture rtl of UART is
begin
	module_rx: entity work.serial_rx
	generic map(
		F_CLK   => F_CLK,
		BAUD    => BAUD,
		POL_INV => POL_INV
	)
	port map(
		clk    => clk,
		aclr_n => aclr_n,
		rd     => rd,
		din    => rx,
		dout   => rx_data
	);

	module_tx: entity work.serial_tx
	generic map(
		F_CLK   => F_CLK,
		BAUD    => BAUD,
		POL_INV => POL_INV
	)
	port map(
		clk    => clk,
		aclr_n => aclr_n,
		wr     => wr,
		din    => tx_data,
		dout   => tx,
		ready  => tx_ready
	);
end architecture;
```
</div></details>

<details><summary>`シリアル通信用クロック生成器を見る/隠す`</summary><div>

```clk_gen.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity clk_gen is
	generic(
		F_CLK   : positive := 48_000_000;
		OUT_FREQ: positive := 9600
		);
	port(
		aclr_n : in  std_logic;
		clk    : in  std_logic;
		clk_out: out std_logic
	);
end entity;

architecture rtl of clk_gen is
begin

	process(aclr_n, clk)
		constant TOP_VAL: natural                    := F_CLK/(OUT_FREQ * 2) - 1;
		variable count  : natural range 0 to TOP_VAL := 0;
	begin
		if aclr_n = '0' then
			count := 0;
		elsif rising_edge(clk) then
			if count >= TOP_VAL then
				clk_out <= not clk_out;
				count := 0;
			else
				count := count + 1;
			end if;
		end if;
	end process;

end architecture;
```
</div></details>

<details><summary>`TXモジュールを見る/隠す`</summary><div>

```serial_tx.vhd
-- 参考: Interface (インターフェース), 2009年09月号, pp.116-118, CQ出版

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity serial_tx is
	generic(
		F_CLK  : positive  := 48_000_000;
		BAUD   : positive  := 9600;
		POL_INV: std_logic := '0' -- 0: 正論理; 1: 負論理	
	);
	port(
		clk   : in std_logic;
		aclr_n: in std_logic;
		wr    : in std_logic;
		din   : in std_logic_vector(7 downto 0);

		dout : out std_logic;
		ready: out std_logic
	);
end entity;

architecture rtl of serial_tx is
	signal in_din: std_logic_vector(7 downto 0);
	signal buf   : std_logic_vector(7 downto 0);

	signal tx_en: std_logic;
	signal load : std_logic;
	signal run  : std_logic;

	signal cbit : natural range 0 to 7;
	signal state: natural range 0 to 2;
	
	signal dout_temp: std_logic;

begin
	module_clk_gen: entity work.clk_gen
	generic map(
		F_CLK    => F_CLK,
		OUT_FREQ => BAUD
	)
	port map(
		aclr_n  => aclr_n,
		clk     => clk,
		clk_out => tx_en
	);
	
	ready <= '1' when run = '0' and load = '0' else '0';
	
	dout <= dout_temp when POL_INV = '0' else not dout_temp;
	
	process(clk, aclr_n)
	begin
		if aclr_n = '0' then
			load <= '0';
		elsif rising_edge(clk) then
			if wr = '1' and run = '0' then 
				load   <= '1';
				in_din <= din;
			end if;
			if load = '1' and run = '1' then
				load <= '0';
			end if;
		end if;
	end process;
	
	process(tx_en, aclr_n)
	begin
		if aclr_n = '0' then
			dout_temp <= '1';
			cbit      <=  0 ;
			state     <=  0 ;
			run       <= '0';
		elsif rising_edge(tx_en) then
			case state is
				when 0 =>
					cbit <= 0;
					if load then
						dout_temp <= '0'; --スタートビット
						state <= state + 1;
						buf   <= in_din;
						run   <= '1';
					else
						dout_temp <= '1';
						run <= '0';
					end if;
				when 1 =>
					if cbit <= 6 then
						dout_temp <= buf(cbit); -- lsbから順番に送信する。
						cbit      <= cbit + 1;
					elsif cbit = 7 then
						dout_temp <= buf(cbit);
						state     <= state + 1;
					end if;
				when 2 =>
					dout_temp <= '1'; --ストップビット
					state     <=  0 ;
				when others =>
					state <= 0;
			end case;
		end if;
	end process;
	
end architecture;
```
</div></details>

<details><summary>`RXモジュールを見る/隠す`</summary><div>

```serial_rx.vhd
-- 参考: Interface (インターフェース), 2009年09月号, pp.116-118, CQ出版

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity serial_rx is
	generic(
		F_CLK  : positive  := 48_000_000;
		BAUD   : positive  := 9600;
		POL_INV: std_logic := '0' -- 0: 正論理; 1: 負論理	
	);
	port(
		clk   : in std_logic;
		aclr_n: in std_logic;
		din   : in std_logic;

		rd  : out std_logic;
		dout: out std_logic_vector(7 downto 0)		
	);
end entity;

architecture rtl of serial_rx is
	signal buf  : std_logic_vector(7 downto 0);
	signal start: std_logic;
	signal cbit : natural range 0 to 159;
	signal rx_en: std_logic;
	
	signal din_temp: std_logic;
begin
	module_clk_gen: entity work.clk_gen
	generic map(
		F_CLK    => F_CLK,
		OUT_FREQ => BAUD * 16
	)
	port map(
		aclr_n  => aclr_n,
		clk     => clk,
		clk_out => rx_en
	);


	din_temp <= din when POL_INV = '0' else not din;

	process(aclr_n, rx_en)
	begin
		if aclr_n = '0' then
			start <= '0';
			cbit  <=  0 ;
			buf   <= (others => '0');
			dout  <= (others => '0');
		elsif rising_edge(rx_en) then
			if start = '0' then
				rd <= '0';
				if din_temp = '0' then -- RXラインがHからLに変化して、
					start <= '1';
				end if;
				cbit <= 0;
			else
				case cbit is
					when 6 => -- それが本当にスタートビットであったら、
						if din_temp = '1' then
							cbit  <=  0 ;
							start <= '0';
						else
							cbit <= cbit + 1;
						end if;
					when 22|38|54|70|86|102|118|134 => -- 16クロックごとに、
						buf  <= din_temp & buf(7 downto 1); -- lsbから順番に読み取って、
						cbit <= cbit + 1;
					when 135 =>
						dout  <= buf; -- 全部読み取り終えたらすぐにデータを確保して、
						cbit <= cbit + 1;
					when 159 => -- ストップビットの末尾で、
						cbit  <=  0 ;
						start <= '0';
						rd    <= '1'; -- 受信完了信号を出す。
					when others =>
						cbit <= cbit + 1;
				end case;
			end if;
		end if;
	end process;
end architecture;
```
</div></details>


#4. 実際に動かす

##4.1 全体の接続図
ポケコンから受信した1文字をそっくりそのまま送り返す。ここでは送受両ラインとも[このモジュール](https://www.switch-science.com/catalog/1523/)でレベル変換したが、ポケコン\(RX\) <- MAX10 \(TX\)のラインはレベル変換しなくてもよい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/a1b8b65c-40d4-ee79-a606-f5a933c75687.png)


##4.2 実行結果
ターミナルのエコーをオンにしているので同じ文字が2回ずつ表示される。
<iframe width="560" height="315" src="https://www.youtube.com/embed/RM-dhGnc8eE" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

##4.3 ロジアナによる観測結果
今、受信した`'a'`をそのまま送り返したところ。通信ラインは負論理である。   
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193936/8484c1a3-cf0d-5a57-db9d-a7055b96fae9.png)


#5. ファイル一式    
[https://github.com/ti-nspire/VHDL_for_Quartus_Prime/tree/main/UART_ver_2](https://github.com/ti-nspire/VHDL_for_Quartus_Prime/tree/main/UART_ver_2)