目次

VHDLコード

 Micom Car Rally Visual Classの場合、BCSを制御し
 路面情報を8ビットに集約した方が便利です。

 移動メカを動かすためには、カウンタ、デコーダを1チップに
 封入して基板サイズを小さくすべきです。そこで、CPLDを採用
 しました。

 利用したCPLDは、XilinxのXC9572。



 XC9572の動かすクロックは、手持ちの関係で4MHzとしました。

 BCSは、200kHzで動いているので、その5倍の1MHzで
 シフトレジスタにφ1、φ2の論理値を入力し、内部
 に1ビットデータを記憶する機構を採用します。



 画像データは、φ1(CLKA)、φ2(CLKB)の立ち上りエッジで
 トコロテンのように押し出されてきます。
 立ち上がりエッジからのデータ出力は最短でも、800ns必要と
 データシートに記されているので、1usか2us後のデータが安定
 したときに、取込めるようにφ1(CLKA)、φ2(CLKB)の立ち上り
 からの時間を調整する工夫を凝らしました。

 4MHzから1MHzを生成するクロックデバイダのVHDLコードは以下。

  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iDCNT <= "00" ;
    elsif rising_edge( CLOCK ) then
      iDCNT <= iDCNT + '1' ;
    end if ;
  end process ;
  iDCK <= '1' when ( iDCNT = "00" ) else '0' ;

 φ1(CLKA)、φ2(CLKB)の論理値をシフトレジスタに格納して
 いくVHDLコードは、以下。

  process (nRESET,iDCK)
  begin
    if ( nRESET = '0' ) then
      iCLKA_SFT <= "000" ;
      iCLKB_SFT <= "000" ;
    elsif rising_edge( iDCK ) then
      iCLKA_SFT <= iCLKA_SFT(1 downto 0) & CLKA ;
      iCLKB_SFT <= iCLKB_SFT(1 downto 0) & CLKB ;
    end if ;
  end process ;

 データを記憶するためのトリガーは、2つのシフトレジスタ
 の出力値をデコードして生成。

  iCLKAB <= '1' when ( iCLKA_SFT = "011" ) else
            '1' when ( iCLKB_SFT = "011" ) else
            '0' ;

 ここまでで、内部レジスタにデータを取込むための
 ラッチトリガーは生成できました。BCSが画像データ
 を生成してくるタイミングは、SHに同期します。



 SHのfallingEdgeを捕らえ、内部シーケンサが動けるようにします。

 SHのfallingEdgeは、シフトレジスタを使い捕捉します。

  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSH_SFT <= "000" ;
    elsif rising_edge( CLOCK ) then
      iSH_SFT <= iSH_SFT(1 downto 0) & SH ;
    end if ;
  end process ;
  iSH_TRG <= '1' when ( iSH_SFT = "110" ) else '0' ;

 シフトレジスタの内部回路は、以下となります。



 シーケンサは、CLKA、CLKBに同期して出力されてくる
 1ビットデータをラッチし、指定カウント値において
 8ビットレジスタの1ビットになるように操作します。

 どこで1ビットのデータを8ビットレジスタの一員に
 するのかは、次のように考えました。

 2048ビットの中央付近の1024から、左右に64の倍数分だけ
 離れた位置の1ビットデータが、路面の情報(白黒)を
 表している。

 GBCは、CLKA、CLKBに同期してゴミも出力するので
 次のようにオフセットを加えて、指定カウント値を
 求めました。

  CONSTANT S0 : integer :=  800 ; -- 32+1024+(-256)
  CONSTANT S1 : integer :=  864 ; -- 32+1024+(-192)
  CONSTANT S2 : integer :=  928 ; -- 32+1024+(-128)
  CONSTANT S3 : integer :=  992 ; -- 32+1024+( -64)
  CONSTANT S4 : integer := 1120 ; -- 32+1024+(  64)
  CONSTANT S5 : integer := 1184 ; -- 32+1024+( 128)
  CONSTANT S6 : integer := 1248 ; -- 32+1024+( 192)
  CONSTANT S7 : integer := 1312 ; -- 32+1024+( 256)

 どこで8ビットレジスタの1ビットに加えるかを
 決めたので、シーケンサのステートごとの処理を
 考えます。

 シーケンサのVHDLコードは、以下。

  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSTATE <= 0 ;
      iREGX  <= X"00" ;
      iREG   <= X"00" ;
      iTMP   <= '0' ;
      iSCNT  <= 0 ;
    elsif rising_edge( CLOCK ) then
      case iSTATE is
        -- wait trigger 
        when 0 => if ( iSH_TRG = '1' ) then
                    iSTATE <= 1     ; -- next
                    iREGX  <= X"00" ; -- clear register
                    iSCNT  <= 0     ; -- clear counter
                  else
                    iSTATE <= 0 ; -- stay
                  end if ;
        -- get data
        when 1 => if ( iCLKAB = '1' ) then
                    iSTATE <= 2   ; -- next
                    iTMP   <= DOS ; -- latch 1 bit
                  else
                    iSTATE <= 1 ; -- stay
                  end if ;
        -- data counter increment
        when 2 => iSTATE <= 3 ; -- next
                  iSCNT  <= iSCNT + 1 ; -- counter increment
        -- store datum to temporary register
        when 3 => iSTATE <= 4 ; -- next
                  -- store
                  if ( iSCNT = S0 or
                       iSCNT = S1 or
                       iSCNT = S2 or
                       iSCNT = S3 or
                       iSCNT = S4 or
                       iSCNT = S5 or
                       iSCNT = S6 or
                       iSCNT = S7    ) then
                    iREGX <= iREGX(6 downto 0) & iTMP ;
                  end if ;
        -- judge (? complete)
        when 4 => if ( iSCNT = SLAST ) then
                    iSTATE <= 6 ; -- return first state
                  else
                    iSTATE <= 5 ; -- loop control
                  end if ;
        -- loop control
        when 5 => iSTATE <= 1 ;
        -- return first state 
        when 6 => iSTATE <= 0 ;
                  iREG   <= iREGX ; -- copy
        -- default
        when others =>
                  iSTATE <= 0 ;
      end case ;
    end if ;
  end process ;

 シーケンサが扱う回路は、次のようになります。



 シーケンサは、1ビットレジスタにDOSの値を記憶します。
 内部のカウンタで、1ビットレジスタの値をシフトレジスタ
 に転送するタイミングを決定。2048ビットのデータスキャン
 が終わったなら、シフトレジスタの内容をレジスタに転送。

 この動作をSHを利用して、繰返します。

 1ビットレジスタは、200kHzのクロックでデータを確保し
 シフトレジスタは、4MHzで1ビットレジスタ値を転送する
 ので、シーケンサは殆どのステートで待ち状態になります。

 まとめると、以下。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity taa is
  port (
    -- system
    nRESET : in  std_logic ;
    CLOCK  : in  std_logic ; -- 4MHz
    -- divided clock
    DCLK   : out std_logic ; -- 1MHz
    -- BCS
    SH     : in  std_logic ;
    CLKA   : in  std_logic ;
    CLKB   : in  std_logic ;
    DOS    : in  std_logic ;
    -- monitor output
    LOUT   : out std_logic_vector(7 downto 0) ;
    -- output
    OST    : out std_logic_vector(7 downto 0) --;
  );
