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")) ) }