Back to Blog

基于ZYNQ 的多轴运动控制平台关键技术研发-Linux+xenomai开源实时系统设计(二)

#linux+xenomai#实时系统#ZYNQ

4. SoC 双核系统与核间多任务通信设计

本章详细介绍 AMP 架构下 SoC 核系统的设计和核间任务通信的实现方法。首

先,进行双核系统的搭建并完成设备驱动的移植;然后,进行基于共享内存的通信模

块设计;最后,针对系统不同通信任务的要求,提出多任务通信的实现方案。

4.1 双核系统设计与实现

4.1.1 Linux 实时系统搭建

(1) Xenomai 实时补丁安装

机器人控制系统需要具有强实时性能力,在运行时必须保证运动控制相关的任

务调度和数据通信的实时处理和响应。Linux 系统因支持内核抢占、中断屏蔽和优先

级抢占调度等机制,无法提供严格的强实时性能。Linux 系统的实时性优化有两种方

法,分别为 Linux 内核源码改进和 Linux 双内核扩展。其中,内核改进法方便用户进

行指定方向的性能优化,但存在工作量大,改造难度高等问题。

因此,本文通过 Xenomai 对 Linux 系统进行拓展,实现双内核实时系统,即满足

系统使用要求,同时提高了开发效率。Xenomai 采用 Adeos(Adaptive Domain

Environment for Operating System)技术保证硬实时的拓展,双内核架构如图 4.1 所示。

在采用 Adeos 的双核架构中,Adeos 主要完成域的调度、中断和域的管理等工

作。在 Adeos 架构中设置了域的优先级,Xenomai 域要高于普通的 Linux 域。因此,

当系统接收到中断信号时,

Adeos 将中断分配给 Xenomai 进行处理;只有当 Xenomai

域的任务执行完成后,

Linux 域中的任务才会继续执行。通过上述调度方法,Adeos

保证了 Xenomai 域的优先级,从而保证了任务的实时处理。如图 4.2 所示,本文使用

Linux 4.9.0 版本内核和 Xenoma 3.0.7 实现实时系统的搭建。

为了保证 Xenomai 正确使用,表 4.1 中列出内核配置中重要的 Xenomai 部分功

能项。其中,内核需要开启 Xenomai 相关的服务项,同时关掉与实时性功能相冲突

的内核选型,保证系统的正常运行。

在 Linux+Xenomai 系统搭建完成后,验证 Xenoma 是否安装成功。在 Linux 启动

完成后,通过 dmesg |grep I-pipe 和 dmesg |grep Xenomai 命令查看安装状态。如图 4.3

所示,系统完成 Xenomai 实时补丁的安装。

(2) Linux 设备驱动移植

基于 SoC 关键通信接口设计,在 Linux 系统下完成对外设驱动的移植,保证系

统设备的正确使用,实现平台对外通信。如图 4.4 所示,Linux 4.9.0 内核使用总线架

构的设备模型。

 在总线架构的设备模型中,系统的设备和驱动是分离的,使用总线机制对二者进

行匹配。总线设备模型为系统的驱动移植提供了一致性接口,简化了移植流程,提高

了开发效率。

进行 SoC 平台通信接口设备的驱动移植,具体分为两种方法:

(1)当内核提供该设备驱动时,直接使用内核中的驱动。

(2)对于内核中不提供驱动的设备,为其设计驱动程序并进行移植。

首先,通过查看内核 config 配置文件,Linux-4.9.0 内核提供千兆以太网、USB

HUB 和 UART 的设备驱动支持。因此采用方法(1)进行驱动移植,具体如下:使

用 make menuconfig 命令开启内核配置;将设备驱动配置为静态加载,实现系统启

动时驱动自加载功能;使用 arm-linux-gcc 完成 zImage 的编译生成。如图 4.5 所

示,在使用 zImage 完成系统启动后,通过 dmesg 命令查看内核日志,千兆以太

网、USB HUB 和 UART 的驱动程序已完成注册,设备可以正常使用。

Linux-4.9.0 内核中不提供 AX88772B 网卡芯片的驱动程序,USB 扩展的百兆以

