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つのファイルに上記3定義を記述する
- → $castの結果はtrue
- vlog -sv data_item.sv m_scrbd.sv tb_top.sv
- → classを認識できず、コンパイルエラー
- m_scrbd.svとtb_top.svの、module定義の外にdata_itemをinclude
- → コンパイルOK。$cast false
- m_scrbd.svとtb_top.svの、module定義の中にdata_itemをinclude
- → コンパイル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