Forum: Mikrocontroller und Digitale Elektronik Was macht ein µC nach main?


von Daniel K. (danie1)


Lesenswert?

Hallo zusammen,
rein interessehalber, was macht ein IC nach der C-Methode "main"?
Ein C-Programm auf einem Betriebssystem ist dann zuende, baut ggf. noch 
ein paar Speicherkonstrukte ab und gibt den Returnwert zurück.
Was macht ein IC normalerweise?

Klar, meistens haben wir eine Endlosschleife, in der unser Code läuft, 
aber trotzdem gibt es doch ein "danach".
Und noch was: Was geschieht bei einer Division durch Null, einer 
Nullpointer-Exception oder Out-Of-Memory-Exception, die normalerweise 
(auf einem Rechner mit Betriebssystem) das Programm beendet?
Kann man dann ein Reset auslösen und von vorn beginnen? Gibt es Register 
für sowas?

Ich habe keine konkrete Hardware im Sinn, das ist eher eine theoretische 
Frage.

Danke
Daniel

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

Daniel K. schrieb:
> Hallo zusammen,
> rein interessehalber, was macht ein IC nach der C-Methode "main"?
> Ein C-Programm auf einem Betriebssystem ist dann zuende, baut ggf. noch
> ein paar Speicherkonstrukte ab und gibt den Returnwert zurück.
> Was macht ein IC normalerweise?

Das selbe sofern er das Ende von main() auch wirklich erreicht. Nicht 
selten bleiben die aber im sogenannten main-loop, einer geplanten 
Endlosschleife, stecken.

von M. K. (sylaina)


Lesenswert?

Daniel K. schrieb:
> Und noch was: Was geschieht bei einer Division durch Null, einer
> Nullpointer-Exception oder Out-Of-Memory-Exception, die normalerweise
> (auf einem Rechner mit Betriebssystem) das Programm beendet?

Zu vielfältig hier alles aufzuzählen. Im besten Fall bleibt der IC 
einfach hängen und macht nicht weiter, im schlimmsten Fall rechnet er 
mit falschen Werten weiter. Es ist eigentlich immer besser, die Software 
vor die Wand fahren zu lassen als das Auto, dass der IC steuert. Deshalb 
muss man sich auch immer gute Testroutinen überlegen und jede Menge 
Fehler berücksichtigen ;)

von Sebastian R. (sebastian_r569)


Lesenswert?

Das kommt wohl immer auf die Architektur an.
Wenn keine Endlosschleife läuft, dann ist er halt einfach fertig und der 
Programcounter hat fertig.
Die ganzen Registerinhalte bleiben dann halt so und gut ist.

Zumindest der AVR hat z.B. keine Probleme mit Division durch 0. Er 
landet dann in keiner internen Endlosschleife oder so, sondern die 
Struktur fängt das ab und es kommt nur "not a number" als Ergebnis 
zurück.

Für Fälle, in denen der Controller in einer unbeabsichtigten Schleife 
hängen bleiben kann, gibt es den Watchdog. Wenn der nicht regelmäßig 
getreten wird, resettet er den Controller.

von my2ct (Gast)


Lesenswert?

Daniel K. schrieb:
> Was macht ein IC normalerweise?

Der µC bekommt sein Programm vom Compiler/Linker.
Guck dir im LST-File an, was der für Maschinencode erzeugt.

von NichtWichtig (Gast)


Lesenswert?

einfache Antwort: RTFM

:)

Beitrag #5700772 wurde vom Autor gelöscht.
von Erich (Gast)


Lesenswert?

Daniel K. schrieb:
> Klar, meistens haben wir eine Endlosschleife, in der unser Code läuft,
> aber trotzdem gibt es doch ein "danach".
> Und noch was: Was geschieht bei einer Division durch Null, einer
> Nullpointer-Exception oder Out-Of-Memory-Exception, die normalerweise
> (auf einem Rechner mit Betriebssystem) das Programm beendet?
> Kann man dann ein Reset auslösen und von vorn beginnen? Gibt es Register
> für sowas?

Alle diese Betriebszustände sollten erkannt, ggf. ein zugehöriger 
Fehlerstatus gespeichert werden.
Oft landet man dann in einer Endlosschleife, aus welcher entweder ein 
"Watchdog" nach definierter Zeit einen "Reset" auslöst. Oder es wird auf 
manuelles Abschalten der Versorgungsspannung gewartet (die ggf. auch 
ausgelöst wurde).
Nach Wiederanlauf (über Reset oder nächstem Power-On) kann ggf. der 
gespeicherte Fehlerstatus ausgewertet und daraufhin bestimmte 
Zusatzaktivitäten gestartet werden.
Manche uC haben Sonderregister, die diese Funktionen unterstützen.
Die allermeisten haben den "Watchdog" konfigurierbar integriert.

Allereinfachster Fall ist: Warten auf Watchdog-Reset, Neuanlauf.

Gruss

von Peter D. (peda)


Lesenswert?

my2ct schrieb:
> Guck dir im LST-File an

Z.B. AVR-GCC:
1
  ba:  04 d0         rcall  .+8        ; 0xc4 <main>
2
  bc:  06 c0         rjmp  .+12       ; 0xca <_exit>
3
4
000000c4 <main>:
5
int main(void)
6
{
7
  while (false)
8
  {
9
  }
10
}
11
  c4:  80 e0         ldi  r24, 0x00  ; 0
12
  c6:  90 e0         ldi  r25, 0x00  ; 0
13
  c8:  08 95         ret
14
15
000000ca <_exit>:
16
  ca:  f8 94         cli
17
18
000000cc <__stop_program>:
19
  cc:  ff cf         rjmp  .-2        ; 0xcc <__stop_program>

von Bimbo. (Gast)


Lesenswert?

Was soll er schon machen? Weiterhin das hinterlegte Programm ausführen! 
Nach Main können lauter NOPs stehen. Dann macht der µC NOP, NOP, NOP, 
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 
NOP, NOP, NOP, NOP.................
...
...
...
... bis das Ende vom Flash erreicht ist und macht vorne beim Flash 
weiter. Dort steht dann in der Regel dein Programm oder Bootloader.

Wenn du deinen Flash vor dem Programmieren nicht gelöscht hast, kann 
nach dem Main wirres Zeug stehen, dann wird halt irgendetwas gemacht - 
aber eben das was da steht!

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Daniel K. schrieb:
> Was macht ein IC normalerweise?
Das, was im Datenblatt steht.

Und wenn das IC ein µC ist, dann das, was im Programm steht.

"Üblicherweise" bleibt ein µC nach "Beendigung" von main() aber wie 
beschrieben in einer Endlosschleife stehen. Genaueres steht im 
Startup-Code.

Daniel K. schrieb:
> Klar, meistens haben wir eine Endlosschleife, in der unser Code läuft,
> aber trotzdem gibt es doch ein "danach".
Nein. In einem halbwegs brauchbar designten µC Programm nicht.

> Und noch was: Was geschieht bei einer Division durch Null, einer
> Nullpointer-Exception oder Out-Of-Memory-Exception, die normalerweise
> (auf einem Rechner mit Betriebssystem) das Programm beendet?
Das kommt auf den Prozessor und den dazugehörigen Code an.

