インターフェース
インターフェースはソースコードの最上位コンポーネントの1つです。インターフェースはオーバーライド可能なパラメータ、インターフェース定義を持ちます。
オーバーライド可能なパラメータについてはモジュールと同じです。
インターフェース定義では modport を宣言することができます。modport はモジュールのポート宣言で、ポートを束ねて接続するために使うことができます。
interface InterfaceA #(
param ParamA: u32 = 0,
param ParamB: u32 = 0,
) {
var a: logic;
var b: logic;
modport master {
a: output,
b: input ,
}
modport slave {
b: input ,
a: output,
}
}
さらに、import キーワードを付けて指定された関数は modport を通して呼び出すことができます。
interface InterfaceA {
var a: logic;
var b: logic;
function a_and_b -> logic<2> {
return {a, b};
}
modport mp {
a : input ,
b : input ,
a_and_b: import,
}
}
module ModuleA (
ab_if: modport InterfaceA::mp,
) {
let _ab: logic<2> = ab_if.a_and_b();
}
modportのデフォルトメンバー
modportの全てのメンバーを指定する代わりに、以下のようにデフォルトメンバーを指定することができます。
..input: インターフェース内の全ての変数をinputとして追加..output: インターフェース内の全ての変数をoutputとして追加..same(modport_name, ...): 指定されたmodportと同じメンバーを同じ方向で追加(インポートされた関数を含む)..converse(modport_name, ...): 指定されたmodportと同じメンバーを、方向を逆にして追加(インポートされた関数はそのまま維持)
デフォルトメンバーの指定は通常の明示的なメンバーと一緒に使うこともできます。
interface InterfaceA {
var a: logic;
var b: logic;
var c: logic;
modport mp_a {
a: output,
}
modport mp_b {
b: input,
c: input,
}
modport master {
..same(mp_a, mp_b)
}
modport slave {
..converse(mp_a, mp_b)
}
modport monitor {
..input
}
modport driver {
b: input,
..output
}
}
ミックスイン
インターフェースは mixin 宣言によって、他のインターフェースのメンバを取り込めます。ミックスインされたインターフェースの全メンバ(変数、関数、modport)は、そのインターフェースで直接宣言されたかのように展開されます。これは複数のインターフェースで共通の定義を共有するのに便利です。
例えばバスプロトコルは、コマンドチャネルとレスポンスチャネルのような独立したチャネルから構成されることがよくあります。各チャネルを別々のインターフェースとして定義してミックスインすることで、それらのチャネルを複数のバスインターフェースで再利用できます。ジェネリックなインターフェースは、mixin 宣言でジェネリック引数を与えることでミックスインできます。
// Command channel: a master issues read/write commands to a slave
interface Command::<ADDR_WIDTH: u32, DATA_WIDTH: u32> {
var cmd_ready: logic ;
var cmd_valid: logic ;
var cmd_write: logic ;
var cmd_addr : logic<ADDR_WIDTH>;
var cmd_data : logic<DATA_WIDTH>;
modport mp_cmd {
cmd_ready: input ,
cmd_valid: output,
cmd_write: output,
cmd_addr : output,
cmd_data : output,
}
}
// Response channel: a slave returns read data to a master
interface Response::<DATA_WIDTH: u32> {
var rsp_ready: logic ;
var rsp_valid: logic ;
var rsp_data : logic<DATA_WIDTH>;
modport mp_rsp {
rsp_ready: output,
rsp_valid: input ,
rsp_data : input ,
}
}
// A memory bus composed from the command and response channels
interface MemoryBus::<ADDR_WIDTH: u32, DATA_WIDTH: u32> {
mixin Command::<ADDR_WIDTH, DATA_WIDTH>;
mixin Response::<DATA_WIDTH>;
modport master {
..same(mp_cmd, mp_rsp)
}
modport slave {
..converse(mp_cmd, mp_rsp)
}
}
alias interface MemoryBus32 = MemoryBus::<32, 32>;
ミックスインされるインターフェースには以下の制限があります。
- ミックスインの対象はインターフェースでなければなりません。モジュールやプロトタイプはミックスインできません。
- インターフェースは自分自身をミックスインできません。
- 上書き可能なパラメータ(
param/const)を持つインターフェースはミックスインできません。(ジェネリックなインターフェースは、上記のようにジェネリック引数を与えることでミックスインできます。) - 自身が
mixin宣言を持つインターフェースはミックスインできません。(mixinはネストできません。) - メンバ名は、そのインターフェースとミックスインされる全インターフェースを通じて一意でなければなりません。名前の衝突はエラーになります。
インターフェースインスタンスとmodportポートの接続
インターフェースインスタンスとmodportポートは、SystemVerilogと同様に、互換性のあるモジュールポートあるいは generic インターフェースに接続することができます。
interface InterfaceA {
var a: logic;
modport mp {
a: output,
}
}
module ModuleA (
foo_if: modport InterfaceA::mp,
bar_if: modport InterfaceA::mp,
) {
always_comb {
foo_if.a = '0;
bar_if.a = '0;
}
}
module ModuleB (
foo_if: modport InterfaceA::mp,
) {
inst bar_if: InterfaceA;
inst u: ModuleA (
foo_if: foo_if,
bar_if: bar_if,
);
}