历史上的今天
返回首页

历史上的今天

今天是:2024年11月11日(星期一)

正在发生

2019年11月11日 | 几乎是每个arm程序必备的启动代码

2019-11-11 来源:51hei

启动代码是几乎是每个arm程序程序必备的,刚开始看的时候看别人的启动代码时感觉云里雾里,所以懒惰的想法浮现脑中:别人都写好了我还写什么,直接拿来用不就行了,对在我懂得情况下,我一定会拿来就用,但是现在我还不懂,一切就要从头开始,经过几天的努力,现在的感觉是启动代码不过如此 :) ,呵呵。 


;--------------------------------------------------------------------- 

;startup.s 

;系统启动代码 

;起始时间 : 2009.5.7 ----->2009.5.11 

;--------------------------------------------------------------------- 


;--------------------------------------------------------------------- 

GET ./Include/s3c2440.inc ;寄存器地址信息 

GET ./Include/memcfg.inc ;内存控制器配置信息 


;处理器模式 

USERMODE EQU  0x10 

FIQMODE EQU  0x11 

IRQMODE EQU  0x12 

SVCMODE EQU  0x13 

ABORTMODE EQU  0x17 

UNDEFMODE EQU  0x1b 

SYSMODE EQU 0x1f 

;相关掩码 

MODEMASK     EQU  0x1f 

NOINT EQU  0xc0 


;各个处理器模式下堆栈设置 

_STACK_BASEADDRESS EQU 0x33ff8000 ;BANK6 64MB顶部 

UserStack EQU (_STACK_BASEADDRESS-0x3800) ;0x33ff4800 ~ 

SVCStack EQU (_STACK_BASEADDRESS-0x2800) ;0x33ff5800 ~ 

UndefStack EQU (_STACK_BASEADDRESS-0x2400) ;0x33ff5c00 ~ 

AbortStack EQU (_STACK_BASEADDRESS-0x2000) ;0x33ff6000 ~ 

IRQStack EQU (_STACK_BASEADDRESS-0x1000) ;0x33ff7000 ~ 

FIQStack EQU (_STACK_BASEADDRESS-0x0) ;0x33ff8000 ~ 


;导入操作系统入口函数 

IMPORT OSEntry 


;导入外部C语言编写的异常与中断处理函数 

IMPORT vectorUNDEF 

IMPORT vectorSWI 

IMPORT vectorPABT 

IMPORT vectorDABT 

IMPORT vectorIRQ 

IMPORT vectorFIQ 


;导入镜像装载域段起始地址 

IMPORT  |Image$$RO$$Limit|  ; End of ROM code (=start of ROM data) 

IMPORT  |Image$$RW$$Base|   ; Base of RAM to initialise 

IMPORT  |Image$$ZI$$Base|   ; Base and limit of area 

IMPORT  |Image$$ZI$$Limit|  ; to zero initialise 

;-------------------------------------------------------------------- 



;------------------------------------------------------ 

AREA startup, CODE, READONLY 


ENTRY 


;系统向量表 


b vectorRESET ;复位向量 

b vectorUNDEF ;未定义指令 

b vectorSWI ;软中断 

b vectorPABT ;预取指终止 

b vectorDABT ;数据终止 

b . ;系统保留 

b vectorIRQ ;外部中断 

b vectorFIQ ;快速中断 

;------------------------------------------------------- 


;-------------------------------------------------------------------------- 

;复位向量 

;复位向量是ARM处理器上电后第一个被执行的异常 

;此时系统处理管理(SVC)模式 


vectorRESET 


;复位向量有以下六件事要做 


;第一步 : 关闭看门狗定时器屏蔽所有中断 

;第二步 : 配置系统时钟 

;第三步 : 配置内存控制器 

;第四步 : 配置每种处理器模式下堆栈指针 

;第五步 : 初始化镜像运行域 

;第六步 : 跳转到操作系统入口 



;------------------------------------------ 

;第一步 : 关闭看门狗定时器 

;具体内容请参看s3c2440a数据手册的第18章 

ldr r0, =WTCON 

ldr  r1, =0x0 