> Kann man dann ein Reset auslösen und von vorn beginnen?
Stichwort dazu: Watchdog

von Spaßvogel (Gast)


Lesenswert?

Bimbo. schrieb:
> Was soll er schon machen? Weiterhin das hinterlegte Programm ausführen!
> Nach Main können lauter NOPs stehen. Dann macht der µC NOP, NOP, NOP,
> NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
> NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
> NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
> NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
> NOP, NOP, NOP, NOP

Ach so produziert Lego die Noppen für die Bausteine. Jetzt wird mir 
eniges klar.

von Axel S. (a-za-z0-9)


Lesenswert?

Daniel K. schrieb:
> rein interessehalber, was macht ein IC nach der C-Methode "main"?

Ein IC? Die meisten IC wissen nichts von C oder von einer Funktion 
namens main(). Wenn du einen µC meinst, dann schreib das auch.

> Ein C-Programm auf einem Betriebssystem ist dann zuende, baut ggf. noch
> ein paar Speicherkonstrukte ab und gibt den Returnwert zurück.
> Was macht ein IC normalerweise?

Da es nichts gibt, was mit dem Returnwert irgend etwas anfangen könnten 
(im Zweifelsfall gibt es keinerlei Ausgabemöglichkeiten, man denke an 
den µC in der Kaffeemaschine oder im Motorsteuergerät des Autos) ist bei 
einem µC der Returnwert von main() vollkommen nutzlos. Die meisten 
C-Compiler erzeugen Code, der main() aufruft und falls der Aufruf 
zurückkommt, in eine Endlosschleife geht.

Beim arm-gcc ist der Startupcode typisch Teil des Projekts. Der könnte 
dann z.B. so aussehen:
1
void init()
2
{
3
    // do global/static data initialization
4
    unsigned char *src;
5
    unsigned char *dest;
6
    unsigned len;
7
    src= &INIT_DATA_VALUES;
8
    dest= &INIT_DATA_START;
9
    len= &INIT_DATA_END-&INIT_DATA_START;
10
    while (len--)
11
        *dest++ = *src++;
12
13
    // zero out the uninitialized global/static variables
14
    dest = &BSS_START;
15
    len = &BSS_END - &BSS_START;
16
    while (len--)
17
        *dest++=0;
18
19
    main();
20
21
    while(1);
22
}

Hint: weit öfter sieht man diesen Code in Assembler. Das hier habe ich 
mal irgendwo im Web für "erste Schritte mit dem STM32F030" gefunden.


> Und noch was: Was geschieht bei einer Division durch Null, einer
> Nullpointer-Exception oder Out-Of-Memory-Exception, die normalerweise
> (auf einem Rechner mit Betriebssystem) das Programm beendet?

Falls der in Frage stehende µC so etwas hat, lösen die eine Exception 
aus. Eine Exception ist im Prinzip wie ein Interrupt, mit einem eigenen 
Eintrag in der Vektortabelle. Man kann Exceptionhandler im Programm 
vorsehen, wenn man keine hat, geht das Programm typisch ebenfalls in 
eine Endlosschleife. Motto: besser gar nichts machen, als etwas 
falsches.

von Edi R. (edi_r)


Lesenswert?

Lothar M. schrieb:
> Und wenn das IC ein µC ist,

Axel S. schrieb:
> Ein IC? Die meisten IC wissen nichts von C oder von einer Funktion
> namens main().

Ich vermute, der TO meint nicht "das IC" (Integrated Circuit), sondern 
"der IC" (Instruction Counter).

von Bimbo. (Gast)


Lesenswert?

Lothar M. schrieb:
> "Üblicherweise" bleibt ein µC nach "Beendigung" von main() aber wie
> beschrieben in einer Endlosschleife stehen. Genaueres steht im
> Startup-Code.

Das muss aber vom Compiler so hinterlegt werden, dass nach Main eine 
Endlosschleife angelegt wird. Woher soll der Mikrocontroller denn 
wissen, dass hier das beabsichtige Programm endet? Man muss diesem das 
irgendwie mitteilen. Ein spezifisches Register gibts hierfür doch nicht.

Wenn du in Assembler schreibst (Gott habe den Programmierer seelig), 
wird nach deinem eigenen "Main" der Controller idR wieder von vorne 
anfangen, wenn nur NOPs im restlichen Flash liegen.

von Axel S. (a-za-z0-9)


Lesenswert?

Axel S. schrieb:
> Hint: weit öfter sieht man diesen Code in Assembler. Das hier habe ich
> mal irgendwo im Web für "erste Schritte mit dem STM32F030" gefunden.

Jetzt habe ich es wiedergefunden:

http://eleceng.dit.ie/frank/arm/BareMetalSTM32F0Discovery/blinky.html

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Edi R. schrieb:
> Ich vermute, der TO meint nicht "das IC" (Integrated Circuit), sondern
> "der IC" (Instruction Counter).
Dieser Begriff ist für einen PC (Programm Counter) aber äuserst 
unüblich. Aber weil nach dem Ende von main() sinnvollerweise eine 
Endlosschleife kommt, springt der IC/PC immer um ein paar Bit hin und 
her...

von Daniel K. (danie1)


Lesenswert?

Ich meinte einen µC, keinen IC, danke für den Hinweis. Ich habe den 
Betreff angepasst.

von Axel S. (a-za-z0-9)


Lesenswert?

Bimbo. schrieb:
> Das muss aber vom Compiler so hinterlegt werden, dass nach Main eine
> Endlosschleife angelegt wird. Woher soll der Mikrocontroller denn
> wissen, dass hier das beabsichtige Programm endet?

Für den µC endet das Programm gar nicht. Das ist ja gerade der Punkt. 
Der Speicher endet irgendwann mal, das wars aber auch. Und selbst dann 
springt der PC einfach wieder auf den Anfang des Speichers.

Für die Endlosschleife ist bei einem C-Programm der Startupcode 
verantwortlich. Dessen Quelle hängt von der Toolchain und den 
verwendeten Libraries ab. Bei AVR ist er Teil der avr-libc. Bei Cortex-M 
ist er entweder Teil des Programms oder kommt von HAL, SPL oder 
libopenCM3. Bei manchen Toolchains mag sogar der Compiler das erzeugen.

Für Assembler gibt es keinen extra Startupcode. Da muß der Programmierer 
selber zusehen, wie sein Programm an die richtige Stelle kommt, so daß 
der µC das Programm nach dem Reset auch ausführt.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Axel S. schrieb:
> Für die Endlosschleife ist bei einem C-Programm der Startupcode
> verantwortlich.

Man sollte auch bedenken, dass Timer und Interrupts nach einem return 
aus main() durchaus weiterlaufen. Diese werden nämlich in der Regel 
nicht vom Runttime-System wieder deaktiviert.

Wenn das Programm zum Beispiel nur aus ISRs besteht, dann läuft es gar 
bis zu 100% korrekt weiter. Sonst halt nur zu einem Teil davon (0% - 
100%).

von Peter D. (peda)


Lesenswert?

Frank M. schrieb:
> Wenn das Programm zum Beispiel nur aus ISRs besteht, dann läuft es gar
> bis zu 100% korrekt weiter.

