入力はクロックに同期させる

[] [] []


テストベンチの入出力タイミングは、

(1) DUTへの入力はクロックから遅らせる

(2) DUTの出力の観測タイミングはクロックと同時

が基本である。もちろん非同期な入力信号はこうする必要はないのだが、同期設計してあればクロック周期以下の時間をシミュレーションしても意味がないので、クロック同期で入力した方がよい。

初心者はよく入力波形をそのまま記述してしまうが、クロックが複数ある場合などはこの書き方では無理がある。

×// 入力波形をそのまま記述

parameter TCLK_HALF = 5; // クロック半周期

initial begin

               clk = 1'b1; enb = 1'b0; din = 16'd0;

  #(TCLK_HALF) clk = 1'b0; enb = 1'b0; din = 16'd0;

  #(TCLK_HALF) clk = 1'b1; enb = 1'b0; din = 16'd0;

  #(TCLK_HALF) clk = 1'b0; enb = 1'b1; din = 16'd5;

  #(TCLK_HALF) clk = 1'b1; enb = 1'b1; din = 16'd5;

  #(TCLK_HALF) clk = 1'b0; enb = 1'b1; din = 16'd6;

  #(TCLK_HALF) clk = 1'b1; enb = 1'b1; din = 16'd6;

  ...

end

そこで、クロックを個別に作っておいて、入力をこれらのクロックに同期させるとよい。

// クロックの生成

parameter TCLK_HALF = 5; // クロック半周期

initial begin

  clk = 1'b1;

  forever #(TCLK_HALF) clk = ~clk;

end

// クロックに同期させた入力

initial begin

  enb = 1'b0; din = 16'd0;     // 初期値入力

  repeat(2) @(posedge clk);    // 2clk進める

  enb <= 1'b1; din <= 16'd5;   // 入力を変化させる

  @(posedge clk);              // 1clk進める

  din <= 16'd6;                // 入力を変化させる

  ...

end

ここで@(posedge clk);後の代入はノンブロッキング代入(<=)を使う。もしテストベンチでブロッキング代入(=)を使うと、DUTモジュール内のノンブロッキング代入(<=)より常に先に実行されるので下図のようなタイミングになる。

×

// テストベンチにブロッキング代入を使う

initial begin

  dut_in = 1'b0;

  @(posedge clk);

  dut_in = 1'b1;

  ...

end

 

// DUTモジュール内の記述

always @(posedge clk) begin

    dut_reg <= dut_in;

end

このタイミングでも間違いではないが、下図のようにDUTモジュール内の転送タイミングと同様にしておいた方がわかりやすい。

// テストベンチにノンブロッキング代入を使う

initial begin

  dut_in = 1'b0; // 初期値入力

  @(posedge clk);

  dut_in <= 1'b1;

  ...

end

 

// DUTモジュール内の記述

always @(posedge clk) begin

    dut_reg <= dut_in;

end

◇◇

RTL記述はゼロ遅延なのでこれでよいが、配置配線後の遅延付きネットリストに対してもテストベンチが使えるようにするため、さらにノンブロッキング代入の右辺に遅延をつけた方がよい。遅延値(TCO)はタイミング設計値を設定するのが正確だが、機能検証としてはクロック半周期にしておけば大抵は十分である。

// テストベンチに遅延がついたノンブロッキング代入を使う

parameter TCO = TCLK_HALF; // 入力の遅延

initial

  begin

  dut_in = 1'b0;     // 初期値入力

  @(posedge clk)

  dut_in <= #(TCO) 1'b1;

  ...

  end

ここで、右辺に遅延をつけるのと文全体に遅延をつけるのとでは次の違いがある。

 dut_in <= #(TCO) dut_out;  ←現在のdut_outの値をTCO後に代入する

 #(TCO) dut_in <= dut_out;  TCO後のdut_outの値をTCO後に代入する

もし右辺がDUTの出力を含む場合はクロック立ち上がり時点の値を使いたいので、文全体でなく右辺に遅延をつけておく。

ちなみにRTL記述のシミュレーションでも入力に遅延をつけておいた方がよい。なぜなら、FPGA内のPLLのシミュレーションモデルでノンブロッキング代入で記述されていた場合に、遅延値がゼロ(DELAY=0)のときでもテストベンチから入れたクロック(clk)FPGA内部のクロック(PLL出力clk0)が同一でなく順序がついた異なる信号になるためである。

// PLLのシミュレーションモデル

time clk0_delay=DELAY; // ゼロ遅延のときDELAY=0

...

always @(clk) begin

  clk0 <= #(clk0_delay) clk;

end

このときテストベンチから入れたclkよりもFPGA内部のclk0 (PLL出力)の方が順序が後になるので、下図のようにテストベンチにブロッキング代入(=)を使ったときと同じようなタイミングで入力されてしまう。

◇◇◇

入力は決め打ちで入れるだけでなく、DUTの出力に応じて入力すれば柔軟にテストシナリオが書ける

// DUTの出力に応じて入力する

initial begin

  dut_in = 16'h0000;

  // DUTの出力を待つ

  while(dut_ready!==1) @(posedge clk);

  dut_in <= #(TCO) 16'h55aa;

 @(posedge clk);

  // DUTの出力によって入力値を変える

  error_in <= #(TCO) (dut_out!==16'h55aa)? 1'b1:1'b0; 

 @(posedge clk);

  // DUTの出力によって表示を変える

  if(dut_out===16'bzzzz)

    $display("-- ERROR");

  else

    $display("-- dut_out=%h",dut_out);

  ...

end

ここで、!=====1,0の他にz,xも比較できる関係演算子で、論理合成向けの記述では使わないがテストベンチでは使う。(補足 - z,xに対する比較演算子とcase)


[] [] []