Bare-Metal-Startcode für die Initialisierung der Cortex M3 .bss-Region

Inspiriert von hier habe ich einen Bare-Metal-Startcode für Armcortex M3 entwickelt. Ich stoße jedoch auf das folgende Problem: Angenommen, ich deklariere eine nicht initialisierte globale Variable, sagen wir vom Typ unsigned char in main.c

#include ...
unsigned char var; 
...
int main()
{
 ...
}

Dadurch beginnt die .bss-Region in STM32 f103 bei _BSS_START=0x20000000 und endet bei _BSS_END = 0x20000001. Nun der Startcode

    unsigned int * bss_start_p = &_BSS_START; 
    unsigned int * bss_end_p = &_BSS_END;

    while(bss_start_p != bss_end_p)
    {
        *bss_start_p = 0;
        bss_start_p++;
    }

versucht, die gesamte .bss-Region auf Null zu initialisieren. Innerhalb dieser While-Schleife erhöht sich der Zeiger jedoch um 4 Bytes, daher ist nach einem Schritt bss_start_p = 0x20000004, daher ist er immer anders als bss_end_p, was zu einer Endlosschleife usw. führt.

Gibt es dafür eine Standardlösung? Soll ich die Dimension der .bss-Region irgendwie "zwingen", ein Vielfaches von 4 zu sein? Oder sollte ich einen Zeiger auf unsigned char verwenden, um durch die .bss-Region zu gehen? Vielleicht so etwas wie:

    unsigned char * bss_start_p = (unsigned char *)(&_BSS_START); 
    unsigned char * bss_end_p = (unsigned char *)(&_BSS_END);

    while(bss_start_p != bss_end_p)
    {
        *bss_start_p = 0;
        bss_start_p++;
    }
