S5P4418裸机开发(十一):FPU使能 & SWI异常

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

参考 arm芯片中的浮点运算(一)

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 ± 32M
  • ldr 装载指令 寻址范围是 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;

}

工程文件

7_SWI_except

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值