カウンタによるクロック監視回路(その1)

FPGAやCPLDに実装可能


1.はじめに
 通常ディジタル回路ではシステムクロック(以下クロック)をベースに動作するのでクロックが停止するとシステムがダウンします。
 従ってクロックの停止を監視し、クロックが停止したらフェイルセーフとなる様なエラー処理を行なうのが一般的です。

 クロック監視方法で良く見かけるのは図1の様に、例えばHC123の様なリトリガラブルワンショットタイマを被監視クロックCLKで常時リトリガするものです。
 その為にはコンデンサ、抵抗等が必要で、検出時間はC,Rの定数に依るのでバラツキ大です。

 また、この種のICはアナログ系に属し、ディジタル回路内で使用するのはスマートとは言い難いものです。
図1クリックで拡大



2.カウンタによるクロック監視回路
SMD用ユニバーサル基板バナー  クロック源(発振器)を異にしたクロックが2系統以上ある場合はカウンタを用いてクロック相互に監視する図2の方法があります。
 これにより上記回路の欠点を解決できます。
図2クリックで拡大


 CLK2でCLK1を監視するには、CLK2で歩進する2個のカウンタを用意し、一方はCLK1でクリアし、他方はCLK1を反転させた/CLK1でクリアします。
 さらに監視時間に見合ったカウンタのタップでDFFをセットする様にします。
 CLK1が停止すると何れかのカウンタタップが'H'になってDFFがセットされ、クロック停止信号CLK1_STOPが'H'になります。

 同様にしてCLK1でCLK2の監視ができます。

 なお、完全同期型のカウンタによるクロック監視回路(その2)を別途UPしています。


3.VHDL ソースファイル
 図2の回路をディスクリート素子で組むと素子が多くメリットが薄れますが、CPLDやFPGAを使用している場合はそれらに間借りすれば事実上 クロック監視回路の為の必要部品はゼロになります。

 一例として clk0 と clk1 で相互監視する場合の VHDL での記述例とシミュレーション用テストベンチを示します。
 (ダウンロードのページからダウンロードできます)

 添付するソースファイルはASIAN記法(HDLにおける信号名称決定方法)に則っているので、ASIAN記法の具体例にもなっており、 それによる回路は理解し易いというメリットも実感して頂けると思います。

●クロック監視基本回路 clock_monitor.vhd

-------------------------------------------------------------------------------------
--  File Name   : clock_monitor.vhd
--  Function    : クロック監視基本回路
--  Author      : F.O (ProXi)
-- 【備考】     : 本ソースは ASIAN記法 (Attributed SIgnAl Naming)  で記述している。
--              :   詳細は https://www.proxi.co.jp/technolo/asian.htm 参照
-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------
--  クロック監視基本回路
-------------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity clock_monitor is
    generic (WIDTH : integer := 5);             -- 監視用カウンタビット幅  (MSBはカウントには寄与しない)
    port (
      -- in
        a_rst               : in  std_logic;    -- 非同期リセット
        base_clk            : in  std_logic;    -- 監視用ベースクロック
        a_monitored_clk     : in  std_logic;    -- 被監視クロック(非同期)
      -- out
        a_clk_down_status   : out std_logic;    -- クロック停止非ホールド出力(base_clkに同期)
        ah_clk_down         : out std_logic     -- クロック停止ホールド出力(非同期)
    );
end;

architecture rtl of clock_monitor is
    signal xa_clk_down_status   : std_logic;
    signal xah_clk_down         : std_logic;

    signal xav_cnt0             : std_logic_vector(WIDTH-1 downto 0);   -- クロック停止監視用カウンタ0
    signal xav_cnt1             : std_logic_vector(WIDTH-1 downto 0);   -- クロック停止監視用カウンタ1
        ----  xav_cnt0, xav_cnt1 は a_rst 以外の a_monitored_clk 信号でもでクリアされるので非同期カウンタと見なす
    
