前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【嵌入式开发】ARM 代码搬移 ( ARM 启动流程 | 代码搬移 起点 终点 | 链接地址 | 汇编代码 )

【嵌入式开发】ARM 代码搬移 ( ARM 启动流程 | 代码搬移 起点 终点 | 链接地址 | 汇编代码 )

作者头像
韩曙亮
发布2023-03-27 16:06:08
9130
发布2023-03-27 16:06:08
举报
文章被收录于专栏:韩曙亮的移动开发专栏

文章目录

本博客的参考文章及相关资料下载 :

一. ARM 启动流程

ARM 开发板启动方式 : 可以选择从 NorFlash , NandFlash , SD 卡 启动 三种方式 , 这里我们着重介绍 NandFlash 启动的情况 ;

1. 各种类型开发板启动流程

( 1 ) 2440 开发板启动流程简介 ( ① Nand Flash 拷贝 4 KB -> SRAM 垫脚石 | ② PC 指向 0 地址 即 SRAM 起始地址执行 | ③ 初始化内存 拷贝 后续指令到内存执行 )

2440 NandFlash 启动 :

  • 1.垫脚石 ( SRAM ) 简介 : 2440 Nand Flash 启动 , 需要依赖于 很重要的片内部件 -> SRAM , 这个部件 又叫 垫脚石 , 其 位于 ARM 地址空间 的 0 地址处 , 其容量大小是 4KB ;
  • 2.拷贝最前面 4KB : Nand Flash -> SRAM : 处理器上电之后 , 2440 会自动 从 Nand Flash 中拷贝 出 最前端 4 KB 的内容 , 复制进 SRAM 垫脚石 中 ;
  • 3.执行 拷贝的 SRAM 中的 4KB 指令 : PC 指针 指向 0 地址 , 即 指向 垫脚石 SRAM 中的第一条指令 , 然后开始执行 ;
  • 4.SRAM 大小 4KB 局限性 : 这里 注意 , 只能 拷贝 4KB 的指令 , 对于小的 bootloader 程序足够用 , 但对于 u-boot 这种重量级的程序 编译出来 有 100多KB , 显然 4KB 就不够用了 ;
  • 5.代码搬移策略 : 先拷贝 4KB 到 SRAM 中 , 执行这 4KB 指令 , 在这些指令中 , 先 把内存初始化好 , 然后 将后续指令从 Nand Flash 拷贝到内存中执行 ;

( 2 ) 6410 开发板启动流程简介 ( ① 上电 运行 SROM 的 BL0 程序 | ② NandFlash -> SRAM 拷贝 8KB 指令 | ③ 执行 SRAM 指令 拷贝其余 BL 指令到内存中执行 )

6410 NandFlash 启动 :

  • 1.BL0 程序 : 6410 上电之后 , 首先去运行 SROM 中的 Bootloader 0 , BL 0 是 芯片出厂就已经 烧写到 SROM 中的程序 ;
  • 2.BL 0 的 作用 : 从 Nand Flash 中拷贝 8KB 的指令 到 SRAM ( 垫脚石 ) 中 运行 , 显然 8KB 无法满足 大型 Bootloader 程序的要求 ;
  • 3.拷贝代码 : 利用 这 8KB 的程序 , 初始化内存 , 将剩下的 Bootloader 拷贝到 内存中 运行 ;

( 3 ) 210 开发板启动流程简介 ( ① 上电 运行 SROM 的 BL0 程序 | ② NandFlash -> IRAM 垫脚石 拷贝 96KB 指令 | ③ 执行 IRAM 指令 拷贝其余 BL 指令到内存中执行 )

210 NandFlash 启动 :

  • 1.BL0 程序 : 210 上电之后 , 首先去运行 SROM 中的 Bootloader 0 , BL 0 是 芯片出厂就已经 烧写到 SROM 中的程序 ;
  • 2.BL 0 的 作用 : 从 Nand Flash 中拷贝 96KB 的指令 到 IRAM ( 垫脚石 ) 中 运行 , 如果 96 KB 大小不够用 , 就要将剩余的 BL 拷贝到内存中 ;

二. 代码搬移 简介

1. 从 SRAM -> 内存

( 1 ) 代码搬移 简介 ( ① 代码搬移起点 | ② 从 SRAM 搬移 的 原因 | ③ 不搬移也可正常运行 )

代码搬移 简介 :

  • 1.代码搬移起点 : 代码搬移是 将 Nand Flash 中的 BL 搬移到内存中 , 其 起点 应该是 Nand Flash , 本博客 讲解的 代码拷贝的起点 改成 SRAM ( 垫脚石 ) ;
  • 2.从 SRAM 搬移 的 原因 : 从 Nand Flash 读取数据 , 需要对 Nand Flash 进行初始化 ; 代码 搬移 的重要原因是 BL 大小 大于 其 垫脚石 大小 , 当前的指令编译完成后 仅有 不到 1KB 大小 , 其在上电后 , 会将整个的 BL 拷贝到 SRAM 垫脚石 中 ;
  • 3.不搬移也可正常运行 : 代码在 SRAM 中 可以运行完毕 , 不拷贝到内存中也可以正常运行 , 此处只是最代码搬移进行介绍 ;

