タスクでテストベンチを効率化する

(1)ここで学ぶ内容

概要

  • テストベンチの記述効率を高める task について理解する
  • CPUのバス動作など task の記述例を学ぶ

目標

以下の項目を理解し説明できる

  • task の定義と呼び出し
  • task の引き数
  • task によるテストベンチの記述
  • taskfunction の共通点と相違点

修了判定1

本文に示した基本タスクの動作確認をする

⚠️ 修了判定:バスReadアクセスのtask化

設問:CPUバスのReadアクセス(指定したアドレスからデータを読み出す)を行う task である cpu_read を記述せよ。アドレスを引数で受け取り、読み出したデータを別の引数で返すこと。

解答例

task cpu_read;
    input  [7:0] adr;    // 読み出し先アドレス
    output [7:0] dat;    // 読み出したデータ
    begin
        // アドレスと制御信号をセット
        ADDR = adr;
        CS_N = 1'b0;
        RD_N = 1'b0;
        WR_N = 1'b1;
        
        // 1サイクル待ってデータを取得
        @(posedge CLK);
        dat = DATA_OUT;
        
        // 制御信号をネゲート
        CS_N = 1'b1;
        RD_N = 1'b1;
        @(posedge CLK);
    end
endtask

修了判定2

データ入力付きカウンタの、データ入力動作をタスクで記述する

修了判定3

シミュレータを使って、データ入力タスクの動作確認を行う
条件:修了判定2を先に実施する

普通のプログラミングではサブルーチンをつかって効率的に処理を表現しています。VerilogHDLのタスクもサブルーチンと同じように使えます。タスクを覚えるとテストベンチがすっきりかけます。

(2)taskとは

initial 文の beginend の中に記述されたテストベンチは、記述の順番に実行されます。

記述の中で同じ処理を何回も実行することがあれば、この処理をまとめることが出来ます。そしてまとめた処理に名前をつけて、initial 文側で呼び出すことでテストベンチを簡潔にまとめることが出来ます。

この場合処理ABCが二か所で呼び出されています。戻り場所はそれぞれ呼び出した場所になります。VerilogHDLではこのような仕組みをタスクと呼びます。

initial 文(呼び出し側)

begin
1
処理ABC ←呼び出し
2
処理ABC ←呼び出し
3
end

処理ABC(タスク定義)

task
A
B
C
endtask

(3)taskの定義と呼び出し

タスクを利用するときにはタスクを定義する記述とテスト入力側で呼び出す記述が必要です。タスクもモジュールアイテムですので、モジュール内で定義します。タスクの呼び出しはステートメントですので、initial 文などの中で記述します。

テストベンチの構成

module
印加する信号の reg 宣言
テスト対象呼び出し
task
    taskの定義
endtask
initial begin
  ・テスト入力作成
  ・taskの呼び出し
end
endmodule

taskの定義

task タスク名;
    引数宣言
    内部信号宣言
begin
    処理の記述
end
endtask

taskの呼び出し

タスク名(引き数, 引き数, ...);
  • タスクの定義は task で始まり endtask で終わる
  • task に続いて、タスク名・引き数宣言・内部信号を宣言する
  • 引数や内部信号のないタスクも存在する
  • タスクの処理は多くの場合2行以上になるので beginend が必要
  • 引数のないタスクではタスク名とセミコロンだけで呼び出せる

(4)taskによるテストベンチ記述例

タスクの簡単な記述例を紹介します。アップダウンカウンタのテストベンチに利用した例です。このカウンタに対し、リセット→カウントアップ→リセット→カウントダウンを行っています。

アップダウン・カウンタ

端子説明
CK立ち上がりで up/down
RES1で非同期リセット
UP1のとき up、0のとき down
Qカウンタ出力(4bit)
reg  CK, RES, UP;

initial begin           // テスト入力
    UP  = 0;            // 初期設定
    reset_counter;      // カウンタのリセット
    UP  = 1;            // カウントアップ
    #(STEP*20);
    reset_counter;      // カウンタのリセット
    UP  = 0;            // カウントダウン
    #(STEP*20);
    $finish;
end

タスクの定義(カウンタのリセット)

task reset_counter;
begin
          RES = 0;
    #STEP RES = 1;
    #STEP RES = 0;
