本文已参与「新人创作礼」活动,一起开启掘金创作之路。
移植micropython的最小工程到lpc5500微控制器(2) - 调整内存分配
调整linker文件
调整linker文件实际上是确定对芯片存储空间的使用安排,特别是对数据区、堆和栈空间的安排。
linker文件还是使用SDK的原版“LPC55S69_cm33_core0_flash.ld”文件作为模板,但是我考虑要调整其中的内存分块,并且要精简掉不必要的分区,所以单独复制出一份放到“lpc5500”项目的根目录中,便于自己随意改动。
先看一眼lpc55s69芯片的内存资源:

其中从0x2000_0000开始的常规SRAM就有256KB,16KB的PowerQUAD专有内存就不去碰了,从0x1400_0000开始的32KB内存我一开始没好意思用,后面考虑把data(和bss)部分和工程自带的堆放进去,然后把256KB的常规SRAM空间全部预留给栈空间和给micropython的gc内存管理器使用。
这里有个知识点,关于micropython工程的内存使用情况:
- micropython管辖的内存主要是整个系统的栈空间(用来动态编译的放字节码)和gc管理的私有堆空间(存放micropython运行过程中创建的对象)。micropython在运行的初始阶段就要通过代码指定其管辖的栈内存区域和私有堆空间。
- micropython管辖之外的系统层面上使用的数据,例如一些进入micropython之前的硬件初始化函数,系统库函数等使用的数据,一部分放在系统栈空间(micropython会让出这部分栈空间),另一部分放在系统堆空间中(很少)。
- micropython对接底层驱动时,在编写C代码过程中,例如驱动程序,创建的全局变量将会放在系统的data段(或者bss段)中,局部变量同micropython常规的函数调用一样,存放在系统栈中。
按照这个原则,在linker文件中进行调整,我的改动包括如下:
将常规SRAM分成两块
/* Specify the memory areas */
MEMORY
{
m_interrupts (RX) : ORIGIN = 0x00000000, LENGTH = 0x00000140
m_text (RX) : ORIGIN = 0x00000140, LENGTH = 0x0009D000
m_data (RW) : ORIGIN = 0x20000000, LENGTH = 0x00020000
m_data2 (RW) : ORIGIN = 0x20020000, LENGTH = 0x00020000
m_usb_sram (RW) : ORIGIN = 0x40100000, LENGTH = 0x00004000
}
我将常规SRAM分成了m_data和m_data2两块,都是128KB大小。m_data打算用来存放data(bss)和栈,m_data2整块给GC作为micropython的私有堆空间。
原始版本中的用于多核通信的共享内存区域和预留存放双核系统中另一个核的text段被合并到现有区块中,因为micropython中暂时用不到,所以就精简掉了,后面对应区块的定义也可以删掉。至于m_text区域为啥没有填满整个640KB的空间,而是只用了大约630KB的空间,也是参考了手册中关于内存分布情况的说明(见上图),最后这10 KB的空间是预留的,这个可能存放了一些同芯片硬件配置相关的一些数据,总之不要碰它就好。
增大栈的大小
HEAP_SIZE = DEFINED(__heap_size__) ? __heap_size__ : 0x0400;
STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x1C000; /* 112 KB. */
这里指定了栈空间为112KB,放在m_data中。系统堆保留原来的512字节,在用户程序里肯定用不到,但说不准系统库会偷偷摸摸地用一点,所以多少保留一点,也放在m_data中。
/* Uninitialized data section */
.bss :
{
/* This is used by the startup in order to initialize the .bss section */
. = ALIGN(4);
__START_BSS = .;
__bss_start__ = .;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
__END_BSS = .;
} > m_data
.heap :
{
. = ALIGN(8);
__end__ = .;
PROVIDE(end = .);
__HeapBase = .;
. += HEAP_SIZE;
__HeapLimit = .;
__heap_limit = .; /* Add for _sbrk */
} > m_data
.stack :
{
. = ALIGN(8);
. += STACK_SIZE;
} > m_data
添加micropython需要引用的变量
这部分代码是参考imxrt的相关代码添加的,具体就是“ports/mimxrt/boards/MIMXRT1062.ld”文件中的内容。这些变量从linker过程中导出,然后在micropython初始化过程中指定管辖内存范围时传参使用。
/* 112kiB stack. */
/*__stack_size__ = 0x1C000;*/
_estack = __StackTop;
_sstack = __StackLimit;
/* Use 128KB for GC heap. */
_gc_heap_start = ORIGIN(m_data2);
_gc_heap_end = ORIGIN(m_data2) + LENGTH(m_data2);
这里可以看到,gc管辖的私有堆使用了整个m_data2,而整个系统栈的开始和结束位置也被记录在_estack和_sstack变量中。
这两组变量在main()函数开始的时候会被使用到:
int main(void)
{
...
mp_stack_set_top(&_estack);
mp_stack_set_limit(&_estack - &_sstack - 1024);
gc_init(&_gc_heap_start, &_gc_heap_end);
...
}
其中设置micropython栈尾的时候又减去了1K字节的长度,此处猜想应该是防止临时的栈溢出触发系统崩溃的防御措施,把系统栈同别的内存隔远一点更加安全。
(未完待续。。。)