我的目标是将计数器添加到rocket class的 Rocket核心模块的fpga-zynq repository。我想计算 ctrl_stalld,id_stall_fpu 等参数,而芯片在FPGA上运行。
我为默认的火箭配置(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 2016.2内的顶级模块IO端口。我假设项目层次结构必须从火箭模块回溯到顶层模块,并且所有模块的所有IO端口都在连接之间。
但是,我不 完全 了解项目层次结构。我无法在 rocket 和 Top 之间的许多模块上找到连接。我希望这张照片澄清了我想说的话。
黑点和“?”表示未知的层次结构(可能更复杂)。
这是 NOT fpga-zynq项目层次结构的实际表示。
答案 0 :(得分:2)
正如预测的那样,必须连接 Top 和 rocket 之间所有模块的所有I / O端口。 注意:以下解决方案仅适用于单核火箭!
我将完成在Rocket核心中实现32位计数器并将其与Top模块连接所需的步骤。
在哪里可以找到以下类:
火箭 - 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
首先,我们为Rocket类之外的所有计数器创建一个新的Bundle (对于这个例子,它只是一个计数器,但你可以随时添加更多的计数器和其他参数)
class CounterBundle extends Bundle{
val counter = UInt(OUTPUT, width = 32)
}
我们在 Rocket类的 I / O 包中定义了此Bundle 的实例。
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
}
// ...
}
因为我们的"计数器"参数本质上是一根导线,我们无法为自己分配导线,我们会使用寄存器来计算,将其值传输到我们的计数器。如果某些条件为真,计数器将递增,如果重置处于活动状态,重置将返回0。我们把它放在Rocket类中。
val counter_reg = Reg(init = UInt(0, width = 32))
when(/*some condition*/){
counter_reg := counter_reg + 1.U
}
io.cBundle.counter := counter_reg
这是Rocket类的。
在 RocketTile 类中创建了一个新的 Rocket 类实例。
只需添加
val cBundle = new CounterBundle
到RocketTile I / O&#39>。 现在让我们继续并连接RocketTile类中的两个包。
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 类(tile.scala )。
BaseCorePlexModule包含一组磁贴。但由于我们只为单核火箭创建一个计数器,我们只需连接集合中第一个RocketTile的Bundle 。
首先,添加
val cBundle = new CounterBundle
到BaseCorePlexModule正上方的"抽象类BaseCoreplexBundle" 。正如您可能想到的那样,BaseCorePlexBundle保存了BaseCorePlexModule的所有I / O.
然后,连接此Bundle与 BaseCorePlexModule 中的 RocketTile 捆绑包。
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
// ...
}
在进入顶级之前,这是最后一次连接,因为Top类包含参数&#34; target&#34; ,这是BaseTop的子项。
再次,将 CounterBundle的新实例添加到此类的I / O中。 &#34;抽象类BaseTopBundle&#34; 包含BaseTop的所有 I / O&#39> 。
连接两个。
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
// ...
}
Top类包含一个参数&#34; target&#34; 类型为 FPGAZynqTop
val target = LazyModule(new FPGAZynqTop(p)).module
在文件的底部,可以找到FPGAZynqTop作为BaseTop的子项。因此,我们可以通过&#34; target&#34;访问BaseTop I / O.参数。
对于Top I / O,我们不会添加CounterBundle,而是添加32位&#34;计数器&#34;来自Bundle的参数。这样,可以在Vivado中访问计数器,因为生成的Verilog代码将保存32位线。
将 32位UInt参数添加到Top 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将被提取到包含所有信号的所有内容中。
我的新顶级模块看起来像这样。 请务必从Rocket类
导入CounterBundleimport 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