太网接口无法直接使用,因此采用方法(

2)进行 AX88772B 网卡驱动程序的移植。

出于减小开发难度和节约开发时间的目的,本文使用AX88772B官方驱动源码,

并在其基础上根据平台功能需求进行相应设计。通过分析,AX88772B 在使用中,通

过外部存储器保存用户写入的 PHY 芯片 MAC(Media Access Control Address)地址,

进而完成驱动程序注册。为了满足系统快捷化和多样化的配置需求,本文提出一种基

于 AX88772B 芯片的 MAC 地址动态设置方法。具体方法为:将用户自定义 MAC 地

址预先存储在 Flash 设备中;驱动程序在注册中,如果未检测到外部存储器,则从

Flash 中读取用户 MAC 地址,完成注册。如图 4.6 所示,完成 AX88772B 驱动程序

的修改,并在 Linux 环境下生成内核驱动模块,方便用户根据使用情况进行动态加

载。

如图 4.7 所示,系统加载 asix.ko 内核模块,驱动在注册过程中通过 flash 为两个

AX88772B 网卡分配 MAC 地址,网卡驱动移植成功。

4.1.2 伺服裸机系统构建

在 CPU1 上搭载伺服裸机系统需要完成以下三部分工作:Linux 系统在运行时将

CPU 使用数量设置为 1;CPU0 完成 CPU1 伺服应用程序的加载;CPU0 完成 CPU1

的唤醒。

Linux 系统在启动阶段,默认使用 SMP 对称处理模式,同时对两个 CPU 进行管

理,协调 CPU 之间的相关工作。因此,在使用 AMP 架构时,需要限制 Linux 系统

只占用 CPU0,CPU1 预留用于伺服裸机系统。Linux 启动过程中,内核文件根据环

境变量 bootargs 对系统进行相应配置,用户可以通过修改变量 bootargs 实现 CPU 使

用数量的指定。变量 bootargs 的生成分为直接指定法和 Bootloader 生成法两种方式。

其中,直接指定法是在内核配置过程中设置 bootargs,这种方法保证 bootargs 在重新

配置前的唯一性和有效性;Bootloader 生成法是在 Bootloader 文件中设置 bootargs,

并在 Bootloader 执行完毕后将其传递给内核。这种方法提供了用户修改 bootargs 的

可能性和快捷性,有利于软件的开发和维护。本文采用 Bootloader 生成法。在

Bootloader 启动过程中对 bootargs 进行动态配置和保存,将 Linux 系统使用的 CPU

数量设置为 1,从而保证 CPU1 的空闲状态。环境变量 bootargs 值配置如表 4.2 所示。

在保证 CPU1 处于空闲状态后,CPU0 需要完成伺服应用程序 CPU1_APP 的加

载。CPU1_APP 的加载工作是将应用程序写入用户指定的内存空间中。Xilinx 为 SoC

加载裸机程序提供了两种实现方法:第一种是使用 Jtag 烧写器将 CPU1_APP 写入内

存。此方法需要使用到外部烧写器,而且只能实现程序的单次加载,因此适用于系统

开发和调试阶段。第二种方法是使用 Xilinx SDK 工具将 CPU1_APP 集成到 Bootloader

中形成 BOOT.bin 文件;系统启动时执行 BOOT.bin 文件,自动寻找 CPU1_APP 并将

其加载到指定内存中。第二种方法不需要烧写器,加载操作方便;但是,多文件集成

的加载方式破坏了伺服应用程序的独立性,不利于后续软件的单独更新。

本文提出一种基于 Bootcmd 环境变量的应用程序加载方法。Bootcmd 是

Bootloader 的一种环境变量,保存了 Bootloader 在启动后执行的指令操作。通过在

Bootloader 启动阶段,对 Bootcmd 进行动态设置,添加加载 CPU1_APP 程序的指令

操作并保存。如图 4.8 所示,平台在复位操作后,Bootloader 会执行 Bootcmd 中的命

令,将 CPU1_APP 加载到指定内存中。本方法同样适用于 FPGA 烧写文件的动态加

载,有效保证了用户文件的独立性。

Zynq 7020 SoC 在 AMP 架构下,CPU1 在 SoC 上电后首先执行芯片固件程序进

