RMP Real-time kernel

March 23, 2025 · View on GitHub

logo

RMP Real-time kernel

Github release Github commits Discord QQ Group

language Build OpenSSF Best Practices Codacy Badge

Click HERE for English version.

  RMP 是一个专注于形式化验证和简易可用的小型实时系统。它通过采取形式化方法来确保系统的可靠性(当前验证工作还__未完成__;不过,100%的白盒测试分支覆盖率已经达到。目前的内核可以被看作IEC 61508 SIL2预认证等级,或者EAL 4)。所有的实时操作系统必备的功能它都具备,但是并不在此基础上提供更多可选组件以确保内核的精炼性。这样,得到的内核就是一个最小化的内核,可以很方便地对它进行形式化验证。同时,它还可以作为客户操作系统运行在虚拟机监视器上。

  本系统比以 FreeRTOSRT-Thread 为代表的全功能系统相比要得多,而且理解起来应该也相对容易得多。即便本系统仅仅包含了一个.C文件,它仍然提供了高效的内存管理抗锯齿图形界面库其他实用函数,并且这些功能的实现都不消耗任何额外的RAM

  本系统的手册可以在 这里 找到。

  如果想要参与开发,请阅读 参与规范 两个指导文档。如果要提交拉取请求,请使用 拉取请求模板 。 本软件是EDI的官方作品,因此属于 公有领域 。 所有由EDI保留的版权在所有适用的法律条款下尽最大可能地授权给所有实体。

  对于那些由微控制器厂商提供的硬件抽象层软件包,请到 M0A00_Library 软件仓库自行下载。

为什么要做一个新系统?

  现成的实时操作系统大概可分为三类:企业级业余爱好教学用。企业级系统通常是为实地部署准备的,它们在各方面都非常出色。然而,即便其代码可能是开源甚至免费的,其几乎所有的功能安全验证文档和技术细节都是付费内容且叫价奇高。业余爱好者创作的系统则正好相反,虽然免费但缺乏长期的精力投注和合适的质量控制,且屡遭废弃。教学系统介于两者之间,但它专为学生实验开发,缺乏对形式化验证和功能安全等高阶内容的涉猎

  有鉴于此,RMP努力构建一个完全开放、功能良好和可资学习的经认证系统。可以用三种方法来使用这个项目:(1)修改或部署它,(2)从其代码实现和开发过程熟习其原理,或(3)扔掉这个系统并开发一个适合自己的新系统。

  整个系统的构建分为三个阶段。首先,我们不直接编写系统的形式化模型,而是向业余爱好者那样开发它。我们将它移植到尽量多的稀奇古怪的硬件上以验证其抽象的灵活性,并在其上运行尽量多类型的应用程序以确保其接口的可用性。然后,我们从第一个阶段的稳定原型和心智模式出发,反向提炼出其理想行为的形式化模型,并证明该模型具有某些良好的性质。最后,我们开发一套C子集的语义,根据上一步得到的形式化模型重写整个内核并证明其正确性。在这个阶段还会产生功能安全认证所需的一切文档(IEC 61508 SIL 4和ISO 26262 ASIL D,可能还包括EAL;优先处理前者),并将它们开源到公有领域(不包括标准文档本身,因为本项目不持有它们的版权)。

  本系统并无固定的开发进度安排。第一阶段工作已经完成,当前展开的是第二阶段的工作。如果你对此项目感兴趣,欢迎加入本项目。和其它项目不同,本项目欢迎新手

快速演示

可在Linux上直接运行的Demo

  到 这里 编译x86-32位Linux系统上可运行的二进制,观察性能测试的运行结果!

可在红白机(FAMICOM/NES)上直接运行的Demo

  从 这里 下载“游戏”镜像,把它装入你喜欢的模拟器(或者支持南梦宫163扩展芯片的烧录卡并插入实机),观察性能测试的运行结果!

FAMICOM

  南梦宫163扩展芯片是必需的,因为系统需要利用它内部的IRQ计时器来测量性能。在所有扩展芯片中,唯一支持可读取计时器的只有南梦宫163,这颗芯片也被用于“星球大战(Star Wars)”、“三国志II 霸王的大陆”等知名游戏。南梦宫163有时也被叫做Namcot 163,或者iNES扩展019。

内建图形库:控件,界面样例和FXAA抗锯齿细节

Controls Calculator FXAA

