
从20世纪90年代开始,利用硬件描述语言和综合技术设计实现复杂数字系统的方法已经在集成电路设计领域得到普及。随着集成电路集成度的不断提高,传统硬件描述语言和设计方法的开发效率低下的问题越来越明显。近年来逐渐崭露头角的敏捷化设计方法将把集成电路设计带入一个新的阶段。与此同时,集成电路设计也需要一种适应敏捷化设计方法的新型硬件开发语言。本书从实用性和先进性出发,较全面地介绍新型硬件开发语言Chisel和数字系统敏捷化设计方法。全书分两篇。第一篇共10章,主要内容包括Chisel语言简介、Chisel的数据类型、Chisel的模块与硬件类型、Chisel常用的硬件原语、如何将Chisel代码转换生成Verilog HDL代码及基本测试方法、Chisel的黑盒、用Chisel实现多时钟域设计、Chisel的函数应用及其他议题等。第二篇共9章,介绍编写Chisel需要掌握的Scala语言编程基础知识。读者可以根据自身情况,跳过第二篇Scala基础部分,直接学习Chisel的硬件开发功能。本书提供配套的课后练习参考答案、仿真程序代码等。本书可作为集成电路科学与工程、电子信息类、计算机类等相关专业的高年级本科生及研究生的教学用书,也可供从事集成电路设计的工程人员和EDA专业人员学习、参考。
前 言 在数字集成电路设计领域中,最流行的硬件描述语言就是Verilog HDL。Verilog HDL已推出将近四十年,其开发效率低下的问题越来越明显,已经不适应如今动辄百亿晶体管规模的芯片开发。虽然Verilog HDL也有后续改进版本——SystemVerilog,其可使得硬件设计更为方便,但效果并不明显,治标不治本。据调研,过去的几十年间,以年为时间度量单位的芯片开发周期已经成为阻碍芯片设计行业创业的重要阻力。业界急需一套敏捷的硬件开发流程,这其中就需要一种新型的敏捷硬件开发语言。 经过最近十几年的探索,已经有Chisel、Bluespec、SpinalHDL等多种新型硬件开发语言出现,这些语言各有特色,各有优缺点。其中,以加州大学伯克利分校的研究团队发布的一种新型硬件开发语言Chisel(Constructing Hardware In a Scala Embedded Language)最为抢眼。Chisel是一门以Scala为宿主语言开发的硬件构建语言,它是建构在Scala语言之上的领域专用语言(Domain Specific Language,DSL),支持高度参数化的硬件生成器。加州大学伯克利分校在设计著名的RISC-V 处理器的时候引入了Chisel语言,其高抽象程度的描述方式让沉闷已久的芯片设计领域为之震动。借助Chisel,加州大学伯克利分校的研究团队仅花了一年时间和100万美元,就完成了从RISC-V指令集设计到芯片的成功流片。 本书作者在国内高校中较早把Chisel引入电路设计,为了向业界同行分享学习和使用Chisel语言的经验、体会,作者在科技博客网站推出了一系列的Chisel教程。在发布Chisel教程之前,Chisel的学习资料只有官方的在线手册,学习起来晦涩难懂。所以教程被推出后,很快便引起了业内同行的注意,并被转载至十几个不同的技术网站,随后也引来了国内几家著名的集成电路设计公司的关注。 Chisel解决了Verilog的一些痛点。首先,也是最重要的,是在硬件电路设计中引入面向对象的特性。其次,减少了很多不必要的语法,改进了有瑕疵的语法。Verilog的初衷就是用于电路验证,而不是设计,因此存在很多不可综合的语法。在Chisel转换成Verilog的过程中,不会采用这些不可综合的语法,因此编写Chisel时不用担心无法生成电路,对硬件新手而言非常便利。再比如,Verilog的reg不一定指代寄存器,这常常被新手误解,而Chisel的Reg就是寄存器,没有歧义。最后,利用Scala的模式匹配、特质混入、类继承等特性,能够迅速改变电路结构。对于日益庞大的SoC系统,这是非常重要的。 由于目前关于Chisel的详细教程很少,国内外更未有相关的教材出现,为了使我国的集成电路设计教育在扑面而来的敏捷化开发大潮中抢占一席之地,作者决定编写一本介绍Chisel和敏捷硬件开发的教材,为在校本科生、研究生以及从事数字集成电路设计的专业人才讲授该方面的专业知识。 本书从零基础开始,由浅入深,从基础到高级,配合大量实践案例,带领学习者一步一步学会Chisel开发的实用技术。由于Chisel是构建在Scala之上的DSL,因此本教材分两篇:第一篇介绍Chisel语言及敏捷硬件开发知识;第二篇介绍Chisel用到的Scala语言的编程基础知识。读者可以根据自身情况,跳过Scala基础部分,直接学习Chisel的硬件开发功能。 本书提供配套的课后练习参考答案、仿真程序代码等,请登录华信教育资源网(https://www.hxedu.com.cn)注册后下载,也可联系本书编辑(010-88254113,wangxq@phei.com.cn)索取。 本书语言简明扼要、通俗易懂,具有很强的专业性、技术性和实用性。本书第一篇的每一章都附有丰富的习题,供学生课后练习以巩固所学知识。 本书由梁峰、吴斌、张国和、雷冰洁、雷绍充编著。这本书是作者所在的教学科研团队的全体老师和同学们多年的劳动成果,其中研究生肖飞豹、王宗轩、余文麟、成舒婷、曹琪、陈昇杰、郝渊、程才菲、秦海鹏、孙齐伟、王永强、付海生、卞鼐、张洁、杨崟、李佩嵘等做了大量工作,如素材搜集、翻译、整理、录入、程序设计修改和验证等。趁此机会,作者衷心地感谢在编写本书过程中所有给过作者帮助和鼓励的老师与同学们。本书的编写也参考了大量近年来出版的相关技术资料,吸取了许多专家和同人的宝贵经验,在此向他们深表谢意。 作 者 2022年6月
第一篇 敏捷硬件开发语言Chisel 第1章 新型敏捷硬件开发语言——Chisel和Scala 2 1.1 最好的宿主——什么是Scala 2 1.2 敏捷开发——什么是Chisel 2 1.3 Scala入门——让你的代码跑起来 4 1.3.1 Scala的安装方法 4 1.3.2 使用Scala解释器 4 1.3.3 运行Scala脚本 4 1.3.4 编译非脚本文件 5 1.3.5 使用IDEA开发Scala项目 5 1.3.6 总结 7 1.4 章节安排 7 1.5 参考文献 8 1.6 课后练习 8 第2章 Chisel入门及数据类型 9 2.1 Chisel开发环境的安装步骤 9 2.1.1 安装步骤 9 2.1.2 开发环境测试 10 2.1.3 小结 11 2.2 Chisel的常见问题 11 2.3 Chisel的变量与数据类型 12 2.3.1 数据字面量与数据类型 13 2.3.2 数据宽度 14 2.3.3 类型转换 14 2.3.4 向量 15 2.3.5 混合向量 16 2.3.6 包裹 16 2.3.7 Chisel的内建操作符 17 2.3.8 位宽推断 18 2.3.9 Chisel泛型 19 2.4 总结 19 2.5 课后练习 19 第3章 模块与硬件类型 20 3.1 Chisel是如何赋值的 20 3.2 端口(IO) 21 3.2.1 定义端口列表 21 3.2.2 翻转端口列表的方向 21 3.2.3 整体连接 22 3.2.4 动态修改端口 23 3.3 模块 24 3.3.1 模块分类 24 3.3.2 定义模块 26 3.3.3 例化模块 27 3.3.4 例化多个模块 28 3.4 线网 28 3.4.1 Wire 28 3.4.2 WireDefault 29 3.4.3 未驱动的线网 30 3.5 寄存器 31 3.5.1 Reg 31 3.5.2 RegNext 32 3.5.3 RegInit 32 3.5.4 RegEnable 33 3.5.5 ShiftRegister 34 3.5.6 寄存器实例 34 3.5.7 异步寄存器 39 3.6 寄存器组 41 3.7 用when给电路赋值 42 3.8 总结 44 3.9 参考文献 45 3.10 课后练习 46 第4章 Chisel常用的硬件原语 47 4.1 多路选择器 47 4.2 优先编码器 48 4.3 仲裁器 48 4.4 队列 51 4.5 ROM 54 4.6 RAM 55 4.7 带写掩码的RAM 57 4.8 从文件读取数据到RAM 58 4.9 计数器 62 4.10 线性反馈移位寄存器 66 4.11 状态机 70 4.12 总结 73 4.13 参考文献 73 4.14 课后练习 73 第5章 生成Verilog HDL代码与基本测试 74 5.1 生成Verilog HDL代码 74 5.1.1 execute 74 5.1.2 emitVerilog 76 5.2 增加参数的方法 77 5.2.1 Firrtl传递参数 77 5.2.2 给主函数传递参数 78 5.3 编写chisel-iotesters测试 79 5.4 运行chisel-iotesters测试 81 5.5 使用chiseltest进行测试 82 5.6 总结 85 5.7 课后练习 85 第6章 黑盒 86 6.1 例化黑盒 86 6.2 复制Verilog文件 88 6.3 内联Verilog文件 90 6.4 inout端口 91 6.5 总结 92 6.6 课后练习 92 第7章 多时钟域设计 94 7.1 没有隐式端口的模块 94 7.2 定义一个时钟域和复位域 95 7.2.1 withClockAndReset 95 7.2.2 withClock和withReset 96 7.2.3 复位信号的三种类型 103 7.3 使用时钟负沿和低有效的复位信号 103 7.4 示例:异步FIFO 105 7.5 总结 108 7.6 参考文献 108 7.7 课后练习 108 第8章 函数的应用 109 8.1 用函数抽象组合逻辑 109 8.2 用工厂方法简化模块的例化 110 8.3 用Scala的函数简化代码 113 8.4 Chisel的打印函数 114 8.4.1 Scala风格 114 8.4.2 C风格 116 8.5 使用打印函数实例 116 8.6 Chisel的对数函数 119 8.7 与硬件相关的函数 119 8.8 隐式类的应用 122 8.9 递归函数的应用 123 8.10 总结 125 8.11 参考文献 125 8.12 课后练习 125 第9章 其他议题 126 9.1 重命名模块名称 126 9.2 命名规则 127 9.3 重命名模块内信号名称 131 9.3.1 前缀 131 9.3.2 suggestName 132 9.3.3 forceName 133 9.4 参数化的Bundle 135 9.5 FixedPoint 137 9.6 assert 138 9.7 总结 140 9.8 参考文献 140 9.9 课后练习 140 第10章 riscv-mini 141 10.1 riscv-mini简介 141 10.2 数据通路 141 10.3 riscv-mini的子模块 142 10.3.1 ALU模块 142 10.3.2 BrCond模块 143 10.3.3 Cache模块 144 10.3.4 Control模块 145 10.3.5 CSR模块 146 10.3.6 ImmGen模块 147 10.3.7 Instructions模块 147 10.3.8 RegFile模块 148 10.3.9 Datapath模块 148 10.3.10 Core模块 149 10.4 riscv-mini参数化机制 149 10.4.1 Field[T]类 149 10.4.2 View类 150 10.4.3 Parameters类及其伴生对象 151 10.4.4 Config类 152 10.5 参数化机制的应用 152 10.6 总结 157 10.7 参考文献 157 10.8 课后练习 158 第二篇 Scala语言编程基础知识 第11章 Scala的变量及函数 160 11.1 变量定义与基本类型 160 11.1.1 定义一个变量 160 11.1.2 Scala的基本类型 161 11.1.3 小结 164 11.2 函数及其几种形式 164 11.2.1 定义一个函数 164 11.2.2 方法 166 11.2.3 嵌套函数 166 11.2.4 函数字面量 166 11.2.5 部分应用函数 167 11.2.6 闭包 168 11.2.7 函数的特殊调用形式 169 11.2.8 柯里化 170 11.2.9 传名参数 170 11.2.10 小结 172 11.3 参考文献 172 第12章 Scala面向对象编程 173 12.1 类和对象 173 12.1.1 类 173 12.1.2 类的构造方法 174 12.1.3 重写toString方法 175 12.1.4 方法重载 176 12.1.5 类参数 176 12.1.6 单例对象与伴生对象 176 12.1.7 工厂对象与工厂方法 178 12.1.8 apply方法 178 12.1.9 主函数 179 12.1.10 小结 180 12.2 操作符即方法 180 12.2.1 操作符在Scala中的解释 180 12.2.2 三种操作符 181 12.2.3 操作符的优先级和结合性 183 12.2.4 预设操作符 184 12.2.5 对象的相等性 184 12.2.6 小结 185 12.3 类继承 185 12.3.1 Scala的类继承 185 12.3.2 调用超类的构造方法 186 12.3.3 重写超类的成员 187 12.3.4 子类型多态与动态绑定 189 12.3.5 抽象类 189 12.3.6 关于多重继承 190 12.3.7 Scala类的层次结构 190 12.3.8 小结 192 12.4 特质 192 12.4.1 什么是特质 192 12.4.2 特质的层次 193 12.4.3 混入特质的简便方法 194 12.4.4 特质的线性化叠加计算 194 12.4.5 小结 197 12.5 参考文献 197 第13章 包和导入 198 13.1 包 198 13.2 包的层次和精确代码访问 198 13.3 import导入 201 13.4 自引用 201 13.5 访问修饰符 202 13.6 包对象 203 13.7 总结 203 13.8 参考文献 203 第14章 集合 204 14.1 数组 204 14.1.1 数组的定义 204 14.1.2 数组的索引与元素修改 204 14.2 列表 205 14.2.1 列表的定义 205 14.2.2 列表添加数据 205 14.2.3 列表子对象Nil 206 14.3 数组缓冲与列表缓冲 207 14.4 元组 208 14.4.1 元组的定义 208 14.4.2 元组的索引 208 14.4.3 元组作为函数的入口参数 209 14.4.4 元组的遍历 209 14.5 映射 209 14.5.1 映射的定义 209 14.5.2 映射的三种取值方式 210 14.5.3 映射遍历的四种方式 210 14.6 集 211 14.7 序列 211 14.8 集合的常用方法 211 14.8.1 map 211 14.8.2 foreach 211 14.8.3 zip 212 14.8.4 reduce 212 14.8.5 fold 212 14.8.6 scan 212 14.9 总结 212 第15章 内建控制结构 213 15.1 if表达式 213 15.2 while循环 214 15.3 for表达式与for循环 214 15.4 用try表达式处理异常 216 15.4.1 抛出一个异常 216 15.4.2 try-catch 217 15.4.3 finally 217 15.5 match表达式 217 15.6 关于continue和break 218 15.7 关于变量的作用域 219 15.8 总结 220 15.9 参考文献 220 第16章 模式匹配 221 16.1 样例类和对象 221 16.2 模式匹配 222 16.3 模式的种类 222 16.3.1 通配模式 222 16.3.2 常量模式 223 16.3.3 变量模式 223 16.3.4 构造方法模式 224 16.3.5 序列模式 225 16.3.6 元组模式 225 16.3.7 带类型的模式 226 16.3.8 变量绑定模式 226 16.4 模式守卫 226 16.5 密封类 227 16.6 可选值 228 16.7 模式匹配的另类用法 228 16.8 偏函数 229 16.9 总结 230 16.10 参考文献 230 第17章 类型参数化 231 17.1 var类型的字段 231 17.2 类型构造器 232 17.3 型变注解 233 17.4 检查型变注解 233 17.5 类型构造器的继承关系 235 17.6 上界和下界 235 17.7 方法的类型参数 236 17.8 对象私有数据 236 17.9 总结 238 第18章 抽象成员 239 18.1 抽象成员 239 18.2 初始化抽象val字段 240 18.2.1 预初始化字段 241 18.2.2 惰性的val字段 242 18.3 抽象类型 243 18.4 细化类型 244 18.5 Scala的枚举 245 18.6 总结 246 18.7 参考文献 246 第19章 隐式转换与隐式参数 247 19.1 隐式定义的规则 247 19.2 隐式地转换到期望类型 248 19.3 隐式地转换接收端 248 19.4 隐式类 249 19.5 隐式参数 250 19.6 含有隐式参数的主构造方法 251 19.7 上下文界定 252 19.8 多个匹配的隐式定义 253 19.9 总结 255 19.10 参考文献 255