str  r1, [r0, #0x0] 


;屏蔽所有中断 

ldr r0, =INTMSK 

ldr r1, =0xffffffff 

str r1, [r0] 

;------------------------------------------ 




;------------------------------------------ 

;第二步 : 配置系统时钟 

;具体内容请看手册第7章 


;先减少锁相环锁定时间,s3c2440a要求PLL 

;锁定时间>300us,在上电时s3c2440a预设值 

;mpll为晶体频率,我用的晶体频率为12MHz 

;300us*12M = 3600设置LOCKTIME = 0xfff 

;足够了 

ldr r0, =LOCKTIME 

ldr r1, =0xfff0fff0 ;高16为对应UPLL 

;低16为对应MPLL 

str r1, [r0, #0x0] 


;根据器件手册我们还有以下几个事要做 

;step1.配置UPLL 

;step2.配置MPLL 

;注:手册要求先配置UPLL后MPLL 

; 且之间要间隔7NOP 

; 详请看手册第7-21. 

;step3.配置分频系数 


;step1: 

ldr r0, =UPLLCON 

ldr r1, =((56<<12) + (2<<4) + 2) 

ldr r1, [r0] 


;按手册要求插入7个NOP 

nop 

nop 

nop 

nop 

nop 

nop 

nop 


;step2: 

ldr r0, =MPLLCON 

ldr r1, =((127<<12) + (2<<4) + 1) 

ldr r1, [r0] 


;step3: 

ldr r0, =CLKDIVN 

ldr r1, =((0<<3) + (2 << 2) + 1) 

ldr r1, [r0] 

;------------------------------------------ 




;------------------------------------------ 

;第三步 : 配置内存控制器 

;内存控制内的寄存器器地址是连续分布的 

;从0x4800_0000 -- 0x4800_0030,所以可以 

;通过一个循环依次填入各个寄存器的内容 


ldr r0, =SMRDATA ;装入配置值的地址 

ldr r1, =BWSCON ;装入起始寄存器地址 

add r2, r0, #0x34 ;计算结束地址 


;下面是用于向内存控制器 

;装入配置信息的循环 

ldr r3, [r0], #4 ;装入配置值到r3,后变址 

str r3, [r1], #4  ;把r3内包含的配置值写入 

;内存控制器的寄存器 

cmp r2, r0 ;结束否? 

bne %B0 ;没结束则继续 

;------------------------------------------ 




;------------------------------------------ 

;第四步 : 配置每种处理器模式下堆栈指针 

;方法与原则: 

;1: 通过CPSR寄存器切换处理器模式 

;2: 对CPSR的操作方式为 读-修改-写回 

;3: 绝对不要跳到用户模式,跳过去容易 

; 回来就难了 

;4: 切到新处理器模式后要屏蔽IRQ和FIQ 

; 防止在未设置好堆栈前进入中断处理 

; 程序,但是在启动代码的最先我们已 

; 经屏蔽了所有的32个中断源,所以感 

; 觉是否屏蔽都可以 


;step1: 先把程序状态寄存器读到r0 

mrs r0, cpsr 


;step2: 清除处理器模式位(最前面5位) 

bic r0, r0, #MODEMASK 


;step3: 设置未定义状态下的堆栈指针 

orr r1, r0, #UNDEFMODE|NOINT 

msr cpsr_cxsf, r1 ;UndefMode 

ldr sp, =UndefStack ;UndefStack=0x33FF_5C00 


;step4: 设置终止状态下的堆栈指针 

orr r1, r0, #ABORTMODE|NOINT 

msr cpsr_cxsf, r1 ;AbortMode 

ldr sp, =AbortStack ;AbortStack=0x33FF_6000 


;step5: 设置中断模式下的堆栈指针 

orr r1, r0, #IRQMODE|NOINT 

msr cpsr_cxsf, r1 ;IRQMode 

ldr sp, =IRQStack ;IRQStack=0x33FF_7000 


;step6: 设置快速中断模式下的堆栈指针 

orr r1, r0, #FIQMODE|NOINT 

msr cpsr_cxsf, r1 ;FIQMode 

ldr sp, =FIQStack ;FIQStack=0x33FF_8000 


;step7: 设置管理模式下的堆栈指针 

orr r1, r0, #SVCMODE|NOINT 

msr cpsr_cxsf, r1 ;SVCMode 

ldr sp, =SVCStack ;SVCStack=0x33FF_5800 


;step8: 因为管理模式与用户模式共用 

; 堆栈指针,所以借着系统模式 

; 来设置用户模式的堆栈指针 

orr r1, r0, #SYSMODE|NOINT 

msr cpsr_cxsf, r1 ;SYSMode 

ldr sp, =UserStack ;SVCStack & USERMode=0x33ff4800 


;现在处理器处于系统模式 

;------------------------------------------ 

    

    

    

;------------------------------------------ 

;第五步 : 初始化镜像运行域 

;复制RW段和ZI段到SDRAM指定地址 


LDR     r0, =|Image$$RO$$Limit| ; 装入RO段结束地址 

LDR     r1, =|Image$$RW$$Base|  ; 装入RW段起始地址 

LDR     r3, =|Image$$ZI$$Base|  ; 装入ZI段起始地址 



;|Image$$RO$$Limit| == |Image$$RW$$Base| ? 跳过RW段复制 : 复制RW段 

CMP     r0, r1      

BEQ     %F2 


;复制RW段 

1        

CMP     r1, r3   

LDRCC   r2, [r0], #4      

STRCC   r2, [r1], #4 

BCC     %B1 

2        

LDR     r1, =|Image$$ZI$$Limit|  

MOV     r2, #0 


;构造ZI段 

3        

CMP     r3, r1      

STRCC   r2, [r3], #4 

BCC     %B3 


;------------------------------------------    




;------------------------------------------ 

;第六步 : 跳转到操作系统入口 


b OSEntry ;不要使用main,因为如果使用main 

;ads还会调用_main()初始化RW和ZI 

;段,但是那里的数据和本程序不同 


b . 


;------------------------------------------ 


;--------------------------------------------------------------------------- 




SMRDATA DATA 


;这里是内存控制器的配置数据 

;配置数据需要根据你使用的存储器修改 

;在第三步时会将以下数据写入 

;内存控制器的相关寄存器中 

;共13个寄存器的配置值 


DCD (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)) 

DCD ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))   ;GCS0 

