SpinalHDL之杂项

本文作为SpinalHDL学习笔记第二十八篇,介绍SpinalHDL 使用小组件。

目录:

1.PLIC 映射器

2.插件

1.PLIC 映射器

PLIC 映射器定义了 PLIC(平台级中断控制器)的寄存器生成和访问。

PlicMapper.apply(bus: BusSlaveFactory, mapping: PlicMapping)(gateways : Seq[PlicGateway],targets : Seq[PlicTarget])

PlicMapper 的参数:

• bus:连接此控制器的总线

• mapping:一个映射配置(见上文)

• gateways:用于生成总线访问控制的 PlicGateway(中断源)序列

• targets:生成总线访问控制的 PlicTarget 序列(如:多核)

它遵循 riscv 提供的接口: https://github.com/riscv/riscv-plic-spec/blob/master/riscv-plic.adoc

截至目前,有两种内存映射可用:

PlicMapping.sifive

遵循 SiFive 的 PLIC 映射(例如 E31 核心复合手册 ),基本上是一个成熟的 PLIC

PlicMapping.light

此映射生成更轻量级的 PLIC,但代价是缺少一些可选特性:

• 不读取中断优先级

• 不读取中断的挂起位(必须使用声明 (claim)/完成 (complete) 机制)

• 不读取目标的阈值

剩下的寄存器 & 逻辑会被生成。

2.插件

对于某些设计,您可能希望通过使用某种插件来组合组件的硬件,而不是直接在组件中实现硬件。这可以提供一些关键特性:

• 可以通过在组件的参数中添加新的插件来扩展组件的功能。例如,在 CPU 中添加浮点支持。

• 可以通过使用另一组插件来轻松切换相同功能的各种实现。例如,某个 CPU 乘法器的实现可能在某些 FPGA 上表现良好,而其他实现可能在 ASIC 上表现良好。

• 它避免了非常非常庞大的手写顶层结构,其中一切都必须手动连接的情况。相反,插件可以通过查看/使用其他插件的软件接口来发现它们的关联关系。

VexRiscv 和 NaxRiscv 项目就是这方面的例子。它们是具有大部分是空白的顶层的 CPU,其硬件部分通过插件注入。例如:

• PcPlugin

• FetchPlugin

• DecoderPlugin

• RegFilePlugin

• IntAluPlugin

• …

这些插件将通过他们的服务池进行协调/传递/互连。

虽然 VexRiscv 使用严格的同步二阶段系统(设置 (setup)/构建 (build) 回调 (callback)),但 NaxRiscv 采用了一种更灵活的方法,使用 spinal.core.fber API 来分叉实例化线程,这些线程可以联锁,以确保可行的实例化顺序。

插件 API(Plugin API) 提供了一个类似 NaxRiscv 的系统来定义使用插件的可组合组件。

执行顺序

主要思想是您有多个 2 执行环节:

• 设置 (Setup) 环节,在此环节插件可以联锁/保留。其目的并非开始协调/实例化。

• 构建 (Build) 环节,在此环节插件可以协调/实例化硬件。

构建环节将不会在所有 FiberPlugin 完成其设置环节前启动。

class MyPlugin extends FiberPlugin {
val logic = during setup new Area {
// Here we are executing code in the setup phase
awaitBuild()
// Here we are executing code in the build phase
}
}
class MyPlugin2 extends FiberPlugin {
val logic = during build new Area {
// Here we are executing code in the build phase
}
}

简单示例

这是一个简单的虚设示例,其中包含一个将使用两个插件组合的 SubComponent:

