06.实践-Zynq7000-PS侧
06.实践-Zynq7000-PS侧

PS (ZYNQ包含 ARM硬核,Block design中进行配置)
microbiaze 使用PL资源构建软核

XSA:xilinx Support Archive 硬件组件/硬件平台文件;
包含了处理器配置信息、外设的连接信息和器件初始化代码等等

域(Domain):提供了应用程序运行的环境,比如OS(操作系统)或者BSP;
BSP(Board Support Packet) : 板级支持包,如底层硬件和应用程序沟通的桥梁;
1.最小系统总结
什么是最小系统?
- 是系统正常工作的最小条件
- 是其他系统建立的基础
最小系统只包含PS端,不包含PL端
ZYNQ最小系统
- ARM Cortex-A9 (CPU)
- DDR3 Memory Controller (存储器控制器)
- UART -- MIO -- USB UART
MPSoC最小系统
- ARM Cortex-A53
- DDR4 Memory Controller
- UART -- MIO --- USB UART
嵌入式系统开发流程
创建Vivado工程
使用IP Intergrator 创建 Processing System
- Block Design 里面添加PS处理器,对PS处理器做配置
- ARM硬核处理器的性能远高于Microblaze软核,(试用其需要消耗FPGA的PL逻辑资源)
生成顶层HDL
- 对BlockDesign做例化
- PS引脚的IO约束不需要用户手动创建XDC文件
(生成Bitstream)并导出Hardware
XSA:硬件组件/硬件平台文件
包含了处理器配置信息、外设的连接信息和器件
为Vitis/Petalinux工作提供硬件组件
在Vitis中进行软件设计
Platform(平台):平台提供了硬件信息和软件配置
Domain(域):提供了应用程序运行的环境,可以是BSP(板机支持包)或OS(操作系统)
Applcation(应用):用户应用程序
System Project(系统工程): 系统工程是个容器,里面包含1个或多个App,这些app运行在同一个平台的不同domains
上,且在同一时间运行
Workspace(工作空间):里面可以包含任意多个Platform和System Project
板级验证:注意防静电处理,尤其是冬天天气干燥,容易击坏开发板的IO引脚

2.GPIO 总结(MIO-控制LED)
2.1 简介
2.1.1 是什么
General Purpose I/O
是ZYNQ PS端的 一个IO外设,用于观测(Input)和控制(Output)器件引脚的状态
框图

PL可编程逻辑(FPGA)
- 可编程部分
- XADC
- Config AES/SHA 配置加密相关
- selectIO 连接PL端引脚和用户端逻辑
- General-Purpose 通用目的接口
- High-Performance Ports 高性能接口
PS处理系统
APU
Memory Interface
- 动态(DDR3)
- 静态 (仅能连接到MIO,无法连接到EMIO)
zynq最大支持1GB的动态存储
IO peripherals(IOP)
- USB
- GigE(千兆以太网)
- SD/SDIO(控制TF卡,emmc)
- GPIO
- ......
Interconnect
Central Interconnect
PL到内存
(egPL侧控制DDR3)
OCM Interconnect
DMA(直接内存访问)
- 实现对大量数据的搬移
- 内存到内存
- 内存到外设
- 实现对大量数据的搬移
MIO 多路复用IO
实现 将来自PS外设和惊天存储器接口的访问多路复用到PS引脚上
EMIO,扩展MIO
- 当PS端引脚不够用时,可以使用EMIO进行扩展,从而使用PL的IO
- 当某个设备硬件已经连接到OL端,但是通过Verilog代码实现比较复杂的时候,可以使用EMIO让PS来驱动
eg.使用PS端的GigE控制器来连接PL侧的网口外设 注意,存在某些IO控制器无法连接到EMIO
SD/SDIO用于MIO,时钟频率可以达到50MHz;但用于EMIO仅有25MHz;具体查看ug585手册说明
2.1.2 封装
ZYNQ PS 中的外设可以通过 MIO( multiplexed I/O,多路复用 I/O)模块连接到 PS 端的引脚上,也可以通过 EMIO( extended multiplexed I/O interface, 扩展多路 I/O 接口) 连接到 PL 端的引脚。

这里是针对PS I/O的Bank划分
bank 502 -- DDR3
bank 500、501 -- MIO
bank 34、35、13(7020独有)-- PL端引脚
可见此封装,PL端引脚并不多
PL侧可以外挂DDR3吗?
理论可以,但可见DDR3消耗引脚多,一般仅在大规模ZYNQ(MPSOC)上才使用PL端外挂DDR3
2.1.3 GPIO 分组
指的GPIO外设的 IO划分

