8日目になりました。これまでは、やっていること自体はそう大したことをしていません。技術的な土台を作り上げるためですので。今回は、すこし面白いことをしようと思います。ランダムデータを転送し、 読みだして期待値比較させますが、期待値比較は「自動化」します。sequenceの中で書き込みデータをランダム生成し、それを配列に入れておいて、ライトを行ったあとにリードしてリードデータを if 文で比 較することはできますが、例えば1000回のライトリードコンペアを行うとして、
するのは簡単ですね。比較処理は、foreach で回せます。それでは次のはどうでしょう?
この場合、task化しなければとてもやってられませんね。ではさらに複雑化させて、アドレスをランダム化させたらどうなるでしょうか?この場合、どのアドレスにどのデータを書いたか、sequence内で保持し ておかなければ、比較できませんね。言葉で表現するのは簡単だけれど、実際にどうやってコーディングしよう…となると、この辺からスキルの差が生まれてきたりします。
今回は、このような複雑なテストケースであっても、容易に比較ができることを説明します。そのためには、このサイトで無償公開している「汎用比較器」を用います。
スコアボードを使うためには、「analysis_port」というのをモニタに実装する必要があります。今回は、マスタのモニタを使います。また汎用比較器では、比較に使用するデータはクラスに入れて使う仕様なの で、データを格納するクラスを定義します。このファイルは、モデル依存なので、model ディレクトリの中に置いて、includeファイル sample_model.svhに加えます。
class sample_scrbd_item extends uvm_object; bit [7:0] addr, data; `uvm_object_utils_begin(sample_scrbd_item) `uvm_field_int(addr, UVM_DEFAULT) `uvm_field_int(data, UVM_DEFAULT) `uvm_object_utils_end function new (string name="sample_scrbd_item"); super.new(name); endfunctionendclassここで、uvm_field_int ですが、これはクラスに定義したメンバ(addr, data)に対して、特殊な属性を付加するマクロです。UVM_DEFAULTと書かれたところが、その属性をセットするところで、種類はたくさ んあります。今回は、汎用比較器の中でこの「属性」を利用しているので、定義しています。この属性値については、後日どこかのエントリーで書いてみたいと思います。
上記クラスは、モデル依存のファイルですので、モデル置き場(model/ )に置くとします。すると、sample_model.svhは以下のようになります。
`include "sample_seq_item.sv"`include "sample_master_seq_lib.sv"`include "sample_slave_seq_lib.sv"`include "sample_scrbd_item.sv"`include "sample_master_driver.sv"`include "sample_master_monitor.sv"`include "sample_master_sequencer.sv"`include "sample_master_agent.sv"`include "sample_slave_driver.sv"`include "sample_slave_sequencer.sv"`include "sample_slave_agent.sv"`include "sample_env.sv"このクラスを使って、analysis_portでやり取りするための定義をします。変更内容は
class sample_master_monitor extends uvm_monitor; virtual sample_if vif; uvm_analysis_port #(sample_scrbd_item) ap_write; uvm_analysis_port #(sample_scrbd_item) ap_read; event scrbd_e; //イベント定義 `uvm_component_utils(sample_master_monitor) function new (string name, uvm_component parent); super.new(name, parent); ap_write = new("ap_write", this); ap_read = new("ap_read", this); endfunction function void build_phase(uvm_phase phase); bit status; super.build_phase(phase); status = uvm_config_db#(virtual sample_if)::get(this, "", "vif", vif); if(status==1'b0) uvm_report_fatal("NOVIF", {"virtual interface must be set for: ",get_full_name(),".vif"}); endfunction task run_phase(uvm_phase phase); string s_trans_kind; uvm_report_info("MONITOR", "Hi"); fork scrbd_write; join_none //スコアボード処理タスクを飛ばす forever begin @(posedge vif.valid); wait(vif.ready===1'b1); -> scrbd_e; // イベント発行 if(vif.write===1'b1) uvm_report_info("MON", $sformatf("write addr=%02xh wdata=%02xh", vif.addr, vif.wdata)); else if(vif.write===1'b0) uvm_report_info("MON", $sformatf("read addr=%02xh rdata=%02xh", vif.addr, vif.rdata)); else uvm_report_info("MON", $sformatf("signal write is unknown...")); end endtask task scrbd_write; sample_scrbd_item item; forever begin @scrbd_e; item = new(); item.addr = vif.addr; if(vif.write===1'b1)begin item.data = vif.wdata; // write側のanalysis_port ap_write.write(item); end else if(vif.write===1'b0)begin item.data = vif.rdata; ap_read.write(item); // read側のanalysis_port end end endtaskendclassつぎに、テストベンチ側のuvm_envクラスを定義します。クラス名はtb_envとでもしましょうか。ついでに、テストベンチ系ファイルを、tb というディレクトリに入れることにします。gp_scoreboardには、2 つのanalysis_portがあります。monitorに追加したanalysis_port2つと、gp_scoreboardにある2つのanalysis_portをつなぎます。
class tb_env extends uvm_env; sample_env sample_model; //インスタンス名はenvでもいいが、もし複数のモデルを組み込むならば、個々に判別できる //名前でなければならないため、このようなインスタンス名にしている gp_scoreboard #(sample_scrbd_item) sample_scrbd; // 汎用スコアボード `uvm_component_utils(tb_env) function new (string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); sample_model = sample_env::type_id::create("sample_model", this); sample_scrbd = gp_scoreboard#(sample_scrbd_item)::type_id::create("sample_scrbd", this); endfunction// monitorとscoreboardのanalysis_portを接続する
function void connect_phase(uvm_phase phase); sample_model.master.monitor.ap_write.connect(sample_scrbd.ap_exp); //期待値用 sample_model.master.monitor.ap_read.connect(sample_scrbd.ap_obs); //観測地用 endfunctionendclass汎用比較器、gp_scoreboard.svを用意します。こちらからどうぞ。rev1.2が最新です。
ファイルをどこかに置いて、tb_top.svにincludeします。太字が変更箇所です。
`timescale 1ps/1psmodule tb_top; // UVM class library `include "uvm_macros.svh" import uvm_pkg::*; // common library `include "gp_scoreboard.sv" // uvm user code `include "sample_model.svh" `include "tb_env.sv" `include "sample_test.sv" /////////////////////////////////////次に、uvm_testクラスを修正します。いままでは、sample_envを直接組み込んでいましたが、あるべき姿としては、sample_envはtb_envに組み込まれる方が適切です。sample_test.svを修正します。 sample_test2のクラスも、同様に修正してください。なお、せっかくクラスを使っているのだから、extendsでやらないのか?と思われる方もいると思います。その通りなのですが、ステップを踏んでいくこと で「面倒な手間を省く」ことも知ってほしいので、意図的です。
class sample_test extends uvm_test; `uvm_component_utils(sample_test) tb_env tb; function new (string name="sample_test", uvm_component parent=null); super.new(name,parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); uvm_config_db#(uvm_object_wrapper)::set(this, "tb.sample_model.master.sequencer.run_phase", "default_sequence", write_read_seq::type_id::get()); uvm_config_db#(uvm_object_wrapper)::set(this, "tb.sample_model.slave.sequencer.run_phase", "default_sequence", normal_response_seq::type_id::get()); tb = tb_env::type_id::create("tb", this); endfunction task run_phase(uvm_phase phase); uvm_report_info("TEST", "Hello World"); uvm_top.print_topology(); endtaskendclassコンパイルして実行します。
# UVM_INFO @ 300: uvm_test_top.tb.sample_model.master.monitor [MON] write addr=10h wdata=5ah
# UVM_INFO @ 300: uvm_test_top.tb.sample_scrbd [SCRBD] write expected data
# UVM_INFO @ 600: uvm_test_top.tb.sample_model.master.monitor [MON] read addr=10h rdata=5ah
# UVM_INFO @ 600: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 600: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK
ログの部分抽出ですが、スコアボード(SCRBD)のログが出ていることがわかります。期待値を書き込んだメッセージ、観測値を書き込んだメッセージ、そして比較結果が表示されます。
商用シミュレータなら簡単にできるのですが、無償で使えるmodelsim-aseでは「ただのランダム」しか発生できません。具体的に言うと、SystemVerilogの制約 constraintが使えませんので、ちょっと工夫が必要 です。この文章を書いている時点では、まだ考え中なのです。
それでは、手順を踏んで組み込んでみましょう。
write_read_all というsequenceを書いてみたいと思います。
class write_read_all_seq extends sample_master_base_seq; write_seq _write; read_seq _read; `uvm_object_utils(write_read_all_seq) function new (string name="write_read_all_seq"); super.new(name); endfunction virtual task body(); int i; for(i=0; i<256; i=i+1)begin `uvm_create(_write) _write.addr = i; _write.data = $urandom_range(255,0); `uvm_send(_write) end for(i=0; i<256; i=i+1)begin `uvm_create(_read) _read.addr = i; `uvm_send(_read) end endtaskendclass他に、sample_slave_driver.svも修正してください。(これはバグ修正)seq_item_port.item_done(req); //reqを消してくださいuvm_testクラスの追加ですが、このページの一番下を御覧ください。記述量削減したコードを掲載しています。長いので、部分的に掲載します。連続ライト→連続リードのため、以下は連続リードの一部だけ抜き出しています。
# UVM_INFO @ 227300: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 227300: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK. addr=77h
# UVM_INFO @ 227600: uvm_test_top.tb.sample_model.master.monitor [MON] read addr=78h rdata=b1h
# UVM_INFO @ 227600: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 227600: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK. addr=78h
# UVM_INFO @ 228100: uvm_test_top.tb.sample_model.master.monitor [MON] read addr=79h rdata=37h
# UVM_INFO @ 228100: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 228100: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK. addr=79h
# UVM_INFO @ 228700: uvm_test_top.tb.sample_model.master.monitor [MON] read addr=7ah rdata=4dh
# UVM_INFO @ 228700: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 228700: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK. addr=7ah
# UVM_INFO @ 228900: uvm_test_top.tb.sample_model.master.monitor [MON] read addr=7bh rdata=9fh
# UVM_INFO @ 228900: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 228900: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK. addr=7bh
# UVM_INFO @ 229300: uvm_test_top.tb.sample_model.master.monitor [MON] read addr=7ch rdata=6eh
# UVM_INFO @ 229300: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 229300: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK. addr=7ch
write_read_rand_all というsequenceを書いてみたいと思います。コードを説明しておくと、
class write_read_rand_all_seq extends sample_master_base_seq; write_seq _write; read_seq _read; `uvm_object_utils(write_read_rand_all_seq) function new (string name="write_read_rand_all_seq"); super.new(name); endfunction virtual task body(); int i, remain, len; bit [7:0] addr_q[$], read_addr_q[$]; // アドレス生成 for(i=0; i<256; i=i+1)begin addr_q.push_back(i); end while(addr_q.size() != 0)begin len = $urandom_range(16,1); // 1-16のrandom値生成 if( (addr_q.size()-len) < 0 ) len = remain; //remain = remain - len; $display("=================================================================="); uvm_report_info("SEQ", $sformatf("Transaction Start. length=%0d", len)); for(i=0; i<len; i=i+1)begin bit [7:0] addr; `uvm_create(_write) addr = gen_rand_addr(addr_q); read_addr_q.push_back(addr); _write.addr = addr; _write.data = $urandom_range(255,0); `uvm_send(_write) end $display("------------------------------------------------------------------"); for(i=0; i<len; i=i+1)begin `uvm_create(_read) _read.addr = gen_rand_addr(read_addr_q); `uvm_send(_read) end end endtask function bit [7:0] gen_rand_addr(ref bit [7:0] addr_src_q[$]); int index; bit [7:0] rval; //$display("remain=%0d", addr_src_q.size()); index = $urandom_range(addr_src_q.size()-1,0); rval = addr_src_q[index]; addr_src_q.delete(index); return rval; endfunctionendclassこのテストを実行するためには、他にも手を加える必要があります。ちょっと綺麗な作りでないです。すみません。
1.gp_scoreboardをmode2で動かします。そのためには…
sample_master_monitor.svを修正します。
task scrbd_write; sample_scrbd_item item; forever begin @scrbd_e; item = new(); item.addr = vif.addr; if(vif.write===1'b1)begin item.data = vif.wdata; // write側のanalysis_port item.`_GP_SCOREBOARD_MODE2_MARK = vif.addr; ap_write.write(item); end else if(vif.write===1'b0)begin item.data = vif.rdata; item.`_GP_SCOREBOARD_MODE2_MARK = vif.addr; ap_read.write(item); // read側のanalysis_port end end endtask2.tb_top.svに define を加えます。
`timescale 1ps/1ps
`define _GP_SCOREBOARD_MODE2_MARK markmodule tb_top;3.tb_env.svを修正します。
class tb_env extends uvm_env; sample_env sample_model; gp_scoreboard #(sample_scrbd_item, bit[7:0]) sample_scrbd; `uvm_component_utils(tb_env) function new (string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); uvm_config_db#(int)::set(this, "sample_scrbd", "mode", 2); sample_model = sample_env::type_id::create("sample_model", this); sample_scrbd = gp_scoreboard#(sample_scrbd_item, bit[7:0])::type_id::create("sample_scrbd", this); endfunction function void connect_phase(uvm_phase phase); sample_model.master.monitor.ap_write.connect(sample_scrbd.ap_exp); //期待値用 sample_model.master.monitor.ap_read.connect(sample_scrbd.ap_obs); //観測地用 endfunctionendclass4.sample_scrbd_item.svを修正します。
class sample_scrbd_item extends uvm_object; bit [7:0] `_GP_SCOREBOARD_MODE2_MARK; bit [7:0] addr, data; `uvm_object_utils_begin(sample_scrbd_item) `uvm_field_int(addr, UVM_DEFAULT) `uvm_field_int(data, UVM_DEFAULT) `uvm_object_utils_end function new (string name="sample_scrbd_item"); super.new(name); endfunctionendclassたぶんこれらの変更で動くはずです。(ちょっとバグ潰しながら書いていたので、抜けがあるかもしれません)
長いので、部分的に掲載します。アドレスへのアクセス順番がランダムなのがわかると思います。
# ==================================================================
# UVM_INFO @ 297200: uvm_test_top.tb.sample_model.master.sequencer@@write_read_rand_all_seq [SEQ] Transaction Start. length=7
# UVM_INFO @ 297900: uvm_test_top.tb.sample_model.master.monitor [MON] write addr=bah wdata=0bh
# UVM_INFO @ 297900: uvm_test_top.tb.sample_scrbd [SCRBD] write expected data
# UVM_INFO @ 298900: uvm_test_top.tb.sample_model.master.monitor [MON] write addr=e1h wdata=7dh
# UVM_INFO @ 298900: uvm_test_top.tb.sample_scrbd [SCRBD] write expected data
# UVM_INFO @ 299500: uvm_test_top.tb.sample_model.master.monitor [MON] write addr=90h wdata=6bh
# UVM_INFO @ 299500: uvm_test_top.tb.sample_scrbd [SCRBD] write expected data
# UVM_INFO @ 300200: uvm_test_top.tb.sample_model.master.monitor [MON] write addr=74h wdata=07h
# UVM_INFO @ 300200: uvm_test_top.tb.sample_scrbd [SCRBD] write expected data
# UVM_INFO @ 300900: uvm_test_top.tb.sample_model.master.monitor [MON] write addr=03h wdata=49h
# UVM_INFO @ 300900: uvm_test_top.tb.sample_scrbd [SCRBD] write expected data
# UVM_INFO @ 301900: uvm_test_top.tb.sample_model.master.monitor [MON] write addr=e9h wdata=94h
# UVM_INFO @ 301900: uvm_test_top.tb.sample_scrbd [SCRBD] write expected data
# UVM_INFO @ 302200: uvm_test_top.tb.sample_model.master.monitor [MON] write addr=50h wdata=d4h
# UVM_INFO @ 302200: uvm_test_top.tb.sample_scrbd [SCRBD] write expected data
# ------------------------------------------------------------------
# UVM_INFO @ 302800: uvm_test_top.tb.sample_model.master.monitor [MON] read addr=e1h rdata=7dh
# UVM_INFO @ 302800: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 302800: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK. addr=e1h
# UVM_INFO @ 303400: uvm_test_top.tb.sample_model.master.monitor [MON] read addr=50h rdata=d4h
# UVM_INFO @ 303400: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 303400: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK. addr=50h
# UVM_INFO @ 304400: uvm_test_top.tb.sample_model.master.monitor [MON] read addr=e9h rdata=94h
# UVM_INFO @ 304400: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 304400: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK. addr=e9h
# UVM_INFO @ 304800: uvm_test_top.tb.sample_model.master.monitor [MON] read addr=90h rdata=6bh
# UVM_INFO @ 304800: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 304800: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK. addr=90h
# UVM_INFO @ 305600: uvm_test_top.tb.sample_model.master.monitor [MON] read addr=bah rdata=0bh
# UVM_INFO @ 305600: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 305600: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK. addr=bah
# UVM_INFO @ 306300: uvm_test_top.tb.sample_model.master.monitor [MON] read addr=03h rdata=49h
# UVM_INFO @ 306300: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 306300: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK. addr=03h
# UVM_INFO @ 307000: uvm_test_top.tb.sample_model.master.monitor [MON] read addr=74h rdata=07h
# UVM_INFO @ 307000: uvm_test_top.tb.sample_scrbd [SCRBD] write observed data
# UVM_INFO @ 307000: uvm_test_top.tb.sample_scrbd [SCRBD] data compare OK. addr=74h
記述量を減らしたuvm_testクラス記述
これまでの分と、今回追加した2つのuvm_testクラスの定義になります。
`define set_seq(INST,SEQ) \ uvm_config_db#(uvm_object_wrapper)::set(this,`"INST`","default_sequence",SEQ::type_id::get());`define uvm_test_head(NAME) \ class NAME extends sample_test_base; \ `uvm_component_utils(NAME) \ function new (string name=`"NAME`", uvm_component parent=null); \ super.new(name,parent); \ endfunction//-------------------------------------------------------------virtual class sample_test_base extends uvm_test; `uvm_component_utils(sample_test_base) tb_env tb; function new (string name="sample_test_base", uvm_component parent=null); super.new(name,parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); tb = tb_env::type_id::create("tb", this); endfunction task run_phase(uvm_phase phase); uvm_report_info("TEST", "Hello World"); uvm_top.print_topology(); endtaskendclass//-------------------------------------------------------------`uvm_test_head(sample_test) function void build_phase(uvm_phase phase); `set_seq(tb.sample_model.master.sequencer.run_phase, write_read_seq) `set_seq(tb.sample_model.slave.sequencer.run_phase, normal_response_seq) super.build_phase(phase); endfunctionendclass//-------------------------------------------------------------`uvm_test_head(sample_test2) function void build_phase(uvm_phase phase); `set_seq(tb.sample_model.master.sequencer.run_phase, write_read_seq) `set_seq(tb.sample_model.slave.sequencer.run_phase, random_response_seq) super.build_phase(phase); endfunctionendclass//-------------------------------------------------------------`uvm_test_head(sample_test3) function void build_phase(uvm_phase phase); `set_seq(tb.sample_model.master.sequencer.run_phase, write_read_all_seq) `set_seq(tb.sample_model.slave.sequencer.run_phase, random_response_seq) super.build_phase(phase); endfunctionendclass//-------------------------------------------------------------`uvm_test_head(sample_test4) function void build_phase(uvm_phase phase); `set_seq(tb.sample_model.master.sequencer.run_phase, write_read_rand_all_seq) `set_seq(tb.sample_model.slave.sequencer.run_phase, random_response_seq) super.build_phase(phase); endfunctionendclass