Forum: Compiler & IDEs Unterstützt AvrGCC den Atmega2561


von Martin (Gast)


Lesenswert?

hi @ all,

Unterstützt das aktuelle WinAvr GCC Packet den Mega2561?

mfg Martin

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Martin wrote:

> Unterstützt das aktuelle WinAvr GCC Packet den Mega2561?

Der GCC benötigt einen Patch dafür, der aber im aktuellen WinAVR schon
drin ist.

von Martin (Gast)


Lesenswert?

Danke

von Der T. (Gast)


Lesenswert?

Sind eigendlich schon irgendwelche Bugs/Einschränkungen/ect. bekannt?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Der Techniker wrote:

> Sind eigendlich schon irgendwelche Bugs/Einschränkungen/ect. bekannt?

Einschränkungen:

Nun ja, es ist halt eine halbherzige Portierung.  Klingt vielleicht
bisschen negativ (besonders angesichts der vielen und guten Arbeit,
die Björn Haase da reingesteckt hat).  Was es meint ist: der
ATmega256x (und wohl auch die größeren Xmegas, wenn sie denn mal da
sein werden) hat einen 24-bit-PC.  GCC kennt aber nur Zeiger der
Breite 16 oder 32 bit, und er muss auch alle Zeiger für einen
Zielprozessor gleich groß haben.  Um den ATmega256x zu bedienen, hätte
man mit normalen Mitteln also die Zeiger auf 32 bits ausdehnen müssen,
und dabei auch die Zeiger in den RAM vergrößert, obwohl der ja maximal
64 KiB hat.

Stattdessen hatte Marek Michalkiewicz seinerzeit eine Lösung
skizziert, die wohl als Vorbild irgendeinen anderen von GCC
unterstützten exotischen Prozessor hat, und Björn Haase hat diese
Lösung dann umgesetzt.  Man belässt die Zeigergröße dabei auf 16 bits
und unterteilt den Adressraum in Blöcke, die mit diesen Zeigern noch
adressierbar sind.  Da beim AVR die Codeadressen alle durch 2 geteilt
werden, lassen sich damit 128 KiB am Stück adressieren.  Zwischen
jedem der derart entstehenden Blöcke wird eine Sprungtabelle
angeordnet (Trampolin genannt), die als ,,Zwischenhüpf'' für alle
Sprünge dient, die die entsprechende Grenze überschreiten wollen.
Beim ATmega256x gibt es also genau eine davon, bei noch größeren AVRs
könnten es mehrere werden.

