Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

Мусор в ram, размещение стека не по дефлту, разбивка ram

Ср окт 16, 2019 17:02:12

Прошу прошения за сумбурность, просто вопросов много, а тема одна. Проект я делаю в CooCox, так что прошу понять и простить и не предлагать кейлы, иары итп IDE знаю, но хочу именно в CooCox. В общем, думал я насчет того, как лучше отделить переменные в памяти RAM, переменные чисто по смыслу относятся к разным пространствам, одни используются совместно с системными функциями (работа периферией), другие относятся к прикладным и должны быть отделены во-первых для удобства размещения, во-вторых, их хочется использовать для обмена по Модбасу и потому в кучу все мешать не охота. Может это и надуманное решение, но захотелось мне стек разместить вершиной не в конце ram, а где-то со смещением относительно конца ram, так чтоб как раз хватило места на вторую группу переменных.
Тут я столкнулся с некоторыми неудобствами и приколами.
1. Как лучше всего переместить стек? Для CooCox наверное единственным путем это будет настройка адреса вершины стека в ld файле скрипта линкера, так для моего STM32F103RB память выглядит так:
Код:
MEMORY
{
   rom (rx)  : ORIGIN = 0x08000000, LENGTH = 0x00020000
   ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00005000
}

Пробовал разбить память на две группы (правда не совсем понимаю законно ли это, но вроде как работало, обращение к адресам второй группы шло)
Код:
{
   rom (rx)  : ORIGIN = 0x08000000, LENGTH = 0x00020000
   ram0 (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00003000
   ram1 (rwx) : ORIGIN = 0x20003000, LENGTH = 0x00002000
}

соответственно в скрипте я ставил свои сегменты и секции с явным указанием адреса
Код:
.My_Ram_Segment      0x20003000 : {KEEP(*(.My_section))} /*здесь можно было даже дописать после } отсыл к >ram1*/

А все, что по дефолту стояло, я отослал к ram0, так, что даже переделывать особо ничего не пришлось.
Тут возникала некоторая проблемка - бинарник раздумало до 400 мегабайт, причем он отличался от финального (когда я отказался от идеи двух разделов ram) тем, что он был заполнен нулями до конца, а начало один в один совпадало.
Внимание вопрос :) как же быть, можно ли делить ram и как это правильно делать?

2. заметил, что мой CooCox как-то странно ведет себя со стеком и памятью ram как раз где-то на границе, где этот стек реально размещен (это я про регистр sp - указатель на текущий адрес стека).
Короче, sp указывал на адрес в районе 0x20001000, причем ему было все равно, где я указывал положение вершины стека в скрипте линкера, и все равно на деление ram (проверял и когда отказался от идеи делить память и в тестовом проекте вообще без помещения в скрипт линкера своих сегментов с явным указанием адреса размещения).
Вопрос: почему он шел не от вершины, ведь до нее было очень и очень далеко?
Вторая половина вопроса номер два про мусор, ведь насколько я понимаю оперативная память после сброса питания должна быть обнулена? Я сейчас не про программное обнуление, например, как для секции bss, которое прописано в стартап-файле типа startup_stm32f10x_md.c, а про вообще начальное состояние памяти. Так вот, все что до адреса, указанного в стеке (не точно конечно по этому адресу, но близко к нему) было обнулено, а все вплоть до адреса 0x20005000 (причем это как при определении адреса вершины стека равной 0x20003000, так и при 0x20005000) было заполнено каким-то информационным мусором (даже близко не похожим на адреса или какие-то данные из программы).
Что это может быть?
Ну и хотелось бы услышать Ваше мнение по размещению стека не по дефолту, у меня сейчас вот так:
Код:
   .heap (COPY):
   {
      __end__ = .;
      _end = __end__;
      end = __end__;
      *(.heap*)
      __HeapLimit = .;
   } > ram
   
   /* .stack_dummy section doesn't contains any symbols. It is only
   * used for linker to calculate size of stack sections, and assign
   * values to stack symbols later */
   .co_stack (NOLOAD):
   {
      . = ALIGN(8);
      *(.co_stack .co_stack.*)
   } > ram
   
   /* Set stack top to end of ram , and stack limit move down by
   * size of stack_dummy section */
   __StackTop = ORIGIN(ram ) + LENGTH(ram ) - 0x2000;
   __StackLimit = __StackTop - SIZEOF(.co_stack);
   PROVIDE(__stack = __StackTop);

   /* Check if data + heap + stack exceeds ram  limit */
   ASSERT(__StackLimit >= __HeapLimit, "region ram  overflowed with stack")

