目录
系列文章:
1. FPGA Partial Reconfiguration 部分重配置(一)-CSDN博客
2. FPGA Partial Reconfiguration 部分重配置(二)——PS重配置PL-CSDN博客
1. 背景介绍
由于PCIe掉电后上位机需重启才能识别。当上位机不便关机时,需保持XDMA上位机通信模块持续运行,并替换系统中局部的数据分析等模块。
基于 ZYNQ 7100 平台,实现远程配置数据分析模块的重构。本节为PS控制PL加载重构文件。
软件:Vivado 2018.3
ZYNQ平台启动与配置
Ref: ZYNQ PS动态配置PL动态加载_ps加载pl代码-CSDN博客
PS重配置PL
Ref:ZYNQ 实现PL动态重构_zynq动态重构demo-CSDN博客
所谓重配置,即在当前硬件比特运行期间,有一份新的比特通过网络、串口等等其他方式传输到平台,平台将其存储在Falsh、DDR等外部存储介质中,随后将硬件在不断电的情况下配置成新的比特流。
对bitstream可以大致分为以下几种情况:
- Vivado下载:Program and debug –> Open Hardware Manager -> open target -> auto connect -> program device 在弹出界面选择bit文件下载即可
- 固定启动,来自外部存储设备,将比特流固化到存储介质,并选择相应的启动方式。
- 动态配置,即上述所说的不断电更新。数据来源一般是下图中的DRAM。
- 在搭建好硬件及软件环境后,要实现PS重配置PL我们需要2个条件,其一是两份不同表现的PL实现的Bin文件,其二是软件部分DEVC 的正确配置使用。
Ref: Zynq使用PCAP实现PL完全重配置 - 知乎 (zhihu.com)
在Zynq FPGA硬件中使用xdevcfg实现AXI-PCAP桥的控制,通过XDcfgtransfer函数将DRAM内准备好的Bitstream流文件从DRAM传送到PCAP,优点是应用简单且不使用PL任何资源,可实现高速数据传输速率28-130MB/s,但存在问题是重构时挂起处理器。
2. 实现思路
- PL工程搭建。
- bin文件准备。
- PS工程搭建。
- 下板验证。
3. 实现过程
3.1 PL工程搭建
见 FPGA Partial Reconfiguration 部分重配置(一)-CSDN博客
3.2 bin文件准备
bin文件
加载需要的文件不支持.bit文件,需要将.bit 文件转换成.bin 文件
在生成bit stream 时直接生成的.bin 文件不能配置成功,需要用tcl 语言将.bit 文件转换成.bin文件:
// write_cfgmem -force -format BIN -interface SMAPx32 -disablebitswap -loadbit ”up 0x0 path” “path”
write_cfgmem -format bin -loadbit "up 0x0 比特文件名.bit" -file 生成的bin文件名.bin -size 128 -force -interface SMAPx32 -disablebitswap
//例如:write_cfgmem -format bin -loadbit "up 0x0 D:/impl_1/top.bit" -file D:/impl_1/top1.bin -force -interface SMAPx32 -disablebitswap
Ref:Xilinx平台远程更新中FPGA读写Flash设计的讨论 - 知乎 (zhihu.com)
Xilinx常用的配置文件格式有三个(大部分情况下知道这三个就足够了):bit/bin/mcs。
直接写入Flash的数据,对应的是bin文件。bin文件和bit文件都是二进制格式,bin文件直接对应Flash中的二进制数据。
用A7 100T为例子,生成一个bit文件作为基准。分析一下三种文件。
- 默认的情况下没有任何约束,生成的bit文件作为基准,取名为 original.bit 。这是原始文件。
- 在Vivado工程生成bit的同时,可以同时生成一个bin文件,取名 original.bin 。这是原始文件直接转换而来,不包含针对Flash的配置。
- 当获取bit文件之后,可以使用 write_cfgmem 命令来生成bin文件和MCS文件。这种方法默认生成的bin文件,取名 g.bin,可以发现g.bin和original.bin两个文件不太一样。 g.bin 可用于Flash。
如果在使用write_cfgmem命令时添加参数 -interface SPIx1,生成文件spix1.bin,会发现spix1.bin和original.bin两个文件几乎一样。这个原因就是,write_cfgmem这个命令生成的文件是给Flash使用的。所以生成的文件内容会随着Flash配置的改变而改变。
而write_cfgmem生成的bin文件,默认情况下认为Flash接口格式为SMAPX8(TBD)(可以参考同时生成的.prm文件),所以生成的bin文件g.bin做了相关处理(一般是位序和大小端的转换)。而SPI模式下,不需要额外的转换,所以生成的bin文件spix1.bin和original.bin几乎一样。类似,如果选择BPIx8模式生成BPIx8.bin,可以发现和SMAPX8模式下的g.bin文件几乎一样。
所以可以明确,生成bit文件时生成的bin文件并不含有配置的相关信息。如果需要使用bin文件,那么用write_cfgmem,并同时提供相关配置接口信息,是更好的方案。上文提到的使用bin文件时可能会犯错,就在于此。
3.3 PS整体重配置PL
此部分实现PS对PL整体的重配置。
Ref:
1. ZYNQ 实现PL动态重构_zynq动态重构demo-CSDN博客
2. Zynq使用PCAP实现PL完全重配置 - 知乎 (zhihu.com)
Zynq-7000的结构分为PS(ARM)和PL(FPGA),当然也可以理解为PL作为一种外设挂载在PS端。在正常的系统加载顺序(FALSH \ SD -> FSBL -> PL ->BITSTRAM ->PS ELF)完成后重新配置PL程序,可以利用XLINX官方BSP逻辑 xdecfg_polled_example 的Demo例程实现PS配置PL比特流,将指定DDR空间的数据配置到FPGA。
原文链接:Zynq-7000 PS重配置PL_pl重配置通道-CSDN博客
1. 点击<File>、<New>、<Application Project>来创建一个Empty Application SDK工程。
2. 导入xdecfg_polled_example demo。
在demo中添加 PL_Reset 函数后如下:
/***************************** Include Files *********************************/
#include "xparameters.h"
#include "xdevcfg.h"
/************************** Constant Definitions *****************************/
/*
* The following constants map to the XPAR parameters created in the
* xparameters.h file. They are only defined here such that a user can easily
* change all the needed parameters in one place.
*/
#define DCFG_DEVICE_ID XPAR_XDCFG_0_DEVICE_ID
/*
* The BIT_STREAM_LOCATION is a dummy address and BIT_STREAM_SIZE_WORDS is a
* dummy size. This has to replaced with the actual location/size of the bitstream.
*
* The 2 LSBs of the Source/Destination address when equal to 2锟絙01 indicate
* the last DMA command of an overall transfer.
* The 2 LSBs of the BIT_STREAM_LOCATION in this example is set to 2b01
* indicating that this is the last DMA transfer (and the only one).
*/
#define BIT_STREAM_LOCATION 0x20000001 /* Bitstream location */
#define BIT_STREAM_SIZE_WORDS 0x4270270 //0xF6EC0 /* Size in Words (32 bit)*/
/* top.bin 0x4270270 w 17416348 Byte; part1 5169356 Byte */
/*
* SLCR registers
*/
#define SLCR_LOCK 0xF8000004 /**< SLCR Write Protection Lock */
#define SLCR_UNLOCK 0xF8000008 /**< SLCR Write Protection Unlock */
#define SLCR_LVL_SHFTR_EN 0xF8000900 /**< SLCR Level Shifters Enable */
#define SLCR_PCAP_CLK_CTRL XPAR_PS7_SLCR_0_S_AXI_BASEADDR + 0x168 /**< SLCR
* PCAP clock control register address
*/
#define SLCR_PCAP_CLK_CTRL_EN_MASK 0x1
#define SLCR_LOCK_VAL 0x767B
#define SLCR_UNLOCK_VAL 0xDF0D
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
int XDcfgPolledExample(XDcfg * DcfgInstance, u16 DeviceId);
void Pl_Reset(XDcfg *_DcfgInstancePtr);
/************************** Variable Definitions *****************************/
XDcfg DcfgInstance; /* Device Configuration Interface Instance */
/*****************************************************************************/
/**
*
* Main function to call the polled mode example.
*
* @param None.
*
* @return
* - XST_SUCCESS if successful
* - XST_FAILURE if unsuccessful
*
* @note None.
*
******************************************************************************/
int main(void)
{
int Status;
// int value1 = 0;
// value1 = BIT_STREAM_LOCATION ;
/*
* Call the example , specify the device ID that is generated in
* xparameters.h.
*/
Pl_Reset(&DcfgInstance); //此行或许可以去掉
Status = XDcfgPolledExample(&DcfgInstance, DCFG_DEVICE_ID);
if (Status != XST_SUCCESS) {
xil_printf("Dcfg Polled Example Test Failed\r\n");
return XST_FAILURE;
}
xil_printf("Successfully ran Dcfg Polled Example Test\r\n");
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function downloads the Non secure bit stream to the FPGA fabric
* using the Device Configuration Interface.
*
* @param DcfgInstPtr is a pointer to the instance of XDcfg driver.
* @param DeviceId is the unique device id of the device.
*
* @return
* - XST_SUCCESS if successful
* - XST_FAILURE if unsuccessful
*
* @note None
*
****************************************************************************/
int XDcfgPolledExample(XDcfg *DcfgInstPtr, u16 DeviceId)
{
int Status;
u32 IntrStsReg = 0;
u32 StatusReg;
u32 PartialCfg = 0;
XDcfg_Config *ConfigPtr;
/*
* Initialize the Device Configuration Interface driver.
*/
ConfigPtr = XDcfg_LookupConfig(DeviceId);
/*
* This is where the virtual address would be used, this example
* uses physical address.
*/
Status = XDcfg_CfgInitialize(DcfgInstPtr, ConfigPtr,
ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
Status = XDcfg_SelfTest(DcfgInstPtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Check first time configuration or partial reconfiguration
*/
IntrStsReg = XDcfg_IntrGetStatus(DcfgInstPtr);
if (IntrStsReg & XDCFG_IXR_DMA_DONE_MASK) {
PartialCfg = 1;
}
/*
* Enable the pcap clock.
*/
StatusReg = Xil_In32(SLCR_PCAP_CLK_CTRL);
if (!(StatusReg & SLCR_PCAP_CLK_CTRL_EN_MASK)) {
Xil_Out32(SLCR_UNLOCK, SLCR_UNLOCK_VAL);
Xil_Out32(SLCR_PCAP_CLK_CTRL,
(StatusReg | SLCR_PCAP_CLK_CTRL_EN_MASK));
Xil_Out32(SLCR_UNLOCK, SLCR_LOCK_VAL);
}
/*
* Disable the level-shifters from PS to PL.
*/
if (!PartialCfg) {
Xil_Out32(SLCR_UNLOCK, SLCR_UNLOCK_VAL);
Xil_Out32(SLCR_LVL_SHFTR_EN, 0xA);
Xil_Out32(SLCR_LOCK, SLCR_LOCK_VAL);
}
/*
* Select PCAP interface for partial reconfiguration
*/
if (PartialCfg) {
XDcfg_EnablePCAP(DcfgInstPtr);
XDcfg_SetControlRegister(DcfgInstPtr, XDCFG_CTRL_PCAP_PR_MASK);
}
/*
* Clear the interrupt status bits
*/
XDcfg_IntrClear(DcfgInstPtr, (XDCFG_IXR_PCFG_DONE_MASK |
XDCFG_IXR_D_P_DONE_MASK |
XDCFG_IXR_DMA_DONE_MASK));
/* Check if DMA command queue is full */
StatusReg = XDcfg_ReadReg(DcfgInstPtr->Config.BaseAddr,
XDCFG_STATUS_OFFSET);
if ((StatusReg & XDCFG_STATUS_DMA_CMD_Q_F_MASK) ==
XDCFG_STATUS_DMA_CMD_Q_F_MASK) {
return XST_FAILURE;
}
/*
* Download bitstream in non secure mode
*/
Pl_Reset(DcfgInstPtr);
XDcfg_Transfer(DcfgInstPtr, (u8 *)BIT_STREAM_LOCATION,
BIT_STREAM_SIZE_WORDS,
(u8 *)XDCFG_DMA_INVALID_ADDRESS,
0, XDCFG_NON_SECURE_PCAP_WRITE);
/* Poll IXR_DMA_DONE */
IntrStsReg = XDcfg_IntrGetStatus(DcfgInstPtr);
while ((IntrStsReg & XDCFG_IXR_DMA_DONE_MASK) !=
XDCFG_IXR_DMA_DONE_MASK) {
IntrStsReg = XDcfg_IntrGetStatus(DcfgInstPtr);
}
if (PartialCfg) {
/* Poll IXR_D_P_DONE */
while ((IntrStsReg & XDCFG_IXR_D_P_DONE_MASK) !=
XDCFG_IXR_D_P_DONE_MASK) {
IntrStsReg = XDcfg_IntrGetStatus(DcfgInstPtr);
}
} else {
/* Poll IXR_PCFG_DONE */
while ((IntrStsReg & XDCFG_IXR_PCFG_DONE_MASK) !=
XDCFG_IXR_PCFG_DONE_MASK) {
IntrStsReg = XDcfg_IntrGetStatus(DcfgInstPtr);
}
/*
* Enable the level-shifters from PS to PL.
*/
Xil_Out32(SLCR_UNLOCK, SLCR_UNLOCK_VAL);
Xil_Out32(SLCR_LVL_SHFTR_EN, 0xF);
Xil_Out32(SLCR_LOCK, SLCR_LOCK_VAL);
}
return XST_SUCCESS;
}
void Pl_Reset(XDcfg *_DcfgInstancePtr)
{
u32 CtrlReg;
CtrlReg = XDcfg_ReadReg(_DcfgInstancePtr->Config.BaseAddr,
XDCFG_CTRL_OFFSET);
XDcfg_WriteReg(_DcfgInstancePtr->Config.BaseAddr,
XDCFG_CTRL_OFFSET, (CtrlReg |
XDCFG_CTRL_PCFG_PROG_B_MASK));
//@lf reset prog
CtrlReg = XDcfg_ReadReg(_DcfgInstancePtr->Config.BaseAddr,
XDCFG_CTRL_OFFSET);
XDcfg_WriteReg(_DcfgInstancePtr->Config.BaseAddr,
XDCFG_CTRL_OFFSET, (CtrlReg &
(~XDCFG_CTRL_PCFG_PROG_B_MASK)));
//@lf wait for reset
while((XDcfg_ReadReg(_DcfgInstancePtr->Config.BaseAddr, XDCFG_STATUS_OFFSET)
& XDCFG_STATUS_PCFG_INIT_MASK) == 1);
//@lf set prog
CtrlReg = XDcfg_ReadReg(_DcfgInstancePtr->Config.BaseAddr,
XDCFG_CTRL_OFFSET);
XDcfg_WriteReg(_DcfgInstancePtr->Config.BaseAddr,
XDCFG_CTRL_OFFSET, (CtrlReg |
XDCFG_CTRL_PCFG_PROG_B_MASK));
//@lf wait for set
while((XDcfg_ReadReg(_DcfgInstancePtr->Config.BaseAddr, XDCFG_STATUS_OFFSET)
& XDCFG_STATUS_PCFG_INIT_MASK) == 0);
}
3. 将准备好的bin文件通过 Xlinx -> Dump/Restore Date File 方式导入DDR。
- start address 对应demo代码中的 BIT_STREAM_LOCATION - 1 ,BIT_STREAM_LOCATION地址实际上是GP-AXI MASTER地址空间的,并不是DDR里面的,;
- size 对应导入bin文件的字节大小(右键属性可查看),demo代码中相关变量:BIT_STREAM_SIZE_WORDS = size * 4 ;
对于函数中的两个关键宏定义,BIT_STREAM_LOCATION 为读取DDR的起始地址。读取时从DDR地址0x3000001 开始读取,这是因为最低 2bit == 2’b01 用来表示此时为最后一次 DMA 传输。BIT_STREAM_SIZE_WORD 的大小为bin文件字节数*4。
————————————————
4. 下板debug或run,注意不用 Reset system 和 Program FPGA。
PL 部分没有比特流,下载时会提醒硬件还未就绪,忽略继续即可。
小结:
- 此部分函数功能仅可以实现 PL整体 top.bin 的重配置,不能实现PL 局部重构。即上面第3步如果在DDR中加载 partial.bin,最终是无法正常运行的。
- 查看 Dump/Restore Date File 方式是否成功将bin文件导入DDR,可以添加地址数据监控:
3.4 PS局部重配置PL
PL部分重构同样可以使用PCAP实现,软件部分一样可以采用xdevcfg_polled_example例程实现。与之前完全重配置的流程基本是一样,也需要使用write_cfgmem将部分流文件转换为bin文件,也需要将bin文件加载到DRAM中,最后通过xdevcfg设备实现部分重构。有一点需要注意,在完全重构的时候为了清除PL内原有配置,在例程中添加了PL_Reset函数,在部分重构中需要去掉此函数。
先下载了整体 top.bin 文件运行,然后去掉代码中的 Pl_Reset(DcfgInstPtr); 下载 partial.bin,确实可以实现PL 局部重构。但是代码疑似有复位机制,下载运行几秒会回到 top.bin 的状态。应该与 XDcfg_Transfer 函数细节有关,待研究。
/*
* Download bitstream in non secure mode
*/
// Pl_Reset(DcfgInstPtr);
XDcfg_Transfer(DcfgInstPtr, (u8 *)BIT_STREAM_LOCATION,
BIT_STREAM_SIZE_WORDS,
(u8 *)XDCFG_DMA_INVALID_ADDRESS,
0, XDCFG_NON_SECURE_PCAP_WRITE);
关于 XDcfg_Transfer 函数:
//
u32 XDcfg_Transfer (
XDcfg * InstancePtr, // XDcfg 实例指针
void * SourcePtr, // bit Stream 地址
u32 SrcWordLength, // bit stream 长度÷4(Size in Words,32bit)
void * DestPtr, // 目标指针
u32 DestWordLength, // 待传输到目标地址的数据长度(Size in Words)
u32 TransferType // 传输类型,参考xdevcfg.h中宏定义
)
4. 问题
5. 分析
6. Reference
1. ZYNQ 实现PL动态重构_zynq动态重构demo-CSDN博客
2. Zynq使用PCAP实现PL部分重配置 - 知乎 (zhihu.com)
3. Xilinx平台远程更新中FPGA读写Flash设计的讨论 - 知乎 (zhihu.com)
4. Zynq-7000 PS重配置PL_pl重配置通道-CSDN博客
5. ZYNQ PS动态配置PL动态加载_ps加载pl代码-CSDN博客
6. ZYNQ学习笔记(3)-局部重构Partial Reconfiguration_动态和部分重构-CSDN博客
7. ZYNQ linux通过xdevcfg在线更新PL BIT-CSDN博客
8. zynq实现动态加载(Partial Reconfiguration)_zynq partial reconfiguration-CSDN博客
9. ZYNQ | 可重构技术PCAP(1)——例程学习 - ZoroGH's Blog
10. ZYNQ | 可重构技术PCAP(2)——Flash启动 - ZoroGH's Blog
11.