Beim AVR-GCC nicht, der macht ein CLI nach Main (siehe oben).
Ein PWM-Ausgang bleibt aber aktiv.

von Einer K. (Gast)


Lesenswert?

Frank M. schrieb:
> Man sollte auch bedenken, dass Timer und Interrupts nach einem return
> aus main() durchaus weiterlaufen. Diese werden nämlich in der Regel
> nicht vom Runttime-System wieder deaktiviert.

Bei der Kombination AVR + GCC eher nicht.

Siehe dazu:
Peter D. schrieb:
> Z.B. AVR-GCC:  ba:  04 d0         rcall  .+8        ; 0xc4 <main>
>   bc:  06 c0         rjmp  .+12       ; 0xca <_exit>
>
> 000000c4 <main>:
> int main(void)
> {
>   while (false)
>   {
>   }
> }
>   c4:  80 e0         ldi  r24, 0x00  ; 0
>   c6:  90 e0         ldi  r25, 0x00  ; 0
>   c8:  08 95         ret
>
> 000000ca <_exit>:
>   ca:  f8 94         cli
>
> 000000cc <__stop_program>:
>   cc:  ff cf         rjmp  .-2        ; 0xcc <__stop_program>

von Marten Morten (Gast)


Lesenswert?

Ich sage es mal so herum, vernünftige Programmierer kehren nicht aus 
main() zurück.

Macht man das, verliert man die Kontrolle über den µC. Es passiert das, 
was im Startup-Code nach main() steht. Was dort steht ist nur für den 
Notfall gedacht, damit der µC bei ungewolltem Return aus main() nicht 
komplett Amok läuft.

Man kann sich zwar ansehen was dort im Code steht, doch warum drauf 
verlassen? Bei der nächsten Compiler-Installation kann sich das heimlich 
geändert haben. Da programmiert man lieber im eigenen Code was man haben 
möchte wenn der µC nichts mehr zu tun hat.

von Stefan F. (Gast)


Lesenswert?

>was macht ein IC nach der C-Methode "main"?

Die Computer von Lego Mindstorms kehren nach main() wieder zum Hauptmenü 
zurück. Exceptions die nicht gefangen werden, werden auf dem Display 
angezeigt. nach einem Tastendruck erscheint wieder das Hauptmenü.

Du siehst, dass es keine allgemein gültige Antwort gibt.

Ein vernünftiger Computer schaltet sich aus, wenn er gar kein Programm 
mehr auszuführen hat.

von Bernd K. (prof7bit)


Lesenswert?

Marten Morten schrieb:
> Bei der nächsten Compiler-Installation kann sich das heimlich
> geändert haben.

Vernünftigerweise ist der Startupcode Teil des Projekts, dann hat man 
den selbst unter Kontrolle. Zumindest bei µC-Projekten wo man sich auf 
irgendwelche Tricksereien oder spezielle Verhaltensweisen des Startups 
verlassen will. Genauso wie das zugehörige Linkerscript und andere 
hardwarespezifische Sachen. Ich überlass da nichts dem Zufall.

von Markus F. (mfro)


Lesenswert?

Was macht ein Auto auf der Autobahn, wenn man das Lenkrad losläßt und 
trotzdem den Fuß auf dem Gas lässt?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> Zumindest bei µC-Projekten wo man sich auf irgendwelche Tricksereien
> oder spezielle Verhaltensweisen des Startups verlassen will. Genauso wie
> das zugehörige Linkerscript

Bei ARMs ist das gang und gäbe, weil da jeder sein Süppchen kocht (und 
auch genügend halbgewalkte Dinge durchs Netz geistern).

Beim AVR braucht man das nicht, da gibt es einen Linkerscript mit 
passendem Startupcode „aus der Dose raus“, wobei auch genügend Spielraum 
für eigene Modifikationen vorgesehen ist (".noinit"-Variablen, 
".initN"-Sektionen).

: Bearbeitet durch Moderator
von Walter T. (nicolas)


Lesenswert?

Sebastian R. schrieb:
> umindest der AVR hat z.B. keine Probleme mit Division durch 0. Er
> landet dann in keiner internen Endlosschleife oder so, sondern die
> Struktur fängt das ab und es kommt nur "not a number" als Ergebnis
> zurück.

Wo steht das? Und wie sieht NaN bei Int aus?

von Stefan F. (Gast)


Lesenswert?

Walter T. schrieb:
> Sebastian R. schrieb:
>> umindest der AVR hat z.B. keine Probleme mit Division durch 0. Er
>> landet dann in keiner internen Endlosschleife oder so, sondern die
>> Struktur fängt das ab und es kommt nur "not a number" als Ergebnis
>> zurück.
>
> Wo steht das? Und wie sieht NaN bei Int aus?

AVR haben einfach keine speziellen Interrupts/Event für solche Fehler.

von Peter D. (peda)


Lesenswert?

Bernd K. schrieb:
> Vernünftigerweise ist der Startupcode Teil des Projekts

Man muß nicht absolut jeden Schrunz selber schreiben. Ich traue den 
Compilerbauern schon zu, daß sie sich dabei was denken.
Wenn ich z.B. beim AVR das externe RAM-Interface einschalten muß, dann 
plaziere ich das einfach in die .init3-Section. Den Startup-Code fasse 
ich nicht an.

von Walter T. (nicolas)


Lesenswert?

Mein Wissen ist das, dass der AVR überhaupt keine Division kann, sondern 
eine Divisionsroutine aus der C-Library nutzt. Und die beim GCC durchaus 
eine Endlosschleife verursachen kann.

Aber ich bin nur Laie und hätte nichts dagegen, wenn ich mich zu meinen 
Ungusten geirrt hätte.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Also bei meinen Programmen würden nach dem Programmcode im Flash 
definierte Konstanten ausgeführt. Nicht so eine gute Idee, gibt mit 
Sicherheit einen Crash des Controllers, mit Pech durch sinnfreie 
Umprogrammierung von Ports einen Hardwareschaden.

AVRs machen meistens einen Reset wenn man den Befehlszeiger ins Nirvana 
schickt. Entweder direkt ausgelöst durch den CPU-Kern bei illegalen 
Befehlscodes oder weil der Zeiger überläuft und am Anfang des Flash 
wieder beim Reset-Vektor landet.

von x^2 (Gast)


Lesenswert?

Daniel K. schrieb:
> Hallo zusammen,
> rein interessehalber, was macht ein IC nach der C-Methode "main"?
> Ein C-Programm auf einem Betriebssystem ist dann zuende, baut ggf. noch
> ein paar Speicherkonstrukte ab und gibt den Returnwert zurück.
> Was macht ein IC normalerweise?
>
> Klar, meistens haben wir eine Endlosschleife, in der unser Code läuft,
> aber trotzdem gibt es doch ein "danach".
> Und noch was: Was geschieht bei einer Division durch Null, einer
> Nullpointer-Exception oder Out-Of-Memory-Exception, die normalerweise
> (auf einem Rechner mit Betriebssystem) das Programm beendet?
> Kann man dann ein Reset auslösen und von vorn beginnen? Gibt es Register
> für sowas?
>
> Ich habe keine konkrete Hardware im Sinn, das ist eher eine theoretische
> Frage.