а в стартапе вот так:
Код:
/*---------- Configuration-----------------------------------------------*/ 
#define STACK_SIZE       0x00000100      /*!< The Stack size suggest using even number     */
__attribute__ ((section(".co_stack")))
unsigned long pulStack[STACK_SIZE];
Последний раз редактировалось auric Чт окт 17, 2019 10:13:41, всего редактировалось 1 раз.

Re: Мусор в ram, размещение стека не по дефлту, разбивка ram

Ср окт 16, 2019 18:02:20

думал я насчет того, как лучше отделить переменные в памяти RAM, переменные чисто по смыслу относятся к разным пространствам, одни используются совместно с системными функциями (работа периферией), другие относятся к прикладным и должны быть отделены во-первых для удобства размещения, во-вторых, их хочется использовать для обмена по Модбасу и потому в кучу все мешать не охота
Любопытно, для чего нужны эти телодвижения. Пишете на "ассемблере с элементами С"? Просто интересно.

Re: Мусор в ram, размещение стека не по дефлту, разбивка ram

Ср окт 16, 2019 18:24:17

Это называется "горе от ума". :) Не надо мешать компилятору и линкеру делать своё дело. Если хочется что-то от чего-то изолировать, то можно в разных единицах трансляции сделать. Мало? В пространства имён загоните.

Добавлено after 5 minutes 36 seconds:
Не, ну правда, зачем переменные в памяти разносить? Они что, покусают друг друга? Или продифундируют?

Re: Мусор в ram, размещение стека не по дефлту, разбивка ram

Ср окт 16, 2019 19:44:56

Еще раз тезисно опишу интересующие меня вопросы, чтобы дискус не отклонялся от темы, тк проблемы, что мне мешали, я и так сам решил, остальное мне больше интересно в общеобразовательных целях.
1. можно ли делить ram и как это правильно делать? Примеры подобные с деление FLASH я видел.
2.а ссылается ли у вас регистр sp на прописанную в скрипте линковщика вершину стека (условно конечно, тк вызов любой функции и другая работа STM естественно должны уменьшить адрес в регистре sp, но каждый зная свою программу примерно представляет отклонение от вершины)
2.б я мельком находил информацию про "вспышку при запуске RAM" и иных таких вот процессах, но ничего конкретного, но мне почему-то казалось, что RAM после включения STM должна содержать нули до обращения к ней, прошу так сказать подтвердить или опровергнуть возможность появления в какой-то области RAM такого "информационного мусора" в виде ненулевых данных и вероятность отсутствия его в другой (звучит как бред, но я это наблюдал).
ну и напоследок интересно про опыт по размещению стека не по дефолту, то есть например со смещением вершины от конца RAM или вовсе в начале, может кто-то вообще разворачивал движение стека в сторону наращивания адреса.

Re: Мусор в ram, размещение стека не по дефлту, разбивка ram

Ср окт 16, 2019 20:47:25

При включении питания в RAM находятся случайные данные. Стартовый код программы принудительно обнуляет глобальные неинициализированные переменные и записывает начальные значения в инициализированные. После чего вызывает конструкторы глобальных объектов, если код на С++, и передаёт управление в main.

Re: Мусор в ram, размещение стека не по дефлту, разбивка ram

Ср окт 16, 2019 20:52:10

auric писал(а):Короче, sp указывал на адрес в районе 0x20001000, причем ему было все равно, где я указывал положение вершины стека в скрипте линкера
Смотрите стартап файл, в котором расположена таблица прерываний и адрес начала стека.

auric писал(а):Вторая половина вопроса номер два про мусор, ведь насколько я понимаю оперативная память после сброса питания должна быть обнулена?
Она обнуляется программно в стартап файле. Аппаратно это не производится.

