uboot移植准备资料之mini2440裸机实验——存储控制器、NANDFLASH

硬件平台和工具:mini2440, jlink

为了移植uboot做准备,这里仅仅的裸机实验,仅仅包括LED灯,SDRAM和NAND FLASH的操作。

1 方案设计

该实验主要是控制SDRAM和NANDFLASH,LED仅仅作为调试使用。因此,在mini2440的4k的stepping stone(加载自nandflash的前4k字节)中,执行关看门狗、初始化堆栈、初始化时钟等操作。然后跳入以0x30000000为基地址的区域(即SDRAM中)运行,然后在该代码段中操作nandflash,这样验证nandflash操作正常与否的同时,又验证了SDRAM是否初始化成功。

2 程序结构

该工程除了Makefile还有三个文件:boot.s, lib.c, main.c。其中boot.s是第一阶段启动代码,main.c是第二阶段启动代码,lib.c有前两个文件调用的内容。

Makefile如下:

CROSS_COMPILE =arm-none-linux-gnueabi-

AS      = $(CROSS_COMPILE)as

LD      = $(CROSS_COMPILE)ld

CC     = $(CROSS_COMPILE)gcc

OBJCOPY =$(CROSS_COMPILE)objcopy

OBJDUMP =$(CROSS_COMPILE)objdump

 

main.bin: boot.s lib.cmain.c

$(CC) -g -c -o boot.o boot.s

$(CC) -g -c -o lib.o lib.c

$(CC) -g -c -o main.o main.c

$(LD) -Ttext 0x30000000 -g boot.o lib.o main.o -o main_elf           

//上一行链接的main_elfelf格式的文件,不是可执行文件。需要使用objcopy转换为可执行文件

$(OBJCOPY)-O binary -S main_elf main.bin

$(OBJDUMP) -D -m arm  main_elf > main.dis

clean:

rm -f *.o

rm -f main.bin

rm -f main.dis

rm -f main_elf

3 head.s

首先关闭看门狗,设置堆栈,设置存储控制器,初始化时钟,将0x0(stepping stone)开始的代码拷贝到0x30000000(SDRAM)中。跳到0x30000000基地址的main中执行。

.text

.global _start

_start:

@关看门口狗

ldr r0,= 0x53000000

mov r1,#0x0

str r1,[r0]

@设置堆栈,堆栈指向4K地址,

ldr sp,=0x1000

@初始化存储控制器

bl memoryControllerInit

@设置时间

bl clock_init

@拷贝代码到0x30000000

bl copy_to_sdram

@进入SDRAM之前重新设置堆栈

ldr sp,=0x34000000       

@之前一直运行在初始地址为0stepping中,使用的都是bl跳转都是基于相对地址的,所以只在stepping范围内跳转

@要跳转到0x34000000,不再使用bl,直接给PC赋值跳转。由于链接地址是0x30000000,所以这里跳转到SDRAMmain

@跳转到SDRAMmain函数

ldr pc,=main

loop:

b loop

 

copy_to_sdram:

ldr r0,=0x0

ldr r1,=0x30000000

c_loop:

ldr r3,[r0],#4

str r3,[r1],#4

cmp r0,#0x1000

bne c_loop

mov pc,lr

4 main.c

读nandflash从0开始的地址,然后通过led显示。由于这里没有输出串口,这里用led灯判断读书是否为偶数,觉得不保险,可以使用jlink直接查看寄存器的数据。

void main()

{        

//nand flash操作

unsigned long buf[2048];

buf[0]=0;buf[1]=0;buf[2]=0;buf[3]=0;

read_bytes(0,buf,2048);

led1_on_off(buf[0]%2);

led2_on_off((buf[0]>>8)%2);

led3_on_off((buf[0]>>16)%2);

led4_on_off((buf[0]>>24)%2);

while(1);

}

5 lib.c

主要包括时钟相关代码,存储控制器的代码,LED相关的GPIO代码,NANDFLASH相关的代码。其中LED相关的GPIO代码和时钟相关代码较为简单,不详细说明,这里需要记住的是PCLK=200kHz,HCLK=100kHz,FCLK=50kHz。

5.1 存储控制器的设置。

         通过查阅mini2440的原理图,可以知道Bank0接nandflash或norflash(通过开关选择),Bank4接DM9000,Bank6和Bank7接SDRAM。这里不考虑网卡,因此暂时忽略Bank4。

         存储控制器主要就是配置寄存器。其中SDRAM配置完成便可以使用。