end taa ;

architecture behavioral of taa is
  -- 
  CONSTANT SLAST : integer := 2080 ;
  -- sensing location
  CONSTANT S0 : integer :=  800 ; -- 32+1024+(-256)
  CONSTANT S1 : integer :=  864 ; -- 32+1024+(-192)
  CONSTANT S2 : integer :=  928 ; -- 32+1024+(-128)
  CONSTANT S3 : integer :=  992 ; -- 32+1024+( -64)
  CONSTANT S4 : integer := 1120 ; -- 32+1024+(  64)
  CONSTANT S5 : integer := 1184 ; -- 32+1024+( 128)
  CONSTANT S6 : integer := 1248 ; -- 32+1024+( 192)
  CONSTANT S7 : integer := 1312 ; -- 32+1024+( 256)
  -- divided clock
  signal iDCNT : std_logic_vector(1 downto 0) ;
  signal iDCK  : std_logic ;
  -- shift register
  signal iCLKA_SFT : std_logic_vector(2 downto 0) ;
  signal iCLKB_SFT : std_logic_vector(2 downto 0) ;
  signal iCLKAB    : std_logic ; 
  signal iSH_SFT   : std_logic_vector(2 downto 0) ;
  signal iSH_TRG   : std_logic ;
  -- state machine
  signal iSTATE : integer range 0 to 6 ;
  signal iTMP   : std_logic ;
  signal iREGX  : std_logic_vector(7 downto 0) ;
  signal iREG   : std_logic_vector(7 downto 0) ;
  signal iSCNT  : integer range 0 to SLAST ;
