Forum: Mikrocontroller und Digitale Elektronik Atmega SRAM Adresse lesen


von Leopold N. (leo_n)


Lesenswert?

Hallo,

ich möchte aus dem SRAM vom Stack mithilfe des Stackpointerregisters 
Werte lesen.

uint16_t wert = *SP;             // SP = Stackpointerregister

Der Compiler schmeißt mir da einen Fehler hin.
"invalid type argument of unary '*'


Wo ist mein Fehler?

Grüße

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

In C wird der Stack komplett vom Compiler verwaltet. Da solltest du auf 
keinen Fall "direkt" drauf zugreifen, denn das gibt ein Chaos. In C 
kannst du auch nicht direkt auf Prozessor-Register wie "SP" zugreifen. 
Wenn du etwas auf dem Stack ablegen möchtest, benutze einfach lokale 
Variablen.

von Leopold N. (leo_n)


Lesenswert?

Ich möchte nichts drauf ablegen, sondern einfach nur einen beliebigen 
Wert vom Stack lesen, ohne dass der Stack verändert wird.

von Georg G. (df2au)


Lesenswert?

> Wert vom Stack lesen, ohne dass der Stack verändert wird.

Dann musst du notgedrungen mit inline Assembler arbeiten.

von Oliver S. (oliverso)


Lesenswert?

Da der Stackpointer im IO Space liegt, lässt der sich genauso wie jedes 
andere Register direkt in C auslesen. Üblicherweise auf den Adressen 
0x5d und 0x5e. Datenblatt lesen. Nur die 16 Bit musst du von Hand 
zusammbauen.

Oliver

: Bearbeitet durch User
von Niklas Gürtler (Gast)


Lesenswert?

Leopold N. schrieb:
> sondern einfach nur einen beliebigen Wert vom Stack lesen, ohne dass der
> Stack verändert wird.

Was für Werte sind das, an die du auf normale Weise nicht dran kommst? 
Woher weißt du, wo der C Compiler die hin gepackt hat? Das Auslesen kann 
aber den Stack durchaus ändern, denn die Werte müssen ja irgendwo hin... 
In Register oder auf den Stack.

von holger (Gast)


Lesenswert?

>uint16_t wert = *SP;             // SP = Stackpointerregister
>
>Der Compiler schmeißt mir da einen Fehler hin.
>"invalid type argument of unary '*'

Wenn du das ausserhalb einer Funktion schreibst geht das nicht.

von Oliver S. (oliverso)


Lesenswert?

Es geht auch innerhalb einer Funktion nicht.

Aber das geht:
1
 uint16_t wert = SP;

Oliver

von Leopold N. (leo_n)


Lesenswert?

Oliver S. schrieb:
> Es geht auch innerhalb einer Funktion nicht.
>
> Aber das geht:
>
1
 uint16_t wert = SP;
>
> Oliver

Aber dann habe ich ja nur den Stackpointer gespeichert.
Ich will aber Speicherzellen bezogen auf den SP auslesen.

von Falk B. (falk)


Lesenswert?

@Leopold N. (leo_n)

>Aber dann habe ich ja nur den Stackpointer gespeichert.
>Ich will aber Speicherzellen bezogen auf den SP auslesen.

Dann mach es KOMPLETT in Assembler. Dier Mischmasch mit C ist Murks^3.

von Einer K. (Gast)


Lesenswert?

Leopold N. schrieb:
> Oliver S. schrieb:
>> Es geht auch innerhalb einer Funktion nicht.
>>
>> Aber das geht:
>>
1
 uint16_t wert = SP;
>>
>> Oliver
>
> Aber dann habe ich ja nur den Stackpointer gespeichert.
> Ich will aber Speicherzellen bezogen auf den SP auslesen.

In Arduino sieht das so aus:
1
uint16_t *ptr = SP;
2
Serial.println(*ptr);
Sollte auch in C ähnlich funktionieren

von Oliver S. (oliverso)


Lesenswert?

Leopold N. schrieb:
> Aber dann habe ich ja nur den Stackpointer gespeichert.
> Ich will aber Speicherzellen bezogen auf den SP auslesen.

Ja und? Mach doch. Wer C können will, kann auch Pointer und deren 
Arithmetik.

Oliver

von Leopold N. (leo_n)


Lesenswert?

Oliver S. schrieb:
> Leopold N. schrieb:
>> Aber dann habe ich ja nur den Stackpointer gespeichert.
>> Ich will aber Speicherzellen bezogen auf den SP auslesen.
>
> Ja und? Mach doch. Wer C können will, kann auch Pointer und deren
> Arithmetik.
>
> Oliver

Sehr hilfreich.

Beitrag #5584569 wurde vom Autor gelöscht.
Beitrag #5584573 wurde von einem Moderator gelöscht.
von Niklas Gürtler (Gast)


Lesenswert?

Leopold N. schrieb:
> Sehr hilfreich.

Beschreib doch mal genau, was du warum machen willst. Dann kann man 
dir auch besser helfen.

von Leopold N. (leo_n)


Lesenswert?

Niklas Gürtler schrieb:
> Leopold N. schrieb:
>> Sehr hilfreich.
>
> Beschreib doch mal genau, was du warum machen willst. Dann kann man
> dir auch besser helfen.

Ich möchte ein präemptives RTOS programmieren (ich möchte es selbst 
machen, also bitte nicht ankommen und sagen, dass es so etwas schon 
gibt, und der und der das schon viel besser gemacht hat).

Deshalb muss ich beim Taskwechsel den Kontext sichern (32 GP-Register, 
SREG, PC und SP).
Ich lege für jeden Task einen eigenen Stack in Form eines einfachen 
Arrays an.
Wenn ich nun einen Task aufrufe, stelle ich seinen Kontext aus seinem 
Stack wieder her, und setze den PC. Problem ist bloß: Woher kriege ich 
seinen PC?
--> Überlegung: Bei Interrupteintritt (Timer, der Systemzeit zählt und 
auswählt, welcher Task ausgeführt wird für die nächste Periode) wird die 
Rücksprungadresse auf den (taskeigenen) Stack gelegt. Man müsste ihn 
also mit einem Offset vom aktuellen SP auslesen können.

Daher meine Frage - weiß jemand Rat?

Grüße

von Stefan F. (Gast)


Lesenswert?

Ich glaube nicht, dass man den Kontext-Switch in C implementieren kann.

von Niklas Gürtler (Gast)


Lesenswert?

Leopold N. schrieb:
> Ich möchte ein präemptives RTOS programmieren

Aha, das erklärt einiges. Den Kontext Wechsel solltest du am Besten 
komplett in Assembler machen, d.h. die ganze ISR darin schreiben. Sonst 
kommt dir nur der Compiler in die Quere. Du weißt ja sonst z.B. 
überhaupt nicht, was der Compiler so alles am Registern auf den Stack 
gesichert hat und wo somit die Rücksprungadresse relativ zum SP zu 
finden ist.
So etwas geht übrigens auf Cortex-M besser, die sind dafür gemacht ;-)

Leopold N. schrieb:
> bitte nicht ankommen und sagen, dass es so etwas schon gibt, und der und
> der das schon viel besser gemacht hat

Tja, von Wegen... Habe letztens enttäuscht festgestellt dass FreeRTOS 
auf ARM für Mutexe einfach die Interrupts abschaltet. Schlecht für die 
Echtzeit-Fähigkeit. Man hätte ja auch Atomics nutzen können. Dafür dass 
das so angepriesen wird wie die beste Erfindung seit geschnitten Brot...

von Stefan F. (Gast)


Lesenswert?

Hat der (welcher?) ATmega genug RAM für dein Vorhaben?

von Leopold N. (leo_n)


Lesenswert?

Stefanus F. schrieb:
> Hat der (welcher?) ATmega genug RAM für dein Vorhaben?

Kann wählen zwischen Atmega32 (2kB RAM) und Atmega644 (4kB RAM)
Hab bis jetzt aber noch keine Probleme gehabt.

Niklas Gürtler schrieb:
> Aha, das erklärt einiges. Den Kontext Wechsel solltest du am Besten
> komplett in Assembler machen, d.h. die ganze ISR darin schreiben. Sonst
> kommt dir nur der Compiler in die Quere. Du weißt ja sonst z.B.
> überhaupt nicht, was der Compiler so alles am Registern auf den Stack
> gesichert hat und wo somit die Rücksprungadresse relativ zum SP zu
> finden ist.

Ja das hatte ich eigentlich auch vor, hatte mit Assembler bis vor Kurzem 
aber noch garninix am Hut :/
Was mich stört, ist, dass ich bis jetzt nirgendwo gefunden habe, was der 
Compiler normalerweise in welcher Reihenfolge alles auf den Stack haut.
Habs probiert mit .lss file anschauen und Simulator. Daraus werd ich 
aber net ganz schlau.

Gibt es irgendwelche anderen Wege an den PC und SP ranzukommen?

Grüße

von Niklas Gürtler (Gast)


Lesenswert?

Leopold N. schrieb:
> Ja das hatte ich eigentlich auch vor, hatte mit Assembler bis vor Kurzem
> aber noch garninix am Hut :/

Dann lern das erstmal, sonst wird das nix mit eigenem RTOS. AVR 
Assembler ist ja recht simpel.

Leopold N. schrieb:
> Was mich stört, ist, dass ich bis jetzt nirgendwo gefunden habe, was der
> Compiler normalerweise in welcher Reihenfolge alles auf den Stack haut.

Das ist auch nicht fix definiert und ist das Ergebnis eines komplexen 
Algorithmus.  Das lässt sich praktisch nicht vorher sehen.

Leopold N. schrieb:
> Daraus werd ich aber net ganz schlau.
Letzendlich kann man das im Disassembley sehen. Das bringt dir aber nix, 
weil sich das mit jeder Code Änderung, oder Änderung der Compiler 
Optionen oder Version, ändern kann und du nicht jedes Mal das RTOS 
anpassen kannst.

Leopold N. schrieb:
> Gibt es irgendwelche anderen Wege an den PC und SP ranzukommen?

