Kria K26 SOM 在 KV260 开发板上的使用
参考
前言
Xilinx Kria 是一个系统级模块 (SOM) 产品组合,专为各种用例和生产环境中的边缘应用而设计。K26C/I SOM 旨在直接集成到客户的生产设计中,而 SOM 入门套件(例如 KV260)是一个评估和早期开发平台。Kria 系列简化并加速了系统开发,帮助您更快地将产品推向市场。
K26 SOM
K26 系统级模块 (SOM) 是一个生产就绪的硬件平台。K26 SOM 出厂时未在非易失性存储设备(QSPI 和 eMMC)中预加载任何固件或软件配置。
KV260 入门套件
KV260 是一款基于 K26 SOM 的评估套件,专注于视觉应用。Kria 入门套件附带一个存储在 QSPI 非易失性存储器件中的预加载引导固件和 QSPI32 的预设引导模式配置。下面概述了 QSPI 存储器配置和内容的详细信息。
一、Linux开发环境搭建
参考:Ubuntu 20.04 安装配置 及 ZYNQMP开发环境搭建
1. 设置使用local sstate,加速工程编译
注意:Linux系统内核版本需 和 sstate_aarch64/aarch64/universal-4.8 版本一致,否则可能编译异常。如果内核版本不一致,需联网编译一次。
(1) 在Xilinx官网下载 aarch64 sstate-cache 和 Downloads 两个文件:
(2)petalinux-config 配置
- 创建项目:
petalinux-create -t project -s xilinx-k26-starterkit-v2020.2.2-final.bsp
- 配置项目:
petalinux-config
- 选择
Yocto Setting
-
配置 downloads,选择
Add pre-mirror url
-
输入 downloads 缓存包所在路径
file://<path>/downloads for all projects
-
选择
Local sstate feeds settings
-
输入 sstate_aarch64 缓存包所在路径:
/<path>/aarch64
- 关闭 Enable Network sstate feeds
- 使能 Enable BB NO NETWORK
-
保存退出
-
检查配置是否生效: build/conf/plnxtool.conf
-
编译项目:
petalinux-build
(3)问题及解决
报错:ERROR: qemu-xilinx-native-v5.1.0-xilinx-v2020.2+gitAUTOINC+7e3e3ae09a-r0 do_fetch
原因:Linux内核版本不匹配
查看目录:sstate_aarch64_2020.2.2-k26/aarch64/universal-4.8 ,说明该sstate_aarch64支持内核版本为4.8,使用其他版本可能会出现异常。
解决:
- 更换Linux 系统版本,适配 sstate_aarch64 支持的内核版本。
- 先不设置 local sstate ,直接编译。将项目目录下生成的 build/sstate-cache/ubuntu-20.04 目录复制到下载的sstate_aarch64目录中。再进行 local sstate 配置。
二、Petalinux 项目配置编译
1. 创建项目
petalinux-create -t project -s xilinx-k26-starterkit-v2020.2.2-final.bsp
cd xilinx-k26-starterkit-2020.2.2
2. 配置硬件
petalinux-config --get-hw-description='xxx.xsa'
3. 编译项目
petalinux-build
3. 打包 wic 镜像
petalinux-package --wic --bootfiles "ramdisk.cpio.gz.u-boot boot.scr Image system.dtb"
生成的镜像文件将位于 images/linux/petalinux-sdimage.wic
提示:生成的 wic 文件采用固定的分区大小。总文件大小为 4.1GB,但 ext4 分区的实际使用量要低得多。通过压缩文件,文件大小可以显着降低,例如使用 gzip:
gzip images/linux/petalinux-sdimage.wic
这将生成一个名为 images/linux/petalinux-sdimage.wic.gz文件大小为 265M 的新输出文件。
使用 Balena Etcher 将镜像闪存到 SD 卡上。此镜像在功能上等同于 bsp 中预构建的 wic 镜像。
4. 生成 BOOT.BIN
构建 petalinux 并生成 WIC 镜像后,输入以下命令生成 BOOT.BIN
petalinux-package --boot --u-boot --dtb images/linux/u-boot.dtb --force
生成的镜像文件将位于 /image/linux/BOOT.BIN
三、Petalinux 构建 交叉编译 SDK(用于应用程序开发的交叉编译环境)
交叉编译 SDK 对于在主机上针对特定目标体系结构(例如 X86 主机和 ARM 64 位目标)进行应用程序开发非常有用。运行以下命令生成可在 PetaLinux 外部使用的交叉编译:
petalinux-build -s
生成的自解压 shell 脚本安装程序文件位于 images/linux/sdk.sh
SDK 安装程序脚本可以复制到应用程序开发人员的主机上,并通过简单地运行脚本进行安装。按照屏幕上的提示进行操作。
$ images/linux/sdk.sh
PetaLinux SDK installer version 2020.2.2
============================================
Enter target directory for SDK (default: /opt/petalinux/2020.2.2): ./images/linux/sdk
You are about to install the SDK to "/opt/petalinux/xilinx-k26-starterkit-2020.2.2/images/linux/sdk". Proceed [Y/n]? Y
安装 SDK 后, 设置交叉编译开发环境:
source images/linux/sdk/environment-setup-aarch64-xilinx-linux
四、K26 SOM 程序烧写
1. BOOT.BIN 烧写 到 QSPI FLASH
(1)Vivado/Vitis 烧写
mt25qu512-qspi-x4-single
BOOT.BIN 0x200000 zynqmp_fsbl.elf
MMC: mmc@ff160000: 0, mmc@ff170000: 1
Net:
ZYNQ GEM: ff0e0000, mdio bus ff0e0000, phyaddr 1, interface rgmii-id
Could not get PHY for eth0: addr 1
No ethernet found.
mmc1 is current device
Kernel command line: earlycon console=ttyPS0,115200 clk_ignore_unused ext4=/dev/mmcblk1p2:/rootfs init_fatal_sh=1 cma=1000M
petalinux
(2)Uboot 烧写
(3)Linux系统 烧写
查看flash分区
cat /proc/mtd
烧写Flash分区
sudo dd if=BOOT.BIN of=/dev/mtdblock5 conv=fsync
sudo dd if=BOOT.BIN of=/dev/mtdblock7 conv=fsync
2. petalinux-sdimage.wic 烧写 到 SD卡
Linux 和 Windows 使用 balenaEtcher 软件 烧写镜像到 SD卡中
3. 使用SD卡将 petalinux-sdimage.wic 烧写 到 EMMC
Linux系统使用 dd 命令 烧写镜像到 SD卡 或 EMMC 中
sudo dd if=petalinux-sdimage.wic of=/dev/sd<X> conv=fsync
挂载SD卡第三分区到 /mnt
sudo mount /dev/mmcblk1p3 /mnt
cd /mnt/
解压EMMC镜像
sudo gunzip petalinux-sdimage.wic.gz
烧写EMMC
sudo dd if=petalinux-sdimage.wic of=/dev/mmcblk0 conv=fsync
五、系统定制
1. 修改U-boot环境变量
文件:omponents/yocto/layers/meta-som/recipes-bsp/u-boot/files/vars
som_boot=setenv boot_targets mmc0 && setenv bootargs 'earlycon console=ttyPS0,115200 clk_ignore_unused ext4=/dev/mmcblk0p2:/rootfs init_fatal_sh=1 cma=1000M' && run distro_bootcmd
cc_boot=setenv boot_targets mmc1 && setenv bootargs 'earlycon console=ttyPS0,115200 clk_ignore_unused ext4=/dev/mmcblk1p2:/rootfs init_fatal_sh=1 cma=1000M' && run distro_bootcmd
2. 设备树修改
(1)u-boot设备树修改
project-spec/meta-user/recipes-bsp/uboot-device-tree/files/system-user.dtsi
(2)kernel 设备树修改
project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
(3)Petalinux自动生成的设备树
components/plnx_workspace/device-tree/device-tree/system-top.dts
(4)设备树反编译
source sdk/environment-setup-aarch64-xilinx-linux
dtc -I dtb -O dts u-boot.dtb -o u-boot.dts
3. uboot设置启动参数
setenv boot_targets mmc1
setenv bootargs 'earlycon console=ttyPS0,115200 clk_ignore_unused ext4=/dev/mmcblk1p2:/rootfs init_fatal_sh=1 cma=1000M'
setenv bootcmd 0
printenv
run distro_bootcmd
六、裸板的固件更新和恢复应用程序
1. 介绍
SOM 入门套件 QFLASH 具有工厂预编程的 boot 固件。生产(Production)版的 SOM 出厂时 QFLASH 上没有预编程的 boot 固件。现在将入门套件 FLASH 中的固件复制到生产版的SOM中。
K26 启动固件由“Image Selector”应用程序、A/B MPSoC boot 固件分区和“Image Recovery”应用程序组成。这是一个裸板的应用程序,用于手动加载和配置 A 和 B boot分区的内容。
Image Recovery 工具的工作方式与典型的家庭路由器初始配置类似,具有静态 IP 和基于 Web 服务器的工具,用于直接更新 A/B 分区和持久寄存器(persistent register)状态。此工具可用于写入出厂 BOOT.BIN 镜像,使用“Recover Image”功能覆盖任一镜像分区或两者。该工具还可用于修改每个分区的可引导状态。如果上传 BOOT.BIN,该工具默认将该分区配置为“可引导”状态。
如果Linux不能正常工作,Image Selector 工具提供了一种机制来更新主引导设备中的动态引导分区。 如果Linux正常工作,建议使用xmutil引导固件更新实用程序更新引导固件。
2. 使用
当系统通电并按下 FWUEN 按钮时,静态 IP 将打印到 UART,应用程序使用的固定IP地址为192.168.0.111。有关恢复工具的设置和使用的详细信息,请参阅UG1089 。
(1) 通过以太网将PC连接到KV260 Starter Kit
(2) 将PC机设置为与恢复工具在同一子网的静态IP地址(192.168.0.XYZ),但不要设置为192.168.0.111。
(3)设备上电时按住 FWUEN 按钮。 您可看到恢复应用程序的UART打印输出。
(4)在PC上使用浏览器(如Chrome或Firefox)访问网址http:// 192.168.0.111,访问以太网恢复工具。
(5)使用web浏览器中的以太网恢复工具GUI,从PC的文件系统中使用boot . bin文件更新A或B引导固件分区。
3. 复制 KV260 入门套件的 boot 固件
boot 固件 QSPI 内存映射
KV260 入门套件的 boot 固件在生产时预加载到 SOM QSPI 存储器中。该设备有意与 SD 卡隔离,以确保电路板始终处于可启动状态,软件开发人员可以主要专注于操作系统级别更新和比特流的后期绑定加载。QSPI 设备的扇区在生产期间被锁定,以防止客户系统中的意外覆盖;唯一可以覆盖的扇区是上面讨论的 A 和 B 引导分区,并由 xmutil 和 Image Recovery 工具支持。QSPI 存储器映射和读/写访问在下表中定义。
- 查看 flash 分区
cat /proc/mtd
- 查看某个分区信息
sudo mtd_debug info /dev/mtd0
- 读取某个分区数据到文件中
sudo mtd_debug read /dev/mtd0 0 524288 imgsel.bin
- 读取 mtd0、mtd2、mtd10 分区内容到文件中
mtd0/mtd1/mtd6/mtd8:镜像选择和镜像恢复程序
mtd2/mtd3:镜像选择 寄存器
mtd10/mtd11:镜像恢复 Web程序
mtd5/mtd7:Petalinux 生成的 BOOT.BIN
mtd14: Boot FW 版本
4. 烧写 生产版K26 SOM 的 FLASH boot 固件
使用 Vitis SDK 烧写
Linux 设备树中将Flash部分分区锁住了,无法通过 U-boot 和 Linux系统烧写这些分区。
(1)当系统通电并按下 FWUEN 按钮,使用 Vitis SDK 烧写一个裸板程序(目的:不启动 U-boot和Linux)
(2)按照 QSPI Flash 分区 进行 Program Flash。
七、Linux(A53)+FreeRTOS(R5) 启动
1. Multiboot: A53 和 R5 同时独立启动
2. OpenAMP
开放式非对称多处理 (OpenAMP) 是一个框架,它提供了为非对称多处理 (AMP) 系统开发软件应用程序所需的软件组件。
AMP 系统中的主处理器通常会根据需要在远程内核上启动软件。然后,这些内核使用进程间通信 (IPC) 进行通信。这允许主处理器将工作卸载到其他处理器。一般的 OpenAMP 流程如下:
- Linux 主机配置远程处理器并创建共享内存。
- 主控器启动远程处理器。
- 远程处理器调用remoteproc_resource_init() 来为master 创建virtIO/RPMsg 通道。
- 主控接收这些通道并调用创建的回调通道。
- 主机响应远程上下文,确认远程处理器和应用程序。
- 远程调用已注册的 RPMsg 通道。
RPMsg 通道现已建立,双方可以使用 RPMsg 调用进行通信。
要关闭远程处理器:
- 主应用程序向远程应用程序发送特定于应用程序的关闭消息。
- 远程应用程序清理其资源并向主服务器发送确认。
- 远程调用remoteproc_resource_deinit() 函数来释放远程端的remoteproc 资源。
- 主控器关闭远程处理器并释放远程处理器。
A53 Linux 通过 OpenAMP 启动 R5 FreeRTOS hello_world.elf 程序
(1)使用vitis 创建 R5 FreeRTOS hello_world 应用程序
(2)设置 DDR 内存,与设备树中R5程序运行地址一致
rproc_0_reserved: rproc@3ed00000 {
no-map;
reg = <0x0 0x3ed00000 0x0 0x40000>;
};
(3)设置 打印串口
(4)编译,将生成的 hello_world.elf 复制到 linux 中 /lib/firmware 目录下
(5)运行程序
cd /lib/firmware
echo hello_world.elf > /sys/class/remoteproc/remoteproc0/firmware
echo start > /sys/class/remoteproc/remoteproc0/state
echo stop > /sys/class/remoteproc/remoteproc0/state
(6)运行结果
Linux 系统 参见下面:OpenAMP 演示应用程序:OpenAMP Echo-Test
八、Linux(A53)+FreeRTOS(R5) 核间通信
1. OpenAMP 演示应用程序:OpenAMP Echo-Test
Echo-Test 应用程序:将数据包从运行在四核 Cortex-A53 上的 Linux 发送到运行 FreeRTOS 的 Cortex-R5 集群内的单个 Cortex-R5 内核,然后再将它们发回。
ZynqMP Linux Master 在 APU 上运行,内核空间中带有 RPMsg 和一个 RPU 从属。
(1)使用 Petalinux 创建例程:
petalinux-build -c openamp-fw-echo-testd
(2)添加设备树: / project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
/ {
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
rpu0vdev0vring0: rpu0vdev0vring0@3ed40000 {
no-map;
reg = <0x0 0x3ed40000 0x0 0x4000>;
};
rpu0vdev0vring1: rpu0vdev0vring1@3ed44000 {
no-map;
reg = <0x0 0x3ed44000 0x0 0x4000>;
};
rpu0vdev0buffer: rpu0vdev0buffer@3ed48000 {
no-map;
reg = <0x0 0x3ed48000 0x0 0x100000>;
};
rproc_0_reserved: rproc@3ed00000 {
no-map;
reg = <0x0 0x3ed00000 0x0 0x40000>;
};
};
zynqmp-rpu {
compatible = "xlnx,zynqmp-r5-remoteproc-1.0";
#address-cells = <2>;
#size-cells = <2>;
ranges;
core_conf = "split";
reg = <0x0 0xFF9A0000 0x0 0x10000>;
r5_0: r5@0 {
#address-cells = <2>;
#size-cells = <2>;
ranges;
memory-region = <&rproc_0_reserved>, <&rpu0vdev0buffer>, <&rpu0vdev0vring0>, <&rpu0vdev0vring1>;
pnode-id = <0x7>;
mboxes = <&ipi_mailbox_rpu0 0>, <&ipi_mailbox_rpu0 1>;
mbox-names = "tx", "rx";
tcm_0_a: tcm_0@0 {
reg = <0x0 0xFFE00000 0x0 0x10000>;
pnode-id = <0xf>;
};
tcm_0_b: tcm_0@1 {
reg = <0x0 0xFFE20000 0x0 0x10000>;
pnode-id = <0x10>;
};
};
/* if instead for RPU1 use the following:
r5_1: r5@1 {
#address-cells = <2>;
#size-cells = <2>;
ranges;
memory-region = <&rproc_0_reserved>, <&rpu0vdev0buffer>, <&rpu0vdev0vring0>, <&rpu0vdev0vring1>;
pnode-id = <0x8>;
mboxes = <&ipi_mailbox_rpu1 0>, <&ipi_mailbox_rpu1 1>;
mbox-names = "tx", "rx";
tcm_a: tcm@0 {
reg = <0x0 0xFFE90000 0x0 0x10000>;
pnode-id = <0x11>;
};
tcm_b: tcm@1 {
reg = <0x0 0xFFEb0000 0x0 0x10000>;
pnode-id = <0x12>;
};
};
*/
};
zynqmp_ipi1 {
compatible = "xlnx,zynqmp-ipi-mailbox";
interrupt-parent = <&gic>;
interrupts = <0 29 4>;
xlnx,ipi-id = <7>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
/* APU<->RPU0 IPI mailbox controller */
ipi_mailbox_rpu0: mailbox@ff990600 {
reg = <0xff990600 0x20>,
<0xff990620 0x20>,
<0xff9900c0 0x20>,
<0xff9900e0 0x20>;
reg-names = "local_request_region",
"local_response_region",
"remote_request_region",
"remote_response_region";
#mbox-cells = <1>;
xlnx,ipi-id = <1>;
};
};
zynqmp_ipi2 {
compatible = "xlnx,zynqmp-ipi-mailbox";
interrupt-parent = <&gic>;
interrupts = <0 30 4>;
xlnx,ipi-id = <8>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
/* APU<->RPU1 IPI mailbox controller */
ipi_mailbox_rpu1: mailbox@ff990640 {
reg = <0xff3f0b00 0x20>,
<0xff3f0b20 0x20>,
<0xff3f0940 0x20>,
<0xff3f0960 0x20>;
reg-names = "local_request_region",
"local_response_region",
"remote_request_region",
"remote_response_region";
#mbox-cells = <1>;
xlnx,ipi-id = <2>;
};
};
};
设备树中 reserved-memory 节点中描述的内存,显示在 remoteproc 节点的 memory-region 属性中,对于ELF二进制文件被加载的部分,或者二进制文件在运行时用于与OpenAMP-related IPC通信的部分 是必需的。
在这种情况下,rpu0vdev0buffer@3ed48000 范围为 0x3ed48000 - 0x3ee48000,虽然没有在链接描述文件中描述,但是在运行时用于共享缓冲区。因此,这个范围应该在 reserved-memory 节点中描述,这样它就不会被内核过度映射。同样 rpu0vdev0vring0@3ed40000 和 rpu0vdev0vring1@3ed44000 也是如此。
由于运行在单个Cortex-R5核上的OpenAMP应用程序的默认链接器脚本,使用 DDR 地址范围 0x3ed00000 到 0x3ed40000 作为一个部分来加载 Cortex-R5 ELF 二进制文件,这也需要在 reserved-memory 中进行描述节点,以便 ELF 可以在那里运行,而不会覆盖通常映射到内核的内存。
除了 reserved-memory 中描述的节点外,Cortex-R5还有TCM节点,这些节点具有自己的内存范围,与每个Cortex-R5核心耦合。因此,描述了每个内存范围,因为它们可能用于ELF加载,通过这些 TCM 节点分别耦合的相应 R5 remoteproc 节点。
如果您希望运行并发从站,请确保使用的内存不重叠以及 IPI 使用。
(3)运行例程:
echo image_echo_test > /sys/class/remoteproc/remoteproc0/firmware
echo start > /sys/class/remoteproc/remoteproc0/state
echo_test
echo stop > /sys/class/remoteproc/remoteproc0/state
(4)运行结果:
运行 echo start > /sys/class/remoteproc/remoteproc0/state
启动 R5 FreeRTOS 的 image_echo_test 程序:
运行 echo_test
启动 A53 Linux 的 echo_test 程序:
九、U-Boot ETH PHY Reset
1. 在 zynq_gem.c 文件中添加 Gpio Reset 程序
目录:u-boot-xlnx/drivers/net/zynq_gem.c
添加头文件和 gpio 结构体:
#ifdef CONFIG_DM_GPIO
#include <asm/gpio.h>
#endif
struct zynq_gem_priv {
struct emac_bd *tx_bd;
struct emac_bd *rx_bd;
char *rxbuffers;
u32 rxbd_current;
u32 rx_first_buf;
int phyaddr;
int init;
struct zynq_gem_regs *iobase;
struct zynq_gem_regs *mdiobase;
phy_interface_t interface;
struct phy_device *phydev;
ofnode phy_of_node;
struct mii_dev *bus;
struct clk clk;
u32 max_speed;
bool int_pcs;
bool dma_64bit;
#ifdef CONFIG_DM_GPIO
struct gpio_desc phy_reset_gpio;
uint32_t reset_delay;
uint32_t reset_post_delay;
#endif
};
添加获取 gpio 及 复位程序:
函数:
static int zynq_gem_ofdata_to_platdata(struct udevice *dev)
末尾
#ifdef CONFIG_DM_GPIO
int ret = gpio_request_by_name(dev, "phy-reset-gpios", 0,
&priv->phy_reset_gpio, GPIOD_IS_OUT);
if (ret < 0)
return 0; /* property is optional, don't return error! */
priv->reset_delay = dev_read_u32_default(dev, "phy-reset-duration", 1);
if (priv->reset_delay > 1000) {
printf("ZYNQ GEM: phy reset duration should be <= 1000ms\n");
/* property value wrong, use default value */
priv->reset_delay = 1;
}
priv->reset_post_delay = dev_read_u32_default(dev,
"phy-reset-post-delay",
0);
if (priv->reset_post_delay > 1000) {
printf("ZYNQ GEM: phy reset post delay should be <= 1000ms\n");
/* property value wrong, use default value */
priv->reset_post_delay = 0;
}
if (dm_gpio_is_valid(&priv->phy_reset_gpio)) {
printf("ZYNQ GEM: %s: phy-reset-gpios = %d\n", __func__, priv->phy_reset_gpio.offset);
dm_gpio_set_value(&priv->phy_reset_gpio, 1);
mdelay(priv->reset_delay);
dm_gpio_set_value(&priv->phy_reset_gpio, 0);
if (priv->reset_post_delay)
mdelay(priv->reset_post_delay);
}
#endif
2. 在 system-user.dtsi 设备树中添加 phy-reset-gpios 描述
&gem3 {
status = "okay";
phy-handle = <&phy0>;
phy-mode = "rgmii-id";
phy-reset-gpios = <&gpio 38 GPIO_ACTIVE_LOW>;
phy0: ethernet-phy@1 {
reg = <1>;
ti,rx-internal-delay = <0x8>;
ti,tx-internal-delay = <0xa>;
ti,fifo-depth = <0x1>;
ti,dp83867-rxctrl-strap-quirk;
};
};