我试图将PCIe设备连接到芯片厂的设计中,使用现有的edge覆盖VCU118 (稍微修改一下,因为我使用的是不同的板,但这不重要)。
@michael-etzkorn已经在Github上发布了一篇关于这个问题的问题文章,其中他们解释了为什么他们只能使用两个不同的时钟来实现这个功能。
如果我能得到一些关于如何做到这一点的提示(这个问题遗漏了信任的一些实现细节),如果不添加额外的时钟(@michael 指出这可能会引起一些问题),我会很感激。
发布于 2022-06-13 14:05:54
根据你要点中的工作,看起来你已经回答了大部分原来的问题,但既然我已经把这个问题打出来了,我会把它作为一个答案。
要连接任何端口,基本上需要做三件事。
IOBinder将系统中的包从系统中取出,并将其穿孔到芯片组。HarnessBinder将ChipTop中的IO连接到线束上。外交谈判外交节点的参数。这个步骤可能是可选的,但是许多模块,比如PCIe覆盖层中的XDMA包装器,都是外交步骤,所以这通常是必需的步骤。
IOBinder
IOBinder可以拿出你的CanHAveMasterTLMMIOPort并为它打孔。
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)
}
})对于每个端口来说,这看起来可能是相对相同的。然而,我在实验上发现,我不得不为CanHaveSlaveTLPort翻转临时引脚连接CanHaveSlaveTLPort。
HarnessBinder
线束绑定器检索该端口并将其连接到外部捆绑包。从线束中检索pcieClient包并连接到从IOBinders返回的ports.head。这本质上是克隆IO并将其连接到ChipTop中的包的一种奇特的函数式编程方法。
class WithPCIeClient extends OverrideHarnessBinder({
(system: CanHaveMasterTLMMIOPort, th: BaseModule with HasHarnessSignalReferences, ports: Seq[HeterogeneousBag[TLBundle]]) => {
require(ports.size == 1)
th match { case vcu118th: XDMAVCU118FPGATestHarnessImp => {
val bundles = vcu118th.xdmavcu118Outer.pcieClient.out.map(_._1)
val pcieClientBundle = Wire(new HeterogeneousBag(bundles.map(_.cloneType)))
// pcieClientBundle <> DontCare
bundles.zip(pcieClientBundle).foreach{case (bundle, io) => bundle <> io}
pcieClientBundle <> ports.head
} }
}
})另外,我应该注意:这并不是连接到线束的理想方式,因为它可能的BundleMap用户字段是生成的,除非您在那里有pcieClientBundle <> DontCare,否则不会驱动它们。我发现我不得不公开AXI端口,并修改覆盖以输出axi节点,以便在testharness和ChipTop区域之间进行外交操作。
有关该问题的说明以及一些更多的信息,请访问:
信号以及为什么它们只在我的HarnessBinder中有条件地未初始化?
所有这些代码都是关于这个问题的。
TestHarness外交关系
val overlayOutput = dp(PCIeOverlayKey).last.place(PCIeDesignInput(wrangler=pcieWrangler.node, corePLL=harnessSysPLL)).overlayOutput
val (pcieNode: TLNode, pcieIntNode: IntOutwardNode) = (overlayOutput.pcieNode, overlayOutput.intNode)
val (pcieSlaveTLNode: TLIdentityNode, pcieMasterTLNode: TLAsyncSinkNode) = (pcieNode.inward, pcieNode.outward)
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时钟组..。(未解决)
pcieWrangler是我连接axi_aclk的尝试。这不对。这只是创建一个与axi_aclk相同的250 axi_aclk频率的第二个时钟,所以它主要工作,但使用第二个时钟是不正确的。
val sysClk2Node = dp(ClockInputOverlayKey).last.place(ClockInputDesignInput()).overlayOutput.node
val pciePLL = dp(PLLFactoryKey)()
pciePLL := sysClk2Node
val pcieClock = ClockSinkNode(freqMHz = 250) // Is this the reference clock?
val pcieWrangler = LazyModule(new ResetWrangler)
val pcieGroup = ClockGroup()
pcieClock := pcieWrangler.node := pcieGroup := pciePLL 也许您可以进行实验,并了解如何将axi_aclk连接为axi异步逻辑的驱动程序:)
我很乐意就此提出一个问题,因为我自己还不知道答案。
回答一些后续问题
我如何知道我应该为PCIe (主控)预留多大的地址范围?
对于控件,可以匹配覆盖0x4000000中的大小。对于主端口,理想情况下,只需将DMA引擎连接到能够访问主机上的完整地址范围的引擎。否则,您必须执行AXI2PCIE条转换逻辑来访问主机内存的不同区域,这并不有趣。
我相信,只有当您连接一个PCIe根复合体时,才会出现这种情况。如果您正在连接设备,则不需要担心中断节点。如果您确实希望添加它,则必须将NExtInterrupts(3)++添加到您的配置中。在我意识到我不需要它之前,我只了解到了这一点和TestHarness中未注释的代码。如果你觉得你做了,我们可以打开一个新的问题,并尝试更充分地回答这个问题。
https://stackoverflow.com/questions/72590994
复制相似问题