1.はじめに
通常ディジタル回路ではシステムクロック(以下クロック)をベースに動作するのでクロックが停止するとシステムがダウンします。
従ってクロックの停止を監視し、クロックが停止したらフェイルセーフとなる様なエラー処理を行なうのが一般的です。
クロック監視方法で良く見かけるのは図1の様に、例えばHC123の様なリトリガラブルワンショットタイマを被監視クロックCLKで常時リトリガするものです。
その為にはコンデンサ、抵抗等が必要で、検出時間はC,Rの定数に依るのでバラツキ大です。
また、この種のICはアナログ系に属し、ディジタル回路内で使用するのはスマートとは言い難いものです。
クリックで拡大
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;
------------------------------------------------------------------------------------- -- 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;
------------------------------------------------------------------------------------- -- 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;