一、写在前面

本文记录将µC/OS-III从Zynq平台移植到复旦微FMQL20(国产Zynq)的完整过程。整体思路是:先在Zynq上跑通官方移植方案,再以此为参考,逐一替换平台相关模块,移植到FMQL平台。

之所以采用这个路线,是因为:

  • Zynq平台有Micrium官方提供的开箱即用移植方案,网上资料丰富
  • FMQL是国产化Zynq替代方案,ARM部分为四核Cortex-A7(Zynq为双核Cortex-A9),同属Armv7架构
  • 复旦微没有像Xilinx SDK那样完整的闭环开发工具链,需要使用IAR + Procise的组合

关于开发环境:Zynq使用Vivado + SDK(eclipse定制版),生态闭环;FMQL使用Vivado + Procise + IAR的组合。Procise相当于“低配版SDK”,负责生成BSP和导出工程到IAR。

二、Zynq平台µC/OS-III移植(参考基础)

2.1 环境准备

  1. Vivado 2018.3:与Zynq-7000系列兼容性最佳
  2. 正点原子Zynq开发板
  3. USB转串口工具:用于调试信息输出

2.2 Vivado硬件工程创建

  1. 创建Vivado工程
  2. 添加ZYNQ Processing System IP,创建PS系统
  3. 导出硬件到SDK,创建PS SDK程序

2.3 获取并导入µC/OS-III源码

  1. 获取官方移植包:Micrium官方为ZYNQ Cortex-A9提供了开箱即用的移植方案,从GitHub获取:zynq_guide/ucos at main · suisuisi/zynq_guide · GitHub,下载ucos_v1_45.7z压缩包
  2. 将其移动到Vivado目录下方便管理,我是保存在了 ...\Xilinx2018.3\SDK\2018.3\data\embeddedsw\ucos_v1_45
  3. 在SDK中导入源码(Local Repositories方式):
    • 菜单 Xilinx → Repositories
    • Global Repositories中添加ucos,并确认(如果不想全局使用只想单个项目使用就添加到Local Repositories)

2.4 创建µC/OS-III应用工程

  1. 在SDK中新建Application Project,关键参数:
    • Project name: ucos_hello_world
    • OS Platform: 选择"ucos"(导入源码后才会出现)
    • Template: Micrium uc/OS-III Hello World
  2. 工程创建后自动生成:
    • src/app.c - 主程序入口
    • system.mss - BSP配置文件
    • ucos_bsp/ - 板级支持包

2.5 BSP关键配置

  1. 点击打开Ucos III BSP支持包界面
  2. 选择 Modify this BSP's Settings
    • 勾选这几个必要选项:
    • 打开 ucos_standalone配置 stdinstdout

三、从Zynq到FMQL的移植

3.1 FMQL与Zynq的核心差异

在开始移植前,需要理解两个平台的关键差异:

  • ARM 核:Xilinx Zynq 采用双核 Cortex‑A9,复旦微 FMQL 采用四核 Cortex‑A7。
  • ARM 架构:两者同属 Armv7‑A,指令集兼容,为移植提供了基础。
  • 开发环境:Zynq 使用 Vivado + SDK(Xilinx 定制 Eclipse);FMQL 使用 Vivado + Procise + IAR EWARM。
  • 调试工具:Zynq 通常使用标准 JTAG;FMQL 可使用 JTAG‑Cable 或 J‑Link。
  • IDE:Zynq 的 SDK 基于 Eclipse 定制;FMQL 以 IAR EWARM 为主 IDE。
  • BSP 生成:Zynq 由 SDK 自动生成;FMQL 由 Procise 生成后再导出到 IAR 工程。

ARM核同属Armv7架构,指令集兼容,但外设寄存器地址、中断控制器、定时器等底层硬件不同,需要逐个替换平台相关代码。

3.2 环境搭建

必备工具

  1. Vivado:与Zynq相同,用于PL端开发
  2. Procise:复旦微配套软件,用于生成BSP和导出IAR工程
  3. IAR EWARM:FMQL ARM端的主开发环境

3.3 移植总体策略

