Skocz do zawartości
etet100

STM32F3 - crashe związane z użyciem typu double

Pomocna odpowiedź

Witam. Tworzę prostą aplikację pod Stm32CubeIDE z użycie kontrolera STM32F303 (z wbudowanym FPU). Trafiłem na dziwne problemy kiedy
próbowałem użyć sprintf z włączonym wsparciem dla %f. Za każdym razem zamiast poprawnie sformatowanej liczby dostawałem bardzo
długi ciąg cyfr. Znalazłem nawet na githubie jakąś inną implementację sprintf i to znowu często zamiast poprawnej wartości zwracało mi 0.00. W końcu
okazało się, że problem jest z va_arg:

void test(const char* format, ...)
{
  va_list va;
  va_start(va, format);

  double arg1 = va_arg(va, double);

  char buf[102];
  sprintf(buf, "a %.2f", arg1);

  va_end(va);
}

test("format", 0.1234);

Zatrzymuje kod na linii z va_arg i widzę, że to zwraca głupoty. Obszedłem to przy pomocy takiego dziwnego rozwiązania:

  uint64_t doubleBinary = va_arg(va, uint32_t) |
          ((uint64_t)va_arg(va, uint32_t) << 32);
  double arg = *(double*)&doubleBinary;

I tu dostaje poprawną wartość. Ale potem mam taki fragment (to z tego sprintf):

static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
{
  {... tu jakiś kod...}
}

//i wywołanie:
uint64_t doubleAsInt = va_arg(va, uint32_t) |
               ((uint64_t)va_arg(va, uint32_t) << 32);
double doubleAsDouble = *(double*)&doubleAsInt;

idx = _ftoa(out, buffer, idx, maxlen, doubleAsDouble, precision, width, flags);
format++;

No i na tym _ftoa znowu jest problem bo zamiast wejść do procedury to kod wpada mi do:

Default_Handler:
Infinite_Loop:
  b Infinite_Loop

Próbowałem wyłączyć FPU i skompilować wszystko na software. va_arg nadal nie działa ale nie ma tego crasha przy _ftoa. Potem uruchomiłem ten sam program
pod starym EmBitz (GCC w wersji 5) i tam wszystko wydaje się działać bezbłędnie (na tyle na ile udało mi się sprawdzić). Przy czym te projekty nie są identycznie (mój kod jest taki sam ale to co jest generowane przez środowisko już na pewno nie).

Ma ktoś pomysł jak to debugować i o co może chodzić? Nie jestem w tym specjalnie zaawansowany i skończyły mi się już pomysły. W internecie znalazłem jakieś szczątkowe informacje, że double ma 8 bajtów i z tym jest jakiś problem (niestety bez konkretów więc nic mi to nie dało).

Udostępnij ten post


Link to post
Share on other sites

1. To że STM32F4 ma FPU nie oznacza że możesz używać typu double - FPU w tych uK ma tylko 32 bity.

2. Wątpię aby printf w twoim toolchainie drukował double.

3. Tak va_arg nie wolno sotosować - jest to UB najpierw przypisz do zmiennych następnie rób operacje arytmetyczne.

4. Kod łamie zasady strict aliasing i niekoniecznie kod musi robić to co myślisz. Użyj uniii - union punning jest bezpieczny.

 

Generalnie za dużo prób trików, które są UB.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites
1 godzinę temu, stmx napisał:

1. To że STM32F4 ma FPU nie oznacza że możesz używać typu double - FPU w tych uK ma tylko 32 bity.

Ale chyba nadal istnieje tryb software? Wiem, że FPU ich nie wspiera. Wszędzie piszą, że w takiej sytuacji kompilator
po prostu zrealizuje to softwarowo.

1 godzinę temu, stmx napisał:

2. Wątpię aby printf w twoim toolchainie drukował double.

