PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : AVR-XRAM vom C-Compiler verwalten lassen



H.A.R.R.Y.
09.06.2008, 13:09
Hallo Gemeinde,

seit einiger Zeit versuche ich einem ATmega162 externes RAM (XRAM-Interface, 32kB SRAM) anzuflanschen. Die Hardware funktioniert 100%. Das Problem ist die Software, genauer gcc-avr (winAVR).

Also: das XRAM-Interface wird eingeschaltet (section .init1), dann wird per Zeiger direkt ein Bitmuster im externen RAM aufgebaut. Danach wird dieses Muster (wieder per Zeiger) aus dem RAM gelesen und in ein LCD kopiert - daher weiß ich auch, daß das alles sauber arbeitet. Der Quellcode dürfte daher uninteressant sein.

Was nicht mitspielt - und ich habe nach tagelanger Recherche nichts hilfreiches zum Thema gefunden:
Dem Compiler mitteilen, daß er jetzt über erheblich mehr Speicher herrschen darf.

Laut Win-AVR Doku müßte es mit einem Eintrag im Makefile erledigt sein:

avr-gcc ... -Wl,-Tdata=0x800500,--defsym=__heap_end=0x80ffff ...
Leider ist es sowohl dem Compiler als auch dem Linker wurstegal, was ich da hineintippe. Auch ein zusätzliches:

-minit-stack=0x800500 (testweise auch mal auf 0x200) brachte nichts. Der Stacktop liegt grundsätzlich bei 0x4ff und wird durch jede größere Variablen-Deklaration nach unten verschoben. Das führt bei entsprechend vielen Variablen (oder großem Array!) dazu, daß der Stacktop auf 0x0000 oder kleiner (!!!) verbastelt wird et voilà die Maschine geht beim ersten RET in den Wald. Aber davon abgesehen liegt der Stacktop nicht wie erwartet und die anderen Code-Segmente auch nicht da, wo die Doku verspricht (siehe hier http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_ext_ram und hier http://www.nongnu.org/avr-libc/user-manual/malloc.html#malloc_where).

Zwei verschiedene Makefile-Templates habe ich jetzt auch durch, das gleiche schlechte Ergebnis.

Also die Frage: Wie bekomme ich den AVR-GCC dazu dem Stack das komplette interne RAM zu geben und den Rest ins externe RAM zu verlagern und dieses damit vom Compiler verwalten lassen?

Ich hoffe es liegt an mir und nicht an einem massiven Bug im winAvr, nur finde ich den Fehler nicht.

Gruß H.A.R.R.Y.

SprinterSB
09.06.2008, 17:01
Was sagt denn das map-file


... -Wl,-Map=<file> ...

Schon mal mit eigenem Linker-Script versucht?

H.A.R.R.Y.
10.06.2008, 07:10
Das Makefile beginnt mit diesen Zeilen:



PROJECT = XRAMtest
MCU_TARGET = atmega162
DEFINITIONS = -DF_CPU=8000000UL
LIBRARIES = -lm
EXTRA_CLEAN_FILES = *.hex *.bin *.srec
EXTRA_CLEAN_FILES += *.gch
#OPTIMIZE = -Os -mcall-prologues
OPTIMIZE = -O2


COMPILER = avr-gcc
LINKER = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump


TO_COMPILER = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFINITIONS)
TO_LINKER = $(TO_COMPILER) -Wl,--section-start=.data=0x800500,--defsym=__heap_start=0x806000,--defsym=__heap_end=0x807fff,-Map,$(PROJECT).map -o
#TO_LINKER = $(TO_COMPILER) -Wl,-Tdata=0x800500,--defsym=__heap_end=0x807fff,-Map,$(PROJECT).map -o
#TO_LINKER = $(TO_COMPILER) -Wl,-Map,$(PROJECT).map -o
TO_OBJCOPY = -j .text -j .data -O


# Override is only needed by avr-lib build system.
override CFLAGS = $(TO_COMPILER)
override LFLAGS = $(TO_LINKER)


Und daraus "zaubert" der Linker dann dieses hier:



Memory Configuration

Name Origin Length Attributes
text 0x00000000 0x00020000 xr
data 0x00800060 0x0000ffa0 rw !x
eeprom 0x00810000 0x00010000 rw !x
*default* 0x00000000 0xffffffff

Linker script and memory map

