Programación de SRAM sobre SWD

Estoy intentando programar un microcontrolador basado en Cortex M3 sobre SWD, más o menos desde los primeros principios. He escrito una biblioteca de interfaz SWD y la tengo funcionando en un segundo micro.

He estado siguiendo en gran medida esta publicación de blog: http://markdingst.blogspot.ie/2014/03/programming-internal-sram-over-swd.html

Además de esta nota de aplicación de SiLabs: https://www.silabs.com/Support%20Documents/TechnicalDocs/AN0062.pdf

Puedo detener el núcleo y leer y escribir SRAM. Me pregunto cómo obtener el código básico que puedo escribir en SRAM. ¿Puedo simplemente tomar el archivo .hex que genera el compilador y escribirlo en SRAM palabra por palabra a partir de 0x20000000?

Y si es así, ¿qué registros centrales debo configurar para que el nuevo programa en SRAM se ejecute al reiniciar? ¿Será suficiente configurar la PC a 0x20000000?

Agradezco todo y cualquier ayuda! ¡Gracias!

Respuestas (2)

Si tiene un script de vinculación que vincula su ejecutable a una dirección apropiada para ram, entonces podría interpretar el archivo hexadecimal y extraer los bytes de carga útil.

Pero probablemente sería más fácil usar arm-none-eabi-objcopy, es decir

arm-none-eabi-objcopy -O binary myproject.elf myproject.bin

Esto le dará un archivo binario sin procesar que puede escribir en el chip a partir de la dirección base que vinculó. Tenga en cuenta que al hacer las cosas de esta manera, la dirección base no se conserva en el archivo en sí ; deberá asegurarse de mantener la coherencia entre la creación del archivo y el uso del archivo. Aún así, esta es una opción bastante común: muchos programas de flasheo simples solo entienden binarios sin formato, con la dirección de destino dada explícitamente en la línea de comando.

Para ejecutar completamente desde la RAM, necesita un chip que le permita reubicar la tabla de vectores allí; algunos lo hacen y otros no. Si puede hacerlo de una manera que sobreviva al reinicio y tenga el vector de reinicio en la tabla apuntando a su código en RAM, puede funcionar. O podría escribir una tabla que apunte el vector de reinicio en la RAM a la ubicación habitual en flash. Tenga en cuenta que el uso de restablecer a RAM es un poco limitado, ya que normalmente no habría ningún programa para ejecutar, a menos que algo lo coloque allí. Y el algo puede hacer una pequeña limpieza y simplemente ramificarse a la dirección inicial.

Gracias por la respuesta, convertir a .bin era de hecho el paso que me faltaba. Utilicé la herramienta hex2bin para hacer esto, y por ahora estoy contento con usar el controlador flash para escribir este archivo binario para flashear en la dirección 0. Creo que consideraré escribir en SRAM nuevamente en una etapa posterior, y su ¡La descripción de hacerlo me será muy útil!

Ciertamente puedo apreciar el rollo de tus propios primeros principios, y aplaudir eso. Pero también recomiendo caminar antes de correr. El hecho de que quiera hacerlo todo usted mismo no significa que no pueda ni deba aprovechar lo que hay.

Está en el camino correcto, tal vez con sus herramientas o tal vez con las herramientas de otras personas, o tal vez usando un simulador de conjunto de instrucciones. Debe dominar (bueno, ser lo suficientemente bueno) usando una cadena de herramientas o un ensamblador si no quiere escribir en C o escribir su propio ensamblador, por lo que diría que lo haga, pero use un ensamblador y desensamblador que funcione como referencia/validación.

La corteza-m es una arquitectura pseudo harvard. Tienes que leer los documentos de tu parte en particular. Idealmente, el espacio del programa y el espacio de datos no se tocan, están en interfaces separadas, pero con estas partes puede descargar un programa para ram y simplemente ejecutarlo. Pero es específico del chip, recientemente se ocupó de esto. El lado de búsqueda puede usar una dirección diferente para llegar a la RAM como el lado de datos. Un ti msp432 cortex-m4 que estoy jugando con el ram está en 0x20000000 pero no parece que pueda ejecutar código allí, ese mismo sram está asignado en 0x01000000 y puede leer y escribir cualquier espacio de direcciones con transacciones de datos, pero tiene que ejecutar en 0x01000000 para que funcione.

