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

FPGAに実装し易い同期型回路


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

 その為の一つの方法として、クロック源(発振器)を異にしたクロックが2系統以上ある場合は、 カウンタを用いてクロック相互に監視する方法としてカウンタによるクロック監視回路(その1)を表しました。
 同方法は、元はランダムロジックで組む事を想定したものを、FPGAやCPLDにそのまま実装する様にしたもので、非同期回路になっており、 スマートさに欠けます。

 もし最初からFPGAやCPLDに実装する事を前提として設計したら、同期型としてよりスマートになります。
 本稿ではその方法を一例としてまとめたものです。

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


2.カウンタによるクロック監視回路
 図1に回路構成を示します。
図1クリックで拡大

         図1 カウンタによるクロック監視回路構成


(1)概要
SMD用ユニバーサル基板バナー  システムクロック clk で a_monitored_clk を監視します。
 クロックが停止すると非ラッチ、自動復帰のエラー信号 s_clk_down_status と ラッチされるエラー信号 sh_clk_down を出力します。
 sh_clk_down の解除(クリア)は外部から最小1clk周期のパルス幅の sp_error_clear 入力信号で行ないます。

(2)動作説明
 a_monitored_clk周波数が clk周波数の1/2未満の場合はそのままで、1/2以上の場合は divide_counter で適宜分周して1/2以下にして xa_monitored_clk にします。
 実際には分周された信号として divide_counter の MSBを xa_monitored_clk とします。
 divide_counter のビット数は DCNT_BIT で設定し、分周する/しないは DCNT_ENB の 1/0 で設定します。

 次に xa_monitored_clk を clkで同期化しその立ち上がりを1clk周期のパルス幅の xcp_tcnt_clear として検出します。
 xcp_tcnt_clear で time_counter をクリアするので a_monitored_clk が正常であれば time_counter はタイムアップ前にクリアされますが、 a_monitored_clk が停止すると time_counter はクリアされずに clk で歩進します。
 そのカウント値をコンパレータ comparator でエラー検出時間設定値 TIME_UP_VAL と比較し、これを越えたらタイムアップとしてカウンタの歩進を停止させ、 エラー出力します。

 非ラッチ、自動復帰の s_clk_down_status 出力と、ラッチされる sh_clk_down 出力は外部で任意に使用できます。
 エラー処理後、sh_clk_down を sp_error_clear でクリアする必要があります。

(3)設定
 上記の様に、クロック監視基本回路のソースファイル clock_monitor_sync.vhd を使用する場合には、 generic 文でアプリケーションに応じて以下の設定を行ないます。

 DCNT_BIT : 被監視クロック分周カウンタビット数
         設定値 n(自然数)に対して 1/(2^n) に分周される。(n の最小値は不使用時を含め 1)

 DCNT_ENB : 被監視クロック分周カウンタ ENABLE/DISABLE設定
          0:DISABLE 1:ENABLE

 TCNT_BIT : 時間カウンタビット数
         エラー検出時間をカバーできるカウンタビット数を設定する。

 TIME_UP_VAL : エラー検出時間設定値
           エラー検出時間をシステムクロック数で換算した値を設定する。
           エラー検出時間 Terr はシステムクロック(周期 Tとする)数で換算した値 m を設定する。
           具体的には Terr = T * m である。


3.VHDL ソースファイル
 クロック監視基本回路と、その使用例としてシステムクロック clk で a_monitored_clk1 と a_monitored_clk2 を監視する回路を VHDL で記述したものとそのシミュレーション用テストベンチを示します。
 (ソースファイルはダウンロードのページからダウンロードできます)

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

-------------------------------------------------------------------------------------
--  File Name   : clock_monitor_sync.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;
use ieee.std_logic_arith.all;

entity clock_monitor_sync is
    generic (DCNT_BIT       : integer := 1;                 -- 被監視クロック分周カウンタビット数
             DCNT_ENB       : integer := 0;                 -- 被監視クロック分周カウンタ ENABLE/DISABLE  0:DISABLE 1:ENABLE
             TCNT_BIT       : integer := 4;                 -- 時間カウンタビット数
             TIME_UP_VAL    : integer := 15);               -- エラー検出時間設定値(システムクロック換算) 
    port (
      -- in
        a_rst               : in  std_logic;                                -- 非同期リセット
        clk                 : in  std_logic;                                -- システムクロック
        a_monitored_clk     : in  std_logic;                                -- 非同期被監視クロック
        sp_error_clear      : in  std_logic;                                -- エラークリア(パルス入力)
      -- out
        s_clk_down_status   : out std_logic;                                -- クロック停止エラー状態出力(非ラッチ、自動復帰)
        sh_clk_down         : out std_logic;                                -- クロック停止エラーラッチ出力
        cp_tcnt_clear       : out std_logic;                                -- テスト用時間カウンタクリア出力
        sv_tcnt             : out std_logic_vector(TCNT_BIT-1 downto 0)     -- テスト用時間カウンタ出力
    );
