在上一节中,不知道你有没有注意到,以NOR方式启动u-boot后,运行有一行信息:
输出Flash信息这一部分代码是位于board_init_r阶段,执行initr_flash()函数的输出结果。
我们开发板上搭载了型号为S29AL016D70TF102的2MB大小的NOR FLASH,这里输出NOR FALSH大小为0字节,很明显没有能正确识别 2M的NOR FLASH了。
这一节我们将会介绍u-boot如何支持我们的NOR FLASH,这样我们后续就可以通过命令行对NOR FALSH进行读写操作。
一、Mini2440 NOR FLASH介绍
在之前的裸机开发中,我们没有介绍NOR FLASH的裸机程序,如果直接动手修改u-boot程序使其支持NOR FALSH,你可能会很懵逼,这里我们先补充一下NOR FLASH的基础知识。
NOR FLASH存储器接口标准包含CFI和JEDEC:
- CFI为公共Flash接口[Common FlashInterface],CFI是一个公开的标准的从Flash Memory器件中读取数据的接口。它可以使系统软件查询已安装的Flash Memory器件的各种参数,包括器件阵列结构参数、电气和时间参数以及器件支持的功能等。利用CFI可以不用修改系统软件 就可以用新型的和改进的产品代替旧版本的产品。例如:如果新型的Flash Memory的擦除时间只有旧版本的一半,系统软件只要通过CFI读取新器件的擦除时间等参数,修改一下定时器的时间参数即可。
- JEDEC是由生产厂商们制定的国际性协议,主要为 计算机 内存制定。JEDEC用来帮助程序读取Flash的制造商ID和设备ID,以确定Flash的大小和算法,如果芯片不支持CFI,就需使用JEDEC了。工业标准的内存通常指的是符合JEDEC标准的一组内存。
1.1 S29AL016D70TF102
Mini2440开发板就是将2M的NOR FLASH(型号S29AL016D70TF102)焊接在了Bank0上,S29AL016D70TF102这款芯片具有以下性质:
-
灵活的扇区架构:
- 一个 16 KB、两个 8 KB、一个 32 KB 和三十一个64 KB 扇区(字节模式);
- — 1 个 8 Kword、2 个 4 Kword、1 个 16 Kword 和 31 Kword 扇区(字模式);
- Top or bottom boot block configurations available;
-
扇区保护特性:
- A hardware method of locking a sector to prevent any program or erase operations within that sector.
- Sectors can be locked in-system(通过NOR FLASH命令寄存器控制) or via programming equipment(编程器,需要高电压8.5V~12.5V).
- Temporary Sector Unprotect feature allows code changes in previously locked sectors.
- 与 JEDEC 标准的兼容性;
- CFI(通用闪存接口)兼容;
- 擦除挂起/擦除恢复;
- 容量2MB;
Spansion 标准产品有多种封装和操作范围。 芯片型号(有效组合)由下面的元素组成,可以看出S29AL016D70TF102型号的参数MODEL NUMBER为02,bottom boot sector device。
S29AL016D70TF102支持8位和16位两种模式,具体选择哪种模式取决于芯片BYTE引脚信号;这里我们Mini2440采用的是16位模式,S3C2440的A1~A22连接NOR FLASH的A0~A21。需要错位连接的原因是:S3C2440处理器的每个地址对应的是一个BYTE的数据单元,而16位模式下,NOR FLASH的每个地址对应的是一个HALF-WORD(16-BIT)的数据单元。为了保持匹配,所以必须错位连接。这样,从S3C2440处理器发送出来的地址信号的最低位A0对16位Flash来说就被屏蔽掉了。
补充说明:
- 一般来说,ARM处理器内部要设置相应的寄存器,告诉处理器外部扩展的Flash的位宽(8-BIT/16-BIT/32-BIT)。这样,处理器才知道在访问的时候如何从FLASH正确的读取数据;
- 有些ARM处理器内部可以设置地址的错位。对于支持软件选择地址错位的处理器,在连接16-BIT FLASH的时候,硬件上可以不需要把地址线错位。
- 如果处理器支持内部设置地址错位,在实际访问的时候,送出的地址实际上是在MCU内部做了错位处理,其作用是等效于硬件连接上的错位的。
1.2 指令集
Legend:
- X = Don’t care
- RA = Address of the memory location to be read.
- RD = Data read from location RA during read operation.
- PA = Address of the memory location to be programmed. Addresses latch on the falling edge of the WE# or CE# pulse, whichever happens later.
- PD = Data to be programmed at location PA. Data latches on the rising edge of WE# or CE# pulse, whichever happens first.
- SA = Address of the sector to be verified (in autoselect mode) or erased. Address bits A19–A12 uniquely select any sector.
Note:
- 1. See Table 1 for description of bus operations.
- 2. All values are in hexadecimal.
- 3. Except for the read cycle and the fourth cycle of the autoselect command sequence, all bus cycles are write cycles.
- 4. Data bits DQ15–DQ8 are don’t cares for unlock and command cycles.
- 5. Address bits A19–A11 are don’t cares for unlock and command cycles, unless SA or PA required.
- 6. No unlock or command cycles required when reading array data.
- 7. The Reset command is required to return to reading array data when device is in the autoselect mode, or if DQ5 goes high (while the device is providing status data).
- 8. The fourth cycle of the autoselect command sequence is a read cycle.
- 9. The data is 00h for an unprotected sector and 01h for a protected sector. See “Autoselect Command Sequence” for more information.
- 10. Command is valid when device is ready to read array data or when device is in autoselect mode.
- 11. The Unlock Bypass command is required prior to the Unlock Bypass Program command.
- 12. The Unlock Bypass Reset command is required to return to reading array data when the device is in the unlock bypass mode. F0 is also acceptable.
- 13. The system may read and program in non-erasing sectors, or enter the autoselect mode, when in the Erase Suspend mode. The Erase Suspend command is valid only during a sector erase operation.
- 14. The Erase Resume command is valid only during the Erase Suspend mode.
以Autoselect Command Sequence为例,当采用 16 位位宽时,先向地址“555”处写入“AA”,再向地址“2AA”处写 入“55”,接着向地址“555”处写入“90” ,最后从 0 地址“X00”处可以读到“01”。 周期“First”和“Second”是“解锁”,周期“Third”是发出命令。 需要注意的是,以地址"555"为例,每个地址对应的数据长度为2个字节,对应8位位宽地址555<<1=AAA。
以读取厂家ID为例,编写代码时序如下:
- 解锁:向地址AAAH(555<<1)写入AAH,向地址554(2AA<<1)写入55H;
- 命令:向地址AAAH(555<<1)写入90H;
- 读地址0x00得到厂家ID;
由于Mini2440的A1接到NOR FLASH的A0,所以2440发出(555<<1),NOR FLASH才能收到“555”这个地址;同理,只要是2440发出的地址都需要在NOR FLASH的地址基础上<<1。
关于如何对NOR FLASH进行读写、擦除,我就简单说一下:
- 读:NOR FLASH上电后处于数据读取状态(Reading Array Data)。此状态可以进行正常的读,这和读取SDRAM/SRAM/ROM一样。(要是不一样的话,芯片上电后如何从NOR FLASH中读取启动代码)。需要注意的是进入自动选择(Autoselect Command)模式后需要发送复位命令才能回到数据读取状态(Reading Array Data)。
- 擦除:在完成信息获取后一般就要擦除数据。NOR FLASH支持扇区擦除(Sector Erase)和整片擦除(Chip Erase),这2种模式都有对应的命令序列,在完成擦除命令后会自动返回到数据读取(Reading Array Data)状态,在返回前可查询编程的状态。
- 编程(写):完成擦除后就需要对芯片进行写入操作也就是编程,这就需要进入编程(Program)状态。在完成编程命令后会自动返回到数据读取(Reading Array Data)状态,在返回前可查询编程的状态,注意:编程前一定要先擦除。因为编程只能将'1'改写为'0',通过擦写可以将数据全部擦写为'1'。如果绕过解锁模式Unlock Bypass,编程只需要两个周期,而不是4个周期。
1.3 裸机程序
这里就不展示裸机程序了,有兴趣的可以参考mini2440硬件篇之Nor Flash。
二、NOR FLASH支持
2.1 宏定义
在include/configs/smdk2440.h文件有NOR FLASH相关的定义,如下:
#define PHYS_FLASH_1 0x00000000 /* Flash Bank #0 */ #define CONFIG_SYS_FLASH_BASE PHYS_FLASH_1 /*----------------------------------------------------------------------- * FLASH and environment organization */ #define CONFIG_SYS_FLASH_CFI #define CONFIG_FLASH_CFI_DRIVER #define CONFIG_FLASH_CFI_LEGACY #define CONFIG_SYS_FLASH_LEGACY_512Kx16 #define CONFIG_FLASH_SHOW_PROGRESS 45 #define CONFIG_SYS_MAX_FLASH_BANKS 1 #define CONFIG_SYS_FLASH_BANKS_LIST { CONFIG_SYS_FLASH_BASE } #define CONFIG_SYS_MAX_FLASH_SECT (19)
2.2 分析启动信息
在u-boot启动时,我们打印调试信息:
Flash: fwc addr 00000000 cmd f0 00f0 16bit x 16 bit fwc addr 0000aaaa cmd aa 00aa 16bit x 16 bit fwc addr 00005554 cmd 55 0055 16bit x 16 bit fwc addr 0000aaaa cmd 90 0090 16bit x 16 bit fwc addr 00000000 cmd f0 00f0 16bit x 16 bit JEDEC PROBE: ID 1 2249 0 fwc addr 00000000 cmd ff 00ff 16bit x 16 bit fwc addr 00000000 cmd 90 0090 16bit x 16 bit fwc addr 00000000 cmd ff 00ff 16bit x 16 bit JEDEC PROBE: ID be ea00 0 0 Bytes
打印出NOR FLASH的厂家ID=0x01(AMD生产),,设备ID=0x2249,我们查看S29AL016D70TF102芯片的datasheet,发现这两个ID是正确的,但是输出信息中却没有识别到我们NOR FLASH的型号,说明程序底层驱动是对的,但是u-boot没支持我们这个型号的NOR FLASH。
我们定位到 initr_flash(common/board_r.c):
static int initr_flash(void) { ulong flash_size = 0; bd_t *bd = gd->bd; puts("Flash: "); if (board_flash_wp_on()) printf("Uninitialized - Write Protect On\n"); else flash_size = flash_init(); print_size(flash_size, ""); #ifdef CONFIG_SYS_FLASH_CHECKSUM /* * Compute and print flash CRC if flashchecksum is set to 'y' * * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX */ if (getenv_yesno("flashchecksum") == 1) { printf(" CRC: %08X", crc32(0, (const unsigned char *) CONFIG_SYS_FLASH_BASE, flash_size)); } #endif /* CONFIG_SYS_FLASH_CHECKSUM */ putc('\n'); /* update start of FLASH memory */ #ifdef CONFIG_SYS_FLASH_BASE bd->bi_flashstart = CONFIG_SYS_FLASH_BASE; #endif /* size of FLASH memory (final value) */ bd->bi_flashsize = flash_size; #if defined(CONFIG_SYS_UPDATE_FLASH_SIZE) /* Make a update of the Memctrl. */ update_flash_size(flash_size); #endif #if defined(CONFIG_OXC) || defined(CONFIG_RMU) /* flash mapped at end of memory map */ bd->bi_flashoffset = CONFIG_SYS_TEXT_BASE + flash_size; #elif CONFIG_SYS_MONITOR_BASE == CONFIG_SYS_FLASH_BASE bd->bi_flashoffset = monitor_flash_len; /* reserved area for monitor */ #endif return 0; }
flash_init函数在drivers\mtd\cfi_flash.c中定义:
unsigned long flash_init (void) { unsigned long size = 0; int i; #ifdef CONFIG_SYS_FLASH_PROTECTION /* read environment from EEPROM */ char s[64]; getenv_f("unlock", s, sizeof(s)); #endif #ifdef CONFIG_CFI_FLASH /* for driver model */ cfi_flash_init_dm(); #endif /* Init: no FLASHes known */ for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) { flash_info[i].flash_id = FLASH_UNKNOWN; //0xFFFF /* Optionally write flash configuration register */ cfi_flash_set_config_reg(cfi_flash_bank_addr(i), // 什么也没做 里面是空 cfi_flash_config_reg(i)); if (!flash_detect_legacy(cfi_flash_bank_addr(i), i)) flash_get_size(cfi_flash_bank_addr(i), i); size += flash_info[i].size; if (flash_info[i].flash_id == FLASH_UNKNOWN) { #ifndef CONFIG_SYS_FLASH_QUIET_TEST printf ("## Unknown flash on Bank %d " "- Size = 0x%08lx = %ld MB\n", i+1, flash_info[i].size, flash_info[i].size >> 20); #endif /* CONFIG_SYS_FLASH_QUIET_TEST */ } #ifdef CONFIG_SYS_FLASH_PROTECTION else if (strcmp(s, "yes") == 0) { /* * Only the U-Boot image and it's environment * is protected, all other sectors are * unprotected (unlocked) if flash hardware * protection is used (CONFIG_SYS_FLASH_PROTECTION) * and the environment variable "unlock" is * set to "yes". */ if (flash_info[i].legacy_unlock) { int k; /* * Disable legacy_unlock temporarily, * since flash_real_protect would * relock all other sectors again * otherwise. */ flash_info[i].legacy_unlock = 0; /* * Legacy unlocking (e.g. Intel J3) -> * unlock only one sector. This will * unlock all sectors. */ flash_real_protect (&flash_info[i], 0, 0); flash_info[i].legacy_unlock = 1; /* * Manually mark other sectors as * unlocked (unprotected) */ for (k = 1; k < flash_info[i].sector_count; k++) flash_info[i].protect[k] = 0; } else { /* * No legancy unlocking -> unlock all sectors */ flash_protect (FLAG_PROTECT_CLEAR, flash_info[i].start[0], flash_info[i].start[0] + flash_info[i].size - 1, &flash_info[i]); } } #endif /* CONFIG_SYS_FLASH_PROTECTION */ } flash_protect_default(); #ifdef CONFIG_FLASH_CFI_MTD cfi_mtd_init(); #endif return (size); }
其中CONFIG_SYS_MAX_FLASH_BANKS定义为1,FLASH_UNKNOWN 定义为0xFFFF:
#define CONFIG_SYS_MAX_FLASH_BANKS 1 #define FLASH_UNKNOWN 0xFFFF /* unknown flash type */
flash_info定义如下:
#define CFI_MAX_FLASH_BANKS CONFIG_SYS_MAX_FLASH_BANKS flash_info_t flash_info[CFI_MAX_FLASH_BANKS]; /* FLASH chips info */
/*----------------------------------------------------------------------- * FLASH Info: contains chip specific data, per FLASH bank */ typedef struct { ulong size; /* total bank size in bytes */ ushort sector_count; /* number of erase units */ ulong flash_id; /* combined device & manufacturer code */ ulong start[CONFIG_SYS_MAX_FLASH_SECT]; /* virtual sector start address */ uchar protect[CONFIG_SYS_MAX_FLASH_SECT]; /* sector protection status */ #ifdef CONFIG_SYS_FLASH_CFI uchar portwidth; /* the width of the port */ uchar chipwidth; /* the width of the chip */ ushort buffer_size; /* # of bytes in write buffer */ ulong erase_blk_tout; /* maximum block erase timeout */ ulong write_tout; /* maximum write timeout */ ulong buffer_write_tout; /* maximum buffer write timeout */ ushort vendor; /* the primary vendor id */ ushort cmd_reset; /* vendor specific reset command */ uchar cmd_erase_sector; /* vendor specific erase sect. command */ ushort interface; /* used for x8/x16 adjustments */ ushort legacy_unlock; /* support Intel legacy (un)locking */ ushort manufacturer_id; /* manufacturer id */ ushort device_id; /* device id */ ushort device_id2; /* extended device id */ ushort ext_addr; /* extended query table address */ ushort cfi_version; /* cfi version */ ushort cfi_offset; /* offset for cfi query */ ulong addr_unlock1; /* unlock address 1 for AMD flash roms */ ulong addr_unlock2; /* unlock address 2 for AMD flash roms */ const char *name; /* human-readable name */ #endif #ifdef CONFIG_MTD struct mtd_info *mtd; #endif } flash_info_t;