Inline Assembler. Aber in C bringt dir das nichts, weil C genau dafür da 
ist, dass man das nicht braucht bzw. kann. Der Compiler wird dir 
dazwischen pfuschen. Mach's in Assembler oder mache es so wie bei 
protothreads.

von Einer K. (Gast)


Lesenswert?

Stefanus F. schrieb:
> Ich glaube nicht, dass man den Kontext-Switch in C implementieren
> kann.
Natürlich geht das!
Welche sorgen plagen dich?
OK, in C++ geht das.
Aber warum das in C nicht gehen soll, weiß ich nicht.

Leopold N. schrieb:
> Wenn ich nun einen Task aufrufe, stelle ich seinen Kontext aus seinem
> Stack wieder her, und setze den PC. Problem ist bloß: Woher kriege ich
> seinen PC?

Musst du nicht händisch wiederherstellen.
Passiert doch beim reti automatisch.

von Niklas Gürtler (Gast)


Lesenswert?

Leopold N. schrieb:
> . Man müsste ihn also mit einem Offset vom aktuellen SP auslesen können.

Das ist eigentlich gar nicht nötig. Bei Eintritt der ISR sicherst du 
alle Register ("push") und das SREG, und tauschst dann den SP mit dem 
des nächsten Tasks aus. Dann stellst du die Register wieder her ("pop") 
und machst eine normale ISR Rückkehr (reti). So brauchst du den PC gar 
nicht explizit auslesen oder auf den Stack direkt zugreifen.

von Stefan F. (Gast)


Lesenswert?

Leopold N. schrieb:
>> Hat der (welcher?) ATmega genug RAM für dein Vorhaben?
> Kann wählen zwischen Atmega32 (2kB RAM) und Atmega644 (4kB RAM)

Oha!

Dann ermittle mal, wie viel eins deiner realen Projekte jetzt gerade an 
Stack benötigt (Stackpointer auslesen geht ja jetzt). Multipliziere das 
mit der Anzahl deiner Threads, addiere den sonstigen Speicherbedarf 
(Heap, globale Variablen und statische Variablen) dazu und dann nochmal 
über den dauemn gepeilt 60 Bytes pro Thread für deren Verwaltung.

Wenn das nicht in die 2kB bzw 4kB rein passt, hat es keinen Sinn.

> Was mich stört, ist, dass ich bis jetzt nirgendwo gefunden habe, was der
> Compiler normalerweise in welcher Reihenfolge alles auf den Stack haut.

Das musst du auch gar nicht wissen. Der Task-Switcher muss einfach alle 
CPU Register* sichern und für jeden Task (beim seinem Start) einen 
eigenen Stack reservieren.

*) Also R0 bis R31, Akkumulator, Stack-Pointer, Status Register (habe 
ich ein vergessen?)

von Oliver S. (oliverso)


Lesenswert?

Niklas Gürtler schrieb:
> Leopold N. schrieb:
>> Was mich stört, ist, dass ich bis jetzt nirgendwo gefunden habe, was der
>> Compiler normalerweise in welcher Reihenfolge alles auf den Stack haut.
>
> Das ist auch nicht fix definiert und ist das Ergebnis eines komplexen
> Algorithmus.  Das lässt sich praktisch nicht vorher sehen.

Macht man die ISR naked, dann kann man das schon genau vorhersehen. Dann 
steht tatsächlich die Rücksprungadresse oben auf dem Stack. Eine 
Taskwechsel kann man dann z.B. durch umbiegen auf die neue Task 
realisieren.

Die jetzt über die über den aus SP gebildeten Pointer auszulesen, sollte 
allerdings für jemanden, der ein RTOS in C schreiben will, kein Problem 
sein. Wenn doch, löse es. Du wirst die Kenntnisse brauchen.

Ich hab sowas auch mal aus Spaß gebastelt, die Kernfunktionen dazu 
allerdings in Assembler. Funktioniert schon, auch wenn das auf einem AVR 
eher von theoretischem Nutzen ist.

Oliver

: Bearbeitet durch User
von Leopold N. (leo_n)


Lesenswert?

Stefanus F. schrieb:
> Also R0 bis R31, Akkumulator, Stack-Pointer, Status Register (habe
> ich ein vergessen?)

Akkumulator? Davon habe ich bisher bei den Atmegas noch nichts gehört.
Erleuchte mich bitte.



Also schreibe ich die ISR am besten naked.

Über den Rest mache ich mir heute Nachmittag Gedanked, muss jetzt in die 
Uni :)

Grüße

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Macht man die ISR naked, dann kann man das schon genau vorhersehen.


Leopold N. schrieb:
> Also schreibe ich die ISR am besten naked.

Die GCC-Doku sagt dazu:

naked
Use this attribute on the ARM, AVR, MCORE, RX and SPU ports to indicate 
that the specified function does not need prologue/epilogue sequences 
generated by the compiler. It is up to the programmer to provide these 
sequences. The only statements that can be safely included in naked 
functions are asm statements that do not have operands. All other 
statements, including declarations of local variables, if statements, 
and so forth, should be avoided. Naked functions should be used to 
implement the body of an assembly function, while allowing the compiler 
to construct the requisite function declaration for the assembler.


Also doch wieder Assembler. Wie man die nötigen push/pop in einer reinen 
C-Funktion (auch wenn sie naked ist) umsetzen soll weiß ich auch nicht. 
Ohne push geht es nicht, weil man die Register nicht z.B. auf globale 
Variablen sichern kann, weil man dafür die Zeiger-Register überschreiben 
müsste bevor man sie gesichert hat.

von Karl B. (gustav)


Lesenswert?

Leopold N. schrieb:
> Akkumulator

vulgo R16


.def inttmp = r12  ;temporaeres Register fuer Interrupt-;Routinen
.def intreg = r15  ;Zwischenspeicher fuer SREG bei ISRs
.def temp = r16    ;diverse Universalregister

es sind aber nicht alle arithmetische Operationen "unterhalb" R16 
erlaubt.
Das ist ein ganz böser Fallstrick.

ciao
gustav

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Leopold N. schrieb:
> Akkumulator? Davon habe ich bisher bei den Atmegas noch nichts gehört.
> Erleuchte mich bitte.

Dann hat er wohl keinen. Ich habe die AVR schon sehr lange nicht mehr 
low level (Assembler) programmiert. Irgendwann vergisst man Details, 
wenn man nicht trainiert.

von Karl B. (gustav)


Lesenswert?

in  intreg, SREG  ;CPU-Flags sichern
genauso bei den "memory mapped" die entsprechenden Befehle nehmen.
nicht ldi

ciao
gustav

von Einer K. (Gast)


Lesenswert?

Niklas G. schrieb:
> Also doch wieder Assembler. Wie man die nötigen push/pop in einer reinen
> C-Funktion (auch wenn sie naked ist) umsetzen soll weiß ich auch nicht.
> Ohne push geht es nicht, weil man die Register nicht z.B. auf globale
> Variablen sichern kann, weil man dafür die Zeiger-Register überschreiben
> müsste bevor man sie gesichert hat.

Naked halte ich nicht unbedingt für Sinnvoll.
Denn die ISR wird recht komplex.
Sie muss schließlich den Kern des Schedulers beinhalten.
Man wird da drin Funktionen aufrufen

Ruft man in einer ISR Funktionen auf, wird der Compiler gezwungen alle 
Register, welche er selber nutzt zu sichern, und wieder herzustellen.

Und, macht man sie wirklich naked, dann begrenzt sich der Asm Code auf 
die Push und Pop, plus evtl das reti

von Karl B. (gustav)


Angehängte Dateien:

Lesenswert?

Hi,
noch ein Bildchen vom Debugger.
Da sind (Absolut-)Adressen der Register angegeben. (Beispiel hier 
AtTiny4313.)
Müsste man auch "direkt" ansprechen können.
Nur eben noch eine "Umlade"-Routine.
Das Problem dabei: Atomic. In der Befehlsverarbeitungszeit darf nichts 
anderes dazwischenfunken.
(Beispiel für USART-Statusbits auslesen.)

ciao
gustav

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Mal als Anregung wie man es in ASM machen könnte... Hab aber auch länger 
nicht mehr damit zu tun gehabt, Fehler daher inklusive. Man definiert 
sich eine Task-struct, wo man z.B. Prioritäten ablegen kann, aber 
insbesondere auch den Stack Pointer. Dann kann man sich eine C-Funktion 
bauen, welche den Scheduler beinhaltet, d.h. den nächsten auszuführenden 
Task ermittelt. In einer globalen Variable speichert man ab, welcher 
Task gerade läuft:
1
struct Task {
2
  char* stack;
3
};
4
5
struct Task* currentTask;
6
7
struct Task* scheduler () {
8
  // Finde nächsten auszuführenden Task...
9
  return ... ;
10
}

Wenn man präemptiv kontextwechseln möchte, baut man sich eine Timer-ISR 
welche die Umschaltung erledigt:
1
TimerInterrupt:
2
push r16
3
in r16, SREG
4
push r16
5
push r0
6
push r1
7
push r2
8
push r3
9
push r4
10
push r5
11
push r6
12
push r7
13
push r8
14
push r9
15
push r10
16
push r11
17
push r12
18
push r13
19
push r14
20
push r15
21
push r17
22
push r18
23
push r19
24
push r20
25
push r21
26
push r22
27
push r23
28
push r24
29
push r25
30
push r26
31
push r27
32
push r28
33
push r29
34
push r30
35
push r31
36
37
38
ldi ZL, low(currentTask)
39
ldi ZH, high(currentTask)
40
41
in r16, SPL
42
in r17, SPH
43
44
str r16, Z+    ; Stack-Pointer sichern. Verlässt sich darauf, dass "stack" am Anfang von struct Task steht
45
str r17, Z+
46
47
rcall scheduler  ; Scheduler in C aufrufen
48
49
mov ZL, r24    ; Zurückgegebenen Pointer in Z speichern
50
mov ZH, r25
51
52
ldr r16, Z+    ; Stack Pointer des neuen Tasks laden
53
ldr r17, Z
54
55
out SPL, r16  ; Stack Pointer wiederherstellen
56
out SPH, r17
57
58
pop r31
59
pop r30
60
pop r29
61
pop r28
62
pop r27
63
pop r26
64
pop r25
65
pop r24
66
pop r23
67
pop r22
68
pop r21
69
pop r20
70
pop r19
71
pop r18
72
pop r17
73
pop r15
74
pop r14
75
pop r13
76
pop r12
77
pop r11
78
pop r10
79
pop r9
80
pop r8
81
pop r7
82
pop r6
83
pop r5
84
pop r4
85
pop r3
86
pop r2
87
pop r1
88
pop r0
89
pop r16
90
out SREG, r16
91
pop r16
92
reti

