A7处理器异常swi和M3处理器异常svc
1. SWI
在需要软件中断处调用
__SWI 0xNum ;Num为SWI中断处理模块的编 ,见表SwiFunction;软件中断
进入软件中断向量表位置进行执行,其中软件中断向量表一般会直接保存在r0的低3直接用来存储系统调用编 ,后面通过汇编向量表的处理方式进行编 细节的入口处理,跳转对应的处理流程当中去。
SWI_Exception_Function
CMP R0, #12 ;R0中的SWI编 是否大于最大值
/* 下面这句语句把 (LDRLO这条指令的地址+8+ R0*4) 的地址装载到PC寄存器,举例如果上面的 Num=”1″,也就是R0 =
1, 假设LDRLO这条指令的地址是0x00008000,那么根据ARM体系的3级流水线 PC寄存器里指向是下两条指令 于是PC =
0x00008008 也就是伪指令DCD TASK_SW 声明的标 TASK_SW 的地址,注意DCD TASK_SW 这条指令本身不是ARM能执行的指令,也不会占有地址,这条指令靠汇编器汇编成可执行代码,它的意义就是声明
TASK_SW的地址, , [PC, R0, LSL #2] 这个寻址方式就是 PC + R0的值左移2位的值( 0x01
=> 0x04 ),这样PC的值就是0x0000800C, 即ENTER_CRITICAL的地址于是ARM执行该标 下的任务 */
2. svc
一个特殊的中断:SVCall
简述:一种由程序进行触发的中断,默认开启
起源:SVC(系统服务调用,亦简称系统调用)多用于在操作系统之上的软件开发中。SVC 用于产生系统函数的调用请求。例如,操作系统不让用户程序直接访问硬件,而是通过提供一些系统服务函数,用户程序使用 SVC 发出对系统服务函数的呼叫请求,以这种方法调用它们来间接访问硬件。因此,当用户程序想要控制特定的硬件时,它就会产生一个 SVC 异常,然后操作系统提供的 SVC 异常服务例程得到执行,它再调用相关的操作系统函数,后者完成用户程序请求的服务。
用途:可以通过设置,使得一段代码能够被某些中断打断,而不能被另外一些中断打断,比如可用于确保模拟IIC的时序不被打断而造成通信失败
注意:
- SVC 异常是必须立即得到响应的(若因优先级不比当前正处理的高,或是其它原因使之无法立即 响应,将引发HardFault)
- 主从优先级等等概念和普通中断相同(且地位相同,即该特殊中断其实也不特殊)//祝:默认情况下,除HardFault和NMI,其它中断的优先级均为0,0(附加提醒,group设置需先于priority设置),!!BUT!!,中断优先级的设置需要这么调用:NVIC_SetPriority(SVCall_IRQn,NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 1, 1));
在C中使用SVCall
SVC服务函数使用堆栈进行参数传递,故C语言版的SVC服务函数需要一个汇编操作,用于把堆栈中的参数提取到寄存器中
如何触发中断/strong>
step1. 声明函数(__svc会自动生成对应函数)//__svc时keil里的一个宏
step2. 调用函数
Re:STM32 SVCall
flyangchina 2016-08-19 17:25
不错,在svc_handler中应该有调用相应函数的代码。
可以在前面加一个SVC_Table数组来保存函数指针,比如这样:
然后再这样写svc_handler中这样写:
再补充一下__SVC_0的定义,在随意的某个文件中,__SVC_1不管了啊:
使用如下:
这样就有了一个整体的使用svc实现系统调用的简单架构了
Re:STM32 SVCall
Serval 2015-02-10 16:10
关于“svc_number = ((char *) pwdSF[6])[-2]; // 没想到吧,C的数组能用得这么绝! ”的用法,看了半天,贴出来大家看理解的对不对:
首先这行代码的最终目的是取得svc #num中的Num。在Thumb代码(STM32应该多数是Cortex的吧)中,一条指令通常是16位,svc #num的指令通常翻译为0xDFxx –> 其中xx就是#num。所以,Keil手册有如下解释:
0 to 224–1 (a 24-bit value) in an ARM instruction.
0-255 (an 8-bit value) in a Thumb instruction.
代码可以翻译为好懂一点的:
*((char *)pwdSF[6] – 2)
举例:
假设系统配置为小端,低位放低字节。
指令为:svc #1 —> 二进制为0xDF01,所在地址为0x286。
那么实际上,堆栈里压入的PC,存放的是0x288。因为PC压入的是下一条指令。
0x286: DF01 ;svc #1
0x288: ……
Stack: 88 02 00 00
那行奇特的代码就是为了得到这个01。分几步:
1. 转换为(char*)pwdSF[6],并减2
这一步是为了让指针可以精确指到88 02的位置。
(char*)pwdSF[6] – 2
2. 指针取值
*((char *)pwdSF[6] – 2)
3. 指针访问改为下表访问形式
指针取值可以写为下标形式,例如:*(p + i)等同于p[i],个人建议指针使用时都按第二种写法
((char *) pwdSF[6])[-2]
SVC(系统服务调用,亦简称系统调用)和PendSV(可悬起系统调用),它们多用在上了操作系统的软件开发中。SVC用于产生系统函数的调用请求。例如,操作系统通常不让用户程序直接访问硬件,而是通过提供一些系统服务函数,让用户程序使用SVC发出对系统服务函数的呼叫请求,以这种方法调用它们来间接访问硬件。因此,当用户程序想要控制特定的硬件时,它就要产生一个SVC异常,然后操作系统提供的SVC异常服务例程得到执行,它再调用相关的操作系统函数,后者完成用户程序请求的服务。
下面的代码是利用SVC异常来请求系统服务的例子,这个例子仅适用于MDK ARM Compiler 5,在ARM Compiler 6里取消了__svc()这个关键字,需要用户自己实现
__svc(4) uint32_t svc_service_add(uint32_t x, uint32_t y); //__svc是编译器关键字,编译器会插入svc指令,触发svc异常
__svc(5) uint32_t svc_service_sub(uint32_t x, uint32_t y);
void SVC_Handler_C(uint32_t* svc_args) //这里的svc_args是栈指针SP的值
{
uint32_t svc_number;
svc_number = ((uint8_t*)svc_args[6])[-2]; //svc_args[6]是栈里保存的PC值,倒数2字节,得到svc编
switch(svc_number) //根据svc编 执行需要的功能
{
case 4:
svc_args[0] = svc_args[0] + svc_args[1];
break;
case 5:
svc_args[0] = svc_args[0] – svc_args[1];
break;
default:
break;
}
return;
}
__asm void SVC_Handler(void)
{
IMPORT SVC_Handler_C //导入外部函数
TST LR, #4 //检查LR的bit2,这里LR的值是异常返回值EXC_RETURN
ITE EQ
MRSEQ R0, MSP //如果bit2是0,说明进异常前使用的是MSP,把MSP值赋给R0
MRSNE R0, PSP //否则说明进异常前使用的是PSP,把PSP值赋给R0
B SVC_Handler_C //跳转到函数SVC_Handler_C()
}
uint32_t x = 13;
uint32_t y = 6;
uint32_t z1, z2;
int main(void)
{
z1 = svc_service_add(x, y);
z2 = svc_service_sub(x, y);
printf(“x + y = %d rn”, z1);
printf(“x – y = %d rn”, z2);
while(1);
}
下面这个是使用ARM Compiler 6的例子,本人水平有限,不会写带有返回值的C嵌汇编函数,只好用一个指针参数来传递结果。注意ARM编译器6的C嵌汇编语法格式与编译器5不同
__attribute__((always_inline)) void svc_service_add(uint32_t x, uint32_t y, uint32_t* res)
{
register unsigned r0 __asm(“r0”) = x;
register unsigned r1 __asm(“r1”) = y;
register unsigned r2 __asm(“r2”) = (uint32_t)res;
__asm volatile(“SVC #4” :: “r” (r0), “r” (r1), “r” (r2));
__asm volatile(“STR R0, [R2]”);
}
__attribute__((always_inline)) void svc_service_sub(uint32_t x, uint32_t y, uint32_t* res)
{
register unsigned r0 __asm(“r0”) = x;
register unsigned r1 __asm(“r1”) = y;
register unsigned r2 __asm(“r2”) = (uint32_t)res;
__asm volatile(“SVC #5” :: “r” (r0), “r” (r1), “r” (r2));
__asm volatile(“STR R0, [R2]”);
}
void SVC_Handler_C(uint32_t* svc_args)
{
uint32_t svc_number;
svc_number = ((uint8_t*)svc_args[6])[-2];
switch(svc_number)
{
case 4:
svc_args[0] = svc_args[0] + svc_args[1];
break;
case 5:
svc_args[0] = svc_args[0] – svc_args[1];
break;
default:
break;
}
return;
}
void SVC_Handler(void)
{
__asm volatile
(
“TST LR, #4 n”
“ITE EQ n”
“MRSEQ R0, MSP n”
“MRSNE R0, PSP n”
“B SVC_Handler_C n”
);
}
uint32_t x = 13;
uint32_t y = 6;
uint32_t z1, z2;
int main(void)
{
svc_service_add(x, y, &z1);
svc_service_sub(x, y, &z2);
printf(“x + y = %d rn”, z1);
printf(“x – y = %d rn”, z2);
while(1);
}
这里有一篇文章,是Keil官方关于ARM Compiler 6如何实现svc函数的:
https://www.keil.com/support/docs/4022.htm
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!