是否可以使这个混合的c ++ / asm函数符合标准? 函数ePendSV()必须具有以下布局:
ePendSV: //function entry point
mrs r0,PSP
stmdb r0!,{r4-r11,lr}
// compiler can generate any code here doing these things:
readyTcbQueue.pTcb.runTcb->psp = r0;
readyTcbQueue.pTcb.runTcb=readyTcbQueue.pTcb.readyTcb;
r0 = readyTcbQueue.pTcb.readyTcb->psp;
// work with r0 in assembly
ldmia r0!,{r4-r11,lr}
msr PSP,r0
bx lr
readyTcbQueue.pTcb 是一个简单的struct对象,只有两个指向 BragOsTcb 对象的指针;
struct{
BragOsTcb *runTcb;
BragOsTcb *readyTcb;
};
BragOsTcb 是非POD类,但没有虚函数和虚拟继承,如下所示:
class BragOsTcb : public TcbCdllq, public TimerTcbCdllq, public BragOsObject{
public:
BragOsTcb();
....
private:
.....
public:
unsigned long psp;
....
};
TcbCdllq,TimerTcbCdllq,BragOsObject 也是具有类似布局且没有虚拟的简单类。但它们也是非POD。
我已经使这个代码适用于gcc和clang,但这是一个非标准的黑客攻击,可能无法正常工作。
__attribute__((naked)) void ePendSV(){
asm volatile("\
mrs r0,PSP \n\
stmdb r0!,{r4-r11,lr} \n\
\n\
ldr r1,=%0 \n\
ldmia r1,{r2,r3} // r2=runTcb, r3=readyTcb \n\
str r0,[r2,%1] // save psp \n\
str r3,[r1,#0] // runTcb=readyTcb \n\
ldr r0,[r3,%1] // readyTcb->psp \n\
\n\
// restore LR(EXC_RETURN),R11-R4 from new PSP, set new PSP, return \n\
ldmia r0!,{r4-r11,lr} \n\
msr PSP,r0 \n\
bx lr \n\
" :
: "i"(&readyTcbQueue.pTcb),"i"(&(((BragOsTcb*)0)->psp))
: );
// it'is an offsetof hack which might not work
}
谢谢!
答案 0 :(得分:0)
所以你想要的是从一个没有参数的裸函数访问BragOsTcb的psp成员,还有更多它作为硬件中断处理程序调用,所以你不能添加任何其他代码来加载地址您。 BragOsTcb是非POD,因此您担心从类的开头偏移的psp成员对于不同的编译器会有所不同。
我建议采用以下方案:
struct BragOsTcbWrapper
{
BragOsTcb* this_;
unsigned long psp;
};
struct{
BragOsTcbWrapper *runTcb;
BragOsTcbWrapper *readyTcb;
};
class BragOsTcb : public TcbCdllq, public TimerTcbCdllq, public BragOsObject{
public:
BragOsTcb()
: pspHolder({this,0})
{
}
....
private:
.....
public:
BragOsTcbWrapper pspHolder;
....
};
现在你可以这样做:
__attribute__((naked)) void ePendSV(){
asm volatile("\
mrs r0,PSP \n\
stmdb r0!,{r4-r11,lr} \n\
\n\
ldr r1,=%0 \n\
ldmia r1,{r2,r3} // r2=runTcb, r3=readyTcb \n\
str r0,[r2,%1] // save psp \n\
str r3,[r1,#0] // runTcb=readyTcb \n\
ldr r0,[r3,%1] // readyTcb->psp \n\
\n\
// restore LR(EXC_RETURN),R11-R4 from new PSP, set new PSP, return \n\
ldmia r0!,{r4-r11,lr} \n\
msr PSP,r0 \n\
bx lr \n\
" :
: "i"(&readyTcbQueue.pTcb),"i"(&(((BragOsTcbWrapper*)0)->psp))
: );
}
我认为这对你有用
答案 1 :(得分:0)
由于gcc / clang
不支持裸函数中的扩展asm裸 此属性允许编译器构造必需的函数声明,同时允许函数体 汇编代码。指定的函数不会有序言/尾声 编译器生成的序列。只有基本的asm语句才能 安全地包含在裸功能中(参见Basic Asm)。使用时 扩展的asm或基本的asm和C代码的混合似乎可行, 它们不能依赖于可靠地工作而且不受支持。 https://gcc.gnu.org/onlinedocs/gcc/ARM-Function-Attributes.html#ARM-Function-Attributes
我们可以使用普通的C ++函数并从裸
跳转/调用它typedef unsigned long U32;
__attribute__((naked)) void ePendSV(){
asm volatile("\
mrs r0, PSP \n\
stmdb r0!, {r4-r11,lr} \n\
b realSwitchContext"); // this solution costs single jump instruction named **b**. It's valid for ARM-assembly in this case.
// Stack pointer has been saved in r0 register. we dont care about stack for now.
}
extern "C" void realSwitchContext(U32 psp){
readyTcbQueue.pTcb.runTcb->psp = psp;
BragOsTcb *tcb = readyTcbQueue.pTcb.readyTcb;
readyTcbQueue.pTcb.runTcb = tcb;
psp = tcb->psp;
asm volatile("\
ldmia %0!, {r4-r11,lr} \n\
msr PSP, %0 \n\
bx lr" : : "r"(psp)); // we just changed stack pointer then can return here and don't care about C++ stacked data
}
上面的实现中存在一些错误,因为有两个堆栈指针--MSP和PSP。如果C ++将使用堆栈,则MSP将被最后bx lr
指令破坏。下面的解决方案是正确的,但需要2'昂贵'的指令 - bl(通话)和bx lr(返回)
__attribute__((naked)) void ePendSV(){
asm volatile("\
mrs r0, PSP \n\
stmdb r0!, {r4-r11,lr} \n\
bl realSwitchContext \n\
ldmia r0!, {r4-r11,lr} \n\
msr PSP, r0 \n\
bx lr");
}
extern "C" U32 realSwitchContext(U32 psp){
readyTcbQueue.pTcb.runTcb->psp = psp;
BragOsTcb *tcb = readyTcbQueue.pTcb.readyTcb;
readyTcbQueue.pTcb.runTcb = tcb;
return tcb->psp;
}