Forum: Compiler & IDEs atomic-lib für stm32


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Leo B. (luigi)


Lesenswert?

Hey Leute,

wollte mal fragen, ob jemand eine atomic-lib, wie es sie für den AVR-GCC 
gibt 
(http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html), 
auch für die STM32 kennt. Compiler soll auch hier der GCC sein falls das 
wichtig ist.

Ich konnte leider nichts passendes im Internet finden, was mich 
allerdings schon ein wenig wundert. Als ob beim STM32 keiner mehr 
atomare Operationen durchführen möchte...

Vielen Dank
Gruß Leo

von Reiner S. (Gast)


Lesenswert?

Da der AVR nur 8 Bit hat und ein int 16 Bit sind Operationen wie int i++ 
nicht atomar da sie 2 Speicher Zugriffe erfordern.

Der STM32 (ARM) ist ein 32 Bitter und ein int hat 32 Bit und somit ist 
int i++ atomar.

Außerdem hat der ARM einen Befehlssatz der modify Operationen auf 
Assembler Ebene atomar ausführt.
Mit dem STM32F10x_StdPeriph_Driver werden auch I/O Zugriffe atomar.

von Dr. Sommer (Gast)


Lesenswert?

Reiner S. schrieb:
> Der STM32 (ARM) ist ein 32 Bitter und ein int hat 32 Bit und somit ist
> int i++ atomar.
Diese Folgerung stimmt nicht notwendigerweise, ist aber im Falle des ARM 
korrekt. Was allerdings nicht korrekt ist, ist die Annahme dass 
"Variable lesen => Variable inkrementieren => Variable zurückspeichern" 
atomar ist! Dazu braucht man schon Locks. Das geht aber auf 
Multithreading-freundlichen CPU's wie eben dem ARM wohl recht effizient.

Reiner S. schrieb:
> Außerdem hat der ARM einen Befehlssatz der modify Operationen auf
> Assembler Ebene atomar ausführt.
Aber eben nur auf Registern.
> Mit dem STM32F10x_StdPeriph_Driver werden auch I/O Zugriffe atomar.
Ach? Und wie sieht es mit folgendem aus (aus eben dieser Library, 
gekürzt):
1
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState)
2
{
3
  if (NewState != DISABLE) {
4
    ADCx->CR2 |= (uint32_t)ADC_CR2_ADON; // <-- (mindestens) 3 Instruktionen, überhaupt nicht atomar
5
  }  else  {
6
    ADCx->CR2 &= (uint32_t)(~ADC_CR2_ADON); // dito
7
  }
8
}

von Reiner S. (Gast)


Lesenswert?

> Der STM32 (ARM) ist ein 32 Bitter und ein int hat 32 Bit und somit ist
> int i++ atomar.
>>Diese Folgerung stimmt nicht notwendigerweise, ist aber im Falle des ARM
>>korrekt. Was allerdings nicht korrekt ist, ist die Annahme dass
>>"Variable lesen => Variable inkrementieren => Variable zurückspeichern"
>>atomar ist! Dazu braucht man schon Locks. Das geht aber auf
>>Multithreading-freundlichen CPU's wie eben dem ARM wohl recht effizient.
Beim AVR ist int i++ schon auf Registerebene nicht atomar und das meinte 
ich.

Dr. Sommer schrieb:
>> Mit dem STM32F10x_StdPeriph_Driver werden auch I/O Zugriffe atomar.
> Ach? Und wie sieht es mit folgendem aus (aus eben dieser Library,
> gekürzt):void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState)
> {
>   if (NewState != DISABLE) {
>     ADCx->CR2 |= (uint32_t)ADC_CR2_ADON; // <-- (mindestens) 3
> Instruktionen, überhaupt nicht atomar
>   }  else  {
>     ADCx->CR2 &= (uint32_t)(~ADC_CR2_ADON); // dito
>   }
> }

Da habe ich mich unklar ausgedrückt denn ich meine die Zugriffe auf Bit 
Ebene. Das Beispiele mit kann nicht atomar seinen und ST überlässt es 
dem  Anwender bei bedarf vor Aufruf der Funktion die Interrups zu 
sperren.
Das halte ich für eine gute Idee.

von Dr. Sommer (Gast)


Lesenswert?

Reiner S. schrieb:
> Beim AVR ist int i++ schon auf Registerebene nicht atomar und das meinte
> ich.
Jo. Dass das beim ARMv7M auf Registerebene atomar ist bringt aber 
praktisch nix, da es auf Speicherebene nicht atomar ist (außer 
Geschwindigkeit).

Reiner S. schrieb:
> Da habe ich mich unklar ausgedrückt denn ich meine die Zugriffe auf Bit
> Ebene.
Ja aber das bringt nix, da es immer noch load-modify-store ist! Ob die 
Funktion intern mit 1 oder mehreren Operationen auf dem temporären 
Register arbeitet macht doch überhaupt keinen Unterschied.
> Das Beispiele mit kann nicht atomar seinen und ST überlässt es
> dem  Anwender bei bedarf vor Aufruf der Funktion die Interrups zu
> sperren.
> Das halte ich für eine gute Idee.
Ja, ist es auch. Aber zu behaupten die StdPeriphal Library wäre "atomar" 
ist gefährlich ;-)

von Leo B. (luigi)


Lesenswert?

Entweder ist meine Frage unpräziese gestellt, oder das Thema hat sich 
ein wenig vom Ursprünglich angesprochenen/gefragten entfernt.

Klar bietet die ARM-Cortex nen haufen Atomarer Operationen aber bereits 
ein einfaches togglen eines Port-Pins (nur als Beispiel) gehört meines 
Wissens schon nicht mehr dazu...
Da ich aber das Interrupts sperren und freigeben mit 2 Funktionen nicht 
halb so übersichtlich finde wie einen ATOMIC_BLOCK hätte ich mich 
gefreut wenn bereits jemand so etwas für den STM32 (in meinem Fall 
konkret den STM32F100) geschrieben und veröffentlicht hätte. Ich selbst 
stehe noch sehr am Anfang des Verständnisses der ARM-Rechenkerne und 
könnte soetwas vermutlich nicht halb so elegant und performant 
implementieren wie ein erfahrener Programmierer.

