Hallo Forengemeinde,
ich habe bisher ein bisschen Grundlagen mit AVR ATmegas in C gemacht.
Heute habe ich mir mal das Manual zur avrlibc angesehen, insbesondere zu
PROGMEM.
http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_rom_arrayhttp://www.nongnu.org/avr-libc/user-manual/pgmspace.html
Dabei taucht mehrfach der Hinweis auf das Disassembly der Objektdatei
und die Mapdatei auf. Ich denke, es ist an der Zeit ein bisschen tiefer
in die Materie vorzudringen und ein wenig Assembler zu lernen. Dann kann
man sich verschiedene Code-Konstrukte ansehen und entscheiden, was
effizienter umgesetzt wird.
Allerdings finde ich da den Einstieg nicht leicht und ich habe direkt
ein paar Fragen:
1) Wie komme ich an das disassembly und das mapfile? (makefile?)
2) Kann ich mir die mit nem ganz normalen Editor angucken?
3) Wie interpretiert man ein Mapfile?
4) Woran erkenne ich, ob eine Variable jetzt nur im RAM oder auch im ROM
liegt?
Ich möchte gerne die PROGMEM Beispiele (richtig/falsch) nachvollziehen.
Ich hoffe, ihr könnt mir beim Einstieg ein wenig helfen.
Danke.
Gruß
Tom
Tom schrieb:> 1) Wie komme ich an das disassembly und das mapfile? (makefile?)http://www.rn-wissen.de/index.php/Assembler-Dump_erstellen_mit_avr-gcc
In dein Makefile einbauen musst du selbst (wenn's sowas nicht schon
hat).
> 2) Kann ich mir die mit nem ganz normalen Editor angucken?
Mit einem Text-Editor ja, mit einem Grafik-Editor wird's schwierig.
> 3) Wie interpretiert man ein Mapfile?
Ersma eins lesen und dann nachfragen, was unklar ist.
> 4) Woran erkenne ich, ob eine Variable jetzt nur im RAM oder auch im ROM> liegt?http://www.rn-wissen.de/index.php/Avr-gcc/Interna#Sections
Die angegebenen Seiten erklären das sehr schön.
Noch mal danke für den Hinweis!
Aber ich habe da noch eine kleine Frage.
Ich habe ein Standard makefile aus WINavr etwas ergänzt um auch eine
Ausgabe in % und der .o Dateien zu erhalten:
----------------------------------------------------------
# Display size of file.
HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex
#ELFSIZE = $(SIZE) --mcu=$(MCU) --format=avr $(TARGET).elf
ELFSIZE = $(SIZE) --target=elf32-avr $(TARGET).elf
#
MYSIZE1 = $(SIZE) --mcu=$(MCU) -C $(TARGET).elf
MYSIZE2 = $(SIZE) $(OBJ)
MYSIZE3 = $(NM) --size-sort --print-size $(PROJECTNAME).elf
#Siehe:
http://www.rn-wissen.de/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc
sizebefore:
@if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_BEFORE);
$(ELFSIZE); \
2>/dev/null; echo; fi
sizeafter:
@if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_AFTER);
$(ELFSIZE); \
echo ; $(MYSIZE1); $(MYSIZE2); \
2>/dev/null; echo; fi
----------------------------------------------------------
Zum Ausprobieren habe ich jetzt eine globale Variable angelegt einmal
mit und einmal ohne Initialisierung und dann noch mit PROGMEM.
Dabei habe ich mir immer angeguckt, welcher Sectionbereich sich
verändert.
Die Variable habe ich nicht weiter benutzt, nur die angegebene
Deklaration.
----------------------------------------------------------
Keine Änderung der Datei
führt zu:
Size after:
text data bss dec hex filename
17176 94 2178 19448 4bf8 can_debugger.elf
AVR Memory Usage
----------------
Device: at90can128
Program: 17270 bytes (13.2% Full)
(.text + .data + .bootloader)
Data: 2272 bytes (55.5% Full)
(.data + .bss + .noinit)
text data bss dec hex filename
756 19 9 784 310 ./main.o
----------------------------------------------------------
----------------------------------------------------------
uint8_t testArray[20];
führt zu:
Size after:
text data bss dec hex filename
17176 94 2198 19468 4c0c can_debugger.elf
AVR Memory Usage
----------------
Device: at90can128
Program: 17270 bytes (13.2% Full)
(.text + .data + .bootloader)
Data: 2292 bytes (56.0% Full)
(.data + .bss + .noinit)
text data bss dec hex filename
756 19 9 784 310 ./main.o
----------------------------------------------------------
----------------------------------------------------------
uint8_t testArray[20] = {0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9};
führt zu:
Size after:
text data bss dec hex filename
17176 114 2178 19468 4c0c can_debugger.elf
AVR Memory Usage
----------------
Device: at90can128
Program: 17290 bytes (13.2% Full)
(.text + .data + .bootloader)
Data: 2292 bytes (56.0% Full)
(.data + .bss + .noinit)
text data bss dec hex filename
756 39 9 804 324 ./main.o
----------------------------------------------------------
----------------------------------------------------------
uint8_t testArray[20] PROGMEM =
{0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9};
führt zu:
Size after:
text data bss dec hex filename
17196 94 2178 19468 4c0c can_debugger.elf
AVR Memory Usage
----------------
Device: at90can128
Program: 17290 bytes (13.2% Full)
(.text + .data + .bootloader)
Data: 2272 bytes (55.5% Full)
(.data + .bss + .noinit)
text data bss dec hex filename
776 19 9 804 324 ./main.o
----------------------------------------------------------
Fall 2) ohne Initialisierung führt wie erwartet zur Erhöhung von .BSS.
Jedoch ist dies im .o File von main nicht zu sehen.
Woran liegt das? Es müsste hier doch schon bekannt sein, dass keine
Initialisierung stattgefunden hat.
Fall 3) mit Initialisierung vergrößert wie erwartet .DATA.
Aber warum wird .TEXT nicht erhöht, obwohl doch die
Initialisierungswerte im Flash stehen müssten?
Bei Fall 4) mit PROGMEM landet alles erwartungsgemäß in .TEXT, also im
Flash.
Ich hoffe das kann mir jemand anschaulich erklären.
Danke.
Gruß
Tom
Tom schrieb:> Fall 2) ohne Initialisierung führt wie erwartet zur Erhöhung von .BSS.> Jedoch ist dies im .o File von main nicht zu sehen.> Woran liegt das? Es müsste hier doch schon bekannt sein, dass keine> Initialisierung stattgefunden hat.
Keine Ahnung. Sicher, dass du da nicht selber mit den ganzen Varianten
und Ausgaben durcheinander geraten bist?
Tom schrieb:> Fall 3) mit Initialisierung vergrößert wie erwartet .DATA.> Aber warum wird .TEXT nicht erhöht, obwohl doch die> Initialisierungswerte im Flash stehen müssten?
Sie stehen aber nicht in .text, sondern in .data. Sie landen letztlich
im Flash, weil dann beide Sections (.text und .data) ins HEX-File
geschrieben werden. Deswegen taucht .data bei der size-Ausgabe in beiden
Zeilen auf:
Bei Fall 3) gebe ich dir Recht. Das habe ich übersehen.
Aber bei Fall 2) bin ich mir mit der angezeigten Ausgabe sicher.
Das muss einen anderen Grund haben.
Tom schrieb:> Aber bei Fall 2) bin ich mir mit der angezeigten Ausgabe sicher.> Das muss einen anderen Grund haben.
Ok, habe es jetzt selber mal ausprobiert. Es liegt daran, dass der
Compiler die Variable im common-storage platziert. Erst der Linker
verfrachtet sie dann nach .bss. Du kannst beim Compilieren die Option
-fno-common angeben, dann landet sie gleich in .bss, und du bekommst die
entsprechende Ausgabe.
Tom schrieb:> Habe ich durch die Compileroption irgendwelche Nachteile?
Nein.
Im Gegenteil, ohne die Option wird eine bestimmte Unsauberkeit beim
Programmieren verdeckt (mehrfach Definitionen von Variablen in
unterschiedlichen Modulen). Mit der Option führt das zu einem Fehler.
Naja, das würde aber das ABI verädern, und dafür gibt es mM keinen
Grund.
Es wird davon ausgegangen, daß der Anwender die Doku gelesen hat und
sich über die verwendeten (oder nicht-verwendeten) Schalter im Klaren
ist. Es ist auch keine Optimierungs-Option, sondern beeinflusst die
Datenablage.
Kann man das mit der Zwischenplatzierung im "common storage" irgendwo
sehen, oder ist das einfach "Wissen"?
Ich habe Beispiel 2) ohne Initialisierung und Beispiel 3) mit
Initialisierung noch ein mal durchprobiert und nach dem Arraynamen
gesucht:
Fall 2)
.bss 0x0080015e 0x1d main.o
0x0080015e f_timer
can_debugger.map:645: 0x0080015f testArray
can_debugger.map:1300:testArray
main.o
can_debugger.sym:462:0080015f B testArray
main.c:86:uint8_t testArray[20];
main.lst:590: 623 .global testArray
main.lst:591: 624 .global testArray
main.lst:593: 628 testArray:
main.lst:627:C:\Users\XXX\AppData\Local\Temp/cckot7Sf.s:628
.bss:00000001 testArray
main.s:659:.global testArray
main.s:660:.global testArray
.section .bss
main.s:662: .type testArray, @object
main.s:663: .size testArray, 20
main.s:664:testArray:
.skip 20,0
.section .debug_frame,"",@progbits
main.s:1463: .long testArray
main.s:2196: .string "testArray"
main.s:2988: .string "testArray"
Fall 3)
.data 0x00800100 0x27 main.o
can_debugger.map:583: 0x00800100 testArray
can_debugger.map:1300:testArray
main.o
can_debugger.sym:450:00800100 D testArray
main.c:86:uint8_t testArray[20] =
{0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9};
main.lst:561: 580 .global testArray
main.lst:563: 584 testArray:
main.lst:640:C:\Users\XXX\AppData\Local\Temp/ccGYR3Uj.s:584
.data:00000000 testArray
main.s:612:.global testArray
.data
main.s:614: .type testArray, @object
main.s:615: .size testArray, 20
main.s:616:testArray:
.byte 0
.byte 1
.byte 2
.byte 3
.byte 4
.byte 5
.byte 6
.byte 7
.byte 8
.byte 9
.byte 0
.byte 1
.byte 2
.byte 3
.byte 4
.byte 5
.byte 6
.byte 7
.byte 8
.byte 9
main.s:1481: .long testArray
main.s:2214: .string "testArray"
main.s:3006: .string "testArray"
Daraus kann ich nur sehen, wo das Array am Ende landet. Nämlich einmal
in .bss und einmal in .data. Oder sollten mir die Zeilen aus der main.s
mehr sagen?
Ach mist, jetzt hatte ich noch die Option "CFLAGS += -fno-common" drin.
Ohne sieht man es in main.s:
status.1767:
.byte 1
.comm testArray,20,1
.section .debug_frame,"",@progbits
So viel Mühe gegeben und dann doch sinnlos gefragt. Sorry.
Interessanterweise steht es jetzt aber auch noch im MAP file in common:
COMMON 0x00800199 0x14 main.o
0x00800199 testArray
und in der .lst auch:
659 .comm testArray,20,1
main.s:617 .bss:00000000 f_timer
*COM*:00000014 testArray
Da hätte ich jetzt irgendwo schon die Zuordnung zu .bss erwartet.