Hallo, ich würde gerne eine "kleine Multithread" Einheit bauen, die in der Lage ist 2 kleine Programme nacheinander/ Parallel auszuführen. Mir ist durchaus klar, dass es auch VIEL einfacher geht, aber darum geht es mir nicht! Mein Ziel ist es am 2 Kleine Programme, jeweils in ihrer eigenen while(1) Schreibe,Parallele laufen zu lassen. Die Idee ist das über ein TC-Interrupt die beiden Programme getriggert werden. Hierzu müsste es doch reichen den Stack-Pointer zu sichern, und die Rücksprung-Adresse von Programm 1 durch die von Programm 2 zu ersetzen. Zusätzlich müssen die General-purpose Registers gesichert(und mit den gespeicherten von Programm 2 ersetzt) werden, damit keine Schritte/ Berechnungen verloren gehen. Meine Frage bezieht sich nun auf 2 Dinge: 1. Stimmt meine Theorie 2. Wie kann ich die General-purpose Register auslesen und wieder beschreiben gibt es für C überhaupt Befehle dazu? Ich Verwende eine Cortex M4(SAM4SD32C)Prozessor.
:
Bearbeitet durch User
@ Sebastian B. (basti195) >ich würde gerne eine "kleine Multithread" Einheit bauen, die in der Lage >ist 2 kleine Programme nacheinander auszuführen. Hmmm. >Mein Ziel ist es am 2 Kleine Programme, jeweils in ihrer eigenen >while(1) Schreibe,Parallele laufen zu lassen. >Die Idee ist das über ein TC-Interrupt die beiden Programme getriggert >werden. Hierzu müsste es doch reichen den Stack-Pointer zu sichern, und >die Rücksprung-Adresse von Programm 1 durch die von Programm 2 zu >ersetzen. Das allein reicht nicht. >Zusätzlich müssen die General-purpose Registers gesichert(und mit den >gespeicherten von Programm 2 ersetzt) werden, damit keine Schritte/ >Berechnungen verloren gehen. >1. Stimmt meine Theorie Nein, denn sie ist mindestens grob unvollständig. Du hast nur den Hauch einer Skizze, kein Konzept. >2. Wie kann ich die General-purpose Register auslesen und wieder >beschreiben >gibt es für C überhaupt Befehle dazu? Nö. Darum macht man sowas eher in ASM. Schau dir bestehende RTOSe an und denk drüber nach. Sooo einfach ist das nicht, wenn es nicht nur ein singuläres Gemurkse werden soll.
Sebastian B. schrieb: > 2. Wie kann ich die General-purpose Register auslesen und wieder > beschreiben > gibt es für C überhaupt Befehle dazu? Die Sprachelemente von C wissen erstmal nichts (wollen absichtlich nichts wissen) von der darunterliegenden Hardware, sie sollen die Hardware vollständig abstrahieren.
Bernd K. schrieb: > Die Sprachelemente von C wissen erstmal nichts (wollen absichtlich > nichts wissen) von der darunterliegenden Hardware, sie sollen die > Hardware vollständig abstrahieren. Eben, aber für die Taskumschaltung scheint es keine solche Abstraktion in C zu geben.
Falk B. schrieb: > Nö. Darum macht man sowas eher in ASM. ??? Mit nem C-Pointer kannst du quer duchr den Speicher alles manipulieren. Natürlich auch irgendwelche Register.
Das Heißt es gibt keine Weg um das Speichern und Ersetzen der Register mit Inline-Assembler zu machen?
Mit setjmp() und longjmp() kann man so Sachen basteln. Sollte Teil der C Standard Library sein. https://en.wikipedia.org/wiki/Setjmp.h
Sebastian B. schrieb: > Das Heißt es gibt keine Weg um das Speichern und Ersetzen der > Register mit Inline-Assembler zu machen? Klar geht das. Du musst aber noch richtige Stack Frames aufsetzen und die Register sichern. Die Rücksprungadresse kommt in das Stack Frame, das LR bekommt eine Spezial Adresse für den ISR Rücksprung. Lese mal genau das ARMv7M Architecture Manual. Die Architektur ist nämlich genau dafür gemacht, das zu können...
@ (uint32_t*)0x01234567 (Gast) >> Nö. Darum macht man sowas eher in ASM. >??? >Mit nem C-Pointer kannst du quer duchr den Speicher alles >manipulieren. Natürlich auch irgendwelche Register. Auch Zitieren will gelernt sein. >>2. Wie kann ich die General-purpose Register auslesen und wieder >>beschreiben >>gibt es für C überhaupt Befehle dazu? >Nö. Darum macht man sowas eher in ASM. Kaum. Es gibt in C KEINEN Direktzugriff auf den Stackpointer ala ASM. Nur weil der beim AVR als IO-Register exitiert und damit als SP(L/H) zugreifbar ist, ist das noch lange nicht allgemeingültig. CPU-Register sind im "normalen C" gar nicht sicht- und zugreifbar. Diverse Inline ASM-Hackereien haben mit "normalem C" nichts zu tun.
@Sebastian B. (basti195) >Das Heißt es gibt keine Weg um das Speichern und Ersetzen der Register >mit Inline-Assembler zu machen? Doch, den gibt es. Aber das ist ja ASM. Und Inline-ASM ist der beschwerlichste Weg, ASM zu nutzen. Ausser für ein paar kleine, pfiffige Makros tut sich das keiner an. Wenn schon, dann mit getrennten ASM-Dateien, die getrennt assembliert und dann zu deinem C-Projekt gelinkt werden.
Hier ein Beispiel für einen Kontextwechsel - nichts anderes hast du ja vor - in C mit Inline-Assembler:
1 | inline void __attribute__((naked)) MeineISR () { |
2 | asm volatile ("cpsid i"); |
3 | |
4 | // Save unsaved registers.
|
5 | asm volatile ("push {r4, r5, r6, r7, r8, r9, r10, r11, lr}" ::: "memory"); |
6 | |
7 | // Store stack pointer for old thread.
|
8 | asm volatile ("str sp, [%0]" :: "r" (¤tThread->sp)); |
9 | |
10 | // Update running thread.
|
11 | currentThread = getNextThread (); |
12 | |
13 | // Fetch stack pointer for new thread.
|
14 | asm volatile ("ldr sp, [%0]" :: "r" (¤tThread->sp)); |
15 | |
16 | asm volatile ("cpsie i"); |
17 | |
18 | // Load registers and return.
|
19 | asm volatile ("pop {r4, r5, r6, r7, r8, r9, r10, r11, pc}" ::: "memory"); |
20 | }
|
Siehe auch: http://infocenter.arm.com/help/topic/com.arm.doc.ddi0403e.b/index.html Ab Seite 592. http://hardwarebug.org/2010/07/06/arm-inline-asm-secrets/ http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#ss5.3 http://www.ethernut.de/en/documents/arm-inline-asm.html http://wiki.osdev.org/Inline_Assembly/Examples http://www.osdever.net/tutorials/view/a-brief-tutorial-on-gcc-inline-asm
Vielen Dank so weit :) Was ist den der Genau unterschied zwischen Link Register und Stack Pointer? Ist der Stack-Pointer der Punkt wo nach dem ISR hin gesprungen wird und das Link Register wo das Programm vor dem ISR war?
Sebastian B. schrieb: > Was ist den der Genau unterschied zwischen Link Register und Stack > Pointer? Uhoh, wenn du noch solche Fragen stellst solltest du ganz dringend das Architecture Reference Manual lesen, bevor du an so Dinge denkst wie einen Kontextwechsel zu programmieren!
@ Niklas Gürtler (erlkoenig) >> Was ist den der Genau unterschied zwischen Link Register und Stack >> Pointer? >Uhoh, wenn du noch solche Fragen stellst solltest du ganz dringend das >Architecture Reference Manual lesen, bevor du an so Dinge denkst wie >einen Kontextwechsel zu programmieren! Der OP unterschätzt den Aufwand seiner Idee. Wer sowas machen will, muss seine CPU auf ASM-Ebene aus dem FF beherrschen, dazu noch C und die Verknüpfung von beidem. Das ist was für die hohen Semester. In der Zeit hat man die meisten Probleme locker mit kooperativem Multitasking und einfachen Interrupts gelöst. Zumal die Idee, einfach zwei while(1) Schleifen "unsichtbar" zu multiplexen nur für triviale Sachen so einfach geht. In dem Moment, wo die beiden while(1) schleifen miteinenander kommunizieren müssen, wird es deutlich aufwändiger. Ohne gescheite Mechanismen ala Semaphoren, Mutexen etc. wird das nix.
Sebastian B. schrieb: > Ich Verwende eine Cortex M4(SAM4SD32C)Prozessor. Für Cortex M MCUs gibt es FreeRTOS, das enthält entsprechenden Code zur Taskumschaltung.
Sebastian B. schrieb: > ich würde gerne eine "kleine Multithread" Einheit bauen, die in der Lage > ist 2 kleine Programme nacheinander/ Parallel auszuführen. Hört sich so an, als ob du einen (minimalen) Scheduler bauen möchtest: https://en.wikipedia.org/wiki/Scheduling_%28computing%29#Dispatcher > Die Idee ist das über ein TC-Interrupt die beiden Programme getriggert > werden. Hierzu müsste es doch reichen den Stack-Pointer zu sichern, und > die Rücksprung-Adresse von Programm 1 durch die von Programm 2 zu > ersetzen. Trigger zum Auslösen des (Context) Switches hast du also. Du brauchst noch Funktionen zum Saven/Restoren des jeweiligen Contexts. Im minimalen Fall könnte ein Abbild der CPU Register ausreichen. > Zusätzlich müssen die General-purpose Registers gesichert(und mit den > gespeicherten von Programm 2 ersetzt) werden, damit keine Schritte/ > Berechnungen verloren gehen. > > Meine Frage bezieht sich nun auf 2 Dinge: > 1. Stimmt meine Theorie Grundsätzlich ja. Siehe auch o.g. Wikipedia Link. > 2. Wie kann ich die General-purpose Register auslesen und wieder > beschreiben > gibt es für C überhaupt Befehle dazu? Wenn, dann mit setjmp/longjmp wie vom Vorposter beschrieben.
Mal was aehnliches weil ich es vor Jahren versucht und nicht hinbekommen habe. Wie funktioniert das auf groesseren ARMs mit unterschiedlichen Register sets? Sobald der Cintext Change Interrupt aufgerufen wird, schaltet der Core ja in den Interrupt Mode und man hat keinen Zugriff mehr auf die Register des User Modes. Wechselt man waehrend dem Interrupt einfach in den Usermode zurueck oder gibt es eine cleverere Taktik?
S. J. schrieb: > Hört sich so an, als ob du einen (minimalen) Scheduler bauen möchtest Genau so was in etwa habe ich vor :) vielen dank an alle für die schnelle Hilfe. Jetzt weiß ich(oder zumindest habe ich eine Idee °_^ ) wie in etwa ich vorgehen muss und wo ich mir weiteren Lese-Stoff zulegen muss. vielen Dank grüße basti95
Noch einfacher - Kooperatives Multitasking. Nicht der Timer, deine Programme rufen der Scheduler auf. Da finden sich Beispiele, die mit setjump/longjump auskommen. Um interne Register brauchst du dich nicht zu kümmern.
Man kann bei den Cortex M zwischen Prozess- und Interrupt-Stack unterscheiden. Einem Thread-Scheduler kommt das sehr entgegen, da so nicht jeder Thread-Stack die Last von Interrupts tragen muss. Mit setjmp/longjmp geht das dann allerdings nicht. Wenn Interrupts verschachtelt werden können, dann kann nicht jede ISR direkt den Thread umschalten, sondern nur diejenige, die direkt aus dem Thread-Kontext heraus aufgerufen wurde. Kein Problem, wenn alle auf einen anderen Thread umschaltenden Handler auf gleicher niedrigster Priorität laufen. Alternativ kann man bei verschachtelt aufgerufenen ISRs auch die PendSVC Exception nutzen. Der wird niedrigst priorisiert und wenn ein Thread-Switch ansteht aktiviert. Dessen Handler schaltet dann um.
Guest schrieb: > Mal was aehnliches weil ich es vor Jahren versucht und nicht hinbekommen > habe. Wie funktioniert das auf groesseren ARMs mit unterschiedlichen > Register sets? Sobald der Cintext Change Interrupt aufgerufen wird, > schaltet der Core ja in den Interrupt Mode und man hat keinen Zugriff > mehr auf die Register des User Modes. Beim ursprünglichen ARM Befehlssatz kann eine Variante der LDM/STM Befehle auf die Register des User-Modes zugreifen.
:
Bearbeitet durch User
Schau mal hier: Beitrag "Neuer Multitasker Olix" Ich habe sowas vor ein paar Jahren mal fuer einen M16C geschrieben. Das ist im Prinzip das Minimum das man braucht. Ausserdem habe ich mir Muehe gegeben es gut zu dokumentieren. Da musst du nur noch den Assemblerteil selber schreiben. :-) Allerdings, wie Falk schon sagte, man muss dazu den Prozessor gut kennen und sollte Assembler auch drauf haben. Ich kann nur jedem mal empfehlen soetwas selber zu programmieren. Man bekommt da ein ganz gutes Gefuehl fuer einen Controller. .-) Olaf
Beim M4 musst du ggf. noch die Floating Point Register retten. Dadurch würde sich der Aufwand mehr als verdoppeln -- Entwicklung und vor allem Laufzeit. Das ist ein Grund mehr, auf Floating Point möglichst zu verzichten. Wenn man sicher stellen könnte, dass die Register nur von einer Task benutzt werden, müssten sie nicht gerettet werden. Ob man wohl eine Task mit "-mcpu=cortex-m4" und den Rest mit " -mcpu=cortex-m3" übersetzen kann?
eagle user schrieb: > Beim M4 musst du ggf. noch die Floating Point Register retten. Dadurch > würde sich der Aufwand mehr als verdoppeln -- Entwicklung und vor allem > Laufzeit. Schema bei vielen Prozessoren, in Software oder direkt in Hardware: beim Kontextwechsel die FPU deaktivieren. Beim nächsten FPU-Befehl gibts eine Exception und darin kann der Wechsel des FPU-Kontextes falls nötig nachgeholt werden. Eine nicht verwendete FPU erzeugt so praktisch keine Kosten und wenn nur ein Thread sie nutzt wird nichts gewechselt. Da das ein hinlänglich bekanntes Problem aller Prozessoren mit FPU ist hat ARM das bei den Cortex M automatisiert. Siehe Architecture Reference zu "Lazy context save of FP state". > Das ist ein Grund mehr, auf Floating Point möglichst zu verzichten. Nein. Das ist bloss ein Grund mehr, Doku zu lesen. ;-)
:
Bearbeitet durch User
> Beim M4 musst du ggf. noch die Floating Point Register retten. Dadurch > würde sich der Aufwand mehr als verdoppeln Ich wollte meinen Code irgendwann mal auf einen SH2A anpassen. Der duerfte durch seine sechzehn integrierten Registerbaenke ganz schoen flott sein. Wobei ich mich gerade frage wie sich bei modernen Prozessoren wie dem SH2A oder auch den ARMs der Cache auswirkt. Ich koennte mir vorstellen das da ordentlich Leistung verloren geht weil der Inhalt des Cache dann sehr oft ungueltig wird. Olaf
Olaf schrieb: > Wobei ich mich gerade frage wie sich bei modernen Prozessoren wie dem > SH2A oder auch den ARMs der Cache auswirkt. Ich koennte mir vorstellen > das da ordentlich Leistung verloren geht Wenn du auf sehr schnelle Ausführung von ISRs Wert legst und Cache Misses nicht zulässig sind, dann musst sehen, ob du deren Code im Cache festnageln kannst. Grad bei Controllern dürfte das über explizite Cache-Steuerung möglich sein. Es gibt andererseits aber auch recht viele µCs, die eine Art Cache für langsamen Flash-Speicher haben, aber keinen Cache für das interne RAM benötigen. Bei denen legt man den Code der ISRs ins RAM. > weil der Inhalt des Cache dann sehr oft ungueltig wird. Nur wenn du eine MMU mit Adressumsetzung drin hast und der Cache die virtuellen Adressen taggt. Ohne Adressumsetzung (paging) oder bei physikalisch getaggten Caches (das ist die Regel) ändert der Kontextwechsel per se nichts am Cache. Ohne Pinning des ISR-Codes ist das dann eine Wahrscheinlichkeitsrechnung zusammen mit Glück oder Pech der Adresslage der ISRs relativ zu anderem Code und der Assoziatität des Caches. Kann sein, dass eine häufig genug aufgerufene ISR im Cache quasi von selbst festgepinnt ist, nach grösserem Umbau des Codes aber stets rausfliegt.
:
Bearbeitet durch User
A. K. schrieb: > "Lazy context save of FP state". Danke für die Erklärung! Ein guter Foren-Beitrag sagt eben doch mehr als 42 Seiten ARM-Doku ;)
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.