ARM启动方式 特别是关于中断向量的讲解解决了我的迷惑 reset vector的重定向

本文深入探讨了ARM处理器的两种主要启动方式:通过操作CP15重定向reset vector和memory remap机制。文中详细解释了这两种方法的工作原理及其优缺点,并讨论了在不同硬件配置下如何选择合适的启动方案。

        严格的说,ARM内核真正支持的启动方式只有一个:通过操作CP15来实现对reset vector的重定向,比如:硬件复位时指向ROM区域0xffff0000,而在启动代码中再重定向到RAM区域0x00000000


另一个方法更常用,就是memory remapreset vector始终不变固定在0x00000000ROM还是在0xffff0000RAM还是0x00000000,复位后硬件在逻辑上复制ROM的 映像到0x00000000,原来的RAM则被覆盖看不见了,等软件完成初始化之后再通过寄存器设置取消memory remap,将RAM重新显现出来。这一机制并非ARM内核本身所支持的,而是在SoC的时候人为修改memory的访问逻辑而实现的。对ARM内核而 言,所有4GB访问空间全部是存储器,不是代码便是数据,读写这4GB空间并不会影响ARM自身的状态,只是有时我们人为地将某些地址实现为寄存器(如 FIQ寄存器),当数据被写入这些特定地址之后(中断bit置位),其硬件逻辑便会向ARM发送各类硬件信号(FIQ电平有效),改变ARM的运行状态 (进入FIQ中断模式)。

ARM和大多数CPU内核一样,上电复位之后就立刻从某个固定地址(一般是0x00000000)开始取指执行,在这之前。硬件仅负责诸如【禁止全 局FIQ IRQ,进入SVC模式】之类的非常有限的初始化工作,软件需要完成绝大多数的初始化工作之后才能为之后的主程序建立足够的运行环境。直观上我们觉得将 ROM放在ARM的复位地址处,并且在ROM中固化好我们的启动代码是一个好主意,但是实际情况却不是如此,原因有以下几个:

1. ARM人都知道,所有的异常中断入口组成了reset vector,其第一个入口(偏移0处)就是reset地址,如果将reset vector放在ROM中,FIQIRQ等异常入口也跟着被放在了ROM中,如果我们希望在程序运行时改变FIQIRQ的中断入口,ROM就显得不方便了。(虽然也有间接的办法)

2. 有时硬件设计上code并不存放在一个NAND ROM中,可能是串行FLASH,甚至根本没有ROM(如从UART下载)。这时,ARM复位后就不得不指向RAM区域

通常情况下,为了产品后续的升级,程序都会分为两部分Bootloader+App,因为有两个程序,所以需要对中断向量表进行处理,否则,当程序已经跳转到app中运行,当中断产生的时候,响应函数仍然是Bootloader的中断函数,而不是app的 在STM32F103 F3内核上是可以配置寄存器SCB->VTOR设置中断向量表偏移,CH579是M0内核,并不支持这样操作,中断向量表位置固定在(地址0x00000000)位置上 解决思路: 将中断向量表重映射到RAM(内存) 根据当前运行程序将FLASH中的向量表拷贝到RAM中 解决步骤 将地址0x00000000的中断向量表中的全部中断函数都设置为同一个函数,用于映射 编写映射函数 修改RAM配置信息 根据前当前运行的程序拷贝中断向量表到RAM (注意:以下代码都是编写在Bootloader程序中) 第一步:设置映射 这里利用编译器设置函数属性的就可以简单实现 #define ALIAS(x) __attribute__ ((alias (#x))) void NMI_Handler(void) ALIAS(interrupt_vector_relay); void HardFault_Handler(void)ALIAS(interrupt_vector_relay); void SVC_Handler(void) ALIAS(interrupt_vector_relay); void PendSV_Handler(void) ALIAS(interrupt_vector_relay); void SysTick_Handler(void) ALIAS(interrupt_vector_relay); void TMR0_IRQHandler(void) ALIAS(interrupt_vector_relay); void GPIO_IRQHandler(void) ALIAS(interrupt_vector_relay); void SLAVE_IRQHandler(void) ALIAS(interrupt_vector_relay); void SPI0_IRQHandler(void) ALIAS(interrupt_vector_relay); void BB_IRQHandler(void) ALIAS(interrupt_vector_relay); void LLE_IRQHandler(void) ALIAS(interrupt_vector_relay); void USB_IRQHandler(void) ALIAS(interrupt_vector_relay); void ETH_IRQHandler(void) ALIAS(interrupt_vector_relay); void TMR1_IRQHandler(void) ALIAS(interrupt_vector_relay); void TMR2_IRQHandler(void) ALIAS(interrupt_vector_relay); void UART0_IRQHandler(void) ALIAS(interrupt_vector_relay); void UART1_IRQHandler(void) ALIAS(interrupt_vector_relay); void RTC_IRQHandler(void) ALIAS(interrupt_vector_relay); void ADC_IRQHandler(void) ALIAS(interrupt_vector_relay); void SPI1_IRQHandler(void) ALIAS(interrupt_vector_relay); void LED_IRQHandler(void) ALIAS(interrupt_vector_relay); void TMR3_IRQHandler(void) ALIAS(interrupt_vector_relay); void UART2_IRQHandler(void) ALIAS(interrupt_vector_relay); void UART3_IRQHandler(void) ALIAS(interrupt_vector_relay); void WDT_IRQHandler(void) ALIAS(interrupt_vector_relay); 这里利用**attribute ((alias ()))**,将全部的中断函数都设置为函数interrupt_vector_relay()的别名,通过这样声明函数,所有的中断函数等于interrupt_vector_relay()这个函数 步骤2:编写映射函数 我们只需要读取寄存器查看当前是发生了哪个中断,然后对应跳转到真正执行动作的函数即可 _vector_table_pointer 这个是RAM地址,这里利用RAM前面的0x100字节作为中断向量表存放位置 //中断向量表位置 #define _vector_table_pointer 0x20000000 __asm void interrupt_vector_relay(void) { //IPSR是中断状态寄存器,因为每个中断函数地址是4个字节,因此将读取数值*4(左移两位)得到的就是地址偏移 mrs r0, ipsr; //将值左移2位 lsls r0, r0, #0x02; //从存储器地址取数据到R1 ldr r1, =_vector_table_pointer; //计算出偏移 adds r1, r1, r0; //将该地址的值给r1 ldr r1, [r1]; //跳转到相对应的中断 bx r1; nop } 在跳转到APP前,将APP的中断向量表拷贝到RAM里面即可 //APP的地址,根据实际修改 #define APP_BASE 0x00004000 //拷贝中断向量表 memcpy((void *)(_vector_table_pointer),(void *)(APP_BASE),0x100); . 补充:app的中断函数编写和使用与正常情况下一致,无需特殊处理 但如果在Bootloader需要用到中断,比如使用定时器中断 UINT32 TIM_IRQ_ADDR; TIM_IRQ_ADDR = (UINT32)DFU_TMR0_IRQHandler; memcpy((void *)(_vector_table_pointer + 0x40),&TIM_IRQ_ADDR,4); Bootloader不能直接编写TMR0_IRQHandler() 因为这个名字已经被占用了, 需要自定义一个函数比如DFU_TMR0_IRQHandler(),然后将该函数的地址拷贝到RAM中的特定位置,这里的0x40就是对应定时器的中断函数. 根据上述 帮我生成完整的代码
最新发布
07-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值