FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages

ハードウェア記述言語Chiselコンパイラの内部解析(5. Chiselに新しい型と演算子を追加してみる)

https://cdn-ak.f.st-hatena.com/images/fotolife/m/msyksphinz/20181105/20181105012126.png

Chiselを勉強するためにはとりあえず自分専用の拡張Chiselを作ってみるのが良いと思う。 とりあえずは、まずは手っ取り早く新しいデータ型でも追加してみたい。Chiselには以下の基本データ型が存在する。

  • UInt
  • SInt
  • Bool

そこで、これに追加して今度はTIntという型を追加してみる。TIntは特に何もできない新しいデータ型で、役割はUIntと大して変わらない。 そして、TIntに追加する新たな演算子として、代入演算子::=というものを追加してみる。 この演算子も限りなく意味のないものだが、A ::= BBをビット反転してAに格納する。

  • TInt 型を追加する

まず、UInt, SIntなどが定義されているのは./chiselFrontend/src/main/scala/chisel3/Data.scalaで定義されている。 これに新たにTIntクラスとTIntFactoryを追加する。 TIntクラスはUIntクラスをベースにして派生する。

/** A data type for unsigned integers, represented as a binary bitvector. Defines arithmetic operations between other
  * integer types.
  *
  * @define coll [[UInt]]
  * @define numType $coll
  * @define expandingWidth @note The width of the returned $coll is `width of this` + `1`.
  * @define constantWidth  @note The width of the returned $coll is unchanged, i.e., `width of this`.
  */
sealed class TInt private[chisel3] (width: Width) extends UInt(width) with Num[UInt] {
  /** Connect this $coll to that $coll mono-directionally and element-wise.
    *
    * This uses the [[MonoConnect]] algorithm.
    *
    * @param that the $coll to connect to
    * @group Connect
    */
  def ::= (that: TInt)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = {
    this.connect(~that)(sourceInfo, connectionCompileOptions) // scalastyle:ignore line.size.limit
  }
}


// This is currently a factory because both Bits and TInt inherit it.
trait TIntFactory {
  /** Create a UInt type with inferred width. */
  def apply(): UInt = apply(Width())
  /** Create a UInt port with specified width. */
  def apply(width: Width): UInt = new UInt(width)

   /** Create a UInt literal with specified width. */
  protected[chisel3] def Lit(value: BigInt, width: Width): UInt = {
    val lit = ULit(value, width)
    val result = new UInt(lit.width)
    // Bind result to being an Literal
    lit.bindLitArg(result)
  }

  /** Create a UInt with the specified range */
  def apply(range: Range): UInt = {
    apply(range.getWidth)
  }
  /** Create a UInt with the specified range */
  def apply(range: (NumericBound[Int], NumericBound[Int])): UInt = {
    apply(KnownUIntRange(range._1, range._2))
  }
}

このTInt型にはTIntFactoryが追加されている。これはUIntFactoryと同一である。

そして、TInt型には::=型を追加した。これは:=の実装をコピーしているのだが、一点だけ、connectをする前にビット反転の演算子を挿入している。

  def ::= (that: TInt)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = {
    this.connect(~that)(sourceInfo, connectionCompileOptions) // scalastyle:ignore line.size.limit
  }

ついでに、派生元のUIntにも::=演算子がないとコンパイル中に怒られたので追加する。

diff --git a/chiselFrontend/src/main/scala/chisel3/Bits.scala b/chiselFrontend/src/main/scala/chisel3/Bits.scala
index 5a6db7c1..571a3100 100644
--- a/chiselFrontend/src/main/scala/chisel3/Bits.scala
+++ b/chiselFrontend/src/main/scala/chisel3/Bits.scala
@@ -902,6 +902,10 @@ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[U

   private def subtractAsSInt(that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt =
     binop(sourceInfo, SInt((this.width max that.width) + 1), SubOp, that)
+
+  def ::= (that: Bits)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = {
+    this.connect(~that)(sourceInfo, connectionCompileOptions) // scalastyle:ignore line.size.limit
+  }
 }

 // This is currently a factory because both Bits and UInt inherit it.

この::=演算子は、実行される場合に呼ばれるのはMonoConnectクラスのメソッドだ。MonoConnectクラスにTInt同士の接続を行うパタンを追加する。

diff --git a/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala b/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala
index ace7be20..833106e7 100644
--- a/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala
+++ b/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala
@@ -81,6 +81,8 @@ private[chisel3] object MonoConnect {
         elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
       case (sink_e: UInt, source_e: UInt) =>
         elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
+      case (sink_e: TInt, source_e: TInt) =>
+        elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
       case (sink_e: SInt, source_e: SInt) =>
         elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
       case (sink_e: FixedPoint, source_e: FixedPoint) =>
  • テストをする

まず、上記の改造を行ったChiselをコンパイルし、ローカル環境にデプロイする。

sbt publishLocal

終了すると、以下のテストコードを作成した。

  • test_tint.scala
package tint_test

import chisel3._

class tint_test extends Module {
  val io = IO(new Bundle {
    val in  = Input(TInt(8.W))
    val out = Output(TInt(8.W))
  })

  io.out ::= io.in
}

object main extends App {
  chisel3.Driver.emitVerilog(new tint_test())
}

TInt型と、::=演算子を使用して配線の接続を行っている。ではさっそくコンパイルしてみよう。

sbt 'runMain tint_test.main`

コンパイルの結果、以下のVerilogファイルが生成された。

module tint_test(
  input        clock,
  input        reset,
  input  [7:0] io_in,
  output [7:0] io_out
);
  assign io_out = ~ io_in; // @[tint_test.scala 11:10]
endmodule

正しく生成されているようだ。