( 2 ) 代码搬移 起点 ( SRAM 首地址 文档中查询 | 6410 开发板 : 0x0C00_0000 )

各个开发板 代码搬移 的 起点 :

  • 1.2440 中 , SRAM ( 垫脚石 ) 起始地址 是 0x0 ;
  • 2.6410 SRAM ( 垫脚石 ) 起始地址是 0x0C00_0000 ; 6410 手册 Page 116 ;
在这里插入图片描述
在这里插入图片描述
  • 3.210 开发板 IRAM ( 垫脚石 ) 地址起始地址 0xD002_0000 ; 210 手册 Page 30 ;
在这里插入图片描述
在这里插入图片描述

( 3 ) 链接地址 简介 ( 链接起始地址 | 反汇编 | _start 入口函数 | 指令汇编地址 )

链接地址 :

  • 1.链接起始地址 : 在之前写的 链接器脚本中 写的 链接器 起始地址 . = 0x50008000; ;
  • 2.反汇编程序 : 对程序进行反汇编 , 在代码编译目录中 , 执行 arm-linux-objdump -D -S u-boot.elf > dump 命令 , 将反汇编内容输出到 dump 文件中 ( 前提是 有 编译好的 可执行 文件 ) ;
  • 3.查看汇编文件 : 打开 汇编 文件 ;
    • ① _start 入口函数 : 汇编代码的 入口 是 _start 标号 , 查看反汇编之后的代码 , 可以看到 在 _start 标号前 看到地址 0x50008000 , 该地址 是 整个程序的起始地址 , 即 SRAM 的起始地址 ;
    • ② 每行指令都有相应地址 : 每行代码都有一个链接地址 , 可以看到 反汇编 文件中 每行 前面都有一个 链接地址 ;
在这里插入图片描述
在这里插入图片描述

( 4 ) 链接地址 作用 ( C 语言 函数调用 | 汇编 ldr 修改 PC 指针 )

链接地址 作用 :

  • 1.C 语言程序 : 调用 reset() 函数 , 调用之后 , PC 指针 会被 重新赋值 , 去执行 reset() 函数 , 这个 PC 指针 被赋予的 值 就是 reset 标号 前 的 链接地址 ; 如 PC 指针 被赋值成 0x50008058 , 该地址就是 reset() 函数的链接地址 ;
  • 2.汇编指令 : 使用 ldr 伪指令 修改 PC 指针 , 如 ldr PC , reset , 让 PC 指针 执行 reset 函数 , 此时 PC 指针会被赋值成 0x50008058 地址 ;

( 5 ) 指令 跳转 ( 相对跳转 | 绝对跳转 )

指令 跳转 :

  • 1.PC 指针跳转 : 开发板上电后 , PC 指针 首先 指向 0 , 但是 汇编程序的入口 _start 标号的 地址 是 0x50008058 , PC 指针 被赋值为 0x50008058 ;
  • 2.相对跳转 :
    • ① 相对跳转指令 : 使用 b , bl 等指令 产生的跳转 , 就是 相对跳转 ;
    • ② 相对跳转过程 : 在跳转过程中 , 不是将对应 标号的 链接地址 , 直接赋值给 PC 指针 , 而是采用 跳转前的 PC 指针值 + 当前指针 与 要跳转的标号 位置之间的 差值 ;
    • ③ 相对跳转举例 :PC 指针在入口处 _start 地址为 0x50008000 , 如果要执行 reset 标号处的代码 , 需要 跳转到 0x50008058 中 , PC 指针 从 0x50008000 跳转到 0x50008058 中 , 这里只需要 相对跳转 0x58 地址增量 即可 ;
  • 3.绝对跳转 : C 语言中 调用函数 , 或者 修改 PC 指针值 , 的情况 是 绝对跳转 ;

( 6 ) 代码搬移终点 ( 链接器脚本首地址 | 6410 代码搬移终点首地址 0x50008000)

代码搬移 终点 :

  • 1.内存首地址 : 链接起始地址 决定了 程序第一行代码的链接地址 , 即 第一行代码 在 内存中出现的位置 , 如 6410 的第一行代码 的 内存 地址是 0x50008000 ;
  • 2.拷贝终点 : 代码 从 SRAM 拷贝到 内存中 , 这个内存的位置 0x50008000 就是 第一行 代码 被拷贝到的位置 ;
  • 3.拷贝过程 : 代码拷贝的时候 , 需要从 代码的起始地址开始拷贝 , 之后的代码 以此类推 , 拷贝到后续指定标号地址处 , 都要拷贝到对应的位置中 ;
  • 4.6410开发板 拷贝 终点 : 0x50008000 是 6410 开发板 代码拷贝终点 的 第一行 指令的地址 ;

三. 代码搬移 汇编代码

1. 汇编代码编写

( 1 ) 代码搬移 汇编代码

汇编代码 :

