前面在《ZYNQ-7000使用总结(2)——PS部分的使用》中讲述了ZYNQ-7000中PS部分的用法,主要是对软件的使用以及设计流程进行了介绍。但是在实际使用中,往往会将PL和PS部分配合使用,以充分使用ZYNQ的资源,发挥其优势。对于ZYNQ-7000,PS部分可以作为一个子系统独立工作(上篇文章已经介绍),但是PL部分“不能”独立使用(无JTAG的情况下),需要作为PS部分的扩展部分来使用。下面就介绍一个PS和PL配合使用的例子。如果你没有看上一个例子,这里推荐你看一下,因为这里的很多步骤和前面是相同的,这里就不在配图去做说明了。
我们要实现的功能使用定时器和中断去控制一个LED灯的亮灭。会使用PS部分的一个GPIO、EMIO,PL部分的AXI Timer、AXI GPIO。PS和PL部分的连接时通过EMIO和AXI Interconnect总线连接通信。下面进行详细说明。如下图:
• PL部分的AXI GPIO宽度为1位,连接到板子上的开关“SW5”
• PS部分的GPIO有1位通过EMIO接口连接到板子上的开关“SW7”
• PS部分的GPIO的另外1位通过MIO接口连接到了板子上的LED灯“DS23”(这个是板子设计时就连好的,不需要手动连接)
• PL部分的AXI timer定时器连接到PS部分的中断控制器“Global Interrupt Controller”。当你按下板子上前面指定的开关时,计时器开始计时,计时到了以后计时器中断触发。效果就是按下开关室,LED灯灭,中断触发后,LED灯重新被点亮。
• 除此以外,我们在设计中还增加了两个和Chipscope相关的IP核,这个主要用于调试。后续的文章会介绍使用Chipscope进行硬件设计的调试。
设计步骤
XPS中的工作
打开《ZYNQ-7000使用总结(2)——PS部分的使用》中建立的工程,当然也可以按照前面介绍的重新建立一个工程。在PlanAhead里面双击system.xmp,会打开XPS。在XPS中,点击“Bus Interfaces”选项卡。
然后在左边的“IP Catalog”中,找到General Purpose IO,选择其中的AXI General Purpose IO IP核,双击添加。然后会弹出GPIO的配置对话框,如下图:
我们展开Channel 1,默认的GPIO Data Channel Width是32位的,这里我们只需要1位,所以将32修改为1,其他的保持默认,然后OK。会弹出下面的对话框:
我们刚才在PL部分添加了一个GPIO的IP核,现在我们需要把它连接到PS部分,这里就是选择我们将这个IP核连接到哪一个处理器上面,默认的就是我们前面添加的处理器实例,所以这里保持默认值就可以。我们点击OK后,XPS会自动帮我们将该IP核连接到处理器。这里直接点OK即可。一般,PL部分添加的IP核都是通过AXI Interconnect总线连接到PS部分的。如下图:
这个图我们可以在XPS中的“Graphical Design View”里面去看。
同样的方法,我们再依次从IP Catalog中添加以下IP核:AXI Timer/Counter、ChipScope AXI Monitor、Chipscope Integrated Controller。这些IP核的配置部分全部保持默认值即可,如果找不到,可以直接在IP Catalog里面搜索。
至此,本例子所使用的IP核就添加完了,我们添加的这些模块都属于PL部分。刚才XPS已经帮我们将这些模块和PS部分连接在了一起,我们打开“Port”选项卡,展开我们刚才添加的IP核,对照下面的图看一下是否已经全部连接。如果有没有连接的地方,对照下图进行连接:
展开processing_system7_0,按照下图进行连接。
然后我们添加中断,依旧在刚才的图中找到下图中红色框框起来的那行,点击“L to H: No Connection”,会弹出中断连接设置框。我们将左边的axi_timer_0添加到右边的框,然后我们也会得到中断号,比如此例中,中断号是91。点击OK后,便将PL部分的中断和PS部分的中断控制器连接好了。
然后我们再设置一下Chipscope的部分,留作后续调试使用。选择“Bus Interface”选项卡,展开Chipscope相关的IP核,按照下图那样连接:
接下来,我们需要通过EMIO接口将PS部分的GPIO与PL部分连接起来:点击“Zynq”选项卡,选择I/O Peripherals打开Zynq PS Configuration对话框,在Zynq PS Configuration选项卡中,展开打最下边的GPIO,选择EMIO GPIO(Width),并将IO的默认值64改为1,然后关闭对话框。
最后我们再将GPIO接口设置为外部接口:点击“Ports”选项卡,展开processing_system7_0,在下图所示设置点击“Not connected to Exteranl Ports”,在下拉框中选择“External Ports”。这里要解释一下为什么这么做:PL部分与PS部分的连接,我们可以在XPS中直接就通过AXI Interconnect总线连接好,也可以将PL部分的某些接口设置为外部接口(External Ports)预留下来,这些接口以后对于PS部分是可见的。然后我们就可以在PlanAhead中,在UCF(user constraint file)约束文件中将PL的这些对外接口与PS部分的一些接口或是一些外部设备连接起来。后面会看到我们将这个GPIO接口与开关连接起来。
然后,点击左上角的“Run Design Rule Check”按钮,如果控制台中没有输出错误,那么我们在XPS中的设计就算完成了。
我们可以在“Graphical Design View”选项卡里面看我们的设计走线等,如下图这样:
至此,XPS中的工作就算完成了。我们来回顾一下这部分的工作:其实和上一篇文章里面的例子类似,只是这次除了增加处理器IP核,我们新增加了几个IP核,并且将他们和处理器实例连接起来,也就是将PL部分增加的一些模块与PS连接起来。除此以外,我们还将PL模块的一些接口设置为外部接口,留待以后和一些外部设备进行连接,这部分的连接需要在约束文件里面进行。其实也就是我们常说的IO端口分配。
PlanAhead里面的工作
关闭XPS,回到PlanAhead里面。按照以前的方法,右键点击system.xmp文件,选择“Create Top HDL”创建顶层文件。然后点击左边的“Add Source”,依次进行如下操作创建约束文件:Add Source——Add or Create Constraints——Create File,这里约束文件起名为system,然后点击OK,Finish完成约束文件的创建。
创建完以后,“Sources”选项卡里面的文件如图所示:
接下来,我们就要做约束了,其实就是IO端口分配。这里有两种方法,一种是直接在system.ucf文件里面做约束,比如此例的话,应该输入如下内容:
# Connect to Push Button "SW5"
NET axi_gpio_0_GPIO_IO_pin IOSTANDARD=LVCMOS25 | LOC=G19;
# Connect to Push Button "SW7"
NET processing_system7_0_GPIO_pin IOSTANDARD=LVCMOS25 | LOC=F19;
#后面是注释。第2行的意思是将axi_gpio_0_GPIO_IO_pin接口连接到G19,而G19对应的就是板子上的开关SW5,这个对应关系可以在原理图上查到。电平标准使用LVCMOS 2.5V。最后一行同理,F19对应的是板子上的开关SW7。
完成这个约束以后就可以直接点击左边的“Generate Bitstream”生成.bit配置文件。直接点击这个会包含综合、实现等一系列工作。
另外一种做IO约束的方法是使用图形界面,我个人比较喜欢这一种。这种方法,需要先点击右边的“Run Implementation”,等实现完成以后,菜单栏会多出一个“Layout”,然后依次选择Layout——I/O planning,便会出现下图:
我们展开最下边的Scalar ports,按照下图红框所示做选择:
这样完了以后,会自动在ucf文件里面生成约束语句。然后我们再点击Generate Bitstream,生成FPGA配置文件(.bit)文件。
我们还可以在右边展开“RTL Analysis”,打开其中的“Schematic”来查看我们的设计,如下图这样:
对应的“Implementation”里面也有。待比特流文件生成以后,我们在菜单栏一次选择:File——Export——Export Hardware for SDK,出现如下图:
这里如上一篇文章说的那样导出去,因为bug的存在,这里依旧去掉“Launch SDK”选项。导出去以后,PlanAhead里面的工作就算完成了。这里我们回顾一下在PlanAhead里面做的工作:其实就是做了一下文件约束,然后进行了综合、实现,到最后生成比特流文件,将该文件和硬件平台一起导出去。导出的位置可以选,默认位置在工程目录下,具体见上一篇文章。生成的比特流文件时用于配置FPGA的,也就是用于配置PL部分的。硬件平台是PS部分的。
SDK里面的工作
关闭PlanAhead,打开SDK。安装上一篇文章中的方法建立helloworld应用程序工程。然后将下面的代码拷贝到helloworld.c中,覆盖掉原来的代码:
/*
* helloworld.c: simple test application
*/
#include
#include "platform.h"
#include "xil_types.h"
#include "xgpio.h"
#include "xtmrctr.h"
#include "xparameters.h"
#include "xgpiops.h"
#include "xil_io.h"
#include "xil_exception.h"
#include "xscugic.h"
static XGpioPs psGpioInstancePtr;
extern XGpioPs_Config XGpioPs_ConfigTable[XPAR_XGPIOPS_NUM_INSTANCES];
static int iPinNumber = 10;
XScuGic InterruptController; /* Instance of the Interrupt Controller */
static XScuGic_Config *GicConfig;/* The configuration parameters of the
controller */
static int InterruptFlag;
//void print(char *str);
extern char inbyte(void);
void Timer_InterruptHandler(void *data, u8 TmrCtrNumber)
{
print("\r\n");
print("\r\n");
print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n");
print(" Inside Timer ISR \n \r ");
XTmrCtr_Stop(data,TmrCtrNumber);
// PS GPIO Writting
print("LED 'DS23' Turned ON \r\n");
XGpioPs_WritePin(&psGpioInstancePtr,iPinNumber,1);
XTmrCtr_Reset(data,TmrCtrNumber);
print(" Timer ISR Exit\n \n \r");
print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n");
print("\r\n");
print("\r\n");
InterruptFlag = 1;
}
int SetUpInterruptSystem(XScuGic *XScuGicInstancePtr)
{
/*
* Connect the interrupt controller interrupt handler to the hardware
* interrupt handling logic in the ARM processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler,
XScuGicInstancePtr);
/*
* Enable interrupts in the ARM
*/
Xil_ExceptionEnable();
return XST_SUCCESS;
}
int ScuGicInterrupt_Init(u16 DeviceId,XTmrCtr *TimerInstancePtr)
{
int Status;
/*
* Initialize the interrupt controller driver so that it is ready to
* use.
* */
GicConfig = XScuGic_LookupConfig(DeviceId);
if (NULL == GicConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(&InterruptController, GicConfig,
GicConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Setup the Interrupt System
* */
Status = SetUpInterruptSystem(&InterruptController);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Connect a device driver handler that will be called when an
* interrupt for the device occurs, the device driver handler performs
* the specific interrupt processing for the device
*/
Status = XScuGic_Connect(&InterruptController,
XPAR_FABRIC_AXI_TIMER_0_INTERRUPT_INTR,
(Xil_ExceptionHandler)XTmrCtr_InterruptHandler,
(void *)TimerInstancePtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Enable the interrupt for the device and then cause (simulate) an
* interrupt so the handlers will be called
*/
XScuGic_Enable(&InterruptController, XPAR_FABRIC_AXI_TIMER_0_INTERRUPT_INTR);
return XST_SUCCESS;
}
int main()
{
static XGpio GPIOInstance_Ptr;
XGpioPs_Config*GpioConfigPtr;
XTmrCtr TimerInstancePtr;
int xStatus;
u32 Readstatus=0,OldReadStatus=0;
//u32 EffectiveAdress = 0xE000A000;
int iPinNumberEMIO = 54;
u32 uPinDirectionEMIO = 0x0;
// Input Pin
// Pin direction
u32 uPinDirection = 0x1;
int exit_flag,choice,internal_choice;
init_platform();
/* data = *(u32 *)(0x42800004);
print("OK \n");
data = *(u32 *)(0x41200004);
print("OK-1 \n");
*/
print("##### Application Starts #####\n\r");
print("\r\n");
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Step-1 :AXI GPIO Initialization
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
xStatus = XGpio_Initialize(&GPIOInstance_Ptr,XPAR_AXI_GPIO_0_DEVICE_ID);
if(XST_SUCCESS != xStatus)
print("GPIO INIT FAILED\n\r");
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Step-2 :AXI GPIO Set the Direction
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
XGpio_SetDataDirection(&GPIOInstance_Ptr, 1,1);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Step-3 :AXI Timer Initialization
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
xStatus = XTmrCtr_Initialize(&TimerInstancePtr,XPAR_AXI_TIMER_0_DEVICE_ID);
if(XST_SUCCESS != xStatus)
print("TIMER INIT FAILED \n\r");
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Step-4 :Set Timer Handler
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
XTmrCtr_SetHandler(&TimerInstancePtr,
Timer_InterruptHandler,
&TimerInstancePtr);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Step-5 :Setting timer Reset Value
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
XTmrCtr_SetResetValue(&TimerInstancePtr,
0, //Change with generic value
0xf0000000);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Step-6 :Setting timer Option (Interrupt Mode And Auto Reload )
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
XTmrCtr_SetOptions(&TimerInstancePtr,
XPAR_AXI_TIMER_0_DEVICE_ID,
(XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION ));
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Step-7 :PS GPIO Intialization
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GpioConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
if(GpioConfigPtr == NULL)
return XST_FAILURE;
xStatus = XGpioPs_CfgInitialize(&psGpioInstancePtr,
GpioConfigPtr,
GpioConfigPtr->BaseAddr);
if(XST_SUCCESS != xStatus)
print(" PS GPIO INIT FAILED \n\r");
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Step-8 :PS GPIO pin setting to Output
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
XGpioPs_SetDirectionPin(&psGpioInstancePtr, iPinNumber,uPinDirection);
XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, iPinNumber,1);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Step-9 :EMIO PIN Setting to Input port
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
XGpioPs_SetDirectionPin(&psGpioInstancePtr,
iPinNumberEMIO,uPinDirectionEMIO);
XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, iPinNumberEMIO,0);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Step-10 : SCUGIC interrupt controller Intialization
//Registration of the Timer ISR
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
xStatus=
ScuGicInterrupt_Init(XPAR_PS7_SCUGIC_0_DEVICE_ID,&TimerInstancePtr);
if(XST_SUCCESS != xStatus)
print(" :( SCUGIC INIT FAILED \n\r");
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Step-11 :User selection procedure to select and execute tests
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
exit_flag = 0;
while(exit_flag != 1)
{
print(" SELECT the Operation from the Below Menu \r\n");
print("###################### Menu Starts ########################\r\n");
print("Press '1' to use NORMAL GPIO as an input (SW5 switch)\r\n");
print("Press '2' to use EMIO as an input (SW7 switch)\r\n");
print("Press any other key to Exit\r\n");
print(" ##################### Menu Ends #########################\r\n");
choice = inbyte();
printf("Selection : %c \r\n",choice);
internal_choice = 1;
switch(choice)
{
//~~~~~~~~~~~~~~~~~~~~~~~
// Use case for AXI GPIO
//~~~~~~~~~~~~~~~~~~~~~~~~
case '1':
exit_flag = 0;
print("Press Switch 'SW5' push button on board \r\n");
print(" \r\n");
while(internal_choice != '0')
{
Readstatus = XGpio_DiscreteRead(&GPIOInstance_Ptr, 1);
if(1== Readstatus && 0 == OldReadStatus )
{
print("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\r\n");
print("SW5 PUSH Button pressed \n\r");
print("LED 'DS23' Turned OFF \r\n");
XGpioPs_WritePin(&psGpioInstancePtr,iPinNumber,0);
//Start Timer
XTmrCtr_Start(&TimerInstancePtr,0);
print("timer start \n\r");
//Wait For interrupt;
print("Wait for the Timer interrupt to tigger \r\n");
print("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\r\n");
print(" \r\n");
while(InterruptFlag != 1);
InterruptFlag = 0;
print(" ###########################################\r\n ");
print("Press '0' to go to Main Menu \n\r ");
print("Press any other key to remain in AXI GPIO Test \n\r ");
print(" ###########################################\r\n ");
internal_choice = inbyte();
printf("Select = %c \r\n",internal_choice);
if(internal_choice != '0')
{
print("Press Switch 'SW5' push button on board \r\n");
}
}
OldReadStatus = Readstatus;
}
print(" \r\n");
print(" \r\n");
break;
case '2' :
//~~~~~~~~~~~~~~~~~~~~~~~
//Usecase for PS GPIO
//~~~~~~~~~~~~~~~~~~~~~~~~
exit_flag = 0;
print("Press Switch 'SW7' push button on board \r\n");
print(" \r\n");
while(internal_choice != '0')
{
Readstatus = XGpioPs_ReadPin(&psGpioInstancePtr,
iPinNumberEMIO);
if(1== Readstatus && 0 == OldReadStatus )
{
print("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\r\n");
print("SW7 PUSH Button pressed \n\r");
print("LED 'DS23' Turned OFF \r\n");
XGpioPs_WritePin(&psGpioInstancePtr,iPinNumber,0);
//Start Timer
XTmrCtr_Start(&TimerInstancePtr,0);
print("timer start \n\r");
//Wait For interrupt;
print("Wait for the Timer interrupt to tigger \r\n");
print("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\r\n");
print(" \r\n");
while(InterruptFlag != 1);
InterruptFlag = 0;
print(" ###########################################\r\n ");
print("Press '0' to go to Main Menu \n\r ");
print("Press any other key to remain in EMIO Test \n\r ");
print(" ###########################################\r\n ");
internal_choice = inbyte();
printf("Select = %c \r\n",internal_choice);
if(internal_choice != '0')
{
print("Press Switch 'SW7' push button on board \r\n");
}
}
OldReadStatus = Readstatus;
}
print(" \r\n");
print(" \r\n");
break;
default :
exit_flag = 1;
break;
}
}
print("\r\n");
print("***********\r\n");
print("BYE \r\n");
print("***********\r\n");
cleanup_platform();
return 0;
}
该部分代码会在后续文章中作讲解说明。
连接好开发板,打开串口终端。和上次不同的是,这次我们有使用PL部分,所以在运行程序之前需要先对该部分做配置。方法如下,依次选择:Xilinx Tools——Program FPGA,在弹出的对话框中会自动找到我们刚才从PlanAhead里面导出的bit文件(如果没有自动定位到,或者路径不对,就手动去定位,如果没有改导出位置,默认在项目工程目录下),然后点击Program。
如果不出错的话,我们就可以运行程序了。我这里运行结果如下:
总结
本例的流程其实和前面差不多,就是现在XPS中添加一个处理器IP核,即PS部分。然后增加我们需要的IP核,这一部分属于PL部分。添加完以后,我们就是要做一些连线,其实XPS会帮我们做大部分的连接工作,我们往往只是做一些修改或者一些补充。完成了PS和PL部分的连接以后。我们就回到PlanAhead中,做一些文件约束。然后就可以综合、实现,生成用于配置FPGA的比特流文件。然后将硬件平台和比特流文件一起导出去。接下来,我们就在SDK中,基于刚才导出的硬件平台做应用开发。当然,当我们有使用到PL部分时,在运行之前,需要先用比特流文件配置一下PL部分。
这篇文章里面我们也加入了Chipscope的一些调试用的IP核,后面的文章会继续接着讲在ZYNQ中如何使用Chipscope进行硬件调试。同时,关于helloworld.c里面的代码也没有讲解,后面也会专门写文章进行讲解。本文中也涉及到了一些术语,比如MIO、EMIO等,这些可以去查Xilinx的官方文档了解,后面我也打算写一篇文章总结一下这些东西。如果有兴趣,可以关注一下我的文章更新。
这个例子虽然实现的功能很简单,但是涉及的知识点还是比较多的。如果能全部理解吸收,那么使用ZYNQ做更复杂的设计也不会有太大的问题。