Alles das kann man nicht allgemein beantworten. Es hängt vom Compiler, 
dem Startup Code und Runtime sowie natürlich der Architektur ab. Es 
hängt übrigens auch davon ab WIE main() beendet wird. Neben dem normalen 
Weg via return kann in C++ eine nicht gefangene Exception dazu führen 
(siehe set_terminate).

Auch auf dem PC passieren noch sehr viele Dinge nach main(). Es werden 
die per atexit() registrierten Funktionen aufgerufen. Dann werden 
Destruktoren globaler Objekte aufgerufen, auch in C gibt es mehrere 
vergleichbare interne Mechanismen die kaum jemand kennt. Auf dem PC 
würden auch die Standard Streams geflushed und geschlossen. Als letztes 
wird in der Regel abort() aufgerufen. Die Lektüre der libc ist hier sehr 
hilfreich.

Was macht auf einem Embedded Target Sinn? Wir haben z.B. keine 
Klicki-Bunti-App in der Cloud sondern unseren Startup Code selbst 
geschrieben (d.h. compilieren mit GCC und "-nostartfiles" und linken 
unsere Startup Files und Runtime statt dessen). Wir können deshalb nach 
main() eigentlich tun und lassen was wir wollen. Endlosschleife oder 
Tetris starten, alles möglich. Man kann sich überlegen dass ein Reset 
(spezielles Register, bei ARM Cores z.B. SCB_AIRCR.SYSRESETREQ, od. 
Reset durch Watchdog Timeout) oft geeigneter ist als eine Endlosschleife 
und davor das atexit(), Destruktor Handling usw. setzen.


Division durch Null. Bei Division durch Null muss man meist zunächst 
unterscheiden, ob hier CPU oder FPU ursächlich ist. Meistens 
unterscheidet sich das Verhalten danach. Also ob beispielsweise ein int 
oder ein float verwendet wird. Dadurch bedingt hat der Compiler entweder 
CPU oder FPU Instruktionen erzeugt. Eine Division durch Null in der CPU 
führt in der Regel zu einer Interrupt/Exception, eine Division durch 
Null in der FPU kann zu einer Interrupt/Exception führen. Wir arbeiten 
z.B. mit einer MCU wo letzteres manuell aktiviert werden muss. Tut man 
dies nicht, entsteht nur NaN als Ergebnis.

Null-Pointer-Exception. Ist nichts anderes als ein Lesen/Schreiben von 
Adresse 0. Das kann je nach Target problemlos funktionieren - wenn dort 
RAM Speicher liegt. Liegt dort nur ROM würde zwar Lesen gehen aber nicht 
schreiben. Liegt dort kein Speicher geht beides nicht. In der Regel wird 
man dann beim Lesen/Schreiben eine Execption/Interrupt vom Speicherbus 
erhalten. NULL hat im Gegensatz zu einer beliebigen Adresse ohne 
Memory-Mapping lediglich per Definition ein spezielles Verhalten weil es 
von vielen C Funktionen entsprechend interpretiert wird.


Ich fürchte die Antworten auf alle deine Fragen würden ein Buch füllen.

von Rainer V. (a_zip)


Lesenswert?

Also, obwohl ich kein "C'ler" bin, würde ich erwarten, dass wenn "Main" 
abgearbeitet ist, nichts mehr passiert! Es sei denn, es steht da noch 
weiterer Code. Deshalb ist die Frage für mich sinnlos!!
Gruß Rainer

Beitrag #5701386 wurde von einem Moderator gelöscht.
von Willi (Gast)


Lesenswert?

Damit das nicht im Dunklen bleibt:

Was macht Arduino nach main() ?
Er schaltet sämtliche Interrupts ab,
uns geht in eine Endlosschleife.
1
 cli
2
 rjmp  .-2

MfG Willi

von Einer K. (Gast)


Lesenswert?

Rainer V. schrieb:
> würde ich erwarten, dass wenn "Main"
> abgearbeitet ist, nichts mehr passiert!

Erwartungshaltungen möchten enttäuscht werden.
Es ist kein Fehler diese Situation zu untersuchen.

z.B. die exit() Anweisung führt genau zu dem Punkt

Eigentlich oder üblicher weise werden C Programme so 
gebaut/konfiguriert, dass die Kontrolle an das BS übergeben wird.
Nur, auf den kleinen µC gibt es sowas wie ein BS nicht.

Wie so oft in C:
Letztendlich ist der Programmierer für das Verhalten zuständig.
Oft haben die Compiler/Lib Entwickler vorgesorgt.
Aber eben nicht immer.

Willi schrieb:
> Damit das nicht im Dunklen bleibt:
>
> Was macht Arduino nach main() ?
> Er schaltet sämtliche Interrupts ab,
> uns geht in eine Endlosschleife.
Das wissen wir schon.
Denn das ist das vorbereitete AVR-GCC Standard Verhalten.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ben B. schrieb:
> Entweder direkt ausgelöst durch den CPU-Kern bei illegalen Befehlscodes

Es gibt bei einem AVR nichts wie einen Trap, der sowas erreichen könnte.

Alle Befehle lassen sich irgendwie abarbeiten, schlimmstenfalls halt 
als NOP (bspw. beim 0xFFFF des gelöschten Flashs).

Arduino Fanboy D. schrieb:
> Das wissen wir schon.
> Denn das ist das vorbereitete AVR-GCC Standard Verhalten.

Genauer gesagt: es ist die Funktion _exit() aus der Bibliothek. Kann man 
auch durch was eigenes ersetzen.

Der Startup-Code ruft ganz standardkonform das Äquivalent eines 
"exit(main());" auf. Prinzipiell könnte eine eigene _exit()-Funktion 
damit auch den Rückgabewert von main() auswerten, der in Register 
r24:r25 übergeben wird.

: Bearbeitet durch Moderator
von Joachim B. (jar)


Lesenswert?

Was macht ein µC nach main?

Feierabend und am Freitag ins WE gehen?

von Rainer V. (a_zip)


Lesenswert?

Jemand schrieb im Beitrag #5701386:
> Die Realität richtet sich aber nicht nach deinen lustigen Erwartungen

Jau, habe ich verstanden...die üblichen "Blödsinnskommentare"
Nach "Main" ist nix...und wenn doch, dann wars kein Main...oder eben 
weiterer Code. Was gibts da denn zu rätseln???
 "If I where a bird, I would fly to you!"
Gruß Rainer

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rainer V. schrieb:
> Nach "Main" ist nix.

Üblicherweise (je nach Linker) steht nach dem Code von main allerlei 
Bibliothekskrempel, den der Linker noch dazu gepackt hat.  Wo auch sonst 
sollte der hinkommen?

Aber main ist eine Funktion und hat daher an ihrem Ende ein return.

: Bearbeitet durch Moderator
von Axel S. (a-za-z0-9)


Lesenswert?

Rainer V. schrieb:
> Also, obwohl ich kein "C'ler" bin, würde ich erwarten, dass wenn "Main"
> abgearbeitet ist, nichts mehr passiert! Es sei denn, es steht da noch
> weiterer Code. Deshalb ist die Frage für mich sinnlos!!