Address of section .data set to 0x800100
LOAD c:/programme/atmel/winavr/bin/../lib/gcc/avr/4.1.1/../../../../avr/lib/avr5/crtm162.o
Address of section .data set to 0x800500
0x00806000 __heap_start = 0x806000
0x00807fff __heap_end = 0x807fff
LOAD main.o
LOAD c:/programme/atmel/winavr/bin/../lib/gcc/avr/4.1.1/../../../../avr/lib/avr5\libm.a
LOAD c:/programme/atmel/winavr/bin/../lib/gcc/avr/4.1.1/avr5\libgcc.a
LOAD c:/programme/atmel/winavr/bin/../lib/gcc/avr/4.1.1/../../../../avr/lib/avr5\libc.a
LOAD c:/programme/atmel/winavr/bin/../lib/gcc/avr/4.1.1/avr5\libgcc.a
...[das Gemüse hier ist wohl uninteressant]...
0x00000294 _etext = .

.data 0x00800500 0x0 load address 0x00000294
0x00800500 PROVIDE (__data_start, .)
*(.data)
.data 0x00800500 0x0 c:/programme/atmel/winavr/bin/../lib/gcc/avr/4.1.1/../../../../avr/lib/avr5/crtm162.o
.data 0x00800500 0x0 main.o
.data 0x00800500 0x0 c:/programme/atmel/winavr/bin/../lib/gcc/avr/4.1.1/avr5\libgcc.a(_copy_data.o)
.data 0x00800500 0x0 c:/programme/atmel/winavr/bin/../lib/gcc/avr/4.1.1/avr5\libgcc.a(_clear_bss.o)
*(.data*)
*(.rodata)
*(.rodata*)
*(.gnu.linkonce.d*)
0x00800500 . = ALIGN (0x2)
0x00800500 _edata = .
0x00800500 PROVIDE (__data_end, .)

.bss 0x00800500 0x0
0x00800500 PROVIDE (__bss_start, .)
*(.bss)
.bss 0x00800500 0x0 c:/programme/atmel/winavr/bin/../lib/gcc/avr/4.1.1/../../../../avr/lib/avr5/crtm162.o
.bss 0x00800500 0x0 main.o
.bss 0x00800500 0x0 c:/programme/atmel/winavr/bin/../lib/gcc/avr/4.1.1/avr5\libgcc.a(_copy_data.o)
.bss 0x00800500 0x0 c:/programme/atmel/winavr/bin/../lib/gcc/avr/4.1.1/avr5\libgcc.a(_clear_bss.o)
*(.bss*)
*(COMMON)
0x00800500 PROVIDE (__bss_end, .)
0x00000294 __data_load_start = LOADADDR (.data)
0x00000294 __data_load_end = (__data_load_start + SIZEOF (.data))

.noinit 0x00800500 0x0
0x00800500 PROVIDE (__noinit_start, .)
*(.noinit*)
0x00800500 PROVIDE (__noinit_end, .)
0x00800500 _end = .
0x00800500 PROVIDE (__heap_start, .)


Das sieht ja auch soweit gut aus. Mein Problem ist weniger der Linker sondern der Compiler! Der kommt ja bekanntlich vor dem Linker dran und weiß daher (noch) nix von den neuen Einstellungen für das RAM. Ergo nimmt er brav die Default-Werte des Controllers und manipuliert schön den Stackpointer (gekürzter Auszug aus dem Disassembler-Fenster des Simulators, verziert mit einigen Kommentaren meinerseits):



