Перейти к содержанию
    

GCC C++ для AVR, общие вопросы

Всем привет!

 

Подскажите, пожалуйста, каков механизм вызова функций в Си++?

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

Можно как-нибудь "автоматизировать" процесс подсчета? Видимо, читать значение указателя стека в определенные моменты времени?

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

 

Вот спасибо, попробуем :)

Это ключ компилятора или линкера?

 

...

The qualifier static means that the function frame size is purely static. It usually means that all local variables have a static size. In this case, the second field is a reliable measure of the function stack utilization.

...

The qualifier dynamic means that the function frame size is not static. It happens mainly when some local variables have a dynamic size.

...

 

Что значит "все локальные переменные имеют статический размер"? Объявлены как "static" внутри функции или это просто не динамически выделяемая память (из кучи)?

Иными словами, если мы не используем динамическую память (кучу), то компилятор с выше озвученным флагом покажет нам сколько от стека отъедает каждая функция?

 

По поводу "bounded" не понял.

 

A unit compiled with -Wstack-usage will issue a warning for each subprogram whose stack usage might be larger than the specified amount of bytes.

 

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

Изменено пользователем spongebob

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот спасибо, попробуем :)

Это ключ компилятора или линкера?

Компилятора.

Линкер только связывает, ему про стек ничего не известно.

Что значит "все локальные переменные имеют статический размер"? Объявлены как "static" внутри функции или это просто не динамически выделяемая память (из кучи)?

Иными словами, если мы не используем динамическую память (кучу), то компилятор с выше озвученным флагом покажет нам сколько от стека отъедает каждая функция?

значит, что размер однозначно определен на этапе компиляции.

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

than the specified amount of bytes

чем определенное количество байтов.

Где определенное - надо смотреть конкретнее в документации на это сообщение.

 

-Wstack-usage=len

Warn if the stack usage of a function might be larger than len bytes. The

computation done to determine the stack usage is conservative. Any space

allocated via alloca, variable-length arrays, or related constructs is included

by the compiler when determining whether or not to issue a warning.

The message is in keeping with the output of ‘-fstack-usage’.

• If the stack usage is fully static but exceeds the specified amount, it’s:

warning: stack usage is 1120 bytes

• If the stack usage is (partly) dynamic but bounded, it’s:

warning: stack usage might be 1648 bytes

• If the stack usage is (partly) dynamic and not bounded, it’s:

warning: stack usage might be unbounded

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

avr-g++.exe -mmcu=atmega1280 -Wall -ffunction-sections -fdata-sections -fstack-usage -DF_CPU=8000000UL -g -c Src\tst.cpp -o Obj\Debug\Src\tst.o

cc1plus.exe: error: unrecognized command line option "-fstack-usage"

 