(1)    BWSCON

BANK6,7接SDRAM,DW7=10(32位),WS7=0,ST7=0。BANK0有硬件跳线决定,不用设置。

(2)    BANKCON0 .. BANKCON5

默认设置即可

(3)    BANKCON6, BANKCON7

MT=0x11表示SDRAM。查看芯片手册,Trcd最小值是20ns,FCLK=100HM情况下,20ns对应两个clk,这使用2clk,Trcd=1(3 clolcks)。SCAN=0x01,列地址为9。(注:芯片为HY57V561620)

(4)    REFRESH

REFEN=1使能刷新。TREFMD=0自动刷新。根据芯片手册,trp最小值20ns,对应于2个clk,所以设置Trp=1。Trc最小值是65ns,Tsrc=Trc-Trp,最小值是45ns,对应于4.5clock,这里放宽设置为7clocks,所以Tsrc=3

(5)    BANKSIZE

开启burst mode,开启省电模式,仅当内存进行数据操作才输入SCLK频率,根据硬件BK76MAP设为1

(6)    MRSRB6和MRSRB7

CAS latency设置为3,具体设置大一点。具体手册有建议。

 

配置存储控制器的代码如下:

//BWSCON配置项

#define ST7 0

#define WS7     0

#define DW7    2

#define ST6 0

#define WS6     0

#define DW6    2

//BANK6,7配置项

#define MT       3

#define Trcd     1

#define SCAN   1

//REFRESH配置项

#define REFEN 1

#define TREFMD      0

#define Trp       0

#define Tsrc 3

#define Refresh_Counter         0x4f4                                                              //7.8125us*100MHz(HCLK)-2049= 1267.75,刷新周期应该更快一点,所以使用1268=0x4f4

//BANKSIZE配置项

#define BURST_EN  1

#define SCKE_EN     1

#define SCLK_EN      1

#define BK76MAP 1

 

void memoryControllerInit()

{

                   S3C24X0_MEMCTL* memctl = S3C24X0_GetBase_MEMCTL();

                   //mini2440BANCK0NOR FLASHNAND FLASh,由硬件跳线决定,这不用设置。BANK3接网卡,这里不做网卡实验,暂时忽略。BANK6,7SDRAM,DW7=10(32),WS7=0,ST7=0

                   memctl->BWSCON= (ST7<<31) | (WS7<<30) | (DW7<<28) | (ST6<<27) |(WS6<<26) | (DW6<<24);

                   //BANK0-5使用默认值

                   memctl->BANKCON[0]= 0x0700;

                   memctl->BANKCON[1]= 0x0700;

                   memctl->BANKCON[2]= 0x0700;

                   memctl->BANKCON[3]= 0x0700;

                   memctl->BANKCON[4]= 0x0700;

                   memctl->BANKCON[5]= 0x0700;

                   //MT=0x11表示SDRAMTrcd最小值是20nsFCLK=100HM情况下,20ns对应两个clk,这使用2clkTrcd=1(3 clolcks)SCAN=0x01,列地址为9

                   memctl->BANKCON[6]= (MT<<15) | (Trcd<<2) | (SCAN<<0);

                   memctl->BANKCON[7]= (MT<<15) | (Trcd<<2) | (SCAN<<0);

                   //REFEN=1使能刷新。TREFMD=0自动刷新。根据芯片手册,trp最小值20ns,对应于2clk,所以设置Trp=1

                   //Trc最小值是65nsTsrc=Trc-Trp,最小值是45ns,对应于4.5clock,这里放宽设置为7clocks,所以Tsrc=3

                   memctl->REFRESH= (REFEN<<23) | (TREFMD<<22) | (Trp<<20) | (TREFMD<<18)| (Refresh_Counter<<0);

                   //开启burst mode,开启省电模式,仅当内存进行数据操作才输入SCLK频率,根据硬件BK76MAP设为1

                   memctl->BANKSIZE= (BURST_EN<<7) | (SCKE_EN<<5) | (SCLK_EN<<4) |(BK76MAP<<0);

                   //设置CAS latency3

                   memctl->MRSRB6= 0x30;

                   memctl->MRSRB7= 0x30;

}

 

