Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить

Как пользоваться переменными в ld-cкрипте?

Сб окт 23, 2021 12:05:26

Очень неудобно задавать размер блока флеш-памяти в заголовочных файлах, т.к. он все равно должен быть определен в линкере (для выравнивания начала "эмуляции EEPROM во флеш". Конкретно пользуюсь STM32F072CBT6.
В файле stm32f072B.ld задаю:
Код:
MEMORY
{
    rom   (rx) : ORIGIN = 0x08000000, LENGTH = 128K
    ram  (rwx) : ORIGIN = 0x20000000, LENGTH = 16K
}

PROVIDE(_BLOCKSIZE = 2048);

INCLUDE stm32f01234.ld

А в stm32f01234.ld пишу:
Код:
...
  .myvars :
  {
    . = ALIGN(_BLOCKSIZE);
    __varsstart = ABSOLUTE(.);
    KEEP(*(.myvars));
  } > rom


Однако, если я пытаюсь у себя в исходниках обратиться к этой переменной:
Код:
extern const uint32_t _BLOCKSIZE;

то вместо ожидаемого 2048 получаю какое-то непонятное 2953142315.

Вопрос: как мне получить значение переменной _BLOCKSIZE в своем сишном файле?

Добавлено after 5 minutes:
Пытаюсь вот так сделать:
Код:
extern const uint32_t __varsstart, _BLOCKSIZE;
static const uint32_t *blocksize = (uint32_t*)&_BLOCKSIZE;

*blocksize выдает мне 2953142315 вместо 2048!

Добавлено after 20 minutes 40 seconds:
Разобрался. Надо было вот так:
Код:
static const uint32_t blocksize = (uint32_t)&_BLOCKSIZE;

Re: Как пользоваться переменными в ld-cкрипте?

Сб окт 23, 2021 19:51:03

Eddy_Em писал(а):Разобрался. Надо было вот так:
Код:
static const uint32_t blocksize = (uint32_t)&_BLOCKSIZE;

Благодарю! Мне это пригодится.

Re: Как пользоваться переменными в ld-cкрипте?

Пн окт 25, 2021 14:48:55

А зачем создавать какие-то переменные или константы (которые нужно размещать в памяти и обращаться к ним в runtime) если можно объявить всё необходимое при помощи #define?
Я создавал .h-файл, объявлял в нём необходимые константы #define-ами. Затем этот файл включал (#include) и в командный файл линкёра и в .c-файлы.

Re: Как пользоваться переменными в ld-cкрипте?

Пн окт 25, 2021 15:20:31

А зачем создавать какие-то переменные или константы (которые нужно размещать в памяти и обращаться к ним в runtime) если можно объявить всё необходимое при помощи #define?

Да хотя бы затем, чтобы, внеся изменения в ld-скрипт, не ползать по h-файлам и быть уверенным в правильности адресов и размеров данных во флэше, к которым ты обращаешься из программы. Эти адреса-размеры вычислит линковщик на этапе редактирования связей, а не ты приколотишь их гвоздями-дефайнами.

Re: Как пользоваться переменными в ld-cкрипте?

Вт окт 26, 2021 13:26:14

Да хотя бы затем, чтобы, внеся изменения в ld-скрипт, не ползать по h-файлам и быть уверенным в правильности адресов и размеров данных во флэше, к которым ты обращаешься из программы. Эти адреса-размеры вычислит линковщик на этапе редактирования связей, а не ты приколотишь их гвоздями-дефайнами.
Ну так задайте всё нужное в этом самом .h-файле. Едином (если так нужно) и не ползайте.
И кто говорил о каком-то "приколачивании гвоздями"?
Исходный вопрос был в задании: _BLOCKSIZE = 2048
Это "приколачивание гвоздями" или нет? И почему если в .h-файле это "приколачивание гвоздями", то тогда в скрипте линкёра почему это не оно?

Как раз задание в скрипте линкёра это и есть - необходимость ползания по этим скриптам (если например этих скриптов несколько: "отладка во флешь", "отладка в ОЗУ", "отладка в SDRAM", "релиз во флешь с полной оптимизацией" и т.п.). И задание через переменные/const - это невозможность использования этих заданных размеров в выражениях, вычисляемых на этапе компиляции. В то время как через #define в .h-файле - намного гибче и универсальнее.

PS: Да, кстати, определение собственных символьных имён, начинающихся с '_' - плохой стиль и вроде не рекомендуется многими компиляторами, так как зарезервированные внутренние имена (разных констант) компиляторов обычно начинаются с '_'. И возможен конфликт с ними.

Re: Как пользоваться переменными в ld-cкрипте?

Вт окт 26, 2021 16:20:20

И почему если в .h-файле это "приколачивание гвоздями", то тогда в скрипте линкёра почему это не оно?

Потому что в этом линкер-файле УЖЕ определяется размер флеша и RAM, почему бы туда же не засунуть размер страницы?
А что, можно h-файлы в ld-файлы включать что ли? Тогда почему же так не делают? Может, потому что ld-cкрипту нафиг не нужно знать о 100500 регистрах и функциях?
Задание в скрипте линкера удобней: ты все равно в Makefile указываешь, какой ld-скрипт подключить. А вот заголовочный файл может быть один на целое семейство…
И возможен конфликт с ними.

Это намного менее вероятно, чем конфликт с какой-нибудь одноименной функцией или переменной из моего кода. Где нет подчеркивания.

Re: Как пользоваться переменными в ld-cкрипте?

Вт окт 26, 2021 18:14:15

Добавлю, что как только ты начнёшь размещать во флэш много переменных, то высока вероятность, что ты ошибёшься с адресами и их размерами. Ситуацию усугубляет то, что в STM32 даже у одного МК на кристалле размер сегментов флэш существенно разный. Как только ты решишь передвинуть данные в другую страницу, тебе придётся пересчитывать все адреса или изначально городить макросы, которые будут пересчитывать все адреса и смещения относительно базы. И да, даже сам факт твоего выхода за границы сегмента флэш линкёр легко обнаружит в отличие от человека. И ОЗУ разбито на области, подключенные к разным шинам, устройствам и доменам питания. Тоже будешь задавать адреса переменных через дефайны?

Поэтому лучше описать линкёру что и куда тебе нужно положить, он разложит и установит правильные значения переменных, содержащих адреса и размеры, которые ты сможешь уверенно и безопасно использовать. Поэтому считаю, что нужно заморочиться скриптом линковщика, дабы избавить себя от ненужной арифметики.

Re: Как пользоваться переменными в ld-cкрипте?

Ср окт 27, 2021 03:23:27

А что, можно h-файлы в ld-файлы включать что ли?
Код:
//!Linker command file.

#define LINKER_CMD
#include "..\memmap.h"

-stack 0x00000000  //!<stack size
-heap  0x00000040  //!<heap size

SECTIONS
{
  .exceptions:            load = 0xFFFF0000
  .bootStart:             load = RAM_SDRAM_ARM_RX_BEGIN
  .sysvecs              > RAM_ARM, type = NOINIT
  .stkSVC               > RAM_ARM, type = NOINIT
  .stkABT               > RAM_ARM, type = NOINIT
  .stkUND               > RAM_ARM, type = NOINIT
  .stkSYS               > RAM_ARM, type = NOINIT
  .stkFIQ               > RAM_ARM, type = NOINIT
  .stkIRQ               > RAM_ARM, type = NOINIT
  .stkIRQnest           > RAM_ARM, type = NOINIT
  .bssTrapData          > RAM_ARM, type = NOINIT
  .bss0: { *(.bssDMA) } > RAM_SHARED_ARM_RW, type = NOINIT
  .bss                  > RAM_SDRAM_ARM_RW, type = NOINIT
  .bssStk               > RAM_SDRAM_ARM_RW, type = NOINIT
  .bssInternal          > RAM_SHARED_ARM_RW
  .noinit               > RAM_SDRAM_ARM_RW, type = NOINIT
...
ку?
Тогда почему же так не делают?
То что вы этого не_знаете/не_делаете != никто_не_знает/не_делает. Если такая возможность есть в компиляторах/компоновщиках значит кому-то это нужно. И кто-то даже использует.

Задание в скрипте линкера удобней: ты все равно в Makefile указываешь, какой ld-скрипт подключить.
Разницу между константой заданной через #define и через const int ... понимаете? Похоже нет. Тогда для вас - без разницы. :dont_know:

Добавлено after 4 minutes 1 second:
Добавлю, что как только ты начнёшь размещать во флэш много переменных, то высока вероятность

Вы о чём вообще??? Вроде разговор был о задании константы. Причём тут какие-то "размещения", да ещё переменных? И как вообще во флешь можно размещать переменные?
Что такое в си константа (макрос), определяемая через #define - знаете, нет?

Re: Как пользоваться переменными в ld-cкрипте?

Ср окт 27, 2021 08:04:21

Господа, а вот такой вопрос: требуется определить остаток свободной RAM.
В avr_gcc определяется так
как это сделать в arm_gcc?
mallinfo() не предлагать.

Re: Как пользоваться переменными в ld-cкрипте?

Ср окт 27, 2021 09:48:42

jcxz, для фантастов, решивших, что можно в линкер-скрипт инклюды сишные добавлять, вот выхлоп:
Код:
make
  LD      mk/steppers.elf
/opt/bin/arm-none-eabi-gcc --static -nostartfiles -L../inc/ld -Tstm32f072B.ld -Wl,-Map=mk/steppers.map -Wl,--gc-sections -mthumb -mcpu=cortex-m0 -march=armv6-m -mtune=cortex-m0 -msoft-float mk/adc.o mk/buttons.o mk/can.o mk/commonproto.o mk/custom_buttons.o mk/flash.o mk/hardware.o mk/main.o mk/proto.o mk/steppers.o mk/strfunct.o mk/usb.o mk/usb_lib.o mk/startup.o -Wl,--start-group -lc -lgcc -Wl,--end-group /opt/bin/../lib/gcc/arm-none-eabi/9.3.1/libgcc.a -o mk/steppers.elf
/opt/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld: cannot open linker script file stm32f072xb.h: No such file or directory
collect2: error: ld returned 1 exit status
make: *** [Makefile:128: mk/steppers.elf] Error 1

НЕЛЬЗЯ!
Да и незачем это делать. Потому что переменные линкера - это одно, а дефайны препроцессора gcc — совсем другое!
Команда INCLUDE в ld-скрипте ожидает, что включаться будет кусок линкер-скрипта, а никак не обычный заголовочный файл! Они и лежат-то совершенно в разных местах (сокращу путь к директории до INC): заголовочники — в /INC/Fx/ и /INC/cm, а ld-скрипты — в /INC/ld. Вот так может выглядеть директория /INC/ld:
Код:
stm32f030f.ld  stm32f042x6.ld  stm32f0728.ld  stm32f103x4.ld  stm32f103x8.ld  stm32f103xC.ld  stm32f103xE.ld  stm32f103xG.ld
stm32f01234.ld  stm32f042k.ld  stm32f051x8.ld  stm32f072B.ld  stm32f103x6.ld  stm32f103xB.ld  stm32f103xD.ld  stm32f103xF.ld


Добавлено after 5 minutes 37 seconds:
Dimon456, а как вы можете вообще узнать объем оперативы, если ей навстречу стек растет? Это нужно как-то узнать, где сейчас кончается стек, и из этого адреса вычесть адрес последней объявленной переменной и ее sizeof.

Добавлено after 6 minutes 18 seconds:
Да, полез погуглить сейчас — это невозможно. Можно реализовать ОСРВ, которая сможет сообщать, сколько еще под аллокаторы памяти осталось, но она все равно не жрет всю доступную оперативку: есть еще и для личных нужды место, и для стека.
Я вообще не представляю себе, как можно программно определить предельный размер стека, чтобы наверняка не вбабахаться в него. Можно выхлоп gcc посмотреть, скриптом выдрать оттуда нужные цифры и воткнуть в какой-нибудь заголовочный файл в виде макросов. Т.е. делать make дважды для получения актуальных значений.
Это, наверное, единственный вариант.

Re: Как пользоваться переменными в ld-cкрипте?

Ср окт 27, 2021 11:22:22

Eddy_Em писал(а):Dimon456, а как вы можете вообще узнать объем оперативы, если ей навстречу стек растет?

Адрес последней размещённой переменной тебе линковщик скажет. Зная размер сегмента ОЗУ, можно вычислить размер свободной ОЗУ. Но всё усложняется, когда у тебя несколько сегментов ОЗУ. Тут уже нужно смотреть каждый конкретный случай.

Добавлено after 2 minutes 14 seconds:
Eddy_Em писал(а):Я вообще не представляю себе, как можно программно определить предельный размер стека, чтобы наверняка не вбабахаться в него.

Водяные знаки. Пример реализации есть во FreeRTOS.

Добавлено after 35 minutes 43 seconds:
jcxz писал(а): И как вообще во флешь можно размещать переменные?

Легко! Указываешь секцию, в которой её нужно разместить. Если нужно, можно даже смещение относительно начала секции указать и/или выравнивание. И спокойно работаешь как с обычной переменной. Очень удобно, когда нужно сохранять переменные во флэш. Все адреса и размеры вычисляет при таком подходе линковщик. Кстати, размеры областей памяти для хранения переменных, как известно, зависят от упаковки и выравнивания и могут меняться при смене МК.

Re: Как пользоваться переменными в ld-cкрипте?

Ср окт 27, 2021 12:23:51

tonyk, упор был сделан на слове "переменная": во флеше же только константы! Зато их можно сотнями писать. У меня вот 100кБ свободно, а структура конфигурации меньше 50Б занимает. Даже если до 64 округлить, в одну страницу 32 штуки влезет, а суммарно - 160... И лишь на 161-й раз нужно будет стирать флеш и начинать с начала.
Что до свободной оперативки, как я понял, нужно в рантайме узнавать, сколько осталось. А это возможно лишь с точностью до сотни-другой байт.

Re: Как пользоваться переменными в ld-cкрипте?

Ср окт 27, 2021 13:19:08

Не знаю, как в GCC, но в IAR я делал так:
(тут для куска RAM [CCM], но не суть)
.icf файл линкера:
Код:
define symbol __region_CCM_start__ = 0x10000000;
define symbol __region_CCM_end__   = 0x1000FFFF;
define region CCM_region   = mem:[from __region_CCM_start__   to __region_CCM_end__];
place in CCM_region  { section .ccm };


Далее, в одном заголовочнике:
Код:
#define CCM_RAM _Pragma("location=\".ccm\"")


И уже в исходниках, где нужно объявить переменную/массив/структуру в этой области памяти:
Код:

CCM_RAM int32_t chData
[2][AMP_BUFFERS_SIZE/2]; // Многомерный массив
CCM_RAM int32_t leftVol = 1; // Переменная


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

Re: Как пользоваться переменными в ld-cкрипте?

Ср окт 27, 2021 14:07:01

Eddy_Em писал(а):во флеше же только константы!

Ну-у, константы- это вещь условная. Всего лишь замени FLASH на FRAM.
структура конфигурации меньше 50Б занимает

У меня 50К. И большая часть пишется разом. Кстати, в STM32 размер страницы флэш имеет размер от 1К до 256К. Поэтому-то вычисление всех адресов и размеров я отдаю на откуп линкёру.
Что до свободной оперативки, как я понял, нужно в рантайме узнавать, сколько осталось.

Тут подвох в том, что что ОЗУ может быть разбита на несколько областей, подключенных к разным шинам. И переменные ты будешь размещать в разных сегментах. Тогда может получится, что в каждом сегменте осталось по чуть-чуть, а в сумме вполне себе ничего.
А это возможно лишь с точностью до сотни-другой байт.

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

Re: Как пользоваться переменными в ld-cкрипте?

Ср окт 27, 2021 14:19:11

Даже точнее, до байта.

А, дошло!
Ведь небольшая переменная скорей всего будет определена на стеке → адрес переменной минус ее sizeof и даст нам конец оперативки. Остается теперь придумать, как определить, где лежит последний элемент самой оперативки...

Re: Как пользоваться переменными в ld-cкрипте?

Чт окт 28, 2021 14:30:47

jcxz, для фантастов, решивших, что можно в линкер-скрипт инклюды сишные добавлять, вот выхлоп:
Я вам привёл фрагмент реального командного файла линкёра.

Добавлено after 4 minutes 49 seconds:
Легко! Указываешь секцию, в которой её нужно разместить. Если нужно, можно даже смещение относительно начала секции указать и/или выравнивание. И спокойно работаешь как с обычной переменной. Очень удобно, когда нужно сохранять переменные во флэш. Все адреса и размеры вычисляет при таком подходе линковщик.
А теперь покажите как такое можно сделать например на любом XMC4xxx? Или на любом LPC17xx?
А то мануалы их говорят, что это невозможно. :cry:

Добавлено after 15 minutes 16 seconds:
как это сделать в arm_gcc?
Сначала нужно уточнить: Что имеется в виду под "остатком свободной RAM"?
Если речь про объём какого-то региона ОЗУ, не занятый компоновщиком для выходных секций программы (кода/данных), то:
1) Разместить любую переменную/константу в именованной секции (назовём её ".codetailExt").
2) В командном файле компоновщика задать размещение этой секции в конце того региона ОЗУ, незанятый размер которого хотите вычислить.
3) Вычислить незанятый объём как = конец_региона - конец_секции_.codetailExt.
Пример для IAR (фрагмет командного файла компоновщика):
Код:
define region RAM_regionC    = mem:[from 0x20000100 size 0x1FF00]; //DSRAM1 (ro-data)
define block IMAGE_HEAD with fixed order {section .intvec, section .checksum, section .codehead, section .intvecTail, section .codeSignature};
place in RAM_regionC {
  first block IMAGE_HEAD_EXT,
  section .rodata,
  section .text object bsp.o,
  section .text object http.o,
  section .text object json.o,
  section .text object japi.o,
  section .text object dte.o,
  last section .codetailExt
};

