学习基于GHS将freeRTOS移植到RH850 F1L的过程
移植过程参考github的一个教程,其中有部分修改优化,但是不多。
GitHub - mikisama/FreeRTOS_RH850: FreeRTOS port for Renesas RH850
支持gcc、ghs、ccrh、IAR 4种编译器。
有问题一起讨论,也可以直接按照github上的参考一步步移植。
freeRTOS下载
直接到官网下载即可
FreeRTOS - Free RTOS Source Code Downloads, the official FreeRTOS zip file release download
下载完成后,目录如下:
- 图片中的点c是RTOS的源文件,这些不用动,直接拿来用。
- portable中是平台依赖相关的文件,也是我们需要动手移植修改的。
- include文件是API接口文件,代码中使用的时候直接include对应文件即可
移植前准备
RH850 F1L这款芯片比较特殊,用的不是标准的ARM核,而是瑞萨自己开发的,这样对移植工作比较麻烦,特别是关于freeRTOS任务栈初始化、任务启动和切换,这些和mcu底层相关性大的地方,大部分需要查阅瑞萨官方资料。
freeRTOS从启动到开始任务调度之前需要完成下面几件事?
- 任务栈初始化
- 执行first task
- 任务切换(task yield)
- sys tick 任务调度(涉及任务状态的保存)
- OS无限循环任务调度
相应的MCU相关的配置有:
- 中断向量表配置
- sys tick中断配置
- 系统堆栈配置
移植
后面就直接把对应的移植过程一一总结
1、freeRTOS portable目录
展开MemMang和GHS(本人用的编译器)文件夹:
heap_4.c这个不用修改,GHS下面的三个文件需要移植
2、任务栈初始化
port.c中:
StackType_t *pxPortInitialiseStack( StackType_t * pxTopOfStack,
TaskFunction_t pxCode,
void * pvParameters )
{
/* Simulate the stack frame as it would be created by a context switch
* interrupt. */
*( pxTopOfStack ) = ( StackType_t ) 0x30303030; /* R30 (EP) */
*( --pxTopOfStack ) = ( StackType_t ) prvTaskExitError; /* R31 (LP) */
*( --pxTopOfStack ) = ( StackType_t ) pvParameters; /* R6 */
*( --pxTopOfStack ) = ( StackType_t ) 0x07070707; /* R7 */
*( --pxTopOfStack ) = ( StackType_t ) 0x08080808; /* R8 */
*( --pxTopOfStack ) = ( StackType_t ) 0x09090909; /* R9 */
*( --pxTopOfStack ) = ( StackType_t ) 0x10101010; /* R10 */
*( --pxTopOfStack ) = ( StackType_t ) 0x11111111; /* R11 */
*( --pxTopOfStack ) = ( StackType_t ) 0x12121212; /* R12 */
*( --pxTopOfStack ) = ( StackType_t ) 0x13131313; /* R13 */
*( --pxTopOfStack ) = ( StackType_t ) 0x14141414; /* R14 */
*( --pxTopOfStack ) = ( StackType_t ) 0x15151515; /* R15 */
*( --pxTopOfStack ) = ( StackType_t ) 0x16161616; /* R16 */
*( --pxTopOfStack ) = ( StackType_t ) 0x17171717; /* R17 */
*( --pxTopOfStack ) = ( StackType_t ) 0x18181818; /* R18 */
*( --pxTopOfStack ) = ( StackType_t ) 0x19191919; /* R19 */
*( --pxTopOfStack ) = ( StackType_t ) 0x01010101; /* R1 */
*( --pxTopOfStack ) = ( StackType_t ) 0x02020202; /* R2 */
*( --pxTopOfStack ) = ( StackType_t ) portINITIAL_PSW; /* EIPSW */
*( --pxTopOfStack ) = ( StackType_t ) pxCode; /* EIPC */
*( --pxTopOfStack ) = ( StackType_t ) 0x20202020; /* R20 */
*( --pxTopOfStack ) = ( StackType_t ) 0x21212121; /* R21 */
*( --pxTopOfStack ) = ( StackType_t ) 0x22222222; /* R22 */
*( --pxTopOfStack ) = ( StackType_t ) 0x23232323; /* R23 */
*( --pxTopOfStack ) = ( StackType_t ) 0x24242424; /* R24 */
*( --pxTopOfStack ) = ( StackType_t ) 0x25252525; /* R25 */
*( --pxTopOfStack ) = ( StackType_t ) 0x26262626; /* R26 */
*( --pxTopOfStack ) = ( StackType_t ) 0x27272727; /* R27 */
*( --pxTopOfStack ) = ( StackType_t ) 0x28282828; /* R28 */
*( --pxTopOfStack ) = ( StackType_t ) 0x29292929; /* R29 */
return pxTopOfStack;
}
3、first task接口实现
_vPortStartFirstTask:
mov _pxCurrentTCB, r2 # SP = pxCurrentTCB->pxTopOfStack
ld.w 0[r2], r2
ld.w 0[r2], sp
popsp r20 - r29 # Restore General Purpose Register (callee save register)
popsp r6 - r7
ldsr r7, EIPC # Restore EIPC
ldsr r6, EIPSW # Restore EIPSW
popsp r1 - r2 # Restore General Purpose Register (caller save register)
popsp r6 - r19
dispose 0, {ep, lp}
eiret
4、任务切换task yield
_vPortYieldHandler:
prepare {ep, lp}, 0
pushsp r6 - r19 # Save General Purpose Register (caller save register)
pushsp r1 - r2
stsr EIPSW, r6 # Save EIPSW
stsr EIPC, r7 # Save EIPC
pushsp r6 - r7
pushsp r20 - r29 # Save General Purpose Register (callee save register)
mov _pxCurrentTCB, r2 # pxCurrentTCB->pxTopOfStack = SP
ld.w 0[r2], r2
st.w sp, 0[r2]
jarl _vTaskSwitchContext, lp
mov _pxCurrentTCB, r2 # SP = pxCurrentTCB->pxTopOfStack
ld.w 0[r2], r2
ld.w 0[r2], sp
popsp r20 - r29 # Restore General Purpose Register (callee save register)
popsp r6 - r7
ldsr r7, EIPC # Restore EIPC
ldsr r6, EIPSW # Restore EIPSW
popsp r1 - r2 # Restore General Purpose Register (caller save register)
popsp r6 - r19
dispose 0, {ep, lp}
eiret
5 tick 任务调度
_vISRWrapper:
prepare {ep, lp}, 0
pushsp r6 - r19 # Save General Purpose Register (caller save register)
pushsp r1 - r2
stsr EIPSW, r6 # Save EIPSW
stsr EIPC, r7 # Save EIPC
pushsp r6 - r7
//pushsp r20 - r30 # Save General Purpose Register (callee save register)
mov _xInterruptNesting, r6
ld.w 0[r6], r7
cmp 0x0, r7 # if ( xInterruptNesting == 0 )
bne aa # {
mov _pxCurrentTCB, r2 # pxCurrentTCB->pxTopOfStack = SP
ld.w 0[r2], r2 # SP = MainStackTop
st.w sp, 0[r2] # }
mov ___ghsend_stack, sp
aa:
add 0x1, r7 # xInterruptNesting++
st.w r7, 0[r6]
stsr EIIC, r6 # Save EI level Interrupt Cause
ei # Enable interrupt (enable interrupt nesting)
jarl _vISRHandler, lp # Call the ISR Handler
di # Disable interrupt (disable interrupt nesting)
mov _xInterruptNesting, r6
ld.w 0[r6], r7
cmp 0x0, r7 # if ( xInterruptNesting > 0 )
be bb # {
add -1, r7 # xInterruptNesting--
st.w r7, 0[r6] # }
bb:
cmp 0x0, r7
bne dd # if ( xInterruptNesting == 0 )
mov _xPortSwitchRequired, r6 # {
ld.w 0[r6], r7 # if ( xPortSwitchRequired )
cmp 0x0, r7 # {
be cc
st.w r0, 0[r6] # xPortSwitchRequired = pdFALSE
mov _pxCurrentTCB, r2 # SP = pxCurrentTCB->pxTopOfStack
ld.w 0[r2], r2
ld.w 0[r2], sp
pushsp r20 - r29 # Save General Purpose Register (callee save register)
st.w sp, 0[r2] # pxCurrentTCB->pxTopOfStack = SP
jarl _vTaskSwitchContext, lp # vTaskSwitchContext()
mov _pxCurrentTCB, r2 # SP = pxCurrentTCB->pxTopOfStack
ld.w 0[r2], r2
ld.w 0[r2], sp
popsp r20 - r29 # Restore General Purpose Register (callee save register)
jr dd # }
cc: # else
mov _pxCurrentTCB, r2 # {
ld.w 0[r2], r2 # SP = pxCurrentTCB->pxTopOfStack
ld.w 0[r2], sp # }
dd: # }
//popsp r20 - r30 # Restore General Purpose Register (callee save register)
popsp r6 - r7
ldsr r7, EIPC # Restore EIPC
ldsr r6, EIPSW # Restore EIPSW
popsp r1 - r2 # Restore General Purpose Register (caller save register)
popsp r6 - r19
dispose 0, {ep, lp}
eiret
6 OS系统定时器配置
为OS提供心跳。
void ApiSysClkOstm0Init(uint32_t os_ticks)
{
Os_Enable_OS_CPU_Ticks();
ICOSTM0 = (0 << 12) | /* clear interrupt flag */
(0 << 7) | /* unmask interrupt */
#ifdef INT_VECT_EBASE_DIRECT
//(0 << 6) | /* direct method */
#else
(1 << 6) | /* table reference method */
#endif
(0x7 << 0); /* interrupt priority lowest */
OSTM0.CMP = (OSTM_PCLK / os_ticks) - 1;
OSTM0.CTL = 0X01U;
OSTM0.EMU = 0X00U;
}
7 中断配置
中断向量表地址索引有2种方法
Direct Vector Method和Table Reference Method
各路中断需要做相应的配置去使能哪一种。