EMIO GPIO I(input)
特性
- 对于Zynq7000,软件通过GPIO外设可以观测和控制54个MIO,及64个EMIO
- 每个GPIO可以独立且动态的编程,作为输入/输出以及中断模式
- GPIO被分为四个Bank,即四个寄存器组(Zynq-7000)
- 软件通过一组存储器映射(momery-mapped)的寄存器来控制GPIO
存储器映射
对于所有外部设备而言,CPU一律把它们看作是地址空间,若这些地址空间挂载的是外设,那么CPU寻址就映射成寄存器;如果挂载的是存储器,则CPU的寻址就映射为存储器的地址。
2.1.4 GPIO 寄存器
寄存器、库函数(HAL,对寄存器的操作进行了一定的封装,更加简单,但简单使用时,可能显得冗余)

- DATA_RO 用于观测器件引脚的状态(读),无论GPIO被配置成输入还是输出
- DATA (写)、 MASK_DATA_LSW | MASK_DATA_MSW(掩码辅助写 低|高)
- DIRM(方向模式寄存器,0--关闭输出驱动,1--使能输出驱动)
- OEN (0-- 关闭输出使能;1--打开输出使能)
2.1.5 查看GPIO数据手册
ug585-Zynq-7000-TRM
The GPIO is controlled by software through a series of memory-mapped registers.
2.2 硬件设计
任务:使用PS端的MIO控制两个LED,实现LED闪烁的效果,闪烁间隔为1s


2.3 软件设计
2.3.1 寄存器操作方式
ug585-Zynq-7000-TRM-- chapter 14.3 Programming Guide(GPIO)


GPIO的基地址: 0xE000A000 (zynq 7000)
gpio.DIRM_0 偏移地址(XGPIOPS_DIRM_OFFSET):0x00000204
gpio.OEN_0 偏移地址(XGPIOPS_OUTEN_OFFSET):0x00000208
MASK_DATA_0_LSW 偏移地址(XGPIOPS_DATA_LSW_OFFSET):0x00000000
查找原理图,对GPIO引脚进行配置(MIO7 -->对应LED灯)
配置方向模式寄存器
Write xx to the gpio.DIRM_0 (Bank0 )
0000_0000_1000_0000 -> xx=0x0000_0080
配置输出使能寄存器
Write xx to the gpio.OEN_0 (Bank0 )
0000_0000_1000_0000 -> xx=0x0000_0080

