2017年6月问题记录与总结——ZYNQ_7000(PL35X)模块(NOR/SRAM/NAND FLASH 控制器)读取NAND FLASH ID

1.NAND FLASH

1.flash型号

MT29F4G08

2.FLASH基本信息

位宽:8位

容量大小:4Gb = 512MB

BLOCK大小:128K +4k

page大小:2K + 64bytes

block数量:4096(2*2048)

每个block的pages数量:128K/2K = 64

3.FLASH的物理组织

这里plane的概念,应该是两个面,每一面有2048个block

其于信息在图中标注清楚

4.nand flash的寻址机制

一般我们读写数据的时候,会给定一个读写数据的地址,这个地址一般是相对nand flash的基地址的偏移量,比如nand flash的基地址为0xe1000000.

我现在要读取第1000个块的第32个page的第1024个字节处的地址,那么这个地址是:

128k*1000+32*2k+1024 = 0x7d10400

如何将这个地址传给flash呢,flash的地址和周期表如下:

其中:

CA代表page address也就是页内的偏移地址:地址线一共12位,其中A11比较特殊:

每一页有(2K+64)=2112Byte,2112byte 需要12bit来表示,对于2112byte系列的NAND,这2112byte被分成1st half Page Register和2nd half Page Register,各自的访问由地址指针命令来选择,A[11:0]就是所谓的column address(列地址),在进行擦除操作时不需要它,因为以块为单位擦除。64个page需要6bit来表示,占用A[17:12],即该page在块内的相对地址,也就是确定位于哪一页。A11这一位地址被用来设置2048byte的1st half page还是2nd half page,0表示1st,1表示2nd。Block的地址是由A18以上的bit来表示,也就是确定位于哪一块。

PA代表一个block内的页地址:地址线一共6位,就是64个pages

BA代表device内的块地址:地址线一共12位,一共4096个块

第一周期和第二周期传递页内地址

第三周期到第五周期传递的是页地址(块地址和页地址)

0x7d10400

0b 0000 0111 1101 0001 0000 0100 0000 0000(A11不用管)

1th cycle:0x00 (A0~A7)

2th cycle:0x04

3th cycle:0x20

4th cycle:0xFA

5th cycle:0x00

这样,我们要访问nand flash的某个地址的数据的时候,如何将地址传给nand,就清楚了

5.ZYNQ 的SMC模块

SMC名称static memory controller,可以用来做nand,sram和nor的控制器,这里只介绍针对nand的控制部分

该部分的框图如下:

SMC负责处理对nand flash所有的命令、地址和数据操作,在zynq的0xe000e000地址可以访问到smc的控制和状态寄存器:

这里主要是配置功能选择nand 还是nor,flash的位宽,ecc功能,flash对应的timing时序,在配置完成该部分之后,如何将对flash的命令,地址和数据送到nand,这部分手册并没有介绍。

手册:The SMC is based on ARM's PL353 static memory controller.

这部分是通过一个芯片PL353实现的,要搞清楚这部分的具体协议,需要下载手册,手册在xilinx的官网上可以找到(PL350serials)。

The SMC is an Advanced Microcontroller Bus Architecture (AMBA) compliant
System-on-Chip (SoC) peripheral

该器件兼容AXI总线,既可以直接访问,也可以通过axi-apb桥总线直接去访问,如果支持8位的nand flash,axi访问的位宽32位

下面有一张总体框图:

图中可以看到,0xe000e000地址段的寄存器组,是基于apb总线的,并不能访问到命令和读写FIFIO,必须经过“FORMAT”模块,才能到达。

在linux和uboot的源码中,有几段移位操作,就是为了format送到FIFO中去。看懂了这个操作,通过SMC操作flash就基本没问题了。

AXI的接口信号如下:

这里我们不需要去详细了解他的信号概念,只需要大致了解对应的channel就行

这里是apb的总线的外部信号,可以看到0xe000e000地址当中出现的user_status和user_config寄存器,这两个寄存器如何配置暂时不用管


6.PL35X的NAND MEMORY ACCESS

