目次

シリアル送信

 プロトタイピングに利用しているXilinxのFPGA基板には
 レガシータイプのシリアルインタフェースがあります。



 マイクロコンピュータあるいはパーソナルコンピュータと
 FPGAを接続するには、シリアルインタフェースがまだまだ
 現役です。

 はじめて、FPGAとパーソナルコンピュータを接続すること
 挑戦したときは、冗長なVHDLコードを作成しました。

 自分の理解できる範囲で、初学者でも少しの努力で内容を
 把握できるVHDLコードを書くことに挑戦してみます。

 通信のためには、プロトコルを決めなければならないので
 次の仕様としました。

 自分の十八番であるシーケンサで、シリアル送信処理を
 記述します。

 次の手順でシリアル送信のVHDLコードを作成しました。
  1. シーケンサによる並列直列変換で10ビットデータ出力
  2. シリアル送信するデータをトグルスイッチで設定
  3. シーケンサを動かすトリガーを生成回路記述
  4. ダブルバッファ構成でデータを処理
  5. 各シーケンサ用クロック生成
 順を追って、回路を定義していきます。
並列直列変換  文字定数'A'を送信するときは、01000001にスタートビット  ストップビットを加えて1010000010の10ビットにします。  トリガーを貰ったなら、10ビットをスタートビットを出力後  LSBから8ビット、ストップビット1ビットを出力するように  シーケンサを構成すれば充分と判断しました。  状態遷移図を描いてみると、以下。  シーケンサの動作を書きます。
  1. トリガー待ち(トリガーが来たなら10ビットデータをコピー)
  2. スタートビット送信
  3. LSB送信
  4. 2^1ビット送信
  5. 2^2ビット送信
  6. 2^3ビット送信
  7. 2^4ビット送信
  8. 2^5ビット送信
  9. 2^6ビット送信
  10. MSB送信
  11. ストップビット送信
  12. 1に戻る
 クロックをiCLK、トリガーをiTRGとし  ビットカウンタiTXCNTを使いシーケンサ  を記述します。 process (nRESET,iCLK) begin if ( nRESET = '0' ) then iSTATE <= 0 ; iDINXX <= (others => '1') ; elsif rising_edge( iCLK ) then case iSTATE is -- wait trigger when 0 => if ( iTRG = '1' ) then iSTATE <= 1 ; iDINXX <= iDINX ; else iSTATE <= 0 ; end if ; -- start bit when 1 => iTXCNT <= 0 ; iSTATE <= 2 ; -- 2^0 (LSB) when 2 => iTXCNT <= 1 ; iSTATE <= 3 ; -- 2^1 when 3 => iTXCNT <= 2 ; iSTATE <= 4 ; -- 2^2 when 4 => iTXCNT <= 3 ; iSTATE <= 5 ; -- 2^3 when 5 => iTXCNT <= 4 ; iSTATE <= 6 ; -- 2^4 when 6 => iTXCNT <= 5 ; iSTATE <= 7 ; -- 2^5 when 7 => iTXCNT <= 6 ; iSTATE <= 8 ; -- 2^6 when 8 => iTXCNT <= 7 ; iSTATE <= 9 ; -- 2^7 (MSB) when 9 => iTXCNT <= 8 ; iSTATE <= 10; -- stop bit when 10 => iTXCNT <= 9 ; iSTATE <= 11; -- return first state when 11 => iSTATE <= 0 ; iDINXX <= (others => '1') ; -- default when others => iSTATE <= 0 ; end case ; end if ; end process;
