見出し画像

NGなクロック分周

 分周クロックの使い方について少し。
 元のクロックを50分周して低速なクロックを作成、低速なクロックでロジックを動作させたい場合についてです。


[悪い例]

reg    [7:0] clk_cnt;
wire   low_clk;

always @ ( posedge CLK or negedge RESETn ) begin
       if( !RESETn )
               clk_cnt <= 8'd0;
       else if( clk_cnt >= 8'd49 )
               clk_cnt <= 8'd0;
       else
               clk_cnt <= clk_cnt + 8'd1;
end

assign low_clk = ( clk_cnt < 25 );

always @ ( posedge low_clk or negedge RESETn ) begin
       if( !RESETn )
               //初期値
       else
               //ロジック
end

 この例では、分周クロックはwire宣言にして、"clk_cnt < 25"をアサインしています。
 これを実機で動かした場合、設計者の意図したとおりには動作しません。
 改善例を以下に示します。


[改善例]

reg    [7:0] clk_cnt;
reg    low_clk;
always @ ( posedge CLK or negedge RESETn ) begin
       if( !RESETn )
               clk_cnt <= 8'd0;
       else if( clk_cnt >= 8'd49 )
               clk_cnt <= 8'd0;
       else
               clk_cnt <= clk_cnt + 8'd1;
end
always @ ( posedge CLK or negedge RESETn ) begin
       if( !RESETn )
               low_clk <= 1'b0;
      else
               low_clk <= ( clk_cnt < 25 );
end
always @ ( posedge low_clk or negedge RESETn ) begin
       if( !RESETn )
               //初期値
       else
               //ロジック
end

 単にreg宣言に変更し、元クロックで叩いただけですね。

 なにが問題なのか説明します。

 clk_cnt は8bitのレジスタですが、それぞれのレジスタは別々のロジックセルに配置されます(1個のロジックセルには1個のFFしかないので当然ですね)。
 別の場所に配置されるということは、CLK信号の遅延差、出力が次の比較回路に到着するまでの遅延差が違う、ということです。

 clk_cntはが23から24に変化するときを例にしましょう。

5'b10111 → 5'b11000

 という感じで1CLK毎に遷移していきますね。でもこれはあくまでCLKごとの変化です。

 もっと細かい時間で見た場合にはbitごとにバラバラに変化するので

5'b10111 → 5'b11101 → 5'b11001 → 5'b11000

 なんて変わっているのかもしれません。

 low_clkの作成条件では25という数字が出ていますが、これはbit列でいうと 5'b11001 です。
 先の例では23から24への変化ですから low_clk は変化してはいけないのですが、途中で5'b11101なんて値が一瞬出てしまうわけで、これって29ですからね、low_clkも変化しようというものです。

 このようなゲート回路(AND, OR 等の素子)を使ったクロックの作成は、ゲーテッドクロックと呼ばれます。クロック信号をチェックするツール(ものすごく高い)を使えばエラーが出るくらいダメな方法です。

 くれぐれもご注意を。

この記事が気に入ったらサポートをしてみませんか?