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

More than 1 year has passed since last update.

SystemVerilog Advent Calendar 2020 Day 9

posted at

updated at

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

型のパラメータ化と 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 は、ツールの対応がこなれていない為か、バグを踏むことがあるので、要注意です。
例えば、これ

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
2
Help us understand the problem. What are the problem?