您的位置:首页 > 其它

S3C2440的七种模式之——未定义模式(去掉bl print1 bug解决)

2017-12-24 13:10 162 查看




现在做第一个实验,模拟未定义模式。

未定义模式,是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不能识别的指令,就可以进入未定义异常模式了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