IC的帆哥 - chisel
https://icfgblog.com/index.php/tag/chisel/
-
“香山”路上的几个坑
https://icfgblog.com/index.php/Digital/309.html
2021-12-11T21:38:00+08:00
由于项目需要,希望了解开源的香山处理器,希望先跑通一波,虽然有清晰的教程,但还是遇到不少问题,特来记录一番。
一、mill 安装
香山的编译不是基于sbt而是另一个叫mill的工具,我用的是ubuntu 18.04 ,但硬是没找到对应的mill安装指令,apt-install也不行,网上找了一圈,确实官网有,说是Manual安装:
sudo sh -c "curl -L https://github.com/com-lihaoyi/mill/releases/download/0.10.0-M4/0.10.0-M4 > /usr/local/bin/mill && chmod +x /usr/local/bin/mill"
二、github 连不上
运行make init后,需要下载子电路模块以及其他github仓库,具体有这些:
timingScripts
berkeley-hardfloat
api-config-chipsalliance
rocket-chip
block-inclusivecache-sifive
chisel-testers2
不能科学上网的我只能干瞪眼。
网上搜到一个比较有效的办法linux连Github
第一步 :
通过Ping检测最快的节点http://ping.chinaz.com/github.com
第二步 :
选择最快节点添加到hosts文件:
sudo vim /etc/hosts
140.82.114.3 github.com
但最后还是把这些仓库先移到gitee,然后逐个git clone。
三、香山make verilog不成功
当万事具备,运行make verilog,已经出现“编译成功”,但几分钟之后就报error退出。搜到论坛说内存要大于32G,或者修改build.sc,有两个地方Xmx64G,均改成10G,就可以跑通了,最后生成的verilog代码竟然有100M!
附、设置环境变量
在~/.bashrc下添加几个环境变量
vim ~/.bashrc
# 行末添加
export NEMU_HOME=/home/tom/Documents/chisel_test/NEMU
export AM_HOME=/home/tom/Documents/chisel_test/nexus-am
export NUTSHELL_HOME=/home/tom/Documents/chisel_test/nutShell
export NOOP_HOME=/home/tom/Documents/chisel_test/XiangShan
-
chisel 虐我千百遍(一)
https://icfgblog.com/index.php/Digital/301.html
2021-11-17T07:48:00+08:00
前几天想将一个大小端转换的模块转成chisel,感觉挺简单,但真写起来就各种bug。
module EndianCnvt #(parameter NUMLENGTH = 128)(
input [NUMLENGTH-1:0] NumIn,
output [NUMLENGTH-1:0] NumOut
);
localparam BYTENUM = NUMLENGTH/8;
genvar i;
for (i=0; i>3
val temp = Wire(Vec(BYENUM,UInt(8.W)))
for (i >3
val temp = Wire(Vec(BYENUM,UInt(8.W)))//转换成8bit的数组,分别赋值,再转回UInt
for (i >3
val temp = io.NumIn.asTypeOf(Vec(BYENUM,UInt(8.W)))
val temp2 = Wire(Vec(BYENUM,UInt(8.W)))
temp2 := temp.reverse //reverse方法,这里必须借助中间变量temp2,不然也会报错
io.NumOut := temp2.asUInt()
}
object EndianCnvt{
def apply(in1:UInt,NUMLENGTH:Int) :UInt={
val inst = Module(new EndianCnvt(NUMLENGTH))
inst.io.NumIn := in1
inst.io.NumOut
}
}
生成的verilog如下:
module EndianCnvt(
input clock,
input reset,
input [127:0] io_NumIn,
output [127:0] io_NumOut
);
wire [7:0] temp_0 = io_NumIn[7:0]; // @[sub_module.scala 32:32]
wire [7:0] temp_1 = io_NumIn[15:8]; // @[sub_module.scala 32:32]
wire [7:0] temp_2 = io_NumIn[23:16]; // @[sub_module.scala 32:32]
wire [7:0] temp_3 = io_NumIn[31:24]; // @[sub_module.scala 32:32]
wire [7:0] temp_4 = io_NumIn[39:32]; // @[sub_module.scala 32:32]
wire [7:0] temp_5 = io_NumIn[47:40]; // @[sub_module.scala 32:32]
wire [7:0] temp_6 = io_NumIn[55:48]; // @[sub_module.scala 32:32]
wire [7:0] temp_7 = io_NumIn[63:56]; // @[sub_module.scala 32:32]
wire [7:0] temp_8 = io_NumIn[71:64]; // @[sub_module.scala 32:32]
wire [7:0] temp_9 = io_NumIn[79:72]; // @[sub_module.scala 32:32]
wire [7:0] temp_10 = io_NumIn[87:80]; // @[sub_module.scala 32:32]
wire [7:0] temp_11 = io_NumIn[95:88]; // @[sub_module.scala 32:32]
wire [7:0] temp_12 = io_NumIn[103:96]; // @[sub_module.scala 32:32]
wire [7:0] temp_13 = io_NumIn[111:104]; // @[sub_module.scala 32:32]
wire [7:0] temp_14 = io_NumIn[119:112]; // @[sub_module.scala 32:32]
wire [7:0] temp_15 = io_NumIn[127:120]; // @[sub_module.scala 32:32]
wire [63:0] lo = {temp_8,temp_9,temp_10,temp_11,temp_12,temp_13,temp_14,temp_15}; // @[sub_module.scala 35:28]
wire [63:0] hi = {temp_0,temp_1,temp_2,temp_3,temp_4,temp_5,temp_6,temp_7}; // @[sub_module.scala 35:28]
assign io_NumOut = {hi,lo}; // @[sub_module.scala 35:28]
endmodule
功能确实是对的,如何去除这些烦人的注释呢?写了一个脚本remove.py,还挺好用
# python3 remove.py -f yourfile.v
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-f','--file',dest='file',type=str,default='yourfile.v')
args = parser.parse_args()
file_data = ""
remove = 0
with open(args.file,"r",encoding="utf-8") as f:
for line in f:
if(line[1:3]=="if" or line[0:7]=="initial"):
remove += 1
temp = ""
elif(line[1:4]=="end" or line[0:6]=="end //"):
remove -= 1
temp = ""
else:
if(remove!=0):
temp=""
else:
temp = line.split('/')[0]
if((len(temp)!=0) and (temp[-1]!='\n')) :
temp += '\n'
file_data += temp
with open(args.file,"w",encoding= "utf-8") as f:
f.write(file_data)
处理之后瞬间清爽了:
module EndianCnvt(
input clock,
input reset,
input [127:0] io_NumIn,
output [127:0] io_NumOut
);
wire [7:0] temp_0 = io_NumIn[7:0];
wire [7:0] temp_1 = io_NumIn[15:8];
wire [7:0] temp_2 = io_NumIn[23:16];
wire [7:0] temp_3 = io_NumIn[31:24];
wire [7:0] temp_4 = io_NumIn[39:32];
wire [7:0] temp_5 = io_NumIn[47:40];
wire [7:0] temp_6 = io_NumIn[55:48];
wire [7:0] temp_7 = io_NumIn[63:56];
wire [7:0] temp_8 = io_NumIn[71:64];
wire [7:0] temp_9 = io_NumIn[79:72];
wire [7:0] temp_10 = io_NumIn[87:80];
wire [7:0] temp_11 = io_NumIn[95:88];
wire [7:0] temp_12 = io_NumIn[103:96];
wire [7:0] temp_13 = io_NumIn[111:104];
wire [7:0] temp_14 = io_NumIn[119:112];
wire [7:0] temp_15 = io_NumIn[127:120];
wire [63:0] lo = {temp_8,temp_9,temp_10,temp_11,temp_12,temp_13,temp_14,temp_15};
wire [63:0] hi = {temp_0,temp_1,temp_2,temp_3,temp_4,temp_5,temp_6,temp_7};
assign io_NumOut = {hi,lo};
endmodule
附上寄存器、向量初始化的语句备忘
val regs = RegInit(VecInit(Seq.fill(32)(0.U(32.W))))//寄存器向量初始化
val reg = RegNext(updateData,resetData,enable)
val reg2 = ShiftRegister(in , n , en )
val reg3 = ShiftRegister(in ,n ,resetData,en)
val reg4 = Reg(Vec(32,SInt(64.W)))
val array = Wire(Vec(4,SInt(8.W)))
val array1 = array.asTypeOf(Vec(2,UInt(16.W)))
val array2 = VecInit(Seq.fill(4)(false.B))
val array3 = VecInit(Seq.fill(4)(0.S))
//rom
val mem = VecInit("h11".U,"hff".U,"h15".U,"h31".U)
-
chisel中级技巧
https://icfgblog.com/index.php/Digital/282.html
2021-10-22T09:59:00+08:00
过了一遍chisel book和chisel tutorial的内容,学了chisel基本搭电路的语法,能把verilog的代码翻译成chisel,但在翻译的过程中发现仅有这些基本技能还不够,完全没有发挥出scala这种高级语言的优势,知乎看到一篇用Chisel快速搭建FFT流水线电路,发现好多方便的操作,总结下来有以下几点:
local parameter
同类线网Bundle
实例化模块
一、局部变量local parameter
verilog 经常会有局部变量,有些定义位宽,有些定义循环次数,chisel里也能用val 定义变量,但总感觉“不干净”,而且不好复用,有什么办法呢?scala的trait很好用!
可以在Config.scala里定义参数:
package FFT
trait HasDataConfig {
val DataWidth = 32
val BinaryPoint = 16
}
trait HasElaborateConfig {
val FFTLength = 512
val useGauss = false
val supportIFFT = false
}
然后定义时,在需要这些局部变量的模块加上with就可以访问,非常方便:
//用with增加局部变量
class MyComplex extends Bundle with HasDataConfig {
val re = FixedPoint(DataWidth.W, BinaryPoint.BP)
val im = FixedPoint(DataWidth.W, BinaryPoint.BP)
}
二、Bundle线网
实际电路的输入输出其实会分数据线和控制线,还有一些信号属于同类型的,但verilog只能一视同仁,只能在命名上加以区分,但chisel里的Bundle类可以把相同的信号“绑”在一起,既便于阅读,也便于调用。
先定义一个复数的Bundle,然后在Switch模块里就可以直接调用MyComplex,而无需再定义实部虚部,更加简洁。
//Bundle“绑定”相同类型的信号
class MyComplex extends Bundle with HasDataConfig {
val re = FixedPoint(DataWidth.W, BinaryPoint.BP)
val im = FixedPoint(DataWidth.W, BinaryPoint.BP)
}
class Switch extends Module {
val io = IO(new Bundle{
val in1 = Input(new MyComplex)
val in2 = Input(new MyComplex)
val sel = Input(Bool())
val out1 = Output(new MyComplex)
val out2 = Output(new MyComplex)
})
io.out1 := Mux(io.sel, io.in2, io.in1)
io.out2 := Mux(io.sel, io.in1, io.in2)
}
三、伴生对象之工厂方法
别被这个名词唬了,verilog调用子模块时,都需要再连线,很繁琐,代码又长又臭。chisel伴生对象的工厂方法可以很好解决这个问题。
class Switch extends Module {
val io = IO(new Bundle{
val in1 = Input(new MyComplex)
val in2 = Input(new MyComplex)
val sel = Input(Bool())
val out1 = Output(new MyComplex)
val out2 = Output(new MyComplex)
})
io.out1 := Mux(io.sel, io.in2, io.in1)
io.out2 := Mux(io.sel, io.in1, io.in2)
}
//定义伴生对象,apply方法
object Switch {
def apply(in1: MyComplex, in2: MyComplex, sel: Bool): (MyComplex, MyComplex) = {
val inst = Module(new Switch)
inst.io.in1 := in1
inst.io.in2 := in2
inst.io.sel := sel
(inst.io.out1, inst.io.out2)
}
}
这样就可以很方便例化模型,不需要再连线,元素访问也很简单:
//不需要再连线
val sw12 = Switch(in1,in2,swCtrl)
//元素访问
val out1 = sw12._1
val out2 = sw12._2
除此之外,用map生成正弦余弦rom,scala测试代码中还有一些高效操作,还没有完全掌握,得慢慢消化。
-
Chisel 从入门到放弃
https://icfgblog.com/index.php/Digital/263.html
2021-09-05T20:53:00+08:00
吃鸡卷王钦哥在暑假期间入坑Chisel,仍然吃鸡,已然抛弃羽毛球。现在已经可以用Chisel替代verilog做工程项目,这转身真是迅速!诚邀钦哥总结一路踩过的坑,因此有了这篇汇总博文。
官网
1. GitHub - chipsalliance/chisel3: Chisel 3: A Modern Hardware Design Language
2. Chisel/FIRRTL: Introduction (chisel-lang.org)
3. Chisel 中文社区 (chiselchina.com)
4. 官方 API
很多问题在上述链接 2 都能解答,主要在 Resource 和 Cookbooks 目录下,以下给出 General Cookbook 目录下一个常见问题的解答 (很可能遇到的 , 部分位的赋值问题):
Chisel3 does not support subword assignment . The reason for this is that subword assignment generally hints at a better abstraction with an aggregate/structured types, i.e., a Bundle or a Vec.
If you must express it this way, one approach is to blast your UInt to a Vec of Bool and back:
import chisel3._
class Foo extends Module {
val io = IO(new Bundle {
val in = Input(UInt(10.W))
val bit = Input(Bool())
val out = Output(UInt(10.W))
})
val bools = VecInit(io.in.asBools)
bools(0) := io.bit
io.out := bools.asUInt
}
环境配置
1. intellij IDEA 下载
Download IntelliJ IDEA: The Capable && Ergonomic Java IDE by JetBrains
选择社区版本够用了
2. 插件安装
chisel 插件提供了 Chisel Project 模板,可以快速配置 scala 版本和 sbt(scala build tool)
3. 工程创建
路径: project - new project - chisel - empty chisel project,假如没有 SDK 就安装图示的 openjdk.
图中的 GCD Example 是官方提供的一个样例程序,同时提供了设计和内建测试的代码
目前个人的配置如上,sbt 版本最好就是按照官网版本提供的,否则编译过程中报奇怪的错很难界定原因.假如后续 chisel 更新之后代码莫名跑不通了,可以参照官网的说明以及样例程序,检查自己的工程中 sbt 或者 scala verision 是否过时了
4. 文件结构 & chisel 设计模板创建
上图给出了工程文件的结构示意图, 主要关注的是 /src 路径下的文件,以及根目录(Project)下的 /test_run_dir 和 生成的 .v 文件(编译通过才有),其中 /src 文件夹下的主要内容如下图
/src/main/resource 文件夹存放来自外部的 verilogIP,此路径是 chisel 默认的 blackBoxResource(即此处的 verilogIP) 存放路径,以下给出 1 个示例
class data_scheduler(DI_W: Int, DO_W: Int) extends BlackBox
with HasBlackBoxResource
with ParamTable {
val io = IO(new Bundle() {
val params_intl_ch_num = Input(UInt(2.W))
val params_cpi = Input(UInt(SLOW_TIME_CNT_W.W))
val params_frame_interval = Input(UInt(SLOW_TIME_CNT_W.W))
val params_samples_per_tr = Input(UInt(FAST_TIME_CNT_W.W))
val params_total_samples_per_ch = Input(UInt(CH_SAMPLE_CNT_W.W))
val params_fft_in_inv_order = Input(Bool())
... //按照verilogIP端口声明所有的变量
//这里声明的变量名就是chisel例话IP时候的端口名
//另外,chisel并不会检查verilogIP的端口与这里声明的变量是否同名
//因此,这里声明的变量名一定要跟verilogIP端口名完全对应,
//否则会出现"port nof found"错误
})
addResource("/data_scheduler/rtl/data_scheduler.v")
//默认目录为/resource,这里非默认路径,需要进一步指明文件位置
}
/src/main/scala 路径下存放设计文件
假如设计文件分类较多,可以采用 package 来管理,如下图所示
在创建了 Package 之后,例如上图的 SoCComponents,类似地,选中该 Package,右键菜单可以进一步创建 Package,或者是直接就创建一个 Chisel Module 的模板.
5. 生成 verilog
参照官网教程完成设计之后,可以采用如下语句生成 verilog(当前的最新版本),这个生成 verilog 的方法官方一直在迭代,网上的博客很多给的是比较落后的方法
object SoCGen extends App {
val verilogString = (new chisel3.stage.ChiselStage).execute(Array(""),
Seq(ChiselGeneratorAnnotation(() => (new SoCTop))))
//SoCTop是设计文件(Class)
/*
val targetDir = "test_run_dir/SoCTop"
(new ElkStage).execute(
Array("-td", targetDir),
Seq(ChiselGeneratorAnnotation(() => new SoCTop)) //这是生成架构图的,可以先不用
)*/
}
参考博客
入门
以下博客给的生成 verilog 方法基本都已过时 !
如何在 windows 上搭建 chisel 环境(除了 chisel,作者还提供了很多其他数字设计内容)
初学 Chisel 语言,看这篇就够了:最方便简洁的入门资料整理 - 知乎 (zhihu.com)
知乎 chisel 专栏
官网 chiselTest
Introduction | Tour of Scala | Scala Documentation (chisel 内语法看不懂时的参考,且有些 scala 语法提供的语法糖对于减少设计也有帮助))
进阶
用 Chisel 快速搭建 FFT 流水线电路 - 知乎 (zhihu.com)
第二十二章 Chisel 基础——多时钟域设计__iChthyosaur 的博客-CSDN 博客
【Chisel 学习笔记 1】隐式参数(比较高级的传参,可以略过)
ChiSel 学习笔记 - (提供了一个端口列表参数化的方法)
chisel 基础教程 (提供了一个位操作的方法)
算法函数_Chisel 实践——利用 CORDIC 算法计算对数函数_街机时代的博客-CSDN 博客
参数化的 Bundle 的方法以及存在问题的解决
高阶应用
Scala进阶
chisel 高阶用法简介--rocket-chip generator - 知乎 (zhihu.com)
(27 封私信 / 80 条消息) 如何深入理解 rocket-chip core 的代码? - 知乎 (zhihu.com)
RocketChip 分析,内容比较详细
RocketChip 官方文档
RocketChip 学习笔记系列 - 知乎 (zhihu.com)
Verilator 仿真器入门 | Hu '' s Blog (hubohan.space)
Verilator_timewh 的专栏 -CSDN 博客_verilator
-
Chisel,说爱你不容易
https://icfgblog.com/index.php/Digital/253.html
2021-08-29T19:26:00+08:00
很早就听说Chisel,伯克利的又一大作,也听说其学习门槛高,基于scala语言,语法非常灵活。近期的国科大搞的“一生一芯”计划,“香山”处理器等,更是将Chisel推向了又一个高潮,据包老师说,Chisel的开发效率远高于verilog香山高性能riscv处理器,这种提高生产力的语言,怎么能不学?
一、Chisel的难点
Chisel 早期的门槛有两个,一个是开发环境,另一个是从verilog转变。开发环境说来简单, 真搭起来还真不容易,我花了两三天时间才实现想要的效果:
产生电路的.v文件
产生.vcd文件查看波形
不产生波形,基于scala仿真
虽然网上的资料很多,但Chisel的更新很快,按照一些入门教程做,还不一定能跑通,报错也难搜到解决方案,毕竟用scala的人没有python多。
从verilog的转变倒是不难,chisel tutorial 提供了很多样例。推荐阅读顺序:
官网文档
chisel book web
chisel-getting-started,配套chisel tutorial但其仿真用的是iotests,不是最新的chiseltest
chisel-bootcamp可以交叉着看
chisel_cheatsheet.pdf
chisel-book.pdf
二、环境配置
类似的教程网上有很多,一抓一大把,基本上是基于IntelliJIDEA,但我的虚拟机跑起来经常卡,于是转投vscode。IntelliJIDEA牛逼的地方在于,它会实时检查语法错误,如果一直没跑通,可以用它打开再debug。
我的环境是Ubuntu20.04 ,先装好java,sbt,verilator(如果中途报错,搜索就有答案)
sudo apt-get install default-jdk
sudo apt-get install verilator
sudo apt-get install sbt
如果装不上sbt,可以参考官网sbt download
echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | sudo tee /etc/apt/sources.list.d/sbt.list
echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | sudo tee /etc/apt/sources.list.d/sbt_old.list
curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | sudo apt-key add
sudo apt-get update
sudo apt-get install sbt
抱着极简的想法,我决定从零开始,建了这几个文件夹还有一个.sbt文件。
sbt
一直没懂sbt文件是做什么,直到现在才知道,它会下载之后工程需要用到的库,用于版本管理。为什么要学习SBT?这篇文章写得很详细,sbt解决了这些问题:
都是同样的代码,为什么在我的机器上可以编译执行,而在他的机器上就不行?
为什么在我的机器上可以正常打包,而配置管理员却打不出来?
项目组加入了新的人员,我要给他说明编译环境如何设置,但是让我挠头的是,有些细节我也记不清楚了。
我的项目依赖一些jar包,我应该把他们放哪里?放源码库里?
这是我开发的第二个项目,还是需要上面的那些jar包,再把它们复制到我当前项目的svn库里吧
现在是第三次,再复制一次吧 ----- 这样真的好吗?
我写了一个数据库相关的通用类,并且推荐给了其他项目组,现在已经有五个项目组在使用它了,今天我发现了一个bug,并修正了它,我会把jar包通过邮件发给其他项目组。
项目进入测试阶段,每天都要向测试服务器部署一版。每次都手动部署,太麻烦了。
build.sbt
该文件参考chisel book仓库的build.sbt,找了好久才找到这个既简洁,又支持新旧两种测试环境的sbt文件:
scalaVersion := "2.12.13"
scalacOptions ++= Seq(
"-deprecation",
"-feature",
"-unchecked",
"-Xfatal-warnings",
"-Xsource:2.11",
"-language:reflectiveCalls",
// Enables autoclonetype2
"-P:chiselplugin:useBundlePlugin"
)
resolvers ++= Seq(
Resolver.sonatypeRepo("snapshots"),
Resolver.sonatypeRepo("releases")
)
// Chisel 3.4
libraryDependencies += "edu.berkeley.cs" %% "chisel3" % "3.4.3"
libraryDependencies += "edu.berkeley.cs" %% "chisel-iotesters" % "1.5.3"
libraryDependencies += "edu.berkeley.cs" %% "chiseltest" % "0.3.3"
三、开发
生成verilog
接下来可以在/src/main/scala/examples的目录下创建文件FullAdder.scala,这是chisel-tutorial里的样例:
package examples
import chisel3._
class FullAdder extends Module {
val io = IO(new Bundle {
val a = Input(UInt(1.W))
val b = Input(UInt(1.W))
val cin = Input(UInt(1.W))
val sum = Output(UInt(1.W))
val cout = Output(UInt(1.W))
})
// Generate the sum
val a_xor_b = io.a ^ io.b
io.sum := a_xor_b ^ io.cin
// Generate the carry
val a_and_b = io.a & io.b
val b_and_cin = io.b & io.cin
val a_and_cin = io.a & io.cin
io.cout := a_and_b | b_and_cin | a_and_cin
}
// Generate Verilog file
import chisel3.stage.ChiselStage
object u_FullAdder extends App{
(new ChiselStage).emitVerilog(new FullAdder)
}
然后在mytest_a目录下运行sbt。runMain会自动在main目录的scala文件夹搜索,examples表示‘scala’下的目录,u_FullAdder是生成对象。
sbt 'runMain examples.u_FullAdder'
这样会在mytest_a下产生.v,.fir,.json 文件。.v文件长这样
module FullAdder(
input clock,
input reset,
input io_a,
input io_b,
input io_cin,
output io_sum,
output io_cout
);
wire a_xor_b = io_a ^ io_b; // @[FullAdder.scala 15:22]
wire a_and_b = io_a & io_b; // @[FullAdder.scala 18:22]
wire b_and_cin = io_b & io_cin; // @[FullAdder.scala 19:24]
wire a_and_cin = io_a & io_cin; // @[FullAdder.scala 20:24]
assign io_sum = a_xor_b ^ io_cin; // @[FullAdder.scala 16:21]
assign io_cout = a_and_b | b_and_cin | a_and_cin; // @[FullAdder.scala 21:34]
endmodule
仿真
仿真有两个版本,一种是旧版,基于chisel3.iotesters,chisel-tutorial的样例仿真是基于它的,所以也得学。另一种是新版,基于chiseltest,两种相差不大,这篇知乎讲得很好chisel生成Verilog与基本测试,本文很多也是参考这篇的。
chisel3.iotesters
在/src/test/scala/examples的目录下创建文件FullAdderTest.scala,如下:
package examples
import chisel3.iotesters.{PeekPokeTester, Driver, ChiselFlatSpec}
class FullAdderTests(c: FullAdder) extends PeekPokeTester(c) {
for (t > 1) & 1
poke(c.io.a, a)
poke(c.io.b, b)
poke(c.io.cin, cin)
step(1)
expect(c.io.sum, sum)
expect(c.io.cout, cout)
}
}
object FullAdderTestGen extends App{
chisel3.iotesters.Driver.execute(args,()=>new FullAdder())(c => new FullAdderTests(c))
}
然后在mytest_a目录下运行sbt。test表示在src/test/scala目录搜索。
// ./generated目录生成vcd文件
sbt 'test:runMain examples.FullAdderTestGen -td ./generated/FullAdder --backend-name verilator'
// 只输出仿真结果
sbt 'test:runMain examples.FullAdderTestGen -td ./generated/FullAdder --is-verbose'
chiesel.test
将FullAdderTest.scala修改如下:(不需要再写object)
package examples
import org.scalatest._
import chisel3._
import chisel3.experimental.BundleLiterals._
import chiseltest._
import java.util.Random
class FullAdderTest extends FlatSpec with ChiselScalatestTester with Matchers{
behavior of "mytest2"
it should "do something" in{
test(new FullAdder() ){ c =>
val randNum = new Random
val a = randNum.nextInt(2)
val b = randNum.nextInt(2)
val cin = randNum.nextInt(2)
val res = a + b + cin
val sum = res & 1
val cout = (res >> 1) & 1
c.io.a.poke(a.U)
c.io.b.poke(b.U)
c.io.cin.poke(cin.U)
c.clock.step(1)
c.io.sum.expect(sum.U)
c.io.cout.expect(cout.U)
}
}
}
然后在mytest_a目录下运行sbt。
// target_run_dir会生成vcd文件
sbt ‘testOnly examples.FullAdderTest -- -DwriteVcd=1’
// 只输出仿真结果
sbt ‘testOnly examples.FullAdderTest’
测试文件github
补一个另一种生成verilog的方法,生成的代码没有ifdef,RANDMIZE 这种“脏东西”,需要用到chisel 3.5模板@015e98f:
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
import firrtl.options.TargetDirAnnotation
object u_FullAdder extends App {
(new chisel3.stage.ChiselStage).execute(
Array("-X", "verilog","--emission-options=disableMemRandomization,disableRegisterRandomization"),
Seq(ChiselGeneratorAnnotation(() => new FullAdder()),
TargetDirAnnotation("Verilog"))
)
}