前言
本篇文章主要讲解在Xilinx ZedBoard上通过VxWorks6.9动态加载FPGA bit/bin文件,从而实现软件定义功能目的。本文力求讲述清楚PS动态加载PL的原理和实现方法,并配套完整的演示软件和相关代码进行验证。下面将从以下几个方面进行讲解。
- Zynq7000 PL加载原理设计
- VxWorks6.9动态加载FPGA
开发使用工具说明:
- WorkBench3.3
1、Zynq7000 PL加载原理设计
图1 非安全启动流程框图
如图1所示,非安全启动流程中PS通过芯片的固件程序从存储设备中读取引导软件fsbl到芯片RAM运行从而完成PS的整个启动流程。PL加载的通道则从PS通过AXI总线到达设备配置模块,然后通过PCAP进行加载。
除了JTAG加载PL之外,PL可以使用PCAP进行配置。用户可以在任何时候配置PL,无论是在PS使用FSBL启动后,还是在操作系统启动之后加载PL的镜像。本节介绍配置PL的要求。PL加载前必须先上电,才能进行配置。重复加载需要复位清除PL的配置SRAM单元。
表1 设备配置寄存器表
本文主要讲解PCAP配置PL的流程,其主要是通过配置表1的寄存器进行操作。
(1) PCAP配置PL
发送配置位流,PL必须完成内部清除工作。其状态由DevC的status寄存器中的[PCFG_INIT]位指示。内部环回功能也必须禁用,并且[PCAP_MODE]位必须设置。位流被传输到PL使用DevC DMA。PCFG_DONE标志表示PL配置成功。具体配置流程为:
1. 等待PL([0xF8007014]STATUS-bit[4])将PCFG_INIT设置为高;
2. 设置内部环回为0 ([0xF8007080]MCTRL-bit[4]),关闭内部环回;
3. 设置PCAP_PR和PCAP_MODE为1 ([0xF8007000]CTRL-bit[27:26]),选择并使能PCAP接口进行配置;
4. 启动一个DevC DMA传输;
a)源地址:PL位流的位置
b)目的地址:0xFFFFFFFF
c)源长度:PL位流中32位字的总数
d)目的长度:PL位流中32位字的总数
5. 等待PL ([0xF800700C]INT_STS-bit[2])将PCFG_DONE设置为高。
(2) PL重复配置
PL配置完成后,使用PCAP重新配置PL,必须在Control中将PCAP_MODE和PCAP_PR位设置为1。内部环回功能也必须禁用。发送重配置位流到使用DevC DMA的PL,源和目标与初始配置相同。PL重复配置流程如下:
- 设置PCAP_MODE和PCAP_PR为1, ([0xF8007000]CTRL-bit[27:26]),选择并使能PCAP接口进行配置;
2. 从PL中清除之前的配置;
a)设置PCFG_PROG_B([0xF8007000]CTRL-bit[30])高。
b)设置“PCFG_PROG_B ([0xF8007000]CTRL-bit[30])为低”。
c)检查PCFG_INIT = 0 ([0xF8007014]STATUS-bit[4])。
d)向INT_STS[2]写入1,清除PCFG_DONE_INT。
3.检查PCFG_INIT = 1 (STATUS-bit[4]);
4. 设置INT_PCAP_LPBK为0(MCTRL-bit[4]);
5. 通过向INT_STS[12]写入1来清除D_P_DONE_INT;
6. 启动一个DevC DMA传输;
a)源地址:新PL位流的位置。
b)目的地址:0xFFFF_FFFF。
c)Source Length:新的PL位流中32位字的总数。
d) Destination Length:新PL位流中32位字的总数。
7. 轮询或等待devcfg。INT_STS[D_P_DONE_INT]触发中断;
8. (可选)如果清除了之前的配置,请检查INT_STS上的PCFG_DONE_INT。
如果之前的PL配置没有被使用清除,那么PCFG_DONE标志将不会被设置PCFG_PROG_B。在这种情况下,应该使用D_P_DONE_INT来指示新的位流,需要注意的是,PCAP和ICAP接口是相互排斥的。一次只有一个接口可以与PL配置控制器通信,使用哪个接口是由DevC的控制寄存器中的PCAP_PR位控制的。
2、VxWorks6.9动态加载FPGA
本文主要目的是为了讲解VxWorks6.9系统下动态加载FPGA位流文件。对外提供一个VxWorks系统加载FPGA位流文件的接口函数。在BSP包里面实现fpga_load_driver.c、fpga_load_driver.h文件,如下图所示。
/**************************************************************** * 文件名称: fpga_load_driver.h * 文件标识: 定义头文件 * 内容摘要: ZYNQ7000平台FPGA软件加载驱动. * 其它说明: PS->PL加载,涉及PCAP接口.支持bit和bin格式自适应加载. * * 当前版本: V1.00.00 * 作 者: root * 完成日期: 02/19/2020 ****************************************************************/ #ifndef __FPGA_LOAD_H__ #define __FPGA_LOAD_H__
//#define FPGA_LOAD_DEBUG 1
/* fpga load return value definitions */ #define FPGA_SUCCESS 0 #define FPGA_FAIL -1
int zynq_load_fs(char *pFilePath);
#endif /* __FPGA_LOAD_H__ */ |
1、定义操作寄存器接口
#define ZYNQ_SYS_CTRL_BASEADDR 0xF8000000 #define ZYNQ_DEV_CFG_APB_BASEADDR 0xF8007000
/* Reflect slcr offsets */ struct slcr_regs { UINT32 scl; /* 0x0 */ UINT32 slcr_lock; /* 0x4 */ UINT32 slcr_unlock; /* 0x8 */ UINT32 reserved0_1[61]; UINT32 arm_pll_ctrl; /* 0x100 */ UINT32 ddr_pll_ctrl; /* 0x104 */ UINT32 io_pll_ctrl; /* 0x108 */ UINT32 reserved0_2[5]; UINT32 arm_clk_ctrl; /* 0x120 */ UINT32 ddr_clk_ctrl; /* 0x124 */ UINT32 dci_clk_ctrl; /* 0x128 */ UINT32 aper_clk_ctrl; /* 0x12c */ UINT32 reserved0_3[2]; UINT32 gem0_rclk_ctrl; /* 0x138 */ UINT32 gem1_rclk_ctrl; /* 0x13c */ UINT32 gem0_clk_ctrl; /* 0x140 */ UINT32 gem1_clk_ctrl; /* 0x144 */ UINT32 smc_clk_ctrl; /* 0x148 */ UINT32 lqspi_clk_ctrl; /* 0x14c */ UINT32 sdio_clk_ctrl; /* 0x150 */ UINT32 uart_clk_ctrl; /* 0x154 */ UINT32 spi_clk_ctrl; /* 0x158 */ UINT32 can_clk_ctrl; /* 0x15c */ UINT32 can_mioclk_ctrl; /* 0x160 */ UINT32 dbg_clk_ctrl; /* 0x164 */ UINT32 pcap_clk_ctrl; /* 0x168 */ UINT32 reserved0_4[1]; UINT32 fpga0_clk_ctrl; /* 0x170 */ UINT32 reserved0_5[3]; UINT32 fpga1_clk_ctrl; /* 0x180 */ UINT32 reserved0_6[3]; UINT32 fpga2_clk_ctrl; /* 0x190 */ UINT32 reserved0_7[3]; UINT32 fpga3_clk_ctrl; /* 0x1a0 */ UINT32 reserved0_8[8]; UINT32 clk_621_true; /* 0x1c4 */ UINT32 reserved1[14]; UINT32 pss_rst_ctrl; /* 0x200 */ UINT32 reserved2[15]; UINT32 fpga_rst_ctrl; /* 0x240 */ UINT32 reserved3[5]; UINT32 reboot_status; /* 0x258 */ UINT32 boot_mode; /* 0x25c */ UINT32 reserved4[116]; UINT32 trust_zone; /* 0x430 */ /* FIXME */ UINT32 reserved5_1[63]; UINT32 pss_idcode; /* 0x530 */ UINT32 reserved5_2[51]; UINT32 ddr_urgent; /* 0x600 */ UINT32 reserved6[6]; UINT32 ddr_urgent_sel; /* 0x61c */ UINT32 reserved7[56]; UINT32 mio_pin[54]; /* 0x700 - 0x7D4 */ UINT32 reserved8[74]; UINT32 lvl_shftr_en; /* 0x900 */ UINT32 reserved9[3]; UINT32 ocm_cfg; /* 0x910 */ };
struct devcfg_regs { UINT32 ctrl; /* 0x0 */ UINT32 lock; /* 0x4 */ UINT32 cfg; /* 0x8 */ UINT32 int_sts; /* 0xc */ UINT32 int_mask; /* 0x10 */ UINT32 status; /* 0x14 */ UINT32 dma_src_addr; /* 0x18 */ UINT32 dma_dst_addr; /* 0x1c */ UINT32 dma_src_len; /* 0x20 */ UINT32 dma_dst_len; /* 0x24 */ UINT32 rom_shadow; /* 0x28 */ UINT32 reserved1[2]; UINT32 unlock; /* 0x34 */ UINT32 reserved2[18]; UINT32 mctrl; /* 0x80 */ UINT32 reserved3; UINT32 write_count; /* 0x88 */ UINT32 read_count; /* 0x8c */ };
#define devcfg_base ((struct devcfg_regs *)ZYNQ_DEV_CFG_APB_BASEADDR) #define slcr_base ((struct slcr_regs *)ZYNQ_SYS_CTRL_BASEADDR)
#define vxReadl(addr) *(volatile UINT32 *)(addr) #define vxWritel(data, addr) *(volatile UINT32 *)(addr) = (data) |
2、配置DMA传输初始化
static int zynq_dma_transfer(UINT32 srcbuf, UINT32 srclen, UINT32 dstbuf, UINT32 dstlen) { UINT32 isr_status; struct timeval start_time, now;
/* Set up the transfer */ vxWritel((UINT32)srcbuf, &devcfg_base->dma_src_addr); vxWritel(dstbuf, &devcfg_base->dma_dst_addr); vxWritel(srclen, &devcfg_base->dma_src_len); vxWritel(dstlen, &devcfg_base->dma_dst_len);
isr_status = vxReadl(&devcfg_base->int_sts);
/* Polling the PCAP_INIT status for Set */ gettimeofday(&start_time, NULL); while (!(isr_status & DEVCFG_ISR_DMA_DONE)) { if (isr_status & DEVCFG_ISR_ERROR_FLAGS_MASK) { printf("%s: Error: isr = 0x%08X\n", __func__, isr_status); printf("%s: Write count = 0x%08X\n", __func__, vxReadl(&devcfg_base->write_count)); printf("%s: Read count = 0x%08X\n", __func__, vxReadl(&devcfg_base->read_count));
return FPGA_FAIL; }
gettimeofday(&now, NULL); if (now.tv_sec - start_time.tv_sec > CONFIG_SYS_FPGA_PROG_TIME) { printf("%s: Timeout wait for DMA to complete\n", __func__); return FPGA_FAIL; } isr_status = vxReadl(&devcfg_base->int_sts); }
debug("%s: DMA transfer is done\n", __func__);
/* Clear out the DMA status */ vxWritel(DEVCFG_ISR_DMA_DONE, &devcfg_base->int_sts);
return FPGA_SUCCESS; } |
3、 实现FPGA文件动态加载接口
int zynq_load_fs(char *pFilePath) { int fd; int ret; UINT32 swap, diff; int filehead, fileend, filelen; UINT32 *buf_start = NULL;
fd = open(pFilePath, O_RDONLY, 0777);
if (fd < 0) { printf("no %s such file\n", pFilePath); return FPGA_FAIL; }
filehead = lseek(fd, 0, SEEK_SET); fileend = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); filelen = fileend - filehead;
//dcache_disable(); char *buf = (char *)malloc(filelen); if(NULL == buf) { printf("not enough mem!\n"); return FPGA_FAIL; }
read(fd, (char *)buf, filelen);
buf_start = check_data((UINT8 *)buf, filelen, &swap); diff = (UINT32)buf_start - (UINT32)buf; /* fpga bit file */ if (diff != 0) { ret = zynq_load(buf+diff, filelen-diff, BIT_FULL); } else /* fpga bin file */ { ret = zynq_load(buf, filelen, BIT_FULL); }
//dcache_enable(); if (ret != FPGA_SUCCESS) { printf("fpga load fail!\n"); free(buf); close(fd); return FPGA_FAIL; }
printf("fpga load success!\n"); free(buf); close(fd);
return FPGA_SUCCESS; } |