一。运行环境
开发板:jz2440
系统: ubuntu12.04
编译器:arm-linux-gcc
二、特殊寄存器
sdram的操作无需按照时序图来设置,只要设置好相关的13个寄存器,arm处理器里面的存储管理器会自动输出控制信号
1 .long 0x22011110 @ BWSCON 2 .long 0x00000700 @ BANKCON0 3 .long 0x00000700 @ BANKCON1 4 .long 0x00000700 @ BANKCON2 5 .long 0x00000700 @ BANKCON3 6 .long 0x00000700 @ BANKCON4 7 .long 0x00000700 @ BANKCON5 8 .long 0x00018005 @ BANKCON6 9 .long 0x00018005 @ BANKCON7 10 .long 0x008C07A3 @ REFRESH 11 .long 0x000000B1 @ BANKSIZE 12 .long 0x00000030 @ MRSRB6 13 .long 0x00000030 @ MRSRB7
先看BWSCON:
每四位控制一个BKNK
对于SDRAM,ST7设置为0,SRAN则设置为1
WSx通常设置为0
DBx设置为01b,这个一般按照实际情况来设置,还要参照具体开发板上面存储资源。
对于这六个寄存器,主要设置时序的,默认值就欧克
MT[16:15]设置SRAM OR SDRAM ,这里设置0B11
如果是sdram,那么Trcd 设为推荐值0b10
SCAN 设置为9-bit
对于刷新寄存器,注意低11位会根据CLK不同而不同,也就是说使用PLL与否,会有不同的值
由此可算出
Refreh_count =2^12+1-12*64/8092=1955
REFRESF=0x008c0000+1955
先看代码:这里主要有三个head.S ,led.c ,Makefile,其中led的代码同流水灯一样,直接拷贝过来。
此外外,代码参考韦东山先生的源码,经过烧写可验证没问题。
三。直接贴代码
先看Makefile:
1 sdram.bin:head.S led.c 2 arm-linux-gcc -Wall -O2 -c -g -o head.o head.S 3 arm-linux-gcc -Wall -O2 -c -g -o led.o led.c 4 arm-linux-ld -Ttext 0x30000000 head.o led.o -o sdram_elf 5 arm-linux-objcopy -O binary -S sdram_elf sdram.bin 6 arm-linux-objdump -D -m arm sdram_elf>sdram.dis 7 8 clean: 9 rm -f sdram.bin sdram_elf*.o sdram.dis
再看head.h
1 .equ MEM_BASE,0x48000000 2 .equ SDRAM_BASE,0x30000000 3 4 .text 5 .global _start 6 _start: 7 8 bl close_watchdog @关闭看门狗 9 bl mem_set @设置存储寄存器组 10 bl steppingstone_sdram @复制代码到sdram 11 12 ldr pc,=on_sdram 13 on_sdram: 14 ldr sp,=0x34000000 15 bl main 16 halt_loop: 17 b halt_loop 18 19 20 21 close_watchdog: 22 mov r1,#0x53000000 23 mov r2,#0x0 24 str r2,[r1] 25 26 mov pc,lr @返回 27 28 steppingstone_sdram: @起始地址0x00000000,目标地址0x30000000 29 mov r1,#0 30 ldr r2,=SDRAM_BASE 31 mov r3,#1024*4 32 33 34 copy_loop: 35 ldr r4,[r1],#4 36 str r4,[r2],#4 37 38 cmp r1,r3 39 bne copy_loop 40 mov pc,lr @返回 41 42 mem_set: 43 mov r1,#MEM_BASE 44 45 adrl r2,JCQ 46 add r3,r1,#52 @4*13 47 48 set_loop: 49 ldr r4,[r2],#4 50 str r4,[r1],#4 51 cmp r1,r3 52 bne set_loop 53 54 mov pc,lr 55 56 57 .align 4 58 JCQ: 59 .long 0x22011110 @BWSCON 60 .long 0x00000700 @bankcon0 61 .long 0x00000700 @bankcon1 62 .long 0x00000700 @bankcon2 63 .long 0x00000700 @bankcon3 64 .long 0x00000700 @bankcon4 65 .long 0x00000700 @bankcon5 66 .long 0x00018005 @bankcon6 67 .long 0x00018005 @bankcon7 68 .long 0x008c07a3 @refresh 69 .long 0x000000b1 @banksize 70 .long 0x00000030 @mrsrb6 71 .long 0x00000030 @mrsrb7
其实没什么改动,换些名字而已,但是自己敲的话,当然会有一些细节上面会出错,不注意的地方。
以上启动文件是以汇编编写的,下面贴出c语言版的。
head.S:
1 .extern main 2 .text 3 .global _start 4 _start: 5 b reset 6 7 reset: 8 ldr sp,=4096 @ 9 10 bl close_watchdog @关闭看门狗 11 bl mem_set @设置存储寄存器组 12 bl steppingstone_sdram @复制代码到sdram 13 14 15 16 ldr pc,=on_sdram 17 on_sdram: 18 ldr sp,=0x34000000 19 ldr pc,=0x30000000 20 halt_loop: 21 b halt_loop
init.c:
1 /************************************************************************* 2 > File Name: init.c 3 > Author: hulig 4 > Mail: 5 > Created Time: 2014年11月08日 星期六 15时52分55秒 6 >function:init disable watchdog ,init mem .goto main and on 7 >result:ok 8 ************************************************************************/ 9 10 #define WTCON (*(volatile unsigned long *)0x53000000) 11 #define MEM_BASE (*(volatile unsigned long *)0x48000000) 12 13 void close_watchdog() 14 { 15 WTCON =0; //往看门狗寄存器里写0 就可以啦 16 } 17 18 void mem_set() //与sdram设置有关的13个寄存器写入对应的值就ok 19 { 20 unsigned long const mem_jcq[]={ 21 0x22011110, 22 0x00000700, 23 0x00000700, 24 0x00000700, 25 0x00000700, 26 0x00000700, 27 0x00000700, 28 0x00018005, 29 0x00018005, 30 0x008c07a3, 31 0x000000b1, 32 0x00000030, 33 0x00000030, 34 }; 35 36 int i=0; 37 volatile unsigned long *p=(volatile unsigned long *)MEM_BASE; 38 for(;i<13;i++) 39 p[i]=mem_jcq[i]; 40 } 41 42 void steppingstone_sdram(void) 43 { 44 unsigned int *pSrc=(unsigned int *)0; // 将steppingstone 0地址复制到sdram起始地址 45 unsigned int *pDes =(unsigned int *)0x30000000;//sdram起始地址 46 47 while(pSrc<(unsigned int*)4096) 48 { 49 *pDes=*pSrc; 50 pDes++; 51 pSrc++; 52 } 53 54 55 }
上面c代码并不能达到预期的效果,其主要问题点是sdram的初始化,也就是给13个寄存器赋值的时候:
1 《一》。 2 #define BWSCON (*(volatile unsigned long * )0x48000000) 3 #define BANKCON0 (*(volatile unsigned long * )0x48000004) 4 #define BANKCON1 (*(volatile unsigned long * )0x48000008) 5 #define BANKCON2 (*(volatile unsigned long * )0x4800000c) 6 #define BANKCON3 (*(volatile unsigned long * )0x48000010) 7 #define BANKCON4 (*(volatile unsigned long * )0x48000014) 8 #define BANKCON5 (*(volatile unsigned long * )0x48000018) 9 #define BANKCON6 (*(volatile unsigned long * )0x4800001c) 10 #define BANKCON7 (*(volatile unsigned long * )0x48000020) 11 #define REFRESH (*(volatile unsigned long * )0x48000024) 12 #define BANKSIZE (*(volatile unsigned long * )0x48000028) 13 #define MRSRB6 (*(volatile unsigned long * )0x4800002c) 14 #define MRSRB7 (*(volatile unsigned long * )0x48000030) 15 16 17 void mem_set() 18 { 19 BWSCON= 0x22011110; 20 BANKCON0=0x00000700; 21 BANKCON1=0x00000700; 22 BANKCON2=0x00000700; 23 BANKCON3=0x00000700; 24 BANKCON4=0x00000700; 25 BANKCON5=0x00000700; 26 BANKCON6=0x00018005; 27 BANKCON7=0x00018005; 28 REFRESH=0x008c07a3; 29 BANKSIZE=0x000000b1; 30 MRSRB6 =0x00000030; 31 MRSRB7= 0x00000030; 32 } 33 34 35 《二》。 36 void mem_set() //与sdram设置有关的13个寄存器写入对应的值就ok 37 { 38 int i=0; 39 unsigned long *p=(unsigned long *)MEM_BASE; 40 unsigned long const mem_jcq[]={ 41 0x22011110, 42 0x00000700, 43 0x00000700, 44 0x00000700, 45 0x00000700, 46 0x00000700, 47 0x00000700, 48 0x00018005, 49 0x00018005, 50 0x008c07a3, 51 0x000000b1, 52 0x00000030, 53 0x00000030, 54 }; 55 56 for(;i<13;i++) 57 p[i]=mem_jcq[i]; 58 } 59 60 《三》。 61 62 #define MEM_CTL_BASE 0x48000000 63 #define MEM_CTL_END 0x48000034 64 unsigned long const mem_cfg_val[]={ // 声明数组存放内存控制器设置数据 65 66 0x22000000, //BWSCON 67 0x00000700, //BANKCON0 68 0x00000700, //BANKCON1 69 0x00000700, //BANKCON2 70 0x00000700, //BANKCON3 71 0x00000700, //BANKCON4 72 0x00000700, //BANKCON5 73 0x00018005, //BANKCON6 74 0x00018005, //BANKCON7 75 0x008e07a3, //REFRESH(HCLK = 12MHz,该值为0x008e07a3 //HCLK = 100MHz 0x008e04f5) 76 0x000000b1, //BANKSIZE 77 0x00000030, //MRSRB6 78 0x00000030, //MRSRB7 79 };
上面是三种写法,唯有第一种可行。why?
比较一,二,三以及查阅资料可知,其根本原因是位置无关码
注意,在复制代码到sdram之前,链接地址是0x30000000,也就是sdram的起始地址,此时若想正确执行,必须是使用跳转指令。
二和三中都是以数组的形式存储,自然不会是位置无关,因而行不通。
那么,怎么编写位置无关码呢?
先总结基本为两点
一。在汇编中,使用B/BL指令
二。在c中,避免使用static来限定范围;连续寄存器组尽量分别赋值。
附,另一个可行的办法:
1 void memsetup(void) 2 { 3 volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE; 4 5 /* 这个函数之所以这样赋值,而不是像前面的实验(比如mmu实验)那样将配置值 6 * 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到 7 * SDRAM之前就可以在steppingstone中运行 8 */ 9 /* 存储控制器13个寄存器的值 */ 10 p[0] = 0x22011110; //BWSCON 11 p[1] = 0x00000700; //BANKCON0 12 p[2] = 0x00000700; //BANKCON1 13 p[3] = 0x00000700; //BANKCON2 14 p[4] = 0x00000700; //BANKCON3 15 p[5] = 0x00000700; //BANKCON4 16 p[6] = 0x00000700; //BANKCON5 17 p[7] = 0x00018005; //BANKCON6 18 p[8] = 0x00018005; //BANKCON7 19 p[9] = 0x008C07A3; //REFRESH 20 p[10] = 0x000000B1; //BANKSIZE 21 p[11] = 0x00000030; //MRSRB6 22 p[12] = 0x00000030; //MRSRB7 23 }