Das ist sie keineswegs.

Für einen Assemblerprogrammierer (und ich weiß genau, wer sich jetzt 
angesprochen fühlen wird) ist es normal, jeden Aspekt der Programm- 
ausführung unter Kontrolle zu haben. Nicht nur, daß er sein Programm an 
eine feste Adresse im Speicher legt, er kann (und muß!) auch sämtliche 
Vektortabellen mit korrekten, absoluten(!) Adressen füllen, damit der 
Prozessor nach dem Reset, einem Interrupt, einer Exception ... genau zur 
richtigen Stelle springt.

Für diesen Assemblerprogrammierer ist auch klar, daß (s)ein Programm 
kein Ende im herkömmlichen Sinn hat. Ein Prozessor kann nicht "nichts" 
machen. Er kann eine sleep Instruktion ausführen. Oder er kann eine 
Endlosschleife "ab"arbeiten. Aber es gibt nichts "vor" der Ausführung 
seines Programms und nichts "danach". Sobald und solange die 
Betriebsspannung anliegt, arbeitet der Prozessor (s)ein Programm ab. 
Alles ist Programm.

Für einen C-Programmierer sieht die Welt anders aus. Für ihn findet der 
Urknall - der Anfang der Zeit - mit den Eintritt in die main() Funktion 
statt. Was davor kommt, oder danach - davon weiß er nichts. So wie für 
die Physik die Zeit mit dem Urknall beginnt, so beginnt für den 
C-Programmierer  die Zeit mit der ersten Anweisung in main(). 
Andererseits weiß er aber auch, daß davor noch etwas gewesen sein muß. 
Es gibt globale Variablen, die zu diesem Zeitpunkt bereits existieren. 
Die bereits mit ihren Defaultwerten initialisiert worden sind. Es muß 
also vor dem Anfang der Zeit schon etwas gewesen sein.

Und das ist die Laufzeitumgebung von C. Der Teil, der vor main() 
abläuft, heißt Startup-Code. Und wenn etwas vor main() ist, dann ist 
sicher auch etwas danach. main() ist wie die Scheibe Salami in einem 
Sandwich. Allerdings kann man annehmen, daß die Leute, die den 
Startup-Code hinbekommen haben, es auch hinbekommen, den Code nach 
main() so zu gestalten, daß er etwas sinnvolles tut. Und in 99.9% der 
Fälle ist das sinnvollste, nach der Auführung von main() einfach gar 
nichts zu tun. Eine Endlosschleife ist die kanonische Lösung. Und 
deswegen findet man sie auch normalerweise an dieser Stelle.

von Willi S. (ws1955)


Lesenswert?

Unglaublich, was man hier noch alles "lernen" kann (...)

MAIN ist der Startpunkt der Hauptschleife, Punkt!

von Mike (Gast)


Lesenswert?

Das Verhalten bei Controllern ohne Betriebssystem hängt vom Compiler ab: 
Bei den 8bit-PICs ließ der alte C18 das Programm auf der Stelle hüpfen, 
indem er ein
1
 while(1);
 einfügte.

Der neuere XC8 erzeugt ein Reset und startet das Programm neu.

Allgemein gilt jedoch: Wenn das Programm aus der main()-Routine 
herausläuft, hat der Programmierer einen Fehler gemacht.

Beitrag #5701585 wurde von einem Moderator gelöscht.
von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Zwischenzeitlich ist die Originalfrage verschwunden:

hmmmm schrieb im Beitrag #5701585:
> Kann mal jemand probieren, was passiert, wenn die CPU am Ende ankommt?
> Also quasi main() und die while(1) raushauen.
> Bleibt der PC dann wirklich stehen?

Was soll schon passieren? Die CPU läuft erst einmal weiter. Sie ließt 
aus dem Flash die nächsten Werte auf die der PC (Program Counter) zeigt, 
interpretiert diese als Befehl und führt den Befehl aus.

Wenn an der Stelle zufällig Daten stehen interpretierst sie die Daten 
fälschlicherweise als Befehle.

Wenn an der Stelle zufällig Code, z.B. einer Funktion steht, arbeitet 
sie das Stück Programm fälschlicherweise ab.

Das macht die CPU so lange, bis eines der folgenden Dinge passiert:

* Sie stößt zufällig auf einen Wert im Speicher, den sie als Befehl zum 
Anhalten der CPU in irgendeiner Form interpretiert.

Manche CPUs haben solche Befehle. Daher sind Behauptungen wie eine CPU 
läuft immer endlos falsch.

* Sie stößt zufällig auf einen Wert im Speicher, den sie als Befehl 
interpretiert, der aber kein gültiger Befehl ist und stürzt dabei ab, 
hängt sich intern auf. Sie verbleibt dann in einem undefinierten 
Zustand.

Das sollte bei keiner CPU passieren, ist bei moderneren CPUs auch 
selten, aber prinzipiell nicht unmöglich.

* Sie stoßt auf einen Wert im Speicher der einen CPU-Reset auslöst. Das 
kann ein Reset-Befehl sein oder ein Befehl der einen Fehler auslöst, den 
die CPU mit einem Reset behandelt.

Das Programm beginnt wieder zu laufen. Wenn die CPU die Eigenschaft hat, 
bei jeder Art von Reset alle Peripherie komplett neu zu initialisieren, 
dann läuft das Programm eventuell sogar formal richtig.

Ein richtig laufendes Programm heißt nicht notwendigerweise, dass es gut 
ist, dass das Programm anläuft. Zum Beispiel wenn es eine Maschine 
steuert die besser aus sein sollte.

* Externe Signale, wie ein Watchdog-Interrupt, setzen die CPU zurück.

* Externe Signale, wie ein Interrupt, führen zur zwischenzeitlichen 
Ausführung eines Interrupt-Handlers, danach springt die CPU wieder 
zurück von wo sie in den Interrupt eingesprungen ist und macht weiter 
Blödsinn.

Im Interrupt-Händler kann sie auch Blödsinn machen, da eventuell globale 
Variablen, die der Händler braucht, mit falschen Werten belegt sind oder 
der Status von I/O-Registern nicht mehr stimmt.

Passiert nichts was die CPU wie oben beschrieben unterbricht, so läuft 
sie endlos weiter und arbeitet die zufällige gefundenen Informationen im 
Speicher als Befehle ab. Dabei besteht eine gewisse Wahrscheinlichkeit, 
dass sie irgendwo in eine Endlosschleife gerät.

Wie sieht es in der Praxis aus, wenn ein Steuersystem wild läuft und 
Blödsinn macht? Auf diesem Flug 
https://www.youtube.com/watch?v=BoZRhk0W1j4 wurden die Passagiere fast 
zwei Stunden mit dieser Show gequält. Ein Passagier zeigt wie seine 
Herzfrequenz steigt. Gut dass es nur das Licht- und Soundsystem war. 
Wenn es ein System zur Steuerung des Flugzeugs gewesen wäre -> ???

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

Jörg W. schrieb:
> Aber main ist eine Funktion und hat daher an ihrem Ende ein return.

Öhm...nö. Eine
1
void main(void){
2
...
3
}