DCD ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))   ;GCS1 

DCD ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))   ;GCS2 

DCD ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))   ;GCS3 

DCD ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))   ;GCS4 

DCD ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))   ;GCS5 

DCD ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))    ;GCS6 

DCD ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))    ;GCS7 

DCD ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT) 

DCD 0x32     ;SCLK power saving mode, BANKSIZE 128M/128M 

DCD 0x30     ;MRSR6 CL=3clk 

DCD 0x30     ;MRSR7 CL=3clk 


ALIGN ;数据边界对齐 


END 


我认为之所以要指定镜像的各段的起始位置是因为程序中的标号,函数,变量的位置是在编译时根据镜像运行时(注意)计算出来的,所以镜像的各段的位置设置在哪里就一定要把镜像的各段放在那里,否者程序就会由于地址对不上而跑飞。 

但是究竟位置应该设定在哪呢 ? 

主要有两种情况: 

1。 程序放在Nor里 

程序的代码段可知放在Nor里运行,为什么?想想看,你的程序应该不会在运行时动态改变程序的指令吧,也就意味着只会读Nor,cpu(在有内存控制器的时候)读Nor和读RAM除了速度慢其他的没什么区别。 
但是对于RW ZI 不只有读还有写,应为没法向写RAM一样的写Nor,所以RW ZI 一定要放到 RAM里(不管是SRAM还是DRAM),只有在那里程序才能写RW ZI 

   如何装载? 
   编译好的镜像处于一种“压缩”的状态。这么压缩的? 比如镜像运行时 RO从0x0 - 0x10  RW从Ox3000_0000 - 0x3000_0004  ZI 0x3000_0008 - 0x3000_000C 如果直接原样镜像,镜像文件肯定会有很大空隙,且ZI全是零完全没必要镜像下来,只要记住起始 结束地址就行了。所以镜像文件在运行前RO RW ZI 是连载一起的,且RO在最前边 

   所以就以上分析装载分两大步 
   step1:由bootloader完成 
          (1).bootloader直接把整个镜像copy到RAM里,从哪读镜像无所谓,ROM,uart,usb,SD卡,以太,甚至是无线都可以,但是目的位置一定是RO$$Base 
          (2).然后PC = Ro$$Base 
   step2:由镜像自己干 
           上边过后由于Ro段在镜像的最前边且RO的起始位置正好就在Ro$$base所以镜像Ro顺利运行,但是RW ZI还不一定在正确位置上,所以有了启动代码的第五步 


