深入理解 Cortex-M 向量表和向量表重定位

细心的小伙伴肯定能发现,前几篇文章中频繁出现一个名词 —— 向量表。这到底是个什么东西?

今天我们就来深入了解一下向量表以及向量表的重定位。

当 Cortex-M 处理器接受了某个异常请求后,处理器需要确定该异常处理(若为中断则是 ISR)的起始地址。而这个信息就存储在存储器的向量表中。

向量表默认从地址 0 开始,向量地址则为异常编号乘 4 ,如下图所示:

图片

向量表一般被定义在微控制器供应商提供的启动代码中,以 STM32F4 为例,在其 startup_stm32f407xx.s 中有如下一段代码:

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                ; External Interrupts
                DCD     WWDG_IRQHandler                   ; Window WatchDog                                        
                DCD     PVD_IRQHandler                    ; PVD through EXTI Line detection                        
                DCD     TAMP_STAMP_IRQHandler             ; Tamper and TimeStamps through the EXTI line            
                DCD     RTC_WKUP_IRQHandler               ; RTC Wakeup through the EXTI line                       
                DCD     FLASH_IRQHandler                  ; FLASH                                           
                DCD     RCC_IRQHandler                    ; RCC                                             
                DCD     EXTI0_IRQHandler                  ; EXTI Line0                                             
                DCD     EXTI1_IRQHandler                  ; EXTI Line1                                             
                DCD     EXTI2_IRQHandler                  ; EXTI Line2                                             
                DCD     EXTI3_IRQHandler                  ; EXTI Line3                                             
                DCD     EXTI4_IRQHandler                  ; EXTI Line4                                             
                DCD     DMA1_Stream0_IRQHandler           ; DMA1 Stream 0                                   
                DCD     DMA1_Stream1_IRQHandler           ; DMA1 Stream 1                                   
                DCD     DMA1_Stream2_IRQHandler           ; DMA1 Stream 2                                   
                DCD     DMA1_Stream3_IRQHandler           ; DMA1 Stream 3                                   
                DCD     DMA1_Stream4_IRQHandler           ; DMA1 Stream 4                                   
                DCD     DMA1_Stream5_IRQHandler           ; DMA1 Stream 5                                   
                DCD     DMA1_Stream6_IRQHandler           ; DMA1 Stream 6                                   
                DCD     ADC_IRQHandler                    ; ADC1, ADC2 and ADC3s                            
                DCD     CAN1_TX_IRQHandler                ; CAN1 TX                                                
                DCD     CAN1_RX0_IRQHandler               ; CAN1 RX0                                               
                DCD     CAN1_RX1_IRQHandler               ; CAN1 RX1                                               
                DCD     CAN1_SCE_IRQHandler               ; CAN1 SCE                                               
                DCD     EXTI9_5_IRQHandler                ; External Line[9:5]s                                    
                DCD     TIM1_BRK_TIM9_IRQHandler          ; TIM1 Break and TIM9                   
                DCD     TIM1_UP_TIM10_IRQHandler          ; TIM1 Update and TIM10                 
                DCD     TIM1_TRG_COM_TIM11_IRQHandler     ; TIM1 Trigger and Commutation and TIM11
                DCD     TIM1_CC_IRQHandler                ; TIM1 Capture Compare                                   
                DCD     TIM2_IRQHandler                   ; TIM2                                            
                DCD     TIM3_IRQHandler                   ; TIM3                                            
                DCD     TIM4_IRQHandler                   ; TIM4                                            
                DCD     I2C1_EV_IRQHandler                ; I2C1 Event                                             
                DCD     I2C1_ER_IRQHandler                ; I2C1 Error                                             
                DCD     I2C2_EV_IRQHandler                ; I2C2 Event                                             
                DCD     I2C2_ER_IRQHandler                ; I2C2 Error                                               
                DCD     SPI1_IRQHandler                   ; SPI1                                            
                DCD     SPI2_IRQHandler                   ; SPI2                                            
                DCD     USART1_IRQHandler                 ; USART1                                          
                DCD     USART2_IRQHandler                 ; USART2                                          
                DCD     USART3_IRQHandler                 ; USART3                                          
                DCD     EXTI15_10_IRQHandler              ; External Line[15:10]s                                  
                DCD     RTC_Alarm_IRQHandler              ; RTC Alarm (A and B) through EXTI Line                  
                DCD     OTG_FS_WKUP_IRQHandler            ; USB OTG FS Wakeup through EXTI line                        
                DCD     TIM8_BRK_TIM12_IRQHandler         ; TIM8 Break and TIM12                  
                DCD     TIM8_UP_TIM13_IRQHandler          ; TIM8 Update and TIM13                 
                DCD     TIM8_TRG_COM_TIM14_IRQHandler     ; TIM8 Trigger and Commutation and TIM14
                DCD     TIM8_CC_IRQHandler                ; TIM8 Capture Compare                                   
                DCD     DMA1_Stream7_IRQHandler           ; DMA1 Stream7                                           
                DCD     FMC_IRQHandler                    ; FMC                                             
                DCD     SDIO_IRQHandler                   ; SDIO                                            
                DCD     TIM5_IRQHandler                   ; TIM5                                            
                DCD     SPI3_IRQHandler                   ; SPI3                                            
                DCD     UART4_IRQHandler                  ; UART4                                           
                DCD     UART5_IRQHandler                  ; UART5                                           
                DCD     TIM6_DAC_IRQHandler               ; TIM6 and DAC1&2 underrun errors                   
                DCD     TIM7_IRQHandler                   ; TIM7                   
                DCD     DMA2_Stream0_IRQHandler           ; DMA2 Stream 0                                   
                DCD     DMA2_Stream1_IRQHandler           ; DMA2 Stream 1                                   
                DCD     DMA2_Stream2_IRQHandler           ; DMA2 Stream 2                                   
                DCD     DMA2_Stream3_IRQHandler           ; DMA2 Stream 3                                   
                DCD     DMA2_Stream4_IRQHandler           ; DMA2 Stream 4                                   
                DCD     ETH_IRQHandler                    ; Ethernet                                        
                DCD     ETH_WKUP_IRQHandler               ; Ethernet Wakeup through EXTI line                      
                DCD     CAN2_TX_IRQHandler                ; CAN2 TX                                                
                DCD     CAN2_RX0_IRQHandler               ; CAN2 RX0                                               
                DCD     CAN2_RX1_IRQHandler               ; CAN2 RX1                                               
                DCD     CAN2_SCE_IRQHandler               ; CAN2 SCE                                               
                DCD     OTG_FS_IRQHandler                 ; USB OTG FS                                      
                DCD     DMA2_Stream5_IRQHandler           ; DMA2 Stream 5                                   
                DCD     DMA2_Stream6_IRQHandler           ; DMA2 Stream 6                                   
                DCD     DMA2_Stream7_IRQHandler           ; DMA2 Stream 7                                   
                DCD     USART6_IRQHandler                 ; USART6                                           
                DCD     I2C3_EV_IRQHandler                ; I2C3 event                                             
                DCD     I2C3_ER_IRQHandler                ; I2C3 error                                             
                DCD     OTG_HS_EP1_OUT_IRQHandler         ; USB OTG HS End Point 1 Out                      
                DCD     OTG_HS_EP1_IN_IRQHandler          ; USB OTG HS End Point 1 In                       
                DCD     OTG_HS_WKUP_IRQHandler            ; USB OTG HS Wakeup through EXTI                         
                DCD     OTG_HS_IRQHandler                 ; USB OTG HS                                      
                DCD     DCMI_IRQHandler                   ; DCMI  
                DCD     0                                 ; Reserved                                  
                DCD     HASH_RNG_IRQHandler               ; Hash and Rng
                DCD     FPU_IRQHandler                    ; FPU

