MUC外置PSRAM-软件实现页面切换
简单介绍
对于定位在中低端的MCU(通常是某个系列48脚,64脚的型号),不会配备额外的RAM接口给用户外接SRAM,SDRAM或DRAM;这就造成了程序能使用的RAM只能是单片机内部的SRAM,通常只有几十或者100多KB,有时候就不够用了。本文介绍的方案中,使用8脚SPI接口的PSRAM芯片给单片机扩充可用内存,当然了这样的外置RAM由于没法接入ARM内核的数据/地址总线,因此也不能做任何寻址操作(无法直接用指针的方式访问),但通过软件实现的虚拟地址到物理地址映射和换页单元,在页面内可以做到随机访问使用上与单片机内置RAM无异。
硬件部分
PSRAM型号:LY68L6400SLIT
支持SPI和QSPI接口,物理页大小为1KB,容量8MB,页内访问最大时钟速率144MHz,跨页访问最大时钟速率84MHz
某宝最低价格6RMB还包邮,相对还是很划算了。
MCU只需要带SPI接口,如果想要读写性能更好,推荐使用带QSPI接口的型号,同时看下是否有给SPI的DMA通道。
这里使用的MCU为GD32F303RC
,SPI0外设支持QSPI模式,最大时钟30MHz,QSPI模式下等效时钟120MHz,且TX和RX都有DMA通道可用。
PCB布线上,由于速率低,因此和传统的DRAM相比,要求低了很多。
软件实现
直接内存性能测试
这一步不使用软件换页器作为中间层,直接测试的PSRAM裸芯片的访问速度,比较能反应出外置PSRAM读写能力的上限,访问速度主要和QSPI最大时钟频率,QSPI外设位宽,QSPI-DMA位宽以及PSRAM芯片支持的最大时钟频率有关。
参考基准
MCU内部SRAM连续写:
计时时钟使用CM4内核自带的DWT(DataWatchpoint andTrace)单元,它有一个32位的向上计数器CYCCNT,时钟频率就是CPU主频,当前CPU主频为120Mhz,因此CYCCNT每自增1
,时间就过去了t =(1 / f)= 1 / 120000000 = 8.33x10^-9s = 8.3ns
,使用CYCCNT计时的精度非常高。
// 初始化DWT的计时器单元
static void hw_dwt_init(void) {
// DWT timer enable
CoreDebug->DEMCR = 0x1000000;
// clear Cycle Count Register
DWT->CYCCNT = 0x0;
// enable Data Watchpoint and Trace Register
DWT->CTRL = 0x1;
}
SRAM起始地址4字节对齐,使用rtthread
实现的rt_memset函数写内部SRAM:
uint8_t *page0_buffer = (uint8_t *)0x20004000u;
uint32_t start = DWT->CYCCNT;
rt_memset(page0_buffer, 0x0, 16384);
uint32_t end = DWT->CYCCNT;
rt_kprintf("start:%d\n", start);
rt_kprintf("end:%d\n", end);
大小(KB) | CCYCNT | 耗时(us) | 等效速度(MB/s) |
---|---|---|---|
1K | 691 | 5.75603 | 169.65903 |
4K | 2611 | 21.74963 | 179.60075 |
8K | 5171 | 43.07443 | 181.37210 |
16K | 10291 | 85.72403 | 182.27094 |
SRAM起始地址4字节对齐,使用newlib
c标准库memset函数写内部SRAM:
使用了--specs=nano.specs
属性
uint8_t *page0_buffer = (uint8_t *)0x20004000u;
uint32_t start = DWT->CYCCNT;
memset(page0_buffer, 0x0, 16384);
uint32_t end = DWT->CYCCNT;
rt_kprintf("start:%d\n", start);
rt_kprintf("end:%d\n", end);
大小(KB) | CCYCNT | 耗时(us) | 等效速度(MB/s) |
---|---|---|---|
1K | 7181 | 59.81773 | 16.32563623 |
4K | 28898 | 240.72034 | 16.22733667 |
8K | 57569 | 479.54977 | 16.29132259 |
16K | 114920 | 957.2836 | 16.32222677 |
不使用--specs=nano.specs
属性,代码段.text
增大约12.765KB。
大小(KB) | CCYCNT | 耗时(us) | 等效速度(MB/s) |
---|---|---|---|
1K | 883 | 7.35539 | 132.7682829 |
4K | 3379 | 28.14707 | 138.7799867 |
8K | 6707 | 55.86931 | 139.8352691 |
16K | 13363 | 111.31379 | 140.3689516 |
PSRAM写入1
PSRAM连续写入测试:
- PSRAM页内地址对齐,突发长度1KB
- 8bit QSPI@30MHz,DMA 8bit
- 使用rt_device设备框架
- 使用rt_spi驱动框架
- 底层驱动调用库函数
- 异步模式,DMA完成中断(DMA_INT_FLAG_FTF)使用rt_completion(二值信号量)唤醒线程
大小(KB) | CCYCNT | 耗时(us) | 等效速度(MB/s) |
---|---|---|---|
1K | 24801 | 206.59233 | 4.727002692 |
4K | 99209 | 826.41097 | 4.726764457 |
8K | 198625 | 1654.54625 | 4.721838389 |
16K | 397029 | 3307.25157 | 4.724466727 |
PSRAM写入2
- PSRAM页内地址对齐,突发长度1KB
- 8bit QSPI@30MHz,DMA 8bit
- 使用rt_device设备框架
不使用rt_spi驱动框架
- 底层驱动仍调用库函数
同步模式,线程中开启DMA后,while循环轮询中断标志位
大小(KB) | CCYCNT | 耗时(us) | 等效速度(MB/s) |
---|---|---|---|
1K | 22831 | 190.18223 | 5.134877743 |
4K | 91337 | 760.83721 | 5.134146896 |
8K | 182673 | 1521.66609 | 5.134175002 |
16K | 365349 | 3043.35717 | 5.134132843 |
PSRAM写入3
- PSRAM页内地址对齐,突发长度1KB
- 8bit QSPI@30MHz,DMA 8bit
- 使用rt_device设备框架
不使用rt_spi驱动框架
底层驱动直接操作寄存器
使用变量记录下当前QSPI-IO状态,在重复读写操作时,避免多次配置IO工作模式(QSPI为半双工,读/写操作前需要配置IO为/输入/输出模式)
同步模式,线程中开启DMA后,while循环轮询中断标志位
大小(KB) | CCYCNT | 耗时(us) | 等效速度(MB/s) |
---|---|---|---|
1K | 20969 | 174.67177 | 5.590843329 |
4K | 83799 | 698.04567 | 5.595980561 |
8K | 167575 | 1395.89975 | 5.59674862 |
16K | 335111 | 2791.47463 | 5.597399966 |
PSRAM写入4
- PSRAM页内地址对齐,突发长度1KB
QSPI@30MHz,DMA模式,QSPI和DMA位宽根据写入长度自动切换,2字节对齐的使用16bit,其他使用8bit
- 使用rt_device设备框架
不使用rt_spi驱动框架
底层驱动直接操作寄存器
使用变量记录下当前QSPI-IO状态,在重复读写操作时,避免多次配置IO工作模式(QSPI为半双工,读/写操作前需要配置IO为/输入/输出模式)
同步模式,线程中开启DMA后,while循环轮询中断标志位
GD32F303 QSPI模式无法工作在16bit模式下(标准2线SPI可以工作在16位模式),即使QSPI和DMA配置成16bit位宽,当启用QSPI时,也只能输出低8bit的数据,造成输出/读取的数据丢失高8位。如果不在意数据正确性,经过测试写入速度能达到10.17MB/s,就有点可惜。
换页访问性能测试
存在空闲SRAM缓存页
存在空闲缓存页的情况下,调用page_va2pa
函数,只会导致一次从PSRAM载入数据到SRAM的IO操作。
大小(KB) | CCYCNT | 耗时(us) | 等效速度(MB/s) |
---|---|---|---|
1K | 21333 | 177.0639 | 5.5153 |
SRAM缓存页满
当不存在空闲缓存页的情况下,调用page_va2pa
函数,会先查找最近最少使用的缓存页,将其写出PSRAM,再从PSRAM指定的地址读入新的页面数据,会产生2次IO操作,因此速度也会减半。
大小(KB) | CCYCNT | 耗时(us) | 等效速度(MB/s) |
---|---|---|---|
1K | 42611 | 353.6713 | 2.76121 |
240K | 10230656 | 84,914.4448 | 2.76014 |
应用示例
单片机串口接收缓存
// TODO
总结
外存和内存在本质上并没有太大区别,如果外存连到了系统总线上,只会有速度的区别,如果外存没有连到总线上,那么读写方式和速度都会有区别。
优点:在某些场合,比如一定时间内进行数据采样并保存,使用SRAM空间不够,使用Flash速度太慢,这种外挂的PSRAM可以解决数据临时存储问题。
缺点:和带PSRAM控制器的单片机相比,这种方法无法使用直接地址访问,使用上比较麻烦。