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 等の素子)を使ったクロックの作成は、ゲーテッドクロックと呼ばれます。クロック信号をチェックするツール(ものすごく高い)を使えばエラーが出るくらいダメな方法です。
くれぐれもご注意を。
この記事が気に入ったらサポートをしてみませんか?