敏捷硬件开发语言Chisel与数字系统设计(第2版) 课后练习参考答案 第1-5章_第1页
敏捷硬件开发语言Chisel与数字系统设计(第2版) 课后练习参考答案 第1-5章_第2页
敏捷硬件开发语言Chisel与数字系统设计(第2版) 课后练习参考答案 第1-5章_第3页
敏捷硬件开发语言Chisel与数字系统设计(第2版) 课后练习参考答案 第1-5章_第4页
敏捷硬件开发语言Chisel与数字系统设计(第2版) 课后练习参考答案 第1-5章_第5页
已阅读5页,还剩19页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

敏捷硬件开发语言Chisel与数字系统设计(第2版)课后练习参考答案西安交通大学微电子学院

目录TOC\o"1-3"\h\u第1章新型敏捷硬件开发语言——Chisel和Scala 31.1课后练习 3第2章Chisel入门与Scala变量函数基础 52.1课后练习 5第3章Chisel数据类型与Scala集合应用 83.1课后练习 8第4章Chisel硬件设计中的操作、控制与分层 114.1课后练习 11第5章Chisel中的面向对象设计与类型系统解析 155.1课后练习 15第6章Chisel硬件设计原语与高级生成器 186.1课后练习 18第7章Chisel设计的生成、优化与测试 217.1课后练习 21第8章黑盒与多时钟域设计 248.1课后练习 24第9章Scala隐式机制与Chisel函数抽象 279.1课后练习 27第10章Chisel工程化开发规范与版本迁移 3110.1课后练习 31第11章riscv-mini 3411.1课后练习 34第12章Chisel标准库深入与常用设计模式 3512.1课后练习 35新型敏捷硬件开发语言——Chisel和Scala课后练习1.书中提到Chisel是“嵌入在Scala中的语言”。请思考:当我们编写一个Chisel程序(例如`my_design.scala`)并在终端运行它时,计算机的CPU实际上是在直接执行“硬件电路逻辑”,还是在执行“Scala代码”?如果是后者,那么“硬件电路”是在什么时候产生的?答:计算机实际上是在执行Scala代码。当我们运行程序时,Scala代码在JVM上运行,它的执行过程就是“构建电路图”的过程。硬件电路(即SystemVerilog代码)是在Scala代码运行结束后的输出结果。只有当这些生成的代码被放入仿真器或下载到FPGA时,硬件逻辑才算真正开始“工作”。2.第1章提到Scala是基于JVM(Java虚拟机)运行的。请问:最终生成的SystemVerilog文件(`.sv`)内是否包含Java字节码?如果我们将生成的SystemVerilog文件交给晶圆厂去流片生产芯片,这颗芯片在通电工作时,还需要JVM的支持吗?为什么?答:(1).不包含。生成的`.sv`文件是纯文本的硬件描述代码,不包含任何Java字节码。(2).不需要。芯片(或FPGA)是硬件实体,直接根据物理电路规则运行。JVM只是用来运行“生成器”(Chisel编译器)的工具。一旦电路图(SystemVerilog)生成完毕,JVM的任务就完成了,与后续芯片的生产和运行无关。3.在Scala语法中,`val`定义的是不可变变量(Immutable)。请根据文中“Scala是硬件构建语言”的描述推断:当我们用`vala=...`来定义一个硬件组件时,这里的“不可变”是指“这个硬件组件里的电压/数值永远不能变”,还是指“变量`a`永远指向这个特定的硬件组件,不能再指向别的组件”?答:是指“变量`a`永远指向这个特定的硬件组件”(即引用的不可变性)。硬件组件内部的数值(电压)当然是随时间变化的,但`vala`保证了我们在Scala代码中提到的`a`始终代表同一个硬件对象,不会在代码运行中途突然变成代表另一个线网,这有助于构建稳定的电路连接结构。4.书中强调Chisel是“硬件构建语言”(HardwareConstructionLanguage),而Verilog是“硬件描述语言”。请举例说明:如果我们要设计一个包含100个重复模块的电路。使用“描述语言”(Verilog)通常需要怎么做?(提示:是否需要写很多重复代码?)使用“构建语言”(Chisel/Scala)可以利用Scala的什么特性(如循环、函数)来“构建”这100个模块?这体现了Chisel的什么优势?答:Verilog(描述):通常需要手动编写(实例化)100次模块,或者使用复杂的`generate`语法,代码量大且不易维护。Chisel(构建):可以利用Scala的`for`循环。写一个循环100次的Scala代码,每次循环调用一次模块创建函数。Scala运行完这个循环,就会自动在电路中“构建”出100个模块。这体现了Chisel“用软件算法生成硬件结构”的高效率和参数化优势。5.根据1.2节所述,Chisel7.3引入了CIRCT项目和`firtool`工具。请简述:从我们编写的`.scala`源代码,到最终生成工业标准的`.sv`(SystemVerilog)文件,中间经历了哪些主要步骤?为什么要引入`firtool`而不是直接由Scala打印出Verilog?答:(1).路径:`.scala`源代码->(Scala编译器运行)->内存中的电路图->FIRRTL/CIRCT中间表示(IR)->(`firtool`编译)->`.sv`SystemVerilog文件。(2).原因:`firtool`基于LLVM/MLIR架构,能够对中间表示(IR)进行强大的全局优化(如死代码消除、常数传播),并统一处理SystemVerilog的语法细节,确保生成的代码符合工业标准,且对仿真器(如Verilator)友好。6.Scala语言中有`if-else`语句和`for`循环语句。请根据“生成器”的概念推断:如果在Chisel代码的`for(i<-0until4)`循环中定义了硬件逻辑,那么生成的电路中会包含一个“正在循环执行的计数器”吗?还是会并行地生成4份同样的硬件结构?答:会并行地生成4份同样的硬件结构。Scala的`for`循环是在“构建阶段”运行的。它执行了4次“放置硬件”的操作,因此结果是空间上的展开(Unrolling),而不是时间上的轮询。这与软件编程中的循环执行有本质区别。7.书中引用了Java之父JamesGosling对Scala的评价,并提到Scala可以无缝调用Java类库。请思考:在Chisel的“构建阶段”(即生成电路图的时候),我们能否使用Scala/Java的数学库(如`Math.sin`)来预先计算好一个正弦波查找表(ROM)的初始值?这对硬件设计带来了什么便利?答:可以。因为这些计算是在“构建阶段”由电脑CPU完成的。我们可以利用强大的Scala/Java库算出复杂的常数表,然后将这些结果直接硬编码(Hardcode)进生成的电路中(作为ROM或复位值)。这意味着硬件本身不需要具备计算`Math.sin`的能力,却能拥有正弦波数据,极大地简化了硬件设计流程。8.1.1节提到Scala是一门“面向对象”的语言。Verilog虽然也有模块(Module),但缺乏继承、多态等特性。请结合文中RISC-V的案例思考:如果我们要设计一系列处理器(有的带浮点运算单元,有的不带,有的带缓存,有的不带),使用Scala的“类继承”特性相比于传统Verilog的“宏定义(`ifdef`)”或“复制粘贴”有什么潜在优势?答:使用Scala的继承(Inheritance)和特质(Trait),我们可以定义一个基础处理器类,然后通过继承派生出“浮点版”、“缓存版”等子类。子类自动拥有父类的功能,只需补充差异部分。相比于Verilog的`ifdef`(代码充满了预处理指令,难以阅读)或复制粘贴(维护困难,改一个Bug要改多处),Chisel的面向对象方式使得硬件代码具有更好的层次结构、可维护性和复用性。

