目次

Kitchen Timer

 CPLD/FPGAを利用して、キッチンタイマーを作成してみます。

 キッチンタイマーを、どういう場面で利用するか列挙します。

 この他にもあるでしょうが、自分が使う場合は上のリスト通りです。

 仕様を図で具体化します。



 スイッチは、スタートとリセットとします。
 経過時間(分)を、2けたの7セグメントLEDで表示。
 計時中は2秒単位でLEDを点滅。
 タイムアウトしたときに、ブザーを鳴らします。

 表示用の7セグメントLEDは、配線数を減らすために
 ダイナミック点灯します。
 10の位、1の位の各セグメントを利用するために、選択用
 の信号線を使います。

 使う7セグメントLEDによりアノードコモンかカソードコモン
 に分かれますが、FPGA/CPLDを利用するならば、単に出力論理を
 反転するか否かなので、入手できる部品に合わせます。

 計時していることがわかるように使うLEDは、7セグメント
 LEDのDp(デシマルポイント)部分を使います。


ブロック図作成

 仕様で描いた図から、ブロック図を作成します。  ブロック図から、各ブロックの処理を考えます。  ブロックに分割したので、各ブロックを定義します。  ブロックに分割すると、ブロックごとに集中して動作を  考えればよくなります。また、テストする場合も、その  ブロックだけにすればよいので、テストには、マクロセル  (ゲート数)が少ないボードで間に合います。  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. スタートトリガー待ち
  2. スイッチから設定値入力
  3. 設定値からカウンタ値計算
  4. カウンタ値をサブシーケンサに渡す
  5. サブシーケンサが終了フラグを出すまで待つ
  6. ブザーを鳴らす
  7. 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. トリガー待ち
  2. カウンタ値入力、1秒カウンタを0に設定
  3. カウンタ値が0なら6ステートに、0でないなら4ステートに
  4. 1秒経過トリガー待ち、1秒カウンタのインクリメントし5ステートに
  5. 1秒カウンタが60ならゼロクリア、カウンタ値をデクリメントし、3ステートに
  6. 終了フラグをクリアして、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秒経過をカウントします。  サブのシーケンサの動作を定義します。
  1. トリガー待ち
  2. カウンタ値入力、秒カウンタ値を0とする
  3. カウンタ値が0なら7ステートに、0でないなら4ステートに
  4. SOUTの1を判定し、秒カウンタ値をインクリメント
  5. 秒カウンタ値が60なら0に戻し、カウンタ値をデクリメント
  6. 3ステートにもどる
  7. 終了フラグをセット
  8. 終了フラグをクリアして、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として、除算を考えます。
  1. トリガー待ち
  2. バイナリーカウンタ値をコピー(iTMP)、商を格納するレジスタを0クリア(iL10)
  3. iTMPが10未満では5に、それ以外ではiTMP=iTMP-10、iL10=iL10+1を実行し4へ
  4. 3にもどる
  5. iL10(商)、iL01=iTMP(3 downto 0)(余り)
  6. 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は、次のコードでテストしました。 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)

 スピーカにクロックを送るとブザーは、鳴ります。  ブザーを鳴らすタイミングと継続時間が必要です。  タイミングと継続時間を処理するには、シーケンサを使います。  ブザー鳴動のシーケンスを書出してみます。
  1. 開始トリガー待ち
  2. カウンタ初期化
  3. カウンタデクリメント
  4. カウンタの値が0でないなら3にもどる
  5. 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出力は、クロック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に押し込めました。  内部論理を少なくするために、カウンタとデコーダを多用する  構造にしています。  シーケンサのステート割当ては、処理系に任せていますが  グレイコードカウンタかジョンソンカウンタを採用している  ことを、回路を生成させて確認しました。  バイナリカウンタを使っていないので、内部でハザードが発生  することは少ないと思われます。


目次 inserted by FC2 system