历史上的今天
返回首页

历史上的今天

今天是:2024年10月15日(星期二)

正在发生

2021年10月15日 | 2440裸机-14-中断与异常

2021-10-15 来源:eefocus

1.异常与中断的引入

点我查看

1).修改启动文件start.S,增加异常向量表。

默认跳转到Reset,在0地址添加一个跳转指令:b Reset,然后定义一个入口Reset,系统一上电后复位,从0地址开始运行,0地址是一个跳转指令,跳转到Reset的地方。

如果发生了未定义指令异常,CPU直接跳转到0x0000004的地方(这是硬件决定的),在0x04的地方放一条跳转指令,当发生未定义指令异常时,我们自己需要去处理这个异常。

在这里插入图片描述

2).系统一上电从0地址开始运行,在Reset这里,添加一条未定义指令,让他能够进入未定义指令异常模式:
在这里插入图片描述
在执行到这条指令,CPU读取指令之后解析,发现没办法执行,就会发生未定义指令异常,硬件就会跳到:0x0000004的地方开始指令,而我们在0x4的地方放了一条跳转指令。接着就会开始执行:und_handle函数。

如何查看哪些是未定义指令呢?
查看2440的ARM指令集的部分,里面有对指定的定义
在这里插入图片描述
而0xdeadc0de,是没有定义的指令,CPU无法识别出来。

3).在进入异常之后,添加und_handle的处理流程

为了简单起见,当发生这个异常时,只打印出异常指令字符就行了。

使用C语言编写,新建一个文件,命名为:execption.c,内容如下:

#include "uart.h"
 
 void Execption_print(unsigned int cpsr,char *str){
	putString("rnUndifined Execption! cpsr = ");
	//打印CPSR的值
	puthex(cpsr);
	putString("  ");
	putString(str);
	putString("nr");
		
 }1234567891011121314

当处理异常时,就来调用这个函数,即,打印出CPSR的值,看当前处于哪一个模式,然后再打印字符串。

  • 在启动文件处理流程中调用这个函数

这个函数需要两个参数,CPSR和str,先定义

在这里插入图片描述

注意事项:
1.因为und_string的字符串不一定是四字节的整数倍,所以在这个语句后面需要添加四字节指令对齐函数,让指令四字节对齐
在这里插入图片描述
2.因为如果遇到指令异常,会进入指令异常模式,在这个模式中需要使用串口的打印函数,所以在异常指令前面就需要去初始化串口函数,如下:
在这里插入图片描述

4).修改Makefile添加这个新的c文件,进行编译。

内容如下:

all: start.o led.o uart.o sdram_init.o main.o exception.o
	arm-linux-ld -T sdram.lds  $^ -o sdram.elf
	arm-linux-objcopy -O binary -S sdram.elf sdram.bin	
	arm-linux-objdump -D sdram.elf > sdram.dis 
%.o : %.c
	arm-linux-gcc -c -o $@ $<
 %.o : %.S
	arm-linux-gcc -c -o $@ $

Makefile文件解析:

  • 通配符符号:%

①: $@ 表示目标文件

②: $< 表示第1个依赖文件

③: $^ 表示所有依赖文件
在这里插入图片描述
5).上传,编译烧写:
在这里插入图片描述
烧写,查看结果:
在这里插入图片描述
6).流程和结果分析:

1.可以看到CPSR 寄存器的值是 0x600000DB,0xDB = 0b1011011
在这里插入图片描述
他的低4位表示的是CPU的运行模式,查看位的定义:
在这里插入图片描述
可以看到CPSR的最低5位,的确是11011,CPU进入未定义指令异常模式。

2.流程分析:

①上电复位,CPU从0地址开始运行,0地址是一条跳转指令,跳到Reset

②跳转到Reset向下继续执行,做了一系列初始化工作,比如设置系统模式的栈,设置系统时钟,初始化SDRAM,代码重定位,清除bss段,初始化串口等工作。

③执行到und_code,CPU发现是一条未定义指令。 发生未定义指令异常,硬件强制跳到0x4的位置执行。

跳转到何处根据向量表决定,如下,不同异常跳到不同的地址:
在这里插入图片描述
④在0x4地址是一条跳转指令,而在进行入异常之前系统已经帮我们完成了很多动作了,如芯片手册描述

在这里插入图片描述
1.在undefined模式下的寄存器的LR寄存器,保存了被中断模式中下一条即将执行指令的地址(比如在这个例子中,被中断之前是在用户模式下运行了,那么这个LR寄存器就保存了这个模式被中断位置下一条指令的地址)。

2.Undefigned模式下SPSR,保存了被中断模式的CPSR的值。

3.CPSR中的[M4-M0]被设置为11011

4.强制进入Undefined模式 PC跳到0x00000004的地址执行、

⑤跳转到异常的处理函数

在这里插入图片描述
每种模式都必须设置自己的堆栈空间,用于保存r0-r12,以及LR寄存器。

1.设置堆栈空间

2.因为系统模式和Undefined模式公用了r0-r12寄存器,所以需要先备份这些寄存器,以便处理异常返回到系统模式继续执行正常的工作。此外还需要保存此模式下LR寄存器的值,因为如果下面执行到bl指令跳转,就会改变LR的值,就没有办法回到中断之前的位置了。

在这里插入图片描述
3.处理异常,简单来说,就是当发生了这个异常,需要去做一些什么工作,在这里只是打印出当前程序状态寄存器(CPSR)的值,以及打印出一个字符串。

4.当处理完异常之后,就需要回到异常之前的模式了,也就是系统模式,如何回去呢?

恢复在中断之前的寄存器,也就是r0-r12,然后把pc地址指向刚刚保存的LR寄存器开始运行,恢复CPSR的值。

.text.global _start

_start:
	b reset  /* vector 0 : reset */
	b do_und /* vector 4 : 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"
			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	/* 清除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	

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112

改进程序

问题1:

在还没有进入main函数之前使用的是相对跳转,如果烧写到Nand Flash,程序在上电的时候,硬件把Nand Flash前4k的数据拷贝到SRAM,并从0地址开始运行,开始重定位,初始化的一些动作,只有执行到了main函数之后才进行绝对的跳转,使程序真正的在SDRAM运行。

但是现在有一个问题,我们的异常指令是在main函数跳转之前的,使用的是b/bl,相对跳转命令,那程序就仍然还是在SRAM运行。假如说在处理异常模式时,调用某个函数使用bl跳转,如果说这个程序小于4k的话不会有问题,但是如果大于4K的话,因为在SRAM之后4k的大小,所以Nand Flash大于4k的程序是没有办法拷贝到SRAM的,所以直接跳转就会找不到命令,导致出错。

那么可以怎样做呢?

因为程序在执行错误的指令之前已经全部重定位到SDRAM了,所以在发生异常跳转的时候就不要使用相对地址了,使用绝对地址就行了。

在这里插入图片描述
问题2:

ldr pc, =und_handle,是一条伪指令,最终会把und_handle的值保存在内存中,当需要去跳转的时候就去读内存中的值,这个值一般放置在bin文件的后面,但是如果这个bin文件的大小超出4K的话,而存放伪指令的值正好也是大于4k,因为此时程序仍然在SRAM执行,那么在这条伪指令读取地址的时候就会出错,导致整个程序没办法运行。

比如查修改完这条指令的反汇编代码:

在这里插入图片描述
为了避免这种情况发生,我们就指定让他去前4k的内存去读取und_handle这个数值。

在这里插入图片描述
编译看一下反汇编文件:

在这里插入图片描述
一旦执行完ldr pc,=0x30000008,直接就跳转到SDRAM里面执行了,就没有超不超出4k的问题了。
问题3:

在重定位完成之后就应该马上跳到SDRAM里面执行了,因为怕有bin文件大于4k,导致没有办法运行,如果跳转到SDRAM,因为哪里有完整代码的拷贝。

修改如下:

在这里插入图片描述

这里补充一点,为什么使用ldr pc,=sdram,就能跑到SDRAM里面运行呢。因为在还没有重定位之前使用的都是绝对地址。而且如果不使用绝对跳转的话程序就会在SRAM运行,正是因为有了相对地址,即使我们的即使在链接脚本指定了代码段从0x30000000开始,烧写到NOr Flash,和Nand Flash它仍然能够运行。
比如:
这个是我们bin文件的结构。
在这里插入图片描述
当烧写到Nor或者Nand的时候,此时相对地址0x30000000对于他们来说就是0地址。如下图:在这里插入图片描述
现在查看一下代码的反汇编文件,可以看到地址都是从0x30000000开始的,那为什么能在NOR Flash运行,此时在反汇编文件的,0x30000000地址就相当于Nor Flash的0地址。
在这里插入图片描述
在NOR Flash运行,需要进行函数跳转的时候,使用的是相对跳转,bl或者b
在这里插入图片描述
跳转到Reset函数执行,跳转到0x30000050,那么在NOR Flash相对值就是0x50,即为跳转到0x50的地方执行函数。
除非你使用指令 ldr pc,=0x30000050,进行绝对跳转,才真正的挑战到0x30000050这个地址执行(也就是SDRAM的地址)。
所以说在还没有进行重定位之前不可使用绝对跳转,因为还没重定位之前SDRAM是没有指令的,绝对跳转过去之后读取不到指令,系统就挂了。

推荐阅读

史海拾趣

Emulation Technology Inc公司的发展小趣事

面对电子行业的快速发展和市场的不断变化,Emulation始终保持着对技术的持续创新。公司不断投入研发资源,推出了一系列具有领先性能的模拟和仿真产品,满足了客户对于高精度、高效率仿真工具的需求。这些产品不仅提高了工程师们的设计效率,还促进了整个电子行业的发展。

EDI [Electronic devices inc.]公司的发展小趣事

在2000年代初,Eclipse Magnetics公司迎来了一个重要的合作伙伴——IBM。当时,IBM正在寻求一种可靠的磁性技术来支持其开源项目Eclipse。经过深入了解和严格测试,IBM最终选择了Eclipse Magnetics的产品。这一合作不仅为Eclipse Magnetics带来了可观的业务机会,还进一步提升了公司在行业中的影响力。

喜美克斯(Cvilux)公司的发展小趣事

喜美克斯(Cvilux)公司自创立之初,就以其对电子技术的深刻理解和创新精神在行业中崭露头角。公司的创始人是一位电子工程领域的资深专家,他带领着一支充满激情的年轻团队,针对当时市场上电子产品的一些痛点,开始研发具有自主知识产权的新技术。经过数月的努力,他们成功开发出了一款具有高效能、低功耗特点的电子芯片,这一创新技术迅速获得了市场的认可,为喜美克斯公司的发展奠定了坚实的基础。

Blue Giga公司的发展小趣事

为了扩大市场份额和提升品牌影响力,Blue Giga积极寻求与各行业领导者的合作。它与微软、谷歌、英特尔等知名企业建立了战略合作伙伴关系,共同推动物联网和无线连接技术的发展。这些合作不仅为Blue Giga带来了更多的商业机会,也提升了其在行业中的地位。

永丰盈(CST)公司的发展小趣事

在稳步发展国内市场的同时,CST积极拓展国际市场。公司建立了分布世界各地的销售渠道,与多家国际知名企业建立了长期稳定的合作关系。通过不断的市场拓展和品牌建设,CST在国际市场上树立了良好的企业形象和品牌形象。

Desco Tools公司的发展小趣事

随着业务的不断发展,Desco Tools公司开始积极拓展全球市场。公司利用其在防静电技术方面的优势,成功打入欧美、亚洲等多个国家和地区的市场。通过与国际知名企业的合作,Desco Tools公司的产品和解决方案得到了广泛应用,为全球电子行业的发展做出了重要贡献。

问答坊 | AI 解惑

再议Linux与WinCE

Linux是单体内核,即将图形、驱动及文件系统等功能全在操作系统内核中实现,运行在内核状态和同一地址空间,其优点是减少了进程间通信和状态切换的系统开销,获得较高的运行效率;缺点是内核比较庞大! WinCE是微内核,即在内核中实现基本功能, ...…

查看全部问答>

关于液晶显示的问题?

大侠们:            我们用了一个122*32的液晶显 用在跷跷板小车上,用来显示里程 速度等!现在面临的问题是,关于液晶的编程,不会编,用C语言来编。大家有没好的例子 或者什么建议!!!希望大虾们 施 ...…

查看全部问答>

一种基于DDS技术的通信信号的调制方法与设计

一种基于DDS技术的通信信号的调制方法与设计.pdf 论文…

查看全部问答>

瞬态过压问题的本源

关于瞬态过压的保护问题,这里给大家推荐看看瞬态过压的本源,也好让大家设计保护电路。…

查看全部问答>

跳频技术 (FHSS) 及直接序列 (DSSS) 展频技术

2.4G 遥控设备的优势 2.4G的优势就来自于它的带宽。所以下面又引出了个新的概念扩频(spread Specturm)。好了回到刚才的比喻,我们需要在两个城市间快递一份包裹(就是我们的遥控指令)如果走50条车道的公路你只能守法的走其中一条,中途不能变 ...…

查看全部问答>

为什么我的封装放在二层板里会出现这样的情况

为什么我的封装放在二层板里会出现这样的情况,请高手指点不知是否是软件设置上有问题?…

查看全部问答>

关于ATmega8l的计数器问题

请问一下,在ATmega8中,当我的计数器2工作在快速PWM模式后,当计数器2和OCR2匹配时,是只控制OCF2(匹配中断控制位)还是控制OC2(匹配输出引脚),还是两者都控制啊,谢谢啦~~~…

查看全部问答>

如何采集uart格式数据?

大家好,我现在有一个输出格式为uart的图像设备,我要获得它输出的图像数据,请教应该用什么方式?…

查看全部问答>

请问要选择什么入门书, 要实践类的

   如题,我对linux内核有一些了解也有编写小型操作系统的经验,想学习arm嵌入式,请问下选择什么书入门好,我希望要那些实践类的不是那种理论类的, 谢谢…

查看全部问答>

07电子设计大赛获奖论文

本帖最后由 paulhyde 于 2014-9-15 03:59 编辑 07电子设计大赛获奖论文 [ 本帖最后由 laboy 于 2010-8-14 18:22 编辑 ]  …

查看全部问答>