很明显,其结构和上图是完全对应的。

其次可以看到,向量表中还包含主栈指针(MSP)的初始值,这种设计是非常有必要的,根据我们之前的文章,在复位后可能会立马产生 NMI 等异常,因此需要为这些异常的执行准备好运行环境。向量表中的 MSP 可以让处理器在启动时就获取并设置好栈指针,从而有能力应对上述可能发生的异常。详细原理可以参照下文:

资深嵌入式工程师的自我修养 —— Cortex-M3 的复位与复位流程

需要注意的是,Cortex-M 处理器中的向量表和 ARM7TDMI 等传统 ARM 处理器的向量表不同。对于传统的 ARM 处理器,向量表中存在跳转到相应处理的指令,而 Cortex-M 处理器的向量表则为异常处理的起始地址。

一般来说,最开头的地址(0x00000000)应该是启动地址,它可以是 FLASH 存储器或者 ROM 设备,并且在运行时不能对其进行修改。然而,有些应用中需要能够在运行时对异常向量表进行修改或重新定义。为了处理这种操作,Cortex-M3 和 Cortex-M4 处理器实现了一种名为向量表重定位的特性。

向量表重定位特性提供了一个名为向量表偏移寄存器(VTOR)的可编程寄存器。该寄存器定义了被用作向量表的内存起始地址,如下图所示:

图片

需要注意的是,改寄存器在 Cortex-M3 的版本 r2p0 和 r2p1 间稍微有点不同。对于 Cortex-M3 r2p0 或之前的版本,向量表只能位于 CODE 和 SRAM 区域,而这个限制在 Cortex-M3 r2p1 和 Cortex-M4 中已经不存在了。

VTOR 的复位值为 0,如果使用符合 CMSIS 的设备驱动库进行应用编程,则可以通过 SCB->VTOR 来访问该寄存器。想要将向量表重定位到 SRAM 区域的开头处,可以使用以下代码:

// 复制向量表到 SRAM 开头处(0x20000000)的代码示例
// 注意,下面使用的存储器屏障指令(DMB/DSB)是基于架构建议的,
// Cortex-M3 和 Cortex-M4 并非强制要求。

// 字访问指令
#define HW32_REG(ADDRESS) (*((volatile unsigned long *)(ADDRESS)))
#define VTOR_NEW_ADDR     0x20000000
int i;  // 循环计数

// 在设置 VTOR 寄存器之前先将原来的向量表拷贝到 SRAM
for (i = 0; i < 48; i++) {
    // 将每个向量表项拷贝到 SRAM
    HW32_REG((VTOR_NEW_ADDR + (i << 2))) = HW32_REG(i << 2);
}

__DMB();  // 数据存储器屏障, 确保数据已经写入
SCB->VTOR = VTOR_NEW_ADDR;  // 设置向量表偏移寄存器为新的向量表地址
__DSB();  // 数据同步屏障, 确保接下来的所有指令都是用新的配置

当使用 VTOR 时,需要将向量表大小扩展为下一个 2 的整数次方,且新向量表的基地址必须要对齐到这个数值。接下来给出两个示例以参考:

示例一:微控制器有 32 个中断源

向量表大小为(32(用于中断)+16(用于系统异常))× 4(每个向量所占用的字节数)= 192(0xC0)。将其扩展为下一个 2 的整数次方即 256 字节。因此,向量表的地址可被设置为 0x00000000、0x00000100以及 0x00000200 等。

示例二:微控制器有 75 个中断源

向量表大小为(75(用于中断)+16(用于系统异常))× 4(每个向量所占用的字节数) = 364(0x16C)。将其扩展为下一个 2 的整数次方就得到 512 字节。因此,向量表的地址可被设置为 0x00000000、0x00000200 以及 0x00000400 等。

由于中断的最小数量为 1,最小的向量表对齐值为 128 字节。因此,VTOR 的最低 7 位保留,且被强制为 0。

向量表的重定位常用于以下几种情形:

  1. 具有 Bootloader 的设备

有些微控制器具有多个程序存储器,启动 ROM 和用户 FLASH 存储器。微控制器生产商一般会将 Bootloader 预先写到启动 ROM 中,这样在微控制器启动时,启动 ROM 中的 Bootloader 就会首先执行,此时使用的是启动 ROM 中的向量表,而在跳转到用户 FLASH 的应用程序前,VTOR 需要被设置为指向用户 FLASH 存储器的向量表处,使用用户 FLASH 中的向量表(在一个程序存储器中实现 Bootloader 也类似)。

图片

2. 应用程序加载到 RAM 运行

有些情况下,应用程序可能需要从外部设备加载到 RAM 中执行,其本身可能位于 SD 卡中,或者需要通过网络传输。在这种情况下,存储在片上存储器中用于启动的程序需要初始化一些硬件、复制位于外部设备中的应用程序到 RAM,并更新 VTOR 后执行应用程序。

图片

3. 动态修改向量表

有些情况下,ROM 中可能会有一个中断的多个处理实例,可能需要在应用的不同阶段在它们之间进行切换。在这种情况下,可以将向量表从程序存储器复制到 SRAM,并且设置 VTOR 指向 SRAM 中的向量表。由于 SRAM 中的内容可以在任意时间修改,因此可以轻易地在应用的不同阶段修改中断向量。

向量表至少要提供 MSP 的初始值以及用于系统启动的复位向量。另外,对于一些应用,若设备在启动时有触发 NMI 的可能,则还需要加入 NMI 向量和用于错误处理的 HardFault 向量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WKJay_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值