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....
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.
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
*((volatilechar*)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:
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.
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.