Chisel3 Getting Started and Simple project

FIRRTLについて知りたかったので、
まずは
sdkman、Java、sbtのインストール
そして、Chisel3 (Scala)をつかった素朴なプロジェクト作成、テスト、Verilog生成の手順メモ

(成熟しきった開発環境というわけではなさそうなので今時点でのとりあえずの手法、常に公式も参照する)
GitHub - chipsalliance/chisel3: Chisel 3: A Modern Hardware Design Language
GitHub - ucb-bar/chisel-testers2: Repository for chisel3 testers2 open alpha
GitHub - freechipsproject/chisel-template: A template project for beginning new Chisel work
freechipsproject/chisel3

環境

WSL2 (Ubuntu 20.04.1 LTS)
fish

(今回は使用していないけど、dockerとか使った方がよかった)

SBTのためのJavaのためのSDKMANを入れる

sudo apt install zip
curl -s https://get.sdkman.io | bash

fishだと用意されたshellscriptが実行できないので
fisherで入れる
【環境構築】fish shellを用いたDebian環境にSDKMANおよびJava(JDK)をインストールする方法

fisher add reitzig/sdkman-for-fish@v1.4.0
sdk selfupdate force
sdk help

でヘルプが出ればOK

SBTのためのJavaを入れる

sbt Reference Manual — Installing sbt on Linux
によると、AdoptOpenJDKのJavaを入れた方がいいらしい
なんかバージョンが違いすぎると問題があるかもしれないので、この記事を書いている時点で上のページに乗っている11.0.x.hs-adptを入れた。
f:id:katakanan:20210111214248p:plain

sdk list java
sdk install java 11.0.9.hs-adpt

sbtのインストール

sdk install sbt

sbtの動作確認

GitHub - ucb-bar/chisel-tutorial: chisel tutorial exercises and answers
ここをビルドできたら、おそらくOK

Getting the RepoとExecuting Chiselをする

sbt run

だけでよい気がする (sbt testは時間かかる上に、失敗した)

テンプレートプロジェクトを使用した

GitHub - freechipsproject/chisel-template: A template project for beginning new Chisel work

ここを好きなプロジェクト名でクローンして、とりあえずtestが通るはず。

git clone https://github.com/ucb-bar/chisel-template.git test00
cd test00
sbt test

VScodeScala拡張機能

VScodeでひらく。
Scala Syntax (official)はSyntax highlightしかしてくれないっぽいので、
Scala (Metals)を入れた。

build.sbtを編集

libraryDependenciesに追加

      "org.scalatest" %% "scalatest" % "3.0.8",
      "org.scalatest" %% "scalatest" % "3.2.0" % "test"

最後に追加

resolvers ++= Seq(
  Resolver.sonatypeRepo("snapshots"),
  Resolver.sonatypeRepo("releases")
)

編集完了すると、以下のようなのが出るので
Import changesしたほうがよさそう。
f:id:katakanan:20210111215456p:plain
f:id:katakanan:20210111215502p:plain
(コード補完してくれそう)

.gitignoreにいれた

*.db
*.fir
*.v
.bloop

ディレクトリ構成

f:id:katakanan:20210111215909p:plain
templateにあった構成をまねたほうがよい。

main.scala

package example

import chisel3._
import chisel3.stage._

class main extends Module{
    val io = IO(new Bundle{
        val A = Input(UInt(1.W))
        val B = Input(UInt(1.W))
        val C = Output(UInt(1.W))
    })

    io.C := io.A & io.B
}

テストだけならこれだけでもできた。
Verilogも出すなら追記が必要(後述)。

mainTester.scala

日本語で検索して出てくる記事はchisel-iotestersとかいうライブラリを使っていてpeekpoketestを継承してpeek, pokeしているが、
templateのプロジェクトがchisel-tester2なるものを使用していたのでそれで書いてみた。
GitHub - ucb-bar/chisel-testers2: Repository for chisel3 testers2 open alpha
まだalphaバージョンらしいので一部の関数以外は大きく変更される可能性がある。
chisel-testers / iotestersから将来的に移行するらしい

These APIs may be considered stable and are unlikely to change significantly:

Test invocation test
Basic operations: poke, peek, expect, step, including on Bundles
Basic concurrency operations: fork, join
Decoupled library: enqueueNow, expectDequeueNow, their sequence variants, expectPeek, expectInvalid - though the names may be refactored
timescope - though the name may be refactored

package example

import org.scalatest._
import chiseltest._
import chisel3._
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class mainTester extends AnyFlatSpec with ChiselScalatestTester with Matchers {
    it should "test: and circuit" in {
        test(new main){c => 
            c.io.A.poke(0.U)
            c.io.B.poke(0.U)
            c.clock.step()
            c.io.C.expect(0.U)

            c.io.A.poke(1.U)
            c.io.B.poke(0.U)
            c.clock.step()
            c.io.C.expect(0.U)

            c.io.A.poke(0.U)
            c.io.B.poke(1.U)
            c.clock.step()
            c.io.C.expect(0.U)

            c.io.A.poke(1.U)
            c.io.B.poke(1.U)
            c.clock.step()
            c.io.C.expect(1.U)
        }
    }
}

AND回路なのでclockの記述はいらないけど一応。
これで

sbt test

すると、
f:id:katakanan:20210111222124p:plain

わざと失敗するようにして、
f:id:katakanan:20210111222221p:plain
f:id:katakanan:20210111222419p:plain

Verilogの出力

main.scalaに追加

object mainDriver extends App {
    chisel3.Driver.execute(args, () => new main)
}

これで

sbt run

すると、
main.firとmain.vができていた。

;buildInfoPackage: chisel3, version: 3.4.1, scalaVersion: 2.12.12, sbtVersion: 1.3.10
circuit main : 
  module main : 
    input clock : Clock
    input reset : UInt<1>
    output io : {flip A : UInt<1>, flip B : UInt<1>, C : UInt<1>}
    
    node _io_C_T = and(io.A, io.B) @[main.scala 13:18]
    io.C <= _io_C_T @[main.scala 13:10]
   
module main(
  input   clock,
  input   reset,
  input   io_A,
  input   io_B,
  output  io_C
);
  assign io_C = io_A & io_B; // @[main.scala 13:18]
endmodule

chisel3.Driver.executeはDeprecatedらしいので、現時点ので推奨らしき書き方は、

    // chisel3.Driver.execute(args, () => new main)
    val verilogString = (new chisel3.stage.ChiselStage).emitVerilog(new main)
    println(verilogString)

コードが表示されて、なおかつ、ファイルが生成される。
GitHub - katakanan/chisel3_simple_project

Scalaは初めてだが、Chisel自体はHLSなどとは違って少しだけレジスタを意識して書くようなそんなところもありそうなので
普通のソフトウェア言語のように書く、というわけにはまだまだいかなさそう。