辨析NOR FLASH地址左右移的问题

norflash芯片手册command如下:

在这里插入图片描述

下面是2240对norflash的 一些操作:

擦除:
在这里插入图片描述
烧录:
在这里插入图片描述

读取:
在这里插入图片描述

问1:同样是操作norflash,为什么cpu发出norflash芯片手册中解锁等命令时,传参时addr不需要右移,cpu烧录起始地址或发送要擦除扇区地时就得右移呢

注意:nor_cmd函数里已经左移一位了
在这里插入图片描述

答1:

我们通过NOR FLASH的芯片手册得知,要实现解锁功能:要往地址0X555写入0XAA等等几个操作,因为我们是通过NOR的手册查看到的,那么这里肯定是从NOR角度看到的地址,也就是说我们要往第0X555个16位的地址里写入0XAA,因为地址线是错开的缘故,我们CPU必须把地址左移一位后再发生给NOR,这样才能真正地把0XAA写到NOR的第0X555地址里去。

而发送扇区地址的时候,因为我们人操纵的是CPU,所以我们的角度和CPU是一样的,我们也认为NOR是2M * 8bit,而不是1M * 16bit,比如我们要清除地址为0X001F0000时,0X001F0000代表的是第0X001F0000个8bit的地址,因为地址线是错开的原因,我们直接发送CPU角度的地址出去就好,但又因为nor_cmd函数里已经左移一位了,所以我们要提前右移一位以抵消左移的操作。(为什么把NOR看成是2M * 8bit的时候就直接把地址发送出去而不用左移一位呢,这就和cpu内存控制器有关了)

总结就是角度不同,发送扇区地址的时候,我们是站在cpu内存控制器位筛选后的角度,即此时我们将norflash看做2M * 8bit,而NOR FLASH的芯片手册中的解锁功能是站在norflash自己的角度来要求CPU地址总线发出地址的,它自认为自己是1M * 16bit

问2:(addr >> 1) << 1 可能会丢位

提前右移一位以抵消左移的操作,即(addr >> 1) << 1 ,举个例子:
假如我们想把0x11作为起始烧录地址
addr = 0x11 , 则 ((addr >> 1) << 1) = 0x10
细心点会发现,哎?右移一位导致丢位了啊!!
再看
addr = 0x10 , 则 ((addr >> 1) << 1) = 0x10
也就是说,输入addr=2或3(CPU角度的 norflash 为 2M * 8bit),起始烧录地址都对应 1M * 16bit 的 norflash的1(本来是 1 0 ,但 norflash 的 A0 接了CPU的 A1)
为什么要限制?

答2:(注意:为方便说明,我们将起始内存单元称之为第零个内存单元)

之所以这样,是因为我们操作的norflash 是16bit 宽度的,连接如下,可以发现是错位连接的

在这里插入图片描述
这是一种好的烧录习惯,作此限制可以保证我们的起始烧录地址或起始擦除地址是16bit 对齐的
在这里插入图片描述
在这里插入图片描述

问3:为什么把NOR看成是2M * 8bit进行读取访问的时候就直接把地址发送出去而不用左移一位呢

答3:

针对问题3,**内存控制器**绝对是劳苦功高: 示意图如下:
在这里插入图片描述

addr = 0x11 由于norflash的A1接CPU的A0,所以norflash看到的实际上是它1M *16bit的第1个16bit单元,在数据经内存控制器返回CPU时,内存控制器用A0=1,去挑出nor第一个16bit的低8位(注意:这里的示意图为低位在前,仅为了方便观察;实际也有可能是高位在前,如果是高位在前,则内存控制器用A0=1,去挑出nor第一个16bit的高8位)
我们将16bit位宽的norflash看做2M * 8bit, addr = 0x11 ,我们也的确访问到了norflash第3个8bit单元
同理,假如addr = 0x10,我们也的确访问到了2M * 8bit的norflash的第2个8bit单元
所以,这种错位连接实际上保留了内存控制器选择内存单元的权利,即:内存控制器利用被错开的A0去选择内存单元的高低位。

本文参考了韦东山老师的讲解已及关于NOR FLASH地址左右移的问题,特此感谢
如有问题,欢迎评论区指出批评。

以下是norflash.c 的全部代码,出自韦东山老师的课程,有了 前面的基础,再看一遍就豁然开朗了

#include "my_printf.h"
#include "string_utils.h"

#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);
}

unsigned int nor_read_word(unsigned int base, unsigned int offset)
{
	volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
	return *p;
}

unsigned int nor_dat(unsigned int offset)
{
	return nor_read_word(NOR_FLASH_BASE, offset);
}

void wait_ready(unsigned int addr)
{
	unsigned int val;
	unsigned int pre;

	pre = nor_dat(addr >> 1);
	val = nor_dat(addr >> 1);
	while ((val & (1 << 6)) != (pre & (1 << 6)))
	{
		pre = val;
		val = nor_dat(addr >> 1);
	}
}

