PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C Interruptvektor im ATMEL-Datenblatt und im Compilat, Namen der Codeabschnitte in *.ls



oberallgeier
11.06.2016, 16:58
Hallo Forum,
eine Differenz zwischen Datenblatt und Compilat, sprich *lls, ist mir aufgefallen, ich verstehe die nicht.

Controller: mega328
IDE AVRStudio4.18-Build 700

Die originale Vektortabelle aus dem neuesten (11. Juli 2016) Datenblatt, Atmel-8271J-AVR- ATmega-Datasheet_11/2015, Seite 65:

1 0x0000(1) RESET External Pin, Power-on Reset, Brown-out Reset and Watchdog System Reset
2 0x0002 INT0 External Interrupt Request 0
3 0x0004 INT1 External Interrupt Request 1
4 0x0006 PCINT0 Pin Change Interrupt Request 0
5 0x0008 PCINT1 Pin Change Interrupt Request 1
6 0x000A PCINT2 Pin Change Interrupt Request 2
7 0x000C WDT Watchdog Time-out Interrupt
8 0x000E TIMER2 COMPA Timer/Counter2 Compare Match A
9 0x0010 TIMER2 COMPB Timer/Counter2 Compare Match B
10 0x0012 TIMER2 OVF Timer/Counter2 Overflow

Die ISR für Timer2 Compare Match A:

// ================================================== =========================== =
// === Nicht unterbrechbare ISR für timer2 ==================================== =
// Routine zählt hoch im Takt 20 kHz = 50 µs. Verwendung: Heartbeat (LED)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ISR(TIMER2_COMPA_vect) // Vektor 8, Adresse 0x000E
{ //
Izeit_A --; // ###>>> Izeit_1 ist aktuell int16_t ==>> Izeit_1 bleibt
// bis 32000 in der int16-Grenze
if ( ! Izeit_A ) // Interrupt-Timer = 1 ... 20 000 ... (1 sec blink)
// WENN Izeit_1 =|= Null => wahr => Anweisung ausgeführen
{ // Eine Sekunde ist voll =>
Izeit_A = Izthrznt; // => Rückstellen auf Zeithorizont
ToggleBit (PBLED, LBr); // rtLED toggeln HEARTBEAT (on board + Pin)
Isecundn ++; // Sekundenzähler hochtackern, max 9 Std
} // Ende if ( ! Izeit_A )
//
return;
} // Ende ISR(TIMER2_COMPA_vect)
// ================================================== =========================== =

Daraus entsteht jetzt in der *.lls dieses Codestück dessen Adressen ich nicht verstehe.

Disassembly of section .text:

00000000 <__vectors>:
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
4: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
8: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
c: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
10: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
14: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
18: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
1c: 0c 94 79 00 jmp 0xf2 ; 0xf2 <__vector_7>
20: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
24: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>Frage: Wieso heißt dieser Abschnitt ".text"? Wieso haben die andern Abschnitte ihre Namen? Ich finde leider dazu mit der Standardsuche (Tante Goock) keine Antwort.

Hier der Ausschnitt von eben mit dem "vorderen" Teil der *.lls.

XXXXX_01_01.elf: file format elf32-avr

Sections:
Idx Name Size VMA LMA File off Algn
0 .data 00000004 00800100 000001e0 00000274 2**0
CONTENTS, ALLOC, LOAD, DATA
1 .text 000001e0 00000000 00000000 00000094 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .bss 00000004 00800104 00800104 00000278 2**0
ALLOC
3 .debug_aranges 00000020 00000000 00000000 00000278 2**0
CONTENTS, READONLY, DEBUGGING
4 .debug_pubnames 000000a4 00000000 00000000 00000298 2**0
CONTENTS, READONLY, DEBUGGING
5 .debug_info 00000258 00000000 00000000 0000033c 2**0
CONTENTS, READONLY, DEBUGGING
6 .debug_abbrev 00000180 00000000 00000000 00000594 2**0
CONTENTS, READONLY, DEBUGGING
7 .debug_line 00000249 00000000 00000000 00000714 2**0
CONTENTS, READONLY, DEBUGGING
8 .debug_frame 00000090 00000000 00000000 00000960 2**2
CONTENTS, READONLY, DEBUGGING
9 .debug_str 000000ef 00000000 00000000 000009f0 2**0
CONTENTS, READONLY, DEBUGGING
10 .debug_loc 0000006b 00000000 00000000 00000adf 2**0
CONTENTS, READONLY, DEBUGGING
11 .debug_ranges 00000030 00000000 00000000 00000b4a 2**0
CONTENTS, READONLY, DEBUGGING