+00000000: 940C0038 JMP 0x00000038 Jump
...
@00000038: early_XRAM_on
---- main.c ---------------------------------------------------------------------------------------
34: {
+00000038: E880 LDI R24,0x80 Load immediate
=> das geht also schon mal

+00000039: BF85 OUT 0x35,R24 Out to I/O location
+0000003A: 2411 CLR R1 Clear Register
+0000003B: BE1F OUT 0x3F,R1 Out to I/O location
+0000003C: EFCF SER R28 Set Register
=> mit diesem Vorgeplänkel geht es um SP
+0000003D: E0D4 LDI R29,0x04 Load immediate
+0000003E: BFDE OUT 0x3E,R29 Out to I/O location
+0000003F: BFCD OUT 0x3D,R28 Out to I/O location
=> Aha, der steht jetzt auf 0x04ff - paßt also auch
=> bis hierher sieht es also tatsächlich gut aus

=> jetzt kommt ein ganzer Schwung mögliche Initialisierungen von Speicherbereichen, der eigentlich nicht weiter relevant ist....
+00000040: E015 LDI R17,0x05 Load immediate
+00000041: E0A0 LDI R26,0x00 Load immediate
+00000042: E0B5 LDI R27,0x05 Load immediate
+00000043: E9E4 LDI R30,0x94 Load immediate
+00000044: E0F2 LDI R31,0x02 Load immediate
+00000045: C002 RJMP PC+0x0003 Relative jump
+00000046: 9005 LPM R0,Z+ Load program memory and postincrement
+00000047: 920D ST X+,R0 Store indirect and postincrement
+00000048: 30A0 CPI R26,0x00 Compare with immediate
+00000049: 07B1 CPC R27,R17 Compare with carry
+0000004A: F7D9 BRNE PC-0x04 Branch if not equal
+0000004B: E015 LDI R17,0x05 Load immediate
+0000004C: E0A0 LDI R26,0x00 Load immediate
+0000004D: E0B5 LDI R27,0x05 Load immediate
+0000004E: C001 RJMP PC+0x0002 Relative jump
+0000004F: 921D ST X+,R1 Store indirect and postincrement
+00000050: 30A0 CPI R26,0x00 Compare with immediate
+00000051: 07B1 CPC R27,R17 Compare with carry
+00000052: F7E1 BRNE PC-0x03 Branch if not equal
+00000053: 940C0098 JMP 0x00000098 Jump
=> und weiter gehts es
@00000098: main
---- main.c ---------------------------------------------------------------------------------------
41: {
+00000098: 930F PUSH R16 Push register on stack
+00000099: 931F PUSH R17 Push register on stack
+0000009A: 93CF PUSH R28 Push register on stack
+0000009B: 93DF PUSH R29 Push register on stack

=> SP abholen
+0000009C: B7CD IN R28,0x3D In from I/O location
+0000009D: B7DE IN R29,0x3E In from I/O location

=> um 240Bytes verringern
+0000009E: 5FC0 SUBI R28,0xF0 Subtract immediate
+0000009F: 40D0 SBCI R29,0x00 Subtract immediate with carry

=> und den neuen Wert retour schreiben (unter optimierter IRQ-Sperre!)
+000000A0: B60F IN R0,0x3F In from I/O location
+000000A1: 94F8 CLI Global Interrupt Disable
+000000A2: BFDE OUT 0x3E,R29 Out to I/O location
+000000A3: BE0F OUT 0x3F,R0 Out to I/O location
+000000A4: BFCD OUT 0x3D,R28 Out to I/O location
=> damit ist SP=0x040b


Und damit auch klar ist, wieso der Compiler den SP um 240 runterdreht:


int main (void)
{
unsigned char buffer[240];
char *storagePtr; // ext. RAM is located RAMEND..0x7FFF
char check, temp;
unsigned char i;


Dabei kommt mir so eine vage Vermutung: das Array ist bei dieser Deklaration eine private Variable von main() und wird deshalb im Stack platziert?!? - Natürlich ](*,) !!!

Die Deklaration als GLOBALES Array läßt den SP brav auf 0x04ff - AHA. Und wo liegt das globale Array nun? Im externen RAM ab 0x0500 - AHA.
War das Ganze also - wie vermutet - ein Verständnisproblem meinerseits.

Das heißt also nun, Stack und private Variablen (sofern nicht in Register passend) landen hier im internen RAM, alles GLOBALE im externen RAM => die großen (statischen) Arrays deklariere ich also erstmal global um den gewünschten Effekt zu erzielen.

@SprinterSB:
Danke für die Hilfsbereitschaft.

Gruß René

H.A.R.R.Y.
10.06.2008, 07:41
Nachtrag:

Mit dieser Zeile im Makefile funktioniert das Verschieben von .data NICHT:

TO_LINKER = $(TO_COMPILER) -Wl,-Tdata=0x800500,--defsym=__heap_end=0x807fff,-Map,$(PROJECT).map -o
Auch diese Syntax will nicht (wobei ich das Komma anstelle Gleichzeichen für einen Tipfehler halte):

TO_LINKER = $(TO_COMPILER) -Wl,-Tdata,0x800500,--defsym=__heap_end=0x807fff,-Map,$(PROJECT).map -o
Mit dieser Zeile arbeitet das Verschieben von .data wie gewünscht:

TO_LINKER = $(TO_COMPILER) -Wl,--section-start=.data=0x800500,--defsym=__heap_end=0x807fff,-Map,$(PROJECT).map -o

Woran das wohl wieder liegt?