AVR-GCC: Совмещение C и ассемблера в одном проекте

AVR
Как и многие, я люблю ассемблер за скорость, а также возможность полного контроля над машинным кодом и железом. Однако, писать проект целиком на ассемблере утомительно и не всегда целесообразно, особенно в тех участках, где реализуется общая логика работы, не требующая детального контроля — тут С дает существенный выигрыш. Тем не менее, при написании кода для МК довольно часто встречаются отдельные места, где возможности ассемблера действительно необходимы.



Читать дальше
  • +5
  • 25 мая 2011, 22:55
  • _YS_

Комментарии (35)

RSS свернуть / развернуть
<сut>?
0
Ы?
0
А все, поставил уже :)
0
Глюки, видимо…

Вообще, он сначала мне говорил Gateway Timeout.
0
А каким образом идёт связь с .S файлом?.. Он нигде не указывается?
0
.S файл надо просто добавить в проект в группу исходников (в AVR studio — правый клик мышкой по группе -> добавить ...). Дальше IDE с компилятором разберутся сами. Конечно, заголовочный файл тоже надо добавить в соответствующую группу.
0
В С/С++ связь отдельных файлов производится при линковке, компилируются они все независимо. Соответственно, .s файл сперва скармливается ассемблеру, затем полученный объектник (вместе с остальными) — линковщику. Но это на уровне тулчейна, а на уровне IDE над этим делают обертку — проект. По сути — список файлов, которые нужно откомпилировать/ассемблировать и затем слинковать, а также настройки этих процессов.
А вот в паскале (не уверен правда насчет mP, но он вообще кривой) модули связываются непосредственными ссылками — через кластер uses. Поэтому в Delphi (старых версий) проектом называли головной файл программы, а настройки хранили отдельно.
0
Объявление функции inline не избавит от вызова.
Чтобы сделать функцию по-настоящему inline всё её тело должно находиться в заголовочном файле. И ассемблерная вставка тут — единственный выход.
При этом надо понимать, что на AVR вызовы настолько легки, что в большинстве случаев инлайнить функцию было бы даже вредно.
0
Инлайнить функци очень даже полезно. Когда компилятор встраивает функцию, то производит кучу оптимизаций, которые иначе были-бы невозможны. Маленькие функции, которые транслируются меньше, чем, скажем, в десяток комманд, и функции вызываемые всего один раз, встраивать всегда выгодно.
Или, например, что будет, если из обработчика прерывания вызвать функцию, которая не встроится? Правильно — все call-used регистры будут сохранены в стек.
И вообще решать встраивать функцию, или нет задача компиляторов, и они с ней вполне неплохо справляются. Главное им не мешать.
Кстати в последних версиях GCC (начиная с 4.5 с чем-то) появилась оптимазация во время линковки — LTO. И теперь он может заинлайнить даже ассемблерную функцию из отдельного модуля.
0
на AVR вызовы настолько легки
0
Упс. Отправилось раньше чем надо. (
Настолько ли легки?
Вызов функции сносит кучу регистров.
Плюс сама функция начинает сохранять регистры в стек.
Имхо, вызов функции на AVR это ад)
+1
А истина, как всегда, посередине.
0
attribute(__naked__)
и мучайтесь с регистрами сами ;)
0
Какой хитрый о_О
0
attribute(__naked__) работает только на функции, которые C компилирует. Наша же на ассеблере написана. А в нём «attribute(__naked__)» стоит у всех всех функций по-умолчанию, и сохранять регистры приходится рученьками :)
0
А вам посоветую
attribute(__force_inline__)
;). Тогда функция будет встроена, где бы ее тело не было.
0
Вот представь. Попросили компилятора C скомпилировать файл main.c. В ходе компиляции он добавил к исходнику все заголовочные файлы. В частности avr/io.h и asmroutine.h. Теперь ему не составит труда в выходном обьектном файл main.o инлайнить функции как ему нравится. При условии, что он знает, что в этих функциях содержится.
Но функция asm_func находиться в другом файле asmroutine.s. И вообще на другом языке написана. Компилятор C не из вредности не станет её инлайнить. А просто потому что для него эта функция — чёрный ящик расположенный вне досягаемости он него.
attribute(__force_inline__) просит компилятора инлайнить фунцию не взирая на мнение оптимизатора, если это возможно. В нашем случае это не возможно.
+1
Мы говорили про С-функцию как противопоставление ассемблерной Объявление функции inline не избавит от вызова.
Чтобы сделать функцию по-настоящему inline всё её тело должно находиться в заголовочном файле.
Если к ней добавить указанный атрибут, она будет заинлайнена, даже если занимает всю остальную программу, неважно где находится, вне зависимости от компилятора. И это не просьба, а требование :).
0
Мне казалось, мы говорим про ассемблерную функцию, лежащую в соседнем файле…
0
Ещё одно преимущество такого подхода перед ассемблерными вставками — кроссплатформенность. Если ассемблерная вставка правильно поймётся только avr-gcc, то объявление внешней функции закреплено стандартом C и для всех компиляторов всех архитектур можно писать один и тот-же исходник, заменяя только ассемблерный файл в проекте под конкретную архитектуру. Получится гораздо изящнее, чем километровые #ifdef. Атрибуты, кстати, тоже — штука весьма нестандартная.
Главное правильно понимать границы применимости. Если таким способом реализовать функцию __cli()… То очень скоро начнутся крики, что C — тормозное Г для ленивых.
0
Замечательная статья очень помогла!!!
Но прошу сильно не бить а если можно пояснить зачем нужна деректива __ASSEMBLER__
без неё у меня не запустилось!
0
Это не директива, это дефайн, который определен, если файл просматривает ассемблер. Сделано для того, чтобы компилятор С и ассемблер видели как-бы разные файлы, не видя чужие объявления. Иначе объявления для С смущают ассемблер, а определения для ассемблера — С, и она рапортуют об ошибках.
0
*они
0
Для включения заголовочных файлов надо файлам давать суффикс `.S` а не `.s`, тогда их будет можно компилять как `.c` файлы без явного указания, что надо сначала пропустить текст через C-препроцессор, а потом отдать as, `.s` означает что препроцессинг не требуется. Но это похоже важно только для тех кто пишет Makefile руками. Для AVR ещё возможно надо добавить обявление __SFR_OFFSET, если использовать libc заголовки.
0
А ничего, что Win-окружение нечувствительно (и слава Богу) к регистру имен? ;)