hat z.B. an ihrem Ende kein return und wenn doch sollte jeder halbwegs 
gescheite Compiler der letzten 30 Jahre eine Schimpftriade auf den 
Programmierer loslassen.

Ich finde ja, eines zeigt dieser Thread ganz gut: Einigen ist 
offensichtlich nicht klar, wie der Programcounter funktioniert.

von Markus F. (mfro)


Lesenswert?

M. K. schrieb:
> Jörg W. schrieb:
>> Aber main ist eine Funktion und hat daher an ihrem Ende ein return.
>
> Öhm...nö. Eine
>
>
1
void main(void){
2
> ...
3
> }
4
>
>
> hat z.B. an ihrem Ende kein return und wenn doch sollte jeder halbwegs
> gescheite Compiler der letzten 30 Jahre eine Schimpftriade auf den
> Programmierer loslassen.
> ...

Öhm, doch.

Das haben wir vor Kurzem geklärt.
Bei einem standardkonformen Compiler/Laufzeitumgebung steht am Ende von 
main() ein return 0;

Auch wenn der Programmierer das gar nicht hingeschrieben hat.

: Bearbeitet durch User
von Tim (Gast)


Lesenswert?

M. K. schrieb:
> Öhm...nö. Eine
> void main(void){
> ...
> }
>
> hat z.B. an ihrem Ende kein return
Dann schau mal in das Listfile mit der Assemblerausgabe.

Im Initcode steht sowas:
1
98    004F              
2
99    004F  CD 00 00            call    _main   ;Call user program
3
100   0052

Und Deine main wird so übersetzt:
1
20    0000              ._main
2
21    0000  C9                  ret
3
22    0001

von Soul E. (Gast)


Lesenswert?

Rainer V. schrieb:

> Also, obwohl ich kein "C'ler" bin, würde ich erwarten, dass wenn "Main"
> abgearbeitet ist, nichts mehr passiert! Es sei denn, es steht da noch
> weiterer Code. Deshalb ist die Frage für mich sinnlos!!

Was genau ist Deiner Meinung nach "nichts"?

An den Datenleitungen liegen elektrische Spannungen, die logische Pegel 
repräsentieren. Diese können Null oder Eins sein, andere Zustände gibt 
es nicht. Jeder Null-und-Eins-Kombination ist ein (gültiger oder 
ungültiger) Maschinenbefehl eindeutig zugeordnet. Und exakt dieser wird 
ausgeführt.

von W.S. (Gast)


Lesenswert?

Daniel K. schrieb:
> Ich habe keine konkrete Hardware im Sinn, das ist eher eine theoretische
> Frage.

Dann lasse solche Fragen ganz einfach bleiben, denn es gibt dafür keine 
generellen Antworten, auch keine theoretischen. Stattdessen hängt es vom 
jeweiligen Startupcode ab, was da anschließend passiert oder nicht.


Markus F. schrieb:
> Das haben wir vor Kurzem geklärt.
> Bei einem standardkonformen Compiler/Laufzeitumgebung steht am Ende von
> main() ein return 0;

Auch du theoretisierts bloß herum. Ich hab schon einige Startupcodes 
gesehen, wo ein schlichtes
 LDR     R0, =main
 BX      R0
drin stand. Bei sowas ist es schlichtweg VERBOTEN, aus main 
zurückzukehren, da ist auch ein return 0 genauso verboten, denn es gibt 
keine Rückkehr. Von wegen über Standardkonformität zu theoretisieren. 
Und wenn der Compiler meckert, weil ihm bei einem
.. main (..)
{
...
while(...)
{ ...}
}
das return fehlt, dann macht man das eben mit einem harten goto
.. main (..)
{ ...
immerzu:
  ...
goto immerzu;
}

Bei sowas kapiert auch der dümmste Compiler, daß es da niemals 
zurückgeht und er sich das return 0 schenken kann.

W.S.

von Einer K. (Gast)


Lesenswert?

Markus F. schrieb:
> Öhm, doch.
>
> Das haben wir vor Kurzem geklärt.
> Bei einem standardkonformen Compiler/Laufzeitumgebung steht am Ende von
> main() ein return 0;
>
> Auch wenn der Programmierer das gar nicht hingeschrieben hat.

Nicht nur bei main()
(gcc)
1
int funktionOhneReturn()
2
{
3
  
4
}

Wirft eine Warnung:
>  warning: no return statement in function returning non-void

Und wenn man den Returnwert nutzt/auswertet, wird man eine Null 
erhalten. Zuverlässig.

Wie/Ob man dieses "Feature" sinnvoll nutzen könnte, mag mir noch nicht 
einleuchten.

von Markus F. (mfro)


Lesenswert?

W.S. schrieb:
> Markus F. schrieb:
>> Das haben wir vor Kurzem geklärt.
>> Bei einem standardkonformen Compiler/Laufzeitumgebung steht am Ende von
>> main() ein return 0;
>
> Auch du theoretisierts bloß herum.

Ich "theoretisiere nicht herum", ich zitiere aus dem C99-Standard.
Und ich benutze (mindestens) C99-kompatible Compiler die das auch so 
machen (alles andere würde ich nur mit der Kneifzange anfassen).

Im Übrigen hat das "ob oder ob nicht" mit dem Startupcode nicht das 
Geringste zu tun. Wie soll der beeinflussen, was innerhalb main() 
passiert?

von Markus F. (mfro)


Lesenswert?

Arduino Fanboy D. schrieb:
> Wirft eine Warnung:
>>  warning: no return statement in function returning non-void
>
Bei main() nicht.

> Und wenn man den Returnwert nutzt/auswertet, wird man eine Null
> erhalten. Zuverlässig.

mit Ausnahme von main(): UB.

Arduino Fanboy D. schrieb:
> Und wenn man den Returnwert nutzt/auswertet, wird man eine Null
> erhalten. Zuverlässig.

P.S.: das "zuverlässig" ist ziemlich unzuverlässig:
1
int tst(void)
2
{
3
    volatile int i = 2;
4
5
    /* return i; */
6
}

Mit einem (ziemlich aktuellen) ColdFire-Compiler übersetzt:
1
Disassembly of section .text:
2
3
00000000 <tst>:
4
   0:  598f             subql #4,%sp
5
   2:  7002             moveq #2,%d0
6
   4:  2e80             movel %d0,%sp@
7
   6:  588f             addql #4,%sp
8
   8:  4e75             rts

Hier ist der "Returnwert" (m68k-ABI hat den in D0) ziemlich 
"zuverlässig" 2.

: Bearbeitet durch User
von Rainer V. (a_zip)


Lesenswert?

soul e. schrieb:
> Was genau ist Deiner Meinung nach "nichts"?

Sorry, ich habe mich da zu lax ausgedrückt...durch die Art der 
Fragestellung etwas irritiert, habe ich angenommen, dass "main" eine 
Endlosschleife bildet, die natürlich durch alles Mögliche unterbrochen 
werden kann, aber doch "bei sich bei" bleibt. In diesem Sinne passiert 
nach "main" nichts, weil es kein "nach" gibt! Die Idee, ein 
(Unter-)Programm zu schreiben, an das sich kein weiterer Code 
anschließt, die CPU also ins Leere laufen läßt, halte ich für mehr als 
abstrus. Und die Frage, ob oder was da der Compiler noch dran drehen 
könnte, ist genau so dümmlich...
Gruß Rainer

