Chiselで組み合わせ回路

ハードウェアの接続状態を記述して、
コンパイルして実行するとverilogコードを吐いてくれる
[Chisel](https://chisel.eecs.berkeley.edu/)をもうちょっと触ってみた。

Scalaをよく知らないので、変な所があればそういうことだと思ってください。

#信号のデータ型
チュートリアルに載っている
Chiselで定義済みのデータ型は

* `UInt` :符号なし整数
* `SInt` :符号あり整数
* `Bool` :ブーリアン

`UInt`と`SInt`は生成時にビット幅を指定できる
他にも`Flo`(単精度浮動小数点数型)もあるっぽい

定数信号宣言

```scala
val foo = UInt(5) // 3bit
```

ポート宣言

```scala
val in = UInt(INPUT,8) // 8bit input port
```

ちなみにデフォルトで加算などの演算子が定義されている。

`Bundle`を継承することでデータ型を作成することができる。

```scala
class Validatable[T <: Data] (dataSignal: T, validSignal : Bool= Bool(OUTPUT)) extends Bundle {
  val data = dataSignal
  val valid = validSignal
}

val foo = new Validatable(UInt(width = 8))
```

`Vec`で配列を作成できる。

```scala
val foo = Vec.fill(5){UInt(width=8)} // 8bit x 5
val bar = foo(3)
```

`Vec`に対して`map`とか使える

```scala

val io = new Bundle{
  val x = Vec.fill(3){UInt(INPUT,width = 8)}
  val y = Vec.fill(3){UInt(OUTPUT,width = 8)}
}
io.y := io.x.map(x => x + UInt(10))
/*
       1       4
in ----2-------5----->
       3       6

       11      14
out----12------15----->
       13      16
*/
```

`Enum`で列挙型が定義できる

```scala
val s_even :: s_odd :: Nil = Enum(UInt(), 2)
io.even := s_even
io.odd := s_odd
```

#モジュール
`Module`を継承して作成。

```scala
class Mux2 extends Module {
  val io = new Bundle{
    val sel = UInt(INPUT, 1)
    val in0 = UInt(INPUT, 1)
    val in1 = UInt(INPUT, 1)
    val out = UInt(OUTPUT, 1)
  }

  io.out := (io.sel & io.in1) | (~io.sel & io.in0)
}
```
`:=`演算子は verilogの`assign`に相当。
`io.out := ...` の部分は

```velilog
assign io_out = (io_sel & io_in1) | (~io_sel & io_in0)
```
に相当する。
(実際の変換は一時wireが定義されるのでこの通りにはならない)

#サブモジュール

```scala
class TopModule extends Module {
  val io = new Bundle{
      val in = UInt(INPUT,1)
      val out = UInt(OUTPUT,1)
  }

  val subModule = Module(new SubModule())
  subModule.io.in := io.in
  io.out := subModule.io.out
}
```
verilogと似たようなものだが
サブモジュール同士をつなぐ時に一々信号線を用意しなくて済む。


#(Rx的な)filter,mapっぽいものを作ってみる
信号線をストリームとみなして、filterやらmapやら作ってみる。

先ほどの`Validatable`に

```scala
class Validatable[T <: Data] (dataSignal: T, validSignal : Bool= Bool(OUTPUT)) extends Bundle {
  val data = dataSignal
  val valid = validSignal
}
```
こんなものを付け足して

```scala
  def filter(f: T => Bool) : Validatable[T] ={
    new Validatable[T](this.data, f(this.data) & this.valid)
  }

  def map[S <: Data](f: T => S): Validatable[S] ={
    new Validatable[S](f(this.data), this.valid)
  }
```
試しに入力ストリームから偶数を通して+10して出力ストリームに流す素敵なモジュールを作ってみる。

```scala
class NiceModule extends Module {
  val io = new Bundle{
    val in = new Validatable(UInt(width=8)).asInput
    val out = new Validatable(UInt(width=8)).asOutput
  }
  io.out := io.in.filter(x=> x(0) === UInt(0)).map(x => x + UInt(10))
}
```


テストを書いて

```scala
class NiceModuleTester(c: NiceModule) extends Tester(c) {
  poke(c.io.in.valid,1)

  poke(c.io.in.data,2)
  step(1)
  expect(c.io.out.data, 12)
  expect(c.io.out.valid, 1)

  poke(c.io.in.data,3)
  step(1)
  expect(c.io.out.data, 13)
  expect(c.io.out.valid, 0)

  poke(c.io.in.valid,0)

  poke(c.io.in.data,2)
  step(1)
  expect(c.io.out.data, 12)
  expect(c.io.out.valid, 0)
}
/*
input:
data ---2----3----2---->
valid---T----T----F---->

expected:
data ---12---13---X---->
valid---T----F----F---->

X: don't care
*/
```
テストが通るのを確認して、verilogに変換すると

```
module NiceModule(
    input [7:0] io_in_data,
    input  io_in_valid,
    output[7:0] io_out_data,
    output io_out_valid
);

  wire T0;
  wire T1;
  wire T2;
  wire[7:0] T3;


  assign io_out_valid = T0;
  assign T0 = T1 & io_in_valid;
  assign T1 = T2 == 1'h0;
  assign T2 = io_in_data[1'h0:1'h0];
  assign io_out_data = T3;
  assign T3 = io_in_data + 8'ha;
endmodule
```

自動で定義された内部信号`T0~3`を、試しに手動で削ってみると

```
module NiceModule(
    input [7:0] io_in_data,
    input  io_in_valid,
    output[7:0] io_out_data,
    output io_out_valid
);

  wire isEven ;
  assign isEven = (io_in_data[0] == 1'h0);
  assign io_out_valid = isEven & io_in_valid;
  assign io_out_data = io_in_data + 8'ha;
endmodule
```
一応意味はあっている。

まあ趣味で個人的に使う分には面白いのではないかと。