ATTiny13 -- avr-gcc Hello World utiliza más de 100 bytes?

Estoy tratando de escribir un programa para ATTiny13. Mi problema es que tiene enormes restricciones de tamaño. Bueno, cuando hice mi primer programa Hello World, ¡se necesitaron 100 bytes de espacio de programa para hacer que una luz se encendiera y se apagara! ¿Hay alguna opción que pueda darle a avr-gcc para reducir este tamaño? Además, ¿qué hay en el crt0? No estoy muy interesado en el montaje de AVR, así que no lo entiendo mucho.

No quiero tener que dejar de ensamblar para este proyecto..

Como una pequeña nota al margen, algunas personas lo llamarían un programa "Blinky".
@Johan bueno, no estaba seguro de cómo se escribe "blinkenlights"

Respuestas (5)

crt0 es la rutina de inicio para el uC. Las rutinas realizan la configuración de los registros y también la inicialización de los datos.

¿Los 100 bytes incluyen la tabla de vectores de interrupción? No estoy seguro sobre el ATtiny13 pero el ATtiny25/45/85 tiene 15 vectores de interrupción. Esto ocuparía 30 bytes.

gcc tiene una opción para vincular en su crt0. Puede tomar el archivo AVR crt0.S y modificarlo. No es muy largo por lo que no debería ser difícil de hacer.

