四、NAND Flash
4.1 nand flash启动u-boot
nand flash 启动的时候,CPU 需要将 nand flash 中前面 4KB 的内容复制到 SRAM 中执行,然后将 NAND Flash 中的所有内容拷贝到 SDRAM中。
前4 KB 的拷贝 是硬件自动执行的。
4.1.1 地址空间
原理图如下:
nand flash 只有数据总线,并没有像 SDRAM 一样有地址总线。这样就有两种寻址方式就不同。
SDRAM 或 网卡、片内4K内存 都是地址总线接到 2440 上,他们的地址都是 CPU可以看到的,都是CPU发出来的,这为 CPU 统一编址。
nand flash 上页存在地址空间(0~256 M),nand flash 上的 0 地址 和 CPU 上的 0地址是不同的。
nand flash 地址都是通过 数据总线来发送的。
JZ2440板子上用的是大页的 nand flash,大页是指一个页里面有 2K 字节,小页只有 512 字节。结构如下图:
一页分为 2K 字节 + 64字节的OOB,一块的大小为 128K + 4K byte,128/2 = 64 页组成一块。
OOB 在大多数时候不参与编址。
4.1.2 nand flash 操作
CPU 先发出命令给 nandflash 要寻址,然后发送地址,然后再发送 读或写命令,在读或写数据。
nand flash 的命令如下图:
nandflash 引脚功能
- 从硬件上访问 NAND
- 发出命令:CLE引脚,命令发送到数据总线上
- 发出地址:ALE引脚,地址发送到数据总线上
- 传输数据:R/W
- 2440 控制
- 发出命令,寄存器 NFCMMD 寄存器
- 发出地址,寄存器 NFADDR 寄存器
- 读写数据,NFDATA
- 状态,NFSTAT
读流程如下:(其他流程见 nand flash 的芯片手册的第三章)
4.1.3 代码
nand.lds
1 SECTIONS { 2 // head.o init.o nand.o 存放在地址 0x0000 0000 处 3 firtst 0x00000000 : { head.o init.o nand.o} 4 // main.o 存放在地址 0x3000 0000 处 SDRAM 5 second 0x30000000 : AT(4096) { main.o } 6 }
head.S
1 @****************************************************************************** 2 @ File:head.s 3 @ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行 4 @****************************************************************************** 5 6 .text 7 .global _start 8 _start: 9 @函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义 10 ldr sp, =4096 @设置堆栈,栈指向SRAM顶端 11 bl disable_watch_dog @关WATCH DOG 12 bl memsetup @初始化SDRAM 13 bl nand_init @初始化NAND Flash 14 15 @将NAND Flash中地址4096开始的1024字节代码(main.c编译得到)复制到SDRAM中 16 @nand_read_ll函数需要3个参数: 17 ldr r0, =0x30000000 @1. 目标地址=0x30000000,这是SDRAM的起始地址 18 mov r1, #4096 @2. 源地址 = 4096,连接的时候,main.c中的代码都存在NAND Flash地址4096开始处 19 mov r2, #2048 @3. 复制长度= 2048(bytes),对于本实验的main.c,这是足够了 20 bl nand_read @调用C函数nand_read 21 22 ldr sp, =0x34000000 @设置栈 23 ldr lr, =halt_loop @设置返回地址 24 ldr pc, =main @b指令和bl指令只能前后跳转32M的范围,所以这里使用向pc赋值的方法进行跳转 25 halt_loop: 26 b halt_loop
init.c
1 /* WOTCH DOG register */ 2 #define WTCON (*(volatile unsigned long *)0x53000000) 3 4 /* SDRAM regisers */ 5 #define MEM_CTL_BASE 0x48000000 6 7 void disable_watch_dog(); 8 void memsetup(); 9 10 /*上电后,WATCH DOG默认是开着的,要把它关掉 */ 11 void disable_watch_dog() 12 { 13 WTCON = 0; 14 } 15 16 /* 设置控制SDRAM的13个寄存器 */ 17 void memsetup() 18 { 19 int i = 0; 20 unsigned long *p = (unsigned long *)MEM_CTL_BASE; 21 22 /* SDRAM 13个寄存器的值 */ 23 unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON,这里将所有的内存配置都设置了,我们关注SDRAM DW6 0010 即可,为16位 24 0x00000700, //BANKCON0, 25 0x00000700, //BANKCON1 26 0x00000700, //BANKCON2 27 0x00000700, //BANKCON3 28 0x00000700, //BANKCON4 29 0x00000700, //BANKCON5 30 0x00018005, //BANKCON6,SDRAM 的设置,对照寄存器查看参数 31 0x00018005, //BANKCON7 32 0x008C07A3, //REFRESH,刷新寄存器,使能,刷新模式为自刷新,RAS改变时间为2个时钟, 33 //SDRAM Semi Row cycle time 为7个时钟 34 //Refresh Counte 为 11 1010 0011 = 931,Refresh period = (211-refresh_count+1)/HCLK 35 0x000000B1, //BANKSIZE,SDRAM 大小设置为 64M 36 0x00000030, //MRSRB6,SDRAM 模式设置 37 0x00000030, //MRSRB7 38 }; 39 40 for(; i < 13; i++) 41 p[i] = mem_cfg_val[i]; // 为每一个 内存控制器的寄存器设置参数 42 }
nand,c
1 /* nand flash support S3C2410 and S3C2440 */ 2 3 #define LARGER_NAND_PAGE 4 5 #define GSTATUS1 (*(volatile unsigned int *)0x560000B0) 6 #define BUSY 1 7 8 #define NAND_SECTOR_SIZE 512 9 #define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1) 10 11 #define NAND_SECTOR_SIZE_LP 2048 12 #define NAND_BLOCK_MASK_LP (NAND_SECTOR_SIZE_LP - 1) //2047 13 14 typedef unsigned int S3C24X0_REG32; 15 16 17 /* NAND FLASH (see S3C2410 manual chapter 6) */ 18 typedef struct { 19 S3C24X0_REG32 NFCONF; 20 S3C24X0_REG32 NFCMD; 21 S3C24X0_REG32 NFADDR; 22 S3C24X0_REG32 NFDATA; 23 S3C24X0_REG32 NFSTAT; 24 S3C24X0_REG32 NFECC; 25 } S3C2410_NAND; 26 27 /* NAND FLASH (see S3C2440 manual chapter 6) */ 28 typedef struct { 29 S3C24X0_REG32 NFCONF; // nand flash 控制寄存器 30 S3C24X0_REG32 NFCONT; // nand flash 配置寄存器 31 S3C24X0_REG32 NFCMD; // nand flash 命令寄存器 32 S3C24X0_REG32 NFADDR; // nand flash 地址寄存器 33 S3C24X0_REG32 NFDATA; // nand flash 数据寄存器 34 S3C24X0_REG32 NFMECCD0; // nand flash ECC0/1寄存器 35 S3C24X0_REG32 NFMECCD1; // nand flash ECC2/3寄存器 36 S3C24X0_REG32 NFSECCD; // nand flash ECC寄存器 37 S3C24X0_REG32 NFSTAT; // nand flash 工作状态寄存器 38 S3C24X0_REG32 NFESTAT0; // nand flash IO[7:0]状态寄存器 39 S3C24X0_REG32 NFESTAT1; // nand flash IO[15:8]状态寄存器 40 S3C24X0_REG32 NFMECC0; // nand flash ECC0寄存器 41 S3C24X0_REG32 NFMECC1; // nand flash ECC1寄存器 42 S3C24X0_REG32 NFSECC; // nand flash ECC寄存器 43 S3C24X0_REG32 NFSBLK; // nand flash 块开始地址寄存器 44 S3C24X0_REG32 NFEBLK; // nand flash 块结束地址寄存器 45 } S3C2440_NAND; 46 47 48 typedef struct { 49 void (*nand_reset)(void); 50 void (*wait_idle)(void); 51 void (*nand_select_chip)(void); 52 void (*nand_deselect_chip)(void); 53 void (*write_cmd)(int cmd); 54 void (*write_addr)(unsigned int addr); 55 unsigned char (*read_data)(void); 56 }t_nand_chip; 57 58 // 设置 nand 基地址 59 static S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000; 60 static S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000; 61 62 static t_nand_chip nand_chip; 63 64 /* 供外部调用的函数 */ 65 void nand_init(void); 66 void nand_read(unsigned char *buf, unsigned long start_addr, int size); 67 68 /* NAND Flash操作的总入口, 它们将调用S3C2410或S3C2440的相应函数 */ 69 static void nand_reset(void); 70 static void wait_idle(void); 71 static void nand_select_chip(void); 72 static void nand_deselect_chip(void); 73 static void write_cmd(int cmd); 74 static void write_addr(unsigned int addr); 75 static unsigned char read_data(void); 76 77 /* S3C2410的NAND Flash处理函数 */ 78 static void s3c2410_nand_reset(void); 79 static void s3c2410_wait_idle(void); 80 static void s3c2410_nand_select_chip(void); 81 static void s3c2410_nand_deselect_chip(void); 82 static void s3c2410_write_cmd(int cmd); 83 static void s3c2410_write_addr(unsigned int addr); 84 static unsigned char s3c2410_read_data(); 85 86 /* S3C2440的NAND Flash处理函数 */ 87 static void s3c2440_nand_reset(void); 88 static void s3c2440_wait_idle(void); 89 static void s3c2440_nand_select_chip(void); 90 static void s3c2440_nand_deselect_chip(void); 91 static void s3c2440_write_cmd(int cmd); 92 static void s3c2440_write_addr(unsigned int addr); 93 static unsigned char s3c2440_read_data(void); 94 95 /* S3C2410的NAND Flash操作函数 */ 96 97 /* 复位 */ 98 static void s3c2410_nand_reset(void) 99 { 100 s3c2410_nand_select_chip(); 101 s3c2410_write_cmd(0xff); // 复位命令 102 s3c2410_wait_idle(); 103 s3c2410_nand_deselect_chip(); 104 } 105 106 /* 等待NAND Flash就绪 */ 107 static void s3c2410_wait_idle(void) 108 { 109 int i; 110 volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFSTAT; 111 while(!(*p & BUSY)) 112 for(i=0; i<10; i++); 113 } 114 115 /* 发出片选信号 */ 116 static void s3c2410_nand_select_chip(void) 117 { 118 int i; 119 s3c2410nand->NFCONF &= ~(1<<11); 120 for(i=0; i<10; i++); 121 } 122 123 /* 取消片选信号 */ 124 static void s3c2410_nand_deselect_chip(void) 125 { 126 s3c2410nand->NFCONF |= (1<<11); 127 } 128 129 /* 发出命令 */ 130 static void s3c2410_write_cmd(int cmd) 131 { 132 volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFCMD; 133 *p = cmd; 134 } 135 136 /* 发出地址 */ 137 static void s3c2410_write_addr(unsigned int addr) 138 { 139 int i; 140 volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFADDR; 141 142 *p = addr & 0xff; 143 for(i=0; i<10; i++); 144 *p = (addr >> 9) & 0xff; 145 for(i=0; i<10; i++); 146 *p = (addr >> 17) & 0xff; 147 for(i=0; i<10; i++); 148 *p = (addr >> 25) & 0xff; 149 for(i=0; i<10; i++); 150 } 151 152 /* 读取数据 */ 153 static unsigned char s3c2410_read_data(void) 154 { 155 volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFDATA; 156 return *p; 157 } 158 159 /* S3C2440的NAND Flash操作函数 */ 160 161 /* 复位 */ 162 static void s3c2440_nand_reset(void) 163 { 164 s3c2440_nand_select_chip(); 165 s3c2440_write_cmd(0xff); // 发送复位命令 166 s3c2440_wait_idle(); 167 s3c2440_nand_deselect_chip(); 168 } 169 170 /* 等待NAND Flash就绪 */ 171 static void s3c2440_wait_idle(void) 172 { 173 int i; 174 volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT; 175 while(!(*p & BUSY)) //BUSY = 1,NFSTAT的 bit0 = 1 时候,为就绪,则跳出循环准备操作 176 for(i=0; i<10; i++); 177 } 178 179 /* 发出片选信号 */ 180 static void s3c2440_nand_select_chip(void) 181 { 182 int i; 183 s3c2440nand->NFCONT &= ~(1<<1);//将 nFCE 强制拉低,使能片选 184 for(i=0; i<10; i++);//延迟 185 } 186 187 /* 取消片选信号 */ 188 static void s3c2440_nand_deselect_chip(void) 189 { 190 s3c2440nand->NFCONT |= (1<<1);//将 nFCE 强制拉高,不使能片选 191 } 192 193 /* 发出命令 */ 194 static void s3c2440_write_cmd(int cmd) 195 { 196 // 获取 NFCMD 的地址,对NFCMD的地址进行强转 197 volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD; 198 *p = cmd; 199 } 200 201 /* 发出地址 */ 202 static void s3c2440_write_addr(unsigned int addr) 203 { 204 int i; 205 volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR; 206 207 *p = addr & 0xff; 208 for(i=0; i<10; i++); 209 *p = (addr >> 9) & 0xff; 210 for(i=0; i<10; i++); 211 *p = (addr >> 17) & 0xff; 212 for(i=0; i<10; i++); 213 *p = (addr >> 25) & 0xff; 214 for(i=0; i<10; i++); 215 } 216 217 218 static void s3c2440_write_addr_lp(unsigned int addr) 219 { 220 int i; 221 volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR; 222 int col, page; 223 224 col = addr & NAND_BLOCK_MASK_LP; 225 page = addr / NAND_SECTOR_SIZE_LP; 226 227 *p = col & 0xff; /* Column Address A0~A7 */ 228 for(i=0; i<10; i++); 229 *p = (col >> 8) & 0x0f; /* Column Address A8~A11 */ 230 for(i=0; i<10; i++); 231 *p = page & 0xff; /* Row Address A12~A19 */ 232 for(i=0; i<10; i++); 233 *p = (page >> 8) & 0xff; /* Row Address A20~A27 */ 234 for(i=0; i<10; i++); 235 *p = (page >> 16) & 0x03; /* Row Address A28~A29 */ 236 for(i=0; i<10; i++); 237 } 238 239 240 /* 读取数据 */ 241 static unsigned char s3c2440_read_data(void) 242 { 243 volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA; 244 return *p; 245 } 246 247 248 /* 在第一次使用NAND Flash前,复位一下NAND Flash */ 249 static void nand_reset(void) 250 { 251 nand_chip.nand_reset(); 252 } 253 254 static void wait_idle(void) 255 { 256 nand_chip.wait_idle(); 257 } 258 259 static void nand_select_chip(void) 260 { 261 int i; 262 nand_chip.nand_select_chip(); 263 for(i=0; i<10; i++); 264 } 265 266 static void nand_deselect_chip(void) 267 { 268 nand_chip.nand_deselect_chip(); 269 } 270 271 static void write_cmd(int cmd) 272 { 273 nand_chip.write_cmd(cmd); 274 } 275 static void write_addr(unsigned int addr) 276 { 277 nand_chip.write_addr(addr); 278 } 279 280 static unsigned char read_data(void) 281 { 282 return nand_chip.read_data(); 283 } 284 285 286 /* 初始化NAND Flash */ 287 void nand_init(void) 288 { 289 #define TACLS 0 290 #define TWRPH0 3 291 #define TWRPH1 0 292 293 /* 判断是S3C2410还是S3C2440 */ 294 /* GSTATUS1:通用状态寄存器,GSTATUS1 为读寄存器,是用来读出 chip ID,这是用来定位我们的设备的 */ 295 /* 所用的设备为 S3C2440AL,设备ID为 0x32440001 */ 296 if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32440001)) 297 { 298 nand_chip.nand_reset = s3c2410_nand_reset; 299 nand_chip.wait_idle = s3c2410_wait_idle; 300 nand_chip.nand_select_chip = s3c2410_nand_select_chip; 301 nand_chip.nand_deselect_chip = s3c2410_nand_deselect_chip; 302 nand_chip.write_cmd = s3c2410_write_cmd; 303 nand_chip.write_addr = s3c2410_write_addr; 304 nand_chip.read_data = s3c2410_read_data; 305 306 /* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 设置时序 */ 307 s3c2410nand->NFCONF = (1<<15)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0); 308 } 309 else 310 { 311 nand_chip.nand_reset = s3c2440_nand_reset; 312 nand_chip.wait_idle = s3c2440_wait_idle; 313 nand_chip.nand_select_chip = s3c2440_nand_select_chip; 314 nand_chip.nand_deselect_chip = s3c2440_nand_deselect_chip; 315 nand_chip.write_cmd = s3c2440_write_cmd; 316 #ifdef LARGER_NAND_PAGE 317 nand_chip.write_addr = s3c2440_write_addr_lp; 318 #else 319 nand_chip.write_addr = s3c2440_write_addr; 320 #endif 321 nand_chip.read_data = s3c2440_read_data; 322 323 /* 设置时序 */ 324 s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); 325 /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */ 326 s3c2440nand->NFCONT = (1<<4)|(1<<1)|(1<<0); 327 } 328 329 /* 复位NAND Flash */ 330 nand_reset(); 331 } 332 333 334 /* 读函数 */ 335 /* buf = 0x30000000, start_addr = 4096,size = 2048 */ 336 void nand_read(unsigned char *buf, unsigned long start_addr, int size) 337 { 338 int i, j; 339 340 #ifdef LARGER_NAND_PAGE 341 if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)) { 342 return ; /* 地址或长度不对齐 */ 343 } 344 #else 345 if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) { 346 return ; /* 地址或长度不对齐 */ 347 } 348 #endif 349 350 /* 选中芯片 */ 351 nand_select_chip(); 352 353 for(i=start_addr; i < (start_addr + size);) { 354 /* 发出READ0命令 */ 355 write_cmd(0); 356 357 /* Write Address */ 358 write_addr(i); 359 #ifdef LARGER_NAND_PAGE 360 write_cmd(0x30); 361 #endif 362 wait_idle(); 363 364 #ifdef LARGER_NAND_PAGE 365 for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++) { 366 #else 367 for(j=0; j < NAND_SECTOR_SIZE; j++, i++) { 368 #endif 369 *buf = read_data(); 370 buf++; 371 } 372 } 373 374 /* 取消片选信号 */ 375 nand_deselect_chip(); 376 377 return ; 378 }
main.c
1 #define GPFCON (*(volatile unsigned long *)0x56000050) 2 #define GPFDAT (*(volatile unsigned long *)0x56000054) 3 4 #define GPF4_out (1<<(4*2)) 5 #define GPF5_out (1<<(5*2)) 6 #define GPF6_out (1<<(6*2)) 7 8 void wait(volatile unsigned long dly) 9 { 10 for(; dly > 0; dly--); 11 } 12 13 int main(void) 14 { 15 unsigned long i = 0; 16 17 GPFCON = GPF4_out|GPF5_out|GPF6_out; // 将LED1-3对应的GPF4/5/6三个引脚设为输出 18 19 while(1){ 20 wait(30000); 21 GPFDAT = (~(i<<4)); // 根据i的值,点亮LED1-3 22 if(++i == 8) 23 i = 0; 24 } 25 26 return 0; 27 }