import spinal.core._
import spinal.lib.misc.plugin._
// Let's define a Component with a PluginHost instance
class SubComponent extends Component {
val host = new PluginHost()
}
// Let's define a plugin which create a register
class StatePlugin extends FiberPlugin {
// during build new Area { body } will run the body of code in the Fiber build phase, in the context of the PluginHost
val logic = during build new Area {
val signal = Reg(UInt(32 bits))
}
}
// Let's define a plugin which will make the StatePlugin's register increment
class DriverPlugin extends FiberPlugin {
// We define how to get the instance of StatePlugin.logic from the PluginHost.It is a lazy val, because we can't evaluate it until the plugin is binded to its host.
lazy val sp = host[StatePlugin].logic.get
val logic = during build new Area {
// Generate the increment hardware
sp.signal := sp.signal + 1
}
}
class TopLevel extends Component {
val sub = new SubComponent()
// Here we create plugins and embed them in sub.host
new DriverPlugin().setHost(sub.host)
new StatePlugin().setHost(sub.host)
}

该TopLevel 会生成以下 Verilog 代码:

module TopLevel (
input wire clk,
input wire reset
);
SubComponent sub (
.clk (clk ), //i
.reset (reset) //i
);
endmodule
module SubComponent (
input wire clk,
input wire reset
);
reg [31:0] StatePlugin_logic_signal; //Created by StatePlugin
always @(posedge clk) begin
StatePlugin_logic_signal <= (StatePlugin_logic_signal + 32'h00000001); //incremented by DriverPlugin
end
endmodule

联锁/排序

插件可以通过 Retainer 实例相互联锁。每个插件实例都有一个内置锁,可以通过 retain/release 函数进行控制。这是一个基于上面的 简单示例的例子,但这次, DriverPlugin 将通过由其他插件(在我们的例子中是 SetupPlugin)设置的数量对 StatePlugin.logic.signal 递增。为了确保 DriverPlugin 不会过早生成硬件,SetupPlugin 使用 DriverPlugin.retain/release 函数。

import spinal.core._
import spinal.lib.misc.plugin._
import spinal.core.fiber._
class SubComponent extends Component {
val host = new PluginHost()
}
class StatePlugin extends FiberPlugin {
val logic = during build new Area {
val signal = Reg(UInt(32 bits))
}
}
class DriverPlugin extends FiberPlugin {
// incrementBy will be set by others plugin at elaboration time
var incrementBy = 0
// retainer allows other plugins to create locks, on which this plugin will wait before using incrementBy
val retainer = Retainer()
val logic = during build new Area {
val sp = host[StatePlugin].logic.get
retainer.await()
// Generate the incrementer hardware
sp.signal := sp.signal + incrementBy
}
}
// Let's define a plugin which will modify the DriverPlugin.incrementBy variable because letting it elaborate its hardware
class SetupPlugin extends FiberPlugin {
// during setup { body } will spawn the body of code in the Fiber setup phase (it is before the Fiber build phase)
val logic = during setup new Area {
// *** Setup phase code ***
val dp = host[DriverPlugin]
// Prevent the DriverPlugin from executing its build's body (until release() is called)
val lock = dp.retainer()
// Wait until the fiber phase reached build phase
awaitBuild()
// *** Build phase code ***
// Let's mutate DriverPlugin.incrementBy
dp.incrementBy += 1
// Allows the DriverPlugin to execute its build's body
lock.release()
}
}
class TopLevel extends Component {
val sub = new SubComponent()
sub.host.asHostOf(
new DriverPlugin(),
new StatePlugin(),
new SetupPlugin(),
new SetupPlugin() //Let's add a second SetupPlugin, because we can
)
}

这是生成的 verilog:

module TopLevel (
input wire clk,
input wire reset
);
SubComponent sub (
.clk (clk ), //i
.reset (reset) //i
);
endmodule
module SubComponent (
input wire clk,
input wire reset
);
reg [31:0] StatePlugin_logic_signal;
always @(posedge clk) begin
StatePlugin_logic_signal <= (StatePlugin_logic_signal + 32'h00000002); // + 2 as we have two SetupPlugin
end
endmodule

显然,这些示例对于它们的功能来说有些过度,总体上的思路更多地是:

• 协调/创建插件之间的接口(例如跳转 (jump)/刷新 (flush) 端口)

• 安排实例化(例如解码/调度规范)

• 提供一个可扩展的分布式框架(最小硬编码)

  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千穹凌帝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值