外部RAM中的动态向量分配

时间:2019-03-06 14:38:07

标签: c++ memory-management allocator cortex-m

我目前正在使用GCC在C ++中的STM32F7 cortex-m7微控制器上开发自己的大型项目。我需要在外部SDRAM(16 MB)中存储一个宽数组,其中包含音符结构的向量(每个12字节)。我已经创建了工作的FMC和自定义的ram区域

/* Specify the memory areas */
MEMORY
{
RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 512K
FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 1024K
SDRAM (xrw)     : ORIGIN = 0xC0000000, LENGTH = 16M
}

/* Define output sections */
SECTIONS
{
  /* Section créée pour l'allocation dans la SDRAM externe*/
  .fmc :
  {
    . = ALIGN(8);
    *(.fmc)        
    *(.fmc*)
    . = ALIGN(8);
  } >SDRAM

我的数组声明如下:

std::vector<SequencerNoteEvent> NotesVectorArray[kMaxPpqn] __attribute__((section(".fmc")));

到目前为止,还可以。我在外部RAM中创建了向量数组。如何进行创建矢量元素

NotesVectorArray[position].push_back(note);

动态发生在同一外部RAM中吗?我目前只能使用__attribute__(section)

声明静态数据

我读了很多有关C ++分配器,内存池的内容,但是我不了解分配在矢量代码中发生的位置以及如何替换它……我“只是”需要具有相同的分配系统比平时更常见,但在我记忆中的另一部分是这种精确类型。

似乎有多个堆。分散文件和有效内存分配之间的连接位于何处?

预先感谢

1 个答案:

答案 0 :(得分:0)

我遇到了同样的问题,并通过重新定义operator new得到了可行的解决方案。与使用自定义分配器相比,这样做的好处是所有动态分配将自动位于SDRAM中,因此您可以自由使用std :: function或任何需要堆的STL容器,而不必每次传递一个自定义分配器参数。

我做了以下事情:

  1. 通过在编译标志中添加-nostdlib来删除标准库。我还将此添加到链接器标志。还要从编译器和链接器标志中删除和--specs=...。奖励:您将节省〜60-80k的代码空间!

  2. operator newoperator delete编写自己的替代内容。我创建了一个名为new.cpp的新文件,并添加了以下内容:(取自https://arobenko.gitbooks.io/bare_metal_cpp/content/compiler_output/dyn_mem.html

//new.cpp
#include <cstdlib>
#include <new>

void *operator new(size_t size) noexcept { return malloc(size); }
void operator delete(void *p) noexcept { free(p); }
void *operator new[](size_t size) noexcept { return operator new(size); }
void operator delete[](void *p) noexcept { operator delete(p); }
void *operator new(size_t size, std::nothrow_t) noexcept { return operator new(size); }
void operator delete(void *p, std::nothrow_t) noexcept { operator delete(p); }
void *operator new[](size_t size, std::nothrow_t) noexcept { return operator new(size); }
void operator delete[](void *p, std::nothrow_t) noexcept { operator delete(p); }
  1. 在链接描述文件中定义一个与最低SDRAM地址(堆的开始)相对应的变量。这不是严格必要的,因为您可以在第4步的代码中使用常量(0xC0000000),但这使将SDRAM地址保持在一个位置变得容易了。只需添加一行:PROVIDE( _fmc_start = . );
//linker script

 [snip]

  /* Section créée pour l'allocation dans la SDRAM externe*/
  .fmc :
  {
    . = ALIGN(8);
    PROVIDE( _fmc_start = . );
    *(.fmc)     
    *(.fmc*)
    . = ALIGN(8);
  } >SDRAM

  1. new的新实现将直接调用malloc(),当它在堆中需要一块内存时,它将调用_sbrk()。 因此,您必须定义_sbrk()来跟踪堆指针,并且只需在链接器脚本中刚刚定义的最低SDRAM地址处启动指针即可。 这是我所做的:
//sbrk.cpp, or append this to new.cpp
#include <cstdlib>

extern "C" size_t _sbrk(int incr)
{
    extern char _fmc_start; // Defined by the linker
    static char *heap_end;
    char *prev_heap_end;

    if (heap_end == 0)
        heap_end = &_fmc_start;
    prev_heap_end = heap_end;
    //Optionally can check for out-of-memory error here
    heap_end += incr;
    return (size_t)prev_heap_end;
}
  1. 在这一点上,如果您尝试编译,则可能会遇到许多链接器错误。确切的错误将取决于您的项目使用标准库的哪些部分,除了新增和删除之外。就我而言,我使用的是std::function,编译器抱怨它没有__throw_bad_function_call()。您可能还会看到诸如_init()_fini()__errno之类的错误。基本策略是为每个函数创建自己的空函数存根或变量声明。值得快速搜索其中的每一项,以了解它们的用途,您可能会发现您所包含的项目或库正在使用某些您不了解的功能!创建存根时,您需要正确匹配函数签名,因此也需要搜索Internet。许多存根用于处理错误/异常,因此您可以决定如何在函数主体中处理它(或忽略错误)。

例如,以下是一些有关_init()_fini()函数的(陈旧但必需的)信息:https://tldp.org/HOWTO/Program-Library-HOWTO/miscellaneous.html

有关__dso_handle的一些信息:https://lists.debian.org/debian-gcc/2003/07/msg00057.html

__ cxa_pure_virtual():What is the purpose of __cxa_pure_virtual?

这是我的项目需要工作的:

//syscalls.cpp
extern "C" {
void _init(void) {}
void _fini(void) {}
int __errno;
void *__dso_handle = (void *)&__dso_handle;
}
namespace std
{
//These are error handlers. They could alternatively restart, or log an error
void __throw_bad_function_call() { while (1); }
void __cxa_pure_virtual() { while (1); }
} // namespace std

所有之后,它将最终编译,如果您使用调试器,您将看到向量的地址从0xC0000000开始!