Warum brauchen wir einen Bootloader, der von unserem Anwendungsprogramm in Mikrocontrollern getrennt ist?

Warum benötigen wir ein separates Programm im selben Flash-Programmspeicher eines Mikrocontrollers, insbesondere STM32F103, das als Bootloader bezeichnet wird?

Was ist das Besondere daran, es vom Hauptanwendungsprogramm getrennt zu halten?

Macht ein Bootloader eines mikroprozessorbasierten Systems (z. B. PowerPC MPC8270) im Allgemeinen die gleiche Aufgabe wie der eines Mikrocontrollers (z. B. ARM STM32F103) oder führen sie grundlegend unterschiedliche Aufgaben aus, und dennoch werden beide als „Bootloader“ bezeichnet? ?

Aus demselben Grund haben Sie einzelne Chips und Teile und nicht eine riesige monolithische Struktur
Du nicht. Geben Sie einfach Ihr Programm mit den Schaltern und Lichtern auf der Computerkonsole ein.
Genau genommen benötigt man auf einem Mikrocontroller kein separates Bootloader-Programm. Aber wir entscheiden uns meistens für einen wegen der zusätzlichen nützlichen Funktionen, die er bietet. Wenn diese Funktionen nicht benötigt werden, nicht erwünscht sind, können Sie den Bootloader entfernen. Der Mikrocontroller-Bootloader wird normalerweise verwendet, um ein neues Programm in den Flash zu brennen. Es kann manchmal für Debugging-Funktionen, einige Support-Breakpoints und andere Nice-to-have-Funktionen verwendet werden. Auf einem Mikrocomputer lädt typischerweise der Bootloader Programme aus dem Massenspeicher und wird dort benötigt.

Antworten (8)

Ein Bootloader auf einem Mikrocontroller ist für die Aktualisierung der Hauptfirmware über einen anderen Kommunikationskanal als den Programmierkopf verantwortlich. Dies ist nützlich, um Firmware im Feld über BLE, UART, I2C, SD-Karten, USB usw. zu aktualisieren. Es wäre äußerst unpraktisch, von Kunden zu verlangen, Programmierer zu kaufen, nur um die Firmware auf ihren Geräten zu aktualisieren.

Der Grund, warum der Bootloader separat gehalten wird, ist die Zuverlässigkeit. Der Bootloader und der Anwendungscode werden in separaten Flash-Abschnitten abgelegt, sodass der Anwendungscode vom Bootloader gelöscht und neu geschrieben werden kann, ohne dass etwas am Bootloader-Code geändert wird.

Wenn der Bootloader und die Anwendung zusammengehalten würden, müsste der Bootloader-Code in den RAM kopiert werden, bevor er ausgeführt werden könnte, da jedes Firmware-Update den Bootloader-Code im Flash löschen würde. Wenn die Stromversorgung mit dem Bootloader-Code im RAM unterbrochen und der Flash gelöscht würde, würde das Gerät gemauert werden.

Unsere ist der gleiche Grund. Sie befinden sich im selben Flash, aber der Bootloader ist auf Flash-Löschgrenzen ausgerichtet und intelligent genug, um nur den Flash zu löschen, der höher als seine eigenen Adressen ist.
Im Fall von STM32F103-MCUs und Keil- oder GCC-Compilern wird also zuerst die Datei startup.s ausgeführt, und dann wird die Steuerung an den Bootloader übergeben, und sobald der Bootloader die Steuerung zurückgibt, geht er zum Anwendungsprogramm main(). Bitte korrigiere mich, wenn ich mit diesem Verständnis falsch liege.
In manchen Fällen kann der Programmierkopf des Mikroprozessors tatsächlich unzugänglich sein, ohne dass das Gehäuse des Produkts zerlegt werden muss. Daher ist die Möglichkeit, ihn ohne zusätzliche Hardware über den Kommunikationsbus neu zu programmieren, ein Schlüsselfaktor für die Zuverlässigkeit.
@alt-rose Der Bootloader und das Anwendungsprogramm sind separat kompilierte Programme mit jeweils eigenem Startcode und eigener main()Funktion. Beim Einschalten wird der Bootloader-Startcode ausgeführt und ruft die main(). Das Bootloader-Programm sucht nach einem gültigen Anwendungsprogramm und springt dann zum Startcode des Anwendungsprogramms, der die main(). Der Startcode jedes Programms initialisiert die C-Laufzeitumgebung für das jeweilige Programm (dh initialisiert Variablen, Stack usw.) und typischerweise kehrt keines der Programme main()jemals zum Startcode zurück.
@kkrambo Wenn sowohl der Bootloader als auch das Anwendungsprogramm separat kompiliert werden, wie erfährt das Bootloader-Programm dann die Startadresse des Anwendungsprogramm-Startcodes?
@alt-rose: Genauso wie die CPU die Startadresse des Bootloaders erhält - tut sie es nicht. Stattdessen gibt die CPU an, was sie als Startadresse des Bootloaders verwenden wird, und der Bootloader gibt an, was er als Startadresse des Anwendungsprogramms verwenden wird.
main@kkrambo Obwohl allgemein wahr, gibt es keine Anforderung (oder allgemein gültige Tatsache), dass ein Bootloader überhaupt in C oder einer von C abgeleiteten Sprache mit einem geschrieben wird .
@alt-rose Die Startadresse des Anwendungsprogramms wird vom Entwickler zur Entwurfszeit festgelegt. Dann kann es fest in den Bootloader codiert werden, damit der Bootloader weiß, wo die Anwendung startet. Der Bootloader muss auch wissen, wo sich das Anwendungsprogramm befindet, damit er das Anwendungsprogramm überprüfen oder aktualisieren kann.
  1. Damit sich der Ladevorgang von Fehlern erholen kann. Angenommen, während eines Upgrades liegt ein Kommunikationsfehler vor oder die Stromversorgung wird unterbrochen. Wenn der Bootloader Teil der Anwendung wäre, die Sie aktualisieren, könnte der Benutzer es nicht erneut versuchen, ohne spezielle Hardware zum erneuten Flashen des Bootloaders zu verwenden.

  2. Einige Mikrocontroller können keinen Code aus dem RAM ausführen. Wenn der Bootloader mit dem Rest der Software gemischt wurde, könnten Sie Ihre Software nicht wirklich aktualisieren, da Sie keine Flash-Seiten löschen können, aus denen Sie gerade ausführen. Die Problemumgehung besteht darin, den neuen Code zuerst in die zweite Hälfte des Flashs zu brennen und dann dorthin zu springen. Der neue Code kopiert sich dann in die erste Hälfte des Flashs. Der Nachteil ist natürlich, dass das Brennen von Flash normalerweise langsam ist und der Ladevorgang jetzt, da Sie es zweimal tun müssen, bis zu doppelt so lange dauern kann. Außerdem begrenzt diese Problemumgehung Ihre Anwendungsgröße auf nicht mehr als die Hälfte Ihres gesamten Flash.

  3. Gut geschriebene Bootloader versuchen zu überprüfen, ob gültiger Code auf dem Gerät vorhanden ist, bevor sie versuchen, ihn auszuführen. Wenn der Bootloader und anderer Code vermischt wurden, wie können Sie dann sicher sein, dass Ihre Validierungsroutine funktioniert, wenn nicht der gesamte Code geladen wird?

  4. Authentifizierung. Sichere Bootloader versuchen vor der Ausführung zu überprüfen, ob die geladene Anwendung mit einer digitalen Signatur übereinstimmt. Aber wenn der Bootloader und anderer Code gemischt wurden, dann können Sie nicht kontrollieren, was auf dem Gerät läuft, denn sobald der Benutzer neuen Code lädt, können Sie nicht kontrollieren, was beim Start passiert.

Als Beispiel für Punkt 2 haben einige Mikrocontroller beim Start möglicherweise nicht einmal Zugriff auf RAM: Der Raspberry Pi verwendet beispielsweise seine GPU, um den Bootloader von einer SD-Karte zu laden, der dann den ARM-Prozessor und den Speicher aktiviert.

Sie sind im Allgemeinen dazu da, Ihnen zu ermöglichen, Ihr Hauptanwendungsprogramm zu aktualisieren.

Sie benötigen einen Code, der weiß, wie ein Teil des internen Flash gelöscht und neu programmiert werden kann. Dies kann nicht das Hauptprogramm sein, da er nach dem Löschen selbst nicht neu programmiert werden kann.

Der Bootloader ermöglicht es der MCU, mit etwas anderem zu kommunizieren, um ein neues Programm zu akzeptieren, zu speichern und nach einem Reset auszuführen. Wenn Sie keinen Bootloader hatten, wird ein Programmierer benötigt, um auf den Speicher zuzugreifen und das Programm zu installieren.

Das wars so ziemlich. Die MCU kann Code nur über ein spezielles Programmiersubsystem (wie AVRICE oder JTAG) oder durch bereits einen Bootloader im Flash erhalten. Es ist eine Anwendungsentscheidung, wie komplex der Bootloader ist, z. B. können einige Systeme Code aus WiFi laden. Auf sehr niedrigen MCUs wie einem ATTiny sind ein Bootloader (und serielle Pins) ein großer Overhead, sodass Sie immer einen Programmierer verwenden.

Zusätzlich zu den anderen richtigen Antworten zum Zulassen der Neuprogrammierung der Haupt-Firmware vom Bootloader besteht ein weiterer Vorteil des separaten Bootloaders darin, dass Sie die Aufgaben "Einmal beim Booten ausführen" logisch von dem Code trennen können, den Sie während der Laufzeit benötigen. Nachdem der Bootloader seine anfänglichen Konfigurationsaufgaben abgeschlossen hat, kann die Hauptfirmware den Bootloader mit all seinem nicht mehr benötigten Code aus dem Speicher entfernen, wodurch erheblicher RAM-Speicherplatz eingespart wird. Es ist möglich, dies auf andere Weise zu erreichen, aber die Trennung von Bootloader und Firmware macht es auf vielen Architekturen viel einfacher.

Auf einem Mikrocontroller befindet sich der Code höchstwahrscheinlich nie im RAM, sodass er nicht entfernt werden kann. Sie können die Daten des Bootloaders natürlich aus dem RAM verwerfen.
@BenVoigt, es hängt vom Mikrocontroller ab. Einige (hauptsächlich solche mit NOR-Flash) lassen Sie direkt aus dem Flash heraus ausführen, aber andere (normalerweise mit NAND-Flash, was immer häufiger vorkommt) erfordern, dass Sie aus dem RAM heraus ausführen. Manchmal gibt es nicht einmal einen integrierten Flash, und Sie müssen den Code von einem externen Flash-Chip in den lokalen SRAM kopieren, bevor Sie etwas ausführen können.

Die kurze Antwort ist, weil Software großartig ist.

Sie könnten alles, was der Bootloader tut, als "reine Hardware" haben. Aber es ist viel, viel, viel einfacher, die Aufgaben, die der Bootloader erledigt, als Software zu schreiben und dann von Hardware zu interpretieren.

Diese Aufgaben können das Einrichten der Hardware für die Ausführung der "echten" Software (z. B. auf einem Raspberry Pi (über @ErikF)) und das Vorhandensein eines Protokolls umfassen, um das "echte" Programm zu ersetzen, bevor es ausgeführt wird (überprüfen Sie einen Pin, falls vorhanden). Dieser Pin wird gesetzt und dann das echte Programm neu geflasht) oder sogar die Softwareumgebung für das "echte" Programm einrichten.

Bei weniger mikroskalierter Software, wenn Sie eine ausführbare Datei ausführen, bewegt der Anwendungslader Dinge wie das Laden von Teilen Ihrer Daten in den Speicher, repariert manchmal Adressen, richtet Argumente für Haupt- oder andere globale Dinge ein, dreht Ihre vom Betriebssystem bereitgestellten Bibliotheken hoch und springt dann zum Anfang des _mainCodes. Einige dieser Dinge können von einem Bootloader erledigt werden.

In einem Mikrocontroller könnten einige der Aufgaben, die ein Bootloader erledigt, in das Programm aufgeteilt werden. Der Compiler für Ihre Plattform könnte den "Setup"-Code automatisch in jede ausführbare Datei einfügen.

Aber es im Bootloader zu haben bedeutet, dass derselbe Compiler möglicherweise auf unterschiedlicher Hardware funktioniert, da der Bootloader den Unterschied zwischen den Plattformen "verbergen" kann.

Krönen Sie das mit der Tatsache, dass ein Flash des Hauptprogramms den Bootloader nicht gefährdet (und die Möglichkeit, das Hauptprogramm erneut zu flashen), und dass es eine ziemlich großartige Sache ist, einen nicht trivialen Bootloader zu haben.

Ein Teil der Frage, der bisher nicht beantwortet wurde, ist der Unterschied zwischen Bootloadern auf Mikrocontrollern und Mikroprozessorsystemen.

Mikrocontroller

Die meisten Mikrocontroller haben einen eingebauten ROM-Speicher, der ihren Programmcode enthält. Das Ändern dieses Codes erfordert normalerweise ein Programmiergerät, das sich mit der Programmierschnittstelle des Mikrocontrollers verbindet (z. B. ISP auf ATMega). Aber diese Programmierschnittstellen sind im Vergleich zu anderen Schnittstellen in der Regel nicht sehr komfortabel zu verwenden, da sie im gegebenen Kontext möglicherweise nicht ohne Weiteres verfügbar sind. Während beispielsweise fast jeder Computer über USB-Anschlüsse verfügt, ist die für ISP benötigte SPI-Schnittstelle viel seltener, und andere Schnittstellen wie die PID-Schnittstelle, die auf ATXMega verwendet wird, werden nur von dedizierter Programmierhardware unterstützt.

Wenn Sie beispielsweise die Software von einem normalen Computer ohne externe Hardware aktualisieren möchten, können Sie einen Bootloader verwenden, der von einer anderen Art von Schnittstelle liest (z. B. RS232, USB oder RS232 über USB wie beim Arduino), um das Gerät zu programmieren über gängige Schnittstellen.

Wenn Sie diese Funktionalität nicht benötigen, ist der Bootloader jedoch vollständig optional. Der Mikrocontroller kann seinen Code immer noch vollständig ohne den Bootloader ausführen.

Mikroprozessor

Bei einem Mikroprozessor sind die Dinge etwas anders. Während die meisten Mikroprozessoren über ein ROM verfügen, das groß genug für einen Bootloader ist, sind diese ROMs nicht annähernd groß genug, um ein vollständiges Betriebssystem aufzunehmen. Der Zweck des Bootloaders besteht also darin, Hardware zu initialisieren, nach einem bootfähigen Betriebssystem zu suchen, es zu laden und auszuführen. Der Bootloader ist also für jeden einzelnen Start von entscheidender Bedeutung.

Auf x86/x64-Systemen ist dieser Bootloader entweder das BIOS oder das UEFI (im Grunde eine neuere Version eines BIOS).

Manchmal laufen sogar mehrere Bootloader in einer Kette. Wenn Sie beispielsweise ein Dual-Boot-System mit Windows und Linux haben, erhalten Sie möglicherweise Folgendes:

  • BIOS/UEFI bootet und findet GRUB installiert. Es lädt dann GRUB (=Grand Unified Bootloader)
  • GRUB findet eine Art Linux und den Windows-Bootloader. Der Benutzer wählt den Windows-Bootloader aus.
  • Der Windows-Bootloader startet und findet Windows 7 und Windows 10 installiert. Der Benutzer wählt Windows 10 aus.
  • Windows 10 bootet endlich.

In diesem Fall gab es also drei Softwareteile, die als Bootloader betrachtet werden können. Sowohl GRUB als auch der Windows-Bootloader sind hauptsächlich dazu da, dem Benutzer eine bequemere Boot-Auswahloption zu bieten, als das BIOS/UEFI ihnen bieten würde. Es ermöglicht auch das Starten mehrerer Betriebssysteme von derselben Festplatte oder sogar derselben Partition.

