Verilogクロックジェネレータ:設計と実装ガイド
クロックは、異なるブロックを互いに同期させることを可能にするため、デジタル回路の構築に不可欠です。
クロックの特性
デジタルクロックの主要な特性は、クロック周期を決定する周波数、デューティサイクル、および他のクロックとの関係でのクロック位相です。
クロック周期
周波数は、一定期間内に何サイクル存在するかを示します。したがって、クロック周期は1サイクルを完了するのにかかる時間です。
クロックデューティサイクル
クロックがハイレベルである時間の、周期に対する割合がデューティサイクルを定義します。
クロック位相
1サイクルのクロックを360度の完全な円と見なせる場合、別のクロックは円上の異なる位置に相対的に配置され、異なる位相を占めます。例えば、同じ周期の別のクロックが右に1/4周期シフトしている場合、位相差は90度と言えます。
Verilogクロックジェネレータ
シミュレーションは、timescaleディレクティブで指定される限られた精度を持つ所定のtimescaleで動作する必要があります。したがって、timescaleの精度がクロック周期を表現するのに十分かどうかが重要です。例えば、クロック周波数が640000 kHzに設定されている場合、クロック周期は1.5625 nsになりますが、timescale精度が1psでは余分な桁を表現できないため十分ではありません。シミュレーションは最後の桁を丸めて3桁のtimescale精度に収めようとします。これによりクロック周期が1.563 nsになり、実際には639795 kHzを表すことになります!
以下のVerilogクロックジェネレータモジュールには、上記で説明した3つの特性を調整する3つのパラメータがあります。モジュールには、クロックを必要に応じて無効/有効にする入力enableがあります。複数のクロックが共通のenable信号で制御される場合、それらを相対的に位相合わせしやすくなります。
`timescale 1ns/1ps
module clock_gen (
input enable,
output reg clk
);
parameter FREQ = 100000; // kHz単位
parameter PHASE = 0; // 度単位
parameter DUTY = 50; // パーセント単位
real clk_pd = 1.0/(FREQ * 1e3) * 1e9; // nsに変換
real clk_on = DUTY/100.0 * clk_pd;
real clk_off = (100.0 - DUTY)/100.0 * clk_pd;
real quarter = clk_pd/4;
real start_dly = quarter * PHASE/90;
reg start_clk;
initial begin
$display("FREQ = %0d kHz", FREQ);
$display("PHASE = %0d deg", PHASE);
$display("DUTY = %0d %%", DUTY);
$display("PERIOD = %0.3f ns", clk_pd);
$display("CLK_ON = %0.3f ns", clk_on);
$display("CLK_OFF = %0.3f ns", clk_off);
$display("QUARTER = %0.3f ns", quarter);
$display("START_DLY = %0.3f ns", start_dly);
end
// 変数をゼロで初期化
initial begin
clk <= 0;
start_clk <= 0;
end
// クロックがイネーブルされたとき、位相効果を得るために
// クロック駆動を1に遅延させる。start_dlyは設定された
// 位相に対する正しい遅延に設定される。enableが0のとき、
// 現在のクロック周期を完了するのに十分な時間を許可
always @ (posedge enable or negedge enable) begin
if (enable) begin
#(start_dly) start_clk = 1;
end else begin
#(start_dly) start_clk = 0;
end
end
// 歪んだクロックオン/オフ時間でデューティサイクルを実現し、
// クロックがオンの間はこれを継続
always @(posedge start_clk) begin
if (start_clk) begin
clk = 1;
while (start_clk) begin
#(clk_on) clk = 0;
#(clk_off) clk = 1;
end
clk = 0;
end
end
endmodule
異なるクロック周波数でのテストベンチ
module tb;
wire clk1, clk2, clk3, clk4;
reg enable;
reg [7:0] dly;
clock_gen u0(enable, clk1);
clock_gen #(.FREQ(200000)) u1(enable, clk2);
clock_gen #(.FREQ(400000)) u2(enable, clk3);
clock_gen #(.FREQ(800000)) u3(enable, clk4);
initial begin
enable <= 0;
for (int i = 0; i < 10; i= i+1) begin
dly = $random;
#(dly) enable <= ~enable;
$display("i=%0d dly=%0d", i, dly);
#50;
end
#50 $finish;
end
endmodule
xcelium> run FREQ = 100000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 0.000 ns FREQ = 200000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 5.000 ns CLK_ON = 2.500 ns CLK_OFF = 2.500 ns QUARTER = 1.250 ns START_DLY = 0.000 ns FREQ = 400000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 2.500 ns CLK_ON = 1.250 ns CLK_OFF = 1.250 ns QUARTER = 0.625 ns START_DLY = 0.000 ns FREQ = 800000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 1.250 ns CLK_ON = 0.625 ns CLK_OFF = 0.625 ns QUARTER = 0.312 ns START_DLY = 0.000 ns i=0 dly=36 i=1 dly=129 i=2 dly=9 i=3 dly=99 i=4 dly=13 i=5 dly=141 i=6 dly=101 i=7 dly=18 i=8 dly=1 i=9 dly=13 Simulation complete via $finish(1) at time 1110 NS + 0
異なるクロック位相でのテストベンチ
module tb;
wire clk1, clk2;
reg enable;
reg [7:0] dly;
clock_gen u0(enable, clk1);
clock_gen #(.FREQ(50000), .PHASE(90)) u1(enable, clk2);
initial begin
enable <= 0;
for (int i = 0; i < 10; i=i+1) begin
dly = $random;
#(dly) enable <= ~enable;
$display("i=%0d dly=%0d", i, dly);
end
#50 $finish;
end
endmodule
xcelium> run FREQ = 100000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 0.000 ns FREQ = 100000 kHz PHASE = 90 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 2.500 ns FREQ = 100000 kHz PHASE = 180 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 5.000 ns FREQ = 100000 kHz PHASE = 270 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 7.500 ns i=0 dly=36 i=1 dly=129 i=2 dly=9 i=3 dly=99 i=4 dly=13 i=5 dly=141 i=6 dly=101 i=7 dly=18 i=8 dly=1 i=9 dly=13 Simulation complete via $finish(1) at time 1110 NS + 0
異なるデューティサイクルでのテストベンチ
module tb;
wire clk1, clk2, clk3, clk4;
reg enable;
reg [7:0] dly;
clock_gen u0(enable, clk1);
clock_gen #(.DUTY(25)) u1(enable, clk2);
clock_gen #(.DUTY(75)) u2(enable, clk3);
clock_gen #(.DUTY(90)) u3(enable, clk4);
initial begin
enable <= 0;
for (int i = 0; i < 10; i= i+1) begin
dly = $random;
#(dly) enable <= ~enable;
$display("i=%0d dly=%0d", i, dly);
#50;
end
#50 $finish;
end
endmodule
xcelium> run FREQ = 100000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 0.000 ns FREQ = 100000 kHz PHASE = 0 deg DUTY = 25 % PERIOD = 10.000 ns CLK_ON = 2.500 ns CLK_OFF = 7.500 ns QUARTER = 2.500 ns START_DLY = 0.000 ns FREQ = 100000 kHz PHASE = 0 deg DUTY = 75 % PERIOD = 10.000 ns CLK_ON = 7.500 ns CLK_OFF = 2.500 ns QUARTER = 2.500 ns START_DLY = 0.000 ns FREQ = 100000 kHz PHASE = 0 deg DUTY = 90 % PERIOD = 10.000 ns CLK_ON = 9.000 ns CLK_OFF = 1.000 ns QUARTER = 2.500 ns START_DLY = 0.000 ns i=0 dly=36 i=1 dly=129 i=2 dly=9 i=3 dly=99 i=4 dly=13 i=5 dly=141 i=6 dly=101 i=7 dly=18 i=8 dly=1 i=9 dly=13 Simulation complete via $finish(1) at time 1110 NS + 0
enableのトグルでクロックの開始/停止
以下の波形は、enableがローのときクロックが停止し、enableがハイに設定されるとクロックが開始されることを示しています。
Verilog
- Verilogマスター:デジタル回路設計のための完全チュートリアル
- Verilog連結マスター:実践ガイド
- Verilog代入文の理解:種類、構文、ベストプラクティス
- Verilogブロッキング代入とノンブロッキング代入:違いを徹底解説
- Verilog関数のマスター:効率的なRTL設計のために
- Verilogタスクの理解:関数との違い、構文、使用法
- Verilog数学関数のマスター:効率的なハードウェア設計のために
- Verilog $timeformatのマスター:精度と表示制御
- 任意のスコープでのVerilog timescale理解と表示
- VerilogファイルI/Oの完全ガイド:オープン、読み取り、書き込み、クローズ
- Verilog入門:Hello World例で基本をマスター