01. スコアボードを作ってみる

以下の文章は、読み物として読むには悪くないと思う(自分で書いていてなんですが)のですが、「要はどのサンプルを見ればいいか」が自分で読んでもわかりづらい気がしましたので、サンプルとして見ても らうコードを別出ししました。サンプルだけ見たい方は、基本スコアボードのサンプルを御覧ください。

期待値と観測値をインプットして、自動比較を行うコンポーネント、それがスコアボードなわけですが…ここでは、

  • moduleで作った場合
  • classで作った場合

を比べて遊んでみようと思います。クラスライブラリ未使用のスコアボードです。

まずmoduleで簡単に作ってみました。

`timescale 1ps/1ps
module m_scrbd;
  string name = "m_scrbd";
  logic [7:0] exp_q[$];
  logic [7:0] obs_q[$];
  function void set_name(string _name);
    name = _name;
  endfunction
  function void write_exp(logic [7:0] data);
    exp_q.push_back(data);
  endfunction
  function void write_obs(logic [7:0] data);
    obs_q.push_back(data);
  endfunction
  initial begin
    forever begin
      wait(obs_q.size()!=0);
      if(exp_q[0]===obs_q[0])
        $display("%0t [SCRBD] %s compare OK. data=%02xh", $time, name, exp_q[0]);
      else
        $display("%0t [SCRBD] %s compare NG. EXP=%02xh, OBS=%02xh", $time, name, exp_q[0], obs_q[0]);
      exp_q.delete(0);
      obs_q.delete(0);
    end
  end
endmodule

さてここで問題です。このスコアボードは、データの型がlogic [7:0]です。32bitのデータ比較したければ、

  • bit幅を31:0にする(手直し)
  • parameterでbit幅を指定できるようにする

まあ当然、後者の方が利便性が高いですね。だって、元の記述を直す必要がありませんから。

それでは次の問題です。スコアボードで、2種類のデータを比較しなければなりません。上記モデルでは1種類のデータしか比較できません。どうしますか?

  • 期待値のqueueを2つに、観測値のqueueを2つに、それぞれのfunction write_xxxを修正し、比較処理も修正する
    • → なんか面倒ですね
  • queueの型をlogic [7:0]にしないで、classにする
    • → 一歩前進。比較処理は修正が必要ですが、上記よりはまし

さて、次の問題です。比較データを格納するclassを作ったとして、class定義が変わったらどうしますか?

  • 使用するclassの名前に合わせて、スコアボードを修正。期待値比較は、classの中に比較処理を埋め込む

では、このように作り変えたスコアボードを掲載します。

`timescale 1ps/1ps
module m_scrbd;
`include "data_item.sv"
  string name = "m_scrbd";
  //logic [7:0] exp_q[$];
  //logic [7:0] obs_q[$];
  data_item exp_q[$];
  data_item obs_q[$];
  data_item for_handle;
  function data_item get_handle();
    for_handle = new();
    return for_handle;
  endfunction
  function void set_name(string _name);
    name = _name;
  endfunction
  //function void write_exp(logic [7:0] data);
  function void write_exp(data_item item);
    exp_q.push_back(item);
  endfunction
  //function void write_obs(logic [7:0] data);
  function void write_obs(data_item item);
    obs_q.push_back(item);
  endfunction
  initial begin
    forever begin
      wait(obs_q.size()!=0);
      //if(exp_q[0]===obs_q[0])
      if(exp_q[0].compare(obs_q[0]))begin
        $display("%0t [SCRBD] %s compare OK ===================================", $time, name);
        exp_q[0].print;
      end else begin
        $display("%0t [SCRBD] %s compare NG ===================================", $time, name);
        exp_q[0].print;
        obs_q[0].print;
      end
      exp_q.delete(0);
      obs_q.delete(0);
    end
  end
endmodule

data_itemはこんな感じで。

class data_item;
  string name;
  logic [7:0] data0;
  logic [7:0] data1;
  function new (string name="data_item");
    this.name = name;
  endfunction
  function bit compare(data_item item);
    if(data0===item.data0 && data1===item.data1)begin
      return 1;
    end else begin
      return 0;
    end
  endfunction
  function void set_data(logic [7:0] data0, data1);
    this.data0 = data0;
    this.data1 = data1;
  endfunction
  function void print;
    $display("----------------------------------------------------------");
    $display("%0t [%s] data0 : %02xh", $time, name, data0);
    $display("%0t [%s] data1 : %02xh", $time, name, data1);
    $display("----------------------------------------------------------");
  endfunction
endclass

…って作ったら動かないですね。何がダメだったかというと、write_expとwrite_obsのfunctionが動かせない。どうやらclass周りの問題のようです。検証の方法は、tb_topからm_scrbdのdata_itemを、functionで returnし、tb_topのdata_itemと$castでcastingできるか否かで判断することにしました。

  • class data_item
  • module tb_top
  • module m_scrbd

の3つの定義があります。で、ですね、以下を試行しました。

tb_top.sv

  if($cast(item, _scrbd.get_handle()))

m_scrbd.sv (instance is _scrbd)

  function data_item get_handle();
    for_handle = new();
    return for_handle;
  endfunction
  1. 1つのファイルに上記3定義を記述する
    1. → $castの結果はtrue
  2. vlog -sv data_item.sv m_scrbd.sv tb_top.sv
    1. → classを認識できず、コンパイルエラー
  3. m_scrbd.svとtb_top.svの、module定義の外にdata_itemをinclude
    1. → コンパイルOK。$cast false
  4. m_scrbd.svとtb_top.svの、module定義の中にdata_itemをinclude
    1. → コンパイルOK。$cast false

以上のことより、1番のファイル構成でなければうまくいかない(modelsim ASEにて)ようです。面倒ですねー。1番の手法で実行した結果を掲載します。

# 0 [SCRBD] m_scrbd compare OK ===================================
# ----------------------------------------------------------
# 0 [EXP] data0 : 01h
# 0 [EXP] data1 : 2bh
# ----------------------------------------------------------
# 0 [SCRBD] m_scrbd compare OK ===================================
# ----------------------------------------------------------
# 0 [EXP] data0 : 02h
# 0 [EXP] data1 : 56h
# ----------------------------------------------------------
# 0 [SCRBD] m_scrbd compare OK ===================================
# ----------------------------------------------------------
# 0 [EXP] data0 : 03h
# 0 [EXP] data1 : 01h
# ----------------------------------------------------------
# 0 [SCRBD] m_scrbd compare OK ===================================
# ----------------------------------------------------------
# 0 [EXP] data0 : 04h
# 0 [EXP] data1 : 81h
# ----------------------------------------------------------
# 0 [SCRBD] m_scrbd compare OK ===================================
# ----------------------------------------------------------
# 0 [EXP] data0 : 05h
# 0 [EXP] data1 : c4h
# ----------------------------------------------------------
# 0 [SCRBD] m_scrbd compare OK ===================================
# ----------------------------------------------------------
# 0 [EXP] data0 : 06h
# 0 [EXP] data1 : 53h
# ----------------------------------------------------------
# 0 [SCRBD] m_scrbd compare OK ===================================
# ----------------------------------------------------------
# 0 [EXP] data0 : 07h
# 0 [EXP] data1 : deh
# ----------------------------------------------------------
# ** Note: $finish    : tb_top.sv(33)

では次の問題です。moduleで定義したスコアボードをclass定義したらどんなメリットがあるでしょう?ぼくの答えは

  • 比較に使うclassをパラメータにすることで、使用するclassを変更しても、class名変更に関するスコアボードの修正が不要になる

説明していきます。

下記の太字のところ。moduleでは「値」をパラメータ化できましたが、classではそれだけでなく「型」もパラメータ化できます。ついでに、classではinitial文を使えないので、task文に変えています。

//module m_scrbd;
class c_scrbd #(type T = int);
  string name = "m_scrbd";
  //data_item exp_q[$];
  //data_item obs_q[$];
  T exp_q[$];
  T obs_q[$];
  function void set_name(string _name);
    name = _name;
  endfunction
  function void write_exp(T item);
    exp_q.push_back(item);
  endfunction
  function void write_obs(T item);
    obs_q.push_back(item);
  endfunction
  //initial begin
  virtual task run;
    forever begin
      wait(obs_q.size()!=0);
      //if(exp_q[0]===obs_q[0])
      if(exp_q[0].compare(obs_q[0]))begin
        $display("%0t [SCRBD] %s compare OK ===================================", $time, name);
        exp_q[0].print;
      end else begin
        $display("%0t [SCRBD] %s compare NG ===================================", $time, name);
        exp_q[0].print;
        obs_q[0].print;
      end
      exp_q.delete(0);
      obs_q.delete(0);
    end
  //end
  endtask
endclass

data_itemの定義は変わりません。

tb_topはこんな感じで。太字のところはポイントです。

`timescale 1ps/1ps
module tb_top;
  `include "data_item.sv"
  `include "c_scrbd.sv"
  c_scrbd #(data_item) _scrbd;
  initial begin
    int i;
    data_item item;
    logic [7:0] data_q[$];
    _scrbd = new();
    fork _scrbd.run(); join_none
    // データ生成
    for(i=0; i<8; i=i+1)begin
      data_q.push_back($urandom_range(255,0));
    end
    for(i=0; i<8; i=i+1)begin
      item = new("EXP");
      item.set_data(i, data_q[i]);
      _scrbd.write_exp(item);
    end
    for(i=0; i<8; i=i+1)begin
      item = new("OBS");
      item.set_data(i, data_q[i]);
      _scrbd.write_obs(item);
    end
    #100 $finish;
  end
endmodule