Entonces, una vez que descubras eso, y una vez que descubras lo que necesitas hacer, escribe un programa y ensamblalo, entonces sí, es una cuestión "simple" de descargarlo y luego comenzar la ejecución con el contador del programa establecido en la dirección del punto de entrada. .

Dependiendo de qué parte esté usando, puede haber diferentes formas de ingresar, cargadores de arranque basados ​​​​en uart, swd, usb. Algunas placas de desarrollo aparecen como unidades flash extraíbles y usted simplemente copia su archivo .bin y una mcu de la parte frontal lo carga en la mcu de la parte trasera y lo reinicia.

En cuanto al estado de la máquina y lo que su programa necesita hacer eso depende de usted, no podrá cubrir todas las situaciones posibles (hardware bloqueado), por lo que lo primero es comenzar con calma y asumir que acaba de reiniciar por en realidad restableciendo, luego, dependiendo de cómo desee proceder, es posible que deba deshacer todo lo que haya hecho cualquier programa anterior. Generalmente, el firmware de mcu está en flash y el sistema arranca de la misma manera cada vez, por lo que es básicamente una cantidad conocida. Y la suposición aquí es que está utilizando ram para desarrollar un programa que finalmente estará en flash y no tendrá que lidiar con infinitas condiciones de inicio posibles.

El cortex-m arranca de manera que tiene la opción de configurar el puntero de pila en la tabla de vectores y luego la tabla de vectores se encarga de configurar la PC y luego su código lo toma desde allí. Puede elegir que su cargador configure el puntero de pila y el contador del programa, o puede elegir que su programa configure el puntero de pila. Más allá de esa configuración, el sp debería ponerte en marcha. Tenga en cuenta que al usar la instrucción bx (o pop o alguna otra lista corta de posibles instrucciones) el lsbit de la dirección debe configurarse para indicar que está cambiando o permaneciendo en modo pulgar. Ese bit es eliminado por la instrucción, el contador del programa en realidad no tiene ese bit configurado, y es posible que no sea posible configurarlo de todos modos, por lo que 0x20000000 o 0x01000000 deberían ser la dirección correcta, no 0x20000001 ni 0x01000001 IMO.

EDITAR

Uso de herramientas gnu

sram.s

.cpu cortex-m0
.thumb

.thumb_func
.global _start
_start:
    ldr r0,stacktop
    mov sp,r0
    bl notmain
    b hang

.thumb_func
hang:   b .

.align
stacktop: .word 0x20001000

.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr
.end

intermitente01.c

void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );

void notmain ( void )
{
    while(1)
    {
        PUT32(0x20000800,GET32(0x20000800)+1);
    }
}

sram.ld

MEMORY
{
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ram
    .rodata : { *(.rodata*) } > ram
    .bss : { *(.bss*) } > ram
}

Makefile

ARMGNU = arm-none-eabi
#ARMGNU = arm-linux-gnueabi
AOPS = --warn --fatal-warnings -mcpu=cortex-m0
COPS = -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding  -mcpu=cortex-m0
all : blinker01.gcc.thumb.sram.bin

clean:
    rm -f *.bin
    rm -f *.o
    rm -f *.elf
    rm -f *.list
    rm -f *.hex
    rm -f *.srec

sram.o : sram.s
    $(ARMGNU)-as $(AOPS) sram.s -o sram.o

blinker01.gcc.thumb.o : blinker01.c
    $(ARMGNU)-gcc $(COPS) -mthumb -c blinker01.c -o blinker01.gcc.thumb.o

blinker01.gcc.thumb.sram.bin : sram.ld sram.o blinker01.gcc.thumb.o
    $(ARMGNU)-ld -o blinker01.gcc.thumb.sram.elf -T sram.ld sram.o blinker01.gcc.thumb.o
    $(ARMGNU)-objdump -D blinker01.gcc.thumb.sram.elf > blinker01.gcc.thumb.sram.list
    $(ARMGNU)-objcopy blinker01.gcc.thumb.sram.elf blinker01.gcc.thumb.sram.hex -O ihex
    $(ARMGNU)-objcopy blinker01.gcc.thumb.sram.elf blinker01.gcc.thumb.sram.srec -O srec --srec-forceS3
    $(ARMGNU)-objcopy blinker01.gcc.thumb.sram.elf blinker01.gcc.thumb.sram.bin -O binary