Не понимает такой опции :(

Может, староват компилер?

 

C:\WinAVR\bin>avr-g++ -v

Using built-in specs.

Target: avr

Configured with: ../gcc-4.2.2/configure --enable-win32-registry=WinAVR-20071221

--with-gmp=/usr/local --with-mpfr=/usr/local --prefix=/c/WinAVR --target=avr --e

nable-languages=c,c++,objc,ada --with-dwarf2 --disable-nls --enable-doc --disabl

e-shared --disable-libada

Thread model: single

gcc version 4.2.2 (WinAVR 20071221)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не понимает такой опции :(

Может, староват компилер?

 

gcc version 4.2.2 (WinAVR 20071221)

Да уж, много воды за 5 лет утекло...

 

Вот еще интересная ссылочка: http://www.avrfreaks.net/index.php?name=PN...pic&t=52249

Да, но это только в рантайме работает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Касательно вот этого еще вопросы есть: http://www.nongnu.org/avr-libc/user-manual/malloc.html

 

Динамическая память не нужна (куча).

На плате установлена внешняя ОЗУ (32 кБ). Ее хотелось бы использовать по-максимуму :)

Если не указывать линкеру __heap_end и __heap_star, он под кучу память выделит или нет? Если все-таки выделит, то куда поместит? Сразу за bss?

Стек, как я понимаю, всегда лучше размещать во внутренней памяти (скорость выше). Ну а как разместить стек во внешней памяти?

Кстати, в какой секции лучше разместить инициализацию внешней памяти (включение и конфигурирование)? В третьей?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Даже если указывать __heap_start и __heap_end, но не вызывать malloc, память реально не будет использоваться.

Если не указать, но вызвать malloc — линкер выругается на их отсутствие.

 

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

Смотрите еще в теме про mega1280 и внешнюю память

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Фигня какая-то... поставил 2010 WinAVR, он тоже опции -fstack-usage не знает :(

Объясните, пожалуйста, я, может, не понимаю чего? :)

Может, для AVRов это не работает?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вопросы по секциям.

Читаю http://www.nongnu.org/avr-libc/user-manual, но там как-то очень скупо все описано... прошу помочь разобраться.

 

Записи -Wl,--section-start=.data=0x802000 и -Wl,--section-start,.data=0x802000 эквивалентны?

Т. е., синтаксис указания начала расположения секций имеет несколько вариантов?

 

Как на самом деле выглядят секции .data, .bss, .noinit?

 

Как я понимаю, данные из .data копируются в определенный момент времени в ОЗУ, инициализируя переменные. Когда копируются и кем? Нашел нечто в описании секции .init4, но там сказано про контроллеры с ОЗУ > 64 кБ ПЗУ (а это почему?).

Для секции .bss аналогичной операцией является заполнение нулями. Когда?

А для чего нужна секция .noinit? Ведь есть же .data и .bss.

 

Правильно ли я понимаю, что все глобальные или статические переменные, которые инициализированы программистом попадают в .data, а неинициализированные глобальные и статические переменные попадают в .bss и инициализируются нулями (по стандарту Си)?

А что происходит с локальными инициализированными переменными? Они же создаются на стеке, кто их и когда инициализирует?

 

Зачем нужны .finiN, если мы в main() обычно делаем while(1) {};?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Записи -Wl,--section-start=.data=0x802000 и -Wl,--section-start,.data=0x802000 эквивалентны?

Т. е., синтаксис указания начала расположения секций имеет несколько вариантов?

Да, эквивалентны. Это синтаксис ключа -Wl передачи параметров программой-драйвером (avr-)gcc программе-линкеру (avr-)ld:

-Wl,option

Pass option as an option to the linker. If option contains commas, it is split into multiple options at the commas. You can use this syntax to pass an argument to the option. For example, `-Wl,-Map,output.map' passes `-Map output.map' to the linker. When using the GNU linker, you can also get the same effect with `-Wl,-Map=output.map'.

Как на самом деле выглядят секции .data, .bss, .noinit?
Как области памяти.

Как я понимаю, данные из .data копируются в определенный момент времени в ОЗУ, инициализируя переменные. Когда копируются и кем?
Да, верно понимаете. Копируются библиотечным стартап-кодом, который вызывается по вектору сброса и выполняется перед запуском main(). Этот код является частью avr-libc. Аналогично там же обнуляется секция .bss

А для чего нужна секция .noinit? Ведь есть же .data и .bss.
Для переменных, которые не нужно обнулять или инициализировать. Например, чтобы восстановить по их содержимому состояние программы после аварийного сброса по собаке или это может быть внешняя ОЗУ с батарейкой.

Правильно ли я понимаю, что все глобальные или статические переменные, которые инициализированы программистом попадают в .data, а неинициализированные глобальные и статические переменные попадают в .bss и инициализируются нулями (по стандарту Си)?
Да, правильно. Современные компиляторы могут помещать явно инициализированные нулем глобальные переменные в .bss вместо .data, чтобы не хранить их начальные значения и сэкономить таким образом память кода.

 

А что происходит с локальными инициализированными переменными? Они же создаются на стеке, кто их и когда инициализирует?
Исполняемый код функции во время ее исполнения, как правило непосредственно перед использованием локальной переменной. А вот место в стеке резервируется сразу под все локальные переменные на входе в функцию, чтобы не дергать указатель стека на каждую переменную. Естественно, компилятор использует одно и то же место на стеке под разные локальные переменные у которых не пересекается время жизни.

 

Зачем нужны .finiN, если мы в main() обычно делаем while(1) {};?
На случай, если вы захотите выйти из main(). Если вы из main() не выходите - можете удалить их из линкерного скрипта и сэкономить пару байтов кода. А можете подменить библиотечный exit() выключая в нем питание и тогда сможете выключать прибор выходя из main(). Перед выключением будут штатно и в нужном порядке вызываться деструкторы глобальных объектов. Мало ли, может быть вам это нужно. Например, закрыть файлы и отмонтировать файловую систему, предварительно сделав запись об отключении в деструкторе объекта-системного журнала событий. Программы бывают разные.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, верно понимаете. Копируются библиотечным стартап-кодом, который вызывается по вектору сброса и выполняется перед запуском main(). Этот код является частью avr-libc. Аналогично там же обнуляется секция .bss

 

По поводу вызова библиотечного стартап-кода. Как я понимаю, все что содержится в секциях .initN до вызова main() - это и есть стартап? :)

