LPC2000 启动代码Start.s文件简要分析
先说一下启动代码的位置,启动代码是在板子加电后首先执行的。所以非要用汇编来写才行。要完成处理器模式的初始化、设置中断向量表、设置各个模式下的堆栈、初始某些变量从而把系统带到一个合适的运行环境中开始用户程序的运行。
;
-------------------------------------------------------------------------------
;
;
本段设置处理器的模式相关常量
;
;
-------------------------------------------------------------------------------
;
Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs
;
定义常量,处理器的几种工作模式。
;
ARM 有7种工作模式:
;
;
User: 非特权模式,大部分任务执行在这种模式正常程序执行的模式
;
FIQ: 当一个高优先级(fast)中断产生时将会进入这种模式 高速数据传输和通道处理
;
IRQ: 当一个低优先级(normal)中断产生时将会进入这种模式 通常的中断处理
;
SVC: 用于操作系统的保护模式
;
Abort: 当存取异常时将会进入这种模式虚拟存储及存储保护
;
Undef: 当执行未定义指令时会进入这种模式 软件仿真硬件协处理器
;
System: 使用和User模式相同寄存器集的特权模式
;
;
这个值将被写到CPSR寄存器的第0,1,2,3,4,5位,以表示当前的状态。
Mode_USREQU0x10
Mode_FIQEQU0x11
Mode_IRQEQU0x12
Mode_SVCEQU0x13
Mode_ABTEQU0x17
Mode_UNDEQU0x1B
Mode_SYSEQU0x1F
;
设置IRQ和FIQ中断禁止位,这两个值将被写到CPSR寄存器的第6位(FIQ)和第7位(IRQ)。1是禁止,0是允许。
I_BitEQU0x80;
when I bit is set, IRQ is disabled
F_BitEQU0x40;
when F bit is set, FIQ is disabled
;
---------------------------------------------------------------------------------------------------
;
本段将完成堆栈相关的常量,
;
;
---------------------------------------------------------------------------------------------------
;
;
设置各个模式下的栈大小
;
UND_Stack_Size EQU0x00000000
SVC_Stack_Size EQU0x00000008
ABT_Stack_Size EQU0x00000000
FIQ_Stack_Size EQU0x00000000
IRQ_Stack_Size EQU0x00000080
USR_Stack_Size EQU0x00000400
ISR_Stack_Size EQU(UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size)
;
初始化栈空间
;
定义一个数据段,名为STACK,NOINIT - 仅仅保留内存单元,还没有写入值,可读写,ALIGN=3 - 按字节对齐。
AREASTACK, NOINIT, READWRITE, ALIGN=3
;
分配内存,用户模式栈名为Stack_Mem,大小为1024字节;异常模式堆栈__initial_sp 大小为128+8字节。
Stack_MemSPACEUSR_Stack_Size
__initial_spSPACEISR_Stack_Size
Stack_Top
;
Heap_SizeEQU0x00000000
;
堆空间,其中__heap_base 与__heap_limit 这两个符号是给采用了MICROLIB的程序准备的。
AREAHEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_MemSPACEHeap_Size
__heap_limit
;
-------------------------------------------------------------------------------------------
;
;
初始化 VPB总线的频率,Start的时候将分频因子设置为1,表示与CPU CLOCK相同
;
-------------------------------------------------------------------------------------------
;
VPBDIV 是VPB总线的分频因子
;
;
VPBDIV definitions
VPBDIVEQU0xE01FC100;
VPBDIV Address
【LPC2000 启动代码Start.s文件简要分析】
VPBDIV_SETUPEQU1
VPBDIV_ValEQU0x00000000
;
-------------------------------------------------------------------------------------------
;
定义与锁相环相关的寄存器
;
Phase Locked Loop (PLL) definitions
;
-------------------------------------------------------------------------------------------
;
定义 PLL相关寄存器的基地址
PLL_BASEEQU0xE01FC080;
PLL Base Address
;
用偏移量的方法定义其他寄存器
PLLCON_OFSEQU0x00;
PLL Control Offset ;
控制寄存器,写入该寄存器的值在馈送序列执行前不起作用
PLLCFG_OFSEQU0x04;
PLL Configuration Offset ;
配置寄存器,属性同上
PLLSTAT_OFSEQU0x08;
PLL Status Offset ;
状态寄存器,读取状态
PLLFEED_OFSEQU0x0C;
PLL Feed Offset ;
馈送寄存器,使能装载PLL控制和配置信息
PLLCON_PLLEEQU(1<<0);
PLL Enable;
PLL使能,写入到配置寄存器的第0位,
;
初始为0,表示没有激活
PLLCON_PLLCEQU(1<<1);
PLL Connect;
PLL连接使能位 ,写入到配置寄存器的第1位,当PLLC和
;
PLLE都为1,且在有效的PLLE馈送后,将PLLE作为时钟源接
;
入Core,否则Core直接使用振荡器时钟。
PLLCFG_MSELEQU(0x1F<<0);
PLL Multiplier ;
PLL 倍频因子
PLLCFG_PSELEQU(0x03<<5);
PLL Divider;
PLLE 分频因子
PLLSTAT_PLOCKEQU(1<<10);
PLL Lock Status ;
PLL锁定状态位,应该是从PLLSTAT寄存器的第10位读取数值
;
然后与 PLLSTAT_PLOCK 相比较,相同则为PLL锁定状态。
PLL_SETUPEQU1
PLLCFG_ValEQU0x00000024 ;
表示向CFG配置寄存器写入的值为0 01 00100,跟系统所需的频率有关。
;
LPC2119的主频最高是60MHz,振荡器的主频是10MHz
;
分频值为1,倍频值为4,所以主频为40Mhz。
;
-------------------------------------------------------------------------------------
;
存储器加速模块
;
;
-------------------------------------------------------------------------------------
;
Memory Accelerator Module (MAM) definitions
MAM_BASEEQU0xE01FC000;
MAM Base Address
MAMCR_OFSEQU0x00;
MAM Control Offset
MAMTIM_OFSEQU0x04;
MAM Timing Offset
MAM_SETUPEQU1
MAMCR_ValEQU0x00000002
MAMTIM_ValEQU0x00000004
;
---------------------------------------------------------------------------
;
外部存储器扩展
;
;
---------------------------------------------------------------------------
;
External Memory Controller (EMC) definitions
EMC_BASEEQU0xFFE00000;
EMC Base Address
BCFG0_OFSEQU0x00;
BCFG0 Offset
BCFG1_OFSEQU0x04;
BCFG1 Offset
BCFG2_OFSEQU0x08;
BCFG2 Offset
BCFG3_OFSEQU0x0C;
BCFG3 Offset
;
//
EMC_SETUPEQU0
BCFG0_SETUP EQU0
BCFG0_ValEQU0x0000FBEF
BCFG1_SETUP EQU0
BCFG1_ValEQU0x0000FBEF
BCFG2_SETUP EQU0
BCFG2_ValEQU0x0000FBEF
BCFG3_SETUP EQU0
BCFG3_ValEQU0x0000FBEF
;
//
;
External Memory Pins definitions
PINSEL2EQU0xE002C014;
PINSEL2 Address
PINSEL2_ValEQU0x0E6149E4;
CS0..3, OE, WE, BLS0..3,
;
D0..31, A2..23, JTAG Pins
PRESERVE8 ;
汇编伪指令,堆栈8字节对准
;
-------------------------------------------------------------------------------
;
前面都是对寄存器位置的声明,以下是具体代码。定义了一个代码段
;
;
-------------------------------------------------------------------------------
;
;
Area Definition and Entry Point
;
Startup Code must be linked first at Address at which it expects to run.
;
定义个名为RESET的代码段
AREARESET, CODE, READONLY
ARM
;
中断向量表的异常入口共8条语句32字节,在链接的时候保证在0地址处
VectorsLDRPC, Reset_Addr;
将Reset_Addr加载到PC中,程序就跳到reset_addr所指位置。
LDRPC, Undef_Addr
LDRPC, SWI_Addr
LDRPC, PAbt_Addr
LDRPC, DAbt_Addr
NOP;
Reserved Vector
;
LDRPC, IRQ_Addr
LDRPC, [PC, #-0x0FF0];
Vector from VicVectAddr
LDRPC, FIQ_Addr
Reset_AddrDCDReset_Handler ;
分配一段字内存单元给程序段Reset_Handler
Undef_AddrDCDUndef_Handler
SWI_AddrDCDSWI_Handler
PAbt_AddrDCDPAbt_Handler
DAbt_AddrDCDDAbt_Handler
DCD0;
Reserved Address
IRQ_AddrDCDIRQ_Handler
FIQ_AddrDCDFIQ_Handler
;
Undef_HandlerBUndef_Handler
SWI_HandlerBSWI_Handler
PAbt_HandlerBPAbt_Handler
DAbt_HandlerBDAbt_Handler
IRQ_HandlerBIRQ_Handler
FIQ_HandlerBFIQ_Handler
;
-----------------------------------------------------------------------------------
;
;
;
-----------------------------------------------------------------------------------
;
Reset Handler
EXPORT Reset_Handler
Reset_Handler
;
设置扩展存储的引脚
;
Setup External Memory Pins
IF:DEF:EXTERNAL_MODE ;
判断 EXTERNAL_MODE 是否定义,如果定义了就设置响应的管脚。见某论坛上的一个人问,外部存储器设置了但是不能工作,就是这里的 EXTERNAL_MODE 没有被定义导致扩展 存储的引脚的代码没有被编译所以没有被初始化,EXTERNAL_MODE EQU 1,应该就可以了 ,还没测试。
LDRR0, =PINSEL2
LDRR1, =PINSEL2_Val ;
前面 PINSEL2_Val 被初始化为0x0E6149E4,完成一系列管脚的初始化
STRR1, [R0]
ENDIF
;
设置扩展存储器的控制寄存器,分别检测几个bank是否存在,分别进行设置
;
Setup External Memory Controller
IFEMC_SETUP <> 0
LDRR0, =EMC_BASE
IFBCFG0_SETUP <> 0
LDRR1, =BCFG0_Val
STRR1, [R0, #BCFG0_OFS]
ENDIF
IFBCFG1_SETUP <> 0
LDRR1, =BCFG1_Val
STRR1, [R0, #BCFG1_OFS]
ENDIF
IFBCFG2_SETUP <> 0
LDRR1, =BCFG2_Val
STRR1, [R0, #BCFG2_OFS]
ENDIF
IFBCFG3_SETUP <> 0
LDRR1, =BCFG3_Val
STRR1, [R0, #BCFG3_OFS]
ENDIF
ENDIF;
EMC_SETUP
;
设置VPB总线的分频因子,这里分频因子被设置为0
;
Setup VPBDIV
IFVPBDIV_SETUP <> 0
LDRR0, =VPBDIV
LDRR1, =VPBDIV_Val
STRR1, [R0]
ENDIF
;
设置锁相环
;
Setup PLL
IFPLL_SETUP <> 0
LDRR0, =PLL_BASE
MOVR1, #0xAA
MOVR2, #0x55
;
Configure and Enable PLL
MOVR3, #PLLCFG_Val
STRR3, [R0, #PLLCFG_OFS];
PLLCFG_Val = 0x00000024
MOVR3, #PLLCON_PLLE;
R3 = 1
STRR3, [R0, #PLLCON_OFS];
PLLCON = 1,PLL 使能
STRR1, [R0, #PLLFEED_OFS];
写 馈送寄存器命令序列一次是10101010一次01010101,0xAA,0x55。规定的。
STRR2, [R0, #PLLFEED_OFS]
;
等待PLL稳定
;
Wait until PLL Locked
PLL_LoopLDRR3, [R0, #PLLSTAT_OFS];
将状态寄存器装载到R3中
ANDSR3, R3, #PLLSTAT_PLOCK;
PLLSTAT_OFS 与1<<10 进行与且影响 CPSR 的Z位,
BEQPLL_Loop;
Z位为1 跳到PLL_Loop
;
Switch to PLL Clock
MOVR3, #(PLLCON_PLLE:OR:PLLCON_PLLC)
STRR3, [R0, #PLLCON_OFS]
STRR1, [R0, #PLLFEED_OFS]
STRR2, [R0, #PLLFEED_OFS]
ENDIF;
PLL_SETUP
;
-----------------------------------------------------------------------------
;
存储器加速模块
;
Setup MAM
IFMAM_SETUP <> 0
LDRR0, =MAM_BASE
MOVR1, #MAMTIM_Val
STRR1, [R0, #MAMTIM_OFS]
MOVR1, #MAMCR_Val
STRR1, [R0, #MAMCR_OFS]
ENDIF;
MAM_SETUP
;
--------------------------------------------------------------------------
;
内存映射,结构有点复杂额,总之就是根据不同的情况去设置存储器映射控制寄存器,
;
00 Boot装载,中断向量从BootBlock重新映射
;
01 用户FLASH模式,中断向量不重新映射,就在FLASH里面
;
10 用户RAM模式,中断向量从静态RAM重新映射
;
11 用户外部存储器模式,中断向量从外部存储器重新映射。
;
Memory Mapping (when Interrupt Vectors are in RAM)
;
----------------------------------------------------------------------------
MEMMAPEQU0xE01FC040;
Memory Mapping Control ;
IF:DEF:REMAP
LDRR0, =MEMMAP
IF:DEF:EXTMEM_MODE ;
判断是不是扩展模式 是就往R1里写3
MOVR1, #3
ELIF:DEF:RAM_MODE ;
如果是RAM中,往R1里写2
MOVR1, #2
ELSE
MOVR1, #1;
否则R1里写1,唉汇编的编译判断真够烦的。
ENDIF
STRR1, [R0] ;
写到MEMMAP寄存器。
ENDIF
;
Initialise Interrupt System
;
...
;
为处理器的每种处理模式初始化栈空间
;
Setup Stack for each mode
LDRR0, =Stack_Top
;
Enter Undefined Instruction Mode and set its Stack Pointer
MSRCPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit ;
CPSR_c = 0x1B|0x80|0x40 = 意思为禁止IRQ和FIQ,ARM状态,处理
器进入了未定义模式
MOVSP, R0;
栈指针指向了Stack_Top
SUBR0, R0, #UND_Stack_Size;
Stack_Top = Stack_Top - ABT_Stack_Size
;
Enter Abort Mode and set its Stack Pointer
MSRCPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOVSP, R0
SUBR0, R0, #ABT_Stack_Size
;
Enter FIQ Mode and set its Stack Pointer
MSRCPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOVSP, R0
SUBR0, R0, #FIQ_Stack_Size
;
Enter IRQ Mode and set its Stack Pointer
MSRCPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOVSP, R0
SUBR0, R0, #IRQ_Stack_Size
;
Enter Supervisor Mode and set its Stack Pointer
MSRCPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOVSP, R0
SUBR0, R0, #SVC_Stack_Size
;
进入用户模式,并且设置栈空间指针
;
Enter User Mode and set its Stack Pointer
MSRCPSR_c, #Mode_USR
IF:DEF:__MICROLIB;
支持 MICROLIB库,如果C程序的运行库是MICROLIB 则将__initial_sp 导出
EXPORT __initial_sp
ELSE
MOVSP, R0
SUBSL, SP, #USR_Stack_Size
ENDIF
;
进入C代码的执行,为C代码的执行创造一个运行环境
;
Enter the C code
IMPORT __main;
导入外部符号
LDRR0, =__main ;
将main的地址加载到r0中
BXR0;
带状态的跳到main 去执行
IF:DEF:__MICROLIB ;
还是有关使用MICROLIB库的,如果使用了该库就要导出下面连个符号。
EXPORT __heap_base
EXPORT __heap_limit
ELSE
;
User Initial Stack & Heap
AREA|.text|, CODE, READONLY
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDRR0, = Heap_Mem
LDRR1, =(Stack_Mem + USR_Stack_Size)
LDRR2, = (Heap_Mem +Heap_Size)
LDRR3, = Stack_Mem
BXLR
ENDIF
END
一直对底层的东西不太懂,总是朦朦胧胧的感觉,索性拿启动代码看一下吧,指望着能学一下汇编。周末的学习效率是在太低了,一个小小的启动文件竟然费了两天功夫。费了牛劲了看是看完了,还是有好多地方一头雾水。因为认识的深度有限,错误的地方肯定少不了,所以还请您校正。
推荐阅读
- CVE-2020-16898|CVE-2020-16898 TCP/IP远程代码执行漏洞
- Hive常见问题汇总
- 不废话,代码实践带你掌握|不废话,代码实践带你掌握 强缓存、协商缓存!
- 工具|后天就是七夕节,你准备好了吗(送上几个七夕代码,展示你技能的时候到了!)
- 注册分销商的骄傲
- 《机器学习实战》高清中文版PDF英文版PDF+源代码下载
- 霍兰德职业代码对照表
- 如何启动改变
- Hexo代码块前后空白行问题
- spring|spring boot项目启动websocket