5.2 nandflash

         使用的NANDFLASH型号是K9F2G08U0B。

5.2.1nandflash寄存器设置

(1) NFCONF

选用的8bit总线的NandFlash,所以BusWidth设为0。

TWRPH1,TWRPH0以及TACLS根据手册设置。如下图。

NAND FLASH命令锁存时序图

 

NAND FLASH地址锁存时序图

 

2440关于时序要求。


对比上面各图,锁存命令时, TACLS=tCLS-tWP, TWRPH0=tWP, TWRPH1=tCLH。

对比上面各图,锁存地址时, TACLS=tALS-tWP, TWRPH0=tWP, TWRPH1=tALH。

查询电器参数特性表可知:

tCLS最小值是12ns,tWP最小值是12n,tCLH最小值是5ns。

tALS最小值是12ns,tWP最小值是12n,tALH最小值是5ns。

Nand Flash使用HCLK,这里设置为100MHz,对应周期是10ns。所以,NFCONF中TACLS设置为2(个周期), TWRPH0设置为1(1+1个周期),TWRPH1设置为0(0+1个周期)。

 

(2) NFCONT

InitECC设置为1,初始化ECC。

Reg_nCE设置为0,使能片选信号。

MODE设置为1,使能NAND FLASH。

 

(3) NFCMMD NFADDR NFDATA

用于处理命令地址和数据。

(4) NFMECCD0 NFMECCD1 NFMECCD NFMECC0NFMECC1 NFSECC暂时不考虑

(5) NFSTAT

IllegalAccess不合法操作会置位

RnB_TransDetect RnB从0变为1的时候,这个值被置位。写1清除该位。

nCE (Read-only)反应了片选信号nCE的状态。

RnB (Read-only)为0表示NAND FLASH忙,1表示空闲可操作。

 

5.2.2 nandflash操作

         由于移植uboot的时候需要将nandflash读取数据然后移到SDRAM中,所以这里需要介绍一下读操作。

读操作时序图


         从时序图可以看出首先发送0x00命令,然后发送5个字节的地址,然后发送命令0x30命令。这时候R/B会拉低,待R/B重新拉高后就可以从串行数据接口中读数据了。

         S3C2440的nandflash控制器帮着完成具体的时序,其他的我们只需要读写寄存器就可以。先介绍下各个操作。

(1)写命令

写命令只需要向nandflash的NFCMD寄存器中直接写入值便可以。代码如下:

static voidwrite_cmd(uint8_t cmd)

{

         S3C2440_NAND * nand =S3C2440_GetBase_NAND();

         nand->NFCMD= cmd;

         int i;

         for(i=0;i<10;i++);

}

(2)写地址

         写地址只需要向nandflash寄存器中的NFADDR寄存器中写入值便可以。

nandflash的地址需要特殊的说明一下。实质上,nandflash不存在绝对的地址,下面的代码为了便于寻址,仅仅是提出了一个绝对地址的概念。

通过下面两个图可以看出,该nandflash每也有2Kbyte再加上64Byte的OOB,每个块有64页,每个设备有2048块。这里把页内索引字节看成了column,至于索引哪个块中的哪个页统一看成row。IO0-IO10是页内用于索引页内的某个字节。IO11表示是否选中了spare filed(OOB),如果改为为0,IO0-IO11可以访问常规地址区域(页内的前面2K字节)。如果为1,IO0-IO11可以访问spare field区域(页内的2K字节之后的64字节)。IO12-IO28用于选择是哪个块中的那个页。

         通过上面的分析,我们可以把常规的地址区域看成一块连续的地址空间,忽略spare field(OOB),即第一页内前2K常规区域地址是从0到2047,第二页内的常规地址是从2048-4095。则套地址范围仅仅能够访问常规地址区域,由write_addr实现。同时,在用另一个函数write_addr_spare(该函数尚未实验验证)访问spare filed。

注:当然可以用同一个连续的地址区域同时访问常规区域和spare field。但是这样譬如2112-4095这段的地址空间就会被浪费,地址空间范围也扩大到512M(该nandflash为256M)。

static void write_addr(uint32_t addr)