end;

architecture rtl of clock_monitor_sync is
    signal xa_monitored_clk     : std_logic;
    signal xm_monitored_clk     : std_logic;
    signal xs_monitored_clk_q0  : std_logic;
    signal xs_monitored_clk_q1  : std_logic;
    signal xcp_tcnt_clear       : std_logic;
    signal xc_clk_down_status   : std_logic;
    signal xs_clk_down_status_q : std_logic;
    signal xsh_clk_down         : std_logic;
    signal xcp_clk_down_on      : std_logic;
    signal xs1v_dcnt            : std_logic_vector(DCNT_BIT-1 downto 0);
    signal xsv_tcnt             : std_logic_vector(TCNT_BIT-1 downto 0);
    
begin
 
    -- divide_counter (分周器)
    process (a_rst, a_monitored_clk) begin
        if (a_rst = '1') then
            xs1v_dcnt     <= (others => '0');
        elsif (a_monitored_clk'event and a_monitored_clk = '1') then
            xs1v_dcnt <= xs1v_dcnt +1;
        end if;
    end process;
    
    xa_monitored_clk <= a_monitored_clk when (DCNT_ENB = 0) else xs1v_dcnt(DCNT_BIT-1);     -- 被監視信号選択

   -- synchronizer
    process (a_rst, clk) begin
        if (a_rst = '1') then
            xm_monitored_clk        <= '0';
            xs_monitored_clk_q0     <= '0';
            xs_monitored_clk_q1     <= '0';
        elsif (clk'event and clk='1') then
            xm_monitored_clk        <= xa_monitored_clk;
            xs_monitored_clk_q0     <= xm_monitored_clk;
            xs_monitored_clk_q1     <= xs_monitored_clk_q0;
        end if;
    end process;

    xcp_tcnt_clear <= xs_monitored_clk_q0 and (not xs_monitored_clk_q1);  -- 被監視信号の立ち上がり検出

    -- time_counter (時間カウンタ)
    process (a_rst, clk) begin
        if (a_rst = '1') then
            xsv_tcnt <= (others => '0');
        elsif (clk'event and clk='1') then
            if (xcp_tcnt_clear = '1') then          -- 被監視信号の立ち上がりでカウンタクリア
                xsv_tcnt <= (others => '0');
            elsif (sp_error_clear = '1') then       -- エラークリア信号入力でカウンタクリア
                xsv_tcnt <= (others => '0');
            elsif (xc_clk_down_status = '0') then   -- 時間カウンタがタイムアップしない間 clk でインクリメント
                xsv_tcnt <= xsv_tcnt +1;
            end if;
        end if;
    end process;

    -- comparator (check clock down status)
    xc_clk_down_status <= '1' when ( xsv_tcnt >= conv_std_logic_vector(TIME_UP_VAL, TCNT_BIT) ) else '0';

    -- dff
    process (a_rst, clk) begin
        if (a_rst = '1') then
            xs_clk_down_status_q <= '0';
        elsif (clk'event and clk='1') then
            xs_clk_down_status_q <= xc_clk_down_status;
        end if;
    end process;

    xcp_clk_down_on <= xc_clk_down_status and (not xs_clk_down_status_q);   -- クロックダウン停止立ち上がり検出

    --  s-r ff
    process (a_rst, clk) begin
        if (a_rst = '1') then
            xsh_clk_down    <= '0';
        elsif (clk'event and clk='1') then
            if (xcp_clk_down_on = '1') then
                xsh_clk_down    <= '1';             -- クロックダウンエラーをホールド
            elsif (sp_error_clear = '1') then
                xsh_clk_down    <= '0';             -- クロックダウンエラーをクリア
            end if;
        end if;
    end process;

    -- set output
    s_clk_down_status   <= xs_clk_down_status_q;
    sh_clk_down         <= xsh_clk_down;
    cp_tcnt_clear       <= xcp_tcnt_clear;           -- fot test
    sv_tcnt             <= xsv_tcnt;                 -- fot test

end rtl;



●基本監視回路の応用例 clock_monitor_sync_sample.vhd
-------------------------------------------------------------------------------------
--  File Name   : clock_monitor_sync_sample.vhd
--  Function    : 同期型クロック監視基本回路の応用例
--  Author      : F.O (ProXi)
-- 【備考1】    : 本ソースは ASIAN記法 (Attributed SIgnAl Naming)  で記述している。
--              :   詳細は https://www.proxi.co.jp/technolo/asian.htm 参照
--
-- 【備考2】    : 本ソースでは単純化の為に clock_monitor_sync コンポーネントの out信号
--              : を直接 out端子に接続している
-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------
--  クロック監視概要
--    システムクロック clk で a_monitored_clk1, a_monitored_clk2の停止を監視する。
--
--   クロック仕様
--      システムクロック    clk              : 20MHz, 50nSec
--      被監視クロック1     a_monitored_clk1 : 50MHz, 20nSec  内部で8分周して6.25MHz, 160nSec/clock を監視する
--                                             停止監視時間 50nSec/clock x 20 clock = 1 usec 
--      被監視クロック2     a_monitored_clk2 : 8MHz, 125nSec/clock  内部で分周せずclk2を直接監視する
--                                             停止監視時間 50nSec/clock x 25 clock = 1.25 usec 
-------------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;

entity clock_monitor_sync_sample is
    port (
      -- in
        a_rst               : in  std_logic;                        -- システムリセット(非同期)
        clk                 : in  std_logic;                        -- 20MHz, 50nSec
        ----
        a_monitored_clk1    : in  std_logic;                        -- 50MHz, 20nSec  内部8分周 6.25MHz, 160nSec を監視
        sp_error_clear1     : in  std_logic;                        -- a_monitored_clk1 エラークリア(パルス入力)
        ----
        a_monitored_clk2    : in  std_logic;                        -- 8MHz, 125nSec 内部分周無し
        sp_error_clear2     : in  std_logic;                        -- a_monitored_clk2 エラークリア(パルス入力)
      -- out
        s_clk_down_status1  : out std_logic;                        -- クロック停止エラー状態出力1(非ラッチ、自動復帰)
        sh_clk_down1        : out std_logic;                        -- クロック停止エラーラッチ出力1
        cp_tcnt_clear1      : out std_logic;                        -- テスト用時間カウンタクリア出力1
        sv_tcnt1            : out std_logic_vector(4 downto 0);     -- テスト用時間カウンタ出力1
        ----
        s_clk_down_status2  : out std_logic;                        -- クロック停止エラー状態出力2(非ラッチ、自動復帰)
        sh_clk_down2        : out std_logic;                        -- クロック停止エラーラッチ出力2
        cp_tcnt_clear2      : out std_logic;                        -- テスト用時間カウンタクリア出力2
        sv_tcnt2            : out std_logic_vector(4 downto 0)      -- テスト用時間カウンタ出力2
    );
end;

architecture rtl of clock_monitor_sync_sample is

begin
    -- クロック1 監視
    clock_monitor_sync1 : entity work.clock_monitor_sync
        generic map (
            DCNT_BIT    => 3,                   -- 被監視クロック分周カウンタビット数 : 8分周
            DCNT_ENB    => 1,                   -- 被監視クロック分周カウンタ ENABLE/DISABLE  0:DISABLE 1:ENABLE
            TCNT_BIT    => 5,                   -- 時間カウンタビット数
            TIME_UP_VAL => 20                   -- エラー検出時間設定値(システムクロック換算): 50nSec/clock x 20 clock = 1 usec
        )

        port map (
          -- in
            a_rst               => a_rst,                   -- 非同期リセット
            clk                 => clk,                     -- システムクロック
            a_monitored_clk     => a_monitored_clk1,        -- 非同期被監視クロック
            sp_error_clear      => sp_error_clear1,         -- エラークリア(パルス入力)
          -- out
            s_clk_down_status   => s_clk_down_status1,      -- クロック停止エラー状態出力(非ラッチ、自動復帰)
            sh_clk_down         => sh_clk_down1,            -- クロック停止エラーラッチ出力
            cp_tcnt_clear       => cp_tcnt_clear1,          -- テスト用時間カウンタクリア出力
            sv_tcnt             => sv_tcnt1                 -- テスト用時間カウンタ出力
        );


    -- クロック2 監視
    clock_monitor_sync2 : entity work.clock_monitor_sync
        generic map (
            DCNT_BIT    => 1,                   -- 被監視クロック分周カウンタビット数 : 不使用
            DCNT_ENB    => 0,                   -- 被監視クロック分周カウンタ ENABLE/DISABLE  0:DISABLE 1:ENABLE
            TCNT_BIT    => 5,                   -- 時間カウンタビット数
            TIME_UP_VAL => 25                   -- エラー検出時間設定値(システムクロック換算): 50nSec/clock x 25 clock = 1.25 usec
        )

        port map (
          -- in
            a_rst               => a_rst,                   -- 非同期リセット
            clk                 => clk,                     -- システムクロック
            a_monitored_clk     => a_monitored_clk2,        -- 非同期被監視クロック
            sp_error_clear      => sp_error_clear2,         -- エラークリア(パルス入力)
          -- out
            s_clk_down_status   => s_clk_down_status2,      -- クロック停止エラー状態出力(非ラッチ、自動復帰)
            sh_clk_down         => sh_clk_down2,            -- クロック停止エラーラッチ出力
            cp_tcnt_clear       => cp_tcnt_clear2,          -- テスト用時間カウンタクリア出力
            sv_tcnt             => sv_tcnt2                 -- テスト用時間カウンタ出力
        );

end rtl;



●応用例のテストベンチ clock_monitor_sync_sample.vht
-------------------------------------------------------------------------------------
--  File Name   : clock_monitor_sync_sample.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 clock_monitor_sync_sample_vhd_tst IS
END clock_monitor_sync_sample_vhd_tst;
ARCHITECTURE clock_monitor_sync_sample_arch OF clock_monitor_sync_sample_vhd_tst IS
-- constants                                                 
   constant clk_period                  : time := 50 ns;        -- 20 MHz
   constant a_monitored_clk1_period     : time := 20 ns;        -- 50 MHz
   constant a_monitored_clk2_period     : time := 125 ns;       -- 8 MHz

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

SIGNAL a_monitored_clk1 : STD_LOGIC;
SIGNAL a_monitored_clk2 : STD_LOGIC;
SIGNAL a_rst : STD_LOGIC;
SIGNAL clk : STD_LOGIC;
SIGNAL cp_tcnt_clear1 : STD_LOGIC;
SIGNAL cp_tcnt_clear2 : STD_LOGIC;
SIGNAL s_clk_down_status1 : STD_LOGIC;
SIGNAL s_clk_down_status2 : STD_LOGIC;
SIGNAL sh_clk_down1 : STD_LOGIC;
SIGNAL sh_clk_down2 : STD_LOGIC;
SIGNAL sp_error_clear1 : STD_LOGIC;
SIGNAL sp_error_clear2 : STD_LOGIC;
SIGNAL sv_tcnt1 : STD_LOGIC_VECTOR(4 DOWNTO 0);
SIGNAL sv_tcnt2 : STD_LOGIC_VECTOR(4 DOWNTO 0);
COMPONENT clock_monitor_sync_sample
	PORT (
	a_monitored_clk1 : IN STD_LOGIC;
	a_monitored_clk2 : IN STD_LOGIC;
	a_rst : IN STD_LOGIC;
	clk : IN STD_LOGIC;
	cp_tcnt_clear1 : OUT STD_LOGIC;
	cp_tcnt_clear2 : OUT STD_LOGIC;
	s_clk_down_status1 : OUT STD_LOGIC;
	s_clk_down_status2 : OUT STD_LOGIC;
	sh_clk_down1 : OUT STD_LOGIC;
	sh_clk_down2 : OUT STD_LOGIC;
	sp_error_clear1 : IN STD_LOGIC;
	sp_error_clear2 : IN STD_LOGIC;
	sv_tcnt1 : OUT STD_LOGIC_VECTOR(4 DOWNTO 0);
	sv_tcnt2 : OUT STD_LOGIC_VECTOR(4 DOWNTO 0)
	);
END COMPONENT;

BEGIN
	i1 : clock_monitor_sync_sample
	PORT MAP (
-- list connections between master ports and signals
	a_monitored_clk1 => a_monitored_clk1,
	a_monitored_clk2 => a_monitored_clk2,
	a_rst => a_rst,
	clk => clk,
	cp_tcnt_clear1 => cp_tcnt_clear1,
	cp_tcnt_clear2 => cp_tcnt_clear2,
	s_clk_down_status1 => s_clk_down_status1,
	s_clk_down_status2 => s_clk_down_status2,
	sh_clk_down1 => sh_clk_down1,
	sh_clk_down2 => sh_clk_down2,
	sp_error_clear1 => sp_error_clear1,
	sp_error_clear2 => sp_error_clear2,
	sv_tcnt1 => sv_tcnt1,
	sv_tcnt2 => sv_tcnt2
	);


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

    -- 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;


    -- clk 生成
    clk_generate_proces : process begin
            clk <= '0';                    -- clk 正常
            wait for clk_period /2;
            clk <= '1';
            wait for clk_period /2;
    end process;

    -- a_monitored_clk1 生成
    a_monitored_clk1_generate_proces : process begin
        sp_error_clear1  <= '0';

        while (usec_time_cnt < 5) loop
            a_monitored_clk1 <= '1';                    -- a_monitored_clk1 正常
            wait for a_monitored_clk1_period /2;
            a_monitored_clk1 <= '0';
            wait for a_monitored_clk1_period /2;
        end loop;

        a_monitored_clk1 <= '0';                        -- a_monitored_clk1 ダウン(Lのまま)
        wait for 2 us;

        while (usec_time_cnt < 8) loop
            a_monitored_clk1 <= '1';                    -- a_monitored_clk1 正常
            wait for a_monitored_clk1_period /2;
            a_monitored_clk1 <= '0';
            wait for a_monitored_clk1_period /2;
        end loop;

        sp_error_clear1  <= '1', '0' after clk_period;   -- エラーリセット

        --------------------

        while (usec_time_cnt < 15) loop
            a_monitored_clk1 <= '1';                    -- a_monitored_clk1 正常
            wait for a_monitored_clk1_period /2;
            a_monitored_clk1 <= '0';
            wait for a_monitored_clk1_period /2;
        end loop;

        a_monitored_clk1 <= '1';                        -- a_monitored_clk1 ダウン(Hのまま)
        wait for 2 us;

        while (usec_time_cnt < 20) loop
            a_monitored_clk1 <= '1';                    -- a_monitored_clk1 正常
            wait for a_monitored_clk1_period /2;
            a_monitored_clk1 <= '0';
            wait for a_monitored_clk1_period /2;
        end loop;

        sp_error_clear1  <= '1', '0' after clk_period;   -- エラーリセット

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

        wait;
    end process;


    -- a_monitored_clk2 生成
    a_monitored_clk2_generate_proces : process begin
        sp_error_clear2  <= '0';

        while (usec_time_cnt < 7) loop
            a_monitored_clk2 <= '1';                    -- a_monitored_clk2 正常
            wait for a_monitored_clk2_period /2;
            a_monitored_clk2 <= '0';
            wait for a_monitored_clk2_period /2;
        end loop;

        a_monitored_clk2 <= '0';                        -- a_monitored_clk2 ダウン(Lのまま)
        wait for 2 us;

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


        sp_error_clear2  <= '1', '0' after clk_period;   -- エラーリセット

        --------------------

        while (usec_time_cnt < 14) loop
            a_monitored_clk2 <= '1';                    -- a_monitored_clk2 正常
            wait for a_monitored_clk2_period /2;
            a_monitored_clk2 <= '0';
            wait for a_monitored_clk2_period /2;
        end loop;

        a_monitored_clk2 <= '1';                        -- a_monitored_clk2 ダウン(Hのまま)
        wait for 2 us;

        while (usec_time_cnt < 22) loop
            a_monitored_clk2 <= '1';                    -- a_monitored_clk2 正常
            wait for a_monitored_clk2_period /2;
            a_monitored_clk2 <= '0';
            wait for a_monitored_clk2_period /2;
        end loop;

        sp_error_clear2  <= '1', '0' after clk_period;   -- エラーリセット

        while (usec_time_cnt < 100) loop
            a_monitored_clk2 <= '1';                    -- a_monitored_clk2 正常
            wait for a_monitored_clk2_period /2;
            a_monitored_clk2 <= '0';
            wait for a_monitored_clk2_period /2;
        end loop;

        wait;
    end process;


END clock_monitor_sync_sample_arch;




●シミュレーション結果 clock_monitor_sync_sample.bmp
シミュレーション図クリックで拡大


4.その他


 以上

 (初版 2018/09/14)

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