#Bootloader
Bootloader为存储在ROM中的启动代码(一些程序),由TI在芯片出厂时以掩膜方式固化内容。
TI使用ROM空间的一部分,还有一部分剩余空间,用户可以委托TI写入(掩模)所需代码。
##版本
OMAPL138的Bootloader保存在ROM中,检查内存地址0xFFFD0000
,在memory window
中选择Character
,在偏移地址0x08
可以读到版本号:版本号为类似于d800k008
。
##启动模式
启动模式分为两种
除了HPI (Host Port Interface)和2种NOR (Parallel Flash) boot模式,其他模式均使用AIS(Application Image Script)格式。
管脚配置详见Using the OMAP-L132/L138 Bootloader Appendix A Boot Mode Selection Table
##启动顺序
OMAP-L138 SOC 的启动详解
OMAP-L138 的ROM内有DSP 与ARM两份启动代码,芯片上电解复位后,DSP Bootloader 先运行,通过PRU 加载ARM初始化代码,因为DSP 不能访问ARM的片上RAM空间,所以这一步通过PRU 完成,然后DSP 通过PSC 使能ARM,运行ARM的Bootloader, ARM再将DSP 置于复位态,并关闭其时钟。这个过程由芯片内部ROM 代码自动完成,所以从用户的角度,芯片就是从ARM 开始启动的。
ARM Bootloader 读取BOOTCFG 寄存器,获取用户设定的启动模式,然后从相应的外设搬移用户代码到相应的地址,跳转到用户代码的入口地址,完成SOC 的启动过程。
下面这段话未找到出处文档
When the device is powered on, it starts execution from DSP ROM. ARM is in in the disable state at this moment.
The DSP ROM code will Read certain registers to know that ARM is present. (Otherwise it will be a DSP-only boot) Program PDSP0 to prepare for ARM reset vector.
Bring ARM out of reset and let ARM starts execution from its ROM. (Yes, that’s the main difference from OMAPL137 silicon 1.x. ARM has its own ROM and will master the boot process afterwards)
DSP stills in the idle loop.
ARM starts execution from its ROM. It will Put DSP into disable state (probably local reset).
Initialize HW, i.e. PSC, PLL, external memory etc. Read bootcfg registers to decide what boot mode it will be and load and run ARM UBL from appropriate boot media, i.e. SPI flash, NAND, NOR etc.
ARM UBL starts running. Its behavior is totally defined by the SW. For example, TI provided ARM UBL will load and run UBOOT which will further load and run Linux. The Linux application can load and run DSP.out via DSPLINK. This model is the same as DaVinci model.
Industrial customers can choose to load and run a DSP AIS image in certain boot media. Meanwhile ARM UBL also starts booting UBOOT and Linux. In that way, DSP can start processing data before Linux finishing boot.
#主要内存地址的Memory Map
ARM可以访问全部地址空间,DSP不能访问ARM的内存空间。
Section | Size | Start | End |
---|---|---|---|
ARM Local ROM | 64K | 0xFFFD 0000 | 0xFFFD FFFF |
ARM Interrupt Control | 8K | 0xFFFE E000 | 0xFFFE FFFF |
ARM Local RAM | 8K | 0xFFFF 0000 | 0xFFFF 1FFFF |
Section | Size | Start | End |
---|---|---|---|
EMIFA SDRAM data (CS0) | 512M | 0x4000 0000 | 0x5FFF FFFF |
EMIFA async data (CS1) | 32M | 0x6000 0000 | 0x61FF FFFF |
EMIFA async data (CS2) | 32M | 0x6000 0000 | 0x61FF FFFF |
EMIFA async data (CS3) | 32M | 0x6200 0000 | 0x63FF FFFF |
EMIFA async data (CS4) | 32M | 0x6400 0000 | 0x65FF FFFF |
EMIFA async data (CS5) | 32M | 0x6600 0000 | 0x67FF FFFF |
EMIFA Control Regs | 32K | 0x6800 0000 | 0x6800 7FFF |
Shared Memory | 128K | 0x8000 0000 | 0x8001 FFFF |
DDR2/mDDR Control Regs | 32K | 0xB000 0000 | 0xB000 7FFF |
DDR2/mDDR Data | 256M | 0xC000 0000 | 0xCFFF FFFF |
#ARM启动DSP的步骤
OMAPL138的Bootloader启动ARM后(138是DSP先启动,实际上在Bootloader中是先启动DSP,DSP唤醒ARM后,DSP转入IDLE,所以从用户的角度来看,相当于ARM先启动),DSP由ARM负责启动。
DSP启动前,需要确保其处于Local Reset状态,ARM则要处于Supervisor模式下:
在系统运行过程中,ARM 可以控制DSP 的复位,重新加载新的DSP 应用代码,以实现不同的功能。
ARM与DSP还可以通过AIS文件实现同时加载,具体参见这里第6章。
#启动失败的常规检查方法
不加载gel文件,连接仿真器后检查
#ARM用户程序的一般流程
ARM用户程序不像DSP用户程序一样与C/C++类似,只需要配置好中断向量表和主函数main就行了,ARM用户程序需要进行一些特殊设置。
##ARM的“模式”
ARM有7中基本模式,在每种模式下使用各自独立的栈,和一部分各自独立的寄存器,有些操作只有在**Privileged(特权)**模式下才能执行。
7种模式分别为
模式 | 描述 | Privileged mode? | 编码 |
---|---|---|---|
Supervisor(SVC) | Reset后进入,或者执行SWI (Software Interrupt) | Yes | 10011 |
FIQ | 高优先级(快)中断被触发 | Yes | 10001 |
IRQ | 低优先级(普通)中断被触发 | Yes | 10010 |
Abort | 用来处理非法的内存访问 | Yes | 10111 |
Undef | 用来处理未定义的指令 | Yes | 11011 |
System | 与User模式使用同一组寄存器,区别只是System模式为Privileged mode | Yes | 11111 |
User | 执行一般程序、OS任务 | No | 10000 |
模式的区别和作用
Most application programs execute in User mode. When the processor is in User mode, the program being executed is unable to access some protected system resources or to change mode, other than by causing an exception to occur (see Exceptions on page A2-16). This allows a suitably-written operating system to control the use of system resources. The modes other than User mode are known as privileged modes. They have full access to system resources and can change mode freely. Five of them are known as exception modes:
- FIQ
- IRQ
- Supervisor
- Abort
- Undefined.
These are entered when specific exceptions occur. Each of them has some additional registers to avoid corrupting User mode state when the exception occurs (see Registers on page A2-4 for details).
中文简单的解释可以看这个。
###模式切换
####寄存器
ARM共有37个寄存器,其中有6个状态寄存器PSR (Programme Status Register) ,在模式间切换需要用到两组重要的PSR寄存器——C(urrent)PSR和S(aved)PSR,PSR共有6个,分别为CPSR、SPSR_svc、SPRS_abt、SPSR_und、SPSR_irq和SPSR_fiq。
处理器在所有模式下均可访问CPSR,SPSR用来在进入异常^Exception of ARM前保存CPSR的状态,以便从异常返回时回复异常前的工作状态。
几个特殊寄存器:
PSR的定义:
31 N | 30 Z | 29 C | 28 V | 27:9 | 8 I | 7 F | 6 T | 4:0 Mode |
---|---|---|---|---|---|---|---|---|
Negative or less than | Zero | Carry or borrow | Overflow or underflow | Reserved | IRQ disabled(1), enabled(0) | FIQ disabled(1), enabled(0) | Thumb mode(1), ARM mode(0) | Supervisor(SVC) 10011, FIQ 10001, IRQ 10010, Abort 10111, Undef 11011, System 11111, User 10000 |
其中31:28 NZCV为最近一次ALU操作的结果,具体含义为:
####常用汇编指令
关于汇编指令的详细信息,参考《ARM® Compiler armasm User Guide》。
label和mnemonic
label是汇编中符号(symbol)的标识符,表示符号在程序中的地址。
mnemoic是汇编指令的助记符,mnemoic不能在每一行顶格开始写,这样会被认为是定义label,任何非 ‘.’ (点)开头的字符串均是合法的label定义伪指令,即mnemonic之前必须要有空格。
#####MRS
MRS有两种指令:
MRS{cond} Rd, psr
cond 条件[^Condition usage]
[^Condition usage]: 条件语句的语法,以跳转指令B为例,BNE label
表示ALU的上一次计算结果不等于0则跳转到label。
Rd 目标寄存器
psr CPSR或者SPSR。在User和System模式下不要操作SPSR。
cond条件标志:
Code | Suffix | Description | Flags |
---|---|---|---|
0000 | EQ | Equal / equals zero | Z |
0001 | NE | Not equal | !Z |
0010 | CS / HS | Carry set / unsigend higher or same | C |
0011 | CC / LO | Carry clear / unsigned lower | !C |
0100 | MI | Minus / negative | N |
0101 | PL | Plus / positive or zero | !N |
0110 | VS | Overflow | V |
0111 | VC | No overflow | !V |
1000 | HI | Unsigned higher | C and !Z |
1001 | LS | Unsigned lower or same | !C or Z |
1010 | GE | Signed greater than or equal | N == V |
1011 | LT | Signed less than | N != V |
1100 | GT | Signed greater than | !Z and (N == V) |
1101 | LE | Signed less than or equal | Z or (N != V) |
1110 | AL | Always(default) | any |
#####BIC (bit clear)
BIC{S}{cond} Rd, Rn, Operand2
S 计算结束后更新条件标志;
Rd 目标寄存器;
Rn 保存第一个操作数的寄存器
Operand2 第二个操作数
BIC指令对Rn和Operand2的补码进行AND操作,例如: BIC R0, R0, #0x1F ;清除R0寄存器的4:0位。
#####ORR (logical OR)
ORR{S}{cond} Rd, Rn, Operand2
与BIC类似,ORR指令对Rn和Operand2进行OR操作。
#####MSR (general-purpose register to PSR)
与MRS的功能相反。
MSR{cond} PSR_flags, Rm
PSR CPSR或SPSR
flags cxsf中的一个或多个。
CPSR_f、CPSR_x、CPSR_s和CPSR_c通过掩码方式分别表示CPSR的四个域:
f: 标志域掩码(PSR [31:24])
s: 状态域掩码(PSR[23:16])
x: 扩展域掩码(PSR[15:8])
c: 控制域掩码(PSR[7:0])
Rm 源寄存器,不能使用PC。
#####LDR (load register)
将label所指的内容放入寄存器。
LDR{type}{cond}{.W} Rt, label
Rt 目标寄存器
label 标识符
#####ADD (add without carry) 和 SUB (subtract without carry)
ADD{cond} {Rd}, Rn, Operand2
SUB{cond} {Rd}, Rn, Operand2
Rd 目标寄存器
Rn 存有第一个操作数的寄存器
Operand2 第二个操作数
#####LDM (load multiple register) 和 STM (store multiple register)
LDM{addr_mode}{cond} Rn{!}, reglist{^}
STM{addr_mode}{cond} Rn{!}, reglist{^}
addr_mode 见 ARM926EJ-S Technical Reference Manual
Rn 基址寄存器 (base register) ,代表开始传输的起始地址,不能是PC;
! 回写后缀,最终运算结果会写回到Rn中:
例如:LDM SP!, {R0-R2, R3} 结果:R0 = *SP R1 = *(SP + 4) R2 = *(SP + 8) R3 = *(SP + 12) SP = SP + 12
reglist 寄存器列表,可以操作R0至R15
^ A32[^A32 and T32]模式下可用的后缀,并且不能在 User 和 System
模式下使用。表示保存或读取到User模式的寄存器而不是当前模式的寄存器。
[^A32 and T32]: A32 (ARM) 指令集全部为32位指令,T32 (Thumb) 指令集为32位指令和某些架构下16位指令的混合指令集。T32模式下,能够提高代码密度,但会牺牲一些性能。T32指令功能与A32指令几乎完全相同,但是在某些指令中,T32模式会有额外的限制,具体参考ARM926EJ-S Technical Reference Manual。
可以使用STM和LDM来实现压栈和弹栈操作 此时Rn一般为SP,配合面向堆栈的后缀 (Stack-oriented suffixes) 实现压栈和弹栈的操作。 4种 Stack-oriented suffixes 分别为
其中:
Full 表示 SP 在传递前指向栈中最后一个有数据的地址 (full stack) ;
Empty 表示 SP 在传递前指向栈中第一个空闲的地址 (empty stack) ;
Descending 表示堆栈地址由高到低向下生长;
Ascending 表示堆栈地址由低到高向上生长 。
##IRQ与FIQ配置
如果需要使用 IRQ 和 FIQ,则 ARM 不能工作在 User 模式下,一般选择工作在 System 模式。在有操作系统的时候,System 模式用来执行操作系统的底层功能,User 模式用来执行操作系统的任务。
ARM 上电后首先进入 Supervisor 模式,执行 _c_int00
3 后会进入 User 模式。Privileged 模下式可以通过直接操作CPSR切换为其他模式,但进入 User 模式后,如果想切换成别的模式,是不能通过操作CPSR完成的。
在 User 模式下切换模式可以通过调用编译器原语 (Compiler intrinsic) _call_swi()
进入 Supervisor 模式,再切换到 FIQ 和 IRQ 模式,配置其各自的栈区,之后使能PSR中的中断标志。
###ARM的中断机制
和 DSP 的多中断机制不同,ARM 内核只有两个中断—— FIQ 和 IRQ,也被称为宿主中断 (host interrupts)。
ARM 通过中断通道 (channel) 的方式,可以响应101种中断源的中断信息,这101种中断源称为系统中断 (system interrupts)。
101个中断源中:
中断0和1为 JTAG Debug 用的;
中断2为 NINT 不可屏蔽中断,相当于DSP的 NMI;
中断3到10为 PRU 中断。
因此实际 ARM 上可用的外设中断源为91个。
通道0和1通过硬连接 (hard-wired) 映射到 FIQ 上,通道2到31映射到 IRQ 上。通道0的优先级最高,通道31的优先级最低。
###中断映射与执行
####C语言
初始化中断配置之前,调用以下 intrinsics 禁用中断
_disable_interrupts();
或
_disable_IRQ();
_disable_FIQ();
在 AINTC->VBR
中配置系统中断服务函数 (Interrupt Service Routine, ISR) 向量表地址。System ISR 向量表可以是C语言的函数指针列表,这个函数列表是中断向量化的重要组成部分,当一个中断被触发时,AINTC->GPVR
中保存了当前处于挂起 (Pending) 状态的优先级最高的 System Interrupt ISR 的地址,因此在 Host ISR 中,不需要查询再跳转到 System ISR,只需要跳转到 AINTC->GPVR
中的地址开始执行就可以了,这种机制被称为中断向量化 (Interrupt Vectorization)。
static void (*AIntC0IsrTable[IRQ_END_OF_INTERRUPTS])(void);
AINTC->VBR = (unsigned int) AIntC0IsrTable;
此时每一个指针占用一个字 (32-bit word),因此相应的配置 AINTC->VSR
为0,表示中断服务函数长度为4字节。
AINTC->CMR[n]
共有26个(0到25),每个寄存器对应4个 System Interrupt,寄存器内容为要映射到的通道号,如要将中断58 USB0_INT 映射到通道3的代码为
AINTC->CMR[14] = 3 << 16; // CMR[14]的4个字节由高到低对应中断 59 58 57 56
AINTC->ESR1 = 1 << 26; // 使能 System Interrupt 58
_enable_FIQ();
_enable_IRQ();
####Assembly
#####Host ISR 映射
在ARM中,某一个异常被激发后,PC指针会跳转到固定的地址,执行 Host ISR。
异常 | 地址 |
---|---|
Reset Interrupt | 0xFFFF0000 |
Undefined Instructions Interrupt | 0xFFFF0004 |
Software Interrupt | 0xFFFF0008 |
Prefetch Abort Interrupt | 0xFFFF000C |
Data Abort Interrupt | 0xFFFF0010 |
Reserved For Future Expansion | 0xFFFF0014 |
IRQ Interrupt | 0xFFFF0018 |
FIQ Interrupt | 0xFFFF001C |
其中 0xFFFF0000 为 OMAPL138 ARM 片上 RAM 的起始地址,因此在系统初始化时,需要将异常处理函数的写入上述地址:
;****************************************************************************
;* MAP EXCEPTION HANDLER TO CORRESPONDING EXCEPTION ADDRESS
;****************************************************************************
.sect ".intvecs" ; 定义一个初始化的段 .intvecs
INT_VECS: ; EXCEPTION ADDRESS
LDR PC, __c_int00 ;Reset Interrupt (0xFFFF0000)
.word 0 ;Undefined Instructions Interrupt (0xFFFF0004)
LDR PC, __SWI_handler ;Software Interrupt (0xFFFF0008)
.word 0 ;Prefetch Abort Interrupt (0xFFFF000C)
.word 0 ;Data Abort Interrupt (0xFFFF0010)
.word 0 ;Reserved For Future Expansion (0xFFFF0014)
LDR PC, __IRQ_handler ;IRQ Interrupt (0xFFFF0018)
LDR PC, __FIQ_handler ;FIQ Interrupt (0xFFFF001C)
在cmd文件中
--retain INTVECT[^retain] // 如果使用EABI (ELF)格式,需要这句话
MEMORY
{
ARMRAM: o = 0xFFFF0000 l = 0x00002000
ARM_DDR2: o = 0xC0000000 l = 0x04000000 // 64MB
}
SECTIONS
{
.intvecs > ARMRAM /* Interrupt Vectors */
}
关于
--retain
编译选项使用EABI (ELF) 格式编译的情况下,默认不会编译未使用的符号,对于整个程序来说,没有显示调用
INT_VECS
的地方,因此对于编译器会将INT_VECS
识别为未使用的符号,这样就会导致最终的可执行程序中没有这一段代码,表现为程序跑飞。
#####Host ISR
以 IRQ 中断为例
;****************************************************************************
;* FUNCTION DEF: IRQ_handler
;****************************************************************************
IRQ_handler:.asmfunc ; .asmfunc 与 编译选项 -g (--symdebug:dwarf) 配合使用,像C/C++调试一样调试汇编
STMFD SP!, {R0-R12, R14} ; 压栈,保护之前的模式现场
SUB SP, SP, #0x4 ; 调整栈顶指针,指向 empty stack 应该可以用 STMED 代替 STMFD 和 SUB 这两句
LDR R0, _AINTC_GPVR_ADDR ; 将 GPVR 的地址读入 R0
LDR R1, [R0] ; 将 GPVR 中的内容,即当前待处理的中断里,优先级最高的中断 ISR 的地址读入 R1
ADD R14, PC, #0x0 ; 保存 PC 到 LR 中
; 实际存入 LR 的 PC 数值是 debug 时看到的 PC 值加8,即 PC = PC + 8 bytes,原因见下方说明
LDR PC, [R1] ; 执行 System ISR
; System ISR执行完毕后执行下一行汇编指令
ADD SP, SP, #0x4 ; 调整栈顶指针,指向 full stack
LDMFD SP!, {R0-R12, R14} ; 弹栈,恢复现场
SUBS PC, R14, #0x4 ; 返回之前的状态,R14-4表示原现场执行完ADD R14, PC, #0x0 后接下来要执行的指令
.endasmfunc
关于 PC = PC + 8
ARM7(ARMv3 或 ARMv4架构,冯诺依曼结构内核)是三级流水,FETCH - DECODE - EXECUTE。PC 始终指向取指的地址,而不是运行的地址。
因此,当ADD R14, PC, #0x0
被执行时,PC 已经指向了ADD SP, SP, #0x4
,所以执行后有 PC = PC + 8。
在ARM9(ARMv4或ARMv5架构,哈佛结构内核)是五级流水,FETCH - DECODE - EXECUTE - MEMORY - WRITE,但仍然是 PC = PC + 8,因为EXECUTE所处的流水线深度和ARM7是一样的,都是(从1起)第3级,所以与ARM7的三级流水情况一样。
##设定程序入口(Entry Point)
DSP L2 ROM是只读的。 ↩︎
连接仿真器的时候,程序没有任何问题也可能跑飞。 ↩︎
这个函数在 RTS 库里,源文件在 CCS 编译器目录下的 boot.asm。 ↩︎