Miejscowe wyłączenie optymalizacji kodu

0

Witam

Chciałbym wiedzieć czy istnieje możliwość wyłączenia w danym miejscu kodu jego optymalizacji. Piszę obecnie kod na stm32f4 za pomocą kompilatora GNU GCC. Optymalizacja jest ustawiona na -Os.

timerBaseConfig(TIM_TypeDef* timer, uint32_t speed_hz, uint32_t period_ms)
{
	uint32_t clock_speed=rccGetCoreFrequency();
	
	timer->PSC = clock_speed/speed_hz -1;	    // Set prescaler to defined speed in hz
	timer->ARR = (speed_hz/1000)*period_ms;	 	// Auto reload value // @error trzba dojśc co zle liczy - dziwne zachowanie debuga
	timer->DIER |= TIM_DIER_UIE;
	timer->CR1  |= TIM_CR1_CEN;// Enable timer
} 

Po przejrzeniu kodu w assemblerze jak wygląda proces wyliczania wartości okazało się, że w linijce timer->ARR kompilator wpierw dzieli zmienne, następnie przechodzi linijkę niżej, przypisze makro i powraca do poprzedniego miejsca. Przez co dwukrotnie są zapisywane wartości do rejestrów i mi się krzaczy kod. Jeśli nie ma takiej opcji to po prostu wezmę zadeklaruję zmienną, która wymusi wcześniejsze policzenie wyniku tych działań.
Z góry dzięki za pomoc.

1

Wrzuć kod do osobnego pliku, który będziesz kompilował bez optymalizacji.

0

Dobry pomysł :]
Choć to jest jak dla mnie ominięcie problemu, podobne do mojego.

1

A ja nie do końca rozumiem co ci optymalizator psuje. Możesz wrzucić wynik gcc -S? Przecież kod po optymalizacji powinien być równoważny, więc w czym rzecz?

0

Szczerze to jeszcze nie wiem co, bo zmienne są wyliczane poprawnie. Ale to co siedzi w rejestrach po wyjściu z funkcji w żaden sposób nie odzwierciedla tego co chciałem uzyskać. Te wywołanie timer->ARR zwraca bezpośredni adres rejestru w pamięci mikrokontrolera i podejrzewam, że przez te dziwne zachowanie jest on wywoływany conajmniej 2 razy przez co trafiają do niego śmieci a nie wyliczony wynik. Obecnie nie mam takiej możliwości potem podrzuce.

0
timer->ARR = (speed_hz/1000)*period_ms;

Jaką orientacyjną wartość ma speed_hz? Pamiętaj, że typy całkowite w dzieleniu zwracają wartość całkowitą, więc dla speed_hz == 999 wartość (speed_hz/1000)*period_ms będzie równa (999/1000) * period_ms czyli 0 * period_ms

0
kq napisał(a):

gcc ma pragma GCC optimize: https://gcc.gnu.org/onlinedocs/gcc/Function-Specific-Option-Pragmas.html#Function-Specific-Option-Pragmas

czyli #pragma GCC optimize ("O0")

Dzięki o coś takiego mi chodziło :] Przy kompilacji z "O0" wszystko pięknie śmiga.

#pragma GCC optimize ("-O0")

inline void
timerBaseConfig(TIM_TypeDef* timer, uint32_t speed_hz, uint32_t period_ms)
{
	uint32_t temp, clock_speed=rccGetCoreFrequency();

	timer->PSC = 	clock_speed/speed_hz -1;    // Set prescaler to defined speed in hz
	timer->ARR = (speed_hz/1000)*period_ms;	// Auto reload value // @error trzba dojśc co zle liczy - dziwne zachowanie debuga
	timer->DIER = TIM_DIER_UIE;
	timer->CR1 = TIM_CR1_CEN;// Enable timer
}
#pragma GCC optimize ("-Os")
 

Jeżeli dobrze rozumiem to jak na końcu umieszczę #pragma GCC optimize ("-Os") to dalszą część kodu skompiluje mi dalej na stary opcjach, a czy zamiast -Os jest jakiś jakiś parametr, który by użył starych ustawień z Makefile? Mam zamiar po prostu używać tego częściej, a nie chce by za każdym razem kiedy zmienię rodzaj kompilacji szukać po całym kodzie miejsc gdzie wyłączyłem optymalizację.

2

Do tego lepiej użyj #pragma GCC push_options i #pragma GCC pop_options.

#pragma GCC push_options
#pragma GCC  optimize("-O0")

void f(){}

#pragma GCC pop_options
0
kq napisał(a):
timer->ARR = (speed_hz/1000)*period_ms;

Jaką orientacyjną wartość ma speed_hz? Pamiętaj, że typy całkowite w dzieleniu zwracają wartość całkowitą, więc dla speed_hz == 999 wartość (speed_hz/1000)*period_ms będzie równa (999/1000) * period_ms czyli 0 * period_ms

To są rzędy do kilku kilo do kilkunastu mega Hertzów. Dodatkowo rejestr do, którego zapisuję i tak przyjmuje tylko typy całkowite więc nie tracę za bardzo na rozdzielczości.
Ale tak czy siak dzięki, za zainteresowanie :]

