UltraZed-EG PCIe Carrier Card 开发纪录: Hello Cortex-A53

 UltraZed-EG PCIe Carrier Card 开发纪录: 硬件认识 一文中我们了解了 UltraZed-EG PCIe Carrier Card 这一块开发板的一些信息后,是时候来开发点项目啦~

在这篇文章中,我们将让这块开发板的 Cortex-A53 透过 AXIO_GPIO 模块,点亮板子上的 LED 灯,并且透过 ps_uart0 输出一些讯息。
(本文以 Vivado 2018.2 进行开发)

开发目标
我们这次的开发目标是这样子的,除了透过 ps_uart0 输出讯息外,顺便验证如何透过 AXI_GPIO 去控制 User Leds D12 ~ D19 这些位于可程序逻辑区 (Programmable Logic, PL) 上的 LED 们。

由于单纯点亮点暗太无聊了,所以就写成跑马灯的形式吧 (也是蛮无聊的 Orz…)

建立项目

首先让我们打开 Vivado 吧~ 不过在进行这一步之前,请先确定你有依照 让 Vivado 有 UltraZed-EG PCIe Carrier Card 的配置文件 一文的说明,让我们在建立项目的时候可以找到 UltraZed-EG PCIe Carrier Card 这块板子。

启动了 Vivado 后,点选 Create New Project

接下来指定好项目路径和名称

选择 RTL Project

选择 UltraZed-EG PCIe Carrier Card

完成项目的建立

建立 Block Design
由于我们的设计需要用到 Xilinx 一些现成的 IP 的时候,就会需要透过 Block Design 来建立我们的电路设计。

首先点选 IP Integrator -> Create Block Design

接着点选 OK 建立我们的 Block Design

点选 Add IP 按钮去增加我们需要的 IP 核

我们首先寻找 Zynq UltraScale+ MPSoC 并将它加入到我们的 Block Design,并点选 Run BLock Automation 对该 IP 做一些设定

进入到 Run BLock Automation 的设定页面后,确认 zynq_ultra_ps_e_0 有被勾选到,并且 Apply Board Preset 有被设定起来。

点选 OK 完成设定。

接下来,我们根据我们的 开发目标 来加入 LED 的接线

点选 Board ,将 LED 拖曳到 Diagram 内

于是我们的 Block Design 就会变成这个样子

点选 Run Connection Automation 进行联机,会进入到以下窗口,这边直接点选 OK 即可

我们已经完成我们的设计啰,点选 Validate Design 按钮来确认设计没问题

没问题的话,就让我们来结束 Block Design 的工作吧

产生 HDL Wrapper
接下来我们要将刚刚用 Block Design 建立的电路变成 verilog 程序代码,因此会需要进行产生 HDL Wrapper 这个步骤。

对你的 Block Design 档案点选右键,选择 Create HDL Wrapper ,它会根据你项目设定的语言 (VHDL 或是 Verilog) 来产生相对的 HDL 程序代码。

由于这次我们不需要对产出来的东西进行修改,因此选 Let Vivado manage wrapper and auto-update 即可

好了后,假设你的 Block Design 档案叫做 design_1.bd ,那就会产生 design_1_wrapper.v 或是 design_1_wrapper.vhdl 这样的档案。

产生比特流 (bitstream)
前面的处理都好了后,接下来点选 Program and Debug -> Generate Bitstream 去让 Viavado 将这个项目产生出 比特流 (bitstream) ,Zynq UltraScale+ 会在开机的时候根据 bitstream 的信息对 FPGA 进行设定。

这个产生的过程视你的计算机强度如何而决定花多少时间,总之先来泡杯茶吧~

当 bitstream 完成后,我们准备执行 Xilinx SDK 来透过写 C 语言项目来让 Cortex-A53 可以透过 AXI_GPIO 对 LED 进行控制,因此要先将刚刚产生的硬件信息输出给 Xilinx SDK 去。

点选 File -> Export -> Export Hardware

确定你有勾选 Include bitstream 后,点选 OK

完成后,执行 Xilinx SDK

Xilinx SDK
启动 Xilinx SDK 后,点选 File -> New -> Application Project 去建立新的项目

这边我命名这个项目为 hello ,并指定为 standalone 的程序,该程序将运作在 Cortex-A53 的 CPU0 上。

由于我们很懒,因此这次选用预设的样板 Hello World 来建立我们的项目,好了后点选 Finish

当项目建立完成后,会自动打开 hello_bsp 里面的 system.mss ,里面会显示我所使用的外围文件链接或是加入范例程序代码,比如我们如果要了解 axi_gpio 怎样使用的话,可以点选它的手册。

这样就会透过浏览器打开如以下的页面 (或是到 这个网址 看在线文件)

打开 helloworld.c
由于我们是使用样板 Hello World 去建立我们的项目的,因此默认的程序是 helloworld.c ,我们打开它可以看到以下内容
* helloworld.c: simple test application
*
* This application configures UART 16550 to baud rate 9600.
* PS7 UART (Zynq) is not initialized by this application, since
* bootrom/bsp configures it to baud rate 115200
*
* ------------------------------------------------
* | UART TYPE BAUD RATE |
* ------------------------------------------------
* uartns550 9600
* uartlite Configurable only in HW design
* ps7_uart 115200 (configured by bootrom/bsp)
*/

#include
#include "platform.h"
#include "xil_printf.h"
int main()
{
init_platform();

print("Hello World\n\r");

cleanup_platform();
return 0;
}

这个程序会自动透过 ps_uart0 输出 Hello World 讯息,而由于我们将使用的是 ps7_uart ,因此 baudrate 会是 115200 。

