Forum: Mikrocontroller und Digitale Elektronik Wie funktioniert eine Interrupt Service Routine in C?


von Samuel K. (secures)


Lesenswert?

Hallo zusammen

Ich versuche gerade zu verstehen, wie eine Interrupt Service Routine 
codemässig funktioniert.

Mir ist ungefähr klar wie das ganze in Assembler funktioniert: Interrupt 
wird ausgelöst und der Instructionpointer springt zur entsprechenden 
Interrupt-source-adresse, wo dann der Sprung zur Interrupt Service 
Routine hinterlegt ist usw.

Wie genau funktioniert das ganze aber in C? Wenn man zum Beispiel den 
"USART Rx complete" Interrupt eines AtMegas betrachtet, dann wird 
normalerweise das Makro ISR(USART_RXC_vect) aus der "interrupt.h" 
Library von Atmel verwendet.
1
//Auschnitt aus interrupt.h
2
#  define ISR(vector, ...)            \
3
    void vector (void) __attribute__ ((signal,__INTR_ATTRS)) __VA_ARGS__; \
4
    void vector (void)

Leider verstehe ich diesen Codeabschnitt nicht wirklich. Nach meinem 
Verständnis muss hier dem Compiler mitgeteilt werden, wohin der Sprung 
bei einem "Rx complete" Interrupt gehen soll. Ich nehme an, dass das 
irgendwie über die Compiler-Attribute funktioniert. Wo aber wird die 
Information aus "USART_RXC_vect" in diesem Makro verwendet?


Gruss

von Nop (Gast)


Lesenswert?

Samuel K. schrieb:

> Mir ist ungefähr klar wie das ganze in Assembler funktioniert: Interrupt
> wird ausgelöst und der Instructionpointer springt zur entsprechenden
> Interrupt-source-adresse, wo dann der Sprung zur Interrupt Service
> Routine hinterlegt ist usw.

Das ist mit C genauso. Es gibt irgendwo eine Interrupt-Tabelle, und da 
steht dann die Adresse der C-Funktion drin, die diesem Interrupt 
zugeordnet ist.

> Wo aber wird die
> Information aus "USART_RXC_vect" in diesem Makro verwendet?

#define ist eine Präprozessor-Anweisung, das ist reine Textersetzung. 
Der Präprozessor, der wie der Nahme sagt vor dem Compiler durchläuft, 
macht dann das hier daraus, alles in einer Zeile:
1
void USART_RXC_vect(void) __attribute__ ((signal,__INTR_ATTRS)); void USART_RXC_vect(void)

Funktional ist das äquivalent zu zwei Zeilen:
1
void USART_RXC_vect(void) __attribute__ ((signal,__INTR_ATTRS));
2
3
void USART_RXC_vect(void)

Er deklariert also erstmal die Funktion mit den nötigen 
Compiler-Attributen, damit der Compiler mitbekommt, daß das hier eine 
Interrupt-Routine darstellen soll. Das ist der Teil bis zum Semikolon.

Dann kommt die eigentliche Routine wie üblich als void-void-Funktion. 
Daran anschließend kämen die geschweiften Klammern, die Du dann anfügst, 
also mit dem Funktionsinhalt.

von Einer K. (Gast)


Lesenswert?

Gar nicht!

Es wird eine Funktion mit dem Namen generiert.

Der Vector selber ist hier definiert/zugeordnet: z.B. iom328p.h

von Karl M. (Gast)


Lesenswert?

Samuel K. schrieb:
> Wie genau funktioniert das ganze aber in C? Wenn man zum Beispiel den
> "USART Rx complete" Interrupt eines AtMegas betrachtet, dann wird
> normalerweise das Makro ISR(USART_RXC_vect) aus der "interrupt.h"
> Library von Atmel verwendet.
> //Auschnitt aus interrupt.h
> #  define ISR(vector, ...)            \
>     void vector (void) _attribute_ ((signal,__INTR_ATTRS))
> _VA_ARGS_; \
>     void vector (void)

Unter
https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

findet man ein Beispiel:
1
ISR(PCINT0_vect)
2
{
3
// code
4
}

Was könnte das bedeuten?