TLDR

Während der Bootloader in beiden Systemen ähnliche Dinge tut (dem Benutzer hilft, den zu bootenden Code auszuwählen), unterscheiden sich beide stark darin, wie sie dies erreichen und was sie genau tun.

Obwohl es sinnvoll ist, Systeme mit genügend nichtflüchtigem Speicher (ROM oder Flash) mit wahlfreiem Zugriff, um das gesamte Programm zu speichern, von solchen zu unterscheiden, die Code aus dem RAM ausführen müssen, gibt es Mikrocontroller beider Typen und Mikroprozessoren beider Typen.
Natürlich ist der Unterschied zwischen einem Mikrocontroller und einem Mikroprozessor keine harte Grenze und einige Mikrocontroller verhalten sich eher wie ein Mikroprozessor und umgekehrt. Deshalb habe ich den AtMega/Arduino und den x86/x64 als Beispiel genommen, weil sie sich so verhalten.
"Mikroprozessoren verfügen über ein ROM, das groß genug für einen Bootloader ist ... Auf x86/x64-Systemen ist dieser Bootloader entweder das BIOS oder das UEFI" Nein. BIOS oder UEFI werden im Flash-Speicher außerhalb des Chips gespeichert. Das On-Chip-ROM ist für noch niedrigere Funktionen, wie die Initialisierung des Mikrocodes.
@Dakkaron: Ich würde die Grenze zwischen einem Mikroprozessor und einem Mikrocontroller ziehen, je nachdem, ob der Chip so konzipiert ist, dass er für nicht triviale Zwecke ohne etwas anderes auf dem Adressbus verwendet werden kann. Der 8031 ​​würde sich nicht qualifizieren, außer dass es sich funktional um 8051 handelt (was definitiv ein Mikrocontroller ist), der nichts Nützliches im internen ROM enthält, aber ansonsten so konzipiert wäre, dass er vollständig vom internen Speicher verwendet werden kann) . Etwas wie ein RCA / CDP 1802 würde sich nicht qualifizieren, obwohl es zum Ansteuern eines LED-Namensschilds verwendet werden kann ...
...ohne externes RAM und ROM, da RAMless/ROMless-Designs auf triviale Aufgaben beschränkt sind. Etwas wie ein TMS 32050, das, wenn ich mich erinnere, einen Bootloader und ein paar tausend Wörter 16-Bit-Worte RAM intern hat, würde sich jedoch als Mikrocontroller qualifizieren; Obwohl viele Anwendungen mehr RAM erfordern würden, könnte es, wenn es über UART mit einem anderen System verbunden ist, vielen Zwecken dienen, ohne dass sich etwas auf seinem Speicherbus befindet.

Eine Antwort, die nicht behandelt wurde, ist die Notwendigkeit der Trennung von Bedenken in Bezug auf den Startcode.

Dies geschieht, um bestimmte Dinge einzurichten, wie:

  • Zuweisen des C-Stacks
  • Lesen des Stapelzeigers in das Register
  • Lesen des Programmzählers in das Register
  • Deklarieren von Reset-Vektoren
  • Laden der zweiten Stufe (initramfs) in den RAM.

Dies ist eine sehr grobe Annäherung an die durchgeführten Schritte, und ich beschreibe den ARM-Startvorgang, er ist für x86 und andere Architekturen wieder anders.

Der Hauptgrund bleibt jedoch derselbe: Die Zuweisung des C-Stacks muss von der Assemblierung aus erfolgen.

Warum die Ablehnung? Dies ist sowohl relevant als auch genau.
Ich habe es nicht getan. Aber ich denke, das liegt daran, dass das, was Sie hier beschreiben, die Aufgabe des Startcodes und nicht des Bootloaders ist. (In der Tat hat ein Bootloader in dem erwähnten Gerät höchstwahrscheinlich seinen eigenen Startcode und dann macht die Anwendung die gleiche Arbeit noch einmal ...) (und ... auf einem stm32f103 hätten Sie kein initramfs ...)
Gerecht. Ich habe angegeben, dass ich über Startcode spreche.