Hallo,
bin ja vor kurzem auf den 7er gcc umgestiegen. Leider musste ich jetzt
feststellen, dass objdump bei gleichen Parametern ein viel
"schlechteres" Listing erzeugt.
Bei 4.8 war der Speicherbereich der Interrupts mit aufgeführt, sodass
ich auf Anhieb sehen konnte, ob alle Interrupt-Funktionen richtig
erkannt wurden. Das fehlt mir im neuen Format.
Ah, gerade sehe ich, dass der Interrupt-Vektor in einer eigenen Sektion
liegt, die aber nicht disassembliert wurde :(
Bei 4.8 liegen die Interrupts im Bereich .text - beim 7.0er Kompilat
liegen die im Bereich .isr_vector ...
Lässt sich das vielleicht über eine weitere Option wieder bekommen?
Ich habe leider von dem Thema überhaupt keine Ahnung, weshalb mir die
Optionenliste nicht wirklich weiter hilft.
Auf jeden Fall funktionieren würde die disassemble all Option -D ,
allerdings würde er dann die Datensegmente auch mit anzeigen.
Scheinbar ist beim GCC 7 das Segment nicht ausführbar markiert.
Hallo Kai,
danke für Deine schnelle Reaktion!
> Auf jeden Fall funktionieren würde die disassemble all Option -D
das habe ich ausprobiert, komme aber mit den Ausgaben nicht klar.
Der ganze Bereich rund um die Interrupts (zumindest dort, wo ich sie
erwartet hatte) bestand mehr oder weniger aus identischen Anweisungen,
die ich noch nie gesehen hatte.
Kein Bezug zum Quelltext und auch keine Symbole, sodass ich nicht
herausfinden konnte, ob meine Funktion jetzt als Interrupt akzeptiert
wurde oder nicht.
Beim avr soll die Attributliste einer Interruptfunktion ja aus
_signal_, _used__ und __externally_visible_ bestehen.
Beim stm32 fand ich die Verwendung von _interrupt_
Wenn ich das noch mit rein packe, dann wird der Code größer.
Jetzt weiß ich nicht: muss das attribut drin sein, oder erhalte ich dann
Mist?
> Scheinbar ist beim GCC 7 das Segment nicht ausführbar markiert.
Mein Fehler, oder eine Schwäche des compilers?
Reinhard M. schrieb:
> Hallo,
>
> bin ja vor kurzem auf den 7er gcc umgestiegen. Leider musste ich jetzt
> feststellen, dass objdump bei gleichen Parametern ein viel
> "schlechteres" Listing erzeugt.
>
> Bei 4.8 war der Speicherbereich der Interrupts mit aufgeführt, sodass
> ich auf Anhieb sehen konnte, ob alle Interrupt-Funktionen richtig
> erkannt wurden. Das fehlt mir im neuen Format.
> Ah, gerade sehe ich, dass der Interrupt-Vektor in einer eigenen Sektion
> liegt, die aber nicht disassembliert wurde :(
> Bei 4.8 liegen die Interrupts im Bereich .text - beim 7.0er Kompilat
> liegen die im Bereich .isr_vector ...
Kann ich nicht bestätigen.
Da die Architektur offenbar keine Rolle spielt, hab ich mal ein kleines
Beispiel für x86_64-linux-gnu übersetzt: 1 | #include <signal.h>
| 2 | #include <stdlib.h>
| 3 |
| 4 | void leave ()
| 5 | {
| 6 | exit (0);
| 7 | }
| 8 |
| 9 | int main()
| 10 | {
| 11 | signal (SIGFPE, leave);
| 12 | return 0;
| 13 | }
|
1 | $ gcc signal.c && objdump -x | grep leave
| 2 | 0000000000400507 g F .text 000000000000000e leave
|
leave liegt also in .text.
Signale sind aber doch keine Interrupts?
> Signale sind aber doch keine Interrupts?
Könntest Du das bitte etwas ausführlicher erläutern?
> Kann ich nicht bestätigen.
Ok, sorry.
Muss zugeben, dass ich noch nicht alles durch blicke.
Bislang habe ich nur mit AVRs gearbeitet und auch nur in C.
Jetzt durch den Wunsch, die Anwendungen auch auf stm32 laufen zu lassen,
habe ich mich C++ zugewandt.
Vielleicht liegt mein Problem auch an dem Misthaufen, den ich als
Ausgangsbasis genommen habe
(http://github.com/ckormanyos/real-time-cpp). Ich habe mich nach
(schlanken) C++ Bibliotheken umgeschaut und nix passendes gefunden.
Bei dem Beispielprogramm von dem Buch "real-time c++" war wenigstens die
Build-Umgebung so, dass ich sie verstehen und für mich anpassen konnte.
Die Quältexte waren nicht mehr wert, als ein feuchter Furz :(
... und c++ bzw. Objektorientierung muss man mit der Lupe suchen :(
Nachdem ich den Mist in eine akzeptable Form gebracht habe, versuche ich
jetzt, so viel zu verstehen, um alles weg schmeißen zu können, was ich
nicht brauche oder ich so nicht haben will.
Leider muss ich mich dazu mit Themen beschäftigen, mit denen ich nie was
zu tun haben wollte :O
Zum Vergleichen habe ich nur die Listings meiner alten Programme und
das, was jetzt raus kommt. Bei meinen C-Programmen habe ich den
Startup-Code der avr-libC verwendet. Dort musste ich mich um die
richtige Positionierung der Interrupts nicht kümmern. Die Funktion mit
dem richtigen Makro eröffnen und alles war gut.
Der jetzige (avr) startup-Code sieht so aus (wie gesagt - nicht von
mir): 1 | #include <array>
| 2 | #include <cstdint>
| 3 | #include <HalCPU.h>
| 4 |
| 5 | extern "C" void __my_startup () __attribute__((section(".startup"), used, noinline));
| 6 | extern "C" void __vector_unused_irq() __attribute__((signal, used, externally_visible));
| 7 |
| 8 | void __vector_unused_irq() { for(;;) { hal::cpu::nop(); } }
| 9 |
| 10 | namespace
| 11 | {
| 12 | typedef struct struct_isr_type
| 13 | {
| 14 | typedef void(*function_type)();
| 15 |
| 16 | const std::uint8_t jmp[2]; // JMP instruction (0x940C): 0x0C = low byte, 0x94 = high byte.
| 17 | const function_type func; // The interrupt service routine.
| 18 | }
| 19 | isr_type;
| 20 | }
| 21 |
| 22 | extern "C"
| 23 | const volatile std::array<isr_type, 26U> __isr_vector __attribute__((section(".isr_vector")));
| 24 |
| 25 | extern "C"
| 26 | const volatile std::array<isr_type, 26U> __isr_vector =
| 27 | {{
| 28 | // Nr. interrupt source
| 29 | { { 0x0C, 0x94 }, __my_startup }, // 0, reset
| 30 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 1, ext0
| 31 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 2, ext1
| 32 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 3, pin0
| 33 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 4, pin1
| 34 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 5, pin2
| 35 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 6, watchdog
| 36 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 7, timer2 cmp a
| 37 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 8, timer2 cmp b
| 38 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 9, timer2 ovf
| 39 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 10, timer1 cap
| 40 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 11, timer1 cmp a
| 41 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 12, timer1 cmp b
| 42 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 13, timer1 ovf
| 43 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 14, timer0 cmp a
| 44 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 15, timer0 cmp b
| 45 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 16, timer0 ovf
| 46 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 17, spi(TM)
| 47 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 18, usart rx
| 48 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 19, usart err
| 49 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 20, usart tx
| 50 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 21, adc
| 51 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 22, eep Ready
| 52 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 23, comparator
| 53 | { { 0x0C, 0x94 }, __vector_unused_irq }, // 24, two-wire
| 54 | { { 0x0C, 0x94 }, __vector_unused_irq } // 25, spm
| 55 | }};
|
Vermutlich müsste ich den an jeden Interrupt anpassen, den ich verwende
:(
In den Beispieldateien von st habe ich gesehen, dass dort leere
Vorgabe-Funktionen als "weak" deklariert werden. So wie ich es
verstanden habe, würde eine Benutzerfunktion gleichen Namens die
Funktion ersetzen, anstatt zum duplicate-Fehler zu führen.
Funktioniert das Attribut auch unter avr?
Könnte ich damit einen eleganteren Startup-Code erzeugen?
Reinhard M. schrieb:
> extern "C"
> const volatile std::array<isr_type, 26U> __isr_vector
> __attribute__((section(".isr_vector")));
Also in Input-Section .isr_vector.
Welcher Output-Section wird das zugeordnet? Oder ist das Orphan?
Reinhard M. schrieb:
> { { 0x0C, 0x94 }, __my_startup }
Find ich Klasse: Erst den C++ Hammer auspacken um alles "besser und
sauberer" zu implementieren als es die Default-Implementierung macht,
nur um Instruktionen per Maschinen-Code zu implementieren!
0x940c codiert for JMP.
> Find ich Klasse: Erst den C++ Hammer auspacken um alles "besser und
> sauberer" zu implementieren als es die Default-Implementierung macht,
> nur um Instruktionen per Maschinen-Code zu implementieren!
Sach ich doch! Ein einziger Misthaufen - überall mal hingeschissen.
Die goodies sind zusammen geklaut (z.B. Meyers und Co) und der Rest ...
Code der sich nicht übersetzen lässt, Pin-Definitionen in den
Abstraktionsklassen, ein "Betriebssystem" das nur mit C-Funktionen
umgehen kann ...
Je weiter man sich mit dem Stoff befasst, desto schlimmer wird es.
Hier werden Umsteigewillige auf ganz dumme Tour abgezockt :(
> Also in Input-Section .isr_vector.
> Welcher Output-Section wird das zugeordnet? Oder ist das Orphan?
Keine Ahnung. Ich kann ja mal alle startup-Dateien hochladen =:O
Reinhard M. schrieb:
> Keine Ahnung. Ich kann ja mal alle startup-Dateien hochladen =:O
Dort wirst du es nicht finden. Wenn, dann ist es in deinem Linker
Description File beschrieben.
> Dort wirst du es nicht finden. Wenn, dann ist es in deinem Linker
> Description File beschrieben.
Oh uh oh - da wird mir ja gleich wieder schlecht.
Muss ich mir den chice wirklich antun?
Klappt C++ mit dem Startup-Code aus der avrlibc nicht?
Also da würde ich lieber die build-Umgebung umbauen und beim stm32 die
startup-Dateien von st nehmen, als dieses Startsammelsurium zu verwenden
:(
Linker Beschreibung sieht so aus: 1 | /*
| 2 | Copyright Christopher Kormanyos 2007 - 2013.
| 3 | Distributed under the Boost Software License,
| 4 | Version 1.0. (See accompanying file LICENSE_1_0.txt
| 5 | or copy at http://www.boost.org/LICENSE_1_0.txt)
| 6 | */
| 7 |
| 8 | /* Linker script for ATMEL(R) AVR(R) ATmega32P. */
| 9 |
| 10 | INPUT(libc.a libm.a libgcc.a)
| 11 |
| 12 | OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr")
| 13 | OUTPUT_ARCH(avr:5)
| 14 |
| 15 | /* The beginning and end of the program ROM area. */
| 16 | /* Leave 4 bytes for the 32-bit checksum plus 0x10 extra bytes free. */
| 17 | _rom_begin = 0x00000000;
| 18 | _rom_end = 0x00007FEC;
| 19 |
| 20 | /* The beginning and end (i.e., top) of the stack */
| 21 | /* Set up a stack with a size of (1/2)K */
| 22 | _stack_begin = 0x800500;
| 23 | _stack_end = 0x800800;
| 24 |
| 25 | /* The end of the 2K RAM stack */
| 26 | __initial_stack_pointer = 0x800800;
| 27 |
| 28 | MEMORY
| 29 | {
| 30 | ROM(rx) : ORIGIN = 0, LENGTH = 32K - 0x14
| 31 | RAM(rw!x) : ORIGIN = 0x800100, LENGTH = 0x0400
| 32 | }
| 33 |
| 34 | SECTIONS
| 35 | {
| 36 | . = 0x0;
| 37 | . = ALIGN(2);
| 38 |
| 39 | /* ISR vectors */
| 40 | .isr_vector :
| 41 | {
| 42 | *(.isr_vector)
| 43 | . = ALIGN(0x10);
| 44 | KEEP(*(.isr_vector))
| 45 | } > ROM = 0xAAAA
| 46 |
| 47 | /* Startup code */
| 48 | .startup :
| 49 | {
| 50 | *(.startup)
| 51 | . = ALIGN(0x10);
| 52 | KEEP(*(.startup))
| 53 | } > ROM = 0xAAAA
| 54 |
| 55 | /* Program code (text), read-only data and static ctors */
| 56 | .text :
| 57 | {
| 58 | _ctors_begin = .;
| 59 | *(.ctors)
| 60 | . = ALIGN(2);
| 61 | KEEP (*(SORT(.ctors)))
| 62 | _ctors_end = .;
| 63 | *(.progmem*)
| 64 | . = ALIGN(2);
| 65 | *(.trampolines*)
| 66 | . = ALIGN(2);
| 67 | *(.text)
| 68 | . = ALIGN(2);
| 69 | *(.text*)
| 70 | . = ALIGN(2);
| 71 | } > ROM
| 72 |
| 73 | .text :
| 74 | {
| 75 | . = ALIGN(0x10);
| 76 | } > ROM = 0xAAAA
| 77 |
| 78 | .= 0x800100;
| 79 | . = ALIGN(2);
| 80 |
| 81 | /* The ROM-to-RAM initialized data section */
| 82 | .data :
| 83 | {
| 84 | _data_begin = .;
| 85 | *(.data)
| 86 | . = ALIGN(2);
| 87 | KEEP (*(.data))
| 88 | *(.data*)
| 89 | . = ALIGN(2);
| 90 | KEEP (*(.data*))
| 91 | *(.rodata) /* Do *NOT* move this! Include .rodata here if gcc is used with -fdata-sections. */
| 92 | . = ALIGN(2);
| 93 | KEEP (*(.rodata))
| 94 | *(.rodata*)
| 95 | . = ALIGN(2);
| 96 | KEEP (*(.rodata*))
| 97 | _data_end = .;
| 98 | } > RAM AT > ROM
| 99 |
| 100 | /* The uninitialized (zero-cleared) data section */
| 101 | .bss :
| 102 | {
| 103 | _bss_begin = .;
| 104 | *(.bss)
| 105 | . = ALIGN(2);
| 106 | KEEP (*(.bss))
| 107 | *(.bss*)
| 108 | . = ALIGN(2);
| 109 | KEEP (*(.bss*))
| 110 | _bss_end = .;
| 111 | } > RAM
| 112 |
| 113 | _rom_data_begin = LOADADDR(.data);
| 114 | }
|
Auch wenn ich kaum was davon verstehe, sieht es für mich so aus, als
müsste ich die Datei auch für jeden Chip extra anpassen. Korrekt?
Reinhard M. schrieb:
>> Dort wirst du es nicht finden. Wenn, dann ist es in deinem Linker
>> Description File beschrieben.
>
> Oh uh oh - da wird mir ja gleich wieder schlecht.
> Muss ich mir den chice wirklich antun?
Du wolltest die ANtwort auf die Frage, warum .isr_vector nicht Teil von
.text ist. Nun hast du sie.
> Klappt C++ mit dem Startup-Code aus der avrlibc nicht?
Die Antwort auf diese Frage ist nicht wie erwartet 42, sondern 0x940c.
Was erwartest du von Software, die per C++ Maschinencode
zusammenbastelt?
> Also da würde ich lieber die build-Umgebung umbauen und beim stm32 die
> startup-Dateien von st nehmen, als dieses Startsammelsurium zu verwenden
Na dann mach mal. Vergiss einfach das obige "Projekt".
Johann L. schrieb:
> Was erwartest du von Software, die per C++ Maschinencode
> zusammenbastelt?
Das macht der Compiler angeblich auch, oder nicht? Maschinencode aus C++
basteln, mein ich.
Ich finde es eher bemerkenswert schräg dass der AVR da komplette JMP
Instruktionen in seiner Interrupttabelle sehen will, andere Controller
wollen da einfach ein simples Array von Funktionszeigern stehen haben.
Ich hoffe mal daß gcc7 immer noch mit AVRlibc zusammenspielt, denn das
(siehe oben) ist ja reichlich schräges Zeug. Was macht man, wenn man
keinen AVR mit Flash >8k benutzt? Dann sind die "ISR-Slots" nur 2Byte
breit und es wird ein RJMP erwartet. Relativ, nix absolute Adresse.
Wenn C++, dann würde ich das nur auf Basis der AVRlibc machen. Schon
wegen der ganzen Adressen und Bitnummern, die man kaum selbst
zusammenpfriemeln will. "Würde" kann man übrigens streichen, denn bei
mir funktioniert das so.
Carl D. schrieb:
> Ich hoffe mal daß gcc7 immer noch mit AVRlibc zusammenspielt,
Warum sollte das nicht so sein?
> Wenn C++, dann würde ich das nur auf Basis der AVRlibc machen.
Offenbar bricht bereits der Horror aus, wenn eine ISR nicht als
statische Methode implementiert ist... Was ist so schlimm daran, ISR
der AVR-LibC zu verwenden?
Ich würde auch empfehlen, aus den ISRs, die mit Hilfe der avr-libc
Macros definiert werden, einfach die (statischen) Elementfunktionen der
eigenen Klassen aufzurufen. Ist in der ISR nur der Aufruf der
Elementfunktion enthalten, optimiert der Kompiler die zusätzliche
Indirektion weg.
Zumal der geschilderte Hack, statische Elementfunktionen im
Assemblertext den Namen der ISR-Vektoren zu geben, mit Klassen-Templates
gar nicht mehr geht ... (warum der g++ die Attribute zwar akzeptiert,
den Namen aber nicht übernimmt, ist mir jedoch nicht ganz klar.)
> Wenn C++, dann würde ich das nur auf Basis der AVRlibc machen. Schon
> wegen der ganzen Adressen und Bitnummern, die man kaum selbst
> zusammenpfriemeln will. "Würde" kann man übrigens streichen, denn bei
> mir funktioniert das so.
Danke für die Info!
Wenn dem so ist, dann schmeiß ich den startup-Quatsch in den Gulli.
> Offenbar bricht bereits der Horror aus, wenn eine ISR nicht als
> statische Methode implementiert ist... Was ist so schlimm daran, ISR
> der AVR-LibC zu verwenden?
Garnichts ist schlimm daran. Bislang gehe ich davon aus, dass es 2 Arten
von ISRs gibt: die schlanken, wie z.B. den Systemtackt, der wunderbar
auch ohne Klasse funktioniert und dann die anderen, wie z.B. serielle-,
spi- und/oder adc-Handler, bei denen ein Klassenkontext eher auf der
Hand liegt.
Mich hat einfach die Möglichkeit begeistert, dass ich ne statische
Memberfunktion so nennen kann, wie es mir passt und dass ich über den
Assemblerhack sie trotzdem als Interrupt anmelden kann.
Diese Funktionalität finde ich extrem schick - und es hat mal garnix mit
Panik oder religiösem Fanatismus zu tun.
> mit Klassen-Templates gar nicht mehr geht ...
Naja - die Templates werden völlig überschätzt!
In der "Referenz"-Anwendung von dem Typen sind ja auch Templates dabei.
u.a. die Registertemplates ala Meyer ...
... nur funktionieren die nur für statischen Code.
Wenn ich z.B. bei einem Interrupt die Obergrenze für den Zähler während
der Laufzeit mit einer Funktion verändern will, ist schon Schluss mit
dem Template. Das Funktionsargument ist nicht konstant - selbst wenn man
es als const deklariert.
Somit hat das Registertemplate sehr eingeschränkten Nutzwert.
Ich behaupte mal, da war ich mit meinem Klassenansatz schon weiter.
Reinhard M. schrieb:
>
> Naja - die Templates werden völlig überschätzt!
m.E. ganz im Gegenteil! Denn alles was ich zur Compilezeit machen kann,
brauch ich zur Laufzeit nicht mehr.
> In der "Referenz"-Anwendung von dem Typen sind ja auch Templates dabei.
> u.a. die Registertemplates ala Meyer ...
> ... nur funktionieren die nur für statischen Code.
> Wenn ich z.B. bei einem Interrupt die Obergrenze für den Zähler während
> der Laufzeit mit einer Funktion verändern will, ist schon Schluss mit
> dem Template. Das Funktionsargument ist nicht konstant - selbst wenn man
> es als const deklariert.
Das kannst Du doch auch mit einem Template ... nur ist hier die
Obergrenze eben kein Ausdruck der zur Compilezeit auswertbar ist.
Trotzdem kann Deine Klasse insgesamt ein Template sein ...
@Carl Drexler
nochmals ein herzliches Dankeschön für die Bestätigung, dass C++ auch
mit der avrlibc zusammen klappt.
Ich habe jetzt den ganzen Startup-Mist beim avr rausgeschmissen und
jetzt habe ich auch die Liste der Interrupts wieder, die ich so schätzen
gelernt habe :)
> Das kannst Du doch auch mit einem Template ... nur ist hier die
> Obergrenze eben kein Ausdruck der zur Compilezeit auswertbar ist.
> Trotzdem kann Deine Klasse insgesamt ein Template sein ...
Ok, ich bin ganz bestimmt kein Template-Profi - ganz im Gegenteil.
Allerdings habe ich in C sehr viel mit Funktionszeigern gearbeitet. Wenn
ich die Flexibilität jetzt in C++ nachbilden will, dann wird das schnell
haarig.
Keine Sorge, ich habe einen TaskManager-Ansatz gefunden, der es mir
erlaubt, auch nicht virtuelle Memberfunktionen anonym aufzurufen, aber
das ist schon tricky. Mit "normalen" templates kommt man da nicht weit.
Reinhard M. schrieb:
> Mich hat einfach die Möglichkeit begeistert, dass ich ne statische
> Memberfunktion so nennen kann, wie es mir passt und dass ich über
> den Assemblerhack sie trotzdem als Interrupt anmelden kann.
Das geht mit GCC Bordmitteln durch Setzen des Assembler-Namens.
Insbesondere geht es, ohne die AVR-LibC und deren Vector-Tabelle und
Startup-Code über Bord kippen zu müssen:
1 | #include <avr/io.h>
| 2 | #include <avr/interrupt.h>
| 3 |
| 4 | #define SS_(x) #x
| 5 | #define S_(x) SS_(x)
| 6 |
| 7 | #define ISR_ATTR __attribute__((__used__,__externally_visible__,__signal__))
| 8 |
| 9 | #define SIG(x, ...) __asm(S_(x)) ISR_ATTR __VA_ARGS__
| 10 |
| 11 | class C
| 12 | {
| 13 | static char volatile c;
| 14 | static void isr1() SIG (INT0_vect);
| 15 | static void isr2() SIG (INT1_vect, ISR_ALIASOF (INT0_vect));
| 16 | static void panic() __asm ("__vector_default") ISR_ATTR ISR_NAKED;
| 17 | };
| 18 |
| 19 | char volatile C::c;
| 20 |
| 21 | void C::isr1()
| 22 | {
| 23 | c = 0;
| 24 | }
| 25 |
| 26 | void C::panic()
| 27 | {
| 28 | __asm ("seh");
| 29 | reti();
| 30 | }
|
Genau das macht er doch ...
Wilhelm M. schrieb:
> Genau das macht er doch ...
"Genau das" jedenfalls nicht. Die von mir skizzierte Lösung brauch kein
eigenen Startup-Code, braucht kein eigenes Linker-Script, braucht keine
eigene IRQ-Tabelle, ... you name it.
Na ja , dass hat er in einem anderen Thread beschrieben ... das steht ja
auch so im Wiki.
Also den Start-Code in C++ nochmal zu schreiben, halte ich auch nicht
für sinnvoll: wenn man in den C-ISRs einfach die Elementfunktionen der
Klassen aufruft, ist das auf jeden Fall der sauberer Weg und der
Compiler optimiert auf jeden Overhead weg.
>> Genau das macht er doch ...
>
> "Genau das" jedenfalls nicht. Die von mir skizzierte Lösung brauch kein
> eigenen Startup-Code, braucht kein eigenes Linker-Script, braucht keine
> eigene IRQ-Tabelle, ... you name it.
Ihr habt beide Recht :)
Ich habe genau mit dem angefangen, wie Johann L. es hier wieder
ausgeführt hat. Damit hatte ich recht schnell meine C-Module in C++- für
AVR überführt. Mit dem Stand war ich soweit zufrieden und wollte jetzt
den Anwendungsbereich auf stm32 ausdehen - und damit fing das ganze
Galama an.
Ich suchte nach einer C++-Bibliothek für den smt32 oder eben nach einer
Bibliothek mit akzeptablem/verstehbarem build-System.
Ich habe nicht viel gefunden und habe deshalb den Mist von Kormanyos
verwendet, da hier das build-System halbwegs abzeptabel war.
Naja und den startup-Code für den avr hatte ich akzeptiert, da ich davon
ausging, dass es eine Basis für die Portierung der Software war.
Ich habe dann mit der Abstraktion des SystemTicks angefangen - und da
sind die Unterschiede zwischen Avr und stm32 schon recht groß.
Egal wie - jedenfalls wollte ich die Methodik der Funktionsumbenennung
auf beiden Systemen nutzen, fand aber die Ausgaben von objdump (womit
wir wieder beim Thema wären) zu unübersichtlich, um beurteilen zu
können, ob denn die Funktionen jetzt als Interrupts verwendet werden,
oder nicht.
OK, nachdem sich gezeigt hat, dass der Startupcode von Kormanyos so gut
ist, wie ich seinen ganzen Code einschätze, erhebt sich für mich die
Frage, könnte mir jemand dabei helfen, einen Startup-Code für den stm32
zu erstellen, der der Qualität von avrlibc entspricht?
... und vielleicht sogar zu ähnlichen Ausgaben von objdump führt?
Sowohl stm32, als auch Startup-Code an sich ist für mich völliges
Neuland, sodass ich die Aufgabe wohl nicht ohne fremde Hilfe schaffe.
Hallo,
also das mit dem weak scheint zu klappen.
Das signal-Attribut wird bei der arm-Architektur ignoriert. Mit dem
interrupt-Attribut scheint es zu klappen.
Also habe ich das interrupt-Attribut auf AVR ausprobiert - scheint auch
zu klappen.
Was mich etwas irritiert, ist, dass beim arm die ganze push/pop-Orgie
fehlt.
Hier mal die AVR-Variante (am Anfang kann man sehen, dass es als
Interrupt akzeptiert wurde): 1 | make/../tmp/obj/SystemTick.o: file format elf32-avr
| 2 |
| 3 |
| 4 | Disassembly of section .text.__vector_13:
| 5 |
| 6 | 00000000 <__vector_13>:
| 7 | #include <SystemTick.h>
| 8 |
| 9 | volatile sys::systemTick::TickType sys::systemTick::milliSeconds;
| 10 |
| 11 |
| 12 | void sys::systemTick::systemTickHandler() {
| 13 | 0: 78 94 sei
| 14 | 2: 1f 92 push r1
| 15 | 4: 0f 92 push r0
| 16 | 6: 0f b6 in r0, 0x3f ; 63
| 17 | 8: 0f 92 push r0
| 18 | a: 11 24 eor r1, r1
| 19 | c: 8f 93 push r24
| 20 | e: 9f 93 push r25
| 21 | 10: af 93 push r26
| 22 | 12: bf 93 push r27
| 23 | ++sys::systemTick::milliSeconds;
| 24 | 14: 80 91 00 00 lds r24, 0x0000 ; 0x800000 <__SREG__+0x7fffc1>
| 25 | 18: 90 91 00 00 lds r25, 0x0000 ; 0x800000 <__SREG__+0x7fffc1>
| 26 | 1c: a0 91 00 00 lds r26, 0x0000 ; 0x800000 <__SREG__+0x7fffc1>
| 27 | 20: b0 91 00 00 lds r27, 0x0000 ; 0x800000 <__SREG__+0x7fffc1>
| 28 | 24: 01 96 adiw r24, 0x01 ; 1
| 29 | 26: a1 1d adc r26, r1
| 30 | 28: b1 1d adc r27, r1
| 31 | 2a: 80 93 00 00 sts 0x0000, r24 ; 0x800000 <__SREG__+0x7fffc1>
| 32 | 2e: 90 93 00 00 sts 0x0000, r25 ; 0x800000 <__SREG__+0x7fffc1>
| 33 | 32: a0 93 00 00 sts 0x0000, r26 ; 0x800000 <__SREG__+0x7fffc1>
| 34 | 36: b0 93 00 00 sts 0x0000, r27 ; 0x800000 <__SREG__+0x7fffc1>
| 35 | }
| 36 | 3a: bf 91 pop r27
| 37 | 3c: af 91 pop r26
| 38 | 3e: 9f 91 pop r25
| 39 | 40: 8f 91 pop r24
| 40 | 42: 0f 90 pop r0
| 41 | 44: 0f be out 0x3f, r0 ; 63
| 42 | 46: 0f 90 pop r0
| 43 | 48: 1f 90 pop r1
| 44 | 4a: 18 95 reti
|
Die ARM-Variante ohne assembler-Hack: 1 | make/../tmp/obj/SystemTick.o: file format elf32-littlearm
| 2 |
| 3 |
| 4 | Disassembly of section .text._ZN3sys10systemTick17systemTickHandlerEv:
| 5 |
| 6 | 00000000 <sys::systemTick::systemTickHandler()>:
| 7 |
| 8 | volatile sys::systemTick::TickType sys::systemTick::milliSeconds;
| 9 |
| 10 |
| 11 | void sys::systemTick::systemTickHandler() {
| 12 | ++sys::systemTick::milliSeconds;
| 13 | 0: 4a02 ldr r2, [pc, #8] ; (c <sys::systemTick::systemTickHandler()+0xc>)
| 14 | 2: 6813 ldr r3, [r2, #0]
| 15 | 4: 3301 adds r3, #1
| 16 | 6: 6013 str r3, [r2, #0]
| 17 | }
| 18 | 8: 4770 bx lr
| 19 | a: bf00 nop
| 20 | c: 00000000 .word 0x00000000
|
und jetzt mit assembler-Hack, also als Interrupt: 1 | make/../tmp/obj/SystemTick.o: file format elf32-littlearm
| 2 |
| 3 |
| 4 | Disassembly of section .text.__sys_tick_handler:
| 5 |
| 6 | 00000000 <__sys_tick_handler>:
| 7 | #include <SystemTick.h>
| 8 |
| 9 | volatile sys::systemTick::TickType sys::systemTick::milliSeconds;
| 10 |
| 11 |
| 12 | void sys::systemTick::systemTickHandler() {
| 13 | 0: 4668 mov r0, sp
| 14 | 2: f020 0107 bic.w r1, r0, #7
| 15 | ++sys::systemTick::milliSeconds;
| 16 | 6: 4a05 ldr r2, [pc, #20] ; (1c <__sys_tick_handler+0x1c>)
| 17 | void sys::systemTick::systemTickHandler() {
| 18 | 8: 468d mov sp, r1
| 19 | a: b401 push {r0}
| 20 | ++sys::systemTick::milliSeconds;
| 21 | c: 6813 ldr r3, [r2, #0]
| 22 | }
| 23 | e: f85d 0b04 ldr.w r0, [sp], #4
| 24 | ++sys::systemTick::milliSeconds;
| 25 | 12: 3301 adds r3, #1
| 26 | 14: 6013 str r3, [r2, #0]
| 27 | }
| 28 | 16: 4685 mov sp, r0
| 29 | 18: 4770 bx lr
| 30 | 1a: bf00 nop
| 31 | 1c: 00000000 .word 0x00000000
|
Wie kommt es, dass die "normale" Funktion sogar noch kleiner ist?
Ja und wieso werden weder bei der Funktion, noch bei der
Interrupt-Funktion irgendwelche Register gesichert?
Reinhard M. schrieb:
> Wie kommt es, dass die "normale" Funktion sogar noch kleiner ist?
> Ja und wieso werden weder bei der Funktion, noch bei der
> Interrupt-Funktion irgendwelche Register gesichert?
Welcome to the world of ARM ;).
Bei einer "normalen" Funktion kennt jedes ABI ja "Scratch-Register" (die
bei einem Funktionsaufruf beliebig überschrieben werden dürfen) und den
Rest des Registersatzes (die die gerufene Funktion sichern und restoren
muß, falls sie die verwendet).
Bei einem Interrupt werden bei ARM entsprechend des fixen ABI's alle
Scratch-Register automatisch gesichert (und beim Interrupt-Ende
restored).
Im Resultat bedeutet das, daß ein Interrupt-Handler eine ganz normale
Funktion (ohne jeden extra-Schnickschnack) ist.
Hi Markus,
danke für die Erklärungen!
Hm, da gibt es wohl einige Unterschiede zwischen avr und arm :O
Gibt es sowas wie einen Crashkurs für dummies, wo ich mir solche
Geschichten nachlesen kann?
Die gültigen Attribute sind übrigens von Architektur zu Architektur (und
teilweise von Binärformat zu Binärformat) unterschiedlich und in der
GCC- und Binutils-Dokumentation nachzulesen.
Siehe z.B.
https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html#Function-Attributes
https://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html#Variable-Attributes
Danke für die Links!
Beim Suchen nach Attributen bin ich schon mal drüber gestolpert, habe
mir bislang aber nicht die Mühe gemacht, es mal gründlich zu lesen.
Das habe ich jetzt nachgeholt :)
Interessant fand ich, dass interrupt und weak scheinbar für alle
Architekturen gilt. Nur (?) beim avr gibt es das signal, welches ein sei
als erste Interruptanweisung vermeidet.
Dumme Frage: wenn bei einem avr ein Interrupt-Handler durch einen
Interrupt unterbrochen wird, macht er danach mit dem ersten
Interupt-Handler weiter?
Wenn ja, dann wäre das interrupt Attribut ja garnicht so verkehrt.
Oder?
Reinhard M. schrieb:
> Dumme Frage: wenn bei einem avr ein Interrupt-Handler durch einen
> Interrupt unterbrochen wird, macht er danach mit dem ersten
> Interupt-Handler weiter?
Wenn ein Interrupt irgendwelchen Code irgendwo unterbricht, muss er
dorthin auch zurückkehren. Egal, ob der Code nun ein Interrupt-Handler
war oder nicht.
Der Timer-Interrupt hat nicht das Recht, den UART-Interrupt
kaputtzumachen. :-)
S. R. schrieb:
> Der Timer-Interrupt hat nicht das Recht, den UART-Interrupt
> kaputtzumachen. :-)
Er macht ihn nicht kaputt.
Wenn der Timer-Interrupt den UART-Interrupt unterbricht und danach
wieder ins Hauptprogramm zurückkehrt, ist der UART-Interrupt kaputt.
S. R. schrieb:
> Wenn der Timer-Interrupt den UART-Interrupt unterbricht und danach
> wieder ins Hauptprogramm zurückkehrt, ist der UART-Interrupt kaputt.
Ich würde sagen dann wäre das Design des Mikrocontrollers kaputt, sowas
würde keiner kaufen.
Aber wie es aussieht hab ich vorhin das Wörtchen "nicht" überlesen und
bin dadurch ein bisschen im Kontext verrutscht im Eifer des Gefechts.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|