μC/OS-II在AndesCore™ N1033A-S上的移植

Facebook
Twitter
LinkedIn

周杰,应用工程师,晶心宏科技(杭州)有限公司

µC/OS-II 是一种代码公开、可裁剪的嵌入式实时多任务操作系统。该内核通过实现抢占式任务调度算法和多任务间通信等功能,使之具有执行效率高、实时性能优良等特点。另外,其占用空间非常小(最小可裁剪至 2KB)并且具有高度可移植性,因此被广泛的应用于微处理器和微控制器上。

晶心科技 (Andes)作为亚洲首家原创性32 位微处理器IP 与系统芯片平台设计公司,推出的 AndesCore™ N10 系列产品 N1033A-S, 搭配应用广泛的嵌入式实时操作系统 µC/OS-II 以及相关的软硬件开发资源,有效的帮助客户降低现有成本、提升系统效能、减少系统功耗,并缩短产品开发上市时程。本文将介绍如何将 µC/OS-II 移植到 AndesCore™ N1033A-S 处理器上。

1. 开发环境及处理器介绍
  1.1 软/硬件开发环境
  本移植过程使用的软件环境是 AndeSight™ v1.4 集成开发套件,它是晶心科技最新推出的针对各种 AndesCore™的软件集成开发环境,包括编译器、调试器、分析器以及强大的 ESL 工具。硬件平台采用晶心科技的 FPGA 评估板 ADP-XC5,该评估板采用 AndesCore™ N1033A-S 作为处理器内核,并具有丰富的片上资源。

AndesCore™ N1033A-S 介绍
AndesCore™ N10 系列产品 N1033A-S 是一款哈弗结构的 32 位RISC 处理器内核,具有 5 级流水线(pipeline)及动态分支预测(Dynamic branch prediction)架构。N1033A-S 新加入了最新 AndeStar™ V2 指令集,把 CPU 效能推至 1.66DMIPS/Mhz 之上。同时还实现完整的 Audio 指令集,达到完全整合 CPU与 DSP 功能的目标。N1033A-S 还支持向量中断模式以及 2D 直接内存访问 (DMA)功能,更为实时信号处理添增效能。

2.µC/OS-II 在 N1033A-S 上的可移植性分析
  µC/OS-II 具有高度可移植性,目前已经移植到近 40 多种处理器体系上,涵盖从 8 位到 64 位的各种 CPU(包括 DSP)。
  µC/OS-II 的正常运行需要处理器平台满足以下要求: 1)处理器的 C 编译器能产生可重入代码;2)用 C 语言就可以打开和关闭中断;3)处理器支持中断,并且能产生定时中断;4)处理器支持能够容纳一定量数据的硬件堆栈;5)处理器有将堆栈指针和其它 CPU 寄存器读出和存储到堆栈或内存中的指令。
  AndesCore™ N1033A-S 内部提供了 32 个通用寄存器,其中 R31 被用来做专门的堆栈指针。32 根地址线最多可访问 4GB 存储单元,因此只要系统 RAM空间允许,堆栈空间理论不会产生限制。N1033A-S 处理器提供的 AndeStar™ V2指令集包含了丰富且十分高效的对堆栈进行操作的指令。例如指令 SMW(store multiple word)可实现仅使用一条指令将多个寄存器的值存储到堆栈中并同时更新堆栈指针位置,而且还能很好的处理地址非对齐字的存取。N1033A-S 支持中断并能产生定时器中断,处理器中的 PSW(Processor Status Word)寄存器中包含一个全局中断禁止位 GIE,控制它便可实现打开和关闭中断。此外, AndeSight™集成开发环境中内置的编译器可以产生可重入代码,并且支持内联汇编,C 环境中可以任意进行开关中断的操作。综上所述,µC/OS-II 完全可以移植到 N1033A-S 上运行。

3. 移植步骤
  为了方便移植,大部分的 µC/OS-II 代码是用 C 语言写的,用户只需要用 C语言和汇编语言写一些与处理器相关的代码就可以实现移植。这部分工作的内容包括:一个完成基本设置的头文件 os_cpu.h、一个与处理器相关的汇编文件 os_cpu_a.S 和一个与操作系统相关的 C 代码文件 os_cpu_c.c。

3.1 在 os_cpu.h 中完成基本的配置和定义
   3.1.1. 定义与处理器相关的数据类型
为保证可移植性,µC/OS-II 没有直接使用 C 语言中的 short、int 和 long 等数据类型的定义,因为不同的处理器有不同的字长。对于 N1033A-S 这样的 32位处理器,其数据类型定义实现如下:

   3.1.2.定义中断禁止/允许宏
  做为实时内核,µC/OS-II 需要先禁止中断再访问代码临界区,并且在访问完毕后重新允许中断。µC/OS-II 定义了两个宏来禁止和允许中断: OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。在N1033A-S 处理器上的实现代码如下:

GIE_SAVE 和 GIE_RESTORE 的实现如下:

中断禁止时间是判断系统实时性的重要指标之一。中断禁止时间能否达到最短,不仅与操作系统的设计有关,还依赖于处理器结构和编译器产生的代码质量。

