目次

夜間点滅器

 CPLD/FPGAを利用して、夜間点滅器を作成してみます。

 夜間点滅器は、自動車の防犯装置であるイモビライザーを
 イメージすると、わかりやすいでしょう。

 漠然と夜間点滅器としても、設計、開発ができないので
 仕様を決めます。



ブロック図作成

 仕様から、ブロック図を作成します。  クロックを入力し、シーケンサで2ビットのコードを生成します。  2ビットのコードをデコードし、LEDの点灯信号を作成します。  2ビットコードとLEDの点灯、消灯の関係を定義します。  センサーからの信号がLのときに、点滅するとします。  センサー信号がHのとき、消灯にするために、条件を加えます。  シーケンサは、ジョンソンカウンタを構成し、クロックがはいる度に  出力を0→1→3→2→0と繰返すことにします。  LEDに流す電流は、2mA程度必要なので、抵抗値を計算します。  2mA = (3.3V - 2.0V) / R から、650Ωになります。  E24系列で、少し多く電流を流す方がよいので、510Ωか470Ωを  使います。

entity定義

 ブロック図で外部とやりとりする信号が決まれば、entityを定義します。  entityは、単純に箱の内と外を行き来する信号を定義すればよいでしょう。 entity nflash is Port ( -- system nRESET : in std_logic ; -- system reset CLOCK : in std_logic ; -- reference clock -- test SEQOUT : out std_logic_vector(1 downto 0) ; -- I/O LEDS : out std_logic_vector(3 downto 0) ; SENIN : in std_logic --; ); end nflash;  実際のチップを作成後、シーケンサの動作を確認するため  シーケンサ出力をSEQOUTに接続します。

内部回路検討

 ブロック図を作成し、シーケンサとデコーダの動作を決定したので  内部回路を検討します。  シーケンサは、74シリーズのTTLゲートで表現すると、Dタイプ  フリップフロップを2個使った2ビットジョンソンカウンタになります。  ジョンソンカウンタは、シフトレジスタの応用で、最終段の  出力を反転し、初段の入力とするだけで構成できます。  デコーダは、74シリーズのTTLゲートで表現すると、139の  デコーダに相当します。  LEDは、電流を吸込めばよいので、シーケンサ出力に  よりLとなるようにします。

デコーダ定義

 デコーダは、単純な組合せ回路なので、真理値表を作成して  VHDLのコードに変換します。  信号SENINがLレベルで、動作するので、雛形は以下とします。 iLEDS(X) <= '0' when (iSEQ = "??" and SENIN = '0') else '1' ;  信号LEDSは、4ビットなので、シーケンス出力と結びつけて定義します。 signal iLEDS : std_logic_vector(3 downto 0); iLEDS(0) <= '0' when (iSEQ = "00" and SENIN = '0') else '1' ; iLEDS(1) <= '0' when (iSEQ = "01" and SENIN = '0') else '1' ; iLEDS(2) <= '0' when (iSEQ = "11" and SENIN = '0') else '1' ; iLEDS(3) <= '0' when (iSEQ = "10" and SENIN = '0') else '1' ; LEDS <= iLEDS ;

