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

Posted cfas

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式开发裸机引导操作系统和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 ) 举例说明


计算如下内存大小 : 下面 截图 是 一款 内存芯片说明
技术图片

  • 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 存储的地址 指向的内存中
  • 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_6410cpus3c64xxs3c6410cpu_init.S , 其中 ARM内存操作9.u-boot源代码 是博客附件的路径 , uboot_6410cpus3c64xxs3c6410 是 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)-20160812Linux-3.0.1Linux烧写工具 目录下, 开发板光盘资料下载地址 ;
    技术图片
  • 2.设置 SD_Writer.exe 属性 ( win10系统需要进行的设置 ) : 右键点击属性, 在兼容性一栏, 设置 以 Windows 7 兼容模式运行, 并设置 以管理员身份运行此程序 ; 注意 一定要 使用管理员身份 运行 , 否则报错 , 报错信息 Select Volume Error , 无法格式化SD卡 , 无法烧写 程序 ;
    技术图片
  • 3.先格式化 SD 卡 : 注意这里要使用 SD_Writer 中的 format 功能进行格式化 , 按照下面的步骤, 一步一步点击确定执行 ;
    技术图片
    技术图片
    技术图片
  • 4.选择要烧写的文件 : 这里选择 OK6410-A-1G用户光盘(A)-20160812Linux-3.0.1Linux烧写工具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 亮起来;
    技术图片

以上是关于嵌入式开发裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )(代码片的主要内容,如果未能解决你的问题,请参考以下文章

ARM裸机之点亮LED

ARM裸机之点亮LED

嵌入式 linux基于arm中,其中的 内存映射 是啥意思?具体完成啥过程?一定采纳

ARM开发工具软件命令具体解释---嵌入式回归第三篇

嵌入式系统学习开发概述

初试ARM开发板