ハードウェアの気になるあれこれ

技術的に興味のあることを調べて書いてくブログ。主にハードウェアがネタ。

Chisel Bootcamp - Module3.6(5) - 型クラスと使うジェネリクス型

スポンサーリンク

久しぶりにChisel-Bootcampネタ。 前回のChisel-Bootcampの学習ではジェネリクス型について学んだ。

www.tech-diningyo.info

今回はModule3.6の残りの部分を見ていく。ただエラーが出たりするので、そのへんは追って調査する事にして飛ばしていく。

型クラスと使うジェネリクス

ここではMAC演算を行うモジュールを例に進めていくようだ。
そのまま引用したほうが良さそうなのでBootcampの文をペタリ。

以下にMACの例題を示す。これはFixedPointSIntdsptoolsが提供するDspComplex[T]向けにさえも生成可能なMACとなっている。dsptoolsが型クラスを用いているため、型境界の文法が異なっている。これについてはこのBootcampの範囲を超えるので、型クラスの使用に関して情報が必要ならdsptoolsのREADMEを参照してほしい。

以下のMacの型パラメータ部分の表記[T <: Data : Ring]TDataRingのサブタイプであることを意味する。要はDataRingを継承して作られた型であればOKということだ。
Ringdsptoolsで提供される型の一種となるとのこと。

import chisel3.experimental._
import dsptools.numbers._

class Mac[T <: Data : Ring](genIn : T, genOut: T) extends Module {
    val io = IO(new Bundle {
        val a = Input(genIn.cloneType)
        val b = Input(genIn.cloneType)
        val c = Input(genIn.cloneType)
        val out = Output(genOut.cloneType)
    })
    io.out := io.a * io.b + io.c
}

println(getVerilog(new Mac(UInt(4.W), UInt(6.W)) ))
println(getVerilog(new Mac(SInt(4.W), SInt(6.W)) ))
println(getVerilog(new Mac(FixedPoint(4.W, 3.BP), FixedPoint(6.W, 4.BP))))

ここで大事なのは”型クラス”を使っている、ということ。これを使ってMacを構成しておくことでインスタンスの生成時にMacの入出力のデータ型を変更できるようにしている。

  • 実行結果

上記のソースコードnew Macで生成された3つともがエラボレートに成功し、RTLが出力されている。
またそれぞれのケースにおいて、内部の計算がUInt/SInt/FixedPointに対応したものになっていることもわかるかと思う。