Der Assembler-Code setzt voraus, dass das Layout von "struct Task" eine 
bestimmte Form hat, und dass die scheduler-Funktion den Pointer in 
r24/r25 zurückgibt. Das ist im ABI definiert und daher wesentlich 
stabiler als die Register-Zuordnung und Stack-Layout innerhalb von 
Funktionen.

Man sieht dass man eine Menge Zeit braucht zum Register 
sichern/wiederherstellen. Auf ARM ist das schöner :-) :
1
push { r0-r12, LR }
Braucht typischerweise 15 Takte und 4 Bytes Programmspeicher für 14*4=56 
Bytes an Registerinhalten, während der AVR 64 Takte und 64 Bytes 
Programmspeicher für 32 Bytes an Registerinhalten braucht ... ;-) 
(Status-Register jeweils außen vor genommen). Bei Cortex-M werden einige 
Register außerdem automatisch gesichert. Daher macht so etwas da mehr 
Spaß...

von Oliver S. (oliverso)


Lesenswert?

Arduino Fanboy D. schrieb:
> Und, macht man sie wirklich naked, dann begrenzt sich der Asm Code auf
> die Push und Pop, plus evtl das reti

Nein. Dann muss man sich halt selber um alles kümmern, was notwendig 
ist. Was bei einer solch systemnahen Programmierung nicht anders zu 
erwarten war.

Da man zum Taskwechsel eh den ganzen Registersatz wegspeichern, und vor 
dem Reti die Register der neuen Task schreiben muß, sind dazwischen 
Funktionsaufrufe kein Problem.

Ich hab’s damals in Assembler gemacht, aber es geht auch in C. Das wird 
zwar letztendlich „Assembler-C“, aber wer das unbedingt will, soll’s 
halt so machen.

Oliver

von Leopold N. (leo_n)


Lesenswert?

Ich hatte jetzt vor, das ganze aufzuteilen in 3 Funktionen:

save_context() - pusht die 32 GP-Register und SREG auf den Stack, 
speichert den SP in einem Array

restore_context() - popt die 32 GP-Register und SREG vom Stack, lädt den 
aktuellen SP aus dem Array

switch_task() - bestimmt den nächsten auszuführenden Task


Aufgerufen wird folgendermaßen:

save_context();
switch_task();
restore_context();

Problem: Nach save_context startet der Controller wohl neu, vermutlich 
weil der Stackpointer ins Leere zeigt.
Was kann ich da machen?

Grüße

von Oliver S. (oliverso)


Lesenswert?

Den Stackpointer nicht ins Leere zeigen lassen?

Oliver

: Bearbeitet durch User
von Leopold N. (leo_n)


Lesenswert?

Oliver S. schrieb:
> Den Stackpointer nicht ins Leere zeigen lassen?
>
> Oliver

Mal im Ernst, ich frage hier nicht, um mir derart kindische Antworten 
geben zu lassen.
Wenn du nicht helfen möchtest, dann schreib doch bitte einfach gar 
nichts.

von 2⁵ (Gast)


Lesenswert?

Leopold N. schrieb:
> Was kann ich da machen?

Deinen ganze Code zeigen? Startest du save_context(); aus der ISR 
heraus? In C? Hast du dir mal den Assembler Code angeschaut?

von Stefan F. (Gast)


Lesenswert?

Du brauchst wie oben bereits geschrieben für jeden Thread einen eigenen 
Speicherbereich im RAM, wo die die ganzen Registers sicherst. und du 
brauchst auch für jeden Thread einen eigenen Speicherbereich als Stack.

von Leopold N. (leo_n)


Lesenswert?

Stefanus F. schrieb:
> Du brauchst wie oben bereits geschrieben für jeden Thread einen eigenen
> Speicherbereich im RAM, wo die die ganzen Registers sicherst. und du
> brauchst auch für jeden Thread einen eigenen Speicherbereich als Stack.

Also brauche ich pro Task zwei Arrays, einmal für den Kontext und einmal 
für den Stack, richtig?

Wie bekomme ich nach save_context() den PC wieder auf die 
Rücksprungadresse?

2⁵ schrieb:
> Startest du save_context(); aus der ISR

Ja, allerdings momentan noch nicht, da ich erstmal die grundsätzlichen 
Funktionen verstehen und programmieren möchte.

von foobar (Gast)


Lesenswert?

> Wie bekomme ich nach save_context() den PC wieder auf die
> Rücksprungadresse?

Durch den ret auf dem neuen Stack.  Schau dir das Beispiel von Niklas 
an.

von Leopold N. (leo_n)


Lesenswert?

foobar schrieb:
>> Wie bekomme ich nach save_context() den PC wieder auf die
>> Rücksprungadresse?
>
> Durch den ret auf dem neuen Stack.  Schau dir das Beispiel von Niklas
> an.

Aber auf dem neuen Stack steht doch noch gar keine Rücksprungadresse.
Der neue Stack ist doch einfach nur ein leeres Array.

von Stefan F. (Gast)


Lesenswert?

Du verwechselst das Umschalten zwischen den Tasks mit dem Anlegen neuer 
Tasks.

Neue Tasks startet man einfach, indem man Speicherplatz reserviert, den 
Stack-Pointer darauf stellt und dann den Task anspringt.

Wenn du gut englisch kannst, dann lies mal diesen Aufsatz: 
https://stratifylabs.co/embedded%20design%20tips/2013/10/09/Tips-Context-Switching-on-the-Cortex-M3/

von Leopold N. (leo_n)


Lesenswert?

Stefanus F. schrieb:
> Du verwechselst das Umschalten zwischen den Tasks mit dem Anlegen neuer
> Tasks.
>
> Neue Tasks startet man einfach, indem man Speicherplatz reserviert, den
> Stack-Pointer darauf stellt und dann den Task anspringt.
>
> Wenn du gut englisch kannst, dann lies mal diesen Aufsatz:
> 
https://stratifylabs.co/embedded%20design%20tips/2013/10/09/Tips-Context-Switching-on-the-Cortex-M3/

Danke werd ich machen.

Nun habe ich ein kleines anderes Problem:

for(uint8_t i = 0; i < TASKS_MAXIMUM; i++)
{
    tasks_stackpointers[i] = *task_stack[i].memoryblock[TASK_STACK_SIZE 
- 1];
}

Ich will in tasks_stackpointers meinen jeweiligen Stackpointer 
initialisieren.
task_stack ist der Stack des jeweiligen Tasks (einfach nur ein Array). 
Da der SP ja oben beginnt und nach unten zählt, muss ich ja bei der 
obersten Zelle des Arrays anfangen.

task_stack ist definiert als:

typedef struct stack
{
    uint8_t memoryblock[TASK_STACK_SIZE];
}stack;

stack task_stack[TASKS_MAXIMUM];


Nun schmeißt mir der Compiler bei der for-Schleife den Fehler hin.

invalid type argument of unary '*' (have 'int')

task_stackpointer ist definiert als:

uint16_t *tasks_stackpointers[TASKS_MAXIMUM];

von Leopold N. (leo_n)


Lesenswert?

Hm war ja klar....kaum postet man sein Problem fällt einem auch schon 
der Fehler auf....

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Leopold N. schrieb:
> Aber auf dem neuen Stack steht doch noch gar keine Rücksprungadresse.

Doch, als man das letzte Mal aus dem neuen Task heraus-gewechselt hat, 
hat man die Rücksprungadresse darauf gesichert. Den PC musst du nie 
lesen oder schreiben - der wird ja automatisch bei Interrupt-Eintritt 
auf den Stack gesichert, und automatisch bei Interrupt-Rückkehr (reti) 
gesetzt.

Leopold N. schrieb:
> tasks_stackpointers[i] = *task_stack[i].memoryblock[TASK_STACK_SIZE
> - 1];

muss
1
tasks_stackpointers[i] = &task_stack[i].memoryblock[TASK_STACK_SIZE - 1];
sein oder schöner
1
tasks_stackpointers[i] = task_stack[i].memoryblock+TASK_STACK_SIZE-1;

Vielleicht solltest du mal mehr Grundlagen zu C und Assembler lernen, 
bevor du ein RTOS in einem von beiden implementierst...

von Leopold N. (leo_n)


Lesenswert?

Niklas G. schrieb:
> Leopold N. schrieb:
>> tasks_stackpointers[i] = *task_stack[i].memoryblock[TASK_STACK_SIZE
>> - 1];
>
> musstasks_stackpointers[i] = &task_stack[i].memoryblock[TASK_STACK_SIZE
> - 1];sein oder schönertasks_stackpointers[i] =
> task_stack[i].memoryblock+TASK_STACK_SIZE-1;
> Vielleicht solltest du mal mehr Grundlagen zu C und Assembler lernen,
> bevor du ein RTOS in einem von beiden implementierst.

Darauf bin ich ja auch gekommen, stand irgendwie auf dem Schlauch....

Niklas G. schrieb:
> Doch, als man das letzte Mal aus dem neuen Task heraus-gewechselt hat,
> hat man die Rücksprungadresse darauf gesichert. Den PC musst du nie
> lesen oder schreiben - der wird ja automatisch bei Interrupt-Eintritt
> auf den Stack gesichert, und automatisch bei Interrupt-Rückkehr (reti)
> gesetzt.

