Ich will eine Rücksprungadresse auf den Stack schieben (wahrscheinlich
habe ich noch die Byteorder falsch, aber das ist gerade nicht so
spannend), danach folgt ein reti. Das Problem ist, der erste Wert landet
nicht im SRAM, der zweite schon. Noch lustiger wirds, wenn man die 0x01
durch z.B. 0xCC ersetzt, das funktioniert dann.
Mehrere Fragen:
1. Warum funktioniert die zweite Zuweisung und die erste nicht?
2. Warum funktioniert die erste mit einem anderen Wert?
3. Warum muss man wie in dem Code oben den SP klonen, bevor man
dereferenzieren kann (bei letzterer Frage habe ich das böse Gefühl, dass
sich die Antwort auf alle Fragen findet)
Vielen Dank!
Wie funktioniert das denn für nicht konstante Werte in asm? Letztlich
will ich eine Variable auf den Stack schreiben. Ich wüsste nicht, wie
das inline asm geht...
In C würde man für sowas einen Funktionszeiger benutzen und die
Dreckarbeit dem Compiler überlassen. Ein RETI ist ja weiter nichts
als ein Sprung, bei dem das globale Interruptflag gesetzt wird, da
kann man auch vor dem Sprung gleich ein sei() machen (das in seiner
Wirkung ja immer einen Befehl verzögert ist).
Also nochmal in Ruhe:
Das Ganze gehört zu einem Preemtive Multi-Tasking-Scheduler. Der
Scheduler (Timer-Interrupt) springt zwischen verschiedenen
Laufzeitkontexten. D.h. der SP wird auf einen anderen Stack verschoben
(beim Wechseln des Tasks), auf dem es noch keine Rücksprungadresse gibt.
Ergo muss man bei bisher noch nicht ausgeführten Tasks die Startadresse
des entsprechenden Programms unten auf den entsprechenen Stack legen.
Alternativvorschläge zur Implementierung eines RTOS nehme ich zwar
prinzipiell auch gerne entgegen, aber in dem aktuellen Zusammenhang kann
ich nicht einfach die C-Funktionalität verwenden (vielleicht aus
Unwissenheit, deswegen poste ich ja hier).
MfG Steffffan
> Ergo muss man bei bisher noch nicht ausgeführten Tasks die Startadresse> des entsprechenden Programms unten auf den entsprechenen Stack legen.
Warum arbeitest du dazu im Stackkontext des Prozesses, dessen Stack du
erst initialisierst? Lege erst ein Array an, fülle es mit den Daten, die
der Scheduler erwarten würde und löse dann den Kontextwechsel aus bzw.
warte auf den Timerinterrupt. Der AVR-Port des FreeRTOS-Kernels könnte
dir da als Inspiration dienen.
> Lege erst ein Array an, fülle es mit den Daten, die> der Scheduler erwarten würde und löse dann den Kontextwechsel aus bzw.> warte auf den Timerinterrupt.
Ich bin mir nicht sicher, ob ich dich richtig verstehe, deshalb
folgendes zur Klärung.
1. Erst lege ich ein globales Array an, in dem für jeden Task der
entsprechende Stackpointer, Zustand (Running, Ready etc.) und
dergelichen gespeichert wird.
2. Jedes mal, wenn ein Task initialisiert wird, lege ich die
Rücksprungadresse (Anfangsadresse im Program Memory) und eine
entsprechende Anzahl Nullen (als initialisierter Laufzeitkontext) auf
den Stack des jeweiligen Tasks.
3. Danach starte ich den Idle-Task, der in der Zeile vor den betreten
der Endlosschleife die Interrupts einschaltet.
Zum FreeRTOS kenne ich
http://www.freertos.org/implementation/index.html, habe ich mich auch
weitestgehend dran gehalten (glaub ich). Allerdings wird ja inzwischen
die "ISR"-Schreibweise für Interrupts empfohlen, deswegen sieht der
Interrupt bei mir auch entsprechend anders aus.
Inzwischen habe ich einen Work-Around: Erst 0x02 reinschreiben, dann
einmal dekrementieren :-)
Dafür gibt es ein neues Problem, nämlich lässt sich ein Pointer nicht in
eine unsigned int casten. Dazu lasse ich mich natürlich auch gerne
beraten.
> 1. Erst lege ich ein globales Array an, in dem für jeden Task der> entsprechende Stackpointer, Zustand (Running, Ready etc.) und> dergelichen gespeichert wird.
Ja, und die Register.
> 2. Jedes mal, wenn ein Task initialisiert wird, lege ich die> Rücksprungadresse (Anfangsadresse im Program Memory) und eine> entsprechende Anzahl Nullen (als initialisierter Laufzeitkontext) auf> den Stack des jeweiligen Tasks.
Genau, aber auf den kannst du ja auch über einen ganz normalen Pointer
zugreifen. Das muß nicht der SP sein. Ich hänge mal die Datei mit
Taskinitialisierung und Kontextwechsel von FreeRTOS an (hab eh grad drin
nachgeschaut und es daher offen). Da ist es so gemacht, wie ich es
meine.
> Allerdings wird ja inzwischen die "ISR"-Schreibweise für Interrupts> empfohlen, deswegen sieht der Interrupt bei mir auch entsprechend> anders aus.
Es empfielt sich schon, die mit __attribute__((naked)) zu definieren und
den Taskwechsel oder zumindest das Speichern und Laden des Kontexts dann
in inline-Assembler zu implementieren.
> Dafür gibt es ein neues Problem, nämlich lässt sich ein Pointer nicht> in eine unsigned int casten. Dazu lasse ich mich natürlich auch gerne> beraten.
Was heißt "läßt sich ... nicht casten"? Was passiert, wenn du es
versuchst?
Vielen Dank schon mal soweit!
> Was heißt "läßt sich ... nicht casten"? Was passiert, wenn du es> versuchst?
Es kommt sinnloses Zeug raus, meistens Null, selten auch 0x0E (was
nichts mit der Pointeradresse 0x01D2 zutun hat). Habe schon ein Dutzend
verschiedene Klammerungen etc. versucht, immer umständlicher, immer
idiotensicherer, will aber nicht.
Kann es sein das gcc es nicht so mit dem Lesen aus Struct-Arrays hat?
Bastele hier gerade an einer if-Abfrage:
1
#define VIRGIN 0x03
2
3
...
4
5
if(taskStates[currentTask].state==VIRGIN)
6
{
7
...
8
}
Die Alternative wird ignoriert. Der Debugger sagt, das in
taskStates[1].state eine 3 drinsteht. Wenn ich
mache, zeigt die Watchlist nach der Alternative eine 0 in temp.
Den Pointer ziehe ich auch aus dem gleichen Array und es kommt nur Null
raus. Besteht da ein Zusammenhang?