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.
Ich möchte nichts drauf ablegen, sondern einfach nur einen beliebigen Wert vom Stack lesen, ohne dass der Stack verändert wird.
> Wert vom Stack lesen, ohne dass der Stack verändert wird.
Dann musst du notgedrungen mit inline Assembler arbeiten.
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
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.
Es geht auch innerhalb einer Funktion nicht. Aber das geht:
1 | uint16_t wert = SP; |
Oliver
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.
@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.
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
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.
Beitrag #5584569 wurde vom Autor gelöscht.
Beitrag #5584573 wurde von einem Moderator gelöscht.
Leopold N. schrieb: > Sehr hilfreich. Beschreib doch mal genau, was du warum machen willst. Dann kann man dir auch besser helfen.
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
Ich glaube nicht, dass man den Kontext-Switch in C implementieren kann.
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
:
Bearbeitet durch User
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
:
Bearbeitet durch User
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.
in intreg, SREG ;CPU-Flags sichern genauso bei den "memory mapped" die entsprechenden Befehle nehmen. nicht ldi ciao gustav
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
:
Bearbeitet durch User
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ß...
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
Den Stackpointer nicht ins Leere zeigen lassen? Oliver
:
Bearbeitet durch User
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.
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/
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];
Hm war ja klar....kaum postet man sein Problem fällt einem auch schon der Fehler auf....
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...
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.
Ich habe jetzt kein C mehr verwendet und die Funktion als naked deklariert, dennoch funkt der Compiler dazwischen.
Stefanus F. schrieb: > aber > dann kannst du in der Funktion kein C mehr verwenden. Das ist nicht richtig!
Du kannst auch *.s Dateien mit reinem Assembler Quelltext schreiben und das dann mit deinem C Programm linken.
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
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.
:
Bearbeitet durch User
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.
Leopold N. schrieb: > Standard Einstellung Ach so und die wäre? (gebe mal avr-gcc --version ein). Meine ist zum Beispiel 5.4.0.
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
Hatten nicht bereits mehrere Leute geschrieben, dass du den ganzen Task-Wechsel in Assembler schreiben musst?
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.
Naja, da wo er hin will, da kommt er mit C halt nicht hin. Das geht nur zu Fuß mit ASM.
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"
Hast du schon mal ein Programm geschrieben, das aus mehr als nur einer Source-Datei bestand? Oliver
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.
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..
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
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.
:
Bearbeitet durch User
Also ich habe jetzt .s, .S und .asm ausprobiert. Gibt bei allen das Gleiche.
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.
Ich will nur ganz simpel die Funktion mal aufrufen, um zu testen, ob das einbinden funktioniert hat.
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 ?
Ja Und dann mal einen Screenshot vom Projektordner zeigen. Den gerne als Bild. Oliver
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....
Eigentlich sollte SREG in der common.h definiert sein, die durch das avr/io.h mit includiert wird.
Doku lesen hilft in den allermeisten Fällen weiter. https://www.nongnu.org/avr-libc/user-manual/group__asmdemo.html Oliver
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.
Habe jetzt avr/io.h in der Assembler-Datei selbst nocheinmal mit inkludiert (also insgesamt jetzt 2mal im ganzen Programm). Damit funzt es dann.
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.
Wie kann ich eine Konstante, die ich in C mit #define festgelegt habe, in dem Assemblerfile verwenden?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.