Zum Vergleich:
1
[...]
2
someNonAtomicCode();
3
__disable_irq();
4
doSomeThing();
5
// some comment
6
doAtomicStuff();
7
// another comment
8
lastAtomicOperation();
9
__enable_irq();
10
someNonAtomicCode();
11
[...]
1
[...]
2
someNonAtomicCode();
3
ATOMIC_BLOCK(...)
4
{
5
    doSomeThing();
6
    // some comment
7
    doAtomicStuff();
8
    // another comment
9
    lastAtomicOperation();
10
}
11
someNonAtomicCode();
12
[...]
Darf jetzt jeder für sich entscheiden was übersichtlicher ist aber meine 
Meinung dazu steht fest...

es fehlt wie gesagt nur die Atomic-lib. Hoffentlich finde ich noch eine.
Danke
Gruß Leo

von Dr. Sommer (Gast)


Lesenswert?

Es geht einfach nur darum __disable_irq(); und __enable_irq(); um einen 
Code-Block zu packen? Das ist doch supereasy. In C++ geht es elegant mit 
lambdas:
1
template <typename F> inline void doAtomic (F f)  {
2
  __disable_irq ();
3
  f ();
4
  __enable_irq ();
5
}
6
7
int main () {
8
  doAtomic ([] () {
9
    // ... geschützte Operationen ...
10
  });
11
}
Wenn man aus irgendwelchen obskuren Gründen kein C++ verwenden darf muss 
man sich mit ekligen C-Makros behelfen, muss hier mal ein C-Frickler 
helfen :o)

von Karl H. (kbuchegg) (Moderator)


Lesenswert?

Dr. Sommer schrieb:

> man sich mit ekligen C-Makros behelfen, muss hier mal ein C-Frickler
> helfen :o)

In C benutzt man einen Trick.
Die Frage ist, wie kriege ich den Compiler dazu
* den { } Block so zu verpacken, dass
* ich am Anfang des Blocks eine Anweisung einschummeln kann
* ich am ende des Blocks eine Anweisung einschummeln kann

Dazu bietet sich wieder mal eine for-Schleife an.
Ihre 3 Teile benutze ich
* um die Initialisierungsphase dazu zu benutzen, eine Anweisung vor den 
Block zu platzieren
* die Laufbedingung dazu, damit der Block nur 1 mal ausgeführt wird
* den Inkrementteil, um damit die abschliessende Anweisung an das 
Blockende zu setzen und gleichzeitig die Laufbedingung ungültig zu 
machen.

Wie würde sich das in Langform schreiben?

Ich würde zb schreiben
1
  for( __disable_irq(), uint8_t cont = 1; cont == 1; __enable_irq(), cont = 0 )
2
  {
3
    Anweisungen ....
4
  }

Anstelle des 'umständlichen' for, wird der Teil noch in ein Makro 
verpackt und nähere mich damit dem Ziel
1
#define ATOMIC_BLOCK() for( __disable_irq(), uint8_t __cont = 1; __cont == 1; __enable_irq(), __cont = 0 )

was jetzt noch fehlt, sind diverse Argumente an das Makro mit denen man 
das Verhalten steuern kann. Ob man die braucht ist eine andere Frage, 
zur Not kann man ja auch 'verschiedene' ATOMIC_BLOCK Makros machen.

(Ja ich weiß, der Name der Hilfsvariablen __cont ist nicht ganz koscher. 
Ich rechne allerdings so ein Makro schon mehr der Implementierung zu. 
Von daher nehme ich mir das Recht raus, derartige Namen verwenden zu 
dürfen.)

von Dr. Sommer (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> In C benutzt man einen Trick.
Das ist ja mal wieder maximal bösartig... ;-)

von Karl H. (kbuchegg) (Moderator)


Lesenswert?

Dr. Sommer schrieb:
> Karl Heinz Buchegger schrieb:
>> In C benutzt man einen Trick.
> Das ist ja mal wieder maximal bösartig... ;-)


:-)
Ich habs mir von der avr-libc abgeschaut.
Ich hab mich nämlich auch gefragt, wie kriegen die das hin.
Wobei die noch einen zusätzlichen Trick verwenden, der aber nicht 
portabel ist. Die benutzen __cleanup, welches als eine Art Destruktor 
fungiert.
Aber so müsste es auch gehen, so lange sich keiner aus der for-Schleife 
raus-breaked. Womit dann auch der 'schwache' Punkt dieser Lösung 
angesprochen wäre.

: Bearbeitet durch Moderator
von Leo B. (luigi)


Lesenswert?

ich steig ja mit den GCC-Compilern noch nicht so recht durch, aber ist 
GCC nicht gleich GCC? also Dinge die dieses cleanup-attribut die beim 
AVR-GCC offenbar funktionieren, sollten doch auch beim ARM-GCC 
funktionieren oder nicht?
wenn nicht, kann mir jemand sagen wo ich ein entsprechendes Manual 
finde, wo solche tricks drin stehen? sowohl für AVR als auch für den 
ARM.
Ich habe bisher nur diese online-Doku
http://gcc.gnu.org/onlinedocs/
gefunden und die unterscheidet nicht zwischen arm und avr...

passender Link zum Thema:
http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html

cleanup müsste dann beim STM32 genauso funktionieren. Auch wenn nicht 
ganz klar wird was der cleanup-function als Parameter übergeben wird, 
nehme ich doch an, dass es ein Pointer auf entsprechende Variable sein 
wird.

wäre ja sehr angenehm wenn man den AVR-Code da portieren könnte.

Danke euch
Gruß Leo

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Leo B. schrieb:
> ich steig ja mit den GCC-Compilern noch nicht so recht durch, aber ist
> GCC nicht gleich GCC? also Dinge die dieses cleanup-attribut die beim
> AVR-GCC offenbar funktionieren, sollten doch auch beim ARM-GCC
> funktionieren oder nicht?

Für diese Attribute: Ja.

von (prx) A. K. (prx)


Lesenswert?

Wäre noch anzumerken, dass man diverse atomare Operationen im RAM auf 
ARMs ab ARMv6 auch ohne Abschaltung der Interrupts erledigen kann, indem 
man LDREX/STREX bzw. die entsprechenden Funktionen vom CMSIS verwendet. 
Da dabei eine Datenabhängigkeit enthalten ist, gibts auch keine Probleme 
mit der Reihenfolge und Reordering.

