OpenEdv-开源电子网

 找回密码
 立即注册

扫一扫,访问微社区

正点原子新作:阿波罗STM32F767&F429&探索者STM32F4开发板&赶快来下载资料哦。

查看: 671|回复: 1

OK6410开发板学习之一步一步实现精简BootLoader(BL1部分)四

[复制链接]

  离线 

12

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
92
金钱
92
注册时间
2016-7-24
在线时间
13 小时
发表于 2018-4-18 21:00:57 | 显示全部楼层 |阅读模式
正点原子公众号

9.栈初始化:bl init_stack

栈是一种具有后进先出性质的数据组织方式,也就是说后存放的先取出,先存放的后取出。栈底是第一个进栈的数据所处的位置,栈顶是最后一个进栈的数据所处的位置 。如下左图:

                  


根据SP指针指向的位置,栈可以分为满栈和空栈。

1. 满栈:当堆栈指针SP总是指向最后压入堆栈的数据

2. 空栈:当堆栈指针SP总是指向下一个将要放入数据的空位置。ARM采用满栈! 如上图中:

根据SP指针移动的方向,栈可以分为升栈和降栈。
1. 升栈:随着数据的入栈,SP指针从低地址->高地址移动;

2. 降栈:随着数据的入栈,SP指针从高地址->低地址移动。ARM采用降栈! 如下图:


栈帧(stack frame)就是一个函数所使用的那部分栈,所有函数的栈帧串起来就组成了一个完整的栈。栈帧的两个边界分别由fp(r11)和sp(r13)来限定。 如下图:

总结下来,栈主要用来保存局部变量、传递参数、保存寄存器值,显然初始化栈很重要。代码如下:

        @        栈初始化 64M内存用于栈init_stack:        ldr sp, =0x54000000    @ 将0x54000000写入sp寄存器中,栈大小64M    0x5400000000-0x500000000        mov pc, lr                   @ 返回调用处继续往下执行

虽然就是这么一小段代码,但是大家不能小看他,没有他,后面的c语言开发环境就无从说起了。

10.清除bss段:bl clean_bss

bss段主要存放的是未初始化的全局变量,为了防止影响以后的使用,要将其初始化为0。也是为C语言环境做准备,代码如下:

        @ 清除bss段clean_bss:        ldr r0, =bss_start     @ bss段开始地址,参见链接器脚本        ldr r1, =bss_end       @ bss段结束地址        cmp r0, r1             @ 比较bss段开始与结束地址        moveq pc, lr           @ 若相等,返回调用处继续往下执行  clean_loop:              @ 若不相等,执行下面的代码        mov r2, #0             @ r2=0        str r2, [r0], #4       @ 将r2的值(0)写入r0所指定的地址中,同时r0指定的地址+4后存入r0        cmp r0, r1             @ 比较bss段新开始与结束地址        bne clean_loop         @ 若不相等,跳转到clean_loop标号处执行        mov pc, lr             @ 否则,返回调用处继续执行

11.进入c编程环境:ldr pc, =main

到此,也就是海宽任鱼跃,天高任鸟飞了。在c语言环境中,大家不用再为记不住汇编命令感到头疼了,是不是很爽呢?废话少说。上一段c语言环境下的点亮led代码:

#define GPKCON (volatile unsigned long*)0x7F008820#define GPKDAT (volatile unsigned long*)0x7F008824// 延时函数void delay(){        int i = 0;        int j = 0;        for(i = 0; i < 10000; i++)        {                for(j = 0; j < 110; j++)                {                        ;                }        }}int main(){        int val = 0x1e;  *(GPKCON) = 0x1111;        while(1)        {            *(GPKDAT) = val;                        delay();                        val = val<<1;            if((val&0x10) != 0x10)                                val = 0x1f;        }    return 0;    }

本节内容也就讲完了,最后贴上整体代码:

start.S

