суботу, 6 квітня 2013 р.

Помилка в ARM CMSIS

(c) valessiobrito, "Open clipart"
Недавно я вже писав про труднощі, які зустрічаються на дорозі безбашенних програмерів, котрі насмілюються використовувати оптимізацію, програмуючи мікроконтролери (хоча, де ж вона може бути більш потрібною, ніж на цих малопотужних пристроях? ;-)

Ввімкнувши її у простому проектику для STM32 (в CoIDE),  теж зіткнувся з проблемами. Компілятор видав наступне:

       [cc] C:\Users\indrekis\AppData\Local\Temp\ccaX3kx8.s:1162: Error: registers may not be the same -- `strexb r0,r0,[r1]'
       [cc] C:\Users\indrekis\AppData\Local\Temp\ccaX3kx8.s:1187: Error: registers may not be the same -- `strexh r0,r0,[r1]'



З допомогою Гугла зразу знайшов пояснення: "Error: registers may not be the same -- `strexb r0,r0,[r1]'". Машинні команди ARM strexb і strexh не можуть мати в ролі аргументів однакові регістри, а компілятор двічі підсовує їм r0. Справді, у файлі cmsis/core_cm3.c є наступні оголошення:

#elif (defined (__GNUC__)) /*------------------ GNU Compiler ---------------------*/

/* --------- SKIPPED --------- */

uint32_t __STREXB(uint8_t value, uint8_t *addr)
{
   uint32_t result=0;
  
   __ASM volatile ("strexb %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );
   return(result);
}

/* --------- SKIPPED --------- */

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);
}

/* --------- SKIPPED --------- */

#elif (defined (__ICCARM__)) /*------------------ ICC Compiler -------------------*/

/* --------- SKIPPED --------- */


uint32_t __STREXB(uint8_t value, uint8_t *addr)
{
  __ASM("strexb r0, r0, [r1]");
  __ASM("bx lr");
}

/* --------- SKIPPED --------- */

uint32_t __STREXH(uint16_t value, uint16_t *addr)
{
  __ASM("strexh r0, r0, [r1]");
  __ASM("bx lr");
}

Як виправити для IAR iccarm не знаю -- невірні регістри там прописані явно, мало що ABI очікуватиме, але для актуального мені GCC все відносно просто. Слід в обох асемблерних вставках "=r" (result), замість "=r" написати "=&r", це змусить компілятор підібрати інший регістр:

__ASM volatile ("strexb %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value) );
 
Детальніше про такі асемблерні вставки, як їх писати, як вони функціонують і т.д., див. "Ассемблерные вставки в AVR-GCC".

Ймовірно, помилка проявляється лише за ввімкненої оптимізації через те, що компілятор починає хитрувати із використанням регістрів, і вирішує, що двічі використати один і той же буде вигідніше...

Стосовно CoIDE, яка є моєю основною оболонкою для STM32 -- на жаль, таку модифікацію може довестися робити в кожному проекті. Спробувати вирішити проблему раз і назавжди можна наступним чином. Файл, який копіюється в проекти, лежить в деяких піддиректоріях <CoIDE_Path>\CoIDE\repo\Components, головний, здається, в "<CoIDE_Path>\CoIDE\repo\Components\50_CMSIS core\src\cmsis". Однак, повної впевненості, як оболонка їх тасує, в мене немає... (Увага, свій core_cm.c є і в репозиторії CoSmart, але останній не підтримує STM32, тому поки не користувався).

Для тих, хто забув, core_cm3.c є частиною CMSIS, стандартної бібліотеки для всіх Cortex-ів, основа якої розроблена ARM Holdings, допилюється виробниками мікроконтролерів під їх периферію. Детальніше див. розділ "CMSIS i SPL (Standard Peripherals Library)" в одному із попередніх постів, "Огляд STM32 (ARM Cortex-M від STMicroelectronics)", та посилання там.

UPD: Зрозуміло, що помилка ця -- відома, не є чимось новим.  Ось ще декілька посилань: 1, 2, 3. Просто ілюстрація, що буває, коли відважуєшся на оптимізацію. :-) Якщо орієнтуватися -- дрібниця, для початківця -- непідйомне.

В новіших CMSIS вже виправлено, але CoIDE використовує доволі стару. Обіцяють оновлення.

2 коментарі:

  1. Дякую! Хоча, особливо за що хвалити тут і немає --- просто зарисова про відомі факти, з якими зіткнувся. :-)

    ВідповістиВидалити