Wenn man Interrupts abschaltet muss man immer drauf achten, dass der 
Compiler keine komplexe Operation vom Code davor zwischen rein mogelt. 
Das kann einem bei LDREX/STREX zwar auch passieren, aber da spielt es 
kaum eine Rolle.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Bissel was in der Hinsicht gibts im GCC plattformübergreifend schon von 
Haus aus, z.B.
      result = __sync_add_and_fetch(&var, 1);
für atomare Addition einer Variablen.

http://gcc.gnu.org/onlinedocs/gcc-4.7.3/gcc/_005f_005fsync-Builtins.html#_005f_005fsync-Builtins
http://gcc.gnu.org/onlinedocs/gcc-4.7.3/gcc/_005f_005fatomic-Builtins.html#_005f_005fatomic-Builtins

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ja, ist alles "legacy" :-)

Wenn man es wirklich protabel halten will, dann mit C++11 und den Memory 
Models und GCCs libatomic, die m.W. für ARM implementiert ist.

http://gcc.gnu.org/onlinedocs/gcc-4.7.3/gcc/_005f_005fatomic-Builtins.html#g_t_005f_005fatomic-Builtins

: Bearbeitet durch User
von Adib (Gast)


Lesenswert?

die Implementierung des ATOMIC_BLOCK im der avrlibc ist schon eine große 
Sache. Speziell weil man auch heraus break-en und return-en kann.

Um nicht immer glabel die Interrupts abzuschalten, modifiziere ich beim 
STM32 die BASEPRIO nach Bedarf. Dann werden nur gewisse Interrupts 
ausgeblendet.

HTH, Adib.
--

von Philipp S. (schroedi)


Lesenswert?

In einem STM32-Programm für einen Versuchsaufbau habe ich auch ein paar 
Codezeilen, die nicht durch einen Interrupt unterbrochen werden dürfen. 
Das ATOMIC_BLOCK - Konstrukt vom AVR kenne ich auch und finde es 
nützlich.
Da sich die Lösung von Karl Heinz bei mir mit GCC nicht kompilieren 
lässt, habe ich etwas gebastelt. Mit den Informationen von 
http://stm32f4-discovery.com/2015/06/how-to-properly-enabledisable-interrupts-in-arm-cortex-m/ 
komme ich auf folgende Lösung, welche die Funtionalität von 
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) für den AVR nachbildet:
1
#define ATOMIC_BLOCK for( uint32_t __cond = 1, __prim = __get_PRIMASK(); __cond != 0 ? __disable_irq() : 0, __cond != 0; __prim == 0 ? __enable_irq() : 0, __cond = 0 )

Damit man es nachvollziehen kann:
1
#include <stdio.h>
2
3
int Funktion1(void)
4
{
5
  printf("Funktion1\n");
6
  return 0;
7
}
8
9
void Funktion2(void)
10
{
11
  printf("Funktion2\n");
12
}
13
14
void Funktion3(void)
15
{
16
  printf("Funktion3\n");
17
}
18
19
void Funktion4(void)
20
{
21
  printf("Funktion4\n");
22
}
23
24
int main()
25
{
26
  for( int __cond = 1, __prim = Funktion1(); __cond != 0 ? Funktion2() : 0, __cond != 0; __prim == 0 ? Funktion4() : 0, __cond = 0 ) {
27
    Funktion3();
28
  }
29
  return 0;
30
}

von Marc (Gast)


Lesenswert?

Das ist zwar ein alter Thread  aber das Update lässt mir keine Wahl:
Ihr baut die tollsten Konstrukte aber das ganze Konzept hat einen 
eklatanten systematischen Fehler:
Man darf auf keinen Fall am Ende eines atomaren Blocks den Interrupt 
unkontrolliert wieder aktivieren! Sonst passiert es, dass in einem 
Programmteil, in dem der Interrupt explizit ausgeschaltet sein muss, 
durch Aufruf einer Funktion mit dem Konstrukt ungewollte Interrupts 
aktiviert werden!

Die korrekte Vorgehensweise ist:

1. Zustand der Interrupts in einer Variablen sichern
2. Interrupt deaktivieren (einen oder alle…)
3. Atomarer Block…
4. Ursprünglichen Zustand der Interrupts wieder herstellen!

Man kann ja noch argumentieren, dass man als einsamer Entwickler weiß, 
wann die Interrupts an oder aus sind. Aber bei einem System mit vielen 
Entwicklern und Betriebszuständen (Bootloader, Calibrationmode, 
Errormode usw…) überblickt keiner mehr, wann wo Interrupts an/aus sind 
und dass dann bestimmte Funktionen (oder Unterfunktionen von 
Unterfunktionen….) nicht genutzt werden dürfen, da sie von sich aus 
Interrupts aktivieren…

Für IAR gibt es die folgende Intrinsic Funktionen auch für ARM
1. __istate_t  interruptstate = __get_interrupt_state();
2.    __disable_interrupt();
3.  // Atomarer Block…
4.  __set_interrupt_state(interruptstate);


Im Definitive Guide to ARM® Cortex®-M3 and Cortex®-M4 Processors 
empfehlen sie die Verwendung von __get_PRIMASK() und __set_PRIMASK().
Eine Info dazu findet sich in
https://devzone.nordicsemi.com/question/47493/disable-interrupts-and-enable-interrupts-if-they-where-enabled/

Was mich wundert: ist von Euch noch niemand über dieses Problem 
gestolpert?
Ich hatte diesen Fehler schon mehrfach entdeckt und halte ihn für sehr 
gefährlich.

von Falk B. (falk)


Lesenswert?

@  Marc (Gast)

>Man darf auf keinen Fall am Ende eines atomaren Blocks den Interrupt
>unkontrolliert wieder aktivieren!

Doch, wenn man sicher ist, daß er vorher aktiv war ;-)

Die atomic.h des avr gcc bietet dafür 2 Möglichkeiten