{

         inti;

         S3C2440_NAND* nand = S3C2440_GetBase_NAND();

         nand->NFADDR= addr&0xff; 

         for(i=0;i<10;i++);

         nand->NFADDR= (addr>>8)&0x07;                            //这里忽略A11

         for(i=0;i<10;i++);

         nand->NFADDR= (addr>>11)&0xff;                            //从A12开始,对应于地址的第11位

         for(i=0;i<10;i++);

         nand->NFADDR= (addr>>19)&0xff;        

         for(i=0;i<10;i++);

         nand->NFADDR= (addr>>27)&0x01;      

         for(i=0;i<10;i++);

}

 

static void write_addr_spare(uint32_trowNum)

{

         inti;

         S3C2440_NAND* nand = S3C2440_GetBase_NAND();

         nand->NFADDR= 0;

         for(i=0;i<10;i++);

         nand->NFADDR= 1<<3;                     //选择使用spare

         for(i=0;i<10;i++);

         nand->NFADDR= ((rowNum*PAZE_SIZE)>>11)&0xff;                       //从A12开始,对应于地址的第11位

         for(i=0;i<10;i++);

         nand->NFADDR= ((rowNum*PAZE_SIZE)>>19)&0xff;    

         for(i=0;i<10;i++);

         nand->NFADDR= ((rowNum*PAZE_SIZE)>>27)&0x01;  

         for(i=0;i<10;i++);

}

(3)读数据

写命令只需要从nandflash的NFDATA寄存器读数据便可以。代码如下:

static uint8_t read_data()

{

         S3C2440_NAND* nand = S3C2440_GetBase_NAND();

         returnnand->NFDATA;

}

(4)等待命令完成

         该过程对应于时序图中等待R/B重新被拉高的过程。

static void wait_read_ok()

{

         inti;

         S3C2440_NAND* nand = S3C2440_GetBase_NAND();

         while(!(nand->NFSTAT&0x1))

                   for(i=0;i<10;i++);

}

(5) 打开nandflash

         设置nandflash寄存器,并打开片选,时能nandflash等。并发送0xff复位命令。

static void open_nand()

{

                   S3C2440_NAND* nand = S3C2440_GetBase_NAND();

                   nand->NFCONF= (BusWidth<<0) | (TWRPH1<<4) | (TWRPH0<<8) |(TACLS<<12);

                   //初始化ECC,使能片选信号,使能NANDFLASH

                   nand->NFCONT&= ~((1<<4) | (1<<1) | (1<<0)) ;

                   nand->NFCONT|= ((1<<4) | (0<<1) | (1<<0));

                   //发reset命令

                   write_cmd(0xff);

                   wait_read_ok();

}

 

(7)    关闭nandflash

取消片选,关闭nandflash

static void close_nand()

{

                   S3C2440_NAND* nand = S3C2440_GetBase_NAND();

                   //关闭使能片选信号,关闭NANDFLASH

                   nand->NFCONT&= ~((1<<1) | (1<<0)) ;

                   nand->NFCONT|= ((1<<1) | (0<<0));

}

 

(7) 读操作

专门读常规区域,不考虑sparefield。对应于读操作时序图。

void read_bytes(uint32_t addr,uint8_t *data,uint32_t len)

{

         open_nand();

         write_cmd(0x00);

         write_addr(addr);

         write_cmd(0x30);

         wait_read_ok();

         inti,j;

         intpage_num = len/PAZE_SIZE;

         for(i=0;i<page_num;)

         {

                   write_cmd(0);       

                   write_addr(addr+i*PAZE_SIZE);

                   write_cmd(0x30);

                   wait_read_ok();

                   for(j=0;j<PAZE_SIZE;j++)

                   {

                            *data= read_data();

                            data++;

                   }

                   i++;

         }

         close_nand();

}

 

6 实验流程

(1)    使用jink链接jtag接口,并打开j-link Commander

(2)    输入r

(3)    Speed 1200

(4)    Loadbin e:\main.bin 0

(5)    Setpc 0

(6)    g

(7)    h

(8)    loadbin e:\u-boot.bin_opnjtag0x33f80000

(9)    setpc 0x33f80000                                 //保证串口调试终端打开并连接

(10) g

(11) loadbin e:\main.bin 0x30000000

(12) 串口调试终端输入 nanderase 0 0x3000

(13) 串口调试终端输入 nandwrite 0x30000000 0 0x30000

(14) 拔掉jtag,重启。

注:程序源码存在于下载资源处。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值