[info] [0.001] Elaborating design...
[info] [0.639] Done elaborating.
Total FIRRTL Compile Time: 229.9 ms
module cmd2HelperMac( // @[:@3.2]
  input        clock, // @[:@4.4]
  input        reset, // @[:@5.4]
  input  [3:0] io_a, // @[:@6.4]
  input  [3:0] io_b, // @[:@6.4]
  input  [3:0] io_c, // @[:@6.4]
  output [5:0] io_out // @[:@6.4]
);
  wire [7:0] _T_13; // @[UIntTypeClass.scala 39:41:@8.4]
  wire [7:0] _GEN_0; // @[UIntTypeClass.scala 18:40:@9.4]
  wire [8:0] _T_14; // @[UIntTypeClass.scala 18:40:@9.4]
  wire [7:0] _T_15; // @[UIntTypeClass.scala 18:40:@10.4]
  assign _T_13 = io_a * io_b; // @[UIntTypeClass.scala 39:41:@8.4]
  assign _GEN_0 = {{4'd0}, io_c}; // @[UIntTypeClass.scala 18:40:@9.4]
  assign _T_14 = _T_13 + _GEN_0; // @[UIntTypeClass.scala 18:40:@9.4]
  assign _T_15 = _T_14[7:0]; // @[UIntTypeClass.scala 18:40:@10.4]
  assign io_out = _T_15[5:0];
endmodule

[info] [0.000] Elaborating design...
[info] [0.016] Done elaborating.
Total FIRRTL Compile Time: 21.4 ms
module cmd2HelperMac( // @[:@3.2]
  input        clock, // @[:@4.4]
  input        reset, // @[:@5.4]
  input  [3:0] io_a, // @[:@6.4]
  input  [3:0] io_b, // @[:@6.4]
  input  [3:0] io_c, // @[:@6.4]
  output [5:0] io_out // @[:@6.4]
);
  wire [7:0] _T_13; // @[SIntTypeClass.scala 44:41:@8.4]
  wire [7:0] _GEN_0; // @[SIntTypeClass.scala 18:40:@9.4]
  wire [8:0] _T_14; // @[SIntTypeClass.scala 18:40:@9.4]
  wire [7:0] _T_15; // @[SIntTypeClass.scala 18:40:@10.4]
  wire [7:0] _T_16; // @[SIntTypeClass.scala 18:40:@11.4]
  wire [5:0] _GEN_1;
  assign _T_13 = $signed(io_a) * $signed(io_b); // @[SIntTypeClass.scala 44:41:@8.4]
  assign _GEN_0 = {{4{io_c[3]}},io_c}; // @[SIntTypeClass.scala 18:40:@9.4]
  assign _T_14 = $signed(_T_13) + $signed(_GEN_0); // @[SIntTypeClass.scala 18:40:@9.4]
  assign _T_15 = _T_14[7:0]; // @[SIntTypeClass.scala 18:40:@10.4]
  assign _T_16 = $signed(_T_15); // @[SIntTypeClass.scala 18:40:@11.4]
  assign _GEN_1 = _T_16[5:0];
  assign io_out = $signed(_GEN_1);
endmodule

[info] [0.000] Elaborating design...
[info] [0.023] Done elaborating.
Total FIRRTL Compile Time: 19.4 ms
module cmd2HelperMac( // @[:@3.2]
  input        clock, // @[:@4.4]
  input        reset, // @[:@5.4]
  input  [3:0] io_a, // @[:@6.4]
  input  [3:0] io_b, // @[:@6.4]
  input  [3:0] io_c, // @[:@6.4]
  output [5:0] io_out // @[:@6.4]
);
  wire [7:0] _T_13; // @[FixedPointTypeClass.scala 43:59:@8.4]
  wire [6:0] _GEN_0; // @[FixedPointTypeClass.scala 21:58:@9.4]
  wire [6:0] _GEN_1; // @[FixedPointTypeClass.scala 21:58:@9.4]
  wire [7:0] _GEN_2; // @[FixedPointTypeClass.scala 21:58:@9.4]
  wire [8:0] _T_14; // @[FixedPointTypeClass.scala 21:58:@9.4]
  wire [7:0] _T_15; // @[FixedPointTypeClass.scala 21:58:@10.4]
  wire [7:0] _T_16; // @[FixedPointTypeClass.scala 21:58:@11.4]
  assign _T_13 = $signed(io_a) * $signed(io_b); // @[FixedPointTypeClass.scala 43:59:@8.4]
  assign _GEN_0 = {{3{io_c[3]}},io_c}; // @[FixedPointTypeClass.scala 21:58:@9.4]
  assign _GEN_1 = $signed(_GEN_0) << 3; // @[FixedPointTypeClass.scala 21:58:@9.4]
  assign _GEN_2 = {{1{_GEN_1[6]}},_GEN_1}; // @[FixedPointTypeClass.scala 21:58:@9.4]
  assign _T_14 = $signed(_T_13) + $signed(_GEN_2); // @[FixedPointTypeClass.scala 21:58:@9.4]
  assign _T_15 = _T_14[7:0]; // @[FixedPointTypeClass.scala 21:58:@10.4]
  assign _T_16 = $signed(_T_15); // @[FixedPointTypeClass.scala 21:58:@11.4]
  assign io_out = _T_16[7:2];
endmodule

演習:MACオブジェクト

問題文は以下の通り。

Macモジュールは少ない幾つかの入力と唯一の出力を持っている。Chiselでコードを書く際には val out = Mac(a, b, c)のように書けたほうが便利なはずだ。以下のMacモジュールが正しく機能するようにMacのコンパニオン・オブジェクトのメソッドapplyを実装しよう

object Mac {
    def apply[T <: Data : Ring](a: T, b: T, c: T): T = {
      ??? // ここを実装する
    }
}

class MacTestModule extends Module {
    val io = IO(new Bundle {
        val uin = Input(UInt(4.W))
        val uout = Output(UInt())
        val sin = Input(SInt(4.W))
        val sout = Output(SInt())
        //val fin = Input(FixedPoint(16.W, 12.BP))
        //val fout = Output(FixedPoint())
    })
    // for each IO pair, do out = in * in + in
    io.uout := Mac(io.uin, io.uin, io.uin)
    io.sout := Mac(io.sin, io.sin, io.sin)
    //io.fout := Mac(io.fin, io.fin, io.fin)
}
println(getVerilog(new MacTestModule))

ただ、この問題、、、もはや答えを載せるまでも無いよね、、、ということで答えは割愛。だってMAC演算書けば良いんだもの。普通に書けばエラボレートが通ってRTLが生成される。

演習??:積分

問題文は以下。

次の画像のような積分器を設計しよう。genRegのビット幅はn1で、genInのビット幅はn2となる。
Reg/RegInit/RegNext/RegEnableなどをT <: Dataの型のテンプレートとするのを忘れないようにしよう

、、、ということなんですが、これどうみても答えがそのまま載ってるんだよなー。。。。

class Integrator[T <: Data : Ring](genIn: T, genReg: T) extends Module {
    val io = IO(new Bundle {
        val in  = Input(genIn.cloneType)
        val out = Output(genReg.cloneType)
    })

    val reg = RegInit(genReg, Ring[T].zero) // init to zero
    reg := reg + io.in
    io.out := reg
}

class IntegratorSIntTester(c: Integrator[SInt]) extends PeekPokeTester(c) {
    poke(c.io.in, 3)
    expect(c.io.out, 0)
    step(1)
    poke(c.io.in, -4)
    expect(c.io.out, 3)
    step(1)
    poke(c.io.in, 6)
    expect(c.io.out, -1)
    step(1)
    expect(c.io.out, 5)
}

chisel3.iotesters.Driver(() => new Integrator(SInt(4.W), SInt(8.W))) { c => new IntegratorSIntTester(c) }
````

ということでそのまま載せちゃう。
ポイントは問題文にもあるとおりで、`Integrator`内部で使用する各種HW素子の型を`genReg`/`genIn`から引き継ぐようにすること、、くらいかな。
イマイチ`Ring`が理解できてないので、これは追って使うときに調べてみよう。

因みに、これをChiselのデータ型にしたものみたい。

[https://ja.wikipedia.org/wiki/%E7%92%B0_(%E6%95%B0%E5%AD%A6):embed:cite]

Module3.6はこの後に”独自の型を作る”というのが続いているのだが、手元の環境で例題を動かすとヌルポインタ例外が起きて動かないので飛ばすことにした。多分後で`dsptools`を使うことになると思うのでその時にここのサンプルコードについても調査を行おうと思う。

ということでこれでModule3.6もお終い。