```
verwenden Sie weniger als. Bootstraps werden aus einem bestimmten Grund in Assembly geschrieben. Zunächst einmal haben Sie ein .data-Problem erstellt. Es ist eine Henne-und-Ei-Sache zu verwenden / davon auszugehen, dass C funktioniert. Sie verlassen sich mindestens auf .text, .bss und .data, aber Sie schreiben C-Code, der sicherstellt, dass C-Code funktioniert, indem Sie Dinge in C-Code verwenden, die a erfordern Bootstrap möglicherweise in C-Code geschrieben, der auf C-Funktionalität angewiesen ist.
Der Code zum Kopieren von .data ist .bss sehr ähnlich, aber wenn Sie ihn wie den obigen Code schreiben, müssen Sie .data kopieren, um .data zu kopieren.

Antworten (4)

Wie Sie vermuten, geschieht dies, weil der unsigned int-Datentyp 4 Bytes groß ist. Jede *bss_start_p = 0;Anweisung löscht tatsächlich vier Bytes des bss-Bereichs.

Der bss-Speicherbereich muss korrekt ausgerichtet werden. Sie könnten _BSS_START und _BSS_END einfach so definieren, dass die Gesamtgröße ein Vielfaches von vier ist, aber dies wird normalerweise gehandhabt, indem Sie dem Linker-Skript erlauben, die Start- und Stopppositionen zu definieren.

Als Beispiel hier der Linker-Abschnitt in einem meiner Projekte:

.bss (NOLOAD) : ALIGN(4)
{
    __bss_start__ = .;
    *(.bss)
    . = ALIGN(4);
    __bss_end__ = .;
} >RAM

Die ALIGN(4)Aussagen erledigen die Dinge.

Vielleicht möchten Sie auch wechseln

while(bss_start_p != bss_end_p)

Zu

while(bss_start_p < bss_end_p).

Dies wird das Problem nicht verhindern (da Sie möglicherweise 1-3 Bytes mehr löschen, als Sie möchten), aber es könnte die Auswirkungen minimieren :)

@CMarius Nach dem Nachdenken denke ich, dass Ihre Idee mit dem Zeichenzeiger großartig funktionieren würde, obwohl sie mehr Zyklen erfordern würde. Aber ich bin mir nicht sicher, ob es spätere Probleme geben würde, wenn der nächste Speicherbereich nicht ausgerichtet ist, also werde ich es in meiner Antwort nicht erwähnen ...
while(bss_start_p < bss_end_p - 1)gefolgt von einem byteweisen Löschen des verbleibenden Speicherbereichs würde das letzte Problem beseitigen.

Die Standardlösung ist memset():

#include <string.h>
memset(&_BSS_START, 0, &_BSS_END - &_BSS_START)

Wenn Sie die Standardbibliothek nicht verwenden können, müssen Sie entscheiden, ob es in Ihrem Fall in Ordnung ist, die Größe des Speicherbereichs auf 4 Bytes aufzurunden und weiterhin unsigned int *; oder wenn Sie streng sein müssen, in diesem Fall müssen Sie unsigned char *.

Wenn Sie die Größe aufrunden, wie in Ihrer ersten Schleife, bss_start_pkann dies tatsächlich größer als sein, bss_end_paber das ist einfach mit einem Kleiner-als-Vergleich <anstelle eines Ungleichheitstests umzugehen.

Natürlich könnten Sie auch den größten Teil des Speicherbereichs mit 32-Bit-Übertragungen füllen und nur die letzten paar Bytes mit 8-Bit-Übertragungen, aber das ist mehr Arbeit für wenig Gewinn, besonders hier, wenn es nur ein Stück Startcode ist.

Stimme der Verwendung von sehr zu memset(). Aber die Ausrichtung auf 4 Bytes ist mehr oder weniger ein Muss. Warum also nicht?
Form oder Form ist in keiner Weise die Standardlösung für den Bootstrap, um Memset zu verwenden, das ist verrückt.
Sie verwenden nicht dieselbe Sprache, um diese Sprache zu booten
Der Bootstrap-Code und das Linker-Skript sind sehr eng miteinander verbunden. Sie werden feststellen, dass das Linker-Skript die .bss-Datei an mindestens einer 4-Byte-Grenze ausrichtet und dimensioniert, um die Füllung (im Bootstrap) um das Vierfache über Byte-Anweisungen zu verbessern (unter der Annahme von (mindestens) 32-Bit-Bussen, was typisch für Arm ist, aber es gibt Ausnahmen)
@old_timer, die Standard-C-Funktion, um den Speicher auf einen bestimmten Wert zu setzen, ist memset(), und C ist das, was sie zu programmieren scheinen. Die einfache Implementierung von ist memset()auch so ziemlich nur diese Schleife, es ist nicht so, als ob sie von viel anderem abhängt. Da es sich um einen Mikrocontroller handelt, gehe ich auch davon aus, dass keine dynamische Verknüpfung oder ähnliches stattfindet (und wenn ich mir den Link ansehe, gibt es keine, es ist nur ein Aufruf nach dieser Nullschleife), sodass der Compiler in der Lage sein sollte, main()dort memset()einzusteigen zusammen mit allen anderen Funktionen (oder um es inline zu implementieren).
Natürlich für kundenspezifische Situationen, kundenspezifische Lösungen, aber sie verlangten nach einem Standardweg.
Dies ist eine Bootstrap-Startfrage zusätzlich zu einer Frage von weniger als vs. gleich. Sie verwenden Memset einfach nicht für den C-Bootstrap per Definition. Sie verwenden nicht die Sprache, die Sie booten, mit der Sprache, die Sie booten, da Sie diesen Code booten müssen mit einem anderen Bootstrap, damit Sie die Sprache mit der Sprache booten können. oder Sie können es wie alle anderen tun und eine andere Sprache verwenden, dh Assemblersprache für C-Boostraps, ohne Aufrufe von C-Funktionen (selbst wenn memset in asm optimiert ist). Sie schreiben die drei/vier Codezeilen in asm.
Die Operation verlässt sich auch darauf, dass .data vor .bss initialisiert wird. Sie erwarten, dass sie ein Memcpy verwenden, um .data zu initialisieren, damit sie .data initialisieren können, und wie sie dann .data initialisieren, indem sie sich auf .data zur Initialisierung verlassen .Daten? Ebenso macht es absolut keinen Sinn, C zum Bootstrap von C zu verwenden. .data wird auch in ein paar Assemblerzeilen behandelt, da es nicht viele andere leichte Sprachen gibt, die anstelle von asm verwendet werden können, die auch keinen eigenen Bootstrap benötigen würden.
Ja, mit mehr Verständnis der Sprache und was hier vor sich geht, ist es einfach, sich nicht auf .data zu verlassen, um .bss für den Code wie gezeigt zu initieren. Noch einfacher, nicht zu versuchen, C zum Bootstrap von C zu verwenden (je nach Toolchain nach .text/.rodata verschieben)
@old_timer Sie "booten nicht C", Sie booten nur Ihr Programm. Sicherlich können Sie im Bootstrap-Code nicht alles machen, was Sie wollen, aber solange der Code nicht vom Inhalt von abhängt .bss, sollte alles funktionieren.
@old_timer, was genau ist hier die Magie beim "Bootstrapping"? Wenn Sie sich den Code im Link (unter "C-Startcode") ansehen , werden Sie sehen, dass es nur einen einfachen Aufruf von main()am Ende der Startfunktion gibt, der zB löscht bss. Nun, vorausgesetzt, man kann memset()von dort aus anrufen main(), was macht hier den magischen Unterschied? Es sieht aus wie nur ein Funktionsaufruf, ich kann nicht sagen, wie es einen Unterschied machen würde.
@old_timer, was das Bootstrapping einer Sprache betrifft ... Ich glaube nicht, dass reines C auf einem Mikrocontroller viel internen Zustand zum Bootstrapping hat. Sicher, auf einem echten Betriebssystem müsste die Standardbibliothek und andere initialisiert und stdindafür gesorgt werden, dass sie verfügbar sind , aber ich glaube nicht, dass diese auf MCUs so häufig verwendet werden. (Vielleicht habe ich etwas vergessen, sicher.) Und Sie wissen, dass sie bereits C in der Startfunktion verwenden, ja? Wenn Sie der Meinung sind, dass sie das zunächst nicht tun sollten, posten Sie bitte eine entsprechende Antwort (oh, ich sehe, Sie haben es bereits getan, gut.) argcargvmain

Wechseln Sie einfach !=zu <. Das ist normalerweise ohnehin ein besserer Ansatz, da es sich um Probleme wie dieses handelt.

Es gibt unzählige andere Seiten und Beispiele. Viele Tausende, wenn nicht Zehntausende. Es gibt die bekannten C-Bibliotheken mit Linker-Skripten und Boostrap-Code, insbesondere newlib, glibc, aber es gibt noch andere, die Sie finden können. Bootstraping von C mit C macht keinen Sinn.

Ihre Frage wurde beantwortet, Sie versuchen, einen genauen Vergleich von Dingen durchzuführen, die möglicherweise nicht genau sind, möglicherweise nicht an einer bekannten Grenze beginnen oder an einer bekannten Grenze enden. Sie können also das Kleiner-als-Ding tun, aber wenn der Code mit einem genauen Vergleich nicht funktioniert hat, bedeutet dies, dass Sie über .bss hinaus in den nächsten Abschnitt nullen, was möglicherweise dazu führt, dass schlechte Dinge passieren oder nicht, also ersetzen Sie es einfach durch ein Kleiner-als-nicht die Lösung.

Also hier geht TL; DR ist in Ordnung. Sie booten eine Sprache nicht mit dieser Sprache, Sie können sicher damit durchkommen, aber Sie spielen mit dem Feuer, wenn Sie das tun. Wenn Sie gerade erst lernen, wie das geht, müssen Sie auf der sicheren Seite sein, nicht auf dummes Glück oder Fakten, die Sie noch nicht aufgedeckt haben.

Das Linker-Skript und der Bootstrap-Code haben eine sehr enge Beziehung, sie sind verheiratet, an der Hüfte verbunden, Sie entwickeln das eine nicht ohne das andere, was zu massiven Fehlern führt. Und leider wird das Linker-Skript durch den Linker und die Assembler-Sprache durch den Assembler definiert, so dass Sie bei einer Änderung der Toolchains davon ausgehen, dass Sie beide neu schreiben müssen. Warum Assemblersprache? Es benötigt keinen Bootstrap, kompilierte Sprachen tun dies im Allgemeinen. C tut es, wenn Sie Ihre Verwendung der Sprache nicht einschränken möchten, ich beginne mit etwas sehr Einfachem, das minimale Toolchain-spezifische Anforderungen hat, Sie gehen nicht davon aus, dass .bss-Variablen Null sind (macht den Code weniger lesbar, wenn die Variable nie in dieser Sprache initialisiert wird). , versuchen Sie dies zu vermeiden, gilt nicht für lokale Variablen, also müssen Sie auf dem Laufenden sein, wenn Sie es verwenden. warum reden wir also über .bss und .data??? (Globals sind gut für diese Level-Arbeit, aber das ist ein anderes Thema)) Die andere Regel für die einfache Lösung ist, Variablen nicht in der Deklaration zu initialisieren, sondern im Code. Ja, mehr Flash brennt, Sie haben im Allgemeinen viel, nicht alle Variablen werden sowieso mit Konstanten initialisiert, die am Ende Anweisungen verbrauchen.

Sie können dem cortex-m-Design entnehmen, dass sie möglicherweise dachten, es gäbe überhaupt keinen Bootstrap-Code, also keine .data- oder .bss-Unterstützung. Die meisten Leute, die Globals verwenden, können nicht ohne leben, also hier ist:

Ich könnte dies minimaler machen, aber ein minimales Funktionsbeispiel für alle Cortex-MS, die die GNU-Toolchain verwenden. Ich erinnere mich nicht, welche Versionen Sie mit 5.xx oder so bis zum aktuellen 9.xx starten können. Ich habe die Linker-Skripte irgendwo um 3 herum gewechselt. xx oder 4.xx, als ich mehr lernte und als gnu etwas änderte, was mein erstes kaputt machte.

Bootstrap:

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:
    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    bx lr

Einstiegspunkt in C-Code:

void bounce ( unsigned int );

unsigned int a;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

Linker-Skript.

MEMORY
{
    rom : ORIGIN = 0x00000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

All dies könnte kleiner sein und trotzdem funktionieren. Fügen Sie hier einige zusätzliche Dinge hinzu, um es bei der Arbeit zu sehen.

optimierter Build und Link.

00000000 <_start>:
   0:   20001000
   4:   00000015
   8:   0000001b
   c:   0000001b
  10:   0000001b

00000014 <reset>:
  14:   f000 f804   bl  20 <centry>
  18:   e7ff        b.n 1a <done>

0000001a <done>:
  1a:   e7fe        b.n 1a <done>

0000001c <bounce>:
  1c:   4770        bx  lr
    ...

00000020 <centry>:
  20:   2207        movs    r2, #7
  22:   b510        push    {r4, lr}
  24:   4b04        ldr r3, [pc, #16]   ; (38 <centry+0x18>)
  26:   2007        movs    r0, #7
  28:   601a        str r2, [r3, #0]
  2a:   f7ff fff7   bl  1c <bounce>
  2e:   2000        movs    r0, #0
  30:   bc10        pop {r4}
  32:   bc02        pop {r1}
  34:   4708        bx  r1
  36:   46c0        nop         ; (mov r8, r8)
  38:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

Für einige Anbieter möchten Sie 0x08000000 oder 0x01000000 oder andere ähnliche Adressen verwenden, da der Flash dort abgebildet und in einigen Startmodi auf 0x00000000 gespiegelt wird. Bei manchen wird nur so viel Flash bei 0x00000000 gespiegelt, dass Sie möchten, dass der Vektortabellenpunkt auf den Anwendungs-Flash-Speicherplatz nicht Null zeigt. Da es sich um eine Vektortabelle handelt, funktioniert alles.

Beachten Sie zunächst, dass die Cortex-MS nur Daumenmaschinen sind und aus irgendeinem Grund eine Daumenfunktionsadresse erzwungen haben, was bedeutet, dass das lsbit ungerade ist. Kennen Sie Ihre Werkzeuge, die .thumb_func-Direktiven teilen dem GNU-Assembler mit, dass das nächste Label eine Thumb-Funktionsadresse ist. Wenn Sie das +1 in der Tabelle tun, wird dies zum Scheitern führen. Lassen Sie sich nicht dazu verleiten, tun Sie es richtig. Es gibt andere Gnu-Assembler-Möglichkeiten, um eine Funktion zu deklarieren. Dies ist der minimale Ansatz.

   4:   00000015
   8:   0000001b
   c:   0000001b
  10:   0000001b

Es bootet nicht, wenn Sie die Vektortabelle nicht richtig verstehen.

Benötigen Sie wohl nur den Stack-Pointer-Vektor (kann dort alles einfügen, wenn Sie den Stack-Pointer selbst im Code setzen möchten) und den Reset-Vektor. Ich habe ohne besonderen Grund vier hier eingetragen. Normalerweise 16 setzen, aber ich wollte dieses Beispiel verkürzen.

Was ist also das Minimum, das ein C-Bootstrap tun muss? 1. setze den Stapelzeiger 2. null .bss 3. kopiere .data 4. verzweige zum C-Einstiegspunkt oder rufe ihn auf

der C-Einstiegspunkt heißt normalerweise main(). aber einige Toolchains sehen main() und fügen Ihrem Code zusätzlichen Müll hinzu. Ich verwende absichtlich einen anderen Namen. YMMV.

Die Kopie von .data wird nicht benötigt, wenn dies alles RAM-basiert ist. Da es sich um einen Cortex-M-Mikrocontroller handelt, ist dies technisch möglich, aber unwahrscheinlich, sodass die .data-Kopie benötigt wird ... wenn .data vorhanden ist.

Mein erstes Beispiel und ein Codierungsstil besteht darin, sich nicht auf .data oder .bss zu verlassen, wie in diesem Beispiel. Arm kümmerte sich um den Stapelzeiger, sodass nur noch der Einstiegspunkt aufgerufen werden musste. Ich mag es, damit der Einstiegspunkt zurückkehren kann, viele Leute argumentieren, dass Sie das niemals tun sollten. das könntest du dann einfach machen:

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word centry
.word done
.word done
.word done

und nicht von centry() zurückkehren und keinen Handler-Code zurücksetzen.

00000020 <centry>:
  20:   2207        movs    r2, #7
  22:   b510        push    {r4, lr}
  24:   4b04        ldr r3, [pc, #16]   ; (38 <centry+0x18>)
  26:   2007        movs    r0, #7
  28:   601a        str r2, [r3, #0]
  2a:   f7ff fff7   bl  1c <bounce>
  2e:   2000        movs    r0, #0
  30:   bc10        pop {r4}
  32:   bc02        pop {r1}
  34:   4708        bx  r1
  36:   46c0        nop         ; (mov r8, r8)
  38:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000

Der Linker hat Dinge dort abgelegt, wo wir gefragt haben. Und insgesamt haben wir ein voll funktionsfähiges Programm.

Arbeiten Sie also zuerst am Linker-Skript:

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

}

betonend, dass die Namen rom und ram keine Bedeutung haben, verbinden sie nur die Punkte für den Linker zwischen den Abschnitten.

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:
    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    bx lr

.align
.word __data_rom_start__
.word __data_start__
.word __data_end__
.word __data_size__

Fügen Sie einige Elemente hinzu, damit wir sehen können, was die Tools getan haben

void bounce ( unsigned int );

unsigned int a;

unsigned int b=4;
unsigned char c=5;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

Fügen Sie einige Elemente hinzu, die in diesen Abschnitten platziert werden sollen. und bekomme

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000001b    andeq   r0, r0, r11, lsl r0
   c:   0000001b    andeq   r0, r0, r11, lsl r0
  10:   0000001b    andeq   r0, r0, r11, lsl r0

00000014 <reset>:
  14:   f000 f80c   bl  30 <centry>
  18:   e7ff        b.n 1a <done>

0000001a <done>:
  1a:   e7fe        b.n 1a <done>

0000001c <bounce>:
  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

00000030 <centry>:
  30:   2207        movs    r2, #7
  32:   b510        push    {r4, lr}
  34:   4b04        ldr r3, [pc, #16]   ; (48 <centry+0x18>)
  36:   2007        movs    r0, #7
  38:   601a        str r2, [r3, #0]
  3a:   f7ff ffef   bl  1c <bounce>
  3e:   2000        movs    r0, #0
  40:   bc10        pop {r4}
  42:   bc02        pop {r1}
  44:   4708        bx  r1
  46:   46c0        nop         ; (mov r8, r8)
  48:   20000008    andcs   r0, r0, r8

Disassembly of section .data:

20000000 <c>:
20000000:   00000005    andeq   r0, r0, r5

20000004 <b>:
20000004:   00000004    andeq   r0, r0, r4

Disassembly of section .bss:

20000008 <a>:
20000008:   00000000    andeq   r0, r0, r0

Hier sind die Dinge, nach denen wir in diesem Experiment suchen (beachten Sie keinen Grund, tatsächlich Code zu laden oder auszuführen ... kennen Sie Ihre Tools, lernen Sie sie)

  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

Was wir hier also gelernt haben, ist, dass die Position von Variablen in GNU-Linker-Skripten sehr sensibel ist. Beachten Sie die Position von data_rom_start gegenüber data_start , aber warum funktioniert data_end ? Ich lasse Sie das herausfinden. Ich verstehe bereits, warum man sich vielleicht nicht mit Linker-Skripten herumschlagen und einfach zur einfachen Programmierung kommen möchte ...

Eine andere Sache, die wir hier gelernt haben, ist, dass der Linker data_rom_start für uns ausgerichtet hat, wir brauchten dort kein ALIGN(4). Sollen wir davon ausgehen, dass das immer funktioniert?

Beachten Sie auch, dass es auf dem Weg nach draußen aufgefüllt wurde, wir haben 5 Bytes .data, aber es wurde auf 8 aufgefüllt. Ohne ALIGN () können wir die Kopie bereits mit Wörtern erstellen. Könnte das auf der Grundlage dessen, was wir heute mit dieser Toolchain auf meinem Computer sehen, für die Vergangenheit und Zukunft zutreffen? Wer weiß, selbst wenn die ALIGNs regelmäßig überprüfen müssen, ob eine neue Version nicht kaputt gegangen ist, werden sie das von Zeit zu Zeit tun.

Lassen Sie uns von diesem Experiment zu diesem übergehen, nur um sicher zu gehen.

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(4);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   . = ALIGN(4);
   __data_end__ = .;
   } > ted AT > bob
   __data_size__ = __data_end__ - __data_start__;

   . = ALIGN(4);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   . = ALIGN(4);
   __bss_end__ = .;
   } > ted
   __bss_size__ = __bss_end__ - __bss_start__;

}

Bewegen Sie die Enden nach innen, um mit dem übereinzustimmen, was andere Leute tun. Und das hat nichts geändert:

0000001c <bounce>:
  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

Noch ein Schnelltest:

.globl bounce
bounce:
    nop
    bx lr

geben

0000001c <bounce>:
  1c:   46c0        nop         ; (mov r8, r8)
  1e:   4770        bx  lr
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

keine Notwendigkeit, zwischen Bounce und .align zu paddeln

Ohh, stimmt, ich erinnere mich jetzt, warum ich das _end__ nicht hineinstecke. weil es NICHT FUNKTIONIERT.

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(4);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   . = ALIGN(4);
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

   . = ALIGN(4);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   . = ALIGN(4);
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

}

etwas einfacher, aber sehr portabler Code, um dieses Linker-Skript zu heiraten

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
bss_zero:
    stmia r1!,{r2}
    sub r0,#4
    bne bss_zero
bss_zero_done:

    ldr r0,dlen
    cmp r0,#0
    beq data_copy_done
    ldr r1,rstart
    ldr r2,dstart
data_copy:
    ldmia r1!,{r3}
    stmia r2!,{r3}
    sub r0,#4
    bne data_copy
data_copy_done:

    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    nop
    bx lr

.align
bstart: .word __bss_start__
blen:   .word __bss_size__
rstart: .word __data_rom_start__
dstart: .word __data_start__
dlen:   .word __data_size__

geben

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000003d    andeq   r0, r0, sp, lsr r0
   c:   0000003d    andeq   r0, r0, sp, lsr r0
  10:   0000003d    andeq   r0, r0, sp, lsr r0

00000014 <reset>:
  14:   480c        ldr r0, [pc, #48]   ; (48 <blen>)
  16:   2800        cmp r0, #0
  18:   d004        beq.n   24 <bss_zero_done>
  1a:   490a        ldr r1, [pc, #40]   ; (44 <bstart>)
  1c:   2200        movs    r2, #0

0000001e <bss_zero>:
  1e:   c104        stmia   r1!, {r2}
  20:   3804        subs    r0, #4
  22:   d1fc        bne.n   1e <bss_zero>

00000024 <bss_zero_done>:
  24:   480b        ldr r0, [pc, #44]   ; (54 <dlen>)
  26:   2800        cmp r0, #0
  28:   d005        beq.n   36 <data_copy_done>
  2a:   4908        ldr r1, [pc, #32]   ; (4c <rstart>)
  2c:   4a08        ldr r2, [pc, #32]   ; (50 <dstart>)

0000002e <data_copy>:
  2e:   c908        ldmia   r1!, {r3}
  30:   c208        stmia   r2!, {r3}
  32:   3804        subs    r0, #4
  34:   d1fb        bne.n   2e <data_copy>

00000036 <data_copy_done>:
  36:   f000 f80f   bl  58 <centry>
  3a:   e7ff        b.n 3c <done>

0000003c <done>:
  3c:   e7fe        b.n 3c <done>

0000003e <bounce>:
  3e:   46c0        nop         ; (mov r8, r8)
  40:   4770        bx  lr
  42:   46c0        nop         ; (mov r8, r8)

00000044 <bstart>:
  44:   20000008    andcs   r0, r0, r8

00000048 <blen>:
  48:   00000004    andeq   r0, r0, r4

0000004c <rstart>:
  4c:   00000074    andeq   r0, r0, r4, ror r0

00000050 <dstart>:
  50:   20000000    andcs   r0, r0, r0

00000054 <dlen>:
  54:   00000008    andeq   r0, r0, r8

00000058 <centry>:
  58:   2207        movs    r2, #7
  5a:   b510        push    {r4, lr}
  5c:   4b04        ldr r3, [pc, #16]   ; (70 <centry+0x18>)
  5e:   2007        movs    r0, #7
  60:   601a        str r2, [r3, #0]
  62:   f7ff ffec   bl  3e <bounce>
  66:   2000        movs    r0, #0
  68:   bc10        pop {r4}
  6a:   bc02        pop {r1}
  6c:   4708        bx  r1
  6e:   46c0        nop         ; (mov r8, r8)
  70:   20000008    andcs   r0, r0, r8

Disassembly of section .data:

20000000 <c>:
20000000:   00000005    andeq   r0, r0, r5

20000004 <b>:
20000004:   00000004    andeq   r0, r0, r4

Disassembly of section .bss:

20000008 <a>:
20000008:   00000000    andeq   r0, r0, r0

wir können dort anhalten oder weiterfahren. Wenn wir in der gleichen Reihenfolge wie das Linker-Skript initialisieren, ist es in Ordnung, wenn wir zum nächsten Ding übergehen, da wir noch nicht dort angekommen sind. und stm/ldm sind nur erforderlich/gewünscht, um wortausgerichtete Adressen zu verwenden. Wenn Sie also ändern zu:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
    mov r3,#0
    mov r4,#0
    mov r5,#0
bss_zero:
    stmia r1!,{r2,r3,r4,r5}
    sub r0,#16
    ble bss_zero
bss_zero_done:

mit bss zuerst im Linker-Skript, und ja, du willst ble, nicht bls.

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   00000043    andeq   r0, r0, r3, asr #32
   c:   00000043    andeq   r0, r0, r3, asr #32
  10:   00000043    andeq   r0, r0, r3, asr #32

00000014 <reset>:
  14:   480d        ldr r0, [pc, #52]   ; (4c <blen>)
  16:   2800        cmp r0, #0
  18:   d007        beq.n   2a <bss_zero_done>
  1a:   490b        ldr r1, [pc, #44]   ; (48 <bstart>)
  1c:   2200        movs    r2, #0
  1e:   2300        movs    r3, #0
  20:   2400        movs    r4, #0
  22:   2500        movs    r5, #0

00000024 <bss_zero>:
  24:   c13c        stmia   r1!, {r2, r3, r4, r5}
  26:   3804        subs    r0, #4
  28:   ddfc        ble.n   24 <bss_zero>

0000002a <bss_zero_done>:
  2a:   480b        ldr r0, [pc, #44]   ; (58 <dlen>)
  2c:   2800        cmp r0, #0
  2e:   d005        beq.n   3c <data_copy_done>
  30:   4907        ldr r1, [pc, #28]   ; (50 <rstart>)
  32:   4a08        ldr r2, [pc, #32]   ; (54 <dstart>)

00000034 <data_copy>:
  34:   c978        ldmia   r1!, {r3, r4, r5, r6}
  36:   c278        stmia   r2!, {r3, r4, r5, r6}
  38:   3810        subs    r0, #16
  3a:   ddfb        ble.n   34 <data_copy>

0000003c <data_copy_done>:
  3c:   f000 f80e   bl  5c <centry>
  40:   e7ff        b.n 42 <done>

00000042 <done>:
  42:   e7fe        b.n 42 <done>

00000044 <bounce>:
  44:   46c0        nop         ; (mov r8, r8)
  46:   4770        bx  lr

00000048 <bstart>:
  48:   20000000    andcs   r0, r0, r0

0000004c <blen>:
  4c:   00000004    andeq   r0, r0, r4

00000050 <rstart>:
  50:   20000004    andcs   r0, r0, r4

00000054 <dstart>:
  54:   20000004    andcs   r0, r0, r4

00000058 <dlen>:
  58:   00000008    andeq   r0, r0, r8

0000005c <centry>:
  5c:   2207        movs    r2, #7
  5e:   b510        push    {r4, lr}
  60:   4b04        ldr r3, [pc, #16]   ; (74 <centry+0x18>)
  62:   2007        movs    r0, #7
  64:   601a        str r2, [r3, #0]
  66:   f7ff ffed   bl  44 <bounce>
  6a:   2000        movs    r0, #0
  6c:   bc10        pop {r4}
  6e:   bc02        pop {r1}
  70:   4708        bx  r1
  72:   46c0        nop         ; (mov r8, r8)
  74:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

20000004 <c>:
20000004:   00000005    andeq   r0, r0, r5

20000008 <b>:
20000008:   00000004    andeq   r0, r0, r4

Diese Schleifen gehen schneller. Jetzt weiß ich nicht, ob die Ahb-Busse 64 Bit breit sein können oder nicht, aber für einen Arm voller Größe möchten Sie diese Dinge an 64-Bit-Grenzen ausrichten. Ein LDM/STM mit vier Registern an einer 32-Bit-Grenze, aber nicht an einer 64-Bit-Grenze, wird zu drei separaten Bustransaktionen, wobei eine einzelne Transaktion an einer 64-Bit-Grenze ausgerichtet ist, wodurch mehrere Takte pro Befehl eingespart werden.

Da wir Baremetal machen und wir die volle Verantwortung für alles tragen, können wir zuerst bss und dann Daten setzen. Wenn wir dann einen Haufen haben, wächst der Stack von oben nach unten Der richtige Ort, das ist in Ordnung, wir verwenden diesen Speicher noch nicht. dann kopieren wir .data rüber und können in den Haufen übergehen, das ist in Ordnung, Haufen oder nicht, es gibt viel Platz für den Stapel, so dass wir auf niemanden/etwas treten (solange wir im Linker-Skript sicherstellen, dass wir das tun). Wenn es Bedenken gibt, machen Sie die ALIGN () größer, damit wir immer innerhalb unseres Bereichs für diese Füllungen sind.

Also meine einfache Lösung, nimm es oder lass es. Willkommen, um Fehler zu beheben, ich habe dies weder auf Hardware noch auf meinem Simulator ausgeführt ...

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(8);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   . = ALIGN(4);
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

   . = ALIGN(8);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   . = ALIGN(4);
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

}



.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
    mov r3,#0
    mov r4,#0
    mov r5,#0
bss_zero:
    stmia r1!,{r2,r3,r4,r5}
    sub r0,#16
    ble bss_zero
bss_zero_done:

    ldr r0,dlen
    cmp r0,#0
    beq data_copy_done
    ldr r1,rstart
    ldr r2,dstart
data_copy:
    ldmia r1!,{r3,r4,r5,r6}
    stmia r2!,{r3,r4,r5,r6}
    sub r0,#16
    ble data_copy
data_copy_done:

    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    nop
    bx lr

.align
bstart: .word __bss_start__
blen:   .word __bss_size__
rstart: .word __data_rom_start__
dstart: .word __data_start__
dlen:   .word __data_size__


void bounce ( unsigned int );

unsigned int a;

unsigned int b=4;
unsigned char c=5;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

arm-none-eabi-as --warn --fatal-warnings flash.s -o flash.o
arm-none-eabi-ld -o hello.elf -T flash.ld flash.o centry.o
arm-none-eabi-objdump -D hello.elf > hello.list
arm-none-eabi-objcopy hello.elf hello.bin -O binary

alles zusammen und du bekommst:

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   00000043    andeq   r0, r0, r3, asr #32
   c:   00000043    andeq   r0, r0, r3, asr #32
  10:   00000043    andeq   r0, r0, r3, asr #32

00000014 <reset>:
  14:   480d        ldr r0, [pc, #52]   ; (4c <blen>)
  16:   2800        cmp r0, #0
  18:   d007        beq.n   2a <bss_zero_done>
  1a:   490b        ldr r1, [pc, #44]   ; (48 <bstart>)
  1c:   2200        movs    r2, #0
  1e:   2300        movs    r3, #0
  20:   2400        movs    r4, #0
  22:   2500        movs    r5, #0

00000024 <bss_zero>:
  24:   c13c        stmia   r1!, {r2, r3, r4, r5}
  26:   3810        subs    r0, #16
  28:   ddfc        ble.n   24 <bss_zero>

0000002a <bss_zero_done>:
  2a:   480b        ldr r0, [pc, #44]   ; (58 <dlen>)
  2c:   2800        cmp r0, #0
  2e:   d005        beq.n   3c <data_copy_done>
  30:   4907        ldr r1, [pc, #28]   ; (50 <rstart>)
  32:   4a08        ldr r2, [pc, #32]   ; (54 <dstart>)

00000034 <data_copy>:
  34:   c978        ldmia   r1!, {r3, r4, r5, r6}
  36:   c278        stmia   r2!, {r3, r4, r5, r6}
  38:   3810        subs    r0, #16
  3a:   ddfb        ble.n   34 <data_copy>

0000003c <data_copy_done>:
  3c:   f000 f80e   bl  5c <centry>
  40:   e7ff        b.n 42 <done>

00000042 <done>:
  42:   e7fe        b.n 42 <done>

00000044 <bounce>:
  44:   46c0        nop         ; (mov r8, r8)
  46:   4770        bx  lr

00000048 <bstart>:
  48:   20000000    andcs   r0, r0, r0

0000004c <blen>:
  4c:   00000004    andeq   r0, r0, r4

00000050 <rstart>:
  50:   20000008    andcs   r0, r0, r8

00000054 <dstart>:
  54:   20000004    andcs   r0, r0, r4

00000058 <dlen>:
  58:   00000008    andeq   r0, r0, r8

0000005c <centry>:
  5c:   2207        movs    r2, #7
  5e:   b510        push    {r4, lr}
  60:   4b04        ldr r3, [pc, #16]   ; (74 <centry+0x18>)
  62:   2007        movs    r0, #7
  64:   601a        str r2, [r3, #0]
  66:   f7ff ffed   bl  44 <bounce>
  6a:   2000        movs    r0, #0
  6c:   bc10        pop {r4}
  6e:   bc02        pop {r1}
  70:   4708        bx  r1
  72:   46c0        nop         ; (mov r8, r8)
  74:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

20000004 <c>:
20000004:   00000005    andeq   r0, r0, r5

20000008 <b>:
20000008:   00000004    andeq   r0, r0, r4

Beachten Sie, dass dies mit arm-none-eabi- und arm-linux-gnueabi und den anderen Varianten funktioniert, da kein Ghee-Whiz-Zeug verwendet wurde.

Wenn Sie sich umsehen, werden Sie feststellen, dass die Leute mit Ghee-Whiz-Zeug in ihren Linker-Skripten verrückt werden, riesige monströse Küchenspülen-Dinge. Es ist besser, einfach zu wissen, wie es geht (oder besser, wie man die Werkzeuge beherrscht, damit Sie kontrollieren können, was vor sich geht), anstatt sich auf das Zeug von jemand anderem zu verlassen und nicht zu wissen, wo es kaputt gehen wird, weil Sie es nicht verstehen und/oder recherchieren wollen Es.

Als allgemeine Regel sollten Sie eine Sprache nicht mit derselben Sprache booten (Bootstrap in diesem Sinne bedeutet, dass Code ausgeführt wird, der keinen Compiler mit demselben Compiler kompiliert). Sie möchten eine einfachere Sprache mit weniger Bootstrap verwenden. Aus diesem Grund wird C in Assembler ausgeführt, es gibt keine Bootstrap-Anforderungen, Sie beginnen einfach mit der ersten Anweisung nach dem Zurücksetzen. JAVA, sicher, Sie könnten das jvm in C schreiben und dieses C mit asm booten, dann das JAVA booten, wenn Sie mit C wollen, aber auch das JAVA in C ausführen.

Da wir Annahmen zu diesen Kopierschleifen kontrollieren, sind sie per Definition straffer und sauberer als handabgestimmtes Memcpy/Memset.

Beachten Sie, dass Ihr anderes Problem folgendes war:

unsigned int * bss_start_p = &_BSS_START; 
unsigned int * bss_end_p = &_BSS_END;

Wenn diese lokal sind, kein Problem, wenn diese global sind, müssen Sie zuerst .data initialisieren, damit sie funktionieren, und wenn Sie diesen Trick versuchen, .data zu tun, werden Sie scheitern. Lokale Variablen, gut, das wird funktionieren. Wenn Sie sich aus irgendeinem Grund entschieden haben, die statischen Einheimischen (lokale Globals, die ich gerne nenne) zu machen, dann sind Sie wieder in Schwierigkeiten. Jedes Mal, wenn Sie eine Aufgabe in einer Erklärung ausführen, sollten Sie darüber nachdenken, wie das implementiert wird und ob es sicher / vernünftig ist. Jedes Mal, wenn Sie davon ausgehen, dass eine Variable Null ist, wenn sie nicht deklariert ist, gilt dasselbe, wenn eine lokale Variable nicht als Null angenommen wird, wenn sie global ist. Wenn Sie nie davon ausgehen, dass sie Null sind, müssen Sie sich keine Sorgen machen.

großartig, dies ist das zweite Mal, dass ich die maximale Zeichenanzahl in einer Antwort überschritten habe ....
Diese Frage gehört zum Stapelüberlauf, nicht zur Elektrotechnik.
Sich in Ihrer Frage auf einen externen Link zu verlassen, ist auch kein guter Stil. Wenn der Link vor der Frage verschwindet, ergibt die Frage möglicherweise keinen Sinn.
In diesem Fall reichen Ihr Titel und Inhalt aus, um zu wissen, dass Sie versuchen, C auf einem bestimmten Mikrocontroller zu booten, und in die Initialisierung von .bss und .data wandern
wurden aber in diesem Fall von einer ansonsten sehr informativen Website in die Irre geführt.
Darüber hinaus ist die Verwendung von C-Bibliotheksaufrufen in Baremetal ein Problem. Ein großer Prozentsatz der C-Bibliotheksaufrufe erfordert ein System. Baremetal bedeutet per Definition kein System. Selbst wenn Sie sich auf Aufrufe beschränken, die kein System erfordern, müssen Sie möglicherweise mehr als nur diese schlucken, und Sie müssen möglicherweise zusätzliche Arbeit leisten, um die Aufrufe zu erhalten, die Sie nicht zum Erstellen verwenden, damit sie Ihre Binärdatei ohne Erstellungsfehler aufblähen können .
Baremetal bedeutet, dass Sie sich darauf beschränken, welche Sprachunterstützung Sie haben oder an die Sie gewöhnt sind. Mit diesem Gedanken hören Sie auf, .data zu verwenden und davon auszugehen, dass .bss null ist, und Sie können das Leben so viel einfacher machen. zwischen einer und fünf Zeilen Bootstrap-Code. Erledigt. Alles C danach, schönes sauberes reines C, keine Systemsorgen/Schmerzen/Anrufe, die im Weg stehen.
Hier gibt es viele nützliche Informationen! Vielen Dank, dass Sie sich die Zeit genommen haben, diese Antwort zu schreiben :-)
kein Problem, viel Spaß.