zynq的emio和axi_ZYNQ入门实例——三种GPIO应用、中断系统及软硬件交叉触发调试...

R语言

Delphi

erlang

verilog

dart

ZYNQ入门实例——三种GPIO应用、中断系统及软硬件交叉触发调试

一、前言

Xlinx的ZYNQ系列SOC集成了APU、各种专用外设资源和传统的FPGA逻辑,为ARM+FPGA的应用提供助力,降低功耗和硬件设计难度的同时极大提高两者间传输的带宽。之前在研究生课题中使用过ZYNQ搭建环路系统对算法进行板级验证,但并没有深入使用和理解这个异构平台,今天算是对入门的总结。一款SOC的入门必然是GPIO的使用,而中断则是MCU能保证实时性的必杀武器。硬件调试难度高一直是FPGA的痛点,集成ARM的FPGA更是如此,cross-trigger调试有效地解决了这一问题,所以它也作为入门ZYNQ的必要技能。

二、硬件系统搭建

ZYNQ的三种GPIO分别是MIO、EMIO和AXI-GPIO。PS部分直接连接到芯片引脚的IO叫MIO,经过FPGA再连接到引脚的是EMIO。EMIO可以通过硬件约束指定不同的端口号和电压标准,提高了ARM IO的灵活性。而AXI-GPIO相当于是对ARM IO的补充,通过调用AXI-GPIO IP核与外部通信。以下通过一个实例来说明三种IO的使用方式。

系统功能:使用一个MIO使连接其上的LED闪烁,使用8个EMIO同样与LED连接构成流水灯效果,另外再调用一个5bit位宽的AXI-GPIO IP核以终端模式响应电路板上5个按键。

平台:米联客 MIZ702N (ZYNQ-7020)

配置ZYNQ IP,使能MIO和EMIO,配置EMIO位宽是8bit。

使能Cross Trigger和共享中断。

之后添加AXI-GPIO IP Core,配置位宽并使能其中断功能:

运行Run Automatic Connection最终block design系统结构:

这里使用ILA抓取AXI-GPIO的中断信号。

三、软件编程与AXI-GPIO中断模式解析

Implementation,export hardware with bitstream, launch SDK. BSP中自带了硬件系统所使用到的IP的一些示例代码和文档,为入门提供了很好的帮助。

为了方便复用,对Xilinx提供的API做进一步封装,生成gpiops.h gpiops.c gpio.h gpio.c和gic.h文件。接下来重点讲述GIC相关的代码原理。若要使用中断系统,首先要初始化GIC,和其他IP一样包括查找配置和初始赋值两个步骤,分别由LookupConfig和CfgInitialize两个函数完成。后者实际上初始化了中断处理句柄使其指向了一个空结构。要理解内部原理,需要弄清楚XScuGic的数据结构。

其中Handler实际上是一个函数指针类型,用于定义中断产生时的回调函数。而CallBackRef用于传入InstancePtr,即Gic Instance Pointer。GIC初始化完,要将GIC与中断ID和自定义中断回调函数绑定。

内部的核心代码依然和初始化时一致,只不过换成了输入参数:

下一步该使能中断了,一方面是使用GIC对GPIO中断ID的响应,另一方面是使能AXI-GPIO的中断信号。最后是系统对异常的处理函数,这里将其封装在exception_enable中:

总结来看,中断系统建立的步骤为:

1 初始化GIC

2 连接GIC与中断ID和回调函数

3 使能中断

4 使能异常处理

那么为什么完成上述操作后,中断事件发生会立即执行自定义中断回调函数GpioHandler呢?CPU会将中断向量表存储在特定的寄存器中,读取该寄存器可以获取中断向量表内容,里边存放着各个中断ID对应的中断函数入口地址。跳转指令则有中断控制器完成。

接下来是各个文件的软件代码:

1 /*

2 * main.c3 *4 * Created on: 2020年2月22日5 * Author: s6 */

7

8

9 #include "xparameters.h"

10

11 #include "xstatus.h"

12 #include

13 #include "sleep.h"

14

15 #include "gpiops.h"

16 #include "gpio.h"

17 #include "gic.h"

18

19

20 XGpioPs GpioPs; /*The driver instance for GPIO Device.*/

21 XGpio Gpio;22 XScuGic Intc; /*The Instance of the Interrupt Controller Driver*/

23

24

25 #define printf xil_printf /* Smalller foot-print printf */

26

27 #define LOOP_NUM 8

28

29

30

31

32 static u32 MIO_OUT_PIN_INDEX =7; /*LED button*/

33 static u32 EMIO_OUT_PIN_BASE_INDEX = 54;34 volatile u32 IntrFlag; /*Interrupt Handler Flag*/

35

36 void GpioHandler(void *CallbackRef);37 int setupIntSystem(XScuGic *IntcInstancePtr,XGpio *gpioInstancePtr38 ,u32 IntrId);39