µC/OS-III内核源码保持Micrium原版不变,所有平台相关部分按以下原则替换:

  • 中断控制器:Zynq 使用 Xilinx 的 XScuGic 驱动;FMQL 替换为复旦微自研的 FGicPs 驱动,两者接口不同,需重新适配 GIC 初始化、中断使能/禁止及中断连接函数。
  • 系统 Tick 定时器:Zynq 常用 SCU Timer 作为系统时钟节拍源;FMQL 采用 TTC(Triple Timer Counter),对应驱动为 FTtcPs,需要重新实现定时器配置、中断处理及 OSTimeTick() 的调用。
  • 串口打印:Zynq 使用 xil_printf 系列函数输出调试信息;FMQL 底层打印接口为 fmsh_print,因此需要将 UCOS_PrintfUCOS_Print 的实现替换为基于 fmsh_print 的版本。
  • PS 初始化:Zynq 的板级初始化入口为 ps7_init;FMQL 对应的初始化函数为 ps_init,需要在底层启动流程中调用正确的初始化函数。
  • 启动与异常向量:Zynq 使用 GNU 汇编文件 ucos_vectors.S 提供向量表及安装逻辑;FMQL 在 IAR 环境下使用 ucos_cstartup.s,这是一个完整的复位启动文件,包含 resetHandler 入口、向量表、栈初始化及 VBAR 设置,且链接时必须指定入口为 resetHandler
  • CPU/OS 汇编 port:Zynq 的 CPU 抽象层和 OS 移植层使用 GNU 汇编(.S 文件);FMQL 配合 IAR 编译器,必须使用对应的 IAR 汇编语法文件(.s),包括 cpu_a.sos_cpu_a_vfp-none.s,不能混用不同工具链的汇编源文件。

