4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SystemVerilogAdvent Calendar 2020

Day 9

型のパラメータ化と interface based typedef

Last updated at Posted at 2020-12-08

型のパラメータ化と interface based typedef

石谷 @PEZY Computing です。SystemVerilog Advent Calendar 2020 の第3篇目です。
第1篇目、第2篇目はこちら。

SystemVerilog では、

  • 型のパラメータ化
  • interface 内で定義された型の参照

をすることができます。
今回は、これらを紹介し、応用例として、ハンドシェイクプロトコルを実装する、より一般化した interface および機能モジュールを例示していきます。

型のパラメータ化

SystemVerilog では、数値だけではなく、型をパラメータとして宣言することができます。宣言の際は、type キーワードを使います。

module foo_bar #(
  parameter type  DATA_TYPE = logic
)(
  input   DATA_TYPE i_d,
  output  DATA_TYPE o_d
);
endmodule

インスタンスする際に、DATA_TYPE に所望の型を与えます。

typedef enum logic [1:0] {
  FOO_0,
  FOO_1
} foo_enum;

foo_enum  foo_0;
foo_enum  foo_1;

foo_bar #(
  .DATA_TYPE  (foo_enum )
) u_foo (
  .i_d  (foo_0  ),
  .o_d  (foo_1  )
);

logic [1:0] bar_0;
logic [1:0] bar_1;

foo_bar #(
  .DATA_TYPE  (logic[1:0] )
) u_bar (
 .i_d (bar_0  ),
 .o_d (bar_1  )
);

enum では、厳密な型チェックがされます。なので、enum を使う場合は、

  • 出力ポートの型と、接続先の型
  • 代入の両辺の型

を合わせる必要があります。上記の例の場合では、以下のコードは文法エラーになります。

foo_enum    foo_0;
foo_enum    foo_1;
logic [1:0] bar_0;
logic [1:0] bar_1;

assign  foo_0 = bar_0;  //  Syntax Error

foo_bar #(
  .DATA_TYPE  (logic[1:0] )
) u_bar (
  .i_d  (bar_1  ),
  .o_d  (foo_1  ) //  Syntax Error
);

なので、FIFO 等の共通モジュールは、enum の使用を考慮して、型のパラメータ化は必須と言えます。

interface based typedef

interface 内で定義された (typedef された) 型は、そのインスタンスの接続先のモジュール内で使うことができます。これは interface based typedef と呼ばれ、LRM (IEEE 1800-2017) では 6.18 User-defined types にその説明があります。

interface foo_if #(int W = 1);
  typedef logic [W-1:0] foo_type;
  foo_type  foo_data;
  modport mp (input foo);
endinterface

module bar (
  foo_if.mp foo_port
);
  typedef foo_port.foo_type foo_type; //  これ
  foo_type  foo_data;
  assign  foo_data  = foo.foo_data;
endmodule

module baz;
  foo_if #(1) foo_if_1();
  bar u_bar_1 (foo_if_1);

  foo_if #(2) foo_if_2();
  bar u_bar_2 (foo_if_2);
endmodule

上記の例では、foo_if 内で定義された foo_typefoo_port を介して、module bar 内に取り込んでいます。
module bar 自体はパラメータ化されていませんが、

  • u_bar_1 の foo_type は logic [0:0]
  • u_bar_2 の foo_type は logic [1:0]

となります。

応用例として、型定義を一か所にまとめるために使うことができます。
コンフィグレーションパラメータから導かれる型を、それぞれのモジュールで定義するのは、非常に面倒です。
そこで、interface 内に型を定義し、その interface を介して型を取り込むことで、型の定義を一か所に纏められるようになります。以下が使用例になります。

応用例

応用例として、valid/ready 方式のハンドシェイクを実装する interface のより一層の一般化を行います。

まずは interface の定義です。当該 interface は valid/ready/data を持ち、data の型が DATA_TYPE としてパラメータ化されています。

interface handshake_if #(
  type  DATA_TYPE = logic
);
  typedef DATA_TYPE __data_type;

  logic     valid;
  logic     ready;
  DATA_TYPE data;

  function automatic logic ack();
    return valid && ready;
  endfunction

  modport master (
    output  valid,
    input   ready,
    output  data,
    import  ack
  );

  modport slave (
    input   valid,
    output  ready,
    input   data,
    import  ack
  );

型をパラメータ化しているので、enum を直接使えますし、data が構造体の場合、そのメンバーフィールドを直接参照することも可能です。

typedef enum logic {
  FOO_0,
  FOO_1
} foo_enum;

handshake_if #(foo_enum)  foo_if();
assign  foo_if.data = FOO_0;

typedef struct packed {
  logic bar_0;
  logic bar_1;
} bar_struct;

handshake_if #(bar_struct)  bar_if();
assign  bar_if.data.bar_0 = '0;
assign  bar_if.data.bar_1 = '1;

型パラメータ DATA_TYPE__data_type として typedef しているところも、注目点です。
こうすることで、interface based typedef を用いて、接続先に DATA_TYPE を取り込むことができるようになります。

次の機能モジュールです。ここでは FIFO を実装し、以下のようになります。

module handshake_fifo #(
  parameter int DEPTH = 2
)(
  input var           i_clk,
  input var           i_rst_n,
  handshake_if.slave  slave_if,
  handshake_if.master master_if
);
  typedef slave_if.__data_type  __data_type;

  logic push;
  logic pop;
  logic empty;
  logic full;

  always_comb begin
    slave_if.ready  = !full;
    master_if.valid = !empty;
  end

  always_comb begin
    push  = slave_if.ack();
    pop   = master_if.ack();
  end

  common_fifo #(
    .DEPTH  (DEPTH        ),
    .TYPE   (__data_type  )
  ) u_fifo (
    .i_clk    (i_clk          ),
    .i_rst_n  (i_rst_n        ),
    .o_empty  (empty          ),
    .o_full   (full           ),
    .i_push   (push           ),
    .i_data   (slave_if.data  ),
    .i_pop    (pop            ),
    .o_data   (master_if.data )
  );
endmodule

interface based typedef を用いて、DATA_TYPE__data_type として取り込んでいます。なので、handshake_fifo 自体には型に関するパラメータを持たせる必要はありません。

そして、以下が使用例になります。

typedef enum logic {
  FOO_0,
  FOO_1
} foo_enum;

handshake_if #(foo_enum)  foo_if[2]();
handshake_fifo u_foo_fifo (
  .i_clk      (i_clk      ),
  .i_rst_n    (i_rst_n    ),
  .slave_if   (foo_if[0]  ),
  .master_if  (foo_if[1]  )
);

typedef struct packed {
  logic bar_0;
  logic bar_1;
} bar_struct;

handshake_if #(bar_struct)  bar_if[2]();
handshake_fifo u_bar_fifo (
  .i_clk      (i_clk      ),
  .i_rst_n    (i_rst_n    ),
  .slave_if   (bar_if[0]  ),
  .master_if  (bar_if[1]  )
);

まとめ

型のパラメータ化interface based typedef を用いれば、汎用性と利便性が高いモジュール定義を行うことが可能となります。
ただ、interface based typedef は、ツールの対応がこなれていない為か、バグを踏むことがあるので、要注意です。
例えば、これ

4
2
0

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
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?