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 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
位域描述
| 位范围 | 名称 | 说明 |
|---|---|---|
| 15 | T | 跟踪使能:置位后处理器在每条指令执行后触发跟踪异常 |
| 14 | - | 保留位,必须保持清零 |
| 13 | S | 特权状态:0=用户模式,1=超级用户模式 |
| 12 | M | 主/中断状态:由中断异常自动清零,可通过RTE指令或写SR寄存器置位 |
| 11 | - | 保留位,必须保持清零 |
| 10-8 | I | 中断级别掩码:3位对应0-7级,控制可响应的中断优先级 |
| 7-0 | CCR | 条件码寄存器:包含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) ); }
想请教大家的问题
- 针对Cortex-A9的上下文切换,有没有办法基于ColdFire的原有逻辑做适配,而不是完全重写调度器?
- 我上面实现的临界区函数是否正确?有没有更稳妥的Cortex-A9临界区实现方式?
- 从IRQ中触发上下文切换的正确姿势是什么?怎么处理IRQ栈和任务栈的切换问题?
- 综合来看,是修复现有代码更可行,还是直接针对Cortex-A9重写调度器部分更高效?
内容来源于stack exchange