.text.global _start_start:        b reset        ldr pc, _undifined_instruction   @ 未知指令异常        ldr pc, _software_interrupt      @ 软件中断        ldr pc, _prefetch_abort          @ 预取异常        ldr pc, _data_abort              @ 数据终止异常        ldr pc, _not_used                @ 未使用,预留        ldr pc, _irq                     @ 中断        ldr pc, _fiq                             @ 快速中断                                _undifined_instruction:  .word undifined_instruction_software_interrupt:         .word software_interrupt_prefetch_abort:                 .word prefetch_abort_data_abort:                         .word data_abort_not_used:                                 .word not_used_irq:                                         .word irq_fiq:                                         .word resetundifined_instruction:        nop                                software_interrupt:        nopprefetch_abort:        nopdata_abort:        nop                                not_used:        nopirq:        nop                                fiq:        nopreset:        bl set_svc              @ 设置cpu为svc模式        bl set_peri_port        @ 外设基地址初始化        bl disable_watchdog     @ 关闭看门狗        bl disable_interrupt    @ 关闭所有中断        bl disable_mmu          @ 关闭mmu和cache        bl init_clock           @ 时钟初始化        bl mem_init             @ 内存初始化        bl copy_to_ram          @ 拷贝bl到内存中        bl init_stack           @ 栈初始化        bl clean_bss            @ 初始化bss段        ldr pc, =main           @ 进入c语言编程环境,main函数        @bl light_led                @ set the cpu to SVC32 modeset_svc:        mrs        r0,cpsr             @ 将程序状态寄存器取出保存在通用寄存器r0中,参看am架构参考手册:A2.5 Program status registers        bic        r0,r0,#0x1f         @ 将从状态寄存器取出的值(r0)低5位清零后存入r0中        orr        r0,r0,#0xd3         @ 将r0中的值的第0 1 4 6 7位置1后存入r0中,关闭中断与快速中断,进入svc模式        msr        cpsr,r0             @ 将r0的中的值写入程序状态寄存器        mov pc, lr              @ 返回调用处继续往下执行  @ 初始化外设地址,必须做这一步,要不外设无法使用。set_peri_port:        ldr r0, =0x70000000     @ 将基地址0x70000000存入寄存器r0中        orr r0, r0, #0x13       @ 将寄存器r0的值设为0x70000013        mcr p15,0,r0,c15,c2,4          @ 将寄存器r0的值传送到协处理器p15的c15寄存器中,参看arm11内核手册:c15, Peripheral Port Memory Remap Register        mov pc, lr              @ 返回调用处继续往下执行  @ Disable Watchdog 参看s3c6410手册:Watch Dog章节#define pWTCON 0x7e004000  @ addr of watchdog timer control register (WTCON)disable_watchdog:        ldr r0, =pWTCON         @ 将看门狗控制寄存器地址写入寄存器r0中        mov r1, #0x0            @ 将0x0保存在寄存器r1中        str r1, [r0]            @ 将0x0传送到看门狗控制寄存器中        mov pc, lr              @ 返回调用处继续往下执行                @ Disable all interrupts (VIC0 and VIC1),参考s3c6410手册:Vectored Interrupt Controller章节disable_interrupt:        mvn r1,#0x0        ldr r0,=0x71200014      @ VIC0INTENCLEAR地址        str r1,[r0]             @ 将VIC0INTENCLEAR设为0        ldr r0,=0x71300014      @ VIC1INTENCLEAR地址        str r1,[r0]             @ 将VIC1INTENCLEAR设为0                mov pc, lr              @ 返回调用处继续往下执行                @ 关闭MMU和cache        disable_mmu:        mcr p15,0,r0,c7,c7,0    @ 使I/D cache全部失效 参考arm11内核手册:c7, Cache operations        mrc p15,0,r0,c1,c0,0    @ 取出协处理器p15的控制寄存器(c1)内容,存放在r0中 参考arm11内核手册:c1, Control Register        bic r0, r0, #0x00000007 @ 将r0的[2:0]设为0后存入r0        mcr p15,0,r0,c1,c0,0    @ 将r0的值传送到协处理器p15的控制寄存器(c1)中,完成mmu和cache配置        mov pc, lr              @ 返回调用处继续往下执行                @ 初始化时钟 参考s3c6410手册系统控制章节、时钟相关章节#define CLK_DIV0 0x7e00f020    @ 时钟分频寄存器0地址#define OTHERS 0x7e00f900      @ 其他控制寄存器地址#define MPLL_CON 0x7e00f010    @ MPLL控制寄存器地址#define APLL_CON 0x7e00f00c    @ APLL控制寄存器地址#define CLK_SRC 0x7e00f01c     @ 时钟源选择寄存器地址#define DIV_VAL ((0x0<<0)|(0x1<<9)|(0x1<<8)|(0x3<<12))  @ 分频设置值  2b0000_0000_0000_0000_0011_0011_0000_0000 参考uboot设置#define PLL_VAL ((1<<31)|(266<<16)|(3<<8)|(1<<0))       @ PLL设置值   2b1000_0001_0000_1010_0000_0011_0000_0001  参考uboot设置init_clock:        @ 设置时钟分频系数        ldr r0, =CLK_DIV0      @ 将时钟分频寄存器0的地址存入寄存器r0中                     ldr r1, =DIV_VAL       @ 将需要配置到时钟分频寄存器0的数值存入寄存器r1中        str r1, [r0]           @ 将r1的数值传送到时钟分频寄存器0中        @ 设置cpu为异步模式,将OTHERS寄存器内容设置为0xc0        ldr r0, =OTHERS        @ 将OTHERS寄存地址存入寄存器r0中        ldr r1, [r0]           @ 将其他控制寄存器中的数据读出,存入r1中        bic r1,r1,#0xc0        @ 将r1中的数值第6 7 bit位清0后再存入r1中        str r1, [r0]           @ 将r1中的数值传送到OTHERS寄存器中,Asynchronous mode 和 MOUTmpll        @ 设置APLL时钟频率 533MHz        ldr r0, =APLL_CON      @ 将APLL控制寄存器地址写入r0中        ldr r1, =PLL_VAL       @ 将需要配置的PLL数值存入寄存器r1中        str r1, [r0]           @ 将r1中的数值写入APLl控制寄存器中        @ 设置MPLL时钟频率 533MHz        ldr r0, =MPLL_CON      @ 将MPLL控制寄存器地址写入r0中        ldr r1, =PLL_VAL       @ 将需要配置的PLL数值存入寄存器r1中        str r1, [r0]           @ 将r1中的数值写入MPLL控制寄存器中        @ 设置时钟源选择控制寄存器,使用APLL和MPLL输出时钟。        ldr r0, =CLK_SRC       @ 将时钟源选择寄存器地址写入r0中        mov r1, #0x3           @ 将0x03写入寄存器r1中        str r1, [r0]                 @ 将r1中的数据写入时钟源选择控制寄存器中        mov pc, lr                   @ 返回调用处继续往下执行        @ 代码搬移,拷贝bl到内存中copy_to_ram:        ldr r0, =0x0c000000    @ Stepping Stone (Boot Loader)基地址,即要搬移的源地址存入r0        ldr r1, =0x50008000    @ 内存地址,要搬移的目标地址存入r1        add r3, r0, #1024*4    @ 将r0处的地址加4k大小存入r3,要搬移的内存Size 4k,copy_loop:               @ 等待搬移完成        ldr r2, [r0], #4       @ 将地址0x0c000000处数据存入r2后,在增加4后(0x0c000004)存入r0中        str r2, [r1], #4       @ 将r2的数据传送到r1指定的地址处后,在+4(0x500080004)存入r1中        cmp r0, r3             @ 比较r0和r3处地址        bne copy_loop                 @ 若不相等,跳到标号copy_loop处继续执行        mov pc, lr                   @ 返回调用处继续往下执行         @        栈初始化 64M内存用于栈init_stack:        ldr sp, =0x54000000    @ 将0x54000000写入sp寄存器中,栈大小64M    0x5400000000-0x500000000        mov pc, lr                   @ 返回调用处继续往下执行        @ 清除bss段clean_bss:        ldr r0, =bss_start     @ bss段开始地址,参见链接器脚本        ldr r1, =bss_end       @ bss段结束地址        cmp r0, r1             @ 比较bss段开始与结束地址        moveq pc, lr           @ 若相等,返回调用处继续往下执行  clean_loop:              @ 若不相等,执行下面的代码        mov r2, #0             @ r2=0        str r2, [r0], #4       @ 将r2的值(0)写入r0所指定的地址中,同时r0指定的地址+4后存入r0        cmp r0, r1             @ 比较bss段新开始与结束地址        bne clean_loop         @ 若不相等,跳转到clean_loop标号处执行        mov pc, lr             @ 否则,返回调用处继续执行        @ 点亮led#define GPMCON 0x7F008820#define GPMDAT 0x7F008824#define GPMPUD 0x7F008828#define LED1   0x1E#define LED2   0x1D#define LED3   0x1B#define LED4   0x17light_led:        ldr r0,=GPMCON        ldr r1,=0x1111        str r1,[r0]                ldr r0,=GPMPUD        ldr r1,=0x55        str r1,[r0]                ldr r0,=GPMDAT        mov r1,#0x0e        str r1,[r0]        mov pc,lr