Dieser Ansatz funktioniert recht gut, auch wenn er auf den ersten
Blick sehr gewagt aussieht.  Das größte Risiko dieses Ansatzes ist
jedoch, dass der Linker Code über so ein Trampolin hinweg verteilen
könnte, den er besser nicht verteilen sollte.  Ein bekannter und
kritischer Fall stellen Sprungtabellen dar, wie sie z. B. vom Compiler
in großen case-Anweisungen erzeugt werden: da der Index den Offset in
die Sprungtabelle bildet, ist es natürlich tödlich, wenn der Linker
anschließend einen Teil der Sprungtabelle vor und den anderen hinter
dem Trampolin anordnet.  Leider hat der Compiler keine Ahnung, wohin
der Linker den Code mal platzieren wird, und der Linker weiß nicht,
dass der Compiler genau an dieser Stelle einen kritischen Block hat,
der unbedingt zusammenhängend bleiben muss.  Daher hatte Björn initial
im Assembler eine Warnung vorgesehen ("Expression is dangerous with
linker stubs.").  Der Nachteil davon war aber, dass sie in jeder auf
diese Weise erzeugten Sprungtabelle generiert worden ist und damit
eher Verwirrung gestiftet hat.  (Peter Dannegger bezeichnet sowas dann
wohl als ,schweren Bug'...)  Hinzu kommt, dass sie nur einen Teil der
möglichen fatalen Fälle überhaupt erkennen konnte, aber eine sichere
Erkennung der Gefahr gibt es gar nicht.  Daher hat Björn dann
zugestimmt, dass wir die Warnung abschalten. -- Die Gefahr bleibt aber
natürlich, wenn eine solche Tabelle genau über die 128-KiB-Grenze
hinweg aufgeteilt wird, dann kracht es, ohne dass die Tools diesen
Zustand derzeit feststellen und entsprechend darüber warnen könnten.

Das die einzige mir derzeit bekannte Falle im Zusammenhang mit dem
ATmega256x-Patch.

Allerdings hat der ATmega256x-Patch auch noch ,huckepack' einen Patch
mitgebracht, der main() zu einer normalen Funktion macht, wie es im
C-Standard eigentlich schon immer vorgesehen ist.  Dieser Patch bringt
uns zwar zurück zu standardgerechtem Verhalten, vergrößert aber
gleichzeitig das main(), da dort nun wie in jeder anderen Funktion
benutzte Register auf den Stack ,,gerettet'' werden etc. pp.  Abhilfe
dagegen ist es, main() als __attribute__((naked)) zu deklarieren und
eigentlich auch noch __attribute__((noreturn)), obwohl es verpflichtet
ist (vom Standard her), einen int zurückgeben zu können.  Eine
künftige Lösung sollte hier ein _attribute_ bieten, dass
gewissermaßen wie die Kombination der beiden genannten wirkt, aber den
Rückkehrwert trotzdem zulässt.  Das ist dann letztlich das, was beim
IAR-Compiler durch __c_task (vor main()) erreicht wird.

von yalu (Gast)


Lesenswert?

@Jörg Wunsch

Interessanter Beitrag! Danke.

Ich habe keinen gepatchten GCC da, mit dem ich das schnell
ausprobieren könnte, deswegen folgende Frage: Wie werden
Funktionszeiger dargestellt? Da fehlt doch bei 16 Bit 1 Bit
Information, und Trampoline helfen hier sicher auch nicht weiter.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Wie werden
> Funktionszeiger dargestellt?

Gute Frage.  Nächste Frage?

Muss ich auch erst ausprobieren.  Aber prinzipiell könnte da die
Trampoline wohl auch helfen: der Zeiger ist 16 bits und zeigt dann
immer auf das Trampolin.

von Der T. (Gast)


Lesenswert?

Hast du hierfür ein Codebeispiel, was zum Crash führen würde?
(evtl. kann ich es mir besser vorstellen, was du genau meinst)

Dabke!

von Εrnst B. (ernst)


Lesenswert?

1
switch (somevariable) {
2
3
  case 1: /* Viel Code, der compiliert >= 128kb */
4
    break;
5
  case 2: /* anderer Code */
6
}

Wenn der Compiler die Switch-anweisung als Sprungtabelle implementiert, 
und nicht zu if ... else if ... else optimiert, schlägt der Aufruf von 
"anderer Code" bei somevariable==2 fehl.

Statt den 128kb im "case 1" reicht natürlich auch viel weniger code, 
wenn vor dem switch entsprechend mehr ist.

/Ernst

von Der T. (Gast)


Lesenswert?

"Statt den 128kb im "case 1" reicht natürlich auch viel weniger code,
wenn vor dem switch entsprechend mehr ist."

D.h. bei >=128kb Code wird es ein Glücksspiel, was der Compiler draus 
macht?!? :-(

Betrifft das Problem auch Daten (z.B. Grafiken für ein Display), oder 
bleiben die davon unberührt?

Schöne Grüße

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ernst Bachmann wrote:

>
1
> switch (somevariable) {
2
> 
3
>   case 1: /* Viel Code, der compiliert >= 128kb */
4
>     break;
5
>   case 2: /* anderer Code */
6
> }
7
>

Nein, eher:
1
/* 127,9 KiB code hier */
2
...
3
  switch (somevariable) {
4
    case 1: ...
5
    case 2: ...
6
    /* viele case labels hier */
7
    case 13374: ...
8
  }

Nur bei vielen case labels wird der Compiler überhaupt in die
Versuchung kommen, eine Sprungtabelle anzulegen.  Der Code zwischen
den case labels ist nahezu unerheblich, der darf auch über die
128-KiB-Grenze hinweg gehen (das sortiert der Linker schon).  Der
einzig kritische Fall ist, dass die Sprungtabelle selbst vor der
128-KiB-Grenze (und damit dem nächsten Trampolin) beginnt, dann aber
vom Linker geteilt wird.  Kritisch daran ist, dass die
Tabellenberechnung halt nur die Anfangsadresse + Index rechnet und
nicht weiß, dass sie jetzt ab einem bestimmten Index die Größe des
Trampolins zusätzlich addieren müsste.

> Wenn der Compiler die Switch-anweisung als Sprungtabelle
> implementiert, und nicht zu if ... else if ... else optimiert, ...

"pessimiert", wolltest du hier schreiben.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Der Techniker wrote:

> D.h. bei >=128kb Code wird es ein Glücksspiel, was der Compiler
> draus macht?!? :-(

Mehr oder weniger ja.  Du kannst die Sprungtabellen auch mit
-mno-tablejump komplett abschalten, allerdings kann das eben bei
großen switches zu mehr Code und schlechterer Performance (vor allem
zu ungleichmäßiger Laufzeit der einzelnen case labels) führen.

> Betrifft das Problem auch Daten (z.B. Grafiken für ein Display),
> oder bleiben die davon unberührt?

Die würde es natürlich genauso betreffen.  Allerdings können die
Standard-*_P()-Funktionen sowieso nur auf Daten unterhalb 64 KiB
zugreifen, für alles andere musst du dir die Zugriffe mit den
"far"-Versionen selbst organisieren.  Insofern wirst du dir deine
Daten in solchen Fällen wohl gleich manuell an bestimmten Stellen
anordnen.  Dann kannst du natürlich auch gleich oberhalb 128 KiB
anfangen (bzw. oberhalb des Trampolins, aber ich glaube, dass das
Ding nur existiert, wenn es auch wirklich benötigt wird).

von yalu (Gast)


Lesenswert?

Falls es für jemanden von Interesse ist: Ich habe inzwischen meinen
GCC 4.1.2 gepatcht und damit meine Frage von oben beantwortet:

> Wie werden Funktionszeiger dargestellt?

Jörgs Vermutung

> Aber prinzipiell könnte da die Trampoline wohl auch helfen: der
> Zeiger ist 16 bits und zeigt dann immer auf das Trampolin.

war natürlich richtig :-)

Es wird direkt nach der Interruptvektortabelle ein Trampolin angelegt,
das JMPs zu allen Funktionen enthält, die

- oberhalb der Wortadresse 64K (128 KBytes) liegen und

- deren Adresse im Programm einer Variablen zugewiesen oder an eine
  externe Funktion übergeben wird.

Der Funktionszeiger ist dann die Adresse des entsprechenden
JMP-Befehls im Trampolin und kann, da das Trampolin in den unteren 64K
liegt, mit 16 Bits dargestellt werden.

Der Aufruf erfolgt durch einen EICALL. Warum ein ICALL nicht
ausreicht, habe ich allerdings nicht ganz verstanden. Vielleicht
besteht die Möglichkeit, dass der Linker das Trampolin an eine Adresse
legt, die mit ICALL nicht erreichbar ist.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Du kannst mal probieren, ob --relax beim Linker hilft.

von Εrnst B. (ernst)


Lesenswert?

Da könnte man fast in Versuchung kommen, den alten AVR-Port im SDCC 
wieder zu aktivieren (Wurde damals eingestampft, weil der AVR-GCC besser 
war)
Der kommt aus der '51er Welt und hat deswegen eh schon x verschiedene 
Pointer-Arten (idata, xdata, code, generic...), die auch verschiedene 
Längen haben können (z.B. 3 bytes bei gptrs)
/Ernst

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ernst Bachmann wrote:

> Da könnte man fast in Versuchung kommen, den alten AVR-Port im SDCC
> wieder zu aktivieren (Wurde damals eingestampft, weil der AVR-GCC besser
> war)

Du meinst, letzteres hätte sich mittlerweile geändert?

Ich denke, dass du selbst beim AVR-GCC mit -mno-tablejump allemal
besser bedient wärst...

von Εrnst B. (ernst)


Lesenswert?

Jörg Wunsch wrote:

> Du meinst, letzteres hätte sich mittlerweile geändert?

Nö, bestimmt nicht. Hat ja ewig niemand mehr was dran getan.
Aber von seiner "Entwicklungsgeschichte" her ist der SDCC halt besser an 
"Small devices" angepasst, mit Harvard-Architektur, Banking z.B. bei 
PICs, die verschiedenen Speicherarten bei '51ern etc.

/Ernst

von yalu (Gast)


Lesenswert?

Nochmal Thema Funktionszeiger:

>> Der Aufruf erfolgt durch einen EICALL.

Jörg Wunsch schrieb:

> Du kannst mal probieren, ob --relax beim Linker hilft.

Habe ich gerade: --relax ersetzt an vielen Stellen absolute Sprünge
(jmp, call) durch relative (rjmp, rcall), der eicall beim Aufruf eines
Funktionszeigers bleibt aber unverändert.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.