写数据到GPIO引脚
MASK_DATA_0_LSW
1.掩码操作 mask = 1111_1111_0111_1111=0xff7f 2.写数据 data = 0000_0000_1000_0000=0x0080 (写1) data = 0000_0000_0000_0000=0x0000 (写0) 3.写 xx 到 MASK_DATA_0_LSW 写1 xx= 0xff7f_0080 写0 xx= 0xff7f_0000
2.3.2 寄存器操作-代码编写
创建新工程-->添加 main.c
#include <stdio.h> //标准库形式
#include "xil_io.h" // 自定义库
#include "sleep.h"
//宏定义
#define GPIOS_BASE_ADDRESS 0xE000A000
#define XGPIOPS_DIRM_OFFSET 0x00000204
#define XGPIOPS_OUTEN_OFFSET 0x00000208
#define XGPIOPS_DATA_LSW_OFFSET 0x00000000
int main(){
print("GPIO MIO TEST !\n");
// 对GPIO引脚进行配置-配置方向模式寄存器
xil_Out32(GPIOS_BASE_ADDRESS+XGPIOPS_DIRM_OFFSET,0x0000_0080);
// 对GPIO引脚进行配置-配置输出使能寄存器
xil_Out32(GPIOS_BASE_ADDRESS+XGPIOPS_OUTEN_OFFSET,0x0000_0080);
while(1){
xil_Out32(GPIOS_BASE_ADDRESS+XGPIOPS_DATA_LSW_OFFSET,0xff7f_0080);
sleep(1);
xil_Out32(GPIOS_BASE_ADDRESS+XGPIOPS_DATA_LSW_OFFSET,0xff7f_0000);
sleep(1);
}
// 写数据到GPIO引脚 点亮LED
//xil_Out32(GPIOS_BASE_ADDRESS+XGPIOPS_DATA_LSW_OFFSET,0xff7f_0080);
// 写数据到GPIO引脚 熄灭LED
//xil_Out32(GPIOS_BASE_ADDRESS+XGPIOPS_DATA_LSW_OFFSET,0xff7f_0000);
return 0;
}
2.3.3 库函数HAL操作方式
相比于寄存器方式,将重点放在了算法的控制和用户的逻辑实现层面,而不去关注寄存器的实际地址(封装)
创建新工程-->添加 main.c (参考示例程序,编写自己的代码)
导入方式:plaform.spr-->Board Support Package-->Import examples
中断、轮询两个例子(代码的通用性)
#include <stdio.h>
#include "xparameters.h"
#include "xgpiops.h"
#include "sleep.h"
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define MIO_LED0 7// MIO7
XGpioPs Gpio; //GPIO器件的驱动实例
int main(){
print("GPIO MIO TEST !\n");
XGpioPs_Config *ConfigPtr;
//1.初始化GPIO的驱动
//根据器件的ID来查找器件的配置信息
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
//对GPIO的驱动进行初始化
XGpioPs_CfgInitialize(&Gpio,ConfigPtr,ConfigPtr->BaseAddr);
// 设置引脚的方向 0:输入 1:输出
XGpioPs_SetDirectionPin(&Gpio,MIO_LED0,1);
// 设置输出使能 1:使能输出 0:不使能输出
XGpioPs_SetOutputEnablePin(&Gpio,MIO_LED0,1);
while(1){
// 对引脚输出 高电平, 点亮LED灯-0x1
XGpiosPs_WritePin(&Gpio,MIO_LED0,0x1);
sleep(1);
// 对引脚输出 高电平, 熄灭LED灯-0x0
XGpiosPs_WritePin(&Gpio,MIO_LED0,0x0);
sleep(1);
}
return 0;
}
3. EMIO+按键+控制LED
3.1 差异

对于MIO来说,当输出除能时(取消对输出引脚的控制,输出端口由输入端口所决定),输出端口处于三态(3-state);
对于EMIO来说,输出端口不支持三台,因此不受OEN寄存器的影响
EMIOGPIOTN[x]=DIRM[x]&OEN[x],实现输出的控制

3.2 PS按键控制PS_LED

PS_KEY0
- 按键按下,为低电平
- 按键松开,为高电平


#include <stdio.h>
#include "xparameters.h"
#include "xgpiops.h"
#include "sleep.h"
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define MIO_LED0 7// MIO7
#define MIO_KEY0 12 //MIO12
XGpioPs Gpio; //GPIO器件的驱动实例
int main(){
u32 key_value;
print("GPIO EMIO TEST !\n");
XGpioPs_Config *ConfigPtr;
//1.初始化GPIO的驱动
//根据器件的ID来查找器件的配置信息
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
//对GPIO的驱动进行初始化
XGpioPs_CfgInitialize(&Gpio,ConfigPtr,ConfigPtr->BaseAddr);
// 设置PS_KEY0 引脚的方向 0:输入 1:输出
XGpioPs_SetDirectionPin(&Gpio,MIO_KEY0,0);
// 设置 PS_LED0 输出使能 1:使能输出 0:不使能输出
XGpioPs_SetOutputEnablePin(&Gpio,MIO_LED0,1);
while(1){
key_value=XGpios_ReadPin(&Gpio,MIO_KEY0);
XGpios_WritePin(&Gpio,MIO_LED0,~key_value);
}
return 0;
}
3.3 PL_KEY0-EMIO控制 PS_LED灯
3.3.1 vavido配置




验证完成且正确,执行“Generate Output Products"编译,
观察system_wrapper.v文件,观察端口配置是否引入(若无,新建一个 HDL wrapper顶层模块)


利用IOBUF,构建成了一个双向的I/O引脚
约束GPIO_EMIO_KEY
打开原理图
image-20240816152531901 确定PL_KEY0引脚及电压
保存约束 pin.xdc
生成bit流文件,导出xsa(包含bit流文件),覆盖旧的即可
3.3.2 vitis配置
3.3.2.1 更新硬件信息
- 使用新的xsa更新硬件信息