40 intmain()41 {42 intStatus;43 u8 i=0;44 u32 sys_led_out=0x1;45

46 Status = gpiops_initialize(&GpioPs,GPIOPS_DEVICE_ID);47 if (Status !=XST_SUCCESS) {48 returnXST_FAILURE;49 }50

51 Status = gpio_initialize(&Gpio,GPIO_DEVICE_ID);52 if (Status !=XST_SUCCESS) {53 returnXST_FAILURE;54 }55

56

57 /*

58 * Set the direction for the pin to be output and59 * Enable the Output enable for the LED Pin.60 */

61 gpiops_setOutput(&GpioPs,MIO_OUT_PIN_INDEX);62

63 for(i=0;i

67 gpio_setDirect(&Gpio, 1,GPIO_CHANNEL1);68

69 Status = setupIntSystem(&Intc,&Gpio,INTC_GPIO_INTERRUPT_ID);70 if (Status !=XST_SUCCESS) {71 returnXST_FAILURE;72 }73

74 printf("Initialization finish.n");75

76 while(1){77

78 for(i=0;i

80 gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x1);81 usleep(200*1000);82 gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x0);83 }84

85 gpiops_outputValue(&GpioPs, MIO_OUT_PIN_INDEX, sys_led_out);86 sys_led_out = sys_led_out == 0x0 ? 0x1 : 0x0;87 }88 return 0;89 }90

91 int setupIntSystem(XScuGic *IntcInstancePtr,XGpio *gpioInstancePtr92 ,u32 IntrId)93 {94 intResult;95 /*

96 * Initialize the interrupt controller driver so that it is ready to97 * use.98 */

99

100 Result = gic_initialize(&Intc,INTC_DEVICE_ID);101 if (Result !=XST_SUCCESS) {102 returnXST_FAILURE;103 }104

105 XScuGic_SetPriorityTriggerType(IntcInstancePtr, IntrId,106 0xA0, 0x3);107

108 /*

109 * Connect the interrupt handler that will be called when an110 * interrupt occurs for the device.111 */

112 Result =XScuGic_Connect(IntcInstancePtr, IntrId,113 (Xil_ExceptionHandler)GpioHandler, gpioInstancePtr);114 if (Result !=XST_SUCCESS) {115 returnResult;116 }117

118 /*Enable the interrupt for the GPIO device.*/

119 XScuGic_Enable(IntcInstancePtr, IntrId);120

121 /*

122 * Enable the GPIO channel interrupts so that push button can be123 * detected and enable interrupts for the GPIO device124 */

125 XGpio_InterruptEnable(gpioInstancePtr,GPIO_CHANNEL1);126 XGpio_InterruptGlobalEnable(gpioInstancePtr);127

128 /*

129 * Initialize the exception table and register the interrupt130 * controller handler with the exception table131 */

132 exception_enable(&Intc);133

134 IntrFlag = 0;135

136 returnXST_SUCCESS;137 }138

139 void GpioHandler(void *CallbackRef)140 {141 XGpio *GpioPtr = (XGpio *)CallbackRef;142 u32 gpio_inputValue;143

144

145 /*Clear the Interrupt*/

146 XGpio_InterruptClear(GpioPtr, GPIO_CHANNEL1);147 printf("Input interrupt routine.n");148

149 //IntrFlag = 1;

150 gpio_inputValue = gpio_readValue(GpioPtr, 1);151 switch(gpio_inputValue)152 {153 case 30:154 printf("button upn");155 break;156 case 29:157 printf("button centern");158 break;159 case 27:160 printf("button leftn");161 break;162 case 23:163 printf("button rightn");164 break;165 case 15:166 print("button downn");167 break;168 }169

170 }

main.c

1 /*

2 * gpio.h3 *4 * Created on: 2020年2月23日5 * Author: s6 */

7

8 #ifndef SRC_GPIO_H_9 #define SRC_GPIO_H_

10

11 #include "xgpio.h"

12

13 #define GPIO_DEVICE_ID XPAR_GPIO_0_DEVICE_ID

14 #define INTC_GPIO_INTERRUPT_ID XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR

15 #define GPIO_CHANNEL1 0x1F

16

17 int gpio_initialize(XGpio *InstancePtr, u16 DeviceId);18 void gpio_setDirect(XGpio *InstancePtr, unsigned Channel,19 u32 DirectionMask);20 void gpio_outputValue(XGpio *InstancePtr, unsigned Channel, u32 Data);21 u32 gpio_readValue(XGpio *InstancePtr, unsigned Channel);22 #endif /* SRC_GPIO_H_ */

gpio.h

/** gpio.c

*

* Created on: 2020年2月23日

* Author: s*/#include"gpio.h"

int gpio_initialize(XGpio *InstancePtr, u16 DeviceId)

{returnXGpio_Initialize(InstancePtr,DeviceId);

}void gpio_setDirect(XGpio *InstancePtr, unsigned Channel,

u32 DirectionMask)

{

XGpio_SetDataDirection(InstancePtr, Channel,

DirectionMask);

}void gpio_outputValue(XGpio *InstancePtr, unsigned Channel, u32 Data)

{

XGpio_DiscreteWrite(InstancePtr, Channel, Data);

}

u32 gpio_readValue(XGpio*InstancePtr, unsigned Channel)

{returnXGpio_DiscreteRead(InstancePtr, Channel);

}