Un ejemplo simple de asm es... más simple... siguió adelante y agregó un poco de C. Para un stm32 puede, por ejemplo, cargar y ejecutar esto en 0x20000000 como se muestra en el script del enlazador sram.ld. En otro chip (ti msp432, sé que es posible que deba cambiar ese origen a 0x01000000 y cargarlo allí).

Esto produce una serie de formatos para el binario, cada uno es relativamente fácil de analizar, por ejemplo, un srec:

S0200000626C696E6B657230312E6763632E7468756D622E7372616D2E7372656332
S315200000000248854600F008F8FFE7FEE700100020CA
S31520000010016070470068704710B50448FFF7FAFF83
S31520000020411C0248FFF7F4FFF7E7C046000800200E
S70520000000DA

(sugerencia, este es el srec real, puede extraerlo e intentar cargarlo y ejecutarlo)

y úselo para cargar datos en su chip a través de swd y luego ejecútelo. Incluso te da un punto de entrada en el srec. elf, ihex también hacen esto. el archivo de imagen binaria sin procesar que acaba de leer y empujar.

Este ejemplo en particular puede cargar y luego dejar de verificar 0x20000800 y ver qué valor es, luego reanudar y luego detenerse nuevamente y ver que está cambiando. si necesita programas de prueba mucho más simples, diga varios nops y luego un ciclo infinito, puede ver si su programa se está cargando y ejecutando y puede detener y examinar los registros. cambie el número de nops y vea si la PC cambia la próxima vez o haga que modifique los registros y vea. Desde el archivo MAKE, etc., debería poder ver que puede tener todo su programa en sram.s y no necesita el compilador C.

El código anterior funciona con la variante none-eabi o linux-gnueabi.

EDIT2

Cuando construyo para flash, uso un bootstrap ligeramente diferente

.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang

.thumb_func
reset:
    bl notmain
    b hang
.thumb_func
hang:   b .

En teoría, puede hacer que uno haga ambas cosas, eligiendo su punto de entrada, por ejemplo, podría hacer que el código de reinicio establezca el puntero de la pila aunque no sea necesario, ya que está en la tabla de vectores, pero podría hacer que el controlador de reinicio lo haga. , y luego descubra dónde está ese controlador de reinicio y cuando se cargue en sram ingrese en ese desplazamiento, cuando parpadee, simplemente se inicia correctamente. Por supuesto, tendría que construir una posición independiente, prefiero tener que arrancar, luego construir una versión flash y una versión sram usando un script de enlace diferente para cada uno y un programa de arranque diferente que solo varía en el _start y reset anterior, siendo el resto lo mismo.

Tenga en cuenta que (como descubrí desagradablemente) el negocio de inicio/inicio en la cadena de herramientas de GCC retrocede y escribe todo el vector nuevamente al inicio del programa, por lo que incluso si tiene su .hex y lo escribe en una ubicación arbitraria, el inicio ¡el vector se sobrescribirá inmediatamente cuando ingrese a su código! Lidié con eso encontrando el #define que le decía al código de inicio qué cargar en los vectores y arreglándolo.
depende de cómo use la cadena de herramientas, puede poseer la tabla de vectores y controlar todo, no tiene que usar las tablas de vectores ni el código de arranque proporcionado con las herramientas gnu. todo depende de ti. puede tener fácilmente un código en el que simplemente puede bifurcarse o configurar el contador del programa.
si está utilizando swd, puede hacer que se ejecute sin hacer un reinicio basado en vectores. Todavía no conozco muy bien el protocolo swd, pero he usado openocd en varios de estos chips cortex-m y funciona. así que si ellos pueden hacerlo, tú también puedes hacerlo.
No era evidente cómo sacar eso del código de configuración sin hacer un gran lío.
ejemplos agregados a la respuesta