1
22.26.2.2 #define ATOMIC_FORCEON
2
This is a possible parameter for ATOMIC_BLOCK. When used, it will cause the
3
ATOMIC_BLOCK to force the state of the SREG register on exit, enabling the Global
4
Interrupt Status flag bit. This saves on flash space as the previous value of the SREG
5
register does not need to be saved at the start of the block.
6
Care should be taken that ATOMIC_FORCEON is only used when it is known that
7
interrupts are enabled before the block’s execution or when the side effects of enabling
8
global interrupts at the block’s completion are known and understood.
9
22.26.2.3 #define ATOMIC_RESTORESTATE
10
This is a possible parameter for ATOMIC_BLOCK. When used, it will cause the
11
ATOMIC_BLOCK to restore the previous state of the SREG register, saved before
12
the Global Interrupt Status flag bit was disabled. The net effect of this is to make
13
Generated on Wed Jan 6 12:08:48 2010 for avr-libc by Doxygen
14
22.27 <util/crc16.h>: CRC Computations 292
15
the ATOMIC_BLOCK’s contents guaranteed atomic, without changing the state of the
16
Global Interrupt Status flag when execution of the block completes.

von Dr. Sommer (Gast)


Lesenswert?

Marc schrieb:
> Was mich wundert: ist von Euch noch niemand über dieses Problem
> gestolpert?
Doch, seit 2013 schon... Man könnte es so machen:
1
class IRQLocker {
2
  private:
3
    uint32_t m;
4
  public:
5
    inline IRQLocker () { m = __get_PRIMASK (); __disable_irq (); }
6
    inline ~IRQLocker () { if (m == 0) __enable_irq (); }
7
};
8
9
template <typename F>
10
inline void doAtomic (F f) {
11
  IRQLocker l;
12
  f ();
13
}
14
15
void test1 () {
16
  doAtomic ([] () {
17
    /* tu was atomisches */
18
  });
19
}
20
21
void test2 () {
22
  {
23
    IRQLocker l;
24
    /* tu was atomisches */
25
  } // bei der Schließenden Klammer erfolgt das entsperren
26
}

Diese Variante mit extra Klasse hat den Vorteil, dass das entsperren 
auch funktioniert wenn man Exceptions verwendet (RAII-Style). Das Lambda 
braucht man dann nicht mehr unbedingt, wie in test2 zu sehen ist.

von Marc (Gast)


Lesenswert?

Falk B. schrieb:
> @  Marc (Gast)
> Doch, wenn man sicher ist, daß er vorher aktiv war ;-)
Siehe meinen 3. Absatz...

> Die atomic.h des avr gcc bietet dafür 2 Möglichkeiten
Wir sind hier im Thread: atomic-lib für ***stm32***

Gruß
Marc

von Marc (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Marc schrieb:
>> Was mich wundert: ist von Euch noch niemand über dieses Problem
>> gestolpert?
> Doch, seit 2013 schon... Man könnte es so machen:
> class IRQLocker {
}

Jetzt bin ich beruhigt, dass ich nicht der Einzige bin.

Und eines muss ich Ihnen Herr Dr. Sommer jetzt offenbaren:

    Herr Dr. Sommer:
    ich liebe Sie für ihre guten
    Tipps & Informationen zu C++
    auf embedded systemen.


Nachtrag: zum Thema allgemein gab es schon einen Thread mit C 
Beispielcode:
Beitrag "Interrupts ein-/ausschalten beim ARM cortex-M3"

von Philipp S. (schroedi)


Lesenswert?

Marc schrieb:
> Ihr baut die tollsten Konstrukte aber das ganze Konzept hat einen
> eklatanten systematischen Fehler:
> Man darf auf keinen Fall am Ende eines atomaren Blocks den Interrupt
> unkontrolliert wieder aktivieren!

Doch, wird berücksichtigt.
1
... __prim = __get_PRIMASK() ...

von Jan K. (jan_k)


Lesenswert?

A. K. hat es oben zwar schon erwähnt, aber ich finde die Idee wichtig 
und schreibe es deswegen nochmal:

Es müssen nicht zwingend die Interrupts (partiell) ein-/ausgeschaltet 
werden! LDREX und das Pendant STREX gibt es als intrinsics (__LDREX bzw 
__ldrex), womit man super toll selbst atomare Funktionen bauen kann.

Oft läuft die kritische Sektion doch nur auf das Verändern von 1, 2 
Variablen (Zähler, Indizes, etc) Variablen hinaus, da kann man sich 
genau so gut so etwas in die Richtung schreiben (ungetestet, ob das mit 
dem void Zeiger so legitim ist müsste ich nachgucken)
1
int ATOMIC_IncrementVar(void * var)
2
{
3
  int32_t count;
4
  int32_t status;
5
  do
6
  {
7
    count = (int32_t) __ldrex(  (uint32_t *)(var) ); // load variable address exclusively
8
    status = __strex( ++count, (uint32_t *)(var) ); // status != 0, if no exclusive access was guaranteed
9
  }
10
  while ( status != 0 ); // 0 = success
11
  __dmb( 10 );        // recommended by arm -> make sure every memory access is done
12
  return ATOMIC_SUCCESS;
13
}

Der Vorteil ist, dass man im Fehlerfall auch returnen könnte, dann weiß 
man halt, dass die Variable nicht gesetzt wurde oder wie oben 
blockieren, bis es funktioniert.

Klar, für den Zugriff auf exklusive Ressourcen muss man anders vorgehen, 
aber da hilft auch ein atomic block von oben nicht (man könnte aber 
ATOMIC_IncrementVar nehmen, um einen Mutex/Semaphore zu bauen).

von W.S. (Gast)


Lesenswert?

Marc schrieb:
> Was mich wundert: ist von Euch noch niemand über dieses Problem
> gestolpert?

Ähemm..nö.

Bei mir läuft der normale Kram, also main und alles, was darin 
aufgerufen wird, im Usermodus mit grundsätzlich allen Interrupts offen 
und erlaubt. Und mir ist es noch nie untergekommen, daß ich mal das 
Bedürfnis hatte, die Interrupts zeitweise abzuschalten. Eher denke ich 
an andere Synchronisier-Mechanismen als an exclusiven Zugriff zweier 
oder mehrerer Programmteile auf die selbe Ressource.

Wenn man schon irgendwas exclusiv machen will, dann eher mit nem SVC. 
Dessen Inhalt läuft dann im Supervisor-Modus und den kann man von 
vornherein so hoch ansetzen, daß er ungestört läuft. Ist beim Keil recht 
easy zu machen, aber beim GCC braucht es wieder mal nen 
Assembler-Wrapper dafür.

W.S.

von Marc (Gast)


Lesenswert?

Jan K. schrieb:
> Es müssen nicht zwingend die Interrupts (partiell) ein-/ausgeschaltet
> werden! LDREX und das Pendant STREX gibt es als intrinsics (__LDREX bzw
> __ldrex), womit man super toll selbst atomare Funktionen bauen kann.

> ATOMIC_IncrementVar(void * var)


Du hast recht, man sollte vor dem Design erst mal alle 
Lösungsmöglichkeiten abwägen.  Ich setze auch LDREX / STREX ein, je nach 
Problemstellung.

Die Idee deiner Funktion ist gut, ich bin allerdings kein Freund von 
Parameterübergabe mittels void*
Der Compiler wirft keine Warnungen und mein schlampiger Kollege (oder 
ich nach einer langen Nacht…) bauen einen Aufruf mit einem 8 oder 16 Bit 
Parameter…

Und das Unheil nimmt seinen Lauf…

von Jan K. (jan_k)


Lesenswert?

Marc schrieb:
> Die Idee deiner Funktion ist gut, ich bin allerdings kein Freund von
> Parameterübergabe mittels void*
> Der Compiler wirft keine Warnungen und mein schlampiger Kollege (oder
> ich nach einer langen Nacht…) bauen einen Aufruf mit einem 8 oder 16 Bit
> Parameter…
>
> Und das Unheil nimmt seinen Lauf…

Stimmt stimmt, wollte das zuerst generisch aufziehen. Vielleicht sollte 
man das ganze noch kapseln oder halt für jeden Datentyp eine Funktion 
erstellen. Und dann den void Pointer natürlich weg machen ;-)