Parece que no puedo encontrar la fuente crt0, pero en crt1 hay lo que parece ser una tabla de vectores de interrupción. tal vez eso es todo
Tampoco puedo encontrarlo en mi sistema :( Compilé todas las herramientas de la fuente, así que pensé que estaría allí. Si busca en Google "crt0.S atmel", un par de notas de la aplicación Atmel sobre el inicio, crt0 y gcc aparece la optimización Tal vez haya algunos consejos en esos documentos.
@jlu Estoy tratando de descubrir la diferencia entre los dos, pero aún no he obtenido nada bueno en Stack Overflow: stackoverflow.com/questions/2709998/…
avr-libc tiene un CRT diferente para cada tipo de chip AVR, y las distribuciones estándar de avr-libc solo incluyen la versión .o del archivo. El de ATtiny13 se encuentra en [avr-libc-path]/avr-3/lib/crttn13.o
@todbot hmm. Ah, ok, sí, lo tengo en/avr-libc-1.6.7/avr/lib/avr2/attiny13/crttn13.S
Parece que el código crt es gcrt1.S y está en avr-libc/crt1. Creo que Makefile (en el mismo directorio) compila los archivos de objetos para los distintos dispositivos.

Puede usar avr-objdump -d .elf para ver lo que se genera:

Analicémoslo un poco:

[jpc@jpc ~] avr-objdump -d avr.elf | sed -e 's/^/    /' | pbcopy

avr.elf:     file format elf32-avr

Disassembly of section .text:

00000000 <__vectors>:
   0:   09 c0           rjmp    .+18        ; 0x14 <__ctors_end>
   2:   0e c0           rjmp    .+28        ; 0x20 <__bad_interrupt>
   4:   0d c0           rjmp    .+26        ; 0x20 <__bad_interrupt>
   6:   0c c0           rjmp    .+24        ; 0x20 <__bad_interrupt>
   8:   0b c0           rjmp    .+22        ; 0x20 <__bad_interrupt>
   a:   0a c0           rjmp    .+20        ; 0x20 <__bad_interrupt>
   c:   09 c0           rjmp    .+18        ; 0x20 <__bad_interrupt>
   e:   08 c0           rjmp    .+16        ; 0x20 <__bad_interrupt>
  10:   07 c0           rjmp    .+14        ; 0x20 <__bad_interrupt>
  12:   06 c0           rjmp    .+12        ; 0x20 <__bad_interrupt>

Tabla de vectores de interrupción de 20 bytes (al menos algunas de las entradas podrían omitirse si insistiera y prometiera que nunca habilitaría las interrupciones correspondientes).

00000014 <__ctors_end>:
  14:   11 24           eor r1, r1
  16:   1f be           out 0x3f, r1    ; 63
  18:   cf e9           ldi r28, 0x9F   ; 159
  1a:   cd bf           out 0x3d, r28   ; 61
  1c:   02 d0           rcall   .+4         ; 0x22 <main>
  1e:   05 c0           rjmp    .+10        ; 0x2a <_exit>

Borra SREG (no estoy seguro de que esto sea realmente necesario), escribe 0x9f (RAMEND) en SPL (el puntero de pila) y salta a main. El último rjmp es un poco redundante. (podrías prometer que nunca volverás de main)

00000020 <__bad_interrupt>:
  20:   ef cf           rjmp    .-34        ; 0x0 <__vectors>

Procedimiento de interrupción predeterminado para aquellas interrupciones que no tienen una sobrescrita en C. (mismas reglas que para __vectores)

00000022 <main>:
  22:   bb 9a           sbi 0x17, 3 ; 23
  24:   c3 9a           sbi 0x18, 3 ; 24
  26:   c3 98           cbi 0x18, 3 ; 24
  28:   fd cf           rjmp    .-6         ; 0x24 <main+0x2>

Su proceso principal. Ajustado.

0000002a <_exit>:
  2a:   f8 94           cli

0000002c <__stop_program>:
  2c:   ff cf           rjmp    .-2         ; 0x2c <__stop_program>

Estos dos no son muy útiles. _exit probablemente sea requerido por el estándar C y __stop_program es necesario para que funcione como debería.

¿Cuál es su eventual aplicación? Un ATtiny13 tiene 1kB de flash y puede hacer mucho con eso en C. El crt0 es el tiempo de ejecución avr-libc C. Contiene cosas como el manejo de pilas para que pueda usar funciones con argumentos y valores devueltos.

100 bytes para la configuración de C incrustado no es tan malo y tiene un tamaño constante. Duplicar las líneas de la lógica del programa no necesariamente lo hará de 200 bytes. ¿A qué nivel de optimización estás compilando? Deberías estar en "-Os". ¿Y cómo estás compilando esto? Los Makefiles en los proyectos de demostración disponibles en el sitio avr-libc son bastante buenos y completos.

El sencillo programa de encendido/apagado de LED que se muestra a continuación ocupa 62 bytes en un ATtiny13 con "-Os" en el avr-gcc 4.3.3. de CrossPack-AVR:

#incluye <avr/io.h>
#incluir <avr/retraso.h>

int principal (vacío)
{
    DDRB |= _BV( PB3 );
    mientras ( 1 ) {
        PUERTOB |= _BV( PB3 );
        _retraso_ms(200);
        PUERTOB &=~ _BV(PB3);
        _retraso_ms(200);
    }
}

Eliminar las llamadas _delay_ms() lo convierte en 46 bytes.

Un ejemplo más grande en el ATtiny13 son mis prototipos Smart LED . Este código contiene un PWM de software de 3 canales, una conversión de color de HSV a RGB, una máquina de estado y lee dos botones. No está particularmente bien escrito y tiene un tamaño de 864 bytes. Bajo avr-gcc 3.x era aún más pequeño. (por alguna razón, avr-gcc 4 ha hecho que casi todos los programas crezcan unos pocos bytes)

avr-gcc -std=c99 -Wall -Os -mmcu=attiny13 -o hello.out helloworld.ces la línea relevante en mi archivo MAKE (autocreado). y uso un código casi idéntico excepto para voltear el LED que uso PORTB &= ~(1 << LED);y tal
Y sí, el tamaño es constante, pero incluso 46 bytes parece un poco pesado si todo lo que tiene que hacer es configurar un marco de pila

Si tiene poco espacio, pruebe el banco de trabajo integrado de IAR: su versión gratuita 'kickstart' tiene un límite de tamaño de código de palabra de 4K, por lo que es suficiente para ATTiny y probablemente una mejor optimización que gcc

Las comparaciones de optimización son un tema de gran controversia. Yo no iría allí.
@tyblu Estoy de acuerdo, pero IAR es conocido por producir binarios más pequeños que avr-gcc, por ejemplo. Sin embargo, también estaría de acuerdo con mikeselectricstuff y creo que es un buen consejo.

Los dispositivos como ese a menudo se programan en ensamblador, lo que da como resultado ejecutables más pequeños. Vale la pena hacer el esfuerzo y aprender a usarlo.

Estoy de acuerdo, pero en mi humilde opinión, el objetivo no es programar dispositivos completos en ensamblaje (sé que esto se hace con frecuencia y yo también lo hice), sino poder decodificar y verificar lo que el compilador C está haciendo a sus espaldas. También significa que con frecuencia podrá adivinar el compilador y optimizar el código que escribe en C para obtener un tamaño ejecutable pequeño.