cli();
SP = &task_context[actual_task].memoryblock[TASK_CONTEXT_SIZE - 1];
asm volatile(32 GPR und SREG sichern..);
SP = task_context[new_task].memoryblock[0];
asm volatile(32 GPR und SREG wiederherstellen);
actual_task = new_task;
SP = tasks_stackpointers[actual_task];
sei();
tasks_functions[actual_task]();


Wenn ich diesen Code ausführe, hängt sich der Controller auf.

von Stefan F. (Gast)


Lesenswert?

Leopold N. schrieb:
> Hm war ja klar....kaum postet man sein Problem fällt einem auch schon
> der Fehler auf....

Das ist gut. Es ist in der tat oft so, dass man seinen Fehler in dem 
Moment erkennt, wo man sich bemüht, das Problem für andere 
verständlich darzulegen.

von Stefan F. (Gast)


Lesenswert?

Leopold N. schrieb:
> Wenn ich diesen Code ausführe, hängt sich der Controller auf.

Benutze einen Debugger, um zu untersuche, was genau passiert. Entweder 
passiert nicht das was du programmieren wolltest, oder dein Konzept war 
schon fehlerhaft. Das siehst du dann aber, wenn du das Programm Befehl 
für Befehl im Einzelschritt-Verfahren ausführst.

> tasks_functions[actual_task]();

Hier werden die Register des Task Switchers auf den falschen Stack (den 
des Tasks) gesichert und dann die Task-Funktion angesprungen.

von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

Im ersten Bild mein Code, im zweiten das vom Compiler generiert .lss 
file an der Stelle.

Wie kann ich verhindern, dass er extra noch die vier Register R28-R31 
sichert?
Ich habe gelesen, dass man dafür "naked" verwenden kann, wie 
implementiere ich das denn genau (Syntax...)?

von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

Hier nochmal der eigene Code in größer....

von Stefan F. (Gast)


Lesenswert?

Leopold N. schrieb:
> Wie kann ich verhindern, dass er extra noch die vier Register R28-R31
> sichert?

Wurde das nicht bereits erklärt? Ja, mit dem Schlüsselwort naked, aber 
dann kannst du in der Funktion kein C mehr verwenden.

von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

Ich habe jetzt kein C mehr verwendet und die Funktion als naked 
deklariert, dennoch funkt der Compiler dazwischen.

von Einer K. (Gast)


Lesenswert?

Stefanus F. schrieb:
> aber
> dann kannst du in der Funktion kein C mehr verwenden.
Das ist nicht richtig!

von Leopold N. (leo_n)


Lesenswert?

Und witzigerweise pusht er R30 und R31 nur, er popt sie nicht wieder.

von Stefan F. (Gast)


Lesenswert?

Du kannst auch *.s Dateien mit reinem Assembler Quelltext schreiben und 
das dann mit deinem C Programm linken.

von Karl B. (gustav)


Lesenswert?

Leopold N. schrieb:
> Und witzigerweise pusht er R30 und R31 nur, er popt sie nicht wieder.

Hi,
die sind doch Teil des "16-Bit"-Registerpaares, reserviert für Pointer.
Auseinandergerupft geht das nicht.

.def  ZL  =r30
.def  ZH  =r31

ciao
gustav

: Bearbeitet durch User
von Leopold N. (leo_n)


Lesenswert?

Karl B. schrieb:
> Leopold N. schrieb:
>> Und witzigerweise pusht er R30 und R31 nur, er popt sie nicht wieder.
>
> Hi,
> die sind doch Teil des "16-Bit"-Registerpaares, reserviert für Pointer.
> Auseinandergerupft geht das nicht.
>
> .def  ZL  =r30
> .def  ZH  =r31
>
> ciao
> gustav

Nun, das ist mir auch klar...deine Antwort beantwortet aber nicht meine 
Frage, warum der Compiler die beiden Register nur pusht und nicht wieder 
popt..., bzw. warum der Compiler diese Register überhaupt pusht.

von Leopold N. (leo_n)


Lesenswert?

Ich habe diese Funktion ja schließlich als naked deklariert

von S. R. (svenska)


Lesenswert?

Leopold N. schrieb:
> Ich möchte ein präemptives RTOS programmieren (ich möchte es selbst
> machen, also bitte nicht ankommen und sagen, dass es so etwas schon
> gibt, und der und der das schon viel besser gemacht hat).

Was du vorhast, nämlich den CPU-Kontext speichern und wiederherstellen, 
kannst du in C nicht machen. Weil man sowas aber gelegentlich braucht, 
gibt es zwei C-Standardfunktionen, die genau das tun: setjmp() speichert 
den aktuellen Kontext und longjmp() springt in einen gespeicherten 
Kontext.

Diese Funktionen sind immer systemabhängig und in Assembler geschrieben. 
Du brauchst dir also nur anschauen, wie so ein Kontext aufgebaut ist und 
dann kannst du auch SP in der Datenstruktur ändern (= einen neuen Stack 
definieren), bevor du den Kontext anspringst.

Ein kleines, auf diese Funktionen aufsetzendes RTOS für AVR haben wir an 
der Uni mal ziemlich detailliert abgehandelt. Da war so gut wie kein 
Assembler drin, weil der Kontextwechsel von der avr-libc erledigt wird.

von Leopold N. (leo_n)


Lesenswert?

Ich habe mir die setjmp.h mal angesehen. Gibt es irgendwo auch noch die 
Funktionen dazu? Die sind in der .h Datei nur deklariert, aber der 
eigentliche Code steht woanders. Wo ist der denn?

von Stefan F. (Gast)


Lesenswert?

Oh mann...

Ich würde Dir empfehlen, die Sourcen der avr-libc herunter zu laden und 
dort zu suchen.

http://download.savannah.gnu.org/releases/avr-libc/avr-libc-2.0.0.tar.bz2

Es könnte sein, dass du dann die Datei libc/stdlib/setjmp.S findest, 
also den Quelltext dieser Funktion in (Überraschung!) Assembler.

Du musst erst mal gehen lernen, bevor du fliegst. Dieses Projekt 
erinnert mich stark an den Typen neulich, der eine ganze CPU aus 
diskreten Bauteilen zusammenlöten wollte ohne einen blassen Schimmer zu 
haben, was eine CPU eigentlich macht, geschweige denn wie.

von foobar (Gast)


Lesenswert?

Vorsicht: setjmp/longjmp funktionieren innerhalb der C Umgebung bei nem 
kooperativen Wechsel. Man kann damit z.B. Coroutinen implementieren. Für 
einen preämptiven Taskwechsel reichen die allerdings nicht - es werden 
zu wenig Register berücksichtigt.

von S. R. (svenska)


Lesenswert?

Leopold N. schrieb:
> Ich habe mir die setjmp.h mal angesehen. Gibt es irgendwo auch
> noch die Funktionen dazu?

Den Code für setjmp() findest du an der gleichen Stelle wie den Code für 
strcmp() und printf().

foobar schrieb:
> Für einen preämptiven Taskwechsel reichen die allerdings nicht - es
> werden zu wenig Register berücksichtigt.

Welche müssten denn noch berücksichtigt werden?

von foobar (Gast)


Lesenswert?

>> Für einen preämptiven Taskwechsel reichen die allerdings nicht - es
>> werden zu wenig Register berücksichtigt.
>
> Welche müssten denn noch berücksichtigt werden?

Setjmp/longjmp gehen von den C-Aufrufkonventionen aus, d.h., es werden 
nur die Register gesichert, die auch eine C-Funktion nicht ändern darf. 
Arbeitsregister, wie z.B. auch das Statusregister, werden nicht 
gesichert. Bei einem preämptiven Wechsel, der zu jedem beliebigen 
Zeitpunkt stattfinden kann, müßen auch die gerettet werden.

von S. R. (svenska)


Lesenswert?

Okay, danke für den Hinweis und die Erklärung.

von Joachim B. (jar)


Lesenswert?

Leopold N. schrieb:
> Kann wählen zwischen Atmega32 (2kB RAM) und Atmega644 (4kB RAM)
> Hab bis jetzt aber noch keine Probleme gehabt.

und ich mit dem ATmega1284p mit 16KB SRAM keine keine Probleme mehr, 
weder die 128 KB flash noch die 16 KB SRAM wurden mir bis jetzt je zu 
eng und dann kam der ESP32

Stefanus F. schrieb:
> Ich habe die AVR schon sehr lange nicht mehr
> low level (Assembler) programmiert

und ich zuletzt den PC1500 mit LH5803 danach brauchte ich das nie wieder 
in Assembler, war aber auch tricky ich brauchte den PC für relokatiblen 
Code im EEPROM und an den PC (Programmcounter) kam man nur über Umwege 
ran.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Leopold N. schrieb:
> Nun, das ist mir auch klar...deine Antwort beantwortet aber nicht meine
> Frage, warum der Compiler die beiden Register nur pusht und nicht wieder
> popt..., bzw. warum der Compiler diese Register überhaupt pusht.

Welche Compilerversion benutzt du?

Oliver

von Leopold N. (leo_n)


Lesenswert?

Oliver S. schrieb:
> Leopold N. schrieb:
>> Nun, das ist mir auch klar...deine Antwort beantwortet aber nicht meine
>> Frage, warum der Compiler die beiden Register nur pusht und nicht wieder
>> popt..., bzw. warum der Compiler diese Register überhaupt pusht.
>
> Welche Compilerversion benutzt du?
>
> Oliver

Standard Einstellung, daran habe ich noch nichts gemacht.

von Stefan F. (Gast)


Lesenswert?

Leopold N. schrieb:
> Standard Einstellung

Ach so und die wäre? (gebe mal avr-gcc --version ein).

Meine ist zum Beispiel 5.4.0.

von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

Das ist der momentane Code.
Das Speichern der 32 GPR und des SREGS funktioniert.
Probleme gibts erst, wenn es darum geht, einen Thread aufzurufen.

Könnt ihr mal drüberschauen und mich auf Fehler stoßen?

Grüße

von Stefan F. (Gast)


Lesenswert?

Die Compiler-Version bitte!

> Könnt ihr mal drüberschauen und mich auf Fehler stoßen?

Weißt du, wenn du jemanden suchst, der das Rad für Dich neu erfindet, 
dann sage das doch gleich. Aber biete auch eine Belohnung.

Wir helfen gerne bei Konkreten fragen, aber nicht so! Schon gar nicht, 
wenn du weiterhin Rückfragen ignorierst.

von Leopold N. (leo_n)


Lesenswert?

Stefanus F. schrieb:
> Leopold N. schrieb:
>> Standard Einstellung
>
> Ach so und die wäre? (gebe mal avr-gcc --version ein).
>
> Meine ist zum Beispiel 5.4.0.

Sry, hatte die Antwort nicht gesehen, weil ich zeitgleich meinen Beitrag 
verfasst habe...
Kein Grund, gleich unfreundlich zu werden.
Ich habe den Befehl in die Kommandozeile in Atmel Studio 7 eingegeben, 
es kam ein Fehler "avr" ungültiger Befehl

von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

Hab das hier gefunden...weiß nicht ob das weiterhilft

von Stefan F. (Gast)


Lesenswert?

Der Befehl heißt avr-gcc, nicht avr. Wenn er nicht im PATH ist, musst du 
halt selber vorher hin gehen.

Leopold N. schrieb:
> Hab das hier gefunden...weiß nicht ob das weiterhilft
nein, tut es nicht.

> Sry, hatte die Antwort nicht gesehen, weil ich zeitgleich
> meinen Beitrag verfasst habe...

Die Frage war von 12:10

von Leopold N. (leo_n)


Lesenswert?

Stefanus F. schrieb:
> Wir helfen gerne bei Konkreten fragen, aber nicht so! Schon gar nicht,
> wenn du weiterhin Rückfragen ignorierst.

Hm ja, hast Recht, hab keine exakte Frage formuliert.
Ich habe das Programm durch den Simulator gejagt.
Das erste create_thread() hat funktioniert, das zweite auch.
Dann kam switch_thread(), wo er ja den Kontext von Task 1 laden soll und 
dann Task 1 ausführen (da ich ja an das obere Ende des Stacks von Task 1 
den Funktionspointer für led() abgelegt habe).
Das macht er aber nicht, sondern geht wieder zurück zu init_os()

Frage: warum macht er das, bzw. wo ist der Denkfehler

Grüße

von Niklas Gürtler (Gast)


Lesenswert?

Leopold N. schrieb:
> Könnt ihr mal drüberschauen und mich auf Fehler stoßen?

Warum machst du denn direkt nach dem Anlegen des Threads so einen halben 
Kontext Wechsel? Durch das Modifzieren des Stacks funktioniert das 
Zurückkehren aus der Funktion nicht mehr und der Rest des Programms wird 
nicht ausgeführt.

Mitten in einer normalen C Funktion auf dem Stack herum zu pfuschen und 
den Kontext Wechsel zu machen wird nicht klappen. Der Compiler kann 
irgendwelche Stack Zugriffe davor und danach einfügen...

von Leopold N. (leo_n)


Lesenswert?

Stefanus F. schrieb:
>> Sry, hatte die Antwort nicht gesehen, weil ich zeitgleich
>> meinen Beitrag verfasst habe...
>
> Die Frage war von 12:10

Auf den Beitrag von 12:10 hatte ich reagiert.
Nur wusste ich nicht, wie ich die Compilerversion ermitteln kann, 
deshalb habe ich dir erzählt, dass ich die Standardeinstellung verwende.
Jetzt häng dich doch bitte nicht an solchen Kleinigkeiten auf.

Grüße

von Stefan F. (Gast)


Lesenswert?

Hatten nicht bereits mehrere Leute geschrieben, dass du den ganzen 
Task-Wechsel in Assembler schreiben musst?

von Leopold N. (leo_n)


Lesenswert?

Niklas Gürtler schrieb:
> Warum machst du denn direkt nach dem Anlegen des Threads so einen halben
> Kontext Wechsel? Durch das Modifzieren des Stacks funktioniert das
> Zurückkehren aus der Funktion nicht mehr und der Rest des Programms wird
> nicht ausgeführt.

In create_thread() lege ich eigentlich nur einen Stack in dem Array 
thread_stack[] an, damit ich bei switch_thread() auch etwas zum popen 
habe, sonst popt der ja irgendwas.

von Leopold N. (leo_n)


Lesenswert?

Stefanus F. schrieb:
> Hatten nicht bereits mehrere Leute geschrieben, dass du den ganzen
> Task-Wechsel in Assembler schreiben musst?

Siehe switch_thread()....ist ganz in Assembler

von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

Hier nochmal die Assembler-Datei, die der Compiler generiert

von Niklas Gürtler (Gast)


Lesenswert?

Leopold N. schrieb:
> In create_thread() lege ich eigentlich nur einen Stack in dem Array
> thread_stack[] an, damit ich bei switch_thread() auch etwas zum popen
> habe,

Tatsächlich. Und das machst du dann in Assembler? Du schreibst irgendwas 
das zufällig gerade in dem Registern steht... Array Zugriffe gehen auch 
in C. Schreib lieber 0en.

Leopold N. schrieb:
> Siehe switch_thread()....ist ganz in Assembler

Wird aber aus C aufgerufen. Mach die ISR komplett in Assembler.

von Stefan F. (Gast)


Lesenswert?

Steppe das einzeln Zeile für Zeile durch und beobachte dabei den PC und 
den SP. Dann wirst du schon sehen, ab wo es falsch läuft.

Welche Compiler-Version verwendest du?

von Leopold N. (leo_n)


Lesenswert?

Stefanus F. schrieb:
> Welche Compiler-Version verwendest du?

Willst du mich auf den Arm nehmen?
Ich sagte bereits, dass ich nicht weiß, wie ich die Version 
herausbekomme...

von Leopold N. (leo_n)


Lesenswert?

Stefanus F. schrieb:
> Steppe das einzeln Zeile für Zeile durch und beobachte dabei den PC und
> den SP. Dann wirst du schon sehen, ab wo es falsch läuft.

Schön wärs, wenn das so einfach ginge.
Sobald ich aber einen Breakpoint an einem ASM Befehl setze, zeigt mir 
AS7 an, dass der nie angesprungen werden wird....
Das heißt, dass ich nur C-Code beobachten kann.

von Leopold N. (leo_n)


Lesenswert?

Niklas Gürtler schrieb:
> Wird aber aus C aufgerufen. Mach die ISR komplett in Assembler.

Wieso eigentlich?
Ich dachte, es geht darum, dass beim Kontext Switch der Compiler nicht 
dazwischenfunkt.
Ich hatte mir das so vorgestellt, dass ich in regelmäßigen Zeitabständen 
(--> Timer) einen Kontextswitch durchführe, damit die Tasks je nach 
Priorität drankommen.

Ein Kontextswitch besteht (meines Wissens nach) aus:

Alten Kontext sichern (Kontext = 32 GPR, SREG, SP, PC)
Neuen Kontext laden

Fragen:

1) Wie rufe ich die erste Funktion auf? Ich habe das hier versucht mit 
"ret" in switch_thread() --> scheint aber nicht zu klappen

2) Wie kann ich den PC setzen (Zugriff wie auf ein Register meines 
Wissens nach nicht möglich)

Grüße

von Niklas Gürtler (Gast)


Lesenswert?

Leopold N. schrieb:
> Ich sagte bereits, dass ich nicht weiß, wie ich die Version
> herausbekomme..

avr-gcc.exe -v

Leopold N. schrieb:
> Sobald ich aber einen Breakpoint an einem ASM Befehl setze, zeigt mir
> AS7 an, dass der nie angesprungen werden wird....

Ja, weil der keinen Inline Assembler Code auflösen kann. Mach eine reine 
Assembler (.S) Datei, da geht das.

Wenn du es dir unbedingt schwer machen möchtest und C und Assembler 
mischen musst und dabei gegen den Compiler kämpfen musst... machs doch 
erst mal einfach komplett in Assembler, und wenn das klappt übersetze 
Teile nach C. Dann weißt du immerhin schon wie das Kompilat aussehen 
muss.

von Niklas Gürtler (Gast)


Lesenswert?

Leopold N. schrieb:
> Ein Kontextswitch besteht (meines Wissens nach) aus:
>
> Alten Kontext sichern (Kontext = 32 GPR, SREG, SP, PC)
> Neuen Kontext laden

Ja, und wenn du da C im Spiel hast fügt der Compiler davor, dazwischen 
und danach irgendwelche Dinge ein.

Leopold N. schrieb:
> 2) Wie kann ich den PC setzen (Zugriff wie auf ein Register meines
> Wissens nach nicht möglich)

Über (i)jmp natürlich... aber das brauchst du gar nicht. Das passiert 
automatisch beim reti in der ISR.

von Leopold N. (leo_n)


Lesenswert?

Niklas Gürtler schrieb:
> Wenn du es dir unbedingt schwer machen möchtest und C und Assembler
> mischen musst und dabei gegen den Compiler kämpfen musst... machs doch
> erst mal einfach komplett in Assembler, und wenn das klappt übersetze
> Teile nach C. Dann weißt du immerhin schon wie das Kompilat aussehen
> muss.

Das heißt, ich muss jetzt Assembler lernen und zwar voll und ganz?
Ich weiß nicht mal, wie man eine ASM Datei in das Programm einbindet.

von Niklas Gürtler (Gast)


Lesenswert?

Leopold N. schrieb:
> Das heißt, ich muss jetzt Assembler lernen und zwar voll und ganz?

Musst du definitiv. Du kannst kein OS schreiben, welches 
notwendigerweise eine Ebene tiefer ansetzt, ohne Assembler zu können. 
Genau so wenig wie man einen Prozessor entwerfen kann ohne Logik-Gatter 
zu beherrschen... Der AVR Assembler ist nun auch wirklich nicht 
schwierig.

Leopold N. schrieb:
> Ich weiß nicht mal, wie man eine ASM Datei in das Programm einbindet.

Einfach dem Atmel Studio Projekt hinzufügen. Oder einfach erstmal ein 
reines Assembler Projekt anlegen...

von M. K. (sylaina)


Lesenswert?

Leopold N. schrieb:
> Das heißt, ich muss jetzt Assembler lernen und zwar voll und ganz?
> Ich weiß nicht mal, wie man eine ASM Datei in das Programm einbindet.

Das wäre aber schlecht, IMO sind das Grundlagen in der 
µC-Programmierung. Aber keine Angst, ASM bei den AVRs ist noch gut 
überschaubar.

von Einer K. (Gast)


Lesenswert?

Ich glaube auch nicht, dass man das unbedingt in Assembler schreiben 
muss.
OK, die Latte Push und Pop, ja...
Inline Assembler.
Aber den Rest des Kontextwechsels doch nicht.

Natürlich kann man das tun.
Aber man MUSS doch nicht.
Ob man an irgendwelchen Tasklisten in ASM oder in C entlang wackelt, ist 
doch den Listen egal und dem µC doch auch.

von M. K. (sylaina)


Lesenswert?

Naja, da wo er hin will, da kommt er mit C halt nicht hin. Das geht nur 
zu Fuß mit ASM.

von Einer K. (Gast)


Lesenswert?

M. K. schrieb:
> Das geht nur zu Fuß mit ASM.

Ich bin ja irgendwie versucht, das Gegenteil zu beweisen.
Darfs dann auch C++, statt C sein?
(und in/mit der Arduino IDE?)

von S. R. (svenska)


Lesenswert?

Leopold N. schrieb:
>> Welche Compiler-Version verwendest du?
> Ich sagte bereits, dass ich nicht weiß, wie ich die Version
> herausbekomme...

Ja, das haben wir gemerkt. Du versuchst gerade, ein Haus zu bauen, 
kannst aber den Hammer nicht so ganz zuverlässig bedienen - das sind 
Grundlagen. Kenne deine Werkzeuge.

Bisher hast du nur gesagt, dass "avr" kein Befehl ist. Das stimmt auch. 
Jetzt musst du den Befehl "avr-gcc -v" ausführen, und zwar genau da, wo 
deine Toolchain liegt. Wie es dir bereits erklärt wurde.

Wenn du etwas nicht verstehst, dann frage nach. In jedem Fall bekommst 
du hier Antworten (vielleicht nicht unbedingt die, die du haben willst, 
aber das ist ein anderes Thema). Pampig werden hilft jedenfalls nicht. 
Fragen schon.

Meine Frage wäre: Schreibst du das OS, um
- ein OS zu haben,
- den AVR besser zu verstehen,
- das Konzept "OS" besser zu verstehen?

So ganz neu sind RTOSe auf einem AVR nicht. Und so blöd das klingt - 
insbesondere, wenn du dich mit C auskennst - lerne das ABI vom AVR und 
lerne, wie man Assembler und C mischt (sowohl Inline Assembler als auch 
Assemblerquellen). Und wenn du ein paar LEDs blinken lässt, indem du 
ASM-Funktionen aus C aufrufst und umgekehrt oder indem du einen 
Interrupt-Handler ganz in Assembler baust. Wenn du das gelernt und 
verstanden hast, dann kehre zu deinem RTOS zurück. Denn dann wird das 
auch was.

M. K. schrieb:
> Naja, da wo er hin will, da kommt er mit C halt nicht hin.

Mit reinem C geht immerhin kooperatives Multitasking mit setjmp/longjmp, 
das wäre schonmal ein Anfang, wenn man von ASM lieber Abstand halten 
möchte. Damit kommt man auch schon recht weit (und echtzeitfähig geht 
damit auch).

von Leopold N. (leo_n)


Lesenswert?

S. R. schrieb:
> Meine Frage wäre: Schreibst du das OS, um
> - ein OS zu haben,
> - den AVR besser zu verstehen,
> - das Konzept "OS" besser zu verstehen?

Um ein OS zu haben (netter Nebeneffekt ist, dass ich den AVR dann besser 
verstehe)

S. R. schrieb:
> Mit reinem C geht immerhin kooperatives Multitasking mit setjmp/longjmp,
> das wäre schonmal ein Anfang

Habe ich mir schon geschrieben, reicht mir aber nicht :)

von Leopold N. (leo_n)


Lesenswert?

.global TIMER0_COMP_vect

TIMER0_COMP_vect:

   // Assembler Befehle



So wollte ich jetzt die ISR anfangen, die in einer .S Datei steht und 
per #include "..." eingebunden wird.

Jetzt zeigt mir AS7 den Fehler "expected identifier of '(' before '.' 
token"

Dabei habe ich die Syntax von
https://www.nongnu.org/avr-libc/examples/asmdemo/isrs.S
übernommen.

Wo ist mein Fehler?


Grüße

von Leopold N. (leo_n)


Lesenswert?

Leopold N. schrieb:
> Jetzt zeigt mir AS7 den Fehler "expected identifier of '(' before '.'
> token"

Der Fehler bezieht sich auf den Start der .S Datei, also auf:

.global TIMER0_COMP_vect

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Leopold N. schrieb:
> So wollte ich jetzt die ISR anfangen, die in einer .S Datei steht und
> per #include "..." eingebunden wird.

#include bewirkt, dass der Inhalt der Ziel-Datei 1:1 an dieser Stelle 
übernommen wird. Was denkt sich wohl der C-Compiler, wenn da plötzlich 
eine Menge Assembler-Code auftaucht?

Ich glaube, du musst dringend ein paar mehr Grundlagen lernen, bevor du 
dich mit etwas derart fortgeschrittenem auseinander setzt..

von Leopold N. (leo_n)


Lesenswert?

Niklas G. schrieb:
> #include bewirkt, dass der Inhalt der Ziel-Datei 1:1 an dieser Stelle
> übernommen wird. Was denkt sich wohl der C-Compiler, wenn da plötzlich
> eine Menge Assembler-Code auftaucht?

Aber es ist doch eigentlich egal, wo die ISR-Routine steht, schließlich 
started das Programm ja in main() und die ISR wird doch nur bei 
Auftreten des Interrupts aufgerufen.

Ich habe es so eingebunden:

#include <avr/io.h>
#define F_CPU 16000000
#include <avr/interrupt.h>

#include "ISR_ASM.s"

von Oliver S. (oliverso)


Lesenswert?

Hast du schon mal ein Programm geschrieben, das aus mehr als nur einer 
Source-Datei bestand?

Oliver

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Leopold N. schrieb:
> Aber es ist doch eigentlich egal, wo die ISR-Routine steht, schließlich
> started das Programm ja in main() und die ISR wird doch nur bei
> Auftreten des Interrupts aufgerufen.

Ja, aber du kompilierst eine C-Datei. In C-Dateien steht C-Code, kein 
Assembler-Code. Nachdem das #include aufgelöst wurde steht da:
1
Der Code aus <avr/io.h> ...
2
Der Code aus <avr/interrupt.h> ...
3
4
.global TIMER0_COMP_vect
5
6
TIMER0_COMP_vect:
7
8
   // Assembler Befehle

Sieht das aus wie C-Code?

Assembler Dateien muss (darf) man nicht inkluden. Die müssen nur mit 
kompiliert werden.

von Leopold N. (leo_n)


Lesenswert?

Niklas G. schrieb:
> Sieht das aus wie C-Code?
>
> Assembler Dateien muss (darf) man nicht inkluden. Die müssen nur mit
> kompiliert werden.

Ok, das leuchtet ein.
Wenn du nun aber einen Schritt weiter denkst, dann würdest du auch auf 
den Gedanken kommen, dass ich nicht weiß, wie man eine .s Datei 
einbindet bzw. mitkompilieren lässt.
Wenn du mir dieses riesengroße Geheimnis nun einfach verraten würdest, 
wäre das weitaus konstruktiver, als dir das ewig lang aus der Nase 
ziehen zu lassen.

Grüße

von Leopold N. (leo_n)


Lesenswert?

Oliver S. schrieb:
> Hast du schon mal ein Programm geschrieben, das aus mehr als nur einer
> Source-Datei bestand?
>
> Oliver

Habe ich.
Allerdings ist aus dem bisherigen Thread ja wohl ersichtlich, dass .s 
Dateien hier nicht dabei waren.

von Niklas Gürtler (Gast)


Lesenswert?

Leopold N. schrieb:
> Wenn du mir dieses riesengroße Geheimnis nun einfach verraten würdest,
> wäre das weitaus konstruktiver, als dir das ewig lang aus der Nase
> ziehen zu lassen.

Lesen hilft:

Niklas Gürtler schrieb:
> Einfach dem Atmel Studio Projekt hinzufügen. Oder einfach erstmal ein
> reines Assembler Projekt anlegen..

von Oliver S. (oliverso)


Lesenswert?

Leopold N. schrieb:
> Allerdings ist aus dem bisherigen Thread ja wohl ersichtlich, dass .s
> Dateien hier nicht dabei waren.

https://de.m.wikipedia.org/wiki/Lerntransfer

Oliver

von Leopold N. (leo_n)


Lesenswert?

Oliver S. schrieb:
> Leopold N. schrieb:
>> Allerdings ist aus dem bisherigen Thread ja wohl ersichtlich, dass .s
>> Dateien hier nicht dabei waren.
>
> https://de.m.wikipedia.org/wiki/Lerntransfer
>
> Oliver

Kann den bitte jemand aus diesem Tread schmeißen?

von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

Ich habe jetz die .s Datei dem Projekt hinzugefügt und in der main.c als

extern void funktion1();

deklariert.

Nun habe ich das Problem, dass der Compiler die Funktion funktion1() 
nicht findet.
Frage: Warum findet er die nicht?

.s Datei im Anhang.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Leopold N. schrieb:
> extern void funktion1();

Das "extern" ist bei Funktionen immer überflüssig.

Leopold N. schrieb:
> Nun habe ich das Problem, dass der Compiler die Funktion funktion1()
> nicht findet.

Compiler oder Linker? Wie lautet die Fehlermeldung? Wie werden Compiler, 
Assembler und Linker aufgerufen? Zeige mal den Build Log. Möchte Atmel 
Studio Assembler-Dateien vielleicht mit der Endung .asm statt .S haben?

von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

Niklas G. schrieb:
> Compiler oder Linker? Wie lautet die Fehlermeldung? Wie werden Compiler,
> Assembler und Linker aufgerufen? Zeige mal den Build Log. Möchte Atmel
> Studio Assembler-Dateien vielleicht mit der Endung .asm statt .S haben?

Ich benutze immer F7, also "Build Solution".

Build Output im Anhang

Wegen der Endung: Keine Ahnung, mache das ja grad zum ersten Mal

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Leopold N. schrieb:
> Build Output im Anhang

Dann ignoriert Atmel Studio deine .s Datei offensichtlich. Probier mal 
.S oder .asm. Wie sieht die Projektstruktur aus? Probier halt mal ein 
bisschen rum wie die Datei ins Projekt hinzugefügt wird, ich hab grad 
kein Atmel Studio hier.

: Bearbeitet durch User
von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

Hier mal die Struktur.
Probier grad noch ein bisschen aus

von Leopold N. (leo_n)


Lesenswert?

Also ich habe jetzt .s, .S und .asm ausprobiert.
Gibt bei allen das Gleiche.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Leopold N. schrieb:
> Also ich habe jetzt .s, .S und .asm ausprobiert.
> Gibt bei allen das Gleiche.

Im Menü gibt's doch bestimmt die Möglichkeit, eine ganz neue 
Assembler-Datei anzulegen. Die müsste das Atmel Studio dann wohl so 
hinzufügen dass sie auch assemlibisert & gelinkt wird.

von Leopold N. (leo_n)


Lesenswert?

Niklas G. schrieb:
> Leopold N. schrieb:
>> Also ich habe jetzt .s, .S und .asm ausprobiert.
>> Gibt bei allen das Gleiche.
>
> Im Menü gibt's doch bestimmt die Möglichkeit, eine ganz neue
> Assembler-Datei anzulegen. Die müsste das Atmel Studio dann wohl so
> hinzufügen dass sie auch assemlibisert & gelinkt wird.

Habe ich auch schon gemacht. Gibt auch n Fehler.
Ich vermute, dass mein ASM Code falsch ist.
Hast du schonmal über den drübergeschaut?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Leopold N. schrieb:
> Habe ich auch schon gemacht. Gibt auch n Fehler.

Welchen? Gewöhne dir doch bitte mal an, nicht einfach nur "ein Fehler" 
zu sagen. Es gibt in diesem Zusammenhang sehr viele mögliche Fehler. 
Da kann man unmöglich erraten, was für einer das sein soll. Und zwar 
bitte als Text, nicht als Screenshot.

von Oliver S. (oliverso)


Lesenswert?

Leopold N. schrieb:
> Also ich habe jetzt .s, .S und .asm ausprobiert.
> Gibt bei allen das Gleiche.

Dann hast du die Assemblerdatei nicht zum Projekt hinzugefügt.

Oliver

von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

Oliver S. schrieb:
> Dann hast du die Assemblerdatei nicht zum Projekt hinzugefügt.
>
> Oliver

Ich habe im Solution Explorer Rechtsklick auf mein Projekt gemacht --> 
Add --> Exisiting Item und die .s Datei ausgewählt.

Was ist daran falsch?, bzw. wie geht das richtig?

Niklas G. schrieb:
> Welchen? Gewöhne dir doch bitte mal an, nicht einfach nur "ein Fehler"
> zu sagen. Es gibt in diesem Zusammenhang sehr viele mögliche Fehler.
> Da kann man unmöglich erraten, was für einer das sein soll. Und zwar
> bitte als Text, nicht als Screenshot.

Hm ja, sollte ich mir angewöhnen.
Siehe Anhang.

von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

Ich will nur ganz simpel die Funktion mal aufrufen, um zu testen, ob das 
einbinden funktioniert hat.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Leopold N. schrieb:
> Hm ja, sollte ich mir angewöhnen.
> Siehe Anhang.

Niklas G. schrieb:
> Und zwar
> bitte als Text, nicht als Screenshot.

Und zwar den ganzen Build Log. Nicht nur das letzte Stückchen. Und nicht 
nur diese leider in allen IDEs vorhandene nutzlose "Errors" oder 
"Problems" Anzeige.

von Leopold N. (leo_n)


Lesenswert?

Niklas G. schrieb:
> Und zwar den ganzen Build Log. Nicht nur das letzte Stückchen. Und nicht
> nur diese leider in allen IDEs vorhandene nutzlose "Errors" oder
> "Problems" Anzeige.

Wenn du mir verrätst, wo sich der befindet, mach ich das gerne.

von Oliver S. (oliverso)


Lesenswert?

Leopold N. schrieb:
> Ich habe im Solution Explorer Rechtsklick auf mein Projekt gemacht -->
> Add --> Exisiting Item und die .s Datei ausgewählt.

Wennes da keinen Punkt „add existing source file“ gibt, dann sollte das 
passen. Wenn doch, dann nimm lieber den.

> Niklas G. schrieb:
>> Und zwar
>> bitte als Text, nicht als Screenshot.
>
> Hm ja, sollte ich mir angewöhnen.

Ernsthaft. Mach das. Ansonsten gibts hier keine vernünftigen Antworten.

Fehlermeldungen als Text.
Programme als Anhang im Original, niemals als Bild.
Am besten komplett mit allen Dateien, damit man das selber auch mal 
durch den Compiler schicken kann.

Oliver

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Leopold N. schrieb:
> Wenn du mir verrätst, wo sich der befindet, mach ich das gerne.

Hast du doch schon gefunden:

Leopold N. schrieb:
> Build Output im Anhang

Den komplett als Text rauskopieren. Und zwar jetzt mal für eine explizit 
in Atmel Studio neu angelegte Assembler Datei.

von Leopold N. (leo_n)


Lesenswert?

Oliver S. schrieb:
> Wennes da keinen Punkt „add existing source file“ gibt, dann sollte das
> passen. Wenn doch, dann nimm lieber den.

Nein, den gibt es nicht.

Niklas G. schrieb:
> Den komplett als Text rauskopieren. Und zwar jetzt mal für eine explizit
> in Atmel Studio neu angelegte Assembler Datei.

Aber Atmel Studio verwendet doch für reine Assembler Projekte einen 
anderen Assembler als für C Projekt so wie ich das verstanden habe.

von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

So richtig?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Leopold N. schrieb:
> Aber Atmel Studio verwendet doch für reine Assembler Projekte einen
> anderen Assembler als für C Projekt so wie ich das verstanden habe.

Kann sein. Aber du sollst ja eine einzelne Assembler Datei anlegen, kein 
ganzes Assembler Projekt.

Wenn du Atmel Studio GUI das nicht kann bzw. du sie nicht bedient 
bekommst, kannst du das auch klassisch machen und Compiler, Assembler & 
Linker manuell per makefile aufrufen. Da lernt man sowieso mehr und muss 
nicht ewig in einer GUI nach den richtigen Befehlen suchen.

von Leopold N. (leo_n)


Lesenswert?

Niklas G. schrieb:
> Kann sein. Aber du sollst ja eine einzelne Assembler Datei anlegen, kein
> ganzes Assembler Projekt.

Also wieder im Solution Explorer Rechtsklick --> Add --> New Item ?

von Oliver S. (oliverso)


Lesenswert?

Ja

Und dann mal einen Screenshot vom Projektordner zeigen.
Den gerne als Bild.

Oliver

von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

So, hab ich gemacht.

von Leopold N. (leo_n)


Lesenswert?

Und genau die gleiche Fehlermeldung tritt auf.

von Dr. Sommer (Gast)


Lesenswert?

Leopold N. schrieb:
> So, hab ich gemacht.

Es ist sehr verdächtig, dass die Datei welche erfolgreich kompiliert 
wird (main.c) ganz unten steht, und die Datei welche ignoriert wird 
(Assembly1.s) ganz oben. Vielleicht musst du die os.s zum Projekt 
hinzufügen und nicht zur Solution.

von Leopold N. (leo_n)


Angehängte Dateien:

Lesenswert?

Hier noch der Projektordner im Explorer.

von M. K. (sylaina)


Lesenswert?

S. R. schrieb:
> Mit reinem C geht immerhin kooperatives Multitasking mit setjmp/longjmp,
> das wäre schonmal ein Anfang, wenn man von ASM lieber Abstand halten
> möchte. Damit kommt man auch schon recht weit (und echtzeitfähig geht
> damit auch).

Na klar kommt man damit auch schon recht weit, keine Frage. Ich hatte es 
nur so verstanden, dass der TE noch etwas weiter will als "nur" setjmp 
und Co und dann wirds mit C schlicht zu eng. Auf der anderen Seite: Soo 
oft braucht man so etwas nicht, ich hab sowas noch nie gebraucht. Daher 
sind meine ASM-Erfahrungen auch recht überschaubar, die des TEs sind 
aber noch überschaubarer.

Arduino Fanboy D. schrieb:
> Ich bin ja irgendwie versucht, das Gegenteil zu beweisen.
> Darfs dann auch C++, statt C sein?
> (und in/mit der Arduino IDE?)

Ja, C++ geht auch...wird aber ein Bleistift das etwas tiefer geht als 
setjmp bzw. longjmp, oder?

von Oliver S. (oliverso)


Lesenswert?

Leopold N. schrieb:
> Also ich habe jetzt .s, .S und .asm ausprobiert.
> Gibt bei allen das Gleiche.

Dann probier’s nochmal. Vor dem umbenennen jeweils die Datei aus dem 
Projekt entfernen.

Und ja, Projekt != Solution

Oliver

von Leopold N. (leo_n)


Lesenswert?

Oliver S. schrieb:
> Und ja, Projekt != Solution
>
> Oliver

Ah ja,