デコーダテスト

 デコーダを定義したので、仕様通りの振舞いをするかをテストします。  組合わせ回路なので、カウンタを用意して、カウンタの出力をデコーダ  に接続します。カウンタをフリーランさせ、デコーダの出力がどうなる  のかを観測します(全パターン入力テストにします)。  動作確認ができればよいので、CPLDのXC9532を載せたボードに  回路情報を転送して確認しました。テスト用のVHDLコードは、  以下です。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity tst139 is port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- test SENIN : in std_logic ; ROUT : out std_logic_vector(1 downto 0) ; LEDS : out std_logic_vector(3 downto 0) --; ) ; end tst139; architecture Behavioral of tst139 is signal iREG : std_logic_vector(1 downto 0) ; signal iLEDS : std_logic_vector(3 downto 0) ; begin -- LEDS LEDS <= iLEDS ; iLEDS(0) <= '0' when (iREG = "00" and SENIN = '0') else '1' ; iLEDS(1) <= '0' when (iREG = "01" and SENIN = '0') else '1' ; iLEDS(2) <= '0' when (iREG = "11" and SENIN = '0') else '1' ; iLEDS(3) <= '0' when (iREG = "10" and SENIN = '0') else '1' ; -- free running counter process ( nRESET , CLOCK ) begin if ( nRESET = '0' ) then iREG <= "00" ; elsif rising_edge( CLOCK ) then iREG <= iREG + '1' ; end if ; end process ; ROUT <= iREG ; end Behavioral;  テスト前に、期待値と結果が比較できるように集計用紙に 期待値を記入しておきます。  この場合は、以下となります。 counter = 000 -> LEDS = 1110 (expected) : ???? (result) counter = 001 -> LEDS = 1101 (expected) : ???? (result) counter = 010 -> LEDS = 0111 (expected) : ???? (result) counter = 011 -> LEDS = 1011 (expected) : ???? (result) counter = 100 -> LEDS = 1111 (expected) : ???? (result) counter = 101 -> LEDS = 1111 (expected) : ???? (result) counter = 110 -> LEDS = 1111 (expected) : ???? (result) counter = 111 -> LEDS = 1111 (expected) : ???? (result)  手持ちのCPLDボードは、タイマーICの555で2Hzを発振する  ので、フリーランカウンタの出力とデコーダの出力で、 上術の内容が一致するかを目視テストしました。    結果を記入して比較します。 counter = 000 -> LEDS = 1110 (expected) : 1110 (result) counter = 001 -> LEDS = 1101 (expected) : 1101 (result) counter = 010 -> LEDS = 0111 (expected) : 0111 (result) counter = 011 -> LEDS = 1011 (expected) : 1011 (result) counter = 100 -> LEDS = 1111 (expected) : 1111 (result) counter = 101 -> LEDS = 1111 (expected) : 1111 (result) counter = 110 -> LEDS = 1111 (expected) : 1111 (result) counter = 111 -> LEDS = 1111 (expected) : 1111 (result)  期待値と結果が一致しているので、テストは終了です。  シミュレータを動かすよりも、実機の方がテストが早い場合も  あり、CPLD/FPGAボードを持っているならば、検討に値する テスト方法です。  このテストの所要時間は、コード作成を含めて15分でした。  さらに、PeakVHDLで動作テストしました。  タイミングチャートから、SENINが'0'で、デコード  信号が出力されていることがわかります。  テスト用のVHDLコードは、以下としました。   library ieee;   use ieee.std_logic_1164.all;   use ieee.numeric_std.all;   use ieee.std_logic_arith.all;   use std.textio.all;   use work.TST139;   entity TESTBNCH is   end TESTBNCH;   architecture stimulus of TESTBNCH is    component TST139 is    port (    -- system    nRESET : in std_logic ;    CLOCK : in std_logic ;    -- test    SENIN : in std_logic ;    ROUT : out std_logic_vector(1 downto 0) ;    LEDS : out std_logic_vector(3 downto 0) --;    );    end component;    constant PERIOD : time := 50 ns;    constant RPERIOD: time := 70 ns;    constant PER0 : time := 125 ns;    constant PER1 : time := 175 ns;    constant PER2 : time := 255 ns;    -- Top level signals go here...    signal nRESET : std_logic ;    signal CLOCK : std_logic ;    signal SENIN : std_logic ;    signal ROUT : std_logic_vector(1 downto 0) ;    signal LEDS : std_logic_vector(3 downto 0) ;    signal done: boolean := false;   begin    DUT: TST139 port map (nRESET,CLOCK,SENIN,ROUT,LEDS);    CLOCK1: process    variable clktmp: std_ulogic := '0';    begin    wait for PERIOD/2;    clktmp := not clktmp;    CLOCK <= clktmp; -- Attach your clock here    end process CLOCK1;    STIMULUS1: process    begin    -- Sequential stimulus goes here...    --    -- Sample stimulus...    nRESET <= '0' ; -- Reset the system    SENIN <= '0' ;    wait for RPERIOD; -- Wait    nRESET <= '1' ; -- de-assert reset    wait for PER0; -- Wait    SENIN <= '1' ;    wait for PER1; -- Wait    SENIN <= '0' ;    wait for PER2; -- Wait    SENIN <= '1' ;    --    -- Enter more stimulus here...    --    done <= true; -- Turn off the clock    wait; -- Suspend simulation    end process STIMULUS1;   end stimulus;  同時処理が可能なことを利用し、クロックと  与える信号を分けて、タイミングチャートで  一度にデコード結果を確認できます。  シフトレジスタの出力で、デコーダがどんな  動作をするのかを確認できます。

