基于Versal利用AXI HPC执行coherent传输

作者:Peter Zhou,AMD 工程师;来源:AMD Xilinx开发者社区

有的朋友在Versal的板子上跑Linux时,想知道如何使用AXI HPC端口进行CCI coherent的高速数据传输呢?

在解决这个问题之前,首先我们需要修改一些寄存器的配置,为此要解决另外一个问题:

我们需要在CDO中手动修改0xFF41A040,这个寄存器主要修改enable OWO,并且enable broadcast of inner and outer shareable,然后更新PDI,再生成新的XSA文件。

以下是解决这个问题的步骤:

1. cd to
.runs/impl_1/gen_files.

2. cp lpd_data.cdo lpd_data_noMod.cdo

3. 添加下面的这行脚本到lpd_data.txt. 我将其放在 # LPD PERIPHERAL RESET RELESE之前。 我的设计用到了 NoC coherent 接口 和S_AXI_FPD.

4. 手动编辑:
CCI_CFG_1 (LPD_SLCR), enable OWO
mask_write 0xff41A004 0x3F 0x06
CPU (LPD_SLCR), enable broadcast of inner and outer shareable
mask_write 0xFF41A040 0x3 0x3

5. cd to
.runs/impl_1/

6. cp design_1_wrapper.pdi design_1_wrapper_noMods.pdi

7. 用bootgen来创建新的 PDI文件 :
bootgen -arch versal -image project_1_wrapper.bif -w -o project_1_wrapper.pdi -log info

8. 生成PDI文件后,Vivado 重新使用已更新的 *.pdi 。
set_property platform.full_pdi_file /group/bcapps/demarco/scratch/ibm_cci2022p1/myproj/project_1.runs/impl_1/vck190_axi_cci_wrapper_updated.pdi [current_project]

9. 用 Vivado 导出 *.xsa。 TCL 命令用法如下:
write_hw_platform -fixed -include_bit -force -file /group/bcapps/demarco/scratch/ibm_coherencyTake2/cci500Test/project_1/design_1_wrapper_withMod.xsa

上述步骤就重新配置了相关的寄存器,并重新生成了XSA文件。

接下来,我们来详细讲解如何利用AXI HPC执行coherent传输。

本文提供了PS或PL中在各A72 CPUs与不同的masters比如DMAs之间的I/O coherency的方法。关于cache coherency interconnect的详细信息可以参考Versal Adaptive SoC Technical Reference Manual (AM011). 另外可以参考ARM Cortex-A programmers guide for ARMv8-A 来更好的理解异构中的cache coherency.本文包含了bare-metal和linux的用例。

Hardware Block Design

下图实现了四个AXI CDMA Ips 通过S_AXI_LPD, S_AXI_FPD (S_AXI_GP2), NOC_FPD_CCI_0 and NOC_FPD_CCI_1各自执行数据传输,并且加了两个AXI GPIO Ips来控制四个接口的awcache[3:0]/arcache[3:0] and awprot[2:0]/arprot[2:0] bits.

Network on Chip(NoC)

下图实现了NoC的配置,CDMA2 带着GPIO1 channel 1是连接到NOC_FPD_CCI_0,而CDMA3则带着GPIO1 channel 2是连接到NOC_FPD_CCI_1.


另外,两个NMU或者master port 应该经过CCI来走线,而不是直接接入memory控制器。可以通过在Outputs 这个tab页下的选项“PS Cache Coherent Virtual”来保证上述行为。这个选项将使得所有从NMU到CCI NSU的线路都使用Fixed DestID addressing。见下图:

PL DMA LPD/FPD

PL LPD port (S_AXI_LPD)的连接线是连到CDMA1和GPIO0 通道2的。同时PL FPD port(S_AXI_GP2)是连接到GPIO0的通道1的。

Interrupts

Xilinx Linux DMA IP 的driver需要将DMA的中断信号连接到CPU上,所以PL和PS的中断都需要在CIPS的配置向导中enabled,并将CMDA IP的中断信号连接到其上。

Software

Baremetal

Baremetal 软件分别用于APU和RPU,APU程序用于控制example design,配置source buffers和destination buffers和触发DMA,并且给RPU程序指令去执行memory数据传输。APU也要写cache的destination buffer用于检查是否coherency 维护了DMA的传输。见下面的代码:

/* Write Cache */
for (int Index = 0; Index < BUFFER_BYTESIZE; Index++) {
DstBuffer[Index] = 0xFF;
}

/* Perform DMA Transfer */

xil_printf("Destination buffer readback: 0x%0X\r\n", DstBuffer[0]);
aremetal测试程序是运行在APU的EL3。

有两个重要的配置需要在APU程序中执行:
nable snooping in the S4 port of the CCI-500 (APU)
Set the memory as outer shareable in the MMU table entries
/* Enable snooping of APU caches from CCI */
Xil_Out32(SNOOP_CONTROL_SI4, ENABLE_SNOOP);
/* Set memory as outer cacheable */
Xil_SetTlbAttributes((UINTPTR)DstBuffer, NORM_WB_OUT_CACHE);
dsb();

PL DMA LPD/FPD
PL master有多个路径连接到CCI-550 interconnect。S_AXI_FPD (S_ACE_Lite_FPD)是最常用的一个,另外,S_AXI_LPD port也可以用作coherent port,LPD domain master也可以通过CCI S3 port访问DDR。

为了AXI传输成为coherent与APU snooping一致,CCI控制器需要用到share-ability信息,其定义了AxDOMAIN信号。PS-PL接口没有提供此信号的访问接口,这些信号的驱动是基于AxCACHE的值。AxCACHE的值为非0时,将使得AxDOMAIN被设为outer shareable的值。
/* Set the AxPROT and AxCACHE signals */
XGpio_Initialize(&Gpio, XPAR_GPIO_0_DEVICE_ID);
XGpio_DiscreteWrite(&Gpio, FPD_CHANNEL, AXI_ATTR(PROT_UP_S_D, CACHE_OA_M));

/* Set PL_ACELITE_FPD_TZ defined by PL */
Xil_Out32(PL_ACELITE_FPD_TZ, 0x0);
/* Set the AxPROT and AxCACHE signals */
XGpio_Initialize(&Gpio, XPAR_GPIO_0_DEVICE_ID);
XGpio_DiscreteWrite(&Gpio, LPD_CHANNEL, AXI_ATTR(PROT_UP_S_D, CACHE_OA_M));

/* Set PL_AXI_LPD_TZ defined by PL */
Xil_Out32(PL_AXI_LPD_TZ, 0x0);

/* Route PL_AXI_LPD through CCI */
Xil_Out32(PL_AXI_LPD_Route, 0x1);

DMA engine是通过APU利用CDMA 驱动API来管理的:

/* Perform DMA Transfer */
XAxiCdma_SimpleTransfer(&FpdCDma, (UINTPTR)SrcBuffer, (UINTPTR)DstBuffer, BUFFER_BYTESIZE, NULL, NULL);
while (XAxiCdma_IsBusy(&FpdCDma));
/* Perform DMA Transfer */
XAxiCdma_SimpleTransfer(&LpdCDma, (UINTPTR)SrcBuffer, (UINTPTR)DstBuffer, BUFFER_BYTESIZE, NULL, NULL);
while (XAxiCdma_IsBusy(&LpdCDma));

PL DMA CCI0/1
与PL DMA利用S_AXI_LPD和S_AXI_FPD接口相类似的,PL 利用CCI接口需要专用的AXI GPIO IP来管理AXI AxPROT 和 AxCACHE信号。
/* Set the AxPROT and AxCACHE signals */
XGpio_Initialize(&Gpio, XPAR_GPIO_1_DEVICE_ID);
XGpio_DiscreteWrite(&Gpio, CCI0_CHANNEL, AXI_ATTR(PROT_UP_S_D, CACHE_OA_M));
/* Set the AxPROT and AxCACHE signals */
XGpio_Initialize(&Gpio, XPAR_GPIO_1_DEVICE_ID);
XGpio_DiscreteWrite(&Gpio, CCI1_CHANNEL, AXI_ATTR(PROT_UP_S_D, CACHE_OA_M));

LPD DMA
LPD DMA是通过S3 port连接到CCI-500的,并提供了一个接口去配置AxCACHE的与传输控制相关的bits,并且DMA的驱动包含了XZDma_SetChDataConfig这个可以配置可选项的函数。通过DMA_Ch0_TZ可以获得安全访问的权限配置,在BSP中是没有驱动去控制这个寄存器的。另外,DMA_Route这个寄存器需要配置,以便通过CCI-500来路由数据传输。
/* Configuration settings */
Configure.SrcBurstType = XZDMA_INCR_BURST;
Configure.SrcBurstLen = 0xF;
Configure.DstBurstType = XZDMA_INCR_BURST;
Configure.DstBurstLen = 0xF;
Configure.SrcCache = CACHE_OA_M;
Configure.DstCache = CACHE_OA_M;
XZDma_SetChDataConfig(&ZDma, &Configure);

/* Change TZ bit to be secure master */
Xil_Out32(DMA_Ch0_TZ , 0x0);

/* Route LPD DMA through CCI */
Xil_Out32(DMA_Route , 0x1);

而DMA的传输控制是通过XZDma API来执行的:
/* Transfer data */
XZDma_Transfer Data;
Data.SrcAddr = (UINTPTR)SrcBuffer;
Data.DstAddr = (UINTPTR)DstBuffer;
Data.SrcCoherent = 1;
Data.DstCoherent = 1;
Data.Size = BUFFER_BYTESIZE;
XZDma_Start(&ZDma, &Data, 1);
while(XZDma_ChannelState(&ZDma) == XZDMA_BUSY);

RPU
RPU可以直接访问DDR,也可以通过coherent的路径来访问DDR。RPU0_Route寄存器默认是关闭的,但是设置Coherent bit后可以直接通过CCI-500来传输数据。

