疑惑点:为什么在发送解锁命令时,我们不用右移一位,而发送扇区地址时却要右移一位(nor_cmd函数内部已经左移一位)
处理器与NOR flash存储器的错位连接:
对于处理器来说,一个地址对应的是一个字节(8位),也就是说处理器的地址线对应的最小数据单元是字节。
如果处理器最小数据单元是8位,存储器位宽是16位,那在我们写程序时会特意进行16位操作吗?显然不会,我们写代码时,可不管外设到底是多少位。这是如何实现的呢?
原因:有存储控制器(MemoryController)这个中间层。存储控制器根据存储器的位宽,每次总是读/写16位数据。
外设位宽是8时,处理器的A0~AXX与外设的A0~AXX直接相连
外设位宽是16时,处理器的A1~AXX与外设的A0~AYY直接相连,表示不管处理器的A0是0还是1,外设看到的都是同一个地址,对应16位的数据,存储控制器对数据进行选择或组合,再提供给处理器。
外设位宽是32时,处理器的A2~AXX与外设的A0~AZZ直接相连,表示不管处理器的A0A1是00,01,10还是11,外设看到的都是同一个地址,对应32位的数据,“MemoryController”对数据进行选择或组合,再提供给处理器
32位的FLASH,FLASH的A0要接ARM的A2,因为32位地址表示4个字节,每次要跳4个字节的话,那么就是从A2开始才变化,A1 A0不变化
16位的FLASH,FLASH的A0要接ARM的A1,因为16位地址表示2个字节,每次要跳2个字节的话,那么就是从A1开始才变化,A0不变化
8位的FLASH,FLASH的A0要接ARM的A0,因为8位地址表示1个字节,每次要跳1个字节的话,那么就是从A0开始才变化。
对于 16位的FLASH ,我们可以这样认为:16位存储器的设计者将低位A[0]省掉了,我们只要读取一次就可以得到两个字节,读取的这个地址对应于ARM发出的地址的A[21..1],即实际上是存储器需要的偶地址(偶地址是针对ARM发出的地址而言的)。
cpu角度和nor角度:
cpu角度: CPU访问的最小单元是byte ,在它眼里我们的NOR FLASH是2M * 8bit
nor角度: 我们的NOR FLASH是16位宽的,最小单元是2byte,所以它认为它自己是1M * 16bit的
flash的特性是:
1. 每个BIT写数据只能将1写为0,0不能写为1.擦除数据是将所有数据都写为1.因此如果想在已经数据的flash上写入新的数据,则必须先擦除。
2. 在擦除flash的时候,必须是整块的擦除。
#define NOR_FLASH_BASE 0 /* jz2440, nor-->cs0, base addr = 0 */
/* 比如: 55H 98
* 本意是: 往(0 + (0x55)<<1)写入0x98
*/
void nor_write_word(unsigned int base, unsigned int offset, unsigned int val)
{
volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
*p = val;
}
/* offset是基于NOR的角度看到 */
void nor_cmd(unsigned int offset, unsigned int cmd)
{
nor_write_word(NOR_FLASH_BASE, offset, cmd);
}
void do_erase_nor_flash(void)
{
unsigned int addr;
/* 获得地址 */
printf("Enter the address of sector to erase: ");
addr = get_uint();
printf("erasing ...\n\r");
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55);
nor_cmd(0x555, 0x80); /* erase sector */
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55);
nor_cmd(addr>>1, 0x30); /* 发出扇区地址 */
wait_ready(addr);
}
offset 左移(nor的视角):
解锁命令是以nor的视角看到的地址,因为错开的原因,nor_cmd里边会左移一位
我们通过NOR FLASH的芯片手册得知,要实现解锁功能:要往地址0X555写入0XAA等等几个操作,因为我们是通过NOR的手册查看到的,那么这里肯定是从NOR角度看到的地址,也就是说我们要往第0X555个16位的地址里写入0XAA,因为地址线是错开的缘故,我们CPU必须把地址左移一位后再发生给NOR,这样才能真正地把0XAA写到NOR的第0X555地址里去。
addr右移(两种理解,nor的角度):
1. 而发送扇区地址的时候,因为我们人操纵的是CPU,所以我们的角度和CPU是一样的,我们也认为NOR是2M * 8bit,而不是1M * 16bit,比如我们要清除地址为0X001F0000时,0X001F0000代表的是第0X001F0000个8bit的地址,因为地址线是错开的原因,我们直接发送CPU角度的地址出去就好,但又因为nor_cmd函数里已经左移一位了,所以我们要提前右移一位以抵消左移的操作。
2.从cpu角度看,nor的addr要<<1;从nor看,cpu的addr要>>1 (cpu发出的addr要右移一位w)
cpu: 发出的地址是0-0x1fffff, 最小单元是byte ,总共2M * 8bit
nor: 实际物理地址0-0x 7ffff, 最小单元是2byte,总共1M * 16bit
因此,我们向nor_cmd传递的offset便是cpu角度的地址,所以要右移1位
这也解释了nor的读写都是2byte进行的
参考:https://blog.csdn.net/qq_37659294/article/details/89765128
参考:https://blog.csdn.net/MyLovelyJay/article/details/75245380