您的位置 首页 数字

周建功lpc21xx/lpc22xx系列ARM7发动代码剖析

网上已经有人做了一个周立功lpc2000(ARM7TDMI)启动代码分析的文章,我本来想做一个s3c2410(ARM920T)的启动代码分析的,但是看来了一下241…

网上现已有人做了一个周建功lpc2000(ARM7TDMI)发动代码剖析的文章, 我原本想做一个s3c2410(ARM920T)的发动代码剖析的, 可是看来了一下2410的发动代码,发现有些东西还不是了解的很清楚, 我ARM9的经历比较少.

所以仍是做一个ARM7的发动代码剖析吧, 网上那一份比较,我这个首要重视startup.s文件.网上那个startup.s几乎是一笔带过的.

赤色符号的是源码.

SVC_STACK_LEGTH EQU 0

FIQ_STACK_LEGTH EQU 0

IRQ_STACK_LEGTH EQU 256

ABT_STACK_LEGTH EQU 0

UND_STACK_LEGTH EQU 0

NoInt EQU 0x80

USR32Mode EQU 0x10

SVC32Mode EQU 0x13

SYS32Mode EQU 0x1f

IRQ32Mode EQU 0x12

FIQ32Mode EQU 0x11

上面几行代码,不必过多剖析, 界说几个符号罢了, 把EQU想像成C中的#define就能够了. 具体界说的数值,下面的代码用到我再解说.

IMPORT __use_no_semihosting_swi

上面这一句的作用是在代码中禁用 semihosting 机制. 终究什么是semihostiong这儿不多说, 网上有许多. 这儿只阐明Semihosting首要用来调试, 在release版别的代码中一般是要禁用的.

IMPORT FIQ_Exception

IMPORT __main

IMPORT TargetResetInit

上面三行是把要引进的外部标号声明一下,以便下面运用.

EXPORT bottom_of_heap

EXPORT StackUsr

EXPORT Reset

EXPORT __user_initial_stackheap

上面四行是把要给其它文件运用的标号声明

AREA vectors,CODE,READONLY

ENTRY

上面这一行声明汇编文件的进口, 整个文件是从这儿开端履行的.

Reset

LDR PC, ResetAddr

LDR PC, UndefinedAddr

LDR PC, SWI_Addr

LDR PC, PrefetchAddr

LDR PC, DataAbortAddr

DCD 0xb9205f80