von Thomas (Gast)


Lesenswert?

Es ist doch ganz einfach...
Return Werte werden bei den allermeisten Compilern in einem Register 
zurück gegeben. Wenn also return xxx;  im code fehlt seit ein Aufrufer 
einfach den letzten zufälligen Wert der im Register steht.
Deshalb ist es sinnvoll zumindest eine Warnung auszugeben.

Thomas

von Gewässerkundiger (Gast)


Lesenswert?

> Was macht ein µC nach main?
Er schwimmt weiter in den Rhein, dann Main ist nur der längste 
Nebenfluss des Rheins.

von Jim M. (turboj)


Lesenswert?

Daniel K. schrieb:
> Ich habe keine konkrete Hardware im Sinn, das ist eher eine theoretische
> Frage.

Dann ist die Antwort einfach: Das Verhalten ist unbestimmt.

Du musst für eine konkrete Antwort nicht nur die Hardware im Auge haben, 
sondern auch die Software wie C-Compiler und LibC.

Bei µCs verwendet man mitunter auch selbstgeschriebenen Startup Code, 
der dann oft den Return aus main() gar nicht erst behandelt.

von Axel S. (a-za-z0-9)


Lesenswert?

Rainer V. schrieb:
> soul e. schrieb:
>> Was genau ist Deiner Meinung nach "nichts"?
>
> Sorry, ich habe mich da zu lax ausgedrückt...durch die Art der
> Fragestellung etwas irritiert, habe ich angenommen, dass "main" eine
> Endlosschleife bildet, die natürlich durch alles Mögliche unterbrochen
> werden kann, aber doch "bei sich bei" bleibt. In diesem Sinne passiert
> nach "main" nichts, weil es kein "nach" gibt!

Es ist allgemeiner Konsens, daß auf einem µC eine main() Funktion, die 
ein return Statement enthält oder ihr Ende erreichen kann, relativ 
sinnlos ist. Nichtsdestotrotz ist main() erstmal nur eine C-Funktion und 
als solche kann sie im Prinzip beendet werden. Bei Programmen, die in 
einem hosted Environment (z.B. auf deinem PC) laufen, ist das sogar der 
Normalfall.

> Die Idee, ein
> (Unter-)Programm zu schreiben, an das sich kein weiterer Code
> anschließt, die CPU also ins Leere laufen läßt

Die CPU läuft aber nicht "ins Leere". main() ruft sich schließlich nicht 
von alleine auf. Irgendwelcher Code muß den Aufruf von main() enthalten. 
Und genau dieser Code bestimmt auch, was passiert wenn main() 
zurückkehrt. Und wie bereits mehrfach gesagt, heißt dieser Code "Startup 
Code" oder auch "C runtime" und ist in den meisten Fällen Teil der 
Toolchain.

Siehe auch: https://en.wikipedia.org/wiki/Crt0

von Bernd K. (prof7bit)


Lesenswert?

Jörg W. schrieb:
> Bernd K. schrieb:
>> Zumindest bei µC-Projekten wo man sich auf irgendwelche Tricksereien
>> oder spezielle Verhaltensweisen des Startups verlassen will. Genauso wie
>> das zugehörige Linkerscript
>
> Bei ARMs ist das gang und gäbe, weil da jeder sein Süppchen kocht (und
> auch genügend halbgewalkte Dinge durchs Netz geistern).
>
> Beim AVR braucht man das nicht, da gibt es einen Linkerscript mit
> passendem Startupcode „aus der Dose raus“

Ist auch verständlich. Bei AVR kommt alles was die Toolchain bedient von 
ein und dem selben Hersteller, andere Chips die die selbe Toolchain 
bräuchten wird es niemals geben, da konnte Atmel hergehen und das alles 
direkt mit der Toolchain zusammen bundeln und ausliefern.

Aber bei ARM gibt es zig Hersteller und von jedem nochmal je zwei 
Dutzend grundverschiedene Chips. Solange die sich nicht alle aufraffen 
können friedlich zusammenzuarbeiten und gemeinsam ein Repository zu 
pflegen wo alle Startups und Linkerscripte (und bei der Gelegenheit auch 
gleich alle Registerdefinitionen) für alle Cortexe die es gibt auf der 
Welt an zentraler Stelle gepflegt werden (träum...) wird das niemals 
Bestandteil von gcc-arm-embedded werden.

Deshalb liefert jeder Hersteller in seiner eigenen Distribution der 
Toolchain noch seine eigenen Templates und Codegeneratoren mit die für 
seine Chips seinen Startup ausspucken so wie er ihn will.

Aber wenn der separat mitgelieferte oder generierte und ausgespuckte 
Startup dann jedesmal offen vor einem liegt (und man ihn auch ändern 
oder beliebig ersetzen könnte wenn man wollte) hat das auch den Vorteil 
daß man Fragen wie in diesem Thread gar nicht stellen müsste, man würde 
direkt und ohne langes Herumsuchen sofort sehen was bei einem konkret 
passiert vom ersten bis zum letzten Taktzyklus und man würde sofort 
sehen wer schuld hat (Hint: nicht "der Compiler"®) wenn es komische 
Dinge tut oder nicht tut. Es hat also auch Vorteile wenn es so ist wie 
es bei ARM momentan üblich ist, das ist irgendwie transparenter und 
expliziter als der verschachtelte Hokuspokus hinter den Kulissen bei 
AVR.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> Bei AVR kommt alles was die Toolchain bedient von ein und dem selben
> Hersteller, andere Chips die die selbe Toolchain bräuchten wird es
> niemals geben, da konnte Atmel hergehen und das alles direkt mit der
> Toolchain zusammen bundeln und ausliefern.

Atmel hat daran schlicht und ergreifend keinen Handschlag gemacht, 
zumindest nicht zu dem Zeitpunkt, als solche Konzepte implementiert 
worden sind. (Später haben sie sich teilweise mit in die Entwicklung der 
Toolchain eingebracht.)

Im Gegensatz dazu hatte ARM seine Finger schon sehr lange in der 
GCC-Entwicklung mit drin, sie hätten es als OEM-übergreifender 
Hersteller also durchaus in der Hand gehabt, da eine gemeinsame 
Strategie für alle zu implementieren, wie man Startup-Code und 
Linkerscripte mit integriert. War ihnen aber offenbar genauso wenig 
wichtig, wie der GCC lange Zeit Atmel für den AVR unwichtig war (sie 
hatten ja ihre Kooperation mit IAR).

Dass alle ARM-OEMs selbst daran kein großes Interesse haben, ist 
wiederum verständlich. Es hätte schon ARM als Lizenzgeber selbst in die 
Hand nehmen müssen.

von W.S. (Gast)


Lesenswert?

Jörg W. schrieb:
> sie hätten es als OEM-übergreifender
> Hersteller also durchaus in der Hand gehabt..

Jörg, vergiß bitte nicht, daß gerade Arm ein vitales Interesse daran 
hat, daß gerade der GCC ein Stück schlechter gehalten ist als deren 
eigene Tools. Ich hab das ja ausreichend ausprobiert mit Yagarto versus 
Keil.

