Forum: Compiler & IDEs Die pseudo-Havard-Architektur


von Günther F. (taraquedo)


Lesenswert?

Hallo!

Ich frage mich gerade, angeregt von meinem anderen Thread, wo überhaupt 
der Unterschied zwischen

funktionsname("Hallo Welt!");

und

funktionsname(PSTR("Hallo Welt!");

liegt. Beides wird im µC im gleichen Speicher untergebracht bloß im 
ersten Fall am Ende, im zweiten Fall mitten drin.

Welche Vorteile/Nachteile bietet dieses Prinzip? Warum legt gcc 
überhaupt eine Trennung an? Der Atmel unterscheidet das doch nicht oder 
liege ich da falsch?
Ergeben sich zur Laufzeit unterschiede?

Grüße!

von Werner B. (Gast)


Lesenswert?

Die erste Version dürfte kompilieren, die zweite wird der Compiler 
abweisen weil eine schließende Klammer fehlt ;-)

Doch Spass beiseite: Welche "pseudo-Havard-Architektur" meinst du?

In der Annahme du sprichst vom AVR (der keine 
"pseudo-Havard-Architektur" besitzt)

Wie kommst du auf "...bloß im ersten Fall am Ende, im zweiten Fall 
mitten drin."

Im ersten Fall steht der String sowohl im Flash als auch im RAM.
Im zweiten Fall steht er nur im Flash, und die Funktion muss mit den 
entsprechenden *_P - Befehlen auf diesen Speicherbereich zugreifen.

In beiden Fällen steht er "bevorzugt" (= wenn dort noch Platz ist) in 
den ersten 64kByte des Flash.

von Karl H. (kbuchegg)


Lesenswert?

> Im ersten Fall steht der String sowohl im Flash als auch im RAM.

Wobei man sagen muss, dass aus Sicht des Programmes der String
nur im SRAM steht.
Nur: Irgendwie muss der ja auch dorthin kommen. Das macht die
Startup Sequenz, die den String aus dem Flash ins SRAM kopiert,
noch bevor main() die Kontrolle erhält. Danach ist das Original
im Flash eigentlich überflüssig. Gearbeitet wird nur noch mit
der Version im SRAM.


von Günther F. (taraquedo)


Lesenswert?

Jetzt wird mir doch einiges klarer. Also "vergeude" ich jedes mal, wenn 
ich eine Konstante nutze wertvollen Arbeitsspeicher, wenn diese nicht 
direkt aus dem Flash geladen wird. Und das betrifft ja durchaus auch 
Konstanten, die im Assembler als Displacement gespeichert würden.
Betrifft denn die Speicherplatzzuweisung von Konstanten nur die pre-main 
Funktion? Sprich, wenn ich in einem Unterblock/Funktion eine Konstante 
definiere, dann ist diese doch nicht schon im SRAM, wenn ich noch nicht 
dahin verzweigt habe, oder?
Ich hätte das schon längst überprüft, weiss aber nicht, wie ich bei 
meinem AVR-Studio das SRAM anzeigen kann. Weiss jemand, wie das geht? 
Ich komme wohl an den Programmspeicher und das EEPROM, aber nicht an den 
Inhalt des SRAM.

Dann schließe ich also daraus, dass ein AVR eine echte 
Havard-Architektur ist, die Daten und Befehle getrennt verwalten kann? 
Donnerwetter, das hätte ich nicht gedacht.

Grüße!

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Äh, natürlich kommst Du im AVR-Studio ans RAM, was meinst Du wohl, wo 
Variablen gespeichert werden und der Stack liegt?

von Günther F. (taraquedo)


Lesenswert?

Ja, das ist mir schon klar. Nur man kann ihn nicht gesamtheitlich 
anzeigen, so wie man das mit dem Programmspeicher machen kann oder 
übersehe ich da was? Bei Variablen muss ich halt immer den Namen kennen. 
Da kann ich doch nicht sagen: Zeig mir mal alle Speicheradressen von 
0x00 bis 0x10.

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


Lesenswert?

Günther Frings wrote:

Da's noch nicht beantwortet worden ist:

> Welche Vorteile/Nachteile bietet dieses Prinzip?

Vorteile: parallelisierte Buszugriffe und damit kürzere
Abarbeitungszeit.  Drückt sich darin aus, dass ein LDS in 2 Takten
abgearbeitet wird, ein LPM in 3 Takten.  Das hängt damit zusammen,
dass beim LPM der ROM-Bus erst einmal mit dem Lesen des Befehls
beschäftigt ist und danach dann erst für die Daten zur Verfügung
steht.