Das Macro setzt als Erstes einige Parameter als Prototypen Definition 
und anschließend wird eine Funktion ohne Parameter (void) erzeugt.
Der anschließend kommen { .. } die den ISR-Code einschließen

von Dieter R. (drei)


Lesenswert?

Vorschlag:

Beispielprojekt mit Atmel Start anlegen. Da wird die Grundstruktur 
automatisch erzeugt, man sieht sehr schön, wie das funktioniert (sofern 
Atmel Start funktioniert, was nach meiner Erfahrung nicht immer der Fall 
ist, aber meistens).

von Theor (Gast)


Lesenswert?

Im Prinzip funktioniert das nicht anders als in Assembler. Es mag sich 
lohnen, sich noch einmal vor Augen zu führen, dass auch in C 
geschriebene Programme letztlich in Maschinensprache umgesetzt werden.
Folgerichtig wird der Instruction Pointer auf die erste 
Maschinenanweisung der Interrupt Service Routine gesetzt - AUCH und 
TROTZ DEM diese ISR in C geschrieben ist.

Das "vor Augen führen" geht ganz real so, dass man den Compiler per 
Option anweist ein Listing-File zu erzeugen. Diese (und eine andere 
Compiler-Option, mit der nur die Präprozessor-Makros expandiert werden) 
ist dafür nützlich,

Kurz gesagt ist "USART_RXC_vect" die Adresse in der 
Interrupt-Vektor-Tabelle, an der die Adresse der ISR stehen muss - d.h. 
die Adresse derjenigen Instruktion, welche die erste in der ISR ist. Das 
Makro-Argument ist "vector".

Es gibt noch ein paar andere kleine Unterschiede zwischen ISRs und 
"normalen" C-Funktionen aber das steht im Compilerhandbuch - hoffe ich. 
:-)

von leo (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Gar nicht!
>
> Es wird eine Funktion mit dem Namen generiert.

Ja. Aber sicher ist ein Unterschied. Auf Grund des Attributes (ISR) wird 
Code gneriert, der normalerweise ...
- alle verwendeten Register sichert
- beim Return in einem "reti" ein Restaurieren der Flags beinhaltet.

Das ist aber noch alles beeinflussbar.

https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

leo

von W.S. (Gast)


Lesenswert?

Samuel K. schrieb:
> Interrupt
> wird ausgelöst und der Instructionpointer springt zur entsprechenden
> Interrupt-source-adresse, wo dann der Sprung zur Interrupt Service
> Routine hinterlegt ist usw.

Das siehst du aber ein bissel zu eng.
Prinzipiell ist es so, daß eine ISR in C mit einem speziellen Attribut 
versehen wird, normalerweise __irq (nur der GCC tanzt da aus der Reihe), 
damit der Compiler weiß, daß das keine void blabla(void) ist, sondern 
ein Interrupt-Server, der ggf. spezielle Behandlung braucht (z.B. 
Registerverwendung usw.).

So ist das auf der C-Seite.

Auf der HW-Seite mag das alles je nach Plattform ganz anders aussehen. 
Entweder gibt es eine Tafel mit Einsprungadressen oder die 
Einsprungadressen sind in einem Interrupt-Controller vermerkt (ARM7TDMI) 
oder es geht ggf. noch ganz anders, z.B. PIC mit fester Adresse für alle 
und anschließendem Abtesten, wer es gewesen war.

W.S.

von Samuel K. (secures)


Lesenswert?

Vielen Dank für die vielen und v.a. prompten Antworten.

Ich habe inzwischen noch etwas weiter in der avr-libc Library gegraben 
und bin nun auf folgende Deklaration gekommen.

1
 ISR(USART_RX_vect) {...}

wird nach meiner Erkenntnis in folgenden Codeschnipsel übersetzt:
1
void __vector_24(void) __attribute__ ((signal,used));
2
void __vector_24(void) {...}


Wobei das "signal" Attribut die ISR deklariert. 24 ist die Vektornummer 
bzw. im Fall Atmega328 auch die Programmadresse des Interrupts. Für mich 
würde das heissen, dass der Compiler alles hat was er braucht: Er weiss, 
dass es eine ISR ist (signal) und er weiss auch welcher (24).

Was ich noch nicht so ganz verstehe ist, dass die 24 im Namen der 
Funktion steht. Da ich keine weiteren Referenzen im Code auf 
"__vector_24" gefunden habe, gehe ich davon aus, dass das der wirkliche 
Funktionsname ist. Das würde aber für mich heissen, dass der Compiler 
bzw. Linker die Funktion "void __vector_24(void)" kennen muss? Habe aber 
im GCC Manual auf die schnelle nichts gefunden was darauf hindeuten 
würde.

von Georg A. (georga)


Lesenswert?

Samuel K. schrieb:
> Das würde aber für mich heissen, dass der Compiler
> bzw. Linker die Funktion "void __vector_24(void)" kennen muss? Habe aber
> im GCC Manual auf die schnelle nichts gefunden was darauf hindeuten
> würde.

Im Manual findest du das nicht, aber in der crt.S (oder wie e für den 
AVR heisst) der avr-libc. Da sind in der ISR-Tabelle alle __vector_* des 
jeweiligen CPU-Typs aufgeführt, die dann erstmal intern als Dummy-ISR 
definiert sind. Allerdings ist dort der Name "weak", d.h. der Linker 
nimmt den nur, wenn er sonst nicht definiert ist. Damit passiert nichts 
ganz Schlimmes, wenn der Interrupt aufgerufen wird, du aber keine eigene 
ISR dafür hast.

: Bearbeitet durch User
von Teo D. (teoderix)


Lesenswert?

Ich denke du wirst hier fündig:
https://www.avrfreaks.net/forum/header-file-question

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Samuel K. schrieb:
> Ich habe inzwischen noch etwas weiter in der avr-libc Library gegraben
> und bin nun auf folgende Deklaration gekommen.
1
> ISR(USART_RX_vect) {...}
> wird nach meiner Erkenntnis in folgenden Codeschnipsel übersetzt:
1
> void __vector_24(void) __attribute__ ((signal,used));
2
> void __vector_24(void) {...}

Was der Präprozessor macht, sieht man auch mit -save-temps im .i File 
(.ii für C++, .s für Assembler).

> Wobei das "signal" Attribut die ISR deklariert.

Da sich deine Fragen auf die GNU-Tools for AVR beziehen:

Oder "interrupt" für unterbrechbare ISRs mit SEI im Prolog.

> 24 ist die Vektornummer  bzw. im Fall Atmega328 auch die Programmadresse
> des Interrupts. Für mich würde das heissen, dass der Compiler alles hat
> was er braucht: Er weiss, dass es eine ISR ist (signal) und er weiss
> auch welcher (24).

Der Compiler "weiß" nur, dass die Funktion Attribut "signal" und / oder 
"interrupt" hat, und erzeugt anhand dessen einen passenden Prolog und 
Epilog(e) für die Funktion __vector_24.

Der Name der Funktion ist immer __vector_N, und GCC checkt das auch; 
ansonsten macht er aber nix spezielles mit dem Funktionsname.  Der wird 
im Startup-Code der avr-libc (crt*.o) in der vector-Tabelle 
referenziert:

http://svn.savannah.nongnu.org/viewvc/avr-libc/trunk/avr-libc/crt1/gcrt1.S?revision=2519&view=markup#l37

Die Symbole landen also in Section .vectors, welche wiederum im Standard 
Linker-Skript an den Anfang der .text Section lokatiert wird:

http://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=ld/scripttempl/avr.sc;h=05c0b890f050fe3cb2bc3db04178fef69cfbb02a;hb=HEAD#l113

(Das ist nur ein sh-Skript, aus dem der Zoo unterschiedlicher Skripte 
generiert wird. In der Installation finden sich die leichter lesbaren 
expandierten Formen.)

Das KEEP(*(.vectors)) bewirkt, dass die Section selbst dann nicht 
entfernt wird (z.B. mit --gc-sections) wenn sie nicht (bzw. kein Symbol 
daraus) referenziert wird.

Compilerseitig dient dazu Attribut "used", d.h. obwohl die Funktion 
global nicht referenziert wird, darf sie vom Compiler nicht entsorgt 
werden.  Außerdem fehlt da noch Attribut "externally_visible".

> Was ich noch nicht so ganz verstehe ist, dass die 24 im Namen der
> Funktion steht.

Das ist einzig die Naming-Convention der avr-libc:

XYZ_vect (Makro) -> __vector_N (Funktionsname, Symbol) -> Section 
.vectors.

So ist die vector-Tabelle in gcrt1.S einfach durch .macro vector zu 
generieren; alles was man wissen muss ist die Anzahl der Einträge 
(_VECTORS_SIZE aus io*.h).

> Da ich keine weiteren Referenzen im Code auf "__vector_24" gefunden
> habe, gehe ich davon aus, dass das der wirkliche Funktionsname ist.

Ja.  In GNU-Assembler (.S oder .sx oder -x assembler-with-cpp) kann man 
eine ISR z.B. einfach so definieren:
1
#include <avr/io.h>
2
3
.global WDT_vect
4
WDT_vect:
5
    reti
6
7
.global __vector_1
8
__vector_1:
9
    reti

Weiters initialisiert die avr-libc EIND in Section .init1 aus Symbol 
__vectors; das SFR wirkt auf EICALL und EIJMP.  M.W. ist __vectors auch 
speziell in avr-Binutils bei der Erzeugung der von gs() getriggerten 
Linker-Stubs (aka. Trampolines).  Siehe .trampolines im Linker-Skript.

> Das würde aber für mich heissen, dass der Compiler
> bzw. Linker die Funktion "void __vector_24(void)" kennen muss?

Wie gesagt checkt der Compiler nur den Funktionsname, und dass die 
Funktion Prototyp wie void func (void) hat.  Der Linker weiß nichts 
spezielles über die Symbole.

Prolog und Epilog werden anders generiert, i.d.R werden mehr Register 
gesichert (z.B. auch __zero_reg__), und auch SFRs falls nötig wie SREG 
oder RAMPZ.  Ein Teil der Prolog- und Epilog-Generierung ist an den 
Assembler ausgelagert:
1
#include <avr/interrupt.h>
2
3
ISR (WDT_vect)
4
{
5
   PORTB = 1;
6
}
wird compiliert zu
1
  .global  __vector_7
2
  .type  __vector_7, @function
3
__vector_7:
4
  __gcc_isr 1
5
.L__stack_usage = 0 + __gcc_isr.n_pushed
6
  ldi r24,lo8(1)
7
  out 0x5,r24
8
  __gcc_isr 2
9
  reti
10
  __gcc_isr 0,r24
und erst der Assembler expandert die Pseudo-Instruktion nach Analyse des 
Codes zu
1
00000062 <__vector_7>:
2
  62:  8f 93         push  r24
3
  64:  81 e0         ldi  r24, 0x01
4
  66:  85 b9         out  0x05, r24
5
  68:  8f 91         pop  r24
6
  6a:  18 95         reti

http://sourceware.org/binutils/docs-2.32/as/AVR-Pseudo-Instructions.html

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Johann hat das sehr detailliert erklärt - sehr schön!

Kurz gefasst bedeutet es:

Die Makro-Magie hinter dem Schlüsselwort ISR ist extrem Compiler 
spezifisch. mit C hat das kaum etwas zu tun. Es geht hierbei schlicht 
darum, dem Compiler mitzuteilen, dass diese Funktion eben keine normale 
C Funktion ist, sondern Hardwarespezifisch als ISR dekoriert werden 
muss.

Interessant finde ich dabei den Vergleich zu ARM Controllern, die 
brauchen das nämlich nicht. Dort sind die ISR ganz normale C Funktionen. 
Dafür kann allerdings der Compiler nichts, diese Vorgabe kommt vom CPU 
Kern.

von Johann J. (johannjohanson)


Lesenswert?

Stefanus F. schrieb:
> Dort sind die ISR ganz normale C Funktionen.

Tatsächlich? Muss man bei ARM-GCC z.B. nicht auch durch
1
void __attribute__((interrupt)) ...

mitteilen, dass es sich um eine ISR handelt?

von Samuel K. (secures)


Lesenswert?

Vielen Dank für die hilfreichen Antworten. Es ist mir nun um einiges 
klarer wie das ganze funktioniert. Ich werde heute Abend die Antwort von 
Johann noch genau durchstudieren.

Gruss

von Karl (Gast)


Lesenswert?

Johann J. schrieb:
> Tatsächlich? Muss man bei ARM-GCC z.B. nicht auch durch
> void __attribute__((interrupt)) ...
>
> mitteilen, dass es sich um eine ISR handelt?

Kommt wie so oft darauf an, welchen ARM man sich anschauen möchte.

Die älteren ARM7TDMI haben nur 2 Interrupts: Einen (normalen) IRQ und 
einen FIQ (Fast IRQ) Interrupt. Dann braucht es erst mal einen 
Software-Handler, der an die richtige Adresse springt und sich ggf. um 
das Sichern und Wiederherstellen der Register kümmern muss. Bei manchen 
Controllern wird das durch einen Hardware Interrupt-Controller 
unterstützt (AT91SAM7 z.B.), der die Adresse des Handlers ermittelt. 
Wenn man es nicht möchte, braucht man das Attribut nicht unbedingt. Hab 
es damals nicht benutzt.

Die neueren Cortex-M (R?, nicht A) haben den Interrupt-Controller gleich 
dabei (NVIC). Zusätzlich unterstützen sie beim Eintritt in einen 
Interrupt-Handler das Sichern der Register per Hardware nach dem AAPCS. 
Daraus folgt, dass es für den Handler keinen Unterschied macht, ob er 
als normale Funktion aufgerufen wird oder als Interrupt-Handler. 
Dementsprechend braucht es auch keine Attribute, Flags oder sonstwas. 
Der Compiler muss halt den AAPCS umsetzen, aber das tun sie eigentlich 
alle.

von Markus F. (mfro)


Lesenswert?

Johann J. schrieb:
> Stefanus F. schrieb:
>> Dort sind die ISR ganz normale C Funktionen.
>
> Tatsächlich? Muss man bei ARM-GCC z.B. nicht auch durch
>
>
1
> void __attribute__((interrupt)) ...
2
>
>
> mitteilen, dass es sich um eine ISR handelt?

Nein, muß man nicht.

von Oliver S. (oliverso)


Lesenswert?

Markus F. schrieb:
> Nein, muß man nicht.

Wie macht man das denn dann?

Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Markus F. schrieb:
> Johann J. schrieb:
>> Stefanus F. schrieb:
>>> Dort sind die ISR ganz normale C Funktionen.
>>
>> Tatsächlich? Muss man bei ARM-GCC z.B. nicht auch durch
1
>> void __attribute__((interrupt)) ...
>>
>> mitteilen, dass es sich um eine ISR handelt?
>
> Nein, muß man nicht.

Und wozu sind Attribute "interrupt" und "isr" ?

http://gcc.gnu.org/onlinedocs/gcc/ARM-Function-Attributes.html

Oder sind diese Dinge alle in der libgloss oder im CRT versteckt, so 
dass sich normale Anwender nicht darum kümmern müssen?

Anders gefragt:  Wer oder was referenziert die "ganz normalen C 
Funktionen"?  Irgendo müssen die ja referenziert werden?

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Johann J. schrieb:
> Muss man bei ARM-GCC nicht auch mitteilen, dass es sich um eine ISR handelt?

Nein, jedenfalls nicht bei Cortex M0, M3 und M4. Mit anderen ARM Kernen 
habe ich mich nicht beschäftigt.

> Und wozu sind Attribute "interrupt" und "isr" ?

Lies hier nach: 
http://gcc.gnu.org/onlinedocs/gcc/ARM-Function-Attributes.html

> Wer oder was referenziert die "ganz normalen C Funktionen"?

C oder Assembler Code, sowie die Interrupt Vektor Tabelle.

von Markus F. (mfro)


Lesenswert?

Was zeichnet Interupt-Handler (gegenüber "normalen" Funktionen) 
üblicherweise aus?

1.) sie müssen alle verwendeten Register sichern (und nicht nur die, die 
im ABI entsprechend ausgezeichnet sind)
2.) sie müssen (üblicherweise) mit einem speziellen Befehl verlassen 
werden

