Smart ZYNQ板 工程八 将程序固化至QSPI FLASH

ZYNQ 和诸多FPGA一样,从JTAG 下载的二进制文件,断电后是丢失的,为了让程序断电重启后仍然可以工作,需要对程序进行固化,本文将演示,如何把代码固化到板子的QSPI FLASH 芯片上,让系统每次开机从FLASH 芯片加载程序启动

(备注 一般调试不需要固化程序,当程序需要永久保存时候 可以考虑将程序进行固化)

本工程在第7章的基础上进行修改,可选择性查看

本文在 vivado2018.3版本上 演示, 其他版本请自行研究

1. ZYNQ启动流程

1.首先了解ZYNQ的启动过程, ZYNQ的启动过程以 ARM核为主, 上电后 硬件先读取ARM核PS端特定引脚的电平状态以确定 系统从NAND ,QSPI ,SD Card还是 JTAG 启动系统。 启动的方式可以通过板子的拨码开关进行调整

2.ARM的普通FPGA(非ZYNQ)固化是直接将BIT文件通过QSPI写入FLASH就行的,但是对于 ZYNQ 来说,必须要有 PS 端的配合才能固化程序

2. 实际操作演示

具体的启动方式如下图所示, 我们的最小系统板只有 QSPI 和TF的硬件,所以只需要关注QUAD-SPI ,SD,JTAG 三种启动方式就好。也就是看系统上电瞬间MIO5和MIO4两个管脚的电平状态,用来区分不同的上电方式。

在电路上 设计的过程中已将MIO5和MIO4分别接到了 拨动开关上了,也就是按照拨动开关在上电瞬间的状态即可控制系统的启动方式 ,具体可参考板子上的丝印标注

操作,让系统从JTAG 方式启动,以进入调试模式

要想对FLASH 芯片进行程序的固化,我们需要让板子临时从JTAG调试模式进行启动(临时性的) ,只需要在上电前让板子拨码开关保持在S1和S2都在ON的状态再执行上电操作。 如下图所示

2.1软件部分配置

本工程在工程7的基础上 演示固化功能,大部分内容重复 选择性观看

创建一个BLOCK设计

1)IP INTEGRATOR→Create Block Design,在弹出的对话框中输入设计名,最后点击“OK”,如下图所示

2)在右侧的窗口里 ,点击加号,在选择框里搜索ZYNQ,并找到ZYNQ7 PROCESSING SYSTEM ,双击并打开

3)软件自动生成了一个 zynq的block 如下图所示,接下来要做一些相应的设置,双击下图中的ZYNQ核

4)依次在弹窗里找到DDR Configuration→DDR Controller Configuration→DDR3,在Memory Part下拉菜单中根据自己板子上的DDR来选择相应的DDR3,本实验所用到型号:MT41K256M16RE-125,数据位宽选择16bit 最后点击“OK”,如下图所示。

在PS的MIO配置选项的GPIO栏里,增加两路EMIO(因为本次测试的是两个,如果需要增加按键或者其它IO 这里可以对应的调整(这里两路GPIO是为了点亮LED灯看效果演示用的,非固化必须)

5). 重要的一步在block design 的设置界面 使能QSPI的功能 如下图所示(当QSPI 时钟大于40MHZ的时候 就需要勾选Feedback clk)

6)最后 点击“Run Block Automation”如下图所示。在弹出的选项中保持默认,点击“OK”,即可完成对ZYNQ7 Processing System的配置

7)将刚才添加EMIO GPIO 引出 右键GPIO_0—->Make External

8)用线将M_AXI_GP0_ACLK与 FCLK_CLK0连接起来

9)source→Design Source ,右键我们创建的BLOCK工程,点击create HDL wrapper如下图所示。

10)在弹出的对话框里保持默认

11)软件自动为我们生成HDL文件

2.2创建约束文件,并且定义管脚

1)Add Source → Add or create constraints 点Next

因为这个项目没有创建过约束文件 所以这里创建一个约束文件,并在File name 里设置约束文件的名称,并且点击FINISH 完成约束文件的创建

