無償ツールで実践する「ハード・ソフト協調検証」(4) ―― SystemVerilog側のDPI-Cの記述を作成する
tb_prog.svのtb_progモジュールでは,AvalonのMaster BFMのパッケージ(avalon_mm_pkg)をインポートしています(tb_prog.svの6行目).そのあと,一つのimport(15行目)と七つのexport(18~24行目)を宣言しています.
importのdpi_mainタスクは,以下のように何の引き数も持ちません.
import "DPI-C" context task dpi_main(); |
taskの前にcontextがあるのは,C言語側の関数(dpi_main)内でSystemVerilog側のタスク(exportしているタスク)を呼び出すためです.
一方,exportの七つのタスクは,C言語側から呼び出されるものです.
export "DPI-C" task bfm_write32; |
bfm_write32,bfm_write16,bfm_write8の三つのタスクはライト・サイクルを実行します.32,16,8はアクセスするデータのサイズ(32ビット,16ビット,8ビット)です.bfm_read32,bfm_read16,bfm_read8の三つのタスクはリード・サイクルを実行します.ライト・サイクルと同じように,32,16,8はアクセスするデータのサイズです.bfm_nopタスクは,指定したサイクル(クロック)の間,ウェイトします.
C言語側のdpi_main関数の中では,先に説明した七つのexportタスクを使ってSystemVerilog側にアクセスします.
それでは各タスクについて説明します.
●dpi_main
dpi_mainタスクは,以下のようにinitial文内(tb_prog.svの30~42行目)で実行します.
initial begin `BFM.init(); ・・・ #100; |
dpi_mainタスクを実行する前に,AvalonのMaster BFMの初期化関数(init)を実行します.AvalonのMaster BFMのインスタンス名は「`BFMマクロ」を使っています.`BFMマクロは,
`define BFM test_bench.DUT.the_mm_master_bfm_0.mm_master_bfm_0 |
のように定義します.なお,インスタンス名のパスはSOPC Builderが自動生成したものです.
dpi_mainタスクを実行したあと,#100(100ns)だけウェイトし,シミュレーションを終了($finish)します.dpi_mainタスクが実行されると,C言語側のdpi_main関数が呼ばれます.
●bfm_write32,bfm_write16,bfm_write8
bfm_write32,bfm_write16,bfm_write8の三つのタスクはアクセスするデータのサイズが違うだけなので,ここではbfm_write32タスク(tb_prog.svの93~105行目)についてのみ説明します.
task bfm_write32( int unsigned addr, input int unsigned data ); byte_enable = 'hf; bfm_run( REQ_WRITE, addr, byte_enable, data ); endtask : bfm_write32 |
bfm_write32タスクでは,最初のアドレスのチェックを行います.32ビット(4バイト)アクセスなので,アドレスの下位2ビットは0でなければなりません.次にバイト・イネーブル(byte_enable)をすべて1('hf)にして,bfm_runタスクを呼び出しています.
bfm_runタスク(tb_prog.svの50~76行目)は,以下のようにAvalonのMaster BFMに対してサイクルを実行します.
task bfm_run( Request_t request, int addr, int byte_enable, inout int data ); if( request == REQ_WRITE ) `BFM.push_command(); while(1) begin `BFM.pop_response(); if( request == REQ_READ ) endtask : bfm_run |
bfm_runタスク内では,AvalonのMaster BFMが提供しているAPIを使ってバス・サイクルを発生させます.`BFM.set_command_xxxで始まる行でコマンドを設定します.
ライト・サイクルのときは,データを設定します(`BFM.set_command_data).`BFM.push_command()でコマンドを登録します.この時点で,AvalonのMaster BFMは登録されたコマンドを取り出し,バスにサイクルを発生します.bfm_runタスクでは,AvalonのMaster BFMからのレスポンスが戻ってくるまで待ちます.
( `BFM.get_response_queu_size() > 0 ) |
レスポンスが戻ってきたら,`BFM.pop_response()でレスポンス情報を取り出します.リード・サイクルのときは,レスポンスからデータ(data)を取り出します.
●bfm_read32,bfm_read16,bfm_read8
fm_read32,bfm_read16,bfm_read8の三つのタスクはアクセスするデータのサイズが違うだけなので, ライト・サイクルのところと同じようにbfm_read32タスク(tb_prog.svの78~91行目)のみ説明します.
bfm_read32タスクでは,最初のアドレスのチェックを行います.
task bfm_read32( int unsigned addr, output int unsigned data ); if( addr & 'h3 ) begin // address check byte_enable = 'hf; bfm_run( REQ_READ, addr, byte_enable, data ); endtask : bfm_read32 |
32ビット(4バイト)アクセスなので,アドレスの下位2ビットは0でなければなりません.次にバイト・イネーブル(byte_enable)をすべて1('hf)にして,bfm_runタスクを呼び出しています.リードしたデータがdataに書き込まれます.
●bfm_nop
bfm_nopタスク(tb_prog.svの190~195行目)では,引き数で指定されたクロック数だけウェイトします.
task bfm_nop( int no ); for( int n=0 ; n<no ; n++ ) endtask : bfm_nop |
(第5回へ続く)
Verification Enginnerの戯言
http://blogs.yahoo.co.jp/verification_engineer