目次
前
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ビットに加えるかを
決めたので、シーケンサのステートごとの処理を
考えます。
- 0ステート トリガーiSH_TRG待ち(トリガーでカウンタ、レジスタ初期化)
- 1ステート ラッチトリガーiCLKABを利用し、1ビットデータ取込み
- 2ステート 1ビットデータの取込みをしたので、カウンタ値更新
- 3ステート 指定カウンタ値ならば、8ビットレジスタの中に組込む
- 4ステート カウンタ値=最大値で6ステートに遷移、それ以外は5ステートに遷移
- 5ステート 1ステートにもどる
- 6ステート 0ステートにもどる
シーケンサの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" ;
目次
前