/* 进入NOR FLASH的CFI模式
 * 读取各类信息
 */
void do_scan_nor_flash(void)
{
	char str[4];
	unsigned int size;
	int regions, i;
	int region_info_base;
	int block_addr, blocks, block_size, j;
	int cnt;

	int vendor, device;

	/* 打印厂家ID、设备ID */
	nor_cmd(0x555, 0xaa); /* 解锁 */
	nor_cmd(0x2aa, 0x55);
	nor_cmd(0x555, 0x90); /* read id */
	vendor = nor_dat(0);
	device = nor_dat(1);
	nor_cmd(0, 0xf0); /* reset */

	nor_cmd(0x55, 0x98); /* 进入cfi模式 */

	str[0] = nor_dat(0x10);
	str[1] = nor_dat(0x11);
	str[2] = nor_dat(0x12);
	str[3] = '\0';
	printf("str = %s\n\r", str);

	/* 打印容量 */
	size = 1 << (nor_dat(0x27));
	printf("vendor id = 0x%x, device id = 0x%x, nor size = 0x%x, %dM\n\r", vendor, device, size, size / (1024 * 1024));

	/* 打印各个扇区的起始地址 */
	/* 名词解释:
	 *    erase block region : 里面含有1个或多个block, 它们的大小一样
	 * 一个nor flash含有1个或多个region
	 * 一个region含有1个或多个block(扇区)

	 * Erase block region information:
	 *    前2字节+1    : 表示该region有多少个block 
	 *    后2字节*256  : 表示block的大小
	 */

	regions = nor_dat(0x2c);
	region_info_base = 0x2d;
	block_addr = 0;
	printf("Block/Sector start Address:\n\r");
	cnt = 0;
	for (i = 0; i < regions; i++)
	{
		blocks = 1 + nor_dat(region_info_base) + (nor_dat(region_info_base + 1) << 8);
		block_size = 256 * (nor_dat(region_info_base + 2) + (nor_dat(region_info_base + 3) << 8));
		region_info_base += 4;

		//		printf("\n\rregion %d, blocks = %d, block_size = 0x%x, block_addr = 0x%x\n\r", i, blocks, block_size, block_addr);

		for (j = 0; j < blocks; j++)
		{
			/* 打印每个block的起始地址 */
			//printf("0x%08x ", block_addr);
			printHex(block_addr);
			putchar(' ');
			cnt++;
			block_addr += block_size;
			if (cnt % 5 == 0)
				printf("\n\r");
		}
	}
	printf("\n\r");
	/* 退出CFI模式 */
	nor_cmd(0, 0xf0);
}

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);
}

void do_write_nor_flash(void)
{
	unsigned int addr;
	unsigned char str[100];
	int i, j;
	unsigned int val;

	/* 获得地址 */
	printf("Enter the address of sector to write: ");
	addr = get_uint();

	printf("Enter the string to write: ");
	gets(str);

	printf("writing ...\n\r");

	/* str[0],str[1]==>16bit 
	 * str[2],str[3]==>16bit 
	 */
	i = 0;
	j = 1;
	while (str[i] && str[j])
	{
		val = str[i] + (str[j] << 8);

		/* 烧写 */
		nor_cmd(0x555, 0xaa); /* 解锁 */
		nor_cmd(0x2aa, 0x55);
		nor_cmd(0x555, 0xa0); /* program */
		nor_cmd(addr >> 1, val);
		/* 等待烧写完成 : 读数据, Q6无变化时表示结束 */
		wait_ready(addr);

		i += 2;
		j += 2;
		addr += 2;
	}

	val = str[i];
	/* 烧写 */
	nor_cmd(0x555, 0xaa); /* 解锁 */
	nor_cmd(0x2aa, 0x55);
	nor_cmd(0x555, 0xa0); /* program */
	nor_cmd(addr >> 1, val);
	/* 等待烧写完成 : 读数据, Q6无变化时表示结束 */
	wait_ready(addr);
}
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;

	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");
	}
}

void nor_flash_test(void)
{
	char c;

	while (1)
	{
		/* 打印菜单, 供我们选择测试内容 */
		printf("[s] Scan nor flash\n\r");
		printf("[e] Erase nor flash\n\r");
		printf("[w] Write nor flash\n\r");
		printf("[r] Read nor flash\n\r");
		printf("[q] quit\n\r");
		printf("Enter selection: ");

		c = getchar();
		printf("%c\n\r", c);

		/* 测试内容:
		 * 1. 识别nor flash
		 * 2. 擦除nor flash某个扇区
		 * 3. 编写某个地址
		 * 4. 读某个地址
		 */
		switch (c)
		{
		case 'q':
		case 'Q':
			return;
			break;

		case 's':
		case 'S':
			do_scan_nor_flash();
			break;

		case 'e':
		case 'E':
			do_erase_nor_flash();
			break;

		case 'w':
		case 'W':
			do_write_nor_flash();
			break;

		case 'r':
		case 'R':
			do_read_nor_flash();
			break;
		default:
			break;
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值