Forum: Compiler & IDEs wortbreite void*


von kosmonaut pirx (Gast)


Lesenswert?

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

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Mal sizeof (void *) ausgeben lassen?

Ansonsten: Deine Annahme ist korrekt, in avr-gcc ist ein Pointer 16 Bit 
breit.

von kosmonaut pirx (Gast)


Lesenswert?

ach ja, gute idee :)
das ist ja dumm, sehr ärgerlich für 128k flash.
danke vielmals

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


Lesenswert?

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.

von kosmonaut_pirx (Gast)


Lesenswert?

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



von Andreas K. (a-k)


Lesenswert?

> 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.

von kosmonaut_pirx (Gast)


Lesenswert?

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

von Andreas K. (a-k)


Lesenswert?

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.

von kosmonaut_pirx (Gast)


Lesenswert?

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.




von kosmonaut_pirx (Gast)


Lesenswert?

sorry, korrektur,
RAMPZ wird aus dem 32 Bit genommen (aus dem .. dritten byte(C)).
ok.

von Rolf Magnus (Gast)


Lesenswert?

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.

von Andreas K. (a-k)


Lesenswert?

> 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.

von kosmonaut pirx (Gast)


Lesenswert?

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

von Johannes M. (johnny-m)


Lesenswert?

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.

von kosmonaut pirx (Gast)


Lesenswert?

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

von Andreas K. (a-k)


Lesenswert?

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.

von Rolf Magnus (Gast)


Lesenswert?

@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.

von Andreas K. (a-k)


Lesenswert?

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.

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


Lesenswert?

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.

von Andreas K. (a-k)


Lesenswert?

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.

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


Lesenswert?

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.

von Andreas K. (a-k)


Lesenswert?

> 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.

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


Lesenswert?

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.

von Andreas K. (a-k)


Lesenswert?

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 ;-).

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


Lesenswert?

> 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. ;-)

von Rolf Magnus (Gast)


Lesenswert?

> 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.

von Andreas K. (a-k)


Lesenswert?

> 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.

von Rolf Magnus (Gast)


Lesenswert?

> 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
Noch kein Account? Hier anmelden.