You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ColdFire到Zynq Cortex-A9的专有RTOS上下文切换移植求助:修复现有实现还是重写?

ColdFire到Zynq Cortex-A9的专有RTOS上下文切换移植求助:修复现有实现还是重写?

我目前正在把一款原本为ColdFire CPU编写的专有RTOS移植到Zynq(内部是Cortex-A9 Armv7 CPU)上!折腾上下文切换函数好一阵子了,感觉这套实现根本就是为ColdFire量身定做的,完全没法在新平台上正常跑。试了好几种临时方案都没进展,现在纠结要不要把整个调度器实现推倒,针对Zynq从零重写。我会把现有实现细节都列出来,希望有人能给点建议——是有办法修复现有代码、有没有可行的替代方案,还是确实应该直接重写?

ColdFire 原有实现细节

临界区汇编实现

原有RTOS的临界区通过操作SR寄存器关闭中断,对应的汇编函数如下:

_syst_CS:
    move.w sr,d0
    move.w #0x2700,sr
    rts
    nop

_syst_CSEnd:
    move.w 6(a7),d0
    move.w d0,sr
    rts

ColdFire的SR寄存器直接控制中断禁用等核心功能,下面是它的布局和位域说明:

ColdFire 状态寄存器(SR)布局

ASCII位布局:

Bit: 15 14 13 12 11 10 9  8  7  6  5  4  3  2  1  0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| T | 0 | S | M | 0 | I |       CCR       |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

位域描述

位范围名称说明
15T跟踪使能:置位后处理器在每条指令执行后触发跟踪异常
14-保留位,必须保持清零
13S特权状态:0=用户模式,1=超级用户模式
12M主/中断状态:由中断异常自动清零,可通过RTE指令或写SR寄存器置位
11-保留位,必须保持清零
10-8I中断级别掩码:3位对应0-7级,控制可响应的中断优先级
7-0CCR条件码寄存器:包含8位指令执行状态标记

上下文启动与切换实现

以下是ColdFire平台的上下文启动和切换汇编代码:

syst_McuCtxStart(uint32_t *old_sp, uint32_t new_stack, void (*new_pc)(void *), void *new_context);
_syst_McuCtxStart:
    ; 保存当前任务链接
    link a6,#-40
    movem.l d2/d3/d4/d5/d6/d7/a2/a3/a4/a5,(a7)
    move.w sr, d0 ; 保存当前中断级别
    move.l d0, -(a7)
    move.l 8(a6), a0 ; 存储当前栈指针
    move.l a7, (a0)
    ; 启动新任务
    move.l 12(a6), a7
    add.l 16(a6), a7 ; 初始化新栈指针
    move.l 20(a6), a0 ; 新任务入口地址
    move.l 24(a6), d0 ; 新任务上下文参数
    move.l d0, -(a7)
    move.w #0x2000, sr ; 初始化SR寄存器
    jsr (a0) ; 调用新任务入口
loop:
    bra loop ; 防止返回
syst_McuCtxSw(uint32_t *current_context, uint32_t next_context);
_syst_McuCtxSw:
    ; 保存当前任务上下文
    link a6,#-40
    movem.l d2/d3/d4/d5/d6/d7/a2/a3/a4/a5,(a7)
    move.w sr, d0 ; 保存中断级别
    move.l d0, -(a7)
    move.l 8(a6), a0
    move.l a7, (a0) ; 存储当前栈指针到上下文
    ; 恢复下一个任务上下文
    move.l 12(a6), a7
    move.l (a7)+, d0
    move.w d0, sr ; 恢复中断级别
    movem.l (a7),d2/d3/d4/d5/d6/d7/a2/a3/a4/a5
    lea 40(a7),a6
    unlk a6
    rts

定时器中断处理(调度触发入口)

定时器中断是触发任务切换的核心入口,处理函数如下:

_syst_McuCtxIrq:
    move.w #0x2700, sr ; 关闭所有中断,防止嵌套插入定时器请求
    link a6,#0
    lea -16(a7),a7
    movem.l d0/d1/a0/a1,(a7)
    jsr _timer_ReqRaise
    movem.l (a7),d0/d1/a0/a1
    unlk a6
    rte

其中_timer_ReqRaise函数负责停止定时器、将当前任务挂起到等待列表,最终调用syst_McuCtxSw完成任务切换。此外任务切换也可在普通代码流中触发(如任务挂起、终止操作)。

Arm Cortex-A9 架构差异与我的困境

ColdFire和Cortex-A9的核心差异在于:ColdFire没有多特权模式、寄存器分组(banked registers)以及模式独立栈的设计!我一开始为了避开IRQ直接调度的问题,用全局变量标记调度请求,在IDLE任务中批量处理,但这样导致系统确定性极差,完全不符合RTOS的实时性要求。

后来尝试直接在IRQ栈中保存上下文,结果直接出现大量未定义行为,整个中断系统乱成一团,实在没法继续硬撑,只能重新梳理思路。

我基于Vitis工具链的尝试(可能存在问题)

因为是移植到Xilinx的CPU,我用了Vitis开发环境和工具链,临界区部分做了直接转换,但自己也不确定是否正确:

__attribute__((naked)) unsigned int syst_CS(void) {
    __asm__ volatile (
        "mrs r0, cpsr \n\t"          // 读取当前CPSR到r0(作为返回值)
        "orr r1, r0, %0 \n\t"        // 与掩码或操作,关闭IRQ和FIQ
        "msr cpsr_c, r1 \n\t"        // 写回CPSR控制域
        "bx lr \n\t"
    : : "i" (0xC0)                   // 掩码对应CPSR的I(IRQ禁用)和F(FIQ禁用)位
    : "r1"
    );
}

__attribute__((naked)) void syst_CSEnd(unsigned int old_cpsr) {
    __asm__ volatile (
        "msr cpsr_c, %0 \n\t"        // 恢复之前的CPSR控制域
        "bx lr \n\t"
    : : "r" (old_cpsr)
    );
}

想请教大家的问题

  1. 针对Cortex-A9的上下文切换,有没有办法基于ColdFire的原有逻辑做适配,而不是完全重写调度器?
  2. 我上面实现的临界区函数是否正确?有没有更稳妥的Cortex-A9临界区实现方式?
  3. 从IRQ中触发上下文切换的正确姿势是什么?怎么处理IRQ栈和任务栈的切换问题?
  4. 综合来看,是修复现有代码更可行,还是直接针对Cortex-A9重写调度器部分更高效?

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 09:33:08