end
endtask
リセット波形(RES)
←STEP→←STEP→
        |‾‾‾‾|
RES ____| 1  |____
            
  • モジュール内で宣言した信号 RES に対し、タスク内で参照・代入できる
  • タスクの中に遅延を記述できるため、呼び出すたびにシミュレーション時刻が進む

(5)taskの引き数

引数とはタスクの呼び出し側と処理側でデータのやり取りをする仕組みです。引数はタスク名に続いて宣言します。モジュールのポートと同じく input, output, inout の3種類があります。

引数の方向

宣言意味
inputタスクの中で参照する(呼び出し側→タスク)
outputタスクの中で代入する(タスク→呼び出し側)
inoutタスクの中で参照/代入する

taskの定義(構造)

task タスク名;
    引数宣言
    内部信号宣言
begin
    処理の記述
end
endtask

taskの呼び出し

タスク名(引き数, 引き数, ...);

引数ありタスクの記述例

task count_updown;
input dir;
begin
    UP = dir;
    #(STEP*20);
end
endtask

呼び出し例

reset_counter;
count_updown(1'b1);
reset_counter;
count_updown(1'b0);
  • 呼び出し側で実際に与える引数 → 実引き数
  • 定義側で宣言した引数 → 仮引き数
実引き数に使用できる信号の種類(引数方向別)
引数方向実引き数に使用できる型
input定数、ネット型(wire)、レジスタ型(reg)
outputレジスタ型(reg)だけ
inoutレジスタ型(reg)だけ

(6)taskの効果的な利用

タスクは本格的なテストベンチの作成に効果的です。例えばCPUに接続される周辺チップを設計し、検証しているとします。CPUはこのチップに対し内部レジスタへの書き込みを行います。また、内部レジスタの読み出しを行います。

バスモデルの概念図
CPU
ADDR ═══════════════════
DATA ═══════════════════
CTRL ═══════════════════
検証対象
(内部レジスタ)
■ reg
■ reg

書き込みや読み出しの一連の動作をタスクで記述 → バスモデル
システム全体のボードレベル検証にとても役立つ

(7)taskによるバス動作の記述

検証対象の回路を想定して、タスクの記述例を紹介します。この回路には8ビットの内部レジスタが4つあります。これらを区別するための2ビットのアドレス信号A、8ビットのデータ入力DIN、データ出力DOUTがあります。また読み出し信号REB、書き込み信号WEBがあり、これらは0で動作します。

検証対象回路(DUT)

端子説明
A2アドレス入力
DIN8データ入力
DOUT8データ出力
REB1読み出し信号(0で動作)
WEB1書き込み信号(0で動作)

書き込みタイミング(3クロック/1データ)

     ←100nS→
CLK   _|‾|_____|‾|_____
    ←25nS→
A[1:0]  ‾‾‾‾addr‾‾‾‾‾‾
    ←25nS→
DIN[7:0] ‾‾‾data‾‾‾‾‾‾
    ←25nS→←200nS→←25nS→
WEB ‾‾‾‾‾‾‾|_____|‾‾‾‾
        
graph TD subgraph taskを使わない場合 TB1[テストベンチ
initialブロック内] TB1 --> W1[CS=1, WR=0, ADDR=0, DATA=A
#10 CS=0, WR=1] TB1 --> W2[CS=1, WR=0, ADDR=1, DATA=B
#10 CS=0, WR=1] TB1 --> W3[CS=1, WR=0, ADDR=2, DATA=C
#10 CS=0, WR=1] end style TB1 fill:#ffebee,stroke:#c62828;
graph TD subgraph taskを使った場合 TB2[テストベンチ
initialブロック内] TASK{{task cpu_write
バスの制御手順をカプセル化}} TB2 -->|cpu_write(0, A)| TASK TB2 -->|cpu_write(1, B)| TASK TB2 -->|cpu_write(2, C)| TASK end style TB2 fill:#e8f5e9,stroke:#388e3c; style TASK fill:#e3f2fd,stroke:#1976d2;

バスモデルの記述

// 接続信号宣言
reg  [1:0] A;
reg  [7:0] DIN;
reg        REB, WEB;
wire [7:0] DOUT;

// レジスタへの書き込みタスク
task write_reg;
input [1:0] addr;
input [7:0] data;
begin
    # 25  A   = addr;
    # 25  DIN = data;
    # 25  WEB = 0   ;
    #200  WEB = 1   ;
    # 25          ;
end
endtask

// タスクの呼び出し
initial begin
    WEB = 1;
    write_reg( 2'h0, 8'h03 );
    write_reg( 2'h2, 8'hFC );
end
  • システムクロック3周期(300ns)で1データの書き込み
  • 0番レジスタに03、2番レジスタにFCを書き込む

(8)task と function の共通点

taskfunction には以下の共通点があります。

  1. 内部信号を宣言して使用できる(parameterも宣言できる)
  2. 仮引き数・実引き数がある
  3. 定義部と呼び出し部がある
  4. モジュール内に記述する(モジュール外に記述するとエラー)
  5. モジュール内の信号にアクセスできる(ただし function では実行時に誤りを起こしやすいため非推奨)

task — 内部信号の宣言例

task TEMP;
    input a;
    reg   b;
begin
    b = 1;
    ...
endtask

function — 内部信号の宣言例

function FUNC;
    input a;
    reg   b;
begin
    b = 1;
    ...
endfunction

(2)仮引き数・実引き数の例(task)

task TEMP;
    input a;
    ...
endtask

initial begin
    TEMP( EN );   // EN が実引き数
    ...
end

(2)仮引き数・実引き数の例(function)

function FUNC;
    input a;
    ...
endfunction

assign Q = FUNC( EN );  // EN が実引き数

(4)モジュール内に記述(task)

module XYZ;
    ...
    task TEMP;
        input a;
        ...
    endtask
    ...
endmodule

(4)モジュール内に記述(function)

module XYZ;
    ...
    function FUNC;
        input a;
        ...
    endfunction
    ...
endmodule

(5)モジュール内の信号アクセス(task)

module XYZ;
    ...
    reg a;

    task TEMP;
        ...      // a に代入できる
    endtask
    ...
endmodule

(5)モジュール内の信号アクセス(function)

module XYZ;
    ...
    reg a;

    function FUNC;
        ...
        FUNC = a;  // 実行時の誤りを起こしやすい
    endfunction
    ...
endmodule
function では引数宣言せずにモジュール内の信号を参照するのは避けること

(9)task と function の相違点

taskfunction
(1)引き数 入力・出力・双方向すべてOK 入力だけ
(2)戻り値の数 output 宣言すればいくつでも ファンクション名に代入するので1つだけ
(3)遅延の記述 記述できる 記述できない(文法エラーになる)
(4)呼び出し側 ステートメント
(5)主な用途 テストベンチ 組み合わせ回路

(1)引き数

// task: 入力・出力・双方向すべてOK
task TEMP;
    input  a;
    output b;
    inout  c;
    ...
endtask
// function: 入力だけ
function FUNC;
    input a;
    ...
endfunction

(2)戻り値の数

// task: output宣言すればいくつでも
task TEMP;
    output a, b, c;
    ...
endtask
// function: ファンクション名に代入→1つだけ
function FUNC;
    ...
    FUNC = 1;
endfunction

(3)遅延の記述

// task: 遅延を記述できる
task TEMP;
    ...
    #STEP a = 0;   // OK
    ...
endtask

(3)遅延の記述

// function: 遅延を記述できない
function FUNC;
    ...
    #STEP FUNC = 1; // エラー
endfunction

(4)呼び出し側 — task はステートメント

initial begin
    ...
    COUNT( EN );   // ステートメントとして呼び出す
    ...
end

(4)呼び出し側 — function は式

always @( posedge CK )
    Q <= FUNC( EN );  // 式として使用できる
// 代入文の右辺、入力引き数、モジュール接続の入力ポートなど

(5)主な用途 — task

task WRITE_REG;
    input ADDR;
    input DATA;
    ...
endtask  // テストベンチ向き

(5)主な用途 — function

function [3:0] SUM;
    ...
    SUM = A + B;
endfunction  // 組み合わせ回路向き
task で順序回路、function でテストベンチを書くことも可能

(10)ワンポイント・アドバイス

テストベンチも「階層構造」

テストベンチ
initial begin
  ...
  応用タスク呼び出し
  ...
end
応用タスク
  • 基本タスクを呼び出して機能を起動
基本タスク
  • 内部レジスタの読み/書き
  • 外部ファイルのアクセス
検証対象

テストベンチ本体から検証対象に直接アクセスせず、すべてタスクを通じて制御することで効率的な検証が行える

(11)修了判定1

設問 基本タスクの動作確認 — 本文に示したタスク write_reg と、ここに示すタスク read_reg の動作確認をする。ファイル:basic_task.v

read_reg タスクの定義

task read_reg;
input  [1:0] addr;
output [7:0] data;
begin
    # 25   A   = addr;
    #100   REB = 0   ;
    #150   data = DOUT;
           REB = 1   ;
    # 25          ;
end
endtask

読み出しタイミング(3クロック/1データ)

     ←100nS→
CLK   _|‾|_____|‾|_____
    ←25nS→
A[1:0]  ‾‾‾‾addr‾‾‾‾‾‾
         ←100nS→←150nS→←25nS→
REB  ‾‾‾‾‾‾‾|____|‾‾‾‾‾‾‾‾
DOUT[7:0]        DOUT出力
                 ↑dataへ取り込み
        

シミュレーション波形(修了判定1)

時間軸: 0 〜 2000以上 [ns]
信号名波形の変化(各ステップ)
CLK高速クロック繰り返し
A[1:0]01231302
DIN[7:0]10a5f703
WEB書き込み期間(0で動作)読み出し期間
REB1(非活性)0で活性(読み出し)
DOUT[7:0]zz(出力なし)a50310f7

シミュレーション出力ログ(抜粋)

   0 CLK=1 DIN=xx WEB=1 REB=1 DOUT=zz
  25 CLK=1 A=0 DIN=xx WEB=1 REB=1 DOUT=zz
  50 CLK=0 A=0 DIN=10 WEB=1 REB=1 DOUT=zz
  75 CLK=0 A=0 DIN=10 WEB=1 REB=1 DOUT=zz
 100 CLK=1 A=0 DIN=10 WEB=0 REB=1 DOUT=zz
 150 CLK=0 A=0 DIN=10 WEB=0 REB=1 DOUT=zz
 200 CLK=0 A=0 DIN=10 WEB=0 REB=1 DOUT=zz
 250 CLK=0 A=0 DIN=10 WEB=0 REB=1 DOUT=zz
 275 CLK=0 A=0 DIN=10 WEB=1 REB=1 DOUT=zz
 300 CLK=1 A=0 DIN=10 WEB=1 REB=1 DOUT=zz
 325 CLK=1 A=1 DIN=10 WEB=1 REB=1 DOUT=zz
 350 CLK=0 A=1 DIN=a5 WEB=1 REB=1 DOUT=zz
 375 CLK=0 A=1 DIN=a5 WEB=0 REB=1 DOUT=zz
 400 CLK=1 A=1 DIN=a5 WEB=0 REB=1 DOUT=zz
 450 CLK=0 A=1 DIN=a5 WEB=0 REB=1 DOUT=zz
 500 CLK=1 A=1 DIN=a5 WEB=0 REB=1 DOUT=zz
 550 CLK=0 A=1 DIN=a5 WEB=0 REB=1 DOUT=zz
 575 CLK=0 A=1 DIN=a5 WEB=1 REB=1 DOUT=zz
 600 CLK=1 A=1 DIN=a5 WEB=1 REB=1 DOUT=zz
 625 CLK=1 A=2 DIN=a5 WEB=1 REB=1 DOUT=zz
 650 CLK=0 A=2 DIN=f7 WEB=1 REB=1 DOUT=zz
 675 CLK=0 A=2 DIN=f7 WEB=0 REB=1 DOUT=zz
 700 CLK=1 A=2 DIN=f7 WEB=0 REB=1 DOUT=zz
 750 CLK=0 A=2 DIN=f7 WEB=0 REB=1 DOUT=zz
 800 CLK=1 A=2 DIN=f7 WEB=0 REB=1 DOUT=zz
 850 CLK=0 A=2 DIN=f7 WEB=0 REB=1 DOUT=zz
 875 CLK=0 A=2 DIN=f7 WEB=1 REB=1 DOUT=zz
 900 CLK=1 A=2 DIN=f7 WEB=1 REB=1 DOUT=zz
 925 CLK=1 A=3 DIN=f7 WEB=1 REB=1 DOUT=zz
 950 CLK=0 A=3 DIN=03 WEB=1 REB=1 DOUT=zz
 975 CLK=0 A=3 DIN=03 WEB=0 REB=1 DOUT=zz
1000 CLK=1 A=3 DIN=03 WEB=0 REB=1 DOUT=zz
1050 CLK=0 A=3 DIN=03 WEB=0 REB=1 DOUT=zz
1100 CLK=1 A=3 DIN=03 WEB=0 REB=1 DOUT=zz
1150 CLK=0 A=3 DIN=03 WEB=0 REB=1 DOUT=zz
1175 CLK=0 A=3 DIN=03 WEB=1 REB=1 DOUT=zz
1200 CLK=1 A=3 DIN=03 WEB=1 REB=1 DOUT=zz
1225 CLK=1 A=1 DIN=03 WEB=1 REB=1 DOUT=zz
1250 CLK=0 A=1 DIN=03 WEB=1 REB=1 DOUT=zz
1300 CLK=1 A=1 DIN=03 WEB=1 REB=1 DOUT=zz
1325 CLK=1 A=1 DIN=03 WEB=1 REB=0 DOUT=a5
1350 CLK=0 A=1 DIN=03 WEB=1 REB=0 DOUT=a5
1400 CLK=0 A=1 DIN=03 WEB=1 REB=0 DOUT=a5
1450 CLK=0 A=1 DIN=03 WEB=1 REB=0 DOUT=a5
1475 CLK=0 A=1 DIN=03 WEB=1 REB=1 DOUT=zz
1500 CLK=1 A=1 DIN=03 WEB=1 REB=1 DOUT=zz
1525 CLK=1 A=3 DIN=03 WEB=1 REB=1 DOUT=zz
1550 CLK=0 A=3 DIN=03 WEB=1 REB=1 DOUT=zz
1600 CLK=1 A=3 DIN=03 WEB=1 REB=0 DOUT=03
1625 CLK=1 A=3 DIN=03 WEB=1 REB=0 DOUT=03
1650 CLK=0 A=3 DIN=03 WEB=1 REB=0 DOUT=03
1700 CLK=0 A=3 DIN=03 WEB=1 REB=0 DOUT=03
1750 CLK=0 A=3 DIN=03 WEB=1 REB=0 DOUT=03
1775 CLK=0 A=3 DIN=03 WEB=1 REB=1 DOUT=zz
1800 CLK=1 A=3 DIN=03 WEB=1 REB=1 DOUT=zz
1825 CLK=1 A=0 DIN=03 WEB=1 REB=1 DOUT=zz
1850 CLK=0 A=0 DIN=03 WEB=1 REB=1 DOUT=zz
1900 CLK=1 A=0 DIN=03 WEB=1 REB=0 DOUT=10
1925 CLK=1 A=0 DIN=03 WEB=1 REB=0 DOUT=10
1950 CLK=0 A=0 DIN=03 WEB=1 REB=0 DOUT=10
2000 CLK=1 A=0 DIN=03 WEB=1 REB=0 DOUT=10
2050 CLK=0 A=0 DIN=03 WEB=1 REB=0 DOUT=10
2075 CLK=0 A=0 DIN=03 WEB=1 REB=1 DOUT=zz
2100 CLK=1 A=0 DIN=03 WEB=1 REB=1 DOUT=zz
2125 CLK=1 A=2 DIN=03 WEB=1 REB=1 DOUT=zz
2150 CLK=0 A=2 DIN=03 WEB=1 REB=1 DOUT=zz
2200 CLK=1 A=2 DIN=03 WEB=1 REB=0 DOUT=f7
2225 CLK=1 A=2 DIN=03 WEB=1 REB=0 DOUT=f7
2250 CLK=0 A=2 DIN=03 WEB=1 REB=0 DOUT=f7
2300 CLK=1 A=2 DIN=03 WEB=1 REB=0 DOUT=f7
2350 CLK=0 A=2 DIN=03 WEB=1 REB=0 DOUT=f7
2375 CLK=0 A=2 DIN=03 WEB=1 REB=1 DOUT=zz

(12)修了判定2

設問 波形の入力信号を生成するタスク data_load を完成する

COUNT4LD 回路の仕様

RESLDCKQ
1XX0
01D
00Q₀+1
data_load タスクのタイミング(クロック2周期)
     ←STEP→
CK   _|‾|______|‾|___
LD   ‾‾‾‾|___|‾‾‾‾‾‾
D        ← 4'h9 →
            
クロック2周期分のタスク

data_load タスクの記述(完成形)

reg         CK, RES, LD;  // カウンタ入力
reg  [3:0]  D;            // カウンタ入力
wire [3:0]  Q;            // カウンタ出力

// 検証対象(カウンタ)接続
COUNT4LD C1( CK, RES, LD, D, Q );

// データのロードを行うタスク
// 呼び出し例: data_load( 4'h9 );
task data_load;
input [3:0] din;
begin
    #(STEP/2)  LD = 1;
               D  = din;
    #STEP      LD = 0;
    #(STEP/2);
end
endtask

(13)修了判定3

設問 シミュレータを使って、タスク data_load の動作を確認する。条件:修了判定2を先に実施する
ファイル種別ファイル名
回路記述ファイルcount4ld.v
テストベンチ・ファイルcount4ld_test.v

シミュレーション波形(修了判定3)

時間軸: 0 〜 40000以上
信号名変化の様子
CK高速クロック繰り返し
RES最初だけ短時間 HIGH(非同期リセット)
LDdata_load 呼び出し時に 1 → 0 と変化
D[3:0]0 → 9(0x9)→ 6(0x6)と変化
Q[3:0]0 1 2 3 4 5 6 7 8 9 a b c d e f | 0 1 2 3 4 5 9 a b c d e f | 0 1 2 6 7 8 9 a b c d e f ...

シミュレーション出力ログ(抜粋)

     0 CK=1 RES=0 LD=0 D=0 Q=x
   500 CK=0 RES=1 LD=0 D=0 Q=0
  1000 CK=1 RES=1 LD=0 D=0 Q=0
  1500 CK=0 RES=0 LD=0 D=0 Q=0
  2000 CK=1 RES=0 LD=0 D=0 Q=1
  2500 CK=0 RES=0 LD=0 D=0 Q=1
  3000 CK=1 RES=0 LD=0 D=0 Q=2
  3500 CK=0 RES=0 LD=0 D=0 Q=2
  4000 CK=1 RES=0 LD=0 D=0 Q=3
  4500 CK=0 RES=0 LD=0 D=0 Q=3
  5000 CK=1 RES=0 LD=0 D=0 Q=4
  5500 CK=0 RES=0 LD=0 D=0 Q=4
  6000 CK=1 RES=0 LD=0 D=0 Q=5
  6500 CK=0 RES=0 LD=0 D=0 Q=5
  7000 CK=1 RES=0 LD=0 D=0 Q=6
  7500 CK=0 RES=0 LD=0 D=0 Q=6
  8000 CK=1 RES=0 LD=0 D=0 Q=7
  8500 CK=0 RES=0 LD=0 D=0 Q=7
  9000 CK=1 RES=0 LD=0 D=0 Q=8
  9500 CK=0 RES=0 LD=0 D=0 Q=8
 10000 CK=1 RES=0 LD=0 D=0 Q=9
 10500 CK=0 RES=0 LD=0 D=0 Q=9
 11000 CK=1 RES=0 LD=0 D=0 Q=a
 11500 CK=0 RES=0 LD=0 D=0 Q=a
 12000 CK=1 RES=0 LD=0 D=0 Q=b
 12500 CK=0 RES=0 LD=0 D=0 Q=b
 13000 CK=1 RES=0 LD=0 D=0 Q=c
 13500 CK=0 RES=0 LD=0 D=0 Q=c
 14000 CK=1 RES=0 LD=0 D=0 Q=d
 14500 CK=0 RES=0 LD=0 D=0 Q=d
 15000 CK=1 RES=0 LD=0 D=0 Q=e
 15500 CK=0 RES=0 LD=0 D=0 Q=e
 16000 CK=1 RES=0 LD=0 D=0 Q=f
 16500 CK=0 RES=0 LD=0 D=0 Q=f
 17000 CK=1 RES=0 LD=0 D=0 Q=0
 17500 CK=0 RES=0 LD=0 D=0 Q=0
 18000 CK=1 RES=0 LD=0 D=0 Q=1
 18500 CK=0 RES=0 LD=0 D=0 Q=1
 19000 CK=1 RES=0 LD=0 D=0 Q=2
 19500 CK=0 RES=0 LD=0 D=0 Q=2
 20000 CK=1 RES=0 LD=0 D=0 Q=3
 20500 CK=0 RES=0 LD=0 D=0 Q=3
 21000 CK=1 RES=0 LD=0 D=0 Q=4
 21500 CK=0 RES=0 LD=0 D=0 Q=4
 22000 CK=1 RES=0 LD=1 D=9 Q=9  ← data_load(9) ロード
 22500 CK=0 RES=0 LD=1 D=9 Q=9
 23000 CK=1 RES=0 LD=0 D=9 Q=9
 23500 CK=0 RES=0 LD=0 D=9 Q=9
 24000 CK=1 RES=0 LD=0 D=9 Q=a
 24500 CK=0 RES=0 LD=0 D=9 Q=a
 25000 CK=1 RES=0 LD=0 D=9 Q=b
 25500 CK=0 RES=0 LD=0 D=9 Q=b
 26000 CK=1 RES=0 LD=0 D=9 Q=c
 26500 CK=0 RES=0 LD=0 D=9 Q=c
 27000 CK=1 RES=0 LD=0 D=9 Q=d
 27500 CK=0 RES=0 LD=0 D=9 Q=d
 28000 CK=1 RES=0 LD=0 D=9 Q=e
 28500 CK=0 RES=0 LD=0 D=9 Q=e
 29000 CK=1 RES=0 LD=0 D=9 Q=f
 29500 CK=0 RES=0 LD=0 D=9 Q=f
 30000 CK=1 RES=0 LD=0 D=9 Q=0
 30500 CK=0 RES=0 LD=0 D=9 Q=0
 31000 CK=1 RES=0 LD=0 D=9 Q=1
 31500 CK=0 RES=0 LD=0 D=9 Q=1
 32000 CK=1 RES=0 LD=1 D=6 Q=6  ← data_load(6) ロード
 32500 CK=0 RES=0 LD=1 D=6 Q=6
 33000 CK=1 RES=0 LD=0 D=6 Q=6
 33500 CK=0 RES=0 LD=0 D=6 Q=6
 34000 CK=1 RES=0 LD=0 D=6 Q=7
 34500 CK=0 RES=0 LD=0 D=6 Q=7
 35000 CK=1 RES=0 LD=0 D=6 Q=8
 35500 CK=0 RES=0 LD=0 D=6 Q=8
 36000 CK=1 RES=0 LD=0 D=6 Q=9
 36500 CK=0 RES=0 LD=0 D=6 Q=9
 37000 CK=1 RES=0 LD=0 D=6 Q=a
 37500 CK=0 RES=0 LD=0 D=6 Q=a
 38000 CK=1 RES=0 LD=0 D=6 Q=b
 38500 CK=0 RES=0 LD=0 D=6 Q=b
 39000 CK=1 RES=0 LD=0 D=6 Q=c
 39500 CK=0 RES=0 LD=0 D=6 Q=c
 40000 CK=1 RES=0 LD=0 D=6 Q=d
 40500 CK=0 RES=0 LD=0 D=6 Q=d
 41000 CK=1 RES=0 LD=0 D=6 Q=e
 41500 CK=0 RES=0 LD=0 D=6 Q=e
 42000 CK=1 RES=0 LD=0 D=6 Q=f
 42500 CK=0 RES=0 LD=0 D=6 Q=f
 43000 CK=1 RES=0 LD=0 D=6 Q=0
 43500 CK=0 RES=0 LD=0 D=6 Q=0

📌 まとめ

  • task はサブルーチンのように繰り返し処理をまとめてテストベンチを簡潔にする
  • task の定義は task〜endtask。引数は input/output/inout の3方向
  • task 内部には遅延・内部信号・parameter を記述できる
  • バスモデルとして活用すると、ボードレベル検証が効率的になる
  • タスクを「基本タスク」「応用タスク」と階層化することで大規模テストベンチを整理できる
  • function との主な違い: 引数方向・戻り値数・遅延記述の可否・呼び出し構文(ステートメント vs 式)
もふねこ

お疲れさま!タスクを使ったテストベンチの記述、理解できたかな?🐾
タスクをうまく使えば、複雑な検証シナリオもスッキリと書けるようになるね!