目次

マルチPLD構想

 半年間の大学での非常勤講師の仕事が終了したので
 1週間ほどMCR-VCに集中できるようになりました。

 大学では、シリアルインタフェースとサーボモータを
 VHDLコードで動かしていました。



 密着型センサーは、3.3Vで動かせるので、手持ちのCPLDが
 2種載っているボードで、MCR-VCマシンを動かしてみます。



 2種のCPLDは、次のように利用します。



 CoolRunnerIIは、路面センサー(密着方センサー)と
 ロータリーエンコーダからの情報で、左右のモータの
 DUTY比を計算します。

 計算したDUTY比をケーブルで、もう1個のCPLDに転送
 します。

 XC9572XLは、スタートトリガを貰い、CoolRunnerIIに
 報告します。また、与えられた左右のDUTY比でモータ
 の回転を制御します。

 ハードウエアに近い部分に、XC9572XLを配置します。


XC9572XL内部処理

 XC9572XLに接続する回路は、以下です。  PWM波形は、2つのMOSFETドライバフォトカプラに接続します。  スタートトリガーを、プッシュスイッチに接続します。  スイッチを押すと、論理値のLを出力します。  2つのLEDは、次のように使います。   緑 CoolRunnerIIに対し、DUTY比の設定で速度制御ができる     状態にあることを知らせます。同時に、ユーザーに移動     できる状態を提示します。   赤 CoolRunnerIIから、クランク、レーンチェンジの白線を     検出して移動していることを知らせます。  これらの回路は、写真のボードに実装されている部品を  利用します。  スタートトリガーを扱うVHDLコードを考えます。  プッシュスイッチは、チャタリングがあるので、シフトレジスタ  を利用したチャタリング除去回路を定義します。  プッシュスイッチは、負論理で動いているので  正論理に変換します。 iSSTRG <= not SSTRG ;  シフトレジスタを利用して、正論理で立上りエッジを  とらえて、トリガーにします。 process (nRESET,iTCLK) begin if ( nRESET = '0' ) then iSSTRG_SFT <= "000" ; elsif rising_edge( iTCLK ) then iSSTRG_SFT <= iSSTRG_SFT(1 downto 0) & iSSTRG ; end if ; end process ; iSETRG <= '1' when ( iSSTRG_SFT = "011" or iSSTRG_SFT = "001" ) else '0' ;  トリガーを利用して、シーケンサを動かします。  Johnsonカウンタを、トリガーを利用し、動かしたり  止めたりするようにします。  2ビットのJohnsonカウンタを利用すると、デコード  が単純になるのと、ハザードを出しません。  VHDLコードは、非常に単純です。 process (nRESET,iTCLK) begin if ( nRESET = '0' ) then iSTATE <= "00" ; elsif rising_edge( iTCLK ) then case conv_integer(iSTATE) is -- wait start trigger when 0 => if ( iSETRG = '1' ) then iSTATE <= "01" ; end if ; when 1 => iSTATE <= "11" ; -- wait exit trigger when 3 => if ( iSETRG = '1' ) then iSTATE <= "10" ; end if ; -- return first state when 2 => iSTATE <= "01" ; when others => iSTATE <= "00" ; end case ; end if ; end process ; iRUNFLAG <= iSTATE(0) ;  RUNFLAGは、CoolRunnerIIに渡す信号です。  チャタリング除去とシーケンサを回すためのクロックは  100Hz程度であればよく、PWM波形を生成するには10kHz  ほどになります。  個別に定義するのは、面倒なので、クロックジェネレータ  を一つ定義して、使い回します。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity clkgenx is generic ( TOPX : integer ; RMAX : integer --; ); port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- output CLKOUT : out std_logic -- ; ); end clkgenx; architecture Behavioral of clkgenx is signal iSCNT : std_logic_vector(TOPX-1 downto 0); signal iCLK : std_logic ; begin -- output CLKOUT <= iCLK ; -- divider process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iSCNT <= (others => '0') ; iCLK <= '0' ; elsif rising_edge(CLOCK) then if ( conv_integer(iSCNT) = RMAX ) then iSCNT <= (others => '0') ; iCLK <= not iCLK ; else iSCNT <= iSCNT + '1' ; end if ; end if ; end process ; end Behavioral;  PWM波形を生成するには、0〜99を設定するレジスタと  内部カウンタの値と比較するコンパレータがあれば充分  です。  今回は、2チャネルだけなので、専用のコードを定義  して対応します。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity pwmx is port( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- input PRATER : in std_logic_vector(6 downto 0) ; PRATEL : in std_logic_vector(6 downto 0) ; -- output POUTR : out std_logic ; POUTL : out std_logic --; ); end pwmx ; architecture Behavioral of pwmx is signal iPCNT : integer range 0 to 100 ; signal iPOUT : std_logic_vector(1 downto 0) ; signal iPRATER : std_logic_vector(6 downto 0) ; signal iPRATEL : std_logic_vector(6 downto 0) ; begin -- output POUTR <= not iPOUT(0) ; POUTL <= not iPOUT(1) ; -- comparetor iPOUT(0) <= '1' when ( iPCNT < conv_integer( iPRATER ) ) else '0' ; iPOUT(1) <= '1' when ( iPCNT < conv_integer( iPRATEL ) ) else '0' ; -- counter process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iPCNT <= 0 ; iPRATER <= "0000000" ; iPRATEL <= "0000000" ; elsif rising_edge( CLOCK ) then if ( iPCNT = 100 ) then iPCNT <= 0 ; iPRATER <= PRATER ; iPRATEL <= PRATEL ; else iPCNT <= iPCNT + 1 ; end if ; end if ; end process ; end Behavioral;  PWM波形のDUTY比を与えると、波形を乱さないように  内部カウンタの値が0のときに、再設定します。  また、出力は負論理としています。  DUTY比は、CoolRunnerから出力されるので、次の仕様で  入力します。  トリガーのキャッチは、チャタリング除去と同様  シフトレジスタを利用します。 process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iDTRG_SFT <= "000" ; elsif rising_edge(CLOCK) then iDTRG_SFT <= iDTRG_SFT(1 downto 0) & DTRG ; end if ; end process ; iDTRG <= '1' when ( iDTRG_SFT = "011" or iDTRG_SFT = "001" ) else '0' ;  トリガーを利用して、7ビットのDUTY比を内部  レジスタにラッチ(記憶)します。 process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iPRATER <= "0000000" ; iPRATEL <= "0000000" ; elsif falling_edge(CLOCK) then if ( iDTRG = '1' ) then -- left duty ratio if ( DUTY(7) = '1' ) then iPRATEL <= DUTY(6 downto 0) ; else -- right duty ratio iPRATER <= DUTY(6 downto 0) ; end if ; end if ; end if ; end process ;  方向設定の指定、白線クロスなどの信号を  含め、全体は以下としました。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity mdrivex is generic ( TOPX : integer := 7 ; XMAX : integer := 92 ; TOPT : integer := 6 ; TMAX : integer := 50 --; ) ; port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- monitor MCLK : out std_logic ; -- interface RUNFLAG : out std_logic ; HTURN : out std_logic ; CWHITE : in std_logic ; DTRG : in std_logic ; DUTY : in std_logic_vector(7 downto 0) ; -- leg system SSTRG : in std_logic ; TURN : in std_logic ; POUTR : out std_logic ; POUTL : out std_logic ; GLED : out std_logic ; RLED : out std_logic --; ) ; end mdrivex; architecture Behavioral of mdrivex is -- clock generator component clkgenx is generic ( TOPX : integer ; RMAX : integer --; ); port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- output CLKOUT : out std_logic -- ; ); end component ; -- internal clock signal iMCLK : std_logic ; signal iTCLK : std_logic ; -- PWM component pwmx is port( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- input PRATER : in std_logic_vector(6 downto 0) ; PRATEL : in std_logic_vector(6 downto 0) ; -- output POUTR : out std_logic ; POUTL : out std_logic --; ); end component ; -- internal register signal iPRATER : std_logic_vector(6 downto 0) ; signal iPRATEL : std_logic_vector(6 downto 0) ; signal iPOUTR : std_logic ; signal iPOUTL : std_logic ; signal iTURN : std_logic ; -- trigger signal iSSTRG : std_logic ; signal iSSTRG_SFT : std_logic_vector(2 downto 0) ; signal iSETRG : std_logic ; -- state machine signal iSTATE : std_logic_vector(1 downto 0) ; signal iRUNFLAG : std_logic ; -- duty ratio signal iDTRG : std_logic ; signal iDTRG_SFT : std_logic_vector(2 downto 0) ; begin -- clock generator CLKX : clkgenx generic map (TOPX,XMAX) port map (nRESET,CLOCK,iMCLK) ; CLKT : clkgenx generic map (TOPT,TMAX) port map (nRESET,iMCLK,iTCLK) ; -- PWM PWM : pwmx port map (nRESET,iMCLK,iPRATER,iPRATEL,iPOUTR,iPOUTL); -- input iSSTRG <= not SSTRG ; iTURN <= not TURN ; -- output MCLK <= iTCLK ; RLED <= not CWHITE ; GLED <= not iRUNFLAG ; RUNFLAG <= iRUNFLAG ; POUTR <= iPOUTR when ( iRUNFLAG = '1' ) else '1' ; POUTL <= iPOUTL when ( iRUNFLAG = '1' ) else '1' ; HTURN <= iTURN ; -- trigger handling process (nRESET,iTCLK) begin if ( nRESET = '0' ) then iSSTRG_SFT <= "000" ; elsif rising_edge( iTCLK ) then iSSTRG_SFT <= iSSTRG_SFT(1 downto 0) & iSSTRG ; end if ; end process ; iSETRG <= '1' when ( iSSTRG_SFT = "011" or iSSTRG_SFT = "001" ) else '0' ; -- state machine process (nRESET,iTCLK) begin if ( nRESET = '0' ) then iSTATE <= "00" ; elsif rising_edge( iTCLK ) then case conv_integer(iSTATE) is -- wait start trigger when 0 => if ( iSETRG = '1' ) then iSTATE <= "01" ; end if ; when 1 => iSTATE <= "11" ; -- wait exit trigger when 3 => if ( iSETRG = '1' ) then iSTATE <= "10" ; end if ; -- return first state when 2 => iSTATE <= "01" ; when others => iSTATE <= "00" ; end case ; end if ; end process ; iRUNFLAG <= iSTATE(0) ; -- debouncing process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iDTRG_SFT <= "000" ; elsif rising_edge(CLOCK) then iDTRG_SFT <= iDTRG_SFT(1 downto 0) & DTRG ; end if ; end process ; iDTRG <= '1' when ( iDTRG_SFT = "011" or iDTRG_SFT = "001" ) else '0' ; -- duty ratio trigger process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iPRATER <= "0000000" ; iPRATEL <= "0000000" ; elsif falling_edge(CLOCK) then if ( iDTRG = '1' ) then -- left duty ratio if ( DUTY(7) = '1' ) then iPRATEL <= DUTY(6 downto 0) ; else -- right duty ratio iPRATER <= DUTY(6 downto 0) ; end if ; end if ; end if ; end process ; end Behavioral;  ケーブルでCoolRunnerIIと接続するために  ピンアサインを指定します。 # system NET "MCLK" LOC = "P44" ; NET "nRESET" LOC = "P33" ; NET "CLOCK" LOC = "P1" ; # group A NET "POUTR" LOC = P43 ; NET "POUTL" LOC = P41 ; NET "SSTRG" LOC = P39 ; NET "TURN" LOC = P38 ; NET "GLED" LOC = P37 ; NET "RLED" LOC = P36 ; # group B NET "DUTY<0>" LOC = P34 ; NET "DUTY<1>" LOC = P32 ; NET "DUTY<2>" LOC = P31 ; NET "DUTY<3>" LOC = P30 ; NET "DUTY<4>" LOC = P29 ; NET "DUTY<5>" LOC = P28 ; NET "DUTY<6>" LOC = P27 ; NET "DUTY<7>" LOC = P23 ; # group C(7bits) NET "RUNFLAG" LOC = P22 ; NET "HTURN" LOC = P21 ; NET "CWHITE" LOC = P20 ;