Nachteile: hast du ja praktisch schon bemerkt.  Der Datenzugriff auf
den ROM benutzt andere Befehle als der auf den RAM.  Ein
Befehlszugriff auf den RAM ist gar nicht möglich (wobei das manche
sicher nicht als großen Nachteil empfinden werden ;-).  Die Sprache C
hat bislang keinerlei Unterstützung für verschiedene physisch
getrennte Adressräume.  Zwar wurde C auch schon sehr früh auf
Harvard-Maschinen benutzt (PDP-11 mit getrenntem Daten- und
Adressbereich), aber damals hat man das nur gemacht, um den
adressierbaren Speicher pro Task von 64 KiB auf 128 KiB zu vergrößern,
da wurden aus dem Befehlsspeicher dann wirklich nur Befehle gelesen
und aus dem Datenspeicher nur Daten.  Will man hingegen aus dem
Befehlsspeicher Daten lesen, hört's mit den Standardmitteln von C auf.

GCC kennt praktisch nur einen flachen Adressraum, die verschiedenen
Adressräume eines AVR (RAM, ROM, EEPROM) werden daher im AVR-GCC mit
fiktiven Offsets (die außerhalb der Speichergrenzen von AVRs liegen)
in einen flachen 32-bit-Adressraum abgebildet.  Das ist ein ziemlicher
Hack.  Andere Compiler bieten da zwar integrierten Support an, aber
letztlich kann durch die fehlenden C-Hausmittel eine gerufene Funktion
nicht mehr vom bloßen Angucken eines Zeigers her sagen, ob dieser nun
in den RAM oder ROM zeigt.  Es gibt einen Entwurf im
C-Standard-Kommittee für "Embedded C", der u. a. auch den sauberen
Umgang mit mehreren Speicherbereichen abdeckt, aber den hat meines
Wissens noch kein (namhafter) Compilerhersteller implementiert.

von Benedikt K. (benedikt)


Lesenswert?

Jörg Wunsch wrote:

> GCC kennt praktisch nur einen flachen Adressraum, die verschiedenen
> Adressräume eines AVR (RAM, ROM, EEPROM) werden daher im AVR-GCC mit
> fiktiven Offsets (die außerhalb der Speichergrenzen von AVRs liegen)
> in einen flachen 32-bit-Adressraum abgebildet.  Das ist ein ziemlicher
> Hack.

Im GCC gibt es ja den Datentyp prog_char bzw prog_uintxx_t.
Könnte man nicht das ganze so machen wie z.B. in Codevision, dass 
entsprechende Daten von einem solchen Pointer automatisch aus dem Flash 
geladen werden ? Das setzt natürlich voraus, das der Compiler den 
Datentyp strenger überprüft, und eine Umandlung von Pointern in prog_ 
Pointer nicht ohne explizite Casts zulässt. Dies sollte ja eigentlich 
kein allzugroßer Aufwand sein, denn genauso wie Adressen jenseits der 
64k andere Befehle erfordern, verwendet der Compiler für den Flash 
Bereich entsprechende Befehle. Oder habe ich da was übersehen ?

von A.K. (Gast)


Lesenswert?

Wenn man das könnte hätte man das längt getan. Offensichtlich ist jedoch 
der GCC bislang nicht in der Lage, die Assoziation eines Pointers mit 
einem Adressraum zum Codegenerator durchzureichen.

Was man sicherlich realisieren könnte: Alle Pointer als generische 
Pointer implementieren. 3 Bytes lang, mit einem Bit für ROM/RAM. 
Natürlich kostet das etwas: umständlicher platzraubender Zugriff oder 
Laufzeitfunktionen für Zugriff. Und weil GCC auch dann immer noch keine 
Unterschied zwischen Pointern macht, gilt das dann zwangsläufig für 
jeden Pointer.

von A.K. (Gast)


Lesenswert?

> denn genauso wie Adressen jenseits der 64k andere Befehle
> erfordern, verwendet der Compiler für den Flash
> Bereich entsprechende Befehle.

Bei bekannten Adressen ist das kein Problem. Die Krux ist nur, dass 
bekannte Adressen die absolute Ausnahme darstellen. Sowas gibt es 
eigentlich nur bei Zugriffen auf I/O-Ports (*(uchar)0x55). Bereits bei 
einer normalen Variable kennt erst der Linker die Adresse, und dann ist 
es zu spät.

Unter so etwas leiden auch Architekturen, die zwar nur einen einzigen 
Adressraum haben, aber unterschiedlich lange Adressen (z.B. 68xx, M16C) 
in den Befehlen codieren.

von Rolf Magnus (Gast)


Lesenswert?

> Dann schließe ich also daraus, dass ein AVR eine echte
> Havard-Architektur ist, die Daten und Befehle getrennt verwalten kann?

Ja. Es gibt Codespeicher (Flash) und Datenspeicher (RAM), die intern 
über getrennte Busse angebunden sind. Code kann nur aus dem Flash 
ausgeführt werden. Daten können zwas aus RAM und Flash gelesen werden, 
aber die Assembler-Instruktionen sind verschieden.

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.