1.) erledigen die Cortexe automatisch
2.) ist bei Cortexen nicht notwendig, auch das übernimmt der µC

von Arno (Gast)


Lesenswert?

3.) Ihre Adresse steht in einer Tabelle an definierter Speicherstelle / 
wird dem Interrupt Controller oder dem entsprechenden Verzweigungs-Code 
bekannt gegeben, damit sie auch angesprungen werden, wenn der Interrupt 
eintritt

Keine Ahnung, wie das mit GCC für ARM Cortex üblicherweise funktioniert.

MfG, Arno

von Christopher J. (christopher_j23)


Lesenswert?

Arno schrieb:
> Keine Ahnung, wie das mit GCC für ARM Cortex üblicherweise funktioniert.

Für Cortex-M gilt ebenfalls

Arno schrieb:
> 3.) Ihre Adresse steht in einer Tabelle an definierter Speicherstelle

D.h. der NVIC ruft einfach die Funktion auf, deren Funktionszeiger an 
der definierten Stelle im Flash liegt. Typischerweise findet man die 
Namen der Funktionen im Startup-Code als Liste, so dass der Linker dann 
die Adressen zu den Funktionen auflösen und an die entsprechende Stelle 
im Flash packen kann.

PS:

Johann L. schrieb:
> Und wozu sind Attribute "interrupt" und "isr" ?
>
> http://gcc.gnu.org/onlinedocs/gcc/ARM-Function-Attributes.html