基础线程操作

创建一个线程

    RMP_Thd_Crt(&Thd_1            /* 线程控制块 */,
                Func_1            /* 线程入口 */,
                &Stack_1          /* 线程栈地址 */,
                sizeof(Stack_1),  /* 线程栈大小 */,
                (void*)0x12345678 /* 参数 */,
                1                 /* 优先级 */, 
                5                 /* 时间片 */);

删除一个线程

    RMP_Thd_Del(&Thd_1            /* 线程控制块 */);

悬起一个线程

    RMP_Thd_Suspend(&Thd_1        /* 线程控制块 */);

解除线程悬起

    RMP_Thd_Resume(&Thd_1         /* 线程控制块 */);

线程延时

Delay

    void Func_1(void* Param)
    {
        RMP_LOG_S("Parameter passed is ");
        RMP_LOG_H((ptr_t)Param);
        RMP_LOG_S("\r\n");
        while(1)
        {
            RMP_Thd_Delay(30000);
            RMP_LOG_S("Delayed 30000 cycles\r\n\r\n");
        };
    }

    void RMP_Init_Hook(void)
    {
        RMP_Thd_Crt(&Thd_1, Func_1, &Stack_1, sizeof(Stack_1), (void*)0x12345678, 1, 5);
    }

从一个线程向另一个线程的邮箱发送

Send

    void Func_1(void* Param)
    {
        ptr_t Time=0;
        while(1)
        {
            RMP_Thd_Delay(30000);
            RMP_Thd_Snd(&Thd_2, Time, RMP_SLICE_MAX);
            Time++;
        };
    }

    void Func_2(void* Param)
    {
        ptr_t Data;
        while(1)
        {
            RMP_Thd_Rcv(&Data, RMP_SLICE_MAX);
            RMP_LOG_S("Received ");
            RMP_LOG_I(Data);
            RMP_LOG_S("\n");
        };
    }

    void RMP_Init_Hook(void)
    {
        RMP_Thd_Crt(&Thd_1, Func_1, &Stack_1, sizeof(Stack_1), (void*)0x12345678, 1, 5);
        RMP_Thd_Crt(&Thd_2, Func_2, &Stack_2, sizeof(Stack_2), (void*)0x87654321, 1, 5);
    }

计数信号量

Semaphore

    void Func_1(void* Param)
    {
        while(1)
        {
            RMP_Thd_Delay(30000);
            RMP_Sem_Post(&Sem_1, 1);
        };
    }

    void Func_2(void* Param)
    {
        ptr_t Data;
        while(1)
        {
            RMP_Sem_Pend(&Sem_1, RMP_SLICE_MAX);
            RMP_LOG_S("Semaphore successfully acquired!\r\n\r\n");
        };
    }

    void RMP_Init_Hook(void)
    {
        RMP_Sem_Crt(&Sem_1,0);
        RMP_Thd_Crt(&Thd_1, Func_1, &Stack_1, sizeof(Stack_1), (void*)0x12345678, 1, 5);
        RMP_Thd_Crt(&Thd_2, Func_2, &Stack_2, sizeof(Stack_2), (void*)0x87654321, 1, 5);
    }

内存池操作

    /* 初始化内存池 */
    RMP_Mem_Init(Pool, Pool_Size);

    /* 从内存池分配内存 */
    Mem=RMP_Malloc(Pool, Alloc_Size);

    /* 向内存池归还内存 */
    RMP_Free(Pool, Mem);

所有支持架构上的典型性能数据

  本系统的绝对最小值1.6k ROM和432Byte RAM左右,这个大小是在STM32F030F4(Cortex-M0+)的移植上达到的,并包括了第一个线程的60Byte的线程控制块和256Byte的线程栈,以及64Byte的内核中断响应用栈。操作系统内核和最精简的HAL库一共仅占用了52Byte存储。如果你对这个数字仍不满意,那么可以不使用厂商提供的HAL库而自己写一个版本。

  当前的能完成基准测试的最小概念性移植是ATMEGA328P上完成的,它仅有32k Flash和2k SRAM

  在真实项目环境中内核的时间性能如下所示。所有的编译器优化选项都被设为最高(通常是-O3),而且时间优化选项也被打开。所有值均为 CPU 周期的平均值;测试头文件中记录的最坏执行时间约等于该值加上定时器中断造成的最坏干扰。

  • Yield : 两线程间的切换操作。
  • Mail : 两线程间使用邮箱进行发送-接收操作。
  • Sem : 两线程间使用计数信号量进行发布-获取操作。
  • FIFO : 在单个线程内进行FIFO读/写。
  • Msgq : 两线程间的消息队列通信。
  • Bmq : 两线程间的阻塞消息队列通信。
  • Mail/I : 从中断发送到某线程邮箱。
  • Sem/I : 从中断发布信号量。
  • Msgq/I : 从中断发送到消息队列。
  • Bmq/I : 从中断发送到阻塞消息队列。
  • Mem : 进行一对内存分配/释放操作。
  • Alrm : 对五个周期为1/2/3/5/7的闹钟进行一次处理的平均时间。

  MsgqBmq的区别在于前者只有接收方可以阻塞,后者双方都可以阻塞。

纪念性移植

芯片架构工具链YieldMailSemFIFOMsgqBmqMail/ISem/IMsgq/IBmq/IMemAlrm
RP2A03/FC-84MOS6502CC65407354355435202877261044548315180722083507484TBD
RP2A03/MESEN......406054395424204077281044348365185722783557446TBD
SPCE061AunSPGCC694173215489272671370916191475224228893518TBD
PIC32MZ2048MIPSXC32190345305150475620295260370465365TBD
...MIPS-FR64..475630585160775935400360490585371TBD

实用性移植

芯片架构工具链YieldMailSemFIFOMsgqBmqMail/ISem/IMsgq/IBmq/IMemAlrm
ATMEGA328PAVRGCC408719686313106513186246269051073N/ATBD
ATMEGA1284P......4377517173141098135263763992110871680TBD
ATMEGA2560......4497747363261131139665665494211171686TBD
R5F104PJRL78CCRL26156552030892412255395007899641854TBD
PIC24FJ128PIC24FXC16152334271168468654274213352461379TBD
DSPIC33EP512DSPIC33E...214447353219608851368278455602448TBD
MSP430F149MSP430CCS3126415733129851278528487739898N/ATBD
MSP430FR5994MSP430X...468105489149215732072891784117614643291TBD
STM32F030F4Cortex-M0Keil362763666379119616096896169501211N/ATBD
......GCC366802690396124616857056229541200N/ATBD
HC32L136K8Cortex-M0+Keil211422370219646873403350532673542TBD
STM32L071CBCortex-M0+Keil3355815322538921167554524756945N/ATBD
......GCC33765660028494712605786027941003N/ATBD
STM32F103RECortex-M3Keil203438385226684930392354542707518TBD
......GCCTBDTBDTBDTBDTBDTBDTBDTBDTBDTBDTBDTBD
STM32F405RGCortex-M4FKeil180345321180667886309302498626455TBD
......GCC196388345192677953381349566743411TBD
STM32F767IGCortex-M7FKeil176329277174510694328259413516334TBD
......GCC182335288156473643313264375514332TBD
TMS570LS0432Cortex-R4CCS306493460193686897480464592736533TBD
TMS570LC4357Cortex-R5...275479467216746998440435595763482TBD
TMS320F2812C28xCCS217493407229706954436381583727939TBD
TMS320F28335C28x/FPU32...2465134402357511001440413622770946TBD
CH32V307VCRV32IMACGCC209386336172538698350306436555433TBD
...RV32IMAFC...217398341172557705358307444556433TBD
Xeon 6326X86-LINUX...24k24k24k4624k24k31k30k34k53k159TBD

RVM 虚拟化移植

  • V : 普通操作的虚拟化开销。
  • V/I : 中断操作的虚拟化开销。