Disassembly of section .text:

00000000 <__vectors>:
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
4: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
8: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
c: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
10: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
14: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
18: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
1c: 0c 94 79 00 jmp 0xf2 ; 0xf2 <__vector_7>
20: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
24: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>

Für ne Antwort/Erklärung danke ich schon jetzt.

Die Fragen zusammen:
1) Wieso heißt ein Programmabschnitt der *.lss ".text"? Wieso haben die andern Abschnitte ihre Namen? Ich finde leider dazu mit der Suche bei Google keine Antwort.
2) Wieso sind die Interruptvektoren im Datenblatt mit 2 inkrementierte, in der *.lss mit 4? Ok, das ist wohl der Adressraum des mega328 ! ? ! ?
3) Steht irgendwo ne (gute *gg*) Erklärung für Aufbau, Code-Abschnittnamen etc. der *.lls-Datei? Wo bitte?

Unregistriert
11.06.2016, 18:13
2) Wieso sind die Interruptvektoren im Datenblatt mit 2 inkrementierte, in der *.lss mit 4? Ok, das ist wohl der Adressraum des mega328 ! ? ! ?

Die Interrupt Vector Tabelle ist in der relevanten Spalte im Datenblatt mit Program Address bezeichnet.
Ein PC (Programm Counter) Schritt gleich 2 Byte im Speicher.
Zwei Inkrementierungen der Programmadresse sind vier Byte im Adressraum, deren Byteadresse dann in der .lss Datei aufgeführt wird.

Peter(TOO)
11.06.2016, 23:56
Hallo Geier,

Für ne Antwort/Erklärung danke ich schon jetzt.

Die Fragen zusammen:
1) Wieso heißt ein Programmabschnitt der *.lss ".text"? Wieso haben die andern Abschnitte ihre Namen? Ich finde leider dazu mit der Suche bei Google keine Antwort.
2) Wieso sind die Interruptvektoren im Datenblatt mit 2 inkrementierte, in der *.lss mit 4? Ok, das ist wohl der Adressraum des mega328 ! ? ! ?
3) Steht irgendwo ne (gute *gg*) Erklärung für Aufbau, Code-Abschnittnamen etc. der *.lls-Datei? Wo bitte?

Zu 1)
Das sind alte Geschichten aus den Anfängen von C.
Das Source-Programm wird dem Compiler als Text-Datei gefüttert.

.code wurde vor C schon für den Code von Assemblerprogrammen verwendet.
Also kam da einer auf die Idee, das Segment halt .text zu benennen, welches den Maschinencode des C-Programms enthält.
Das cstartup-Modul, welches die CPU-Register und noch ein paar Dinge initialisiert, bevor main() aufgerufen wird, ist meist immer noch in Assembler geschrieben und liegt typischerweise im .code-Segment.
.bss wird für statische Variablen verwendet.
.data enthält initialisierte statische variablen.
Dann gibt es oft noch .stack oder .cstack für den Bereich welcher den Stack enthält.

Die ganzen .debug_xxx-Segmente sind Metadaten für den Debugger und werden nicht auf dem Zielsystem abgelegt. Ein Sourcelevel-Debugger kann damit den Maschinen-Code mit dem Source-Code verknüpfen und alles entsprechend darstellen.

Grundsätzlich sind die Segmentnamen frei wählbar, es gibt halt aber Traditionen für deren Namen.

Bei einem µC werden .code, .text und .data im ROM abgelegt.
cstartup kopiert dann das .data-Segment ins RAM, somit haben alle Variablen einen Anfangswert.
.bss ist einfach der Bereich für statische Variablen ohne Initialisierung. Normalerweise füllt cstartup diesen Bereich des RAM mit Nullen.

http://www.atmel.com/webdoc/avrlibcreferencemanual/mem_sections.html

Die Segmente braucht der Linker um die einzeln übersetzten Module zusammensetzen zu können. Im einfachsten Fall ist dies dein Programm und die Bibliotheks-Funktionen, bei grösseren Projekten wird auch das Anwendungsprogramm in mehrere Module aufgeteilt. Im Object-Code dieser Module gibt es noch keine absoluten Adressen, weil noch nicht bekannt ist, wo sie im Speicher zu liegen kommen.
Der Linker büschelt dann erst mal alles schön zusammen, also alle .text-Segmente werden in einem Block hintereinander gehängt, ebenso .data, .bss und all die anderen in jeweils ihrem Block. Anschliessend kann dann der Linker die absoluten Adressen generieren und an den richtigen Stellen im Code eintragen.
Module, welche besonders viel Stack benötigen, können dann auch noch ein entsprechend grosses Stack-Segment reservieren.

Zum Linker gehört auch noch eine Steuer-Datei. In dieser kann man ablegen auf welchen Adressen ROM und RAM im Speicherbereich abgelegt sind und welche Segmente wo abgelegt werden sollen.
Wenn man dies beherrscht kann man recht tolle Sachen machen! z.B. kann man die Datensegmente von der niedrigsten RAM-Adresse aus ablegen und das Stack-Segment von der höchsten RAM-Adresse aus. Da normalerweise der Stack von oben nach unten wächst hat man die grösste Sicherheit gegen einen Stackoverflow. Alles nicht belegte RAM befindet sich dann zwischen Daten und Stack. Bei besseren Linkern müssen der RAM- und ROM-Bereich auch nicht zusammenhängend sein, bei richtiger Konfiguration der Steuerdatei macht das dann der Linker automatisch.

Zu 2)
http://www.atmel.com/images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
ATmega 48A/48PA und ATmega88A/88PA haben 2 Byte für den Vektor.
ATmega168A/168PA und ATmega328/328P haben 4 Byte.
Abschnitt 2.2, Seite 7.

Zu 3)
Das ist eine harte Frage!
Es gibt kaum grundlegende Werke zu Linkern. Da ist man halt Profi und kommt mit den technischen Angaben zurecht. War irgendwie schon immer so :-(
Hier mal eine Einführung in den Linker:
http://www.seceng.informatik.tu-darmstadt.de/assets/ws09/gdi3/kapitel5assemblerbinder0910.pdf

Etwas älter (1972) aber ausführlicher:
http://www-inst.eecs.berkeley.edu/~cs162/sp06/hand-outs/p149-presser-linker-loader.pdf

Allerdings weiss ich noch nicht, welche Info dir zum Linker wirklich fehlen?
Scheint aber noch einiges an Grundlagen zu fehlen.

MfG Peter(TOO)

oberallgeier
12.06.2016, 08:55
@Gast: Danke für die Hinweise.

Peter: Danke für diese Ausführlichkeit und vor allem dafür, dass Du Dir die Zeit genommen hast mir das so gründich aufzuschreiben. Und das zu so später/früher Stunde! Danke.


.. Scheint aber noch einiges an Grundlagen zu fehlen ..Genauso ist es. Was nutzt der ganze Kernighan-Ritchie? Einmal habe ich den sowieso nur teilweise parat, aber mir fehlt dazu eben der ganze Hintergrund zu der IDE. In früheren Jahren hab ich grad mal FORTRAN gelernt (Eingabe noch über Stapel dieser 80spaltigen Hollerith-Lochkarten ... im Nebenzimmer stand der alte 750-Bit-Trommelspeicher von Zuse im Waschmaschinenformat). Damals und später aber eigentlich nie wirklich was erfahren zu den nachgeschalteten Dingen wie Parser, Recognizer, Zwischencode, Linker und Loader. Da gibts noch endlos weite weiße Flecken . . .

......http://t3.gstatic.com/images?q=tbn:ANd9GcSb9YulV_8UCoV-LTDmoRGvR3oUCeXnECHLlKacfRuwgeATBNyNd3Lzsw (http://images.google.de/imgres?imgurl=https://upload.wikimedia.org/wikipedia/commons/thumb/b/b5/Hollerith-Lochkarte.jpg/640px-Hollerith-Lochkarte.jpg&imgrefurl=https://commons.wikimedia.org/wiki/File:Hollerith-Lochkarte.jpg&h=283&w=640&tbnid=jO67DD0_k7-YYM:&tbnh=90&tbnw=204&docid=J0xsLzJXB29_WM&client=firefox-b&usg=__tJE_gOb13dkEeA30FSbEC34U6Y0=&sa=X&ved=0ahUKEwiFvdaF_KHNAhWEFZoKHcBJA8gQ9QEIODAD)
......©Ausdrückliche Freigabe durch die Geschäftsleitung der GIZEH Werke mit Mail vom 27.11.09


.. Allerdings weiss ich noch nicht, welche Info dir zum Linker wirklich fehlen ..Blöde Anwort: ich auch nicht. Aber ich arbeite an der Fragestellung.

Peter(TOO)
12.06.2016, 16:21
Peter: Danke für diese Ausführlichkeit und vor allem dafür, dass Du Dir die Zeit genommen hast mir das so gründich aufzuschreiben. Und das zu so später/früher Stunde!
Ich weiss ja selbst nicht ob es früh oder spät war, bei meinem Schlafrhythmus :-P


Genauso ist es. Was nutzt der ganze Kernighan-Ritchie?
K&R nutzt die bei deine Frage eigentlich gar nichts!
Der ursprüngliche C-Compiler bestand nur aus Parser und Code-Generator. Der Preprozessor kam erst später und war auch ein eigenes Programm.
Der Code-Generator lieferte dann einen Assembler-Sourcecode. Die weiteren Schritte waren dann Assembler und Linker und ein lauffähiges Programm zu erhalten. Allerdings wurden diese Programme als bekannt vorausgesetzt und von K&R gar nie beschrieben.
Es gab noch einen Pascal-Compiler zu dieser Zeit, der hat von Pascal in C übersetzt!

Um etwas über den Linker zu erfahren, muss man in der Assembler-Literatur forschen. Da gab es aber eigentlich noch keine Vorschriften zu den Segmentnamen. Sehr Hilfreich ist auch noch die Literatur über Mixed-Programming.

Mein grosser Vorteil ist, dass ich über die Hardware-Seite zum Programmieren kann, zuerst über Assembler. Zudem war das auch eine Zeit (1975), als man noch Stolz war 2KByte RAM zu besitzen ;-)
Da war noch alles Übersichtlich, I/O-Adressen und Monitor-Einsprungs-Punkte hatte man noch als Hex-Adresse im Kopf.

Zudem habe ich Programmiersprachen irgendwie immer erst verstanden, wenn ich gesehen habe, was die als Assembler-Code erzeugen.
Der Disassembler war mein grösster Freund, beim Lerner einen neuen Sprache ;-)


Einmal habe ich den sowieso nur teilweise parat, aber mir fehlt dazu eben der ganze Hintergrund zu der IDE.
Die IDEs sind wieder so eine Geschichte :-(

Eigentlich ist immer noch alles wie Früher, mit den ganzen einzelnen Programmen Compiler, Linker usw. oft besteht auch der Compiler noch aus den einzelnen Programmen.
Die IDE erzeugt automatisch die Steuerdateien für diese Programme und ruft sie dann auch automatisch in der richtigen Reihenfolge auf.
Grundsätzlich kann man die IDE auch wegwerfen und alles zu Fuss aufrufen, bzw. Batch-Dateien schreiben, welche alles auch etwas automatisieren.

Ich hatte früher eine Sammlung von Batch-Dateien und einen Editor, als "IDE", aus welchem man externe Programme aufrufen konnte. Damit habe ich auch noch gearbeitet als IAR ihre IDE herausbrachte. War für mich einfacher, als erst zu lernen wie die IDE funktioniert.


Blöde Anwort: ich auch nicht. Aber ich arbeite an der Fragestellung.
Vielleicht trägst du mal zusammen was du weisst! Dann sieht man was so fehlt und kann die Fragen stellen :-)

Hmm.., vielleicht sollte ich auch mal versuchen dazu einen Grundlagenkurs zu erstellen?
Wird dann aber vermutlich nicht auf einen bestimmten Compiler oder eine CPU ausgelegt sein.

MfG Peter(TOO)

oberallgeier
12.06.2016, 17:19
.. Es gab noch einen Pascal-Compiler zu dieser Zeit, der hat von Pascal in C übersetzt! ..
.. Vielleicht trägst du mal zusammen was du weisst! Dann sieht man was so fehlt und kann die Fragen stellen ..
.. Hmm.., vielleicht sollte ich auch mal versuchen dazu einen Grundlagenkurs zu erstellen? ..
Danke! Ach, Pascal in C, was Ähnliches kenn ich - (m)ein Freund hatte sich nen Übersetzer gebaut von FORTRAN nach C *gg*. Sicherlich wäre ein Grundkurs, evtl. im RNWissen schon prächtig. Aber es sind ja nicht lauter so C-Noobs hier wie ich.
Also zum Linker weiß/denke/vermute ich, dass er die relokativen (?) Codeteile zusammenfügt auf absolute Adressen setzt. Aber schon die Aufgabe ein Stück Assemblercode in einen C-Code einzufügen ist für mich nicht machbar. Zumal meine Bedienung des Studio4/WinAVR darin besteht, die Current Configuration Options auszufüllen - mein Make-File generiert bisher IMMER das Studio; die entsprechenden Befehle sind mir ein Rätsel.
Ich werde mich mal da einlesen (müssen) - und melde mich dann mit Fragen. Nicht diese Woche . . .