シーケンサ定義

 シーケンサは、内部にフリップフロップをもつので、  リセット時に、初期状態が決まるようにします。  リセットは、同期型か非同期型になりますが、CPLDでの  利用も考えて非同期型とします。  雛形は、process文で記述します。  process ( nRESET , CLOCK )  begin   if ( nRESET = '0' ) then    iSEQ <= "00" ;   elsif rising_edge( CLOCK ) then    -- ???   end if ;  end process ;  シーケンサの構成は、下図のようになっているので  シフトレジスタ構成にし、最終段の反転値を初段の  入力にします。  process ( nRESET , CLOCK )  begin   if ( nRESET = '0' ) then    iSEQ <= "00" ;   elsif rising_edge( CLOCK ) then    iSEQ <= iSEQ(0) & (not iSEQ(1)) ;   end if ;  end process ;  信号を外部に引き出して、観測できるようにします。  SEQOUT <= iSEQ ;

シーケンサテスト

 シーケンサを定義したので、仕様通りの振舞いをするかをテストします。  順序回路なので、クロックと入力値を与えて、出力を観測します。  このシーケンサは、単純に00→01→11→10→00と出力しているかを  観測するだけで充分です。  この程度であれば、シミュレータを使うまでもなく実機に  シーケンサの内容を転送してテストします。ソースコード  は、次のようにします。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity tst139s is Port ( -- system nRESET : in std_logic; CLOCK : in std_logic; -- Johnson counter out SQOUT : out std_logic_vector(1 downto 0) ); end tst139s; architecture Behavioral of tst139s is signal iSEQ : std_logic_vector(1 downto 0) ; begin -- system process ( nRESET , CLOCK ) begin if ( nRESET = '0' ) then iSEQ <= "00" ; elsif rising_edge( CLOCK ) then iSEQ <= iSEQ(0) & (not iSEQ(1)) ; end if ; end process ; SQOUT <= iSEQ ; end Behavioral;  テスト前に、期待値と結果が比較できるように集計用紙に  期待値を記入しておきます。  この場合は、次のようになります。 reset -> (Q1,Q0) = 00 : ?? (result) clock (raising edge) -> (Q1,Q0) = 01 : ?? (result) clock (raising edge) -> (Q1,Q0) = 11 : ?? (result) clock (raising edge) -> (Q1,Q0) = 10 : ?? (result) clock (raising edge) -> (Q1,Q0) = 00 : ?? (result) clock (raising edge) -> (Q1,Q0) = 01 : ?? (result)  手持ちのCPLDボードは、タイマーICの555で2Hzを発振する  ので、CLOCKに2Hzを入力して、目視テストします。  結果を記入して比較します。 reset -> (Q1,Q0) = 00 : 00 (result) clock (raising edge) -> (Q1,Q0) = 01 : 01 (result) clock (raising edge) -> (Q1,Q0) = 11 : 11 (result) clock (raising edge) -> (Q1,Q0) = 10 : 10 (result) clock (raising edge) -> (Q1,Q0) = 00 : 00 (result) clock (raising edge) -> (Q1,Q0) = 01 : 01 (result)  さらに、PeakVHDLで動作テストしました。  タイミングチャートから、2ビットのジョンソンカウンタ  になっていることがわかります。  テスト用のVHDLコードは、以下としました。   library ieee;   use ieee.std_logic_1164.all;   use ieee.numeric_std.all;   use ieee.std_logic_arith.all;   use std.textio.all;   use work.tst139s;   entity TESTBNCH is   end TESTBNCH;   architecture stimulus of TESTBNCH is    component tst139s is    port (    -- system    nRESET : in std_logic;    CLOCK : in std_logic;    -- output    SQOUT : out std_logic_vector(1 downto 0) --;    );    end component;    constant PERIOD : time := 50 ns;    -- Top level signals go here...    signal nRESET : std_logic ;    signal CLOCK : std_logic ;    signal SQOUT : std_logic_vector(1 downto 0) ;    signal done: boolean := false;   begin    DUT: tst139s port map (nRESET,CLOCK,SQOUT);    CLOCK1: process    variable clktmp: std_ulogic := '0';    begin    wait for PERIOD/2;    clktmp := not clktmp;    CLOCK <= clktmp; -- Attach your clock here    end process CLOCK1;    STIMULUS1: process    begin    -- Sequential stimulus goes here...    --    -- Sample stimulus...    nRESET <= '0' ; -- Reset the system    wait for PERIOD; -- Wait    nRESET <= '1' ; -- de-assert reset    --    -- Enter more stimulus here...    --    done <= true; -- Turn off the clock    wait; -- Suspend simulation    end process STIMULUS1;   end stimulus;

