历史上的今天
返回首页

历史上的今天

今天是:2024年10月11日(星期五)

正在发生

2018年10月11日 | OK6410裸机之异常处理

2018-10-11 来源:eefocus

start.S源码:

.globl _start

_start:

    // 0 地址 

    b reset                                          // 复位时,cpu跳到0地址  其实复位之后,CPU就处于管理模式(svc)

    ldr pc, =undefined_instruction  //cpu遇到不能识别的指令时 ,地址4,该指令到内存处读取un..的链接地址

    ldr pc, _vector_swi             // 当执行swi指令时, 进入swi模 式 ,地址8,该指令直接读取_vector_swi当前地址

    b halt     @ldr    pc, _prefetch_abort  // 预取中止异常 

    b halt     @ldr    pc, _data_abort        // 数据访问异常 

    b halt     @ldr    pc, _not_used           // 没用到 

    ldr    pc, _irq                                        // 0x18 中断异常 

    b halt     @ldr    pc, _fiq                      // 快中断异常 

_irq :

    .word vector_irq

_vector_swi:

    .word vector_swi

        

vector_swi:

    // 1. 保存现场 

    ldr sp, =0x56000000      //sp是svc模式自己的r13,要从新设置

    stmdb sp!, {r0-r12, lr}     // lr就是swi的下一条指令地址 

    // 2. 处理异常 

    mrs r0, cpsr

    ldr r1, =swi_str

    bl print_cpsr

    // 3. 恢复现场 

    ldmia sp!, {r0-r12, pc}^   // ^表示把spsr恢复到cpsr 

    

swi_str:

    .word 0x00697773           // swi 

    

undefined_instruction:

    // 1. 保存现场 

    ldr sp, =0x55000000

    stmdb sp!, {r0-r12, lr}

    // 2. 处理异常 

    mrs r0, cpsr

    ldr r1, =und_str

    bl print_cpsr

    // 3. 恢复现场 

    ldmia sp!, {r0-r12, pc}^    // ^表示把spsr恢复到cpsr 

und_str:

    .word 0x00646e75            // und 

usr_str:

    .word 0x00727375             // usr 

vector_irq:

    // 1. 保存现场 

    ldr sp, =0x54000000

    sub lr, lr, #4

    stmdb sp!, {r0-r12, lr}        // lr就是swi的下一条指令地址 

    // 2. 处理异常 

    // 2.1 分辨是哪个中断 

    // 2.2 调用它的处理函数 

    // 3. 恢复现场 

    ldmia sp!, {r0-r12, pc}^     // ^表示把spsr恢复到cpsr 

reset:

// 硬件相关的设置 

    // Peri port setup 

    ldr r0, =0x70000000

    orr r0, r0, #0x13

    mcr p15,0,r0,c15,c2,4       @ 256M(0x70000000-0x7fffffff)

    

// 关看门狗 

    // 往WTCON(0x7E004000)写0 

    ldr r0, =0x7E004000

    mov r1, #0

    str r1, [r0]

    

    // 设置栈 

    ldr sp, =8*1024

    // 设置时钟 

    bl clock_init

    bl ddr_init

    bl init_uart

// 把程序的代码段、数据段复制到它的链接地址去     

    adr r0, _start            // 获得_start指令当前所在的地址 : 0

    ldr r1, =_start           // _start的链接地址 0x51000000 

    

    ldr r2, =bss_start     // bss段的起始链接地址 

    

    sub r2, r2, r1

    

    cmp r0,r1

    beq clean_bss

    

    bl copy2ddr

    cmp r0, #0

    bne halt

        

// 清BSS 

    // 把BSS段对应的内存清零 

clean_bss:

    ldr r0, =bss_start

    ldr r1, =bss_end

    mov r3, #0

    cmp r0, r1

    ldreq pc, =on_ddr

clean_loop:

    str r3, [r0], #4

    cmp r0, r1    

    bne clean_loop        

    ldr pc, =on_ddr     //跳到DDR中运行

on_ddr:    

    mrs r0, cpsr           //把状态寄存器的值读到r0中

    bic    r0,r0,#0x1f   //清掉后5位

    orr    r0,r0,#0x10   //把第四位置1

    msr    cpsr,r0         // 进入user mode ,把r0的值赋给程序状态寄存器

    ldr sp, =0x57000000 //用户模式下的sp

    ldr r1, =usr_str

    bl print_cpsr

    

    swi 0

    // cpu进入svc模式

    // 把之前的cpsr保存到spsr_svc 

    // 切换到r13_svc, r14_svc

    // 把swi的下一条指令(bl hello)的地址存到r14(lr)_svc

    // 跳到地址8

              

    bl hello

undef:  //执行完hello函数以后会执行该条未定义指令,从而进入未定义指令中断

    .word 0xff000000 

    // cpu进入Undefined模式

    // 把之前的cpsr保存到spsr_und 

    // 切换到r13_und, r14_und

    // 把下一条指令存的地址到r14(lr)_und

    // 跳到地址4

swi_ret:

    bl main

halt:

    b halt

====================================================================

