Kann man bei den AVR-Mikrocontrollern PORTC |= xx, PORTC &= xx und PINC
= xxx (Toggle) schreiben, wenn Interrupt-Code im Programm ist, der
gleichzeitig andere Bits in PORTC ändert?
Es ginge nicht, wenn dieser C-Befehle zu zwei Befehlen führen (Port
lesen und schreiben), aber das ist nicht der Fall, oder?
Falls der Zäh-Compiler diese Befehle "richtig" umsetzt, sollte es gehen.
Schau doch mal in die Befehlsreferenz zum AVR um zu sehen, was geht.
So weit mir bekannt, gehen ?= mit einem Register und einer Konstanten.
Wie die Werte aber in das (Vergleichs-)Register kommen ist eine andere
Sache.
Gerd schrieb:> Es ginge nicht, wenn dieser C-Befehle zu zwei Befehlen führen
Das kannst du im Assemblerlisting überprüfen und dir so selber
beantworten.
Gerd schrieb:> Es ginge nicht, wenn dieser C-Befehle zu zwei Befehlen führen
Er führt sogar zu noch viel mehr Befehlen, weil nach dem Lesen und vor
dem Schreiben der Wert noch Verändert werden muss: Read-MODIFY-Write.
A. K. schrieb:> Bei Ports 0..31 und festem Einzelbit ist &= und |= atomar, sonst nicht.
Das wird dann umgesetzt mit den Befehlen sbi und cbi
Wie soll ein Compiler die die Grenzen eines AVR sprengen?
Wir wissen nichtmal, um welches AVR es geht. Jedoch werden die Ports
über Adressregister auf dem Datenbus angesprochen. Es gibt zwar SBR und
CBR Maschinenbefehle (einzelnes Bits setzen), die jedoch nur für
Register in der CPU wirken.
Man muss die Ports immer erst auslesen, Bit ändern und das Ergebnis
zurückschreiben.
Zumindest ist das bei einem Atmega8 etc. so.
Gerd schrieb:> Es ginge nicht, wenn dieser C-Befehle zu zwei Befehlen führen (Port> lesen und schreiben), aber das ist nicht der Fall, oder?
Kommt (mindestens) auf den Port und den Controller an. Bei Hochsprachen
auch noch auf die Intelligenz des Codegenerators.
Hast du uns alles nicht verraten, also musst du die Datenblätter selbst
lesen. Also das tun, was du sowieso hättest tun sollen, bevor du
wildfremde Leute mit solchen Trivialitäten behelligst...
Ich verstehe keinen Assemböer. Hier der Code, wenn jemand helfen möchte.
Es geht um PORTC vom ATMega1284: An dem läuft Software-SPI (s.u.) und
andere Bits werden in Interrupts geändert. Im C-Code für SPI verändern
die Makros SOFTSPI_SCK_TOGGLE(), SOFTSPI_MOSI_HIGH(), SOFTSPI_MOSI_LOW()
den PORTC. CLI() notwendig?
Disassembly of section .text.softspi_write_byte:
Gerd schrieb:> Kann man bei den AVR-Mikrocontrollern PORTC |= xx, PORTC &= xx und PINC> = xxx (Toggle) schreiben, wenn Interrupt-Code im Programm ist, der> gleichzeitig andere Bits in PORTC ändert?
SFR = val
ist atomar falls SFR ein 8-Bit SFR ist, unabhängig von Optimierungsstufe
und Adresse des SFR.
PORTC |= xx
PORTC &= xx
Notwendige Bedingungen, dass das atomar ist (avr-gcc):
* PORTC ist ein 8-Bit SFR mit einer Adresse von __AVR_SFR_OFFSET__ bis
__AVR_SFR_OFFSET__ + 0x1f, und die Adresse muss zur Compilezeit bekannt
sein. (__AVR_SFR_OFFSET__ ist ein Built-in Makro von avr-gcc.)
Alternativ kann PORTC eine Deklaration mit Attribut io_low sein.
* xx ist zur Compilerzeit bekannt, und xx ist exakte 2-er Potenz von
0x1..0x80 (Fall |=) oder das 1-er Komplement einer solchen 2-er Potenz
(Fall &=).
* Optimierung ist nicht -O0 oder -Og.
Es gibt jedoch ältere Compilerversionen, wo dies nicht hinreichend
ist, z.B. wird bei 2× hintereinander SFR &= 0x7f das 0x7f erst in ein
Register geladen, was dann zu LDI + 2*(IN+AND+OUT) führt.
Bei aktuellen Versionen (ab ca. 4.7) hab ich das noch nicht beobachtet,
aber generell ist das nicht definitiv auszuschließen.
Eigentlich müsste sich mal jemand die Mühe machen, entsprechende atomic
Built-ins zu implementieren, die dann ggf. LAS, LAT verwenden oder
Interrupts temporär deaktivieren falls eine der obigen Bedingungen nicht
erfüllt ist, z.B. xx keine 2-er Potenz ist oder Werte nicht zur
Compiletime bekannt sind.
Es ist unsauberer Stil, wenn du in einem Interrupt auf eine
Hardwareressource schreibend zugreifst, welche auch im normalen
Programmfluss verändert wird.
Wenn du sämtlichen hardwarezugriff im Interrupt verhinderst, bist du aus
der Sache raus. oder aber der Hardwarezugriff auf diese Ressource
passiert ausschlißelich im Interrupt.
Gerd schrieb:> ... Makros ...> CLI() notwendig?
Ja, sieht so aus. Denn xx ist nicht das 1-er Komplement einer 2-er
Potenz, eine der o.g genannten notwendigen Voraussetzungen:
> 14: 88 b1 in r24, 0x08 ; 8> 16: 8c 7f andi r24, 0xFC ; 252> 18: 88 b9 out 0x08, r24 ; 8
0xFC = 0b11111100 ist nicht darstellbar als 0xff ^ (1 << n).
Die Makro-Definitionen wurden nicht genannt.
Bla bla schrieb:> Es ist unsauberer Stil, wenn du in einem Interrupt auf eine> Hardwareressource schreibend zugreifst, welche auch im normalen> Programmfluss verändert wird.
Sind mehrere Portpins mit völlig verschiedener Funktion verschiedener
Module eine gemeinsame Ressource? Alternative?
>Das kannst du im Assemblerlisting überprüfen und dir so selber>beantworten.
Das würde ich so nicht unterschreiben, Du hast damit nur einen
speziellen Fall geprüft.
Vor 5-10 Jahren. Kundenprojekt Microchip PIC + HiTech C-Compiler. Ein
1
BitStructIrgendwas.Bit42=1;
Wurde vom Compiler IM GLEICHEN Programm auf 4 verschiedenen Arten
umgesetzt.
Dabei war sowohl ein:
assembler_bit_set byte007.4
als auch ein:
assembler_byte007 = 0;
assembler_byte007 |= 0x10;
Beim Setzen wurde also das Bit kurzzeitig sogar rückgesetzt. Was dazu
führte, dass auf dem LIN Bus (Treiber lief im Interrupt) hin und wieder
eine 0 gesendet wurde.
A. K. schrieb:> Sind mehrere Portpins mit völlig verschiedener Funktion verschiedener> Module eine gemeinsame Ressource?
Kommt 'drauf an. Nämlich im Kern exakt darauf, ob sie sich atomar
ansteuern lassen. Der TO hat hier also durchaus eine wunden Punkt
gefunden. Naja, nicht einen wirklich neuen...
Wer auf Nummer sicher gehen will/muss, nimmt halt einfach an, das es
eine gemeinsame Resource ist und trifft die nötigen Vorkehrungen gegen
unerwünschte Beeinflussungen.
Wer 100% sicher sein kann, dass er atomar zugreifen kann (Asm rules!),
der kann darauf verzichten und den dadurch möglichen Speedup nutzen. And
again: Asm rules...
C/C++ stinks...
LinTau schrieb:> BitStructIrgendwas.Bit42 = 1;> Wurde vom Compiler IM GLEICHEN Programm auf 4 verschiedenen Arten> umgesetzt.>> Dabei war sowohl ein:>> assembler_bit_set byte007.4>> als auch ein:>> assembler_byte007 = 0;> assembler_byte007 |= 0x10;
Sehen allesamt korrekt aus.
Woher soll der Compiler wissen, dass du ihm per Interrupt in die Suppe
spuckst und dort Daten aus dem Struct abholst?
Oder andersrum: Wenn Daten im Struct konsistnet sein sollen, dann muss
diese Semaphore so gesteuert werden, dass während der Manipulation kein
anderer Zugriff erlaubt wird.
Im einfachsten Fall geht das, indem man eine lokale Kopie anlegt, und
dort drin herumrechnet. Wenn alles fertig berechnet ist, wird
schlussendlich das lokale Abbild bei gesperrtem Interrupt auf den
globalen Sendepuffer kopiert.
Oder kürzer: man sollte nicht dem Compiler die Schuld für ein falsches
System-/Softwaredesign geben.