von Marc (Gast)


Lesenswert?

Vermutlich wäre eine Lösung für jeden Datentyp am sichersten.
(Noch schöner in C++ mit Templates...)

Und statt einem counter++ einen zusätzlichen Parameter, dann kann man 
fexibel addieren/subtrahieren. Beispielsweise so (habe die 
Variablennamen noch etwas angepasst):
1
bool ATOMIC_int32Add(int32_t *var, int32_t off)
2
{
3
  int32_t tempVal;
4
  int32_t lockedOut;
5
  do
6
  {
7
    tempVal = (int32_t) __LDREX((unsigned long *)var); 
8
    tempVal += off;
9
    lockedOut = __STREX(tempVal,(unsigned long *)var); 
10
  }
11
  while ( lockedOut ); // lockedOut: 0==Success, 1==instruction is locked out
12
  __DMB(10);  // recommended by arm -> make sure every memory access is done
13
  return true;
14
}

Den Rückgabewert könnte man noch verwerfen... Die Frage ist, ob man noch 
ein Abbruchkriterium einbaut wenn die Schleife X mal durchlaufen wurde.


Dazu eine Frage an die Leute, die dieses Verfahren schon mehrfach 
genutzt haben:

In welchen Fällen kam es zu Problemen (z.B. durch DMA Zugriffe in den 
Speicher), die dazu führten, dass __STREX immer ein "locked out" 
meldete?

Da ist ein  __disable_interrupt().... deterministischer. Was ist eure 
Erfahrung?

von (prx) A. K. (prx)


Lesenswert?

Und wenn ldrex/strex nicht weiterhelfen, dann kann man bei den Cortex M 
dennoch um die Abschaltung aller Interrupts herum kommen, indem man die 
Priorität nur so weit verändert, dass nur die relevanten Interrupts 
gesperrt werden. Dank des recht exakt dafür implementierten Registers 
BASEPRI_MAX geht das ausgesprochen einfach.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Marc schrieb:
> Da ist ein  __disable_interrupt().... deterministischer.

Sehr deterministisch und im Fall von DMA auch sehr sinnlos. Gegen DMA 
hilft die Abschaltung von Interrupts überhaupt nicht. Bei mehreren Cores 
sieht es ähnlich aus.

Wenn man die Sorge hat, in diese Falle laufen zu können, dann kann man 
solche Spinlocks auch ent-determinisieren, indem das Verhalten (Timing) 
abhängig von einem Durchlaufzähler variiert.

: Bearbeitet durch User
von Marc (Gast)


Lesenswert?

A. K. schrieb:
>> Da ist ein  __disable_interrupt().... deterministischer.
> Sehr deterministisch und im Fall von DMA auch sehr sinnlos.

Ich denke dass ein Disabling .... Enabling deterministischer sein könnte 
als eine Schleife (ich sage nicht, dass es immer so ist...)

Szenario:
An verschiedenen Stellen im Code (auch in Interrupts) müssen mehrere 
Variablen modifiziert werden.  Beim Disabling kann ich genau bestimmen 
wie lange die Interrupts maximal gesperrt sind.
Bei LDREX / STREX geht das nur mit einer zusätzlichen Abbruchbedingung. 
Aber die Ausführungszeit schwankt eben (1 ... n Schleifendurchläufe) und 
ich muss danach prüfen ob es zum Abbruch kam

Vermutlich bin ich zu pessimistisch, bisher hatte ich keinen kritischen 
Spinlock.Daher eben meine Frage oben.

Anders formuliert: hatte jemand bei LDRES / STRED größere Probleme und 
was war die Ursache?

von (prx) A. K. (prx)


Lesenswert?

Marc schrieb:
> Ich denke dass ein Disabling .... Enabling deterministischer sein könnte
> als eine Schleife (ich sage nicht, dass es immer so ist...)

Interrupts abzuschalten macht das Zeitverhalten in dieser Phase 
deterministisch. Andererseits vergrössert sich der variable Anteil der 
Latenz von Interrupts, die Latenz wird also weniger deterministisch. 
Letztlich eine Abwägung, wo man den Schwerpunkt setzt.

Bei Forderung von taktgenauem Zeitverhalten im Hauptprogramm bei darin 
enthaltenen Zugriffen auf solche Variablen ist die Abschaltung natürlich 
obligatorisch. Das ist aber ein eher ungewöhnliches Szenario, zumal in 
komplexen Controller-Anwendungen mit etlichen Interrupts und ggf. DMA.

> An verschiedenen Stellen im Code (auch in Interrupts) müssen mehrere
> Variablen modifiziert werden.

So lange alle zu einer Variablen gehörenden ISRs die gleiche preemption 
priority haben, ist in ISRs kein ldrex/strex-Verfahren erforderlich. 
Diese Interrupts sind davon also nicht betroffen.

NB: Wer sich bei dem Cortex M fragt, wieso die Priorisierung des NVIC 
nicht durchweg preemptive ist, der hat hier eine Antwort.

