如何从 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.scalafpga-zynq/common/src/main/scala/Top.scala 可能必须建立,因为我能够访问并进一步连接 Xilinx Vivado 内部的顶级模块 IO 端口2016.2.我假设项目层次结构必须从 rocket module 回溯到 Top module 并且所有模块之间的所有 IO 端口都已连接。

但是,我相当了解项目层次结构。我找不到 rocketTop 之间的许多模块的连接。我希望这张图片能澄清我想说的话。

箭头代表模块之间的IO连接。

黑点和“?”代表未知层次结构(可能更复杂)。

不是 fpga-zynq 项目层次结构的实际表示。

正如预测的那样,Toprocket 之间的所有模块的所有 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