2)Sources → Constraints 里找到刚才创建的约束文件 双击并打开该XDC约束文件

在约束文件里面复制下面代码来对输出的GPIO进行管脚(所有的管脚转接板上丝印都有实际标注对应的IO)

set_property IOSTANDARD LVCMOS33 [get_ports GPIO_0_0_tri_io[0]]
set_property IOSTANDARD LVCMOS33 [get_ports GPIO_0_0_tri_io[1]]

set_property PACKAGE_PIN K16 [get_ports GPIO_0_0_tri_io[0]]
set_property PACKAGE_PIN J16 [get_ports GPIO_0_0_tri_io[1]]

2.3 综合布线和生成bit文件

按下绿色箭头对工程进行编译

按下Generate Bitstream 完成综合以及生成bit文件

2.4 SDK程序编写

1)File→Export→Export hardware…,在弹出的对话框中勾选“include bitstream”,点击“OK”确认,如下图所示。

2)File→Lauch SDK,在弹出的对话框中,保存默认,点击“OK”,如下图所示。

系统将自动打开SDK开发环境

3)新建一个工程 file→new→Application Project,来新建一个“Application Project”,如下图所示。

4)在新建工程名中输入自己的工程名称,点击NEXT

5)选择空工程,点击完成FINISH

6) 在工程中添加main.c文件 src—>New—>Source File 如下图所示

7)在弹出的窗口中填入main.c 并且保存

8).打开刚才创建的main.c

然后 写入以下代码(代码是在 例程的基础上进行精简的) 有一个地方值得注意 EMIO的 IO口编号 是从54开始的,也就是我VIVADO 下创建的 EMIO端口,在PS端都是从54-55-56 依次排序的(小贴士 小于54的是MIO 也就是芯片PS的硬件IO口)

#include "xparameters.h"
#include "xgpiops.h"
#include "xstatus.h"
#include "xplatform_info.h"

#define LED1    	54
#define LED2  		55

#define GPIO_DEVICE_ID  	XPAR_XGPIOPS_0_DEVICE_ID
XGpioPs Gpio;


void Gpio_Init(void){
	XGpioPs_Config *ConfigPtr;

	ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);

	XGpioPs_SetDirectionPin(&Gpio, LED1, 1);
	XGpioPs_SetOutputEnablePin(&Gpio, LED1, 1);

	XGpioPs_SetDirectionPin(&Gpio, LED2, 1);
	XGpioPs_SetOutputEnablePin(&Gpio, LED2, 1);

	XGpioPs_WritePin(&Gpio, LED1, 0);
	XGpioPs_WritePin(&Gpio, LED2, 0);
}


#define LED_DELAY     10000000
volatile int Delay;

int main(void)
{
	Gpio_Init();

	while(1){

		XGpioPs_WritePin(&Gpio, LED1, 1);
		XGpioPs_WritePin(&Gpio, LED2, 0);
		
		for (Delay = 0; Delay < LED_DELAY; Delay++);

		XGpioPs_WritePin(&Gpio, LED1, 0);
		XGpioPs_WritePin(&Gpio, LED2, 1);

		for (Delay = 0; Delay < LED_DELAY; Delay++);

	};

	return 0;
}

3.下载到板子上进行验证

选中工程中的硬件平台,并点击右键→Program FPGA,在弹出的对话框中选择默认,点击“program”,完成FPGA PL部分的Program工作

2)选中我们生成的GPIO工程 展开绿色箭头(RUN)右边的图标,选择Run As→1 Launch on Hardware(System Debugger)

可以看到板子上的LED1灯在闪烁

备注 :如果 RUN 的时候弹出错误 可以按照下面的操作 进行设置 再进行DEBUG

之后点 APPLY 然后 再选择Run As→1 Launch on Hardware(System Debugger)看是否下载成功(如果仍然不行,请对板子进行断电后重试,一般发生这种问题的原因是因为debug的时候跟之前运行的程序产生冲突导致的)

备注 这里的调试下载是没有固化的,断电后 程序会丢失。