代码语言:javascript
复制
copy_to_ram:

	ldr r0, =0x0c000000								@ 设置 代码搬移 起始地址 首地址 , 即 SRAM 垫脚石的 首地址 , 将改地址存放在 r0 寄存器中
	ldr r1, =0x50008000								@ 设置 代码搬移 终点 首地址 , 即 内存的首地址 , 将该地址存放在 r1 寄存器中
	add r3, r0, #1024*4								@ 设置 复制多少指令到 内存中 , 这里复制 4KB 数据 从 SRAM 到 内存 中 ; 

copy_loop:											@ 循环复制代码

	ldr r2, [r0], #4								@ 取出 r0 寄存器的地址 ( 即 SRAM 中的地址 ) 中的数据 放入 r2 寄存器  , 读取完毕后 , r0 中的地址 累加 4
	str r2, [r1], #4								@ 将 r2 中的内容 , 写入到 r1 寄存器对应的地址中 , 写出完毕后 , r1 中的地址 累加 4 
	cmp r0, r3										@ 查看 r0 地址 增量 是否 增加了 4KB , 到了代码搬移的末尾
	bne copy_loop									@ 如果 r0 r3 地址不一致 , 说明还没拷贝完毕 , 继续 跳转回 copy_loop 标号 拷贝
	
	mov pc, lr

( 2 ) 汇编完整代码

代码语言:javascript
复制
@****************************  
@File:start.S  
@  
@BootLoader 初始化代码 
@****************************  

.text                                   @ 宏 指明代码段  
.global _start                          @ 伪指令声明全局开始符号  
_start:                                 @ 程序入口标志  
        b   reset                       @ reset 复位异常  
        ldr pc, _undefined_instruction  @ 未定义异常, 将 _undefined_instruction 值装载到 pc 指针中  
        ldr pc, _software_interrupt     @ 软中断异常  
        ldr pc, _prefetch_abort         @ 预取指令异常  
        ldr pc, _data_abort             @ 数据读取异常  
        ldr pc, _not_used               @ 占用 0x00000014 地址                            
        ldr pc, _irq                    @ 普通中断异常  
        ldr pc, _fiq                    @ 软中断异常  

_undefined_instruction: .word undefined_instruction @ _undefined_instruction 标号存放了一个值, 该值是 32 位地址 undefined_instruction, undefined_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 fiq                           @ 快速中断处理  

undefined_instruction:                              @ undefined_instruction 地址存放要执行的内容  
        nop  

software_interrupt:                                 @ software_interrupt 地址存放要执行的内容  
        nop  

prefetch_abort:                                     @ prefetch_abort 地址存放要执行的内容  
        nop  

data_abort:                                         @ data_abort 地址存放要执行的内容  
        nop  

not_used:                                           @ not_used 地址存放要执行的内容  
        nop  

irq:                                                @ irq 地址存放要执行的内容  
        nop  

fiq:                                                @ fiq 地址存放要执行的内容  
        nop  

reset:                                              @ reset 地址存放要执行的内容  
		bl set_svc                                  @ 跳转到 set_svc 标号处执行
		bl set_serial_port							@ 设置外设基地址端口初始化
		bl disable_watchdog                         @ 跳转到 disable_watchdog 标号执行, 关闭看门狗
		bl disable_interrupt						@ 跳转到 disable_interrupt 标号执行, 关闭中断
		bl disable_mmu								@ 跳转到 disable_mmu 标号执行, 关闭 MMU 
		bl init_clock								@ 跳转到 init_clock 标号, 执行时钟初始化操作
		bl mem_init									@ 跳转到 mem_init 标号 , 执行内存初始化操作 , 该段代码定义在 mem.S 文件中
		bl light_led								@ 打开开发板上的 LED 发光二极管
		bl copy_to_ram								@ 代码搬移 , 从 SRAM 到 内存中

set_svc:
		mrs r0, cpsr                                @ 将 CPSR 寄存器中的值 导出到 R0 寄存器中
		bic r0, r0, #0x1f                           @ 将 R0 寄存器中的值 与 #0x1f 立即数 进行与操作, 并将结果保存到 R0 寄存器中, 实际是将寄存器的 0 ~ 4 位 置 0
		orr r0, r0, #0xd3                           @ 将 R0 寄存器中的值 与 #0xd3 立即数 进行或操作, 并将结果保存到 R0 寄存器中, 实际是设置 0 ~ 4 位 寄存器值 的处理器工作模式代码
		msr cpsr, r0                                @ 将 R0 寄存器中的值 保存到 CPSR 寄存器中
		mov pc, lr									@ 返回到 返回点处 继续执行后面的代码
		
#define pWTCON 0x7e004000                           @ 定义看门狗控制寄存器 地址 ( 6410开发板 )
disable_watchdog:                                 
		ldr r0, =pWTCON                             @ 先将控制寄存器地址保存到通用寄存器中
		mov r1, #0x0                                @ 准备一个 0 值, 看门狗控制寄存器都设置为0 , 即看门狗也关闭了
		str r1, [r0]                                @ 将 0 值 设置到 看门狗控制寄存器中 
		mov pc, lr									@ 返回到 返回点处 继续执行后面的代码
		
