在MM32F5微控制器上使用外扩SRAM作为主内存

本文详细介绍了如何在MM32F5微控制器上使用外扩SRAM作为主内存。通过创建bootloader工程,先激活FSMC接口,然后跳转到application工程,使编译器能够自动管理外扩的SRAM。内容涵盖了硬件电路设计、软件初始化流程、bootloader和application工程的创建,以及调试和内存分配的注意事项。通过这种方法,开发者可以在不修改代码库的情况下,充分利用大容量的外扩SRAM。

在MM32F5微控制器上使用外扩SRAM作为主内存

苏勇,2022年8月

引言

MM32F5微控制器基于Arm STAR-MC1微控制器,最高主频可达120MHz,集成了FPU单元和DSP扩展指令集,有不错的算力。但片内集成的128KB的RAM和256KB的FLASH,如果想支持代码量比较大的软件框架,就可能会力不从心,例如,TensorFlow Lite或者基于MicroPython的OpenMV这样的应用就需要更多的内存空间做缓存。但MM32F5微控制器带有FSMC接口和QSPI接口并支持基于QSPI的XIP(eXecute In Place,就地执行),可以分别外扩SRAM和FLASH存储器,这就为扩展存储资源提供了可能。在本文中,将介绍使用FSMC接口外接SRAM扩展内存的过程。在后续的文章中,在后续文章中,还会继续介绍使用QSPI对接qspiflash存储器实现外扩FLASH的过程。

硬件电路

MM32F5微控制器上集成了FSMC(Flexable Static Memory Controller)接口,可以外接并口的SRAM存储器。

在PLUS-F5270开发板上,对应外扩了一个1MB大小的PSRAM存储器作为扩展内存,如图x所示。

在这里插入图片描述

图x FSMC对接SRAM存储器

软件设计

使用FSMC接口外扩的SRAM存储设备之前,必须先激活微控制器的FSMC接口,包括启用对FSMC接口外设的访问开关、配置FSMC接口对应的外部引脚,以及配置FSMC的时钟源和工作模式等操作。基于这样的使用前提,一般情况下使用外扩SRAM,都是在应用程序中激活FSMC硬件外设接口,之后通过在指定地址分配内存,或者访问绝对地址的方式访问新扩展出来的内存,但此时默认的主内存还是片内的SRAM。这种使用扩展SRAM的方式对于规模较小或者绑定具体应用的项目,因为涉及到对代码的改动以及对存储管理的工作量较小并且明确,在一定程度上是可以接受的。但对于移植已有现有的项目,或者是规模较大的框架性软件,开发者通常不愿意(也不建议)深入到代码库中去人为指定每个可能的全局变量的绝对地址,仅将管理的目标地址区间从片内SRAM转移到了外扩的SRAM而已,而希望能够一如既往地让编译器自动管理内存的分配机制。

编译器自动管理内存,就涉及到在芯片上电初始化过程中对编译器运行时环境的初始化过程中对堆栈进行初始化,配置栈顶和栈底、堆底和堆顶指针等,也包括将内存中BSS段的数据清零,将DATA段数据的初值从FLASH搬运到SRAM中等。这些操作的过程,大多被封装在集成开发环境自带的库中(例如Keil的__main函数,经过一系列同编译器相关的准备工作后才跳转到用户的main()函数),不开放给用户修改,而其中使用的和计算出的内存地址,也都是在编译过程中预先定义的。

  • 如果用户强行在链接命令文件中指定默认的主内存空间为外扩存储,那么在芯片启动过程中,预定义的初始化运行时环境的操作,将会在未初始化好FSMC接口等硬件的时候直接访问FSMC扩展出的内存空间,必然出错。可能会提示的错误是hardfault,标记为访问了无效的地址。此时,若是用户在集成开发环境的__main函数之前的SystemInit()函数中先激活FSMC等外扩SRAM相关的硬件也是可行的,但必须要注意,这个过程中,除了CPU中仅有的寄存器外,不能使用任何栈内存,因为此时烧写在默认的中断向量表首位的栈顶地址所指向的空间还是不可访问的。具体来说,就需要用汇编命令完成对所有相关硬件外设的初始化操作,这确实是一个考验人耐心的事情。这里简单看一下SDK中的复位中断服务程序中的启动程序代码,见代码x。
