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:
> 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):
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. */
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:
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.
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):
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?
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:> 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.