トリガー生成  データ設定に、トグルスイッチを  送信にはプッシュスイッチを利用  します。  プッシュスイッチは、シンクロナイザを利用  して、データ入力のトリガーを実現。 -- input trigger (invert logical level) iSEND <= not SEND ; -- debouncing with 10kHz (synchronizer) process (nRESET,iSCLK) begin if (nRESET = '0') then iSEND_SFT <= "000"; elsif rising_edge(iSCLK) then iSEND_SFT <= iSEND_SFT(1 downto 0) & iSEND ; end if; end process; iSEND_TRG <= '1' when ( iSEND_SFT = "011" or iSEND_SFT = "001" ) else '0';  このままでは、何度も送信シーケンサにトリガーを  与えてしまうので、ワンショットになるような回路  を入れます。タイミングチャートでみると以下。 iSEND_TRGから、iTRGを生成する  には、シーケンサを利用します。  ハザードが出ないように、シーケンサは  ジョンソンカウンタを使います。 process (nRESET,iSCLK) begin if ( nRESET = '0' ) then iTSTATE <= "00"; elsif rising_edge(iSCLK) then case conv_integer(iTSTATE) is -- wait trigger when 0 => if ( iSEND_TRG = '1' ) then iTSTATE <= "01" ; else iTSTATE <= "00" ; end if ; -- skip (send pulse) when 1 => iTSTATE <= "11" ; -- wait (release) when 3 => if ( iSEND_TRG = '0' ) then iTSTATE <= "10" ; else iTSTATE <= "11" ; end if ; -- return first state when 2 => iTSTATE <= "00" ; -- default when others => iTSTATE <= "00" ; end case ; end if; end process; iTRG <= '1' when ( iTSTATE = "01" ) else '0' ;  状態遷移図で描くと、次のようになります。
データバッファリング  トグルスイッチにより送信データを設定するので  PLD内部にデータバッファを用意します。  データバッファは、10ビットレジスタとして  スタート、ストップビットを加えます。  トリガーを貰ってから、10ビットデータを  生成する回路を定義します。 process (nRESET, CLOCK) begin if (nRESET = '0') then iDINX <= (others => '0'); elsif rising_edge(CLOCK) then if ( iSEND_TRG = '1' ) then iDINX(0) <= '0'; -- start bit iDINX(8 downto 1) <= (not DINX); -- data iDINX(9) <= '1'; -- stop bit end if; end if; end process; iLOUT <= iDINX(8 downto 1);  設定データが、内部レジスタに入っていることを  確認するため、基板上のLEDに8ビットデータを  表示します。 LOUT <= iLOUT;
クロックジェネレータ  利用しているFPGA基板は、50MHzクロックで  動作させるので、分周しシーケンサに必要な  クロックを生成します。  9600bpsは9.6kHzに相当するので、次の計算で  カウンタの最大値とDUTY比を50%にする値を  求めます。  9600bpsを生成する分周回路を定義します。 constant CNT_MAX : integer := 5208 ; constant CNT_HALF : integer := 2603 ; process (nRESET, CLOCK) begin if (nRESET = '0') then iCNT <= 0; iCLK <= '0' ; elsif rising_edge(CLOCK) then if ( iCNT < CNT_MAX ) then iCNT <= iCNT + 1; if ( iCNT < CNT_HALF ) then iCLK <= '1' ; else iCLK <= '0' ; end if ; else iCNT <= 0; end if; end if; end process;  スイッチからデータを入力するトリガーの  ためにシンクロナイザを使います。それに  使うクロックを10kHzにします。 constant SCNTX_MAX : integer := 4999 ; process (nRESET,CLOCK) begin if (nRESET = '0') then iSCNTX <= 0 ; elsif rising_edge(CLOCK) then if ( iSCNTX < SCNTX_MAX ) then iSCNTX <= iSCNTX + 1 ; else iSCNTX <= 0 ; end if ; end if; end process; iSCLK <= '1' when ( iSCNTX = 0 ) else '0' ;
ブロック図  シリアル送信の内部回路を  ブロック図でまとめます。
全ソースコード  ブロック図から、VHDLの最終ソースコードを作成します。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity test701a is port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- trigger SEND : in std_logic ; -- data DINX : in std_logic_vector(7 downto 0) ; -- serial data TxD : out std_logic ; -- monitor LOUT : out std_logic_vector(7 downto 0) --; ); end test701a; architecture Behavioral of test701a is -- constant values constant SCNTX_MAX : integer := 4999 ; constant CNT_MAX : integer := 5208 ; constant CNT_HALF : integer := 2603 ; -- buffer signal iDINX : std_logic_vector(9 downto 0); -- input buffer signal iDINXX : std_logic_vector(9 downto 0); -- serial buffer signal iLOUT : std_logic_vector(7 downto 0); -- LED monitor -- trigger signal iSEND : std_logic; signal iSEND_TRG : std_logic; signal iSEND_SFT : std_logic_vector(2 downto 0); signal iCNT : integer range 0 to CNT_MAX ; -- sequencer signal iCLK : std_logic ; signal iTRG : std_logic; signal iSTATE : integer range 0 to 11; signal iTXCNT : integer range 0 to 9; -- divider signal iSCLK : std_logic ; signal iSCNTX : integer range 0 to SCNTX_MAX ; -- one shot pulse controller signal iTSTATE : std_logic_vector(1 downto 0) ; begin -- output TxD <= iDINXX(iTXCNT) ; LOUT <= iLOUT; -- input trigger (invert logical level) iSEND <= not SEND ; -- clock divider (50MHz -> 10kHz) process (nRESET,CLOCK) begin if (nRESET = '0') then iSCNTX <= 0 ; elsif rising_edge(CLOCK) then if ( iSCNTX < SCNTX_MAX ) then iSCNTX <= iSCNTX + 1 ; else iSCNTX <= 0 ; end if ; end if; end process; iSCLK <= '1' when ( iSCNTX = 0 ) else '0' ; -- debouncing with 10kHz (synchronizer) process (nRESET,iSCLK) begin if (nRESET = '0') then iSEND_SFT <= "000"; elsif rising_edge(iSCLK) then iSEND_SFT <= iSEND_SFT(1 downto 0) & iSEND ; end if; end process; iSEND_TRG <= '1' when ( iSEND_SFT = "011" or iSEND_SFT = "001" ) else '0'; -- generate one shot pulse process (nRESET,iSCLK) begin if ( nRESET = '0' ) then iTSTATE <= "00"; elsif rising_edge(iSCLK) then case conv_integer(iTSTATE) is -- wait trigger when 0 => if ( iSEND_TRG = '1' ) then iTSTATE <= "01" ; else iTSTATE <= "00" ; end if ; -- skip (send pulse) when 1 => iTSTATE <= "11" ; -- wait (release) when 3 => if ( iSEND_TRG = '0' ) then iTSTATE <= "10" ; else iTSTATE <= "11" ; end if ; -- return first state when 2 => iTSTATE <= "00" ; -- default when others => iTSTATE <= "00" ; end case ; end if; end process; iTRG <= '1' when ( iTSTATE = "01" ) else '0' ; -- data buffering process (nRESET, CLOCK) begin if (nRESET = '0') then iDINX <= (others => '0'); elsif rising_edge(CLOCK) then if ( iSEND_TRG = '1' ) then iDINX(0) <= '0'; -- start bit iDINX(8 downto 1) <= (not DINX); -- data iDINX(9) <= '1'; -- stop bit end if; end if; end process; iLOUT <= iDINX(8 downto 1); -- generate 9600bps clock process (nRESET, CLOCK) begin if (nRESET = '0') then iCNT <= 0; iCLK <= '0' ; elsif rising_edge(CLOCK) then if ( iCNT < CNT_MAX ) then iCNT <= iCNT + 1; if ( iCNT < CNT_HALF ) then iCLK <= '1' ; else iCLK <= '0' ; end if ; else iCNT <= 0; end if; end if; end process; -- TxD data send sequencer process (nRESET,iCLK) begin if ( nRESET = '0' ) then iSTATE <= 0 ; iDINXX <= (others => '1') ; elsif rising_edge( iCLK ) then case iSTATE is -- wait trigger when 0 => if ( iTRG = '1' ) then iSTATE <= 1 ; iDINXX <= iDINX ; else iSTATE <= 0 ; end if ; -- start bit when 1 => iTXCNT <= 0 ; iSTATE <= 2 ; -- 2^0 (LSB) when 2 => iTXCNT <= 1 ; iSTATE <= 3 ; -- 2^1 when 3 => iTXCNT <= 2 ; iSTATE <= 4 ; -- 2^2 when 4 => iTXCNT <= 3 ; iSTATE <= 5 ; -- 2^3 when 5 => iTXCNT <= 4 ; iSTATE <= 6 ; -- 2^4 when 6 => iTXCNT <= 5 ; iSTATE <= 7 ; -- 2^5 when 7 => iTXCNT <= 6 ; iSTATE <= 8 ; -- 2^6 when 8 => iTXCNT <= 7 ; iSTATE <= 9 ; -- 2^7 (MSB) when 9 => iTXCNT <= 8 ; iSTATE <= 10; -- stop bit when 10 => iTXCNT <= 9 ; iSTATE <= 11; -- return first state when 11 => iSTATE <= 0 ; iDINXX <= (others => '1') ; -- default when others => iSTATE <= 0 ; end case ; end if ; end process; end Behavioral;  VHDLコードに続けて、ピンアサインを考えた  UCFを定義します。 # system NET "nRESET" LOC = "P107" ; NET "CLOCK" LOC = "P56" ; # transmitt data NET "TxD" LOC = "P66" ; # monitor NET "LOUT<0>" LOC = "P132" ; NET "LOUT<1>" LOC = "P124" ; NET "LOUT<2>" LOC = "P113" ; NET "LOUT<3>" LOC = "P112" ; NET "LOUT<4>" LOC = "P117" ; NET "LOUT<5>" LOC = "P116" ; NET "LOUT<6>" LOC = "P123" ; NET "LOUT<7>" LOC = "P122" ; # data NET "DINX<0>" LOC = "P111" ; NET "DINX<1>" LOC = "P114" ; NET "DINX<2>" LOC = "P136" ; NET "DINX<3>" LOC = "P141" ; NET "DINX<4>" LOC = "P120" ; NET "DINX<5>" LOC = "P119" ; NET "DINX<6>" LOC = "P129" ; NET "DINX<7>" LOC = "P128" ; # trigger NET "SEND" LOC = "P18" ; #(P18は、プッシュスイッチPB2に接続されている)  トグルスイッチで01000001、01000010、01000011を  設定すると、次の表示に。  (端末ソフトはTeraTerm。OSはWindows7)
目次

inserted by FC2 system