0

Genialne :D
No to więcej do szczęścia mi nie potrzeba.

Jeszcze raz dzięki. Widzę, że muszę trochę poczytać o gcc :P

Temat uważam za zamknięty.

4

lepiej przywracać opcje kompilatora inaczej:

#pragma GCC optimize(initial)

albo

#pragma GCC optimize(push)
#pragma GCC optimize ("-O0")

// twoj kod

#pragma GCC optimize(pop)

https://gcc.gnu.org/wiki/FunctionSpecificOpt#Stage2:_pragma_syntax

0

@DuMaM, a tak spoza optymalizacyjnych pytań: co to za STM i czy możesz pokazać konkretny błąd? Tzn. co dokładnie i jak (jeszcze najlepiej w ASM) oblicza się nie tak? Bo to raczej wskazuje na błąd innego typu, jakby sterta wjechała w stos, gdzieś przekraczasz indeks tablicy itd.
Zwłaszcza, jeżeli bez optymalizacji to działa.

Używasz przerwań podczas wywoływania tej funkcji [tzn. jakieś przerwanie może wejść już w trakcie ustawiania timera]? Masz ustawione main stack i process stack?

EDIT: I jeszcze pytanie jakie IDE: CooCox ma (miał?) spaprany moduł podglądu SFR (u mnie objawiało się to błędnymi odczytami rejestrów FSMC) i czasem potrafi pokazać cuda,

0

Używam STM32L151. Nie sądzę, żeby takie proste obliczenia tak mi zapchały pamięć, że się razem zjadą ze sobą sterta ze stosem. Żadne przerwanie nie może wejść bo jeszcze nic nie jest aktywne. Timery są mi potrzebne od na samym początku więc ich internalizacją zajmuję się na samym starcie, zaraz po wystartowaniu zegara.
A piszę w Eclipsie i nie używam żadnego modułu do SFR (choć czasem by się przydał).