从上面的实现代码看到,由于 Andes 处理器提供了 setgie.d 和 setgie.e 两条直接控制中断的开关的指令,整个禁止/允许中断的过程经过编译器产生的机器码只有 3/2 条,最大限度地减小了中断禁止时间。

   3.1.3. 定义栈增长方向
   µC/OS-II 使用结构常量 OS_STK_GROWTH 来指定堆栈的增长方式,设置为 0 表示堆栈从下往上增长,设置为 1 表示从上往下增长。这里我们定义成后者,即堆栈的增长方向是从内存高地址向低地址方向递减并且堆栈指针总是指向栈 顶数据:

   3.1.4. 定义 OS_TASK_SW()宏
  OS_TASK_SW()是一个宏,它在 µC/OS-Ⅱ 从低优先级任务切换到最高优先级任务时被调用的。任务切换只是简单的将处理器寄存器保存到将被挂起的任务
的堆栈中,并且将更高优先级的任务从堆栈中恢复出来。可采用两种方式定义这个宏,使用软中断将中断向量指向 OSCtxSW()函数;或者直接调用 OSCtxSW()函数,这里我们采用后者(OSCtxSW()函数的实现将在后面介绍):

3.2 处理器相关部分汇编实现
  µC/OS-Ⅱ 的移植需要用户编写三个最基本的汇编语言函数:
OSStartHighRdy(),OSCtxSw(),OSIntCtxSw()。它们会共用一些代码,为了方便阅读将它们写在同一个汇编文件 os_cpu_a.S 中。

   3.2.1 OSStartHighRdy():运行优先级最高的就绪任务。
   OSStartHighRdy()函数是在 OSStart()多任务启动之后,负责从最高优先级任务的 TCB 控制块中获得该任务的堆栈指针 SP,并通过SP 恢复 CPU 现场以启动最高优先级的任务执行。另外 OSStartHighRdy()还必须在最高优先级任务恢复之前和调用 OSTaskSwHook()之后设置OSRunning 为 TRUE。其实现代码如下:

   3.2.2 OSCtxSw()和OSIntCtxSw()
  OSCtxSw()是任务优先级切换函数,它的作用是先将当前任务的CPU 现场保存到该任务的堆栈中,然后获得最高优先级任务的堆栈指针,并从该堆栈中恢复此任务的 CPU 现场,使之继续执行,该函数就完成了一次任务切换。
  OSIntCtxSw()是中断级的任务切换函数。由于中断可能会使更高优先级的任务进入就绪态,因此为了让更高优先级的任务能立即运行,在中断服务子程序最后会调用 OSIntCtxSw()做任务切换。这样做能够尽快的让高优先级的任务得到相应的处理,保证系统的实时性能。
  OSCtxSw()和OSIntCtxSw()都是用于任务切换的函数,其区别在于,在 OSIntCtxSw()中无需再保存处理器寄存器,因为在OSIntCtxSw()之前已发生中断,所以可以保证所有的处理器寄存器都被正确地保存到了被中断的任务的堆栈之中。OSCtxSw()和OSIntCtxSw()实现代码如下:

  N1033A-S 处理器定义了四级(0-3)中断,在各级中断的转换时需要保存当前中断层级的寄存器。调用 OSCtxSw()时,中断将由 0 级(即没有中断)转到 1 级,所以需要将第 0 级的寄存器 PSW 和 PC 保存到第 1 级的寄存器 IPSW和 IPC 中。CtxSave 和 CtxRestore 两个宏用来保存和恢复任务上下文。需要保存或恢复的寄存器包括 32 个通用寄存器(R0-R31)的值、程序计数器(PC) 的值以及处理器状态字寄存器(PSW) 的值。宏IntlSwitch n 通过修改 PSW.INIT 的值来切换中断层级。CtxSave 和 IntlSwitch 的汇编实现如下(由于 CtxRestore 与 CtxSave 过程类似,这里不做赘述):

3.3 移植 C 语言编写的几个与操作系统相关的函数
    µC/OS-Ⅱ 有六个与 CPU 相关的函数:OSTaskStkInit()、OSTaskCreateHook()、OSTaskDelHook()、OSTaskSwHook()、 OSTaskStatHook()、OSTimeTickHook(),它们被定义在 ucos_ii.h 中。其中唯一必须移植的函数是任务堆栈初始化函数 OSTaskStkInit(),其它五个函数必须得声明但没必要包含代码。因此这里我们只介绍 OSTaskStkInit(),其代码的实现如下:

OSTaskStkInit()在任务创建时被调用,负责初始化任务的堆栈结构并返回新堆栈的指针,使得堆栈看起来就像刚发生过中断并将所有的寄存器保存到堆栈中
的情形一样。除了要保存任务的地址、变量的指针以及处理器状态字的值外, Andes N1033A-S 处理器还要求用户保存所有 32 个通用寄存器(R0-R31)、四个用户寄存器(d0.hi, d0.lo, d1.hi, d1.lo)。还有一点需要注意,在 N1033A-S 处理器中,堆栈指针的地址必须满足 8Byte 对齐,程序最后一段逻辑即将堆栈指针调整到正确的位置,这一点在编写其他代码例如在宏 CtxSave 中同样需要注意。

4. 結語
  基于 AndesStar™架构的优势,可以很容易的实现 µC/OS-Ⅱ 在 N1033A-S处理器上的移植。不仅 µC/OS-Ⅱ ,其它嵌入式操作系统也可以很方便地移植到 AndesCore™相应的处理器上,例如 Nuclues、FreeRTOS 以及 Contiki。
  晶心科技利用 AndesCore™ N1033A-S 高效能的 Audio ISA 和 FPGA 开发平台弹性的设计架构,基于各种 RTOS,为客户提供了的丰富的软件资源(中间件、优化的函数库、应用实例等)以及完整的多媒体语音解决方案,从而帮助客户更快地在 Andes 平台上进行产品开发。