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++;
}
```
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 :)
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_p
kann dies tatsächlich größer als sein, bss_end_p
aber 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.
memset()
. Aber die Ausrichtung auf 4 Bytes ist mehr oder weniger ein Muss. Warum also nicht?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)..bss
, sollte alles funktionieren.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.stdin
dafü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.)
argc
argv
main
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.
Oldtimer
Oldtimer