1. 程式人生 > >2017年6月問題記錄與總結——ZYNQ_7000(PL35X)模組(NOR/SRAM/NAND FLASH 控制器)讀取NAND FLASH ID

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可以將資訊全部讀出來。