Размещение кучи FreeRTOS в разделе CCMRAM для STM32

При разработке одного девайса на базе STM32F407 столкнулся с проблемой нехватки оперативной памяти. Назначение самого девайса не принципиально, но важно, что изначальный код писался для десктопной системы и его нужно было просто портировать на микроконтроллер под управлением FreeRTOS. А так как исходный код был написан на С++ и вопрос об экономии ОЗУ даже не стоял, то и вылезла соответствующая проблема.

Заниматься оптимизацией кода, одновременно добавляя себе проблем с поиском новых ошибок, очень не хотелось. Поэтому своевременно вспомнилось, что данная версия микроконтроллера имеет на борту дополнительный сегмент ОЗУ размером 64К (CCM SRAM), который сейчас никак не был задействован. Эврика — вот оно, решение!

Но к сожалению, все оказалось не так просто.


Результаты поиска готового решения


В официальной документации на CCMRAM приводятся примеры для размещения в ней исполняемого кода, стека или отдельных переменных.

Поиск по форумам выдал несколько ссылок на разные способы использования CCMRAM, но к сожалению, все они являлись разными вариациями тех способов, которые были описаны в официальной документации. И все они требовали перелопачивать исходный код для добавления атрибутов при объявлении каждой функции или переменной.

Для GCC, в моем случае, примерно вот так:

__attribute__((section(".ccmram")));

В добавок, некоторые переменные имеют значение по умолчанию, что требует модифицировать загрузчик таким образом, что бы при старте прошивки такие переменные копировались в отдельную область для инициализированных или обнуленных переменных.

Ну и финальной сложностью были ограничения самой CCMRAM. Она висит на отдельной шине к которой у DMA нет доступа, а прямой доступ к памяти планировалось использовать очень активно.

Другими словами, решая одну проблему, можно было нечаянно добавить ворох других, и зарыться в отладку для поиска привнесенных багов.

К счастью, удалось найти простое решение со стороны FreeRTOS.


Размер кучи был меньше размера сегмента CCM RAM и решение напрашивалось само-собой — переместить кучу в данный раздел.

И это удалось сделать минимальными правками кода.

  1. В ld файле (в моем случае STM32F407VGTX_FLASH.ld) добавляется новая секция:

    .ccmram :
     {
       . = ALIGN(8);
       . = . + _Min_Heap_Size;
       . = ALIGN(8);
     } >CCMRAM
  2. В секции « ._user_heap_stack » комментируется или удаляется строка

    /*    . = . + _Min_Heap_Size; */

    Строки с _Min_Heap_Size требуются для выдачи предупреждений линковщиком в случае недостатка размера ОЗУ.
  3. В теле программы добавляется единственная переменная

    uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] __attribute__((section(".ccmram"))) = {0};
  4. А при сборке проекта добавляется препроцессорное определение

    configAPPLICATION_ALLOCATED_HEAP=1

В результате — куча FreeRTOS в CCM SRAM с минимальным количеством правок в исходном коде!
@rsashka
12.03.2021 15:00 UTC
Первоисточник

Комментарии

@Sun-ami
12.03.2021 11:52 UTC
+1
IAR позволяет сделать проще — определить регион, состоящий из нескольких диапазонов. То есть можно сделать отдельный регион для объектов, использующих DMA, и отдельный регион для всего остального, частично размещённый в CCM
@rsashka
12.03.2021 11:58 UTC
0
Безусловно можно. Но все же хотелось сделать все с минимальными переделками, чтобы не словить ошибок из-за неправильного аллоцирования объектов.

Да и использую я gcc под STM32CubeIDE.
@ramzes2
12.03.2021 13:29 UTC
+2
В FreeRTOS можно даже выделить место под кучу на нескольких участках памяти:
www.freertos.org/a00111.html#heap_5
@rsashka
12.03.2021 13:42 UTC
0
Спасибо, не знал.
Хотя, если честно, то я и не разбирался настолько подробно (сам сейчас использую heap_4).
12.03.2021 15:25 UTC
+1
heap_4 — классика, когда нужно получать и освобождать память. По сути 5-я — то же самое, но добавляется возможность занять несколько разных адресных пространств.
Собственно, сам использую похожий трюк у себя одном в проекте. Хотя началось всё как эксперимент, но работает. Разве что там не куча, а переменные, к которым не требуется доступ по DMA.
12.03.2021 15:34 UTC
0
Я понял (уже почитал описание). Хотя мне все равно какой вариант кучи использовать, т.к. выделенную память я не освобождаю (только один раз выделил на старте и больше не трогаю).

Просто у меня задача была немного другая. Не увеличить размер кучи, а перенести её в другой раздел с минимальными изменениями в исходном коде.

Ну а Хабр решил использовать как шпаргалку для памяти :-). Может еще кому пригодится.
@Alex-111
12.03.2021 18:39 UTC
+2

А как решена заявленная проблема с DMA? Теперь выделенную в RTOS память нельзя передать функциям, которые используют DMA или они должны проверять какую память им передали и перебуферизовывать ее?

@rsashka
13.03.2021 04:45 UTC
0
Изначально буфера для DMA распределялись статически, а не в куче, поэтому проблем не возникло.
@mctMaks
15.03.2021 13:33 UTC
+1
у себя делал так (проект под Segger Embedded Studio), когда хотел «выгнать» РТОС в отдельный регион:
__attribute__((section(".bss2"))) static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];

непосредственно в файле heap_4.h для контроллера stm32l471.

Там ОЗУ на 2 сегмента поделена по диапазону адресов:
<root name="STM32L471RG">
  <MemorySegment name="FLASH" start="0x08000000" size="0x00100000" access="ReadOnly" />
  <MemorySegment name="RAM" start="0x20000000" size="0x00018000" access="Read/Write" />
  <MemorySegment name="RAM2" start="0x10000000" size="0x00008000" access="Read/Write" />
</root>


Особых проблем с работой РТОС не заметил