CoolRunnerII内部処理

 CoolRunnerIIには、次の機能を担当させます。  センサー情報から、左右のDUTY比を算出するには  「もし〜ならば...とする」方式で指定します。  センサー情報は、クランク、レーンチェンジでは  普通走行とは異なる解釈しなければならないので  モードを用意して対応します。  モードは、次の4種類にしました。  センサーから情報入手後、モードを見てDUTY比を  設定するシーケンサを定義します。 process (nRESET,iMCLK) begin if ( nRESET = '0' ) then iJSTATE <= "000" ; iJRDUTY <= 0 ; -- right duty iJLDUTY <= 0 ; -- left duty iJRDUTYX <= 0 ; -- right duty iJLDUTYX <= 0 ; -- left duty iJCNT <= 0 ; -- delay counter iMODE <= 0 ; -- NORMAL 0 / LANE_RIGHT 1 / LANE_LEFT 2 / CRANK 3 elsif rising_edge(iMCLK) then if ( iJTRG = '1' ) then case conv_integer(iJSTATE) is -- get sensor data when 0 => iJSTATE <= "001" ; iSENSOR <= iJSENSOR ; -- convert when 1 => iJSTATE <= "011" ; case conv_integer(iJOUT) is -- all_black when 0 => if ( iMODE = 2 ) then -- LANE_LEFT iJRDUTYX <= 25 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 100000 ; -- 1000ms = 100000 x 10us elsif ( iMODE = 1 ) then -- LANE_RIGHT iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 25 ; -- left duty iJCNT <= 100000 ; -- 1000ms = 100000 x 10us elsif ( iMODE = 3 ) then -- CRANK iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 15 ; -- left duty else iJRDUTYX <= 0 ; -- right duty iJLDUTYX <= 0 ; -- left duty end if ; -- tiny_right when 1 => if ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 20 ; -- right duty iJLDUTYX <= 27 ; -- left duty iJCNT <= 25 ; else iJRDUTYX <= 20 ; -- right duty iJLDUTYX <= 25 ; -- left duty iJCNT <= 23 ; end if ; -- right when 2 => if ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 25 ; -- left duty iJCNT <= 20 ; else iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 25 ; -- left duty iJCNT <= 22 ; end if ; -- big_right when 3 => if ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 5 ; -- right duty iJLDUTYX <= 30 ; -- left duty iJCNT <= 15 ; else iJRDUTYX <= 5 ; -- right duty iJLDUTYX <= 30 ; -- left duty iJCNT <= 17 ; end if ; -- center when 4 => if ( iMODE = 2 ) then -- LANE_LEFT iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 35 ; -- left duty iJCNT <= 100000 ; iMODE <= 0 ; -- change normal elsif ( iMODE = 1 ) then -- LANE_RIGHT iJRDUTYX <= 35 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 100000 ; iMODE <= 0 ; -- change normal elsif ( iMODE = 3 ) then -- CRANK iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 5 ; else iJRDUTYX <= 35 ; -- right duty iJLDUTYX <= 35 ; -- left duty iJCNT <= 10 ; end if ; -- tiny_left when 5 => if ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 25 ; -- right duty iJLDUTYX <= 20 ; -- left duty iJCNT <= 25 ; else iJRDUTYX <= 25 ; -- right duty iJLDUTYX <= 20 ; -- left duty iJCNT <= 27 ; end if ; -- left when 6 => if ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 25 ; -- right duty iJLDUTYX <= 10 ; -- left duty iJCNT <= 20 ; else iJRDUTYX <= 25 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 23 ; end if ; -- big_left when 7 => if ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 30 ; -- right duty iJLDUTYX <= 5 ; -- left duty iJCNT <= 15 ; else iJRDUTYX <= 30 ; -- right duty iJLDUTYX <= 5 ; -- left duty iJCNT <= 17 ; end if ; -- right_white when 8 => if ( iMODE = 2 ) then -- LANE_LEFT iJRDUTYX <= 5 ; -- right duty iJLDUTYX <= 55 ; -- left duty iJCNT <= 1000 ; iMODE <= 0 ; -- change NORMAL elsif ( iMODE = 1 ) then -- LANE_RIGHT iJRDUTYX <= 55 ; -- right duty iJLDUTYX <= 5 ; -- left duty iJCNT <= 1000 ; iMODE <= 0 ; -- change NORMAL elsif ( iMODE = 3 ) then -- CRANK iJRDUTYX <= 5 ; -- right duty iJLDUTYX <= 55 ; -- left duty iJCNT <= 45 ; iMODE <= 0 ; -- change NORMAL else iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 10000 ; iMODE <= 1 ; -- change LANE (right) end if ; -- left_white when 9 => if ( iMODE = 2 ) then -- LANE_LEFT iJRDUTYX <= 55 ; -- right duty iJLDUTYX <= 5 ; -- left duty iJCNT <= 1000 ; iMODE <= 0 ; -- change NORMAL elsif ( iMODE = 1 ) then -- LANE_RIGHT iJRDUTYX <= 5 ; -- right duty iJLDUTYX <= 55 ; -- left duty iJCNT <= 1000 ; iMODE <= 0 ; -- change NORMAL elsif ( iMODE = 3 ) then -- CRANK iJRDUTYX <= 55 ; -- right duty iJLDUTYX <= 5 ; -- left duty iJCNT <= 45 ; iMODE <= 0 ; -- change NORMAL else iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 10000 ; iMODE <= 2 ; -- change LANE (left) end if ; -- all_white when 10 => if ( iMODE = 3 ) then -- CRANK iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 100 ; elsif ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 10000 ; iMODE <= 3 ; -- change CRANK else -- LANE_LEFT / LANE_RIGHT end if ; when others => NULL ; end case ; -- latch when 3 => iJSTATE <= "111" ; iJRDUTY <= iJRDUTYX ; -- right duty iJLDUTY <= iJLDUTYX ; -- left duty -- delay when 7 => if ( iJCNT = 0 ) then iJSTATE <= "110" ; else iJCNT <= iJCNT - 1 ; end if ; -- dummy (auto skip) when 6 => iJSTATE <= "100" ; -- return first state when 4 => iJSTATE <= "000" ; -- default when others => iJSTATE <= "000" ; end case ; else iJRDUTY <= 0 ; -- right duty iJLDUTY <= 0 ; -- left duty end if ; end if ; end process ;  11個のセンサー情報と4モードの組合せから  DUTY比を設定しています。更に、モードにより  遅延時間を変えています。  DUTY比を設定後、他のブロックへの転送処理は  専用シーケンサを用意して対応します。  専用シーケンサは、Johnsonカウンタを利用して  Blind_RunかJudge_Runを判定しています。  (Blind_Runは、スタート直後の直進を表現します。   Judge_Runは、センサーを利用した走行です。) process (nRESET,iMCLK) begin if ( nRESET = '0' ) then iDSTATE <= "00" ; iDUTY <= (others => '1') ; elsif rising_edge(iMCLK) then case conv_integer(iDSTATE) is -- get left duty when 0 => iDSTATE <= "01" ; if ( iPSTATE = "011" ) then iDUTY <= '1' & conv_std_logic_vector(iBDUTY,7) ; else iDUTY <= '1' & conv_std_logic_vector(iJLDUTY,7) ; end if ; -- send trigger when 1 => iDSTATE <= "11" ; -- get right duty when 3 => iDSTATE <= "10" ; if ( iPSTATE = "011" ) then iDUTY <= '0' & conv_std_logic_vector(iBDUTY,7) ; else iDUTY <= '0' & conv_std_logic_vector(iJRDUTY,7) ; end if ; -- send trigger and return first state when 2 => iDSTATE <= "00" ; -- default when others => iDSTATE <= "00" ; end case ; end if ; end process ; iTRG <= '1' when ( iDSTATE = "01" or iDSTATE = "10" ) else '0' ;  センサーから得られる情報は、8ビット=256パターンありますが  すべて利用するのは、面倒なので10パターンに集約させます。  パターンから出力される状態判定値は、次のようにしました。  256パターンから、11個の状態を抽出するコードを定義します。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity judge is port ( -- input DIN : in std_logic_vector(7 downto 0) ; -- output JOUT : out std_logic_vector(3 downto 0) --; ); end judge; architecture Behavioral of judge is signal iJOUT : std_logic_vector(3 downto 0); begin -- output JOUT <= iJOUT ; -- convert iJOUT <= "0000" when ( DIN = X"00" ) else -- all_black "0001" when ( DIN = X"0C" ) else -- tiny_right "0010" when ( DIN = X"06" ) else -- right "0011" when ( DIN = X"0E" ) else -- big_right "0100" when ( DIN = X"18" ) else -- center "0100" when ( DIN = X"1C" ) else -- center "0100" when ( DIN = X"38" ) else -- center "0101" when ( DIN = X"30" ) else -- tiny_left "0110" when ( DIN = X"60" ) else -- left "0111" when ( DIN = X"C0" ) else -- big_left "1000" when ( DIN = X"E0" ) else -- left_white "1000" when ( DIN = X"F0" ) else -- left_white "1000" when ( DIN = X"F8" ) else -- left_white "1001" when ( DIN = X"07" ) else -- right_white "1001" when ( DIN = X"0F" ) else -- right_white "1001" when ( DIN = X"1F" ) else -- right_white "1010" when ( DIN = X"FF" ) else -- all_white "1111" ; end Behavioral;  プリント基板に実装している制御ボードには、センサー情報を  モニタする他に、8個のLEDがあります。この8個のLEDを使い  内部の状態を表示して、テスト、デバッグに役立てます。  8個のLEDを上位、下位の各4ビットずつに状態を割当てます。  下位4ビットは、スタート直後の直進におけるDUTY比を  モニタします。(0消灯、1点灯)  上位4ビットは、走行モードを表します。(0消灯、1点灯)  単純な組合せ回路でよいので、VHDLコードは次のように指定しました。 iSTATUS(3 downto 0) <= "1000" when ( iBSTATE = 1 ) else -- duty 5% "0100" when ( iBSTATE = 3 ) else -- duty 15% "0010" when ( iBSTATE = 5 ) else -- duty 30% "0001" when ( iBSTATE = 7 ) else -- duty 50% "0000" ; iSTATUS(7 downto 4) <= "1000" when ( iMODE = 0 ) else -- NORMAL "0100" when ( iMODE = 3 ) else -- CRANK "0010" when ( iMODE = 1 ) else -- LANE_RIGHT "0001" when ( iMODE = 2 ) else -- LANE_LEFT "0000" ;  CoolRunnerIIは、他のブロックから入力した動作フラグを見て  Blind_RunとJudge_Runのどちらかを実行します。  この動作を監督するタスク(シーケンサ)を定義します。 process (nRESET,iMCLK) begin if ( nRESET = '0' ) then iPSTATE <= "000" ; elsif rising_edge(iMCLK) then case conv_integer(iPSTATE) is -- wait RFLAG rising edge when 0 => if ( iRFLAG_R_TRG = '1' ) then iPSTATE <= "001" ; end if ; -- send blind run trigger when 1 => iPSTATE <= "011" ; -- wait blind run flag when 3 => if ( iBFLAG = '1' ) then iPSTATE <= "111" ; end if ; -- send judge run trigger when 7 => iPSTATE <= "110" ; -- wait RFLAG falling edge when 6 => if ( iRFLAG_F_TRG = '1' ) then iPSTATE <= "100" ; end if ; -- return first state when 4 => iPSTATE <= "000" ; -- default when others => iPSTATE <= "000" ; end case ; end if ; end process ; iBTRG <= '1' when ( iPSTATE = "001" ) else '0' ; iJTRG <= '1' when ( iPSTATE = "111" ) else '0' ;  トリガーとフラグを利用し、2つのシーケンサを動かします。  2つのシーケンサをBlind_Run、Judge_Runとします。  スタート直後の直進(Blind_Run)は、時間で動くように定義しました。 process (nRESET,iMCLK) begin if ( nRESET = '0' ) then iBSTATE <= 0 ; iBCNT <= 0 ; iBDUTY <= 0 ; elsif rising_edge(iMCLK) then case iBSTATE is -- wait trigger when 0 => if ( iBTRG = '1' ) then iBSTATE <= 1 ; end if ; -- set duty ratio (5%) when 1 => iBSTATE <= 2 ; iBCNT <= 100000 ; iBDUTY <= 5 ; -- delay when 2 => if ( iBCNT = 0 ) then iBSTATE <= 3 ; else iBCNT <= iBCNT - 1 ; end if ; -- set duty ratio (15%) when 3 => iBSTATE <= 4 ; iBCNT <= 200000 ; iBDUTY <= 15 ; -- delay when 4 => if ( iBCNT = 0 ) then iBSTATE <= 5 ; else iBCNT <= iBCNT - 1 ; end if ; -- set duty ratio (30%) when 5 => iBSTATE <= 6 ; iBCNT <= 200000 ; iBDUTY <= 30 ; -- delay when 6 => if ( iBCNT = 0 ) then iBSTATE <= 7 ; else iBCNT <= iBCNT - 1 ; end if ; -- set duty ratio (50%) when 7 => iBSTATE <= 0 ; iBDUTY <= 50 ; -- default when others => iBSTATE <= 0 ; end case ; end if ; end process ; iBFLAG <= '1' when ( iBSTATE = 7 ) else '0' ;  まとめると、以下のコードとなります。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity mcrx5 is generic ( TOPX : integer := 4 ; RMAX : integer := 9 --; ) ; Port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- sensor inputs SENSOR : in std_logic_vector(7 downto 0) ; -- external unit input RFLAG : in std_logic ; -- run flag from PWM control board -- OUTPUT DUTY : out std_logic_vector(7 downto 0) ; TRG : out std_logic ; TRGX : out std_logic ; STATUS : out std_logic_vector(7 downto 0) ; SOUT : out std_logic_vector(7 downto 0) ; LED : out std_logic --; ); end mcrx5; architecture behavioral of mcrx5 is -- clock generator component clkgenx is generic ( TOPX : integer ; RMAX : integer --; ); port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- output CLKOUT : out std_logic -- ; ); end component ; -- judge component judge is port ( -- input DIN : in std_logic_vector(7 downto 0) ; -- output JOUT : out std_logic_vector(3 downto 0) --; ); end component ; -- clock signal iMCLK : std_logic ; -- parent sequencer signal iPSTATE : std_logic_vector(2 downto 0) ; signal iBTRG : std_logic ; -- blind run trigger signal iBFLAG : std_logic ; -- blind run flag signal iJTRG : std_logic ; -- judge run trigger signal iRFLAG_SFT : std_logic_vector(2 downto 0) ; signal iRFLAG_R_TRG : std_logic ; signal iRFLAG_F_TRG : std_logic ; -- blind run sequencer signal iBSTATE : integer range 0 to 7 ; signal iBCNT : integer range 0 to 300000 ; signal iBDUTY : integer range 0 to 63 ; -- judge run sequencer signal iJSTATE : std_logic_vector(2 downto 0) ; signal iJRDUTYX : integer range 0 to 63 ; signal iJLDUTYX : integer range 0 to 63 ; signal iJRDUTY : integer range 0 to 63 ; signal iJLDUTY : integer range 0 to 63 ; signal iJSENSOR : std_logic_vector(7 downto 0) ; signal iSENSOR : std_logic_vector(7 downto 0) ; signal iJOUT : std_logic_vector(3 downto 0) ; signal iMODE : integer range 0 to 3 ; signal iJCNT : integer range 0 to 100000 ; -- update duty signal iDSTATE : std_logic_vector(1 downto 0) ; signal iDUTY : std_logic_vector(7 downto 0) ; signal iTRG : std_logic ; -- signal iSTATUS : std_logic_vector(7 downto 0) ; begin -- clock CLKX : clkgenx generic map (TOPX,RMAX) port map (nRESET,CLOCK,iMCLK) ; -- judge JUDGEX : judge port map (iSENSOR,iJOUT) ; -- input iJSENSOR < not SENSOR ; -- output DUTY <= iDUTY ; TRG <= iTRG ; -- monitor LED <= not iMCLK ; SOUT <= not iJSENSOR ; STATUS <= not iSTATUS ; TRGX <= iMCLK ; -- internal status iSTATUS(3 downto 0) <= "1000" when ( iBSTATE = 1 ) else -- duty 5% "0100" when ( iBSTATE = 3 ) else -- duty 15% "0010" when ( iBSTATE = 5 ) else -- duty 30% "0001" when ( iBSTATE = 7 ) else -- duty 50% "0000" ; iSTATUS(7 downto 4) <= "1000" when ( iMODE = 0 ) else -- NORMAL "0100" when ( iMODE = 3 ) else -- CRANK "0010" when ( iMODE = 1 ) else -- LANE RIGHT "0001" when ( iMODE = 2 ) else -- LANE LEFT "0000" ; -- trigger shift register process (nRESET,iMCLK) begin if ( nRESET = '0' ) then iRFLAG_SFT <= "000" ; elsif rising_edge(iMCLK) then iRFLAG_SFT <= iRFLAG_SFT(1 downto 0) & RFLAG ; end if ; end process ; iRFLAG_R_TRG <= '1' when ( iRFLAG_SFT = "011" or iRFLAG_SFT = "001" ) else '0' ; iRFLAG_F_TRG <= '1' when ( iRFLAG_SFT = "110" or iRFLAG_SFT = "100" ) else '0' ; -- parent sequencer process (nRESET,iMCLK) begin if ( nRESET = '0' ) then iPSTATE <= "000" ; elsif rising_edge(iMCLK) then case conv_integer(iPSTATE) is -- wait RFLAG rising edge when 0 => if ( iRFLAG_R_TRG = '1' ) then iPSTATE <= "001" ; end if ; -- send blind run trigger when 1 => iPSTATE <= "011" ; -- wait blind run flag when 3 => if ( iBFLAG = '1' ) then iPSTATE <= "111" ; end if ; -- send judge run trigger when 7 => iPSTATE <= "110" ; -- wait RFLAG falling edge when 6 => if ( iRFLAG_F_TRG = '1' ) then iPSTATE <= "100" ; end if ; -- return first state when 4 => iPSTATE <= "000" ; -- default when others => iPSTATE <= "000" ; end case ; end if ; end process ; iBTRG <= '1' when ( iPSTATE = "001" ) else '0' ; iJTRG <= '1' when ( iPSTATE = "111" ) else '0' ; -- blind run sequencer process (nRESET,iMCLK) begin if ( nRESET = '0' ) then iBSTATE <= 0 ; iBCNT <= 0 ; iBDUTY <= 0 ; elsif rising_edge(iMCLK) then case iBSTATE is -- wait trigger when 0 => if ( iBTRG = '1' ) then iBSTATE <= 1 ; end if ; -- set duty ratio (5%) when 1 => iBSTATE <= 2 ; iBCNT <= 100000 ; iBDUTY <= 5 ; -- delay when 2 => if ( iBCNT = 0 ) then iBSTATE <= 3 ; else iBCNT <= iBCNT - 1 ; end if ; -- set duty ratio (15%) when 3 => iBSTATE <= 4 ; iBCNT <= 200000 ; iBDUTY <= 15 ; -- delay when 4 => if ( iBCNT = 0 ) then iBSTATE <= 5 ; else iBCNT <= iBCNT - 1 ; end if ; -- set duty ratio (30%) when 5 => iBSTATE <= 6 ; iBCNT <= 200000 ; iBDUTY <= 30 ; -- delay when 6 => if ( iBCNT = 0 ) then iBSTATE <= 7 ; else iBCNT <= iBCNT - 1 ; end if ; -- set duty ratio (50%) when 7 => iBSTATE <= 0 ; iBDUTY <= 50 ; -- default when others => iBSTATE <= 0 ; end case ; end if ; end process ; iBFLAG <= '1' when ( iBSTATE = 7 ) else '0' ; -- judge run sequencer process (nRESET,iMCLK) begin if ( nRESET = '0' ) then iJSTATE <= "000" ; iJRDUTY <= 0 ; -- right duty iJLDUTY <= 0 ; -- left duty iJRDUTYX <= 0 ; -- right duty iJLDUTYX <= 0 ; -- left duty iJCNT <= 0 ; -- delay counter iMODE <= 0 ; -- NORMAL 0 / LANE_RIGHT 1 / LANE_LEFT 2 / CRANK 3 elsif rising_edge(iMCLK) then if ( iJTRG = '1' ) then case conv_integer(iJSTATE) is -- get sensor data when 0 => iJSTATE <= "001" ; iSENSOR <= iJSENSOR ; -- convert when 1 => iJSTATE <= "011" ; case conv_integer(iJOUT) is -- all_black when 0 => if ( iMODE = 2 ) then -- LANE_LEFT iJRDUTYX <= 25 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 100000 ; -- 1000ms = 100000 x 10us elsif ( iMODE = 1 ) then -- LANE_RIGHT iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 25 ; -- left duty iJCNT <= 100000 ; -- 1000ms = 100000 x 10us elsif ( iMODE = 3 ) then -- CRANK iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 15 ; -- left duty else iJRDUTYX <= 0 ; -- right duty iJLDUTYX <= 0 ; -- left duty end if ; -- tiny_right when 1 => if ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 20 ; -- right duty iJLDUTYX <= 27 ; -- left duty iJCNT <= 25 ; else iJRDUTYX <= 20 ; -- right duty iJLDUTYX <= 25 ; -- left duty iJCNT <= 23 ; end if ; -- right when 2 => if ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 25 ; -- left duty iJCNT <= 20 ; else iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 25 ; -- left duty iJCNT <= 22 ; end if ; -- big_right when 3 => if ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 5 ; -- right duty iJLDUTYX <= 30 ; -- left duty iJCNT <= 15 ; else iJRDUTYX <= 5 ; -- right duty iJLDUTYX <= 30 ; -- left duty iJCNT <= 17 ; end if ; -- center when 4 => if ( iMODE = 2 ) then -- LANE_LEFT iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 35 ; -- left duty iJCNT <= 100000 ; iMODE <= 0 ; -- change normal elsif ( iMODE = 1 ) then -- LANE_RIGHT iJRDUTYX <= 35 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 100000 ; iMODE <= 0 ; -- change normal elsif ( iMODE = 3 ) then -- CRANK iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 5 ; else iJRDUTYX <= 35 ; -- right duty iJLDUTYX <= 35 ; -- left duty iJCNT <= 10 ; end if ; -- tiny_left when 5 => if ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 25 ; -- right duty iJLDUTYX <= 20 ; -- left duty iJCNT <= 25 ; else iJRDUTYX <= 25 ; -- right duty iJLDUTYX <= 20 ; -- left duty iJCNT <= 27 ; end if ; -- left when 6 => if ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 25 ; -- right duty iJLDUTYX <= 10 ; -- left duty iJCNT <= 20 ; else iJRDUTYX <= 25 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 23 ; end if ; -- big_left when 7 => if ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 30 ; -- right duty iJLDUTYX <= 5 ; -- left duty iJCNT <= 15 ; else iJRDUTYX <= 30 ; -- right duty iJLDUTYX <= 5 ; -- left duty iJCNT <= 17 ; end if ; -- right_white when 8 => if ( iMODE = 2 ) then -- LANE_LEFT iJRDUTYX <= 5 ; -- right duty iJLDUTYX <= 55 ; -- left duty iJCNT <= 1000 ; iMODE <= 0 ; -- change NORMAL elsif ( iMODE = 1 ) then -- LANE_RIGHT iJRDUTYX <= 55 ; -- right duty iJLDUTYX <= 5 ; -- left duty iJCNT <= 1000 ; iMODE <= 0 ; -- change NORMAL elsif ( iMODE = 3 ) then -- CRANK iJRDUTYX <= 5 ; -- right duty iJLDUTYX <= 55 ; -- left duty iJCNT <= 45 ; iMODE <= 0 ; -- change NORMAL else iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 10000 ; iMODE <= 1 ; -- change LANE (right) end if ; -- left_white when 9 => if ( iMODE = 2 ) then -- LANE_LEFT iJRDUTYX <= 55 ; -- right duty iJLDUTYX <= 5 ; -- left duty iJCNT <= 1000 ; iMODE <= 0 ; -- change NORMAL elsif ( iMODE = 1 ) then -- LANE_RIGHT iJRDUTYX <= 5 ; -- right duty iJLDUTYX <= 55 ; -- left duty iJCNT <= 1000 ; iMODE <= 0 ; -- change NORMAL elsif ( iMODE = 3 ) then -- CRANK iJRDUTYX <= 55 ; -- right duty iJLDUTYX <= 5 ; -- left duty iJCNT <= 45 ; iMODE <= 0 ; -- change NORMAL else iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 10000 ; iMODE <= 2 ; -- change LANE (left) end if ; -- all_white when 10 => if ( iMODE = 3 ) then -- CRANK iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 100 ; elsif ( iMODE = 0 ) then -- NORMAL iJRDUTYX <= 15 ; -- right duty iJLDUTYX <= 15 ; -- left duty iJCNT <= 10000 ; iMODE <= 3 ; -- change CRANK else -- LANE_LEFT / LANE_RIGHT end if ; when others => NULL ; end case ; -- latch when 3 => iJSTATE <= "111" ; iJRDUTY <= iJRDUTYX ; -- right duty iJLDUTY <= iJLDUTYX ; -- left duty -- delay when 7 => if ( iJCNT = 0 ) then iJSTATE <= "110" ; else iJCNT <= iJCNT - 1 ; end if ; -- dummy (auto skip) when 6 => iJSTATE <= "100" ; -- return first state when 4 => iJSTATE <= "000" ; -- default when others => iJSTATE <= "000" ; end case ; else iJRDUTY <= 0 ; -- right duty iJLDUTY <= 0 ; -- left duty end if ; end if ; end process ; -- update duty process (nRESET,iMCLK) begin if ( nRESET = '0' ) then iDSTATE <= "00" ; iDUTY <= (others => '1') ; elsif rising_edge(iMCLK) then case conv_integer(iDSTATE) is -- get left duty when 0 => iDSTATE <= "01" ; if ( iPSTATE = "011" ) then iDUTY <= '1' & conv_std_logic_vector(iBDUTY,7) ; else iDUTY <= '1' & conv_std_logic_vector(iJLDUTY,7) ; end if ; -- send trigger when 1 => iDSTATE <= "11" ; -- get right duty when 3 => iDSTATE <= "10" ; if ( iPSTATE = "011" ) then iDUTY <= '0' & conv_std_logic_vector(iBDUTY,7) ; else iDUTY <= '0' & conv_std_logic_vector(iJRDUTY,7) ; end if ; -- send trigger and return first state when 2 => iDSTATE <= "00" ; -- default when others => iDSTATE <= "00" ; end case ; end if ; end process ; iTRG <= '1' when ( iDSTATE = "01" or iDSTATE = "10" ) else '0' ; end behavioral;  ピンアサインを定義します。 # system NET "CLOCK" LOC = "P38" ; NET "LED" LOC = "P92" ; NET "nRESET" LOC = "P143" ; # output group A (J6) NET "RFLAG" LOC = "P87" ; NET "TRGX" LOC = "P80" ; NET "TRG" LOC = "P81" ; # output group B (J6) NET "DUTY<7>" LOC = "P78" ; NET "DUTY<6>" LOC = "P79" ; NET "DUTY<5>" LOC = "P76" ; NET "DUTY<4>" LOC = "P77" ; NET "DUTY<3>" LOC = "P74" ; NET "DUTY<2>" LOC = "P75" ; NET "DUTY<1>" LOC = "P70" ; NET "DUTY<0>" LOC = "P71" ; # output group A (J4) NET "SENSOR<7>" LOC = "P142" ; NET "SENSOR<6>" LOC = "P140" ; NET "SENSOR<5>" LOC = "P139" ; NET "SENSOR<4>" LOC = "P138" ; NET "SENSOR<3>" LOC = "P137" ; NET "SENSOR<2>" LOC = "P136" ; NET "SENSOR<1>" LOC = "P135" ; NET "SENSOR<0>" LOC = "P134" ; # output group B (J4) NET "SOUT<7>" LOC = "P133" ; NET "SOUT<6>" LOC = "P132" ; NET "SOUT<5>" LOC = "P131" ; NET "SOUT<4>" LOC = "P130" ; NET "SOUT<3>" LOC = "P129" ; NET "SOUT<2>" LOC = "P128" ; NET "SOUT<1>" LOC = "P126" ; NET "SOUT<0>" LOC = "P125" ; # output group C (J4) NET "STATUS<7>" LOC = "P124" ; NET "STATUS<6>" LOC = "P121" ; NET "STATUS<5>" LOC = "P120" ; NET "STATUS<4>" LOC = "P119" ; NET "STATUS<3>" LOC = "P118" ; NET "STATUS<2>" LOC = "P117" ; NET "STATUS<1>" LOC = "P116" ; NET "STATUS<0>" LOC = "P115" ;  ここまでのVHDLコードでは、ロータリーエンコーダの  情報を入力していません。
目次

inserted by FC2 system