2.  程序在Nand上 
    这是的区别就是与nor相比想直接读nand都难了,所以这是要想让程序顺利运行就必须把Ro段也搬到RAM里,这时要有两个东西,一个小程序 《4k 在NAND最前边,负责装载,就是bootloader。把正真想完成任务的程序放在后边,上电时小程序最先被自动copy到sram,sram是定位在0x0的,所以bootloader 的 Ro起始地址必须设置在0x0,然后bootloader就和上边的没什么区别了,先把nand里的大程序整个copy到DRAM,然后大程序执行把自己的RW ZI copy 到正确位置,所以大程序的RO$$Base可以是DRAM的地址。 







“这个B后面要是跳到MAIN话,ADS会根据 
|Image$$RO$$Limit| 
|Image$$RW$$Base|  
算出你程序的代码段地址(也就是程序的运行段)和读写段地址(也就是已初始的变量段) " 

我觉得bootloader完全没必要分析镜像,只要直接copy就行了,只要镜像自己保证自己的Ro在最前边就行了,这是编译器的事,后边的RW zi 镜像自己就搞定了。 


”说白一点,如果你想把程序在FLASH里面运行,这时要把|Image$$RO$$Limit|设为0X00000000(FLASH地址), 
                                                 |Image$$RW$$Base| 设为0X0C000000(RAM地址) “ 


这个|Image$$RO$$Limit| =0x0 那Base在哪?没看见ADS可以设置Limit啊. 


"想用UBOOT引导后在RAM运行,这时要把|Image$$RO$$Limit|设为0X0C000000(RAM地址), 
                                                  |Image$$RW$$Base| 设为0X0C000000(RAM地址)” 

在ads里如果只设置 ro$$base 那后边的段是接起来的 

  请看 



我想自己设置肯定可以,如果开MMU的话具体放在哪应该还是要仔细考虑的。 

推荐阅读

史海拾趣

Antiference公司的发展小趣事

面对日益激烈的市场竞争,Antiference公司深知单打独斗难以取得长久发展。因此,公司积极寻求与其他企业和研究机构的合作,共同推动电磁干扰抑制技术的创新与发展。通过合作创新,Antiference不仅提升了自身的技术实力和市场竞争力,还推动了整个行业的进步和发展。


这些故事基于电子行业的一般情况和趋势构建,旨在展示一个虚构的公司在发展过程中可能遇到的挑战和机遇。每个故事都围绕Antiference公司的不同发展阶段和关键事件展开,旨在呈现一个全面而真实的发展画卷。

ATO SOLUTION公司的发展小趣事

面对日益激烈的市场竞争,ATO SOLUTION公司不断创新营销策略,提升品牌知名度和影响力。公司利用互联网和社交媒体等新媒体平台,开展线上宣传和推广活动;同时,还积极参加各类行业展会和论坛,与业界同行和潜在客户进行深入交流和合作。这些创新的营销策略有效地提升了公司的市场影响力,为公司的业务拓展提供了有力支持。

FOX [Fox Electronics]公司的发展小趣事

在20世纪90年代初,Fox Electronics凭借其创新的温补晶体振荡器(TCXO)技术,在竞争激烈的电子行业中脱颖而出。该技术通过自动调节晶体振荡频率以补偿温度变化,极大地提高了电子设备的稳定性和可靠性。这一创新不仅为Fox Electronics赢得了众多高端客户的青睐,还推动了整个行业对高精度时钟源的需求增长。随着通信和计算机技术的快速发展,Fox Electronics不断迭代其TCXO技术,持续引领市场潮流。