gpio.c

1 /*

2 * gpiops.c3 *4 * Created on: 2020年2月23日5 * Author: s6 */

7

8 #include "gpiops.h"

9

10 int gpiops_initialize(XGpioPs *InstancePtr,u16 DeviceId)11 {12 XGpioPs_Config *ConfigPtr;13

14 ConfigPtr =XGpioPs_LookupConfig(DeviceId);15 returnXGpioPs_CfgInitialize(InstancePtr, ConfigPtr,16 ConfigPtr->BaseAddr);17 }18

19 void gpiops_setOutput (XGpioPs *InstancePtr,u32 Pin)20 {21 XGpioPs_SetDirectionPin(InstancePtr, Pin, 1);22 XGpioPs_SetOutputEnablePin(InstancePtr, Pin, 1);23 }24

25 void gpiops_setInput(XGpioPs *InstancePtr,u32 Pin)26 {27 XGpioPs_SetDirectionPin(InstancePtr, Pin, 0);28 }29

30 void gpiops_outputValue(XGpioPs *InstancePtr,u32 Pin,u32 Data)31 {32 XGpioPs_WritePin(InstancePtr, Pin, Data);33 }34

35

36 u32 gpiops_readValue(XGpioPs *InstancePtr,u32 Pin)37 {38 /*Read the state of the data so that it can be verified.*/

39 returnXGpioPs_ReadPin(InstancePtr, Pin);40 }

gpiops.c

1 /*

2 * gpio.h3 *4 * Created on: 2020年2月23日5 * Author: s6 */

7

8 #ifndef SRC_GPIOPS_H_9 #define SRC_GPIOPS_H_

10

11 #include "xgpiops.h"

12

13 #define GPIOPS_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID

14

15

16

17 int gpiops_initialize(XGpioPs *InstancePtr,u16 DeviceId);18 void gpiops_setOutput (XGpioPs *InstancePtr,u32 Pin);19 void gpiops_setInput(XGpioPs *InstancePtr,u32 Pin);20 void gpiops_outputValue(XGpioPs *InstancePtr,u32 Pin,u32 Data);21 u32 gpiops_readValue(XGpioPs *InstancePtr,u32 Pin);22

23

24 #endif /* SRC_GPIOPS_H_ */

gpiops.h

1 /*

2 * gic.h3 *4 * Created on: 2020年2月23日5 * Author: s6 */

7

8 #ifndef SRC_GIC_H_9 #define SRC_GIC_H_

10

11 #include "xscugic.h"

12

13 #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID

14

15 s32 gic_initialize(XScuGic *InstancePtr,u16 DeviceId)16 {17 XScuGic_Config *IntcConfig;18

19 IntcConfig =XScuGic_LookupConfig(DeviceId);20 if (NULL ==IntcConfig) {21 returnXST_FAILURE;22 }23

24 returnXScuGic_CfgInitialize(InstancePtr, IntcConfig,25 IntcConfig->CpuBaseAddress);26 }27

28 void exception_enable(void *Data)29 {30 Xil_ExceptionInit();31 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,32 (Xil_ExceptionHandler)XScuGic_InterruptHandler, Data);33

34 /*Enable non-critical exceptions*/

35 Xil_ExceptionEnable();36 }37

38

39 #endif /* SRC_GIC_H_ */

gic.h

四、交叉触发调试

右键工程文件夹->Run As/Debug As分别用于代码下载和调试。SDK基于GDB提供了强大的调试能力,支持断点运行,可查看内部寄存器、地址数值以及汇编代码等。Debug As ->Debug Configuartion,双击System Debugger新建ELF文件。勾选Reset entire system和Program FPGA,因为ELF只是软件,硬件信息存储在bitstream中。最重要的是勾选enable cross-triggering。

点击enable cross-triggering右侧的按钮,按照如下操作使能Processor to Fabric Trigger.

再次create使能Fabric to Processor Trigger:

最后点击Debug下载软硬件代码并进入调试界面。

1 首先尝试PS触发PL调试:

指定中断回调函数起始位置一个断点。然后进入VIVADO,打开Hardware Manager连接硬件。注意此时触发模式选择IN_ONLY。此时不用设置ILA抓取信号的触发条件,因为触发由PS端的PC控制。点击Run Trigger等待触发条件。这时回到SDK点击Resume按钮使代码开始运行。按下任意按键产生中断,此时软件代码运行到断点处停止,ILA随即抓取中断信号。

2 尝试PL触发PS调试:

这回在VIVADO中设置触发模式为OR_TRIG_IN,并启动触发条件为上升沿触发。按下按键,C运行到满足ILA触发条件时C代码立即停止,故PL控制了PS端的程序运行。

可以看到此时程序进入IRQHandler。

串口终端也打印进入中断函数的信息,正确响应中断。到此示例结束。本文虽是对ZYNQ入门的整理,但涉及到的东西很多,包括GPIO应用、中断系统建立和相应机制、调用AXI总线IP核、软件设计以及软硬件交叉触发调试流程。

内容来源于网络,如有侵权请联系客服删除

你可能感兴趣的:(zynq的emio和axi)