> Bei LDREX / STREX geht das nur mit einer zusätzlichen Abbruchbedingung.
> Aber die Ausführungszeit schwankt eben (1 ... n Schleifendurchläufe) und
> ich muss danach prüfen ob es zum Abbruch kam

Kannst du ein im relevanten Rahmen zeitkritisches Szenario entwerfen, in 
dem ein solches pro Durchlauf nur wenige Takte dauernde Spinlock mehr 
als einige wenige Versuche benötigt, am Ende gar in ein Deadlock 
reinlaufen kann?

Ich kann mir dafür nur Szenarien vorstellen, in denen das Programm fast 
die gesamte Zeit in Interrupts verbringt.

: Bearbeitet durch User
von Marc (Gast)


Lesenswert?

Danke A. K. für deine Infos.

>Ich kann mir dafür nur Szenarien vorstellen, in denen das Programm fast
die gesamte Zeit in Interrupts verbringt.

Meine Bedenken gingen eher Richtung DMA.
Szenario: Ein Stück Code wird in einem hoch priorisierten Interrupt 
ausgeführt. Die ISR soll möglichst kurz sein (eben um die Latenz für 
andere ISRs... nicht unnötig zu erhöhen). Parallel läuft aber ein DMA 
Schreibzyklus, der dazu führt, dass LDREX/STREX x Schleifen durchläuft.

Mmmm, ich glaube ich versuche mich mal an Testcode. Kann für's 
Verständnis nie schaden...

von (prx) A. K. (prx)


Lesenswert?

Marc schrieb:
> Parallel läuft aber ein DMA
> Schreibzyklus, der dazu führt, dass LDREX/STREX x Schleifen durchläuft.

Die ARMv7-M Referenz erweckt nicht den Eindruck, dass diese Befehle 
geeignet sind, sich gegen Zugriffe eines einfachen DMA Controllers für 
Peripherie-Transfers auf die gleiche Adresse abzusichern.

Ich gehe davon aus, dass solche DMA Zugriffe auf ausreichend 
verschiedene Adressen dem entsprechenden Monitor nicht ins Gehege 
kommen. Andernfalls wäre intensives DMA mit LDREX/STREX prinzipiell 
unverträglich.

Anders sieht es bei konkurrierenden Zugriffen mehrerer Cores aus. Da 
allerdings kommt man mit der Abschaltung von Interrupts nicht weiter und 
muss mit solchen Verfahren arbeiten. Da wird es dann auch etwas 
komplizierter.

: Bearbeitet durch User
von Jan K. (jan_k)


Lesenswert?

Es gibt hier z.B. einige Infos zu den Exclusiven Monitoren: 
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/CJAGCFAF.html

Und da steht konkret, wie er im M3 und M4 implementiert ist: 
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka16180.html

Und das ist hier das Manual zu der ARMv7-M Architektur, alles ab A3.4 
wird interessant. 
https://www.google.de/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0ahUKEwiKwrae_6fMAhVUGsAKHZpUAGkQFgggMAA&url=https%3A%2F%2Fweb.eecs.umich.edu%2F~prabal%2Fteaching%2Feecs373-f10%2Freadings%2FARMv7-M_ARM.pdf&usg=AFQjCNHNAdnUhWjznYcJGCLNNdzgMGQmgw&sig2=bu77UAoozA51G2cblKD8Bg

Guckt man sich das state machine diagram in figure A3-2 an, steht da nur 
was von Store und StoreExcl instructions, werden diese vom DMA benutzt? 
Wenn nein, sollte der DMA kein Problem machen.

Edit: Oder meinst du echt einen Zugriff auf gleiche Adressen? Ich dachte 
eher du meintest eine Endlosschleife, die auf Grund von false negatives 
des LDREX Befehls entstehen.

Noch ein edit: der Cortex M3 scheint nicht einzelne Speicherbereiche zu 
taggen, sondern guckt nur, ob einfach, ob der monitor im exclusive 
Zustand ist und verweigert ggf das exklusive Schreiben

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Jan K. schrieb:
> Noch ein edit: der Cortex M3 scheint nicht einzelne Speicherbereiche zu
> taggen, sondern guckt nur, ob einfach, ob der monitor im exclusive
> Zustand ist und verweigert ggf das exklusive Schreiben

Zumindest ist das entsprechend der Fussnote in ARMv7-M zulässig: "The 
IMPLEMENTATION DEFINED options for the local monitor are consistent with 
the local monitor being constructed so that it does not hold any 
physical address, but instead treats any access as matching the address 
of the previous LDREX. In such an implementation, the Exclusives 
reservation granule defined in Tagging and the size of the tagged memory 
block on page A3-75 is the entire memory address range."

Über das konkrete Verhalten vom CM3 Core finde ich grad keine Details. 
Beim CM4 und CM7 steht das aber ausdrücklich so drin, also wird es beim 
CM3 wohl auch so sein.

In diesem Fall hätte man dann aber doch ein DMA Problem, sofern der DMA 
Controller in diesem Sinn als "observer" gilt und beim DMA Zugriff 
folglich unabhängig von der Adresse dies zutrifft: "It is UNPREDICTABLE 
whether a store to a tagged physical address causes a tag in the local 
monitor to be cleared if that store is by an observer other than the one 
that caused the physical address to be tagged."

Bei dieser Kaffeesatzleserei bleibt mir vorerst die Erkenntnis, dass 
sich daraus keine zweifelsfreie Aussage über DMA ablesen lässt. Und 
eigentlich nur die heuristische Logik verbleibt, dass ein DMA Controller 
als "observer" arg wenig Sinn ergibt.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Ok, hier wird es klarer:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka16180.html

"For a design containing a single processor and some other agent such as 
a DMA controller, it is acceptable to tie EXRESPx = 0 if software which 
makes use of semaphore operations can ensure that the other agent is not 
accessing that address range while the semaphore operation is in 
progress."

M.a.W: Bei Controllern mit einheitlich von DMA ansprechbarem RAM wird 
der Zustand des Monitors von DMA wie erwartet nicht beeinflusst.

Bei Controllern mit verschiedenen nur teilweise von DMA ansprechbaren 
RAM-Blöcken oder -Adressräumen könnte angebracht sein, diese Variablen 
im non-DMA-RAM zu platzieren. Für den Fall, dass der Controller sich 
unwahrscheinlicherweise die Mühe macht, die Adressräume beim STREX 
auseinander zu halten. Da müsste man sonst bei ST&Co nachschlagen.

