我在看韦东山老师嵌入式JZ2440裸板编程中的nor flash操作章节的时候发现一个问题想了很久才想明白,特来分享。
(大前提:s3c设为nor启动的时候nor flash基地址对应cpu编址为0)
**具体问题描述:**因为在此案例中cpu地址线和nor flash的地址线的连接方式是错位连接的。图中 cpu的地址线A1 - A20 分别连在nor flash地址线的A0 - 19,这样引起的问题就是cpu发出某个地址后nor flash所接受的地址被这样接法的硬件强行右移了一位。
如图:
比如说cpu发送地址为0x555,那么nor flash所接受的地址就是0x2aa。
如图:
右移一位后的数据:
那么为了方便编程,(站在cpu的角度)能够使我函数发出了地址能够与nor flash的地址对应上,即要在nor flash某地址上进行操作那么就要对地址进行特殊处理。这个处理就是我先把函数传入的地址参数左移一位。例如解锁命令在nor flash地址0x555写入0xaaa。那么cpu最后发送的地址应该就是0xaaa,然后nor flash得到的地址就是0x555。
即为了方便编程函数掺入的地址参数也应该给0X555。那么怎样才能使nor flash读到的地址也为0x555呢?解决办法就是我地址参数addr(0x555)先左移一位变成0xaaa,而后被地址线解法的硬件又移了一位变成了0x555。这样的传入函数的地址就能与nor flash接收到的地址对应起来了,我想在哪个地址读就在哪个地址读。
#define NOR_FALSH_ADDR_BASE 0
nor_cmd(0x555, 0xaa); /* 解锁 */
/*调用下面函数*/
/* offset是基于NOR的角度看到 */
void nor_cmd(unsigned int addr, unsigned int cmd)
{
nor_write_word(addr, cmd);
}
void nor_write_word( unsigned int addr, unsigned int val)
{
volatile unsigned short *p = (volatile unsigned short *)(NOR_FALSH_ADDR_BASE + (addr<< 1));
*p = val; /*这里发出地址,写入数据*/
}
我们这里nor_cmd()函数传入的地址是0x555,最终cmd数据写入的地址是addr经过左移一位得到的0xaaa,然后nor flash接收到的就是0x555.
**问题来了,在后续的nor flash某扇区擦除、某地址读和写的时候都没有对地址进行左移处理,而且程序写完烧写后实验能得到正确的数据,这是为什么呢?
下图为读数据没有进行地址左移时,编译烧写后用串口读nor flash 0x40地址与对应的烧写在norflash bin文件的对比结果:
我一直倔强的觉得有问题,应该要左移处理。于是我对读某地址对应的的数据的函数中的地址参数也进行了左移处理,发现结果刚好相差了一个左移的一位的数据。**
代码:
void do_read_nor_flash(void)
{
unsigned int addr;
volatile unsigned char *p;
int i, j;
unsigned char c;
unsigned char str[16];
/* 获得地址 */
printf("Enter the address to read: ");
addr = get_uint();
p = (volatile unsigned char *)(addr<<1); /*地址左移处理*/
printf("Data : \n\r");
/* 长度固定为64 */
for (i = 0; i < 4; i++)
{
/* 每行打印16个数据 */
for (j = 0; j < 16; j++)
{
/* 先打印数值 */
c = *p++;
str[j] = c;
printf("%02x ", c);
}
printf(" ; ");
for (j = 0; j < 16; j++)
{
/* 后打印字符 */
if (str[j] < 0x20 || str[j] > 0x7e) /* 不可视字符 */
putchar('.');
else
putchar(str[j]);
}
printf("\n\r");
}
烧写进开发板用串口工具调试对0x40进行读写,然后把结果与烧写在nor flash的bin文件作对比:
串口读nor flash地址为0x40的数据得到的却是0x80的数据,刚好是0x40左移一位后地址上的数据,这到底是怎么回事呢?
问题:
为什么执行nor flash解锁和读芯片ID等对应命令时函数传入的地址需要左移一位,而往nor flash对应扇区地址擦书、读、写数据是时函数对应的地址却不需要左移一位呢?如果左移却发现读到的数据不正确?
问题解答:
下面引入另一段相关知识:
我们知道nor flash可以像内存一样读,而且在s3c2440里面是统一编址的。那么我需要访问那个地址上的数据都是站在cpu的角度看的。(nor flash启动时nor flash的基地址对应 s3c2440 的0地址)
我们这里用到的nor flash是16位宽的,最小单元是2byte,所以它认为它自己是1M * 16bit的。所以它每次发送给cpu的数据是16位的。
而s3c2440 cpu访问的最小单元是byte,它每次读写数据都是8bit,在它眼里我们的NOR FLASH是2M * 8bit。
因为这里的不同而又为了能够正常准确的读写nor flash上的数据从而对他们的地址线的接法做出了要求。
在s3c2440芯片手册上可以找到如下图:
上图的示意和我们上面的硬件原理图的地址线错位接法是一致的,所以这种错位接法并不是硬件设计人员自己随意这么接的,是为了能工准确的操作不同位宽的nor flash硬件由nor falsh型号和cpu决定的。
其他的几种位宽或多个flash的地址线接法:
那么要怎样才能读写数据呢?
如图:
s3c2440 cpu访问的最小单元是byte,而16bit位宽的nor flash每个地址对应位16bit的数据,这16bit的数据相当于两个8bit的数据(高8bit数据和低8bit的数两个部分),cpu需要读出某个地址的数据时(8bit),cpu发出一份地址,nor flash得到的地址是相对于cpu发出的地址右移一位以后的地址,然后norflash会把该norf lash接收到的地址上对应的16bit数据发送给cpu。但是cpu只需要8bit的怎么办呢?这时候soc的memory controller会自己根据cpu的发送的地址来挑选出来其中的8bit给cpu。
例如(如上图):
cpu需要读0x03上的数据,这个地址由cpu发送出去,nor flash接收到的地址就是0x01,在这里cpu的本意是要读取nor flash上的第三个8bit的数据,而这个8bit数据正好为位于nor flash的0x01处。同时这种nor flash与cpu地址线的连接方法使nor flash接收的地址也正好为0x01。所以这时候nor flash将0x01上的16bit数据发送给soc的memory controller,然后memory controller根据cpu所给的地址0x03挑选出这16bit的数据中的高8bit发送给cpu,到此cpu成功得到它想要的8bit数据。
再例如:
cpu需要读0x02上的数据,这个地址由cpu发送出去,nor flash接收到的地址也是0x01,在这里cpu的本意是要读取nor flash上的第2个8bit的数据,而这个8bit数据正好为位于nor flash的0x01处。同时这种nor flash与cpu地址线的连接方法使nor flash接收的地址也正好为0x01。所以这时候nor flash将0x01上的16bit数据发送给soc的memory controller,然后memory controller根据cpu所给的地址0x02挑选出这16bit的数据中的低8bit发送给cpu,到此cpu成功得到它想要的8bit数据。
所以cpu每次读取8bit数据,发送给norflash一个地址,关键在于cpu想要得到nor flash上的第几个8bit数据,而不关心nor flash的具体地址。
而且这种地址线的接法cpu地址对应的容量大小与norf lash对应地址处的容量才能对应上(如,cpu的地址0x10 00008bit 等于8388608bit,即1048576 byte,即1024kbyte,即1mb,与之对应的nor flash地址为0x80000,0x8000016bit也等于8,388,608bit,即1048576 byte,即1024kbyte,即1mb)。
总结:由于cpu的位宽和falsh的位宽不同以及flash的个数等原因,而又为了使cpu在每个地址上能够读写对应位宽的数据,所以有了这种地址线错位的接法。也正式这种接法能够使cpu在对应地址能够读写到对应位宽的准确数据,
所以该cpu只关心在第几个8bit上读写到数据,并不关心nor flash到底在哪个地址上读写的数据,又程序函数的地址参数又是站在cpu的角度上的,所以这里擦除、读、写 nor flash数据时并不需要进行地址左移。
那么在写命令和读nor flash ID等参数时必须要进行函数传入的地址参数左移一位呢?
那是因为目标地址是在nor flash的芯片手册上看到的,只有nor flash接收到了相应的准确地址和数据才能执行相应动作,并且比如把ID等读回来,就必须要去nor flash的准确地址读才能读到正确的数据(这里的地址是nor flash本身规定的,所以为了方便使函数传入的地址和nor falsh接收到的的地址一致,要把函数传入的地址参数先进行左移一位,然后由被错位地址线接法右移一位)。
本文图片截自于s3c2440芯片手册或MX29LV800BT/BB芯片手册以及个人手绘,第一次写博客有不足之处还望多多包涵和指教,谢谢。
另外文中使用的register tool工具为本人业余写的小工具,如果有兴趣欢迎去我的主页下载。
下载地址:Register tool v1.04