我正在为ARM Cortex-M3处理器开发自定义操作系统。要与我的内核交互,用户线程必须生成SuperVisor Call(SVC)指令(以前称为SWI,用于SoftWare中断)。 ARM ARM中此指令的定义是:
这意味着该指令需要立即参数,而不是寄存器值。
这让我很难以可读的方式构建我的界面。它需要如下代码:
asm volatile( "svc #0");
当我更喜欢像
这样的东西时svc(SVC_YIELD);
但是,我无法构造这个函数,因为SVC指令需要立即参数,当值通过寄存器传入时我无法提供。
对于后台,svc指令在内核中解码如下
#define SVC_YIELD 0
// Other SVC codes
// Called by the SVC interrupt handler (not shown)
void handleSVC(char code)
{
switch (code) {
case SVC_YIELD:
svc_yield();
break;
// Other cases follow
这个案例陈述正在迅速失控,但我认为没有办法解决这个问题。欢迎任何建议。
我最初考虑过
__attribute__((naked)) svc(char code)
{
asm volatile ("scv r0");
}
但是,当然,这不起作用,因为SVC需要寄存器参数。
解决问题的蛮力企图如下:
void svc(char code)
switch (code) {
case 0:
asm volatile("svc #0");
break;
case 1:
asm volatile("svc #1");
break;
/* 253 cases omitted */
case 255:
asm volatile("svc #255");
break;
}
}
但这有一个令人讨厌的代码味道。当然,这可以做得更好。
最后的尝试是在RAM中生成指令(其余代码从只读Flash运行)然后运行它:
void svc(char code)
{
asm volatile (
"orr r0, 0xDF00 \n\t" // Bitwise-OR the code with the SVC encoding
"push {r1, r0} \n\t" // Store the instruction to RAM (on the stack)
"mov r0, sp \n\t" // Copy the stack pointer to an ordinary register
"add r0, #1 \n\t" // Add 1 to the address to specify THUMB mode
"bx r0 \n\t" // Branch to newly created instruction
"pop {r1, r0} \n\t" // Restore the stack
"bx lr \n\t" // Return to caller
);
}
但这也感觉不对劲。此外,它不起作用 - 这里有一些我做错了;也许我的指令没有正确对齐,或者我没有设置处理器以允许在此位置从RAM运行代码。
我必须研究最后一个选项。但是,我觉得我应该能做类似的事情:
__attribute__((naked)) svc(char code)
{
asm volatile ("scv %1"
: /* No outputs */
: "i" (code) // Imaginary directive specifying an immediate argument
// as opposed to conventional "r"
);
}
但是我没有在文档中找到任何这样的选项,我无法解释如何实现这样的功能,所以它可能不存在。我该怎么做?
答案 0 :(得分:20)
您希望使用约束来强制将操作数分配为8位立即数。对于ARM,这是约束I
。所以你想要
#define SVC(code) asm volatile ("svc %0" : : "I" (code) )
有关所有constaints的摘要,请参阅GCC文档 - 您需要查看特定于处理器的注释以查看特定平台的约束。在某些情况下,您可能需要查看gcc源代码中架构的.md
(机器描述)文件以获取完整信息。
还有一些优秀的ARM特定gcc文档here。在标题"输入和输出操作数"下面几页。它提供了所有ARM约束的表格
答案 1 :(得分:2)
使用宏怎么样:
#define SVC(i) asm volatile("svc #"#i)
答案 2 :(得分:1)
正如克里斯·多德(Chris Dodd)在关于宏观的评论中指出的那样,它并不是很有效,但确实如此:
#define STRINGIFY0(v) #v
#define STRINGIFY(v) STRINGIFY0(v)
#define SVC(i) asm volatile("svc #" STRINGIFY(i))
但请注意,如果您将枚举值传递给它,只有#defined值,它将无法工作。
因此,克里斯'上面的答案是最好的,因为它使用立即值,这至少是拇指指示所需要的。
答案 3 :(得分:1)
我的解决方案(“即时生成指令编码”):
#define INSTR_CODE_SVC (0xDF00)
#define INSTR_CODE_BX_LR (0x4770)
void svc_call(uint32_t svc_num)
{
uint16_t instrs[2];
instrs[0] = (uint16_t)(INSTR_CODE_SVC | svc_num);
instrs[1] = (uint16_t)(INSTR_CODE_BX_LR);
// PC = instrs (or 1 -> thumb mode)
((void(*)(void))((uint32_t)instrs | 1))();
}
它的工作原理和它的优于switch-case变体,它可以为256 svc提供~2kb的ROM。这个func不必放在RAM部分,FLASH就可以了。 如果svc_num应该是运行时变量,则可以使用它。
答案 4 :(得分:0)
As discussed in this question, the operand of SVC
is fixed, that is it should be known to the preprocessor, and it is different from immediate Data-processing operands.
The gcc manual reads
'I'- Integer that is valid as an immediate operand in a data processing instruction. That is, an integer in the range 0 to 255 rotated by a multiple of 2.
Therefore the answers here that use a macro are preferred, and the answer of Chris Dodd is not guaranteed to work, depending on the gcc version and optimization level. See the discussion of the other question.