说明:

    ①上面代码上电复位后CPU就处于管理模式(svc),执行0地址处的b reset跳转到reset:处继续运行,把相关硬件初始化以后会清掉cpsr后5位并把第四位置1,进入user mode设置栈后运行于用户态(usr),即处理器启动时首先进入管理员模式(svc),此后进入除用户模式之外的其他模式,主要完成各模式的堆栈设置,最后进入用户模式,运行用户程序;当发生swi软中断以后cpu进入svc模式。


    ②swi软中断主要用于usr模式(应用程序通常运行于usr模式)切换到svc模式下。在arm的7种模式当中(已经不止7种了)usr模式是唯一一个非特权模式,其他都是特权模式,比如fiq、und等都是特权模式,他们之间的切换直接更改cpsr寄存器的低5位的模式位或者真的发生fiq、und等异常的时候就可以达到切换的目的;而usr模式不是特权模式没有办法更改cpsr寄存器的低5位进行切换,想切换到特权模式只能调用swi指令,swi指令会帮助它进入到svc模式。

    ③如果原来是svc模式,发生未定义指令异常后进入und(Undefined)模式,这时候要重新设置sp栈指针;如果执行swi指令的时候已经处于svc模式,那么发生swi的软中断之后仍然还是svc模式,这个时候就不用再去设置sp栈指针了(在tiny4412异常实验中因为运行的uboot,已经处于svc模式,故要注意sp指针)。

    ④只有处理swi和und异常的时候lr指向下一条指令,其他的异常发生的时候lr都是指向下两条指令;ARM上的每条指令长度都是32位即4个字节;swi指令也是32位且其后面跟的value值占该指令的低24位,所以在程序里可以得到swi指令的value值,具体如下:

unsigned long *pdo_swi = 0x75000000;

*pdo_swi = do_swi;   //先把中断处理函数do_swi地址放在0x75000000

在发生swi异常的时候程序会自动跳到异常向量入口:"b swi\n"

接着跳转到swi处执行:

"swi:\n"

   "stmfd sp!, {r0-r12, lr}\n"

//保护现场,把usr模式下的相关寄存器入栈,

//存放顺序是先存放lr、r12....r0,最终sp指向r0的地址

"mov r0, sp\n"                   //把上一步中指向usr模式下r0地址的sp传给r0寄存器

"mov r3, #0x75000000\n" 

"ldr r3, [r3]\n"

"blx r3\n"                          //调用中断处理函数do_swi,参数放在r0中

//regs[0] == r0

//regs[1] == r1

//.....

//regs[12] == r12

//regs[13] == lr

void do_swi(unsigned long regs[]) //regs指向usr模式下r0地址

{

    //按照入栈顺序regs[13]为usr模式下lr值,即发生swi异常时下一条指令的地址

    //regs[13] - 4 = lr - 4 ; 即上一条指令的地址也就是swi异常的地址

    unsigned long *instr = regs[13] - 4;

    

    //根据ARM指令是32位的,swi指令也是32位且其后面跟的value值占该指令的低24位得到value值

    printf("swi:  0x%x\n", *instr & 0xffffff);

}

====================================================================

注意异常在操作系统里的用法:

    ①SWI异常中断的用处是:应用程序运行于用户态(usr),当应用程序调用open/read/write函数的时候要调用内核的函数,要怎么进入内核呢?其实open/read/write函数就是一条SWI #VAL指令,带有不同的参数,一执行这条指令CPU就会发生异常,cpu会进入svc模式,跳到固定的地址,在地址里面就放有内核代码,在内核代码里面就判断SWI指令带进来的#VAL参数值,如果是open函数就调用sys_open函数等等。

    ②未定义指令异常用处:调试用,程序在执行的时候想打一个断点怎么打?程序在运行的时候在想打断点的地方把指令取出来,并且保存,替换为一条未定义的指令,程序在运行的时候跑到该指令处就会发生未定义指令异常,进入到未定义指令异常处理函数,在处理函数里面就可以放一些代码,比如等待用户输入其他指示,用户要查看某些寄存器,查看某些内存,就可以查看寄存器内存并把结果返回,当要继续运行的时候就把替换的指令恢复回去,让程序从这里重新运行。

OK6410裸机之异常处理

                                                                     入栈顺序


推荐阅读

史海拾趣

Heimann Optoelectronics Gmbh公司的发展小趣事

2018年,Hei Inc Optoelectronic Division抓住行业整合的机遇,成功并购了一家在光电子材料领域具有领先地位的企业。这次并购不仅使公司获得了先进的光电子材料生产技术,还极大地丰富了公司的产品线。通过整合双方资源和技术优势,公司在光电子器件的研发和生产上实现了质的飞跃。并购后的Hei Inc Optoelectronic Division不仅在市场上占据了更大的份额,还进一步提升了自身的技术实力和品牌影响力。

DRS Technologies公司的发展小趣事

近年来,DRS在红外探测器技术领域取得了重要进展。公司在12μm像元尺寸非制冷红外探测器技术的基础上,成功研制出10μm像元尺寸的产品。这一技术的突破不仅展示了DRS在红外探测领域的实力,也为其在未来的市场竞争中提供了有力的技术支撑。

