Forum: Compiler & IDEs Ausgaben von objdump


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 Reinhard M. (reinhardm)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Kai S. (zigzeg)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Reinhard M. (reinhardm)


Bewertung
0 lesenswert
nicht lesenswert
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?

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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.

von S. R. (svenska)


Bewertung
0 lesenswert
nicht lesenswert
Signale sind aber doch keine Interrupts?

von Reinhard M. (reinhardm)


Bewertung
0 lesenswert
nicht lesenswert
> 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?

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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?

von Johann L. (gjlayde) Benutzerseite


Bewertung
-1 lesenswert
nicht lesenswert
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.

von Reinhard M. (reinhardm)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
> 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

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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.

von Reinhard M. (reinhardm)


Bewertung
0 lesenswert
nicht lesenswert
> 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?

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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".

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Carl D. (jcw2)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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?

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
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.)

von Reinhard M. (reinhardm)


Bewertung
0 lesenswert
nicht lesenswert
> 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.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
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 ...

von Reinhard M. (reinhardm)


Bewertung
0 lesenswert
nicht lesenswert
@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.

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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
}

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Genau das macht er doch ...

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Na ja , dass hat er in einem anderen Thread beschrieben ... das steht ja 
auch so im Wiki.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Reinhard M. (reinhardm)


Bewertung
0 lesenswert
nicht lesenswert
>> 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.

von Reinhard M. (reinhardm)


Bewertung
0 lesenswert
nicht lesenswert
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?

von Markus F. (mfro)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Reinhard M. (reinhardm)


Bewertung
0 lesenswert
nicht lesenswert
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?

von S. R. (svenska)


Bewertung
0 lesenswert
nicht lesenswert
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

von Reinhard M. (reinhardm)


Bewertung
0 lesenswert
nicht lesenswert
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?

von S. R. (svenska)


Bewertung
0 lesenswert
nicht lesenswert
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. :-)

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
S. R. schrieb:
> Der Timer-Interrupt hat nicht das Recht, den UART-Interrupt
> kaputtzumachen. :-)

Er macht ihn nicht kaputt.

von S. R. (svenska)


Bewertung
0 lesenswert
nicht lesenswert
Wenn der Timer-Interrupt den UART-Interrupt unterbricht und danach 
wieder ins Hauptprogramm zurückkehrt, ist der UART-Interrupt kaputt.

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
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.

: Bearbeitet durch User

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.