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_elf是elf格式的文件,不是可执行文件。需要使用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??????? @之前一直运行在初始地址为0的stepping中,使用的都是bl跳转都是基于相对地址的,所以只在stepping范围内跳转 @要跳转到0x34000000,不再使用bl,直接给PC赋值跳转。由于链接地址是0x30000000,所以这里跳转到SDRAM的main中 @跳转到SDRAM的main函数 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(); ?????????????????? //mini2440中BANCK0接NOR FLASH或NAND FLASh,由硬件跳线决定,这不用设置。BANK3接网卡,这里不做网卡实验,暂时忽略。BANK6,7接SDRAM,DW7=10(32位),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表示SDRAM。Trcd最小值是20ns,FCLK=100HM情况下,20ns对应两个clk,这使用2clk,Trcd=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,对应于2个clk,所以设置Trp=1。 ?????????????????? //Trc最小值是65ns,Tsrc=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 latency为3 ?????????????????? 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,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,重启。 注:程序源码存在于下载资源处。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |