⚠️裸机仓库https://gitee.com/simonchina_carel_li/mini2440-bare-metal.git⚠️Tag:11-sdram1. 目的目前我们的固件运行还是使用SOC内部的SRAM它只有4KB的空间现在我们想解锁开发板上的64MB的SDRAM空间从而跑更复杂的程序2. 方案分析再次贴出原理图目前的信息地址线错位2bits连接访问粒度为字访问32bitSDRAM接在BANK6上SDRAM的并联容量是64MB那么可用的地址空间为0x30000000 ~ 0x0x34000000LADDR24~25位段用来确定SDRAM片内的四层BANK之一LnWBE0/1/2/3用来进行字节访问我们需要配置好内存控制器然后内存控制器就用使用我们期望的行为和参数进行匹配的SDRAM访问那么怎么设置内存控制器呢3. 要配置哪些参数设置内存控制器就是配置相关的寄存器我们要设置哪些关键行为和参数呢以我这个开发板上的EM63A165-6G166MHz版本为例查询芯片手册填写参数表参数要设置成多少CAS Latency (列选通潜伏期简称 CL)2/3 clocks是否使用UB/LB就是字节访问使用是否使用nWATT引脚硬件等待不使用没有此引脚我们采用软件等待TaccBANK6总线宽度32bitBANK6储存器类型 MT同步SDRAMTrcdRAS 到 CAS 的延迟≥ 18 nsSCAN列地址数9 bits行地址数13 bitsREFENSDRAM 刷新使能使能TREFMDSDRAM 刷新模式CBR/自动刷新TrpSDRAM RAS 预充电时间≥ 18 nsTrc SDRAM 半行周期时间≥ 60 ns刷新周期8192 次 / 64ms 7.8125usBURST_ENARM 核突发Burst操作使能使能SCKE_ENSDRAM 掉电模式使能 SCKE 控制填0不休眠SCLK_EN只在 SDRAM 访问周期期间 SCLK使能以降低功耗填 1访问时再使能时钟为了省电BK76MAPBank6/7 存储器映射64M/64MWBL写突发长度0 代表 “Programmed Burst Length”写突发长度跟着读突发长度走。1 代表单字写。正常工作一律填 0TM测试模式正常使用绝对不能改必须填 00CLCAS 等待时间latency该芯片支持 CL2 或 3, 100MHz的hclk下强烈建议 (CL3)BT突发类型Sequential线性顺序突发BL突发长度此位必须固定填 000。因为 S3C2440 的内存控制器会自己通过硬件逻辑来连续发地址控制 Cache Line 填充不需要开启 SDRAM 内部的突发计数器不需要全部知道这些参数究竟什么意思只要知道它们是内存控制器操作SDAM的行为模式和参数就行了4. 代码实现4.1 SDRAM初始化我们使用汇编实现其实C代码实现sdram的初始化可以只不过如果用C实现我们需要放在.data段复制和.bss段清零之后这样的话sdram初始化之后我们还需要重新进行新的.data段复制和.bss段清零, 显得复杂和臃肿因此我们使用汇编实现这样直接放在.data段复制和.bss段清零之前就好了。创建汇编文件common/sdram.s,查阅手册对照上述的方案配置项表配置这几个寄存器具体的配置项见注释.equ BWSCON, 0x48000000 .equ BANKCON6, 0x4800001C .equ REFRESH, 0x48000024 .equ BANKSIZE, 0x48000028 .equ MRSRB6, 0x4800002C .section .text .global sdram_setup sdram_setup: 初始化BWSCON 对 Bank 6 使用 UB/LB WAIT 禁止 数据总线宽度32bits ldr r0, BWSCON ldr r1, [r0] bic r1, r1, #(0b1111 24) orr r1, r1, #((1 27) | (0 26) | (0b10 24)) str r1, [r0] 初始化BANKCON6 ldr r0, BANKCON6 ldr r1, [r0] ldr r2, ((0b11 15) | (0b1111 0)) bic r1, r1, r2 同步 DRAM Trcd 18ns, HCLK 101.25MHz周期9.88ns, 所以设置值为2个周期的延迟 SCAN 9bits ldr r2, ((0b11 15) | (0b00 2) | (0b01 0)) orr r1, r1, r2 str r1, [r0] 初始化REFRESH SDRAM 刷新使能 SDRAM 刷新模式 - CBR/自动刷新 Trp 18ns, HCLK 101.25MHz周期9.88ns, 所以设置值为2个周期的延迟 Trc Trp Tsrc 60ns, 所以Tsrc 42ns, Tsrc设置值为5个周期的延迟 刷新周期 7.8us, 对应值1260 ldr r0, REFRESH ldr r1, ((1 23) | (0 22) | (0 20) | (0b01 18) | (1260 0)) str r1, [r0] 初始化BANKSIZE ARM 核突发Burst操作使能 禁止 SDRAM 掉电模式 只在 SDRAM 访问周期期间 SCLK使能以降低功耗 Bank6/7 存储器映射 - 64MB/64MB ldr r0, BANKSIZE ldr r1, ((1 7) | (0 5) | (1 4) | (0b001 0)) str r1, [r0] 初始化MRSRB6 WBL写突发长度 - 突发固定 TM测试模式 - 模式寄存器组固定 CL CAS 等待时间latency - 3个时钟 BT 突发类型 - Sequential线性顺序突发/连续固定 BL 突发长度 - S3C2440 的内存控制器会自己通过硬件逻辑来连续发地址控制 Cache Line 填充不需要开启 SDRAM 内部的突发计数器 ldr r0, MRSRB6 ldr r1, ((0 9) | (0 7) | (0b11 4) | (0 3) | (0 0)) str r1, [r0] bx lr4.2 SDRAM测试验证我们要验证什么东西接线是否短路或虚焊UB/LB 字节掩码是否生效读写性能评估话不多说上代码具体细节看注释创建C文件sdram/main.c,#include s3c2440a.h // 宏定义 SDRAM 的基地址和容量 #define SDRAM_BASE 0x30000000 #define SDRAM_SIZE_BYTES (64 * 1024 * 1024) // 64MB #define printf uart0_printf /** * brief SDRAM 综合测试函数 */ void sdram_test() { volatile uint32_t *pWord; volatile uint8_t *pByte; uint32_t i, read_val; printf(\r\n\r\n); printf( SDRAM 诊断与性能测试开始 \r\n); printf(\r\n); /*------------------------------------------------------------------------- * 测试一地址线与数据线综合测试 (Address-in-Address Test) * 原理将当前的物理地址作为数据写入该物理地址中。 * 作用这是排查硬件布线短路最有效的算法。如果 A1 和 A2 短路或者 * D5 虚焊读回来的数据绝对不等于它的物理地址。 *------------------------------------------------------------------------*/ printf([1/3] 正在进行全盘 32-bit 地址映射与数据校验 (64MB)...\r\n); // 1. 填入数据 for (i 0; i SDRAM_SIZE_BYTES; i 4) { pWord (volatile uint32_t *)(SDRAM_BASE i); *pWord (uint32_t)pWord; // 把指针本身的值(地址)当做数据写进去 if (i % 0x100000 0) { printf( 已填入 %d MB\n, (i 1) / (1024 * 1024)); } } // 2. 读出校验 for (i 0; i SDRAM_SIZE_BYTES; i 4) { pWord (volatile uint32_t *)(SDRAM_BASE i); read_val *pWord; if (read_val ! (uint32_t)pWord) { printf(\r\n[!] 严重硬件错误: 32-bit 读写校验失败!\r\n); printf( 地址: 0x%x\r\n, (uint32_t)pWord); printf( 期望: 0x%x\r\n, (uint32_t)pWord); printf( 实际: 0x%x\r\n, read_val); printf( 异或: 0x%x (可用于排查哪根引脚短路)\r\n, (uint32_t)pWord ^ read_val); return; } if (i % 0x100000 0) { printf( 已校验 %d MB\n, (i 1) / (1024 * 1024)); } } printf( - 校验通过数据线与地址线无短路/断路。\r\n); /*------------------------------------------------------------------------- * 测试二UB/LB 字节掩码测试 (Byte Enable Test) * 作用验证你的 BWSCON 寄存器中 UB/LB 设定是否生效硬件 DQM 引脚是否正常。 * 如果不正常写 8-bit 数据会把其他 24-bit 冲刷掉。 *------------------------------------------------------------------------*/ printf([2/3] 正在进行 UB/LB 字节掩码 (8-bit) 读写保护测试...\r\n); // 1. 先写一个 32-bit 的背景数据 pWord (volatile uint32_t *)SDRAM_BASE; *pWord 0x00000000; // 2. 用 8-bit 指针只修改其中几个字节 pByte (volatile uint8_t *)SDRAM_BASE; pByte[0] 0xAA; // 写入最低字节 (小端模式) pByte[2] 0xCC; // 写入次高字节 // 3. 再次用 32-bit 读出验证未被修改的字节是否依然是 0 read_val *pWord; // 期望结果0x00CC00AA (小端模式高地址存高字节) if (read_val ! 0x00CC00AA) { printf(\r\n[!] 严重硬件错误: UB/LB 字节掩码测试失败!\r\n); printf( 期望: 0x00CC00AA\r\n); printf( 实际: 0x%x\r\n, read_val); printf( 请检查 BWSCON 的 WS 位以及硬件 DQM 连线\r\n); return; } printf( - 校验通过字节掩码功能正常。\r\n); /*------------------------------------------------------------------------- * 测试三读写性能评估 (带 Cache 提示) * 说明在裸机中精确测速需要使用硬件定时器 (Timer)。 * 这里提供一个 1MB 块拷贝的循环框架如果你开启了 Timer可以加上时间戳。 *------------------------------------------------------------------------*/ printf([3/3] 正在进行 1MB 块顺序读写性能测试...\r\n); // 我们测试将 SDRAM 前 1MB 的数据拷贝到第 2 个 1MB 的区域 volatile uint32_t *pSrc (volatile uint32_t *)SDRAM_BASE; volatile uint32_t *pDst (volatile uint32_t *)(SDRAM_BASE 0x100000); // 偏移 1MB uint32_t words_1MB (1024 * 1024) / 4; // -- TODO: 如果你有定时器驱动在这里记录 start_time -- // uint32_t start_time timer_get_ticks(); for (i 0; i words_1MB; i) { pDst[i] pSrc[i]; } // -- TODO: 在这里记录 end_time计算 MB/s -- // uint32_t end_time timer_get_ticks(); // printf( - 耗时: %d ticks\r\n, end_time - start_time); printf( - 读写循环完成\r\n); printf(\r\n); printf( SDRAM 测试全部通过你的硬件像岩石一样稳定。\r\n); printf(\r\n); } int main() { sdram_test(); for (;;); return 0; }4.3 运行编译make sdram烧录运行为了方便示例仅拿4M区域做验证其实现阶段我们是没有开启MMU和Cache的而且编译优化也未开启其实SDRAM的性能测试结果肯定不尽人意目前1MB块读写大概是2S~先挖个坑等后面开启MMU和Cache的测试再来比较体验差异~