Т. е., он "размазывается" по секциям .init0 ... .init9 с дальнейшей возможностью коррекции каждой из секций программистом (часть секций менять нельзя, как я понял)?

Кстати, когда мы объявляем функцию способом типа void my_init_portb (void) __attribute__ ((naked)) __attribute__ ((section (".init3"))); то это замещает все что было помещено стартапом в эти секции? А если несколько аналогичных выше указанным объявлениям, то все что объявлено разместится в секции .init3?

 

На случай, если вы захотите выйти из main(). Если вы из main() не выходите - можете удалить их из линкерного скрипта и сэкономить пару байтов кода. А можете подменить библиотечный exit() выключая в нем питание и тогда сможете выключать прибор выходя из main(). Перед выключением будут штатно и в нужном порядке вызываться деструкторы глобальных объектов. Мало ли, может быть вам это нужно. Например, закрыть файлы и отмонтировать файловую систему, предварительно сделав запись об отключении в деструкторе объекта-системного журнала событий. Программы бывают разные.

 

Понятно, достаточно удобная вещь получается, если писать на Си++ :)

Кстати, можете сказать что-нибудь по поводу избыточности/неизбыточности Си++ по отношению к Си и насколько оптимальным получается код для AVR, ARM?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Стартап является универсальным для всех контроллеров, поддерживаемых библиотекой и вся необходимая ему информация берется из macros.inc и каких-то других файлов библиотеки?

 

Изменено пользователем spongebob

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

По поводу вызова библиотечного стартап-кода. Как я понимаю, все что содержится в секциях .initN до вызова main() - это и есть стартап? :)
Да.

Т. е., он "размазывается" по секциям .init0 ... .init9 с дальнейшей возможностью коррекции каждой из секций программистом (часть секций менять нельзя, как я понял)?
Можно и добавлять и замещать.

Кстати, когда мы объявляем функцию способом типа void my_init_portb (void) __attribute__ ((naked)) __attribute__ ((section (".init3"))); то это замещает все что было помещено стартапом в эти секции? А если несколько аналогичных выше указанным объявлениям, то все что объявлено разместится в секции .init3?
Если имя не совпадает с библиотечными, то добавляется. Если имя совпадает с глобальной библиотечной меткой - то замещает:

extern "C" __attribute__((section(".init4"),__naked__)) void __do_clear_bss()
{
    asm volatile ("clr R2");
}
extern "C" __attribute__((section(".init4"),__naked__)) void __abyrvalg()
{
    asm volatile ("clr R3");
}


000004bc <__do_clear_bss>:
     4bc:    22 24           eor    r2, r2

000004be <__abyrvalg>:
     4be:    33 24           eor    r3, r3

000004c0 <__do_copy_data>:
     4c0:    11 e0           ldi    r17, 0x01; 1
     4c2:    a0 e0           ldi    r26, 0x00; 0
     4c4:    b1 e0           ldi    r27, 0x01; 1
     4c6:    e0 ea           ldi    r30, 0xA0; 160
     4c8:    f0 e1           ldi    r31, 0x10; 16
     4ca:    02 c0           rjmp    .+4     ; 0x4d0 <__do_copy_data+0x10>
     4cc:    05 90           lpm    r0, Z+
     4ce:    0d 92           st    X+, r0
     4d0:    a2 30           cpi    r26, 0x02; 2
     4d2:    b1 07           cpc    r27, r17
     4d4:    d9 f7           brne    .-10    ; 0x4cc <__do_copy_data+0xc>
     ................................

 

Кстати, можете сказать что-нибудь по поводу избыточности/неизбыточности Си++ по отношению к Си и насколько оптимальным получается код для AVR, ARM?
Много копий сломано тут на эту тему. Если писать с умом - то ни байта лишнего не будет. Ровно столько же, сколько занял бы делающий то же самое код на голом Си, но при этом со всеми преимуществами, которые дают плюсы: перегрузка операторов, конструкторы-деструкторы, ссылки, инкапсуляция, наследование, шаблоны, enum как отдельный тип, константы и многое другое.

 

Стартап является универсальным для всех контроллеров, поддерживаемых библиотекой и вся необходимая ему информация берется из macros.inc и каких-то других файлов библиотеки?
Да, на этапе компиляции библиотеки из этого файла строится куча файлов crtXXX.o, а при линковке gcc подключает нужный в зависимости от ключа -mmcu=.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...