Skip to content

GCC 4.6 и CMSIS: исправляем ошибку компиляции

by admin on October 29th, 2015

GCC 4.6 и CMSIS: исправляем ошибку компиляции

http://we.easyelectronics.ru/PahanMenski/gcc-46-i-cmsis-ispravlyaem-oshibku-kompilyacii.html

В процессе изучения STM32 решил я обновить компилятор. Сказано — сделано. Свеженький Sourcery G++ Lite с GCC 4.6.1 на борту был скачен и установлен. Вот только проекты, использующие CMSIS дружно перестали компилироваться выдавая ошибки:

Error: registers may not be the same -- `strexh r0,r0,[r1]'
Error: registers may not be the same -- `strexb r0,r0,[r1]'


Гугление быстро выявило наличие данного бага в баг-трекере GCC. Впрочем, как оказалось, это вовсе не баг, а фича текущей версии. Все дело в том, что использование одного и того же регистра в качестве первого и второго операндов данных команд приводит, согласно спецификации ARMv7m, к неопределенному поведению. Кстати, в документации от STM32 об этом не говорится, и работает, насколько я понимаю, правильно. Тем не менее, спеку надо чтить, поэтому поищем, как исправить это досадное недоразумение.
На просторах интернета встречаются два решения:

uint32_t __STREXH(uint16_t value, uint16_t *addr)
{
   uint32_t result=0;
   __ASM volatile ("strexh r2, %2, [%1] \n" \
       "    mov %0, r2" : "=r" (result) : "r" (addr), "r" (value) : "r2" );
   return(result);
}

и

uint32_t __STREXH(uint16_t value, uint16_t *addr)
{
   register uint32_t result asm("r2");
   __ASM volatile ("strexh %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );
   return(result);
}

Очевидным минусом является явное использование регистра r2. Все же задачу выбора свободного регистра лучше было бы поручить компилятору. Попробуем сделать это!
Итак, мы хотим, чтобы в качестве выходного параметра использовался регистр, отличный от входных. В GCC мы можем использовать знак амперсанда, чтобы указать, что регистр должен использоваться исключительно как выходной. Таким образом, просто добавим знак амперсанда после символа =

uint32_t __STREXH(uint16_t value, uint16_t *addr)
{
   uint32_t result=0;
   __ASM volatile ("strexh %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value));
   return(result);
}

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


From → Coding

No comments yet

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS