CMSIS_RTOS_V2 rtx_delay.c 代码分析

时间:2024-04-02 15:38:58

本源文件就2个函数

 

// ==== Public API ====

 

/// Wait for Timeout (Time Delay).

osStatus_t osDelay (uint32_t ticks) {

osStatus_t status;

 

EvrRtxDelay(ticks); // 调试函数用,发布版本为空

if (IsIrqMode() || IsIrqMasked()) {

EvrRtxDelayError((int32_t)osErrorISR);

status = osErrorISR;

} else {

status = __svcDelay(ticks); // 关键函数__svcDelay(ticks);

}

return status;

}

 

/// Wait until specified time.

osStatus_t osDelayUntil (uint32_t ticks) {

osStatus_t status;

 

EvrRtxDelayUntil(ticks); // 调试函数用,发布版本为空

if (IsIrqMode() || IsIrqMasked()) {

EvrRtxDelayError((int32_t)osErrorISR);

status = osErrorISR;

} else {

status = __svcDelayUntil(ticks); // 关键函数__svcDelayUntil(ticks);

}

return status;

}

 

从上面代码分析,本质就是要分析 函数__svcDelay(ticks); 和 __svcDelayUntil(ticks);

 

但是奇怪的是这2个函数我们找不到。不过倒是有两行代码引起了我的注意

// Service Calls definitions

//lint ++flb "Library Begin" [MISRA Note 11]

SVC0_1(Delay, osStatus_t, uint32_t)

SVC0_1(DelayUntil, osStatus_t, uint32_t)

//lint --flb "Library End"

显然上面这两个宏是关键。

 

根据宏的定义

#define SVC_ArgN(n) \

register uint32_t __r##n __ASM("r"#n)

 

#define SVC_ArgR(n,a) \

register uint32_t __r##n __ASM("r"#n) = (uint32_t)a

 

#define SVC_ArgF(f) \

register uint32_t __rf __ASM(SVC_RegF) = (uint32_t)f

 

#define SVC_In0 "r"(__rf)

#define SVC_In1 "r"(__rf),"r"(__r0)

#define SVC_In2 "r"(__rf),"r"(__r0),"r"(__r1)

#define SVC_In3 "r"(__rf),"r"(__r0),"r"(__r1),"r"(__r2)

#define SVC_In4 "r"(__rf),"r"(__r0),"r"(__r1),"r"(__r2),"r"(__r3)

 

#define SVC_Out0

#define SVC_Out1 "=r"(__r0)

 

#define SVC_CL0

#define SVC_CL1 "r1"

#define SVC_CL2 "r0","r1"

 

#define SVC0_1(f,t,t1) \

__attribute__((always_inline)) \

__STATIC_INLINE t __svc##f (t1 a1) { \

SVC_ArgR(0,a1); \

SVC_ArgF(svcRtx##f); \

SVC_Call0(SVC_In1, SVC_Out1, SVC_CL1); \

return (t) __r0; \

}

 

#define SVC_Call0(in, out, cl) \

__ASM volatile ("svc 0" : out : in : cl)

 

#else // !(defined(__CC_ARM) || defined(__ICCARM__))

 

//lint -esym(522,__svc*) "Functions '__svc*' are impure (side-effects)"

 

#if ((defined(__ARM_ARCH_7M__) && (__ARM_ARCH_7M__ != 0)) || \

(defined(__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ != 0)) || \

(defined(__ARM_ARCH_8M_MAIN__) && (__ARM_ARCH_8M_MAIN__ != 0)) || \

(defined(__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ != 0)))

#define SVC_RegF "r12"

#elif ((defined(__ARM_ARCH_6M__) && (__ARM_ARCH_6M__ != 0)) || \

(defined(__ARM_ARCH_8M_BASE__) && (__ARM_ARCH_8M_BASE__ != 0)))

#define SVC_RegF "r7"

#endif

 

我们可以展开:

SVC0_1(Delay, osStatus_t, uint32_t) =>

__attribute__((always_inline)) __STATIC_INLINE osStatus_t __svcDelay(uint32_t a1)

{

register uint32_t __r0 __asm("r0") = (uint32_t)a1;

register uint32_t __rf __asm("r12") = (uint32_t)svcRtxDelay;

__asm volatile ("svc 0" : "=r"(__r0): "r"(__rf),"r"(__r0): "r1")

}

 