Ich vermute mal das ist für legacy, d.h. ARM7TDMI o.Ä.
In der verlinkten Doku steht ja auch, dass das Attribut "isr" für ARMv7M 
ignoriert wird. Das müsste eigentlich auch für ARMv6M, d.h. Cortex-M0 
und M0+ gelten.

: Bearbeitet durch User
von void (Gast)


Lesenswert?

Christopher J. schrieb:
> In der verlinkten Doku steht ja auch, dass das Attribut "isr" für ARMv7M
> ignoriert wird.

Lies das nochmal bitte:
"On ARMv7-M the interrupt type is ignored, and the attribute means 
the function may be called with a word-aligned stack pointer."

von Johann J. (johannjohanson)


Lesenswert?

Christopher J. schrieb:
> D.h. der NVIC ruft einfach die Funktion auf, deren Funktionszeiger an
> der definierten Stelle im Flash liegt. Typischerweise findet man die
> Namen der Funktionen im Startup-Code als Liste, so dass der Linker dann
> die Adressen zu den Funktionen auflösen und an die entsprechende Stelle
> im Flash packen kann

Also eigentlich wie überall:

Die Funktion, welche den Interrupt bearbeiten soll wird irgendwo bekannt 
gemacht, damit der Compiler/Linker entsprechend arbeiten können.