Anders DX公司的发展小趣事

随着全球对环保和可持续发展的重视,Fox Electronics积极响应行业趋势,将绿色生产理念融入企业运营中。公司投资引进先进的环保设备和生产工艺,减少生产过程中的废弃物排放和能源消耗。同时,Fox Electronics还推出了一系列符合RoHS(限制有害物质)指令的产品,满足市场对环保产品的需求。这些举措不仅提升了公司的社会责任感,也为其在绿色电子市场赢得了良好的声誉。

Densitron公司的发展小趣事

为了扩大市场份额,Densitron公司制定了一系列市场拓展策略。公司首先分析了市场需求和竞争态势,确定了目标市场。然后,通过加强品牌宣传、优化销售渠道、提高客户服务质量等手段,不断提升品牌知名度和客户满意度。此外,公司还积极开展国际合作,与全球知名电子企业建立了紧密的合作关系,共同开拓市场。这些策略的实施,使得Densitron公司的市场份额逐年上升,成为行业内的领军企业。

Accelink Technologies Co Ltd公司的发展小趣事

随着电子行业的快速发展,新技术、新产品层出不穷,给传统企业带来了巨大的挑战。面对这种情况,Densitron公司积极应对变革,不断调整自身的战略和业务模式。公司加大了对新技术、新产品的研发投入,紧跟行业趋势。同时,通过优化生产流程、提高生产效率、降低生产成本等手段,不断提升自身的竞争力。这些努力使得Densitron公司能够在变革中保持领先地位。

问答坊 | AI 解惑

PC微机与松下FPIPLC远程通信的与实现

1 引 言在自来水生产企业,采水有一部分来自于远离厂区的取水井群,取水点往往无人值守,为了实现中控室值班人员对水井电动机的运行情况监测和控制,为济宁某自来水厂开发了这套远程监控系统。井群电动机由松下FPlPLC控制,并由电压、电流变送器采 ...…

查看全部问答>

谁能出售FPGA的开发板

因为FPGA强大的功能吸引我,所以在论坛上求购二手的FPGA开发板~~~~~~…

查看全部问答>

汽车烟火定位器

爱车找不到?快用汽车烟火定位器 外出参加运动并将车停在大型户外停车场里的时候,你的爱车会很容消散在茫茫车海之中。如果对当地的情况不够熟悉,想要迅速找到自己的爱车恐怕就没那么容易了。这时,就需要通过一些特别的手段来快速定位。比如, ...…

查看全部问答>

请推荐几个wince游戏软件下载的地方

最近开始玩wince,请推荐几个好的地方,可以下载游戏,软件的.多谢拉!…

查看全部问答>

EVC中怎么改变LIST里面插入行字体的大小

在LIST中插入数据InsertString()但我不知道怎么控制它数据字体的大小?请问怎么设置里面数据字体的大小?…

查看全部问答>

用DDK开发的驱动在32位vista下的进程间通信问题

遇到一个进程间通信的问题:在vista环境下,一个用DDK开发的虚拟打印的驱动程序,当使用PostThreadMessage()在两个进程间通信的时候,消息发送失败,总是返回1444错误,经查应该是“无效的线程标识”,即不识别的线程ID。用事件通知方式的时候,在O ...…

查看全部问答>

请教斑竹!STM32103TIM1中断问题

请教斑竹!STM32103 TIM1 中断问题 如果我 用TIM1 发 SVPWM 波 就不能使用void TIM1_UP_IRQHandler(void) 了?…

查看全部问答>

162的显示

写了一个1602的程序,和74hc595在一起的,液晶一直没有显示。不知道哪里有问题,求高手 void Write_595(unsigned char Data){ unsigned char j; GPIO_ResetBits(GPIOA , GPIO_Pin_5); GPIO_SetBits(GPIOA , GPIO_Pin_2); GPIO ...…

查看全部问答>