disable_interrupt:
	mvn r1,#0x0										@ 将 0x0 按位取反, 获取 全 1 的数据, 设置到 R1 寄存器中
	ldr r0,=0x71200014								@ 设置第一个中断屏蔽寄存器, 先将 寄存器 地址装载到 通用寄存器 R0 中 
	str r1,[r0]										@ 再将 全 1 的值设置到 寄存器中, 该寄存器的内存地址已经装载到了 R0 通用寄存器中

	ldr r0,=0x71300014								@ 设置第二个中断屏蔽寄存器, 先将 寄存器 地址装载到 通用寄存器 R0 中 
	str r1,[r0]										@ 再将 全 1 的值设置到 寄存器中, 该寄存器的内存地址已经装载到了 R0 通用寄存器中
	mov pc, lr										@ 返回到 返回点处 继续执行后面的代码
	
disable_mmu : 
	mcr p15,0,r0,c7,c7,0							@ 设置 I-Cache 和 D-Cache 失效
	mrc p15,0,r0,c1,c0,0							@ 将 c1 寄存器中的值 读取到 R0 通用寄存器中
	bic r0, r0, #0x00000007							@ 使用 bic 位清除指令, 将 R0 寄存器中的 第 0, 1, 2 三位 设置成0, 代表 关闭 MMU 和 D-Cache
	mcr p15,0,r0,c1,c0,0							@ 将 R0 寄存器中的值写回到 C1 寄存器中
	mov pc, lr										@ 返回到 返回点处 继续执行后面的代码
	
set_serial_port : 
	ldr r0, =0x70000000								@ 将基地址装载到 r0 寄存器中, 该基地址 在 arm 核 手册中定义
	orr r0, r0, #0x13								@ 设置初始化基地址的范围, 将 r0 中的值 与 0x13 立即数 进行或操作, 将结果存放到 r0 中
	mcr p15, 0, r0, c15, c2, 4						@ 将 r0 中的值设置给 c15 协处理器 
	mov pc, lr

#define CLK_DIV0 0x7E00F020							@ 定义 CLK_DIV0 寄存器地址, 时钟的分频参数都是通过该寄存器进行设置的 
#define OTHERS 0x7E00F900							@ 定义 OTHERS 寄存器地址, 用于设置 CPU 异步工作模式
#define CLK_VAL ( (0x0 << 0) | (0x1 << 9) | (0x1 << 8) | (0x3 << 12) ) @ 设置 CLK_DIV0 寄存器的值, 即 各个时钟分频器的参数
#define MPLL_CON 0x7E00F010							@ 定义 MPLL_CON 寄存器地址常量
#define APLL_CON 0x7E00F00C 						@ 定义 APLL_CON 寄存器地址常量
#define PLL_VAL ( (0x1 << 31) | (266 << 16) | (3 << 8) | (1 << 0) )	@ 设置 PLL 控制寄存器的值
#define CLK_SRC 0x7E00F01C							@ 定义 CLK_SRC 时钟源控制寄存器的地址常量
init_clock : 
	ldr r0, =CLK_DIV0								@ 将 CLK_DIV0 的地址装载到 r0 通用寄存器中
	ldr r1, =CLK_VAL 								@ 将 要设置给 CLK_DIV0 寄存器的值 CLK_VAL 立即数 装载到 r1 通用寄存器中; 
	str r1, [r0]									@ 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中
	
	ldr r0, =OTHERS									@ 将 OTHERS 寄存器地址存到 r0 通用寄存器中
	ldr r1, [r0]									@ 将 r0 寄存器存储的地址指向的寄存器中的值读取到 r1 通用寄存器中
	bic r1, r1, #0xc0								@ 将 r1 寄存器中的值的 第 6 位 和 第 7 位 设置成 0
	str r1, [r0]									@ 将 r1 寄存器中的值 写出到 r0 寄存器存储的地址指向的内存位置 即 OTHERS 寄存器
	
	ldr r0, =APLL_CON								@ 将 APLL_CON 寄存器地址存到 r0 通用寄存器中
	ldr r1, =PLL_VAL								@ 将 要设置给 APLL_CON 寄存器的值 PLL_VAL 立即数 装载到 r1 通用寄存器中;
	str r1, [r0]									@ 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中, 即 将 PLL_VAL 的值 设置到 APLL_CON 寄存器中
	
	ldr r0, =MPLL_CON								@ 将 MPLL_CON 寄存器地址存到 r0 通用寄存器中
	ldr r1, =PLL_VAL                                @ 将 要设置给 MPLL_CON 寄存器的值 PLL_VAL 立即数 装载到 r1 通用寄存器中;
	str r1, [r0]                                    @ 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中, 即 将 PLL_VAL 的值 设置到 MPLL_CON 寄存器中
	
	ldr r0, =CLK_SRC								@ 将 CLK_SRC 寄存器地址设置到 r0 通用寄存器中
	mov r1, #0x3									@ 将 0x3 立即数设置给 r1 寄存器
	str r1, [r0]									@ 将 r1 中存储的立即数设置给 r0 寄存器存储的地址指向的内存中, 即 CLK_SRC 寄存器中
	
	mov pc, lr
	
