# chisel **Repository Path**: nicelusanjin/chisel ## Basic Information - **Project Name**: chisel - **Description**: chisel learning notes - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-05-04 - **Last Updated**: 2025-05-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: Notes, Languages ## README # Chisel Bootcamp ## 0. Chisel Demo ```scala // Generalized FIR filter parameterized by the convolution coefficients class FirFilter(bitWidth: Int, coeffs: Seq[UInt]) extends Module { val io = IO(new Bundle { val in = Input(UInt(bitWidth.W)) val out = Output(UInt()) }) // Create the serial-in, parallel-out shift register val zs = Reg(Vec(coeffs.length, UInt(bitWidth.W))) zs(0) := io.in for (i <- 1 until coeffs.length) { zs(i) := zs(i-1) } // Do the multiplies val products = VecInit.tabulate(coeffs.length)(i => zs(i) * coeffs(i)) // Sum up the products io.out := products.reduce(_ +& _) } ``` ## 2.1 First Module val:不可以被重新赋值 var:可以被重新赋值 Module示例: ```scala class Passthrough extends Module { val io = IO(new Bundle { val in = Input(UInt(4.W)) val out = Output(UInt(4.W)) }) io.out := io.in } ``` 匿名Bundle子类: ```scala new Bundle { val in = Input(UInt(4.W)) val out = Output(UInt(4.W)) } ``` 也可以自定义类继承Bundle类 * 统一Input、Output方向 ```scala class Packet extends Bundle { val header = UInt(16.W) val addr = UInt(16.W) val data = UInt(32.W) } class MyModule extends Module { val inPacket = IO(Input(new Packet)) val outPacket = IO(Output(new Packet)) val reg = Reg(new Packet) reg := inPacket outPacket := reg } ``` * 不统一的方向 ```scala class MyBundle extends Bundle { val in = Input(UInt(4.W)) val out = Output(UInt(4.W)) } class Passthrough extends Module { val io = IO(new MyBundle) io.out := io.in } ``` 带参数的生成器: ```scala class PassthroughGenerator(width: Int) extends Module { val io = IO(new Bundle { val in = Input(UInt(width.W)) val out = Output(UInt(width.W)) }) io.out := io.in } ``` > 在主构造器中不带val/var的变量是局部变量,只能在类中使用,而且默认是不可改变的 数字信号类型与值 ```scala UInt(4.W), SInt(4.W) //这是类型 3.U(4.W), -2.S(4.W) //这是值 ``` printf可以打印模拟时的状态 ## 2.2 Combine Logic * 如果中间值可以复用,则使用Wire,否则直接赋值 ```scala //可以复用 val sqaure = Wire(UInt(4.W)) sqaure := io.x * io.x //Wire类型 //直接赋值 val sum = io.x * io.y //UInt类型 ``` * := 左侧可以是Wire,Register,Output ## 2.3 Control Flow * 普通数字判断相等用==,UInt,SInt信号判断相等用\=\== * 多个驱动语句驱动同一个信号,只有最后一个生效 * 选择器: ```scala when(someBooleanCondition) { // things to do when true }.elsewhen(someOtherBooleanCondition) { // things to do on this condition }.otherwise { // things to do if none of th boolean conditions are true } ``` * 状态机 ![](images/fsm.png) ```scala // life gets hard-er class GradLife extends Module { val io = IO(new Bundle { val state = Input(UInt(2.W)) val coffee = Input(Bool()) val idea = Input(Bool()) val pressure = Input(Bool()) val nextState = Output(UInt(2.W)) }) val idle :: coding :: writing :: grad :: Nil = Enum(4) //每个Enum可以看做UInt使用 io.nextState := idle when(io.state === idle){ when(io.coffee) {io.nextState := coding} .elsewhen(io.idea) {io.nextState := idle} .elsewhen(io.pressure) {io.nextState := writing} }.elsewhen(io.state === coding){ when(io.coffee) {io.nextState := coding} .elsewhen(io.idea || io.pressure) {io.nextState := writing} }.elsewhen(io.state === writing) { when(io.coffee || io.idea) {io.nextState := writing} .elsewhen(io.pressure) {io.nextState := grad} } } // Test test(new GradLife) { c => // verify that the hardware matches the golden model for (state <- 0 to 3) { for (coffee <- List(true, false)) { for (idea <- List(true, false)) { for (pressure <- List(true, false)) { c.io.state.poke(state.U) c.io.coffee.poke(coffee.B) c.io.idea.poke(idea.B) c.io.pressure.poke(pressure.B) c.io.nextState.expect(gradLife(state, coffee, idea, pressure).U) } } } } } println("SUCCESS!!") // Scala Code: if we get here, our tests passed! ``` ## 2.4 Sequential Logic ### Registers 任何Module都会隐式存在一个clock和reset成员。 ```scala class RegisterModule extends Module { val io = IO(new Bundle { val in = Input(UInt(12.W)) val out = Output(UInt(12.W)) }) val register = Reg(UInt(12.W)) //随机初始化 register := io.in + 1.U io.out := register } test(new RegisterModule) { c => for (i <- 0 until 100) { c.io.in.poke(i.U) c.clock.step(1) c.io.out.expect((i + 1).U) } } println("SUCCESS!!") ``` `step(1)`表示时钟周期+1,状态部件更新状态。 组合逻辑不需要用到`step`,因为在`poke()`的瞬间就会传导到输出。 > One important note is that Chisel distinguishes between types (like `UInt`) and hardware nodes (like the literal `2.U`, or the output of `myReg`). While > ```scala > val myReg = Reg(UInt(2.W)) > ``` > is legal because a Reg needs a data type as a model, > ```scala > val myReg = Reg(2.U) > ``` > is an error because `2.U` is already a hardware node and can't be used as a model. #### RegNext 对于简单的寄存器,可以不用指示它存储的类型,可以RegNext直接通过输出推断类型的位宽 ```scala class RegNextModule extends Module { val io = IO(new Bundle { val in = Input(UInt(12.W)) val out = Output(UInt(12.W)) }) // register bitwidth is inferred from io.out io.out := RegNext(io.in + 1.U) //随机初始化 } test(new RegNextModule) { c => for (i <- 0 until 100) { c.io.in.poke(i.U) c.clock.step(1) c.io.out.expect((i + 1).U) } } println("SUCCESS!!") ``` #### RegInit 具有指定的初始值,当reset有效(高电平)时(同步)置为初始值。 ```scala val myReg = RegInit(UInt(12.W), 0.U) val myReg = RegInit(0.U(12.W)) ``` ```scala class RegInitModule extends Module { val io = IO(new Bundle { val in = Input(UInt(12.W)) val out = Output(UInt(12.W)) }) val register = RegInit(0.U(12.W)) register := io.in + 1.U io.out := register } println(getVerilog(new RegInitModule)) ``` > Note that the generated verilog now has a block that checks `if (reset)` to reset the register to 0. > Also note that this is inside the `always @(posedge clock)` block. > Chisel's implicit reset is active **high** and **synchronous**. > The register is still initialized to random junk before reset is called. > The `PeekPokeTesters` always call reset before running your test, but you can manually call reset as well using the `reset(n)` function, where reset is high for `n` cycles. ### Control flow 寄存器也遵循**最后驱动信号有效**规则,可以在when,elsewhen,otherwise中被驱动。 在chisel中,**位选信号不可以被赋值**:`out(0) := in`是错误的 ### Explicit clock and reset 在Module内部的所有Register都受到隐式的clock和reset信号控制,如果想让他们由其他时钟信号控制,可以用`withClock(){}`、`withReset(){}`和`withClockAndReset(){}`包裹起来。 ```scala import chisel3.experimental.{withClock, withReset, withClockAndReset} class ClockExamples extends Module { val io = IO(new Bundle { val in = Input(UInt(10.W)) val alternateReset = Input(Bool()) val alternateClock = Input(Clock()) val outImplicit = Output(UInt()) val outAlternateReset = Output(UInt()) val outAlternateClock = Output(UInt()) val outAlternateBoth = Output(UInt()) }) val imp = RegInit(0.U(10.W)) imp := io.in io.outImplicit := imp withReset(io.alternateReset) { // everything in this scope with have alternateReset as the reset val altRst = RegInit(0.U(10.W)) altRst := io.in io.outAlternateReset := altRst } withClock(io.alternateClock) { val altClk = RegInit(0.U(10.W)) altClk := io.in io.outAlternateClock := altClk } withClockAndReset(io.alternateClock, io.alternateReset) { val alt = RegInit(0.U(10.W)) alt := io.in io.outAlternateBoth := alt } } println(getVerilog(new ClockExamples)) ``` ## 2.6 ChiselTest ### Basic Tester Implementation | | iotesters | ChiselTest | | :------- | :------------------------- | :-------------------- | | poke | poke(c.io.in1, 6) | c.io.in1.poke(6.U) | | peek | peek(c.io.out1) | c.io.out1.peek() | | expect | expect(c.io.out1, 6) | c.io.out1.expect(6.U) | | step | step(1) | c.io.clock.step(1) | | initiate | Driver.execute(...) { c => | test(...) { c => | ### Modules with Decoupled Interface #### A queue example ```scala class QueueModule[T <: Data](ioType: T, entries: Int) extends MultiIOModule { val in = IO(Flipped(Decoupled(ioType))) val out = IO(Decoupled(ioType)) out <> Queue(in, entries) } ``` > Queue(in, entries)是单例对象Queue的apply方法,可以理解为apply(enq:DecoupledIO[T], entries: Int): DecoupledIO[T],实际上做了三件事: > > 1. 实例化了一个队列硬件模块q(在生成的verilog里会是一个真实的FIFO) > 2. 自动将`in`连接到该队列的输入端口(相当于`queue.io.enq <> in`) > 3. 返回队列的输出端口(即`queue.io.deq`) > > 这种写法是Chisel的语法糖,允许模块生成器(如`Queue`)在返回端口的同时完成部分连接。 > > `<>`用于连接两个有方向的变量,左端是输入,右端是输出。注意,对于模块内部而言,输入是可以当其他子模块的输入的,这和从外部看来不一样。 #### EnqueueNow and expectDequeueNow ChiselTest内置了一些用于测试DecoupledIO的方法: | method | description | | :--------------- | :----------------------------------------------------------- | | enqueueNow | Add (enqueue) one element to a `Decoupled` input interface | | expectDequeueNow | Removes (dequeues) one element from a `Decoupled` output interface | ```scala test(new QueueModule(UInt(9.W), entries = 200)) { c => // Example testsequence showing the use and behavior of Queue c.in.initSource() c.in.setSourceClock(c.clock) c.out.initSink() c.out.setSinkClock(c.clock) val testVector = Seq.tabulate(200){ i => i.U } testVector.zip(testVector).foreach { case (in, out) => c.in.enqueueNow(in) c.out.expectDequeueNow(out) } } ``` > 模板代码`initSource`,`setSourceClock`是用来保证Decoupled接口中`ready`和`valid`字段在测试开始前正确初始化。 > > 由于`initSource`返回对象本身,所以也可以写在一行: > > `c.in.initSource().setSourceClock(c.clock)` > > `c.out.initSink().setSinkClock(c.clock)` #### EnqueueSeq and DequeueSeq 使用Seq批量添加元素和删除元素: | method | description | | :--------------- | :----------------------------------------------------------- | | enqueueSeq | Continues to add (enqueue) elements from the `Seq` to a `Decoupled` input interface, once a time, util the sequence is exhausted | | expectDequeueSeq | Removes (dequeues) elements from a `Decoupled` output interface, once a time, and compares each one to the next element of the Seq | ```scala test(new QueueModule(UInt(9.W), entries = 200)) { c => // Example testsequence showing the use and behavior of Queue c.in.initSource() c.in.setSourceClock(c.clock) c.out.initSink() c.out.setSinkClock(c.clock) val testVector = Seq.tabulate(100){ i => i.U } c.in.enqueueSeq(testVector) c.out.expectDequeueSeq(testVector) } ``` > 在这个例子中,`enqueueSeq`必须比`expectDequeueSeq`先完成,而且,如果`testVector`的长度超过了队列大小,就会报错,因为队列会满而`enqueueSeq`无法完成所有的元素添加。 > > 更多用法请参照[TestAdapters.scala](https://github.com/ucb-bar/chiseltest/blob/d199c5908828d0be5245f55fce8a872b2afb314e/src/main/scala/chisel3/tester/TestAdapters.scala) #### Fork and Join in ChiselTest 使用多个线程同时进行单元测试: | method | description | | :----- | :----------------------------------------------------------- | | fork | launches a concurrent code block, additional forks can be run concurrently(并发) to this one via the .fork appended to end of the code block of the preceding fork | | join | re-unites multiple related forks back into the calling thread | ```scala test(new QueueModule(UInt(9.W), entries = 200)) { c => // Example testsequence showing the use and behavior of Queue c.in.initSource() c.in.setSourceClock(c.clock) c.out.initSink() c.out.setSinkClock(c.clock) val testVector = Seq.tabulate(300){ i => i.U } fork { c.in.enqueueSeq(testVector) }.fork { c.out.expectDequeueSeq(testVector) }.join() } ``` > 两个fork块同时发生,第一个fork块用于持续向队列中添加元素,直到testVector耗尽为止,第二个fork块用来在数据可用时,弹出队首元素并比较。 > > 并发执行的顺序与代码中的顺序基本一致,即一次添加,一次弹出,循环往复。 > > 注意:两个线程应当独立运行,而不需要通信,否则会有bug。 #### Using Fork and Join with GCD(最大公约数) ```scala class GcdInputBundle(val w: Int) extends Bundle { val value1 = UInt(w.W) val value2 = UInt(w.W) } ``` ```scala class GcdOutputBundle(val w: Int) extends Bundle { val value1 = UInt(w.W) val value2 = UInt(w.W) val gcd = UInt(w.W) } ``` ```scala /** * Compute GCD using subtraction method. * Subtracts the smaller of registers x and y from the larger until register y is zero. * value input register x is then the Gcd * returns a packet of information with the two input values and their GCD */ class DecoupledGcd(width: Int) extends MultiIOModule { val input = IO(Flipped(Decoupled(new GcdInputBundle(width)))) val output = IO(Decoupled(new GcdOutputBundle(width))) val xInitial = Reg(UInt()) val yInitial = Reg(UInt()) val x = Reg(UInt()) val y = Reg(UInt()) val busy = RegInit(false.B) val resultValid = RegInit(false.B) input.ready := ! busy output.valid := resultValid output.bits := DontCare when(busy) { // during computation keep subtracting the smaller from the larger when(x > y) { x := x - y }.otherwise { y := y - x } when(y === 0.U) { // when y becomes zero computation is over, // signal valid data to output if the output is ready output.bits.value1 := xInitial output.bits.value2 := yInitial output.bits.gcd := x output.valid := true.B busy := ! output.ready } }.otherwise { when(input.valid) { // valid data available and no computation in progress, grab new values and start val bundle = input.deq() x := bundle.value1 y := bundle.value2 xInitial := bundle.value1 yInitial := bundle.value2 busy := true.B resultValid := false.B } } } ``` ```scala test(new DecoupledGcd(16)) { dut => dut.input.initSource().setSourceClock(dut.clock) dut.output.initSink().setSinkClock(dut.clock) val testValues = for { x <- 1 to 10; y <- 1 to 10} yield (x, y) val inputSeq = testValues.map { case (x, y) => (new GcdInputBundle(16)).Lit(_.value1 -> x.U, _.value2 -> y.U) } val resultSeq = testValues.map { case (x, y) => new GcdOutputBundle(16).Lit(_.value1 -> x.U, _.value2 -> y.U, _.gcd -> BigInt(x).gcd(BigInt(y)).U) } fork { dut.input.enqueueSeq(inputSeq) }.fork { for (expected <- resultSeq) { dut.output.expectDequeue(expected) dut.clock.step(5) // wait some cycles before receiving the next output to create backpressure } }.join() } ``` > 由于输入的不再是单个数值,而是线束,所以需要使用Lit方法对线束进行初始化。 ## 3.1 Generators: Parameters ### Parameter Passing #### example: Parameterized Scala Object ```scala class ParameterizedScalaObject(param1: Int, param2: String) { println(s"I have parameters: param1 = $param1 and param2 = $param2") } val obj1 = new ParameterizedScalaObject(4, "Hello") val obj2 = new ParameterizedScalaObject(4 + 2, "World") ``` #### example: Parameterized Chisel Object ```scala class ParameterizedWidthAdder(in0Width: Int, in1Width: Int, sumWidth: Int) extends Module { require(in0Width >= 0) require(in1Width >= 0) require(sumWidth >= 0) val io = IO(new Bundle { val in0 = Input(UInt(in0Width.W)) val in1 = Input(UInt(in1Width.W)) val sum = Output(UInt(sumWidth.W)) }) // a +& b includes the carry, a + b does not io.sum := io.in0 +& io.in1 } ``` > require()在generator中使用,assert()在模拟中使用 ### Option and Default Arguments 有时候一个方法有返回值,有的时候没有,为了避免报错,需要用到`Option`类型。 #### Example: Erroneous Map index Call 下面代码会直接报错,因为map引用了一个不存在的key: ```scala val map = Map("a" -> 1) val a = map("a") println(a) //1 val b = map("b") println(b) //java.util.NoSuchElementException: key not found: b at scala.collection.immutable.Map$Map1.apply(Map.scala:263) ``` #### Example: Getting Uncertain Indices 下面的代码不会报错,因为get方法返回的是一个`Option`类型的值,它有两个子类:`Some`和`None` ```scala val map = Map("a" -> 1) val a = map.get("a") println(a) //Some(1) val b = map.get("b") println(b) //None ``` #### Example:Get or Else! `Option`类型也有`get`方法,用来获取`Some`中的值,在本身为`None`时会报错,因此,可以使用`getOrElse`方法: ```scala val some = Some(1) val none = None println(some.get) // Returns 1 // println(none.get) // Errors! println(some.getOrElse(2)) // Returns 1 println(none.getOrElse(2)) // Returns 2 ``` ### Options for Parameters with Defaults 通常,一个参数没有一个特别好的初始值,这时候就可以使用`Option`的`None`作为默认值 #### Example:Optional Reset ```scala class DelayBy1(resetValue: Option[UInt] = None) extends Module { val io = IO(new Bundle { val in = Input( UInt(16.W)) val out = Output(UInt(16.W)) }) val reg = if (resetValue.isDefined) { // resetValue = Some(number) RegInit(resetValue.get) } else { //resetValue = None Reg(UInt()) } reg := io.in io.out := reg } ``` ### Match/Case Statements #### Example:Value Matching ```scala // y is an integer variable defined somewhere else in the code val y = 7 /// ... val x = y match { case 0 => "zero" // One common syntax, preferred if fits in one line case 1 => // Another common syntax, preferred if does not fit in one line. "one" // Note the code block continues until the next case case 2 => { // Another syntax, but curly braces are not required "two" } case _ => "many" // _ is a wildcard that matches all values } println("y is " + x) ``` > 每条语句后面自带break #### Example:Multiple Value Matching ```scala def animalType(biggerThanBreadBox: Boolean, meanAsCanBe: Boolean): String = { (biggerThanBreadBox, meanAsCanBe) match { case (true, true) => "wolverine" case (true, false) => "elephant" case (false, true) => "shrew" case (false, false) => "puppy" } } println(animalType(true, true)) ``` #### Example:Type Matching ```scala val sequence = Seq("a", 1, 0.0) sequence.foreach { x => x match { case s: String => println(s"$x is a String") case s: Int => println(s"$x is an Int") case s: Double => println(s"$x is a Double") case _ => println(s"$x is an unknown type!") } } ``` #### Example:Multiple Type Matching ```scala val sequence = Seq("a", 1, 0.0) sequence.foreach { x => x match { case _: Int | _: Double => println(s"$x is a number!") case _ => println(s"$x is an unknown type!") } } ``` #### Example:Type Matching and Erasure 类型匹配有缺陷,当多态发生时会擦除所有的多态类型,下面的语句只会匹配第一个case语句: ```scala val sequence = Seq(Seq("a"), Seq(1), Seq(0.0)) sequence.foreach { x => x match { case s: Seq[String] => println(s"$x is a String") case s: Seq[Int] => println(s"$x is an Int") case s: Seq[Double] => println(s"$x is a Double") } } ``` #### Example:Optional Reset Matching ```scala class DelayBy1(resetValue: Option[UInt] = None) extends Module { val io = IO(new Bundle { val in = Input( UInt(16.W)) val out = Output(UInt(16.W)) }) val reg = resetValue match { case Some(r) => RegInit(r) case None => Reg(UInt()) } reg := io.in io.out := reg } ``` ### IOs with Optional Fields 有时候我们想让一些IO端口可有可无。 #### Example:Optional IO with Option ```scala class HalfFullAdder(val hasCarry: Boolean) extends Module { val io = IO(new Bundle { val a = Input(UInt(1.W)) val b = Input(UInt(1.W)) val carryIn = if (hasCarry) Some(Input(UInt(1.W))) else None val s = Output(UInt(1.W)) val carryOut = Output(UInt(1.W)) }) val sum = io.a +& io.b +& io.carryIn.getOrElse(0.U) io.s := sum(0) io.carryOut := sum(1) } test(new HalfFullAdder(false)) { c => require(!c.hasCarry, "DUT must be half adder") // 0 + 0 = 0 c.io.a.poke(0.U) c.io.b.poke(0.U) c.io.s.expect(0.U) c.io.carryOut.expect(0.U) // 0 + 1 = 1 c.io.b.poke(1.U) c.io.s.expect(1.U) c.io.carryOut.expect(0.U) // 1 + 1 = 2 c.io.a.poke(1.U) c.io.s.expect(0.U) c.io.carryOut.expect(1.U) // 1 + 0 = 1 c.io.b.poke(0.U) c.io.s.expect(1.U) c.io.carryOut.expect(0.U) } test(new HalfFullAdder(true)) { c => require(c.hasCarry, "DUT must be half adder") c.io.carryIn.get.poke(0.U) // 0 + 0 + 0 = 0 c.io.a.poke(0.U) c.io.b.poke(0.U) c.io.s.expect(0.U) c.io.carryOut.expect(0.U) // 0 + 0 + 1 = 1 c.io.b.poke(1.U) c.io.s.expect(1.U) c.io.carryOut.expect(0.U) // 0 + 1 + 1 = 2 c.io.a.poke(1.U) c.io.s.expect(0.U) c.io.carryOut.expect(1.U) // 0 + 1 + 0 = 1 c.io.b.poke(0.U) c.io.s.expect(1.U) c.io.carryOut.expect(0.U) c.io.carryIn.get.poke(1.U) // 1 + 0 + 0 = 1 c.io.a.poke(0.U) c.io.b.poke(0.U) c.io.s.expect(1.U) c.io.carryOut.expect(0.U) // 1 + 0 + 1 = 2 c.io.b.poke(1.U) c.io.s.expect(0.U) c.io.carryOut.expect(1.U) // 1 + 1 + 1 = 3 c.io.a.poke(1.U) c.io.s.expect(1.U) c.io.carryOut.expect(1.U) // 1 + 1 + 0 = 2 c.io.b.poke(0.U) c.io.s.expect(0.U) c.io.carryOut.expect(1.U) } println("SUCCESS!!") // Scala Code: if we get here, our tests passed! ``` #### Example:Optional IO with Zero-Width Wires 也可以用0宽度表示不生成该端口。 ```scala class HalfFullAdder(val hasCarry: Boolean) extends Module { val io = IO(new Bundle { val a = Input(UInt(1.W)) val b = Input(UInt(1.W)) val carryIn = Input(if (hasCarry) UInt(1.W) else UInt(0.W)) val s = Output(UInt(1.W)) val carryOut = Output(UInt(1.W)) }) val sum = io.a +& io.b +& io.carryIn io.s := sum(0) io.carryOut := sum(1) } ``` ### Implicits #### Implicit Arguments ##### Example:Implicit Cats ```scala object CatDog { implicit val numberOfCats: Int = 3 //implicit val numberOfDogs: Int = 5 def tooManyCats(nDogs: Int)(implicit nCats: Int): Boolean = nCats > nDogs val imp = tooManyCats(2) // Argument passed implicitly! val exp = tooManyCats(2)(1) // Argument passed explicitly! } CatDog.imp CatDog.exp ``` > 首先我们定义了一个隐式`Int`类型变量`numberOfCats`,**在一个命名空间内,同一种类型的隐变量只能存在一个**。 > > 以下两种情况会报错: > > * 两个或以上的同类型的隐变量在同一个命名空间中定义 > * 编译器无法找到必要的隐变量来执行方法 #### Implicit Conversions ```scala class Animal(val name: String, val species: String) class Human(val name: String) implicit def human2animal(h: Human): Animal = new Animal(h.name, "Homo sapiens") val me = new Human("Adam") println(me.species) ``` > 当访问不存在的成员变量(species)时,编译器会去寻找是否有隐式方法将一个对象(human)转为另一个对象(animal),然后调用另一个对象(animal)的成员。 ## 3.2 Generators: Collections ```scala class RegisterFile(readPorts: Int) extends Module { require(readPorts >= 0) val io = IO(new Bundle { val wen = Input(Bool()) val waddr = Input(UInt(5.W)) val wdata = Input(UInt(32.W)) val raddr = Input(Vec(readPorts, UInt(5.W))) val rdata = Output(Vec(readPorts, UInt(32.W))) }) // A Register of a vector of UInts val reg = RegInit(VecInit(Seq.fill(32)(0.U(32.W)))) when (io.wen) { reg(io.waddr) := io.wdata } for (i <- 0 until readPorts) { when (io.raddr(i) === 0.U) { io.rdata(i) := 0.U } .otherwise { io.rdata(i) := reg(io.raddr(i)) } } } ``` ```scala test(new RegisterFile(2) ) { c => def readExpect(addr: Int, value: Int, port: Int = 0): Unit = { c.io.raddr(port).poke(addr.U) c.io.rdata(port).expect(value.U) } def write(addr: Int, value: Int): Unit = { c.io.wen.poke(true.B) c.io.wdata.poke(value.U) c.io.waddr.poke(addr.U) c.clock.step(1) c.io.wen.poke(false.B) } // everything should be 0 on init for (i <- 0 until 32) { readExpect(i, 0, port = 0) readExpect(i, 0, port = 1) } // write 5 * addr + 3 for (i <- 0 until 32) { write(i, 5 * i + 3) } // check that the writes worked for (i <- 0 until 32) { readExpect(i, if (i == 0) 0 else 5 * i + 3, port = i % 2) } } ``` ## 3.3 Interlude: Chisel Standard Library ### Decoupled: A Standard Ready-Valid Interface `ready`和`valid`只有在数据传输完成之后才能进行更新。 ```scala val myChiselData = UInt(8.W) // or any Chisel data type, such as Bool(), SInt(...), or even custom Bundles val myDecoupled = Decoupled(myChiselData) ``` > * `valid`: Output(Bool) > * `ready`: Input(Bool) > * `bits`: Output(UInt(8.W)) ### Queues ```scala class FIFO extends Module { val io = IO(new Bundle { val in = Flipped(Decoupled(UInt(8.W))) val out = Decoupled(UInt(8.W)) }) io.out <> Queue(io.in, 2) //2-element queue } ``` ### Arbiters 仲裁器从n个`DecoupledIO`源引导到1个`DecoupledIO`出口,可以指定优先级。Arbiter本身是一种**组合电路**,需要搭配其他接口使用。 * `Arbiter`: 低索引值的优先级更高 * `RRArbiter`: 循环优先级 ```scala class PriorityArbiter extends Module { val io = IO(new Bundle { val in = Flipped(Vec(2, Decoupled(UInt(8.W)))) val out = Decoupled(UInt(8.W)) }) val arbiter = Module(new Arbiter(UInt(8.W),2)) // 2 to 1 Priority Arbiter arbiter.io.in <> io.in io.out <> arbiter.io.out } ``` ```scala test(new Module { // Example circuit using a priority arbiter val io = IO(new Bundle { val in = Flipped(Vec(2, Decoupled(UInt(8.W)))) val out = Decoupled(UInt(8.W)) }) // Arbiter doesn't have a convenience constructor, so it's built like any Module val arbiter = Module(new Arbiter(UInt(8.W), 2)) // 2 to 1 Priority Arbiter arbiter.io.in <> io.in io.out <> arbiter.io.out }) { c => c.io.in(0).valid.poke(false.B) c.io.in(1).valid.poke(false.B) c.io.out.ready.poke(false.B) println(s"Start:") println(s"\tin(0).ready=${c.io.in(0).ready.peek().litValue}, in(1).ready=${c.io.in(1).ready.peek().litValue}") println(s"\tout.valid=${c.io.out.valid.peek().litValue}, out.bits=${c.io.out.bits.peek().litValue}") c.io.in(1).valid.poke(true.B) // Valid input 1 c.io.in(1).bits.poke(42.U) c.io.out.ready.poke(true.B) // What do you think the output will be? println(s"valid input 1:") println(s"\tin(0).ready=${c.io.in(0).ready.peek().litValue}, in(1).ready=${c.io.in(1).ready.peek().litValue}") println(s"\tout.valid=${c.io.out.valid.peek().litValue}, out.bits=${c.io.out.bits.peek().litValue}") c.io.in(0).valid.poke(true.B) // Valid inputs 0 and 1 c.io.in(0).bits.poke(43.U) // What do you think the output will be? Which inputs will be ready? println(s"valid inputs 0 and 1:") println(s"\tin(0).ready=${c.io.in(0).ready.peek().litValue}, in(1).ready=${c.io.in(1).ready.peek().litValue}") println(s"\tout.valid=${c.io.out.valid.peek().litValue}, out.bits=${c.io.out.bits.peek().litValue}") c.io.in(1).valid.poke(false.B) // Valid input 0 // What do you think the output will be? println(s"valid input 0:") println(s"\tin(0).ready=${c.io.in(0).ready.peek().litValue}, in(1).ready=${c.io.in(1).ready.peek().litValue}") println(s"\tout.valid=${c.io.out.valid.peek().litValue}, out.bits=${c.io.out.bits.peek().litValue}") } ``` ```scala Start: in(0).ready=0, in(1).ready=0 out.valid=0, out.bits=0 valid input 1: in(0).ready=1, in(1).ready=1 out.valid=1, out.bits=42 valid inputs 0 and 1: in(0).ready=1, in(1).ready=0 out.valid=1, out.bits=43 valid input 0: in(0).ready=1, in(1).ready=0 out.valid=1, out.bits=43 ``` ### Bitwise Utilities #### PopCount 返回输入信号bit中1(高电平)的个数,类型为`UInt` ```scala class PopCountBit extends Module{ val io = IO(new Bundle { val in = Input(UInt(8.W)) val out = Output(UInt(8.W)) }) io.out := PopCount(io.in) } ``` #### Reverse 反转bit ```scala class ReverseBit extends Module { val io = IO(new Bundle { val in = Input(UInt(8.W)) val out = Output(UInt(8.W)) }) io.out := Reverse(io.in) } ``` ### OneHot encoding utilities * UInt to OneHot: `UIntToOH` * OneHot to UInt: `OHToUInt` ```scala class UToO extends Module { val io = IO(new Bundle { val in = Input(UInt(4.W)) val out = Output(UInt(16.W)) }) io.out := UIntToOH(io.in) } ``` ```scala class OToU extends Module { val io = IO(new Bundle { val in = Input(UInt(16.W)) val out = Output(UInt(4.W)) }) io.out := OHToUInt(io.in) } ``` ### Muxes #### Priority Mux ```scala test(new Module { // Example circuit using PriorityMux val io = IO(new Bundle { val in_sels = Input(Vec(2, Bool())) val in_bits = Input(Vec(2, UInt(8.W))) val out = Output(UInt(8.W)) }) io.out := PriorityMux(io.in_sels, io.in_bits) }) { c => c.io.in_bits(0).poke(10.U) c.io.in_bits(1).poke(20.U) // Select higher index only c.io.in_sels(0).poke(false.B) c.io.in_sels(1).poke(true.B) println(s"in_sels=${c.io.in_sels(0).peek().litValue}, out=${c.io.out.peek().litValue}") // Select both - arbitration needed c.io.in_sels(0).poke(true.B) c.io.in_sels(1).poke(true.B) println(s"in_sels=${c.io.in_sels(0).peek().litValue}, out=${c.io.out.peek().litValue}") // Select lower index only c.io.in_sels(0).poke(true.B) c.io.in_sels(1).poke(false.B) println(s"in_sels=${c.io.in_sels(0).peek().litValue}, out=${c.io.out.peek().litValue}") } ``` #### OneHot Mux 如果输入select信号不满足Onehot,则会发生未定义的行为 ```scala test(new Module { // Example circuit using Mux1H val io = IO(new Bundle { val in_sels = Input(Vec(2, Bool())) val in_bits = Input(Vec(2, UInt(8.W))) val out = Output(UInt(8.W)) }) io.out := Mux1H(io.in_sels, io.in_bits) }) { c => c.io.in_bits(0).poke(10.U) c.io.in_bits(1).poke(20.U) // Select index 1 c.io.in_sels(0).poke(false.B) c.io.in_sels(1).poke(true.B) println(s"in_sels=${c.io.in_sels(0).peek().litValue}, out=${c.io.out.peek().litValue}") // Select index 0 c.io.in_sels(0).poke(true.B) c.io.in_sels(1).poke(false.B) println(s"in_sels=${c.io.in_sels(0).peek().litValue}, out=${c.io.out.peek().litValue}") // Select none (invalid) c.io.in_sels(0).poke(false.B) c.io.in_sels(1).poke(false.B) println(s"in_sels=${c.io.in_sels(0).peek().litValue}, out=${c.io.out.peek().litValue}") // Select both (invalid) c.io.in_sels(0).poke(true.B) c.io.in_sels(1).poke(true.B) println(s"in_sels=${c.io.in_sels(0).peek().litValue}, out=${c.io.out.peek().litValue}") } ``` #### MuxCase ```scala MuxCase(default, Array(c1 -> a, c2 -> b, ...)) ``` c1, c2,...是Boolean类型的变量 #### MuxLookup ```scala MuxLookup(idx, default)(Seq(0.U -> a, 1.U -> b, ...)) ``` 相当于: ```scala MuxCase(default, Array((idx === 0.U) -> a, (idx === 1.U) -> b, ...)) ``` ### Counter ```scala test(new Module { // Example circuit using Mux1H val io = IO(new Bundle { val count = Input(Bool()) val out = Output(UInt(2.W)) }) val counter = Counter(3) // 3-count Counter (outputs range [0...2]) when(io.count) { counter.inc() } io.out := counter.value }) { c => c.io.count.poke(true.B) println(s"start: counter value=${c.io.out.peek().litValue}") c.clock.step(1) println(s"step 1: counter value=${c.io.out.peek().litValue}") c.clock.step(1) println(s"step 2: counter value=${c.io.out.peek().litValue}") c.io.count.poke(false.B) c.clock.step(1) println(s"step without increment: counter value=${c.io.out.peek().litValue}") c.io.count.poke(true.B) c.clock.step(1) println(s"step again: counter value=${c.io.out.peek().litValue}") } ``` ## 3.3 Highter-Order Functions