Да и тут разговор именно о самом заголовочнике. as никогда не поймет строчки типа void func(void), потому надо ее от него закрыть.
0
ОС — нет, а вот ассемблер вполне может проверять регистр расширения.
0
Возможно. Но разговор-то о заголовочнике. Сомневаюсь, что расширением файла исходника можно заставить асм игнорировать сишные объявления в хедере. Хедер, если кто не понял, проходится как С, так и асмом.
0
Сомневаюсь, что расширением файла исходника можно заставить асм игнорировать сишные объявления в хедере.
Это да.
0
Я без этого дефайна __ASEMBLER__ пробовал и с расширением .s и .S при этом компилятор вываливал кучу одинаковых ошибок!
0
Так естесственно. Дело не в расширении, а в содержимом файла. Причем именно заголовочного.
0
Не надо запутывания, я говорил о том, что gcc не запустит cpp для .s файла (как ведет себя порт для win не знаю) ели этого не указать явно.

$ cp entry.S entry.s
$ avr-gcc -c entry.S -mmcu=atmega16
$ avr-gcc -c entry.s -mmcu=atmega16
entry.s: Assembler messages:
entry.s:39: Error: constant value required
entry.s:41: Error: constant value required
$ avr-gcc -c -x assembler-with-cpp  entry.s -mmcu=atmega16
$ 
0
Дыг cpp тут не при чем. Дело в том, что .s подключает заголовочник с сишными объявлениями, и асм их, естесственно, не понимает. Поэтому их надо спрятать за проверкой дефайна.
0
cpp = C Pre Processor, и я не говорил, что это каким-то образом отменяет необходимость вырезания C обявлений при включении кода в asm файл.
0
В общем все правы, только друг друга не поняли. :)
0
попробовал в Atmel studio 6 данный прием..., чтото он не видит ассемблерную функцию undefined reference to `asm_func'
0
Собсно почитал комменты… помогло переименование расширения в *.S
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.