一、NAND原理
NAND 无地址空间,地址和数据的发送都依赖于LDATA[0:7]这一串数据总线。
不看随机页编程,看到从高位到低位的页,总共分为64个页面,每个页的组成是2K + 64 个byte,一个块的大小是(128K + 4K)byte,64页组成一块。
1.1 NAND Flash的编址
nand flash的的页的大小是(2048 + 64)byte,64这个数据是不参与编址的。
访问 nand flash:
-
- 发出命令:读、写、擦除
- 发出地址
- 传输数据
命令设置如下:
1.2 nand flash的访问
可以看看上面的模式选择,然后对应引脚进行操作,就可以进行读写操作。命令和数据就通过这些引脚来进行操作。但是不必直接操作这些引脚,可以通过寄存器来进行操作。
查看S3C2440的手册,可以看到如下的信息:
写命令寄存器就相当于NAND Flash寄存器命令周期,写地址寄存器就相当于NAND Flash寄存器的地址周期......
我们的NAND Flash是X8的,即是8位NAND Flash,对应2440上面的一些控制寄存器:
NAND Flash命令寄存器:
地址寄存器:
数据寄存器:
状态寄存器:
还有一些其他寄存器是与读写有关的。
操作这些寄存器,2440会自动驱动PIN脚给出信号。
二、u-boot中设置nand启动
2.1 建立init.c
这是NAND启动的初始化文件,里面包含了nand启动的初始化函数。代码添加到 borad/samdung/jz2440/ 目录下:
1 /* NAND FLASH控制器 */ 2 #define NFCONF (*((volatile unsigned long *)0x4E000000)) 3 #define NFCONT (*((volatile unsigned long *)0x4E000004)) 4 #define NFCMMD (*((volatile unsigned char *)0x4E000008)) 5 #define NFADDR (*((volatile unsigned char *)0x4E00000C)) 6 #define NFDATA (*((volatile unsigned char *)0x4E000010)) 7 #define NFSTAT (*((volatile unsigned char *)0x4E000020)) 8 9 /* GPIO */ 10 #define GPHCON (*(volatile unsigned long *)0x56000070) 11 #define GPHUP (*(volatile unsigned long *)0x56000078) 12 13 /* UART registers*/ 14 #define ULCON0 (*(volatile unsigned long *)0x50000000) 15 #define UCON0 (*(volatile unsigned long *)0x50000004) 16 #define UFCON0 (*(volatile unsigned long *)0x50000008) 17 #define UMCON0 (*(volatile unsigned long *)0x5000000c) 18 #define UTRSTAT0 (*(volatile unsigned long *)0x50000010) 19 #define UTXH0 (*(volatile unsigned char *)0x50000020) 20 #define URXH0 (*(volatile unsigned char *)0x50000024) 21 #define UBRDIV0 (*(volatile unsigned long *)0x50000028) 22 23 #define TXD0READY (1<<2) 24 25 26 void nand_read_ll(unsigned int addr, unsigned char *buf, unsigned int len); 27 28 /* 判定是否是nor启动 */ 29 static int isBootFromNorFlash(void) 30 { 31 volatile int *p = (volatile int *)0; 32 int val; 33 34 val = *p; 35 *p = 0x12345678; 36 if (*p == 0x12345678) 37 { 38 /* 写成功, 是nand启动 */ 39 *p = val; 40 return 0; 41 } 42 else 43 { 44 /* NOR不能像内存一样写 */ 45 return 1; 46 } 47 } 48 49 /* 拷贝代码到sdram */ 50 void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len) 51 { 52 int i = 0; 53 54 /* 如果是NOR启动 */ 55 if (isBootFromNorFlash()) 56 { 57 while (i < len) 58 { 59 dest[i] = src[i]; 60 i++; 61 } 62 } 63 else 64 { 65 //nand_init(); 66 nand_read_ll((unsigned int)src, dest, len); 67 } 68 } 69 70 /* 清除BSS */ 71 void clear_bss(void) 72 { 73 extern int __bss_start, __bss_end; 74 int *p = &__bss_start; 75 76 for (; p < &__bss_end; p++) 77 *p = 0; 78 } 79 80 /* nand初始化 */ 81 void nand_init_ll(void) 82 { 83 #define TACLS 0 84 #define TWRPH0 1 85 #define TWRPH1 0 86 /* 设置时序 */ 87 NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); 88 /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */ 89 NFCONT = (1<<4)|(1<<1)|(1<<0); 90 } 91 92 static void nand_select(void) 93 { 94 NFCONT &= ~(1<<1); 95 } 96 97 static void nand_deselect(void) 98 { 99 NFCONT |= (1<<1); 100 } 101 102 static void nand_cmd(unsigned char cmd) 103 { 104 volatile int i; 105 NFCMMD = cmd; 106 for (i = 0; i < 10; i++); 107 } 108 109 static void nand_addr(unsigned int addr) 110 { 111 unsigned int col = addr % 2048; 112 unsigned int page = addr / 2048; 113 volatile int i; 114 115 NFADDR = col & 0xff; 116 for (i = 0; i < 10; i++); 117 NFADDR = (col >> 8) & 0xff; 118 for (i = 0; i < 10; i++); 119 120 NFADDR = page & 0xff; 121 for (i = 0; i < 10; i++); 122 NFADDR = (page >> 8) & 0xff; 123 for (i = 0; i < 10; i++); 124 NFADDR = (page >> 16) & 0xff; 125 for (i = 0; i < 10; i++); 126 } 127 128 static void nand_wait_ready(void) 129 { 130 while (!(NFSTAT & 1)); 131 } 132 133 static unsigned char nand_data(void) 134 { 135 return NFDATA; 136 } 137 138 void nand_read_ll(unsigned int addr, unsigned char *buf, unsigned int len) 139 { 140 int col = addr % 2048; 141 int i = 0; 142 143 /* 1. 选中 */ 144 nand_select(); 145 146 while (i < len) 147 { 148 /* 2. 发出读命令00h */ 149 nand_cmd(0x00); 150 151 /* 3. 发出地址(分5步发出) */ 152 nand_addr(addr); 153 154 /* 4. 发出读命令30h */ 155 nand_cmd(0x30); 156 157 /* 5. 判断状态 */ 158 nand_wait_ready(); 159 160 /* 6. 读数据 */ 161 for (; (col < 2048) && (i < len); col++) 162 { 163 buf[i] = nand_data(); 164 i++; 165 addr++; 166 } 167 168 col = 0; 169 } 170 171 /* 7. 取消选中 */ 172 nand_deselect(); 173 }
修改 borad/samdung/jz2440/ 目录下的 makefile:
2.2 去掉代码重定位
去掉 -pie 选项:
注释掉第82行:
2.3 修改代码链接地址
Jz2440.h (include\configs)
2.4 修改链接脚本文件
U-boot.lds (arch\arm\cpu) 链接脚本的修改,本版本u-boot将一个文件夹下面的c文件都链接成一个.o文件,名字为built-in.o,所以我们直接写上built-in.o即可。
将这两个built-in.o放入链接脚本当中:
2.5 修改 board_init_f
在crt0.S中设置nand 启动的代码:
这一段代码就是 nand 重定位代码了。CONFIG_SYS_TEXT_BASE 是自己设置的,可以自行设置大小。
接下来的代码就是跳转到 board_init_f_mem 和 board_init_f 中执行初始化。
init_sequence_f 链表中要注释掉下面两行,并更改:
2.6 修改重定位代码
首先在board_init_f(Board_f.c (common) )添加返回函数:
1 unsigned int board_init_f(ulong boot_flags) 2 { 3 4 gd->flags = boot_flags; 5 gd->have_console = 0; 6 7 if (initcall_run_list(init_sequence_f)) 8 hang(); 9 #if 0 10 #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && 11 !defined(CONFIG_EFI_APP) 12 /* NOTREACHED - jump_to_copy() does not return */ 13 hang(); 14 #endif 15 #endif 16 return (unsigned int)(gd->new_gd); //添加的返回值,返回id供 board_init_r调用 17 }
board_init_f 函数原型记得在 include/common中也修改。
修改crt0.S 代码如下:
ENTRY(_main) /* * Set up initial C runtime environment and call board_init_f(0). * 初始化C运行环境并且调用 board_init_f(0) 函数 */ /* * 初始化栈地址 */ #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK) ldr sp, =(CONFIG_SPL_STACK) #else /* Generic-asm-offsets.h (include\generated) * #define GENERATED_GBL_DATA_SIZE 192 * JZ2440.h(include\config) * #define PHYS_SDRAM_1 0x30000000 * #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 * #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x1000 - GENERATED_GBL_DATA_SIZE) * * CONFIG_SYS_INIT_SP_ADDR = 0x30000000 + 0x1000 - 192(0xc0) = 0x30000f40 */ ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) /* 设置CFIG_SYS_INIT_SP_ADDR定义的地址,include/configs/jz2440.h中定义 */ #endif #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */ mov r3, sp bic r3, r3, #7 mov sp, r3 #else /* sp 的8字节对齐 */ bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ #endif /* 设置nand 启动 */ bl nand_init_ll /* 跳转到borad/samsung/jz2440/init.c 中执行 nand_init_ll 函数 */ mov r0, #0 ldr r1, =(CONFIG_SYS_TEXT_BASE) /*CONFIG_SYS_TEXT_BASE=0x33f00000 程序的链接地址*/ ldr r2, =0x100000 /* 程序大小 */ bl copy_code_to_sdram /* 拷贝代码到SDRAM */ bl clear_bss /* 清除bss */ ldr pc, =call_board_init_f /* 执行第一阶段初始化 */ bl call_board_init_r /* 执行第二阶段代码 */ adr lr, here /*设置返回地址为下面的here,重定位到sdram后返回here运行*/ ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off 取重定位地址偏移值 */ add lr, lr, r0 /*返回地址加偏移地址等于重定位后在sdram中的here地址*/ here: /*返回后跳到sdram中运行 */ /* * now relocate vectors */ bl relocate_vectors /* Set up final (full) environment */ bl c_runtime_cpu_setup /* we still call old routine here */ call_board_init_f: mov r0, sp /* r0 = sp */ bl board_init_f_mem /*跳转到 board_init_f_mem 执行*/ mov sp, r0 mov r0, #0 bl board_init_f /* 调用单板的初始化函数,跳转到 borad_init_f 处执行 */ call_board_init_r: ldr r1, =CONFIG_SYS_TEXT_BASE /* call board_init_r */ bl board_init_r /* this is auto-relocated! */ ENDPROC(_main)
here的代码一定不能注释掉,注释掉后就启动不了
2.7 去掉-pie选项
如下图:
三、编译烧写
make CROSS_COMPILE=arm-2440-linux-gnueabi-
生成的 u-boot.bin 有470K 左右。
然后通过jtag 工具直接烧写进nand 的 0地址中,拨码开关拨到 nand 启动,显示如下: