Forum: Compiler & IDEs Schreiben in SRAM per pointer


von Steffffan (Gast)


Lesenswert?

Hallo,
habe folgenden c-Code für den ATMega16:
1
// Global pointer
2
unsigned char* happyPtr = 0;
3
4
...
5
6
// Write on stack
7
happyPtr = SP;
8
*happyPtr = (unsigned char)0x01;
9
happyPtr -= 1;
10
*happyPtr = (unsigned char)0x2D;
11
happyPtr -= 1;
12
SP -= 2;

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!

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> Ich will eine Rücksprungadresse auf den Stack schieben

Bist Du Dir wirklich ganz sicher, daß Du das in C machen willst?

von Steffffan (Gast)


Lesenswert?

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

von Rolf Magnus (Gast)


Lesenswert?

> Ich will eine Rücksprungadresse auf den Stack schieben , danach folgt
> ein reti.

Warum springst du nicht direkt?

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


Lesenswert?

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

von Steffffan (Gast)


Lesenswert?

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

von Rolf Magnus (Gast)


Lesenswert?

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

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


Lesenswert?

Außerdem denke ich, dass dieser Teil eines jeden RTOS wirklich
besser/einfacher in Assembler implementierbar ist.

von Steffffan (Gast)


Lesenswert?

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

von Rolf Magnus (Gast)


Angehängte Dateien:

Lesenswert?

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

von Steffffan (Gast)


Lesenswert?

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
1
volatile unsigned char temp = taskStates[currentTask].state;
2
3
if(temp == VIRGIN)
4
{
5
...
6
}

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?

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.