其中有一段汇编下的点亮led的代码,大家正好可以和c语言环境下的代码对比一下,看一下孰优孰劣。可能大家发现,怎么内存初始化的代码不见了,别急,马上就来:

内存初始化代码 mem.S

.text.global mem_initmem_init:        ldr r0, =0x7e00f120  @配置内存系统子程序寄存器地址     mov r1, #0x8         @将0x8存入r1寄存器    str r1, [r0]         @将r1寄存器里的数据写入配置内存系统子程序寄存器中    ldr r0, =0x7e001004  @内存控制命令寄存器        mov r1, #0x4         @根据手册得知需要先进入配置模式    str r1, [r0]         @将寄存器r1内容写入内存控制命令寄存器中    ldr r0, =0x7e001010  @刷新寄存器地址    ldr r1, =( ( 7800 / ( 1000000000/133000000 ) + 1 ) )      @设置刷新时间    str r1, [r0]         @将寄存器r1内容写入刷新寄存器寄存器中    ldr r0, =0x7e001014  @CAS latency寄存器    mov r1, #(3 << 1)    @r1=0x6    str r1, [r0]         @将寄存器r1内容(0x06)写入CAS latency寄存器中    ldr r0, =0x7e001018  @t_DQSS寄存器    mov r1, #0x1         @r1=0x1    str r1, [r0]         @将寄存器r1内容(0x01)写入t_DQSS寄存器中    ldr r0, =0x7e00101c  @T_MRD寄存器    mov r1, #0x2         @r1=0x2    str r1, [r0]         @将寄存器r1内容(0x02)写入T_MRD寄存器中    ldr r0, =0x7e001020  @t_RAS寄存器    ldr r1, =( ( 45 / ( 1000000000 / 133000000 ) + 1 ) )   @r1=6.95    str r1, [r0]         @将寄存器r1内容(6.95)写入t_RAS寄存器中    ldr r0, =0x7e001024  @t_RC寄存器    ldr r1, =( ( 68 / ( 1000000000 / 133000000 ) + 1 ) )   @r1=10.044    str r1, [r0]         @将寄存器r1内容(10.044)写入t_RC寄存器中    ldr r0, =0x7e001028  @t_RCD寄存器    ldr r1, =( ( 23 / ( 1000000000 / 133000000 ) + 1 ) )   @r1=3.99    str r1, [r0]         @将寄存器r1内容(3.99)写入t_RCD寄存器中    ldr r0, =0x7e00102c  @t_RFC寄存器    ldr r1, =( ( 80 / ( 1000000000 / 133000000 ) + 1 ) )   @r1=11.64       str r1, [r0]         @将寄存器r1内容(11.64)写入t_RFC寄存器中    ldr r0, =0x7e001030  @t_RP寄存器    ldr r1, =( ( 23 / ( 1000000000 / 133000000 ) + 1 ) )   @r1=3.99    str r1, [r0]         @将寄存器r1内容(3.99)写入t_RP寄存器中    ldr r0, =0x7e001034  @t_rrd寄存器    ldr r1, =( ( 15 / ( 1000000000 / 133000000 ) + 1 ) )   @r1=2.995    str r1, [r0]         @将寄存器r1内容(2.995)写入t_rrd寄存器中    ldr r0, =0x7e001038  @t_wr寄存器    ldr r1, =( ( 15 / ( 1000000000 / 133000000 ) + 1 ) )   @r1=2.995 @  ldr r2, [r0]    str r1, [r0]         @将寄存器r1内容(2.995)写入T_MRD寄存器中    ldr r0, =0x7e00103c  @t_wtr寄存器    mov r1, #0x07        @r1=0x07    str r1, [r0]         @将寄存器r1内容(0x07)写入T_MRD寄存器中    ldr r0, =0x7e001040  @t_xp寄存器    mov r1, #0x02        @r1=0x02    str r1, [r0]         @将寄存器r1内容(0x02)写入t_xp寄存器中    ldr r0, =0x7e001044  @t_xsr寄存器    ldr r1, =( ( 120 / ( 1000000000 / 133000000 ) + 1 ) )   @r1=16.69    str r1, [r0]         @将寄存器r1内容(16.69)写入t_xsr寄存器中    ldr r0, =0x7e001048   @t_esr寄存器    ldr r1, =( ( 120 / ( 1000000000 / 133000000 ) + 1 ) )   @r1=16.69    str r1, [r0]         @将寄存器r1内容(16.69)写入t_esr寄存器中    ldr r0, =0x7e00100c  @内存控制配置寄存器 P1MEMCFC    ldr r1, =0x00010012  @配置控制器  r1=2b0000_0000_0000_0001_0000_0000_0001_0010    str r1, [r0]         @将寄存器r1内容(0x10012)写入P1MEMCFC寄存器中    ldr r0, =0x7e00104c  @32位DRAM配置控制寄存器    ldr r1, =0x0b45      @r1=2b0000_0000_0000_0000_0000_1011_0100_0101  选择Mobile DDR SDRAM    str r1, [r0]         @将寄存器r1内容(0x0b45)写入T寄存器中    ldr r0, =0x7e001200  @片选寄存器    ldr r1, =0x150f8     @r1=0x150f8  r1=2b0000_0000_0000_0001_0101_0000_1111_1000   Bank-Row-Column organization    str r1, [r0]         @将寄存器r1内容(0x02)写入T_MRD寄存器中    ldr r0, =0x7e001304  @用户配置寄存器    mov r1, #0x0         @r1=0x0    str r1, [r0]         @将寄存器r1内容(0x00)写入用户配置寄存器中    ldr r0, =0x7e001008  @直接命令寄存器    ldr r1, =0x000c0000  @r1=2b0000_0000_0000_1100_0000_0000_0000_0000   makes DRAM Controller issue ‘NOP’ memory    str r1, [r0]         @将寄存器r1内容(0xc0000)写入直接命令寄存器中    ldr r1, =0x00000000  @r1=0   makes DRAM Controller issue‘PrechargeAll’ memory command.     str r1, [r0]         @将寄存器r1内容(0x0)写入直接命令寄存器中    ldr r1, =0x00040000  @r1=2b0000_0000_0000_0100_0000_0000_0000_0000  makes DRAM Controller issue ‘Autorefresh’ memory command.    str r1, [r0]         @将寄存器r1内容(0x40000)写入直接命令寄存器中    ldr r1, =0x000a0000  @r1=2b0000_0000_0000_1010_0000_0000_0000_0000  makes DRAM Controller issue ‘MRS’ memory command.     str r1, [r0]         @将寄存器r1内容(0xa0000)写入直接命令寄存器中    ldr r1, =0x00080032  @r1=2b0000_0000_0000_1000_0000_0000_0011_0010    str r1, [r0]         @将寄存器r1内容(0x80032)写入直接命令寄存器中  makes DRAM Controller issue ‘EMRS’ memory command.    ldr r0, =0x7e001004  @内存控制命令寄存器    mov r1, #0x0         @根据手册得知需要进入ready模式    str r1, [r0]         @将寄存器r1内容写入内存控制命令寄存器中@检查内存初始化是否准备好check_dmc1_ready:        ldr r0, =0x7e001000  @DRAM控制状态寄存器    ldr r1, [r0]         @将DRAM控制状态寄存器中内容写入寄存器r1中    mov r2, #0x3         @将0x03写入寄存器r2中    and r1, r1, r2       @将r1和r2与运算后存在r1中,即只保持读出的值的[1:0]位    cmp r1, #0x1         @将读出的值与0x01比较    bne check_dmc1_ready @若不相等,跳转到标号check_dmc1_ready处继续执行,检查内存状态    nop    mov pc, lr           @返回调用处继续往下执行

