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
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.
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
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.
>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.
@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.
>>>> 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:
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
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.
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
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...
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
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.
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.
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.
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?)
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
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
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.
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
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.
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
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
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
structTask{
2
char*stack;
3
};
4
5
structTask*currentTask;
6
7
structTask*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ß...
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
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
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.
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?
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.
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.
> 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.
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.
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];
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
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.
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.
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.
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...)?
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.
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
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.
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.
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?
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.
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.
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?
>> 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.
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.
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
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.
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
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.
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
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
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
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...
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
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.
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
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.
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?
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...
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.
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
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.
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.
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.
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...
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.
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.
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?)
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).
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 :)
.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
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
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..
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"
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
DerCodeaus<avr/io.h>...
2
DerCodeaus<avr/interrupt.h>...
3
4
.globalTIMER0_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.
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
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.
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..
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?
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.
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?
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
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.
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.
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?
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.
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
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.
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.
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.
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
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.
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.
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.
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 ?
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.
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?
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
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
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
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....
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.
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.
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.
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?
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.
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?
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
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.
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?
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.
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.