背景:大约一个月前,我开始使用 Chipyard,以便在 VCU118 上使用 RISC-V 内核构建快速原型。Chipyard 是完美的,但需要我加强并学习 Chisel 和 Rocket-chip 工具,以将互连扩展到我的设计。
第一个连接的硬件是赛灵思提供的 PCIe 到 AXI 桥接 IP,称为 XDMA。fpga-shells
已经为这个 IP 提供了包装器、外壳和覆盖层,因此通过对 Chipyard 系统、HarnessBinders 和 IOBinders 的一些研究,我设法将它连接起来。叠加层是这样放置的:
val overlayOutput = dp(PCIeOverlayKey).last.place(PCIeDesignInput(wrangler=dutWrangler.node, corePLL=harnessSysPLL)).overlayOutput
val (pcieNode: TLNode, pcieIntNode: IntOutwardNode) = (overlayOutput.pcieNode, overlayOutput.intNode)
val (pcieSlaveTLNode: TLIdentityNode, pcieMasterTLNode: TLAsyncSinkNode) = (pcieNode.inward, pcieNode.outward)
有两个从站,但我只展示一个的 IOBinders 和 HarnessBinders。我假设我的另一个 Port mixin 运行正常,因为它与CanHaveMasterTLMMIO
端口完全相同,但具有单独的键和不同的地址范围。我意识到这效率低下,但比创建外部总线更容易。这是 IOBinder,它利用CanHaveMasterTLMMIOPort
将主 MMIO 端口引入系统总线。
class WithXDMASlaveIOPassthrough extends OverrideIOBinder({
(system: CanHaveMasterTLMMIOPort) => {
val io_xdma_slave_pins_temp = IO(DataMirror.internal.chiselTypeClone[HeterogeneousBag[TLBundle]](system.mmio_tl)).suggestName("tl_slave_mmio")
io_xdma_slave_pins_temp <> system.mmio_tl
(Seq(io_xdma_slave_pins_temp), Nil)
}
})
我检索并连接 TestHarness 中的从节点,如下所示:
val inParamsMMIOPeriph = topDesign match { case td: ChipTop =>
td.lazySystem match { case lsys: CanHaveMasterTLMMIOPort =>
lsys.mmioTLNode.edges.in(0)
}
}
val inParamsControl = topDesign match {case td: ChipTop =>
td.lazySystem match { case lsys: CanHaveMasterTLCtrlPort =>
lsys.ctrlTLNode.edges.in(0)
}
}
val pcieClient = TLClientNode(Seq(inParamsMMIOPeriph.master))
val pcieCtrlClient = TLClientNode(Seq(inParamsControl.master))
val connectorNode = TLIdentityNode()
// pcieSlaveTLNode should be driven for both the control slave and the axi bridge slave
connectorNode := pcieClient
connectorNode := pcieCtrlClient
pcieSlaveTLNode :=* connectorNode
最后,我将节点连接到线束绑定器。在这一步中,我遵循了在 Chipyard 中连接 DDR 的方式。
class WithPCIeClient extends OverrideHarnessBinder({
(system: CanHaveMasterTLMMIOPort, th: BaseModule with HasHarnessSignalReferences, ports: Seq[HeterogeneousBag[TLBundle]]) => {
require(ports.size == 1)
th match { case vcu118th: FCMVCU118FPGATestHarnessImp => {
val bundles = vcu118th.fcmOuter.pcieClient.out.map(_._1)
val pcieClientBundle = Wire(new HeterogeneousBag(bundles.map(_.cloneType)))
pcieClientBundle <> DontCare // Some signals aren't being driven to this bundle, but it's hard to know how critical that is. Only happens when myPeripheral is on.
bundles.zip(pcieClientBundle).foreach{case (bundle, io) => bundle <> io}
pcieClientBundle <> ports.head
} }
}
})
我添加了pcieClientBundle <> DontCare
以抑制这些未驱动的信号。这个问题只影响驱动到两个从机的信号。
信号如:
a.bits.user.amba_prot.fetch
a.bits.user.amba_prot.secure
a.bits.user.amba_prot.modifiable
a.bits.user.amba_prot.privileged
在两根电线中都没有被驱动(导致$RefNotInitializedException
FIRRTL 通过)。我知道这些来自,TLToAXI4
但奇怪的是,只有当我将另一个外围设备连接到总线时,它们才会被驱动。该外设不会离开 ChipTop。它有三个主 AXI 总线连接到系统,如下所示:
( peirpheralMasterNode1
:= TLBuffer(BufferParams.default)
:= TLWidthWidget(8)
:= AXI4ToTL()
:= AXI4UserYanker(capMaxFlight=Some(16))
:= AXI4Fragmenter()
:= AXI4IdIndexer(idBits=3)
:= AXI4Buffer()
:= peripheralTop.Master1)
fbus.fromPort(Some("PERIPHERAL_MASTER_1"))() := peripheralMasterNode1
外围设备的 AXI Lite 从站是我怀疑问题所在。它的节点声明如下:
val regCfgSlv = AXI4SlaveNode(Seq(AXI4SlavePortParameters(
slaves = Seq(AXI4SlaveParameters(
address = AddressSet.misaligned(0xf000E0000L, 0x1000L),
resources = regCfgDevice.reg("config"),
// executable = true, // Determines whether processor can execute from this memory.
supportsWrite = TransferSizes(1, 4),
supportsRead = TransferSizes(1, 4),
interleavedId = Some(0))),
beatBytes = 4
)))
它连接到系统总线使用toSlave
sbus.toSlave(Some(portName)){
(peripheralTop.regCfgSlv
:= AXI4Buffer()
:= AXI4UserYanker(capMaxFlight=Some(2))
:= TLToAXI4()
// := TLWidthWidget(sbus.beatBytes))
:= TLFragmenter(4,
p(CacheBlockBytes),
// sbus.beatBytes,
holdFirstDeny = true)
:= TLWidthWidget(sbus.beatBytes))
}
当我通过设置 myPeripheral 的密钥将这个 mixin 外围设备包含在我的配置中时,我收到关于两个 HarnessBinders 客户端捆绑 [ ]a.bits.user.amba_prot
都没有驱动信号的错误。val pcieClientBundle = Wire(new HeterogeneousBag(bundles.map(_.cloneType)))
当我只使用 myPeripheral 或 XDMA 之一时,它就$RefNotInitializedException
消失了。
这是我的配置。我试过WithMyPeripheral
四处走动无济于事。
class CustomRocketConfig extends Config(
new WithPCIeMMIOPort ++ // add default external master port
new WithControlPort ++ // add control port for pcie cfg. // TODO: Crossbar this on MMIO Port? Move both MMIO and Control to a port on System Bus?
new freechips.rocketchip.subsystem.WithDefaultSlavePort ++ // add default external slave port
new WithMyPeripheral(MyPeripheralParams()) ++ // Link up myPeripheral
new freechips.rocketchip.subsystem.WithNBigCores(2) ++
new freechips.rocketchip.subsystem.WithNExtTopInterrupts(3) ++
new chipyard.config.AbstractConfig)
class WithPCIeTweaks extends Config (
new WithPCIeClient ++
new WithPCIeManager ++
new WithPCIeCtrlClient ++ // Same for these harness binders - ME
new WithXDMAMasterIOPassthrough ++
new WithXDMASlaveIOPassthrough ++
new WithXDMACtrlIOPassthrough // I imagine these three IOBinders can be combined into one - ME
//TODO: Probably need harness binder and io binder for interrupt if we use it
)
class myRocketConfig extends Config (
// new WithMyPeripheral(MyPeripheralParams()) ++ // Link up myPeripheral
new WithPCIeTweaks ++
new WithVCU118Tweaks ++
new WithMyVCU118System ++
new CustomRocketConfig
)
我希望我的问题清晰而有趣。什么是a.user.amba_prot
信号,为什么当我连接 XDMA 和外围设备时它们没有被驱动?为什么我可以声明其中之一myPeripheral
或XDMA
,但是,当我连接两者时,这些信号没有驱动程序?我意识到这是一个困难的问题,在一个已经很少被查看的标签中有很多活动部件。如果您花时间阅读本文并提出建议,我们将非常感谢您的善意和专业知识。
编辑:我认为问题可能是 Test Harness 的外交区域和 ChipTop 的外交区域之间的参数协商失败。这是 XDMA 中的控制节点。
val control = AXI4SlaveNode(Seq(AXI4SlavePortParameters(
slaves = Seq(AXI4SlaveParameters(
address = List(AddressSet(c.control, c.ecamMask)),
resources = device.reg("control"),
supportsWrite = TransferSizes(1, 4),
supportsRead = TransferSizes(1, 4),
interleavedId = Some(0))), // AXI4-Lite never interleaves responses
beatBytes = 4)))
这是系统看到的端口。
val mmioTLNode = TLManagerNode(
mmioPortParamsOpt.map(params =>
TLSlavePortParameters.v1(
managers = Seq(TLSlaveParameters.v1(
address = AddressSet.misaligned(params.base, params.size),
resources = device.ranges,
executable = params.executable,
supportsGet = TransferSizes(1, sbus.blockBytes),
supportsPutFull = TransferSizes(1, sbus.blockBytes),
supportsPutPartial = TransferSizes(1, sbus.blockBytes))),
beatBytes = params.beatBytes)).toSeq)
mmioPortParamsOpt.map { params =>
sbus.coupleTo(s"port_named_$portName") {
(mmioTLNode
:= TLBuffer()
:= TLSourceShrinker(1 << params.idBits)
:= TLWidthWidget(sbus.beatBytes)
:= _ )
}
}
param.beatBytes
当前设置为 8 ( site(MemoryBusKey).beatBytes
)。但控制配置从节点为4。