
1. 项目概述深入MTK平台Flash配置的底层逻辑在嵌入式开发特别是基于联发科MTK功能机平台的开发中Flash的配置是系统能否正常启动和运行的基石。很多工程师在拿到一个新的Flash型号时往往对着一堆宏定义和配置文件感到困惑这些地址、大小、分区到底是怎么来的为什么这么定义今天我就结合自己踩过的坑和项目经验把MTK平台以较早期的功能机平台如MT6223/MT6250为例上关于Flash配置特别是NOR Flash启动模式下的那些关键宏定义掰开揉碎了讲清楚。这不是一份照本宣科的官方文档翻译而是一个一线工程师从原理到实践、从寄存器到文件系统的深度解析。无论你是刚接触MTK平台的新手还是想深入理解底层存储机制的老鸟这篇文章都能帮你把这块硬骨头啃下来。2. 核心概念与硬件基础扫盲在进入具体的代码分析之前我们必须先统一“语言”理解几个核心的硬件和基础概念。这就像盖房子前得先认识砖头和水泥一样。2.1 NOR Flash vs NAND Flash不只是名字不同MTK功能机平台常使用NOR Flash或NORNAND的MCP多芯片封装方案。它们的区别远不止名字NOR Flash特点是芯片内执行XIP, eXecute In Place。CPU可以直接从NOR Flash中取指令执行无需先将代码拷贝到RAM。因此它常被用作启动设备存放Bootloader和核心系统代码。它的读写以字节为单位随机访问速度快但容量相对较小成本较高。NAND Flash特点是高容量、低成本。但它不能直接执行代码数据必须以页Page为单位进行读写。通常用作大容量存储存放文件系统如用户数据、多媒体文件。在MTK平台上当配置为“SYSTEM DRIVE ON NAND”时文件系统才会放在NAND上。一个关键类比你可以把NOR Flash想象成你书桌上那本最常翻的、随时可以打开某一页就开始阅读的参考书XIP。而NAND Flash就像你家那个巨大的书柜存了很多书但你想看哪本必须先把它从书柜里拿出来读到RAM里才能阅读。2.2 Flash的物理结构Block, Sector/Page, OOB这是理解后续所有分区和大小计算的关键。以文档中提到的三星K5L6331CAA一款NORNAND MCP的NAND部分为例1 Block 32 Pages块Block是擦除Erase操作的最小单位。想象成笔记本的一个章节要重写这个章节必须把整章撕掉。1 Page 512 Bytes (Data Field) 16 Bytes (OOB)页Page是编程Program和读取Read操作的最小单位。其中512字节是存放有效数据的地方那额外的16字节OOBOut-Of-Band区是干什么的由于NAND Flash物理特性有坏块且存储可能出错OOB区就用来存放ECC校验码、坏块标记等管理信息。MTK的文件系统驱动通常是FAT会负责处理这些OOB数据。1 Word 2 Bytes 16 Bits在嵌入式领域尤其是跟底层寄存器打交道时“字长”很重要。很多地址和大小配置都是以“字”为单位需要和字节进行换算。实操心得当你从Flash厂商的Datasheet里看到这些参数时一定要记录下来。后续在custom_EMI.h、Flash_opt.h中的配置几乎都是围绕这些物理参数展开的。配置错了轻则读写错误重则系统无法启动。2.3 关键文件角色说明MTK的BSP板级支持包里文件众多我们先理清几个核心配置文件的作用Custom_memorydevice.hFlash型号的“身份证”。这里定义了当前项目使用的具体Flash型号CS0_PART_NUMBER、类型MEMORY_DEVICE_TYPE以及文件系统分区的基础宏如NOR_BOOTING_NOR_FS_SIZE。这是所有配置的起点。Custom_EMI.h外部存储器接口的“控制器配置”。EMIExternal Memory Interface是CPU连接外部存储器的总线控制器。这个文件里配置了EMI控制器的时序参数EMI_SettingOnCS和工作模式EMI_GENERAL_CONTROL。这部分配置必须严格对应Flash Datasheet里的AC时序图否则会导致读写不稳定。Flash_opt.hFlash物理布局的“地图”。这里根据Datasheet定义了Flash的内部Bank划分、每个Bank的Block大小和数量。这是进行FOTA分区、文件系统分区计算的直接依据。Custom_flash.c文件系统区域的“最终规划图”。这个文件通常由脚本如emiGenV2.pl根据前面文件生成里定义了文件系统FS区域在Flash中的具体布局RegionInfo即用哪些Block、每个Block多大来组成用户可见的存储盘。3. 从零解析Flash配置宏定义全解现在我们进入正题逐行分析那些让人头疼的宏定义。3.1 起点Flash型号与基础分区 (Custom_memorydevice.h)#define MEMORY_DEVICE_TYPE NOR_RAM_MCP #define CS0_PART_NUMBER K5L6331CAA #define CS1_PART_NUMBER K5L6331CAAMEMORY_DEVICE_TYPE告诉系统我们用的是NOR Flash和RAM的MCP封装。如果是纯NOR可能是NOR如果是NORNAND MCP可能是NOR_NAND_MCP。这个宏会影响底层驱动初始化流程。CS0_PART_NUMBER芯片选择0Chip Select 0上连接的Flash型号。这是最关键的一行系统会根据这个字符串去一个巨大的Excel表MemoryDeviceList_SinceXXX.xls里查找该型号的所有物理参数和预配置。CS1_PART_NUMBER芯片选择1上的型号。这里和CS0相同说明我们用的是同一个MCP芯片可能它内部有两个Die或Bank被分别片选。重要操作定义好型号后必须将make/$(PROJECT).mak例如make/CUSTOMER_PROJECT.mak中的SYSGEN变量设为TRUE。这个操作的意思是执行系统生成。MTK的编译系统会根据你定义的PART_NUMBER自动从那个Excel表中提取时序、分区等信息并生成或更新custom_EMI.h、Flash_opt.h等文件。如果你改了型号却没SYSGEN编译用的还是旧配置必然出错。3.2 文件系统分区详解NOR启动模式当系统从NOR Flash启动时文件系统可以放在NOR上也可以放在NAND上如果有的话。相关宏定义决定了磁盘如何呈现给用户。#define NOR_BOOTING_NOR_FS_FIRST_DRIVE_SECTORS 512作用定义用户盘User Drive的大小。在MTK功能机中连接USB后电脑上看到的那个可移动磁盘就是用户盘。单位扇区数Sectors。这里的关键是此处的“扇区”是逻辑扇区固定为512字节这是为了兼容PC的FAT文件系统标准。计算用户盘实际容量 512 sectors * 512 bytes/sector 262,144 bytes 256 KB。查看验证编译后在build\$(PROJECT)\log\ckSysDrv.log文件中可以搜索到FS First Drive Size 262144这就是对该配置的反馈。系统盘文件系统剩余的空间会自动划为系统盘System Drive用于存储NV非易失性配置参数、短信、电话本等系统数据对用户不可见。注意文档中特别警告MUST NOT assign as (TOTAL_FS_SIZE - SIZE_OF_SYSTEM_DRIVE)。为什么因为文件系统如FAT32本身需要一些空间来存放引导扇区MBR/DBR、文件分配表FAT、根目录区等元数据。如果你简单地把总空间减去系统盘大小作为用户盘会侵占这些管理区域的空间导致文件系统损坏。所以用户盘大小必须明确指定且必须小于总FS空间 - 系统盘空间 - 文件系统元数据开销。3.3 文件系统分区详解NAND存储方案如果项目采用“系统盘在NAND”或“FOTA升级包在NAND”的方案则需要配置NAND上的文件系统。#define NOR_BOOTING_NAND_FS_BASE_ADDRESS 0x00000000 #define NOR_BOOTING_NAND_FS_SIZE 0x08000000 #define NOR_BOOTING_NAND_FS_FIRST_DRIVE_SECTORS 220000NOR_BOOTING_NAND_FS_BASE_ADDRESSNAND Flash上文件系统区域的起始地址。通常从0开始。注意这个地址是NAND芯片内部的逻辑地址并非CPU内存映射地址NOR才有内存映射。NOR_BOOTING_NAND_FS_SIZENAND上文件系统区域的总大小。0x08000000 128 MB。这决定了系统管理多大的NAND空间作为磁盘。NOR_BOOTING_NAND_FS_FIRST_DRIVE_SECTORS同上定义NAND上用户盘的大小单位512字节扇区。220000 sectors * 512 bytes/sector ≈ 112.64 MB。剩下的空间约15.36 MB用作系统盘。配置选择逻辑是选NOR FS还是NAND FS这取决于硬件成本、容量需求和性能。NOR FS访问速度快内存映射但容量小、贵NAND FS容量大、便宜但访问需要通过驱动速度慢。功能机上如果用户需要存储大量MP3、照片NAND FS是必须的。3.4 硬件时序配置让CPU和Flash说同一种语言 (custom_EMI.h)这是最需要谨慎对待的部分配置错误会导致系统不稳定、死机、数据损坏。#define EMI_GENERAL_CONTROL 0xeeeefb80 const kal_uint32 EMI_SettingOnCS[4] { 0x44c94324, // CS0 setting (for Flash) 0x44c94404, // CS1 setting (for RAM) 0, 0 };EMI_GENERAL_CONTROL设置EMI控制器的全局工作模式如数据总线宽度16位/32位、时钟选择、低功耗模式等。这个值通常由平台原厂提供或从参考设计中得来。EMI_SettingOnCS[4]这是一个数组每个元素是一个32位的魔法数字分别对应CS0~CS3的时序配置。0x44c94324和0x44c94404不是凭空想象的它们每一位都代表了具体的时序参数位域分解这个32位数通常包含地址建立时间、数据建立时间、读写保持时间、等待周期等。你需要查阅MTK平台EMI控制器的编程手册了解每个位域的含义。如何得到最可靠的方法是使用MTK提供的EMI配置工具通常是一个GUI工具或Excel计算表。你输入Flash Datasheet里的时序参数如tWC, tRC, tOE等工具会自动计算出这个十六进制值。严禁直接拷贝其他项目的值除非你100%确定使用的是同型号Flash和同频率的CPU时钟。避坑指南如果系统在访问Flash时随机死机尤其是在高低温测试时首要怀疑对象就是EMI时序。时序配置过于紧张值太小会导致在极端条件下采样失败过于宽松值太大则会影响性能。务必根据Datasheet的最差情况Max值来配置。4. Flash物理布局与FOTA分区设计 (Flash_opt.h)这个文件直接映射了Flash芯片的物理结构是进行空间分配的基础。4.1 Bank与Block划分根据K5L6331CAA的Datasheet其NOR部分被划分为4个Bank每个Bank的Block大小和数量不同BankNumber of BlocksBlock Size (K Words)Block Size (Bytes)说明0848,192小Block Bank通常用于存放Bootloader或关键参数1483265,536大Block Bank用于存放主代码2483265,536大Block Bank用于存放主代码315 832 465,536 8,192混合Bank末尾的8个小Block常被划给文件系统注意单位是K Words1 Word 2 Bytes所以4K Words 8KB32K Words 64KB。4.2 FOTA分区定义FOTA无线固件升级需要一个独立、受保护的区域来存放升级包。MTK通过以下宏定义来划分这个区域#define CONFIG_FOTA_NOR_BLOCK_DEF \ {0x0, 0x2000}, \ // 起始地址0大小0x2000 (8KB) {0x10000, 0x10000}, \ // 起始地址0x10000大小0x10000 (64KB) {0x7F0000, 0x2000}, // 起始地址0x7F0000大小0x2000 (8KB) #define CONFIG_FOTA_NOR_BANK_DEF \ {0x100000, 1}, \ // 从地址0x100000开始占用1个Bank {0x300000, 2}, \ // 从地址0x300000开始占用2个Bank {0x100000, 1}, // 从地址0x100000开始占用1个Bank (注意这里看起来像笔误或特定含义通常地址应递增)CONFIG_FOTA_NOR_BLOCK_DEF以Block为单位定义FOTA区域。它描述了FOTA区域由哪些不连续的Block组成。例如第一行表示从地址0开始拿一个大小为0x20008KB的Block这对应Bank 0的小Block。这非常灵活可以跨Bank收集碎片空间。CONFIG_FOTA_NOR_BANK_DEF以Bank为单位定义FOTA区域。它描述了FOTA区域由哪些连续的Bank组成。例如{0x100000, 1}表示从地址0x100000即1MB边界开始连续的1个BankBank 2划给FOTA。这种方式更常见因为FOTA升级包通常是一个连续的大文件。如何选择这取决于Flash的物理结构和你的升级包大小。如果Flash的Block大小一致用BANK_DEF最简单。如果Flash像K5L6331CAA这样有大小Block混合且你想充分利用空间比如把不用的、零散的小Block也用于FOTA则可能需要结合BLOCK_DEF。但管理起来更复杂。4.3 ROM大小计算从CONFIG_FOTA_NOR_BANK_DEF我们可以反推用于存储代码的ROM只读存储器区域大小。假设它描述的是非FOTA的代码区域注意需要根据具体项目配置解读这里以文档描述为例0x100000 * 1 0x300000 * 2 0x100000 * 1 0x800000计算结果为8MB。这就是留给应用程序、内核等代码的空间。4.4 Region定义连接物理与逻辑#define CONFIG_FOTA_NOR_REGION_DEF \ {0x2000, 8}, \ // 8个大小为0x2000的Block {0x10000, 126}, \ // 126个大小为0x10000的Block {0x2000, 8}, // 8个大小为0x2000的Block这个定义是CONFIG_FOTA_NOR_BLOCK_DEF的另一种视角。它不关心起始地址只关心不同大小的Block各有几个。它是对Flash物理布局的抽象描述头尾各有8个小Block中间有126个大Block。这个定义会被底层驱动用来遍历和管理所有Block。5. 文件系统区域最终定稿 (Custom_flash.c)这个文件里的RegionInfo数组是文件系统在Flash上物理布局的最终决定版。FLASH_REGIONINFO_VAR_MODIFIER FlashRegionInfo RegionInfo[] { {0x10000, 15}, {0x2000, 8}, EndRegionInfo /* Dont modify this line */ };含义文件系统区域由15个大小为0x1000064KB的Block和8个大小为0x20008KB的Block组成。计算总大小0x10000 * 15 0x2000 * 8 0xF0000 0x10000 0x100000字节即1MB。来源这个布局正是取自前面Datasheet里Bank 3的描述15个32KWord Block 8个4KWord Block。在NOR启动模式下文件系统通常就放在最后一个Bank里。这个RegionInfo必须和Flash_opt.h里对Bank 3的描述完全一致否则文件系统驱动会访问到错误的物理位置导致数据丢失。5.1 文件系统大小宏的最终决议在Custom_flash.c中我们看到了文件系统总大小的最终定义逻辑#ifndef NOR_BOOTING_NOR_FS_SIZE // 如果Custom_memorydevice.h里没有定义 #define NOR_ALLOCATED_FAT_SPACE (0x00100000) // 默认1MB #else #define NOR_ALLOCATED_FAT_SPACE (NOR_BOOTING_NOR_FS_SIZE) // 使用自定义大小 #endif这提供了一个灵活性你可以在Custom_memorydevice.h里用NOR_BOOTING_NOR_FS_SIZE覆盖默认的1MB设置。但请注意这个大小不能超过RegionInfo所描述的总物理空间1MB。5.2 代码运行基地址#ifndef NOR_BOOTING_NOR_FS_BASE_ADDRESS #define NOR_FLASH_BASE_ADDRESS (0x00700000) #else #define NOR_FLASH_BASE_ADDRESS (NOR_BOOTING_NOR_FS_BASE_ADDRESS) #endifNOR_FLASH_BASE_ADDRESS定义了代码区域在CPU内存映射空间中的起始地址。对于NOR XIP这就是你的程序开始执行的地方。0x00700000这个值表示代码从7MB地址开始。这意味着CPU认为Flash的前7MB0x00000000 ~ 0x006FFFFF是文件系统或其他用途从7MB开始才是可以执行的代码。这个地址必须与链接脚本scatter file中的ROM加载地址严格对齐。6. 编译检查与大小分析配置了这么多最终我们关心两件事我的代码和文件系统到底占了多少空间会不会溢出了6.1 查看编译结果MTK的编译系统通常是ADS或RVCT在编译链接完成后会生成一个*.lis文件列表文件其中包含以下关键信息Total RO Size(Code RO Data) xxxxx // ROM size Total RW Size(RW Data ZI Data) yyyyy // RAM size Total ROM Size(Code RO Data RW Data) zzzzz // 烧录文件大小RO只读包括代码Code和只读数据如const常量。这部分最终存放在Flash的代码区域。RW可读写包括已初始化的全局/静态变量。这部分比较特殊其初始值存放在Flash中属于ROM Size但运行时变量本身在RAM中。ZI零初始化未初始化或显式初始化为0的全局/静态变量。这部分不占Flash空间只在程序加载时在RAM中分配并清零。Total ROM Size这是最终需要烧录到Flash中的二进制文件通常是*.bin的大小。它等于Code RO Data RW Data的初始值。你必须确保Total ROM Size 你为代码预留的ROM空间如前计算的8MB。6.2 空间不足怎么办如果编译发现ROM空间不足你有以下几个选择按推荐顺序排列优化代码检查是否有冗余代码、调试信息过多、库文件是否可裁剪。这是最根本的方法。调整FOTA分区如果FOTA区域预留过大比如为未来升级预留了双倍空间可以适当减小。但这会影响未来升级包的大小。调整文件系统分区如果NOR_ALLOCATED_FAT_SPACE或用户盘定义得过大可以适当减小。但这会牺牲用户存储空间。更换大容量Flash这是硬件改动成本最高。一个常见的误区认为Total RW Size和Total ZI Size也占Flash。其实它们只占RAM。你需要确保它们之和小于硬件RAM总量。7. 实战配置一套新的Flash型号假设我们要将项目中的Flash从K5L6331CAA换成另一颗型号为MX25L6406E的NOR Flash。获取Datasheet找到MX25L6406E的官方数据手册。修改Custom_memorydevice.h#define CS0_PART_NUMBER MX25L6406E执行SYSGEN在项目make目录下的.mak文件中设置SYSGEN TRUE然后重新编译或执行sysgen相关命令。这一步会让工具链自动从MemoryDeviceList.xls中查找MX25L6406E的配置并尝试生成custom_EMI.h和Flash_opt.h。检查生成的文件打开custom_EMI.h检查EMI_SettingOnCS的值。切勿直接使用应使用EMI配置工具根据新Flash的Datasheet时序参数重新计算并验证这个值。打开Flash_opt.h检查CONFIG_FOTA_NOR_BANK_DEF等宏定义是否与新Flash的容量64Mb 8MB、扇区/Block大小MX25L6406E可能是4KB或64KB的Sector匹配。根据新的物理布局你可能需要手动调整这些定义。调整文件系统分区在Custom_memorydevice.h中根据新Flash的总容量和代码预估大小重新计算并定义NOR_BOOTING_NOR_FS_SIZE和NOR_BOOTING_NOR_FS_FIRST_DRIVE_SECTORS。更新Custom_flash.c如果Flash的物理布局Bank/Block划分发生变化RegionInfo数组也需要相应更新以匹配新Flash上分配给文件系统的物理Block。全面测试进行烧录、启动、读写文件、FOTA升级等全套测试。特别注意高低温环境下的稳定性。8. 常见问题与排查实录问题1系统无法启动卡在初始化阶段。排查首先检查CS0_PART_NUMBER是否拼写正确是否在MemoryDeviceList.xls中存在。然后重点检查custom_EMI.h中的时序配置。用示波器测量Flash的片选(CS#)、读使能(OE#)、写使能(WE#)信号看波形是否正常时序参数建立、保持时间是否满足Datasheet要求。这是最常见的原因。问题2文件系统格式化失败或读写文件时随机出错。排查检查Custom_flash.c中的RegionInfo定义是否与Flash_opt.h中描述的物理Block布局一致。一个Block算错了整个文件系统链就会错乱。检查NOR_ALLOCATED_FAT_SPACE是否大于RegionInfo描述的总物理空间。如果是NAND Flash检查驱动中的ECC校验配置是否正确OOB区布局是否与Flash型号匹配。问题3编译成功但烧录后程序跑飞。排查检查NOR_FLASH_BASE_ADDRESS。确保这个地址与你的链接脚本中定义的ROM加载地址完全一致。在lis文件中查看代码的入口地址Entry point看是否指向这个基地址附近。问题4FOTA升级时升级包写入失败。排查检查CONFIG_FOTA_NOR_BLOCK_DEF或CONFIG_FOTA_NOR_BANK_DEF定义的区域是否确实是可擦写的没有存放正在运行的代码。检查该区域的大小是否大于你的升级包.bin文件的大小。检查FOTA驱动中对该区域进行擦除和编程的地址计算是否正确是否与这些宏定义对应。问题5用户看到的磁盘容量和配置的不一样。排查确认NOR_BOOTING_NOR_FS_FIRST_DRIVE_SECTORS的单位是512字节的扇区。用计算器算一下扇区数 * 512 / 1024 / 1024得到的就是MB数。另外在Windows中由于文件系统开销FAT表、MBR等显示的“可用空间”会小于磁盘总容量这是正常的。理解MTK平台的Flash配置关键在于建立起从硬件Datasheet-EMI/Flash配置宏-文件系统分区-编译链接结果这一整条逻辑链。每一个数字都不是随意的其背后要么是硬件的物理约束要么是软件的逻辑设计。最忌讳的就是“拿来主义”盲目拷贝其他项目的配置。每当换一颗Flash或者调整存储分区时静下心来拿着Datasheet和这份指南从头推导一遍你就能真正掌控你的存储系统避免很多难以调试的底层问题。