FPU使能
心血来潮想优化一下UART初始化部分,之前是固定的115200,改成根据传入的参数值来设定波特率;
// 设置UART0时钟,一般设置为10MHz
UARTCLKENB |= (1 << 2);
UARTCLKGEN0L &= ~((3 << 2) | (0xFF << 5));
p = (PLLSETREG0 >> 18) & 0x3F;
m = (PLLSETREG0 >> 8) & 0x3FF;
s = (PLLSETREG0 >> 0) & 0xFF;
tmp = (m * 24) / (p * my_pow(2, s)); // PLL0的时钟大小
UARTCLKGEN0L |= ((tmp / 10 - 1) << 5); // 生成10MHz时钟
·······
// 配置波特率 baud
UARTIBRD &= ~0xFFFF;
tmpf = (10000000 / (16.0 * baud));
UARTIBRD |= (int)tmpf; // 整数部分
UARTFBRD &= ~0x3F;
tmpf = tmpf - (int)tmpf; // 小数部分
UARTFBRD |= (int)(tmpf * 2 * 32 + 0.5);
编译下载,上电运行,莫名产生了未定义指令异常。查看反汇编发现,是vldr, vmov ...
等浮点运算相关的指令产生的异常。但是A9不可能不支持浮点。
解决办法1,生成软浮点指令
arm-linux-gcc 默认是编译硬浮点指令,-v
可以查看相关属性,--with-fpu=vfpv3 --with-float=hard
;
既然芯片不认硬浮点指令,可以使用-mfloat-abi=soft
生成软浮点指令;
具体运算时调用gcc的软浮点库;
解决办法2,开启FPU
芯片的FPU默认是没有开启的,使能FPU后才能执行硬浮点指令;
使能操作:
enableFPU:
MRC p15, 0, r0, c1, c1, 2
ORR r0, r0, #3<<10
MCR p15, 0, r0, c1, c1, 2
LDR r0, =(0xF << 20)
MCR p15, 0, r0, c1, c0, 2
MOV r3, #0x40000000
VMSR FPEXC, r3
SWI异常程序模型
SWI,即software interrupt软件中断。该指令产生一个SWI异常。意思就是处理器模式改变为超级用户模式,CPSR寄存器保存到超级用户模式下的SPSR寄存器,并且跳转到SWI向量。其ARM指令格式如下:
SWI{cond} immed_24
Cond域:是可选的条件码 (参见 ARM汇编指令条件执行详解).
immed_24域:范围从 0 到 224 -1 的表达式, (即0-16777215)。用户程序可以使用该常数来进入不同的处理流程。
---------------------本引用来自 panqihe 的CSDN 博客 ,全文地址请点击:SWI指令—软件中断实例详解(原创)
产生SWI异常
swi 0x123 // 软中断
异常中断处理程序
void except_swi(){
unsigned int swi_addr, val = 0;
__asm__(
"stmdb sp!, {r0-r12, lr}\n"
"mov %0, lr\n" // 异常指令的下一条指令地址
"ldr r0, [lr, #-4]\n"
"bic r0, r0, #0xFF000000\n"
"mov %1, r0\n" // 取出24bit中断号
:"=r"(swi_addr), "=r"(val)
); // 保存现场
swi_addr -= 4; // 异常指令地址
print_cpsr(); // 打印cpsr
printf("[%x] Triggered SWI exceptions 0x%x\r\n", swi_addr, val);
asm("ldmia sp!, {r0-r12, pc}^\n"); // 恢复现场,'^'表示恢复CPSR;
}
内联汇编可以参考:Linux C语言内联汇编-读写变量
用户模式下,不能通过修改CPSR来切换模式,而特权模式下可以。通过SWI异常从用户模式切换到特权模式;
对异常向量表的优化
在S5P4418裸机开发(十):Undef异常处理中,为了让CPU跳到我们自定义的异常处理函数中,修改了SD卡上2nboot
部分的数据,可能每次编译都要修改;
板子上电后,2nboot
部分会复制到SRAM中再运行,修改SRAM中的数据更方便;
b 跳转指令
寻址范围是 PC ± 32Mldr 装载指令
寻址范围是 PC ± 4k- 将一个32位的值放入PC,需要文字池,占用两条指令的空间,所以在
2nboot
的末尾放跳向异常处理函数的指令,开头部分修改成跳向末尾部分的指令,两级跳转;
#define StartAddr 0xFFFF0200
#define UndefV (*(volatile u32 *)(StartAddr + 0x04))
#define SWIV (*(volatile u32 *)(StartAddr + 0x08))
#define gotoStartAddr (0xFFFF0200 + 0x7BC0) // 在2nboot的末尾修改指令
#define ResetVGoto (*(volatile u32 *)(gotoStartAddr + 0x00))
#define UndefVGoto (*(volatile u32 *)(gotoStartAddr + 0x04))
#define valStartAddr (0xFFFF0200 + 0x7BE0) // 存放要跳转的值
#define ResetVval (*(volatile u32 *)(valStartAddr + 0x00))
#define UndefVval (*(volatile u32 *)(valStartAddr + 0x04))
void except_vector_init(){
//两级跳转
u32 b_code = 0xEA000000 + ((0x7BC0 - 0x08) >> 2); // b 跳转
u32 ldr_code = 0xE59FF018; // ldr pc, [pc, #0x18]
// Undef Handler
UndefV = b_code;
UndefVGoto = ldr_code;
UndefVval = (u32)&except_und;
// SWI Handler
SWIV = b_code;
SWIVGoto = ldr_code;
SWIVval = (u32)&except_swi;
}