: Bearbeitet durch User
von Jan K. (jan_k)


Lesenswert?

Noch eine andere Sache:
In 
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0375g/chr1359124997286.html 
steht, dass es sein kann, dass der Compiler store Befehle zwischen 
ldrex/strex packen kann. Das wäre eine blöde Sache, weil das exklusive 
Schreiben dann nie funktionieren würde. Ich vermute, dass die 
Quintessenz daraus ist, die Funktion vermutlich doch wieder als 
Assembler umzusetzen...

Darüberhinaus ist der intrinsic Befehl als deprecated markiert, weiß da 
jmd was drüber?

Die Passage tritt erst bei armcc v5.06 auf, vorher nicht:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0376d/CJAECCJJ.html 
(v5.05)

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Jan K. schrieb:
> steht, dass es sein kann, dass der Compiler store Befehle zwischen
> ldrex/strex packen kann.

Normale Store-Befehle auf andere Variablen sind im hier betrachteten 
Rahmen (single core, local monitor) sicherlich kein Problem. Der Monitor 
wird die ebenso wenig mitkriegen wie die DMA Zugriffe.

: Bearbeitet durch User
von Jan K. (jan_k)


Lesenswert?

Ich hab das so verstanden, dass jeder store Befehl den monitor 
resetten KANN und dieser eben nicht nur auf eine Adresse guckt:
"The local monitor's Exclusives Reservation Granule is 4GB, meaning that 
no address information is stored in the local monitor."

Somit würde selbst bei einem store auf eine andere Speicheradresse nie 
ein erfolgreiches exklusives Schreiben resultieren.

"When a processor writes using any instruction other than a 
Store-Exclusive:
• if the write is to a physical address that is not covered by its local 
monitor the write does not affect
the state of the local monitor
• if the write is to a physical address that is covered by its local 
monitor it is IMPLEMENTATION DEFINED
whether the write affects the state of the local monitor.
If the local monitor is in its Exclusive Access state and a processor 
performs a Store-Exclusive to any
address other than the last one from which it has performed a 
Load-Exclusive, it is IMPLEMENTATION
DEFINED whether the store succeeds, but in all cases the local monitor 
is reset to its Open Access state. In
ARMv7-M, the store must be treated as a software programming error.
"

Und "The compiler does not guarantee that it will preserve the state of 
the exclusive monitor. It may generate load and store instructions 
between the LDREX instruction generated for the __ldrex intrinsic and 
the STREX instruction generated for the __strex intrinsic. Because 
memory accesses can clear the exclusive monitor, code using the __ldrex 
and __strex intrinsics can have unexpected behavior. Where LDREX and 
STREX instructions are needed, ARM recommends using embedded assembly."

Bin ich schief gewickelt? =)

von (prx) A. K. (prx)


Lesenswert?

Jan K. schrieb:
> Ich hab das so verstanden, dass jeder store Befehl den monitor
> resetten KANN und dieser eben nicht nur auf eine Adresse guckt:

Ist implementation defined. Da das hier aber ausdrücklich als absolute 
Minimalimplementierung ausgeführt ist, um LDREX/STREX in dieser Rolle 
überhaupt nutzen zu können, wird es darauf hinauslaufen, dass der 
Monitor von anderen Stores als STREX überhaupt nichts mitbekommt. Die 
Alternative wäre aufwendiger und hätte keinerlei Mehrwert.

: Bearbeitet durch User
von M. K. (kichi)


Lesenswert?

Leo B. schrieb:
> wollte mal fragen, ob jemand eine atomic-lib, wie es sie für den AVR-GCC
> gibt
> (http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html),
> auch für die STM32 kennt.

Philipp S. schrieb:
> #define ATOMIC_BLOCK for( uint32_t __cond = 1, __prim = __get_PRIMASK();
> __cond != 0 ? __disable_irq() : 0, __cond != 0; __prim == 0 ?
> __enable_irq() : 0, __cond = 0 )

Der Vollständigkeit halber:
1
#define ATOMIC_BLOCK(type) for( uint32_t __cond = 1, __prim = __get_PRIMASK(); \
2
                                __cond != 0 ? __disable_irq() : 0, __cond != 0; \
3
                                (type & __prim) == 0 ? __enable_irq() : 0, __cond = 0 )
4
#define ATOMIC_FORCEON          0
5
#define ATOMIC_RESTORESTATE     1
sollte sich Verhalten wie das AVR-Pendant.

Alternativ hat sich schonmal jemand die Mühe gemacht die ganze atomic.h 
zu portieren:
https://github.com/PaulStoffregen/cores/blob/master/teensy3/util/atomic.h

: Bearbeitet durch User
von M. K. (kichi)


Lesenswert?

1
#define ATOMIC_BLOCK(type) for( uint32_t __cond = 1, __prim = __get_PRIMASK(); \
2
                                __cond != 0 ? __disable_irq() : 0, __cond != 0; \
3
                                (type && __prim) == 0 ? __enable_irq() : 0, __cond = 0 )
4
#define ATOMIC_FORCEON          0
5
#define ATOMIC_RESTORESTATE     1
bietet dem Compiler noch die Möglichkeit zu optimieren.
Man beachte "(type && __prim)" statt "(type & __prim)".

: Bearbeitet durch User
von Adib (Gast)


Lesenswert?

Hallo Leute,

das sieht allles richtig gut aus.

Wie muss man das schreiben, um die BASEPRIO temporär zu setzen?

Danke und Grüße, Adib.

von M. K. (kichi)


Lesenswert?

Adib schrieb:
> Wie muss man das schreiben, um die BASEPRIO temporär zu setzen?

Falls du das im Zusammenhang mit den ATOMIC_BLOCKs meinst, versuch mal
1
#define ATOMIC_BLOCK(prio) for( uint32_t __cond = 1, __prim = __get_BASEPRI(); \
2
                                __cond != 0 ? __set_BASEPRI(prio) : 0, __cond != 0; \
3
                                __set_BASEPRI(__prim), __cond = 0 )