这部分是重点

SMC定义了两种phase来传输数据,command phase和data phase,这两个phase包含了command的值,sddress cycles的数量还有smc的片选

下面这个表分别给出了两个phase在axi地址上的对应位解释:

6.1command phsae传输

command phase的传输在axi总线上总是一个写事件

address cycles的范围为0~7,实际目前的flash一般都是5个cycle

start command就是手册对应的发起请求操作的命令,例如读页,擦除块,读取ID等等

end command是第二种命令,该命令在所有cycles执行结束之后执行,也可以理解为第二个命令

end command valid表示end command是否要被执行

6.2data phase传输

data phase的传输在axi总线上是读写都存在的时间,读写flash

end command在数据传输之后,该命令会被处理

end command valid表示end command是否要被执行

clearcs 当设置的时候,nand flash的片选会被接触,当不设置的时候,片选保持

ecc last改为如果没有设置ecc使能则无效,否则作为最后一个指令传输到nand

7read id

有了以上信息,我们尝试的读取nand flash的id

smc control and status register base addr:0xe000e000

smc nand flash memory base addr:0xe1000000

在读取ID之前,需要设置zynq这一端对smc模块的控制寄存器,具体代码如下:

int zynq_nand_flash_ctl_init(void)
{
	u32 status;
	/* disable interrupts */
	zynq_nand_write(&zynq_nand_smc_base->cfr,ZYNQ_NAND_CLR_CONFIG);
	
	/* Initialize the NAND interface by setting cycles and operation mode */
	zynq_nand_write(&zynq_nand_smc_base->scr,ZYNQ_NAND_SET_CYCLES);
	
	/*flash 为8位位宽*/
	zynq_nand_write(&zynq_nand_smc_base->sor,ZYNQ_NAND_SET_OPMODE_8BIT); 
	zynq_nand_write(&zynq_nand_smc_base->dcr,ZYNQ_NAND_DIRECT_CMD);
	/* Wait till the ECC operation is complete */
	status = zynq_nand_waitfor_ecc_completion();
	if (status < 0) {
		printf("%s: Timeout\n",__FUNCTION__);
		return status;
	}
	/* Set the command1 and command2 register */
	zynq_nand_write(&zynq_nand_smc_base->emcmd1r, ZYNQ_NAND_ECC_CMD1);
	zynq_nand_write(&zynq_nand_smc_base->emcmd2r, ZYNQ_NAND_ECC_CMD2);
	return 0;
}

然后读取ID的指令:0x90,之前需要reset一下0xff,通过command写操作,将指令传递进去,就可以读取到id了:

	cmd_phase_addr = (unsigned int)xnand.nand_base        |
			 (curr_cmd->addr_cycles << ADDR_CYCLES_SHIFT)    |
			 (end_cmd_valid << END_CMD_VALID_SHIFT)          | 
			 (COMMAND_PHASE)                                 |
			 (end_cmd << END_CMD_SHIFT)                      |
			 (curr_cmd->start_cmd << START_CMD_SHIFT);
	printf("cmd_phase_addr:%0x\n",cmd_phase_addr);
	
	cmd_addr = (unsigned int)cmd_phase_addr;
	/* Get the data phase address */
	end_cmd_valid = 0;
	data_phase_addr = (unsigned int)xnand.nand_base       |
			  (0x0 << CLEAR_CS_SHIFT)                         |
			  (end_cmd_valid << END_CMD_VALID_SHIFT)          |
			  (DATA_PHASE)                                    |
			  (end_cmd << END_CMD_SHIFT)                      |
			  (0x0 << ECC_LAST_SHIFT);
	
	zynq_nand_mem_base = data_phase_addr;

这里读取ID不再是从基地址0xe1000000读取,根据pl35x的协议,这里zynq_nand_mem_base被赋值为了data_phase_addr,取数据需要从这个地址去取。


第一个cycle为manufacture id,第二个cycle为dev id,依次,一共5个cycle可以将信息全部读出来。

猜你喜欢

转载自blog.csdn.net/deep_l_zh/article/details/79297876