目次
前
次
夜間点滅器
CPLD/FPGAを利用して、夜間点滅器を作成してみます。
夜間点滅器は、自動車の防犯装置であるイモビライザーを
イメージすると、わかりやすいでしょう。
漠然と夜間点滅器としても、設計、開発ができないので
仕様を決めます。
- 電源電圧は、3.3Vとする
- 4個のLEDをもつ
- 昼夜の判断は、フォトトランジスタを利用
- 夜になると、4個のLEDを点滅する
- 点滅パターンは左から右に1個ずつ点灯
- 動作クロックは1Hz程度とする
ブロック図作成
仕様から、ブロック図を作成します。
クロックを入力し、シーケンサで2ビットのコードを生成します。
2ビットのコードをデコードし、LEDの点灯信号を作成します。
2ビットコードとLEDの点灯、消灯の関係を定義します。
- (Q1,Q0)=(0,0) -> (LED3,LED2,LED1,LED0)=(ON ,OFF,OFF,OFF)
- (Q1,Q0)=(0,1) -> (LED3,LED2,LED1,LED0)=(OFF,ON ,OFF,OFF)
- (Q1,Q0)=(1,1) -> (LED3,LED2,LED1,LED0)=(OFF,OFF,ON ,OFF)
- (Q1,Q0)=(1,0) -> (LED3,LED2,LED1,LED0)=(OFF,OFF,OFF,ON )
センサーからの信号がLのときに、点滅するとします。
センサー信号がHのとき、消灯にするために、条件を加えます。
- (SENIN,Q1,Q0)=(1,*,*) -> (LED3,LED2,LED1,LED0)=(OFF,OFF,OFF,OFF)
- (SENIN,Q1,Q0)=(0,0,0) -> (LED3,LED2,LED1,LED0)=(ON ,OFF,OFF,OFF)
- (SENIN,Q1,Q0)=(0,0,1) -> (LED3,LED2,LED1,LED0)=(OFF,ON ,OFF,OFF)
- (SENIN,Q1,Q0)=(0,1,1) -> (LED3,LED2,LED1,LED0)=(OFF,OFF,ON ,OFF)
- (SENIN,Q1,Q0)=(0,1,0) -> (LED3,LED2,LED1,LED0)=(OFF,OFF,OFF,ON )
シーケンサは、ジョンソンカウンタを構成し、クロックがはいる度に
出力を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ビットジョンソンカウンタになります。
- previous (Q1,Q0)=(0,0) -> now (Q1,Q0)=(0,1)
- previous (Q1,Q0)=(0,1) -> now (Q1,Q0)=(1,1)
- previous (Q1,Q0)=(1,1) -> now (Q1,Q0)=(1,0)
- previous (Q1,Q0)=(1,0) -> now (Q1,Q0)=(0,0)
ジョンソンカウンタは、シフトレジスタの応用で、最終段の
出力を反転し、初段の入力とするだけで構成できます。
デコーダは、74シリーズのTTLゲートで表現すると、139の
デコーダに相当します。
- (G,B,A)=(1,0,0) -> (Y3,Y2,Y1,Y0)=(H,H,H,H)
- (G,B,A)=(0,0,0) -> (Y3,Y2,Y1,Y0)=(H,H,H,L)
- (G,B,A)=(0,0,1) -> (Y3,Y2,Y1,Y0)=(H,H,L,H)
- (G,B,A)=(0,1,0) -> (Y3,Y2,Y1,Y0)=(H,L,H,H)
- (G,B,A)=(0,1,1) -> (Y3,Y2,Y1,Y0)=(L,H,H,H)
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で代用します。これで夜でなければ
動作確認できないという問題が解決します。
クロックは、下のような回路を作成して対応します。トリマーを
回すと周波数を変えられます。
目次
前
次