3.4 移植文件组织情况

  1. Export Hardware:选择导出 EmptyFSBL(也可以不导出 Empty在IAR直接创建UCOS III工程)
  2. 在IAR修改Empty工程为UCOS III工程
    • 添加组织文件:用于保存各类源码,其中FM_QL_bsp是Procise导出工程自动生成的,FM_QL_ucos_bsp是需要自己组织的,UcosIII_Test是应用工程
  3. FM_QL_ucos_bsp的文件作用:
    • ucos_common_v1_45:提供 uC/CPU 硬件抽象层(中断开关、临界区、位操作等)和 uC/LIB 基础库(字符串、内存、数学辅助)
    • ucos_osiii_v1_45:包含 uC/OS-III 全部内核源文件,以及针对 ARMv7-A + IAR 的汇编移植文件(os_cpu_a_vfp-none.s
    • ucos_standalone_v1_45:提供 FMQL 平台的最小初始化与打印支撑,替代原 Xilinx standalone 库。
    • ucos_v1_45:包含顶层 BSP 适配文件 fmql_ucos_bsp.c、启动汇编 ucos_cstartup.s、中断接口声明 ucos_int.h 以及应用配置 app_cfg.h

四、新增与修改的平台适配文件说明

本章说明在移植过程中新增编写和基于原 Zynq BSP 修改的文件。其中一部分是工具链相关的汇编 port 文件,另一部分是 FMQL 平台自己的 BSP 胶水层。这些文件的共同点是:它们都与具体硬件平台或编译工具链绑定,不能直接沿用 Zynq BSP 中对应文件。

4.1 新增的平台适配文件

  1. cpu.h
    • 文件位置:ucos_common_v1_45/src/uC-CPU/ARM-Cortex-A/ARMv7-A/IAR/cpu.h
    • 本文件定义 ARMv7-A CPU port 的数据类型、堆栈格式、大小端配置、临界区进入/退出接口、内存屏障指令封装,以及若干底层汇编函数的声明。本工程移植时,参照了 Micrium 官方提供的 ARMv7-A GNU port 的接口定义与运行语义,将其按 IAR EWARM 编译器的语法规则重新编写,形成此 IAR 版本。
  2. cpu_a.s
    • 文件位置:ucos_common_v1_45/src/uC-CPU/ARM-Cortex-A/ARMv7-A/IAR/cpu_a.s
    • 本文件是 cpu.h 中声明的底层 CPU 操作的汇编实现,包括关中断/开中断(含状态保存与恢复)、WFI/WFE 等待指令、前导零/尾随零计数(CLZ/RBIT+CLZ)、数据同步屏障(DSB)等。该文件以 GNU 版本 cpu_a.S 的功能语义为蓝本,按 IAR 汇编语法重新实现。
  3. os_cpu.h
    • 文件位置:ucos_osiii_v1_45/src/uCOS-III/Ports/ARM-Cortex-A/ARMv7-A/IAR/os_cpu.h
    • 本文件是 uC/OS-III 与 ARMv7-A CPU port 的接口头文件,定义了异常编号、任务栈初始化接口、上下文切换相关声明等。其内容与工具链相关,同样必须选用 IAR 版本。
  4. os_cpu_a_vfp-none.s
    • 文件位置:ucos_osiii_v1_45/src/uCOS-III/Ports/ARM-Cortex-A/ARMv7-A/IAR/os_cpu_a_vfp-none.s
    • 本文件是 uC/OS-III 最核心的 ARMv7-A 汇编 port,负责首次任务启动、任务切换、异常入口现场保存与恢复、调用 OS_CPU_ExceptHndlr() 进行中断分发,以及在中断退出时调用 OSIntExit() 检查是否需要任务切换。本文件是以 GNU 版本 os_cpu_a_vfp-none.S 的功能为基准,按 IAR 汇编语法重新编写而成的 IAR 版本。文件名的 vfp-none 后缀表示不保存 VFP 浮点上下文,这是当前工程的一个配置选择;如果后续任务使用硬浮点或 NEON 指令,需要重新评估该选项。
  5. fmql_ucos_bsp.c
    • 文件位置:ucos_v1_45/src/fmql_ucos_bsp.c
    • 本文件是 FMQL 平台 BSP 适配层的核心 C 文件,它并非简单地“裁剪” Zynq 的某个文件,而是将 Zynq BSP 中分散在多个文件里的平台相关功能,统一在 FMQL 的硬件接口上重新实现。具体替代关系如下:
      • 替代 ucos_startup.c 中的启动流程函数:
        • UCOSStartup():负责调用底层初始化、CPU_Init()Mem_Init()OSInit(),创建启动任务并启动调度器。
        • UCOSStartupTask():作为启动任务实体,调用后续各项子初始化,最后跳转到用户主函数。
        • UCOS_StdInOutInit()UCOS_PrintfInit():在 FMQL 中为空实现,实际打印功能由 UCOS_Print/Printf 直接驱动硬件。
        • UCOS_Print()UCOS_Printf():用于调试追踪输出,FMQL 直接实现为调用 fmsh_print,替代了原版基于 outbyte 的输出方式。
      • 替代 ps7/ucos_impl.c 中的底层初始化:
        • UCOS_LowLevelInit():FMQL 版本执行 init_platform()(更完整的板级初始化)和 UCOS_IntInit(),取代了 Zynq 版本的 Xil_DCacheFlush() + UCOS_IntInit()
        • UCOS_StartupInit():FMQL 版本为空实现,因为向量表安装已在汇编文件 ucos_cstartup.s 中完成,而 Zynq 版本需要调用 _install_ucos_vector_table()
      • 替代 ps7/ucos_int_impl.c 中的 GIC 中断控制接口:
        • UCOS_IntInit():负责初始化 GIC 中断控制器。Zynq 使用 XScuGic + Xil_Exception,FMQL 使用自研的 FGicPs 驱动完成初始化。
        • UCOS_IntSrcEn() / UCOS_IntSrcDis():分别调用 FGicPs_Enable() / FGicPs_Disable() 使能或禁止指定中断。
        • UCOS_IntPrioSet()UCOS_IntTargetSet()UCOS_IntTypeSet():通过 FGicPs_SetPriorityTriggerType()FGicPs_InterruptMaptoCpu() 设置中断优先级、目标 CPU 和触发类型。
        • UCOS_IntVectSet():一站式注册中断,调用 FGicPs_Connect() 绑定 ISR。
        • OS_CPU_ExceptHndlr():由汇编异常入口调用,负责分发 IRQ/FIQ。Zynq 版本通过 switch 调用 IRQInterrupt()/FIQInterrupt(),FMQL 版本直接调用 FGicPs_InterruptHandler_IRQ()FGicPs_InterruptHandler_FIQ()
      • 替代 bsp/ps7/ucos_osiii_bsp.c 中的系统 tick 实现:
        • UCOS_TmrTickInit():初始化系统时钟节拍定时器,FMQL 使用 TTC,Zynq 使用 SCU Timer,配置方式不同但功能等价。
        • UCOSTmrTickHandler():作为定时器中断服务函数,清除中断标志并调用 OSTimeTick()
      • 替代 ucos_printf.cucos_stdinout.c 中的打印与输入输出:
        • UCOS_Printf():使用标准 vsnprintf() 格式化后调用 fmsh_print() 输出,替代原版的自定义格式解析和 outbyte 方式。
        • UCOS_Print():直接调用 fmsh_print() 输出字符串。
        • UCOS_Read():空实现,返回 0。
  6. ucos_cstartup.s
    • 文件位置:ucos_v1_45/src/ucos_cstartup.s
    • 本文件是 FMQL 平台完整的 IAR 复位启动文件,提供 resetHandler 入口、异常向量表、各 CPU 模式栈初始化、CP15 系统控制寄存器配置、VFP 使能、VBAR 设置,最后跳转到 main。它与 Zynq 的 ucos_vectors.S 有本质区别:Zynq 版本仅提供向量表和安装函数,在运行时将向量表切换到 uC/OS 版本;而 FMQL 版本是芯片复位后的第一段执行代码,从复位入口开始完整接管启动流程。因此 IAR 链接配置中必须将程序入口指定为 resetHandler,并配合 a7_axisram.icf 链接脚本使用。

4.2 基于原文件修改的配置文件

以下文件是在原 Zynq BSP 对应文件基础上修改而来。修改分为两类:一类是平台相关的必要调整(如中断号、硬件初始化方式),另一类是根据当前应用需求对内核功能模块做裁剪。裁剪部分并非 FMQL 硬件强制要求,而是为了精简系统、减小资源占用;如果后续应用需要对应功能,可以重新打开。

  1. cpu_cfg.h
    • 文件位置:ucos_common_v1_45/src/cpu_cfg.h
    • 本文件的主要修改包括:
      • 关闭 32 位和 64 位时间戳支持(CPU_CFG_TS_32_EN / CPU_CFG_TS_64_EN 设为 DEF_DISABLED),说明当前工程不依赖 uC/CPU 的时间戳模块。
      • 保持启用 ARM 汇编优化的前导零和尾随零计数(CPU_CFG_LEAD_ZEROS_ASM_PRESENT / CPU_CFG_TRAIL_ZEROS_ASM_PRESENT),使用 CLZRBIT+CLZ 指令实现,这也是 ARMv7-A port 的常规做法,并非 FMQL 特有。
      • 关闭缓存管理 API(CPU_CFG_CACHE_MGMT_EN = DEF_DISABLED)。当前 PL UART echo 应用不涉及 DMA 或 cacheable buffer 与外设共享,因此可以关闭;后续若使用 DMA、网络、USB、SD 卡等,需要重新启用并提供 FMQL 对应的 cache flush/invalidate 实现。
      • CPU_CFG_NVIC_PRIO_BITS 宏设为 4,该宏原本是 Cortex-M NVIC 控制器的配置项,在 Cortex-A7 + GIC 路径上没有实际作用,属于模板遗留配置,保留它不影响当前功能。
  2. os_cfg.h
    • 文件位置:ucos_osiii_v1_45/src/os_cfg.h
    • 本文件主要对 uC/OS-III 内核功能模块进行开关控制。与 Zynq 模板相比,以下模块被关闭:
      • 事件标志组(OS_CFG_FLAG_EN = 0u
      • 内存分区管理(OS_CFG_MEM_EN = 0u
      • 消息队列(OS_CFG_Q_EN = 0u
      • 任务消息队列(OS_CFG_TASK_Q_EN = 0u
      • 软件定时器(OS_CFG_TMR_EN = 0u,不影响 TTC 硬件 tick)
      • 任务私有寄存器(OS_CFG_TASK_REG_TBL_SIZE = 0u
    • 保留开启的模块包括信号量(OS_CFG_SEM_EN = 1u)和互斥量(OS_CFG_MUTEX_EN = 1u),用于 PL UART 回显应用中的 ISR 与任务同步,以及控制台打印的互斥保护。
  3. os_cfg_app.h
    • 文件位置:ucos_osiii_v1_45/src/os_cfg_app.h
    • 本文件与应用级配置相关。主要修改是将消息池容量设为 0(OS_CFG_MSG_POOL_SIZE = 0u),与消息队列裁剪保持一致。系统 tick 频率保持为 1000 Hz(OS_CFG_TICK_RATE_HZ = 1000u),空闲任务栈大小保持为 256 字,这两项与 Zynq 模板一致。
  4. app_cfg.h
    • 文件位置:ucos_v1_45/src/app_cfg.h
    • 本文件是 FMQL 顶层 BSP 的应用配置头文件,与 Zynq 模板相比有以下新增和调整:
      • 新增启动任务参数:UCOS_START_TASK_PRIO 设为 5,UCOS_START_TASK_STACK_SIZE 设为 1024,以及 UCOS_START_DEBUG_TRACE 启用调试打印。这些宏被 fmql_ucos_bsp.c 直接使用。
      • 新增中断相关配置:UCOS_TICK_INT_PRIO 设为 0xA0(即 160),指定系统 tick 中断在 GIC 中的优先级;UCOS_GIC_CPU0_TARGET 设为 0x01,指定中断目标为 CPU0。
      • 关闭 APP_COMMON_ENABLED(设为 DEF_DISABLED),进一步精简对 µC/Common 通用模块的依赖。
  5. ucos_bsp.h
    • 文件位置:ucos_v1_45/src/ucos_bsp.h
    • 本文件是 BSP 对应用层暴露的接口声明。与 Zynq 版本相比:
      • FMQL 版本只定义了三种简单类型宏(CPU 类型、UART 类型、系统定时器类型),没有引入 TCP/IP、Shell、文件系统、USB 等可选中间件相关的类型或声明。
      • 声明的 BSP 函数仅限于最基础的 9 个:UCOS_LowLevelInitUCOS_StartupInitUCOS_TmrTickInitUCOSStartupUCOS_StdInOutInitUCOS_PrintfInitUCOS_PrintUCOS_PrintfUCOS_Read。去除了 Zynq 版本中大量的可选中间件初始化函数声明(如 Shell、DHCP、DNS、HTTP、MQTT、Telnet、iPerf、USB 等)。
      • FMQL 版本只包含 cpu.hlib_def.h,不依赖 ucos_impl.h 或 Xilinx 异常框架头文件,所有的平台实现细节都封闭在 fmql_ucos_bsp.c 内部。
  6. ucos_int.h
    • 文件位置:ucos_v1_45/src/ucos_int.h
    • 本文件是中断接口声明头文件。与 Zynq 版本相比:
      • FMQL 版本只包含 cpu.hlib_def.h,没有引入 xil_exception.hucos_int_impl.h,因此不依赖 Xilinx 的异常处理框架。
      • 在自身文件中直接定义了 UCOS_INT_FNCT_PTR(中断回调函数指针类型)和 UCOS_INT_TYPE(中断号类型),确保了头文件的完整性和自包含性。
      • Zynq 版本则将这两个类型的定义放在 ucos_int_impl.h 中,通过包含关系间接获取,与 Xilinx 的 Xil_InterruptHandler 类型绑定。

五、总结

从Zynq到FMQL的µC/OS-III移植,核心是平台相关代码的逐块替换,而非从头移植。关键工作包括:

  1. 工具链适配:GNU汇编 → IAR汇编
  2. 启动代码重写:ucos_vectors.S → ucos_cstartup.s
  3. 中断控制器替换:XScuGic → FGicPs
  4. 定时器替换:SCU Timer → FTtcPs
  5. 打印输出替换:xil_printf → fmsh_print
  6. 开发环境切换:SDK → Procise + IAR
    FMQL与Zynq同属Armv7架构,指令集兼容,这大大降低了移植难度。主要工作量集中在外设驱动层和工具链适配层。掌握Zynq上的移植流程后,对照替换上述模块,即可在FMQL平台上顺利运行µC/OS-III。
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