ターゲットチップ選定

 できる限り消費電力を抑えておきたいので、3.3Vを最高電圧としています。  3.0Vであれば、乾電池2本で動作させられます。  最近のFPGAは1.8V程度でも、動作できますが、LEDを点灯させるには  2.5Vはほしいところです。CPLDの中でも、オレンジ1個のボルタ電池 で動くCoolRunner2を利用します。  CLOCKとして、1Hzを想定しましたが、超低周波を生成するクロック  ジェネレータの入手性が悪いので、CPLD/FPGA内部で分周したクロック  を作ることにします。  1Hzを生成するには、2のn乗で分周すると考えれば、1kHz程度  を発振器ICの555で生成するとします。  シーケンサで、4分周しているので、1kHzを1024分周すると  すれば、10(1024分周相当)−2(4分周相当)=8ビットの  カウンタを用意すれば充分です。  分周器を定義します。  signal iCLK : std_logic ;  signal iCNT : std_logic_vector(7 downto 0) ;  -- system  process ( nRESET , CLOCK )  begin   if ( nRESET = '0' ) then    iCNT <= (others => '0') ;   elsif rising_edge( CLOCK ) then    iCNT <= iCNT + '1' ;   end if ;  end process ;  iCLK <= '1' when ( conv_integer(iCNT) = 0 ) else '0' ;  シーケンサは、iCLKでドライブされるように再定義します。  -- sequencer  process ( nRESET , iCLK )  begin   if ( nRESET = '0' ) then    iSEQ <= "00" ;   elsif rising_edge( iCLK ) then    iSEQ <= iSEQ(0) & (not iSEQ(1)) ;   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 nflash is Port ( -- system nRESET : in std_logic ; -- system reset CLOCK : in std_logic ; -- reference clock -- test SEQOUT : out std_logic_vector(1 downto 0) ; -- I/O LEDS : out std_logic_vector(3 downto 0) ; SENIN : in std_logic --; ); end nflash; architecture behavioral of nflash is signal iCLK : std_logic ; signal iCNT : std_logic_vector(7 downto 0) ; signal iSEQ : std_logic_vector(1 downto 0) ; signal iLEDS: std_logic_vector(3 downto 0) ; begin -- system process ( nRESET , CLOCK ) begin if ( nRESET = '0' ) then iCNT <= (others => '0') ; elsif rising_edge( CLOCK ) then iCNT <= iCNT + '1' ; end if ; end process ;  iCLK <= '1' when ( conv_integer(iCNT) = 0 ) else '0' ; -- sequencer process ( nRESET , iCLK ) begin if ( nRESET = '0' ) then iSEQ <= "00" ; elsif rising_edge( iCLK ) then iSEQ <= iSEQ(0) & (not iSEQ(1)) ; end if ; end process ; SEQOUT <= iSEQ ; -- decoder LEDS <= iLEDS ; iLEDS(0) <= '0' when (iSEQ = "00" and SENIN = '0') else '1' ; iLEDS(1) <= '0' when (iSEQ = "01" and SENIN = '0') else '1' ; iLEDS(2) <= '0' when (iSEQ = "11" and SENIN = '0') else '1' ; iLEDS(3) <= '0' when (iSEQ = "10" and SENIN = '0') else '1' ; end behavioral;

実機動作確認

 シミュレータでテストし、チップに回路情報を転送したならば完成  というわけにはいきません。実機で動作するかを確認します。  ここで、ボタンスイッチボードとLEDボードが必要になります。  SENIN信号を、ボタンのON、OFFで代用します。これで夜でなければ  動作確認できないという問題が解決します。  クロックは、下のような回路を作成して対応します。トリマーを  回すと周波数を変えられます。


目次 inserted by FC2 system