1. MTK平台内存架构概览
各位同学,咱们今天聊聊MTK SoC的内存架构。说实话,搞驱动开发这么多年,我踩过最多的坑就是内存相关的。你想想看,一个系统跑着跑着突然卡死,或者DMA传着传着数据就错了,十有八九是内存访问没搞对。
我个人习惯,拿到一个新平台,第一件事就是先把内存层次结构摸清楚。为什么?因为不同的内存区域,访问速度能差好几个数量级。你写驱动的时候,数据放哪儿、怎么搬,直接决定了性能。
1.1 内存层次结构总览
MTK SoC的内存架构,说白了就是一套金字塔结构。从最顶层到最底层,速度越来越慢,容量越来越大。
| 层级 | 类型 | 典型容量 | 访问延迟 | 功耗 |
|---|---|---|---|---|
| L1 Cache | SRAM | 32KB - 64KB | 1-2 cycles | 极高 |
| L2 Cache | SRAM | 256KB - 1MB | 10-20 cycles | 高 |
| 内部SRAM | SRAM | 几百KB | 20-50 cycles | 中 |
| DRAM | DDR | 几GB | 100-300 cycles | 低 |
嗯,这里要注意,Cache是硬件自动管理的,但SRAM和DRAM是我们驱动工程师需要直接打交道的。
1.2 L1/L2 Cache:速度的王者
Cache这玩意儿,说白了就是CPU的"贴身秘书"。它把最常用的数据放在离CPU最近的地方。我在项目中遇到过,有个同事写了个视频解码驱动,性能死活上不去。后来一查,发现关键数据全在DRAM里,每次访问都要等上百个时钟周期。
为什么会这样?因为Cache的命中率太低了。你想想看,CPU访问L1 Cache只要1-2个周期,访问DRAM要200个周期,差了100倍!
关键点:Cache是透明的,但驱动开发者必须了解它的行为。比如DMA传输时,如果CPU和DMA同时访问同一块内存,Cache一致性就是个大问题。
我曾经在一个音频驱动项目里,DMA把数据写到了DRAM,但CPU读的时候发现数据不对。查了两天才发现,CPU读的是Cache里的旧数据,根本没去DRAM拿新的。这就是典型的Cache一致性问题。
1.3 内部SRAM:小而美的"快存"
内部SRAM,我习惯叫它"片内快存"。它不像Cache那样自动管理,而是由我们手动分配使用。容量不大,一般几百KB,但速度比DRAM快得多。
我个人建议,把那些对延迟极度敏感的数据放在SRAM里。比如中断处理函数里要用的变量、实时性要求高的音频缓冲区。
实战技巧:在MTK平台上,内部SRAM通常有专门的地址区间。比如MT6789平台上,SRAM基地址是0x00100000,大小256KB。你可以用ioremap把它映射到内核虚拟地址空间。
// 内部SRAM映射示例
#define SRAM_BASE 0x00100000
#define SRAM_SIZE (256 * 1024)
void *sram_virt = ioremap(SRAM_BASE, SRAM_SIZE);
if (!sram_virt) {
pr_err("SRAM映射失败!\n");
return -ENOMEM;
}
// 在SRAM中分配关键数据
struct critical_data *data = (struct critical_data *)sram_virt;
data->flag = 0x55AA; // 初始化标志
1.4 DRAM:大容量的"仓库"
DRAM就是咱们常说的DDR内存。容量大,几GB起步,但访问延迟也大。你想想看,从DRAM读一个数据,要经过内存控制器、总线、Cache,最后才到CPU,不慢才怪。
不过,DRAM也有它的优势——便宜、容量大。大部分数据,比如视频帧、图片、应用代码,都放在DRAM里。
避坑指南:我曾经在调试一个4K视频播放驱动时,发现帧率始终上不去。后来用性能分析工具一看,DDR带宽被占满了。原来有个模块在不停地做DMA传输,把DDR总线堵死了。解决办法是把一些不紧急的数据放到SRAM里,或者用DMA的优先级控制。
1.5 不同内存区域的访问延迟对比
咱们用个具体的例子来说明。假设CPU要读取一个32位整数:
- L1 Cache命中:约1-2ns,几乎不耗电
- L2 Cache命中:约5-10ns,功耗适中
- 内部SRAM:约10-20ns,功耗略高
- DRAM:约50-100ns,功耗最低
你看,从L1到DRAM,延迟差了50倍以上。所以写驱动的时候,一定要想清楚:这个数据该放哪儿?
1.6 功耗特性:性能与功耗的博弈
搞嵌入式的人都知道,功耗是个大问题。Cache虽然快,但功耗也高。SRAM次之,DRAM最省电。
我记得有个项目是做智能手表,电池只有300mAh。为了省电,我们把大部分数据都放在DRAM里,只有最关键的几个变量放在SRAM里。结果续航从8小时提升到了12小时。
但代价是什么?系统响应变慢了。用户按一下按钮,要等几百毫秒才有反应。这就是典型的性能与功耗的博弈。
我的建议:在MTK平台上,你可以通过mtk_mem_profile这个工具来查看当前内存的使用情况和功耗分布。我一般会在驱动初始化时,根据当前的工作模式(高性能/低功耗)动态调整数据存放的位置。
1.7 实战中的内存选择策略
说了这么多理论,咱们来点实际的。在MTK平台上写驱动,怎么选内存?
- 中断处理函数:数据放SRAM,因为中断里不能等
- 音频/视频流:数据放DRAM,用DMA传输,CPU只管配置
- 关键状态变量:放SRAM,保证原子性和低延迟
- 大块数据缓冲区:放DRAM,用Cacheable映射,利用Cache加速
嗯,这里要注意,Cacheable映射虽然快,但会引入一致性问题。如果你用DMA往DRAM写数据,CPU读的时候一定要做Cache刷新操作。
// DMA传输后的Cache刷新
dma_sync_single_for_cpu(dev, dma_addr, size, DMA_FROM_DEVICE);
// 或者更粗暴的方式
__flush_dcache_area(virt_addr, size);
我个人习惯,在DMA传输完成后,总是显式地做一次Cache刷新。虽然会损失一点性能,但能避免很多诡异的bug。
1.8 本章小结
好了,咱们把MTK的内存架构捋了一遍。记住几个关键点:
- Cache最快但不可控,SRAM又快又可控但容量小,DRAM慢但容量大
- 写驱动时,根据数据的实时性要求和访问频率来选择存放位置
- DMA传输时,一定要注意Cache一致性问题
- 功耗和性能是矛盾的,要根据应用场景做取舍
下一章,咱们会深入讲解DMA传输的实战技巧。到时候我会拿一个真实的摄像头驱动案例,手把手教你怎么配置DMA、怎么处理Cache一致性。敬请期待!