Hallo,
ich habe hier einen AT91SAM9XE512. Das ist ein ARM926EJ-S Prozessor. Ich
wollte mir jetzt aus Interesse mal Multitasking anschauen. Dafür habe
ich mir einen Thread Scheduler überlegt. Der sollte Folgendes tun:
- PC auf den Stack kopieren
- SP in Threadliste sichern
- nächsten SP aus der Threadliste laden
- "return", wobei der neue PC vom Stack geholt wird
Mein Problem ist nun, so glaube ich, dass der Scheduler Interrupt (ein
Periodic Interval Timer) logischerweise im Interrupt-Mode ausgeführt
wird, deshalb einen eigenen Stack hat und das ganze System ins Wasser
fällt. Ich habe versucht den Interrupt-Startup-Code (also das ASM
Programm) umzuschreiben, aber dabei habe ich irgendwie versagt.
Gibt es eine Standardprozedur für das Context Switching? Ich habe nach
Multithreading auf ARM gegoogelt aber nichts gefunden.
Das hier ist mein Code: http://pastebin.com/JZfG10V6
Aber wie gesagt, ich weiß nicht wie ich den InterruptHandler schreiben
muss. Der original-Handler von Atmel sieht so aus:
http://pastebin.com/5cMt6pWa
Ich bin für jede Hilfe dankbar.
> http://www.freertos.org/
Habe ich mir nun mal angeschaut. So wie ich das sehe machen die es
genauso wie ich es mir vorgestellt habe. Nur, der richtige
Einstiegspunkt für den Interrupt, den habe ich nicht gefunden.
Allerdings habe ich auch nur ein ARM7 Verzeichnis gesehen, keins für
ARM9.
Guest schrieb:> Mein Problem ist nun, so glaube ich, dass der Scheduler Interrupt (ein> Periodic Interval Timer) logischerweise im Interrupt-Mode ausgeführt> wird, deshalb einen eigenen Stack hat und das ganze System ins Wasser> fällt.
Du bist hier schon auf dem richtigen Weg. Allerdings musst du natürlich
die Register der unterbrochenen Task im Task-Kontext sichern, also auf
dem Task-Stack oder in einem Task-State-Record. Auf dem IRQ-Stack nützen
sie dir nichts.
Weshalb der IRQ Handler natürlich entsprechend umgeschrieben werden
muss. Beispiele dafür gibts in jedem von den dutzenden RTOS-Kernels, die
sich im Netz finden.
Dafür gibts zwei Varianten: Die eine sichert den Kontext im Interrupt
immer schon so, dass die Tast drin gewechselt werden kann. Nachteil: Es
wird mehr gesichert als unbedingt nötig, die Latenz wächst. Die andere
Variante fragt am Ende des IRQ-Dispatchers (irqHandler) ein Flag ab, ob
die Task gewechselt wurde, und zieht den Kontext-Save/Restore separat
durch. Bei IRQs ohne Taskswitch ist das billiger, mit Task-Switch
teurer.
>Du bist hier schon auf dem richtigen Weg. Allerdings musst du natürlich>die Register der unterbrochenen Task im Task-Kontext sichern, also auf>dem Task-Stack oder in einem Task-State-Record. Auf dem IRQ-Stack nützen>sie dir nichts.
Ja genau. Das habe ich versucht. Es ist jetzt erstmal nur der PIT
Interrupt aktiv, die anderen kümmern nicht. Ich habe es also mal so
versucht: http://pastebin.com/1PUnKyrk
Allerdings gibt das nur komisches Verhalten. Ich starte das Debugging,
und egal wie lange ich warte, wenn ich auf Pause drücke ist der ARM ganz
am Anfang dieses Interrupt Handlers. Dann kann ich mich
instruktionsweise durchsteppen, lande dann im SchedulerInterrupt, wenn
ich dann auf Play drücke springt er in den ersten Thread und führt den
aus. Leider wird der Scheduler Interrupt nie wieder ausgeführt.
Guest schrieb:> Ja genau. Das habe ich versucht. Es ist jetzt erstmal nur der PIT> Interrupt aktiv, die anderen kümmern nicht. Ich habe es also mal so> versucht: http://pastebin.com/1PUnKyrk
Du sicherst die Register im SVC-Stack. Sie müssen aber in den
Task-Stack, also den Stack vom USR/SYS-Kontext, oder in ein
Task-State-Record, aber eben nicht in den als IRQ-Stack verwendeten
SVC-Stack.
Der SVC-Kontext hat ein eigenes LR. Das LR im IRQ-Kontext wird bei dir
also nicht gesichert und der Return vom Handler springt zu dem was im LR
vom SVC zufällig drin stand.
Es könnte nicht schaden, wenn du mal in Beispielcode reinschaust, oder
in entsprechende Doku. So enthält auch das ARM ARM erklärten
Beispielcode für einen Task-Switch.
Du könntest dir anfangs das Leben auch etwas erleichtern, indem du nicht
zwei Probleme gleichzeitig angehst, nämlich Taskswitching und nested
IRQs. Ohne nested IRQs ist die komplizierte Umschaltung in den
SVC-Kontext unnötig, was die Sache deutlich erleichtert.
>Du sicherst die Register im SVC-Stack. Sie müssen aber in den>Task-Stack, also den Stack vom USR/SYS-Kontext, oder in ein>Task-State-Record, aber eben nicht in den als IRQ-Stack verwendeten>SVC-Stack.
Ich dachte das normale Programm läuft auch im SVC-Mode :/. Denn in der
Entry-Funktion schaltet er ganz am schluss auf SVC-Mode um und geht dann
in die low-level-init Funktion, und dann nach main(). Schaltet der
Compiler da in den Usermode?
>Der SVC-Kontext hat ein eigenes LR. Das LR im IRQ-Kontext wird bei dir>also nicht gesichert und der Return vom Handler springt zu dem was im LR>vom SVC zufällig drin stand.
Ja, DAS ist mir natürlich nicht eingefallen. Wie sicher ich denn das
LR_irq? In den SVC Mode, alle Register sichern, in den IRQ Mode, LR in
ein unge-banktes Register laden, wieder in den SVC Mode, Register
sichern? Das klingt zu kompliziert.
>Es könnte nicht schaden, wenn du mal in Beispielcode reinschaust, oder>in entsprechende Doku. So enthält auch das ARM ARM erklärten>Beispielcode für einen Task-Switch.
Bin schon auf der Suche!
>Du könntest dir anfangs das Leben auch etwas erleichtern, indem du nicht>zwei Probleme gleichzeitig angehst, nämlich Taskswitching und nested>IRQs. Ohne nested IRQs ist die komplizierte Umschaltung in den>SVC-Kontext unnötig, was die Sache deutlich erleichtert.
Das war nun aber eher unfreiwillig^^ Wollte ja in den SVC Mode weil die
Tasks auch SVC sind. Dachte ich.