目次
前
次
Kitchen Timer
CPLD/FPGAを利用して、キッチンタイマーを作成してみます。
キッチンタイマーを、どういう場面で利用するか列挙します。
- カップ麺の茹で上がり待ち
- 温泉卵の茹で時間
- ライントレーサの周回時間
- 感光基板の露光時間計時
- プリント基板のエッチング時間計時
この他にもあるでしょうが、自分が使う場合は上のリスト通りです。
仕様を図で具体化します。
スイッチは、スタートとリセットとします。
経過時間(分)を、2けたの7セグメントLEDで表示。
計時中は2秒単位でLEDを点滅。
タイムアウトしたときに、ブザーを鳴らします。
表示用の7セグメントLEDは、配線数を減らすために
ダイナミック点灯します。
10の位、1の位の各セグメントを利用するために、選択用
の信号線を使います。
使う7セグメントLEDによりアノードコモンかカソードコモン
に分かれますが、FPGA/CPLDを利用するならば、単に出力論理を
反転するか否かなので、入手できる部品に合わせます。
計時していることがわかるように使うLEDは、7セグメント
LEDのDp(デシマルポイント)部分を使います。
ブロック図作成
仕様で描いた図から、ブロック図を作成します。
ブロック図から、各ブロックの処理を考えます。
- システムクロック分周器(CLOCKGEN)
- 計時シーケンサ(SEQUENCER)
- スイッチシンクロナイザ(SWSYNC)
- DIPスイッチデータ入力(DALATCH)
- 7セグメントLEDデコーダ(DISPLAY)
- ブザー鳴動器(BSEQUENCER)
- LED点滅器(FLAHSER)
ブロックに分割したので、各ブロックを定義します。
ブロックに分割すると、ブロックごとに集中して動作を
考えればよくなります。また、テストする場合も、その
ブロックだけにすればよいので、テストには、マクロセル
(ゲート数)が少ないボードで間に合います。
VHDLのコードは、トップレベルに結線情報だけを記述するので
見通しがよくなります。
各ブロックを定義する前に、入出力信号を確定しておきます。
entity定義
ブロック図で外部とやりとりする信号が決まれば、entityを定義します。
entity ktimer is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- system clock
-- start trigger
STRG : in std_logic ;
-- minute
M10 : in std_logic_vector(3 downto 0) ; -- x10
M01 : in std_logic_vector(3 downto 0) ; -- x1
-- output
TLEDSEL : out std_logic_vector(1 downto 0) ;
TLEDOUT : out std_logic_vector(6 downto 0) ;
SLEDOUT : out std_logic ;
BUZOUT : out std_logic -- ;
);
end ktimer;
entityが決まれば、接続回路図を作成できます。
システムクロック分周器(CLOCKGEN)
計時するためには、基本となるクロックが必要になります。
時計用クロックとして利用する周波数は、32.768kHzなので
このクロックを分周し、システムで使うクロックを生成します。
システムは、人間が認知できない程度に高速であればよいので
32.768kHz(32768Hz)を分周し、327Hzと1Hz(1s)を生成します。
カウンタのビットサイズをどうするかを考えます。
32768Hzから327Hzを生成するには、100分周します。
0から100のカウントなので、8ビットバイナリカウンタで
実現できます。
32768Hzから1Hzを生成するには、32768分周します。
2の16乗で65536なので、32768分周は16ビットバイナリカウンタで
実現できます。
システムの内部クロックと1Hzのクロック生成を定義します。
signal iPCOUNT : std_logic_vector( 7 downto 0) ;
signal iSCOUNT : std_logic_vector(15 downto 0) ;
signal iPCLK : std_logic ;
signal iSCLK : std_logic ;
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iPCOUNT <= (others => '0') ;
iSCOUNT <= (others => '0') ;
elsif rising_edge( CLOCK ) then
if ( conv_integer( iPCOUNT ) = 99 ) then
iPCOUNT <= (others => '0') ;
else
iPCOUNT <= iPCOUNT + '1' ;
end if ;
if ( conv_integer( iSCOUNT ) = 32767 ) then
iSCOUNT <= (others => '0') ;
else
iSCOUNT <= iSCOUNT + '1' ;
end if ;
end if ;
end process ;
iPCLK <= '1' when ( conv_integer( iPCOUNT ) < 50 ) else '0' ;
iSCLK <= '1' when ( conv_integer( iSCOUNT ) < 16384 ) else '0' ;
iPCLKを、キッチンタイマーのシステムクロックに利用します。
1秒間とその整数倍の処理は、iSCLKを使います。
このIPを見直して、机上デバッグします。
最初にiSCLKを見て、まずいとわかります。
32768で、0に戻していますが、16ビットのバイナリーカウンタは
0から32767まで変化するので、32768を判断できません。
従って、ifによる判定は不要です。
signal iPCOUNT : std_logic_vector( 7 downto 0) ;
signal iSCOUNT : std_logic_vector(15 downto 0) ;
signal iPCLK : std_logic ;
signal iSCLK : std_logic ;
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iPCOUNT <= (others => '0') ;
iSCOUNT <= (others => '0') ;
elsif rising_edge( CLOCK ) then
-- increment
iSCOUNT <= iSCOUNT + '1' ;
-- judge
if ( conv_integer( iPCOUNT ) = 99 ) then
iPCOUNT <= (others => '0') ;
else
iPCOUNT <= iPCOUNT + '1' ;
end if ;
end if ;
end process ;
iPCLK <= '1' when ( conv_integer( iPCOUNT ) < 50 ) else '0' ;
iSCLK <= '1' when ( conv_integer( iSCOUNT ) < 16384 ) else '0' ;
iPCLKの生成に、iPCOUNTを用意していますが、これは不要です。
16ビットのバイナリーカウンタのiSCOUNTの値を使えば、判定できます。
iSCOUNTの下位7ビットで、99に一致したとき、iPCLKに1を出力する
仕様で、327Hzを生成できます。
signal iSCOUNT : std_logic_vector(15 downto 0) ;
signal iPCLK : std_logic ;
signal iSCLK : std_logic ;
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSCOUNT <= (others => '0') ;
elsif rising_edge( CLOCK ) then
-- increment
iSCOUNT <= iSCOUNT + '1' ;
end if ;
end process ;
iPCLK <= '1' when ( iSCOUNT(6 downto 0) = "1100011" ) else '0' ;
iSCLK <= '1' when ( conv_integer( iSCOUNT ) < 50 ) else '0' ;
ブザーを鳴らすために、800Hzほどのクロックを利用するとします。
このクロックを、iBCLKとして定義しておきます。
32768/800=40.96となるので、40を判定するコードを加えます。
全体では、次のようになります。
signal iSCOUNT : std_logic_vector(15 downto 0) ;
signal iBCLK : std_logic ;
signal iPCLK : std_logic ;
signal iSCLK : std_logic ;
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSCOUNT <= (others => '0') ;
elsif rising_edge( CLOCK ) then
-- increment
iSCOUNT <= iSCOUNT + '1' ;
end if ;
end process ;
iBCLK <= '1' when ( iSCOUNT(5 downto 0) = "101000" ) else '0' ;
iPCLK <= '1' when ( iSCOUNT(6 downto 0) = "1100011" ) else '0' ;
iSCLK <= '1' when ( conv_integer( iSCOUNT ) < 50 ) else '0' ;
このコードをCPLDのXC9572が実装されているボードに載せてテストしました。
その場合のソースコードは、以下です。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity mclock is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- clock
BCLK : out std_logic ;
PCLK : out std_logic ;
SCLK : out std_logic --;
);
end mclock;
architecture Behavioral of mclock is
signal iSCOUNT : std_logic_vector(15 downto 0) ;
begin
-- generate system clock
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSCOUNT <= (others => '0') ;
elsif rising_edge( CLOCK ) then
-- increment
iSCOUNT <= iSCOUNT + '1' ;
end if ;
end process ;
-- output
BCLK <= '1' when ( iSCOUNT(5 downto 0) = "101000" ) else '0' ;
PCLK <= '1' when ( iSCOUNT(6 downto 0) = "1100011" ) else '0' ;
SCLK <= '1' when ( conv_integer( iSCOUNT ) < 50 ) else '0' ;
end Behavioral;
測定には、周波数カウンタがついたマルチメータを使いました。
オシロスコープやベンチタイプの周波数カウンタで、簡単にチェックできますが
デジタルマルチメータには、周波数カウンタ付きのものがあり、安価に入手可能
なので、1台用意しておくとよいでしょう。
また、アナログマルチメータでも、800Hz、300Hz、1Hzの出力でメータが振れる
ので判断できます。
スイッチシンクロナイザ(SWSYNC)
リセットスイッチは、他のブロックで定義済なので
スタートトリガースイッチのシンクロナイザを定義します。
シンクロナイザの基本は、シフトレジスタです。
3ビットのシフトレジスタを用意し、1秒の整数分の1ごとに
STRGの論理値を読込みます。
指定値になっていれば、フラグをセットします。
スタートトリガーが入ったかは、シフトレジスタ値が0か
どうかで判断します。
リセット値には、初期値として7を設定します。
signal iSSTRG : std_logic_vector(2 downto 0) ;
signal iSTRG : std_logic ;
process ( nRESET , iPCLK )
begin
if ( nRESET = '0' ) then
iSSTRG <= "111" ;
elsif rising_edge( iPCLK ) then
-- get logic level
iSSTRG <= iSSTRG(1 downto 0) & STRG ;
end if ;
end process ;
iSTRG <= '1' when ( iSSTRG = "000" ) else '0' ;
フラグ設定は、単純なデコーダで実現します。
スタートトリガーの回路図は、以下です。
このコードをCPLDのXC9572が実装されているボードに載せてテストしました。
その場合のソースコードは、以下です。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity isync is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- trigger
TRG : in std_logic ;
-- out
ITRG : out std_logic --;
);
end isync;
architecture Behavioral of isync is
signal iSTRG : std_logic_vector(2 downto 0) ;
begin
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSTRG <= "111" ;
elsif rising_edge( CLOCK ) then
-- get logic level
iSTRG <= iSTRG(1 downto 0) & TRG ;
end if ;
end process ;
ITRG <= '1' when ( iSTRG = "000" ) else '0' ;
end Behavioral;
計時シーケンサ定義(SEQUENCER)
計時処理は、シーケンサ(ステートマシン)で実現します。
ステートマシンの処理を考えます。
- スタートトリガー待ち
- スイッチから設定値入力
- 設定値からカウンタ値計算
- カウンタ値をサブシーケンサに渡す
- サブシーケンサが終了フラグを出すまで待つ
- ブザーを鳴らす
- 1に戻る
順に、各ステートで利用するブロックと処理を考えます。
スタートトリガー待ち
スタートトリガーは、スイッチシンクロナイザ(SWSYNC)が
生成するトリガーiSTRGを利用します。
トリガーの1を判断して、次のステートに進めます。
スイッチから設定値入力
スイッチの設定値は、スタートトリガーを出したときには
確定しているので、ステートマシンの中では単純にラッチ
すれば充分です。
設定値からカウンタ値計算
DIPスイッチのデータはiM10、iM01に保存しているとします。
2つのレジスタ値から、計算でカウンタ値を求めます。
iM10を10の位、iM01を1の位であるとすれば、最大99に
なるので、次の代入式を実行します。
signal iTCNT : std_logic_vector(6 downto 0);
iTCNT <= ("000" & iM01) + (iM10 & "000") + ("00" & iM01 & '0');
iM10の値を10倍するため、10=8+2であることを使っています。
カウンタ値を求めたなら、次のステートに進めます。
カウンタ値をサブシーケンサに渡す
カウンタ値は計算されているので、サブシーケンサ(SUBSEQ)に
トリガーiETRGを与えて、SUBSEQが値を参照できるようにします。
トリガーiETRGを1にしたら、次のステートに進めます。
サブシーケンサが終了フラグを出すまで待つ
トリガーiETRGを0に戻して、SUBSEQが終了フラグiFINEを
1にするまで待ちます。
iFINEが1になったら、次のステートに進めます。
ブザーを鳴らす
ブザーは、ブザー鳴動器(BSEQUENCER)にトリガーを与えて
鳴動を依頼します。トリガーをiBRTGとします。
ここまでの内容を、コードに変換します。
signal iTCNT : std_logic_vector(6 downto 0);
type stype is (S0,S1,S2,S3,S4,S5) ;
signal iCUR : stype ;
signal iETRG : std_logic;
signal iBTRG : std_logic;
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
iTCNT <= (others => '0');
elsif rising_edge( CLOCK ) then
-- state machine
case iCUR is
-- wait trigger
when S0 => if ( TRG = '1' ) then
iCUR <= S1 ;
iTCNT <= ("000" & M01) + (M10 & "000") + ("00" & M01 & '0');
else
iCUR <= S0 ;
end if ;
-- wake up SUBSEQ
when S1 => iCUR <= S2 ;
-- idel
when S2 => iCUR <= S3 ;
-- wait flag
when S3 => if ( FINE = '1' ) then
iCUR <= S4 ;
else
iCUR <= S3 ;
end if ;
-- wake up BSEQUENCER
when S4 => iCUR <= S5 ;
-- return first state
when S5 => iCUR <= S0 ;
-- default
when others =>
iCUR <= S0 ;
end case ;
end if ;
end process ;
iETRG <= '1' when ( iCUR = S1 or iCUR = S2 ) else '0' ;
iBTRG <= '1' when ( iCUR = S4 or iCUR = S5 ) else '0' ;
iBUSY <= '1' when ( iCUR = S1 or iCUR = S2 or iCUR = S3 ) else '0' ;
計時中は、7セグメントLEDのDpを点滅させる仕様です。
イネーブルフラグiBUSYを用意して、LED点滅器(FLAHSER)を動かします。
計時は、iCURがS1、S2、S3のときに限定されます。
論理圧縮は、処理系に任せています。
iBUSY <= '1' when ( iCUR = S1 or iCUR = S2 or iCUR = S3 ) else '0' ;
ここまでで、制御シーケンスが確定したので、サブシーケンサ(SUBSEQ)の
動作を考えます。
SUBSEQは、トリガーで起動されたなら、指定されたカウンタ
の値を、60秒ごとにデクリメントして、0でフラグを設定
すればよいでしょう。
トップダウンでSUBSEQの動作を記述してみます。
- トリガー待ち
- カウンタ値入力、1秒カウンタを0に設定
- カウンタ値が0なら6ステートに、0でないなら4ステートに
- 1秒経過トリガー待ち、1秒カウンタのインクリメントし5ステートに
- 1秒カウンタが60ならゼロクリア、カウンタ値をデクリメントし、3ステートに
- 終了フラグをクリアして、1に戻る
60秒経過を判定しなければ、このシーケンサを実現できません。
60秒経過は、1秒経過が実現できれば、カウンタを動かして判断できます。
1秒経過を知る処理を考えます。
システムクロック分周器(CLOCKGEN)で、1秒クロックは生成しているので
1秒クロックが0から1へと遷移する立ち上がりを捕捉し、1秒変化を通知
するとよいでしょう。
ticktackとして定義します。
-- tick tack
process ( nRESET , iPCLK )
begin
if ( nRESET = '0' ) then
iSSCLK <= "1111" ;
elsif rising_edge( iPCLK ) then
iSSCLK <= iSSCLK(2 downto 0) & iSCLK ;
end if ;
end process ;
SOUT <= '1' when ( iSSCLK = "0001" ) else '0' ;
SOUTを見ながら、60秒経過をカウントします。
サブのシーケンサの動作を定義します。
- トリガー待ち
- カウンタ値入力、秒カウンタ値を0とする
- カウンタ値が0なら7ステートに、0でないなら4ステートに
- SOUTの1を判定し、秒カウンタ値をインクリメント
- 秒カウンタ値が60なら0に戻し、カウンタ値をデクリメント
- 3ステートにもどる
- 終了フラグをセット
- 終了フラグをクリアして、1に戻る
各ステートの処理が確定したので、コードに変換します。
process ( nRESET , iPCLK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
iMCNT <= "000000" ;
iTSCNT <= "0000000";
elsif rising_edge( iPCLK ) then
-- state machine
case iCUR is
-- wait trigger
when S0 => if ( TRG = '1' ) then
iCUR <= S1 ;
iMCNT <= "000000" ;
iTSCNT <= COUNTER ;
else
iCUR <= S0 ;
end if ;
-- judge time over
when S1 => if ( conv_integer(iTSCNT) = 0 ) then
iCUR <= S5 ;
else
iCUR <= S2 ;
end if ;
-- update 60 second counters
when S2 => if ( EDGE = '1' ) then
iCUR <= S3 ;
iMCNT <= iMCNT + 1 ;
else
iCUR <= S2 ;
end if ;
-- judge 60 second
when S3 => if ( conv_integer(iMCNT) = 60 ) then
iCUR <= S4 ;
iMCNT <= "000000" ;
iTSCNT <= iTSCNT - 1 ;
else
iCUR <= S2 ;
end if ;
-- handling
when S4 => iCUR <= S1 ;
-- set iFINE
when S5 => iCUR <= S6 ;
-- idle
when S6 => iCUR <= S7 ;
-- clear iFINE and return first state
when S7 => iCUR <= S0 ;
-- default
when others =>
iCUR <= S0 ;
end case ;
end if ;
end process ;
SUBSEQは、同時に分をカウントダウンする処理も担当させます。
カウントダウン結果の表示は、7ビットバイナリーカウンタを
2けたの10進数に変換する処理で実現します。
この処理は、バイナリーカウンタの値から10を引くことの繰り返しです。
アセンブリ言語で除算するのと同じ方法です。
10の位、1の位のレジスタをiL10、iL01として、除算を考えます。
- トリガー待ち
- バイナリーカウンタ値をコピー(iTMP)、商を格納するレジスタを0クリア(iL10)
- iTMPが10未満では5に、それ以外ではiTMP=iTMP-10、iL10=iL10+1を実行し4へ
- 3にもどる
- iL10(商)、iL01=iTMP(3 downto 0)(余り)
- 1に戻る
シーケンサに変換します。
process ( nRESET , iPCLK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
iL10 <= "0000" ;
iL01 <= "0000" ;
iTMP <= "0000000" ;
elsif rising_edge( iPCLK ) then
-- state machine
case iCUR is
-- wait trigger
when S0 => if ( TRG = '1' ) then
iCUR <= S1 ;
iL10 <= "0000" ;
iTMP <= COUNTER ;
else
iCUR <= S0 ;
end if ;
-- calculate (loop)
when S1 => if ( conv_integer(iTMP) < 10 ) then
iCUR <= S2 ;
else
iL10 <= iL10 + 1 ;
iTMP <= iTMP - 10 ;
iCUR <= S1 ;
end if ;
-- convert
when S2 => iL01 <= iTMP(3 downto 0) ;
iCUR <= S3 ;
-- update counter and return first state
when S3 => iCUR <= S0 ;
-- default
when others =>
iCUR <= S0 ;
end case ;
end if ;
end process ;
すべて定義できたので、各ブロックの動作をテストします。
テストは、下から上にします。
下のブロックのテストができたならば、そのブロックを使って
いる上位ブロックをテストしていきます。
また、並列に動作している場合は、個別にテストします。
並列に動作しているとみなせるのは、次の2つです。
- ticktack(1秒変化)
- divider(分の2けた計算)
ticktackは、次のコードでテストしました。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity ticktack is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- sub clock
SCLOCK : in std_logic ;
-- counter out
SOUT : out std_logic --;
) ;
end ticktack;
architecture Behavioral of ticktack is
signal iSSCLK : std_logic_vector(3 downto 0) ;
begin
-- tick tack
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSSCLK <= "1111" ;
elsif rising_edge( CLOCK ) then
iSSCLK <= iSSCLK(2 downto 0) & SCLOCK ;
end if ;
end process ;
SOUT <= '1' when ( iSSCLK = "0001" ) else '0' ;
end Behavioral;
与えるクロックSCLOCKを1秒程度にして、SOUTをアナログ
テスターの電圧測定で測定すると、針が動いてよくわかります。
分の2けた計算は、次のコードでテストしました。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity divider is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- trigger
TRG : in std_logic ;
-- data
COUNTER : in std_logic_vector(6 downto 0) ;
-- counter out
D10 : out std_logic_vector(3 downto 0) ;
D01 : out std_logic_vector(3 downto 0) --;
) ;
end divider;
architecture Behavioral of divider is
type stype is (S0,S1,S2,S3);
signal iCUR : stype ;
signal iL10 : std_logic_vector(3 downto 0);
signal iL01 : std_logic_vector(3 downto 0);
signal iTMP : std_logic_vector(6 downto 0);
begin
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
iL10 <= "0000" ;
iL01 <= "0000" ;
iTMP <= "0000000" ;
elsif rising_edge( CLOCK ) then
-- state machine
case iCUR is
-- wait trigger
when S0 => if ( TRG = '1' ) then
iCUR <= S1 ;
iL10 <= "0000" ;
iTMP <= COUNTER ;
else
iCUR <= S0 ;
end if ;
-- calculate (loop)
when S1 => if ( conv_integer(iTMP) < 10 ) then
iCUR <= S2 ;
else
iL10 <= iL10 + 1 ;
iTMP <= iTMP - 10 ;
iCUR <= S1 ;
end if ;
-- convert
when S2 => iL01 <= iTMP(3 downto 0) ;
iCUR <= S3 ;
-- update counter and return first state
when S3 => iCUR <= S0 ;
-- default
when others =>
iCUR <= S0 ;
end case ;
end if ;
end process ;
-- output
D10 <= iL10 ;
D01 <= iL01 ;
end Behavioral;
8入力のDIPスイッチを使います。
7個のスイッチはカウンタ値入力に割り当てます。
残りの1個のスイッチは、トリガーに利用します。
出力は、8個のLEDの点灯、消灯で確認します。
最下位レベルのブロックがテストできたならば
最上位のSEQUENCERをテストします。
テストに利用したコードは、以下です。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity sequencer is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- trigger and digit
TRG : in std_logic ;
M10 : in std_logic_vector(3 downto 0) ;
M01 : in std_logic_vector(3 downto 0) ;
TCNT : out std_logic_vector(6 downto 0) ;
-- sub sequencer trigger and flag
ETRG : out std_logic ;
FINE : in std_logic ;
-- status
BUSY : out std_logic ;
-- buzzer trigger
BTRG : out std_logic --;
);
end sequencer;
architecture Behavioral of sequencer is
signal iTCNT : std_logic_vector(6 downto 0);
type stype is (S0,S1,S2,S3,S4,S5) ;
signal iCUR : stype ;
begin
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
iTCNT <= (others => '0');
elsif rising_edge( CLOCK ) then
-- state machine
case iCUR is
-- wait trigger
when S0 => if ( TRG = '1' ) then
iCUR <= S1 ;
iTCNT <= ("000" & M01) + (M10 & "000") + ("00" & M01 & '0');
else
iCUR <= S0 ;
end if ;
-- wake up SUBSEQ
when S1 => iCUR <= S2 ;
-- idel
when S2 => iCUR <= S3 ;
-- wait flag
when S3 => if ( FINE = '1' ) then
iCUR <= S4 ;
else
iCUR <= S3 ;
end if ;
-- wake up BSEQUENCER
when S4 => iCUR <= S5 ;
-- return first state
when S5 => iCUR <= S0 ;
-- default
when others =>
iCUR <= S0 ;
end case ;
end if ;
end process ;
ETRG <= '1' when ( iCUR = S1 or iCUR = S2 ) else '0' ;
BTRG <= '1' when ( iCUR = S4 or iCUR = S5 ) else '0' ;
BUSY <= '1' when ( iCUR = S1 or iCUR = S2 or iCUR = S3 ) else '0' ;
TCNT <= iTCNT ;
end Behavioral;
8入力のDIPスイッチをM10、M01の設定に利用します。
トリガースイッチは、2個をプッシュスイッチに割当てます。
出力は、9個のLEDを利用します。
M10、M01から計算したカウンタ値を、7個のLEDの点灯、消灯で確認します。
ETRG、BTRG、BUSYは3個のLEDの点灯、消灯で確認します
SUBSEQのテストコードは、以下です。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity subseq is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- parent sequencer handling
TRG : in std_logic ;
COUNTER : in std_logic_vector(6 downto 0) ;
FINE : out std_logic ;
-- ticktack
EDGE : in std_logic ;
-- divider
DTRG : out std_logic ;
-- counter out
NOWCOUNT : out std_logic_vector(6 downto 0) --;
);
end subseq;
architecture Behavioral of subseq is
signal iMCNT : std_logic_vector(5 downto 0);
signal iTSCNT : std_logic_vector(6 downto 0);
type sstype is (S0,S1,S2,S3,S4,S5,S6,S7);
signal iCUR : sstype ;
begin
-- sub sequencer
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
iMCNT <= "000000" ;
iTSCNT <= "0000000";
elsif rising_edge( CLOCK ) then
-- state machine
case iCUR is
-- wait trigger
when S0 => if ( TRG = '1' ) then
iCUR <= S1 ;
iMCNT <= "000000" ;
iTSCNT <= COUNTER ;
else
iCUR <= S0 ;
end if ;
-- judge time over
when S1 => if ( conv_integer(iTSCNT) = 0 ) then
iCUR <= S5 ;
else
iCUR <= S2 ;
end if ;
-- update 60 second counters
when S2 => if ( EDGE = '1' ) then
iCUR <= S3 ;
iMCNT <= iMCNT + 1 ;
else
iCUR <= S2 ;
end if ;
-- judge 60 second
when S3 => if ( conv_integer(iMCNT) = 60 ) then
iCUR <= S4 ;
iMCNT <= "000000" ;
iTSCNT <= iTSCNT - 1 ;
else
iCUR <= S3 ;
end if ;
-- handling
when S4 => iCUR <= S1 ;
-- set iFINE
when S5 => iCUR <= S6 ;
-- idle
when S6 => iCUR <= S7 ;
-- clear iFINE and return first state
when S7 => iCUR <= S0 ;
-- default
when others =>
iCUR <= S0 ;
end case ;
end if ;
end process ;
DTRG <= '1' when ( iCUR = S4 ) else '0' ;
FINE <= '1' when ( iCUR = S5 or iCUR = S6 ) else '0' ;
NOWCOUNT <= iTSCNT ;
end Behavioral;
8入力のDIPスイッチをカウンタ値の入力とEDGEに割当てます。
カウンタ値の入力は、BCDにしておくことを忘れないようにします。
プッシュスイッチを1個利用して、TRGの代用とします。
カウンタの出力は、7個のLEDで2進表示します。
他の2個のLEDで、DTRG、FINEを確認します。
VHDLシミュレータを利用すると、コンピュータの画面上ですべて
終わりますが、実際のスイッチやLEDを接続した環境を使う方が
便利です。シミュレータは、コンピュータ内の仮想空間上での
動作なので、実感が得られないのと、体感スピードが掴めません。
7セグメントLEDデコーダ(DISPLAY)
7セグメントLEDデコーダは、2けたの7セグメントLEDがあるので
2個必要と単純に考えますが、ダイナミック点灯するので、実際は1個に
します。
7セグメントLEDデコーダそのものは、単純です。
次のように定義すれば、おしまいです。
process (LVAL)
begin
case LVAL is
when "0000" => TLEDOUT <= "0111111" ; -- 0
when "0001" => TLEDOUT <= "0000110" ; -- 1
when "0010" => TLEDOUT <= "1011011" ; -- 2
when "0011" => TLEDOUT <= "1001111" ; -- 3
when "0100" => TLEDOUT <= "1100110" ; -- 4
when "0101" => TLEDOUT <= "1101101" ; -- 5
when "0110" => TLEDOUT <= "1111101" ; -- 6
when "0111" => TLEDOUT <= "0000111" ; -- 7
when "1000" => TLEDOUT <= "1111111" ; -- 8
when "1001" => TLEDOUT <= "1101111" ; -- 9
when others => TLEDOUT <= "0000000" ; -- dummy
end case;
end process;
今回は2つの7セグメントLEDを利用するので、配列に点灯
パターンを保存しておき、インデックスを使いデータを引き出す
テーブル参照の方が楽です。
テーブル参照で利用する7セグメントLEDの点灯パターンを
定義すると、以下となります。
subtype LEDPAT is std_logic_vector(6 downto 0) ;
type LEDCODE is array (0 to 15) of LEDPAT ;
constant LEDP : LEDCODE := (
"0111111", -- 0
"0000110", -- 1
"1011011", -- 2
"1001111", -- 3
"1100110", -- 4
"1101101", -- 5
"1111101", -- 6
"0000111", -- 7
"1111111", -- 8
"1101111", -- 9
"0000000", -- dummy
"0000000", -- dummy
"0000000", -- dummy
"0000000", -- dummy
"0000000", -- dummy
"0000000" -- dummy
) ;
設定か計時かを判断するのは、このブロックに担当させます。
表示する数字は、設定時と計時中は異なるので、シーケンサを
利用して切り替えられるようにします。
計時中を示すフラグは、iBUSYで既に定義されているので
iBUSYの論理値で、使うレジスタを切り替えます。
上の図で、シーケンス動作を考え、まとめます。
process ( nRESET , iPCLK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
iDM10 <= "0000" ;
iDM01 <= "0000" ;
elsif rising_edge( iPCLK ) then
-- state machine
case iCUR is
-- judge and latch
when S0 => if ( iBUSY = '1' ) then
iDM10 <= DB10 ;
iDM01 <= DB01 ;
else
iDM10 <= DA10 ;
iDM01 <= DA01 ;
end if ;
iCUR <= S1 ;
-- upper digit handling
when S1 => iCUR <= S2 ;
DOUT <= LEDP( conv_integer(iDM10) ) ;
-- enable upper power
when S2 => iCUR <= S3 ;
-- disable upper power
when S3 => iCUR <= S4 ;
-- lower digit handling
when S4 => iCUR <= S5 ;
DOUT <= LEDP( conv_integer(iDM01) ) ;
-- enable lower power
when S5 => iCUR <= S6 ;
-- disable lower power
when S6 => iCUR <= S7 ;
-- return first state
when S7 => iCUR <= S0 ;
-- default
when others => iCUR <= S0 ;
end case ;
end if ;
end process ;
iSELU <= '0' when ( iCUR = S2 ) else '1' ;
iSELL <= '0' when ( iCUR = S5 ) else '1' ;
このコードをCPLDのXC95108が実装されているボードに載せてテストしました。
その場合のソースコードは、以下です。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity disp is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- select
SEL : in std_logic ;
-- data in
DA10 : in std_logic_vector(3 downto 0) ;
DA01 : in std_logic_vector(3 downto 0) ;
-- data in
DB10 : in std_logic_vector(3 downto 0) ;
DB01 : in std_logic_vector(3 downto 0) ;
-- data out
SELOUT : out std_logic_vector(1 downto 0) ;
DOUT : out std_logic_vector(6 downto 0) --;
);
end disp;
architecture Behavioral of disp is
subtype LEDPAT is std_logic_vector(6 downto 0) ;
type LEDCODE is array (0 to 15) of LEDPAT ;
constant LEDP : LEDCODE := (
"0111111", -- 0
"0000110", -- 1
"1011011", -- 2
"1001111", -- 3
"1100110", -- 4
"1101101", -- 5
"1111101", -- 6
"0000111", -- 7
"1111111", -- 8
"1101111", -- 9
"0000000", -- dummy
"0000000", -- dummy
"0000000", -- dummy
"0000000", -- dummy
"0000000", -- dummy
"0000000" -- dummy
) ;
type stype is (S0,S1,S2,S3,S4,S5,S6,S7) ;
signal iCUR : stype ;
signal iDM10 : std_logic_vector(3 downto 0);
signal iDM01 : std_logic_vector(3 downto 0);
signal iSELU : std_logic ;
signal iSELL : std_logic ;
begin
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
iDM10 <= "0000" ;
iDM01 <= "0000" ;
elsif rising_edge( CLOCK ) then
-- state machine
case iCUR is
-- judge and latch
when S0 => if ( SEL = '1' ) then
iDM10 <= DB10 ;
iDM01 <= DB01 ;
else
iDM10 <= DA10 ;
iDM01 <= DA01 ;
end if ;
iCUR <= S1 ;
-- upper digit handling
when S1 => iCUR <= S2 ;
DOUT <= LEDP( conv_integer(iDM10) ) ;
-- enable upper power
when S2 => iCUR <= S3 ;
-- disable upper power
when S3 => iCUR <= S4 ;
-- lower digit handling
when S4 => iCUR <= S5 ;
DOUT <= LEDP( conv_integer(iDM01) ) ;
-- enable lower power
when S5 => iCUR <= S6 ;
-- disable lower power
when S6 => iCUR <= S7 ;
-- return first state
when S7 => iCUR <= S0 ;
-- default
when others => iCUR <= S0 ;
end case ;
end if ;
end process ;
iSELU <= '0' when ( iCUR = S2 ) else '1' ;
iSELL <= '0' when ( iCUR = S5 ) else '1' ;
SELOUT <= iSELU & iSELL;
end Behavioral;
このテストには、8ビットのDIPスイッチを2個(BCDを4けた入力)
トグルスイッチを1個利用します。
7個のLEDで、7セグメントLEDの点灯パターンを、2個のLEDで
上位、下位のどちらの7セグメントLEDを選択しているかを判断
します。
ブザー鳴動器(BSEQUENCER)
スピーカにクロックを送るとブザーは、鳴ります。
ブザーを鳴らすタイミングと継続時間が必要です。
タイミングと継続時間を処理するには、シーケンサを使います。
ブザー鳴動のシーケンスを書出してみます。
- 開始トリガー待ち
- カウンタ初期化
- カウンタデクリメント
- カウンタの値が0でないなら3にもどる
- 1にもどる
シーケンスの内容から、カウンタ値が正であれば、クロックiBCLKを
スピーカに出力するイネーブル信号を生成できれば、ブザー鳴動が
実現できそうです。
カウンタ値が正のときに、BUZOUTに'1'を出力し、クロックiBCLKと
ANDをとります。これで、ブザー鳴動になります。
シーケンサを動かすには、計時シーケンサからトリガーをもらいます。
計時シーケンサが与えるトリガーをiBTRGとすると、ブザー鳴動用の
ローカルシーケンサは、次のように記述できます。
type stype is (S0,S1,S2,S3) ;
signal iBCUR : stype ;
signal iBCOUNT : std_logic_vector(9 downto 0) ;
process ( nRESET , iPCLK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
-- time counter
iBCOUNT <= (others => '0' ) ;
elsif rising_edge( iPCLK ) then
-- judge
case iCUR is
-- wait trigger
when S0 => if ( BTRG = '1' ) then
iCUR <= S1 ;
end if ;
-- set counter value
when S1 => iBCOUNT <= (others => '1' ) ;
iCUR <= S2 ;
-- counter decrement
when S2 => iBCOUNT <= iBCOUNT - 1 ;
iCUR <= S3 ;
-- judge
when S3 => if ( conv_integer( iBCOUNT ) = 0 ) then
iCUR <= S0 ;
else
iCUR <= S2 ;
end if ;
-- default
when others => iCUR <= S0 ;
end case ;
end if ;
end process ;
iBUZOUT <= '1' when ( iCUR >= S1 ) else '0' ;
ローカルシーケンサに、iSCLKを使ってもよいのではと考えましたが
計時シーケンサは、iPCLKで動作しているので、このローカルシーケンサ
がiSCLKで動いていると、トリガーをとり損ねる可能性があります。
計時シーケンサとの同期を考え、iPCLKを使います。
このコードをCPLDのXC9572が実装されているボードに載せてテストしました。
その場合のソースコードは、以下です。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity buz is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- trigger
BTRG : in std_logic ;
-- buzzer sound
BUZOUT : out std_logic --;
);
end buz;
architecture Behavioral of buz is
type stype is (S0,S1,S2,S3) ;
signal iCUR : stype ;
signal iBCOUNT : std_logic_vector(9 downto 0) ;
begin
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
-- time counter
iBCOUNT <= (others => '0' ) ;
elsif rising_edge( CLOCK ) then
-- judge
case iCUR is
-- wait trigger
when S0 => if ( BTRG = '1' ) then
iCUR <= S1 ;
else
iCUR <= S0 ;
end if ;
-- set counter value
when S1 => iBCOUNT <= (others => '1' ) ;
iCUR <= S2 ;
-- counter decrement
when S2 => iBCOUNT <= iBCOUNT - 1 ;
iCUR <= S3 ;
-- judge
when S3 => if ( conv_integer( iBCOUNT ) = 0 ) then
iCUR <= S0 ;
else
iCUR <= S2 ;
end if ;
-- default
when others =>
iCUR <= S0 ;
end case ;
end if ;
end process ;
BUZOUT <= '1' when ( iCUR >= S1 ) else '0' ;
end Behavioral;
トリガーとなるBRTGは、プッシュスイッチを利用します。
論理レベルの確認だけなので、LEDで確認できます。
LED点滅器(FLAHSER)
LEDを点滅させるには、1秒ごとにHかLを出力するだけで充分です。
タイマー動作をしていなければ、消灯することが望ましいでしょう。
LEDは、Lで点灯として、動作をまとめます。
- 計時中は、1秒ごとにHかLを出力
- 計時でないときは、Hを出力
1秒ごとのH、L出力は、クロックiSCLKをそのまま出力すれば実現できます。
計時シーケンサが、計時中であることを示すフラグを出しているので
フラグの状態で、Hを出力するかクロックを出力するを指定します。
図で示すと、次のようになります。
図からコードに変換します。
SLEDOUT <= iSCLK when ( iBUSY = '1' ) else '1' ;
全ソースコード
全ブロックを定義したので、まとめます。
エンティティとトップモジュールです。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity ktimer is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- system clock 32.768kHz
-- start trigger
STRG : in std_logic ;
-- minute
M10 : in std_logic_vector(3 downto 0) ; -- x10
M01 : in std_logic_vector(3 downto 0) ; -- x1
-- output
TLEDSEL : out std_logic_vector(1 downto 0) ;
TLEDOUT : out std_logic_vector(6 downto 0) ;
SLEDOUT : out std_logic ;
BUZOUT : out std_logic -- ;
);
end ktimer;
architecture Behavioral of ktimer is
-- system clock
component mclock
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- clock
BCLK : out std_logic ;
PCLK : out std_logic ;
SCLK : out std_logic --;
);
end component ;
signal iBCLK : std_logic ;
signal iPCLK : std_logic ;
signal iSCLK : std_logic ;
-- synchronizer
component isync
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- trigger
TRG : in std_logic ;
-- clock
ITRG : out std_logic --;
);
end component ;
signal iSTRG : std_logic ;
-- display
component disp
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- select
SEL : in std_logic ;
-- data in
DA10 : in std_logic_vector(3 downto 0) ;
DA01 : in std_logic_vector(3 downto 0) ;
-- data in
DB10 : in std_logic_vector(3 downto 0) ;
DB01 : in std_logic_vector(3 downto 0) ;
-- data out
SELOUT : out std_logic_vector(1 downto 0) ;
DOUT : out std_logic_vector(6 downto 0) --;
);
end component ;
-- buzzer
component BUZ
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- trigger
BTRG : in std_logic ;
-- buzzer sound
BUZOUT : out std_logic --;
);
end component ;
signal iBENABLE : std_logic ;
-- sequencer
component sequencer
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- trigger and digit
TRG : in std_logic ;
M10 : in std_logic_vector(3 downto 0) ;
M01 : in std_logic_vector(3 downto 0) ;
TCNT : out std_logic_vector(6 downto 0) ;
-- sub sequencer trigger and flag
ETRG : out std_logic ;
FINE : in std_logic ;
-- status
BUSY : out std_logic ;
-- buzzer trigger
BTRG : out std_logic --;
);
end component ;
signal iBUSY : std_logic;
signal iBTRG : std_logic;
signal iFINE : std_logic;
signal iNOWCNT : std_logic_vector(6 downto 0);
signal iL10 : std_logic_vector(3 downto 0);
signal iL01 : std_logic_vector(3 downto 0);
-- sub sequencer
component subseq
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- parent sequencer handling
TRG : in std_logic ;
COUNTER : in std_logic_vector(6 downto 0) ;
FINE : out std_logic ;
-- ticktack
EDGE : in std_logic ;
-- divider
DTRG : out std_logic ;
-- counter out
NOWCOUNT : out std_logic_vector(6 downto 0) --;
);
end component ;
signal iETRG : std_logic;
signal iSNOWCNT : std_logic_vector(6 downto 0) ;
-- ticktack
component ticktack
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- sub clock
SCLOCK : in std_logic ;
-- counter out
SOUT : out std_logic --;
) ;
end component ;
signal iTICKTACK : std_logic ;
-- divider
component divider
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- trigger
TRG : in std_logic ;
-- data
COUNTER : in std_logic_vector(6 downto 0) ;
-- counter out
D10 : out std_logic_vector(3 downto 0) ;
D01 : out std_logic_vector(3 downto 0) --;
) ;
end component ;
signal iCTRG : std_logic ;
begin
-- generate system clock
MCLOCKP : mclock port map (nRESET,CLOCK,iBCLK,iPCLK,iSCLK) ;
-- synchronizer
ISYNCP : isync port map (nRESET,iPCLK,STRG,iSTRG) ;
-- display
DISPL : disp port map (nRESET,iPCLK,iBUSY,iL10,iL01,M10,M01,TLEDSEL,TLEDOUT) ;
-- buzzer
BUZZER : buz port map (nRESET,iPCLK,iBTRG,iBENABLE) ;
BUZOUT <= iBCLK and iBENABLE ;
-- LED flashing
SLEDOUT <= iSCLK when (iBUSY = '1') else '1' ;
-- sequencer
SEQP : sequencer port map (nRESET,iPCLK,iSTRG,M10,M01,iNOWCNT,iETRG,iFINE,iBUSY,iBTRG);
-- sub sequencer
SSEQP : subseq port map (nRESET,iPCLK,iETRG,iNOWCNT,iFINE,iTICKTACK,iCTRG,iSNOWCNT);
-- ticktack
TICKP : ticktack port map (nRESET,iPCLK,iSCLK,iTICKTACK);
-- divider
DIVX : divider port map (nRESET,iPCLK,iCTRG,iSNOWCNT,iL10,iL01) ;
end Behavioral;
クロックジェネレータです。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity mclock is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- clock
BCLK : out std_logic ;
PCLK : out std_logic ;
SCLK : out std_logic --;
);
end mclock;
architecture Behavioral of mclock is
signal iSCOUNT : std_logic_vector(15 downto 0) ;
begin
-- generate system clock
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSCOUNT <= (others => '0') ;
elsif rising_edge( CLOCK ) then
-- increment
iSCOUNT <= iSCOUNT + '1' ;
end if ;
end process ;
-- output
BCLK <= '1' when ( iSCOUNT(5 downto 0) = "101000" ) else '0' ;
PCLK <= '1' when ( iSCOUNT(6 downto 0) = "1100011" ) else '0' ;
SCLK <= '1' when ( conv_integer( iSCOUNT ) < 50 ) else '0' ;
end Behavioral;
入力シンクロナイザは、以下です。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity isync is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- trigger
TRG : in std_logic ;
-- out
ITRG : out std_logic --;
);
end isync;
architecture Behavioral of isync is
signal iSTRG : std_logic_vector(5 downto 0) ;
begin
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSTRG <= (others => '1') ;
elsif rising_edge( CLOCK ) then
-- get logic level
iSTRG <= iSTRG(4 downto 0) & TRG ;
end if ;
end process ;
ITRG <= '1' when ( conv_integer(iSTRG) = 0 ) else '0' ;
end Behavioral;
出力は、表示、ブザー、デシマルポイント点滅になります。
デシマルポイント点滅は、トップレベルで実施しています。
7セグメントLEDへの表示は、以下です。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity disp is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- select
SEL : in std_logic ;
-- data in
DA10 : in std_logic_vector(3 downto 0) ;
DA01 : in std_logic_vector(3 downto 0) ;
-- data in
DB10 : in std_logic_vector(3 downto 0) ;
DB01 : in std_logic_vector(3 downto 0) ;
-- data out
SELOUT : out std_logic_vector(1 downto 0) ;
DOUT : out std_logic_vector(6 downto 0) --;
);
end disp;
architecture Behavioral of disp is
subtype LEDPAT is std_logic_vector(6 downto 0) ;
type LEDCODE is array (0 to 15) of LEDPAT ;
constant LEDP : LEDCODE := (
"0111111", -- 0
"0000110", -- 1
"1011011", -- 2
"1001111", -- 3
"1100110", -- 4
"1101101", -- 5
"1111101", -- 6
"0000111", -- 7
"1111111", -- 8
"1101111", -- 9
"0000000", -- dummy
"0000000", -- dummy
"0000000", -- dummy
"0000000", -- dummy
"0000000", -- dummy
"0000000" -- dummy
) ;
type stype is (S0,S1,S2,S3,S4,S5,S6,S7) ;
signal iCUR : stype ;
signal iDM10 : std_logic_vector(3 downto 0);
signal iDM01 : std_logic_vector(3 downto 0);
signal iSELU : std_logic ;
signal iSELL : std_logic ;
begin
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
iDM10 <= "0000" ;
iDM01 <= "0000" ;
elsif rising_edge( CLOCK ) then
-- state machine
case iCUR is
-- judge and latch
when S0 => if ( SEL = '1' ) then
iDM10 <= DB10 ;
iDM01 <= DB01 ;
else
iDM10 <= DA10 ;
iDM01 <= DA01 ;
end if ;
iCUR <= S1 ;
-- upper digit handling
when S1 => iCUR <= S2 ;
DOUT <= LEDP( conv_integer(iDM10) ) ;
-- enable upper power
when S2 => iCUR <= S3 ;
-- disable upper power
when S3 => iCUR <= S4 ;
-- lower digit handling
when S4 => iCUR <= S5 ;
DOUT <= LEDP( conv_integer(iDM01) ) ;
-- enable lower power
when S5 => iCUR <= S6 ;
-- disable lower power
when S6 => iCUR <= S7 ;
-- return first state
when S7 => iCUR <= S0 ;
-- default
when others => iCUR <= S0 ;
end case ;
end if ;
end process ;
iSELU <= '0' when ( iCUR = S2 ) else '1' ;
iSELL <= '0' when ( iCUR = S5 ) else '1' ;
SELOUT <= iSELU & iSELL ;
end Behavioral;
ブザーです。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity buz is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- trigger
BTRG : in std_logic ;
-- buzzer sound
BUZOUT : out std_logic --;
);
end buz;
architecture Behavioral of buz is
type stype is (S0,S1,S2,S3) ;
signal iCUR : stype ;
signal iBCOUNT : std_logic_vector(9 downto 0) ;
begin
-- buzzer
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
-- time counter
iBCOUNT <= (others => '0' ) ;
elsif rising_edge( CLOCK ) then
-- judge
case iCUR is
-- wait trigger
when S0 => if ( BTRG = '1' ) then
iCUR <= S1 ;
else
iCUR <= S0 ;
end if ;
-- set counter value
when S1 => iBCOUNT <= (others => '1' ) ;
iCUR <= S2 ;
-- counter decrement
when S2 => iBCOUNT <= iBCOUNT - 1 ;
iCUR <= S3 ;
-- judge
when S3 => if ( conv_integer( iBCOUNT ) = 0 ) then
iCUR <= S0 ;
else
iCUR <= S2 ;
end if ;
-- default
when others =>
iCUR <= S0 ;
end case ;
end if ;
end process ;
BUZOUT <= '1' when ( iCUR >= S1 ) else '0' ;
end Behavioral;
シーケンサは、2つにわけてあります。
サブシーケンサを制御するシーケンサ定義です。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity sequencer is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- trigger and digit
TRG : in std_logic ;
M10 : in std_logic_vector(3 downto 0) ;
M01 : in std_logic_vector(3 downto 0) ;
TCNT : out std_logic_vector(6 downto 0) ;
-- sub sequencer trigger and flag
ETRG : out std_logic ;
FINE : in std_logic ;
-- status
BUSY : out std_logic ;
-- buzzer trigger
BTRG : out std_logic --;
);
end sequencer;
architecture Behavioral of sequencer is
signal iTCNT : std_logic_vector(6 downto 0);
type stype is (S0,S1,S2,S3,S4,S5) ;
signal iCUR : stype ;
begin
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
iTCNT <= (others => '0');
elsif rising_edge( CLOCK ) then
-- state machine
case iCUR is
-- wait trigger
when S0 => if ( TRG = '1' ) then
iCUR <= S1 ;
iTCNT <= ("000" & M01) + (M10 & "000") + ("00" & M01 & '0');
else
iCUR <= S0 ;
end if ;
-- wake up SUBSEQ
when S1 => iCUR <= S2 ;
-- idel
when S2 => iCUR <= S3 ;
-- wait flag
when S3 => if ( FINE = '1' ) then
iCUR <= S4 ;
else
iCUR <= S3 ;
end if ;
-- wake up BSEQUENCER
when S4 => iCUR <= S5 ;
-- return first state
when S5 => iCUR <= S0 ;
-- default
when others =>
iCUR <= S0 ;
end case ;
end if ;
end process ;
ETRG <= '1' when ( iCUR = S1 or iCUR = S2 ) else '0' ;
BTRG <= '1' when ( iCUR = S4 or iCUR = S5 ) else '0' ;
BUSY <= '1' when ( iCUR = S1 or iCUR = S2 or iCUR = S3 ) else '0' ;
TCNT <= iTCNT ;
end Behavioral;
サブシーケンサです。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity subseq is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- parent sequencer handling
TRG : in std_logic ;
COUNTER : in std_logic_vector(6 downto 0) ;
FINE : out std_logic ;
-- ticktack
EDGE : in std_logic ;
-- divider
DTRG : out std_logic ;
-- counter out
NOWCOUNT : out std_logic_vector(6 downto 0) --;
);
end subseq;
architecture Behavioral of subseq is
signal iMCNT : std_logic_vector(5 downto 0);
signal iTSCNT : std_logic_vector(6 downto 0);
type sstype is (S0,S1,S2,S3,S4,S5,S6,S7);
signal iCUR : sstype ;
begin
-- sub sequencer
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
iMCNT <= "000000" ;
iTSCNT <= "0000000";
elsif rising_edge( CLOCK ) then
-- state machine
case iCUR is
-- wait trigger
when S0 => if ( TRG = '1' ) then
iCUR <= S1 ;
iMCNT <= "000000" ;
iTSCNT <= COUNTER ;
else
iCUR <= S0 ;
end if ;
-- judge time over
when S1 => if ( conv_integer(iTSCNT) = 0 ) then
iCUR <= S5 ;
else
iCUR <= S2 ;
end if ;
-- update 60 second counters
when S2 => if ( EDGE = '1' ) then
iCUR <= S3 ;
iMCNT <= iMCNT + 1 ;
else
iCUR <= S2 ;
end if ;
-- judge 60 second
when S3 => if ( conv_integer(iMCNT) = 60 ) then
iCUR <= S4 ;
iMCNT <= "000000" ;
iTSCNT <= iTSCNT - 1 ;
else
iCUR <= S2 ;
end if ;
-- handling
when S4 => iCUR <= S1 ;
-- set iFINE
when S5 => iCUR <= S6 ;
-- idle
when S6 => iCUR <= S7 ;
-- clear iFINE and return first state
when S7 => iCUR <= S0 ;
-- default
when others =>
iCUR <= S0 ;
end case ;
end if ;
end process ;
DTRG <= '1' when ( iCUR = S4 ) else '0' ;
FINE <= '1' when ( iCUR = S5 or iCUR = S6 ) else '0' ;
NOWCOUNT <= iTSCNT ;
end Behavioral;
サブシーケンサは、1秒クロックの立ち上がりを利用して
60秒経過を求めています。TickTackとして定義しました。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity ticktack is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- sub clock
SCLOCK : in std_logic ;
-- counter out
SOUT : out std_logic --;
) ;
end ticktack;
architecture Behavioral of ticktack is
signal iSSCLK : std_logic_vector(3 downto 0) ;
begin
-- tick tack
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSSCLK <= "1111" ;
elsif rising_edge( CLOCK ) then
iSSCLK <= iSSCLK(2 downto 0) & SCLOCK ;
end if ;
end process ;
SOUT <= '1' when ( iSSCLK = "0001" ) else '0' ;
end Behavioral;
サブシーケンサが計算した秒を、分に変換するために
dividerを定義しています。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity divider is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- reference clock
-- trigger
TRG : in std_logic ;
-- data
COUNTER : in std_logic_vector(6 downto 0) ;
-- counter out
D10 : out std_logic_vector(3 downto 0) ;
D01 : out std_logic_vector(3 downto 0) --;
) ;
end divider;
architecture Behavioral of divider is
type stype is (S0,S1,S2,S3);
signal iCUR : stype ;
signal iL10 : std_logic_vector(3 downto 0);
signal iL01 : std_logic_vector(3 downto 0);
signal iTMP : std_logic_vector(6 downto 0);
begin
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
-- state machine
iCUR <= S0 ;
iL10 <= "0000" ;
iL01 <= "0000" ;
iTMP <= "0000000" ;
elsif rising_edge( CLOCK ) then
-- state machine
case iCUR is
-- wait trigger
when S0 => if ( TRG = '1' ) then
iCUR <= S1 ;
iL10 <= "0000" ;
iTMP <= COUNTER ;
else
iCUR <= S0 ;
end if ;
-- calculate (loop)
when S1 => if ( conv_integer(iTMP) < 10 ) then
iCUR <= S2 ;
else
iL10 <= iL10 + 1 ;
iTMP <= iTMP - 10 ;
iCUR <= S1 ;
end if ;
-- convert
when S2 => iL01 <= iTMP(3 downto 0) ;
iCUR <= S3 ;
-- update counter and return first state
when S3 => iCUR <= S0 ;
-- default
when others =>
iCUR <= S0 ;
end case ;
end if ;
end process ;
-- output
D10 <= iL10 ;
D01 <= iL01 ;
end Behavioral;
利用したCPLD/FPGAボード
ソースコードは、XilinxのCoolRunnerIIで動作確認しました。
利用したボードには、XC2C128-7の144ピンTQ144が載っています。
ISE_WebPackのVer9.1.03iを使いましたが、この処理系で結果は
次のようにレポートされています。
Macrocells Product Terms Function Block Registers Pins
Used/Tot Used/Tot Inps Used/Tot Used/Tot Used/Tot
119/128 ( 93%) 297 /448 ( 66%) 274 /320 ( 86%) 99 /128 ( 77%) 22 /100 ( 22%)
XC2C256を利用して、開発を始めましたが、内部論理の見直しで
XC2C128に押し込めました。
内部論理を少なくするために、カウンタとデコーダを多用する
構造にしています。
シーケンサのステート割当ては、処理系に任せていますが
グレイコードカウンタかジョンソンカウンタを採用している
ことを、回路を生成させて確認しました。
バイナリカウンタを使っていないので、内部でハザードが発生
することは少ないと思われます。
目次
前
次