复位BSP资源,fsbl资源
清除一下工程,再次执行 "Build Project"进行编译
3.3.2.2 修改main.c 代码,实现功能
核心任务:增加EMIO定义,实现控制
// XDC文件决定 GPIO_EMIO[0] 54 GPIO_EMIO[1] 55 GPIO_EMIO[2] 56 . . .
#include <stdio.h>
#include "xparameters.h"
#include "xgpiops.h"
#include "sleep.h"
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define MIO_LED0 7// MIO7
#define MIO_KEY0 12 //MIO12
#define EMIO_KEY0 54 //PL KEY0
XGpioPs Gpio; //GPIO器件的驱动实例
int main(){
u32 pl_key_value;
print("GPIO EMIO TEST !\n");
XGpioPs_Config *ConfigPtr;
//1.初始化GPIO的驱动
//根据器件的ID来查找器件的配置信息
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
//对GPIO的驱动进行初始化
XGpioPs_CfgInitialize(&Gpio,ConfigPtr,ConfigPtr->BaseAddr);
// 设置PL_KEY0 引脚的方向 0:输入 1:输出
XGpioPs_SetDirectionPin(&Gpio,EMIO_KEY0,0);
// 设置 PS_LED0 输出使能 1:使能输出 0:不使能输出
XGpioPs_SetOutputEnablePin(&Gpio,MIO_LED0,1);
while(1){
pl_key_value=XGpios_ReadPin(&Gpio,EMIO_KEY0);
XGpios_WritePin(&Gpio,MIO_LED0,~pl_key_value);
}
return 0;
}
编译工程,下载程序

4. GPIO MIO按键中断

4.1 简介
中断(Interrupt):打断CPU执行正常的程序,转而处理紧急程序,然后返回原程序暂停的程序继续运行,就叫中断
意义:高效处理紧急程序,不会一直占用CPU资源
异常:非中断
- 实时控制:在确定时间内对相应事件作出响应
- 故障处理:检测到故障,需要第一时间处理
- 数据传输:不确定数据何时会到来

上图中,标志ZYNQ的中断ID为52(来自I/O的中断,标记为这个数字)

I/O有很多的引脚,如何确定中断来自的引脚?
需要结合 INT_MASK、INT_STAT进行判断
INT_TYPE | 中断类型 | 电平触发,边沿触发 |
INT_POLARITY | 中断极性 | 电平:高/低电平哪个触发 ;边沿:上or下? |
INT_ANY | 仅在INT_TYPE=边沿触发 时 有效 | 为1,同时支持上升沿和下降沿触发 |
INT_STAT | 中断状态寄存器 | |
INT_MASK | 中断屏蔽寄存器 | |
INT_DIS | 关闭 | |
INT_EN | 打开 |
同时配置INT_DIS、INT_EN 以最后执行的语句为执行结果
GIC 通用中断控制器
4.2 编程指南
UG585 GPIO 中断相关


4.3 例程-1 PS_KEY1 控制PS_LED2
使用底板上的 PS 端的用户按键 PS_KEY1 通过中断控制核心板上 LED2 的亮灭



#include <stdio.h>
#include "xparameters.h"
#include "xgpiops.h"
#include "sleep.h"
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define MIO_LED 0 // MIO0
#define MIO_KEY 11 //MIO11
XGpioPs Gpio; //GPIO器件的驱动实例
int main(){
u32 key_value;
print("GPIO MIO TEST !\n");
XGpioPs_Config *ConfigPtr;
//1.初始化GPIO的驱动
//根据器件的ID来查找器件的配置信息
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
//对GPIO的驱动进行初始化
XGpioPs_CfgInitialize(&Gpio,ConfigPtr,ConfigPtr->BaseAddr);
// 设置PL_KEY1 引脚的方向 0:输入 1:输出
XGpioPs_SetDirectionPin(&Gpio,MIO_KEY,0);
// 设置 MIO_LED 输出使能 1:使能输出 0:不使能输出
XGpioPs_SetOutputEnablePin(&Gpio,MIO_LED,1);
while(1){
key_value=XGpios_ReadPin(&Gpio,MIO_KEY);
XGpios_WritePin(&Gpio,MIO_LED0,~key_value);
}
return 0;
}
4.4 Vitis 常用C语言总结

4.5 中断代码
参照官方模板


