【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

【嵌入式开发】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 ) 举例说明

计算如下内存大小 : 下面 截图 是 一款 内存芯片说明
【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

  • 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 手册目录下 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 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 芯片的 片选分布图 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 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 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 2.规格解析 : 有 4 个 L-Bank , 每个 L-Bank 有 4M 个 存储单元 , 每个存储单元 有 16 bit , 两层含义 ① 单元格容量 2 字节 , ② 其数据宽度为 16 位 ;
  • 3.2440 芯片数据线个数 : 查询 mini2440 原理图可得 , 其 数据线有 32 位 , DATA 0 ~ DATA31 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 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 寄存器 :
【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

  • 1.简介 :
    • ① 作用 : BWSCON 寄存器 是 设置总线宽度 和 等待状态 的 控制寄存器 ;
    • ② 寄存器地址 : 0x4800 0000 ;
    • ③ 寄存器初始值 : 0x000 000 ;
  • 2.寄存器位 与 Bank 的对应设置 : 32 位 的寄存器 , 分成 8 组 , 分别对应 一个 L-Bank 设置 ; 因此 下面 介绍 一组 值的设置即可 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 3.Bank7 设置 : ST7 , WS7 , DW7 设置 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
    • ① 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 寄存器 :
【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

  • 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 寄存器 :
【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

  • 1.内存类型 MT ( Memory Type ) 设置 : [ 16:15 ] 位 , 决定 Bank6 或 Bank7 的 内存 的类型 , 2440 中 , 内存使用的是 SRAM , 这里取值 11 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

  • 2.设置其他参数 : 之后的 14 位 被分为 两种情况 , 当 MT 类型设置为 ROM 或 SRAM 时 , 即 [ 16:15 ] 取 00 值 , 需要设置 [ 14 : 0 ] 位 ; 当 MT 类型设置为 SDRAM 时 , 即 [16:15] 取 11 值 , 只需要设置 [ 3 : 0 ] 位 即可 ; 2440 芯片 使用的是 SDRAM , 这里只需要设置 [ 3 : 0 ] 位 即可 , 此时 [ 14 : 4 ] 位 全部取值 0 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

    • ① Trcd 设置 : [ 3 : 2 ] 表示的是 行列地址选择 信号的转换延时 , 00 代表 2 个时钟 , 01 代表 3 个时钟 , 10 代表 4 个时钟 , 该值在 时序图中 明确指出 , Trcd 取值 是 2 个时钟 , 这里设置该值为 00 ;
      【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
    • ② SCAN 域设置 : [ 1 : 0 ] 位 设置 , 表示 列地址 数目 , 00 表示 8 位 , 01 表示 9 位 , 10 表示 10 位 ; 行地址 和 列地址 信息需要去查询 SRAM 内存芯片文档 HY57V561620F(L)T§Series_(Rev1.1).pdf , 其中可以查到 Column Address : CA0 ~ CA8 , 有 9列 , 该 SCAN 域 取值为 01 ;
      【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 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 工作原理是需要不断的 定期 的 进行刷新 ;
【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

  • 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 即可 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 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 寄存器设置 :
【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

  • 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 , 这两个寄存器内容 和 意义 是一样的 ;
【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

  • 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 芯片的各种寄存器 , 之前进行的 看门狗 , 时钟 , 内存初始化 等操作 , 相关寄存器的地址都处于该 内存段 ; 如 下 举例 :
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 5.主存储区 ( 0x00000000 ~ 0x6FFFFFFF ) : 占 1972 MB 空间 , 下面小节详细展开讲解 ;
  • 6.与 2440 对比 :
    • ① 2440 内存空间分布 : 从大的角度来说 , 2440 的地址空间 分为 ① 内部空间 和 ② 外设空间 , 加起来 共 4GB 大小 ;
    • ② 2440 内存外设空间 : 大小 1GB, 分为了 8个 Bank, 每个 Bank 128MB ;

( 2 ) 6410 主存储区划分 ( 保留区 2GB | 外设区 256MB | 主存储区 2GB )

S3C6410 主存储区 ( 0x00000000 ~ 0x6FFFFFFF ) 划分 :
【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

  • 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 位 的数据线用于输出 , 如下图 :
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

回顾 : 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 存储控制器进入不同的状态 ;
      【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 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 ) :
【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

  • 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 , 作为数据线使用 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
									@ 设置 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 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
															@ 步骤一 : 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 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
									@ 步骤五 : 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 状态 , 继续等待 再次验证 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

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 中 下半部分的 文字截图是准确的 ;
【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

  • 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 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

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烧写工具 目录下, 开发板光盘资料下载地址 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 2.设置 SD_Writer.exe 属性 ( win10系统需要进行的设置 ) : 右键点击属性, 在兼容性一栏, 设置 以 Windows 7 兼容模式运行, 并设置 以管理员身份运行此程序 ; 注意 一定要 使用管理员身份 运行 , 否则报错 , 报错信息 Select Volume Error , 无法格式化SD卡 , 无法烧写 程序 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 3.先格式化 SD 卡 : 注意这里要使用 SD_Writer 中的 format 功能进行格式化 , 按照下面的步骤, 一步一步点击确定执行 ;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 4.选择要烧写的文件 : 这里选择 OK6410-A-1G用户光盘(A)-20160812\Linux-3.0.1\Linux烧写工具\mmc_ram256.bin 文件;
  • 5.烧写文件到 SD 卡中 : 直接点击 Program 按钮, 就将启动程序烧写到了 SD 卡中;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 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;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 3.SecureCRT 连接串口 : 打开 SecureCRT 软件, 点击快速连接, 然后在弹出的对话框中按照下面进行配置, ① 首先要选择 Serial 协议, ② 然后选择端口, 这个端口从设备管理器中查看, ③ 波特率选择 115200, ④ 取消 RTS/CTS 选项;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 4.打开开发板 ( 很重要 ) : 选中 SecureCRT 软件, 然后按住空格键不放, 这个操作很重要, 打开开发板开关, ① 先按住空格键, ②再打开开关;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 5.首先格式化 Nand Flash : 选择 [1] 选项, 格式化 Nand Flash;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 6.选择从 SD 卡中烧写 : 选择 [2] Burn image from SD card 选项, 从 SD 卡中向开发板烧写程序;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 7.选择烧写 u-boot : 选择 [2] Flash u-boot, 烧写 u-boot, 会从 SD 卡中查找 u-boot.bin 文件, 然后烧写到 nand flash 中, 如果 SD 卡中 没有 u-boot.bin 会报错;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 8.设置从 Nand Flash 启动 : 设置开发板上的启动开关, (1~8) 位置 : x, x, x, 1, 1, 0, 0, 1; 此时 四个 LED 全亮;
  • 9.效果展示 : 设置的 GPBDAT 寄存器值为 0b110000, 四个 LED 灯都亮起来;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
  • 10.修改 LED 灯显示参数后显示结果 : 设置 GPBDAT 寄存器中值为 0b110101 是 第一个 和 第三个 LED 亮起来;
    【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
上一篇:python 字符串替换


下一篇:SpringCloud微服务的Eureka