Poniżej zamieszczam kod ASM z optymalizacją.

   86       {
          timerBaseConfig(TIM_TypeDef*, unsigned long, unsigned long):
080029cc:   push {r4, r5, r6, lr}
080029ce:   mov r4, r0
080029d0:   mov r5, r1
080029d2:   mov r6, r2
 87       	uint32_t clock_speed=rccGetCoreFrequency();
080029d4:   bl 0x800278c <rccGetCoreFrequency()>
 89       	timer->PSC  |= clock_speed/speed_hz -1;    // Set prescaler to defined speed in hz
080029d8:   ldrh r3, [r4, #40]      ; 0x28
080029da:   udiv r0, r0, r5
080029de:   subs r0, #1
 90       	timer->ARR 	|= (speed_hz/1000)*period_ms;	// Auto reload value // @error trzba dojśc co zle liczy - dziwne zachowanie debuga
080029e0:   mov.w r2, #1000 ; 0x3e8
 89       	timer->PSC  |= clock_speed/speed_hz -1;    // Set prescaler to defined speed in hz
080029e4:   orrs r3, r0
 90       	timer->ARR 	|= (speed_hz/1000)*period_ms;	// Auto reload value // @error trzba dojśc co zle liczy - dziwne zachowanie debuga
080029e6:   udiv r5, r5, r2
 89       	timer->PSC  |= clock_speed/speed_hz -1;    // Set prescaler to defined speed in hz
080029ea:   uxth r3, r3
 90       	timer->ARR 	|= (speed_hz/1000)*period_ms;	// Auto reload value // @error trzba dojśc co zle liczy - dziwne zachowanie debuga
080029ec:   muls r6, r5
 89       	timer->PSC  |= clock_speed/speed_hz -1;    // Set prescaler to defined speed in hz
080029ee:   strh r3, [r4, #40]      ; 0x28
 90       	timer->ARR 	|= (speed_hz/1000)*period_ms;	// Auto reload value // @error trzba dojśc co zle liczy - dziwne zachowanie debuga
080029f0:   ldr r3, [r4, #44]       ; 0x2c
080029f2:   orrs r6, r3
080029f4:   str r6, [r4, #44]       ; 0x2c
 91       	timer->DIER |= TIM_DIER_UIE;
080029f6:   ldrh r3, [r4, #12]
080029f8:   uxth r3, r3
080029fa:   orr.w r3, r3, #1
080029fe:   strh r3, [r4, #12]
 92       	timer->CR1  |= TIM_CR1_CEN;// Enable timer
08002a00:   ldrh r3, [r4, #0]
08002a02:   uxth r3, r3
08002a04:   orr.w r3, r3, #1
08002a08:   strh r3, [r4, #0]
08002a0a:   pop {r4, r5, r6, pc}  {

A tu bez optymalizacji

   86       {
          timerBaseConfig(TIM_TypeDef*, unsigned long, unsigned long):
080029f4:   push {r7, lr}
080029f6:   sub sp, #24
080029f8:   add r7, sp, #0
080029fa:   str r0, [r7, #12]
080029fc:   str r1, [r7, #8]
080029fe:   str r2, [r7, #4]
 87       	uint32_t clock_speed=rccGetCoreFrequency();
08002a00:   bl 0x800278c <rccGetCoreFrequency()>
08002a04:   str r0, [r7, #20]
 89       	timer->PSC  |= clock_speed/speed_hz -1;    // Set prescaler to defined speed in hz
08002a06:   ldr r3, [r7, #12]
08002a08:   ldrh r3, [r3, #40]      ; 0x28
08002a0a:   uxth r2, r3
08002a0c:   ldr r1, [r7, #20]
08002a0e:   ldr r3, [r7, #8]
08002a10:   udiv r3, r1, r3
08002a14:   uxth r3, r3
08002a16:   subs r3, #1
08002a18:   uxth r3, r3
08002a1a:   orrs r3, r2
08002a1c:   uxth r2, r3
08002a1e:   ldr r3, [r7, #12]
08002a20:   strh r2, [r3, #40]      ; 0x28
 90       	timer->ARR 	|= (speed_hz/1000)*period_ms;	// Auto reload value // @error trzba dojśc co zle liczy - dziwne zachowanie debuga
08002a22:   ldr r3, [r7, #12]
08002a24:   ldr r2, [r3, #44]       ; 0x2c
08002a26:   ldr r1, [r7, #8]
08002a28:   ldr r3, [pc, #56]       ; (0x8002a64 <timerBaseConfig(TIM_TypeDef*, unsigned long, unsigned long)+112>)
08002a2a:   umull r0, r3, r3, r1
08002a2e:   lsrs r3, r3, #6
08002a30:   ldr r1, [r7, #4]
08002a32:   mul.w r3, r1, r3
08002a36:   orrs r2, r3
08002a38:   ldr r3, [r7, #12]
08002a3a:   str r2, [r3, #44]       ; 0x2c
 91       	timer->DIER |= TIM_DIER_UIE;
08002a3c:   ldr r3, [r7, #12]
08002a3e:   ldrh r3, [r3, #12]
08002a40:   uxth r3, r3
08002a42:   orr.w r3, r3, #1
08002a46:   uxth r2, r3
08002a48:   ldr r3, [r7, #12]
08002a4a:   strh r2, [r3, #12]
 92       	timer->CR1  |= TIM_CR1_CEN;// Enable timer
08002a4c:   ldr r3, [r7, #12]
08002a4e:   ldrh r3, [r3, #0]
08002a50:   uxth r3, r3
08002a52:   orr.w r3, r3, #1
08002a56:   uxth r2, r3
08002a58:   ldr r3, [r7, #12]
08002a5a:   strh r2, [r3, #0]
 93       }
08002a5c:   adds r7, #24
08002a5e:   mov sp, r7
08002a60:   pop {r7, pc}
08002a62:   nop 
08002a64:   ldr r5, [pc, #844]      ; (0x8002db4 <usartPrintf(unsigned long, char const*, ...)+20>)
08002a66:   asrs r2, r4, #1
0

Dokładnie przanalizuję to jak wyjdę z roboty, ale na gorąco: tam ma być
timer->PSC |= czy timer->PSC = Bo disassembler raczej sam operatora nie zmienia [chyba, że się mylę]

0

Jest dobrze "timer->PSC=" bo to rejestr 2 bajtowy przechowujący wartość preskalera dla licznika.

Zrobiłem update kodu.

0

Wiem co to jest PSC.

Patrz, na stronie pierwszej masz:
timer->PSC = clock_speed/speed_hz -1; // Set prescaler to defined speed in hz

A w disasemblerze:
89 timer->PSC |= clock_speed/speed_hz -1; // Set prescaler to defined speed in hz

zwróć uwagę na sumę bitową ("pałkę" |) przed równa się (=). Dziwi mnie taka postać po deasemblacji, bo np. u mnie przypisanie wygląda tak (arm-none-eabi-gcc v4.7.4, STM32F4):

  65:../src/i2c/i2c.c ****   I2C1->TRISE = 41; // ( 1000n s / 25ns ) + 1
 216              		.loc 1 65 0
 217 009c 4FF4A843 		mov	r3, #21504
 218 00a0 C4F20003 		movt	r3, 16384
 219 00a4 4FF02902 		mov	r2, #41
 220 00a8 1A84     		strh	r2, [r3, #32]	@ movhi

Czyli przypisanie to przypisanie.

EDIT: poza tym instrukcje ORRS skądś się musiały wziąć.
Makefile'a masz dobrego? Ew. make clean dla bhp?

0

Z czasem robiłem zmiany w kodzie i początkowo nim wstawiłem kod z ASM miałem ustawione "lub", a nie przypisanie. Teraz mam przypisanie i wierz mi, że w niczym to się nie różni jeżeli chodzi o głupoty jakie wpisywane są do rejestru. Jc dotyczyło to rejestru timer->ARR.

Makefile mam na pewno dobrego (od Freddiego).

Dzięki za pomoc i poświęcony czas, ale problem jak dla mnie już został rozwiązany :]

0

Jak uważasz, narzucać się nie będę, aczkolwiek jak dla mnie to wiązanie buta dżdżownicą.

1 użytkowników online, w tym zalogowanych: 0, gości: 1