芯片架构工具链YieldMailSemFIFOMsgqBmqVMail/ISem/IMsgq/IBmq/IV/IMemAlrm
STM32L071CBCortex-M0+Keil3827016093021007134714%1370129215451741147%N/A1216
......GCC4007516493211064142019%1424134116031796210%N/A1291
STM32F405RGCortex-M4FKeil25243637220070892440%1180108812881452281%385798
......GCC312540448204656100859%1336125214041572250%380739
STM32F767IGCortex-M7FKeil1842932751445047056%772742899983135%275578
......GCC1923522921484666506%90385310011119239%270508
CH32V307VCRV32IMACGCC23338433614848962917%1287123913401421267%390605
...RV32IMAFC...32549743616961676750%1789174218571951399%390608

  作为对比,RT-Linux 4.12在Cortex-M7上的最好线程切换时间是25000时钟周期(它太大了以至于只能从FMC SDRAM来运行,所以这个对比事实上很不公平)。这是使用futex测得的;如使用其他IPC如管道等,则结果更差。

  以上实验均不包含任何作弊手段(比如激进的针对特定工具链的优化、不经过调度器而切换线程、使用平均情况较快但最坏执行时间不可控的调度器,甚至是使用RMS式的无栈协程切换),且在测试头文件中报告的最坏执行时间均为真实值。尽管我们为了更普遍地进行对比而在列出平均值,但对RTOS而言,最坏执行时间是唯一有意义的时间,因此一切有利于平均性能但不利于最坏执行时间的启发式优化都不适用。如果追求速度是最高目的,不可能有操作系统比RMS或DOS快,因为RMS的理论切换时间是0(所有协程均内联且只有一个状态时),而作为单任务操作系统的DOS则根本不存在切换时间。

明确不考虑支持的架构

架构原因替代方案
PIC18硬件堆栈使用RMS状态机操作系统
AVR32正在消亡使用大众化的Cortex-M和Cortex-R
x86-64高级系统使用RME微内核操作系统
Cortex-A高级系统使用RME微内核操作系统

  本RTOS主要面向资源受限的微控制器,不会提供对微处理器的支持。本项目也不考虑多核支持,因为多核微控制器很少是对称的,而且各核之间既无原子操作支持又无缓存一致性,即便RMP支持它们,其上的并行编程也极具挑战性。对于多核微控制器,推荐在每个核上起一个RMP实例,实例之间则可以用处理器间中断(Inter-Processor Interrupts,IPIs)互相通信。

新手上路

  下面的说明会帮助你在本地快速建立一个可用来评估测试本系统的工程。请参看系统的中文文档以获取更多信息。

准备工作

  要运行测试,你需要一块含有上述所列的微控制器的开发板。推荐使用 STM32 Nucleo 系列开发板或 MSP430 Launchpad 系列开发板。不要使用QEMU模拟器来测试本系统,因为QEMU有很多不完善之处,与真正的硬件行为并不一致。

  如果你没有开发板,那么RMP也有一个 基于x86处理器的Linux移植 。然而,该移植使用了ptrace系统调用和信号系统,因此性能较差,这一点可以从性能测试的数据看出。

  对于其他平台的支持应该也是容易实现的,但是当前并没有支持计划。对于那些Cortex-A和具备内存管理单元(MMU)的其他处理器,可以使用RME 实时多核心微内核;RME也支持一部分的Cortex-M和全部的Cortex-R。

编译指南

  在 Project 文件夹下能够找到各种微控制器的移植好的 MakefileKeilCCSMPLAB 的工程样板。参看各个工程文件夹下的自述文件以获取更多关于如何编译和运行该工程的信息。某些工程需要额外的厂商硬件抽象层库的支持,它们可以在 M0A00_Library 软件仓库被找到。

运行测试

  要运行测试,只要将测试下载到对应的开发板并开始单步调试即可。某些例子会采用两个LED来指示系统当前的状态,此时要填充LED的点亮和熄灭函数来运行该示例。

  要使用本系统内建的图形库,请参考用户手册的相关章节。

生产部署

  当部署本系统到生产环境时,请仔细阅读本系统自带的手册,以确保各项配置正确。本系统的手册可以在 Document 文件夹下找到。

支持的工具链

  • GCC/CLANG
  • Keil uVision (ARMCC/ARMCLANG)
  • Code Composer Studio
  • MPLAB X XC16/XC32

  其他的工具链现在不推荐或者当前不受支持,虽然要增加新的支持应该也很简单。

参与项目

  请阅读CONTRIBUTING.md文档来获得关于行为规范和提交代码的相关信息。

EDI 工程信息

  • M5P01 R6T1

杰出贡献者

  • 宋磊锋 - ARM Cortex-M3/4/7 的GCC汇编支持。
  • 侯润升 - ARM Cortex-M4/7 的RVM支持,以及lwIP示例。
  • 王逸鹤 - 稳定的x86/linux/ptrace移植。
  • 张然 - C28x数字信号处理器移植。
  • 张凯 - 白盒测试用例。
  • 刘皓天 - RL78的移植。