search
LoginSignup
3
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

SystemVerilog Advent Calendar 2020 Day 8

posted at

updated at

RTL で依存性の注入(っぽいこと)をする

RTL で依存性の注入(っぽいこと)をする

石谷 @PEZY Computing です。SystemVerilog Advent Calendar 2020 の2回目です。1回目inside 演算子の解説を行いました。

今回は RTL 中で依存性の注入(っぽいこと)をやってみようと思います。依存性の注入 (Dependency Injection: DI) については、

当たりを参考にしてください。

はじめに

RTL を書いていると、「ほとんどの機能は同じだが、微妙に違う」module が多く出てくると思います。
バスの MUX を例にとると、

  • ラウンドロビンな MUX
  • 固定優先順位な MUX
  • ランダムな調停を行う MUX

のように、調停方法によって、微妙に違う MUX を実装する必要があります。

以下のように、parameter でどの調停方法を使うかを指定できるようにし、generate 文でその実装を切り替えれるようにすれば、一応の解決を見ます。

module bus_mux #(
  parameter ARBITRATION = 0
);
  if (ARBITRATION == 0) begin : g_roundrobin
    //  ラウンドロビンを実装
  end
  else if (ARBITRATION == 1) begin : g_fixed_priority
    //  固定優先順位を実装
  end
  else begin : g_invalid
    //  該当なし
    //  エラー
  end
endmodule

ただし、この方法には、

  • 調停方法が増えるたびに bus_mux を変更する必要がある
  • 一品ものの特殊な調停方法に対応しにくい

などの欠点があります。解決するには、調停方法の実装を bus_mux 外部に追い出す必要があります。

依存性の注入(っぽいこと)を適用してみる

「依存性の注入」とは?

使われ方によって変化する機能を、どの実装を使うかの選択も含め、外部に追い出して、機能間を疎結合にし、拡張性やメンテナンス性、テスト容易性を向上させる設計パターンの1つです。

例えば、ある機能 A がログ出力する場合を考えます。ログの出力先として、

  • ファイル
  • 端末
  • テストの用のモック

等々が考えられます。パラメータによって出力先を切り替える場合、先ほどの bus_mux の場合と同じ問題にあたってしまいます。
そこで DI の登場です。DI では、

  1. 出力先ごとのログ出力を実装したログ出力オブジェクトを定義する
    • 各ログ出力オブジェクトは共通のインターフェースを持つ
  2. 機能 A インスタンス時に、所望のログ出力オブジェクトを与える

ようにすることで、機能 A の実装とログ出力の実装を切り離すことができます。
ログの出力先が増えた場合、所望のログ出力オブジェクトを実装し、機能 A に与えることで実現できます。
これは、機能 A の実装を変えることなく実現できます。

RTL 上でやってみる

SystemVerilog の RTL 上では、

  • 機能オブジェクトは interface を使って実装
  • 接続には generic interface を使用

することで、依存性の注入(っぽこと)を実現します。

先ほどの bus_mux の例を考えます。
調停方法が bus_mux から分離したい機能です。なので、interface を使って各調停方法(調停オブジェクトと呼ぶことにします)を実装します。

interface roundrobin_arbiter #(
  parameter int ENTRIES = 2
)(
  input var i_clk, input var i_rst_n
);
  logic [ENTRIES-1:0] request;
  logic [ENTRIES-1:0] grant;

  modport arbiter (
    output  request,
    input   grant
  );

  //  ラウンドロビンを実装
endinterface

interface fixed_priority_arbiter #(
  parameter int ENTRIES = 2
);
  logic [ENTRIES-1:0] request;
  logic [ENTRIES-1:0] grant;

  modport arbiter (
    output  request,
    input   grant
  );

  //  固定優先度での調停を実装
endinterface

各調停オブジェクトは、共通のインターフェースとして、modport arbiter を持ちます。

次に、調停オブジェクトの接続についてです。これは generic interface を使って実現します。
generic interface を使えば、interface を介して参照する信号や function が存在しさえすれば、任意の interface のインスタンスを接続することができます。
なので、bus_mux の調停オブジェクト使用周辺の実装は、以下のようになります。

module bus_mux (
  interface.arbiter arbiter
  foo_if.slave      slave_if[2],
  foo_if.master     master_if
);
  logic [1:0] grant;

  always_comb begin
    arbiter.request[0]  = slave_if[0].request;
    arbiter.request[1]  = slave_if[1].request;
    grant               = arbiter.grant;
  end
endmodule

最後に bus_mux と調停オブジェクトの接続についてです。それぞれをインスタンスして、普通に繋ぐだけです。

fixed_priority_arbiter u_arbiter();
bus_mux u_mux (
  .arbiter  (u_arbiter  ),
  // 省略
);

調停方法をラウンドロビンにする場合は、fixed_priority_arbiter の代わりに、roundrobin_arbiter をインスタンスするだけです。

実装例

依存性の注入(っぽい)を適用した例を GitHub のリポジトリに置いてあります。
https://github.com/taichi-ishitani/rtl_with_di

bus_mux.sv が MUX の実装です。
調停オブジェクトへの参照を、interface.arbiter arbiter として、generic interface を使って宣言しています。
調停オブジェクトの要件は、

  • 名前が arbitermodport を持つこと
  • 上記 modport は、出力 request と、入力 grant を持つこと

があげられます。
調停オブジェクトとの接続があるだけで、それ以外の調停に関する記述は MUX の記述中には一切ありません。

fixed_prioriy_arbiter.svroundrobin_arbiter.sv が調停オブジェクトの実装になります。
別々の interface として定義されていますが、上記の要件に合致する modport を持っているので、bus_mux の調停オブジェクトとして使うことができます。

bus_mux_top.sv が使用例を示すトップモジュールになります。
2つの bux_mux のインスタンスがありますが、どの調停オブジェクトを使うかを切り替えることで、調停の種類の切り替えを行っています。

まとめ

上記の例では、MUX の実装と、調停の実装とを分離できました。
他の調停方法を使いたい場合は、所望の調停方法を実装した、調停オブジェクトを実装するだけでよく、MUX の実装には影響はありません。MUX の実装が変化に対して強くなった、と言えます。
また、調停オブジェクトは MUX だけではなく、他のモジュールにも使えるので、より再利用性が高まったともいえます。

再度の宣伝

YAML とか Excel で記述したレジスタマップから、RTL 等を自動生成するツール (RgGen) を作っています。
面倒な CSR のコーディングをせずに済むので、手前味噌ですが、便利なツールです。
よかったら、使ってみてください。

Qiita にも紹介記事を載せてあります。

UVM で記述した AMBA AXI/APB の VIP (モドキ) も公開しています。
こちらも、よければ、見てみてください。

SystenVerilog で記述した Network On Chip (NoC) も公開しています。
Design Compiler および Vivado でエラボレーションできるところまでは確認しています。
RTL 中で SystemVerilog をどこまで使えるのかの参考になると思います。
(ただし Quartus は知らない。)
UVM を使った検証環境も同梱しているので、UVM を使った検証環境の参考にもなると思います。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
3
Help us understand the problem. What are the problem?