chisel-tutorial代码学习 - 01
1. chisel-tutorial初介绍
chisel-tutorial是ucb写的一个代码库,放在github上,用于初学者学习一些chisel的基础语法。
重点推荐这个github库的wiki页面,对chisel基础语法基本都涉及到了。
我这里就是简单的记录一下这个代码的学习笔记。
2. chisel-tutorial代码库的文件结构
下面是文件结构,直接从wiki上粘贴过来的。编译器通过build.sbt
这个文件下载相应的依赖。
chisel-tutorial/
build.sbt # project description
run-examples.sh # shell script to execute one or more examples
run-problem.sh # shell script to execute one or more problems
run-solution.sh # shell script to execute one or more solutions
src/
main/
scala/
examples/ # chisel examples
Accumulator.scala ...
problems/ # skeletal files for tutorial problems
Counter.scala ...
solutions/ # solutions to problems
Counter.scala ...
test/
resources/
in.im24
in.wav
scala/
examples/ # examples testers
AdderTests.scala ...
problems/ # problems testers
AccumulatorTest.scala ...
solutions/ # solutions testers
AccumulatorTests.scala ...
util/
TutorialRunner.scala
3. chisel代码初体验: GCD.scala
GCD.scala
这个文件是wiki里让看的第一个chisel代码,具备了chisel代码的基本元素,这个模块的作用是计算2个16-bit无符号数的最大公约数。我这里直接把代码贴过来,
package examples
import chisel3._
/**
* Compute the GCD of 'a' and 'b' using Euclid's algorithm.
* To start a computation, load the values into 'a' and 'b' and toggle 'load'
* high.
* The GCD will be returned in 'out' when 'valid' is high.
*/
class GCD extends Module {
val io = IO(new Bundle {
val a = Input(UInt(16.W))
val b = Input(UInt(16.W))
val load = Input(Bool())
val out = Output(UInt(16.W))
val valid = Output(Bool())
})
val x = Reg(UInt())
val y = Reg(UInt())
when (io.load) {
x := io.a; y := io.b
} .otherwise {
when (x > y) {
x := x - y
} .elsewhen (x <= y) {
y := y - x
}
}
io.out := x
io.valid := y === 0.U
}
代码GCD.scala
分为3大部分,
package examples
涉及scala的包管理,这里暂不作细致介绍import chisel3._
引入chisel3的库文件,其中._
表示将chisel3的所有库文件都引入进来,不引入就不可以使用chisel
定义的关键词,比如IO
、Bundle
、UInt
、when
等等- 最后一大部分就是
class GCD extends Module {}
,这部分可以等价理解为verilog的模块定义,包括端口定义和逻辑设计。chisel
中定义模块,需要这个class
继承Module
类- 首先是端口定义
val io = IO(new Bundle{ ... })
,这几个字母是固定的,io
就是io
,IO
就是IO
,new Bundle{}
就是new Bundle{}
,不能写错,不理解就死记住,将来再理解。 - 接着定义了2个寄存器
Reg
分别是x
和y
,用作中间变量 - 然后是一个
when
代码块,直接把这个代码块理解成verilog里always @ (posedge clock)
块的if ... else if ... else ...
就行了,完全是同一个意思 - 在
when
代码块里,x
和y
的赋值用的是:=
操作符,这是因为在scala
中,val
定义的变量是不可变变量,一旦定义好之后,不可以重新赋值,因此chisel
定义了:=
操作符,用于val
变量的重新赋值,最后两个输出的赋值也是同理。注意,chisel
中变量首次定义或者说赋值使用=
操作符,后面的重赋值使用:=
操作符。 - 最后给两个输出端口赋值,也就是verilog里
assign
- 首先是端口定义
整个GCD.scala
的代码比较简单,需要注意的就是2点,
- 1是端口列表里没有
clock
信号和reset
信号,这是因为chisel会隐式的添加这两个信号到端口列表中,不需要我们手动添加。最后生成verilog代码后就可以看到这两个信号了。 - 2是
x
和y
这样的中间变量不需要指定位宽,chisel具有强大的位宽推断功能,会自动指定所需要的最小位宽
4. chisel的testbench
在verilog中,我们写完一个模块,一般还会写一个testbench来仿真验证这个模块的功能,chisel也是一样的。对于刚才所写的GCD.scala
这个模块,在/src/test/scala/examples/
路径下有一个测试文件,GCDTests.scala
,相当于verilog里的testbench。这里把代码贴过来,
package examples
import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}
class GCDTests(c: GCD) extends PeekPokeTester(c) {
val inputs = List( (48, 32), (7, 3), (100, 10) )
val outputs = List( 16, 1, 10)
var i = 0
do {
poke(c.io.a, inputs(i)._1)
poke(c.io.b, inputs(i)._2)
poke(c.io.load, 1)
step(1)
poke(c.io.load, 0)
var ready = false
do {
ready = peek(c.io.valid) == 1
step(1)
} while (t < 100 && ! ready)
expect(c.io.out, outputs(i))
i += 1
} while (t < 100 && i < 3)
if (t >= 100) fail
}
class GCDTester extends ChiselFlatSpec {
behavior of "GCD"
backends foreach {backend =>
it should s"test the basic gcd circuit" in {
Driver(() => new GCD, backend)((c) => new GCDTests(c)) should be (true)
}
}
}
首先使用import
引入iotesters
,这是chisel的仿真库,包括常用的仿真方法peek
、poke
、step
、expect
方法。
然后定义了一个类GCDTests(c)
去继承PeekPokeTester
,主构造器接收一个待测模块GCD
类型的对象c
,并将c
传递给PeekPokeTester
。在类的内部,主要由一个do..while
代码块构成,写了3个测试用例,使用poke
给待测模块的输入赋值,使用expect
去检验待测模块的输出和期望值是不是相等的,如果相等,就会在仿真时输出success
。
5. 运行chisel仿真
按照wiki的指示,运行./run-examples.sh GCD
,经过编译和仿真后有如下打印,
test GCD Success: 3 tests passed in 29 cycles in 0.044277 seconds 654.96 Hz
[info] [0.015] RAN 24 CYCLES PASSED
Tutorials passing: 1
[success] Total time: 7 s, completed 2021-8-19 20:32:17
表示3个测试用例都通过了。
6. 生成verilog代码和vcd波形文件
使用刚才的脚本命令,再增加一个环境变量即可。
./run-examples.sh GCD --backend-name verilator
运行这个脚本命令,就会在chisel-tutorial/test_run_dir/examples/GCD/
目录下,生成许多编译和仿真的文件,中间我们最关心的就是两个,一个是产生的verilog文件,一个是.vcd
波形文件。使用gtkwave就可以打开波形文件看,例如这次生成的GCD.vcd
使用gtkwave打开就如下图所示,