読み込み中...

Verilogにおけるtimescaleの使い方と活用例8選

timescale 徹底解説 Verilog
この記事は約28分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラム(回路記述)の基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●Verilogのtimescaleとは?

Verilogは、ハードウェア記述言語として広く使われています。

デジタル回路の設計やシミュレーションに欠かせないツールですが、その中でもtimescaleという概念は特に重要です。

timescaleは、Verilogコードにおける時間の単位と精度を定義する機能です。

適切なtimescaleの設定は、シミュレーション結果の正確性や回路の動作タイミングに大きな影響を与えます。

○timescaleの意味と構文

timescaleディレクティブは、Verilogコードの冒頭で宣言されます。

基本的な構文は次のようになります。

`timescale <時間単位> / <時間精度>

例えば、timescale 1ns / 1psと記述した場合、時間単位は1ナノ秒、時間精度は1ピコ秒となります。時間単位は、遅延や時間間隔を指定する際の基準となります。

時間精度は、シミュレーション中に扱える最小の時間単位を表します。

実際のコードでtimescaleを使用する例を見てみましょう。

`timescale 1ns / 1ps

module example;
  reg clk;
  initial begin
    clk = 0;
    forever #5 clk = ~clk;  // 5ns毎にクロックを反転
  end
endmodule

このコードでは、5ナノ秒ごとにクロック信号が反転します。

timescaleの設定により、#5は5ナノ秒を意味します。

○シミュレーション精度への影響

適切なtimescale設定は、シミュレーションの精度に直接影響します。

時間単位と精度のバランスが重要です。

時間単位が大きすぎると、細かい遅延を表現できません。

逆に、精度が高すぎるとシミュレーション時間が大幅に増加する可能性があります。

例えば、高速なシリアル通信インターフェースを設計する場合、ナノ秒単位の精度が必要になる場合があります。

一方、低速のインターフェースであれば、マイクロ秒単位で十分な場合もあります。

設計する回路の要件に応じて、適切なtimescaleを選択することが大切です。

○サンプルコード1:基本的なtimescale宣言と使用例

次のコードは、timescaleを使用した基本的な例です。

クロック生成とデータ伝送のタイミングを示しています。

`timescale 1ns / 100ps

module timescale_example;
  reg clk, data, q;

  // クロック生成
  initial begin
    clk = 0;
    forever #5 clk = ~clk;  // 10ns周期のクロック
  end

  // データ生成
  initial begin
    data = 0;
    #12 data = 1;  // 12ns後にデータを1に設定
    #20 data = 0;  // さらに20ns後にデータを0に設定
  end

  // フリップフロップ
  always @(posedge clk) begin
    q <= #1 data;  // 1ns遅延でデータをラッチ
  end

  // シミュレーション結果の表示
  initial begin
    $monitor("Time=%0t clk=%b data=%b q=%b", $time, clk, data, q);
    #50 $finish;  // 50ns後にシミュレーション終了
  end
endmodule

このコードを実行すると、次のような出力が得られます。

Time=0 clk=0 data=0 q=x
Time=5 clk=1 data=0 q=0
Time=10 clk=0 data=0 q=0
Time=12 clk=0 data=1 q=0
Time=15 clk=1 data=1 q=1
Time=20 clk=0 data=1 q=1
Time=25 clk=1 data=1 q=1
Time=30 clk=0 data=1 q=1
Time=32 clk=0 data=0 q=1
Time=35 clk=1 data=0 q=0
Time=40 clk=0 data=0 q=0
Time=45 clk=1 data=0 q=0

この出力から、クロックの変化、データの変化、そしてフリップフロップの動作を時系列で確認できます。

timescaleの設定により、1ナノ秒単位で時間が表示され、100ピコ秒の精度でイベントが記録されています。

●timescaleを活用したVerilog設計の基礎

Verilogでtimescaleを活用することで、より現実的で正確な回路設計が可能になります。

ここでは、実際の設計例を通じて、timescaleの活用方法を詳しく見ていきましょう。

○サンプルコード2:クロック生成器の実装

クロック生成は、デジタル回路設計の基本です。

timescaleを適切に設定することで、より正確なクロック信号を生成できます。

ここでは、50MHz(20ns周期)のクロックを生成する例を紹介します。

`timescale 1ns / 1ps

