本文寫於 2024年10月4日,此時 chisel 最新版本為 6.5.0 。
Overview
Chisel (Constructing Hardware In a Scala Embedded Language) 是新興的硬體描述語言,是採用Scala作為基礎、利用chisel第三方庫的Domain Specific Language。此前第一次學習 Chisel 的時候教程推薦的是用 Scala-Cli 進行構建;但是日後肯定要用多檔案進行構建,因此需要尋找能夠進行工程級構建的方法。在網上找了許久總感覺不得其法, Chisel bootcamp 中 Jupyter 用的 setup 程式碼也不能直接套過來用,很難受。在銳神影片的建議下決定自己找些 example 來學習,故有此文。銳神影片連結為: Chisel 入門 - 王銳 - 一生一芯雙週分享會 。
獲取 Github repo
首先透過這個連結獲取 Github 專案模板並跟隨指引部署到自己的 Github 倉庫:Chisel Project Template
我把基於這個模板新建的repo命名為: chisel_helloworld
。這個倉庫包含很多東西,其中我們需要關注的內容有:
chisel_helloworld
├── ...
├── README.md
├── src
│ ├── main
│ │ └── scala
│ │ ├── gcd
│ │ │ ├── DecoupledGCD.scala
│ │ │ └── GCD.scala
│ │ └── misc
│ │ └── Hello.scala
│ └── test
│ └── scala
│ └── gcd
│ └── GCDSpec.scala
├── build.sbt
└── build.sc
其中 src/main/scala/misc/Hello.scala
是我新增的檔案,其內容為:
package misc
/*
* This code is a minimal hardware described in Chisel.
*
* Blinking LED: the FPGA version of Hello World
*/
import chisel3._
/**
* The blinking LED component.
*/
class Hello extends Module {
val io = IO(new Bundle {
val led = Output(UInt(1.W))
})
val CNT_MAX = (50000000 / 2 - 1).U
val cntReg = RegInit(0.U(32.W))
val blkReg = RegInit(0.U(1.W))
cntReg := cntReg + 1.U
when(cntReg === CNT_MAX) {
cntReg := 0.U
blkReg := ~blkReg
}
io.led := blkReg
}
/**
* An object extending App to generate the Verilog code.
*/
object Hello extends App {
emitVerilog(new Hello())
}
這份程式碼來自 chisel-example 。我修改了兩處:
- 第一行的
package misc
:在同一個package
裡面可以有很多份程式碼,會在後面再討論這件事情 - 倒數第二行的
emitVerilog(new Hello())
:顧名思義,這裡是構建產生.v
的 verilog 檔案的語句,其完整API路徑是chisel3.emitVerilog
,由於我們有一個import chisel3._
因此這裡可以直接使用
構建專案得到 verilog 檔案
Scala的構建工具有兩個,一個是 SBT ,另一個是 mill 。從上面的檔案目錄樹裡可以看到有 build.sbt
和 build.sc
兩個檔案,前者是 SBT 的配置檔案,後者是 mill 的配置檔案。只要有 build.sbt
就可以使用 SBT 進行構建;使用 mill 的專案則常常同時具有 build.sbt
和 build.sc
。這兩個檔案是專案配置檔案,開啟 build.sbt
就能看到所用 Scala 以及 Chisel 的版本資訊。
上面這個專案中的 build.sbt
檔案長這樣:
// See README.md for license details.
ThisBuild / scalaVersion := "2.13.12"
ThisBuild / version := "0.1.0"
ThisBuild / organization := "com.github.playasmegumin"
val chiselVersion = "6.2.0"
lazy val root = (project in file("."))
.settings(
name := "chisel_helloworld",
libraryDependencies ++= Seq(
"org.chipsalliance" %% "chisel" % chiselVersion,
"org.scalatest" %% "scalatest" % "3.2.16" % "test",
),
scalacOptions ++= Seq(
"-language:reflectiveCalls",
"-deprecation",
"-feature",
"-Xcheckinit",
"-Ymacro-annotations",
),
addCompilerPlugin("org.chipsalliance" % "chisel-plugin" % chiselVersion cross CrossVersion.full),
)
可以看到這裡的 Chisel 版本是 6.2.0 。Chisel 的版本迭代非常快,在跑網路上的 demo 時如果出現編譯問題請檢視對應版本的 Chisel API ,連結在這: Docs - chisel-lang
因為沒搞明白 mill 怎麼使,這裡我用的是 SBT ,直接在專案根目錄執行 sbt run
即可。
$ sbt run
[info] welcome to sbt 1.9.7 (Eclipse Adoptium Java 17.0.12)
[info] loading settings for project chisel_helloworld-build from plugins.sbt ...
[info] loading project definition from /home/duzelong/ysyx/chisel/chisel_helloworld/project
[info] loading settings for project root from build.sbt ...
[info] set current project to chisel_helloworld (in build file:/home/duzelong/ysyx/chisel/chisel_helloworld/)
[info] compiling 1 Scala source to /home/duzelong/ysyx/chisel/chisel_helloworld/target/scala-2.13/classes ...
Multiple main classes detected. Select one to run:
[1] gcd.GCD
[2] misc.Hello
Enter number:
因為在 src/main/scala
下面有兩個 “包” :
└── src
└── main
└── scala
├── gcd
│ ├── DecoupledGCD.scala
│ └── GCD.scala
└── misc
└── Hello.scala
所以這裡 SBT 給了我們兩個選項,一個是 gcd
包的 GCD
,另一個是 misc
包的 Hello
。這裡需要辨析一下,包的歸屬和目錄結構沒有什麼關係,主要是在 GCD.scala
和 Hello.scala
分別用 package gcd
和 package misc
定義了各自所屬的包名。資料夾結構可以隨你喜歡的設定,不會有很大的影響。一開始我還嘗試了這樣的放置方法:
└── src
└── main
└── scala
├── gcd
│ ├── DecoupledGCD.scala
│ └── GCD.scala
└── Hello.scala
也並不影響檔案和 package
的從屬關係,這個 package
事實上很像名稱空間。如果採用下面的目錄結構,同時刪除 src/main/scala/Hello.scala
的 package misc
:
└── src
└── main
└── scala
├── gcd
│ ├── DecoupledGCD.scala
│ └── GCD.scala
├── misc
│ └── Hello.scala
└── Hello.scala
則執行 sbt run
後會顯示:
$ sbt run
[info] welcome to sbt 1.9.7 (Eclipse Adoptium Java 17.0.12)
[info] loading settings for project chisel_helloworld-build from plugins.sbt ...
[info] loading project definition from /home/duzelong/ysyx/chisel/chisel_helloworld/project
[info] loading settings for project root from build.sbt ...
[info] set current project to chisel_helloworld (in build file:/home/duzelong/ysyx/chisel/chisel_helloworld/)
[info] compiling 1 Scala source to /home/duzelong/ysyx/chisel/chisel_helloworld/target/scala-2.13/classes ...
Multiple main classes detected. Select one to run:
[1] Hello
[2] gcd.GCD
[3] misc.Hello
Enter number:
可見,在不同包裡的 Hello
模組不會互相沖突,但是如果兩個 Hello.scala
中都有 package misc
,則會下面的結果:
$ sbt run
[info] welcome to sbt 1.9.7 (Eclipse Adoptium Java 17.0.12)
[info] loading settings for project chisel_helloworld-build from plugins.sbt ...
[info] loading project definition from /home/duzelong/ysyx/chisel/chisel_helloworld/project
[info] loading settings for project root from build.sbt ...
[info] set current project to chisel_helloworld (in build file:/home/duzelong/ysyx/chisel/chisel_helloworld/)
[info] compiling 1 Scala source to /home/duzelong/ysyx/chisel/chisel_helloworld/target/scala-2.13/classes ...
[info] compiling 2 Scala sources to /home/duzelong/ysyx/chisel/chisel_helloworld/target/scala-2.13/classes ...
[error] /home/duzelong/ysyx/chisel/chisel_helloworld/src/main/scala/misc/Hello.scala:14:7: Hello is already defined as class Hello
[error] class Hello extends Module {
[error] ^
[error] /home/duzelong/ysyx/chisel/chisel_helloworld/src/main/scala/misc/Hello.scala:34:8: Hello is already defined as object Hello
[error] object Hello extends App {
[error] ^
[error] two errors found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 2 s, completed 2024年10月4日 下午7:29:02
就是很經典的符號衝突錯誤。
接上面的步驟,我們執行 sbt run
後選擇 misc.Hello
,則有:
$ sbt run
[info] welcome to sbt 1.9.7 (Eclipse Adoptium Java 17.0.12)
[info] loading settings for project chisel_helloworld-build from plugins.sbt ...
[info] loading project definition from /home/duzelong/ysyx/chisel/chisel_helloworld/project
[info] loading settings for project root from build.sbt ...
[info] set current project to chisel_helloworld (in build file:/home/duzelong/ysyx/chisel/chisel_helloworld/)
[info] compiling 2 Scala sources to /home/duzelong/ysyx/chisel/chisel_helloworld/target/scala-2.13/classes ...
Multiple main classes detected. Select one to run:
[1] Hello
[2] gcd.GCD
[3] misc.Hello
Enter number: 3
[info] running misc.Hello
[success] Total time: 10 s, completed 2024年10月4日 下午7:31:03
然後會在根目錄裡發現多了一個 Hello.sv
,裡面就是我們需要的 Verilog 程式碼,至此本文的目的就達成了。
Others
當然這裡還有其他幾個值得細究的點。
在哪裡都能執行 sbt run
嗎?
目前看來是不行,如果在 src/
目錄下執行 sbt run
就會有這樣的後果:
src$ sbt run
[warn] No sbt.version set in project/build.properties, base directory: /home/duzelong/ysyx/chisel/chisel_helloworld/src
[info] welcome to sbt 1.10.2 (Eclipse Adoptium Java 17.0.12)
[info] set current project to src (in build file:/home/duzelong/ysyx/chisel/chisel_helloworld/src/)
[error] java.lang.RuntimeException: No main class detected.
[error] at scala.sys.package$.error(package.scala:30)
[error] stack trace is suppressed; run last Compile / bgRun for the full output
[error] (Compile / bgRun) No main class detected.
[error] Total time: 1 s, completed 2024年10月4日 下午7:32:24
看來在哪裡執行 sbt run
, SBT 就會把哪裡當作專案的根目錄。
生成 Hello.sv
的位置可以改嗎?
在專案根目錄執行 sbt run
的時候就會在根目錄生成 Hello.sv
。我猜測是直接把輸出檔案生成在了執行 sbt run
的目錄。但是實際上只能在專案根目錄執行 sbt run
,因此我的猜測沒什麼意義。
如果要修改生成 verilog 檔案的路徑,可能需要深入 SBT 的構建指令碼,那就之後再看。
生成 verilog 檔案的專案元件是什麼?
似乎是用 FIRRTL 生成的,可以再看看。這塊應該是 Chisel 自身的特性。