Guten Tag,
mein Programm für die Fernrohrsteuerung ist jetzt schon >2k und in ASM
geschrieben. Obwohl das AVR-Studio keine Fehlermeldung ausgibt, springt
der Programmcounter beim Aufrufen von bestimmten Funktionen immer auf
Reset. Durch umschichten der Include-Dateien konnte ich zwar das
Problem beheben, aber das war wohl keine Lösung. So habe ich mir die
Funktion RCALL genau angesehen und siehe da die Reichweite ist +/-2k.
Also gut nehme ich CALL. Und da liegt meine Frage:
Kann ich ohne Probleme das RCALL durch CALL ersetzen oder muß ich noch
etwas beachten.
Bis jetzt weiß ich aus der Hilfe das CALL nur nach vorwärts hüpfen kann
(0 ≤ k < 64K ). Wenn das so ist, wie löse ich dann Sprünge zurück?
Ich meine nicht "ret" sondern den Aufruf einer Routine XXXXXX (im
liegt Speicher z.B. bei 3k). Innerhalb der Routine XXXXX wird die
Routine YYYYY aufgerufen. Diese liegt im Speicher ganz am Anfang. ->
Größer 2k zurück.
Geht das überhaupt mit Call?
Gruß
Stevko
Rufus, das habe ich auch gedacht aber er springt mit Call in die
Routine.
z.B. Call Temp_Messung , funktioniert aber das ist vorwärts. Ich möchte
nicht alle Rcall's tauschen ohne es genau zu wissen.
Gruß
Stevko
Da der Speicher bei 0 beginnt und das Ziel absolut ist, gibt es beim
call keine Unterscheidung zwischen vorwärts und rückwärts. Wie Rufus
Frage impliziert, spring er einfach an jede beliebige Stelle im RAM,
egal ob die vor oder nach deinem call Aufruf liegt.
Hi,
CALL verlangt als Operand die absolute Zieladresse. Das bedeutet, dass
0 <= k <= 64K am Anfang des Speichers loszählt und nicht an deiner
aktuellen Position. Somit geht es damit vor und zurück.
RCALL deckt beim AVR den Adressraum +- 2k Worte ab. Bei Controllern mit
<= 8k Flash ist der gesamte Adressraum mit RCALL erreichbar. Daher ist
die Int-Tabelle auch nur single word orientiert. Bei >8K Flash muß für
RCALL die Zieladresse innerhalb von +-2K Worten liegen, sonst ist der
Befehl CALL zu verwenden.
Achso habe ich vergessen ATMega 32 ist der Proz.
Da das Call bei mir vorwärts funktioniert und der Aufruf nach
ca.20Bytes erfolgt(nicht bei Null), nehme ich an das der Kompiler
automatisch die absolute Adresse in den Code einfügt.
Sehe ich das richtig so?
Gruß
Stevko
Hallo alle zusammen!
Genau dieses Problem hat mich auch schon mal mächtig Nerven gekostet:
Absolut nicht zu erklärende Resets beim Aufruf bestimmter
Unterfunktionen bei einem Assemblerprogramm. Auch hier wurde das
AVR-Studio benutzt.
Eine nähere Betrachtung mit dem Simulator brachte zu Tage, dass der
liebe Assembler beim "rcall" nicht prüft, ob das Ziel ausserhalb der
Reichweite liegt. Die im Befehl codierte Adresse war schlichtweg falsch
(der höherwertige Teil wurde einfach abgeschnitten), ohne das jemals
eine entsprechende Fehlermeldung ausgegeben wurde.
Ich weiß leider die genaue Versionsnummer des Assemblers nicht mehr,
eventuell mal eine neuere Version des AVR-Studios probieren.
@Stevko: Schau Dir einfach mal im simulator an, wohin die einzelnen
Funktionsaufrufe springen. Da könnte die Überraschung warten.
Bei uns wurden jedenfalls aus Sicherheitsgründen alle "rcall"-Befehle
gegen "call" ausgetauscht.
MfG
Stefan Widmann
Der AVR-Assembler prüft schon, ob ein rcall reicht, man muss ihn aber
auch lassen. Bei den Assembler-Options gibt es schon seit langem ein
Kästchen mit der Bezeichnung "wrap relative jumps". Wenn dort ein
Hacken drinn ist, springt der assembler bei langen Sprüngen ausserhalb
seiner 2kWorte Reichweite einfach -Sprungweite zurück. Bei AVRs bis zu
8kB Flash funktioniert das auch problemlos. Nur für die größeren MUSS
man den Hacken rausnehmen, will man nicht auf die Nase fallen.
Jörg
Ok, Danke an alle.
Und wenn es der Peter sogar bestätigt, werde ich heute Abend alle
RCALL's gegen CALL austauschen und kann hoffentlich ruhig schlafen.
Gruß
Stevko
@plitzi
Na das ist doch ein guter Tip. Werde es auch auprobieren. Denn bei mir
war offensichtlich der Hacken drin, da ich keine Fehlermeldung bekam.
Gruß
Stevko
Wieso ist bei rcall und Sprungadressen von +-2k bei 4k (Word)
Programmspeicher IMMER der gesamte Programmspeicher adressierbar?
Wenn mein rcall bei Adresse 4000 steht und ich auf Adresse 1000 springen
will, dann sind dies mehr als 2048 Adressen und es ist trotzdem nicht
mehr als 4096 Programmspeicheradressen verbraucht worden.
Till Xxx schrieb:> Wenn mein rcall bei Adresse 4000 steht und ich auf Adresse 1000 springen> will, dann sind dies mehr als 2048 Adressen und es ist trotzdem nicht> mehr als 4096 Programmspeicheradressen verbraucht worden.
4000 + 1096 = 1000
4000 1111 1010 0000
+ 1096 0100 0100 1000
= 1000 (1) 0011 1110 1000
Gruß
Jobst
Till Xxx schrieb:> Wieso ist bei rcall und Sprungadressen von +-2k bei 4k (Word)> Programmspeicher IMMER der gesamte Programmspeicher adressierbar?
Weil die Mathematik des Zweierkomplements zusammen mit dem Wraparound am
Programmspeicherende das halt hergibt.
> Wenn mein rcall bei Adresse 4000 steht und ich auf Adresse 1000 springen> will, dann sind dies mehr als 2048 Adressen
4000d= FA0
1000d= 3E8
Die nötige Sprungdistanz:
3E8
-FA0
----
F448 = -3000d
modulo Prgrammspeichergröße
F448
&07FF
-----
448 = +1096d
gesprungen wird als 1096 Adressen nach vorn statt 3000 nach hinten.
Probe:
FA0
+448
----
13E8
modulo Programmspeichergröße
13E8
&07FF
-----
3E8 = 1000d
Voilá, wir sind da, wo wir hinwollten.
Hi
Ich habe ein wenig mitgelesen und wenn ich eine bereits gegebene Antwort
gebe, hab ich sie einfach überlesen. Der Befehl "RCALL" ist ein
relativer Befehl und sollte vom Compiler auch als solcher berechnet
werden. Verschiebt sich der Speicher aus irgendeinem Grund, so ist die
Distanz immer noch vorhanden. Bei einem Absoluten Sprung ist dies
natürlich nicht der Fall.
Also, ein Programm ruft an Adresse 1000 einen RJmp zu 1200. Demnach
setzt das Programm an der Speicherstelle aktuelle Adresse+200, also der
Distanz, fort. Aus irgendwelchen Gründen wird nun das gesamte Programm
um 1000 Speicherzellen verschoben, ohne neu übersetzt zu werden. Dann
steht der Aufruf an der Adresse 2000. Der relative Sprung erfolgt nun
zur Adresse 2200. Wäre es ein einfacher JMP gewesen, würde immer noch
die Adresse 1200 angesprungen. In der Regel ist das auf einem AVR mit
Flash-Speicher nicht so relevant, aber bei Controllern mit Programm in
einem RAM gibt es schon dahingehend Unterschiede. So werden
Programmblöcke vom Programmierer gar nicht erkennbar mal nach x, mal
nach y geladen. Dein absoluter Sprung im Programm ist dann das AUS.
Ich glaube, dein Programmfehler liegt woanders, denn eine
Bereichsüberschreitung meldet der Compiler. Grad wenn man in ASM
programmiert, übersieht man schon ganz gern mal ein Register, welches in
einer ISR verändert wird und auf den Stack gehört. So zum Beispiel ein
Unterprogramm, aufgerufen aus einer ISR. Die Register innerhalb der
Subroutine müssen auch gesichert werden!
Gruß oldmax
oldmax schrieb:> aber bei Controllern mit Programm in> einem RAM gibt es schon dahingehend Unterschiede. So werden> Programmblöcke vom Programmierer gar nicht erkennbar mal nach x, mal> nach y geladen. Dein absoluter Sprung im Programm ist dann das AUS.
Deshalb gibt es bei solchen Systemen ja auch Mechanismen, die für
absolute Sprünge die Adresse korrigieren, bevor das Programm ausgeführt
wird.
Dafür gibt es Tabellen, in denen steht, an welchen Positionen die
Startadresse hinzuaddiert werden muss.
Gruß
Jobst
oldmax schrieb:> So werden> Programmblöcke vom Programmierer gar nicht erkennbar mal nach x, mal> nach y geladen.
Warum sollte das so sein? Zufallsgenerator im Startup-Code? Wo das z.B.
vorkommen kann, sind Programme, die dynamisch gelinkt sind.
Jobst M. schrieb:> oldmax schrieb:>> aber bei Controllern mit Programm in>> einem RAM gibt es schon dahingehend Unterschiede. So werden>> Programmblöcke vom Programmierer gar nicht erkennbar mal nach x, mal>> nach y geladen. Dein absoluter Sprung im Programm ist dann das AUS.>> Deshalb gibt es bei solchen Systemen ja auch Mechanismen, die für> absolute Sprünge die Adresse korrigieren, bevor das Programm ausgeführt> wird.
Das geschieht aber nicht dadurch, daß absolute Sprünge vom
(dynamischen) Linker korrigiert werden. Stattdessen wird bereits beim
Compilieren des Codes und Erstellen der .dll / .so dafür Sorge getragen,
daß entsprechender Code erzeugt wird. Beim GCC z.B. mittels -fpic oder
-fpie. Beides wird z.B. vom avr-gcc nicht unterstützt.
Hi
Wenn man euch so zuhört, dann fragt man sich, wofür die Erbauer der
Befehlscodes den RCALL und RJMP-Befehl erfunden haben. Weil relativ
adressieren schneller ist? Na ja, RCALL = 3Takte, CALL = 4Takte. Bei JMP
und RJMP ist ebenfalls 1 Takt weniger beim relativen Aufruf, aber ist
das wirklich der Grund?
Na ja, mir ist es egal. Relativ heißt für mich immer noch, das mein Code
ab jeder beliebigen Stelle im Speicher laufen kann, solange kein
direkter Sprung innerhalb des Blocks programmiert ist. Ist zum Beispiel
auch gegeben, wenn eine bereits compilierte Subroutine in das Programm
eingegliedert wird.
Ok, ich bin kein Profi, also werd ich mich jetzt dem WE widmen.
Gruß oldmax
Verschieblichkeit des fertig erzeugten Binärcodes ist bei der Konzeption
solcher Mikrocontroller völlig irrelevant. Das betrifft nur Systeme, die
Programme dynamisch laden, nicht solche, in denen das Programm aus dem
Flash-ROM ausgeführt wird.
RCALL ist aber kürzer als CALL, spart also neben einem Takt auch Platz.
Über kurze Distanzen kann man RCALL statt CALL verwenden. Darum geht es.
Manche Mikrocontroller verwende(te)n statt PC-relativer kurzer Sprünge
solche innerhalb der aktuellen Codeseite. Das ist zwar technisch etwas
einfacher, sorgt aber für geharnischte Flüche bei Programmierern und
Compiler-Autoren, also ist man davon abgekommen.
oldmax schrieb:> i> Wenn man euch so zuhört, dann fragt man sich, wofür die Erbauer der> Befehlscodes den RCALL und RJMP-Befehl erfunden haben.
Weil mehr nicht in eine Standard-16-Bit-Instruktion reingepaßt hat. JMP
ist dann einer der wenigen besonderen Ausnahme-Befehle, die 32 Bit breit
sind. Den gibt's dann auch nur auf Prozessoren, die ihn wirklich
brauchen.
> Na ja, mir ist es egal. Relativ heißt für mich immer noch, das mein Code> ab jeder beliebigen Stelle im Speicher laufen kann, solange kein> direkter Sprung innerhalb des Blocks programmiert ist.
Beim RCALL heißt das aber auch, daß alle Funktionen, die dein Code
aufruft, sich gemeinsam mit diesem auch immer mitverschieben müssen.