Das ist so ähnlich wie das Verhältnis Wolfsburg versus Skoda.

Aber für die anfängliche und strunzalberne Frage im Eröffnungspost ist 
das alles völlig nebensächlich.

Ich bin auch nicht dafür, mir eine totale Vereinheitlichung zu wünschen. 
Gilt für alles, schließlich haben wir in der Geschichte schon genug 
Versuche zur totalen Vereinheitlichung gehabt. Nee, nicht mit mir.

Und..
Jörg W. schrieb:
> Es hätte schon ARM als Lizenzgeber selbst in die
> Hand nehmen müssen.

Das haben sie. Arm hat Keil aufgekauft und man kann die Früchte in Form 
von ADS, MDK und neuerdings auch eine LLVM-Ablegerversion kriegen. Gegen 
Geld natürlich. Ja, Geld - das ist in der Industrie die Normalität. Und 
Steuern sollen ja auch dabei herauskommen.

W.S.

von Bernd K. (prof7bit)


Lesenswert?

W.S. schrieb:
> Yagarto

Ein inoffizieller Fork von gcc von vor 10 Jahren? Es gibt jetzt einen 
offiziellen arm-gcc von ARM persönlich betreut und gehostet der eng mit 
upstream gcc zusammenarbeitet.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Axel S. schrieb:
> Es ist allgemeiner Konsens, daß auf einem µC eine main() Funktion, die
> ein return Statement enthält oder ihr Ende erreichen kann, relativ
> sinnlos ist.

Wie kommst Du denn auf dieses schmale Brett. Es sind durchaus 
Anwendungen denkbar, die nach getaner Arbeit terminieren.
Z.B. eine simple Einschaltverzögerung. Gegenüber einem 555 mit Elko hat 
ein µC den Vorteil, daß man die Zeit einfach im Sourcecode in Klartext 
einsetzen kann, daß er ein funktionierendes Poweron-Reset hat und daß er 
erheblich genauer ist, als ein Elko mit -20/+50% Toleranz.

Der C99 Standard schreibt vor, daß implizit ein "return 0;" zu erfolgen 
hat, sobald der Compiler erkennt, daß das Main terminiert. Es gibt also 
keinen Grund, das auf einem µC nicht zu tun. Im Gegenteil, ein Standard 
konformer Compiler ist immer zu bevorzugen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

W.S. schrieb:
> örg, vergiß bitte nicht, daß gerade Arm ein vitales Interesse daran hat,
> daß gerade der GCC ein Stück schlechter gehalten ist als deren eigene
> Tools.

Dem widerspricht ihre eigene Mitarbeit bei GCC.  Kann aber sein, dass 
sie da auch erst später eingestiegen sind (Atmel beim AVR aber noch 
weniger).  Keil ist ja auch nur'n Zukauf, war also (im Gegensatz zur 
IAR-Kooperation bei Atmel's AVR) nicht von vornherein gesetzt.

> Aber für die anfängliche und strunzalberne Frage im Eröffnungspost ist
> das alles völlig nebensächlich.

Korrekt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter D. schrieb:
> Es gibt also keinen Grund, das auf einem µC nicht zu tun.

Doch: in deinem Anwendungsfall würde man wohl eher die Interrupts 
sperren und den Prozessor in den Sleep schicken, um sinnloses 
Energieverplempern zu vermeiden. Dann passiert ab diesem Punkt bis zum 
nächsten Reset wirklich rein gar nichts. ;-)

In der Tat hatte ich so einen Anwendungsfall schon mal in den Fingern: 
ein kleiner PIC, der einen PLL-IC (in einem Wettersatellitenempfänger) 
beim Einschalten in Abhängigkeit von ein paar DIP-Schaltern 
konfigurieren musste und danach nichts mehr zu tun hatte.

von Axel S. (a-za-z0-9)


Lesenswert?

Peter D. schrieb:
> Axel S. schrieb:
>> Es ist allgemeiner Konsens, daß auf einem µC eine main() Funktion, die
>> ein return Statement enthält oder ihr Ende erreichen kann, relativ
>> sinnlos ist.
>
> Wie kommst Du denn auf dieses schmale Brett.

Das ist es nicht.

> Es sind durchaus
> Anwendungen denkbar, die nach getaner Arbeit terminieren.
> Z.B. eine simple Einschaltverzögerung.

Da ist kein Widerspruch. "Arbeit einstellen" ist nicht notwendig das 
gleiche wie "aus main() zurückkehren". Ein halbwegs erfahrener 
Programmierer würde den Kern dann in eine Endlosschleife schicken. Oder 
- besser - einen Sleep-Mode nutzen, um Strom zu sparen.

> Der C99 Standard schreibt vor, daß implizit ein "return 0;" zu erfolgen
> hat, sobald der Compiler erkennt, daß das Main terminiert.

Was hat das mit der Frage zu tun? Der Rückgabewert von main() ist doch 
vollkommen unerheblich. Wenn man nicht gerade einen selber geschriebenen 
Startup-Code verwendet, der den auswertet, dann wird der einfach nur 
weggeworfen. Und es bleibt bei fremdem Startup-Code die Frage, was der 
als nächstes macht.

Irgendwo weiter oben im Thread wollte jemand beobachtet haben, daß sein 
µC nach dem Ende von main() wieder von vorn anfängt - wie nach einem 
Reset. Und das will man nun ganz sicher nicht haben, gerade in der von 
dir skizzierten Anwendung. Dann doch besser eine eigene Endlosschleife 
am Ende von main().

von Peter D. (peda)


Lesenswert?

Axel S. schrieb:
> Irgendwo weiter oben im Thread wollte jemand beobachtet haben, daß sein
> µC nach dem Ende von main() wieder von vorn anfängt - wie nach einem
> Reset.

Das würde mich auch mal interessieren, welcher Compiler das sein soll.
Typisch steht Main nicht am Ende des Flash, d.h. man würde danach in 
eine Unterfunktion hinein laufen, was einen Stackunterlauf bewirkt.

von Bernd K. (prof7bit)


Lesenswert?

Peter D. schrieb:
> Axel S. schrieb:
>> Irgendwo weiter oben im Thread wollte jemand beobachtet haben, daß sein
>> µC nach dem Ende von main() wieder von vorn anfängt - wie nach einem
>> Reset.
>
> Das würde mich auch mal interessieren, welcher Compiler das sein soll.

Derjenige könnte auch ohne sich dessen bewußt zu sein beobachtet haben 
daß dann nach kurzer Zeit in der Endlosschleife sein Watchdog auslöst.

> welcher Compiler das sein soll.

Außerdem ist das nicht der Compiler sondern der Startupcode der das 
Verhalten vor und nach der main bestimmt, meist hat der Chiphersteller 
sich diesen Code aus dem Finger gesogen und den Compiler hat er sich 
woanders organisiert und der Compiler ist unschuldig, der Compiler hat 
ein sehr eng begrenztes Aufgabengebiet, das ganze Drumherum wird separat 
davon ans verkäufliche Gesamtgebilde drangeflickt aber in seinem 
innersten ist der Compiler nur ein unschuldiger Compiler.

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
Noch kein Account? Hier anmelden.