hallo, mal eine frage, welche wohl recht banal erscheinen dürfte, mich aber zum grübeln bringt. Es gibt ja MCUs, da wird, bevor in die main gesprungen wird, eine art startup routine ausgeführt, die so grundlegende settings übernimmt, wie zB die main clock einzustellen oder den stack zu initialisieren. Ich Frage mich nun, wieso solche Dateien, oft als Assembler code geschrieben werden und nicht in c. wann muss ich explizit auf Assembler zurückgreifen und wann nicht?
hansi schrieb: > Ich Frage mich nun, wieso solche Dateien, oft als Assembler code > geschrieben werden und nicht in c. Die Antwort hast du doch oben schon selbst gegeben, daher meine Frage an dich: Macht es Sinn etwas in C zu implementieren bevor die C-Runtime, respektive der Stack initialisiert sind?
hansi schrieb: > wann muss ich explizit auf Assembler zurückgreifen und wann nicht? in dem o.g. Fall auf jeden Fall nicht. Assembler nur wenn die Anwendung auf den Takt genau zeitkritisch ist, oder wenn der Controller bis zu letzt ausnutzt werden soll. Für alle Anwendungen mit einer Stückzahl kleiner 10 nimmt man einfach den nächst größeren Controller und kann dann wieder normal in C programmieren. Axel
Düsendieb schrieb: > Assembler nur wenn die Anwendung auf den Takt genau zeitkritisch ist, > oder wenn der Controller bis zu letzt ausnutzt werden soll. Antwort passt leider nicht zur Frage (nochmal genau lesen).
Es gibt verschiedene Gründe: 1. Assembler ist einfach gängiger und schafft optimierten Code. 2. Du erwähnst ja schon, dass beispielsweise der Stack initialisiert wird. Wenn noch kein Stack vorhanden ist, kannst du keine C-Funktionen verwenden, da die Parameterübergabe als auch die Rücksprungadresse im Stack abgelegt werden bzw. auf den Stack zugreifen. 3. Oft benötigst du spezielle (Assembler)Befehle des Prozessors um z.B. Mode-Switches durchzuführen. Das wäre dann inline-Assembler, also warum nicht gleich in Assembler. Es gibt auch Controller bzw. Compilerfamilien, die einen zweistufigen Startup-Code nutzen. Das heißt ganz grundlegende Initialisierungen werden mit einem Assembler-Modul realisiert und etwas aufwendigere mit einem C-Startup-Code Beispielsweise für Variableninitialisierung. IAR EWARM ist ein Beispiel.
m.E. nach legt die tool chain, also die linker settings (?), den bereich im memory fest, wo der stack liegen soll oder? mit initialisieren meinte ich eher pattern reinschreiben. ok war ein dummes beispiel. also im konkreten fall wird folgendes gemacht: -clk settings werden gesetzt -clk stabilisierungs zeit wird gesetzt -boot table erstellt etc.. ich mein im endeffekt werden hier nur system register beschrieben. das geht doch auch in c odeR?
Loonix schrieb: > Antwort passt leider nicht zur Frage (nochmal genau lesen). Die Antwort stand darüber: >in dem o.g. Fall auf jeden Fall nicht Der Rest war als Zusatzinfo gedacht. Axel
hansi schrieb: > ich mein im endeffekt werden hier nur system register beschrieben. das > geht doch auch in c odeR? Das musste in der Regel bei C Programmierung gar nicht selber machen, da hat schon der Conpiler dran gedacht.
Assembler nimmt man, weil genau das ausgeführt wird, was man hinschreibt, also in genau der Reihenfolge und in genau der Zeit. In C ist das nicht so, sondern hängt vom Optimierungslevel und von der Compilerversion ab. Deshalb sind auch Sytembibliotheken (delay.h, wdt.h) nicht in C geschrieben, sondern in Inline-Assembler. Peter
ne eben nicht, das steht ja in meinen start configuration file. gerade was die clock settings angeht. denke mal low level hat mit punkt 1 und 3 schon recht. es ginge in c wäre aber unpassend?! punkt 2, verstehe ich noch nicht ganz, da ich dachte das der linker weiss wo stack liegt und wie groß er ist und eben diesen auch verwaltet....
ah ok, also prinzipiell wäre es aber möglich (kein optimierung, etc..)?
hansi schrieb: > ah ok, also prinzipiell wäre es aber möglich (kein optimierung, etc..)? Jein. Solange das C-Environment nicht steht, kannst du C nicht oder nur eingeschränkt verwenden. Und alles, was man vor der Erstellung des C-Environments ausführen MUSS, geht halt nur in Assembler. Ob dazu jetzt auch die Einstellung des Taktes oder andere Einstellungen am Prozessor selber gehört, hängt von dessen Architektur ab. Bei vielen kann man solche Dinge auch am Anfang von main in C machen. Oliver
Oliver schrieb: > Jein. Solange das C-Environment nicht steht, kannst du C nicht oder nur > eingeschränkt verwenden. Und alles, was man vor der Erstellung des > C-Environments ausführen MUSS, geht halt nur in Assembler. was genau meinst du mit c environment? was gehört alles dazu?
hansi schrieb: > wann muss ich explizit auf Assembler zurückgreifen und wann nicht? Angenommen du willst mit einer einfachen for-Schleife alle Register auf 0 setzen. In C wird eines dieser Register als Zähl-Index, ein anderes zur Adressierung benutzt. Sowie du das erste dieser beiden Register in deiner Schleife überschreibst, beisst sich die Katze in den Schwanz. Deswegen geht das nur in Assembler. mfg.
hansi schrieb: > was genau meinst du mit c environment? was gehört alles dazu? es düfte schon an Variablen scheitern. Denn die Variabeln liegen normlerweise auf dem Stack und der ist ja noch nicht da. Also du kannst C verwenden nur ohne Funktionen und Variablen.
Peter schrieb: > Also du kannst C verwenden nur ohne Funktionen und Variablen. So ist es. Die Ausgangsfrage klingt allerdings nach ARM7. Da kannst du im Prinzip sofort nach der Stack-Initilisierung eingeschränkt in C loslegen, eben mit der Einschränkung, daß noch keinerlei Variablen initilaisiert sind. So etwas wie REGISTER_XY = 0x42; geht allerdings schon. Takteinstellungen und ähnliches kann man machen, wann man will, auch am Anfang von main in C, oder noch viele später. Oliver
Ich würde mal sagen 100% aller Anfängerprogramme und 80% oder mehr der Profianwendungen lassen sich locker in C Programmieren. Assembler wird hier oft nur wegen des (zweifelhaften) Lerneffekts empfohlen
hansi schrieb: > was genau meinst du mit c environment? was gehört alles dazu? Initialisierung des Stack Initialisierung des Heap (falls das Programm malloc/free verwendet) Initialisierung globaler Variablen Diese Reihenfolge ist sinnvoll, wenn außer C- auch C++-Programme zur Ausführung kommen können, dann braucht man nämlich einen funktionstüchtigen Stack (und ggfs. Heap), um globale Objekte zu konstruieren. Werden nur C-Programme unterstützt, dann kann man auch die Variablen zuerst erledigen. Die Einstellung von Hardware-Registern hat hiermit in der Regel (*) nichts zu tun und kann prinzipiell gleichermaßen vor oder nach der C-Initialisierung geschehen. (*) Ausnahmen gibt es natürlich, z.B. Setzen des Stackpointers. Oder wenn man externen Speicher hat (Heap für größere Datenmengen im externen RAM), muss man natürlich, bevor man die C-Runtime den Heap initialisieren lässt, den XMem-Controller an den Start bringen. Aber selbst das könnte man als primitive C-Routine formulieren und vom Assembler-Startupcode aufrufen lassen.
Düsendieb schrieb: > Ich würde mal sagen 100% aller Anfängerprogramme und 80% oder mehr der > Profianwendungen lassen sich locker in C Programmieren. Thema verfehlt... Oiver
> ah ok, also prinzipiell wäre es aber möglich (kein optimierung, etc..)? Sowas ist moeglich und wird auch durchaus so gemacht. Allerdings bedarf es unter Umstaenden eines erfahreren C-Programmierer der mit dem Compiler und Linker gut umgehen kann. Denn wie schon angedeutet wurde, es sind ein paar Dinge zu beachten: 1. Der Stack muss gesetzt werden. 2. Andere Register muessen eventuell gesetzt werden. Ein Beispiel dafuer waeren bei M16C das einstellen der Gehaeusegroesse. 3. Die Reihenfolge kann wichtig sein. Auch optimierungslevel des Compilers. 4. Die Position im Speicher der Prozessors kann wichtig sein. 5. Es muessen Variable vom Linkerscript oder der Programmierumgebung an das C-File uebergeben werden. (z.B Position und groesse des Stacks) 6. Man verschenkt beim Aufruf von main() eine Stackposition oder muss sich halt einen Sprung einfallen lassen. > In C wird eines dieser Register als Zähl-Index, ein anderes zur > Adressierung benutzt. Sowie du das erste dieser beiden Register in > deiner Schleife überschreibst, beisst sich die Katze in den Schwanz. > > Deswegen geht das nur in Assembler. Nein. Setze einfach als erstes den Stack und verwende dann Variable die nur auf dem Stack liegen. Es geht also durchaus in C, allerdings muss man trotzdem wie ein Assemblerprogrammierer denken, sich also darueber im klaren sein was der Compiler jetzt daraus macht. Es geht aber noch perverser. :-) So sieht das ganze z.B bei Renesas aus: void start(void) { set_cpu(); // initialize mcu ram_init(); } Also ein C-funktion die ihrerseits wieder andere Funktionen aufruft und zwar ohne das es bis jetzt schon einen Stack gibt. Und so sieht dann set_cpu aus: #pragma inline set_cpu() void set_cpu(void) { isp = &_istack_top; // set interrupt stack pointer flg = 0x0080UL; // set flag register sp = &_stack_top; // set user stack pointer sb = (_UDWORD *)0x400UL; // 400H fixation (Do not change) _asm(" fset b"); sb = (_UDWORD *)0x400UL; _asm(" fclr b"); intb = (_UDWORD *)VECTOR_ADR; // set variable vector's address } Das sieht man wieso man erst ohne Stack auskommt. Ausserdem wird lustigerweise erstmal doch wieder Assembler benutzt auch wenn es im C-Code steht. Ein Problem koennte vielleicht sein das ein anderer Compiler soetwas... isp = &_istack_top; // set interrupt stack pointer ...uebersetzt indem er einen Zwischenwert auf den Stack speichert. Olaf
hansi schrieb: > wann muss ich explizit auf Assembler zurückgreifen und wann nicht? Das war die Ausgangsfrage. und ich meine immer noch, wenn man sauber in C programmiert braucht man nur aller seltenst auf Assembler zurückgreifen. Axel
Düsendieb schrieb: > Das war die Ausgangsfrage. nein war sie nicht. Es ging im C in start up routinen. Also dem teil vor Main. Und mit diesen haben 100% aller Anfängerprogramme überhaupt nicht zu tun.
Peter schrieb: > Und mit diesen haben 100% aller Anfängerprogramme überhaupt nicht > zu tun. genau, weil das der Compiler von alleine erledigt
Düsendieb schrieb: > genau, weil das der Compiler von alleine erledigt Eben nicht. Der Compiler erledigt von den Dingen, um die es hier geht, überhaupt nichts. Der weiß noch nicht einmal was davon. Für einen C-Compiler gibt überhaupt kein Leben vor main(), wohl aber für die Hardware. Alles davor macht der Startupcode, der entweder (für den Anfänger versteckt) in der toolchain mitgeliefert und in den ebenfalls mitgelieferten linkerscripts berücksichtigt wird, oder eben vom Benutzer unabhängig vom Compiler erstellt werden muß. Oliver
Assembler ist halt besser als jeder compiler, weil er direct auf hardware zugreift.Compiler übersetzt nur in den assembler, und das tut er nicht so gut.Eigentlich hängt das von dem compiler ab, was für den code er dann erschaft.Auf jeden Fal ist das nicht so effizient wie assembler, das is so als ob du durch einen übersetzer mit jemandem sprichst.
Programist schrieb: > Assembler ist halt besser als jeder compiler, weil er direct auf > hardware zugreift.Compiler übersetzt nur in den assembler, und das tut > er nicht so gut.Eigentlich hängt das von dem compiler ab, was für den > code er dann erschaft.Auf jeden Fal ist das nicht so effizient wie > assembler, das is so als ob du durch einen übersetzer mit jemandem > sprichst. Alter, bist du ein spacko...
Hallo, mal eine Frage zu dem Thema... ich habe in ARM7 startup-Routinen schon handgeschriebene memcopy Funktionen gesehen, die nur einen Speicherbereich mit 0x00, oder sonstigen "Mustern" beschrieben haben zur Initialisierung oder Stackverbrauchbestimmung. Wäre das nicht auch in C vor Runtime-Init programmierbar, solange ich keinen Stack verwende und alle Variablen vollständig in den Prozessorregistern halten kann? Gruß Benni
Benni schrieb: > solange ich > keinen Stack verwende und alle Variablen vollständig in den > Prozessorregistern halten kann? und wie willst du das machen? (aber bitte ohne irgendwelche C erweiterungen)
Naja klar, ohne C pragma oder sonstwas wird da nix laufen, aber im Embedded Bereich gehts doch sowieso fast nicht ohne ;) Ich mein viel hardwarespezifischer kanns ja nichtmehr werden. Ich finde nur es wäre leichter zu lesen und überblicken, als eigentlich simple Speicherzuweisungen nach Assembler zu packen. Benni
Benni schrieb: > ch finde > nur es wäre leichter zu lesen und überblicken, als eigentlich simple > Speicherzuweisungen nach Assembler zu packen. was ist an ASM nicht einfach zu lesen, immer schöne kurze zeilen. Es mir lieber als der C code den man hier manchmal sieht.
>> genau, weil das der Compiler von alleine erledigt...
So ist es.
Komische Diskussion, jeder brauchbare C Compiler generiert den
entsprechenden Startup Code und biete darüber hinaus auch eine
Möglichkeit diesen zu beeinflußen. Alles andere wäre eine Krücke.
Bernd N. schrieb: > Komische Diskussion, jeder brauchbare C Compiler generiert den > entsprechenden Startup Code Nein. Die meisten Compiler kümmern sich darum überhaupt nicht, sondern sie überlassen das der Bibliothek und dem Linker. Wie schon weiter oben geschrieben wurde: für den Compiler gibt es vor (und nach) main() keine Welt. Ob die Laufzeitumgebung nun ihrerseits dafür was mitbringt oder nicht, scheint von System zu System sehr unterschiedlich gehandhabt zu werden. Manche haben das komplett in die Bibliothek integriert, manche liefern einfach auch nur eine Bausteindatei mit, die man in sein Projekt mit einbindet und ggf. zusätzlich modifizieren kann.
Bernd N. schrieb: >>> genau, weil das der Compiler von alleine erledigt... > > So ist es. > > Komische Diskussion, jeder brauchbare C Compiler generiert den > entsprechenden Startup Code und biete darüber hinaus auch eine > Möglichkeit diesen zu beeinflußen. Alles andere wäre eine Krücke. Also wenn macht die IDE das eventuell. Der Compiler selber generiert garantiert kein Start Up Code.
Düsendieb schrieb: > Oliver schrieb: >> (für den Anfänger versteckt) > > sag ich doch Hier geht es anscheinend um einen ARM, und in dessen toolschains ist das schon nicht mehr versteckt, auch nicht für Anfänger. Da kannst (und musst) du als Anwender sowohl im startupcode, als auch in den linkerscripts selber rumbasteln. Es gibt dazu zwar Beispiele, aber mehr nicht. Und was z.B. auf deinem PC alles passiert, bevor da mal ein main() eines von dir geschriebenen Programms loslaufen kann, kannst du ja mal versuchen, rauszufinden. Angefangen vom ersten Start des Bios. Viel Spaß... Die Welt besteht nicht nur aus AVR's. Oliver
ok, ARM kann ich nicht mitreden und ob Compiler oder IDE, Toolchain, aknn man natürlich drüber streiten. Jedenfalls können es die Toolchains mit denen ich arbeite und jede bietet die Möglichkeit: - den StartupCode zu generieren - den StartupCode zu modifizieren - den StartupCode zu unterdrücken, respektive selbst zu schreiben Gibts nen link zu dem Armen ? :-)
Jörg Wunsch schrieb: > Ob die Laufzeitumgebung nun ihrerseits dafür was mitbringt oder nicht, > scheint von System zu System sehr unterschiedlich gehandhabt zu > werden. Es scheint nicht so, es ist so :-) > Manche haben das komplett in die Bibliothek integriert, Siehe sehr vorbildlich die WinAVR-Toolchain. Das ist tatsächlich "make & flash & go" ;-) Da weiß die Toolchain, welcher Startupcode benötigt wird. Für ARM gibt es sicher auch soetwas, wenn man eine IDE/Toolchain teuer kauft. Da würde ich das zumindest erwarten. Einen Cortex-M3 kann man in reinem(!) C starten. Habe aber noch nirgends gesehen, dass das jemand genutzt hat. Und die erste Funktion die dort anläuft ist main(), kein Assemblerbefehl vorher! Dort kann man auf den Startupcode in Assembler vollständig verzichten. Für den SPARC (LEON2, LEON3 von Gaisler) liefert Gaislers freie BCC-Toolchain (basiert auf GCC) den Startupcode auch mit. Geht wie bei WinAVR, kompilieren, linken und "run". Falls mal jemand mit SPARC spielen möchte :-) Da den Startupcode selber in Assembler schreiben ist schon nicht mehr trivial. Und reines C würde auch nicht gehen. Bis man dem die "Fenster geputzt" hat.... ;-)
Hier ein konkretes Beispiel, warum Startup-Code idR nur in Assembler geschrieben werden kann -- egal ob er mit dem Compiler mitkommt oder bei der Generierung von Libs erst mithilfe des Compilers erzeugt wird: Das ABI von avr-gcc sieht vor, daß in R1 immer der Wert 0 steht. avr-gcc geht davon aus, daß dieser Wert in R1 steht. Wie also bekommt man den Wert 0 in R1 hinein? Nach dem PowerUp stehen in den Registern R0-R31 eines AVR zufällige Werte. Eine Möglichkeit ist Assembler, klar:
1 | EOR R1, R1 ; R1 = 0 |
Wie würde man das in reinem C machen? Selbt unter Verwendung übelsten Hacks (die AVR-Register werden an die RAM-Adressen 0..31 gespiegelt) gelingt das nicht:
1 | *((volatile char*) 1) = 0; |
wird von avr-gcc nämlich übersetzt als
1 | STS 1, R1 |
denn der Compiler geht davon aus, daß 0 in R1 drinne steht. Also speichert er R1 nach 1 ab, um die 0 an Adresse 1 zu bekommen. Autsch Daher wird die Quelle des Startup-Code in Assembler geschrieben. Falls man eigene Funktionen vor main ausgeführt haben will und dazu schon eine funktionierende C-Umgebung braucht, dann kann man sich nach dem Basis-Startup-Code (also nach Setup von SP, clear-bss, copy-data, etc. ) und vor main einklinken mittels GNU-C:
1 | static void __attribute__((constructor,used)) |
2 | foo (void) |
3 | {
|
4 | // ...
|
5 | }
|
900ss D. schrieb: > Einen Cortex-M3 kann man in reinem(!) C starten. Das stimmt. Der CM3-Kern beginnt bei Adresse 0x00000000 und lädt das 32-Bit-Wort, das er dort findet, in den Stack-Pointer. Damit ist der Stack initialisiert. 900ss D. schrieb: > Und die erste Funktion die dort > anläuft ist main(), kein Assemblerbefehl vorher! Dort kann man auf den > Startupcode in Assembler vollständig verzichten. Das zweite Wort (ab Adresse 0x00000004) wird in den Instruction-Pointer geladen. Das kann theoretisch die Adresse von main () sein. In der Regel wirds aber ein paar Variablen zu initialisieren geben, und das muss laut C-Standard vor dem Eintritt in main erledigt sein. Es wird also wahrscheinlich etwas Startup-Code zwischen Reset und main geben müssen. Du hast aber Recht damit, dass dieser STartupcode nicht in Assembler geschrieben sein muss.
Auf einigen Architekturen wie z.B. PowerPC müssen Coreregister geändert werden, was nur über eigene Assemblerbefehle (mtspr, mfspr) möglich ist. Stackpointer, Exeception handling, Timebase, Watchdog, Floating Point Unit usw. Die entsprechenden Register liegen nicht im Adressraum. Abgesehen davon ist dieser Codeteil dann fix und man sucht nicht a der Stelle nach Fehlern, wenn am Compiler was nicht stimmt oder eine Eisntellung geändert wurde.
OK danke für die zahlreichen antworten. ich denke da sind ein paar gute dabei und es ist auch etwas klarer geworden. was ich aber nicht ganz verstehe ist, wieso der stack explizit in assembler initialisiert werden muss. Stack ist ein bereich im RAM. der linker erstellt doch eine memory map. vorher habe ich zB in den settings festgelegt, das addresse 0xabc - 0xaff im RAM liegen und als stack benutzt werden können. die MCU selbst braucht doch nur die start address für den stack pointer 0xaff. nun kann die mcu fröhlich pushen und poppen. was genau muss ich da in einer start up festlegen? immerhin künmmert sich doch der linker um solche sachen?? er legt ja auch das ROM fest für meinen programm code. danke p.s. es war kein ARM gemeint :) nehmen wir es eher als allgemein gültige frage. wobei der M3 ja zB dann rausfällt wie oben beschrieben.
hansi schrieb: > was ich aber nicht ganz verstehe ist, wieso der stack explizit in > assembler initialisiert werden muss. > ... > die MCU selbst > braucht doch nur die start address für den stack pointer 0xaff. Und wie kommt die Adresse in das Stackpointer-Register hinein? Genau: Durch irgendwas wie load sp, 0xaff Normalerweise hast du in C keinen direkten Zugriff auf den Stackpointer. Du müsstest Inline-Assembler verwenden, und du müsstest die Funktion so kompilieren, dass Vor- und Nachspann den Stackpointer nicht verbiegen ("attribute naked" oder was es da bei deinem Compiler so für Spezialitäten gibt). Auf jeden Fall ist das ein viel größerer Aufriss als eine simple Anweisung direkt im Startupcode.
willibald schrieb: > Normalerweise hast du in C keinen direkten Zugriff auf den Stackpointer. Hängt auch wieder von der CPU ab. Auf dem AVR ist der SP ein normales IO-Register, das man auch via C erreichen kann. Überdies kommen aktuelle AVRs mit einem Reset-Wert im SP daher, der auf das obere Ende des internen SRAM zeigt, da muss man nur noch was dran ändern, wenn man den Stack aus irgendeinem Grund woanders haben will.
also besteht das "einrichten" des stacks eingentlich nur aus dem setzten des stack pointers?
hansi schrieb: > also besteht das "einrichten" des stacks eingentlich nur aus dem setzten > des stack pointers? Ja. Manchmal wird noch der ganze Stack mit einem Bitmuster vorbelegt, was es zur Laufzeit ermöglicht, den "Hochwasserpegel" zu bestimmen oder Überläufe zu erkennen. Aber für die Funktion des Stacks an sich reicht das Setzen des Pointers. Und wie Jörg Wunsch schrieb: Manche CPUs brauchen nicht mal das.
hansi schrieb: > also besteht das "einrichten" des stacks eingentlich nur aus dem setzten > des stack pointers? Das hängt von der Architektur ab. Bei Infineon TriCore zum Beispiel muss auf dem Stack-Bereich (CSA, Context Save Area) erst eine verkettete Liste aufgebaut werden, welche einzelne Contexte enthält, und der Anfang der Liste in ein spezielles Register geschrieben werden -- einmal fürs normale Programm, einmal für die Interrupts.
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.