Незанятый размер региона RAM_regionC будет = 0x20020000 - (u32)__section_end(".codetailExt") (это уже в си-коде).

Re: Как пользоваться переменными в ld-cкрипте?

Чт окт 28, 2021 16:07:33

jcxz, если вычислить таким способом размер, то он будет включать и область, занятую стеком!

Я вам привёл фрагмент реального командного файла линкёра.

ОК, доказываю. Вот файл линкера:
Код:
/* Linker script for STM32F030f4, 16K flash, 4K RAM. */

#include "include.h"

/* Define memory regions. */
MEMORY
{
    rom   (rx) : ORIGIN = 0x08000000, LENGTH = 16K
    ram  (rwx) : ORIGIN = 0x20000000, LENGTH = 4K
}

PROVIDE(_BLOCKSIZE = 1024);

/* Include the common ld script. */
INCLUDE stm32f01234.ld

Вот - включаемый:
Код:
#define FIRSTMACRO      (1)
#define SECOND
extern int testval;
void testfunc();

А вот - выхлоп линкера:
Код:
make
  LD      mk/chiller.elf
/opt/bin/arm-none-eabi-gcc --static -nostartfiles -L../inc/ld -Tstm32f030f.ld -Wl,-Map=mk/chiller.map -Wl,--gc-sections -mthumb -mcpu=cortex-m0 -march=armv6-m -mtune=cortex-m0 -msoft-float mk/adc.o mk/hardware.o mk/main.o mk/mainloop.o mk/protocol.o mk/usart.o mk/startup.o -Wl,--start-group -lc -lgcc -Wl,--end-group /opt/bin/../lib/gcc/arm-none-eabi/9.3.1/libgcc.a -o mk/chiller.elf
/opt/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld:stm32f030f.ld:3: ignoring invalid character `#' in expression
/opt/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld:stm32f030f.ld:3: syntax error
collect2: error: ld returned 1 exit status
make: *** [Makefile:118: mk/chiller.elf] Error 1

Так что, не надо тут байки травить! Нельзя в линкер-скрипт обычный сишный заголовочный файл включить!

Re: Как пользоваться переменными в ld-cкрипте?

Чт окт 28, 2021 16:34:24

jcxz, если вычислить таким способом размер, то он будет включать и область, занятую стеком!
Я не знаю что у вас там за бардак в исходниках, что стек лезет в неиспользуемую область. В нормально построенной программе, стек компонуется в секцию .bss или в .noinit или в специальную секцию стека.

Так что, не надо тут байки травить! Нельзя в линкер-скрипт обычный сишный заголовочный файл включить!
Тут так же как со стеком выше: кто-то умеет, кто-то - нет. Не каждому дано. :dont_know:

Re: Как пользоваться переменными в ld-cкрипте?

Чт окт 28, 2021 16:43:03

jcxz писал(а):Или на любом LPC17xx? А то мануалы их говорят, что это невозможно.

UM10360, страница 620 мне говорит, что на LPC17xx всё, о чём я говорил выше применительно к СТМ32, точно также работает для LPC17хх. Разница только в размерах и адресах сегментов. Поэтому читаем мануал на линкёр.
Вот пример описания памяти для STM32F411RE. Обращаю внимание, что некоторые сегменты объединены в одну область, а некоторые используются по отдельности.


А вот как в эти сегменты размещаются данные:

Если для программирования LPC17xx используется GCC, то достаточно изменить адреса сегментов в соответствии с UM10360, p.620. И, разумеется, прочитать мануал на GNUсный линкёр .

Re: Как пользоваться переменными в ld-cкрипте?

Чт окт 28, 2021 16:50:28

Не каждому дано. :dont_know:

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

А стек и оперативка растут друг навстречу другу. И обычно размер стека не фиксируют: если слишком мало дашь, может переполниться, если слишком много - часть оперативки попросту не будет использоваться.

Поэтому если оперативка на секции не разбита (что правильно), то никак не узнать размер. Разве что какие-нибудь хитрые МК, у которых несколько типов оперативки - там уже точно можно узнать для всех типов, которые без стека.
Ответить