Rocket-chip提供了非常多TileLink协议相关的模块,可以直接使用,但是rocket-chip并没有提供比较完善的文档可以参考。本节以Xbar为例,用一个小例子来说明如何使用rocket-chip中TileLink的模块。
本节参考的文档如下:
Chipyard文档第9节:TileLink and Diplomacy Reference
Rocket-chip中diplomacy相关的文档
我们首先创建主设备,如下所示。主设备每隔8个周期发送一次Get请求,且每次请求的地址递增8。
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
class MyClient(implicit p: Parameters) extends LazyModule {
val node = TLClientNode(
Seq(
TLMasterPortParameters.v1(
clients = Seq(
TLMasterParameters.v1(
name = "my-client"
)
)
)
)
)
lazy val module = new LazyModuleImp(this) {
val (tl, edge) = node.out(0)
dontTouch(tl)
val cycle = RegInit(0.U(32.W))
cycle := cycle + 1.U
tl.a.valid := (cycle(2, 0) === 0.U)
tl.d.ready := true.B
val base = RegInit("h20000".U(32.W))
val (_, gbits) = edge.Get(0.U, base + cycle, 2.U)
tl.a.bits := gbits
}
}
接下来创建从设备,如下所示。从设备的地址基址作为一个参数传入,但范围固定。在TLSlaveParameters
参数中我们可以指定从设备的一些性质,如地址范围、支持的请求类型等。该从设备只能接受Get请求,且d通道内返回的数据就是a通道内传过来的地址。
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
class MyManager(val base: BigInt)(implicit p: Parameters) extends LazyModule {
val device = new SimpleDevice("my-device", Seq("my-device0"))
val beatBytes = 8
val node = TLManagerNode(
Seq(
TLSlavePortParameters.v1(
Seq(
TLSlaveParameters.v1(
address = Seq(AddressSet(base, 0xff)),
resources = device.reg,
regionType = RegionType.UNCACHED,
executable = true,
supportsGet = TransferSizes(1, beatBytes)
)
),
beatBytes = beatBytes
)
)
)
lazy val module = new LazyModuleImp(this) {
val (tl, edge) = node.in(0)
dontTouch(tl)
tl.d.valid := tl.a.valid
tl.a.ready := tl.d.ready
tl.d.bits := edge.AccessAck(tl.a.bits, tl.a.bits.address)
}
}
最后我们创建Top模块,演示如何使用Xbar,如下所示。我们有一个主设备和两个从设备,从设备地址范围分别为0x20000
-0x200ff
和0x20100
-0x201ff
。
1
2
3
4
5
6
7
8
9
10
11
12
13
class Top()(implicit p: Parameters) extends LazyModule {
val xbar = LazyModule(new TLXbar)
val client0 = LazyModule(new MyClient)
val manager0 = LazyModule(new MyManager(0x20000))
val manager1 = LazyModule(new MyManager(0x20100))
xbar.node := client0.node
manager0.node := xbar.node
manager1.node := xbar.node
lazy val module = new LazyModuleImp(this) {}
}
以下是生成verilog与DAG的代码,生成的graphml文件可以通过yED打开查看,或者直接用yED Live在线平台查看。
1
2
3
4
5
6
7
8
9
10
11
12
13
import chipsalliance.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.util.HasRocketChipStageUtils
object Elaborate extends App with HasRocketChipStageUtils {
override def main(args: Array[String]): Unit = {
implicit val config = Parameters.empty
val top = LazyModule(new Top())
val verilog = chisel3.stage.ChiselStage.emitVerilog(top.module)
writeOutputFile(".", "Top.v", verilog)
writeOutputFile(".", "Top.graphml", top.graphML)
}
}
测试方便起见,我们可以创建对应的Tester,在src/test/scala/Tester.scala
,如下所示。之后我们只需要通过sbt test
就可以直接进行仿真并生成波形,不过这种方式只适用于简单的测试,之后进一步的测试可能仍然需要自己写Verilator的测试代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import chipsalliance.rocketchip.config.Parameters
import chiseltest._
import freechips.rocketchip.diplomacy.LazyModule
import org.scalatest.flatspec.AnyFlatSpec
class Tester extends AnyFlatSpec with ChiselScalatestTester {
it should "test" in {
val annotation = Seq(
VerilatorBackendAnnotation,
WriteVcdAnnotation
)
test(LazyModule(new Top()(Parameters.empty)).module).withAnnotations(annotation) { tb =>
tb.clock.step(400)
}
}
}