// 参照 GpioIntrExample 函数 配置I/O SetupInterruptSystem 配置中断
///////////////////////////// main.c /////////////////////////////
#include <stdio.h>
#include "xparameters.h"
#include "xgpiops.h"
#include "sleep.h"
//中断能与异常
#include "xscugic.h"
#include "xil_exception.h"
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define MIO_LED 0 // MIO0
#define MIO_KEY 11 //MIO11
XGpioPs Gpio; //GPIO器件的驱动实例
#define GPIO_INTERRUPT_ID XPAR_XGPIOPS_0_INTR // ZYNQ:52
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
xScuGIC Intc; //GIC器件驱动实例
void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio, u16 GpioIntrId);
void IntrHandler(void *CallBackRef, u32 Bank, u32 Status);
int main(){
u32 key_value;
print("GPIO Interrupt TEST !\n");
XGpioPs_Config *ConfigPtr;
//1.初始化GPIO的驱动
//根据器件的ID来查找器件的配置信息
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
//对GPIO的驱动进行初始化
XGpioPs_CfgInitialize(&Gpio,ConfigPtr,ConfigPtr->BaseAddr);
// 设置PL_KEY1 引脚的方向 0:输入 1:输出
XGpioPs_SetDirectionPin(&Gpio,MIO_KEY,0);
// 设置 MIO_LED 输出使能 1:使能输出 0:不使能输出
XGpioPs_SetOutputEnablePin(&Gpio,MIO_LED,1);
// GPIO 中断配置
SetupInterruptSystem(&Intc,&Gpio,GPIO_INTERRUPT_ID);
while(1){
key_value=XGpios_ReadPin(&Gpio,MIO_KEY);
XGpios_WritePin(&Gpio,MIO_LED0,~key_value);
}
return 0;
}
///////////// SetupInterruptSystem 函数修改
void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio, u16 GpioIntrId)
{
int Status;
XScuGic_Config *IntcConfig;//GIC配置信息的驱动实例
//异常初始化
xil_ExceptionInit();
//根据中断控制器的器件ID来查找配置信息
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
//根据查找到的配置信息初始化中断控制器
XScuGic_CfgInitialize(GicInstancePtr,IntcConfig,IntcConfig->CpuBaseAddress);
//注册中断请求异常的处理程序
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,Xil_ExceptionHandler)XScuGic_InterruptHandler,
GicInstancePtr);
//关联GPIO中断处理程序
// 方式1: 发生中断 -->XGpioPs_IntrHandler -->IntrHandler(用户自定义中断处理)
XScuGic_Connect(GicInstancePtr, GpioIntrId,(Xil_ExceptionHandler)XGpioPs_IntrHandler,
(void *)Gpio);
XGpioPs_SetCallbackHandler(Gpio,(void *)Gpio,IntrHandler);
/* 方式2:
XScuGic_Connect(GicInstancePtr, GpioIntrId,(Xil_ExceptionHandler)IntrHandler,(void *)Gpio);
此时,IntrHandler 函数中需要依照 XGpioPs_IntrHandler 的配置,清除标志位
执行 XGpioPs_IntrClearPin(...)
/*
//设置GPIO中断的触发类型(仅特定引脚)--下降沿
XGpioPs_SetIntrTypePin(Gpio,MIO_KEY,XGPIOPS_IRQ_TYPE_EDGE_FALLING);
//使能GPIO引脚的中断
XGpioPs_IntrEnablePin(Gpio,MIO_KEY);
//使能GIC的GPIO中断
XScuGic_Enable(GicInstancePtr,GpioIntrId);
//使能异常处理
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
// return XST_SUCCESS;
}

//参照官方例程,编写自己的中断回调函数
void IntrHandler(void *CallBackRef, u32 Bank, u32 Status)
{
XGpioPs *Gpio = (XGpioPs *)CallBackRef;
printf(" Interrupt Detect!");
}
关于按键中断的抖动处理
//参照官方例程,编写自己的中断回调函数,此处仅有一个中断(无需判断是什么中断触发) //(中断中不建议运行太长的程序) u8 key_press=0; u8 led_val = 0; void IntrHandler(void *CallBackRef, u32 Bank, u32 Status) { u8 int_state; XGpioPs *Gpio = (XGpioPs *)CallBackRef; //printf(" Interrupt Detect!"); int_state=XGpioPs_IntrGetStatusPin(Gpio,MIO_KEY); /* 若是针对多个按键中断 则 if(int_state) //判断是引脚 MIO_KEY的中断 key_press=1; */ printf(" int_state = %x!\n",int_state); // 关闭GPIO MIO-KEY引脚 中断 key_press=1; XGpioPs_IntrDisablePin(Gpio,MIO_KEY); } int main(){ // ...... XGpioPs_WritePin(&Gpio,MIO_LED,led_val);//初始值 // ...... while(1){ if(Key_press){ usleep(20000);//20 ms key_value=XGpioPs_ReadPin(&Gpio,MIO_KEY); if(key_value == 0)// 认为按键按下 { led_val=~led_val; XGpioPs_WritePin(&Gpio,MIO_LED,led_val);//初始值 } //使能GPIO引脚的中断 XGpioPs_IntrEnablePin(&Gpio,MIO_KEY); key_press=0; } } // ...... }
5. AXI GPIO 控制按键LED
5.1 AXI GPIO 与 AXI接口
**定义: **AXI GPIO 是 ZYNQ PL部分的一个软核,实现了通用输入输出接口的功能,并通过AXI协议实现了与处理系统的通信,使得开发者可以方便地控制和扩展GPIO接口。
AXI 互联接口作为 ZYNQ PS 和 PL 之间的桥梁, 能够使两者协同工作,进而形成一个完整的、 高度集成的系统
对比项 | GPIO | AXI GPIO |
---|---|---|
资源类型 | 硬核 | 软核(PL端资源实现) |
连接I/O | 可以连接到PS IO 或者 PL IO | 不能连接PS IO ,只能连接PL端 IO |
连接PL逻辑 | 支持(EMIO) | 支持 |
灵活性和可扩展性 | 较低 | 较高 |
- EMIO引脚数目受限
- 针对纯FPGA器件,AXI GPIO更佳
5.2 PS、PL数据交互
https://blog.csdn.net/qq_45362336/article/details/138466433
ARM+FPGA>2? ---通信接口要搞清楚


