hi @ all, Unterstützt das aktuelle WinAvr GCC Packet den Mega2561? mfg Martin
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.
Sind eigendlich schon irgendwelche Bugs/Einschränkungen/ect. bekannt?
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.
@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.
> 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.
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!
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
"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
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.
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).
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.
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
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...
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.