一、NAND Flash关键硬件知识
采用k9f2g08x0a NAND Flash芯片 :(其芯片手册可在网上下载)
Organization
- Memory Cell Array : (256M 8M) x 8bit
- Data Register : (2K 64) x 8bit
Automatic Program and Erase
- Page Program : (2K 64)Byte
- Block Erase : (128K 4K)Byte
Page Read Operation
- Page Size : (2K 64)Byte
图1 NAND Flash 结构组织
图2 NAND Flash命令表
二、NAND Flash 控制寄存器
NAND FLASH 还是 NOR Flash 配置,硬件电路已设计好,仅供加深理解。
图3 PIN CONFIGURATION
NFCONF 0x4E000000 R/W NAND flash configuration register 0x0000100X
推荐值TACLS=0 TWRPH0=6 TWRPH1=2 buswitdth 8bit (TACLS+TWRPH0+TWRPH1>=50ns)
NFCONT 0x4E000004 R/W NAND flash control register 0x0384
推荐值 (1<<4) | (1<<1) | (1<<0)
NFCMMD 0x4E000008 R/W NAND flash command set register 0x00
NFCMMD [7:0] NAND flash memory command value 0x00 |
NFADDR 0x4E00000C R/W NAND flash address set register 0x0000XX00
NFADDR [7:0] NAND flash memory address value 0x00 |
NFDATA 0x4E000010 R/W NAND flash data register 0xXXXX
可8bit 16bit 32bit访问 如图4
NFDATA [31:0] NAND flash read/program data value for I/O 0xXXXX
Note: Refer to data register configuration in Page 6-5. |
图4
NFSTAT 0x4E000020 R/W NAND flash operation status register 0xXX00
三、硬件电路图
图4 NAND Flash管脚连接图
四、NAND Flash 读操作
K9F2G08U0A的一页为(2K+64)字节(加号前面的2K表示的是main区容量,加号后面的64表示的是spare区容量),它的一块为64页,而整个设备包括了2048个块。这样算下来一共有2112M位容量,如果只算main区容量则有256M字节(即256*8位)。要实现用8个IO口来要访问这么大的容量,K9F2G08U0A规定了用5个周期来实现。第一个周期访问的地址为A0~A7;第二个周期访问的地址为A8~A11,它作用在IO0~IO3上,而此时IO4~IO7必须为低电平;第三个周期访问的地址为A12~A19;第四个周期访问的地址为A20~A27;第五个周期访问的地址为A28,它作用在IO0上,而此时IO1~IO7必须为低电平。前两个周期传输的是列地址,后三个周期传输的是行地址。通过分析可知,列地址是用于寻址页内空间,行地址用于寻址页,如果要直接访问块,则需要从地址A18开始。
图4 读操作时序图
五、源代码
start.S //引导程序
- /*
- *************************************************************************
- *
- * lcl bootloader
- *
- *************************************************************************
- */
- .globl _start
- _start: b start_code
- start_code:
- /*
- * set the cpu to SVC32 mode
- */
- mrs r0, cpsr
- bic r0, r0, #0x1f
- orr r0, r0, #0xd3
- msr cpsr, r0
- #define pWTCON 0x53000000
- #define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
- #define INTSUBMSK 0x4A00001C
- #define CLKDIVN 0x4C000014 /* clock divisor register */
- #define CLK_CTL_BASE 0x4C000000 /* clock base address */
- #define MDIV_405 0x7f << 12 /* MDIV 0x7f*/
- #define PSDIV_405 0x21 /* PDIV SDIV 0x2 0x1 */
- /* turn off the watchdog */
- ldr r0, =pWTCON
- mov r1, #0x0
- str r1, [r0]
- /*
- * mask all IRQs by setting all bits in the INTMR - default
- */
- ldr r1, =0x7fff
- ldr r0, =INTSUBMSK
- str r1, [r0]
- /* FCLK:HCLK:PCLK = 1:4:8 */
- ldr r0, =CLKDIVN
- mov r1, #5 //1:4:8
- str r1, [r0]
- mrc p15, 0, r1, c1, c0, 0
- orr r1, r1, #0xc0000000
- mcr p15, 0, r1, c1, c0, 0
- mov r1, #CLK_CTL_BASE
- mov r2, #MDIV_405
- add r2, r2, #PSDIV_405
- str r2, [r1, #0x04] /* MPLLCON address Mpll=405MHZ */
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- /*
- *MMU and SDRAM initialize
- */
- bl cpu_init_crit
- /*
- *call c function . sp is 0x34000000
- */
- mov fp, #0 // no previous frame, so fp=0
- ldr sp, =0x34000000
- bl NandInit
- bl ReadImageFromNand
- /*
- *run in sdram
- */
- ldr lr, =halt_loop @设置返回地址
- ldr pc, =Main //0x30000834
- halt_loop:
- b halt_loop
- .globl ReadPage512
- ReadPage512: //读512字节
- stmfd sp!, {r2-r7}
- mov r2, #0x200
- 1:
- ldr r4, [r1] //取出32bit,按8bit字访问
- ldr r5, [r1] //取出32bit
- ldr r6, [r1] //取出32bit
- ldr r7, [r1] //取出32bit
- stmia r0!, {r4-r7} //存 64bit 到buffer中
- ldr r4, [r1]
- ldr r5, [r1]
- ldr r6, [r1]
- ldr r7, [r1]
- stmia r0!, {r4-r7} //存 64bit 到buffer中
- ldr r4, [r1]
- ldr r5, [r1]
- ldr r6, [r1]
- ldr r7, [r1]
- stmia r0!, {r4-r7} //存 64bit 到buffer中
- ldr r4, [r1]
- ldr r5, [r1]
- ldr r6, [r1]
- ldr r7, [r1]
- stmia r0!, {r4-r7} //存 64bit 到buffer中
- subs r2, r2, #64 //减去已读出的64byte
- bne 1b; //循环读出直到512byte
- ldmfd sp!, {r2-r7}
- mov pc,lr //返回
- cpu_init_crit:
- /*
- * flush v4 I/D caches
- */
- mov r0, #0
- mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
- mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
- /*
- * disable MMU stuff and caches
- */
- mrc p15, 0, r0, c1, c0, 0
- bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
- bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
- orr r0, r0, #0x00000002 @ set bit 2 (A) Align
- orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
- mcr p15, 0, r0, c1, c0, 0
- /*
- * before relocating, we have to setup RAM timing
- * because memory timing is board-dependend, you will
- * find a lowlevel_init.S in your board directory.
- */
- mov ip, lr
- bl memsetup /* memory control configuration */
- mov lr, ip
- mov pc, lr
- memsetup:
- /* memory control configuration */
- /* make r0 relative the current location so that it */
- /* reads SMRDATA out of FLASH rather than memory ! */
- #define MEM_CTL_BASE 0x48000000
- mov r1,#MEM_CTL_BASE
- adr r2,mem_cfg_val
- add r3,r1,#52
- 1:
- ldr r4, [r2], #4
- str r4, [r1], #4
- cmp r1, r3
- bne 1b
- mov pc,lr
- .align 4
- /* the literal pools origin */
- mem_cfg_val:
- .long 0x22011110
- .long 0x00000700
- .long 0x00000700
- .long 0x00000700
- .long 0x00000700
- .long 0x00000700
- .long 0x00000700 @BANK5
- .long 0x00018005
- .long 0x00018005
- .long 0x00ac03f4 @REFRESH // 12MHZ 0x00ac07a3
- .long 0x000000B1
- .long 0x00000030
- .long 0x00000030
def.h
- #ifndef __DEF_H__
- #define __DEF_H__
- #define U32 unsigned int
- #define U16 unsigned short
- #define S32 int
- #define S16 short int
- #define U8 unsigned char
- #define S8 char
- #endif /*__DEF_H__*/
nand.h
- #ifndef __NAND_H
- #define __NAND_H
- void NandInit(void);
- int NandIsGoodBlock(U32 addr);
- int NandReadOneSector(U8 * buffer, U32 addr);
- void Uart_SendString(char *);
- #define PAGE_UNKNOWN 0
- #define PAGE512 1
- #define PAGE2048 2 //本芯片为2K一页
- #define BYTE_SECTOR_SHIFT (g_page_type == PAGE512 ? 9 : 11) //2K a page=11bit
- #define SECTOR_BLOCK_SHIFT (g_page_type == PAGE512 ? 5 : 6) //64 pages=6bit
- #define SECTOR_SIZE (1 << BYTE_SECTOR_SHIFT) //2K
- #define BLOCK_SIZE (SECTOR_SIZE << SECTOR_BLOCK_SHIFT) //128K
- extern int g_page_type;
- #endif /*__NAND_H*/
nand.c
- #include "def.h"
- #include "nand.h"
- /***********nand flash address*********************/
- #define __REGb(x) (*(volatile unsigned char *)(x))
- #define __REGw(x) (*(volatile unsigned short *)(x))
- #define __REGi(x) (*(volatile unsigned int *)(x))
- #define NF_BASE 0x4e000000
- #define NFCONF __REGi(NF_BASE + 0x0)
- #define NFCONT __REGi(NF_BASE + 0x4)
- #define NFCMD __REGb(NF_BASE + 0x8)
- #define NFADDR __REGb(NF_BASE + 0xc)
- #define NFDATA __REGb(NF_BASE + 0x10)
- #define NFSTAT __REGb(NF_BASE + 0x20)
- /***********nand flash address end*********************/
- #define NF_MECC_UnLock() {NFCONT&=~(1<<5);}
- #define NF_MECC_Lock() {NFCONT|=(1<<5);}
- #define NF_CMD(cmd) {NFCMD=cmd;}
- #define NF_ADDR(addr) {NFADDR=addr;}
- #define NF_nFCE_L() {NFCONT&=~(1<<1);}
- #define NF_nFCE_H() {NFCONT|=(1<<1);}
- #define NF_RSTECC() {NFCONT|=(1<<4);}
- #define NF_RDDATA8() (NFDATA)
- #define NF_WAITRB() {while(!(NFSTAT&(1<<1)));}
- //wait tWB and check F_RNB pin.
- // RnB Signal
- #define NF_CLEAR_RB() {NFSTAT |= (1<<2);} // Have write '1' to clear this bit.
- #define NF_DETECT_RB() {while(!(NFSTAT&(1<<2)));}
- #define TACLS 0 // 1-clk(0ns)
- #define TWRPH0 6 // 3-clk(25ns)
- #define TWRPH1 2 // 1-clk(10ns) //TACLS+TWRPH0+TWRPH1>=50ns
- #define ECC_VERIFY 0
- int g_page_type = PAGE_UNKNOWN;
- extern void ReadPage512(U8* buf, volatile unsigned char *nf_data_ptr);
- static inline void NandReset(void)
- {
- NF_nFCE_L();
- NF_CLEAR_RB();
- NF_CMD(0xFF); //reset command
- NF_DETECT_RB();
- NF_nFCE_H();
- }
- static inline void delay(void)
- {
- #if 0
- volatile int i;
- for (i = 0; i < 1000; i++);
- #endif
- }
- static inline U32 NandCheckId(void)
- {
- U8 Mid, Did, DontCare, id4th;
- NF_nFCE_L();
- NF_CMD(0x90);
- NF_ADDR(0x0);
- delay();
- Mid = NF_RDDATA8();
- Did = NF_RDDATA8();
- DontCare = NF_RDDATA8();
- id4th = NF_RDDATA8();
- NF_nFCE_H();
- switch(Did) { /*选择各种支持的芯片类型*/
- case 0x76:
- g_page_type = PAGE512;/* Samsung K91208 * *Hynix HY27US08121A*/
- break;
- case 0xF1: /* Samsung K9F1G08U0B */
- case 0xD3: /* Samsung K9K8G08 */
- case 0xDA: /* Samsung K9F2G08U0B */
- case 0xDC:
- g_page_type = PAGE2048;
- break;
- default:
- ;
- }
- return (U32) ((Mid << 24) | (Did << 16) | (DontCare << 8) | id4th);
- }
- void NandInit(void)
- {
- NFCONF = (TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4) | (0 << 0);
- NFCONT =
- (0 << 13) | (0 << 12) | (0 << 10) | (0 << 9) | (0 << 8) | (0 << 6) |
- (0 << 5) | (1 << 4) | (1 << 1) | (1 << 0);
- NFSTAT = 0;
- NandReset();
- NandCheckId();
- return ;
- }
- static inline int NandIsGoodBlockP512(U32 addr)
- {
- U32 sector = addr >> 9;
- U8 bad_value;
- NandReset();
- NF_nFCE_L();
- NF_CLEAR_RB();
- NF_CMD(0x50);
- NF_ADDR(5);
- NF_ADDR(sector & 0xff);
- NF_ADDR((sector >> 8) & 0xff);
- NF_ADDR((sector >> 16) & 0xff);
- delay();
- NF_DETECT_RB();
- bad_value = NF_RDDATA8();
- NF_nFCE_H();
- if (bad_value == 0xFF) {
- return 1;
- }
- return 0;
- }
- static inline int NandIsGoodBlockP2048(U32 addr)
- {
- U32 sector = addr >> 11;
- U8 bad_value;
- NandReset();
- NF_nFCE_L();
- NF_CLEAR_RB();
- NF_CMD(0x0);
- NF_ADDR(2048 & 0xFF);
- NF_ADDR((2048 >>8) & 0xff);
- NF_ADDR(sector & 0xff);
- NF_ADDR((sector >> 8) & 0xff);
- NF_ADDR((sector >> 16) & 0xff);
- NF_CMD(0x30);
- delay();
- NF_DETECT_RB();
- bad_value = NF_RDDATA8();
- NF_nFCE_H();
- if (bad_value == 0xFF) {
- return 1;
- }
- return 0;
- }
- int NandIsGoodBlock(U32 addr)
- {
- int ret;
-
- switch(g_page_type) {
- case PAGE512:
- {
- unsigned int i;
- // for ugly method of Chen Yongqiang
- for (i = 0; i < 128 * 1024; i+= 16 * 1024) {
- ret = NandIsGoodBlockP512(addr / (128 * 1024) * (128 * 1024) + i );
- if (!ret) {
- break;
- }
- }
- }
- break;
- case PAGE2048:
- ret = NandIsGoodBlockP2048(addr);
- break;
- default:
- for(;;);
- }
- return ret;
- }
- static inline int NandReadOneSectorP512(U8 * buffer, U32 addr)
- {
- U32 sector;
- sector = addr >> 9;
- NandReset();
- #if 0
- NF_RSTECC();
- NF_MECC_UnLock();
- #endif
- NF_nFCE_L();
- NF_CLEAR_RB();
- NF_CMD(0x00);
- NF_ADDR(0x00);
- NF_ADDR(sector & 0xff);
- NF_ADDR((sector >> 8) & 0xff);
- NF_ADDR((sector >> 16) & 0xff);
- delay();
- NF_DETECT_RB();
- ReadPage512(buffer, &NFDATA);
- #if 0
- NF_MECC_Lock();
- #endif
- NF_nFCE_H();
- return 1;
- }
- static inline int NandReadOneSectorP2048(U8 * buffer, U32 addr)
- {
- U32 sector;
- sector = addr >> 11;
- delay();
- NandReset();
- #if 0
- NF_RSTECC();
- NF_MECC_UnLock();
- #endif
- NF_nFCE_L();
- NF_CLEAR_RB();
- NF_CMD(0x00);
- NF_ADDR(0x00);
- NF_ADDR(0x00);
- NF_ADDR(sector & 0xff);
- NF_ADDR((sector >> 8) & 0xff);
- NF_ADDR((sector >> 16) & 0xff);
- NF_CMD(0x30);
- delay();
- NF_DETECT_RB();
- ReadPage512(buffer + 0 * 512, &NFDATA);
- ReadPage512(buffer + 1 * 512, &NFDATA);
- ReadPage512(buffer + 2 * 512, &NFDATA);
- ReadPage512(buffer + 3 * 512, &NFDATA);
- #if 0
- NF_MECC_Lock();
- #endif
- NF_nFCE_H();
- return 1;
- }
- int NandReadOneSector(U8 * buffer, U32 addr)
- {
- int ret;
-
- switch(g_page_type) {
- case PAGE512:
- ret = NandReadOneSectorP512(buffer, addr);
- break;
- case PAGE2048:
- ret = NandReadOneSectorP2048(buffer, addr);
- break;
- default:
- for(;;);
- }
- return ret;
- }
- void ReadImageFromNand(void)
- {
- unsigned int Length;
- U8 *RAM;
- unsigned BlockNum;
- unsigned pos;
- Length = 0x100000;/*将拷贝NAND Flash 中1M的内容到SDRAM中*/
- Length = (Length + BLOCK_SIZE - 1) >> (BYTE_SECTOR_SHIFT + SECTOR_BLOCK_SHIFT) << (BYTE_SECTOR_SHIFT + SECTOR_BLOCK_SHIFT); // align to Block Size
- /*长度对齐,这很关键*/
- BlockNum = 0x00000 >> (BYTE_SECTOR_SHIFT + SECTOR_BLOCK_SHIFT);//拷贝的起始地址,地址对齐,这很关键不然会使拷贝的程序无法运行
- RAM = (U8 *)0x30000000; //拷贝至SDRAM的起始处
- for (pos = 0; pos < Length; pos += BLOCK_SIZE) {
- unsigned int i;
- // skip badblock
- for (;;) {
- if (NandIsGoodBlock
- (BlockNum <<
- (BYTE_SECTOR_SHIFT + SECTOR_BLOCK_SHIFT))) {
- break;
- }
- BlockNum++; //try next
- }
- for (i = 0; i < BLOCK_SIZE; i += SECTOR_SIZE) {
- int ret =
- NandReadOneSector(RAM,
- (BlockNum <<
- (BYTE_SECTOR_SHIFT +
- SECTOR_BLOCK_SHIFT)) + i);
- RAM += SECTOR_SIZE;
- ret = 0;
- }
- BlockNum++;
- }
- return ;
- }
main.c
- /*
- GPBCON 0x56000010 Port B control
- GPBDAT 0x56000014 Port B data
- GPBUP 0x56000018 Pull-up control B
- LED1 GPB5 LED2 GPB6 LED3 GPB7 LED4 GPB8
- GPB8 [17:16] 00 = Input 01 = Output
- 10 = nXDREQ1 11 = Reserved
- GPB7 [15:14] 00 = Input 01 = Output
- 10 = nXDACK1 11 = Reserved
- GPB6 [13:12] 00 = Input 01 = Output
- 10 = nXBREQ 11 = reserved
- GPB5 [11:10] 00 = Input 01 = Output
- 10 = nXBACK 11 = reserved
- #define S3C24X0_GPIO_BASE 0x56000000
- GPGCON = 0000 0000 1000 0000 1010 1000 1000 0010=0x80a882
- GPG0 Input/output EinT8 – – K1
- GPG3 Input/output EinT11 nSS1 – K2
- GPG5 Input/output EinT13 SPIMISO1 – k3
- GPG6 Input/output EinT14 SPIMISO1 – k4
- GPG7 Input/output EinT15 SPICLK1 – k5
- GPG11 Input/output EinT19 TCLK1 – k6
- */
- #define GPBCON (*(volatile unsigned long *) 0x56000010)
- #define GPBDAT (*(volatile unsigned long *) 0x56000014)
- #define GPBUP (*(volatile unsigned long *) 0x56000018)
- #define GPGCON (*(volatile unsigned long *) 0x56000060)
- #define GPGDAT (*(volatile unsigned long *) 0x56000064)
- #define GPGUP (*(volatile unsigned long *) 0x56000068)
- #define GPX_up 0x00000000
- #define GPB5_out (1<<(5*2))
- #define GPB6_out (1<<(6*2))
- #define GPB7_out (1<<(7*2))
- #define GPB8_out (1<<(8*2))
- #define GPG0_in ~(3<<(0*2))
- #define GPG3_in ~(3<<(3*2))
- #define GPG5_in ~(3<<(5*2))
- #define GPG6_in ~(3<<(6*2))
- static inline void leds_init()
- {
- GPBCON = (GPB5_out | GPB6_out | GPB7_out | GPB8_out);
- GPBUP = GPX_up;
- }
- static inline void buttons_init()
- {
- GPGCON = (GPG0_in & GPG3_in & GPG5_in & GPG6_in);
- GPGUP = GPX_up;
- }
- int Main()
- {
- unsigned long dwDat;
- leds_init();
- buttons_init();
- while (1)
- {
- dwDat=GPGDAT;
- if(dwDat & (1<<0))
- GPBDAT |=(1<<5);
- else
- GPBDAT &=~(1<<5);
- if(dwDat & (1<<3))
- GPBDAT |=(1<<6);
- else
- GPBDAT &=~(1<<6);
- if(dwDat & (1<<5))
- GPBDAT |=(1<<7);
- else
- GPBDAT &=~(1<<7);
- if(dwDat & (1<<6))
- GPBDAT |=(1<<8);
- else
- GPBDAT &=~(1<<8);
- }
- return 0;
- }
sdram.lds //运行地址与实时分配
- SECTIONS {
- firtst 0x00000000 : { start.o nand.o}
- second 0x30001000 : AT(4096) { main.o }
- }
Makefile
- objs := start.o nand.o main.o
- nand_sdram.bin : $(objs)
- arm-linux-ld -Tsdram.lds -o nand_sdram_elf $^
- arm-linux-objcopy -O binary -S nand_sdram_elf $@
- arm-linux-objdump -D -m arm nand_sdram_elf > nand_sdram.dis
- %.o:%.c
- arm-linux-gcc -Wall -c -O2 -o $@ $<
- %.o:%.S
- arm-linux-gcc -Wall -c -O2 -o $@ $<
- clean:
- rm -f nand_sdram.dis nand_sdram.bin nand_sdram_elf *.o
以下为能直接运行的二进制文件,下载到Nand Flash 的 Black0 直接以Nand Flash运行。
阅读(2170) | 评论(0) | 转发(0) |