copy_to_ram:

	ldr r0, =0x0c000000								@ 设置 代码搬移 起始地址 首地址 , 即 SRAM 垫脚石的 首地址 , 将改地址存放在 r0 寄存器中
	ldr r1, =0x50008000								@ 设置 代码搬移 终点 首地址 , 即 内存的首地址 , 将该地址存放在 r1 寄存器中
	add r3, r0, #1024*4								@ 设置 复制多少指令到 内存中 , 这里复制 4KB 数据 从 SRAM 到 内存 中 ; 

copy_loop:											@ 循环复制代码

	ldr r2, [r0], #4								@ 取出 r0 寄存器的地址 ( 即 SRAM 中的地址 ) 中的数据 放入 r2 寄存器  , 读取完毕后 , r0 中的地址 累加 4
	str r2, [r1], #4								@ 将 r2 中的内容 , 写入到 r1 寄存器对应的地址中 , 写出完毕后 , r1 中的地址 累加 4 
	cmp r0, r3										@ 查看 r0 地址 增量 是否 增加了 4KB , 到了代码搬移的末尾
	bne copy_loop									@ 如果 r0 r3 地址不一致 , 说明还没拷贝完毕 , 继续 跳转回 copy_loop 标号 拷贝
	
	mov pc, lr
	

	
#define GPBCON 0x7F008820
#define GPBDAT 0x7F008824
light_led : 
	ldr r0, =GPBCON								    @ 将 0x7F008820 GPM 控制寄存器的地址 0x7F008820 装载到 r0 寄存器中
	ldr r1, =0x1111								 	@ 设置 GPM 控制寄存器的行为 为 Output 输出, 即每个对应引脚的设置为 0b0001 值
	str r1, [r0]									@ 将 r1 中的值 存储到 r0 指向的 GPBCON 0x7F008820 地址的内存中
	
	ldr r0, =GPBDAT									@ 将 GPBDAT 0x7F008824 地址值 装载到 r0 寄存器中
	ldr r1, =0b110101								@ 计算 GPM 数据寄存器中的值, 设置 0 为 低电平, 设置 1 为高电平, 这里设置 0 ~ 3 位为低电平, 其它为高电平
	str r1, [r0]									@ 将 r1 中的值 存储到 r0 指向的 GPBDAT 0x7F008824 地址的内存中
	mov pc, lr

( 3 ) mem.S ( 内存初始化 )

代码语言:javascript
复制
@****************************  
@File:mem.S  
@  
@内存 初始化代码 
@****************************  

