五、nand flash 操作
原文地址:http://blog.csdn.net/woshidahuaidan2011/article/details/51220911
by jaosn Email: [email protected]
于nor flash相比,nand flash的容量要大许多,nand 不同于nor ,其采用i/o接口只能采取顺序访问,s3c2440不仅具有nandflash的接口,而且还可以利用某些机制实现直接从nandflash启动并运行程序。相对于norflash,它具有一些优势,但它的一个劣势是很容易产生坏块,因此在使用nandflash时,往往要利用校验算法发现坏块并标注出来,以便以后不再使用该坏块。nandflash没有地址或数据总线,如果是8位nandflash,那么它只有8个IO口,这8个IO口用于传输命令、地址和数据。nandflash主要以page(页)为单位进行读写,以block(块)为单位进行擦除。每一页中又分为main区和spare区,main区用于正常数据的存储,spare区用于存储一些附加信息,如块好坏的标记、块的逻辑地址、页内数据的ECC校验和等。
NANDFLASH是以页为单位写,以块为单位来擦除,那么它们的区别就在这个上面:
1Gb 为大页 page=2048Kb BLOCK=128K
512Mb 为小页 page=512byte BLOCK=16K
一般情况下,对于小页的nand falsh读写只能按照以也为单位进行读写,但是对于K9F2G08R0A是支持按字节读取的。
刚才上面有提到ECC校验,ECC是“Error Correcting Code”的简写,中文名称是“错误检查和纠正”,ECC是每256个字节生产一个24位的值。这样可以看出来,大页需要每一页需要8个ECC 小页每页只需要2个ECC。
NANDFLASH是这样利用ECC进行检测的,NANDFLASH在写的时候会生成一个原始的ECC值保存在页的SPARE区,当要读页时也会生产一个ECC值,会跟SPARE的值进行异或比较,看结果是否为0,在工程开发中假如在大页换小页的时候,一定要修改这个算法,相反也是。
一般情况下,由于nand的价格优势,用于会把启动代码放到nand 里面,而把一些主要运行的代码放到nor里面去执行。其实,三星公司是nandflash的最大供应商,所以2440对其的支持也就不足为怪了,但是nand flash只能按照页来读取,并且nand flash很容易出错,既然要对nand支持就要解决这两个问题,三星公司是这么做的,在2440内部装备了一块称为Steppingstone大小为4k字节的SRAM,在2440启动之初,系统会自动把nand的前4k字节的代码拷贝到Steppingstone,然后去Steppingstone执行这4K字节的代码,通过这4K字节的代码用户可以把nand
4k后面的代码拷贝到sdram,然后条状到sdram中去执行程序,这一过程的前半部分可以看下图来理解;为了解决nand存取数据送一出错的问题,nand支持硬件的ECC,通过ECC来保证数据的准确性。
本人使用开发板使用的是三星公司的K9F2G08U0C,大小为256m(大页),其引脚图如下图:
其引脚的功能分别是:
名称 |
类型 |
描述 |
ALE |
I |
地址锁存使能。ALE为高时,在下降沿,地址信息通过I/O[7:0]锁存片内的地址寄存器。如果传输的不是地址信息,ALE应该为低。 |
I |
片选。一旦器件进入PROGRAM或ERASE操作,可以变无效。 |
|
CLE |
I |
命令锁存使能。CLE为高时,在上升沿,命令通过I/O[7:0]锁存到命令寄存器,当不传输命令时,CLE应该为低。 |
PRE |
I |
上电读使能。 |
I |
读使能。 |
|
I |
写使能。 |
|
I |
写保护。当为低时候,所有的PROGRAM和ERASE都被禁止。 |
|
I/O[7:0] |
I/O |
数据输入/输出。传输命令、数据、地址。仅在读操作时,数据是输出。 |
R/, |
O |
准备好/忙。集电极开路输出。外部需要接上拉电阻,这个脚表示芯片正在进行PROGRAM或ERASE操作。在读操作期间,表示数据正从阵列中传输到串行数据寄存器中,一旦这些操作完成,R/回到High-Z状态。 |
Vcc |
电源 |
电源 |
Vss |
地 |
地 |
上面的这些引脚可以结合时序图来理解,nand 通过8个IO来实现数据和地址的读取或者发送,这把个IO既可以发送数据也可以发送地址,但是是分时复用的,首先看一下,发送地址和命令的时序图:
可以看到,nand采用的时钟频率是HCLK来提供的,但是这个图是命令和地址传输画到一张图上了,也就是说CLE和ALE实际上是两个控制引脚。先看下地址是如何传输的:地锁存ALE信号为高电平的情况(此时CLE为低电平)且写使能信号WE为低的情况下,地址或者命令信号会通过8个IO传输到给nand flash;当要传输命令时,命令锁存CLE信号为高电平的情况下(此时ALE为低电平),在WE上升沿时刻,命令通过I/O[7:0]锁存到命令寄存器。
此外这里有必要分析一下图中的TACLS,TWRPH0,TWRPH1的含义:
显然,图中有关其具体的含义已经标记的很清楚了,只是用文字不太容易描述出来,
TACLS可以理解为从CLE/ALE开始高电平(准备发送地址或者命令)到WE开始低电平(此时可以发送地址或者命令)的时间
TWRPH0 就是WE低电平持续的时间(地址可写入的时间)。
TWRPH1 可以理解为从WE上升沿到CLE/ALE下降沿出现的时间(注意以上的时间单位均为HCLK周期)
那这些时间到底是多少呢?
这个问题可以去数据手册查看:
上图为nand芯片手册上的时序图,对比上面图可以看出来,
TACLS=Tcls-twp TWRPH0=twp TWRPH1=tclh
所以说接下来只需要知道Tcls twp tclh这三个值就好,在nand芯片手册里面有一下介绍:
可以看到Tcls在3.3v最小是12ns twp最小是12ns tclh最小为5ns
TACLS=Tcls-twp=0 TWRPH0=twp=12ns TWRPH1=tclh=5ns
nand使用的时钟源是HCLK,假设HCLK为100m 也就是HCLK的周期大概是10ns
由于TACLS的最小值为1个HCLK周期所以取最小值为1就可以
TWRPH0应该为最小值应该为2个HCLK周期
TWRPH1 最小可为1个HCLK周期
传输地址/命令后,接下来看一下nand是如何通过分时复用8个IO传输数据的:
nand对于读取或者写入数据设计的比较方便,在读数据的时候只需要在输入地址后将读数据信号RE设置为低电平就可以完成数据的读取;在写数据的时候只需要在输入地址后将写数据信号WE设置为低电平就可以写入数据。这里的TWRPH0,TWRPH1不在赘述。
nand有关时序的比较简单,接下来看一下其组织示意图:
通过上面的图可以看到nand每页有2K+64个Byte,这里所谓的2k字节就是前面所说的main区64字节就是所说的spare区,一般情况下,除非特别的说明这64这个字节不算内存的,因为这里面存放的是ECC校验码等信息,所以一般场合,读取地址为2048(2k)的内存数据,是指的第二页的第一个字节并不是读取spare区的地址。然后,nand 把每64页称作一块,一个nand共有2048个块。
刚才有提到,所有的地址和数据多事通过8个io传输到nand的,刚才有提到是利用分时复用的原理,那么就详细说明一下传输的过程,先看一张图:
从图中可以看到,对于地址的传输,nand是分5次传输的,其中前两次传输的是列地址,后三次传输的行地址,那么行地址列地址到底是什么意思?
上面有提到,nand每页有2k字节的数据,nand读出或者写入数据最小的单位是字节,那么这里所谓的列地址就是每一页的页内地址,页内的地址(0-2112) 1Page=(2k+64)Bytes,所以需要12位(2^12=2096>2k+64),但实际上不访问后面64个字节的,所以有效数据位是11位;所谓的行地址就是页间寻址,一块nand一共有2048*64=131072页,Row Address(A12-A28共 17位),2^17=131072。总的来说,行地址是页间寻址,就是告诉你要读那一页,列地址是页内寻址,告诉我需要页内的哪一个字节。
具体的可以参考芯片的数据手册,但是由于S3C2440本身已经对nand flash的支持,所以不再需要根据是芯片手册进行时序图进行配置,而是根据2440的数据手册配置s3cc2440的相应的寄存器就可以。
2440对nand的支持确实省去了好多时间,因此下面只需要介绍一下有关nand驱动器的使用,对NAND有关时序的设置就不在赘述,2440的nand控制与nand的连接方式为:
上图可以看到,接连方式比起其他设备要简单的多,引脚对应方式也比较简单,不在赘述。
通过上面的介绍,已经大致对nand有所了解,接下来就需要用程序对nand进行控制了,在进行对nand操作之前,需要首先配置nand flash,2440提供了NFCONF寄存器来配置nand的有关信息:
NFCONF提供的是对nand的一些配置信息,接下来详细说明一下:
BusWidth:IO总线宽度设置位,设置位1表示16位宽度,0表示8位宽度。但是在nand复位的时候或者在nand睡眠模式转变为唤醒模式的时候,该寄存器的值等于GPG15引脚的值,在复位过后,或者在该寄存器的值跟GPG15引脚没有任何关系。该位的值可以用软件来控制。
AddrCycle:只读寄存器,当AdvFlash为0时,该位为0表示地址设置要有是3次循环,为1表示要有4次循环;当AdvFlash为1时,该位为0表示地址设置要有是4次循环,为1表示要有5次循环。跟BusWidth原理一样复位该位受GPG14影响。
PageSize:只读寄存器,当AdvFlash为0时,该位为0表示nand 每页有256个字,为1表示nand 每页有512个字节;当AdvFlash为1时,该位为0表示nand 每页有1024个字,为1表示nand 每页有2048个字节。跟BusWidth原理一样复位该位受GPG13影响.。
AdvFlash:只读寄存器,该位为0表示支持的是每页有256或者512字节的nand;该位为1表示支持的是每页有1024或者2048字节的nand;跟BusWidth原理一样复位该位受NCON0影响。
TWRPH1:TWRPH1持续时间设置值(大小为0~7),时间= HCLK ×(TWRPH1 + 1)
在上面的分析可以知道TWRPH1
TWRPH0:TWRPH0持续时间设置值(大小0-7)时间= HCLK ×(TWRPH0 + 1)
TACLS:CLE&ALE持续时间设置值(大小0-3) 时间= HCLK ×TACLS
NFCONF寄存器最要是完成的一些有关nand的配置信息,完成了nand有关的配置就可利用他的控制寄存器,对nand打开或者挂起等作出控制了,对于nand有关控制的寄存器主要是NFCONT:
该寄存器对于nand来说是必不可缺少的设置选项
MODE:nand使能位,该位设置为1使能nand
Reg_nCE:控制的nFCE信号,相当于nand芯片的片选CE信号,为0选择nand
initECC:只写寄存器。设置为1初始化ECC解码器和编码器
MainECCLock:锁存main区的ECC的生成
main区的ECC状态寄存器为NFMECC0/1
SpareECCLock:锁存spare区的ECC的生成 spare区的ECC状态寄存器为NFSECC
RnB_TransMode:RnB信号(对应的是nand芯片的忙碌or空闲信号R/B)的转变(包括有高电平降为低电平或者由低电平升为高电平)检测信号,当为0时上升沿检测。为1下降沿
EnbRnBINT:RnB信号状态输入信号转换中断控制寄存器;设置为1时,当RnB信号状态发生变换会触发中断。
EnbIllegalAccINT:非法存取中断寄存器,设置为0禁止非法存取中断。当cpu想要写入或者擦除nand锁存区数据的时候会发生非法存取中断。对于区域锁存看下一条。
Soft Lock:软件锁存配置寄存器,该位可用程序在任何时候做出改变,该位设置为1时,寄存器NFSBLK(存放的是nand要读取或者写入或者控制区域的的开始地址)到NFEBLK(存放的是nand要读取或者写入或者控制区域的结束地址)-1的这块区域存于不锁存状态,整块nand除了上面指定的其他区域写入命令或者数据全是无效的,但是可以读取数据。当用户想如写入或者擦除锁存地区时,就会出现非法存取错误。此时NFSTAT[3]将会置1,这里要注意的是,假如NFSBLK和NFEBLK的地址数据相同,那么整块nand将处于锁存状态。
Lock-tight:这个寄存器比较霸道,看到tight(紧密地,彻底地)应该可以感觉到他的压迫感。该位一旦被设置1,后面将无法清除该位(复位或者芯片由睡眠模式切换到工作模式该位可以被自动清除)。该位的功能与Soft
Lock功能完全相同,只是SoftLock的值可以随时改变,Lock-tight却不可以。
nand的控制寄存器总算介绍完了,但是在将控制器的时候有提到main区ECC状态寄存器为NFMECC0/1、spare区的ECC状态寄存器为NFSECC、地址起始存放寄存器NFSBLK、地址结束存放寄存器NFEBLK,还有nand的状态寄存器NFSTAT,下面做出一一介绍。
首先对于8位nand flash,main区只有NFMECC0寄存器:
NFMECC0存放的8位nand flash的main区ECC,每读一次数据就对应一个ECC校验码,32位一共需要读4次(每次8位)。当对该寄存器进行读写的时候一定要确保maid去ecc处于非锁存状态也就是MainECCLock(NFCONT[5])为0。
①对于spare区的ECC状态寄存器为NFSECC,见下图:
对于8位nand,读取NFSECC【0到15】就可以,当对该寄存器进行读写的时候一定要确保main区ecc处于非锁存状态也就是SpareECCLock(NFCONT[6])为0。
②NFSECC和NFSBLK是相互搭配的寄存器,决定了nand规定块的开始和结束地址:
该寄存一次可以存放三块地址,其中2个8位的地址,1个16位的地址。其详细解释可以看对于NFCONF的介绍和结合下面的图来理解:
③对于and的状态寄存器NFSTAT,该寄存器主要是一切状态标记位,看一下图:
RnB:只读寄存器,当nand flash处于忙碌状态时,该位为0,处于可操作状态时该位为1
nCE:标记片选信号nCE引脚的状态
RnB_TransDetect:nand忙碌/空闲信号转换寄存器,当RnB由低电平变化为高电平(由忙碌变为空闲)时,该位会自动置1,如果此时使能中断的话(EnbRnBINT(NFCONF【9】)=1),还会触发中断信号,向该位写1清除数据。
IllegalAccess:一旦用户要编程或者擦除被锁存区域的数据,该位会自动置1
有关对nand的配置、控制寄存器及其关键的状态寄存器大概已经介绍完毕,通过上面的寄存器既可以完成nand的配置了。前面已经介绍过,nand只有8个io分时复用用来阐述命令,地址和数据,那么接下来存放这三个的有关寄存器:
①首先说明命令设置寄存器NFCMMD
该寄存器只使用了前8位用来存放nand的有关命令,写命令时,只需要将要传递给nand的命令写入该寄存器就可,nand有关的命令如下图所示:
下面有必要对上面的命令的含义及其使用方式暂且做一下简要的说明:
上表可以看到,命令的输入在两个循环周期内完成,每次8位。
有关读写的流程看下图:
流程图中已经表现的很清晰,不再赘述。
②接下来说明地址设置寄存器NFADDR
与命令设置寄存器类似,图中比较简单详细,不再赘述。
③最后介绍一下数据寄存器NFDATA
该寄存32位可用,假如想写入数据只需要把数据放到该寄存器就可以,假如想读数据,只需要读该寄存器就可以。但是对于8位的nand来说,其使用方式见下图:
也就是说,在8位的nand flash按照字节存取的时候,只用到NFDATA的低八位。这里特别要要注意,不然很容易出错。
至此,有关nand的介绍已经完毕,接下来进行总结nand使用概括,并附带代码注释:
总结:
在使用nand时首先到对nand进行初始化,初始化过程如下:
1、配置nand flash主要是配置一些位宽的选择和时序的设置 可通过NFCONF
2、控制nand flash 主要是设置是否使用nand 和关键ECC的设置
3、复位nand flash 主要是初始化有关nand寄存器,向nand发送复位命令(0xff)发送复位命令包括
①选中nand芯片,即使能nand flash
可通过NFCONT【1】寄存器来实现
②发送复位信号 向NFCMD寄存器写入0xff
③等待nand恢复空闲状态,相当于等待RnB(NFSTAT【2】)置1
④关闭nand芯片片选,即关闭nand flash 可通过NFCONT【1】寄存器来实现
下面列出代码:
void nf_init(void)
{
//HCLK=100Mhz
#defineNFTACLS 1 //
#defineNFTWRPH0 1 //
#defineNFTWRPH1 0 //
//1、配置nand flash主要是配置一些位宽的选择和时序的设置可通过NFCONF
rNFCONF= ( (NFTACLS<<12)|(NFTWRPH0<<8)|(NFTWRPH1<<4) );
rNFCONF&=(~(1<<0));
//2、控制nandflash主要是设置是否使用nand和关键ECC的设置
rNFCONT =(0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);
//使能nand 关闭片选 初始化ECC编码解码器 锁存main区的ECC的生成 锁存space区的ECC的生成 检测上升沿
//关闭RnB中断 关闭非法存取中断 不锁存nand 区域
//3、复位nand flash 主要是初始化有关nand寄存器,向nand发送复位命令(0xff)发送复位命令包括
nf_reset(); //复位
}
static void nf_reset(void)
{
//①选中nand芯片,即使能nandflash可通过NFCONT【1】寄存器来实现
nf_select(1);
//rNFSTAT|= (1<<2);
//②发送复位信号向NFCMD寄存器写入0xff
nf_cmd(0xff);
//③等待nand恢复空闲状态,相当于等待RnB(NFSTAT【0】)置1
nf_readly();
//④关闭nand芯片片选,即关闭nandflash可通过NFCONT【1】寄存器来实
nf_select(0);
}
假如还需要介绍的话,就需要介绍nand 的随机存取(按字节读取)了,对于随机写入具体的可以看下图:
在进行随机读取的时候,大概就成是这样的(结合上图来理解):
2、 把地址页地址按照五次循环写入到nand flash
3、 输入按页读取的第二个周期的命令0x30
4、 等待忙碌信号
5、 发送按字节读的第一个周期的读命令 0x05
6、 输入随机写入的页内地址
7、 发送按字节读的第二个周期的读命令 0xe0
8、 读取寄存器NFDATA8低八位得到读取值
unsigned char nf_read(unsigned int block, unsigned intpage, unsigned int add)
{
unsigned char buffer=0;
unsigned int page_number =(block<<6) + page;//由于每块有64(2^6=64)页得到页的绝对地址
nf_reset();//复位
nf_select(1);//使能片选
//rNFSTAT |= (1<<2);
nf_cmd(0);
//发送按页读的第一个周期的读命令
nf_addr(0x00);
//先发送页间地址
nf_addr(0x00);
nf_addr((page_number)& 0xff); //取出行地址(页间地址的低8位)
nf_addr((page_number>> 8) & 0xff); //取出行地址(页间地址的次低8位)
nf_addr((page_number>> 16) & 0xff); //取出行地址(页间地址的次高8位)
nf_cmd(0x30); //发送按页读的第二个周期的读命令
nf_readly();//等待完成要求动作
nf_cmd(0x05); //发送按字节读的第一个周期的读命令
nf_addr((char)(add&0xff)); //先发送页间地址
nf_addr((char)((add>>8)&0x0f));
nf_cmd(0xe0);//发送按字节读的第二个周期的读命令
nf_readly();//等待完成要求动作
buffer=rNFDATA8;//读取值
nf_select(0);//关闭片选
return buffer;
}
nand的按字节写操作的流程为:
1、 输入按页写入的第一个周期的命令0x80
2、 把地址页地址按照五次循环写入到nand flash
3、 写入按字节写入的周期的命令0x85
4、 写入页间地址
5、 写入好输入的数据
6、 写入按页写入的第二个周期的命令
/******************************
****nand flash指定地址写函数
****一块nand有2048块 每块有64页 每页有2k byte
****函数的参数是块地址 页地址 业内地址 要写入的数据
****************************/
void nf_write(unsigned int block,unsigned int page,unsigned int add, unsigned char dat)
{
unsigned int page_number =(block<<6)+page; //由于每块有64(2^6=64)页得到页的绝对地址
nf_select(1);//使能片选
//rNFSTAT |= (1<<2);
nf_cmd(0x80);//nand写入第一个周期的命令
nf_addr(0x00); //为什么业内地址这里写0
nf_addr(0x00); //0x80是按页写入的命令,也就是说假如要写入数据必须从该页的起始地址开始写入数据
nf_addr((page_number)& 0xff); //取出行地址(页间地址的低8位)
nf_addr((page_number>> 8) & 0xff); //取出行地址(页间地址的次低8位)
nf_addr((page_number>> 16) & 0xff); //取出行地址(页间地址的次高8位)
nf_cmd(0x85);//写入按字节写入的周期的命令
nf_addr((char)(add&0xff)); //输入页间地址
nf_addr((char)((add>>8)&0x0f)); //这里是取出次低位的低4位因为A12-A15必须为0
nf_data_byte(dat); //输入要写的数据
nf_cmd(0x10);//nand写入第二个周期的命令
nf_readly();//等待完成要求动作,
nf_select(0);//关闭片选
}
接下介绍按快擦除的时序:
这里的擦除是按照块擦除,因此写入的时候不需要写入列地址(页内地址),只需要行地址(页间地址)就可以
1、 写入擦除的第一个周期的命令0x60
2、 输入要擦除的行地址
3、 写入擦除的第二个周期的命令0XD0
4、 等待完成擦除工作。
/******************************
****nand flash擦除函数
****一块nand有2048块 nand 只能按照块擦除
****函数的参数是块地址
****************************/
void nf_erase(unsigned int block)
{
int del=0;
unsigned int pagenum=(block<<6);//由于每块有64(2^6=64)页 得到该块对应页地址
nf_reset();//复位
nf_select(1);//使能片选
// rNFSTAT |= (1<<2);
nf_cmd(0x60);//nand擦除的第一个周期的命令
nf_addr( pagenum & 0xff); //输入行地址,也就是输入页间地址
nf_addr((pagenum>>8) & 0xff);
nf_addr((pagenum>>16) & 0xff);
nf_cmd(0xd0);//nand擦除的第二个周期的命令
for (del = 0; del< 2000; del++) ;//延时一会等待擦除
nf_readly();//等待完成要求动作
nf_select(0);//关闭片选
}
到这里有关nand的操作就基本完成了,
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、分割线、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
下面是按照韦东山的实验,可不关注。
这里做一个实验:
试验目的:使nandflash开始4k代码考别到sram后(硬件自动完成),然后将4k以后代码copy到sdram内运行的功能。
代码参考附录。在附录中有一个.lds的文件,这里有必要介绍一下:
lds文件是决定一个可执行程序的各个段的存储位置,以及入口地址,这也是链接定位的作用。对lds文件的完整描述为:
SECTIONS{
...
secname start BLOCK(align)(NOLOAD):AT(ldadr)
{contents}>region:phdr = fill
...
}
secname和contents是必须的,前者用来命名这个段,后者用来确定代码中的什么部分放在这个段,以下是这个描述中的一些关键字的解释。
1、secname:段名
2、contents:决定哪些内容放在本段,可以使整个目标文件,也可以是目标文件中的某段(代码段,数据段等)
3、start:是段的重定位地址,本段连接(运行)的地址, 如果代码中有位置无关指令,程序运行时这个段必须放在这个地址上,start可以用任意一种描述地址的符号来描述。
1、 AT(ldar):定义本段存储(加载)的地址,如果不使用这个选项,则加载地址等于运行地址通过这个选项可以控制各个分段分别保存于输出文件中的不同位置
对于上面的.dls这里的内容:
SECTIONS {
firtst 0x00000000 : { head.o init.onand.o}
second 0x30000000: AT(4096) { main.o }
}
head.o放在0x00000000地址开始处,init.o放在head.o后面他的运行地址也是0x00000000,即连接地址和存储地址相同 (没有AT指定);main.o放在4096(0x1000是AT指定的存储地址)开始处,但他的运行地址在0x30000000,运行之前需要从0x1000(加载地址)复制到0x30000000(运行地址处),此过程也就需要读取flash,把程序拷贝到相应位置才能运行。这就是存储地址和运行的不同,称为加载时域和运行时域可以再.lds连接脚本文件中分别制定。
按照上面所说的contents决定哪些内容放在本段,
决定哪些内容放在本段,一个程序本质上都是由 bss段、data段、text段三个组成的,在内存中,段可以分为:
bss段:
BSS段(bsssegment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文BlockStarted by Symbol的简称。BSS段属于静态内存分配。
data段:
数据段(datasegment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
text段:
代码段(codesegment/textsegment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
rodata段:
存放C中的字符串和#define定义的常量
heap堆:
堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
stack栈:
是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
常量段:
常量段一般包含编译器产生的数据(与只读段包含用户定义的只读数据不同)。比如说由一个语句a=2+3编译器把2+3编译期就算出5,存成常量5在常量段中
接下来再来分析这么一段连接文件:
SECTIONS {
. = 0x30000000; //从0x0位置开始 前面的分号表示连接符,说明跟下个命令是一块的
.text : { *(.text) } //指定代码段
.rodata ALIGN(4) : {*(.rodata)} //指定只读段 ALIGN(4)是4字节对齐
.data ALIGN(4) : { *(.data) } //指定读写段
.bss ALIGN(4) : { *(.bss) *(COMMON) }
}
参考文献:
http://blog.csdn.net/zhaocj/article/details/5795254
http://blog.chinaunix.net/uid-20309750-id-214959.html
http://blog.csdn.net/wonxxx/article/details/39891319
http://blog.chinaunix.net/uid-29076366-id-4080602.html
http://blog.csdn.net/ouyang_linux007/article/details/7448814
http://blog.csdn.net/yeyuangen/article/details/6766567
http://blog.csdn.net/wenjie345304221/article/details/6859272