Cortex-M架构使用了“向量表查表机制”,当异常发生时,内核会自动从向量表查找出Handler的入口地址。向量表其实是一个 WORD(32 位整数)数组,每个下标对应一种异常,该下标元素的值则是该异常 handler的入口地址。
向量表在地址空间中的位置是可以设置的,通过SCB中的VTOR寄存器来指出向量表的地址。在复位后,该寄存器的值为0。因此,在地址0处必须包含一张向量表,用于初始时的异常分配。但在系统运行之后我们可以通过修改VTOR寄存器重定位向量表到其他地址,可以是Flash区域,也可以是RAM区域。但必须注意向量表的起始地址的要求:必须先求出系统中共有多少个向量,再把这个数字向上增大到是 2 的整次幂,而起始地址必须对齐到后者的边界上。例如,如果一共有68个中断,则共有 68+16(系统异常)= 84 个向量,向上增大到 2 的整次幂后值为 128,因此地址必须能被 128*4=512 整除。
本文以IAR EWARM开发环境为例,将STM32F107的向量表重定位到SRAM的起始地址。EWARM要求默认的向量表要命名为“__vector_table”,以便它的调试系统能够正确识别出向量表,并且放置在Flash的零地址。
然后,在SRAM也定义一个向量表,这个表的命名可以任意,这里命名为“sram_vector_table”。在系统初始化之后,将__vector_table里面的内容复制到sram_vector_table,实现新的向量表构建。因为要控制向量表的存放位置,所以通过#pragma预处理指令将向量表放置在一个自定义的section中,命名为“.vector”。“__root”关键字强制编译器保留向量表,不进行任何优化操作。
#define VECTOR_LEN 84UL
#pragma location = ".vector"
__root uint32_t
sram_vector_table[VECTOR_LEN]; /* Vector table in sram */
然后在IAR链接器脚本icf文件中,控制.vector的存放位置,将其放置在SRAM的起始位置:
place at start of RAM_region { section .vector };
在代码中适当的位置对sram_vector_table进行构建:
在进行重定位向量表时应该先关闭全局中断,避免过程中发生中断造成不可预料的情况发生。SRAM_BASE是RAM的起始地址0x20000000,VECT_TAB_OFFSET为0。
重新设置了VTOR之后,再次开启全局中断,至此中断向量表完成了重定位。