一.前言
ONFI即 Open NAND Flash Interface,開放NAND閃存接口.是一個由100多家制造、設計或啟用NAND閃存的公司組成的行業工作組,主要是Intel和鎂光。致力于簡化NAND閃存集成到消費電子產品、計算平臺和任何其他需要固態大容量存儲的應用程序中。為NAND閃存定義標準化的組件級接口規范以及連接器和模塊外形規格。使用開放接口標準加強了來自不同供應商的NAND設備的兼容性和互操作性。這增加了標準器件的供應基礎,縮短了設計時間并加快了上市時間。
從軟件的角度,主要是讀取符合ONFI標準的參數,比如容量等信息,用以做驅動代碼的兼容性設計。這部分信息一般放置在指定的參數PAGE中,描述芯片組織、功能、時序和其他行為參數的數據結構。
以下基于GD5F1GQ5xExxG詳細介紹ONFI的參數頁。
二.讀取參數頁
參考《GD5F1GQ5xExxG DATASHEET》的8.11 Read Parameter Page (P27)
參數PAGE至少有三份參數副本存儲在設備中,讀PAGE到Cache(13h) 之后,從Cache讀(03H/0BH)命令可用于讀指定的數據,可以一次只讀一份,如果校驗錯誤再偏移讀下一份。
操作過程如下:
1.配置B0寄存器的OTP_EN位為1
2.然后回讀確認B0寄存器的OTP_EN位置1
3.使用(13h)指令加載地址24’h000004的PAGE到Cache中
4.讀C0寄存器,等待OIP為0表示操作完
5.使用(03H/0BH)命令從Cache中讀出指定數據。
如下是使用邏輯分析儀抓取的一次完整的操作
(1)先讀B0寄存器,設置OTP_EN位為1(bit6), 然后回讀確認OTP_EN置位
(2)使用(13h)指令加載地址24’h000004的PAGE到Cache中,第一次讀C0寄存器為0x01即OIP=1還未完成,然后后面繼續讀OIP變為了0表示完成。
(3)03H指令讀取整個PAGE的數據,可以看到256字節就重復了。
可以看到256字節重復了8遍,剛好2KB,最后還有128B的Spare區域
三.參數頁數據結構
Byte
|
O/M
|
Description
|
3.3V/1.8V
|
實際值(數據小端模式)
|
0-3
|
M
|
Parameter page signature
|
4FH
|
“ONFI”
|
4-5
|
M
|
Revision number
|
00H
|
|
6-7
|
M
|
Features supported
|
00H
|
|
8-9
|
M
|
Reserved (0)
|
00H
|
|
10-31
|
Reserved (0)
|
00H
|
||
Manufacturer Information block
|
||||
32-43
|
M
|
Device manufacturer (12 ASCII characters)“GIGADEVICE ”
|
47H
|
“GIGADEVICE ”
|
44-63
|
M
|
Device model (20 ASCII characters)
“GD5F1GQ5U”x4 2.7v ~ 3.6v
“GD5F1GQ5R”x4 1.7v ~ 2.0v
|
47H
20H
20H
20H
20H
20H
|
“GD5F1GQ5R”
|
64
|
M
|
JEDEC manufacturer ID“C8”
|
C8H
|
C8
|
65-66
|
O
|
Date code
|
00H
|
|
67-79
|
Reserved
|
00H
|
||
Memory organization block
|
||||
80-83
|
M
|
Number of data bytes per page
|
00H
|
PAGE數據大小
0x00000800=2KB
|
84-85
|
M
|
Number of spare bytes per page
|
80H
|
PAGE的SPARE區域大小0x0080=128字節
|
86-89
|
M
|
Number of data bytes per partial page
|
00H
|
分頁大小0x0200=512字節
即一個PAGE分為4個小頁
|
90-91
|
M
|
Number of spare bytes per partial page
|
20H
|
每個分頁SPARE區域大小0x0020=32字節
|
92-95
|
M
|
Number of pages per block
|
40H
|
每個塊的PAGE數0x00000040=64塊
|
96-99
|
M
|
Number of blocks per logical unit (LUN)
|
00H
|
每個邏輯單元的塊數0x00000400=1024個
|
100
|
M
|
Number of logical units (LUNs)
|
01H
|
邏輯單元數1
|
101
|
M
|
Reserved
|
00H
|
|
102
|
M
|
Number of bits per cell
|
01H
|
每個存儲單元的位數1,即SLC
|
103-104
|
M
|
Bad blocks maximum
|
14H
|
最大壞塊數20
|
105-106
|
M
|
Block endurance
|
01H
|
耐久性0x0501 (指的什么待查)
|
107
|
M
|
Guaranteed valid blocks at beginning of target
|
01H
|
保證存儲開始的該塊數在生命周期是有效的
|
108-109
|
M
|
Block endurance for guaranteed valid blocks
|
00H
|
保證有效區塊的耐久性
|
110
|
M
|
Number of programs per page
|
04H
|
每頁有4個可編程小頁
|
111
|
M
|
Partial programming attributes
|
00H
|
部分頁面編程無約束
小頁連續放一起,SPARE區域連續放一起。
|
112
|
M
|
Number of bits ECC correctability
|
00H
|
|
113
|
M
|
Number of interleaved address bits
|
00H
|
|
114
|
O
|
Interleaved operation attributes
|
00H
|
|
115-127
|
Reserved
|
00H
|
||
Electrical parameters block
|
||||
128
|
M
|
I/O capacitance
|
08H
|
|
129-130
|
M
|
IO clock support
|
00H
|
|
131-132
|
O
|
Reserved (0)
|
00H
|
|
133-134
|
M
|
tPROG Maximum page program time (us)
|
58H
|
|
135-136
|
M
|
tBERS Maximum block erase time (us)
|
10H
|
|
137-138
|
M
|
tR Maximum page read time (us)
|
3CH
|
|
139-140
|
M
|
Reserved
|
00H
|
|
141-163
|
Reserved
|
00H
|
||
Vendor block
|
||||
164-165
|
M
|
Vendor specific Revision number
|
00H
|
|
166-253
|
Vendor specific
|
00H
|
||
254-255
|
M
|
Integrity CRC
|
Set on test
|
0x3E80
|
Redundant parameter pages
|
||||
256-511
|
M
|
Value of bytes 0-255
|
||
512-767
|
M
|
Value of bytes 0-255
|
||
768+
|
O
|
Additional redundant parameter pages
|
校驗值如下
Device Model
|
ORGANIZATION
|
VCC RANGE
|
CRC value B254/B255
|
“GD5F1GQ5UxxG”
|
X4
|
2.7v ~ 3.6v
|
58H/F3H
|
“GD5F1GQ5RxxG”
|
X4
|
1.7v ~ 2.0v
|
80H/3EH
|
其中1.“O”代表可選,“M”代表強制
完整性CRC(循環冗余檢查)字段用于驗證參數頁面的內容是否已正確傳輸到主機。有關詳細信息,請參閱ONFI 1.0規范。CRC應使用以下16位生成器多項式計算:G(X)=X^16+X^15+X^2+1,此十六進制多項式可表示為8005h
CRC值應在計算開始前用4F4Eh的值進行初始化。在計算出最終CRC值之后,沒有對其應用XOR。不存在數據字節或CRC計算值的反轉。
四.關鍵代碼
讀參數頁的主要流程,相關底層接口這里不再貼出
int nand_read_param_page(uint8_t* buffer, uint16_t start, uint16_t len)
{
int res = 0;
/* 設置OTP_EN = 1 */
res = nand_set_otp(NAND_B_OPT_ACCESS,NAND_REG_RETRY);
if(res == 0)
{
/* 加載數據到Cache */
res = nand_read_page_to_cache(NAND_PARAM_PAGE_ADDR,NAND_PAGE_TO_CACHE_RETRY);
if(res == 0)
{
/* 從Cache讀數據 */
res = nand_read_from_cache(start, len, buffer);
if(res == 0)
{
/* 成功 */
}
else
{
res = -3;
}
}
else
{
return -2;
}
}
else
{
res = -1;
}
/* 切回正常模式 */
nand_set_otp(NAND_B_OPT_NORMAL,NAND_REG_RETRY);
return res;
}
五.uCFS的NAND FTL中使用ONFI
fs_dev_nand_part_onfi.c
fs_dev_nand_part_onfi.h
函數FS_NAND_PartONFI_ParamPageParse根據參數頁解釋參數到FS_NAND_PART_DATA結構體
/*
*********************************************************************************************************
* FS_NAND_PartONFI_ParamPageParse()
*
* Description : Parse the ONFI parameter page.
*
* Argument(s) : p_part_data Pointer to a NAND part data object.
* ----------- Argument validated by caller.
*
* p_err Pointer to variable that will receive return the error code from this function :
* ----- Argument validated by caller.
*
* FS_ERR_DEV_NAND_ONFI_INVALID_PARAM_PAGE Invalid parameter page.
* FS_ERR_DEV_NAND_ONFI_VER_NOT_SUPPORTED ONFI version not supported.
* FS_ERR_NONE Parameter page parsed successfully.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
static void FS_NAND_PartONFI_ParamPageParse (FS_NAND_PART_DATA *p_part_data,
FS_ERR *p_err)
{
CPU_BOOLEAN is_ver_supported;
CPU_BOOLEAN has_ext_pp;
CPU_BOOLEAN is_bus_16;
CPU_BOOLEAN is_invalid;
CPU_INT08U value;
CPU_INT08U multiplier;
CPU_INT08U lun_nbr;
CPU_INT16U version;
CPU_INT32U pg_size;
CPU_INT32U max_size;
CPU_INT32U nb_pg_per_blk;
CPU_INT32U blk_cnt;
CPU_INT64U max_blk_erase;
CPU_DATA ix;
*p_err = FS_ERR_NONE;
/* -------------- REV INFO AND FEATURES --------------- */
/* Validate ONFI version. */
MEM_VAL_COPY_GET_INT16U_LITTLE(&version, &FS_NAND_PartONFI_ParamPg[4]);
is_invalid = DEF_BIT_IS_SET(version, DEF_BIT_00);
if (is_invalid == DEF_YES) {
*p_err = FS_ERR_DEV_INVALID;
return;
}
ix = 0;
is_ver_supported = DEF_NO;
while ((is_ver_supported != DEF_YES) &&
(ix < FS_NAND_PART_ONFI_V_QTY)){
is_ver_supported = DEF_BIT_IS_SET(version, FS_NAND_PartONFI_SupportedVersions[ix]);
ix++;
}
if (is_ver_supported == DEF_YES) {
version = FS_NAND_PartONFI_SupportedVersions[ix - 1u];
} else {
FS_TRACE_DBG(("FS_NAND_PartONFI_ParamPageParse(): ONFI version not supported by this driver.rn"));
*p_err = FS_ERR_DEV_INVALID;
return;
}
/* Identify features. */
has_ext_pp = DEF_BIT_IS_SET(FS_NAND_PartONFI_ParamPg[6], FS_NAND_PART_ONFI_FEATURE_EX_PP);
is_bus_16 = DEF_BIT_IS_SET(FS_NAND_PartONFI_ParamPg[6], FS_NAND_PART_ONFI_FEATURE_BUS_16);
if (is_bus_16 == DEF_YES) {
p_part_data-??>BusWidth = 16u;
} else {
p_part_data->BusWidth = 8u;
}
/* Extended parameter page length. */
if (has_ext_pp == DEF_YES) {
FS_NAND_PartONFI_ExtParamPageLen = FS_NAND_PartONFI_ParamPg[12];
FS_NAND_PartONFI_ExtParamPageLen |= (CPU_INT16U)((CPU_INT16U)FS_NAND_PartONFI_ParamPg[13] ?< DEF_INT_08_NBR_BITS);
FS_NAND_PartONFI_ExtParamPageLen *= 16u;
}
/* Number of parameter pages. */
if (version >= FS_NAND_PART_ONFI_V21) {
FS_NAND_PartONFI_ParamPageCnt = FS_NAND_PartONFI_ParamPg[14];
} else {
FS_NAND_PartONFI_ParamPageCnt = 3u;
}
/* ----------- IDENTIFY MEMORY ORGANIZATION ----------- */
/* Page size. */
MEM_VAL_COPY_GET_INT32U_LITTLE(&pg_size, &FS_NAND_PartONFI_ParamPg[80]);
max_size = (FS_NAND_PG_SIZE) -1;
if (pg_size > max_size) {
FS_TRACE_DBG(("FS_NAND_PartONFI_ParamPageParse(): Page size does not fit in container type FS_NAND_PG_SIZE.rn"));
*p_err = FS_ERR_DEV_INCOMPATIBLE_LOW_PARAMS;
return;
}
p_part_data->PgSize = pg_size;
/* Spare size. */
MEM_VAL_COPY_GET_INT16U_LITTLE(&(p_part_data->SpareSize), &FS_NAND_PartONFI_ParamPg[84]);
/* Nb of pages per blk. */
MEM_VAL_COPY_GET_INT32U_LITTLE(&nb_pg_per_blk, &FS_NAND_PartONFI_ParamPg[92]);
max_size = (FS_NAND_PG_PER_BLK_QTY) -1;
if (nb_pg_per_blk > max_size) {
FS_TRACE_DBG(("FS_NAND_PartONFI_ParamPageParse(): Nb of pages per blk does not fit in container type FS_NAND_PG_PER_BLK_QTY.rn"));
*p_err = FS_ERR_DEV_INCOMPATIBLE_LOW_PARAMS;
return;
}
p_part_data->PgPerBlk = nb_pg_per_blk;
/* Nb of block per logical unit. */
MEM_VAL_COPY_GET_INT32U_LITTLE(&blk_cnt, &FS_NAND_PartONFI_ParamPg[96]);
max_size = (FS_NAND_BLK_QTY) - 1;
if (blk_cnt > max_size) {
FS_TRACE_DBG(("FS_NAND_PartONFI_ParamPageParse(): Blk cnt does not fit in container type FS_NAND_BLK_QTY.rn"));
*p_err = FS_ERR_DEV_INCOMPATIBLE_LOW_PARAMS;
return;
}
/* Nb of logical units. */
lun_nbr = FS_NAND_PartONFI_ParamPg[100];
p_part_data->BlkCnt = blk_cnt * lun_nbr;
/* Max nb of bad blocks per logical unit. */
MEM_VAL_COPY_GET_INT16U_LITTLE(&(p_part_data->MaxBadBlkCnt), &FS_NAND_PartONFI_ParamPg[103]);
p_part_data->MaxBadBlkCnt *= lun_nbr;
/* Max programming operations per block. */
value = FS_NAND_PartONFI_ParamPg[105];
multiplier = FS_NAND_PartONFI_ParamPg[106];
if (multiplier > 9u) {
FS_TRACE_DBG(("FS_NAND_PartONFI_ParamPageParse: Max blk erase count larger than supported.rn"));
*p_err = FS_ERR_DEV_INCOMPATIBLE_LOW_PARAMS;
return;
}
max_blk_erase = value * FS_NAND_PartONFI_PowerOf10[multiplier];
max_size = (CPU_INT32U) - 1;
if (max_blk_erase > max_size) {
FS_TRACE_DBG(("FS_NAND_PartONFI_ParamPageParse(): Max blk erase cnt does not fit in container type CPU_INT32U.rn"));
*p_err = FS_ERR_DEV_INCOMPATIBLE_LOW_PARAMS;
return;
}
p_part_data->MaxBlkErase = max_blk_erase;
/* Max nb of partial page programming. */
p_part_data->NbrPgmPerPg = FS_NAND_PartONFI_ParamPg[110];
/* ECC correction needed. */
p_part_data->ECC_NbrCorrBits = FS_NAND_PartONFI_ParamPg[112];
if (p_part_data->ECC_NbrCorrBits == 0xFFu) {
p_part_data->ECC_CodewordSize = 0u;
if (has_ext_pp == DEF_FALSE) {
FS_TRACE_DBG(("FS_NAND_PartONFI_ParamPageParse(): ECC located in extended param pg, but dev does not have one.rn"));
*p_err = FS_ERR_DEV_INVALID;
return;
} else {
*p_err = FS_ERR_DEV_NAND_ONFI_EXT_PARAM_PAGE;
}
} else {
p_part_data->ECC_CodewordSize = 528u;
}
/* Factory defect mark type. */
if (version >= FS_NAND_PART_ONFI_V21) {
p_part_data->DefectMarkType = DEFECT_SPARE_L_1_PG_1_OR_N_ALL_0;
} else {
/* #### NAND drv not compatible with 'any loc'. */
p_part_data->DefectMarkType = DEFECT_SPARE_L_1_PG_1_OR_N_ALL_0;
}
}
六.參考
https://www.onfi.org/specifications
最新版本是5.1
補充其中的CRC算法
static uint16_t const crc16_table[256] = {
0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
};
uint16_t onfi_crc(uint8_t* buffer, uint32_t len)
{
/* G(X) = X^16 + X^15 +X^2 + 1, 8005H
* 初始值 4F4Eh
* 無XOR,無輸出翻轉
*/
uint16_t crc = 0x4F4E;
while (len--)
crc = (crc < 8) ^ crc16_table[((crc >> 8) ^ *buffer++) & 0x00FF];
return crc;
}
審核編輯黃宇
-
閃存
+關注
關注
16文章
1789瀏覽量
114924 -
NAND
+關注
關注
16文章
1682瀏覽量
136155 -
接口
+關注
關注
33文章
8596瀏覽量
151147 -
ONFI
+關注
關注
1文章
5瀏覽量
7705
發布評論請先 登錄
相關推薦
評論