.text                               @ 宏 指明代码段  
.global mem_init                    @ 伪指令  mem.S 可以理解成一个函数 , 该函数由 start.S 进行调用 , 它必须是一个全局的
mem_init:							@ 定义内存初始化的标号 , 在 start.S 中进行调用


									@ 设置 MEM_SYS_CFG 寄存器中的 [ 7 ] 位 , 设置 XmlDATA [31 : 16] pin 脚作用
									@ 这些 pin 脚 用于作为 内存输出的 数据线 的
									@ 如果 该位 为 0 , 那么 就作为 [ 31 : 16 ] 位的数据线引脚 , 这里设置为 0 即可
    ldr r0, =0x7e00f120				
    mov r1, #0x0
    str r1, [r0]


									@ 步骤一 : DRAM 控制器进入配置状态
	ldr r0, =0x7e001004				@ 将 DRAM CONTROLLER COMMAND REGISTER 寄存器地址装在到 r0 中
    mov r1, #0x4						@ 设置 DRAM 控制命令寄存器 ( DRAM CONTROLLER COMMAND REGISTER  ) 值 0x4 进入配置 ( Config ) 状态
    str r1, [r0]					@ 将 r1 装载到 r0 所指向的内存地址对应的空间中


									@ 步骤二 : 设置一系列寄存器
									
									
    ldr r0, =0x7e001010  			@刷新寄存器地址
    ldr r1, =( ( 7800 / ( 1000000000/133000000 ) + 1 ) )      @设置刷新时间
    str r1, [r0]

    ldr r0, =0x7e001014  			@CAS latency寄存器
    mov r1, #(3 << 1)
    str r1, [r0]

    ldr r0, =0x7e001018  			@t_DQSS寄存器
    mov r1, #0x1
    str r1, [r0]

    ldr r0, =0x7e00101c  			@T_MRD寄存器
    mov r1, #0x2
    str r1, [r0]

    ldr r0, =0x7e001020   			@t_RAS寄存器
    ldr r1, =( ( 45 / ( 1000000000 / 133000000 ) + 1 ) )
    str r1, [r0]

    ldr r0, =0x7e001024   			@t_RC寄存器
    ldr r1, =( ( 68 / ( 1000000000 / 133000000 ) + 1 ) )
    str r1, [r0]

    ldr r0, =0x7e001028   			@t_RCD寄存器
    ldr r1, =( ( 23 / ( 1000000000 / 133000000 ) + 1 ) )
    str r1, [r0]

    ldr r0, =0x7e00102c   			@t_RFC寄存器
    ldr r1, =( ( 80 / ( 1000000000 / 133000000 ) + 1 ) )
    str r1, [r0]

    ldr r0, =0x7e001030   			@t_RP寄存器
    ldr r1, =( ( 23 / ( 1000000000 / 133000000 ) + 1 ) )
    str r1, [r0]

    ldr r0, =0x7e001034   			@t_rrd寄存器
    ldr r1, =( ( 15 / ( 1000000000 / 133000000 ) + 1 ) )
    str r1, [r0]

    ldr r0, =0x7e001038   			@t_wr寄存器
    ldr r1, =( ( 15 / ( 1000000000 / 133000000 ) + 1 ) )
 @  ldr r2, [r0]
    str r1, [r0]

    ldr r0, =0x7e00103c   			@t_wtr寄存器
    mov r1, #0x07
    str r1, [r0]

    ldr r0, =0x7e001040   			@t_xp寄存器
    mov r1, #0x02
    str r1, [r0]

    ldr r0, =0x7e001044   			@t_xsr寄存器
    ldr r1, =( ( 120 / ( 1000000000 / 133000000 ) + 1 ) )
    str r1, [r0]

    ldr r0, =0x7e001048   			@t_esr寄存器
    ldr r1, =( ( 120 / ( 1000000000 / 133000000 ) + 1 ) )
    str r1, [r0]

    ldr r0, =0x7e00100c   			@内存控制配置寄存器
    ldr r1, =0x00010012   			@配置控制器
    str r1, [r0]

    ldr r0, =0x7e00104c   			@32位DRAM配置控制寄存器
    ldr r1, =0x0b45
    str r1, [r0]

    ldr r0, =0x7e001200   			@片选寄存器
    ldr r1, =0x150f8
    str r1, [r0]

    ldr r0, =0x7e001304   			@用户配置寄存器
    mov r1, #0x0
    str r1, [r0]								
									
									
									
									@ 步骤三 : 可以不执行 , 等待 电压 和 时钟稳定下来 , 但是电压和时钟本来就是稳定的
									

	
	
									@ 步骤四 : 内存初始化
									
									
									@ 步骤四 : 内存初始化 1. 写入 NOP 命令 : 
									
									@ 执行过程 : 向 Direct Command Register 的 Memory Command [19:18] 域中写入 0b11
									@ 整体值为 0b 11 00 0000 0000 0000 0000 , 转为 16 进制为 0xC0000
									
	ldr r0, =0x7e001008				@ 装载 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中
	ldr r1, =0xc0000				@ 装载 要写入的值 到 r1 寄存器中
	str r1, [r0]					@ 将 r1 装载到 r0 所指向的内存地址对应的空间中
	
	
									@ 步骤四 : 内存初始化 2. 写入 Precharge All 命令 : 
									
									@ 执行过程 : 向 Direct Command Register 的 Memory Command [19:18] 域中写入 0b00
									@ 整体值为 0b 00 00 0000 0000 0000 0000 , 转为 16 进制为 0x0
									
	ldr r0, =0x7e001008				@ 装载 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中
	ldr r1, =0x0					@ 装载 要写入的值 到 r1 寄存器中
	str r1, [r0]					@ 将 r1 装载到 r0 所指向的内存地址对应的空间中
									 

									@ 步骤四 : 内存初始化 3 , 4 . 写入 Autorefresh 命令 : 该步骤执行两次
									
									@ 执行过程 : 向 Direct Command Register 的 Memory Command [19:18] 域中写入 0b01
									@ 整体值为 0b 01 00 0000 0000 0000 0000 , 转为 16 进制为 0x40000
									
	ldr r0, =0x7e001008				@ 装载 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中
	ldr r1, =0x40000				@ 装载 要写入的值 到 r1 寄存器中
	str r1, [r0]					@ 将 r1 装载到 r0 所指向的内存地址对应的空间中	

	ldr r0, =0x7e001008				@ 装载 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中
	ldr r1, =0x40000				@ 装载 要写入的值 到 r1 寄存器中
	str r1, [r0]					@ 将 r1 装载到 r0 所指向的内存地址对应的空间中	


									@ 步骤四 : 内存初始化 5. 写入 MRS 命令 : ( EMRS )
									
									@ 执行过程 : 向 Direct Command Register 的 Memory Command [19:18] 域中写入 0b10
									@ 同时还需要设置 Bank Address , 该步骤设置的是 EMRS 的 Bank Address
									@ 整体值 转为 16 进制为 0xa0000
									
	ldr r0, =0x7e001008				@ 装载 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中
	ldr r1, =0xa0000				@ 装载 要写入的值 到 r1 寄存器中
	str r1, [r0]					@ 将 r1 装载到 r0 所指向的内存地址对应的空间中	



									@ 步骤四 : 内存初始化 6. 写入 MRS 命令 : ( MRS )
									
									@ 执行过程 : 向 Direct Command Register 的 Memory Command [19:18] 域中写入 0b10, 
									@ 同时还需要设置 Bank Address , 该步骤设置的是 MRS 的 Bank Address
									@ 整体值 转为 16 进制为 0x80032
									
	ldr r0, =0x7e001008				@ 装载 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中
	ldr r1, =0x80032				@ 装载 要写入的值 到 r1 寄存器中
	str r1, [r0]					@ 将 r1 装载到 r0 所指向的内存地址对应的空间中		
									 


									@ 步骤五 : DRAM 控制器进入 Ready 状态
	ldr r0, =0x7e001004				@ 将 DRAM CONTROLLER COMMAND REGISTER 寄存器地址装在到 r0 中
    mov r1, #0x0						@ 设置 DRAM 控制命令寄存器 ( DRAM CONTROLLER COMMAND REGISTER  ) 值 0x0 进入配置 ( Ready ) 状态
    str r1, [r0]					@ 将 r1 装载到 r0 所指向的内存地址对应的空间中
	
					
									@ 步骤六 : 检查 DRAM 控制器 是否 进入 Ready 状态