Re: Мусор в ram, размещение стека не по дефлту, разбивка ram

Ср окт 16, 2019 21:10:04

1. Можно, как раз скриптами линкера. Но это бывает редко нужно - к примеру, если прицепляете внешнюю SRAM в дополнение к внутренней или что-то уж очень хитрое делаете с бутлоадером. Даже RTOS-ы со всякими отдельными стеками под каждую задачу и собственными аллокаторами памяти в куче обычно туда не лезут.

2а. SP инициализируется значением, с которого начинается таблица векторов прерываний. Задается в startup файле.

2б. Без принудительно инициализации в RAM мусор, да. Нулями забивается программно в том же startup файле, дефолтно только BSS.

стек можно разместить и иногда размещают по-разному, но, опять же, непонятно, зачем. По крайней мере от меня смысл ускользает - стандартное расположение и направление вполне логично и разумно. "Развернуть" стек принципиально можно, но это развлечение скорее для ассемблерщиков - ибо в С попросить линкер подвинуть адреса еще можно, но способа сказать компилятору использовать другие команды для доступа к данным в стеке я не знаю.

Re: Мусор в ram, размещение стека не по дефлту, разбивка ram

Чт окт 17, 2019 10:05:30

Прошу прощения, только потом заметил, что не новый пост добавил а редактировал предыдущий, ответ Мурику и arkhnchul будут здесь. Видать движок так работает, чтоб несколько постов одного автора не набивать.

При включении питания в RAM находятся случайные данные. Стартовый код программы принудительно обнуляет глобальные неинициализированные переменные и записывает начальные значения в инициализированные. После чего вызывает конструкторы глобальных объектов, если код на С++, и передаёт управление в main.

если правильно понимаю в стартапе это void Default_Reset_Handler(void), в ней есть секция ASM:
Код:
 /* Zero fill the bss segment.  This is done with inline assembly since this
     will clear the value of pulDest if it is not kept in a register. */
  __asm("  ldr     r0, =_sbss\n"
        "  ldr     r1, =_ebss\n"
        "  mov     r2, #0\n"
        "  .thumb_func\n"
        "zero_loop:\n"
        "    cmp     r0, r1\n"
        "    it      lt\n"
        "    strlt   r2, [r0], #4\n"
        "    blt     zero_loop");


Поставив точку останова до этой секции залез в память и там почему-то уже стояли условно обнуленными (за исключением единичных ячеек, с которыми я разбираться не стал) первая часть RAM (в том числе и относящаяся к bss). Правда за чистоту эксперимента стал сейчас сомневаться, тк питания сбрасывал исключительно до погасания светодиодов, может в памяти "энергии" дольше держатся. За информацию спасибо, а где можно почитать про это явление (про память именно на STMках)? Может поисковый запрос какой-то хитрый по ключевым словам подскажите про это явление, до этого искал, не встречал конкретики именно про STM32.

Добавлено after 10 minutes 17 seconds:
auric писал(а):Короче, sp указывал на адрес в районе 0x20001000, причем ему было все равно, где я указывал положение вершины стека в скрипте линкера
Смотрите стартап файл, в котором расположена таблица прерываний и адрес начала стека.

Спасибо за внимание к вопросу! Что касается стартапа, я брал стандартный CooCox, там все что касается стека:
Код:
/*---------- Configuration-----------------------------------------------*/ 
#define STACK_SIZE       0x00000100      /*!< The Stack size suggest using even number     */
__attribute__ ((section(".co_stack")))
unsigned long pulStack[STACK_SIZE];

Остальное в скрипте и там вот что:
Код:
   /* .stack_dummy section doesn't contains any symbols. It is only
   * used for linker to calculate size of stack sections, and assign
   * values to stack symbols later */
   .co_stack (NOLOAD):
   {
      . = ALIGN(8);
      *(.co_stack .co_stack.*)
   } > ram
   
   /* Set stack top to end of ram , and stack limit move down by
   * size of stack_dummy section */
   __StackTop = ORIGIN(ram ) + LENGTH(ram ) - 0x2000;
   __StackLimit = __StackTop - SIZEOF(.co_stack);
   PROVIDE(__stack = __StackTop);

   /* Check if data + heap + stack exceeds ram  limit */
   ASSERT(__StackLimit >= __HeapLimit, "region ram  overflowed with stack")