代码x SDK中的启动程序代码
Reset_Handler:
                ldr      r0, =__INITIAL_SP
                msr      psp, r0

                ldr      r0, =__STACK_LIMIT
                msr      msplim, r0
                msr      psplim, r0

                #if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
                ldr      r0, =__STACK_SEAL
                ldr      r1, =0xFEF5EDA5U
                strd     r1,r1,[r0,#0]
                #endif

                bl       SystemInit

                bl       __main

                .fnend
                .size    Reset_Handler, . - Reset_Handler
  • 一些技术高超的工程师可能会想到一些巧妙的做法,能不能先用缺省的片内SRAM支持编译工具链的初始化过程,然后在应用程序中初始化FSMC外设(此时仍使用片内SRAM),然后再试图重建内存管理系统,将芯片系统中内存相关的指针人为重建在外扩SRAM中呢?且不说这是一个极其麻烦的过程,需要把编译工具链中的每个同内存相关的配置变量都翻出来重新人为计算并赋值一遍,一个明显的限制在于,所有将要放在外扩的大SRAM中的数据必须在较小的片内SRAM中必须预先存放一份副本,之后在应用程序运行的过程中转移到外扩SRAM。此时,在编译阶段,编译器会限定整个程序能使用的存储空间不能大于片内SRAM的大小,否则编译器仍然会报错并拒绝生成可执行文件。这就限制了能够直接使用外扩SRAM的空间,同最常用的将外扩SRAM当成辅助存储空间的做法没有实质区别。

使用bootloader初始化硬件环境的思路

为了让用户的工程直接在一个可用的外扩SRAM上建立存储管理系统,一个可行的设计,是使用额外使用一个bootloader工程(或者在芯片内部的电路实现上直接用一段ROM承载bootloader工程中的操作),在使用少量片内SRAM的条件下,通过常规的调用驱动API的方式(而不用汇编语句序列),先准备好使用FSMC的硬件环境,例如配置时钟系统、引脚复用功能、FSMC接口外设等等。在bootloader工程最后的部分,直接跳转到一个约定的、存放用户application工程的地址,开始自行application工程。在application是一个完全独立的工程,不用激活FSMC就可以直接使用外扩的SRAM,因此可以利用编译工具链直接在外扩的SRAM上重建存储管理系统。在application工程中,用户将完全不用干预内存的分配情况,就像之前一样完全交由编译器自行管理;由于不再使用bootloader工程,片内的SRAM可以作为独立的一块可用的存储空间(就像之前在片内SRAM看外扩SRAM一样),继续为应用程序提供存储服务。

进一步分析,探讨把bootloader工程放到ROM中的可能性。程序一旦写入ROM中,就不能有任何改变了。但在配置外扩SRAM的时候,仍需要人为指定外扩SRAM映射的地址范围(开始地址和空间大小),这个设定在不同应用场景中可能会不一样,受成本和功能的权衡,可能有时候会用或大或小的存储器设备,因具体选型不同配置参数也会随之发生变化,因此不适合直接固化在ROM中。除非是对应合封的芯片,需要固定规格的SRAM芯片的晶元已经同微控制器一起被封在芯片内部,倒是不失为一种高集成的SOC解决方案。或者也可以用类似回调的方式,由用户在某种基础的协议下向ROM中的小程序提供外扩SRAM芯片专属的配置,例如在手册里说明在特定的用户可编程的FLASH存储区中存放了关于SRAM的配置信息,也是可行的,但处理过程就多了几个步骤。没有扩展多种SRAM的情况下,实在没必要在芯片里设计这么一块ROM。不过,这种方式在外扩FLASH的时候确实用到了,在后续的文章中将会提及,外扩FLASH的各厂家设计生产的nor FLASH型号芯片在使用上存在差异,在flashless的芯片中必须在ROM中设计程序首先识别外扩spiflash芯片的型号,从而使用对应的配置信息初始化spiflash芯片,到时也将会有一番细致地阐述。

接下来,将详细介绍创建bootloader工程和用户在application工程中开发应用的实现过程和应用要点。

创建bootloader工程

顶级执行流程函数main()

在实现最简单功能的bootloader工程中,缺省使用片内的SRAM作为主内存设备,仅仅需要完成的工作包括:

  • 初始化外扩SRAM的接口FSMC
  • 在NVIC_VTOR寄存器中重定位中断向量表的基地址。后续application工程中的中断向量表将位于自己可执行二进制文件的最开始。application工程执行过程中,将通过NVIC_VTOR寄存器和中断向量表项的偏移值确定实际的中断服务程序入口地址。
  • 为application工程的栈指针寄存器SP(MSP/PSP)赋初值。这个初值即为application工程中的中断向量表的第一个表项中存放的数值,这个值是由application工程的链接器算出来的。

最后跳转到application可执行程序的位置,后续执行application工程。

为了确定从芯片上电到执行application程序这段时间,bootloader确实按照预期正常工作,在本例实现的bootloader工程中使用了一个GPIO控制的小灯指示执行过程中可能出现的错误:

  • 初始化配置指示灯亮
  • 如果顺利执行到跳转到application的前一步,那么就可以熄灭指示灯,顺利进入跳转过程
  • 如果在bootloader执行的过程中遇到任何问题,例如可以增加一个验证外扩SRAM可以工作的检测过程,就在原地等待,此时指示灯将保持常亮

本例创建的bootloader工程中的main()函数,见代码x。

代码x bootloader工程中的main() 函数
/* define the memory range would be used in application firmware. */
#define BOARD_APP_EXEC_ROM_OFFSET (0x4000) /* 16KB. */
#define BOARD_APP_EXEC_ROM_BASE   (0x08000000 + BOARD_APP_EXEC_ROM_OFFSET)
#define BOARD_APP_EXEC_ROM_LIMIT  (0x08000000 + 0x80000) 
#define BOARD_APP_EXEC_RAM_BASE   (0x68000000) /* ext sram base address. */
#define BOARD_APP_EXEC_RAM_LIMIT  (BOARD_APP_EXEC_RAM_BASE + 0x100000)

...

int main(void)
{
   
   
    /* setup the boot clock, pins. */
    BOARD_Init();
    
    /* prepare a led to tell if everything is ok. */
    app_led_init();
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值