chisel
终于来到了敏捷型语言开发。chisel(Constructing Hardware in a Scala Embedded
Language)提供了更高层次的抽象,可以实现更快速的构建硬件,使得硬件更容易被理解和修改。
chisel’s env
要使用chisel首先要有java环境,每到这个环节我们一定要引入一个环境管理工具(因为依旧有多版本java的使用需求)。
这里引入的工具是SDKMAN!,他类似于nodejs的nvm和python的pyenv,可以使用SDKMAN!管理java环境的不同版本。
1 | curl -s "https://get.sdkman.io" | bash |
运行以上命令就可以下载SDKMAN!.
我们可以通过SDKMAN!来安装指定版本的OpenJDK.
1 | sdk install java 17.0.1-open # install openjdk 17.0.1 |
安装完成之后的切换版本:
1 | sdk list java |
如果要查看所有通过sdk已安装的java版本,可以查看~/.sdkman/candidates/java
chisel的运行需要scala,这两者的安装方式如下:
1 | sudo pacman -S sbt |
我们可以通过以下命令确认chisel环境准备就绪:
1 | curl -O -L https://github.com/chipsalliance/chisel/releases/latest/download/chisel-example.scala |
如果成功构建了chisel项目,则说明scala环境没有问题。
关于firtool,chisel6.0以后会自动配置,除非使用的是很老版本的OS;如果需要自己配置firtool,可以在~/.zshrc中加入:
1 | export CHISEL_FIRTOOL_PATH=/path/to/firtool |
scala-cli一般只适用于少数文件的组织,对于一个较大的项目,准备一个构建工具可以方便很多。sbt就是一个构建工具,印象中的chisel最开始就是用sbt来组织项目的,但是今天打开官方文档的时候发现现在更推荐使用mill,那我们也来尝尝新:
1 | yay -S mill |
verilator
如果需要接入verilator,还需要至少支持C++14和make的C++编译器,不过一般PC随便下个gcc就可以解决问题。
verilator的下载:
1 | sudo pacman -S verilator |
neovim
chisel本质还是scala,所以可以通过:
1 | :MasonInstall metals |
为nvim添加智能补全,跳转等功能。
syntax
对于一个没有怎么认真学过java的人来说,chisel这个语法确实是抽象。不过好在我们已经写过了DFT系列3-4,这个java的语法还是和UVM有一点相像
首先是最重要的继承语法,和UVM很像:
1 | //> using scala 2.13.18 |
在这里和UVM一样,用extends <base_class>完成类的继承。
最开头的//>用来引入依赖,分别指定了scala和chisel版本,比如此处使用了scala 2.13.18, chisel 7.11.0版本。
class
同样是类似UVM,chisel也内置了很多class和method用来简化设计。有以下几个常用的class:
- Module
这是chisel中最基本的构建块,每个硬件设计模块都应该继承Module.
1 |
|
在上面的代码中,创建了一个MyModule的新模块,在这个模块中,创建了一个IO模块(名为io),传入的参数是一个Bundle类型,包括一个输入和输出,输入和输出都是8.W.
val在设计中常用于定义硬件信号,寄存器,模块等,他本质是scala的关键字,定义一个不可变的变量(const),此处是不可变绑定,也就是说val x,x的值可以改变,但是不能把他变成另一种类型或者指向其他对象。
UInt(8.W)代表一种数据类型,也就是无符号整数,传入的参数用来定义无符号整数的格式,此处定义了8.W,.W代表参数用于控制width,意味着8用于指定位宽。
- Bundle
用于组合多个信号端口,作为模块的输入输出接口。
- IO
用于声明模块的输入输出信号。
- UInt, SInt
代表无符号和有符号整数类型。
- Wire
用于定义组合逻辑中的信号,通常表示连线。
1 | val sum = Wire(UInt(8.W)) |
:=用于重新赋值(也就是第二次及以后的赋值)。
- Reg
用于定义时序逻辑的存储器,用来存储数据。
1 | val reg = Reg(UInt(8.W)) |
- RegInit
表示带有初始值的寄存器。
1 | val reg = RegInit(0.U(8.W)) |
初始值被设定为0.U(8.W),0被指定为.U,就是代表数值为无符号数0,8.W就是8位宽。
- Clock
定义时钟信号。
1 | val clk = Clock() |
- Reset
定义复位信号。
1 | val rst = Reset() |
- Cat
可以拼接多个信号。
1 | val a = UInt(4.W) |
- Mux
实现多路选择器(其实只能二选一,多路需要嵌套或者用其他方式比如Vec+options实现)。
1 | val sel = UInt(2.W) |
- Decoupled
用于流水线设计的数据通道,是可以设计数据流和控制信号分离的类型。
定义带有valid和ready信号的数据流通道。
1 | val Decoupled = Decoupled(UInt(8.W)) |
- Vec
一个向量类型。
1 | val v = Vec(4, UInt(8.W)) |
此外还有控制语句when/otherwise:
1 | when(io.in1 > 0.U){ |