Здесь есть еще один ньюанс, вот выдержка из мапа:
Код:
.co_stack       0x20000c18      0x400 load address 0x08003698
                0x20000c18                . = ALIGN (0x8)
 *(.co_stack .co_stack.*)
 .co_stack      0x20000c18      0x400 ..\obj\startup_stm32f10x_md.o
                0x20000c18                pulStack
                0x20003000                __StackTop = ((ORIGIN (ram) + LENGTH (ram)) - 0x2000)
                0x20002c00                __StackLimit = (__StackTop - SIZEOF (.co_stack))
                [!provide]                PROVIDE (__stack, __StackTop)
                0x00000001                ASSERT ((__StackLimit >= __HeapLimit), region ram  overflowed with stack)

Откуда взялось .co_stack 0x20000c18 0x400 ..\obj\startup_stm32f10x_md.o ? у меня вроде как #define STACK_SIZE 0x00000100 /*!< The Stack size suggest using even number */
Ну про остальные сдвиги уже писал (чешу тыковку)...
auric писал(а):Вторая половина вопроса номер два про мусор, ведь насколько я понимаю оперативная память после сброса питания должна быть обнулена?
Она обнуляется программно в стартап файле. Аппаратно это не производится.

Понял, спасибо.

Добавлено after 12 minutes 41 second:
1. Можно, как раз скриптами линкера. Но это бывает редко нужно - к примеру, если прицепляете внешнюю SRAM в дополнение к внутренней или что-то уж очень хитрое делаете с бутлоадером. Даже RTOS-ы со всякими отдельными стеками под каждую задачу и собственными аллокаторами памяти в куче обычно туда не лезут.

Ну вот я встречал, когда память на старших моделях СТМ имеет реально разные группы адресов, так как раз две группы памяти, по кейлу помню, там если без скрипта собственными настройками тоже была возвожность делить, а как это правильно делать, я так и не понял, потому как работало, но криво. Про бинарник уже писал. Хотя сама программа при этом грузилась и работала без ошибок. Теперь чисто для самообразования хотел закрыть этот вопрос, тк как раз со своим самописным хитрым бутлоадером потом хотел что-то замутить в этом проекте.
2а. SP инициализируется значением, с которого начинается таблица векторов прерываний. Задается в startup файле.

Да это в самом самом начале запуска кода, я же говорю про другие этапы, следующие, когда он уже реально указывает на текущую позицию в стеке, она почему-то сильно далеко от своего ориентира - вершины стека, ну не может же по сути простая программа наполнить стек настолько, что он почти к концу подошел, ну мне по крайней мере не верится. К тому же меняя адрес вершины стека я ни на байт не сместил эти значения, проверял с разными настройками, стек стоит как "вкопанный", значит его что-то туда сориентировало.
2б. Без принудительно инициализации в RAM мусор, да. Нулями забивается программно в том же startup файле, дефолтно только BSS.

ок
стек можно разместить и иногда размещают по-разному, но, опять же, непонятно, зачем. По крайней мере от меня смысл ускользает - стандартное расположение и направление вполне логично и разумно. "Развернуть" стек принципиально можно, но это развлечение скорее для ассемблерщиков - ибо в С попросить линкер подвинуть адреса еще можно, но способа сказать компилятору использовать другие команды для доступа к данным в стеке я не знаю.

Тут тоже вопрос чисто для полноты картины понимания, сдвинуть как я понял не проблема (хотя сдвинуть точно на какую-то величину все-таки для меня пока проблема), а встречал где-то упоминания без конкретных примеров, что вроде как можно размещать стек и в начало и даже разворачивать ход его наполнения, вот это бы для себя хотелось так сказать либо опровергнуть либо подтвердить.

Re: Мусор в ram, размещение стека не по дефлту, разбивка ram

Чт окт 17, 2019 11:35:43