接下来我们的任务就是加入 LED 的控制功能了,不过在这之前,也许读一下 UG643 (2018.2): Xilinx Standalone Library Documentation - OS and Libraries Document Collection.pdf 这份文件可以让我们对 Xilinx 的函式库有所了解些。

加入 LED 控制
我们基于刚刚打开的 helloworld.c 来加入我们对 AXI_GPIO 的控制,首先先加入两个 header file。
#include "sleep.h" // for usleep()
#include "xgpio.h" // for gpio control

每个 AXI_GPIO 模块都会有两个 channel 作为输出,由于这边我们走得是预设的,也就是第一个 channel,因此先做个 macro 好方便后续的程序撰写。

* The following constant is used to determine which channel of the GPIO is
* used for the LED if there are 2 channels supported.
*/
#define LED_CHANNEL 1

接下来,在 main() 里面,执行 init_platform(); 后面的位置加入我们对 GPIO 的初始化,假设初始化失败的话,则透过 xil_printf() 输出错误讯息并回传 XST_FAILURE 。

其中 XPAR_GPIO_0_DEVICE_ID 定义在 xparameter.h 里面,为 Xilinx SDK 自动产生出来的档案,你可以将其对应回我们的 Block Design 里面的 axi_gpio_0 。
XGpio Gpio; /* The Instance of the GPIO Driver */

/* Initialize the GPIO driver */
int Status = XGpio_Initialize(&Gpio, XPAR_GPIO_0_DEVICE_ID);
if (Status != XST_SUCCESS) {
xil_printf("Gpio Initialization Failed\r\n");
return XST_FAILURE;
}

初始化完成后,由于 LED 是属于 GPIO 的输出功能,因此要指定这些 GPIO 为 Output

Set the direction for all signals as LED output */
XGpio_SetDataDirection(&Gpio, LED_CHANNEL, 0);

接着我们定义两个变量,一个是用来控制 LED 当前状态的变量,另外一个则是控制我们跑马灯的方向
* D12 is ON by default, D13 ~ D19 are OFF
*
* D19, D18, D17, D16, D15, D14, D13, D12
*
* 0 0 0 0 0 0 0 1
*/
int LED = 0b00000001;
* 0: left to right (D12 -> D19)
* 1: right to left (D19 -> D12)
*/
int direction = 0;

最后则是我们的循环,我们透过 XGpio_DiscreteWrite() 去对我们的 LED_CHANNEL 写入当前 LED 的输出状态,并透过定义在 sleep.h 里面的 usleep() 来做点延迟,避免因为视觉暂留效应而导致我们肉眼以为 LED 没有在闪烁。

当 LED 为 0b10000000 时,也就是 D19 为 ON 的情况,修改我们的 direction 变量,让原本对 LED 变量进行左移的运作改成右移。

当 LED 为 0b00000001 时则相反,让 LED 变量变成左移运算。
Loop forever blinking the LED */
while (1) {
/* Set the LED to High */
XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, LED);

/* Wait a small amount of time so the LED is visible */
usleep(20 * 1000); /* delay 20ms */

/* Clear the LED bit *

XGpio_DiscreteClear(&Gpio, LED_CHANNEL, LED);

/* Wait a small amount of time so the LED is visible */
usleep(20 * 1000); /* delay 20ms */

/* When D19 is ON, change direction */
if (LED == 0b1000000)
direction = 1; /* 1: right to left (D19 -> D12) */
/* When D12 is ON, change direction */
if (LED == 0b0000001)
direction = 0; /* 0: left to right (D12 -> D19) */

/* Change LED status according to direction */
if (direction == 0)
LED = LED << 1; /* shift left */
else
LED = LED >> 1; /* shift right */
}

就这样,我们的程序完成了,可以开始进行刻录啰~

设定 JTAG 下载

为了透过 Micro USB 连接到 UltraZed-EG PCIe Carrier Card 上的 JTAG 来进行下载,我们需要对 UltraZed-EG上的 SW2 要进行一些调整,变成下图这样。

这样子就可以透过 Micro USB 走 JTAG 下载的路线,将程序下载下去

下载到开发板 (FPGA)

由于我们到目前为止还没有将编译好的比特流 (bitstream) 下载到我们的 UltraZed-EG PCIe Carrier Card 去,因此先来下载吧。

点选 Xilinx -> Program FPGA 进入到下载页面

点选 Program 将我们的比特流 (bitstream) 下载下去

下载好了后,由于我们的程序还没烧到 Cortex-A53 上,因此要进行下载 ELF 的动作

下载到开发板 (ELF)
点选 Run -> Run Configuration 去建立我们新的执行目标

对 Xilinx C/C++ Application (GDB) 点两下,建立执行目标,并确认 Run psu_init 和 PL Powerup 有被勾选起来。

点选 Application 确认我们的下载目标是 psu_cortexa53_0 ,并且下载的 elf 档案没有错

点选 Run 即开始下载啰,希望一切顺利 ~

结果

按照本篇文章的设定,你的 UltraZed-EG PCIe Carrier Card 显示应该如以下影片:

另外,我们也可以透过 minicom, emacs, tio, gtkterm 等终端机软件,连接上 /dev/ttyUSB1 来查看透过 printf() 输出的讯息。

取得程序代码

本文的范例已经上传到 coldnew/ultrazed_pciecc_helloA53 ,你可以透过以下命令获得

git clone https://github.com/coldnew-examples/ultrazed_pciecc_helloA53.git

延伸阅读

本文转载自:coldnew's blog