TQ2440开发板学习纪实--- 从NAND Flash读取数据,把代码搬运到SDRAM运行
Posted smstong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TQ2440开发板学习纪实--- 从NAND Flash读取数据,把代码搬运到SDRAM运行相关的知识,希望对你有一定的参考价值。
因为依赖于S3C2440的开机自动从Nandflash复制数据到片内SRAM执行,目前我们的可执行程序体积仍然不能大于4KB的限制。而我们的程序目前已经非常接近这个限制大小了,为了能够继续开发,必须突破这个限制。为此需要实现代码搬运功能,把程序从Nandflash搬运到SDRAM中去,并跳转到SDRAM执行。
本文为啥不实现NandFlash写? 因为对于我们的实现目前尚未需求。更重要的是,写操作涉及到擦除,更涉及到NandFlash寿命。为了减少写次数,延长NandFlash寿命,一般会在软件层实现写缓存。这就涉及到大量的代码,会偏离我们实验的主题--代码搬运。等以后确实需要保存数据到NandFlash时,再实现写功能不迟。
1 NAND Flash基础知识
1.1 TQ2440板载Nandflash芯片基本情况
NAND Flash作为一种存储芯片,其读写操作需要特定的时序和规范。实际上也存在相关的工业标准,用来标准化Nandflash的读写等操作。
与RAM和ROM不同,NAND Flash的传送数据、地址、命令时共用同一组针脚,通过不同的时序加以区分。下图是TQ2440开发板上搭载的Nandflash接口图:
其接口非常简单,其中
- IO0-IO7,8个针脚用于传送数据、地址、命令。
- WP,写保护,直接接地。
- WE,写使能
- RE,读使能
- CE,片选
- ALE,地址锁存
- RDY/B,忙闲状态
- CLE,命令锁存
- VSS,接地
- VCC,电源
1.2 Nand flash基本术语
- 页(Page),NandFlash以页为单位进行读写,常见的页大小为512B,1KB,2KB等。
- 块(BLOCK),NandFlash在写入数据之前必须先擦除(写入1),而擦除的最小单元就是块,常见的块大小为64页。
- 擦除。NandFlash写入之前必须先擦除,也就是全部写入1。因为写的时候只能写0,不能写1。
- 数据带宽,也就是每次传送数据的位数(I/O针脚数),常见的有8,16。
- 地址周期,传送一个完整的地址,需要发送几次数据。
1.3 S3C2440片内NandFlash控制器
(1)硬件读取
S3C2440片内集成了NandFlash控制器,启动时,通过外部针脚的电平高低的硬件配置来获得页大小,数据宽度,地址周期等要素,从而可以在启动时能够完成读取Nandflash到SRAM。
(2)软件读取
S3C2440的片内NandFlash控制器同样对用户开放,允许用于以软件操纵它来读写NandFlash。
2 利用S3C2440片内NandFlash控制器读取NandFlash数据
2.1 相关寄存器
- NFCONF 配置寄存器, 用于设置NandFlash时序
- NFCONT 控制寄存器,用于控制片选等信号
- GPACON 引脚复用,GPIO和NandFlash控制信号切换
- NFCMD 命令寄存器,把命令写入时,自动发送
- NFADDR 地址寄存器,把地址写入时,自动发送
- NFDATA 数据寄存器,把数据写入时,自动发送。读取时,自动更新。
NFSTAT 状态寄存器,探测NANDFLASH是否忙
其他ECC相关寄存器,用于错误校验,暂时可以不用。
具体如何设置,请参考S3C2440的数据手册和Nandflash的数据手册。
2.2 初始化
void nand_init()
rGPACON |= (0x3F << 17); /* GPIO */
rNFCONF &= ~(3<<12 | 7<<8 | 7<<4); /* clock */
rNFCONF |= (1<<12 | 4<<8 | 0<<4);
rNFCONT |= 1; /* enable NF controller */
rNFCONT &= ~(1<<1); /* Chip select */
rNFCONT &= ~(1<<12); /* disable soft lock */
rNFSTAT |= 1<<2; /* clear BUSY */
nand_send_cmd(CMD_RESET);
nand_wait();
2.3 页内任意读
NandFlash一般读取的最小单位为页。也有的NandFlash支持单字节读取,但是并不常用。我们可以通过先读一页,然后选择性的保存数据即可实现页内任意大小读取。
/* read [start,start+count) in a page */
int nand_read_page(unsigned char* buf, int iPage, int start, int count)
nand_chip_enable();
nand_send_cmd(CMD_PAGE_READ);
nand_send_addr(0x00);
nand_send_addr(0x00);
nand_send_addr(iPage & 0xFF);
nand_send_addr( (iPage >> 8) & 0xFF);
nand_send_addr( (iPage >> 16) & 0x1);
nand_send_cmd(CMD_PAGE_READ_CONFIRM);
nand_wait();
unsigned char * p = buf;
int i;
unsigned char c;
for (i=0; i<2048; i++)
if ( i<start || i>=start+count )
c = rNFDATA8;
else
*p++ = rNFDATA8;
nand_chip_disable();
return count;
2.4 全范围任意读
基于页内任意读,我们进而可以实现全范围任意读。原理如下:
任何读操作涉及的范围无非有两种情况:
(1)所需数据正好全部属于同一个页。此时执行页内任意读即可。
(2)所需数据跨两个或多个页。此时对于每一页按顺序分别执行任意读即可。只是第一页和最后一页可能可能只需读部分数据,中间的页则是全页读取。
实现代码如下:
/* read size bytes starting from addr */
int nand_read(unsigned char* buf, unsigned int addr, int size)
int startPage = addr / 2048;
int endPage = (addr+size-1) / 2048;
int startByte = addr % 2048;
/* if the range is in only one page */
if (startPage == endPage)
return nand_read_page(buf, startPage, startByte, size);
/* if the range is across 2 or more pages */
int n = nand_read_page(buf, startPage, startByte, 2048-startByte); /* the first page */
int page = startPage+1;
while (page < endPage) /* middle page(s) */
n += nand_read_page(buf+n, page, 0, 2048);
page ++;
n += nand_read_page(buf+n, endPage, 0, (size-n)); /* last page */
return n;
3 代码搬运
3.1 需求分析
(1)程序的前4KB,要完成的基本功能:CPU时钟初始化,SDRAM初始化,NandFlash初始化,UART0初始化。然后把NandFlash中的程序整体复制到SDRAM中,最后使能中断,跳转到Main函数执行。
(2)由于尚未启动MMU,所以异常向量入口位于绝对物理地址0开始处,也就是SRAM中。
(3)4KB之后的程序完成其他硬件初始化。
3.2 关键技术
3.2.1 位置无关代码
程序的前4KB首先是被硬件自动复制到SRAM中执行,然后是被自己搬运到SDRAM中运行,这就需要这部分代是地址无关代码。具体来说:
- 程序跳转需要使用相对跳转指令,如B,BL,它们都是相对于当前PC的值,增减适当偏移量来完成跳转。
- C语言会自动根据跳转范围优先选用相对跳转指令,我们的程序跳转范围小于32MB,所以C编译器会自动生成位置无关代码。
3.2.2 绝对地址跳转
这个正好与相对地址跳转相反,绝对地址跳转指令如 BX,或者给PC直接赋值的指令如 mov pc, Rn, add pc, rn, rm等等。它们都是直接给PC强制赋值,从而时间4GB范围的跳转。
SRAM地址空间为[0x00000000, 0x00001000),而SDRAM的地址范围是[0x30000000,0x34000000),显然两者最小距离也远远超过了相对跳转的范围。
目前需要绝对地址跳转的地方有:
- 向Main()函数跳转
- 向异常处理程序跳转
3.2.3 链接过程的控制
搬运代码的功能需要位于可执行程序的前4KB,这可以通过链接脚本来控制。我们的链接脚本如下。
ENTRY(ResetEntry)
SECTIONS
. = 0x30000000;
.text ALIGN(4):
init.o(.*)
boot.o(.*)
uart.o(.*)
nand.o(.*)
*(.text)
.data ALIGN(4):
*(.data)
*(.rodata)
.bss ALIGN(4):
*(.bss)
其中需要在SRAM中运行的文件是: init.s,boot.c,以及uart.c。
3.3 源码导读
基本的流程是:
(1)init.s 完成CPU时钟初始化,SDRAM初始化,各模式堆栈初始化,以及异常向量表的建立。
(2)然后调用boot.c里的boot()函数,此函数调用uart.c的函数完成串口初始化,调用nand.c里的函数完成代码搬运。
(3)跳转到main.c的Main()函数。
4 完整源码
完整源码下载,V0.9。
以上是关于TQ2440开发板学习纪实--- 从NAND Flash读取数据,把代码搬运到SDRAM运行的主要内容,如果未能解决你的问题,请参考以下文章
TQ2440开发板学习纪实--- 设置时钟频率,让CPU运行的更快
TQ2440开发板学习纪实--- 启用IRQ中断,告别低效的轮询!
TQ2440开发板学习纪实--- 初始化片外RAM,让程序有更大内存空间
TQ2440开发板学习纪实--- 利用Undefined异常模拟BLX指令