module clock_generator(
  output reg clk
);

  // 50MHz (20ns周期) のクロックを生成
  initial begin
    clk = 0;
    forever #10 clk = ~clk;  // 10ns毎に反転
  end

  // シミュレーション用の波形ダンプ
  initial begin
    $dumpfile("clock_waveform.vcd");
    $dumpvars(0, clock_generator);
    #200 $finish;  // 200ns後にシミュレーション終了
  end

endmodule

このコードでは、1ナノ秒の時間単位と1ピコ秒の精度を設定しています。

クロックは10ナノ秒ごとに反転するため、20ナノ秒(50MHz)の周期を持つクロック信号が生成されます。

○サンプルコード3:非同期リセット回路の設計

非同期リセットは、デジタル回路の信頼性を高める重要な要素です。

timescaleを適切に設定することで、リセット信号の適用タイミングを正確に制御できます。

`timescale 1ns / 100ps

module async_reset_example(
  input clk,
  input rst_n,  // アクティブLowリセット
  input d,
  output reg q
);

  // 非同期リセット付きフリップフロップ
  always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
      q <= 1'b0;  // リセット時は0にセット
    else
      q <= d;     // 通常動作時はデータをラッチ
  end

  // テストベンチ
  initial begin
    clk = 0;
    rst_n = 1;
    d = 0;

    // クロック生成
    forever #5 clk = ~clk;
  end

  initial begin
    // シミュレーションシナリオ
    #10 d = 1;
    #15 rst_n = 0;  // リセットをアサート
    #5  rst_n = 1;  // リセットを解除
    #10 d = 0;
    #20 $finish;
  end

  // 結果表示
  initial begin
    $monitor("Time=%0t rst_n=%b d=%b q=%b", $time, rst_n, d, q);
  end

endmodule

このコードでは、非同期リセット機能を持つフリップフロップを実装しています。

リセット信号(rst_n)がアクティブ(Low)になると、出力(q)は即座に0にリセットされます。

timescaleの設定により、リセット信号の変化とその影響を100ピコ秒の精度で観察できます。

○サンプルコード4:遅延を考慮したフリップフロップの記述

実際の回路では、ゲートの遅延やワイヤの伝搬遅延が存在します。

timescaleを使用することで、フリップフロップに伝搬遅延を追加し、より現実的な動作をシミュレートできます。

`timescale 1ns / 10ps

module delayed_flipflop(
  input clk,
  input d,
  output reg q
);

  // 遅延を考慮したフリップフロップ
  always @(posedge clk) begin
    q <= #0.5 d;  // 0.5ns遅延でデータをラッチ
  end

  // テストベンチ
  reg test_clk, test_d;
  wire test_q;

  delayed_flipflop dff(.clk(test_clk), .d(test_d), .q(test_q));

  initial begin
    test_clk = 0;
    test_d = 0;

    // クロック生成(100MHz)
    forever #5 test_clk = ~test_clk;
  end

  initial begin
    // テストシナリオ
    #10 test_d = 1;
    #10 test_d = 0;
    #10 test_d = 1;
    #20 $finish;
  end

  // 結果表示
  initial begin
    $monitor("Time=%0t clk=%b d=%b q=%b", $time, test_clk, test_d, test_q);
  end

endmodule

このコードでは、フリップフロップに0.5ナノ秒の遅延を追加しています。

timescaleの精度を10ピコ秒に設定することで、この遅延を正確にシミュレートできます。