LDR PC, [PC, #-0xff0]

LDR PC, FIQ_Addr

上面几行是装备间断向量表. 间断向量表的次序是不能变的,由于这是ARM7规则的,能够参阅相关书本. 这儿有几个问题要阐明一下.

榜首, 关于DCD 0xb9205f80, 依照ARM7的间断向量表分布图, 这个方位是个保存位. 可是终究为什么要用0xb9205f80这个数值呢.

依据周建功的说法, nxp系列的lpc21xx,lpc22xx片子要求”间断向量表中所有数据32位累加和为0,不然程序不能脱机运转”, 我在AXD反汇编了一下(如下图),把间断向量表中的8个机器码累加了一下:0xe59ff018*6+0xe51ffff0+0xb9205f80,没错, 结果是零. 可是我遇到一个问题, 便是我在试验中,把0xb9205f80这个数值改成任何值,程序运转都没问题. 头大了, 这个问题待处理中……(期望高手看到了能够点拨一二).

第二,关于LDR PC, [PC, #-0xff0].这儿本应该放IRQ间断的, 为什么是这么一句话. 其实在我blog的其间一篇文章里有提到过这一点.

ARM7的三级流水线结构导致了PC指向的是当时指令的后8个字节. 原本IRQ是应该放在0x00000018处的. LDR PC, [PC, #-0xff0]这条句子履行后, PC的当时值便是0x00000018+8-0xff0. 很简单计算出它的结果是0xfffff030. 看一下lpc22xx的手册就知道. 这个地址便是VICVectAddr. 也便是说原本这个地址是应该放IRQ服务程序的进口地址的,可是这个地址被放在了VICVectAddr 这个寄存器里. 英文手册里有一段对V%&&&&&%VectAddr 描绘. 看了之后就简单理解是怎么回事了: Vector Address Register. When an IRQ interrupt occurs, the IRQ service routine can read this register and jump to the value read

ResetAddr DCD ResetInit

UndefinedAddr DCD Undefined

SWI_Addr DCD SoftwareInterrupt

PrefetchAddr DCD PrefetchAbort

DataAbortAddr DCD DataAbort

Nouse DCD 0

IRQ_Addr DCD 0

FIQ_Addr DCD FIQ_Handler

这几行是为上面间断向量表中的间断标号分配内存空间, 也便是它们的履行地址. 一开端我有个疑问, 为什么不直接用LDR PC, ResetInit,还要用DCD中转一下, 后来上网查了一下,才茅塞顿开, ldr指令中的地址有必要为当时指令地址是4KB范围内, 用DCD中转一下就能够在整个程序空间寻址.

Undefined

B Undefined

SoftwareInterrupt

B SoftwareInterrupt

PrefetchAbort

B PrefetchAbort

DataAbort

B DataAbort

FIQ_Handler

STMFD SP!, {R0-R3, LR}

BL FIQ_Exception

LDMFD SP!, {R0-R3, LR}

SUBS PC, LR, #4

这几行不必过多解说, 仅仅阐明上面几个反常怎么履行.

InitStack

MOV R0, LR

;设置管理形式仓库

MSR CPSR_c, #0xd3

LDR SP, StackSvc

;设置间断形式仓库

MSR CPSR_c, #0xd2

LDR SP, StackIrq

;设置快速间断形式仓库

MSR CPSR_c, #0xd1

LDR SP, StackFiq

;设置间断形式仓库

MSR CPSR_c, #0xd7

LDR SP, StackAbt

;设置未界说形式仓库

MSR CPSR_c, #0xdb

LDR SP, StackUnd

;设置体系形式仓库

MSR CPSR_c, #0xdf

LDR SP, =StackUsr

MOV PC, R0

上面是一个子函数, 函数名为InitStack. 顾名思意, 这个函数设置ARM七种作业形式下的仓库. 关于这一段代码有三点要说.

榜首,MSR CPSR_c, #0xdf, 这一句把ARM的作业形式设置为体系形式,或许也能够说是用户形式, 由于体系形式与用户形式是同享相同的寄存器组. 用0xdf对CPSR寄存器赋值,就把IRQ间断封闭了(能够查一下CRSR的具体阐明), 代码正常履行时处理器是处在用户形式的,所以IRQ间断是不会履行的. 所以,假如用周建功的这个发动代码,当你的程序中需求间断时,要把0xdf改成0x5f. 之前看到许多人在网上说用周建功的ADS工程模板,进不了间断,许多情况下是这个原因.

第二, 并不是每一种形式下的仓库都用设置的, 比如说假如你的程序中不会用到FIQ,就能够不必设置快速间断下的仓库.

第三, 留意LDR SP, =StackUsr这个句子, 其它都是没有=号的, 为什么这个要用等号呢? 这便是LDR伪指令与LDR指令的区别了,LDR SP, =StackUsr是把StackUsr表明的地址装载到sp,LDR SP, StackUnd是把StackUnd表明地址的内容装载到sp,留意下面几句

StackSvc DCD SvcStackSpace + (SVC_STACK_LEGTH – 1)* 4

StackIrq DCD IrqStackSpace + (IRQ_STACK_LEGTH – 1)* 4

StackFiq DCD FiqStackSpace + (FIQ_STACK_LEGTH – 1)* 4

StackAbt DCD AbtStackSpace + (ABT_STACK_LEGTH – 1)* 4

StackUnd DCD UndtStackSpace + (UND_STACK_LEGTH – 1)* 4

能够看到,没有”=”的标号都现已用DCD初始化了, 而StackUsr终究是什么呢, 它是由下面的句子决议的

(startup.s文件)

AREA Stacks, DATA, NOINIT

StackUsr

(涣散加载文件)

STACKS 0x40002000 UNINIT

{

Startup.o (Stacks)

}

这样就理解了, StackUsr肯定是0x40000000~0x400020000之间的某个数. 用户形式下的仓库空间便是它了.

ResetInit

BL InitStack

BL TargetResetInit

B __main

处理器上电复位后经过间断向量表进入该函数,__main函数首要作业是初始化C的库函数, 并由它进入C的main函数.

__user_initial_stackheap

LDR r0,=bottom_of_heap

; LDR r1,=StackUsr

MOV pc,lr

__user_initial_stackheap函数是ADS的一个库函数, 假如程序中用到的涣散加载文件, 这个函数有必要要被完成. 应用程序的栈和heap是在C库函数初始化过程中建立起来的。能够经过重定向对应的子程序来改动仓库和heap的方位. 仓库的地址在涣散加载文件里现已指定好,本函数不应该修正它们的值. 用r0,r1别离回来heap和stack的基址. 关于ADS的存储器机制我们能够去网上查更具体的材料.

StackSvc DCD SvcStackSpace + (SVC_STACK_LEGTH – 1)* 4

StackIrq DCD IrqStackSpace + (IRQ_STACK_LEGTH – 1)* 4

StackFiq DCD FiqStackSpace + (FIQ_STACK_LEGTH – 1)* 4

StackAbt DCD AbtStackSpace + (ABT_STACK_LEGTH – 1)* 4

StackUnd DCD UndtStackSpace + (UND_STACK_LEGTH – 1)* 4

AREA MyStacks, DATA, NOINIT, ALIGN=2

SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ;Stack spaces for Administration Mode

IrqStackSpace SPACE IRQ_STACK_LEGTH * 4 ;Stack spaces for Interrupt ReQuest Mode

FiqStackSpace SPACE FIQ_STACK_LEGTH * 4 ;Stack spaces for Fast Interrupt reQuest Mode

AbtStackSpace SPACE ABT_STACK_LEGTH * 4 ;Stack spaces for Suspend Mode

UndtStackSpace SPACE UND_STACK_LEGTH * 4 ;Stack spaces for Undefined Mode

上面几行代码是为各个形式下的仓库分配空间. 其间MyStacksA的方位会在涣散加载文件中指定.

IF :DEF: EN_CRP

IF . >= 0x1fc

INFO 1,”\nThe data at 0x000001fc must be 0x87654321.\nPlease delete some source before this line.”

ENDIF

CrpData

WHILE . < 0x1fc

NOP

WEND

CrpData1

DCD 0x87654321 ;/*When the Data is 为0x87654321,user code be protected. 当此数为0x87654321时,用户程序被维护 */

ENDIF

上面这几行其实是加密芯片用的, lpc21xx和lpc22xx系列的ARM7,当你的工程挑选RelInFlash时, 代码写进flash,芯片也一起被加密, 加密状态下JTAG也读不到芯片, 也不能单步调试, 要解密的话有必要要用ISP彻底擦除一下. 上面的代码的意思便是在地址0x1fc处放数据0x87654321, 然后完成加密的功用, 但条件是IF :DEF: EN_CRP, 也便是界说了EN_CPP这个宏. 而这个宏是在当挑选了RelInFlash时ADS主动界说的. 然后,再说一下0x87654321的问题. LPC2100 系列ARM7微控制器是世界首款可加密的ARM芯片,对其加密的办法是经过用户程序在指定地址上设置规则的数据。PHILIPS公司规则,关于 LPC2100芯片(除LPC2106/2105/2104外),当片内FLASH地址0x000001FC处的数据为0x87654321时,芯片即被加密. 所以问题搞定.

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/zhishi/shuzi/275722.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部