Hallo allerseits. Eine Frage zum Task Context Switch Mechanismus(FreeRTOS): Ein Task A muß aufgrund eines anderen Tasks B (höhere Priorität) in den Suspend Modus. A muß nun sein Context in seinem Task Stack speichern (für eine spätere Wiederaufnahme). Dazu bedient sich FreeRTOS folgendem Makro. (das Makro sieht für jede Portierung natürlich anders aus(hier AVR Port), der grundsätzliche Mechanismus aber ist natürlich immer derselbe, mich interessiert hier mehr der Grundsatz als AVR spezifische Sachen) #define portSAVE_CONTEXT() \ asm volatile ( \ push r0 \ (1) in r0, _SREG_ \ (2) cli \ (3) push r0 \ (4) push r1 \ (5) clr r1 \ (6) push r2 \ (7) push r3 \ push r4 \ push r5 \ : push r30 \ push r31 \ lds r26, pxCurrentTCB \ (8) lds r27, pxCurrentTCB + 1 \ (9) in r0, _SP_L_ \ (10) st x+, r0 \ (11) in r0, _SP_H_ \ (12) st x+, r0 \ (13) ); Bis zu (8) werden notwendige Register im Task Stack abgelegt. In (8) und (9) aber wird jetzt ein Task Stack Pointer ins X Register geladen. Es steht geschrieben(Doku): "...pxCurrentTCB holds the address from where the tasks stack pointer can be retrieve..." Der Task Stack Pointer wird doch aber auch in (10)->(13), für spätere Wiederaufnahme abgelegt. Das verstehe ich nicht. Welcher ist jetzt nun der Task Stack Pointer? Für eine Wiederaufnahme des Tasks A müsste doch eigentlich der Stack Pointer Wert zum Zeitpunkt kurz vor der Unterbrechung (also (10) -> (13)) ausreichen. Danke für jede Antwort. Gruß Christian
> geladen. Es steht geschrieben(Doku): > "...pxCurrentTCB holds the address from where the tasks stack pointer > can be retrieve..." > Der Task Stack Pointer wird doch aber auch in (10)->(13), für spätere > Wiederaufnahme abgelegt. > Das verstehe ich nicht. Welcher ist jetzt nun der Task Stack Pointer? > Für eine Wiederaufnahme des Tasks A müsste doch eigentlich der Stack > Pointer Wert zum Zeitpunkt kurz vor der Unterbrechung (also (10) -> > (13)) Okay, ich muss zugeben, ich kenn mich nur begrenzt damit aus, kann mir es aber so erklären: Jeder Task hat ja seinen eigenen Stack, dieser wird, wie in deiner zitierten Doku pxCurrentTCB genannt. Hiermit erreiche ich also den Stack, der zu genau dem Task gehört. (wäre dann für jeden Task unterschiedlich) SP_L und SP_H sind ja die Stackpointer für den Globalen Stack, also den, den es nur einmal gibt. Dieser wird in R0 geladen und im Pointer X gespeichert. Nun müsste man wissen, was passiert mit dem Pointer X danach? oder wird er hier nun gespeichert. Fazit: pxCurrentTCP und SP sind vermutlich (meine Vermutung) nicht die gleichen Pointer. Ein Taskzugehöriger und ein globaler. Zur Wiederaufnahem müsste dann der pxCurrentTCB wieder gefunden werden. Was macht der Schedduler mit den Registern r26 und r27?? Ich hoffe, ich konnte helfen. Grüße, Benni
Folgendes Makro wird für Task A aufgerufen, wenn er wieder ausführbar wird: #define portRESTORE_CONTEXT() \ asm volatile ( \ lds r26, pxCurrentTCB \ (1) lds r27, pxCurrentTCB + 1 \ (2) ld r28, x+ \ out _SP_L_, r28 \ (3) ld r29, x+ \ out _SP_H_, r29 \ (4) pop r31 \ pop r30 \ : pop r1 \ pop r0 \ (5) out _SREG_, r0 \ (6) pop r0 \ (7) ); Verstehe aber das portRESTORE_CONTEXT() und das portSAVE_CONTEXT() Makro noch nicht. Auf was bezieht sich pxCurrentTCB vor der Unterbrechung und auf was danach? Gruß Christian
Habe mir mal den AVR InstructionSet angeschaut(bin kein AVR Spezi): lds r26, pxCurrentTCB \ (8) lds r27, pxCurrentTCB + 1 \ (9) in r0, SP_L \ (10) st x+, r0 \ (11) in r0, SP_H \ (12) st x+, r0 \ (13) Die Zeilen 8 bis13 addressieren ja indirekt. D.h. der Stackpointer wird hier ja nach X kopiert und der pxCurrentTCB zeigt ja auf X. Dann macht es (glaube ich) Sinn: Bevor er (A) unterbrochen wird, wird der SP (aktuell für A) nach X gespeichert. Nimmt A den Betrieb wieder auf, kann er auf den SP vor der Unterbrechung durch pxCurrentTCB zugreifen. Im Makro "portRESTORE_CONTEXT()" wird dann der SP auf den Wert von pxCurrentTCB gesetzt. Dadurch hat A bei Neuaufnahme wieder denselben SP wie vor der Unterbrechung. Wäre froh, wenn mir das mal jemand bestätigt. Wenn das so richtig ist bleibt noch eine Frage: Zur Wiederaufnahme ist doch neben dem aktualisieren des Register Context auch noch ein Update des PC nötig. Oder reicht das "Register Context Saving" hier völligst aus, um ein Task zu starten?
Christian schrieb: > Auf was bezieht sich pxCurrentTCB vor der Unterbrechung und auf was > danach? Das bezieht sich vermutlich immer auf das gleiche. Hier wird ja, laut deiner zitierten Dokumentation, gesagt, an dieser Stelle findet man den taskeigenen Stack - also irgendeine Adresse. Weißt du, wie der Taskstack implementiert ist im RTOS? Wird da der normale Stack verwendet oder ist das einfach ein Speicherbereich im RAM? Benni [EDIT] Woher nimmst du diese Info?? > und der pxCurrentTCB zeigt ja auf X
Im Falle des Avr wird der Stack im SRAM erzeugt. Das pxCurrentTCB auf X zeigt war nicht glücklich ausgedrückt. Über pxCurrentTCB wird nur der SP abgelegt und auch wieder zugegriffen. Also ein Platzhalter für die Adresse des SP.
Christian schrieb: > Im Falle des Avr wird der Stack im SRAM erzeugt. > Das pxCurrentTCB auf X zeigt war nicht glücklich ausgedrückt. > Über pxCurrentTCB wird nur der SP abgelegt und auch wieder zugegriffen. > Also ein Platzhalter für die Adresse des SP. Achja, deswegen auch r26 und r27. Ich erinnere mich.
Vielleicht löst sich die Verwirrung, wenn man sich mal die Definition des TCB ansieht (tskTCB => tasks.c). Dessen erste Komponente ist der Task-Stack-Pointer, verziert mit der Bemerkung "THIS MUST BE THE FIRST MEMBER OF THE STRUCT". pxCurrentTCB zeigt auf den aktuellen TCB und obiger Code schreibt/liest ebendiese erste Komponente des TCB. Und weil er das in Assembler tut, und dabei einen Offset von 0 annimmt, steht der erwähnte Kommentar da dran.
Zum "starten" des Tasks: Der Task-Kontext wird entweder vor einem Interrupt-Handler oder in einer aufrufenden Funktion gesichert (wenn der Task freiwillig pausiert). In beiden Fällen ist die Rücksprungadresse die in den Task zurück führt bereits auf den Stack gesichert worden (entweder durch den µC selbst beim Interrupt oder durch den Task beim Aufrufen der Funktion). Es reicht also dann, den Kontext des Tasks wiederherzustellen und mit RET oder RETI zur ursprünglichen Position zu springen, die Adresse liegt ja auf dem Stack. Gruß, Tobi
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.