Código minúsculo para volcar la memoria flash

Estoy tratando de escribir el código más pequeño posible para extraer el firmware del microcontrolador XMC4500 de Infineon .

El código debe caber en un búfer de 30 bytes que me permite tener 15 instrucciones de máquina usando el conjunto de instrucciones de 16 bits de Thumb.

Comenzando con C, mi intento es volcar la memoria flash a través de un solo pin GPIO (ver pregunta original) siguiendo este ingenioso truco.

Básicamente lo que estoy haciendo es:

  1. Configure las direcciones de los pines GPIO para la salida
  2. Parpadeo LED1 (pin 1.1) con un reloj (reloj serie SPI)
  3. Parpadeo LED2 (pin 1.0) con bits de datos (SPI MOSI)
  4. Olfatear pines con un analizador lógico

EDITAR:

  1. ACTUALIZAR BLOQUE DE CÓDIGO C
  2. AGREGAR BLOQUE DE CÓDIGO DE MONTAJE
#include "XMC4500.h"

void main() {
  // start dumping at memory address 0x00000000
  unsigned int* p = (uint32_t *)(0x0u);

  // configure port1 output (push-pull)
  PORT1->IOCR0 = 0x8080u;

  for(;;) {
    int i = 32;

    int data = *(p++);

    do {
      // clock low
      PORT1->OUT = 0x0;

      // clock high with data bits
      PORT1->OUT = 0x2u | data;

      data >>= 1;

    } while (--i > 0);
  }
}
main:
    ; PORT1->IOCR0 = 0x8080UL
    ldr r1, =0x48028100 ; load port1 base address to R1
    movw r2, #0x8080 ; move 0x8080 to R2
    str r2, [r1, #0x10]

main_1:
    ; start copying at address 0x00000000
    ; R12 is known to be zeroed
    ldr.w r2, [r12], #0x4 ; int data = *(p++)
    movs r3, #32 ; int i = 32

main_2:
    ; PORT1->OUT = 0x0
    ; clock low
    ; R12 is known to be zeroed
    str r12, [r1]

    ; PORT1->OUT = 0x2 | data
    ; clock high with data bits
    orr r4, r2, #0x2
    str r4, [r1]

    asrs r2, r2, #0x1 ; data >>= 1

    subs r3, r3, #0x1 ; i--
    bne.n main_2 ; while (--i > 0)
    b.n main_1 ; while(true)

Sin embargo, el tamaño del código sigue siendo demasiado grande para cumplir con mis requisitos.

¿Hay algo que pueda hacer para reducir aún más mi código? ¿Algo que se pueda optimizar o dejar de lado?

No creo que esto sea realmente posible, pero intentaré responderlo. Editar: ¿Podría publicar lo que el ensamblador hizo con esto?
Si bien puede ser útil hablar de esto conceptualmente en C, es casi seguro que tendrá que realizar la optimización final directamente en el código ensamblador.
Tienes muchas ramificaciones allí que no pareces necesitar. "Si el bit es uno, envíe uno, y si es cero, envíe cero" parece que debería poder simplificarlo.
Si todo lo demás falla, pruebe los trucos de código de automodificación.
@Dzarda Encuentre el ensamblaje generado arriba.
@ user3696425 Entonces, ¿cuál es el tamaño del código en este punto?
@Dzarda Lo siento, olvidé mencionar: ahora son 44 bytes.

Respuestas (2)

No estoy acostumbrado a programar para procesadores ARM y no sé qué compilador usas, así que tal vez los cambios propuestos no hagan nada en absoluto, pero bueno, ¡al menos intentémoslo!

1-Inline sus funciones:

Un buen compilador ya debería alinear una función si las optimizaciones están bien establecidas, pero vale la pena alinearlo para eliminar las llamadas y los ret.

2-Evitar la ramificación:

En algunas arquitecturas un IF se puede traducir en: cargar, probar, bifurcar, tres instrucciones, si puede hacerlo sin bifurcar puede usar menos instrucciones.

Entonces, el código propuesto es:

    int main() 
    {
      // start dumping at memory address 0x08000000
      unsigned int *p;
      int i;

      p = (uint32_t *)(0x08000000u);

      // configure pin 1.0 and pin 1.1 as output (push-pull)
      PORT1->IOCR0 = 0x8080UL;

      do
      {
        for (i = 0; i < 32; i++)
        {
          // set pin 1.1 to low (SPI clock)
          PORT1->OUT &= (~0x2UL);
          PORT1->OUT = (PORT1->OUT & 0xFFFE) | (data & 0x01)
          PORT1->OUT |= 0x2UL;
          data >>= 1;
        }

      }while(p++)
    }

Pruébalo y comenta los resultados.

¡Buen punto de partida! Logró reducir el tamaño del código a 44 bytes. Ahora necesito profundizar en el ensamblaje generado (consulte el bloque de código anterior) para mejorar aún más el tamaño del código.

Esto supone que su programa de 32 bytes es el único código disponible en el chip. No se permiten llamadas externas.


Una preocupación con su código C sería la inclusión y el uso del encabezado "XMC4500.h". A pesar de que solo está utilizando la estructura "PORT1" para que el resto se optimice / se optimizará, sigo pensando que puede obtener un acceso de registro GPIO más eficiente que eso.

En segundo lugar, por su ausencia de información sobre la "configuración del proyecto", asumo que no ha tomado ninguna medida para obligar a su enlazador a comprimir su código en una ubicación específica, omitir la tabla de interrupciones, el código de inicialización, etc. Es posible que desee echar un vistazo aquí . Básicamente, lo que dice es que al modificar los archivos .ld/.lds puede obligar al enlazador a colocar secciones específicas de código en direcciones específicas.
Tal script podría verse así:

SECTIONS {
        . = YOUR_DESIRED_ADDRESS; 
        .text : { * (.text); }
}

Editaré la respuesta a medida que surjan nuevas ideas.

1. Mi programa es el único código en el chip. 2. Estoy bien con mi archivo de encabezado, no hay necesidad de preocuparse por eso. 3. La combinación y ubicación de secciones también está bien.
Solo un pensamiento aleatorio, ¿por qué no fusionar estos dos complementos? and.w r4, r2, #0x1, add.w r4, r4, #0xFF00 EDITAR : Lo siento, mi error (y & agregar)
Lo siento amigo, esto requiere un gurú de ensamblaje ARM, no yo. Pero sospecho que puede cortar 2 tal vez 4 bytes, no se ajusta a 32 bytes ...
No hay problema. Logró optimizar el tamaño del código a 36 bytes (consulte los bloques de código actualizados arriba). Faltan tres instrucciones más (6 bytes) y estoy bien :-)