FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Chiselを使ったハードウェアデザインでトレイトを活用するためには(2. MultiIOModuleを使ったI/Oの追加)

f:id:msyksphinz:20190113171015p:plain
Chiselでのトレイトの活用

Chiselでトレイトを使って機能を拡張する話、関数の追加やポート幅の拡張などの方法は分かったが、ポートを増やすためにはどのようにすれば良いのだろうか。

いろいろ調べていると、MultiIOModuleという機能が使えそうであった。

github.com

MultiIOModuleのサンプルを見ると、モジュールに対してトレイトでI/Oを追加したり、機能を追加することができるように見える。 この手法について調査した。

ベースとなるモジュール

ベースとなるモジュールは以下だ。派生元がModuleクラスではなく、MultiIOModuleであることに注意。 in_Ain_Bから受け取った値を、ハッシュ値を計算してhashに出力する単純な機能だ。

abstract class MultiIOTest extends MultiIOModule {
  def Width: Int = 32
  def HashWidth: Int = 32
  val in_A = IO(Input(SInt(Width.W)))
  val in_B = IO(Input(SInt(Width.W)))
  val hash = IO(Output(UInt(HashWidth.W)))

  hash := (in_A.asUInt ^ in_B.asUInt)(HashWidth-1, 0)
}

ここで、幾つかのトレイトを定義する。ここでは、64bitのポートと16bitのポートを追加するトレイトだ。ポート名はval_outと言う。

trait AddMultiIOTrait extends MultiIOModule {
  def OutWidth: Int = 64
  val val_out = IO(Output(UInt(OutWidth.W)))

  val_out := 0.U
}


trait SubMultiIOTrait extends MultiIOModule {
  def OutWidth: Int = 16
  val val_out = IO(Output(UInt(OutWidth.W)))

  val_out := 1.U
}

では、このトレイトを接続する鵜。ベースとなるMultiIOTestモジュールに対して、トレイトとしてAddMultiIOTraitSubMultiIOTraitを追加するわけだ。

class AddMultiIOTest extends MultiIOTest with AddMultiIOTrait
class SubMultiIOTest extends MultiIOTest with SubMultiIOTrait

一方で、このトレイトで追加したI/Oに、ベースとなるモジュールからI/Oを接続して拡張する場合にはどうするか。 このためにはトレイトに機能を追加する方法と、モジュールに機能を追加する方法がある。

FuncMultiIOTraitは、ポートの宣言だけを行っている。一方でFunc2MultiIOTraitはポートの宣言と、機能の追加(in_A - in_B)の実装を行っている。

trait FuncMultiIOTrait extends MultiIOModule {
  def OutWidth: Int = 32
  val sub_out = IO(Output(SInt(OutWidth.W)))
}


trait Func2MultiIOTrait extends MultiIOTest {
  def OutWidth: Int = 32
  val sub_out = IO(Output(SInt(OutWidth.W)))

  sub_out := in_A - in_B
}

これらは、どちらもインスタンス化できる。

class FuncMultiIOTest extends MultiIOTest with FuncMultiIOTrait {
  sub_out := in_A - in_B
}
class Func2MultiIOTest extends MultiIOTest with Func2MultiIOTrait

結果として、Func2MultiIOTest.vを見てみよう。

object MultiIOTest extends App {
  chisel3.Driver.execute(args, () => new AddMultiIOTest())
  chisel3.Driver.execute(args, () => new SubMultiIOTest())
  chisel3.Driver.execute(args, () => new FuncMultiIOTest())
  chisel3.Driver.execute(args, () => new Func2MultiIOTest())
}
module Func2MultiIOTest( // @[:@3.2]
  input         clock, // @[:@4.4]
  input         reset, // @[:@5.4]
  input  [31:0] in_A, // @[:@6.4]
  input  [31:0] in_B, // @[:@7.4]
  output [31:0] hash, // @[:@8.4]
  output [31:0] sub_out // @[:@9.4]
);
  wire [31:0] _T_10; // @[multiio_test.scala 15:17:@11.4]
  wire [31:0] _T_11; // @[multiio_test.scala 15:31:@12.4]
  wire [32:0] _T_16; // @[multiio_test.scala 43:19:@16.4]
  wire [31:0] _T_17; // @[multiio_test.scala 43:19:@17.4]
  assign _T_10 = $unsigned(in_A); // @[multiio_test.scala 15:17:@11.4]
  assign _T_11 = $unsigned(in_B); // @[multiio_test.scala 15:31:@12.4]
  assign _T_16 = $signed(in_A) - $signed(in_B); // @[multiio_test.scala 43:19:@16.4]
  assign _T_17 = $signed(in_A) - $signed(in_B); // @[multiio_test.scala 43:19:@17.4]
  assign hash = _T_10 ^ _T_11; // @[multiio_test.scala 15:8:@15.4]
  assign sub_out = $signed(_T_17); // @[multiio_test.scala 43:11:@19.4]
endmodule

Verilogが正しく生成できている!