実行結果を見ると、入力(d)の変化から出力(q)の変化までに、クロックエッジからの0.5ナノ秒の遅延が確認できるでしょう。

○サンプルコード5:パルス幅変調(PWM)回路の設計

パルス幅変調(PWM)は、デジタル信号を使って連続的な制御を行う技術です。

timescaleを適切に設定することで、PWM信号の精度を高めることができます。

`timescale 1ns / 1ps

module pwm_generator(
  input clk,
  input [7:0] duty_cycle,  // 0-255の範囲で指定
  output reg pwm_out
);

  reg [7:0] counter;

  always @(posedge clk) begin
    if (counter < duty_cycle)
      pwm_out <= 1'b1;
    else
      pwm_out <= 1'b0;

    counter <= counter + 1;
  end

  // テストベンチ
  reg test_clk;
  reg [7:0] test_duty_cycle;
  wire test_pwm_out;

  pwm_generator pwm(.clk(test_clk), .duty_cycle(test_duty_cycle), .pwm_out(test_pwm_out));

  initial begin
    test_clk = 0;
    test_duty_cycle = 8'd128;  // 50%デューティサイクル

    // クロック生成(100MHz)
    forever #5 test_clk = ~test_clk;
  end

  initial begin
    // テストシナリオ
    #1000 test_duty_cycle = 8'd64;   // 25%デューティサイクル
    #1000 test_duty_cycle = 8'd192;  // 75%デューティサイクル
    #1000 $finish;
  end

  // 結果表示
  initial begin
    $monitor("Time=%0t duty_cycle=%d pwm_out=%b", $time, test_duty_cycle, test_pwm_out);
  end

endmodule

このPWM生成器では、8ビットの分解能(256段階)でデューティサイクルを制御しています。

timescaleを1ナノ秒/1ピコ秒に設定することで、PWM信号の立ち上がりと立ち下がりを正確にシミュレートできます。

実行結果を見ると、デューティサイクルの変更に応じてPWM信号のパターンが変化することが確認できるでしょう。

●SystemVerilogにおけるtimescaleの新機能と活用法

SystemVerilogは、Verilogの拡張版として開発された言語です。時間の扱いに関して、より柔軟で強力な機能を実装しています。

SystemVerilogでのtimescale機能を活用すれば、より精密で効率的な回路設計が可能になります。

○サンプルコード6:SystemVerilogでのtimescale指定

SystemVerilogでは、モジュールごとに異なるtimescaleを指定できます。

複数のモジュールを組み合わせる際に非常に便利な機能です。

`timescale 1ns / 1ps

module fast_module;
  timeunit 1ns;
  timeprecision 1ps;

  initial begin
    #1.5 $display("Time in fast_module: %t", $time);
  end
endmodule

module slow_module;
  timeunit 1us;
  timeprecision 1ns;

  initial begin
    #1.5 $display("Time in slow_module: %t", $time);
  end
endmodule

module top_module;
  fast_module fast();
  slow_module slow();

  initial begin
    #2 $display("Time in top_module: %t", $time);
    #10 $finish;
  end
endmodule

実行結果

Time in fast_module: 1.500 ns
Time in slow_module: 1.500 us
Time in top_module: 2.000 ns

各モジュールで異なる時間単位を使用しているにもかかわらず、SystemVerilogは適切に時間を管理します。

fast_moduleでは1.5ナノ秒後、slow_moduleでは1.5マイクロ秒後に表示が行われています。

○サンプルコード7:階層的なtimescale設定の実装

SystemVerilogでは、階層的にtimescaleを設定できます。

親モジュールの設定を子モジュールが継承する仕組みです。

`timescale 1ns / 1ps

module parent_module;
  timeunit 10ns;
  timeprecision 100ps;

  child_module child();

  initial begin
    #1.5 $display("Time in parent_module: %t", $time);
  end
endmodule

module child_module;
  initial begin
    #1.5 $display("Time in child_module: %t", $time);
  end
endmodule

module top_module;
  parent_module parent();

  initial begin
    #2 $display("Time in top_module: %t", $time);
    #10 $finish;
  end
endmodule

実行結果

Time in parent_module: 15.000 ns
Time in child_module: 15.000 ns
Time in top_module: 2.000 ns

親モジュール(parent_module)で設定されたtimeunitが子モジュール(child_module)に継承されています。

両モジュールとも15ナノ秒後に表示が行われています。

○サンプルコード8:時間単位の自動推論機能の利用

SystemVerilogは、明示的なtimescale指定がない場合、コンパイル時に自動的に時間単位を推論する機能を持っています。

module auto_timeunit;
  initial begin
    #1.5 $display("Time: %t", $time);
    #10ns $display("Time after 10ns: %t", $time);
    #1us $display("Time after 1us: %t", $time);
  end
endmodule

実行結果

Time: 1.500 ns
Time after 10ns: 11.500 ns
Time after 1us: 1011.500 ns

明示的なtimescale指定がなくても、SystemVerilogは適切に時間を解釈しています。

ただし、この機能に頼りすぎると、意図しない動作を引き起こす可能性があるため、重要な設計では明示的な指定を推奨します。

○サンプルコード9:タイムユニット指定を用いた高精度設計

SystemVerilogのタイムユニット指定を使用すると、非常に高精度な時間管理が可能になります。

`timescale 1fs / 1fs

module high_precision_module;
  timeunit 1fs;
  timeprecision 1fs;

  logic clk;
  real period = 1.33333; // 1.33333 fs period

  initial begin
    clk = 0;
    forever #(period/2) clk = ~clk;
  end

  initial begin
    $monitor("Time: %t, Clock: %b", $time, clk);
    #10 $finish;
  end
endmodule

実行結果

Time: 0.000000 fs, Clock: 0
Time: 0.666665 fs, Clock: 1
Time: 1.333330 fs, Clock: 0
Time: 1.999995 fs, Clock: 1
Time: 2.666660 fs, Clock: 0
Time: 3.333325 fs, Clock: 1
Time: 3.999990 fs, Clock: 0
Time: 4.666655 fs, Clock: 1
Time: 5.333320 fs, Clock: 0
Time: 5.999985 fs, Clock: 1

フェムト秒レベルの精度で時間を管理できています。

周期1.33333フェムト秒のクロックが正確に生成されていることがわかります。

●よくあるtimescaleエラーと対処法

timescaleの使用には様々な注意点があります。よく遭遇するエラーとその対処法を見ていきましょう。

○「timescale宣言が見つかりません」エラーの解決策

Verilogコンパイラによっては、timescale宣言が必須の場合があります。

宣言忘れによるエラーは比較的頻繁に発生します。

エラーメッセージ例

Error: No timescale directive found in module.

解決策として、ファイルの先頭に適切なtimescale宣言を追加してください。

`timescale 1ns / 1ps

module my_module;
  // モジュールの内容
endmodule

timescale宣言を忘れずに記述することで、時間単位の不整合を防ぎ、シミュレーション結果の信頼性が向上します。

○異なるtimescale間の衝突を防ぐテクニック

複数のモジュールを組み合わせる際、異なるtimescale設定が衝突することがあります。

問題例

// module_a.v
`timescale 1ns / 1ps
module module_a;
  // モジュールの内容
endmodule

// module_b.v
`timescale 1us / 1ns
module module_b;
  // モジュールの内容
endmodule

// top_module.v
module top_module;
  module_a a();
  module_b b();
endmodule

解決策として、トップモジュールで共通のtimescaleを設定し、サブモジュールではtimescale宣言を省略してみましょう。

// top_module.v
`timescale 1ns / 1ps
module top_module;
  module_a a();
  module_b b();
endmodule

// module_a.v と module_b.v からtimescale宣言を削除

