一. 内存 简介
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 ;
-
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 存储的地址 指向的内存中
- 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,