das sollte sich den alten Werk merken, einen neuen setzen (den du mit 
"prio" übergeben musst) und anschließend den alten wiederherstellen. 
Getestet habe ich es ehrlich gesagt nicht, müsste aber funktionieren.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Wenn man anfangs BASEPRI_MAX statt BASEPRI setzt, dann vermeidet man das 
Risiko, die Prio versehentlich zu reduzieren. Das könnte sonst 
passieren, wenn man diesen Code in ISRs oder verschachtelt verwendet.
1
#define ATOMIC_BLOCK(prio) for( uint32_t __cond = 1, __prio = __get_BASEPRI(); \
2
                                __cond != 0 ? __set_BASEPRI_MAX(prio) : 0, __cond != 0; \
3
                                __set_BASEPRI(__prio), __cond = 0 )

: Bearbeitet durch User
von Adib (Gast)


Lesenswert?

Hallo Leute,

mein Keil Compiler kommt offensichtlich nicht mit dem Konstrukt hier 
klar:
1
__cond != 0 ? __set_BASEPRI_MAX(prio) : 0

Es gibt eine komische Fehlermeldung:
1
TOMIC ....

Also das "A" wird vom "TOMIC" getrennt ???
Es hat wohl was mit der Anweisung ohne return __set_BASEPRI_MAX zu tun.

Danke und Grüße, Adib.
--

von (prx) A. K. (prx)


Lesenswert?

Yep, dieser Ausdruck ist kein korrektes C, weil links vom : vom Typ 
void. Probier mal:
  __cond != 0 ? (__set_BASEPRI_MAX(prio), 0) : 0

von Adib (Gast)


Lesenswert?

Danke, funzt. Adib.

von M. K. (kichi)


Angehängte Dateien:

Lesenswert?

Ich habe das mal wiederverwertbar in einer Header-Datei zusammengefasst,
vielleicht kann es ja jemand brauchen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Leo B. schrieb:
> Hey Leute,
>
> wollte mal fragen, ob jemand eine atomic-lib, wie es sie für den AVR-GCC
> gibt
> (http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html),
> auch für die STM32 kennt. Compiler soll auch hier der GCC sein falls das
> wichtig ist.
>
> Ich konnte leider nichts passendes im Internet finden,

Na du HAST doch bereits was gefunden, nämlich util/atomic.h der 
AVR-LibC:  Es ist nämlich lediglich ein Header und KEINE Bibliothek.

http://svn.savannah.nongnu.org/viewvc/trunk/avr-libc/include/util/atomic.h?root=avr-libc&view=markup

Der Header ist dann für deine Maschine anzupassen, insbesondere die 
weiter unten im Headetr verwendeten
1
/* Internal helper functions. */
2
static __inline__ uint8_t __iSeiRetVal(void)
3
{
4
    sei();
5
    return 1;
6
}
7
8
static __inline__ uint8_t __iCliRetVal(void)
9
{
10
    cli();
11
    return 1;
12
}
13
14
static __inline__ void __iSeiParam(const uint8_t *__s)
15
{
16
    sei();
17
    __asm__ volatile ("" ::: "memory");
18
    (void)__s;
19
}
20
21
static __inline__ void __iCliParam(const uint8_t *__s)
22
{
23
    cli();
24
    __asm__ volatile ("" ::: "memory");
25
    (void)__s;
26
}
27
28
static __inline__ void __iRestore(const  uint8_t *__s)
29
{
30
    SREG = *__s;
31
    __asm__ volatile ("" ::: "memory");
32
}

Was du anpassen musst:

sei(): IRQs global zulassen (I-Flag := 1)

cli(): IRQs global deaktivieren (I-Flag := 0)

SREG: Bei AVR ein 8-Bit SFR, über welches das I-Flag gelesen und
      geschrieben werden kann; bei STM geht das wohl über Support
      Funktionen wie __set_BASEPRI und __get_BASEPRI

uint8_t: Bei STM vermutlich ein 32-Bit Wert, also der Return-Typ von
      __get_BASEPRI

#include <avr/interrupt.h>: Wird nicht mehr gebraucht.
#include <avr/io.h>: dito

#include <stdint.h>: Neues Include falls uint32_t o.ä. verwendet werden
         sollen.

Und last not least Copyright beachten, d.h. das entsprechende Intro 
kopieren, und wenn du willst dich unter Dean Camera verewigen :-)

von technikus (Gast)


Lesenswert?

Michael K. schrieb:
> Ich habe das mal wiederverwertbar in einer Header-Datei
> zusammengefasst,
> vielleicht kann es ja jemand brauchen.

Mein Compiler motzt hier:
1
#define ATOMIC_BLOCK(type) for( uint32_t __cond = 1, __prim = __get_PRIMASK(); \
2
                                __cond != 0 ? __disable_irq() : 0, __cond != 0; \
3
                                (type && __prim) == 0 ? __enable_irq() : 0, __cond = 0 )

Schweregrad  Code  Beschreibung  Projekt  Datei  Zeile 
Unterdrückungszustand
Fehler    second operand to the conditional operator is of type 'void', 
but the third operand is neither a throw-expression nor of type 'void' 
Debounce  C:\Users\daniel\Desktop\Debounce\Debounce\Debounce\cm_atomic.h 
80

Er schein __disable_irq() und __enable_irq() nicht zu kennen.

Könnt ihr mir weiter helfen?

von eagle user (Gast)


Lesenswert?

Nett :) Wenn der Compiler __disable_irq() nicht kennen würde, gäbe es 
den Fehler nicht! Unbekannte Funktionen liefern int und nicht void. A.K. 
hat das Problem schon ca. 5 Beiträge weiter oben erklärt:
Beitrag "Re: atomic-lib für stm32"

von M. K. (kichi)


Lesenswert?

technikus schrieb:
> Könnt ihr mir weiter helfen?

eagle user schrieb:
> A.K. hat das Problem schon ca. 5 Beiträge weiter oben erklärt:
> Beitrag "Re: atomic-lib für stm32"

Stimmt, da fehlt was. Mach mal
1
#define ATOMIC_BLOCK(type) for( uint32_t __cond = 1, __prim = __get_PRIMASK(); \
2
                                __cond != 0 ? (__disable_irq(), 0) : 0, __cond != 0; \
3
                                (type && __prim) == 0 ? __enable_irq() : 0, __cond = 0 )
draus. Habe grad keinen Compiler zur Hand, ist also ungetestet. 
Vermutlich braucht es das bei __enable_irq() dann auch noch, also:
1
(__enable_irq(), 0)

von technikus (Gast)


Lesenswert?

Passt so.
Danke

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.