不要问我为什么这么设置,我也不是很清楚,参考uboot设置的,总之能用,以后慢慢去理解吧!

C语言环境下的代码:

#define GPKCON (volatile unsigned long*)0x7F008820#define GPKDAT (volatile unsigned long*)0x7F008824// 延时函数void delay(){        int i = 0;        int j = 0;        for(i = 0; i < 10000; i++)        {                for(j = 0; j < 110; j++)                {                        ;                }        }}int main(){        int val = 0x1e;  *(GPKCON) = 0x1111;        while(1)        {            *(GPKDAT) = val;                        delay();                        val = val<<1;            if((val&0x10) != 0x10)                                val = 0x1f;        }    return 0;    }

终于写完了,也算是给自己这段时间通宵达旦学习一个交代吧!感觉自己没有白白浪费时间,还是学到一点东西,这些记不住,以后只能通过这篇博文复习巩固了。加油,继续接下来的学习。


回复

使用道具 举报

  离线 

21

主题

178

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1588
金钱
1588
注册时间
2012-8-25
在线时间
400 小时
发表于 2018-4-19 08:36:39 | 显示全部楼层
天书看不来
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则




QQ|联系我们|手机版|官方淘宝店|新浪微博|微信公众平台|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2018-6-23 01:09

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表
/* */