0%

如何使用Chisel标准库

Decoupled:标准的Ready-Valid接口

Chisel提供的常用接口之一是 DecoupledIO,为传输数据提供一个随时有效的接口。
DecoupledIO的原理是,当源端有数据要传入时使能valid信号,并且将数据传入bits
当接收器准备好接受数据时,使能ready信号,当readyvalid在一个周期内都被使能时,数据被认为是被传输的。

DecoupledIO Bundle的字段:

  • valid: Output(Bool)
  • ready: Input(Bool)
  • bits: Output(UInt(width.W(or U)))

    Flipped

    Flipped会改变DecoupledIObits字段的默认方向,DecoupledIObits字段默认方向是Output,在使用Flipped之后,bits字段的方向会变为Input。
    下面两段代码是等价的。
    1
    val in = Decoupled(UInt(8.W))
    1
    2
    3
    4
    5
    val in = new Bundle {
    val valid = Output(Bool())
    val ready = Input(Bool())
    val bits = Output(UInt(8.W))
    }
    在使用Flipped之后,DecoupledIO的输出方向会变成Input。
    下面两段代码也是等价的。
    1
    val in = Flipped(Decoupled(UInt(8.W)))
    1
    2
    3
    4
    5
    val in = new Bundle {
    val valid = Input(Bool())
    val ready = Output(Bool())
    val bits = Input(UInt(8.W))
    }

    Queue

    Queue创建一个FIFO队列,两边都有Decoupled接口,允许背压(backpressure)。数据类型和元素的数量都是可配置的。

Code

首先用sbt创建一个Scala项目,sbt scala/scala3.g8
然后进入项目文件夹
修改build.sbt,将build.sbt修改为以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ThisBuild / scalaVersion     := "2.12.13"
ThisBuild / version := "0.1.0"
ThisBuild / organization := "%ORGANIZATION%"

lazy val root = (project in file("."))
.settings(
name := "%NAME%",
libraryDependencies ++= Seq(
"edu.berkeley.cs" %% "chisel3" % "3.4.3",
"edu.berkeley.cs" %% "chiseltest" % "0.3.3" % "test",
"edu.berkeley.cs" %% "rocketchip" % "1.2.6",
"edu.berkeley.cs" %% "chisel-iotesters" % "1.5.+"
),
scalacOptions ++= Seq(
"-Xsource:2.11",
"-language:reflectiveCalls",
"-deprecation",
"-feature",
"-Xcheckinit",
"-P:chiselplugin:useBundlePlugin"
),
addCompilerPlugin("edu.berkeley.cs" % "chisel3-plugin" % "3.4.3" cross CrossVersion.full),
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)
)

然后创建一个src/main/scala/QueueTest/QueueTest.scala文件,里面填入以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
package QueueTest

import chisel3._
import chisel3.util._

class QueueTest extends Module {
val io = IO(new Bundle {
val in = Flipped(Decoupled(UInt(8.W)))
val out = Decoupled(UInt(8.W))
})
val queue = Queue(io.in,2) //创建一个Queue
io.out <> queue
}

再创建一个src/main/scala/QueueTest/QueueTestSpec.scala文件,里面填入以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package QueueTest

import chisel3._
import chisel3.util._
import chisel3.tester._
import chisel3.tester.RawTester.test
import org.scalatest.FreeSpec

object testMain extends App {
test(new QueueTest) { c =>
c.io.out.ready.poke(false.B)
c.io.in.valid.poke(true.B) // Enqueue an element
c.io.in.bits.poke(42.U)
println(s"Starting:")
println(s"\tio.in: ready=${c.io.in.ready.peek().litValue}")
println(s"\tio.out: valid=${c.io.out.valid.peek().litValue}, bits=${c.io.out.bits.peek().litValue}")
c.clock.step(1)

c.io.in.valid.poke(true.B) // Enqueue another element
c.io.in.bits.poke(43.U)
// What do you think io.out.valid and io.out.bits will be?
println(s"After first enqueue:")
println(s"\tio.in: ready=${c.io.in.ready.peek().litValue}")
println(s"\tio.out: valid=${c.io.out.valid.peek().litValue}, bits=${c.io.out.bits.peek().litValue}")
c.clock.step(1)

c.io.in.valid.poke(true.B) // Read a element, attempt to enqueue
c.io.in.bits.poke(44.U)
c.io.out.ready.poke(true.B)
// What do you think io.in.ready will be, and will this enqueue succeed, and what will be read?
println(s"On first read:")
println(s"\tio.in: ready=${c.io.in.ready.peek()}")
println(s"\tio.out: valid=${c.io.out.valid.peek()}, bits=${c.io.out.bits.peek()}")
c.clock.step(1)

c.io.in.valid.poke(false.B) // Read elements out
c.io.out.ready.poke(true.B)
// What do you think will be read here?
println(s"On second read:")
println(s"\tio.in: ready=${c.io.in.ready.peek()}")
println(s"\tio.out: valid=${c.io.out.valid.peek()}, bits=${c.io.out.bits.peek()}")
c.clock.step(1)

// Will a third read produce anything?
println(s"On third read:")
println(s"\tio.in: ready=${c.io.in.ready.peek()}")
println(s"\tio.out: valid=${c.io.out.valid.peek()}, bits=${c.io.out.bits.peek()}")
c.clock.step(1)
}
Driver.execute(args, () => new QueueTest) //生成 .v文件
}

最后打开命令行,再命令行里如下以下代码,下面这行代码会在generated/QueueTest里生成verilog代码

1
sbt "test:runMain QueueTest.testMain --target-dir generated/QueueTest"

仿真结果

Starting:
io.in: ready=1
io.out: valid=0, bits=0
After first enqueue:
io.in: ready=1
io.out: valid=1, bits=42
On first read:
io.in: ready=Bool(false)
io.out: valid=Bool(true), bits=UInt<8>(42)
On second read:
io.in: ready=Bool(true)
io.out: valid=Bool(true), bits=UInt<8>(43)
On third read:
io.in: ready=Bool(true)
io.out: valid=Bool(false), bits=UInt<8>(42)

参考资料

Interlude: Chisel Standard Library
What does Flipped() do in Chisel3?