入休眠状态。此时,必须通过 CPU0 才能将 CPU1 唤醒。基于 Xilinx 提供的 CPU1 唤

醒方法, 在进行 CPU1 伺服软件的重运行操作时,需要将双系统进行复位,不满足

运动控制系统的使用要求。

因此,本文提出一种针对 SoC AMP 架构下的 CPU1 可单独重运行的方法。方法

的实现流程如下:(1)系统启动阶段,CPU0 完成 CPU1_APP 的加载;

(2)系统启动 完成后,CPU0 将一段可实现 CPU1 程序跳转的指令写入地址 0x0 的内存中;

(3) CPU0 中执行 CPU1 的复位操作;

(4)CPU1 重启后,执行地址 0x0 处的跳转指令,

跳转到 CPU1_APP 加载地址

(5)CPU1 执行 CPU1_APP,伺服系统重运行完成。

基于图 4.9 所示方法,完成 CPU1 程序的加载和运行,实现伺服系统的搭建。

4.2 核间通信模块设计

基于双核间多任务通信需求,本文采用共享内存 OCM 作为通信媒介,并进行如

下通信模块设计:共享内存的管理方法、核间信号量设计和消息邮箱设计。

4.2.1 共享内存的管理方法

共享内存是双核间任务通信的物理介质,用于保存通信数据。本文采用 SoC 芯

片内部的 256K 片上内存作为共享内存,其在 4G 寻址空间的地址范围为

0xFFFC0000~0xFFFFFFFF。片上共享内存 OCM 在 SoC 芯片启动过程中,会被用于

系统启动文件的加载和运行;系统启动完成后,OCM 内部数据并不为空。因此,在

使用 OCM 进行任务通信时,为了保证 OCM 中数据的安全性和一致性,需要进行如

下操作:

(1)机器人控制程序开始运行时,Linux 系统完成 OCM 访问申请并对 OCM 进

行初始化;

2)每次完成伺服系统重运行后,Linux 系统需要对 OCM 进行初始化;

3)机器人控制程序运行结束时,Linux 系统关闭内存映射,释放共享内存。

针对以上操作,设计并实现了用于 Linux 系统访问 OCM 的三个接口函数,分别

为:System_Ocm_Req()、System_Ocm_Init()和 System_Ocm_Free(),函数功能依次为

OCM 申请、OCM 初始化和 OCM 释放。

在 Linux 系统中,用户进程无法同裸机程序一样直接对物理内存进行访问。为了

保证用户进程对共享内存的快速访问,System_Ocm_Req()函数通过内存映射方法

(Memory Map,简称 MMAP),完成共享内存的申请操作。MMAP 是 Linux 系统下

的一种系统调用,其功能是将一个文件或者其他对象映射到进程的地址空间。如图

4.10 所示,System_Ocm_Req()中使用 MMAP 完成 256KB 大小的 OCM 物理内存空

间的访问申请。申请成功后,System_Ocm_Req()函数返回一个基地址指针,用户进

程通过指针方式对 OCM 进行读写操作。

System_Ocm_Init()函数主要完成对 OCM 的状态检查,读写校验和擦除。状态检

查中,Linux 访问 OCM 状态寄存器并判断 OCM 是否存在通信错误,如果存在错误

则对其进行复位操作;如果状态正常,则进行读写校验。读写校验中,通过向 OCM

写入用户测试数据,写入完成后将 OCM 中数据读出,比较写读前后数据的一致性。

如果读写校验成功,对 OCM 进行擦除操作,保证双核系统通信的安全性;否则,表

明 OCM 校验失败,Linux 系统进行相应的出错处理。

在机器人控制软件运行结束时,为了保证系统的安全性,需要关闭 OCM 在进程

空间中的映射,此时通过 System_Ocm_ Free ()函数实现。System_Ocm_ Free ()中通过

使用 munmap()系统调用,实现映射关系的解除,完成 OCM 的释放。如图 4.11 所示,

Linux 在任务执行阶段,对 OCM 的进行有序访问。

4.2.2 核间信号量设计

为了保证双核对 OCM 数据访问的互斥性和一致性,引入信号量设计思想,并根

