|
在RTX系统中,使用SVC函数有以下几点好处:1、由于函数是通过SVC软中断进行调用的,函数相当于自带了互斥锁,无需其他的加锁措施。2、函数通过中断调用,可以优先执行。
当然也有一些缺点:1、由于是通过中断调用,不适合调用等待性质的函数。2、SVC调用的函数中能调用的操作系统函数有限,只能调用可以在中断中使用的系统函数。
下面讲解一下如何自定义SVC函数。
首先讲一下SVC调用的原理,SVC指令调用后,处理器大致产生进行了以下几个操作:
1、保护现场,将寄存器值存入堆栈中
2、将SVC指令后面的立即数加载到R1寄存器中
3、产生软中断,进入到SVC_Handler中断服务函数中
4、将当前R1寄存器和0进行比较,若相等就会跳转到RTX系统的SVC中断处理,若不等则跳入SVC_User的处理函数,因此自定义的SVC函数标号务必要大于0,
5、在SVC_User中,把全局数组osRtxUserSVC的首地址加载到寄存器里,比较这个数组的第一个元素和SVC标号,如果SVC标号大于这个元素值,则判定为调用错误直接退出,因此osRtxUserSVC这个数组至少要有一个成员,且第一个成员
存储的就是我们定义的SVC函数个数。
6、随后就是将堆栈中的r0-r3的值恢复出来,并调用osRtxUserSVC[(SVC标号)]的函数指针,并将r0作为返回值。
RTX5中具体的汇编代码如下:
知道了具体的调用原理后,自定义SVC函数就容易多了。
首先定义一个全局类型的指针数组 void * const osRtxUserSVC[];
- extern void * const osRtxUserSVC[];
- #define USE_SVC_COUNT 0
- void * const osRtxUserSVC[USE_SVC_COUNT+1] = {
- (void *)USE_SVC_COUNT,
- };
复制代码 在这个数组的第一个成员填入自定义的svc函数个数,后面的成员直接填入函数指针,示例如下:
static Mem_Root sys_heap_root;
static void sys_heap_init(void){
Mem_Manage_Init(&sys_heap_root,xHeapRegions);
}
static void* svc_sys_malloc(size_t want_size){
return Mem_Manage_Malloc(&sys_heap_root,want_size);
}
static void* svc_sys_realloc(void* src_addr,size_t want_size){
return Mem_Manage_Realloc(&sys_heap_root,src_addr,want_size);
}
static void* svc_sys_aligend_alloc(size_t align_byte,size_t want_size){
return Mem_Manage_Aligned_Alloc(&sys_heap_root,align_byte,want_size);
}
static void svc_sys_free(void* addr){
Mem_Manage_Free(&sys_heap_root,addr);
}
static size_t svc_sys_get_heap_size(void){
return sys_heap_root.total_size;
}
static size_t svc_sys_get_remain_size(void){
return sys_heap_root.remain_size;
}
extern void * const osRtxUserSVC[];
#define USE_SVC_COUNT 6
void * const osRtxUserSVC[USE_SVC_COUNT+1] = {
(void *)USE_SVC_COUNT,
(void *)svc_sys_malloc,
(void *)svc_sys_realloc,
(void *)svc_sys_aligend_alloc,
(void *)svc_sys_free,
(void *)svc_sys_get_heap_size,
(void *)svc_sys_get_remain_size
};
这样我们就可以通过SVC指令调用svc函数啦,为了方便使用,我们要把SVC指令包上一层函数的封皮AC5
- <blockquote>void* sys_malloc(size_t want_size)__svc(1);
复制代码 后面的__svc(1)后缀相当于告诉编译器,这个函数声明就是个小马甲,实际上是调用的是svc的1号指令,也就是svc_sys_malloc
AC6
在AC6中就没有这么人性化了,需要了解一点嵌入式汇编的知识,这里只讲用到的一点点知识,详细的GNU C 嵌入汇编知识可以看这个网址:http://www.ethernut.de/en/documents/arm-inline-asm.html
在GNU C 的标准中,一个嵌入汇编的语句有着如下的格式
- __asm(代码:输出操作数列表:输入操作数列表:破坏寄存器列表);
复制代码 这里除了代码部分以外,其余都是伪代码,用于告诉编译器哪里会改变,如输入输出操作数列表是用于告诉编译器哪个参数会在指令前后发生改变,与c语言里的变量交互也是在输入输出列表中,破坏寄存器列表用于告诉编译器其他可能发生改变的地方,如内存,标志位等,此处主要填写不在输入输出列表中,却会被显式或隐式改变的量,如内存、标志位等。
语法允许省略掉任意元素,限制是至少要保留代码或破坏寄存器列表,要不然这个代码就是无意义的了。也就是说 诸如__asm("mov r0,r0");__asm(:::"memory");这样的代码都是合法的,不过为了编程清晰,也可以写成__asm("mov r0,r0"::;
了解一点arm汇编语法后我们就可以编写svc函数了。
arm中C语言函数参数传递遵循的是ATPCS原则,ATPCS中规定了函数参数使用r0-r3寄存器传递,返回值使用r0进行传递,这也限制了我们在定义svc函数时,函数参数不要超过四个(AC5也一样,汇编代码中只加载了r0-r3的寄存器),超过四个可以用结构体指针传递。普通的函数没有参数限制,多余的参数都会自动加载的堆栈中,编译器会进行合适的处理。
下面给出AC6情况下svc函数定义范例
- __attribute__((always_inline)) static inline void* sys_malloc(size_t want_size){
- register size_t __r0 __asm("r0") = (size_t)want_size;
- __asm volatile ("svc 1":"=r"(__r0):"r"(__r0):"cc","memory");
- return (void*)__r0;
- }
复制代码 __attribute__((always_inline)) static inline的作用是声明函数是静态内联的,方便我们在头文件中定义。register声明变量为寄存器变量,__asm("r0") 指定变量的寄存器。volatile用于防止指令被优化掉,"svc 1"定义了我们要执行的指令为1号,输出参数表中的"=r"指定了输出为寄存器后面的(__r0)则指定了具体的C语言变量;输入参数列表中"r"(__r0)指定输入的是寄存器类型的C语言变量__r0,破坏寄存器列表中"cc"告诉编译器,条件寄存器可能会被更改,"memory"告诉编译器内存可能会更改。最后返回__r0完成调用。
多参数的情况
多参数时参数按r0-r3的顺序往下排,不要超过4个,举个栗子:
- __attribute__((always_inline)) static inline void* sys_malloc(size_t want_size){
- register size_t __r0 __asm("r0") = (size_t)want_size;
- __asm volatile ("svc 1":"=r"(__r0):"r"(__r0):"cc","memory");
- return (void*)__r0;
- }
- __attribute__((always_inline)) static inline void*sys_realloc(void* src_addr,size_t want_size){
- register size_t __r0 __asm("r0") = (size_t)src_addr;
- register size_t __r1 __asm("r1") = (size_t)want_size;
- __asm volatile ("svc 2":"=r"(__r0):"r"(__r0),"r"(__r1):"cc","memory");
- return (void*)__r0;
- }
- __attribute__((always_inline)) static inline void* sys_aligend_alloc(size_t align_byte,size_t want_size){
- register size_t __r0 __asm("r0") = (size_t)align_byte;
- register size_t __r1 __asm("r1") = (size_t)want_size;
- __asm volatile ("svc 3":"=r"(__r0):"r"(__r0),"r"(__r1):"cc","memory");
- return (void*)__r0;
- }
- __attribute__((always_inline)) static inline void sys_free(void* addr){
- register size_t __r0 __asm("r0") = (size_t)addr;
- __asm volatile ("svc 4"::"r"(__r0):"cc","memory");
- }
- __attribute__((always_inline)) static inline size_t sys_get_heap_size(void){
- register size_t __r0 __asm("r0");
- __asm volatile ("svc 5":"=r"(__r0)::"cc","memory");
- return (size_t)__r0;
- }
- __attribute__((always_inline)) static inline size_t sys_get_remain_size(void){
- register size_t __r0 __asm("r0");
- __asm volatile ("svc 6":"=r"(__r0)::"cc","memory");
- return (size_t)__r0;
- }
复制代码 至此,我们就可以愉快的使用自定义的svc函数啦
|
评分
-
查看全部评分
|