da lag der Hund begraben.
Habs jetzt im Projekt drin.
Jetzt kommen andere Errors, mit denen ich mich jetzt erstmal 
beschäftigen werde.
Danke bis hierhin.

Grüße

von Leopold N. (leo_n)


Lesenswert?

Jetzt habe ich das Problem bei dieser Zeile

in r0, SREG

SREG kennt er nicht.
Gibt es dafür ein include file (m32def.inc) und wenn ja, wo befindet es 
sich?

Gleiches Problem für _SREG__ und __SREG und _SREG und _SREG
Ebenfalls bei _SP_L__ und __SP_H_

Grüße

von Leopold N. (leo_n)


Lesenswert?

Leopold N. schrieb:
> Gleiches Problem für SREG_ und __SREG und _SREG und _SREG
> Ebenfalls bei SP_L_ und __SP_H_

Hm irgendwie macht er mehr unterstriche hin als ich eigentlich platziert 
hatte....

von M. K. (sylaina)


Lesenswert?

Eigentlich sollte SREG in der common.h definiert sein, die durch das 
avr/io.h mit includiert wird.

von Oliver S. (oliverso)


Lesenswert?

Doku lesen hilft in den allermeisten Fällen weiter.

https://www.nongnu.org/avr-libc/user-manual/group__asmdemo.html

Oliver

von Leopold N. (leo_n)


Lesenswert?

M. K. schrieb:
> Eigentlich sollte SREG in der common.h definiert sein, die durch das
> avr/io.h mit includiert wird.

Nun da habe ich es auch gerade gefunden, aber das interessiert den 
Compiler/Linker/whatever nicht.

von Leopold N. (leo_n)


Lesenswert?

Habe jetzt avr/io.h in der Assembler-Datei selbst nocheinmal mit 
inkludiert (also insgesamt jetzt 2mal im ganzen Programm).
Damit funzt es dann.

von Einer K. (Gast)


Lesenswert?

Leopold N. schrieb:
> Habe jetzt avr/io.h in der Assembler-Datei selbst nocheinmal mit
> inkludiert (also insgesamt jetzt 2mal im ganzen Programm).

Das wirst du für jede einzelne Übersetzungseinheit machen müssen!
Ist doch auch logisch, oder nicht?
Und wenn es Hundert mal ist.... Watt mutt, datt mutt.

von S. R. (svenska)


Lesenswert?

Leopold N. schrieb:
> S. R. schrieb:
>> Meine Frage wäre: Schreibst du das OS, um
>> - ein OS zu haben,
>> - den AVR besser zu verstehen,
>> - das Konzept "OS" besser zu verstehen?
>
> Um ein OS zu haben (netter Nebeneffekt ist,
> dass ich den AVR dann besser verstehe)

Dir geht es also darum, ein OS zu haben und für irgendwas zu benutzen.

Dann lass den Quatsch und nimm ein OS, was fertig und getestet ist, und 
was es möglicherweise auch für andere Architekturen gibt. Da hast du in 
der Anwendung mehr von.

>> Mit reinem C geht immerhin kooperatives Multitasking mit setjmp/longjmp,
>> das wäre schonmal ein Anfang
>
> Habe ich mir schon geschrieben, reicht mir aber nicht :)

Was reicht daran denn nicht? Präemptiv arbeitende Betriebssysteme bieten 
eine wesentlich bessere Grundlage für echt beschissen zu debuggende 
Fehler. Besonders, wenn einem das Wissen darüber fehlt, wie man es 
richtig macht.

Davon abgesehen habe ich das Gefühl, dass es bei dir an den Grundlagen 
deines Tools massiv hapert. Und an der Fähigkeit einer ordentlichen 
Fehlerbeschreibung: "Hab ich gemacht, geht nicht." ist keine 
Fehlerbeschreibung - das ist nichteinmal nutzlos, das kostet andere 
sogar noch Zeit.

Aber gut, jedem das seine. Oder jedem Tierchen sein Pläsierchen.

von Leopold N. (leo_n)


Lesenswert?

Ich habe mir jetzt ein Array für den Kontext und ein Array für den Stack 
eines Threads angelegt.
Nun möchte ich ja bei Eintreten des Interrupts den Thread wechseln 
(somit also auch den aktuellen Kontext sichern und den neuen Kontext 
wiederherstellen).
Um nun aber den aktuellen Kontext zu sichern, muss ich diesen auf den 
Kontext-Stack des aktuellen Threads pushen. Dazu muss ich ja den SP 
manipulieren. Dafür brauche ich aber GP-Register, da ich ja irgendwie 
die neue Adresse laden muss. Damit verändere ich aber die GP-Register 
selber, wodurch ich ja den Kontext des aktuellen Threads ändere.
Wie kann ich das lösen?

Mein Ansatz: Globale Variablen definieren, in die ich die für die 
Neuberechnung des SP benötigten GP-Register zwischenspeichere, dann den 
SP manipulieren, dann die GP-Register wiederherstellen und anschließend 
alle GP-Register, SREG und PC pushen.
Zum Wiederherstellen des neuen Kontextes gleiches Verfahren nur 
umgekehrt.
Klingt meiner Ansicht nach nach einem recht großen Aufwand.

Habt ihr Ideen, wie man das besser lösen kann?

von Stefan F. (Gast)


Lesenswert?

Leopold N. schrieb:
> Klingt meiner Ansicht nach nach einem recht großen Aufwand.

Ja, aber ich denke so wirst du es wohl machen müssen, wenigstens für ein 
paar Register.

Die ARM Cortex M3 haben für solche Sachen praktischerweise zwei 
Stack-Pointer zwischen denen man wechseln kann, ohne irgendwelche 
Register zu verändern.

von Leopold N. (leo_n)


Lesenswert?

new_thread = thread_context[actual_thread].memoryblock[0];
 16e:  e0 91 6b 00   lds  r30, 0x006B  ; 0x80006b <actual_thread>
 172:  83 e2         ldi  r24, 0x23  ; 35
 174:  e8 9f         mul  r30, r24
 176:  f0 01         movw  r30, r0
 178:  11 24         eor  r1, r1
 17a:  e1 57         subi  r30, 0x71  ; 113
 17c:  ff 4f         sbci  r31, 0xFF  ; 255
 17e:  80 81         ld  r24, Z
 180:  80 93 6a 00   sts  0x006A, r24  ; 0x80006a <new_thread>
 184:  08 95         ret


Die oberste Zeile ist meine Anweisung, der Rest stammt vom Compiler.
Bis 178 verstehe ich auch alles.
Aber warum kommt dann diese Subtraktion von 113 und 255?
Und wo kommt in dem ganzen Assemblercode die Adresse von 
thread_context[] vor, die er ja irgendwie in die Berechnung mit 
einfließen lassen muss?

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Leopold N. schrieb:
> Aber warum kommt dann diese Subtraktion von 113 und 255?
> Und wo kommt in dem ganzen Assemblercode die Adresse von
> thread_context[] vor, die er ja irgendwie in die Berechnung mit
> einfließen lassen muss?

Die 16bit Subtraktion koennte genausogut eine 16bit Addition (des 2er 
Komplements) sein, und dann ist's wahrscheinlich genau die Stelle wo die 
Adresse von thread_context miteinfliesst.

Gruss
WK

von Niklas Gürtler (Gast)


Lesenswert?

Leopold N. schrieb:
> Um nun aber den aktuellen Kontext zu sichern, muss ich diesen auf den
> Kontext-Stack des aktuellen Threads pushen. Dazu muss ich ja den SP
> manipulieren.

Pushe es doch ganz normal direkt auf den Stack des aktuellen Threads, 
mit der push Anweisung. Dann enthält der Stack sowohl die Register als 
auch die Daten die der Thread selbst auf den Stack gesichert hat. Genau 
wie in meinem Beispiel gezeigt. Auf genau das Problem habe ich doch auch 
schön hingewiesen.

von Leopold N. (leo_n)


Lesenswert?

Niklas Gürtler schrieb:
> Pushe es doch ganz normal direkt auf den Stack des aktuellen Threads,
> mit der push Anweisung. Dann enthält der Stack sowohl die Register als
> auch die Daten die der Thread selbst auf den Stack gesichert hat. Genau
> wie in meinem Beispiel gezeigt. Auf genau das Problem habe ich doch auch
> schön hingewiesen.

Hatte ich mir auch schon überlegt, von der Trennung her finde ich es 
allerdings sauberer, einen Kontextstack und einen Funktionsstack zu 
haben.
Von der Berechnung her (somit auch der Ausführungszeit) ist es aber 
wahrscheinlich besser, es in einem zu speichern.

Liege ich damit richtig?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Leopold N. schrieb:
> Hatte ich mir auch schon überlegt, von der Trennung her finde ich es
> allerdings sauberer, einen Kontextstack und einen Funktionsstack zu
> haben.

Bei normalen Funktionsaufrufen werden ja auch die Register auf den Stack 
gesichert. Hier werden nur nochmal explizit alle Register beim 
Kontextwechsel gesichert. So hast du alles zusammen an einer Stelle, der 
Stack enthält praktisch den ganzen Kontext. Finde ich nicht so verkehrt.

Mangels Speicherschutz auf dem AVR kann man das System eh nicht 
besonders sicher machen, weshalb ein möglicher Stack Overflow an der 
Stelle auch keinen besonderen Unterschied mehr macht.

Das Sichern von Registern in temporäre globale Variablen finde ich 
hingegen nicht so schön. Das funktioniert ja nur Dank lds/sts, und die 
können IIRC auch nicht immer den ganzen Adressraum abdecken.

von Leopold N. (leo_n)


Lesenswert?

Wie kann ich eine Konstante, die ich in C mit #define festgelegt habe, 
in dem Assemblerfile verwenden?

von Stefan F. (Gast)


Lesenswert?

Leopold N. schrieb:
> Wie kann ich eine Konstante, die ich in C mit #define festgelegt habe,
> in dem Assemblerfile verwenden?

Gar nicht. Du musst sie in eine separate Datei packen und diesen sowohl 
in deinem Assembler-File als auch in deinem C-File inkludieren.

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.