Chisel入门与Scala变量函数基础课后练习1.请阅读以下Scala代码片段,分析并回答:最后打印的`result`变量的值是多少?并解释为什么`x`的值发生了变化(或没有发生变化)。```scalavalx=10valresult={valx=20x+5}+xprintln(result)```答:值:35。解析:1.外层的valx=10定义了x。2.花括号{}创建了一个新的作用域。在内部,valx=20遮蔽(shadow)了外层的x。3.代码块内的x+5使用的是内部的x(20),结果为25。result接收这个块的返回值。4.块外部的+x使用的是外层的x(10)。5.所以计算过程是25+10=35。2.在Chisel硬件生成中,我们通常使用`val`来定义硬件组件。请分析以下代码是否合法?如果合法,说明`m`是`val`定义的,为什么还能修改其内容?```scalaimportscala.collection.mutable.ArrayBuffervalm=ArrayBuffer(1,2)m+=3//m=ArrayBuffer(1,2,3,4)//假设下一行试图重新赋值```如果在最后一行解开注释,试图执行`m=ArrayBuffer(1,2,3,4)`,会发生什么?这对于理解硬件模块中的`valwire=Wire(...)`有什么启发?答:合法性:m+=3是合法的,但m=...是非法的。解析:val保证的是引用不可变。即m始终指向同一个ArrayBuffer对象,不能让m去指向另一个对象(所以解开注释会报错)。但是,ArrayBuffer对象本身是可变的(Mutable),我们通过引用修改了对象内部的数据。硬件启发:在Chisel中,valwire=Wire(...)表示wire这个变量名永远指向那根特定的硬件连线,你不能让wire这个名字突然去代表另一根线。但是,这根线上的信号值(电压)是随时间变化的。3.Scala允许使用下划线`_`简化函数字面量。请将以下标准的函数字面量定义改写为使用下划线的精简形式。```scala//原始形式valadd=(a:Int,b:Int)=>a+b```如果改写为`valf=_+_`,编译器会报错吗?为什么?应该如何正确地使用下划线并声明类型?答:改写:valf=(_:Int)+(_:Int)。报错原因:如果只写valf=_+_,Scala编译器会报错,因为无法推断这两个占位符的类型。正确用法:必须在占位符后显式声明类型,如(_:Int),或者显式声明变量f的类型:valf:(Int,Int)=>Int=_+_。4.第2章介绍了柯里化和传名参数。假设我们定义了一个函数`defrepeat(n:Int)(block:=>Unit)`。请问:(1)这个函数的参数列表有几组?(2)`block`参数前面的`=>`符号代表什么特性?(3)在调用时,为什么我们可以写成类似`repeat(3){println("Hi")}`的形式,这种写法对Chisel自定义硬件生成逻辑(如自定义循环生成器)有何意义?答:(1)两组。这是柯里化(Curried)函数。(2)传名参数(By-nameparameter)。表示block在传入时不会立即求值,而是在函数体内部每次调用时才求值。(3)这种写法让函数调用看起来像语言内置的控制结构(如if或while)。在Chisel中,这允许我们创建自定义的硬件生成逻辑(DSL),让代码更具可读性和表达力。5.Chisel7.3引入了现代化的编译流程。请按照顺序排列以下编译阶段:A.SystemVerilog(.sv)B.FIRRTL(FlexibleIntermediateRepresentation)C.Chisel源码(.scala)D.CHIRRTL(ChiselHighFIRRTL)E.LowFIRRTL回答:`firtool`工具是在哪两个阶段之间工作的?它的核心作用是什么?答:顺序:C(Scala)->D(CHIRRTL)->B(FIRRTL)->E(LowFIRRTL)->A(SystemVerilog)。firtool位置:工作在E(LowFIRRTL)和A(SystemVerilog)之间(实际上它接管了从FIRRTL到SystemVerilog的大部分后端工作)。核心作用:将硬件中间表示(IR)编译成高质量的、符合工业标准的SystemVerilog代码,并执行多种优化(如死代码消除、常量传播)。6.在Scala中,`vala=10`和`valb=10.U`(注意:`.U`是Chisel提供的扩展,虽然本章未深讲,但提到了Scala基础类型)。请根据2.1.2节“Scala的基本类型体系”判断:变量`a`的类型是什么?变量`a`是在编译阶段存在的数值,还是在电路运行阶段存在的寄存器?这与SystemVerilog中的`parameter`有相似之处吗?答:类型:a的类型是Scala的Int(32位有符号整数)。存在阶段:a存在于编译阶段(Scala运行阶段)。它不是硬件电路的一部分,而是生成电路时的参数或常量。相似性:是的,它非常类似于SystemVerilog中的parameter或localparam,用于在生成硬件时控制位宽、循环次数或常量值。7.SystemVerilog支持0,1,x,z四种状态,而Chisel的核心模型主要支持0和1。如果你的设计需要通过一个双向IO端口(inout)去控制一个外部的I2C总线设备(需要处理高阻态z),根据2.3.6节的内容,你应该使用Chisel的什么机制来实现?直接在Module内部写`io.out:="bz"`合法吗?答:机制:必须使用BlackBox。合法性:不合法。Chisel的原生类型(UInt/SInt/Bool)只支持二态逻辑(0/1),无法表达高阻态z。必须通过BlackBox封装一段Verilog代码(如assignout=enable?in:8'bz;)来实现三态门,并在Chisel中实例化这个BlackBox。8.在开发阶段和最终发布阶段,我们可能会使用不同的`firtoolOpts`。请问:(1)选项`-disable-all-randomization`的作用是什么?为什么在回归测试(RegressionTest)中通常建议开启它?(2)选项`-O=3`代表什么?为什么在调试阶段(Debug)可能不建议使用该级别?答:(1)作用:禁止仿真时寄存器的随机初始化值(通常寄存器上电值为随机的X)。原因:开启后可以确保每次仿真运行的结果是确定性的(Deterministic),避免因随机初值不同导致有时候Pass有时候Fail,便于回归测试比对结果。(2)含义:代表最高级别的编译优化(激进优化)。不建议原因:激进的优化可能会重命名信号、合并逻辑或消除中间连线,导致生成的SystemVerilog代码与源代码对应关系变差,波形难以阅读,从而增加调试难度。

Chisel数据类型与Scala集合应用课后练习1.在编写一个通用的硬件多路选择器函数时,我们希望它既能切换UInt类型的信号,也能切换SInt甚至Bundle类型的信号。请问:该函数的输入参数类型应该声明为UInt、Bits还是Data?为什么?如果声明为Bits,会对传入的信号有什么限制?答:应该声明为Data。原因:Data是所有Chisel硬件类型的根基类,包括UInt、SInt以及复合类型Bundle和Vec。声明为Data可以让多路选择器支持任意硬件类型。Bits限制:如果声明为Bits,则输入参数只能是UInt或SInt等位向量类型,无法支持Bundle或Vec这样复杂的聚合类型。2.阅读以下代码:vala=2.U(2.W)//二进制10valb=a.asBoolvalc=a(0)vald=(a===1.U)请分析变量b、c、d的值(即它们代表的硬件信号逻辑值是true还是false),并解释为什么b的结果可能违反直觉。答:b(false):a.asBool在Chisel中通常只取最低位(LSB)。2.U是二进制10,最低位是0,所以结果为false。这是一个常见的陷阱,不要用asBool来判断非零。c(false):a(0)提取第0位,即0,结果为false。d(false):2.U===1.U显然不成立。修正:如果想判断a是否为真(非零),应使用a=/=0.U。3.假设我们有以下两段代码:代码A:valvec=VecInit(10.U,20.U,30.U,40.U)validx=Wire(UInt(2.W))idx:=1.Uvalout=vec(idx)代码B:valarr=Array(10.U,20.U,30.U,40.U)validx=1//ScalaIntvalout=arr(idx)请问:哪一段代码会生成硬件多路选择器(MUX)?另一段代码在生成的Verilog中会表现为什么形式?答:代码A会生成硬件多路选择器。因为idx是硬件信号(UInt),电路必须在运行时根据idx的电平动态选择输出,这需要MUX电路。代码B不会生成MUX。因为idx是Scala的常量1,在编译阶段(Elaborationtime)Chisel就已经知道你要取第1个元素(20.U)。生成的Verilog中,out会直接连接到20.U对应的常量或连线上,没有任何选择逻辑。4.给定两个信号vala=Wire(UInt(4.W))和valb=Wire(UInt(4.W))。请计算以下操作结果的位宽:(1)a+b(2)a+&b(3)a+%b(4)Mux(cond,a,b)如果a的值为15,b的值为1,上述三个加法操作的结果数值分别是多少?答:位宽:(1)a+b:5位(max(4,4)+1)。(2)a+&b:5位(同普通加法,保留进位)。(3)a+%b:4位(环绕加法,结果截断为操作数最大位宽)。(4)Mux:4位(取最大分支位宽)。数值(a=15,b=1):(1)16(10000,5位)。(2)16(10000,5位)。(3)0(0000,4位,16对16取模)。5.定义如下接口:classMyInterfaceextendsBundle{valreq=Input(Bool())valack=Output(Bool())}如果我们在模块端口定义中使用valio=IO(Flipped(newMyInterface)),请问io.req和io.ack的方向分别变成了什么?这种机制在连接“主设备(Master)”和“从设备(Slave)”时有什么作用?答:方向:io.req变为Output,io.ack变为Input。作用:Flipped能够自动翻转Bundle内所有字段的方向。在连接主从设备时,我们只需要定义一套接口(通常以Master视角定义),Slave端直接使用Flipped(newMyInterface)即可实现端口的完美对接,无需手动重新定义一个方向相反的Bundle。6.Vec要求所有元素类型相同。如果我们需要创建一个数组,第一个元素是UInt(8.W),第二个元素是UInt(16.W),第三个元素是Bool(),应该使用什么数据结构?如果我们想用一个硬件索引信号idx去动态访问这个数组(例如array(idx)),Chisel允许这样做吗?为什么?答:数据结构:应使用MixedVec。动态索引:不允许直接使用硬件索引idx访问MixedVec。原因:MixedVec的元素类型(位宽)不同。如果允许动态索引,返回值的类型(位宽)在硬件生成时是不确定的(取决于运行时idx的值),这违背了硬件静态类型的原则。必须使用Mux树或者将所有元素填充(Pad)到相同位宽后转为普通Vec才能进行动态索引。7.假设有一个Scala列表valtaps=List(1,2,4,8),代表滤波器的系数。我们有一个输入信号in。请写出一行Chisel代码,利用Scala的map和reduce(或foldLeft)函数,计算输入信号与这些系数乘积的和(即计算∑ (答:代码:valsum=taps.map(c=>in*c.U).reduce(_+_)//或者valsum=taps.map(c=>in*c.U).foldLeft(0.U)(_+_)优势:体现了Scala的元编程(Meta-programming)能力。我们用一行代码就生成了一个包含多个乘法器和加法器的“乘加运算树”结构,且系数是参数化的。如果用Verilog,通常需要手写多个assign语句或复杂的generate块。8.分析以下表达式的求值逻辑:valres=a&b===0.U根据Chisel的操作符优先级,这个表达式是先计算a&b还是先计算b===0.U?如果设计者的初衷是“判断a和b按位与的结果是否为0”,这个表达式正确吗?如果不正确,应该如何修改?答:求值顺序:根据Scala/Chisel优先级,===(相等比较)的优先级高于&(位与)。所以表达式会被解析为a&(b===0.U)。正确性:不正确。这会先判断b是否等于0,得到一个Bool,然后试图将a与这个Bool进行位与,这通常会导致类型错误或逻辑错误。修改:必须加括号。(a&b)===0.U。

Chisel硬件设计中的操作、控制与分层课后练习1.请分析以下两段代码,指出哪一段代码无法生成有效的硬件计数器,并解释原因(从“硬件生成期”与“电路运行时”的角度)。代码A:valcnt=RegInit(0.U(8.W))when(io.en){cnt:=cnt+1.U}代码B:varcnt=0.U(8.W)while(cnt<10.U){//试图用while循环生成计数逻辑cnt:=cnt+1.U}答:代码B无法生成有效计数器。原因:while是Scala的控制结构,运行在硬件生成期。cnt<10.U是一个涉及硬件节点比较的表达式,它返回的是一个Bool类型的对象,而不是Scala的Boolean值。Scala无法判断while的条件(或者会抛出类型错误)。即使语法通过,这也是试图在编译时展开循环,而不是生成运行时的时序逻辑。代码A正确:使用了RegInit和when,这是标准的Chisel时序逻辑描述,会生成带使能的寄存器电路。2.阅读以下代码,分析生成的电路是否存在问题?如果存在问题,Chisel编译器会报什么错误?如何修改才能正确生成一个当sel为false时输出0的组合逻辑电路?valout=Wire(UInt(8.W))when(io.sel){out:=io.in}io.out:=out答:问题:Wire未完全初始化(Notfullyinitialized)。错误:Chisel编译器会报错,提示out在io.sel为false的路径上没有被赋值。修改:when(io.sel){out:=io.in}.otherwise{out:=0.U//补充默认路径}//或者使用WireDefault(0.U(8.W))3.假设我们有一个参数useAdder:Boolean(Scala变量)和一个硬件信号enable:Bool(Chisel信号)。请写出一段代码:(1)当useAdder为true时,生成一个加法器;否则生成一个减法器。(这是生成期的结构选择)(2)在生成的运算器内部,当硬件信号enable为高电平时输出运算结果,否则输出0。(这是运行时的逻辑控制)答://(1)生成期选择结构valresult=if(useAdder){io.a+io.b}else{io.a-io.b}//(2)运行时逻辑控制when(io.enable){io.out:=result}.otherwise{io.out:=0.U}4.给定输入信号in在时钟周期0,1,2,3的值分别为10,20,30,40。请问以下信号out在时钟周期2的输出值是多少?(假设寄存器无复位或已复位为0)vald1=RegNext(io.in)vald2=RegNext(d1)valout=d2答:输出值:10。解析:d1延迟1周期,d2延迟2周期(相对于io.in)。Cycle0:in=10Cycle1:d1=10(in@0)Cycle2:d2=10(d1@1)所以Cycle2时,out输出的是Cycle0的输入值。5.我们需要设计一个指令译码器,根据2位操作码opcode(UInt类型)选择输出结果。为什么不能直接使用Scala的match语句(如下所示)来实现这个运行时的硬件逻辑?//错误示范valres=io.opcodematch{case0.U=>io.a+io.bcase1.U=>io.a-io.b//...}应该使用Chisel的什么工具函数来替代?答:原因:Scala的match是在编译期执行的。io.opcode是一个硬件类型的对象(UInt),它的值在编译期是未知的(它只有在电路运行时才有电压值)。Scala无法在编译时匹配这个硬件对象的值。替代工具:应使用MuxLookup(或MuxCase、嵌套when)。例如:io.out:=MuxLookup(io.opcode,0.U)(Seq(0.U->(io.a+io.b),1.U->(io.a-io.b)))6.定义接口classMyInterfaceextendsBundle{valreq=Input(Bool());valack=Output(Bool())}。在顶层模块中,我们实例化了两个子模块:Producer(生产者)和Consumer(消费者)。Producer的端口定义为valio=IO(newMyInterface)(即req是输入,ack是输出)。为了让Producer和Consumer能够直接对接(producer.io<>consumer.io),Consumer的端口应该如何定义?请写出代码。答:定义:valio=IO(Flipped(newMyInterface))。解析:MyInterface定义了req为Input,ack为Output。Producer使用原样接口。为了对接,Consumer必须作为“从设备”或“响应端”,其端口方向必须与MyInterface相反(req变Output,ack变Input)。Flipped函数正是用来翻转整个Bundle方向的。7.在4.3节介绍的分层设计中,为什么强调“层间仅通过固定接口交互,隐藏内部实现细节”?如果我们在“处理层(ProcessingLayer)”中直接访问“接口层(InterfaceLayer)”内部定义的私有Wire信号(例如ifLernalWire),这违反了什么设计原则?在Chisel编译或生成电路时会带来什么潜在风险?答:原则:违反了模块封装性(Encapsulation)和层间解耦原则。风险:1.可维护性差:如果接口层内部实现修改(重命名或删除了internalWire),处理层的代码会直接报错。2.生成错误:在Chisel中,跨模块直接访问私有Wire(未通过IO端口)通常是非法的,或者生成的Verilog层次结构会非常混乱(信号由顶层穿透),导致综合后的网表难以对应原设计。8.参考4.3.4节的“剪枝机制”。假设我们有一个配置参数hasDebug:Boolean=false。代码如下:valdebugLogic=if(hasDebug){valmon=Module(newPerformanceMonitor())mon.io.en:=true.BSome(mon.io.out)}else{None}请问:当hasDebug为false时,PerformanceMonitor模块会被实例化吗?生成的Verilog代码中会包含性能监控器的逻辑吗?这体现了Chisel作为硬件生成器的什么优势?答:是否实例化:不会。Verilog内容:生成的Verilog完全不包含性能监控器的逻辑。优势:体现了零成本抽象和生成器的灵活性。未被选中的Scala代码分支根本不会被执行,因此不会向电路图中添加任何多余的节点。这比Verilog的ifdef更加强大和安全,因为它是基于图灵完备的语言进行构建控制的。

Chisel中的面向对象设计与类型系统解析课后练习1.在Scala中,多态(Polymorphism)通常指运行时根据对象类型调用不同方法。请分析:在Chisel中,如果我们定义了一个泛型模块classALU[T<:Data](gen:T),并分别实例化Module(newALU(UInt(8.W)))和Module(newALU(SInt(8.W)))。(1)这属于“生成期多态”还是“运行时多态”?(2)生成的Verilog代码中,这两个ALU模块是同一个module还是两个不同的module?答:(1)生成期多态。Scala编译器在编译/运行Chisel代码生成电路时,根据传入的类型参数T(UInt或SInt)执行了不同的生成逻辑。(2)两个不同的module。因为它们的内部逻辑(如有符号运算vs无符号运算)生成的电路结构不同,Chisel/Firtool会将其生成为两个名字不同的Verilog模块(例如ALU_UInt和ALU_SInt)。2.定义接口:classHandshakeextendsBundle{valvalid=Output(Bool())valready=Input(Bool())}现在有两个模块Producer和Consumer。Producer的端口定义为valio=IO(newHandshake)。为了让Consumer能直接与Producer连接(producer.io<>consumer.io),Consumer的端口应该如何定义?A.valio=IO(newHandshake)B.valio=IO(Flipped(newHandshake))C.valio=IO(Input(newHandshake))请选择正确答案并解释原因。答:B.valio=IO(Flipped(newHandshake))。原因:Handshake定义了valid为Output(输出),ready为Input(输入)。这通常是“生产者”视角。Consumer作为“消费者”,其端口方向必须相反(valid变Input,ready变Output)。Flipped函数正是用来翻转整个Bundle方向的。3.假设我们有一个复杂的滤波器模块Filter,其构造参数非常多。我们希望提供一个简单的APIFilter.default(width:Int)来快速创建默认配置的滤波器。请写出Filter的伴生对象定义,实现这个default工厂方法。该方法应返回一个Filter模块的新实例。答:objectFilter{defdefault(width:Int):Filter={//假设Filter类构造函数需要更多参数,这里填入默认值newFilter(width,taps=4,coefficients=Seq(1,2,1,0))}}//使用:valf=Module(Filter.default(8))4.我们需要设计一个通用的路由器(Router)接口,它有n个输入端口和m个输出端口,每个端口的数据位宽为w。请定义一个参数化的Bundle类RouterIO(n:Int,m:Int,w:Int),内部使用Vec来定义输入和输出端口数组。答:classRouterIO(n:Int,m:Int,w:Int)extendsBundle{valin=Input(Vec(n,UInt(w.W)))valout=Output(Vec(m,UInt(w.W)))}//注意:Vec放在Input/Output内部,或者Input(Vec(...))均可,//但标准写法通常是valin=Input(Vec(n,UInt(w.W)))5.我们定义了一个特质HasDebugOutput,旨在为模块添加一个额外的调试端口:traitHasDebugOutput{this:Module=>valdebug=IO(Output(UInt(32.W)))

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论