begin
    -- クロック停止監視用カウンタ0    (被監視クロック停止し'0'状態継続を検出)
    process (a_rst, base_clk, a_monitored_clk) begin
        if (a_rst = '1') then
            xav_cnt0 <= (others => '0');
        elsif (a_monitored_clk = '1') then                  -- 被監視クロックが'1'でカウンタ非同期クリア
            xav_cnt0 <= (others => '0');
        elsif (base_clk'event and base_clk = '1') then      -- 被監視クロックが'0'でカウント
            if (xav_cnt0(WIDTH-1) = '0') then               -- カウンタのMSBが'0'で歩進、'1'でカウントアップとして歩進停止
                xav_cnt0 <= xav_cnt0 + '1';
            end if;
        end if;
    end process;

    -- クロック停止監視用カウンタ1    (被監視クロック停止し'1'状態継続を検出)
    process (a_rst, base_clk, a_monitored_clk) begin
        if (a_rst = '1') then
            xav_cnt1 <= (others => '0');
        elsif (a_monitored_clk = '0') then                  -- 被監視クロックが'0'でカウンタ非同期クリア
            xav_cnt1 <= (others => '0');
        elsif (base_clk'event and base_clk = '1') then      -- 被監視クロックが'1'でカウント
            if (xav_cnt1(WIDTH-1) = '0') then               -- カウンタのMSBが'0'で歩進、'1'でカウントアップとして歩進停止
                xav_cnt1 <= xav_cnt1 + '1';
            end if;
        end if;
    end process;

    -- クロック停止非ホールド信号生成
    xa_clk_down_status <= xav_cnt0(WIDTH-1) or xav_cnt1(WIDTH-1);

    -- クロック停止ホールド信号生成  (非同期ラッチ)
    process (a_rst, xa_clk_down_status) begin
        if (a_rst = '1') then
            xah_clk_down <= '0';
        elsif (xa_clk_down_status'event and xa_clk_down_status = '1') then
            xah_clk_down <= '1';
        end if;
    end process;

    -- 出力信号セット
    a_clk_down_status   <= xa_clk_down_status;
    ah_clk_down         <= xah_clk_down;

end rtl;



●基本監視回路の応用例 clk_check_process.vhd
-------------------------------------------------------------------------------------
--  File Name   : clk_check_process.vhd
--  Function    : クロック相互監視回路の応用例
--  Author      : F.O (ProXi)
-- 【備考】     : 本ソースは ASIAN記法 (Attributed SIgnAl Naming)  で記述している。
--              :   詳細は https://www.proxi.co.jp/technolo/asian.htm 参照
-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------
--  クロック相互監視処理    (クロック監視回路の応用例)
--      本例はクロック0 (20MHz, 50nSec)の停止とクロック1 (9.8304MHz, 101.725nSec) 
--      の停止を相互に監視させるものとする。
-------------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity clk_check_process is
    port (
      -- in
        a_rst           : in  std_logic;    -- システムリセット(非同期)
        clk0            : in  std_logic;    -- クロック0   20MHz, 50nSec
        clk1            : in  std_logic;    -- クロック1   9.8304MHz, 101.725nSec
      -- out
        a_clk_down0     : out std_logic;    --  クロック0停止非ホールド出力
        ah_clk_down0    : out std_logic;    --  クロック0停止ホールド出力
        a_clk_down1     : out std_logic;    --  クロック1停止非ホールド出力
        ah_clk_down1    : out std_logic     --  クロック1停止ホールド出力
    );
end;

architecture rtl of clk_check_process is
begin
    -- クロック0 監視
    clock_monitor0 : entity work.clock_monitor
        generic map (WIDTH => 5)                    -- 停止監視時間 16 clock x 101.725nSec/clock = 1.6276 usec 
        port map (
          -- in
            a_rst               => a_rst,           -- リセット入力(非同期)
            base_clk            => clk1,            -- 監視用ベースクロック
            a_monitored_clk     => clk0,            -- 被監視クロック(非同期)
          -- out
            a_clk_down_status   => a_clk_down0,     -- クロック0停止非ホールド出力
            ah_clk_down         => ah_clk_down0     -- クロック0停止ホールド出力
        );

    -- クロック1 監視
    clock_monitor1 : entity work.clock_monitor
        generic map (WIDTH => 6)                    -- 停止監視時間 32 clock x 50nSec/clock = 1.6 usec 
        port map (
          -- in
            a_rst               => a_rst,           -- リセット入力(非同期)
            base_clk            => clk0,            -- 監視用ベースクロック
            a_monitored_clk     => clk1,            -- 被監視クロック(非同期)
          -- out
            a_clk_down_status   => a_clk_down1,     -- クロック0停止非ホールド出力
            ah_clk_down         => ah_clk_down1     -- クロック0停止ホールド出力
        );

end rtl;



●応用例のテストベンチ clk_check_process.vht
-------------------------------------------------------------------------------------
--  File Name   : clk_check_process.vht
--  Function    : クロック監視回路  テストベンチ
--  Author      : F.O (ProXi)
-- 【備考】     : 本ソースは Quartus II (Altera)  の test bench template Writer で
--                生成したファイルを編集したものである。
-------------------------------------------------------------------------------------
LIBRARY ieee;                                               
USE ieee.std_logic_1164.all;                                
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;

ENTITY clk_check_process_vhd_tst IS
END clk_check_process_vhd_tst;
ARCHITECTURE clk_check_process_arch OF clk_check_process_vhd_tst IS
-- constants                                                 
   constant clk0_period     : time := 50 ns;        -- 20 MHz
   constant clk1_period     : time := 101.725 ns;   -- 9.8304 MHz

-- signals                                                   
SIGNAL a_clk_down0      : STD_LOGIC;
SIGNAL a_clk_down1      : STD_LOGIC;
SIGNAL a_rst            : STD_LOGIC;
SIGNAL ah_clk_down0     : STD_LOGIC;
SIGNAL ah_clk_down1     : STD_LOGIC;
SIGNAL clk0             : STD_LOGIC;
SIGNAL clk1             : STD_LOGIC;

signal usec_time_cnt    : integer range 0 to 250 := 0;   -- uSec time_counter

COMPONENT clk_check_process
	PORT (
	a_clk_down0     : OUT STD_LOGIC;
	a_clk_down1     : OUT STD_LOGIC;
	a_rst           : IN STD_LOGIC;
	ah_clk_down0    : OUT STD_LOGIC;
	ah_clk_down1    : OUT STD_LOGIC;
	clk0            : IN STD_LOGIC;
	clk1            : IN STD_LOGIC
	);
END COMPONENT;
BEGIN
	i1 : clk_check_process
	PORT MAP (
    	a_clk_down0     => a_clk_down0,
	    a_clk_down1     => a_clk_down1,
	    a_rst           => a_rst,
	    ah_clk_down0    => ah_clk_down0,
	    ah_clk_down1    => ah_clk_down1,
	    clk0            => clk0,
	    clk1            => clk1
	);

    ---------------------------------------
    -- system reset
    a_rst  <= '1', '0' after 1 us;          -- システムリセット

    -- uSec タイムカウンタ
    time_count_process : process begin
        while (usec_time_cnt < 300) loop
            wait for 1 us;
            usec_time_cnt <= usec_time_cnt + 1;
        end loop;
        wait;

    end process;

    -- clk0 生成
    clk0_generate_proces : process begin
        while (usec_time_cnt < 5) loop
            clk0 <= '1';                    -- clk0 正常
            wait for clk0_period /2;
            clk0 <= '0';
            wait for clk0_period /2;
        end loop;

        clk0 <= '0';                        -- clk0 ダウンで '0'
        wait for 3 us;

        while (usec_time_cnt < 12) loop
            clk0 <= '1';                    -- clk0 正常
            wait for clk0_period /2;
            clk0 <= '0';
            wait for clk0_period /2;
        end loop;

        clk0 <= '1';                        -- clk0 ダウンで '1'
        wait for 3 us;

        while (usec_time_cnt < 50) loop
            clk0 <= '1';                    -- clk0 正常
            wait for clk0_period /2;
            clk0 <= '0';
            wait for clk0_period /2;
        end loop;

        wait;

    end process;

      -- clk1 生成
    clk1_generate_proces : process begin
        while (usec_time_cnt < 20) loop
            clk1 <= '1';                    -- clk1 正常
            wait for clk1_period /2;
            clk1 <= '0';
            wait for clk1_period /2;
        end loop;

        clk1 <= '0';                        -- clk1 ダウンで '0'
        wait for 3 us;

        while (usec_time_cnt < 27) loop
            clk1 <= '1';                    -- clk1 正常
            wait for clk1_period /2;
            clk1 <= '0';
            wait for clk1_period /2;
        end loop;

        clk1 <= '1';                        -- clk1 ダウンで '1'
        wait for 3 us;

        while (usec_time_cnt < 50) loop
            clk1 <= '1';                    -- clk1 正常
            wait for clk1_period /2;
            clk1 <= '0';
            wait for clk1_period /2;
        end loop;

        wait;

    end process;

END clk_check_process_arch;

SMDクランプバナー

 以上のソースはあくまでも基本形であり、実回路へ応用する際は、同期回路化、クロック周波数の最適化(分周)、監視時間(クロック数)の最適化、 ホールド信号のクリア信号追加等、種々のカスタマイズが考えられます。

 また、同ソースは ASIAN記法で記述しているので、ASIAN記法の使用例にもなっています。

 なお、当社はソースについての責任を負えません。応用する場合は応用者の責任で行なって下さい。

 以下に ModelSim ALTERA STARTER EDITION 10.1e Revision: 2013.06 (ALTERA)によるシミュレーション結果を示します。

(1)全体
 clk0が 16監視クロック間以上停止するとエラー a_clk_down0、 ah_clk_down0 が立ちます。

 同様に clk1 が 32監視クロック間以上停止するとエラー a_clk_down1、 ah_clk_down1 が立ちます。

シミュレーション図1クリックで拡大


(2)clk0 停止動作部拡大

シミュレーション図2クリックで拡大


(2)clk1 停止動作部拡大

シミュレーション図2クリックで拡大


 以上

 (改訂D 2018/7/9)

−−−−− 本ページはここまで −−−−−