check_ready:
    
    ldr r0, =0x7e001000 			@ 将 DRAM CONTROLLER STATUS REGISTER 地址 装载到 r0 寄存器中
    ldr r1, [r0]					@ 将 r0 寄存器存储的地址对应的内存中的内容装载到 r1 寄存器中 , 这个 DRAM CONTROLLER STATUS REGISTER 寄存器的值就获取到了
    mov r2, #0x3					@ 将 立即数 3 设置给 r2 寄存器中, 用于 与操作 , 获取最后的 两位 二进制数值
    and r1, r1, r2					@ 将 r1 ( 第二个 ) 与 r2 进行 与 操作 , 将结果放入 r1 ( 第一个 ) 寄存器中
    cmp r1, #0x1					@ 将 与 结果 与 0x1 进行比较 , 如果相等 继续执行 , 如果不相等, 跳转到 check_ready 继续执行判定操作
	
    bne check_ready					@ 如果不相等, 跳转到 check_ready 继续执行判定操作
    nop
									 
									
    mov pc, lr

( 4 ) u-boot.lds ( 链接器脚本 )

代码语言:javascript
复制
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS {
	. = 0x50008000;
	
	. = ALIGN(4);
	.text :
	{
	start.o (.text)
	*(.text)
	}

	. = ALIGN(4);
	.data : 
	{
	*(.data)
	}
	
	. = ALIGN(4);
	bss_start = .;
	.bss : 
	{
	*(.bss) 
	}
	bss_end = .;
}

( 5 ) Makefile ( 编译脚本 )

代码语言:javascript
复制
all: start.o mem.o
	arm-linux-ld -Tu-boot.lds -o u-boot.elf $^		
	arm-linux-objcopy -O binary u-boot.elf u-boot.bin 
	
%.o : %.S
	arm-linux-gcc -g -c $^
	
%.o : %.c
	arm-linux-gcc -g -c $^
	
.PHONY: clean	
clean:
	rm *.o *.elf *.bin

2. 编译输出可执行文件


编译过程 :

  • 1.文件准备 : 将 汇编代码 ( start.S ) 链接器脚本 ( gboot.lds ) makefile 文件 拷贝到编译目录 ;
  • 2.执行编译命令 : make ;
  • 3.编译结果 : 可以看到 生成了 编译目标文件 start.o, 链接文件 u-boot.elf, 可执行的二进制文件 u-boot.bin ;
这里写图片描述
这里写图片描述

3. 烧写代码到开发板并执行

( 1 ) OK6410 开发板启动切换方式


OK6410 开发板启动切换方式 : 通过控制 开发板右侧的 8个开关来设置启动来源;

  • 1.sd 卡启动 : (1~8) 位置 : 0, 0, 0, 1, 1, 1, 1, 1;
  • 2.nand flash 启动 : (1~8) 位置 : x, x, x, 1, 1, 0, 0, 1;
  • 3.nor flash 启动 : (1~8) 位置 : x, x, x, 1, 0, 1, 0, x;

( 2 ) 制作 SD 卡启盘 并 准备程序


制作 SD 卡启动盘 :

  • 1.找到开发板的烧写工具 : OK6410-A 开发板的烧写工具 在开发光盘 A 的 OK6410-A-1G用户光盘(A)-20160812\Linux-3.0.1\Linux烧写工具 目录下, 开发板光盘资料下载地址 ;
这里写图片描述
这里写图片描述
  • 2.设置 SD_Writer.exe 属性 ( win10系统需要进行的设置 ) : 右键点击属性, 在兼容性一栏, 设置 以 Windows 7 兼容模式运行, 并设置 以管理员身份运行此程序 ; 注意 一定要 使用管理员身份 运行 , 否则报错 , 报错信息 Select Volume Error , 无法格式化SD卡 , 无法烧写 程序 ;
这里写图片描述
这里写图片描述
  • 3.先格式化 SD 卡 : 注意这里要使用 SD_Writer 中的 format 功能进行格式化 , 按照下面的步骤, 一步一步点击确定执行 ;
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
  • 4.选择要烧写的文件 : 这里选择 OK6410-A-1G用户光盘(A)-20160812\Linux-3.0.1\Linux烧写工具\mmc_ram256.bin 文件;
  • 5.烧写文件到 SD 卡中 : 直接点击 Program 按钮, 就将启动程序烧写到了 SD 卡中;
这里写图片描述
这里写图片描述
  • 6.准备 LED 灯程序 : 将编译出的 gboot.bin 文件名修改成 u-boot.bin, 必须修改成该文件名, 否则无法烧写上去;
  • 7.将程序拷贝到 SD 卡中 : 将程序直接拷贝到 SD 卡中即可;