auric, ищите таблицу прерываний. Первой ячейкой будет адрес стека.

Re: Мусор в ram, размещение стека не по дефлту, разбивка ram

Чт окт 17, 2019 11:52:59

auric, ищите таблицу прерываний. Первой ячейкой будет адрес стека.

ета?
Код:
(void *)&pulStack[STACK_SIZE],     /*!< The initial stack pointer         */

если добавить это:
Код:
/*---------- Configuration-----------------------------------------------*/
#define STACK_SIZE       0x00000100      /*!< The Stack size suggest using even number     */
__attribute__ ((section(".co_stack")))
unsigned long pulStack[STACK_SIZE];

то получается, что __StackTop вовсе не вершина, хм....осталось разобраться откуда взялось .co_stack 0x20000c18 0x400 ..\obj\startup_stm32f10x_md.o

Re: Мусор в ram, размещение стека не по дефлту, разбивка ram

Чт окт 17, 2019 12:03:19

искал, не встречал конкретики именно про STM32
это общее свойство RAM. Вряд ли где-то заострен вопрос именно по стм-кам :dont_know:
Что касается стартапа, я брал стандартный CooCox
возьмите лучше обычный от ST, там оно имхо более прямолинейно.

Откуда взялось .co_stack 0x20000c18 0x400 ..\obj\startup_stm32f10x_md.o ? у меня вроде как #define STACK_SIZE 0x00000100
ну Семен Семеныч) 0x100 чего?
#define STACK_SIZE 0x00000100 /*!< The Stack size suggest using even number */
__attribute__ ((section(".co_stack")))
unsigned long pulStack[STACK_SIZE];

Остальное в скрипте и там вот что
вообще говоря, линкер не распределяет и не размещает память под конкретно стек - только примерно прикидывает, не пересечется ли он с кучей. В оригинальном стартапе размер стека тоже подставляется линкером, но это не обязательно.

Re: Мусор в ram, размещение стека не по дефлту, разбивка ram

Чт окт 17, 2019 12:12:19

искал, не встречал конкретики именно про STM32
это общее свойство RAM. Вряд ли где-то заострен вопрос именно по стм-кам :dont_know:
Что касается стартапа, я брал стандартный CooCox
возьмите лучше обычный от ST, там оно имхо более прямолинейно.

Откуда взялось .co_stack 0x20000c18 0x400 ..\obj\startup_stm32f10x_md.o ? у меня вроде как #define STACK_SIZE 0x00000100
ну Семен Семеныч) 0x100 чего?
#define STACK_SIZE 0x00000100 /*!< The Stack size suggest using even number */
__attribute__ ((section(".co_stack")))
unsigned long pulStack[STACK_SIZE];

Остальное в скрипте и там вот что
вообще говоря, линкер не распределяет и не размещает память под конкретно стек - только примерно прикидывает, не пересечется ли он с кучей. В оригинальном стартапе размер стека тоже подставляется линкером, но это не обязательно.

Точно long, в смысле даже не не в нем дело, я чет так заморочился с скриптом, что забыл, что забыл что STACK_SIZE может быть отнесен к чему угодно, в голове автоматом байт проставился))) ну все, хоть в этих вопросах пятен не оставил ))) спасибо

Re: Мусор в ram, размещение стека не по дефлту, разбивка ram

Чт окт 17, 2019 12:15:44

ета?
Код:
(void *)&pulStack[STACK_SIZE],     /*!< The initial stack pointer         */
похоже на то. В стартапе ST без coocox-овых придумок все проще

Код:
...
  .section .isr_vector,"a",%progbits
  .type g_pfnVectors, %object
  .size g_pfnVectors, .-g_pfnVectors


g_pfnVectors:

  .word _estack
  .word Reset_Handler
  .word NMI_Handler
  .word HardFault_Handler
...

вот _estack - как раз адрес конца стека, его подставляет линкер, ибо в его скрипте
Код:
...
/* Highest address of the user mode stack */
_estack = 0x20005000;    /* end of RAM */
...
который, кстати, в этом скрипте больше вообще никак не используется :dont_know:
Ответить