Tach,
ich bin ganz neu in der ARM-Welt. Habe erst angefangen, mit LPC2000 zu
basteln :-) Genauer gesagt verwende ich einen LPC2364.
So, und nun habe ich dazu ein paar kleine Fragen...
1. Der ARM-Core kennt ja verschiedene Modi - UND, ABT, SVC und so
weiter. Im Startup-Code wird dann ja einmal in jeden dieser Modi
umgeschaltet, und der Stackpointer initialisiert. Das ist schön und gut;
aber was bringen denn diese Modi, wenn man einfach so zwischen denen
umschalten kann? Was unterscheidet die Modi von einander?
Früher habe ich mit Motorola 68k gebastelt; die haben auch einen
"Supervisor" und einen "User" Mode. Nach dem Reset war man immer im
Supervisor, und konnta da alles initialisieren; wenn man erst einmal in
den User-Mode geschaltet hatte, konnt man bestimmte Register nicht mehr
verändern und auch den Status nür über spezielle Befehle wieder
wechseln. Sozusagen als "Schutz", damit der User das System nicht
zerschiessen kann, indem er unberechtigte Sachen ändert.
Kann das der ARM auch?
2. Der Assembler ist ja ein bisschen gewöhnungsbedürftig ;-) man kann
Konstanten manchmal mit mov in ein Register laden, und manchmal benötigt
man LDR, und manchmal LDR mit diesem komischen = Operator (LDR r0,
=0x1234).
Frage: Findet sich irgendwo eine übersichtliche Liste der Befehle, die
der ARM7 kann, und wie die Syntax dieser auszusehen hat?
Henry schrieb:
> Was unterscheidet die Modi von einander?
Link-Register, Stack-Pointer und SPSR.
Die Modi sind nötig, weil im Unterschied zum 68000 Return-Adresse und
alten Statusregister bei Abort/... Exceptions nicht auf dem Stack
landen, sondern im R14 und SPSR des jeweiligen Exception-Modus. RISC
lässt grüssen.
Die manuelle Umschaltung ist nur für die Initialisierung von R13
(Stack-Pointer) nötig. 68K macht das über Spezialbefehl, ARM über
Modusumschaltung.
> Kann das der ARM auch?
Ja. Das unterscheidet USER und SYSTEM Mode.
> Frage: Findet sich irgendwo eine übersichtliche Liste der Befehle, die> der ARM7 kann, und wie die Syntax dieser auszusehen hat?
Es gibt auf arm.com zwei Kurzreferenzen. Da ist allerdings alles drin,
nicht nur ARM7.
Hallo A.K.,
warum kann ich dann im Startup-Code einfach so zwischen User und System
Mode umschalten? Sollte diese Umschaltung nicht auch irgendwie
eingeschränkt sein, sodass man vom einen Mode zwar in den anderen kommt,
aber nicht mehr zurück? sonst würde das ja keinen Sinn ergeben.
Ich kenne deinen Startup-Code nicht, aber normalerweise wird nicht in
den User-Mode geschaltet.
Der System-Mode benutzt die gleichen Register wie der User-Mode,
deswegen reicht es, den zu Initialisieren.
Jürgen
Henry schrieb:
> warum kann ich dann im Startup-Code einfach so zwischen User und System> Mode umschalten?
Von System nach User geht. Umgekehrt nicht.
Überlicherwise verwendet mal bei Verwendung als Mikrocontroller ohne
Betriebssystem entweder den einen oder den anderen Modus, je nach
persönlichem Geschmack.
Ansonsten verwenden Handler für verschachtelte Interrupts u.U. den
System Modus, denn der Interrupt Modus ist nicht reentrant. Ein
Konstruktionsfehler der ARM Architektur.
Aber klar doch. Nested IRQ oder FIQ gehen ohne Probleme.
Modespezifische-Register sichern, I/F Flags wieder löschen. Irgendwann
wieder setzen, Mode register zurückspielen.
Kannst machen bis dir der Speicher ausgeht.
Nur solange der Handler keine Funktion aufruft. Auch keine
Compiler-Runtime.
Der Interrupt-Handler verwendet genauso R14 als Link-Register für seinen
eigenen Code wie jeder andere. Und wenn er eine Funktion aufruft, dann
landet die Return-Adresse wie üblich ebendort. Und wird beim ersten
Schachtelinterrupt von der Hardware überschrieben.
Weshalb ein Handler für nested Interrupts vor/mit Freigabe des
Interrupts den Modus wechselt. Das fällt nur nicht so auf, weil das mit
der Freigabe vom I-Flag kombiniert wird. Siehe
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka11008.html.
Wissen sollte man es aber schon, denn es bedeutet, dass solche
Interrupt-Handler hauptsächlich den System/User-Stack beanspruchen, nur
2 Worte pro Level landen im Interrupt-Stack.
Maxxie schrieb:
> Erm, wie bei jedem branch with link, sicherst du R14(lr) auf dem Stack.
Eine Funktion, die keine weitere Funktion aufruft, ist nicht
verpflichtet R14 zu sichern. Wird dies meistens auch nicht tun, wie bei
jedem branch with link.
Selbst wenn du den Compiler dazu bringen könntest dies immer zu tun
besteht dennoch zwischen BL und dem STM-Befehl immer noch ein Fenster in
dem R14 zerstört wird, wenn der Interrupt genau da hinein rutscht.
Hältst du ARM für blöd?
Maxxie schrieb:
> Da ist kein Unterschied zu rekursiven Funktionsaufrufen in Hinsicht des> LR.
Oh doch. Der Interrupt ist asynchron, kann zu jedem Zeitpunkt auftreten
zu dem er freigegeben ist. Der rekursive Funktionsaufruf hingegen
geschieht nur an genau kontrollierter Stelle. Das genau ist der
Unterschied.
Leute,
der LR wird gesichert (gemeinsam mit dem cpsr und evtl anderen genutzten
registern, wärend das i flag noch gesetzt ist. Damit sind diese Werte
verlässlich.
Danach kann ich zu jedem Zeitpunkt gerne wieder unterbrochen werden, und
lösche das i flag.
Am Ende der (unterbrechenbaren) Interruptbehandlung wirds i flag wieder
gesetzt. LR und co wieder hervorgeholt und per MOVS pc,lr
herausgesprungen.
Wo seht ihr da ein Problem?
1
@ISR:
2
stmdb sp!,{r0-r4,r12,lr} ; ABI conform: in whatever_wherever veränderbare register
3
MRS r0,CPSR
4
stmdb sp!,{r0}
5
BIC r0,#0xC0
6
MSR CPSR, r0 ; bis hier kann kein reentry auftreten
Maxxie schrieb:
> BL @whatever_wherever
Explizit passiert hier;
R14_irq = PC
PC = whatever
Und nun kommt an genau dieser Stelle, also direkt unmittelbar sofort
nach BL, der Schachtelinterrupt und macht:
R14_irq = PC
SPSR_irq = CPSR
PC = 0x000000xx
schwupps, weg ist der vorherige Inhalt von R14. Wenn also
irgendwannwhatever beendet wird, geht es ab in den Wald, genauer gesagt
dorthin wo der Schachtelinterrupt auftrat. Und nicht hinter den BL.