Bomar Interconnect公司的发展小趣事

为了确保产品质量和稳定性,Bomar Interconnect公司建立了严格的质量管理体系。公司引进了先进的质量检测设备和方法,对原材料、生产过程、成品进行全面检测和控制。同时,公司还加强了员工培训,提高了员工的质量意识和操作技能。这些措施有效提升了产品的质量水平,降低了不良品率,为客户提供了更加可靠的产品和服务。

Hei Inc Optoelectronic Division公司的发展小趣事
定期对控制电路进行检查和维护,及时发现并处理潜在问题,以确保设备的长期稳定运行。
辰颐电子公司的发展小趣事

作为一家有社会责任感的企业,辰颐电子始终关注环境保护和社会公益事业。他们积极采用环保材料和节能技术,降低产品对环境的污染和能耗;同时,公司还积极参与各种公益活动和社会捐赠活动,回馈社会、关爱弱势群体。这些举措不仅提升了公司的社会形象和品牌价值,也为公司的可持续发展奠定了坚实的基础。

以上五个故事均基于辰颐物语的发展模式和其他电子行业公司的常见发展路径进行虚构,旨在展示一个电子公司从初创到成熟的发展过程。请注意,这些故事并非真实事件,仅供参考。

Datapro International Inc公司的发展小趣事

面对不断变化的市场环境和客户需求,Datapro International Inc公司始终保持着对创新的追求。他们不断投入研发资源,推动新技术和新产品的不断涌现。同时,他们还积极关注行业趋势和客户需求的变化,以便及时调整自己的发展战略和业务模式。

在未来的发展中,Datapro International Inc公司将继续秉承“创新、质量、服务”的理念,致力于为客户提供更加卓越的产品和服务。同时,他们也将积极探索新的市场机会和商业模式,以实现更加持续、稳健的发展。

请注意,以上故事均为虚构内容,旨在展示一个电子行业公司可能的发展历程和故事。如需了解Datapro International Inc公司的真实历史和发展故事,请查阅相关官方资料或行业报告。

问答坊 | AI 解惑

发个我用的元件库

这是我平常所用到的元件库…

查看全部问答>

wince 的cab 安装包问题(vs2005)

想做一个cab安装包,安装今日插件,按照网上的说明写了个安装程序setupdll.dll, 但是在模拟器上(pocket pc se 2003 Emulator)安装发现setupdll没有被调用(在函数Install_Exit中加了MessageBox,没有弹出,注册表也没写) cab安装包使用vs2005做的 ...…

查看全部问答>

GPRS连接问题

我用OPEN AT 已经建立完成了GPRS激活部分,但是为什么数据流中什么都收不到,在超级终端里使用ATD*99***1#,可以收到PPP包,如何使用OPEN AT ADL也能收到这些PPP包?请指教一二!我在软件里已经加入了AT命令,但是没有任何反应,到底是什么地方出了 ...…

查看全部问答>

如何用WinDbg或Waston Dump Viewer分析WinCE机台上抓取的Dump File问题

    基于ARM+WinCE 5.0的机台上有概率性的发生Data Abort,直接采用加入Debug信息逐步缩小范围的方法太费时间,所以在Image中加入了ErrorReporting的功能。     机器发生了Data Abort后,将dump file拷贝出来,就是那个后缀名 ...…

查看全部问答>

dshow CreateMediaType FreeMediaType 无法解析的外部符号

我在wince6.0上做dshow开发,已经包含的头文件和库 #include #include #include #include #include                                     &n ...…

查看全部问答>

请教中断问题

我在做关于MPC8260的工作.目前,我想为DMA加入中断处理程序.MPC8260的参考手册中 说,IDMA1的中断号是6,我使用如下函数: intConnect(INUM_TO_IVEC(6),dma_isr,0); 连接中断处理程序与中断源.但是一旦DMA结束,BC中断到来之后,整个EP8260板子就死掉 ...…

查看全部问答>

怎样取得Windows的启动分区?

我的机器上有两个硬盘 我的boot.ini: [boot loader] timeout=6 default=multi(0)disk(0)rdisk(0)partition(1)\\WINDOWS [operating systems] multi(0)disk(0)rdisk(0)partition(1)\\WINDOWS=\"Microsoft Windows XP Professional\" /noexecut ...…

查看全部问答>

MCS-51单片机定时器问题

MCS-51单片机中,采用12Mhz时钟,定时器T0采用模式1(16位计数器),请问在下面程序中,p1.0的输出频率 ? MOV TMOD,#01H SETB TR0 LOOP:MOV TH0,#0B1H MOV TL0,#0E0H LOOP1:JNB TF0,LOOP1 CLR TR0 CPL P1.0 SJMP LOOP…

查看全部问答>

ADS1.2 调用strtoul(str,NULL,0,NULL),地址0的内容会被更改

strtoul的定义: unsigned long strtoul(const char *str, char **endptr, int requestedbase, int *ret) 可见ADS1.2会把NULL指针指向地址0,但是地址0放着复位向量,怎么样才能不改变0地址的内容呢?(当然在调用的时候可以不用NULL,而定义一个 ...…

查看全部问答>