本源文件就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这句代码
看了一天,总算整明白RTX的套路了。
但是
LDRB R1,[R1,#-2] ; Load SVC number
这句话让我懵逼了好一会,结果代码调试进去才知道咋回事。。。
一图胜千言,直接看调试结果。
【R1】- 2 的内容是 DF 00
DF 00是什么?请看下面的图:
上面这个SVC指令的机器码就是DF, imm8=0 刚好也就是DF 00 也就是 svc 0.
而上面不正式调用 svc 0这个指令了么?
至此 上面的这张图的含义就很直观了。
还有一点要注意,RTX不能关闭中断,否则报错。刚开始搞糊涂了,看代码才知道不能关中断
发人生的第一篇blog, 洗洗睡了。