前言
前面的部分简单介绍了Chisel——新型敏捷硬件开发语言,也简单说明了开发环境部署以及示例项目的运行。接下来学习Chisel入门,数据类型与操作符号。
对于操作系统要求较低,IDEA适用于多个平台,但在验证阶段会根据不同的操作系统适配不同的EDA工具。
操作系统:Linux、mac OS、Windows 10
开发工具:IDEA、CLion、Verilator、GTKWave
Chisel开发环境部署
安装步骤
- 安装SBT
SBT(Simple Build Tool,简单构造工具)是一种用于构建 Scala 项目的构建工具。SBT 具有以下功能:
-
依赖管理:SBT 可以管理项目所需的依赖库和插件。它使用类似于 Maven 的方式来定义和解析依赖关系,可以自动下载和管理所需的库。
-
构建定义:通过 SBT 的构建定义文件(通常是 build.sbt),可以定义项目的结构、编译选项、测试配置等。这些构建定义可以用于编译、运行和测试项目。
-
任务管理:SBT 具有丰富的任务管理功能,可以定义和执行各种任务。例如,可以使用 SBT 执行编译、运行、测试、打包等操作。
-
插件支持:SBT 允许使用插件来扩展构建过程和功能。可以使用现有的插件或编写自定义插件,以满足特定项目的需求。
SBT 是 Scala 社区广泛使用的构建工具,它简化了 Scala 项目的构建和管理过程,并提供了强大的工具和功能,以提高开发效率。
使用brew、apt-get安装均可
安装后查看SBT
MacBook-Pro ~ % sbt sbtVersion
[info] welcome to sbt 1.8.3 (Homebrew Java 20.0.1)
[info] loading global plugins from /Users/.sbt/1.0/plugins
[info] loading project definition from /Users/project
[info] set current project to zhuketong (in build file:/Users/)
[info] 1.8.3
- 安装Git
Git 是一个分布式版本控制系统,用于跟踪和管理文件的变化。现已成为开源软件开发中最流行的版本控制系统之一。
Git 的主要功能包括:
-
版本控制:Git 可以跟踪文件的所有修改历史,并记录每个版本的变化。这使得开发团队可以轻松地查看、比较和恢复文件的不同版本。
-
分支管理:Git 具有强大的分支管理功能,允许用户创建、合并和删除分支。这使得多人协作开发更加灵活,并能够同时进行多个功能的开发和测试。
-
协作与远程仓库:Git 支持与远程仓库的交互,可以将本地仓库的变更推送到远程仓库,或者从远程仓库拉取最新的代码。这样多人协作开发时可以方便地分享和同步代码。
-
代码审查:Git 提供了代码审查的功能,开发人员可以轻松地在不影响主代码库的情况下创建和管理代码审查请求,以便团队成员对代码进行评审和讨论。
-
撤销和修改:Git 允许用户撤销修改并回滚到之前的版本。它还提供了修改历史记录、修改合并和修改冲突解决等功能,帮助开发人员更好地管理代码变更。
MacBook-Pro ~ % git -v
git version 2.40.1
- 安装Verilator
Verilator 是一个开源的硬件描述语言(HDL)仿真和验证工具,用于对 Verilog 和 SystemVerilog 代码进行静态分析和编译,并生成高性能的仿真模型。
MacBook-Pro ~ % verilator -version
Verilator 5.006 2023-01-22 rev UNKNOWN.REV
Windows平台可以使用其他仿真工具:
- ModelSim:ModelSim 是 Mentor Graphics 公司推出的一款广泛使用的 HDL 仿真器。它支持 Verilog和 VHDL 两种硬件描述语言,并提供了丰富的仿真和调试功能,如波形查看、代码覆盖率分析等。
- Xilinx Vivado Simulator:Xilinx Vivado 是 Xilinx 公司的一套集成开发环境,其中包含了 Vivado Simulator 作为默认的仿真工具。Vivado Simulator 支持Verilog、VHDL 和 SystemVerilog,具有较好的性能和调试能力。
- 安装g++
Verilator 是一个基于 C++ 的开源项目,用于将 Verilog 代码转换为高速模拟器。在编译 Verilator 时,它会使用 g++ 编译器来处理 C++ 代码,并生成可执行的仿真器。
MacBook-Pro ~ % g++ -v
Apple clang version 14.0.3 (clang-1403.0.22.14.1)
Target: arm64-apple-darwin22.5.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
- 安装GTKWave
GTKWave 是一个开源的波形查看器,用于显示和分析数字电路仿真的波形数据。它支持多种不同的波形文件格式,包括 VCD (Value Change Dump)、LXT、FST 等,可以在图形界面中以直观的方式展示波形信号的变化。
Windows下可以使用ModelSim、Vivado等工具
环境测试
- 项目部署
在第一部分已经导入了git项目,接下来在此基础上进行开发。(没有完成项目导入的读者可以查看本专栏第一篇博文)
> test
[IJ]test
[info] compiling 1 Scala source to /Users/IdeaProjects/chisel-template-main/target/scala-2.13/classes ...
[info] done compiling
[info] compiling 2 Scala sources to /Users/IdeaProjects/chisel-template-main/target/scala-2.13/test-classes ...
[info] done compiling
[info] GCDSpec:
[info] - Gcd should calculate proper greatest common denominator
[info] Run completed in 2 seconds, 584 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 7 s, completed 2023-5-31 15:04:31
- 编写代码
在/chisel-template-main/src/main/scala
目录下创建文件 AND.scala,以下为文件内容:
import chisel3._
class AND extends RawModule {
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
}
在/chisel-template-main/src/test/scala
目录下创建文件 ADNtest.scala,以下为文件内容:
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
object testMain extends App{
(new ChiselStage).execute(Array("--target-dir","generated/and"),Seq(ChiselGeneratorAnnotation(()=>new AND)))
}
- 运行代码
在testMain处运行代码,项目目录/chisel-template-main/generated/and
中生成文件AND.v
,内容如下:
module AND(
input io_a,
input io_b,
output io_c
);
assign io_c = io_a & io_b;
endmodule
- 仿真代码
使用CLion编写main.cpp仿真驱动代码,项目名为 simulate,同时将 .v 文件放置于同一目录结构下。仿真代码如下:
#include "VAND.h" // Verilator生成的头文件
#include <iostream>
#include <verilated.h>
#include <verilated_vcd_c.h>
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
// 实例化模块
VAND* andModule = new VAND;
// 初始化仿真时钟
andModule->io_a = 0;
andModule->io_b = 0;
// 创建波形文件
Verilated::traceEverOn(true);
VerilatedVcdC* vcd = new VerilatedVcdC;
andModule->trace(vcd, 99);
vcd->open("sim.vcd");
// 运行仿真
for (int i = 0; i < 10; ++i) {
// 更新输入信号
andModule->io_a = i % 2;
andModule->io_b = (i + 1) % 2;
// 执行一个时钟周期
andModule->eval();
// 记录波形
vcd->dump(i);
// 输出结果
std::cout << "io_c = " << andModule->io_c << std::endl;
}
// 关闭波形文件
vcd->close();
// 清理资源
andModule->final();
delete andModule;
delete vcd;
return 0;
}
注意:VAND.h 头文件是后续执行Verilator命令生成的,若缺失其他头文件自行填充即可
例如:缺失 verilated_vcd_c.h 头文件,在 CMakeLists.txt 添加如下内容
include_directories(/opt/homebrew/Cellar/verilator/5.006/share/verilator/include)
include_directories(obj_dir)
- 编译代码
在终端中运行命令
MacBook-Pro simulate % verilator -Wall --cc -trace AND.v --exe main.cpp
目录结构中会产生 obj_dir
文件夹
生成编译仿真模型:
MacBook-Pro simulate % make -C obj_dir -j -f VAND.mk VAND
运行测试代码:
MacBook-Pro simulate % ./obj_dir/VAND
在目录结构中会生成 sim.vcd
波形文件
- GTKWave波形图
在终端中执行如下命令,打开GTKWave查看波形图
MacBook-Pro simulate % gtkwave sim.vcd
实验环境问题说明
实验环境中涉及到的工具繁多,初学者可能会被各种环境问题绊倒,但所幸macOS、Linux中集成的工具包功能强大,能解决多数问题。而对于环境配置问题大部分也可以在网络中找到答案,读者可以查阅资料解决相关问题。
欢迎读者在评论区互动交流,同时补充实验过程未能提及的细节。
数据类型
UInt、SInt、Bool
UInt
、SInt
和 Bool
是表示不同数据类型的类,用于在硬件设计中表示无符号整数、有符号整数和布尔值。
-
UInt
(无符号整数):用于表示非负整数。可以通过指定位宽来创建 UInt
变量。
- 用于表示非负整数,适用于计数器、地址、数据宽度等无符号整数值的表示。
- 可以通过指定位宽来创建
UInt
变量。
- 支持常见的算术和逻辑运算,如加法、减法、乘法、除法、与运算、或运算、位移等。
- 可以使用
:=
操作符进行赋值,也可以使用 Wire
来声明和连接 UInt
信号。
val myUInt: UInt = UInt(8.W) // 创建一个 8 位无符号整数变量
-
SInt
(有符号整数):用于表示有符号整数。和 UInt
类似,可以通过指定位宽来创建 SInt
变量。
- 用于表示有符号整数,适用于需要表示正负整数值的场景。
- 可以通过指定位宽来创建
SInt
变量。
- 支持常见的算术和逻辑运算,如加法、减法、乘法、除法、与运算、或运算、位移等。
- 可以使用
:=
操作符进行赋值,也可以使用 Wire
来声明和连接 SInt
信号。
val mySInt: SInt = SInt(8.W) // 创建一个 8 位有符号整数变量
-
Bool
(布尔值):用于表示逻辑值,即 true 或 false。
- 用于表示逻辑值,即 true 或 false。
- 可以通过
Bool()
创建布尔值变量。
- 支持常见的逻辑运算,如与运算、或运算、非运算、异或运算等。
- 可以使用
:=
操作符进行赋值,也可以使用 Wire
来声明和连接 Bool
信号。
val myBool: Bool = Bool() // 创建一个布尔值变量
Vec[T]
Vec[T]
是一种表示具有固定大小的向量(数组)的数据类型,其中 T
是元素的类型。Vec[T]
可以用于在硬件设计中表示具有固定长度的数据集合。
以下是 Vec[T]
的一些常见用法和特性:
- 声明
Vec[T]
变量:
val myVec: Vec[UInt] = Vec(Seq.fill(4)(0.U(8.W)))
// 声明一个具有 4 个 8 位无符号整数的向量
- 获取
Vec[T]
的长度:
val length: Int = myVec.length // 获取向量的长度,这里为 4
- 访问
Vec[T]
中的元素:
val element: UInt = myVec(0) // 访问向量中索引为 0 的元素
- 更新
Vec[T]
中的元素:
myVec(1) := 10.U // 更新向量中索引为 1 的元素为 10
- 使用
Vec[T]
进行循环操作:
for (i <- 0 until myVec.length) {
// 对向量中的每个元素执行操作
// 例如:myVec(i) := myVec(i) + 1.U
}
Bundle
Bundle
是一种数据类型,用于组合多个信号或数据成为一个复合类型。它类似于结构体或记录,在硬件设计中用于组织和传递多个相关的信号。
使用 Bundle
,可以创建自定义的数据结构,其中包含多个字段或成员,每个字段可以是不同的数据类型。通过将相关信号组合到一个 Bundle
中,可以更好地组织和管理设计中的复杂信号关系。
以下是使用 Bundle
的常见用法:
- 定义
Bundle
类型:
import chisel3._
class MyBundle extends Bundle {
val field1 = UInt(8.W)
val field2 = Bool()
val field3 = UInt(4.W)
}
在上述示例中,我们定义了一个名为 MyBundle
的 Bundle
类型,其中包含了三个字段:field1
(8 位无符号整数)、field2
(布尔值)和 field3
(4 位无符号整数)。
- 创建
Bundle
实例:
val myBundle = Wire(new MyBundle)
通过使用 new
关键字创建一个 MyBundle
的实例,可以声明一个 myBundle
变量来表示该 Bundle
。
- 访问
Bundle
字段:
myBundle.field1 := 42.U
myBundle.field2 := true.B
myBundle.field3 := 8.U
可以使用点运算符访问 Bundle
的各个字段,并对其进行赋值。
操作符
下表列出了一些常用的 Chisel 操作符及其在硬件设计中的作用:
操作符 |
描述 |
:= |
用于信号赋值 |
:= (连接符号) |
用于连接多个信号或连接信号和常数 |
Wire |
声明一个信号变量 |
Reg |
声明一个寄存器变量 |
Module |
声明一个硬件模块 |
when / elsewhen / otherwise
|
用于条件语句的建立 |
printf |
在仿真中打印调试信息 |
Vec |
声明一个向量(数组) |
Cat |
连接多个信号或常数形成一个更大的信号 |
RegNext |
在时钟边沿将当前值传递给下一个时钟周期的寄存器 |
Mux |
用于多路选择 |
valid / bits
|
用于处理带有效性和数据的接口 |
WireDefault |
设置信号的默认值 |
Fill |
复制一个信号多次以形成更大的信号 |
Counter |
声明一个计数器 |
Switch / Case
|
用于多路选择的建立 |
MuxCase |
用于多路选择的建立(更灵活的版本) |
PriorityMux |
用于优先级多路选择 |
OHToUInt |
用于将单热编码转换为无符号整数 |
UIntToOH |
用于将无符号整数转换为单热编码 |
下表列出了一些常用的 Chisel 运算符及其在硬件设计中的作用:
位运算符 |
描述 |
& |
位与运算符,对两个信号执行位与操作 |
| |
位或运算符,对两个信号执行位或操作 |
^ |
位异或运算符,对两个信号执行位异或操作 |
~ |
取反运算符,对信号执行位取反操作 |
<< |
左移运算符,将信号向左移动指定的位数 |
>> |
右移运算符,将信号向右移动指定的位数 |
<< UInt |
无符号左移运算符,将信号向左移动指定的位数 |
>> UInt |
无符号右移运算符,将信号向右移动指定的位数 |
<< SInt |
有符号左移运算符,将信号向左移动指定的位数 |
>> SInt |
有符号右移运算符,将信号向右移动指定的位数 |
缩减位运算符 |
描述 |
& |
缩减位与运算符,对位向量执行位与操作并缩减为单个位 |
| |
缩减位或运算符,对位向量执行位或操作并缩减为单个位 |
^ |
缩减位异或运算符,对位向量执行位异或操作并缩减为单个位 |
~& |
缩减位与非运算符,对位向量执行位与非操作并缩减为单个位 |
~| |
缩减位或非运算符,对位向量执行位或非操作并缩减为单个位 |
~^ |
缩减位异或非运算符,对位向量执行位异或非操作并缩减为单个位 |
比较运算符 |
描述 |
=== |
等于运算符,用于比较两个信号或数据是否相等 |
=/= |
不等于运算符,用于比较两个信号或数据是否不相等 |
< |
小于运算符,用于比较两个信号或数据的大小关系 |
<= |
小于等于运算符,用于比较两个信号或数据的大小关系 |
> |
大于运算符,用于比较两个信号或数据的大小关系 |
>= |
大于等于运算符,用于比较两个信号或数据的大小关系 |
移位运算符 |
描述 |
<< |
左移运算符,将信号或数据向左移动指定的位数 |
>> |
右移运算符,将信号或数据向右移动指定的位数 |
<< UInt |
无符号左移运算符,将信号或数据向左移动指定的位数 |
>> UInt |
无符号右移运算符,将信号或数据向右移动指定的位数 |
<< SInt |
有符号左移运算符,将信号或数据向左移动指定的位数 |
>> SInt |
有符号右移运算符,将信号或数据向右移动指定的位数 |
部分位运算符 |
描述 |
val t = a(n) |
抽取第 n 位 |
val t = a(m,n) |
抽取第 n ~ m 位 |
Fill(n: Int, value: UInt): UInt |
接受两个参数,n 表示生成的向量的位数,value 表示要填充的值。 |
Cat(vals: Seq[Data]): UInt |
表示将所有输入信号按照顺序连接起来的结果 |
总结
学习Chisel设计硬件的开发过程,从环境部署到编译运行,再到模拟仿真,其中细节甚多,且都是不可避免的步骤,今后的实验中也需要反复进行这些步骤,所以针对过程中产生的问题要及时发现解决,积累经验。
此外也了解了Chisel硬件开发的基本数据类型与操作符运行,为以后的开发设计完成铺垫。
本文到此结束
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)