如何从 fpga-zynq 存储库中的 Top 模块检索 Rocket Core 模块参数
How to retrieve Rocket Core module parameters from Top module in fpga-zynq repository
我的目标是 添加计数器 到 rocket class of the Rocket Core module of the fpga-zynq repository。我想计算 ctrl_stalld、id_stall_fpu 等参数 ... 而芯片在 FPGA [=43] 上是 运行 =].
我成功为默认火箭配置 (ZynqConfig) 生成了 Verilog 代码和 Vivado 项目。我把它加载到 ZedBoard 的 FPGA 上并得到了它 运行。 我知道如何在核心中实现计数器,但我不确定如何从外部检索它们。
我想,fpga-zynq/rocket-chip/src/main/scala/rocket/rocket.scala 和 fpga-zynq/common/src/main/scala/Top.scala 可能必须建立,因为我能够访问并进一步连接 Xilinx Vivado 内部的顶级模块 IO 端口2016.2.我假设项目层次结构必须从 rocket module 回溯到 Top module 并且所有模块之间的所有 IO 端口都已连接。
但是,我不相当了解项目层次结构。我找不到 rocket 和 Top 之间的许多模块的连接。我希望这张图片能澄清我想说的话。
箭头代表模块之间的IO连接。
黑点和“?”代表未知层次结构(可能更复杂)。
这不是 fpga-zynq 项目层次结构的实际表示。
正如预测的那样,Top 和 rocket 之间的所有模块的所有 I/O 端口都必须连接。
注意:以下解决方案仅适用于单核火箭!
我将完成在 Rocket 核心中实现 32 位计数器并将其与 Top 模块连接的必要步骤。
在哪里可以找到以下 classes:
火箭 -- fpga-zynq/rocket-chip/src/main/scala/rocket/rocket.scala
RocketTile -- fpga-zynq/rocket-chip/src/main/scala/rocket/tile.scala
BaseCorePlexModule -- fpga-zynq/rocket-chip/src/main/scala/coreplex/BaseCoreplexModule.scala
BaseTop -- fpga-zynq/rocket-chip/src/main/scala/rocketchip/BaseTop.scala
置顶 -- fpga-zynq/common/src/main/scala/Top.scala
1. 在火箭中实现计数器 class
首先,我们为我们所有的计数器创建一个新的包,在火箭 class 之外
(在本例中,它只是一个计数器,但您可以随时向捆绑包添加更多计数器和其他参数)
class CounterBundle extends Bundle{
val counter = UInt(OUTPUT, width = 32)
}
我们在 Rocket I/O Bundle 中定义了一个 Bundle 的 实例 class.
class Rocket(implicit p: Parameters) extends CoreModule()(p) {
val io = new Bundle {
val interrupts = new TileInterrupts().asInput
val hartid = UInt(INPUT, xLen)
val imem = new FrontendIO()(p.alterPartial({ case CacheName => "L1I" }))
val dmem = new HellaCacheIO()(p.alterPartial({ case CacheName => "L1D" }))
val ptw = new DatapathPTWIO().flip
val fpu = new FPUIO().flip
val rocc = new RoCCInterface().flip
val cBundle = new CounterBundle
}
// ...
}
由于我们的 "counter" 参数本质上是一条线,我们不能给它自己分配一条线,我们将使用一个 寄存器来计算 和 将其值转移到我们的计数器。如果某些条件为真,计数器将 递增 ,如果重置处于活动状态,则 重置 返回 0。我们把它放在火箭里面 class.
val counter_reg = Reg(init = UInt(0, width = 32))
when(/*some condition*/){
counter_reg := counter_reg + 1.U
}
io.cBundle.counter := counter_reg
火箭就是这样 class。
2. 连接 Rocket class CounterBundle 与 Top class
2.1 火箭 → RocketTile
一个新的Rocket class 实例在RocketTile class.
只需添加
val cBundle = new CounterBundle
到 RocketTile I/O 的。
现在让我们继续 连接 RocketTile class.
内的两个包
class RocketTile(clockSignal: Clock = null, resetSignal: Bool = null)
(implicit p: Parameters) extends Tile(clockSignal, resetSignal)(p) {
val buildRocc = p(BuildRoCC)
val usingRocc = !buildRocc.isEmpty
val nRocc = buildRocc.size
val nFPUPorts = buildRocc.filter(_.useFPU).size
val core = Module(new Rocket)
io.cBundle := core.io.cBundle
// ...
}
如果您想知道 RocketTile I/O 的 在哪里定义,请查找 TileIO class 在同一个文件中 (tile.scala).
2.2 RocketTile → BaseCorePlexModule
BaseCorePlexModule 拥有 一组图块 。但是由于我们只是为 单核火箭 创建一个计数器,我们可以 连接集合中第一个 RocketTile 的 Bundle.
首先,添加
val cBundle = new CounterBundle
到 BaseCorePlexModule 正上方的 "abstract class BaseCoreplexBundle"。正如您可能想的那样,BaseCorePlexBundle 包含 BaseCorePlexModule 的所有 I/O。
然后,连接这个Bundle与RocketTile Bundle在BaseCorePlexModule.
abstract class BaseCoreplexModule[+L <: BaseCoreplex, +B <: BaseCoreplexBundle](
c: CoreplexConfig, l: L, b: => B)(implicit val p: Parameters) extends LazyModuleImp(l) with HasCoreplexParameters {
val outer: L = l
val io: B = b
// Build a set of Tiles
val tiles = p(BuildTiles) map { _(reset, p) }
io.cBundle := tiles.head.io.cBundle
// ...
}
2.3 BaseCorePlexModule → BaseTop
这是在进入顶部class之前进行的最后一次连接,因为顶部[=251] =] 包含一个参数 "target",它是 BaseTop.
的 子级
同样,将 CounterBundle
的 新实例添加到此 class 的 I/O。 "abstract class BaseTopBundle" 包含 BaseTop 的所有 I/O's。
将两者联系起来。
abstract class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle](
val p: Parameters, l: L, b: => B) extends LazyModuleImp(l) {
val outer: L = l
val io: B = b
val coreplex = p(BuildCoreplex)(outer.c, p)
io.cBundle := coreplex.io.cBundle
// ...
}
2.4 BaseTop → Top
Top class 持有类型为 FPGAZynqTop
的参数 "target"
val target = LazyModule(new FPGAZynqTop(p)).module
在文件底部,可以找到 FPGAZynqTop 作为 BaseTop
的 子项。因此,我们可以通过"target"参数访问BaseTopI/O的
对于TopI/O,我们不打算添加CounterBundle,而是添加Bundle中的32位"counter"参数。这样,就可以在 Vivado 中访问计数器,因为生成的 Verilog 代码将包含 32 位线。
将 32 位 UInt 参数 添加到顶部 I/O
val counter = UInt(OUTPUT, width = 32)
将它与 CounterBundle 中的计数器连接。
class Top(implicit val p: Parameters) extends Module {
val io = new Bundle {
val ps_axi_slave = new NastiIO()(AdapterParams(p)).flip
val mem_axi = new NastiIO
val counter = UInt(OUTPUT, width = 32)
}
val target = LazyModule(new FPGAZynqTop(p)).module
val slave = Module(new ZynqAXISlave(1)(AdapterParams(p)))
io.counter := target.io.cBundle.counter
// ...
}
结论
使用此配置可以在芯片在 FPGA 上运行时计算 Rocket Core 参数。我在 ZedBoard 上测试了它。
使用此设置,您可以简单地向 CounterBundle 添加更多计数器/参数,而无需再次遍历 Rocket 和 Top 之间的所有模块。当然,您仍然需要更改 Top 模块。
编辑
在向 Bundle 添加越来越多的参数后,我意识到每次将所有这些都添加到 Top 模块变得很烦人。您可以将CounterBundle 直接添加到Top 模块I/O。在生成的 Verilog 代码中,CounterBundle 将被提取到所有 i包含信号。
我的新 Top 模块看起来像这样。 一定要从 Rocket 导入 CounterBundle class
import rocket.CounterBundle
class Top(implicit val p: Parameters) extends Module {
val io = new Bundle {
val ps_axi_slave = new NastiIO()(AdapterParams(p)).flip
val mem_axi = new NastiIO
val cBundle = new CounterBundle
}
val target = LazyModule(new FPGAZynqTop(p)).module
val slave = Module(new ZynqAXISlave(1)(AdapterParams(p)))
io.cBundle := target.io.cBundle
// ...
}
为 Top 模块生成的 Verilog 代码中的段落如下所示。
module Top(
input clock,
input reset,
// ...
output [31:0] io_cBundle_counter
);
// ...
endmodule
我的目标是 添加计数器 到 rocket class of the Rocket Core module of the fpga-zynq repository。我想计算 ctrl_stalld、id_stall_fpu 等参数 ... 而芯片在 FPGA [=43] 上是 运行 =].
我成功为默认火箭配置 (ZynqConfig) 生成了 Verilog 代码和 Vivado 项目。我把它加载到 ZedBoard 的 FPGA 上并得到了它 运行。 我知道如何在核心中实现计数器,但我不确定如何从外部检索它们。
我想,fpga-zynq/rocket-chip/src/main/scala/rocket/rocket.scala 和 fpga-zynq/common/src/main/scala/Top.scala 可能必须建立,因为我能够访问并进一步连接 Xilinx Vivado 内部的顶级模块 IO 端口2016.2.我假设项目层次结构必须从 rocket module 回溯到 Top module 并且所有模块之间的所有 IO 端口都已连接。
但是,我不相当了解项目层次结构。我找不到 rocket 和 Top 之间的许多模块的连接。我希望这张图片能澄清我想说的话。
黑点和“?”代表未知层次结构(可能更复杂)。
这不是 fpga-zynq 项目层次结构的实际表示。
正如预测的那样,Top 和 rocket 之间的所有模块的所有 I/O 端口都必须连接。 注意:以下解决方案仅适用于单核火箭!
我将完成在 Rocket 核心中实现 32 位计数器并将其与 Top 模块连接的必要步骤。
在哪里可以找到以下 classes:
火箭 -- fpga-zynq/rocket-chip/src/main/scala/rocket/rocket.scala
RocketTile -- fpga-zynq/rocket-chip/src/main/scala/rocket/tile.scala
BaseCorePlexModule -- fpga-zynq/rocket-chip/src/main/scala/coreplex/BaseCoreplexModule.scala
BaseTop -- fpga-zynq/rocket-chip/src/main/scala/rocketchip/BaseTop.scala
置顶 -- fpga-zynq/common/src/main/scala/Top.scala
1. 在火箭中实现计数器 class
首先,我们为我们所有的计数器创建一个新的包,在火箭 class 之外 (在本例中,它只是一个计数器,但您可以随时向捆绑包添加更多计数器和其他参数)
class CounterBundle extends Bundle{
val counter = UInt(OUTPUT, width = 32)
}
我们在 Rocket I/O Bundle 中定义了一个 Bundle 的 实例 class.
class Rocket(implicit p: Parameters) extends CoreModule()(p) {
val io = new Bundle {
val interrupts = new TileInterrupts().asInput
val hartid = UInt(INPUT, xLen)
val imem = new FrontendIO()(p.alterPartial({ case CacheName => "L1I" }))
val dmem = new HellaCacheIO()(p.alterPartial({ case CacheName => "L1D" }))
val ptw = new DatapathPTWIO().flip
val fpu = new FPUIO().flip
val rocc = new RoCCInterface().flip
val cBundle = new CounterBundle
}
// ...
}
由于我们的 "counter" 参数本质上是一条线,我们不能给它自己分配一条线,我们将使用一个 寄存器来计算 和 将其值转移到我们的计数器。如果某些条件为真,计数器将 递增 ,如果重置处于活动状态,则 重置 返回 0。我们把它放在火箭里面 class.
val counter_reg = Reg(init = UInt(0, width = 32))
when(/*some condition*/){
counter_reg := counter_reg + 1.U
}
io.cBundle.counter := counter_reg
火箭就是这样 class。
2. 连接 Rocket class CounterBundle 与 Top class
2.1 火箭 → RocketTile
一个新的Rocket class 实例在RocketTile class.
只需添加
val cBundle = new CounterBundle
到 RocketTile I/O 的。 现在让我们继续 连接 RocketTile class.
内的两个包class RocketTile(clockSignal: Clock = null, resetSignal: Bool = null)
(implicit p: Parameters) extends Tile(clockSignal, resetSignal)(p) {
val buildRocc = p(BuildRoCC)
val usingRocc = !buildRocc.isEmpty
val nRocc = buildRocc.size
val nFPUPorts = buildRocc.filter(_.useFPU).size
val core = Module(new Rocket)
io.cBundle := core.io.cBundle
// ...
}
如果您想知道 RocketTile I/O 的 在哪里定义,请查找 TileIO class 在同一个文件中 (tile.scala).
2.2 RocketTile → BaseCorePlexModule
BaseCorePlexModule 拥有 一组图块 。但是由于我们只是为 单核火箭 创建一个计数器,我们可以 连接集合中第一个 RocketTile 的 Bundle.
首先,添加
val cBundle = new CounterBundle
到 BaseCorePlexModule 正上方的 "abstract class BaseCoreplexBundle"。正如您可能想的那样,BaseCorePlexBundle 包含 BaseCorePlexModule 的所有 I/O。
然后,连接这个Bundle与RocketTile Bundle在BaseCorePlexModule.
abstract class BaseCoreplexModule[+L <: BaseCoreplex, +B <: BaseCoreplexBundle](
c: CoreplexConfig, l: L, b: => B)(implicit val p: Parameters) extends LazyModuleImp(l) with HasCoreplexParameters {
val outer: L = l
val io: B = b
// Build a set of Tiles
val tiles = p(BuildTiles) map { _(reset, p) }
io.cBundle := tiles.head.io.cBundle
// ...
}
2.3 BaseCorePlexModule → BaseTop
这是在进入顶部class之前进行的最后一次连接,因为顶部[=251] =] 包含一个参数 "target",它是 BaseTop.
的 子级同样,将 CounterBundle
的 新实例添加到此 class 的 I/O。 "abstract class BaseTopBundle" 包含 BaseTop 的所有 I/O's。将两者联系起来。
abstract class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle](
val p: Parameters, l: L, b: => B) extends LazyModuleImp(l) {
val outer: L = l
val io: B = b
val coreplex = p(BuildCoreplex)(outer.c, p)
io.cBundle := coreplex.io.cBundle
// ...
}
2.4 BaseTop → Top
Top class 持有类型为 FPGAZynqTop
的参数 "target"val target = LazyModule(new FPGAZynqTop(p)).module
在文件底部,可以找到 FPGAZynqTop 作为 BaseTop
的 子项。因此,我们可以通过"target"参数访问BaseTopI/O的对于TopI/O,我们不打算添加CounterBundle,而是添加Bundle中的32位"counter"参数。这样,就可以在 Vivado 中访问计数器,因为生成的 Verilog 代码将包含 32 位线。
将 32 位 UInt 参数 添加到顶部 I/O
val counter = UInt(OUTPUT, width = 32)
将它与 CounterBundle 中的计数器连接。
class Top(implicit val p: Parameters) extends Module {
val io = new Bundle {
val ps_axi_slave = new NastiIO()(AdapterParams(p)).flip
val mem_axi = new NastiIO
val counter = UInt(OUTPUT, width = 32)
}
val target = LazyModule(new FPGAZynqTop(p)).module
val slave = Module(new ZynqAXISlave(1)(AdapterParams(p)))
io.counter := target.io.cBundle.counter
// ...
}
结论
使用此配置可以在芯片在 FPGA 上运行时计算 Rocket Core 参数。我在 ZedBoard 上测试了它。
使用此设置,您可以简单地向 CounterBundle 添加更多计数器/参数,而无需再次遍历 Rocket 和 Top 之间的所有模块。当然,您仍然需要更改 Top 模块。
编辑
在向 Bundle 添加越来越多的参数后,我意识到每次将所有这些都添加到 Top 模块变得很烦人。您可以将CounterBundle 直接添加到Top 模块I/O。在生成的 Verilog 代码中,CounterBundle 将被提取到所有 i包含信号。
我的新 Top 模块看起来像这样。 一定要从 Rocket 导入 CounterBundle class
import rocket.CounterBundle
class Top(implicit val p: Parameters) extends Module {
val io = new Bundle {
val ps_axi_slave = new NastiIO()(AdapterParams(p)).flip
val mem_axi = new NastiIO
val cBundle = new CounterBundle
}
val target = LazyModule(new FPGAZynqTop(p)).module
val slave = Module(new ZynqAXISlave(1)(AdapterParams(p)))
io.cBundle := target.io.cBundle
// ...
}
为 Top 模块生成的 Verilog 代码中的段落如下所示。
module Top(
input clock,
input reset,
// ...
output [31:0] io_cBundle_counter
);
// ...
endmodule