ZYNQ从放弃到入门(十)- 操作系统uC/OS
操作系统介绍
我们之前的文章都是基于“裸机”系统,这种情况适合比较简单的示例,但如果我们要使用更先进的处理系统并最大限度地发挥 Zynq SoC 的双核 ARM Cortex-A9 MPCore 处理器的优势,我们需要一个操作系统。有很多系统可供选择:
从上面的徽标可以看出,Zynq SoC 得到了可以在 Zynq SoC 上实现的非常庞大的操作系统和内核生态系统的支持。其中许多操作系统是大多数软件工程师所熟悉的。因此,在这篇博客中,我们将着眼于如何在 Zynq 上实现以下操作系统:
- uC/OS-iii – Micrium uC/OS 系列的商用实时系统,已通过 MISRA-C、DO178B A 级、SIL3/4 和 IEC61508 认证。
- Free RTOS – Real Time Engineering Limited
在http://www.freertos.org提供的免费实时系统。有一个 SIL3 认证的免费 RTOS 版本,称为High Integrity Systems的 SafeRTOS 。
- Linux – Xilinx PetaLinux发行版(http://www.xilinx.com/tools/petalinux-sdk.htm)。我们将考虑通过 GitHub 使用标准发行版,并将重新编译内核以针对我们的设计进行定制。
- Android – Zynq SoC 的 Gingerbread 发行版
实施这些不同的操作系统需要数周时间,并创建更深入的示例,使我们能够使用一些我们尚未真正接触过的 Zynq SoC 资源,例如 USB 和以太网。由于 Zynq SoC 有两个 ARM Cortex-A9 MPCore 处理器内核,我们还将研究通常称为 AMP 的非对称多核处理,我们将在 Zynq 内的每个处理器内核上运行不同的操作系统。
操作系统是为许多不同的应用程序设计的,从用户界面到嵌入式系统再到安全关键应用程序。当大多数人想到操作系统时,他们往往会想到在个人计算机上运行的 Windows 或 Linux。
这些嵌入式设计中对操作系统的需求源于这样一个事实,即复杂的软件应用程序需要任务调度和处理器资源和内存的管理,以及更多的软件管理方面。虽然工程师可以从头开始为裸机设计编写操作系统,但这样的工作需要相当长的时间来开发、验证和支持。如今,在已经有这么多可用的情况下编写一个新的操作系统并不是最有效地利用工程师的时间。我们需要做的就是为最终应用程序选择正确的操作系统。在下一节中,我们将介绍不同类型的实时操作系统,以及如何选择最适合正在开发的应用程序的操作系统。
Micrium uC/os介绍
打算在 MicroZed 上实现的第一个操作系统是 Micrium uC/OSiii,这是一个可在此处下载的硬实时操作系统。
https://github.com/weston-embedded?q=uC-&type=&language=
我选择 Micrium uC/OSiii 作为我的第一个操作系统示例,因为我最感兴趣的是将 Zynq SoC 用于工业、军事、航空航天和其他具有挑战性环境的应用。我认为 Zynq SoC 的优势在这些应用领域非常明显。它们很好地展示了 SWAP-C(尺寸、重量和功耗成本)以及 Zynq SoC 实现与所有其他替代方案相比的性能优势。然而,在我开始讨论操作系统实现之前,提供一些关于实时操作系统 (RTOS) 的背景信息。那么 RTOS 与通用操作系统的区别是什么?RTOS 是确定性的。这意味着系统的响应将在规定的期限内完成。
但是系统是否必须始终满足这些期限才能被归类为实时系统?实际上,不!三个 RTOS 类别以不同的方式处理截止日期:
- Hard RTOS – 错过最后期限被归类为系统故障。
- Firm RTOS – 偶尔错过最后期限是可以接受的,并且不归类为失败。
- Soft RTOS – 错过最后期限只会降低结果的实用性。
RTOS 围绕运行任务(有时称为进程)的概念进行操作。这些任务中的每一个都执行所需的系统功能。例如,任务可能从接口读取数据或执行计算。一个非常简单的实时系统可能只使用一个任务,但更有可能的是多个任务将同时在处理器上运行。这些任务之间的切换称为“上下文切换-context switching”,要求 RTOS 在上下文切换开始下一个任务之前保存每个任务的处理器状态。RTOS 将此处理器状态保存在任务堆栈中。确定下一个要运行的任务由 RTOS 内核控制,这个决定可能很复杂——特别是如果我们想避免任务相互锁定的死锁——但两种基本的决策方法是:
- Time sharing ——每个任务在处理器上都有一个专用的时隙。具有较高优先级的任务可以有多个时隙。时间片通过常规中断或定时器来控制。这种方法通常称为循环调度。
- Event Driven – 仅在任务完成或必须运行更高优先级的任务时才切换任务。这种方法通常称为抢占式调度。
Micrium 的 μC/OSiii 是一种抢先式 RTOS。因此,它运行具有最高优先级的已准备好执行的任务。在下一节博客中,我们将了解任务之间如何通信(通常称为进程间通信)。我们还将更详细地研究任务死锁和饥饿等事件。对于许多人来说,这次讨论将是一个回顾。但是,我认为在我们更深入地研究操作系统和 Zynq SoC 时,解释我们将使用的所有基本概念非常重要。
死锁和饥饿
当两个或多个任务想要共享一个资源时(例如 Zynq SoC 的 XADC),这些任务可能会同时请求资源。需要控制资源访问以防止争用,这是操作系统最重要的职责之一。如果没有正确的资源管理,可能会发生死锁或饥饿。
以下是死锁和饥饿的定义:
- Deadlock ——当一个任务持有一个资源,在任务完成之前无法释放它,并且当前无法完成,因为它需要另一个任务当前持有的另一个资源。如果第二个任务需要第一个任务持有的资源,系统将永远不会退出这个死锁状态。死锁对于 RTOS 来说是一个极其糟糕的情况。
- Starvation- 当任务无法运行时发生,因为它需要的资源总是分配给另一个任务。由于缺乏资源,该任务饿死了。
可以想象,多年来,关于死锁和饥饿的主题已经写了很多,并且提出了许多解决方案。例如,有Dekker 算法(https://en.wikipedia.org/wiki/Dekker's_algorithm),这是第一个已知的互斥正确解决方案。它是一种共享内存机制,不需要特殊的“测试和设置”指令(但因此仅限于管理两个相互竞争的任务),并归功于荷兰数学家 Theodorus Dekker。处理死锁最常用的方法是使用信号量,通常分为两种类型:二进制信号量和计数信号量。二进制信号量控制对一种资源的访问,例如硬件资源。计数信号量控制对相同的、可互换的资源池(例如内存缓冲区)的访问。
通常,每个资源都有一个分配给它的二进制信号量。请求任务将在执行之前等待资源变得可用,一旦任务完成,它就会释放资源。二进制信号量通常使用 WAIT 和 SIGNAL 信号。任务将在信号量上等待。当资源空闲时,可能是立即(或不是),操作系统会将资源控制权交给任务。当任务完成时,它将发出信号完成并释放资源。但是,如果当任务在信号量上等待时资源被占用,操作系统会暂停该任务,直到资源空闲。WAIT 任务可能必须等到当前正在执行的任务用资源完成,或者如果它被更高优先级的任务抢占,WAIT 可能需要更长时间。
引入任务优先级的概念也带来了优先级倒置的问题。有一类更灵活的二进制信号量称为互斥量,现代操作系统经常使用这些信号量来防止优先级倒置。
计数信号量的工作方式与二进制信号量相同,但是当有多个资源可用时使用它们——例如数据存储。当每个资源都分配给请求任务时,计数会减少以显示剩余的可用资源数量。当信号量计数达到零时,没有更多可用资源,并且在计数达到零后请求这些资源中的一个或多个的进程将被挂起,直到释放所需数量的资源。任务通常需要相互通信,并且有多种方法可以实现这一点。最简单的方法是使用由信号量管理的数据存储,如上所述。更复杂的通信方法包括消息队列。
使用消息队列时,希望向另一个任务发送信息的任务将消息发布到队列。当任务希望从队列接收消息时,它会在队列上挂起。因此,消息队列像 FIFO 一样工作。然而,在µC/OS-III 操作系统中,可以将消息队列配置为以 LIFO 方式运行。消息队列及其组织可能非常复杂。幸运的是,这些编程工具在预先编写的操作系统的帮助下使用起来比从头开始创建要容易得多。在稍微解释了资源共享和任务如何通信之后,下一节将着眼于如何在 ZYNQ 板上启动和运行 µC/OS-III 操作系统。
在ZYNQ上运行µC/OS并实现TCP通讯
介绍了上面这些基础知识之后,是时候在基于 Zynq 板卡上实现我们的第一个操作系统了。那将是 Micrium 的uC/OS RTOS。(注意:Micrium 的 uC/OS-II 实时操作系统曾在火星好奇号漫游车上运行)。
显然,第一步是从 Micrium 的网站下载 uC/OS。下载链接如下:
https://github.com/weston-embedded/uC-OS3
https://github.com/suisuisi/zynq_guide/tree/main/ucos
下载后,只需将几个 zip 文件解压缩到计算机上 Xilinx 安装下的正确目录中。
所有资源:
https://github.com/suisuisi/zynq_guide/tree/main/ucos