SVC0_1(DelayUntil, osStatus_t, uint32_t) =>

__attribute__((always_inline)) __STATIC_INLINE osStatus_t __svcDelayUntil(uint32_t a1)

{

register uint32_t __r0 __asm("r0") = (uint32_t)a1;

register uint32_t __rf __asm("r12") = (uint32_t)svcRtxDelayUntil;

__asm volatile ("svc 0" : "=r"(__r0): "r"(__rf),"r"(__r0): "r1")

}

 

10.6 Forcing inline assembly operands into specific registers

Sometimes specifying the exact register that is used for an operand is preferable to letting the compiler allocate a register automatically.

For example, the inline assembly block may contain a call to a function or system call that expects an argument or return value in a particular register.

To specify the register to use, the operand of the inline assembly statement must be a local register variable, which you declare as follows:

register int foo __asm("r2");

register float bar __asm("s4") = 3.141;

A local register variable is guaranteed to be held in the specified register in an inline assembly statement where it is used as an operand. Elsewhere it is treated as a normal variable, and can be stored in any register or in memory. Therefore a function can contain multiple local register variables that use the same register if only one local register variable is in any single inline assembly statement.

Example

// This function uses named register variables to make a Linux 'read' system call.

// The three arguments to the system call are held in r0-r2, and the system

// call number is placed in r7.

int syscall_read(register int fd, void *buf, unsigned count) {

register unsigned r0 __asm("r0") = fd;

register unsigned r1 __asm("r1") = buf;

register unsigned r2 __asm("r2") = count;

register unsigned r7 __asm("r7") = 0x900003;

__asm("svc #0"

: "+r" (r0)

: "r" (r1), "r" (r2), "r" (r7));

return r0;

}

 

而源代码里面有svcRtxDelay和svcRtxDelayUntil的函数实现

 

// ==== Service Calls ====

 

/// Wait for Timeout (Time Delay).

/// \note API identical to osDelay

static osStatus_t svcRtxDelay (uint32_t ticks) {

 

if (ticks != 0U) {

if (osRtxThreadWaitEnter(osRtxThreadWaitingDelay, ticks)) {

EvrRtxDelayStarted(ticks);

} else {

EvrRtxDelayCompleted(osRtxThreadGetRunning());

}

}

 

return osOK;

}

 

/// Wait until specified time.

/// \note API identical to osDelayUntil

static osStatus_t svcRtxDelayUntil (uint32_t ticks) {

 

ticks -= osRtxInfo.kernel.tick;

if ((ticks == 0U) || (ticks > 0x7FFFFFFFU)) {

EvrRtxDelayError((int32_t)osErrorParameter);

//lint -e{904} "Return statement before end of function" [MISRA Note 1]

return osErrorParameter;

}

 

if (osRtxThreadWaitEnter(osRtxThreadWaitingDelay, ticks)) {

EvrRtxDelayUntilStarted(ticks);

} else {

EvrRtxDelayCompleted(osRtxThreadGetRunning());

}

 

return osOK;

}

SVC 0 提升代码权限 通过SVC从thread mode升级到特权模式,也就是SVC_Handler的

BLX R12这句代码

CMSIS_RTOS_V2 rtx_delay.c 代码分析

看了一天,总算整明白RTX的套路了。

但是 

LDRB     R1,[R1,#-2]            ; Load SVC number  

这句话让我懵逼了好一会,结果代码调试进去才知道咋回事。。。

CMSIS_RTOS_V2 rtx_delay.c 代码分析

一图胜千言,直接看调试结果。

【R1】- 2 的内容是 DF 00

DF 00是什么?请看下面的图:

CMSIS_RTOS_V2 rtx_delay.c 代码分析

 

上面这个SVC指令的机器码就是DF, imm8=0 刚好也就是DF 00 也就是 svc 0.

而上面不正式调用 svc 0这个指令了么?

CMSIS_RTOS_V2 rtx_delay.c 代码分析

 

至此 上面的这张图的含义就很直观了。

 

还有一点要注意,RTX不能关闭中断,否则报错。刚开始搞糊涂了,看代码才知道不能关中断

 

CMSIS_RTOS_V2 rtx_delay.c 代码分析

 

发人生的第一篇blog, 洗洗睡了。