一。运行环境
开发板:jz2440
系统: ubuntu12.04
编译器:arm-linux-gcc
二、特殊寄存器
sdram的操作无需按照时序图来设置,只要设置好相关的13个寄存器,arm处理器里面的存储管理器会自动输出控制信号
.long 0x22011110 @ BWSCON
.long 0x00000700 @ BANKCON0
.long 0x00000700 @ BANKCON1
.long 0x00000700 @ BANKCON2
.long 0x00000700 @ BANKCON3
.long 0x00000700 @ BANKCON4
.long 0x00000700 @ BANKCON5
.long 0x00018005 @ BANKCON6
.long 0x00018005 @ BANKCON7
.long 0x008C07A3 @ REFRESH
.long 0x000000B1 @ BANKSIZE
.long 0x00000030 @ MRSRB6
.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:
sdram.bin:head.S led.c
arm-linux-gcc -Wall -O2 -c -g -o head.o head.S
arm-linux-gcc -Wall -O2 -c -g -o led.o led.c
arm-linux-ld -Ttext 0x30000000 head.o led.o -o sdram_elf
arm-linux-objcopy -O binary -S sdram_elf sdram.bin
arm-linux-objdump -D -m arm sdram_elf>sdram.dis clean:
rm -f sdram.bin sdram_elf*.o sdram.dis
再看head.h
.equ MEM_BASE,0x48000000
.equ SDRAM_BASE,0x30000000 .text
.global _start
_start: bl close_watchdog @关闭看门狗
bl mem_set @设置存储寄存器组
bl steppingstone_sdram @复制代码到sdram ldr pc,=on_sdram
on_sdram:
ldr sp,=0x34000000
bl main
halt_loop:
b halt_loop close_watchdog:
mov r1,#0x53000000
mov r2,#0x0
str r2,[r1] mov pc,lr @返回 steppingstone_sdram: @起始地址0x00000000,目标地址0x30000000
mov r1,#
ldr r2,=SDRAM_BASE
mov r3,#* copy_loop:
ldr r4,[r1],#
str r4,[r2],# cmp r1,r3
bne copy_loop
mov pc,lr @返回 mem_set:
mov r1,#MEM_BASE adrl r2,JCQ
add r3,r1,# @* set_loop:
ldr r4,[r2],#
str r4,[r1],#
cmp r1,r3
bne set_loop mov pc,lr .align
JCQ:
.long 0x22011110 @BWSCON
.long 0x00000700 @bankcon0
.long 0x00000700 @bankcon1
.long 0x00000700 @bankcon2
.long 0x00000700 @bankcon3
.long 0x00000700 @bankcon4
.long 0x00000700 @bankcon5
.long 0x00018005 @bankcon6
.long 0x00018005 @bankcon7
.long 0x008c07a3 @refresh
.long 0x000000b1 @banksize
.long 0x00000030 @mrsrb6
.long 0x00000030 @mrsrb7
其实没什么改动,换些名字而已,但是自己敲的话,当然会有一些细节上面会出错,不注意的地方。
以上启动文件是以汇编编写的,下面贴出c语言版的。
head.S:
.extern main
.text
.global _start
_start:
b reset reset:
ldr sp,= @ bl close_watchdog @关闭看门狗
bl mem_set @设置存储寄存器组
bl steppingstone_sdram @复制代码到sdram ldr pc,=on_sdram
on_sdram:
ldr sp,=0x34000000
ldr pc,=0x30000000
halt_loop:
b halt_loop
init.c:
/*************************************************************************
> File Name: init.c
> Author: hulig
> Mail:
> Created Time: 2014年11月08日 星期六 15时52分55秒
>function:init disable watchdog ,init mem .goto main and on
>result:ok
************************************************************************/ #define WTCON (*(volatile unsigned long *)0x53000000)
#define MEM_BASE (*(volatile unsigned long *)0x48000000) void close_watchdog()
{
WTCON =; //往看门狗寄存器里写0 就可以啦
} void mem_set() //与sdram设置有关的13个寄存器写入对应的值就ok
{
unsigned long const mem_jcq[]={
0x22011110,
0x00000700,
0x00000700,
0x00000700,
0x00000700,
0x00000700,
0x00000700,
0x00018005,
0x00018005,
0x008c07a3,
0x000000b1,
0x00000030,
0x00000030,
}; int i=;
volatile unsigned long *p=(volatile unsigned long *)MEM_BASE;
for(;i<;i++)
p[i]=mem_jcq[i];
} void steppingstone_sdram(void)
{
unsigned int *pSrc=(unsigned int *); // 将steppingstone 0地址复制到sdram起始地址
unsigned int *pDes =(unsigned int *)0x30000000;//sdram起始地址 while(pSrc<(unsigned int*))
{
*pDes=*pSrc;
pDes++;
pSrc++;
} }
上面c代码并不能达到预期的效果,其主要问题点是sdram的初始化,也就是给13个寄存器赋值的时候:
《一》。
#define BWSCON (*(volatile unsigned long * )0x48000000)
#define BANKCON0 (*(volatile unsigned long * )0x48000004)
#define BANKCON1 (*(volatile unsigned long * )0x48000008)
#define BANKCON2 (*(volatile unsigned long * )0x4800000c)
#define BANKCON3 (*(volatile unsigned long * )0x48000010)
#define BANKCON4 (*(volatile unsigned long * )0x48000014)
#define BANKCON5 (*(volatile unsigned long * )0x48000018)
#define BANKCON6 (*(volatile unsigned long * )0x4800001c)
#define BANKCON7 (*(volatile unsigned long * )0x48000020)
#define REFRESH (*(volatile unsigned long * )0x48000024)
#define BANKSIZE (*(volatile unsigned long * )0x48000028)
#define MRSRB6 (*(volatile unsigned long * )0x4800002c)
#define MRSRB7 (*(volatile unsigned long * )0x48000030) void mem_set()
{
BWSCON= 0x22011110;
BANKCON0=0x00000700;
BANKCON1=0x00000700;
BANKCON2=0x00000700;
BANKCON3=0x00000700;
BANKCON4=0x00000700;
BANKCON5=0x00000700;
BANKCON6=0x00018005;
BANKCON7=0x00018005;
REFRESH=0x008c07a3;
BANKSIZE=0x000000b1;
MRSRB6 =0x00000030;
MRSRB7= 0x00000030;
} 《二》。
void mem_set() //与sdram设置有关的13个寄存器写入对应的值就ok
{
int i=;
unsigned long *p=(unsigned long *)MEM_BASE;
unsigned long const mem_jcq[]={
0x22011110,
0x00000700,
0x00000700,
0x00000700,
0x00000700,
0x00000700,
0x00000700,
0x00018005,
0x00018005,
0x008c07a3,
0x000000b1,
0x00000030,
0x00000030,
}; for(;i<;i++)
p[i]=mem_jcq[i];
} 《三》。 #define MEM_CTL_BASE 0x48000000
#define MEM_CTL_END 0x48000034
unsigned long const mem_cfg_val[]={ // 声明数组存放内存控制器设置数据 0x22000000, //BWSCON
0x00000700, //BANKCON0
0x00000700, //BANKCON1
0x00000700, //BANKCON2
0x00000700, //BANKCON3
0x00000700, //BANKCON4
0x00000700, //BANKCON5
0x00018005, //BANKCON6
0x00018005, //BANKCON7
0x008e07a3, //REFRESH(HCLK = 12MHz,该值为0x008e07a3 //HCLK = 100MHz 0x008e04f5)
0x000000b1, //BANKSIZE
0x00000030, //MRSRB6
0x00000030, //MRSRB7
};
上面是三种写法,唯有第一种可行。why?
比较一,二,三以及查阅资料可知,其根本原因是位置无关码
注意,在复制代码到sdram之前,链接地址是0x30000000,也就是sdram的起始地址,此时若想正确执行,必须是使用跳转指令。
二和三中都是以数组的形式存储,自然不会是位置无关,因而行不通。
那么,怎么编写位置无关码呢?
先总结基本为两点
一。在汇编中,使用B/BL指令
二。在c中,避免使用static来限定范围;连续寄存器组尽量分别赋值。
附,另一个可行的办法:
void memsetup(void)
{
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE; /* 这个函数之所以这样赋值,而不是像前面的实验(比如mmu实验)那样将配置值
* 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到
* SDRAM之前就可以在steppingstone中运行
*/
/* 存储控制器13个寄存器的值 */
p[] = 0x22011110; //BWSCON
p[] = 0x00000700; //BANKCON0
p[] = 0x00000700; //BANKCON1
p[] = 0x00000700; //BANKCON2
p[] = 0x00000700; //BANKCON3
p[] = 0x00000700; //BANKCON4
p[] = 0x00000700; //BANKCON5
p[] = 0x00018005; //BANKCON6
p[] = 0x00018005; //BANKCON7
p[] = 0x008C07A3; //REFRESH
p[] = 0x000000B1; //BANKSIZE
p[] = 0x00000030; //MRSRB6
p[] = 0x00000030; //MRSRB7
}