S3C2440的七种模式之——未定义模式
Posted 悄然拔尖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了S3C2440的七种模式之——未定义模式相关的知识,希望对你有一定的参考价值。
现在做第一个实验,模拟未定义模式。
未定义模式,是cpu遇到自己不能识别的指令时候做出的异常处理。
arm指令的机器码一定是按照某种规范要求的,不然你随意写一条指令,cpu不是都可以执行吗?在cpu没有定义该条指令含义的情况下,我们执行了这样未定义的指令,就会进入未定义异常。
现在我们要模拟一个未定义异常,所以我们只要写出一个cpu无法识别的指令即可。
在这之前,要明白一个道理,在内存中执行的机器码,只有0,1两个值,不同的指令被分解为不同的0,1信号的机器码。
所以,我们在运行内存中存放一个32bit的值,这个值又恰恰是上图所不能表示的指令,那么这样,就可以测试未定义异常了。我们采用.word 关键字,.word expression就是在当前位置放一个word型的值,这个值就是expression 。
.word 接收4字节的数据,放在当前地址。
现在我们仔细观察上面的那个图,找到一个未定义的机器码,把这个值放在.word的地方,当cpu运行到.word 地址时,发现这个地址的0,1数据即机器码它不能识别,就发生未定义异常。
所以我们可以使用 .word 0xf3000000来表示一条未定义指令,但大家用的比较多的是.word 0xbadc0de(很形象,表示坏代码)。
现在更改star.S:
.text .global _start _start: b reset /* vector 0 : reset */ ldr pc, und_addr /* vector 4 : und */ und_addr: .word do_und do_und: /* 执行到这里之前: * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址 * 2. SPSR_und保存有被中断模式的CPSR * 3. CPSR中的M4-M0被设置为11011, 进入到und模式 * 4. 跳到0x4的地方执行程序 */ /* sp_und未设置, 先设置它 */ ldr sp, =0x34000000 /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */ /* lr是异常处理完后的返回地址, 也要保存 */ stmdb sp!, {r0-r12, lr} /* 保存现场 */ /* 处理und异常 */ mrs r0, cpsr ldr r1, =und_string bl printException /* 恢复现场 */ ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */ und_string: .string "undefined instruction exception" .align 4 reset: /* 关闭看门狗 */ ldr r0, =0x53000000 ldr r1, =0 str r1, [r0] /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */ /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */ ldr r0, =0x4C000000 ldr r1, =0xFFFFFFFF str r1, [r0] /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */ ldr r0, =0x4C000014 ldr r1, =0x5 str r1, [r0] /* 设置CPU工作于异步模式 */ mrc p15,0,r0,c1,c0,0 orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA mcr p15,0,r0,c1,c0,0 /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) * m = MDIV+8 = 92+8=100 * p = PDIV+2 = 1+2 = 3 * s = SDIV = 1 * FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M */ ldr r0, =0x4C000004 ldr r1, =(92<<12)|(1<<4)|(1<<0) str r1, [r0] /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定 * 然后CPU工作于新的频率FCLK */ /* 设置内存: sp 栈 */ /* 分辨是nor/nand启动 * 写0到0地址, 再读出来 * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动 * 否则就是nor启动 */ mov r1, #0 ldr r0, [r1] /* 读出原来的值备份 */ str r1, [r1] /* 0->[0] */ ldr r2, [r1] /* r2=[0] */ cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */ ldr sp, =0x40000000+4096 /* 先假设是nor启动 */ moveq sp, #4096 /* nand启动 */ streq r0, [r1] /* 恢复原来的值 */ bl sdram_init //bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */ /* 重定位text, rodata, data段整个程序 */ bl copy2sdram ldr pc, =sdram sdram: /* 清除BSS段 */ bl clean_bss bl uart0_init bl print1 /* 故意加入一条未定义指令 */ und_code: .word 0xdeadc0de /* 未定义指令 */ bl print2 //bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ ldr pc, =main /* 绝对跳转, 跳到SDRAM */ halt: b halt
cpu处理异常的时候,是有专门地址的,这里的未定义und异常,在地址0x4处,当发生未定义异常时,硬件让程序从地址0x4处执行。这里说明一下汇编中定义字符串:
.string “XXXX” 表示定义的字符串包含最后的\\0.
.ascii "XXXX"表示定义的字符串不包含最后的\\0.
.asciiz "XXXX"表示定义的字符串包含最后的\\0,z代表zero。
参考:(点击这里)
.align 4 放在.string 后面是为了使后面的指令四字节对齐,因为字符串的字节数不一定是四字节对齐的。
进入异常处理前需要保存现场,异常处理之后需要恢复现场。
现在说明下面代码段:
ldr pc, und_addr /* vector 4 : und */ und_addr: .word do_und
但是我在想,为什么韦老大说nand启动,怕访问超过4k空间,所以采取上面的操作方式,这样保证肯定无论是否超过4k,程序都可以正常运行。但是,我们的sdram已经重定位完毕了,那样sdram已经是可以访问的了,所以就算超过4k也不会有问题啊?(这个等到后面上文件系统的时候测试,我觉得这里是不存在老师说的那种情况的)。
Question:
去掉star.S中的 bl print1
发现不会进入未定义异常了?这是为什么?
经过分析,是puts函数问题,只有调用了puts函数,才会进入未定义异常。原因。。。
我们上面忽略了一点,就是cond所占用的四个字节条件,
所以,未定义指令要忽略高4位cond条件码。
在测试过程中,我能发现是puts函数的问题,可是,puts函数是我们重定义的库函数,这和stm32 printf映射串口有一点像,具体编译器在操作puts的时候做了什么不得而知,但是可以肯定的是puts让cpsr高四位变成了无条件执行,才触发了未定义异常。现在我们把未定义指令的高四位改成1110,后面加上arm不能识别的指令,就可以进入未定义异常模式了。
以上是关于S3C2440的七种模式之——未定义模式的主要内容,如果未能解决你的问题,请参考以下文章