トップモジュールでtimescaleを統一することで、異なる時間単位間の衝突を防ぎ、一貫性のあるシミュレーション結果を得られます。

○シミュレーション結果が予想と異なる場合のデバッグ方法

時々、シミュレーション結果が予想と異なることがあります。

timescaleに関連する問題が原因である可能性があります。

デバッグ手順

  1. すべてのモジュールでtimescale設定を確認します。
  2. 時間に関連する文(#遅延など)を見直します。
  3. $timeシステム関数を使用して、各ポイントでの時間を出力します。

`timescale 1ns / 1ps

module debug_example;
  reg clk;

  initial begin
    clk = 0;
    forever #5 clk = ~clk;
  end

  initial begin
    $monitor("Time: %t, Clock: %b", $time, clk);
    #100 $finish;
  end
endmodule

実行結果

Time: 0, Clock: 0
Time: 5, Clock: 1
Time: 10, Clock: 0
Time: 15, Clock: 1
...
Time: 95, Clock: 1

$monitor文を使用することで、時間の進行とクロックの変化を詳細に観察できます。

予想と異なる動作が発生した場合、時間とイベントの関係を細かく分析することが可能になります。

●FPGAデザインにおけるtimescaleの応用例

FPGAデザインは、高度な電子回路を柔軟に実現できる技術です。

timescaleを適切に活用することで、FPGAの性能を最大限に引き出すことができます。

実際のプロジェクトで役立つtimescale活用例を見ていきましょう。

○サンプルコード10:高速シリアル通信インターフェースの設計

高速シリアル通信は、FPGAの重要な応用分野です。

正確なタイミング制御が求められるため、timescaleの設定が重要になります。

`timescale 1ps / 1ps

module serial_interface (
    input wire clk,
    input wire reset,
    input wire [7:0] data_in,
    output reg serial_out
);

    reg [2:0] bit_counter;
    reg [7:0] shift_register;

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            bit_counter <= 3'b000;
            shift_register <= 8'b0;
            serial_out <= 1'b1;
        end else begin
            if (bit_counter == 3'b000) begin
                shift_register <= data_in;
                serial_out <= 1'b0;  // スタートビット
                bit_counter <= bit_counter + 1;
            end else if (bit_counter < 3'b111) begin
                serial_out <= shift_register[0];
                shift_register <= {1'b0, shift_register[7:1]};
                bit_counter <= bit_counter + 1;
            end else begin
                serial_out <= 1'b1;  // ストップビット
                bit_counter <= 3'b000;
            end
        end
    end

endmodule

1ピコ秒の精度でtimescaleを設定しています。

高速シリアル通信では、ビット間のタイミングが極めて重要です。

このレベルの精度があれば、ギガビット級の通信速度でも正確なシミュレーションが可能になります。

○サンプルコード11:DDR3メモリコントローラの実装

DDR3メモリは、高速かつ複雑なタイミング要求を持つインターフェースです。

timescaleの適切な設定が、正確なシミュレーションには欠かせません。

`timescale 1ns / 1ps

module ddr3_controller (
    input wire clk,
    input wire reset,
    input wire [31:0] address,
    input wire [63:0] write_data,
    output reg [63:0] read_data,
    output reg ready
);

    // DDR3のタイミングパラメータ(単位:クロックサイクル)
    localparam tRCD = 5;  // RAS to CAS Delay
    localparam tRP = 5;   // Row Precharge Time
    localparam tCAS = 5;  // Column Access Strobe Latency

    reg [3:0] state;
    reg [3:0] counter;

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            state <= 4'b0000;
            counter <= 4'b0000;
            ready <= 1'b0;
        end else begin
            case (state)
                4'b0000: begin  // アイドル状態
                    if (address != 32'b0) begin
                        state <= 4'b0001;
                        counter <= tRCD;
                        ready <= 1'b0;
                    end
                end
                4'b0001: begin  // RAS to CAS Delay
                    if (counter == 4'b0000) begin
                        state <= 4'b0010;
                        counter <= tCAS;
                    end else begin
                        counter <= counter - 1;
                    end
                end
                4'b0010: begin  // Column Access
                    if (counter == 4'b0000) begin
                        read_data <= {64{1'b1}};  // ダミーデータ
                        state <= 4'b0011;
                        counter <= tRP;
                    end else begin
                        counter <= counter - 1;
                    end
                end
                4'b0011: begin  // Precharge
                    if (counter == 4'b0000) begin
                        state <= 4'b0000;
                        ready <= 1'b1;
                    end else begin
                        counter <= counter - 1;
                    end
                end
            endcase
        end
    end

endmodule

1ナノ秒の時間単位と1ピコ秒の精度を設定しています。

DDR3メモリのタイミングパラメータは非常にタイトで、ナノ秒単位の制御が必要です。

この設定により、メモリアクセスの各段階を正確にシミュレートできます。

○サンプルコード12:プログラマブル遅延ラインの構築

FPGAの利点の一つは、プログラマブルな遅延を実現できることです。

timescaleを活用すれば、精密な遅延制御が可能になります。

`timescale 1ps / 1ps

module programmable_delay_line #(
    parameter MAX_DELAY = 1000
) (
    input wire clk,
    input wire reset,
    input wire [9:0] delay_value,
    input wire signal_in,
    output reg signal_out
);

    reg [9:0] counter;
    reg [9:0] latched_delay;
    reg signal_delayed;

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            counter <= 10'b0;
            latched_delay <= 10'b0;
            signal_delayed <= 1'b0;
            signal_out <= 1'b0;
        end else begin
            if (counter == 10'b0) begin
                latched_delay <= (delay_value > MAX_DELAY) ? MAX_DELAY : delay_value;
                signal_delayed <= signal_in;
                counter <= latched_delay;
            end else begin
                counter <= counter - 1;
            end

            if (counter == 10'b1) begin
                signal_out <= signal_delayed;
            end
        end
    end

endmodule

1ピコ秒の精度でtimescaleを設定しています。

この精度があれば、1ピコ秒から1ナノ秒までの範囲で、1ピコ秒刻みの遅延を実現できます。

高周波回路や精密なタイミング制御が必要な用途に適しています。

○サンプルコード13:高精度タイマー回路の開発

FPGAを使った計測機器では、高精度なタイマーが必要になることがあります。

timescaleの適切な設定が、タイマーの精度を左右します。

`timescale 1ns / 1ps

module high_precision_timer (
    input wire clk,
    input wire reset,
    input wire start,
    input wire stop,
    output reg [63:0] elapsed_time
);

    reg running;
    reg [63:0] counter;

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            running <= 1'b0;
            counter <= 64'b0;
            elapsed_time <= 64'b0;
        end else begin
            if (start && !running) begin
                running <= 1'b1;
                counter <= 64'b0;
            end else if (stop && running) begin
                running <= 1'b0;
                elapsed_time <= counter;
            end

            if (running) begin
                counter <= counter + 1;
            end
        end
    end

endmodule

1ナノ秒の時間単位と1ピコ秒の精度を設定しています。

この設定により、1ナノ秒の分解能を持つ64ビットカウンターを実現しています。

理論上、約584年間の時間を1ナノ秒の精度で計測できる高精度タイマーとなります。

まとめ

Verilogにおけるtimescaleは、デジタル回路設計の精度と信頼性を大きく左右する重要な概念です。

適切なtimescale設定は、シミュレーション結果の正確性を高め、実際のハードウェア動作との一致を確保します。

本記事では、timescaleの基本から応用まで、幅広いトピックを取り上げました。

timescaleの概念を深く理解し、適切に活用することは、今後のキャリアにおいても大きな強みとなるはずです。