PLI なしで Backdoor アクセスを実現する
石谷 @ PEZY Computing です。 Hardware Description Language Advent Calendar 2023 の3日目と言うことで、今年は、RgGen が生成する UVM RAL モデルのとある機能の解説という誰得な投稿をしようと思います。
はじめに
UVM RAL は CSR への読み書きを抽象化するための仕組みで、アドレスやプロトコルを意識せずに、CSR への読み書きを実行できます。
uart_ral_model.dll.write(status, 'haa);
uart_ral_model.dlm.read(status, value);
UVM RAL には backdoor アクセス
と言って、バスアクセスを起こさずに、対象レジスタに値を設定したり、読んだりる機能があります。
uart_ral_model.dll.poke(status, 'haa); // write に相当
uart_ral_model.dlm.peek(status, value); // read に相当
対象レジスタを直接操作するので、バスアクセスを起こすより、シミュレーション時間を短縮することができます。ただ、既存の実装では、PLI を使った C のプログラムを介して実装されており、PLI の使用は実行時間の増加につながります。
拙作の RgGen が出力する RAL モデルでは、PLI を使わない backdoor アクセスを実現させています。本稿では、どのように実現したかを解説したいと思います。
なぜ PLI を使っているか?
なぜ、既存の実装では、実行時間の悪化を許容してまで、PLI を使って backdoor アクセスを実現させているのでしょうか?
それは、package
内で定義された class
からは、以下のように、
tb.dut.u_csr_block.u_dll.value_reg = 'haa;
階層アクセスを用いる事が出来ないためです。そのため、対象レジスタへの階層パスを文字列で指定し、PLI を使って実装された C の関数を SystemVerilog のコー上から呼び出すことで、backdoor アクセスを実現させています。
RgGen での実現方法
方針
module
(回路) の世界と class
(検証環境) の世界を繋ぐ方法は、
-
module
中にinterface
をインスタンスする -
interface
のインスタンスをvirtual interface
として、class に渡す -
vittual interface
を介して、interface
中の信号を操作する
が王道パターンです。RgGen でもこの方法を採用します。RgGen では、RTL も自動生成なので、生成した RTL に、内部信号を制御するための interface
を事前にインスタンスしておきます。
RgGen が生成する RTL の構造は、
のようになっています。
u_register
は register 階層の機能を実装していて、
- CSR バスから来たリクエストをデコードし、配下の bit field に分配する
- 書き込みマスク、読み出しマスクを生成
- 配下の bit field が保持する値を纏めて、読み出しデータを作る
を行っています。
u_bit_field
は bit field 階層の機能を実現していて、CSR の記憶素子の役割を担っています。
つまり、u_register
を乗っ取って、
- 書き込みデータ、書き込みマスク、読み出しマスクを上書き
- 纏めた読み出しデータを参照
するための interface
をインスタンスし、それの virtual interface
を検証環境の世界に渡すことができれば、PLI なしに backdoor アクセスを実現できます。
RTL 側の準備
対象となる CSR ブロックの RTL も自動生成されるので、上記の仕組みを予め仕込んでおくことができます。
register 階層には、rggen_register_common がインスタンスされています。このモジュール中に、rggen_backdoor および rggen_backdoor_if がインスタンスされています。
rggen_backdoor_if には、レジスタアクセスを乗っ取る用の
- 書き込みデータ
- 書き込みマスク
- 読み出しマスク
が宣言されています。これらが有効の際は、バスからのアクセスに代わり、配下の bit_field を更新します。
また、rggen_backdoor_if に宣言されている読み出しデータには、各 bit field からの読み出しデータが接続されています。
rggen_backdoor_if には、以下のメソッドが定義されていて、
これらのメソッドを用いて、レジスタアクセスを起こしたり、リードデータを取得したりします。
rggen_backdoor_if は、RTL 中で BFM (Bus Function Model) として機能するようになっています。
検証環境との接続
class
からのmodule
への階層アクセスは使えないので、rggen_backdoor_if の virtual interface
を直接取得することは不可能です。package
中に virtual interface
を保持するための変数を宣言し、この変数を介して、module
の世界から virtual interface
を取り出します。
また、
-
$sformatf("%m")
で、これが呼び出されたmodule
の階層パスを取得できる - 生成された RAL モデルには、すでに階層パスが設定されている
ことから、階層パスの文字列をキーとすることで、virtual interface
の受け渡しを簡単に行うことができます。
rggen_backdoor_pkg がそのための package
です。この package
には、rggen_backdoor_if の virtual interface
の連想配列が宣言されています。
RTL 側で $sformatf("%m")
をキーにして、この連想配列に rggen_backdoor_if の virtual interface
を設定します。
検証環境側の対応
uvm_reg は register 階層のモデリングを行う class です。この class
に、write
/read
などのレジスタアクセスを行うメソッドが定義されています。また、この class
には、以下の backdoor アクセスを行うメソッドが定義されており、
これらのメソッドをオーバーラードして、対応します。
これらのメソッドが呼ばれたら、
- 設定されている階層パスをキーにして、rggen_backdoor_if の
virtual interface
を取り出す - rggen_backdoor_if 内に定義されているメソッドを呼び出し、レジスタアクセスを起こす
ようになっています。
アクセスにもう一階層経由していたり、各シミュレータや言語の差分を吸収するための記述があったりしますが、今回は割愛します。
詳細は、rggen_backdoor_pkg や rggen_ral_backdoor_pkg の実装を参照ください。
まとめ
このように、RgGen では、BFM として機能する interface
を RTL 中にインスタンスすることで、PLI なしの backdoor アクセスを実現してします。PLI 未使用なので、シミュレーション実行時間への影響が少ない事が利点の1つですが、他ににも利点があります。
まずは、副作用 (write 1 clear/read clear など) も再現できる、と言う点です。bit field から見ると、backdoor アクセスも通常のアクセスも何ら変わりがないからです。 PLI を使った backdoor アクセスでは、値を強制的に上書きしているだけなので、副作用の再現まではできません。
次に、UVM 未使用でも backdoor アクセスを使える、と言う点です。backdoor アクセスを実現している rggen_backdoor_if や rggen_backdoor_pkg は、UVM を使っていません。従来の module ベースの検証環境などからも、backdoor アクセスを行うことができます。
ただ、PLI を使った実装に比べて、以下の欠点があります。
まずは、RTL を検証用に変更している、と言う点です。合成の際は rggen_backdoor_if のインスタンスを作らないようにしているので、検証時と合成時とでは、記述に差分があることになります。なので、これを良しとするかは、使用時に検討が必要になるでしょう。
次に、書き込みに 1 サイクル分の時間がかかる、と言う点です。副作用の再現を重視して、通常のアクセスと同じようにレジスタアクセスを起こすようになっています。そのため、PLI を使った backdoor アクセスでは 0 時間でアクセスを実行できますが、本実装では 1 サイクル分の時間を要してしまいます (バスモデルからバスアクセスを起こすことに比べれば、短時間ですが。)。アクセスするレジスタの数が多く、シーケンシャルにアクセスを実行する場合に、問題になるかもしれません。
そして、合成後のネットリストに対しては適用できない、と言う点です。PLI を使った場合では、階層パスを適切に設定すれば、合成後のネットリストに対しても backdoor アクセスを実行できます。本実装の場合は、ネットリストは合成ツールが生成する Verilog なので、rggen_backdoor_if をインスタンスしようがありません。
最後に
宣伝です。社内で使っているツールや、共通モジュールなどを公開しています。
-
pzbcm
- FIFO や調停回路などの共通モジュール詰め合わせ
- 社内で使っているバスプロトコル (pzcorebus) 用の interface 定義や各種モジュール
-
pztb-core
- 検証用コンポーネント詰め合わせ
- pzcorebus や tilelink、UART などの VIP (UVM ベース)
- UVM ベースの検証環境を作るための共通クラス群
-
flgen
- ファイルリストを記述するための DSL と生成ツールを提供
- Ruby の構文が使えるので、かなり力業なこともできる
-
https://github.com/taichi-ishitani/rice/blob/master/tb/cosim/cosim.rb
- 実行ディレクトリにある *.h を読んで、Verilog のマクロを定義
-
https://github.com/taichi-ishitani/rice/blob/master/tb/cosim/cosim.rb