【嵌入式开发】ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
一. 内存 简介
1. 两大内存分类
( 1 ) DRAM 简介 ( 定期刷新 | 速度慢 | 成本低 )
DRAM 简介 :
- 1.硬件描述 : DRAM 基本由一个个小电容基本原件组成, 电容的两端保留电荷;
- 2.优缺点描述 :
- ① 优点 : 成本很低, 很便宜;
- ② 缺点 : 需要 定期刷新数据, 速度较慢;
- a.定期刷新 : DRAM 需要定期给其存储介质 ( 电容 ) 充电, 刷新数据, 否则数据会丢失;
- b.速度慢 : 其存取速度比较慢;
- 3.使用场景 : 一般开发板上使用的是 DRAM, 如 128M , 256 M 内存的 开发板 使用的就是 DRAM 内存 , 这些内存比较便宜 ;
( 2 ) SRAM 简介 ( 不需刷新 | 存取速度快 | 功耗大 | 成本高 )
SRAM 简介 :
- 1.使用特性 ( 不需刷新 ) : SRAM 具有 静止存取 的功能, 不需要定期刷新其存储介质, 就可以保存数据;
- 2.优缺点介绍 :
- ① 优点 : 访问存取速度很快;
- ② 缺点 : a. 功耗比较大, b. 成本很高;
- 3.使用场景 : CPU 内部一般使用 SRAM, 一般只有 几KB 大小, 其访问速度非常快, 如 启动时的 stepping stone ( 垫脚石 ) ;
2. DRAM 分类 ( SDRAM | DDR | DDR2 )
DRAM 分为 SDRAM, DDR, DDR2 三种类型;
(1) SDRAM 简介 ( 动态随机访问存储器 | 同步时钟 | 动态刷新 | 随机访问 )
SDRAM 简介 :
- 1.全称 : Synchronous Dynamic Random Access Memory, 即 同步 动态 随机访问 存储器;
- 2.同步时钟 : 内存中的 数据传输 和 内部命令传输 都是以 同步时钟为准, 所有的工作都基于该同步时钟;
- 3.动态刷新 : 内存的 存储单元 需要 不断的刷新 , 以 保证数据的存在 ;
- 4.随机访问 : 数据访问可以 不按照 线性次序进行 , 可以 *读写任意一个地址的数据 ;
- 5.使用场景 : 2440 开发板 的 内存 一般是 SDRAM ;
(2) DDR 和 DDR2 ( DDR 是 SDRAM 传输速率的 2 倍 | DDR2 是 DDR 传输速率的 2 倍 )
DDR 和 DDR2 简介 :
- 1.DDR ( Double Data Rate SDRAM ) : 即 双倍速率 同步动态随机存储器 , 与 上一节 介绍的 SDRAM 对比 ,:
- ① SDRAM 传输数据 : SDRAM 只能在 时钟脉冲 的 上升沿 传输数据 , 不能再 下降沿 传输数据 ;
- ② DDR 传输数据 : DDR 除了 在 时钟脉冲 的 上升沿 传输数据外 , 还能 在 时钟脉冲的 下降沿 传输数据 ;
- ③ SDRAM 与 DDR 对比结果 : 如果 时钟脉冲 频率相同 , DDR 的数据传输速率 是 SDRAM 的 2 倍 ;
- 2.DDR2 简介 : DDR2 在 DDR 的 基础上 进行了技术上的 改进 , DDR2 的数据传输速率是 DDR 的 2倍 ;
- 4.使用场景 : 6410 开发板 使用 DDR 内存 , 210 开发板 的 内存 是 DDR2 类型的 ;
3. 内存的内部结构
( 1 ) Logical Bank ( 行列表格 | L-Bank 4 个组成内存 | 内存寻址信息 ① L-Bank 选择信号 ② 行地址 ③ 列地址 )
L-Bank 简介 :
- 1.内部逻辑 ( 表格结构 ) : 内存 内部 逻辑 与 表格 类似 , 数据 存放在 单元格 中 , 这个 表格 有 行 和 列 , 可以根据 行号 和 列号 读取 对应单元格 中的数据 ;
- 2.数据读写 : 读取 内存 表格 中的数据时 , 指定 行号 ( 行地址 ) 和 列号 ( 列地址 ) , 根据 两个地址值 , 就可以 准确的找到 存储数据的 单元格 ;
- 3.L-Bank 引入 : 上面所描述的 表格 , 就是 Logical-Bank ( L-Bank ) ;
- 4.L-Bank 数量 : 在 一个 内存中 , 将 所有的 内存单元 封装到一个 L-Bank 成本很高 , 一般情况下 会 在 一块内存 中 设置 4 个 L-Bank ;
- 5.内存寻址信息 : 如果 要 到 内存中 存取数据 , 需要 三种数据 , 来寻找对应数据 ;
- ① L-Bank 选择信号 : 选择 内存中 哪个 L-Bank , 一个内存有 4 个 L-Bank ;
- ② 行地址 : L-Bank 的 行号 ;
- ③ 列地址 : L-Bank 的 列号 ;
4. 推导 内存 容量 计算公式
( 1 ) 内存 容量 计算公式 ( 内存容量 = 4 * L-Bank 单元格数目 * 单元格容量 )
内存容量 = L-Bank 个数 * L-Bank 容量
L-Bank 个数 一般 是 4 个
L-Bank 容量 = 单元格数目 * 单元格容量
内存容量 = 4 * L-Bank 单元格数目 * 单元格容量
( 2 ) 举例说明
计算如下内存大小 : 下面 截图 是 一款 内存芯片说明
- 1.参数列举 : 该 SDRAM 有 4 个 L-Bank , 每个 L-Bank 有 4M 个 存储单元 , 每个存储单元 有 16 bit , 即 2 字节 ;
- 2.计算结果 : 可以得到 4 * 4M * 2Byte = 32MB , 该内存大小为 32MB ;
二. 2440 开发板 内存初始化
1. 2440 地址空间
( 1 ) S3C2440 芯片地址线 ( ADDR0 ~ ADDR26 27根地址线 )
2440 芯片地址线 :
- 1.地址线 : 查询 mini2440 原理图 , 可以看到 2440 芯片 对外提供的 引脚 , 在 左侧 的有 27 根地址线 ADDR0 ~ ADDR26 ; 该手册 在 博客提供的下载文件中 的 2440 手册目录下 ;
- 2.可访问外设空间大小 : 有 27 根 地址线 , 说明 它能 访问 2 的 27 次方 大小的空间 , 即 128M 的内存空间 ;
计算其访问的字节数 : 2^27 = 134217728 字节 ( Byte ) , 将 Byte 转为 MB : 134217728 / 1024 / 1024 = 128 MB ;
- 3.内存太小 : 如果只有 128M 内存 , 这个内存太小了 , 需要使用下面的方法 扩大内存 , 如 片选信号 ;
( 2 ) 片选信号 ( nGCS0 ~ nGCS7 8 个片选信号 | 8 * 128MB = 1024MB = 1GB 内存 )
片选信号 :
- 1.片选信号引入 : 2440 芯片 有 27 根 地址线 , 能访问 128MB 的内存 , 为了 扩大 访问范围 , 这里 提供 多个芯片 , 即 27 根地址线 , 可以 访问 不同的 内存芯片 , 这样 就实现了 扩大内存的 目的 ; 转换芯片 就需要用到 片选信号 ;
- 2.2440 片选信号 : S3C2440 芯片 , 提供了 8 个片选信号 , 分别是 nGCS0 ~ nGCS7 信号 ;
- 3.片选信号选中 : 当 nGC0 ~ 7 中的 某个信号 出现有效值 , 那么 S3C2440 芯片的 27 根地址线 , 就 访问 该信号对应的 128MB 内存空间 ;
- 4.芯片内存空间计算 : S3C2440 芯片 有 8 * 128MB = 1024MB = 1GB 内存 ;
( 3 ) 片选信号 与 内存地址
片选信号 与 内存地址 :
- 1.查询 S3C2440 文档 : S3C2440.pdf , Page 194 , 展示了 S3C2440 芯片的 片选分布图 ;
- 2.两种启动方式 : 左侧 展示的是 NOR Flash 启动方式 , 右侧展示的是 Nand Flash 启动方式 , Nand Flash 中可以看到 0 区域是 4KB 的 SRAM 垫脚石 ;
- 3.内存地址由来 : 一般情况下 , 内存是安排在 片选 6 和 片选 7 中 , 片选 6 的起始地址是 0x30000000 , 这也是 内存起始地址是 0x30000000 的原因 ;
( 4 ) 存储控制器 ( 转换 地址 -> L-Bank 行 列 | 初始化存储控制器 )
存储控制器 简介 :
- 1.片选安排 : 芯片 将 不同的 外设 安排在 不同的 片选地址中 , 如 将内存安排在 片选 6 和 片选 7, 将网卡芯片安排在片选 3 中 , 将 Nor Flash 安排在 片选 0 上 ;
- 2.外设的地址值 : 处理器访问 外设 时 , 只会访问 一个地址 ;
- 3.CPU 对地址值不知情 : CPU 对 地址值对应的什么外设 是不知情的 , 其访问对应的地址 , 还需要通过 存储控制器 将地址转换为 对应的 存储单元 ;
- 4.存储控制器 : 通过 存储控制器 , 对 地址 进行相应处理 , 将地址分解为 ① 内存的 L-Bank 序号 , L-Bank 中的 ② 行地址 和 ③ 列地址 , 然后 才能 访问到 地址 对应的 数据存储单元 ;
- 5.初始化内存操作 : 初始化内存 就是 对 存储控制器 进行初始化 , 不是初始化 内存芯片 ;
2. S3C2440 芯片 与 内存芯片 的 硬件 连接
( 1 ) 芯片 与 内存芯片 连接方式 ( 4Banks * 4M * 16Bit = 32MB | 两个内存芯片 16位 并联到 2440 芯片的数据线上 32位 )
芯片 与 内存 芯片 连接方式 :
- 1.查询 S3C2440 对应的内存芯片手册 ( HY57V561620.pdf ) : 可以到到 其 内存芯片规格是 4Banks * 4M * 16Bit Synchronous DRAM , 可以得到 4 * 4M * 2Byte = 32MB , 该内存大小为 32MB , 每个内存芯片大小 32MB ;
- 2.规格解析 : 有 4 个 L-Bank , 每个 L-Bank 有 4M 个 存储单元 , 每个存储单元 有 16 bit , 两层含义 ① 单元格容量 2 字节 , ② 其数据宽度为 16 位 ;
- 3.2440 芯片数据线个数 : 查询 mini2440 原理图可得 , 其 数据线有 32 位 , DATA 0 ~ DATA31 ;
- 4.将内存芯片 并联 与 数据线 连接 : 内存芯片宽度为 16位 , 2440 芯片 数据线有 32 位 ; 为了不出现资源浪费 , 这里采用 两个 内存芯片并联的方式 , 每个 16 位 , 并联后 32 位 , 对应 2440 芯片的 32位 数据线 ; 2440 芯片 高 16 位 数据线 接到 一个 内存芯片上 , 2440 芯片 低16 位 接到 另一个 内存芯片上 ;
- 5.数据读取 : 2440 芯片 数据线 高 16位 从 第一个芯片上读取 16位数据 , 低 16 位数据线 从 第二个芯片上读取 16位数据 , 这样 一次可以读取 32 位 数据 ; 2440 开发板上采用的是 2 个 32MB 内存并联方式连接 , 其内存大小是 64MB ;
3. 设置 2440 存储控制寄存器
( 1 ) 存储控制器设置 ( 作用 控制 CPU 对外设访问 | 配置 | 文档 S3C2440.pdf Page193 | )
存储控制器 设置 :
- 1.存储控制器作用 : 其 作用 是 控制 CPU 对外设的访问 , 如 内存 , 网卡芯片 , NOR Flash 等 ;
- 2.存储控制器 配置 : 存储控制器 不是 完全智能的 , 需要我们 配置存储控制器 以 什么方式进行访问 ;
- 3.参考文档位置 : 在 S3C2440.pdf 芯片手册中 , 第 5 章 , Memory Controller , Page193 , 介绍了 存储控制器 工作原理 , 以及 设置方法 ;
( 2 ) BWSCON 寄存器 ( BUS WIDTH & WAIT CONTROL REGISTER 总线宽度 和 等待控制寄存器 )
BWSCON 寄存器 :
- 1.简介 :
- ① 作用 : BWSCON 寄存器 是 设置总线宽度 和 等待状态 的 控制寄存器 ;
- ② 寄存器地址 : 0x4800 0000 ;
- ③ 寄存器初始值 : 0x000 000 ;
- 2.寄存器位 与 Bank 的对应设置 : 32 位 的寄存器 , 分成 8 组 , 分别对应 一个 L-Bank 设置 ; 因此 下面 介绍 一组 值的设置即可 ;
- 3.Bank7 设置 : ST7 , WS7 , DW7 设置 ;
- ① ST7 设置 : 对应 BWSCON 的 [31] 位 , 决定 SRAM 是否使用了 UB/LB pin 脚 ; 设置 0 是没有使用 UB/SB , 设置 1 是使用了 UB/SB pin 脚 ; 其 并 没有使用 UB/LB pin 脚 , 设置 0 ;
- ② WS7 设置 : 对应 BWSCON 的 [30] 位 , 决定 Bank7 是否使用 等待状态 , 设置 0 不使用 , 设置 1 使用 ; 这里设置 0 , 不适用 等待状态 ;
- ③ DW7 设置 : 对应 BWSCON 的 [29:28] 位 , 决定 Bank7 总线宽度 , 00 = 8位总线宽度 , 01 = 16位总线宽度 , 10 = 32位总线宽度 , 11 = 保留位 ; 查看芯片手册可以知道 , 2440 的总线宽度时 32 位的 , 这里设置 10 值 ;
- ④ 寄存器值确定 : 这里 只配置 Bank6 和 Bank7 , 其它都配置成 0 , 因此 Bank7 配置 为 0b0010 , Bank6 与 Bank7 配置一样 , 也是 0b0010 , 其它位均使用 0 默认值 ; 最终 BWSCON 寄存器值为 0b 0010 0010 0000 0000 0000 0000 0000 0000 , 十六进制值 为 0x22000000 ;
- 4.BWSCON 寄存器设置 :
0x22000000
( 3 ) BANKCON 寄存器 ( BANK CONTROL REGISTER : Bank 0 ~ 5 控制寄存器 )
BANKCON 0~ 5 寄存器 :
- 1.简介 : 该 寄存器 是 一组寄存器 , 共 6 个, 每个寄存器 都 控制 一个 Bank ;
- 2.寄存器值设置 : 这一组寄存器是 控制 Bank 0 ~ Bank5 的, 内存是在 Bank6 和 Bank7 中, 因此这里 保持默认值 0x0700 不变即可;
- 3.Bank0 ~ Bank5 控制寄存器设置 :
0x00000700
0x00000700
0x00000700
0x00000700
0x00000700
0x00000700
( 4 ) BANKCON 寄存器 ( BANK CONTROL REGISTER : Bank 6 ~ 7 控制寄存器 )
BANKCON 6~ 7 寄存器 :
1.内存类型 MT ( Memory Type ) 设置 : [ 16:15 ] 位 , 决定 Bank6 或 Bank7 的 内存 的类型 , 2440 中 , 内存使用的是 SRAM , 这里取值 11 ;
-
2.设置其他参数 : 之后的 14 位 被分为 两种情况 , 当 MT 类型设置为 ROM 或 SRAM 时 , 即 [ 16:15 ] 取 00 值 , 需要设置 [ 14 : 0 ] 位 ; 当 MT 类型设置为 SDRAM 时 , 即 [16:15] 取 11 值 , 只需要设置 [ 3 : 0 ] 位 即可 ; 2440 芯片 使用的是 SDRAM , 这里只需要设置 [ 3 : 0 ] 位 即可 , 此时 [ 14 : 4 ] 位 全部取值 0 ;
- ① Trcd 设置 : [ 3 : 2 ] 表示的是 行列地址选择 信号的转换延时 , 00 代表 2 个时钟 , 01 代表 3 个时钟 , 10 代表 4 个时钟 , 该值在 时序图中 明确指出 , Trcd 取值 是 2 个时钟 , 这里设置该值为 00 ;
- ② SCAN 域设置 : [ 1 : 0 ] 位 设置 , 表示 列地址 数目 , 00 表示 8 位 , 01 表示 9 位 , 10 表示 10 位 ; 行地址 和 列地址 信息需要去查询 SRAM 内存芯片文档 HY57V561620F(L)T§Series_(Rev1.1).pdf , 其中可以查到 Column Address : CA0 ~ CA8 , 有 9列 , 该 SCAN 域 取值为 01 ;
- ① Trcd 设置 : [ 3 : 2 ] 表示的是 行列地址选择 信号的转换延时 , 00 代表 2 个时钟 , 01 代表 3 个时钟 , 10 代表 4 个时钟 , 该值在 时序图中 明确指出 , Trcd 取值 是 2 个时钟 , 这里设置该值为 00 ;
3.BANKCON 寄存器值 : 0b 11 ( 内存类型设置 ) 00000000000 ( SRAM 时设置 0 , ROM 时 设置其它 ) 00 ( 行列地址信号转换延迟 2 时钟 ) 01 ( 设置列地址有 9 位 ) , 二进制值 0b11000000000000001 , 转为 16 进制 为 0x18001 ;
4.Bank6 和 Bank7 控制寄存器设置为 :
0x00018001
0x00018001
( 5 ) REFRESH CONTROL REGISTER 刷新控制寄存器 ( 负责控制 SDRAM 刷新 )
刷新控制寄存器 : 管理 SRAM 刷新 , SRAM 工作原理是需要不断的 定期 的 进行刷新 ;
- 1.REFEN 域 ( Refresh Enable ) : [ 23 ] 位 ; 设置 SDRAM 是否需要刷新 ; 0 = 不刷新 , 1 = 刷新 ; 这里选择 1 , 要进行刷新 ;
- 2.TREFMD 域 ( Refresh Mod ) : [ 22 ] 位 ; 设置 SDRAM 的刷新模式 ; 0 = CBR/Auto Refresh 自动刷新 , 1 = Self Refresh 自己刷新 ; 这里我们选择 0 自动刷新 ;
- 3.Trp 域 : [ 21 : 20 ] 位 ; 设置 SDRAM 的准备充电时间 ; 00 = 2 个时钟 , 01 = 3 个时钟 ; 10 = 4 个时钟 , 11 = 不准备充电 ; 翻到 205 页 的时序图中 , 可以看到 Trp 预充电时间是 2 个时钟 , 这里设置 00 即可 ;
- 4.Tsrc 域 : [ 19 : 18 ] 位 ; 设置 一行 需要 刷新的时间 ; 该值通常是 7 个时钟 , 这里使用默认值 11 , 代表刷新一行需要 7 个时钟时间 ;
- 5.不支持的域 : [ 17 : 16 ] 和 [ 15 : 11 ] 位 不支持 , 暂时没有使用 , 前者取值 00 , 后者取值 00000 ;
- 6.Refresh Counter 域 : [ 10 : 0 ] 位 ; 刷新计数值 , SDRAM 每隔一段时间就需要刷新 , 这个时间间隔由该域进行设定 ;
- ① 计算公式 : 刷新时间 与 刷新计数 公式 : refresh_period = ( 2^11 - refresh_count + 1 ) / HCLK , HCLK 是提供给内存使用的时钟 , 其内存时钟是核时钟的 1/4 即 100MHz, 推导出 refresh_count = 2 ^ 11 + 1 - refresh_peroid * HCLK ;
- ② 计算案例 : 这里文档中给出一个示例 , 如果 refresh_period 为 7.8us , HCLK 为 100MHz , 计算出 refresh_count = 2 ^ 11 + 1 - 100 * 7.8 = 1269 ;
- ③ 最终取值 : 这里我们取值 1269 , 转为二进制 10011110101 ;
- 7.SDRAM 刷新控制寄存器取值 : REFEN = 1 ; TREFMD = 0 ; Trp = 00 ; Tsrc = 11 ; 不支持域 [ 17 : 11 ] = 00 00000 ; Refresh Counter = 10011110101 ; 整合为 0b100011000000010011110101 , 转为十六进制 0x8C04F5 ;
( 6 ) BANK SIZE 寄存器 ( 设置 BANK 的大小 )
BANK SIZE 寄存器设置 :
-
1.BURST_EN :
- ① 位数 : 寄存器位数 [ 7 ] ;
- ② 作用 : 设置 ARM 核 的 突发模式 ( burst operation ) 是否使能 ;
- ③ 突发模式 : 突发模式 是 访问内存时 , 一次性可以使用 批量数据 , 使用突发模式后 , 可以提高内存访问效率 ;
- ④ 取值 : 一般情况下 , 打开 突发模式 , 这里设置 1 ;
2.Reserved : 寄存器位数 [ 6 ] , 没有使用该位 ; 使用默认值 0 ;
3.SCKE_EN : 寄存器位数 [ 5 ] ; 是否使用节电模式 ; 0 = 不使用 , 1 = 使用 ; 这里 我们 设置 1 , 打开节电模式 ;
4.SCLK_EN : : 寄存器位数 [ 4 ] ; 这里文档中推荐设置成 1 , 直接设置 1 即可 ;
5.Reserved : 寄存器位数 [ 3 ] , 没有使用该位 ; 使用默认值 0 ;
6.BK76MAP : 寄存器位数 [ 2 : 0 ] ; 设置 BANK 6 / 7 容量大小 , 之前讲到过 内存芯片 与 硬件的连接方式是 两块 32M 的内存连接 , 形成 64 M 容量 , 这里设置 001 代表其 容量是 64MB ;
7.寄存器值 : BURST_EN [ 7 ] = 1 ; Reserved [ 6 ] = 0 ; SCKE_EN [ 5 ] = 1 ; SCLK_EN [ 4 ] = 1 ; Reserved [ 3 ] = 0 ; BK76MAP [ 2 : 0 ] = 001 ; 其二进制值为 0b10110001 , 转为 16 进制 为 0xB1 ;
( 7 ) SDRAM MOD REGISTER SET REGISTER ( 设置 BANK6 和 BANK7 模式的寄存器 )
SDRAM MODE REGISTER SET REGISTER : BANK6 和 BANK7 分别对应一个寄存器 MRSRB6 和 MRSRB7 , 这两个寄存器内容 和 意义 是一样的 ;
- 1.CL : 寄存器位 [ 8 : 7 ] ; 设置 CAS 潜伏期 ; 在下面 时序图 中 , 找到 CAS 信号 , 潜伏期指的是 RAS 变为低电平 到 CAS 变为 低电平 之间的时钟 , 在 时序图中可以看到 这期间 持续了 3 个时钟 ; 因此 这里 取 011 值 , 代表 3 个时钟 ;
- 2.其它域设置 : 该寄存器的 TM , BT , BL 域 的 值是固定的 , 文档中已经写了 Fixed , 因此不用设置 , 该寄存器只需要设置 CL 即可 ;
- 3.寄存器取值 : Reserved [ 11 : 10 ] = 00 ; WBL [ 9 ] = 0; TM [ 8 : 7 ] = 00 ; CL[ 6 : 4 ] = 011 ; BT [ 3 ] = 0; BL [ 2 : 0 ] = 000 ; 最终二进制值为 0b000000110000 , 转为 16 进制为 0x30 ;
( 8 ) 内存相关寄存器总结 ( 13 个寄存器 )
内存相关寄存器总结 :
- 1.BWSCON ( 总线宽度 和 等待控制寄存器 ) : 0x22000000 ;
4. 编写汇编指令设置上述寄存器值
( 1 ) 汇编循环方法设计
汇编循环方法设计 :
- 1.设置寄存器值方法 : 参考一下 设置 其它寄存器的代码 , ① 首先将地址 装在到 r0 寄存器中 , ② 然后 将 要设置的 寄存器值 装载到 r1 寄存器中 , ③ 最后将 r1 寄存器中的值 设置 到 r0 寄存器地址对应的内存中 ;
#define CLK_DIV0 0x7E00F020 @ 定义 CLK_DIV0 寄存器地址, 时钟的分频参数都是通过该寄存器进行设置的
#define OTHERS 0x7E00F900 @ 定义 OTHERS 寄存器地址, 用于设置 CPU 异步工作模式
#define CLK_VAL ( (0x0 << 0) | (0x1 << 9) | (0x1 << 8) | (0x3 << 12) ) @ 设置 CLK_DIV0 寄存器的值, 即 各个时钟分频器的参数
ldr r0, =CLK_DIV0 @ 将 CLK_DIV0 的地址装载到 r0 通用寄存器中
ldr r1, =CLK_VAL @ 将 要设置给 CLK_DIV0 寄存器的值 CLK_VAL 立即数 装载到 r1 通用寄存器中;
str r1, [r0] @ 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 2.引入循环 : 在上面的方法中 , 每设置一个寄存器 , 需要 3 行代码 , 如果设置 内存的 13 个寄存器 , 需要 39 行代码 , 代码量略高 , 这里可以使用循环来进行设置 ;
- 3.循环方案 :
- ① 值处理 : 将 13 个寄存器值 , 用数组存储起来 , 用指针指向首地址 即可 , 之后指针递增即可 ;
- ② 寄存器地址处理 : 13 寄存器 的 地址都是递增的 , 只要记录 第一个寄存器 BWSCON 地址 0x48000000 即可 , 之后递增就行 ; 最后一个寄存器 MRSRB7 地址为 0x48000030 作为循环退出条件 ;
( 2 ) 汇编实现
实现的汇编代码 : 详细请看注释 :
#define mem_contrl 0x48000000 @ 定义 13 个寄存器中第一个寄存器 BWSCON 内存地址 0x48000000
init_sdram: @ 标号, 执行该段代码的入口
ldr r0, =mem_contrl @ 将寄存器内存地址装在到 r0 寄存器中
add r3, r0, #4*13 @ 计算出结束循环的寄存器地址 , r0 中存储的地址 加上 4字节 * 13个寄存器, 是 0x48000034, 即 MRSRB7 寄存器的结束地址
adrl r1, mem_data @ 将 13 个寄存器数字的首地址 装载到 r1 寄存器中, 之后每次使用完 地址 加 4 即可
0: @ 0 作为循环跳转用的标号
ldr r2, [r1], #4 @ 从 r1 指针指向的地址中取出数据 , 将该数据 存储到 r2 中 , 取出数据后 , r1 指针 加 4
str r2, [r0], #4 @ 将 r2 寄存器中的数据 写出到 r0 指针指向的内存地址中 ( 即实际的内存控制寄存器中 ) , 之后 r2 指针 加 4
cmp r0, r3 @ 循环控制 : 对比 r0 指针 与 最后一个 ( 第 13 个 ) 寄存器 末地址进行对比
bne 0b @ 如果不相等 , 跳转 , 0b 代表向前跳转到 0 标号处
mov pc, lr @ 如果相等 , 那么整个方法执行完毕, 13 个寄存器都设置完毕
mem_data: @ 13 个寄存器值 数组, .long 是伪指令 , 指明每个数据的长度
.long 0x22000000 @ BWSCON 寄存器值 , 设置 总线宽度 和 等待状态
.long 0x00000700 @ BANKCON0 寄存器 , 设置 BANK0 的寄存器
.long 0x00000700 @ BANKCON1 寄存器 , 设置 BANK1 的寄存器
.long 0x00000700 @ BANKCON2 寄存器 , 设置 BANK2 的寄存器
.long 0x00000700 @ BANKCON3 寄存器 , 设置 BANK3 的寄存器
.long 0x00000700 @ BANKCON4 寄存器 , 设置 BANK4 的寄存器
.long 0x00000700 @ BANKCON5 寄存器 , 设置 BANK5 的寄存器
.long 0x00018001 @ BANKCON6 寄存器 , 设置 BANK6 的寄存器
.long 0x00018001 @ BANKCON7 寄存器 , 设置 BANK7 的寄存器
.long 0x008c04f5 @ REFRESH 寄存器 , 刷新控制寄存器
.long 0x000000b1 @ BANKSIZE 寄存器 , 设置 BANK 大小 ;
.long 0x00000030 @ MRSRB6 寄存器, 设置 BANK6 模式
.long 0x00000030 @ MRSRB7 寄存器, 设置 BANK7 模式
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
三. 6410 开发板 内存初始化
1. S3C6410 地址空间
( 1 ) 6410 地址空间分布 ( 保留区 2GB | 外设区 256MB | 主存储区 2GB )
6410 地址空间分布 :
- 1.地址总线与空间大小 : S3C6410 的 处理器 有 32 位 地址总线 , 因此其寻址空间 为 4GB , 与 2440 相同 ;
- 2.空间分布 : S3C6410 的 4GB 空间被分为 3 个区域 , 分别是 ① 保留区 , ② 外设区 , ③ 主存储区 ;
- 3.保留区 ( 0x80000000 ~ 0xFFFFFFFF ) : 2G ~ 4G 的空间 , 占 2G 空间 , 没有使用 , 暂时保留 ;
- 4.外设区 ( 0x70000000 ~ 0x7FFFFFFF ) : 占 256MB 空间 , 该段地址主要存放 6410 芯片的各种寄存器 , 之前进行的 看门狗 , 时钟 , 内存初始化 等操作 , 相关寄存器的地址都处于该 内存段 ; 如 下 举例 :
- 5.主存储区 ( 0x00000000 ~ 0x6FFFFFFF ) : 占 1972 MB 空间 , 下面小节详细展开讲解 ;
- 6.与 2440 对比 :
- ① 2440 内存空间分布 : 从大的角度来说 , 2440 的地址空间 分为 ① 内部空间 和 ② 外设空间 , 加起来 共 4GB 大小 ;
- ② 2440 内存外设空间 : 大小 1GB, 分为了 8个 Bank, 每个 Bank 128MB ;
( 2 ) 6410 主存储区划分 ( 保留区 2GB | 外设区 256MB | 主存储区 2GB )
S3C6410 主存储区 ( 0x00000000 ~ 0x6FFFFFFF ) 划分 :
- 1.Boot 镜像区 :
- ① 大小 范围 : 128MB , 地址范围 0x0000 0000 ~ 0x07FF FFFF ;
- ② 作用 : 启动映射 , 该区域 不会 固定地 关联 某个 硬件 , 当选中某硬件作为启动设备时 , 如 SD卡驱动 , NandFlash ( I-ROM ) 启动 , 会将该硬件 映射到该区域中 ;
- ③ 举例 : 将 I-ROM 设置为启动设备 , 即 将其 映射到 Boot Image 中 , ARM 处理器一旦上电 , 就会运行 第一条指令 , 即 运行的是 I-ROM 中的指令 ;
- 2.内部存储区 :
- ① 大小 和 范围 : 128MB 大小 , 地址范围 0x0800 0000 ~ 0x0FFF FFFF ;
- ② 分为两部分 :
- a. I_ROM : 64MB , 地址范围 : 0x0800 0000 ~ 0x0BFF FFFF ;
- b. I_SRAM : 64MB , 地址范围 : 0x0C00 0000 ~ 0x0FFF FFFF ;
- ③ S3C6410 的 I_ROM 和 I_SRAM : 6410 中 I_ROM 只有 32KB , I_SRAM ( 垫脚石 ) 只有 8KB , 这两个区域使用时都没有占满 ;
- 3.静态存储区 : 6 * 128MB , 地址范围 0x1000 0000 ~ 0x3FFF FFFF ; 可以用于外接设备 , 如 NorFlash ; 该区域 被 分为 6 个 Bank , 每个 Bank 分为 128MB 大小 ;
- 4.保留区 : 2 * 128 MB , 地址范围 0x4000 0000 ~ 0x4FFF FFFF ;
- 5.动态存储区 : 2 * 256MB , 地址范围 0x5000 0000 ~ 0x6FFF FFFF ;
- ① 内存描述 : 将程序下载到 6410 开发板内存中 , 其内存地址就是 0x5000 0000 , 即 动态存储区 ;
- ② 与 2440 的内存首地址 对比 : 2440 开发板 芯片内存是 0x3000 0000 , 这是因为 芯片 将 内存放在了 Bank6 位置 , 这个 Bank 6 的起始地址就是 0x3000 0000 ; 在 6410 中 , 将内存放在了 动态存储区中 , 因此 6410 开发板上的内存地址是从 0x5000 0000 开始的 ;
2. S3C6410 内存芯片硬件连接方式
S3C6410 开发板内存芯片介绍 :
- 1.内存芯片容量及连接方式 : 6410 开发板 内存容量是 256MB , 是由 2 片 128MB 内存芯片并联起来的 ;
- 2.芯片地址线和数据线 : 两个 芯片 的 地址线 和 数据线 都是 16 位 , 其 16 位的地址线是相同的 , 但是 其数据线不同 , 即 有 32 位 的数据线用于输出 , 如下图 :
回顾 : 2440 开发板内存容量是 64MB , 是由 2 个 32MB 的芯片并联起来 , 形成的 64MB 的容量 ;
3. S3C6410 内存初始化
( 1 ) 6410 内存初始化流程 ( Config 状态 | 寄存器设置 | 电压时钟稳定 | 内存初始化 | Ready 状态 | 检查 Ready 状态 )
S3C2440 开发板 内存初始化 , 只需要设置 13 个寄存器的值即可 , 对于设置的顺序 是没有要求的 ; 但是 S3C 6440 开发板 的内存初始化 , 需要按照指定的流程 来进行操作 ;
S3C6410 内存初始化流程 : 文档位置 : S3C6410X.pdf , Page 192 , 5.4.1 DRAM CONTROLLER INITIALIZATION SEQUENCE ;
- 1.存储控制器进入配置 ( Config ) 状态 : memc_cmd 写入3位二进制数字 0b100 , 设置 DRAM 存储控制器 进入 Config 状态 ;
- ① Memc_cmd 介绍 : Memc_cmd 是 DRAM CONTROLLER COMMAND REGISTER 寄存器中的 [ 2 : 0 ] 位 ;
- ② 文档位置 : S3C6410X.pdf , Page 193 , 5.5.2 DRAM CONTROLLER COMMAND REGISTER ;
- ③ Memc_cmd 作用 : 设置 DRAM 存储控制器进入不同的状态 ;
- 2.向一系列相关寄存器中写入参数 : 内存时序参数寄存器 , 芯片配置寄存器 , id 配置寄存器 中 写入指定的参数 ;
- 3.等待电压时钟稳定 : 等待 200 微秒 , 等待 SDRAM 电压 和 时钟 稳定 ; 但是 当 CPU 处理器工作时 , 其电压和时钟就已经稳定了 , 该步骤可以不执行 ;
- 4.执行初始化 : 除了要初始化 内存控制器外, 还要 对内存 进行初始化操作 ;
- ① 内存初始化 : 文档中给出了两种内存的初始化步骤 , 5.4.2 是 SDRAM 类型的内存初始化序列 ( 5.4.2 SDR/MOBILE SDR SDRAM INITIALIZATION SEQUENCE ) ; 5.4.3 是 DDR 类型的内存初始化序列 ( 5.4.3 DDR/MOBILE DDR SDRAM INITIALIZATION SEQUENCE ) ; 6410 开发板中使用的是 DDR 类型的内存 , 因此这里参考 5.4.3 中的内存初始化顺序 ;
- 5.存储控制器设置 Ready 状态 : 将 DRAM 存储控制器设置成 Ready 状态 , 设置 Memc_cmd 为 0b000 ;
- 6.检查存储控制器状态 : 检查存储控制器是否是 Ready 状态 , 检查 memc_stat 域 直到该值成为 0b01 ;
( 2 ) u-boot 内存初始化源码阅读 ( Config 状态 | 寄存器设置 | 电压时钟稳定 | 内存初始化 | Ready 状态 | 检查 Ready 状态 )
u-boot 内存初始化相关 源码 解析 :
- 1.源码位置 :
- ① 源码路径 : ARM内存操作\9.u-boot源代码\uboot_6410\cpu\s3c64xx\s3c6410\cpu_init.S , 其中 ARM内存操作\9.u-boot源代码 是博客附件的路径 , uboot_6410\cpu\s3c64xx\s3c6410 是 u-boot 源码路径 ;
- ② 下载地址 : 直接下载该博客附件即可 , 其中有 u-boot 源码 ;
- 2.源码注释解析 : ( 仅做参考 )
#include <config.h>
#include <s3c6410.h>
.globl mem_ctrl_asm_init
mem_ctrl_asm_init:
ldr r0, =ELFIN_MEM_SYS_CFG @Memory sussystem address 0x7e00f120
mov r1, #0xd @ Xm0CSn2 = NFCON CS0, Xm0CSn3 = NFCON CS1
str r1, [r0]
ldr r0, =ELFIN_DMC1_BASE @DMC1 base address 0x7e001000
ldr r1, =0x04 @ 第一步操作 : 0x4 转成二进制 0b100 , 设置 存储控制寄存器 Config 状态
str r1, [r0, #INDEX_DMC_MEMC_CMD] @ 将 0b100 设置到 memc_cmd 域 中
ldr r1, =DMC_DDR_REFRESH_PRD @ 第二部操作 : 设置一系列寄存器 ;
str r1, [r0, #INDEX_DMC_REFRESH_PRD]
ldr r1, =DMC_DDR_CAS_LATENCY
str r1, [r0, #INDEX_DMC_CAS_LATENCY]
ldr r1, =DMC_DDR_t_DQSS
str r1, [r0, #INDEX_DMC_T_DQSS]
ldr r1, =DMC_DDR_t_MRD
str r1, [r0, #INDEX_DMC_T_MRD]
ldr r1, =DMC_DDR_t_RAS
str r1, [r0, #INDEX_DMC_T_RAS]
ldr r1, =DMC_DDR_t_RC
str r1, [r0, #INDEX_DMC_T_RC]
ldr r1, =DMC_DDR_t_RCD
ldr r2, =DMC_DDR_schedule_RCD
orr r1, r1, r2
str r1, [r0, #INDEX_DMC_T_RCD]
ldr r1, =DMC_DDR_t_RFC
ldr r2, =DMC_DDR_schedule_RFC
orr r1, r1, r2
str r1, [r0, #INDEX_DMC_T_RFC]
ldr r1, =DMC_DDR_t_RP
ldr r2, =DMC_DDR_schedule_RP
orr r1, r1, r2
str r1, [r0, #INDEX_DMC_T_RP]
ldr r1, =DMC_DDR_t_RRD
str r1, [r0, #INDEX_DMC_T_RRD]
ldr r1, =DMC_DDR_t_WR
str r1, [r0, #INDEX_DMC_T_WR]
ldr r1, =DMC_DDR_t_WTR
str r1, [r0, #INDEX_DMC_T_WTR]
ldr r1, =DMC_DDR_t_XP
str r1, [r0, #INDEX_DMC_T_XP]
ldr r1, =DMC_DDR_t_XSR
str r1, [r0, #INDEX_DMC_T_XSR]
ldr r1, =DMC_DDR_t_ESR
str r1, [r0, #INDEX_DMC_T_ESR]
ldr r1, =DMC1_MEM_CFG
str r1, [r0, #INDEX_DMC_MEMORY_CFG]
ldr r1, =DMC1_MEM_CFG2
str r1, [r0, #INDEX_DMC_MEMORY_CFG2]
ldr r1, =DMC1_CHIP0_CFG
str r1, [r0, #INDEX_DMC_CHIP_0_CFG]
ldr r1, =DMC_DDR_32_CFG
str r1, [r0, #INDEX_DMC_USER_CONFIG]
@ 第三部操作 : 等待 时钟 和 电压 稳定 , 文档中说明 该步骤可以省略
@ 第四步操作 : 内存初始化
@DMC0 DDR Chip 0 configuration direct command reg @ 内存初始化 1. 向 Direct Command Register 的 Memory Command [19:18] 域中写入 0b11 , 整体值为 0b11 0000 0000 0000 0000 , 转为 16 进制为 0xC0000
ldr r1, =DMC_NOP0 @ 将 DMC_NOP0 值装载到 r1 中, 该值是 DMC_NOP0 = 0x0C0000
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
@Precharge All @ 内存初始化 2. 向 Direct Command Register 的 Memory Command [19:18] 域中写入 0b00, DMC_PA0 = 0x000000
ldr r1, =DMC_PA0
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
@Auto Refresh 2 time @ 内存初始化 3. 向 Direct Command Register 的 Memory Command [19:18] 域中写入 0b01, DMC_AR0 = 0x040000 , 连做两次
ldr r1, =DMC_AR0
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
@MRS @ 内存初始化 4. 向 Direct Command Register 的 Memory Command [19:18] 域中写入 0b10, 发出 MRS 命令 , 这里也是写出 2 次
ldr r1, =DMC_mDDR_EMR0
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
@Mode Reg
ldr r1, =DMC_mDDR_MR0
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
@Enable DMC1 @ 第五步操作 : 设置存储控制器 Ready 状态
mov r1, #0x0
str r1, [r0, #INDEX_DMC_MEMC_CMD]
check_dmc1_ready: @ 第六步操作 : 检查 memc_stat 域 是否 成为 0b01 ,
ldr r1, [r0, #INDEX_DMC_MEMC_STATUS] @ 即 代表 存储控制器是 Ready 状态, 如果不是, 继续跳转 循环 等待 , 直到到达 Ready 状态
mov r2, #0x3
and r1, r1, r2
cmp r1, #0x1
bne check_dmc1_ready
nop
mov pc, lr
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
( 3 ) S3C 6410 内存初始化 相关编译文件 和 函数调用接口
前期准备 : 将 内存初始化代码 单独写在一个文件中 , mem.S , 然后在 start.S 中进行调用 ;
- 1.mem.S 代码内容 :
@****************************
@File:mem.S
@
@内存 初始化代码
@****************************
.text @ 宏 指明代码段
.global mem_init @ 伪指令 mem.S 可以理解成一个函数 , 该函数由 start.S 进行调用 , 它必须是一个全局的
mem_init: @ 定义内存初始化的标号 , 在 start.S 中进行调用
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 2.start.S 中调用 :
reset: @ reset 地址存放要执行的内容
...
bl init_clock @ 跳转到 init_clock 标号, 执行时钟初始化操作
bl mem_init @ 跳转到 mem_init 标号 , 执行内存初始化操作 , 该段代码定义在 mem.S 文件中
bl light_led @ 打开开发板上的 LED 发光二极管
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 3.Makefile 文件修改 : 这里多了一个 mem.S 文件 , 因此需要修改 Makefile 文件, 编译该文件 ; 在 第一行 增加了 mem.o ;
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
( 4 ) S3C6410 内存控制器初始化 ( DRAM CONTROLLER INITIALIZATION SEQUENCE )
S3C6410 内存控制器初始化 ( DRAM CONTROLLER INITIALIZATION SEQUENCE ) :
- 0.设置 MEM_SYS_CFG 寄存器 中的 数据线 pin 脚 : MEM_SYS_CFG 寄存器初始值 0x0000_0080 , 要设置的是 第 [ 7 ] 位 , 作用是 : 设置 XmlDATA [ 31 : 16 ] pin 脚的 作用 ; Xml 对应的区域是 动态存储区 ( Dynamic Memory , 2 * 256MB ) , 对应 下图3 中右侧框起来的 pin 脚 ; 0 = 这些 pin 脚用作 [ 31 : 16 ] 位的 数据线 , 1 = 这些 pin 脚 给 SROM 使用 ; 这里设置 0 , 作为数据线使用 ;
@ 设置 MEM_SYS_CFG 寄存器中的 [ 7 ] 位 , 设置 XmlDATA [31 : 16] pin 脚作用
@ 这些 pin 脚 用于作为 内存输出的 数据线 的
@ 如果 该位 为 0 , 那么 就作为 [ 31 : 16 ] 位的数据线引脚 , 这里设置为 0 即可
ldr r0, =0x7e00f120
mov r1, #0x0
str r1, [r0]
- 1
- 2
- 3
- 4
- 5
- 6
- 1.DRAM 控制命令寄存器 ( DRAM CONTROLLER COMMAND REGISTER ) 设置 Config 状态 : 设置 DRAM 存储控制寄存器的 memc_cmd 域 ( [ 2 : 0 ] ) 为 0b100 , 高位全部设置 0 ; DRAM 控制命令寄存器地址是 0x7E001004 , 设置的值是 0b100 , 转为16进制 0x4 ;
@ 步骤一 : DRAM 控制器进入配置状态
ldr r0, =0x7e001004 @ 将 DRAM CONTROLLER COMMAND REGISTER 寄存器地址装在到 r0 中
mov r1, 0x4 @ 设置 DRAM 控制命令寄存器 ( DRAM CONTROLLER COMMAND REGISTER ) 值 0x4 进入配置 ( Config ) 状态
str r1, [r0] @ 将 r1 装载到 r0 所指向的内存地址对应的空间中
- 1
- 2
- 3
- 4
- 2.设置一系列寄存器值 : 逐个参考文档分析 ;
- 3.内存初始化 : 下面会有单个小节 S3C6410 内存初始化 ( DDR/MOBILE DDR SDRAM INITIALIZATION SEQUENCE ) 详细解析 ; 下面给出带注释源码 ;
- 4.DRAM 控制命令寄存器 ( DRAM CONTROLLER COMMAND REGISTER ) 设置 Ready 状态 : 设置 DRAM 存储控制寄存器的 memc_cmd 域 ( [ 2 : 0 ] ) 为 0b000 , 高位全部设置 0 ; DRAM 控制命令寄存器地址是 0x7E001004 , 设置的值是 0b100 , 转为16进制 0x0 ;
@ 步骤五 : 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 所指向的内存地址对应的空间中
- 1
- 2
- 3
- 4
- 5.检查是否进入 Ready 状态 : 在 DRAM 控制状态寄存器 ( DRAM CONTROLLER STATUS REGISTER ) 中的 Controller status 域 中的值 , 如果是 0b 01 就是 Ready 状态 ; 如果没有 进入 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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
( 5 ) S3C6410 内存初始化 ( DDR/MOBILE DDR SDRAM INITIALIZATION SEQUENCE )
S3C6410 内存初始化 ( DDR/MOBILE DDR SDRAM INITIALIZATION SEQUENCE ) : 这里需要注意的是 , 初始化序列 和 表格 中的指令不准确, 图2 中 下半部分的 文字截图是准确的 ;
- 1.DIRECT COMMAND REGISTER 发出 Nop 内存指令 : DIRECT COMMAND REGISTER 寄存器 的 Memory command 域, 写入 NOP 指令 , 需要 设置 [ 19:18 ] 位 为 0b11 , 整个寄存器值为 0b 11 00 0000 0000 0000 0000 , 转为十六进制 0xC0000 ;
@ 步骤四 : 内存初始化 1. 发出 NOP 命令 :
@ 执行过程 : 向 Direct Command Register 的 Memory Command [19:18] 域中写入 0b11 , 整体值为 0b11 0000 0000 0000 0000 , 转为 16 进制为 0xC0000
ldr r0, =0x7e001008 @ 装载 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中
ldr r1, =0xc0000 @ 装载 要写入的值 到 r1 寄存器中
str r1, [r0] @ 将 r1 装载到 r0 所指向的内存地址对应的空间中
- 1
- 2
- 3
- 4
- 5
- 2.DIRECT COMMAND REGISTER 发出 Prechargeall 内存指令 : DIRECT COMMAND REGISTER 寄存器 的 Memory command 域, 写入 Prechargeall 指令 , 需要 设置 [ 19:18 ] 位 为 0b00 , 整个寄存器值为 0b 00 00 0000 0000 0000 0000 , 转为十六进制 0x0 ;
@ 步骤四 : 内存初始化 2. 写入 Precharge All 命令 :
@ 执行过程 : 向 Direct Command Register 的 Memory Command [19:18] 域中写入 0b00
@ 整体值为 0b00 0000 0000 0000 0000 , 转为 16 进制为 0x0
ldr r0, =0x7e001008 @ 装载 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中
ldr r1, =0x0 @ 装载 要写入的值 到 r1 寄存器中
str r1, [r0] @ 将 r1 装载到 r0 所指向的内存地址对应的空间中
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 3.DIRECT COMMAND REGISTER 发出 Autorefresh 内存指令 ( 执行两次 ) : DIRECT COMMAND REGISTER 寄存器 的 Memory command 域, 写入 Autorefresh 指令 , 需要 设置 [ 19:18 ] 位 为 0b01 , 整个寄存器值为 0b 01 00 0000 0000 0000 0000 , 转为十六进制 0x40000 ;
@ 步骤四 : 内存初始化 3. 写入 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 所指向的内存地址对应的空间中
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 4.DIRECT COMMAND REGISTER 发出 MRS 内存指令 : DIRECT COMMAND REGISTER 寄存器 的 Memory command 域, 写入 MRS 指令 , 需要 设置 [ 19:18 ] 位 为 0b01 , 同时需要设置 EMRS 的 Bank Address , Bank Address 类型 在 [ 17 : 16 ] 位设置 , 地址值 在 Address_13_to_0 域 [ 13 : 0 ] 设置 , 整个寄存器值为 0xa0000 ;
@ 步骤四 : 内存初始化 5. 写入 MRS 命令 :
@ 执行过程 : 向 Direct Command Register 的 Memory Command [19:18] 域中写入 0b10, 同时还需要设置 Bank Address
@ 整体值 转为 16 进制为 0xa0000
ldr r0, =0x7e001008 @ 装载 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中
ldr r1, =0xa0000 @ 装载 要写入的值 到 r1 寄存器中
str r1, [r0] @ 将 r1 装载到 r0 所指向的内存地址对应的空间中
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 5.DIRECT COMMAND REGISTER 发出 MRS 内存指令 ( RMRS ) : DIRECT COMMAND REGISTER 寄存器 的 Memory command 域, 写入 MRS 指令 , 需要 设置 [ 19:18 ] 位 为 0b01 , 同时需要设置 EMRS 的 Bank Address , Bank Address 类型 在 [ 17 : 16 ] 位设置 , 地址值 在 Address_13_to_0 域 [ 13 : 0 ] 设置 , 整个寄存器值为 0xa0000 ;
@ 步骤四 : 内存初始化 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 所指向的内存地址对应的空间中
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 6.DIRECT COMMAND REGISTER 发出 MRS 内存指令 ( MRS ) : DIRECT COMMAND REGISTER 寄存器 的 Memory command 域, 写入 MRS 指令 , 需要 设置 [ 19:18 ] 位 为 0b01 , 同时需要设置 EMRS 的 Bank Address , Bank Address 类型 在 [ 17 : 16 ] 位设置 , 地址值 在 Address_13_to_0 域 [ 13 : 0 ] 设置 , 整个寄存器值为 0x80032 ;
@ 步骤四 : 内存初始化 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 所指向的内存地址对应的空间中
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
4. S3C6410 内存初始化 完整代码
( 1 ) start.S ( 入口 )
@****************************
@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 发光二极管
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
#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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
( 2 ) mem.S ( 内存初始化 )
@****************************
@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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
( 3 ) u-boot.lds ( 链接器脚本 )
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 = .;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
( 4 ) Makefile ( 编译脚本 )
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
5. 编译输出可执行文件
编译过程 :
- 1.文件准备 : 将 汇编代码 ( start.S ) 链接器脚本 ( gboot.lds ) makefile 文件 拷贝到编译目录 ;
- 2.执行编译命令 :
make
; - 3.编译结果 : 可以看到 生成了 编译目标文件 start.o, 链接文件 u-boot.elf, 可执行的二进制文件 u-boot.bin ;
6. 烧写代码到开发板并执行
( 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 亮起来;