1、通用 AXI(General Purpose AXI):一条 32 位数据总线,适合 PL 和 PS 之间的中低速通信。接口是透传的不带缓冲。总共有四个通用接口:两个 PS 做主机,另两个 PL 做主机。
2、加速器一致性端口(Accelerator Coherency Port):在 PL 和 APU 内的 SCU 之间的单个异步连接,总线宽度为 64 位。这个端口用来实现 APU cache 和 PL 的单元之间的一致性。PL 是做主机的。
3、高性能端口(High Performance Ports):四个高性能 AXI 接口,带有 FIFO 缓冲来提供“批量”读写操作,并支持 PL 和 PS 中的存储器单元的高速率通信。数据宽度是 32 或 64 位,在所有四个接口中 PL 都是做主机的。
5.3 AXI GPIO 简介
官方手册:pg144-axi-gpio

5.4 硬件设计
调用 AXI GPIO IP 核,使用中断机制,实现领航者底板上 PL 端按键 PL_KEY0控制核心板上 PS 端 LED2 亮灭的功能。

AXI Interconnect IP:用于将一个(或多个)AXI存储器映射(memory-mapped)的主器件连接到一个(或多个)存储器映射的从器件。
仅使用一个 AXI_GPIO,一个M_AXI_GP0,是无法使用AXI Interconnect 的



可以对 ip2intc_irpt 信号线上加一个debug,则可以通过ILA进行信号观察,(当GPIO电平变化时 ,其产生一个高电平脉冲)