以上内容除了BLOCK DESIGN 里设置QSPI的部分和 第七个工程不同外,其他过程几乎相同, 接下来才是 PS部分的固化设置

4. 进行固化的操作

程序固化分为两步1. 创建FSBL模块,2.创建image(boot.bin)文件

在zynq上运行程序的时候,加载过程中肯定需要用到一个文件,那就是fsbl,是zynq启动第一阶段的加载程序,经过了fsbl这一阶段,后面系统才能够运行裸奔程序或者是引导操作系统的u-boot

1)创建FSBL:在SDK中新建工程如下

设置工程名fsbl,点next选择FSBL模板

2)制作BOOT.bin用于固化到FLASH

BOOT.bin由fsbl.elf文件 vivado生成的fpga 的bit文件 app工程的elf文件(这里为led工程的elf)三个文件组成。在生成image的时候要分别填入三个文件的路径, 这里也有个偷懒的方法,就是在点 create boot image之前先选中 我们的APP工程,这样系统会自动为我们添加路径。如下所示

a) 先选中 我们的APP工程目录,这里是LED_CODE( 不是FSBL 也不是BSP 和 platform) 如果一开始已经被选中了,请先用鼠标选择上下任意一个目录,再选择回APP目录,否则路径可能不自动加载

b) 点xilinx –>create boot image

在界面里分别设置 boot.bin的输出路径,以及生成boot.bin需要的三个文件的路径(这里有一个偷懒的方法,就是在点下Create Boot Image 之前先选中我们的APP工程),如下图所示,如果没有出现三个文件的路径,请重试上一步。 如果有三个文件路径,则直接点create image生成镜像

3) 烧录 镜像

生成完boot.bin后,下一步将boot.bin 下载到QSPI flash 中

点Program Flash

按上图操作 导入 boot镜像 文件,和fsbl.elf文件,将flash type 设置为 qspi-x4-sig ,然后将板子拨码开关调整到JTAG模式,并重新上电(或者按下POR RST键),之后再点击PROGRAM 开始下载(必须在JTAG模式下,拨码开关调整到JTAG 模式 并且重启板子或者按下POR RST键)

点击PROGRAM

待程序下载成功后,将跳线位置换回到QSPI上

重新上电(或者按下POR RST键), 正常的话两个LED 会开始闪烁,说明板子的FPGA 已经正常初始化完整,flash 下载成功

如果下载过程中出现下图所示,说明系统上电时没有正常进入JTAG 调试模式,请进入JTAG模式后再尝试下载

以下是完整工程:(板子有多个版本,请按照对应板子的芯片型号进行下载测试)

“Smart ZYNQ板 工程八 将程序固化至QSPI FLASH”的6个回复

  1. 程序烧录时出现下面的情况:
    Zynq> ERROR: [Xicom 50-186] Error while detecting SPI flash device – unrecognized JEDEC id bytes: f0, 64, fd

    Problem in running uboot
    Flash programming initialization failed.

    ERROR: Flash Operation Failed

    1. 检查一下 手上的板子是 Smart ZYNQ 的板子 还是 Smart ZYNQ SP 或者SL的板子, 看看是不是打开成错误的工程了
      这个页面的 工程是 clg400封装的

      如果是自己做的工程,可以先用我网站的上的工程测试看看
      另外 在固化前 先设置成 JTAG模式 然后重新上电 再进行固化试试

  2. 报错:
    Error while detecting SPI flash device – unrecognized JEDEC id bytes: 00, 00, 00
    烧写模式是JTAG模式没问题,但是一直报错。

    解决:
    前面一直烧写不成功,我就多次断电上电重新烧写,知道有一次成功了,后面就一直成功了。

  3. 现在我想实现一种功能,就是我的程序里有一些参数,例如控制目标A,这个量是可以变得,我想要实现的功能是,我把A固化在具体的Flash里,然后程序上电后在这个Falsh里读取出这个参数值。这样的目的是不用每一次在头文件里修改A,而是通过上位机和ARM通讯操作Flash修改A的值,这样的功能应该怎么样实现。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注