Ale floaty niby drukuje. A przetestowałem to na 1000 sposobów i nie działa. A zanim zainstalowałem te nowe STM32CubeIDE to 
nie miałem takich problemów. Czego bym nie podstawił jako parametr %f to dostaje wartość w stylu 51275175258712571248012958901285791207581251.
Tak samo czego bym nie podstawił do tego do tej metody ze zmiennymi parametrami do dostaje coś w okolicach 0. I nie ma znaczenia
czy włącze te cholerne FPU czy nie.

1 godzinę temu, stmx napisał:

3. Tak va_arg nie wolno sotosować - jest to UB najpierw przypisz do zmiennych następnie rób operacje arytmetyczne.

"TAK" czyli jak? Bo ten kod uprościłem na koniec. Wcześniej było przepisywanie do zmiennych i nie zmieniło to kompletnie nic.

1 godzinę temu, stmx napisał:

4. Kod łamie zasady strict aliasing i niekoniecznie kod musi robić to co myślisz. Użyj uniii - union punning jest bezpieczny.

Nie wiem co łamie kod ale próbowałem na dziesiątki sposobów. Właściwie to nawet podstawienie do tej funkcji stałej 
nic nie zmienia. Dopiero zmiana jednego z jej parametrów z double na przykładowo int powoduje, że kod się nie wypiepsza
na wywołaniu. 

Udostępnij ten post


Link to post
Share on other sites

No i niestety to raczej nie jest kwestia wersji GCC. Skompilowałem ten projekt pod wersją 5 i wyszło dokładnie to samo. 

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Zrobiłem jeszcze raz ten sam projekt w Atollic Studio (tym razem GCC 6.3.1) i działa bezbłędnie. Czyli ten STM32CubeIDE jakoś niedobrze
tworzy projekt. Ale w internecie nie ma śladu po takich przypadku. Wszystkim działa tylko mi nie.

Udostępnij ten post


Link to post
Share on other sites

@etet100 nie wiem czy to konkretnie wina STM32CubeIDE, ale pamiętaj, że to nowe oprogramowanie (oficjalna premiera była kilka tygodni temu). Mogą tam jeszcze być pewne błędy, których nikt jeszcze nie odnalazł 😉

Udostępnij ten post


Link to post
Share on other sites

Niby nowe ale to zdaje się jest oparte w dużej części na już istniejących rozwiązaniach. Ten CubeMX to przecież nic nowego. A wychodzi mi, że
to on coś źle generuje. Choć mogę się w 100% mylić.

Udostępnij ten post


Link to post
Share on other sites

Już kompletnie zdesperowany postanowiłem porównać pliki linkera generowane przez atollic i cubeide. Różnic jest ogólnie sporo ale zauważyłem na początku różnicę

_estack = 0x2000A000;    /* end of "RAM" Ram type memory */

i

_estack = 0x20009FFF;    /* end of "RAM" Ram type memory */

Zmieniłem to 9FFF na A000 (jak w Atollic) i poszło! Potem zacząłem zacząłem szukać pod tym
kątem i jest takie coś:

https://community.st.com/s/question/0D50X0000Ansc6GSQQ/stm32cubeide-linker-lptim-lsi-clock

Troszkę inny kontroler, identyczne problemy, identyczne rozwiązanie. I piszą, że ST o tym wie ale się nie przejmuje.
Czy to w takim razie może traktować jak poprawne rozwiązanie?

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Jeśli faktycznie generowane jest _estack = 0x20009FFF; to straszna bieda. Cały stos jest źle wyrównywany, aż dziwne że cokolwiek wtedy działa.

Udostępnij ten post


Link to post
Share on other sites

Czy to jest pewne, że tak właśnie się dzieje za każdym razem? Sprawdzał to ktoś jeszcze czy to tylko odosobniony przypadek? Niestety nie mam jak tego sprawdzić osobiście w tej chwili ale będzie trzeba to mieć na uwadze w razie co. Może warto to zgłosić.

Udostępnij ten post


Link to post
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »

×