Nix Magie und nix "kann beliebig heißen" - muss bekannt gemacht sein und 
damit gut.

von Oliver S. (oliverso)


Lesenswert?

Markus F. schrieb:
> Was zeichnet Interupt-Handler (gegenüber "normalen" Funktionen)
> üblicherweise aus?
>
> 1.) sie müssen alle verwendeten Register sichern (und nicht nur die, die
> im ABI entsprechend ausgezeichnet sind)
> 2.) sie müssen (üblicherweise) mit einem speziellen Befehl verlassen
> werden
>
> 1.) erledigen die Cortexe automatisch
> 2.) ist bei Cortexen nicht notwendig, auch das übernimmt der µC

Irgendwie muß man dem Compiler und auch dem Linker mitteilen, daß eine 
Funktion als ISR dient. Ganz ohne jedes Attribut klappt das nicht.

Oliver

von Nop (Gast)


Lesenswert?

Oliver S. schrieb:

> Irgendwie muß man dem Compiler und auch dem Linker mitteilen, daß eine
> Funktion als ISR dient. Ganz ohne jedes Attribut klappt das nicht.

Doch, tut es bei Cortex-M. ISRs sind dort ganz normale C-Routinen ohne 
besonderen Prolog/Epilog.

von Christopher J. (christopher_j23)


Lesenswert?

void schrieb:
> Lies das nochmal bitte:
> "On ARMv7-M the interrupt type is ignored, and the attribute means
> the function may be called with a word-aligned stack pointer."

