He desarrollado, inspirado a partir de aquí , un código de inicio básico para arm cortex M3. Sin embargo, me encuentro con el siguiente problema: supongamos que declaro una variable global no inicializada, digamos de tipo char sin firmar en main.c
#include ...
unsigned char var;
...
int main()
{
...
}
esto hace que la región .bss en STM32 f103 comience en _BSS_START=0x20000000 y finalice en _BSS_END = 0x20000001. Ahora, el código de inicio
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++;
}
intenta inicializar a cero toda la región .bss. Sin embargo, dentro de ese ciclo while, el puntero aumenta con 4 bytes, por lo tanto, después de un paso bss_start_p = 0x20000004, por lo tanto, siempre será diferente de bss_end_p, lo que conduce a un ciclo infinito, etc.
¿Hay alguna solución estándar para esto? ¿Se supone que debo "forzar" de alguna manera la dimensión de la región .bss para que sea un múltiplo de 4? ¿O debería usar un puntero a char sin firmar para caminar a través de la región .bss? Tal vez algo como:
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++;
}
```
Como sospecha, esto sucede porque el tipo de datos int sin firmar tiene un tamaño de 4 bytes. Cada *bss_start_p = 0;
declaración borra cuatro bytes del área bss.
El rango de memoria bss debe alinearse correctamente. Simplemente puede definir _BSS_START y _BSS_END para que el tamaño total sea un múltiplo de cuatro, pero esto generalmente se maneja permitiendo que la secuencia de comandos del enlazador defina las ubicaciones de inicio y finalización.
Como ejemplo, aquí está la sección del enlazador en uno de mis proyectos:
.bss (NOLOAD) : ALIGN(4)
{
__bss_start__ = .;
*(.bss)
. = ALIGN(4);
__bss_end__ = .;
} >RAM
Las ALIGN(4)
declaraciones se encargan de las cosas.
Además, es posible que desee cambiar
while(bss_start_p != bss_end_p)
a
while(bss_start_p < bss_end_p)
.
Esto no evitará el problema (ya que podrías estar borrando de 1 a 3 bytes más de lo que deseas), pero podría minimizar el impacto :)
while(bss_start_p < bss_end_p - 1)
seguido de una limpieza de bytes del rango de memoria restante eliminaría la última preocupación.La solución estándar es memset()
:
#include <string.h>
memset(&_BSS_START, 0, &_BSS_END - &_BSS_START)
Si no puede usar la biblioteca estándar, entonces tendrá que decidir si está bien en su caso redondear el tamaño del área de memoria hasta 4 bytes y continuar usando unsigned int *
; o si necesita ser estricto al respecto, en cuyo caso necesitaría usar unsigned char *
.
Si redondea el tamaño, como en su primer ciclo, entonces bss_start_p
puede terminar siendo mayor que, bss_end_p
pero eso es fácil de manejar con una comparación menor que <
en lugar de una prueba de desigualdad.
Por supuesto, también podría llenar la mayor parte del área de la memoria con transferencias de 32 bits, y solo los últimos bytes con transferencias de 8 bits, pero eso es más trabajo por poca ganancia, particularmente aquí cuando es solo una pieza de código de inicio.
memset()
. Pero la alineación a 4 bytes es más o menos imprescindible. Entonces por qué no hacerlo?memset()
, y C es lo que parecen estar programando. La implementación simple de memset()
también es más o menos ese bucle, no es como si dependiera de mucho más. Dado que es un microcontrolador, también asumo que no hay un enlace dinámico o algo así (y mirando el enlace, no lo hay, es solo una llamada main()
después de ese ciclo de puesta a cero), por lo que el compilador debería ser capaz de entrar memset()
allí junto con cualquier otra función (o para implementarlo en línea)..bss
, todo debería funcionar.main()
en la parte inferior de la función de inicio que, por ejemplo, borra bss
. Ahora, suponiendo que uno pueda llamar memset()
desde main()
¿qué hace la diferencia mágica aquí? Parece solo una llamada de función, no puedo decir cómo marcaría la diferencia.stdin
y otros, y organizarse argc
y argv
estar disponible para main
, pero no creo que se usen comúnmente en MCU. (Es posible que haya olvidado algo, claro). Y te das cuenta de que ya están usando C en la función de inicio, ¿sí? Si cree que, para empezar, no deberían hacer eso, publique una respuesta en ese sentido (oh, veo que ya lo hizo, bien).Solo cambia !=
a <
. Ese suele ser un mejor enfoque de todos modos, ya que trata problemas como este.
Hay innumerables otros sitios y ejemplos. Muchos miles, si no decenas de miles. Existen las conocidas bibliotecas c con scripts de enlace y código boostrap, newlib, glibc en particular, pero hay otras que puede encontrar. Bootstraping C con C no tiene sentido.
Su pregunta ha sido respondida, está tratando de hacer una comparación exacta de cosas que podrían no ser exactas, podría no comenzar en un límite conocido o terminar en un límite conocido. Por lo tanto, puede hacer menos que, pero si el código no funcionó con una comparación exacta, eso significa que está poniendo a cero más allá de .bss en la siguiente sección, lo que puede o no causar que sucedan cosas malas, así que simplemente reemplace con menos que no. la solución.
Así que aquí va TL; DR está bien. No inicias un idioma con ese idioma, puedes salirte con la tuya seguro, pero estás jugando con fuego cuando haces eso. Si recién está aprendiendo a hacer esto, debe tener cuidado, no la suerte tonta o los hechos que aún no ha descubierto.
El script del enlazador y el código de arranque tienen una relación muy íntima, están casados, unidos por la cadera, no se desarrolla uno sin el otro, lo que lleva a un fracaso masivo. Y desafortunadamente, el script del enlazador está definido por el enlazador y el lenguaje ensamblador definido por el ensamblador, por lo que, a medida que cambie las cadenas de herramientas, tendrá que volver a escribir ambos. ¿Por qué lenguaje ensamblador? No necesita arranque, los lenguajes compilados generalmente lo necesitan. C lo hace si no desea limitar su uso del idioma. Comenzaré con algo muy simple que tiene requisitos mínimos específicos de la cadena de herramientas, no asuma que las variables .bss son cero (hace que el código sea menos legible si la variable nunca se inicializa en ese idioma , intente evitar esto, no es cierto para las variables locales, por lo que debe estar al tanto de cuándo lo usa. Entonces, ¿por qué estamos hablando de .bss y .data? (las variables globales son buenas para el trabajo de este nivel, pero ese es otro tema)) la otra regla para la solución simple es no inicializar variables en la declaración, hacerlo en el código. sí, quema más flash, generalmente tiene mucho, no todas las variables se inicializan con constantes de todos modos que terminan consumiendo instrucciones.
Puede notar por el diseño de cortex-m que pueden haber estado pensando que no hay ningún código de arranque, por lo que no hay compatibilidad con .data ni .bss. La mayoría de las personas que usan globales no pueden vivir sin ellos, así que aquí va:
Podría hacer esto más mínimo pero un ejemplo funcional mínimo para todos los cortex-ms usando la cadena de herramientas gnu, no recuerdo qué versiones puede comenzar con 5.xx o más hasta el 9.xx actual. Cambié los scripts del enlazador en algún lugar alrededor de 3. xx o 4.xx a medida que aprendí más y como gnu cambié algo que rompió mi primero.
oreja:
.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
punto de entrada en el código C:
void bounce ( unsigned int );
unsigned int a;
int centry ( void )
{
a = 7;
bounce(a);
return(0);
}
guión del enlazador.
MEMORY
{
rom : ORIGIN = 0x00000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
Todos estos podrían ser más pequeños y seguir funcionando, agregue algunas cosas adicionales aquí solo para verlo en el trabajo.
compilación y enlace optimizados.
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
para algunos proveedores, desea usar 0x08000000 o 0x01000000 u otras direcciones similares, ya que el flash se asigna allí y se refleja en 0x00000000 en algunos modos de arranque. algunos solo tienen una parte del flash reflejada en 0x00000000, por lo que desea que la tabla de vectores apunte al espacio flash de la aplicación, no a cero. ya que está basado en tablas vectoriales, todo funciona.
primero tenga en cuenta que los cortex-ms son solo máquinas de pulgar y, por alguna razón, impusieron una dirección de función de pulgar, lo que significa que lsbit es impar. Conozca sus herramientas, las directivas .thumb_func le dicen al ensamblador gnu que la siguiente etiqueta es una dirección de función de pulgar. hacer el +1 en la tabla conducirá al fracaso, no caiga en la tentación de hacerlo, hágalo bien. hay otras formas de ensamblador gnu para declarar una función, este es el enfoque mínimo.
4: 00000015
8: 0000001b
c: 0000001b
10: 0000001b
no arrancará si no obtienes la tabla de vectores correctamente.
Podría decirse que solo necesita el vector del puntero de la pila (puede poner cualquier cosa allí si desea configurar el puntero de la pila usted mismo en el código) y el vector de reinicio. Puse cuatro aquí sin ninguna razón en particular. Suele poner 16 pero quería acortar este ejemplo.
Entonces, ¿qué es lo mínimo que debe hacer un bootstrap de C? 1. establecer el puntero de la pila 2. cero .bss 3. copiar .data 4. bifurcarse o llamar al punto de entrada C
el punto de entrada de C suele llamarse main(). pero algunas cadenas de herramientas ven main() y agregan basura adicional a su código. Intencionalmente uso un nombre diferente. YMMV.
la copia de .data no es necesaria si todo está basado en ram. al ser un microcontrolador cortex-m, es técnicamente posible pero poco probable, por lo que se necesita la copia de .data ... si hay .data.
Mi primer ejemplo y un estilo de codificación es no confiar en .data ni .bss, como en este ejemplo. Arm se encargó del puntero de la pila, por lo que lo único que queda es llamar al punto de entrada. Me gusta tenerlo para que el punto de entrada pueda regresar, muchas personas argumentan que nunca deberías hacer eso. podrías hacer esto entonces:
.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word centry
.word done
.word done
.word done
y no regresar de centry () y no tener código de controlador de reinicio.
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
el enlazador ha puesto las cosas donde le pedimos. Y en general tenemos un programa totalmente funcional.
Entonces, primero trabaje en el script del enlazador:
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__;
}
enfatizando que los nombres rom y ram no tienen significado, solo conectan los puntos para el enlace entre secciones.
.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__
agregue algunos elementos para que podamos ver lo que hicieron las herramientas
void bounce ( unsigned int );
unsigned int a;
unsigned int b=4;
unsigned char c=5;
int centry ( void )
{
a = 7;
bounce(a);
return(0);
}
agregue algunos artículos para colocar en esas secciones. y obten
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
aquí están las cosas que estamos buscando en ese experimento (no tenga en cuenta ninguna razón para cargar o ejecutar ningún código... conozca sus herramientas, apréndalas)
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
Entonces, lo que aprendimos aquí es que la posición de las variables es muy sensible en los scripts de gnu linker. tenga en cuenta la posición de data_rom_start frente a data_start , pero ¿por qué funciona data_end ? Dejaré que lo averigües. Ya entiendo por qué uno podría no querer tener que meterse con los scripts del enlazador y simplemente llegar a la programación simple ...
así que otra cosa que aprendimos aquí es que el enlazador alineó data_rom_start para nosotros, no necesitábamos un ALIGN(4) allí. ¿Deberíamos suponer que eso siempre funcionará?
También tenga en cuenta que se rellenó al salir, tenemos 5 bytes de .data pero lo rellenó a 8. Sin ningún ALIGN(), ya podemos hacer la copia usando palabras. Según lo que vemos con esta cadena de herramientas en mi computadora hoy, ¿podría ser cierto para el pasado y el futuro? Quién sabe, incluso con los ALIGN que deben verificar periódicamente para confirmar que alguna versión nueva no rompió las cosas, lo harán de vez en cuando.
de ese experimento pasemos a esto solo para estar seguros.
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__;
}
moviendo los extremos hacia adentro para que sea consistente con lo que hacen otras personas. Y eso no lo cambió:
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
una prueba rápida más:
.globl bounce
bounce:
nop
bx lr
donación
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
no es necesario rellenar entre rebote y .align
Ohh, claro, ahora recuerdo por qué no puse el _fin__ dentro. porque NO FUNCIONA.
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__;
}
un código simple, pero muy portátil para casarse con este script de enlace
.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__
donación
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
podemos parar allí o seguir adelante. Si inicializamos en el mismo orden que la secuencia de comandos del enlazador, está bien si pasamos a lo siguiente, ya que aún no hemos llegado allí. y stm/ldm solo se requieren/desean usar direcciones alineadas con palabras, por lo que si cambia a:
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:
con bss primero en el script del enlazador, y sí, quiere ble, no 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
esos bucles irán más rápido. ahora no sé si los buses ahb pueden tener 64 bits de ancho o no, pero para un brazo de tamaño completo, querrá alinear estas cosas en los límites de 64 bits. un ldm/stm de cuatro registros en un límite de 32 bits pero no en un límite de 64 bits se convierte en tres transacciones de bus separadas, donde alineadas en un límite de 64 bits hay una única transacción que ahorra varios relojes por instrucción.
ya que estamos haciendo baremetal y somos completamente responsables de todo lo que podemos poner, digamos bss primero, luego datos, luego, si tenemos un montón, entonces la pila crece de arriba hacia abajo, por lo que si ponemos a cero bss y derramamos algo siempre que comencemos en el lugar correcto que está bien, no estamos usando esa memoria todavía. luego copiamos .data y podemos verterlos en el montón, eso está bien, montón o no, hay mucho espacio para la pila, por lo que no estamos pisando a nadie/nada (siempre y cuando nos aseguremos de que lo hagamos en el script del enlazador). si existe alguna preocupación, haga que ALIGN() sea más grande para que estemos siempre dentro de nuestro espacio para estos rellenos.
así que mi solución simple, tómalo o déjalo. Bienvenido a corregir cualquier error, no ejecuté esto en el hardware ni en mi simulador...
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
Ponlo todo junto y obtienes:
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
tenga en cuenta que esto funciona con arm-none-eabi- y arm-linux-gnueabi y las otras variantes ya que no se usaron cosas ghee whiz.
Descubrirás cuando mires a tu alrededor que la gente se volverá loca con las cosas geniales de ghee en sus scripts de enlace, cosas enormes y monstruosas del fregadero de la cocina. Es mejor simplemente saber cómo hacerlo (o mejor cómo dominar las herramientas para que pueda controlar lo que sucede) en lugar de confiar en las cosas de otra persona y no saber dónde se va a romper porque no entiende y/o quiere investigar él.
como regla general, no arranque un lenguaje con el mismo lenguaje (bootstrap en este sentido significa ejecutar código sin compilar un compilador con el mismo compilador) desea usar un lenguaje más simple con menos arranque. Es por eso que C se realiza en ensamblaje, no tiene requisitos de arranque, solo comienza desde la primera instrucción después del reinicio. JAVA, seguro que puede escribir el jvm en C y arrancar ese C con asm y luego arrancar el JAVA si lo desea con C, pero también ejecutar el JAVA en C también.
Debido a que controlamos las suposiciones sobre estos bucles de copia, son, por definición, más estrictos y limpios que memcpy/memset ajustados a mano.
Tenga en cuenta que su otro problema fue este:
unsigned int * bss_start_p = &_BSS_START;
unsigned int * bss_end_p = &_BSS_END;
si estos son locales bien, no hay problema, si estos son globales, entonces necesita .data inicializados primero para que funcionen y si intenta ese truco para hacer .data, entonces fallará. Variables locales, bien, eso funcionará. si por alguna razón decidiste hacer locales estáticos (globales locales como me gusta llamarlos) entonces estás nuevamente en problemas. Sin embargo, cada vez que realiza una tarea en una declaración, debe pensar en ello, cómo se implementa y si es seguro/sano. Cada vez que asume que una variable es cero cuando no se declara, el mismo trato, si una variable local no se supone que es cero, si es global, entonces lo es. si nunca asume que son cero, entonces nunca tendrá que preocuparse.
viejo contador de tiempo
viejo contador de tiempo