hallo, kann mir jemand auf anhieb die wortbreite eines void* in avr-gcc nennen? ich vermute 16 bit. zumindest da beschwert sich der kompiler nicht. vll ist das auch der grund, warum die guten pgmspace-routinen nur bis 64k definiert sind (u.a. ja, ich hab mir ein paar beispiele daraus angeschaut) vielen dank, schönen abend, bye kosmo
Mal sizeof (void *) ausgeben lassen? Ansonsten: Deine Annahme ist korrekt, in avr-gcc ist ein Pointer 16 Bit breit.
ach ja, gute idee :) das ist ja dumm, sehr ärgerlich für 128k flash. danke vielmals
kosmonaut pirx wrote: > [...] vll Viel lieber? Viel länger? Wir sind doch hier nicht beim Morsecode. > ist das auch der grund, warum die guten pgmspace-routinen nur bis 64k > definiert sind Naja, der Grund ist eher, dass ein uint8_t * auch 16 bits breit ist. ;-) Ja, um auf mehr als 64 KiB ROM im Datenmodus zuzugreifen, muss man zusätzlich zum normalen Zeiger noch RAMPZ behandeln, das machen all die _P-Routinen nicht. Dafür ist der Linkerscript so gebaut, dass er ROM-Daten direkt nach der Initialisierung, aber vor dem restlichen Code ablegt, damit sie möglichst unterhalb der 64-KiB-Grenze liegen. Für Programmzugriffe wird der ROM 16-bit-weise adressiert, daher ist dort die ,,magische Grenze'' erst bei 128 KiB -- was der Grund war, warum der Patch für die ATmega256x so lange gebraucht hat, bis jemand eine Idee hatte und jemand anders sie auch umgesetzt hat.
moin moin, >> [...] vll >Viel lieber? >Viel länger? >Wir sind doch hier nicht beim Morsecode. chat-sprache, entschuldigung. vll == vielleicht (man schreibt es einfach zu häufig) >Dafür ist der Linkerscript so gebaut, dass er >ROM-Daten direkt nach der Initialisierung, aber vor dem restlichen >Code ablegt, damit sie möglichst unterhalb der 64-KiB-Grenze liegen. "möglichst" finde ich eine nette ausdrucksweise :) >Für Programmzugriffe wird der ROM 16-bit-weise adressiert, daher ist >dort die ,,magische Grenze'' erst bei 128 KiB -- was der Grund war, >warum der Patch für die ATmega256x so lange gebraucht hat, bis jemand >eine Idee hatte und jemand anders sie auch umgesetzt hat kapier ich nicht .. 16Bit-weise? ich vermute, du meinst die verwendung des RAMPZ und die 16 Bit adressierung, ansonsten habe ich keine idee, wieso die grenze bei 128k liegen soll. vom ATmega256x habe ich bisher nur gehört, keinen plan davon. aber noch einmal zu meinem problemchen: mein ziel war/ist es, meiner anwendung eine routine zum schreiben in das flash anzubieten. etwa so:
1 | const void * PROGMEM |
2 | memcpy_p(const void * PROGMEM dest, void * src, size_t len, MEM mem) |
das letzte argument ist dazu da, um die quelle der daten zu kennzeichnen
(flash oder sram).
ich gehe derzeit davon aus, dass das durch die 16bit- void-pointer mit
>64k nicht funktioniert. ist diese annahme korrekt?
danke,
bye kosmo
> kapier ich nicht .. 16Bit-weise?
1 Befehlswort = 1 Adresse. Nächstes Befehlswort = Adresse+1. Da ein
Befehlswort 2 Bytes gross ist, sind mit einem 16 Bit breiten Program
Counter 64KWorte = 128KByte realisierbar.
Und da GCC verschiedene Pointer nicht verschieden implementieren kann,
gibt's bei >128KB Flash gewisse Probleme.
Für Datenzugriff auf Flash besteht das Problem seit langem, dank
Mega128. Und deshalb existieren entsprechende Lib-Funktionen, die mehr
als 64KB adressieren können. Nur kann man da nicht "void *" verwenden,
sonden sollte den dort vereinbarten Datentyp nutzen.
hallo, >Und deshalb existieren entsprechende Lib-Funktionen, die mehr >als 64KB adressieren können. >Nur kann man da nicht "void *" verwenden, >sonden sollte den dort vereinbarten Datentyp nutzen ich dachte, ich kenne mich in der avr-libc etwas aus. anscheinend doch nicht. welcher ist der datentyp, von dem du sprichst? Danke, bye kosmo
In avr/pgmspace.h gibt es ein paar pgm_read_xxx_far Funktionen, denen die Adresse als uint32_t übergeben wird. Damit lassen sich Adressen jenseits der ersten 64KB ansprechen.
hm, also als datentyp würde ich das ja nicht gerade bezeichnen. die pgm-far-dinger sind imho "bloss" assembler-macros, die explizit das RAMPZ setzen. übergeben werden zwar 32 bit, da gebe ich dir recht. verwendet werden davon aber immer nur 16, was auch sonst. es obliegt dem programmierer, seine 32-bit-adresse zu beurteilen und das entsprechende far- oder near-macro aufzurufen. ergo müsste ich mir für ein allgemeingültiges vorgehen zu einem void* (oder uint8_t* oder oder :) noch merken, in welcher "bank" die adresse liegt. oje.
sorry, korrektur, RAMPZ wird aus dem 32 Bit genommen (aus dem .. dritten byte(C)). ok.
Warum gibt's eigentlich nicht so ähnlich wie früher mal im DOS Zeigerarten "near" und "far"? Es gibt meines Wissens gcc-Targets, die sowas unterstützen.
> Es gibt meines Wissens gcc-Targets, die sowas unterstützen.
Details?
Bislang hiess es immer, dass GCC nicht in der Lage ist, für verschiedene
Pointer verschiedene Maschinendarstellungen zu verwenden. Das aber ist
eine Grundvoraussetzung.
hallo, ich will diesen thread nicht endlos gestalten, aber ihc muss noch einmal nerven: >1 Befehlswort = 1 Adresse. Nächstes Befehlswort = Adresse+1. Da ein >Befehlswort 2 Bytes gross ist, sind mit einem 16 Bit breiten Program >Counter 64KWorte = 128KByte realisierbar. ums kurz zu machen: der PC adressiert wörter? danke, bye kosmo
kosmonaut pirx wrote: > ums kurz zu machen: der PC adressiert wörter? Ja. EDIT (jetzt wirds doch länger): Macht ja auch Sinn. Schließlich sind alle AVR-Instruktionen 16 oder (seltener) 32 Bit breit. Deshalb wäre ein Byte-Zugriff ziemlich sinnlos. Beim Zugriff auf Daten sieht das natürlich anders aus, weshalb der (e)lpm-Befehl ja auch ein bisschen "tricksen" muss, um an einzelne Bytes heranzukommen.
hallo, das mit der breite der befehle ist mir inzwischen auch im manual übern weg gelaufen. es ist nur irritierend, dass der zugriff auf daten byteweise erfolgt. nun gut, so langsam habe ich's geschnallt. euch vielen dank, bye kosmo
Wenn du wirklich irritiert werden willst, dann programmier mal abwechselnd mit Atmels Assembler und mit dem GNU-Assembler. Atmel zählt nämlich im Code konsequenterweise Worte, GAS aber trotzdem Bytes.
@Andreas Kaiser: Ich dachte wirklich, daß es sowas gibt, aber finde nichts. Vielleicht habe ich mich da getäuscht. Immerhin gibt es aber so Sachen wie: 5.32.1 M32R/D Variable Attributes One attribute is currently defined for the M32R/D. model (model-name) Use this attribute on the M32R/D to set the addressability of an object. The identifier model-name is one of small, medium, or large, representing each of the code models. Small model objects live in the lower 16MB of memory (so that their addresses can be loaded with the ld24 instruction). Medium and large model objects may live anywhere in the 32-bit address space (the compiler will generate seth/add3 instructions to load their addresses). Das bringt einem aber natürlich nichts, wenn man über Zeiger darauf zugerifen will. Hier könnte aber C++ mal wieder seine Vorteile voll ausspielen. Man könnte damit ein Zeiger-Klassentemplate schreiben, das sich fast genau so verhält wie ein normaler Zeiger, aber sich automatisch um RAMPZ kümmert. Ich hab auch schon solche Spezialzeiger für RAM- und EEPROM-Zugriff geschrieben. Leider gibt es da ein Problem, nämlich den Operator->, der leider nicht so problemlos geht.
Global kannst du alle Pointer einstellen wie du willst, d.h. per Compiler-Option von 16bit Pointern auf 32bit Pointer umstellen. Das betrifft dann aber alle Pointer. Was nicht geht: einzelne Pointer entsprechend kennzeichnen. Das ist die alte wohlbekannte Krux an GCC und grösstes Hindernis bei der Portierung auf etliche Controller-Architekturen. Mit C++ bist zu wohl auf dem richtigen Weg, damit lassen sich jene generischen Pointer realisieren, wie sie von anderen Compilern für AVR oder 8051 implementiert werden. Als Kombination von Adresse und Addressraum. Billig ist das allerdings nicht und bei den Library-Funktionen wie strxxx und printf hilft das auch nicht weiter, es sei denn du implementierst auch diese neu.
Andreas Kaiser wrote: > Wenn du wirklich irritiert werden willst, dann programmier mal > abwechselnd mit Atmels Assembler und mit dem GNU-Assembler. Atmel zählt > nämlich im Code konsequenterweise Worte, GAS aber trotzdem Bytes. Atmel's Assembler ist da einfach in der eigenen Suppe gewachsen. Macht niemand sonst, obwohl RISC-CPUs schon seit vielen Jahren nur Vielfache ihrer Wortgröße adressieren können (und die ist dann größer als 16 Bits). Wenn du als Programmierer jedesmal zwischen Bytes und der jeweiligen Maschinenwortgröße umrechnen darfst, bekommste doch auf Dauer eine Macke. Bekommste ja auch beim AVR, da LPM inkonsequenterweise Bytes adressieren kann... Meiner Meinung nach wäre es sinnvoller gewesen, sie hätten LPM dann auch auf 16-bit-Worten aufgesetzt. Das gelegentlich mal verplemperte Byte an Ende eines Strings macht das Kraut nicht fett.
Wie stellst du dir ein solches LPM vor? Als 16bit Ladebefehl? Ok. Dann werden aber alle String-Operation *_p deutlich komplizierter, denn wo steht geschrieben, dass der String an einer geraden Adresse anfängt. Bliebe allenfalls die Variante eines Byte-LPM mit Byteauswahl im Carr<flag oder so. Auch nicht der Weisheit letzter Schluss. Die angesprochenen RISCs übrigens arbeiten zwar wortweise, adressieren aber durchaus Bytes, anders als AVR im Code. Besseres Beispiel ist Maxim MAX2000 (auch sonst lohnend, in dessen Doku mal reinzuschauen). Das ist die wohl einzige Mikroprozessorarchitektur jenseits der 70er Jahre, die allen Ernstes auch Daten wortweise adressiert. Will man Bytes laden/speichern, stellt man das Speicherinterface ad hoc auf Byteadressierung um. Da kommt Freude auf, zumal die Adresse eines Objektes auch noch davon abhängt, in welchem Speicher der Befehl steht, der darauf zugreift.
Andreas Kaiser wrote: > Wie stellst du dir ein solches LPM vor? Als 16bit Ladebefehl? Ok. Ja, werden eben als Ziel immer zwei Register geladen. > Dann > werden aber alle String-Operation *_p deutlich komplizierter, denn wo > steht geschrieben, dass der String an einer geraden Adresse anfängt. Das wäre dann die Bedingung gewesen. Lässt sich im Compiler einfach realisieren. Wie geschrieben, man verplempert mit 50%iger Wahrscheinlichkeit ein Byte, aber das stört beim ROM eher nicht. Auch den Rest (Zugriff auf die beiden gelesenen Bytes) kann ein Compiler einfach erledigen, und das war ja mal die Grundidee hinter RISC. Dafür wäre wenigstens die Adressierung konsistent gewesen.
> Das wäre dann die Bedingung gewesen. Lässt sich im Compiler einfach > realisieren. Und was wird aus:
1 | char hex[] PROGMEM = "0123456789ABCDEF"; |
2 | pgm_read_byte(&hex[i]); |
3 | strcpy_p(buf, "unsinn"+(sinn ? 2 : 0)); |
sizeof(char)==1 per Definition. Geht also nur, wenn pro Wort nur 1 Byte gespeichert wird.
Andreas Kaiser wrote: > Und was wird aus: >
1 | char hex[] PROGMEM = "0123456789ABCDEF"; |
2 | > pgm_read_byte(&hex[i]); |
pgm_read_byte() kann es logischerweise nicht geben, wenn die
zu Grunde liegende Maschine das nicht kann.
>
1 | strcpy_p(buf, "unsinn"+(sinn ? 2 : 0)); |
Verboten. ;-) strcpy_p() ist keine Standard-Funktion, es wäre der Implementierung also einfach möglich gewesen zu erklären, dass das Ergebnis nur definiert ist, solange der Parameter ein string literal ist, das vom Compiler in den Flash gelegt worden ist.
Jörg Wunsch wrote: > pgm_read_byte() kann es logischerweise nicht geben, wenn die > zu Grunde liegende Maschine das nicht kann. Und was ist mit &hex[i]? Wenn es dem Compiler überhaupt noch gestattet sein soll, selbst Adressen für Flash-Datenobjekte zu vergeben und zu nutzen, dann muss dieser Ausdruck erlaubt sein. Sofern man sich einigermassen an den C Kontext halten und keine neue Programmiersprache für die Flash-Objekte erfinden will. Nope. Wenn &hex[i] überhaupt definiert ist, dann ist 1 char = 1 Flash-Wort, alle Probleme sind gelöst - ausser dem entstehenden Platzproblem. Nur diese Variante ist wirklich konsistent. Wenn es dir dann noch gelingt, im Codeword 2 Bits einzusparen, dann hast du einen PIC14 ;-).
> Wenn es dir dann noch gelingt, im Codeword 2 Bits einzusparen, dann hast > du einen PIC14 ;-). Ick. ;-) Ich würde wohl eher das Codewort auf 32 Bits ausdehnen. Ach nee, hilft auch nicht, dann hab' ich einen ARM. ;-)
> Billig ist das allerdings nicht Doch, wenn man es richtig implementiert schon. Mein Flash-Pointer ist kein bischen größer als direkte Zugriffe auf Flash-Variablen mittels pgm_read_*. Es wird immer angenommen, mit C++ habe man einen immanenten Overhead gegenüber C, aber das ist Unsinn. > und bei denLibrary-Funktionen wie strxxx und printf hilft das auch > nicht weiter, es sei denn du implementierst auch diese neu. Das stimmt. Aber ein cout-artiges Interface wäre eh viel geschickter auf einem AVR als printf.
> Doch, wenn man es richtig implementiert schon. Ich dachte eher an generische Pointer, d.h. 3 Byte für die Adresse, 1 für den Adressraum (code/data). Das ist schon ein bischen teurer. Das wird man bei AVRs aus Platzgründen eher nicht inlinen wollen, und kostet damit doch etwas mehr Zeit.
> Ich dachte eher an generische Pointer Ach so. An sowas hatte ich auch schon mal gedacht, per Compilerflag ein-/ausschaltbar. >, d.h. 3 Byte für die Adresse, 1 ür den Adressraum (code/data). Im Prinzip reicht auch ein Bit für den Adressraum. 23 Bits für die Adresse reichen ja auch für alle bisherigen AVRs. Erst wenn mal einer mit mehr als 8MB Flash rauskommt, braucht man noch ein Byte mehr. > Das ist schon ein bischen teurer. Ja, aber nur dort, wo auch wirklich die Unterscheidung zur Laufzeit gemacht werden muß. Für direkte Variablenzugriffe (und wegoptimierte Zeigerderferenzierungen) zahlt man nichts extra. Da kann schon der Compiler abhängig von der Adresse den richtigen Ladebefehl einsetzen.
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.