Stimmt, das hatte ich glatt gelesen als "the interrupt attribute is 
ignored". Mit dem Stack-Alignment habe ich mich ehrlich gesagt noch 
nicht auseinandergesetzt. 8 Byte Alignment scheint standard zu sein und 
ist bei manchen Cortexen (M0, M0+ und M7) wohl auch gar nicht 
veränderlich:
https://community.arm.com/developer/ip-products/processors/f/cortex-m-forum/6344/what-is-the-meaning-of-a-64-bit-aligned-stack-pointer-address


Johann J. schrieb:
> Nix Magie und nix "kann beliebig heißen" - muss bekannt gemacht sein und
> damit gut.

Kann "beliebig" heißen im Sinne, dass der Name in der Vektortabelle im 
Startup-Code mit dem Namen der Funktion übereinstimmen muss, damit der 
Linker den Funktionszeiger an die entsprechende Stelle setzen kann. 
Nimmt man den Startup-Code als gottgegeben an, dann kann man eben nur 
den Namen verwenden, der darin vorgegeben ist. Ob es sinnvoll ist einen 
anderen Namen als den vorgegebenen zu verwenden, darüber kann man sich 
natürlich streiten. Ich meine aber, dass es eher nicht so sinnvoll ist.

Bei den Cortexen gibt es auch eine Konvention zu den IRQ-Bezeichnern an 
die sich fast alle Hersteller halten (außer natürlich TI...). Konvention 
ist PERIPHERIENAME_IRQHandler, d.h. wenn die Peripherie im CMSIS-Header 
z.B. mit "USART3" bezeichnet wird, dann ist die Wahrscheinlichkeit sehr 
hoch, dass der entsprechende Handler USART3_IRQHandler() heißt. 
Außnahmen bestätigen natürlich die Regel, z.B. wenn sich 
unterschiedliche Peripherie einen IRQ teilt oder eine Peripherie mehrere 
IRQs hat.

von Oliver S. (oliverso)


Lesenswert?

Es geht gar nicht um Prolog und Epilog.

Es geht darum daß der Compiler jede Funktion, die nach seiner Kenntnis 
niemals aufgerufen wird, einfach wegoptimieren darf, und das auch 
gnadenlos tut, und es geht darum, daß die Adresse der ISR ja irgendwie 
in die Interrupttabelle gelangen muß.

Beides erfordert etwas Automagie in der Funktionsdefinition und im 
Linkerscript.

Ohne das gehts nicht.

Oliver

von Johann J. (johannjohanson)


Lesenswert?

Oliver S. schrieb:
> Beides erfordert etwas Automagie

Nix Magie - Deklaration.

von Markus F. (mfro)


Lesenswert?

Oliver S. schrieb:
> Beides erfordert etwas Automagie in der Funktionsdefinition und im
> Linkerscript.

Nö. Wenn der Startup-Code Assembler ist, optimiert keiner die 
Vektortabelle weg. Und weil jene eben die C-Handler referenziert, werden 
die auch nicht wegoptimiert.

Keinerlei Magie notwendig.

Bloß ein bißchen: damit man nicht in jedem Pups-Programm alle ISRs 
(aus)schreiben muß, kann man (z.B.) im Startupcode Dummy-Handler "weak" 
definieren. Dann muß man nur noch die schreiben, die man wirklich 
braucht.

von Axel S. (a-za-z0-9)


Lesenswert?

Arno schrieb:
> 3.) Ihre Adresse steht in einer Tabelle an definierter Speicherstelle /
> wird dem Interrupt Controller oder dem entsprechenden Verzweigungs-Code
> bekannt gegeben, damit sie auch angesprungen werden, wenn der Interrupt
> eintritt
>
> Keine Ahnung, wie das mit GCC für ARM Cortex üblicherweise funktioniert.

Genauso wie beim AVR auch. Die ISR haben alle "magische" Namen und 
werden über diesen Namen in der Vektortabelle referenziert. Zusätzlich 
gibt es eine Dummy-ISR mit weak Aliases für alle magischen Namen. Wenn 
der Linker die Vektortabelle zusammenstellt, tut er für die 
existierenden ISR die Adressen der realen Funktionen rein und für alle 
anderen den Alias auf die Dummy-ISR.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.