据双核间任务通信的使用要求完成核间信号量的设计。信号量是一种用于多进程(或

线程)情况下,避免共享资源被不同对象同时访问的保护机制[44]。采用信号量同步

机制时,进程(或线程)会获取一个非负整数的信号量,表示共享资源的访问状态。

信号量非零时,表示有空闲共享资源,允许进程(或线程)访问;信号量为零时,表

示共享资源为空,此时进程(或线程)将处于等待状态。如果共享资源数量大于 1,

称为计数信号量(Counting semaphore);如果共享资源数量为 1,信号量只有 0 或 1

两种状态,称为二进制信号量(binary semaphore)。

本文设计了二进制的核间信号量,并根据双核间任务通信时序,对核间信号量的

访问进行约束。核间信号量表示 OCM 数据区的访问状态,具体定义如表 4.3 所示。

其中,核间信号量为 1 时,表示 OCM 数据区处于空闲,可以发起任务通信;核间信

号量为 0 时,表示 OCM 数据处于忙状态,任务通信正在进行。如果信号量出现为其

他状态,表明双核系统在任务通信过程中发生通信错误。

在双核间任务通信开始前,Linux 系统对信号量进行初始化,将其设置为 1。在

双核间任务通信阶段,对信号量进行以下操作:

(1)双核系统对信号量均有可读/可写权限,若读写操作同时发生时,读操作优

先权高于写操作优先权。

(2)双核系统任务通信阶段,进行“CPU0 读-CPU0 修改-CPU1 读-CPU1 修改”

顺序的信号量访问。

其中,(1)保证了在任务通信过程中,双核系统可以根据通信状态对信号量进行

设置;在访问优先级设置上,读操作高于写操作,当双核系统同时对信号量进行读写

操作时,保证信号量先被读取,避免通信状态的丢失。(

2)设置了通信过程中双核系

统对信号量的访问顺序。在双核间任务通信时,通信由 Linux 系统先发起,此时系统

读取信号量并判断其状态,当信号量为 1 时开始访问 OCM;当信号量为 0 时,表示

任务通信已处于进行状态。图 4.12 表示双核系统任务通信阶段的信号量状态切换。

在 linux 系统完成 OCM 访问后,将信号量设置为 0。伺服系统持续读取信号量,当

信号量为 1 时,表示未发生通信;当信号量为 0 时,表示通信正在进行,伺服系统开

始访问 OCM。当伺服系统完成 OCM 访问后,将信号量重新设置为 1,表示 OCM 处

于空闲状态。

4.2.3 数据消息邮箱设计

双核系统任务通信阶段,需要支持多种类型数据通信,因此采用消息邮箱实现数

据传输。消息邮箱是一种任务间的数据通信方法,设计简单且系统开销少[45]。系统

任务将通信数据打包处理后发送给邮箱,邮箱接收到数据邮件后,根据邮箱状态将邮

件转发至其他任务。针对多轴运动控制的使用要求和邮箱通信的特点,本文设计两种

邮箱通信方式,分别为固定长度邮箱通信和可变长度邮箱通信。

固定长度邮箱通信中,邮件数据长度固定,系统可根据通信数据的使用要求设置

为 8/16/32/64 位。采用固定长度邮箱进行任务通信时,在双核系统初始化阶段,需要

对邮箱地址进行设置,保证双核系统对同一邮箱的访问。

可变长度邮箱通信时,邮件内容为通信数据帧,数据帧长度不定。为了保证邮件

的正确解析,双核系统需要对数据帧进行统一设置。因此,设计一种支持读写命令和

可变数据长度的数据帧格式,帧结构如表 4.4 所示。邮件数据帧的数据采用 32 位对

齐方式,方便双核系统进行数据帧的保存和解析。同时,在双核系统初始化阶段,需

要对邮箱地址进行设置,保证双核系统对同一邮箱的访问。

为固定长度和可变长度两种邮箱通信方式设计不同的接口函数,分别为

DRV_Control()和 DRV_ASYControl(),函数的具体功能定义如表 4.5 和表 4.6 所示。

信迈提供ZYNQ+LINUX+Xenomai+运动控制系统实时系统移植方案。