begin
  -- output
  DCLK <= iDCK ;
  OST  <= iREG ;
  LOUT <= not iREG ;

  -- clock divider
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iDCNT <= "00" ;
    elsif rising_edge( CLOCK ) then
      iDCNT <= iDCNT + '1' ;
    end if ;
  end process ;
  iDCK <= '1' when ( iDCNT = "00" ) else '0' ;

  -- clock shifter
  process (nRESET,iDCK)
  begin
    if ( nRESET = '0' ) then
      iCLKA_SFT <= "000" ;
      iCLKB_SFT <= "000" ;
    elsif rising_edge( iDCK ) then
      iCLKA_SFT <= iCLKA_SFT(1 downto 0) & CLKA ;
      iCLKB_SFT <= iCLKB_SFT(1 downto 0) & CLKB ;
    end if ;
  end process ;

  -- internal lacth clock trigger
  iCLKAB <= '1' when ( iCLKA_SFT = "011" ) else
            '1' when ( iCLKB_SFT = "011" ) else
            '0' ;

  -- synchronizer
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSH_SFT <= "000" ;
    elsif rising_edge( CLOCK ) then
      iSH_SFT <= iSH_SFT(1 downto 0) & SH ;
    end if ;
  end process ;
  iSH_TRG <= '1' when ( iSH_SFT = "110" ) else '0' ;

  -- state machine
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSTATE <= 0 ;
      iREGX  <= X"00" ;
      iREG   <= X"00" ;
      iTMP   <= '0' ;
      iSCNT  <= 0 ;
    elsif rising_edge( CLOCK ) then
      case iSTATE is
        -- wait trigger 
        when 0 => if ( iSH_TRG = '1' ) then
                    iSTATE <= 1     ; -- next
                    iREGX  <= X"00" ; -- clear register
                    iSCNT  <= 0     ; -- clear counter
                  else
                    iSTATE <= 0 ; -- stay
                  end if ;
        -- get data
        when 1 => if ( iCLKAB = '1' ) then
                    iSTATE <= 2   ; -- next
                    iTMP   <= DOS ; -- latch 1 bit
                  else
                    iSTATE <= 1 ; -- stay
                  end if ;
        -- data counter increment
        when 2 => iSTATE <= 3 ; -- next
                  iSCNT  <= iSCNT + 1 ; -- counter increment
        -- store datum to temporary register
        when 3 => iSTATE <= 4 ; -- next
                  -- store
                  if ( iSCNT = S0 or
                       iSCNT = S1 or
                       iSCNT = S2 or
                       iSCNT = S3 or
                       iSCNT = S4 or
                       iSCNT = S5 or
                       iSCNT = S6 or
                       iSCNT = S7    ) then
                    iREGX <= iREGX(6 downto 0) & iTMP ;
                  end if ;
        -- judge (? complete)
        when 4 => if ( iSCNT = SLAST ) then
                    iSTATE <= 6 ; -- return first state
                  else
                    iSTATE <= 5 ; -- loop control
                  end if ;
        -- loop control 
        when 5 => iSTATE <= 1 ;
        -- return first state 
        when 6 => iSTATE <= 0 ;
                  iREG   <= iREGX ; -- copy
        -- default
        when others =>
                  iSTATE <= 0 ;
      end case ;
    end if ;
  end process ;

end behavioral;

 XC9572は、マクロセル数は72。
 上のVHDLコードは56マクロセルを消費しました。
 80%程度の消費量なので、多少余裕があります。

 UCFファイルは、次のように定義。

# system
NET "CLOCK"  LOC = "P5"  ;
NET "nRESET" LOC = "P39" ;

# BCS
NET "SH"   LOC = "P1" ;
NET "CLKA" LOC = "P2" ;
NET "CLKB" LOC = "P3" ;
NET "DOS"  LOC = "P4" ;

NET "DCLK" LOC = "P9" ;

# sensor data (for monitor LED)
NET "LOUT<0>" LOC = "P11" ;
NET "LOUT<1>" LOC = "P12" ;
NET "LOUT<2>" LOC = "P13" ;
NET "LOUT<3>" LOC = "P14" ;
NET "LOUT<4>" LOC = "P18" ;
NET "LOUT<5>" LOC = "P19" ;
NET "LOUT<6>" LOC = "P20" ;
NET "LOUT<7>" LOC = "P22" ;

# sensor data
NET "OST<0>"  LOC = "P24" ;
NET "OST<1>"  LOC = "P25" ;
NET "OST<2>"  LOC = "P26" ;
NET "OST<3>"  LOC = "P27" ;
NET "OST<4>"  LOC = "P28" ;
NET "OST<5>"  LOC = "P29" ;
NET "OST<6>"  LOC = "P33" ;
NET "OST<7>"  LOC = "P34" ;


目次

inserted by FC2 system