SVC中断方式调用用户函数
本章节为大家讲解如何采用SVC中断方式调用用户函数。当用户将RTX任务设置为工作在非特权级模式时,任务中是不允许访问特权级寄存器的,这个时候使用SVC中断,此问题就迎刃而解了。 本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407。 19.1 SVC中断 19.2 RTX中SVC中断方式调用函数方法 19.3 实验例程说明 19.4 总结
19.1 SVC中断
19.1.1 SVC功能介绍 SVC用于产生系统函数的调用请求。例如,操作系统通常不让用户程序直接访问硬件,而是通过提供一些系统服务函数,让用户程序使用SVC发出对系统服务函数的呼叫请求,以这种方法调用它们来间接访问硬件。因此,当用户程序想要控制特定的硬件时,它就要产生一个SVC异常,然后操作系统提供的SVC异常服务例程得到执行,它再调用相关的操作系统函数,后者完成用户程序请求的服务。 SVC这种“提出要求——得到满足”的方式很好: (1)它使用户程序从控制硬件的繁文缛节中解脱出来,而是由OS负责控制具体的硬件。 (2)OS的代码可以经过充分的测试,从而能使系统更加健壮和可靠。 (3)它使用户程序无需在特权级下执行,用户程序无需承担因误操作而瘫痪整个系统的风险。 (4)通过SVC的机制,还让用户程序变得与硬件无关,因此在开发应用程序时无需了解硬件的操作细节,从而简化了开发的难度和繁琐度,并且使应用程序跨硬件平台移植成为可能。开发应用程序唯一需要知道的就是操作系统提供的应用编程接口(API),并且在了解了各个请求代号和参数表后,就可以使用SVC来提出要求了(事实上,为使用方便,操作系统往往会提供一层封皮,以使系统调用的形式看起来和普通的函数调用一致。各封皮函数会正确使用SVC指令来执行系统调用)。其实,严格地讲,操作硬件的工作是由设备驱动程序完成的,只是对应用程序来说,它们也相当于操作系统的一部分。如下图所示: SVC异常通过执行”SVC”指令来产生。该指令需要一个立即数,充当系统调用代号。SVC异常服务例程稍后会提取出此代号,从而获知本次调用的具体要求,再调用相应的服务函数。例如, SVC 0x3 ; 调用3号系统服务 在SVC服务例程执行后,上次执行的SVC指令地址可以根据自动入栈的返回地址计算出。找到了SVC指令后,就可以读取该SVC指令的机器码,从机器码中萃取出立即数,就获知了请求执行的功能代号。如果用户程序使用的是PSP,服务例程还需要先执行MRS Rn, PSP指令来获取应用程序的堆栈指针。通过分析LR的值,可以获知在SVC指令执行时,正在使用哪个堆栈。 注意,我们不能在SVC服务例程中嵌套使用SVC指令(事实上这样做也没意义),因为同优先级的异常不能抢占自身。这种作**产生一个用法fault。同理,在NMI服务例程中也不得使用SVC,否则将触发硬fault。
19.1.2 SVC触发方式 SVC的异常号是11,支持可编程。SVC异常可以由SVC指令来触发,也可以通过NVIC来软件触发(通过寄存器NVIC->STIR触发软中断)。这两种方式触发SVC中断有一点不同:软件触发中断是不精确的,也就是说,抢占行为不一定会立即发生,即使当时它没有被掩蔽,也没有被其它ISR阻塞,也不能保证马上响应。这也是写缓冲造成的,会影响到与操作NVIC STIR相临的后一条指令:如果它需要根据中断服务的结果来决定如何工作(如条件跳转),则该指令可能会误动作——这也可以算是紊乱危象的一种表现形式。为解决这个问题,必须使用一条DSB指令,如下例所示: MOV R0,#SOFTWARE_INTERRUPT_NUMBER LDRR1,=0xE000EF00 ; 加载NVIC软件触发中断寄存器的地址 STR R0, [R1] ; 触发软件中断 DSB ; 执行数据同步隔离指令 ... 但是这种方式还有另一种隐患:如果欲触发的软件中断被除能了,或者执行软件中断的程序自己也是个异常服务程序,软件中断就有可能无法响应。因此,必须在使用前检查这个中断已经在响应中了。为达到此目的,可以让软件中断服务程序在入口处设置一个标志。而SVC要精确很多,SVC指令后,只要此时没有其它高优先级的异常也发生了,SVC中断服务程序可以得到立即执行。
19.1.3 SVC的使用 SVC是用于呼叫OS所提供的API(RTX是采用的这种方式)。用户程序只需知道传递给OS的参数,而不必知道各API函数的地址。SVC指令带一个8位的立即数,可以视为是它的参数,被封装在指令本身中,如: SVC 3 ;呼叫3号系统服务 则3被封装在这个SVC指令中。因此在SVC服务例程中,需要读取本次触发SVC异常的SVC指令,并提取出8位立即数所在的位段,来判断系统调用号。
19.1.4 RTX使用的SVC中断服务号 SVC的0号系统服务被RTX系统占用,即SVC 0,用户只能使用从1开始的服务号。而且用户使用的时候一定要保证从1开始,连续递增使用,范围1 – 255。
|