参考资料 : OK6410烧写裸板程序方法 这是之前写过的博客, 仅作为参考;


( 3 ) SecureCRT 连接开发板并烧写程序


SecureCRT 连接开发板并烧写程序 步骤 :

  • 1.硬件连接操作 : 使用 USB 转 串口工具 将电脑 与 开发板链接, USB 插在电脑端, 串口端插在 开发板上, 插上电源适配器, 但是不要打开电源开关;
  • 2.开发板设置 : 将开发板右侧的开关设置成 SD 卡启动, 即 (1~8) 位置 : 0, 0, 0, 1, 1, 1, 1, 1; 该步骤很重要;
  • 2.查询串口端口号 : 在设备管理器中查看串口端口号, 这里可以看到是 COM9;
这里写图片描述
这里写图片描述
  • 3.SecureCRT 连接串口 : 打开 SecureCRT 软件, 点击快速连接, 然后在弹出的对话框中按照下面进行配置, ① 首先要选择 Serial 协议, ② 然后选择端口, 这个端口从设备管理器中查看, ③ 波特率选择 115200, ④ 取消 RTS/CTS 选项;
这里写图片描述
这里写图片描述
  • 4.打开开发板 ( 很重要 ) : 选中 SecureCRT 软件, 然后按住空格键不放, 这个操作很重要, 打开开发板开关, ① 先按住空格键, ②再打开开关;
这里写图片描述
这里写图片描述
  • 5.首先格式化 Nand Flash : 选择 [1] 选项, 格式化 Nand Flash;
这里写图片描述
这里写图片描述
  • 6.选择从 SD 卡中烧写 : 选择 [2] Burn image from SD card 选项, 从 SD 卡中向开发板烧写程序;
这里写图片描述
这里写图片描述
  • 7.选择烧写 u-boot : 选择 [2] Flash u-boot, 烧写 u-boot, 会从 SD 卡中查找 u-boot.bin 文件, 然后烧写到 nand flash 中, 如果 SD 卡中 没有 u-boot.bin 会报错;
这里写图片描述
这里写图片描述
  • 8.设置从 Nand Flash 启动 : 设置开发板上的启动开关, (1~8) 位置 : x, x, x, 1, 1, 0, 0, 1; 此时 四个 LED 全亮;
  • 9.效果展示 : 设置的 GPBDAT 寄存器值为 0b110000, 四个 LED 灯都亮起来;
这里写图片描述
这里写图片描述
  • 10.修改 LED 灯显示参数后显示结果 : 设置 GPBDAT 寄存器中值为 0b110101 是 第一个 和 第三个 LED 亮起来;
这里写图片描述
这里写图片描述
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一. ARM 启动流程
    • 1. 各种类型开发板启动流程
      • ( 1 ) 2440 开发板启动流程简介 ( ① Nand Flash 拷贝 4 KB -> SRAM 垫脚石 | ② PC 指向 0 地址 即 SRAM 起始地址执行 | ③ 初始化内存 拷贝 后续指令到内存执行 )
      • ( 2 ) 6410 开发板启动流程简介 ( ① 上电 运行 SROM 的 BL0 程序 | ② NandFlash -> SRAM 拷贝 8KB 指令 | ③ 执行 SRAM 指令 拷贝其余 BL 指令到内存中执行 )
      • ( 3 ) 210 开发板启动流程简介 ( ① 上电 运行 SROM 的 BL0 程序 | ② NandFlash -> IRAM 垫脚石 拷贝 96KB 指令 | ③ 执行 IRAM 指令 拷贝其余 BL 指令到内存中执行 )
  • 二. 代码搬移 简介
    • 1. 从 SRAM -> 内存
      • ( 1 ) 代码搬移 简介 ( ① 代码搬移起点 | ② 从 SRAM 搬移 的 原因 | ③ 不搬移也可正常运行 )
      • ( 2 ) 代码搬移 起点 ( SRAM 首地址 文档中查询 | 6410 开发板 : 0x0C00_0000 )
      • ( 3 ) 链接地址 简介 ( 链接起始地址 | 反汇编 | _start 入口函数 | 指令汇编地址 )
      • ( 4 ) 链接地址 作用 ( C 语言 函数调用 | 汇编 ldr 修改 PC 指针 )
      • ( 5 ) 指令 跳转 ( 相对跳转 | 绝对跳转 )
      • ( 6 ) 代码搬移终点 ( 链接器脚本首地址 | 6410 代码搬移终点首地址 0x50008000)
  • 三. 代码搬移 汇编代码
    • 1. 汇编代码编写
      • ( 1 ) 代码搬移 汇编代码
      • ( 2 ) 汇编完整代码
      • ( 3 ) mem.S ( 内存初始化 )
      • ( 4 ) u-boot.lds ( 链接器脚本 )
      • ( 5 ) Makefile ( 编译脚本 )
    • 2. 编译输出可执行文件
      • 3. 烧写代码到开发板并执行
        • ( 1 ) OK6410 开发板启动切换方式
        • ( 2 ) 制作 SD 卡启盘 并 准备程序
        • ( 3 ) SecureCRT 连接开发板并烧写程序
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档