RPU0_TZ寄存器默认是配置成安全模式的,所以这个寄存器不需要更改。
/* Route RPU0 through CCI */
Xil_Out32(RPU0_Route, 0x1);

APU通过IPI通信管道来管理RPU的数据传输。RPU程序就是简单的监视IPI管道,响应中断,读取发送过来的消息,获取buffer地址并执行读或写的操作。
APU生成一个消息包含source地址,destination地址和buffer size:
/* Send message to RPU0 */
u32 Msg[3] = {(UINTPTR)SrcBuffer, (UINTPTR)DstBuffer, BUFFER_BYTESIZE};
XIpiPsu_WriteMessage(&IpiInst,DestCpuMask, Msg, 3, XIPIPSU_BUF_TYPE_MSG);
XIpiPsu_TriggerIpi(&IpiInst, DestCpuMask);
XIpiPsu_PollForAck(&IpiInst, DestCpuMask, 100000);

RPU则读这条收到的消息,并执行将source buffer复制数据到destination buffer:
/* Read Incoming Message Buffer Corresponding to Source CPU */
XIpiPsu_ReadMessage(InstancePtr, InstancePtr->Config.TargetList[SrcIndex].Mask, TmpBufPtr, 3, XIPIPSU_BUF_TYPE_MSG);

u8* SrcBuffer = (u8*)TmpBufPtr[0];
u8* DstBuffer = (u8*)TmpBufPtr[1];
for (int Index = 0; Index < TmpBufPtr[2]; Index++) {
DstBuffer[Index] = SrcBuffer[Index];
}

Linux
Linux上的测试程序是在shell命令行执行的,根据 Xilinx Linux Soft DMA Driver wiki页面,执行 Linux DMA Test driver。为了测试硬件cache coherency,需要先准备一些事项。

Cache management
Linux kernel DMA framework维护了基于硬件的coherency架构。

这个例程是为了展示CCI-500提供的cache coherency特性,在每个DMA IP中加上了"dma-coherent"的属性,这个属性表明了硬件是cache coherent的,所以软件无需再考虑维护系统coherency了。
&axi_cdma_0 {
dma-coherent;
}

&axi_cdma_1 {
dma-coherent;
}

&axi_cdma_2 {
dma-coherent;
}

&axi_cdma_3 {
dma-coherent;
}

AXI signaling
为了PL DMA传输是cache coherent的,AxPROT and AxCACHE 信号需要用AXI GPIO IP来配置。在Linux kernel的用户空间执行,并且是在EL1/0(non-secure)的状态下,non-secure属性需要在AxPROT信号中用到。

devmem 0xa4020000 32 0x2b
devmem 0xa4020008 32 0x2b
devmem 0xa4050000 32 0x2b
devmem 0xa4050008 32 0x2b

Register initialization
为了基于PL的masters能有coherency, 需要配置几个控制寄存器。因为安全的原因,Linux用户空间是不能直接访问这些寄存器的,所以需要在系统boot阶段就初始化这些寄存器。可以创建用户CDO文件来配置这些寄存器。

Inner Cache Broadcasting
Linux配置MMU为cacheable的memory,且内部是可共享的以支持SMP的操作。因为kenerl或用户空间程序不能直接修改MMU表,内部cache广播的特性则可以用作内部cacheable的传输的广播。在外围的APU,CCI处理穿过系统的coherency。在APU复位的时候Lpd_apu寄存器的brdc_inner bit必须被写。

TZ bit usage
在Versal板子上,因为安全原因,AxPROT bits是由PL来驱动的而不是PS,并且默认值是设为0的。为了在PL中enable这些信号,PL_AXI_LPD_TZ 和 PL_AXI_FPD_TZ 这两个寄存器需要先被设置。

LPD Routing
PS中LPD AXI接口默认是不经过CCI的,所以PL_AXI_LPD_Route寄存器需要配置之后才能使数据传输路由经过CCI。下面是coherency.cdo文件的应用例子。
write 0x00FF41A040 0x3
write 0x00FD690118 0x0
write 0x00FF510050 0x0
write 0x00FE600018 0x1

此CDO文件之后被加入到用户的BIF文件中,并被用于生成boot image。

the_ROM_image:
{
image {
{ type=bootimage, file=
/project-spec/hw-description/vck190_axi_cci_wrapper.pdi }
{ type=bootloader, file=
/images/linux/plm.elf }
{ core=psm, file=
/images/linux/psmfw.elf }
}
image {
id = 0x1c000000, name=apu_subsystem
{ type=cdo, file=
/images/linux/coherency.cdo}
{ type=raw, load=0x00001000, file=
/images/linux/system-default.dtb }
{ core=a72-0, exception_level=el-3, trustzone, file=
/images/linux/bl31.elf }
{ core=a72-0, exception_level=el-2, file=
/images/linux/u-boot.elf }
}
}

最新文章

最新文章