5.5 软件设计-官方代码
于 4.GPIO MIO 按键中断基础上进行修改
参照官方示例: xgpio_intr_tapp_example.c
ug585-Chapter7
- Private,Shared and Software Interrupts
- PS and PL Shared Peripheral Interrupts (SPI)
pg144-Chapter3
Vitis中 quick fix 一次性修改
6. 程序固化
6.1 概念
在 ZYNQ SoC 的启动和配置过程中, 既需要 PS 的配置信息,又需要 PL 的配置信息。 为了简化配置 PS和 PL 的处理过程, ZYNQ 的配置顺序与 Xilinx FPGA 的配置顺序有所不同, 差异来源于以下两种类型的文件 :
- FPGA BIT 文件——定义 PL 的行为
- 软件 ELF 文件——运行在 PS 中的程序
**在 ZYNQ 中, PS 作为主器件, PL 可以看作是 PS 的一个外设,因此需要由 PS 来配置 PL。**这个配置顺序的优势是它允许对 PS 单独上电的时候, 此时 PL 不上电,以减小功耗。 不过也有例外, 就是我们在使用JTAG 下载程序的时候, 此时是使用电脑作为主机来配置 PL。
软件代码和配置 FPGA 的 BIT 文件可以存储在连接到 PS 端的配置存储器件中。 PS 支持多种片外非易失性存储器( Quad SPI Flash, NAND Flash, NOR Flash 或 SD 卡) 。领航者 ZYNQ 开发板从硬件上支持 SD卡和 QSPI 作为配置器件。
ZYNQ SoC 的启动由片上的 BootROM 开始。片上 BootROM 是 ZYNQ 芯片上的一块非易失性存储器,它包含了 ZYNQ 所支持的配置器件的驱动, 而且里面的代码是不可修改的。 BootROM 中的代码首先会在片外的非易失性存储器中寻找一个头文件, 头文件里定义了一些启动信息, 用于配置 BootROM 的运行。 这些启动信息包括是程序是否就地执行( excute in place) , FSBL 的偏移地址以及是否为安全模式等。头文件的存在确保 BootROM 能够按照配置器件被格式化后的方式操作。
BootROM 执行之后,下一个配置阶段被称为 First-Stage Boot Loader( FSBL), 它是由设计者所创建的。FSBL 可以配置 DDR 存储器和硬件设计过程中所定义的一些外设。这些器件需要在加载软件应用及配置 PL之前就初始化完毕。
FSBL的工作内容:
- 初始化PS;
- 如果提供了BIT文件,则配置PL;
- 加载裸机应用程序到DDR中,或者加载Second-Stage Boot Loader (SSBL);
- 开始执行裸机应用程序,或者SSBL
PL 的配置是通过处理器配置访问接口( Processor Configuration Access Port, PCAP) 进行的,它允许对PL 进行部分配置或者完全配置。一旦 PS 启动运行之后, PL 可以在任意时刻被配置, FSBL 和应用程序可以清除、 配置以及使能 PL。
6.2 构建ZYNQ启动镜像
Boot ROM 头文件:控制 Boot ROM 设置,比如就地执行、 加密、 FSBL 偏移量、镜像文件大小等
First-Stage Boot Loader
PL 配置文件, 即 BIT 文件
运行在 PS 上的软件应用程序
ZYNQ SoC 使用多个模式引脚来决定配置器件的类型, 软件的存储位置以及其他的系统设置, 这些引脚共享 PS 端的 MIO 引脚。 总共有 7 个模式引脚, 分别为 MIO[8:2]。其中,前四个引脚定义启动模式,第五个引脚定义是否使用 PLL, 第六个和第七个引脚定义上电过程中 MIO bank0 和 bank1 的 bank 电压。
启动过程如下:

在上电复位( Power On Reset, POR) 之后,硬件会采集模式引脚的状态, 禁用器件内部的模块,并根据模式引脚的设置选择是否使能 PS 的时钟锁相环。而在其他的复位条件下, 比如软复位, 硬件不会执行上 述的动作。
Note: PS 的复位按键,属于上电复位( POR),如果按下 PS 的复位按键,硬件会重新采集模式引脚的状态。
6.3 具体操作
硬件操作
- Vivado中 ,Block design 模式下,ZYNQ Processing System 模块修改其配置。 主要包括使能 QSPI 以及 SD 控制器外设, 并分别设置 Bank0 和 Bank1 的电平标准 。
- 生成bit流文件
- 生成 xsa文件
软件操作
创建新工程,添加 xsa文件的界面中需要勾选 Generate boot components (生成启动组件) 选项
创建main.c文件及镜像程序代码,编译工程
工程编译过后可以看到系统工程和应用工程下各自生成了一个 Debug 文件夹,系统工程下的 Debug 文件里包含的是 BOOT.BIN(固化文件),应用工程下的 Debug 文件里包含的是 axi_gpio_fsbl.elf(应用工程烧录文件)
烧写程序
烧写到SD卡中
烧写到QSPI Flash中(连接JTAG下载器)
在 Vitis 软件的菜单栏中点击“ Xilinx->Program Flash”
image-20240817172633872
7. 自定义IP核
自定义 IP 核可以定制化系统设计,以达到设计重用的目的
在 Vivado 软件中,通过创建和封装 IP 向导的方式来自定义 IP 核,支持将当前工程、工程中的模块或者指定文件目录封装成 IP 核,也可以创建一个带有 AXI4 接口的 IP 核,用于 PS 和 PL 的数据通信。