ATTiny13 - Hello World usa mais de 100 bytes?

9

Estou tentando escrever um programa para o ATTiny13. Meu problema é que ele tem enormes restrições de tamanho. Bem, ao criar meu primeiro programa Hello World, foram necessários 100 bytes de espaço no programa apenas para acender e apagar a luz! Existem opções que eu possa dar ao avr-gcc para diminuir esse tamanho? Além disso, o que está no crt0? Eu não gosto muito da montagem do AVR, então não entendo muito ..

Eu não quero ter que cair na montagem para este projeto ..

Earlz
fonte
Como uma pequena nota lateral, algumas pessoas chamariam isso de programa "Blinky".
Johan
11
@Johan bem, eu não tinha certeza de como se escreve "Blinkenlights"
Earlz

Respostas:

9

crt0 é a rotina de inicialização do uC. As rotinas executam a configuração dos registradores e também a inicialização dos dados.

Os 100 bytes incluem a tabela de vetores de interrupção? Não tenho certeza sobre o ATtiny13, mas o ATtiny25 / 45/85 possui 15 vetores de interrupções. Isso levaria 30 bytes.

O gcc tem uma opção para vincular no seu crt0. Você pode pegar o arquivo AVR crt0.S e modificá-lo. Como não é muito longo, não deve ser difícil de fazer.

jluciani
fonte
Não consigo encontrar a fonte crt0, mas em crt1 existe o que parece ser uma tabela de vetores de interrupção. Talvez seja isso
Earlz
Também não consigo encontrá-lo no meu sistema :( Compilei todas as ferramentas da fonte, então pensei que elas estariam lá. Se você procurar por "crt0.S atmel", algumas notas do aplicativo Atmel sobre inicialização, crt0 e gcc Talvez haja algumas dicas nesses documentos
jluciani
@jlu Estou tentando descobrir a diferença entre os dois, mas ainda não
obtive
2
O avr-libc possui um CRT diferente para cada tipo de chip AVR, e as distribuições padrão do avr-libc incluem apenas a versão .o do arquivo. O do ATtiny13 está localizado em [avr-libc-path] /avr-3/lib/crttn13.o
todbot
@todbot hmm. Ah, ok sim eu tê-lo em/avr-libc-1.6.7/avr/lib/avr2/attiny13/crttn13.S
Earlz
19

Você pode usar avr-objdump -d .elf para ver o que está sendo gerado:

Vamos analisar um pouco:

[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>

Tabela de vetores de interrupção de 20 bytes (pelo menos algumas das entradas podem ser omitidas se você insistir e prometer que nunca ativará as interrupções correspondentes).

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>

Limpa SREG (não tenho certeza se isso é realmente necessário), grava 0x9f (RAMEND) no SPL (o ponteiro da pilha) e pula para o principal. O último rjmp é meio redundante. (você pode prometer nunca voltar do main)

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

Procedimento de interrupção padrão para aquelas interrupções que não possui uma sobrescrita em C. (mesmas regras que para __vetores)

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>

Seu proc principal. Justa.

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

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

Estes dois não são muito úteis. _exit é provavelmente exigido pelo padrão C e __stop_program é necessário para que ele funcione como deveria.

jpc
fonte
16

Qual é a sua eventual aplicação? Um ATtiny13 possui 1kB de flash e você pode fazer muito com isso em C. O crt0 é o tempo de execução C do avr-libc. Ele contém itens como manipulação de pilha, para que você possa usar funções com argumentos e valores de retorno.

100 bytes para a configuração C incorporada não são muito ruins e seu tamanho é constante. Dobrar as linhas da lógica do programa não significa necessariamente 200 bytes. Em que nível de otimização você está compilando? Você deve estar em "-Os". E como você está compilando isso? Os Makefiles nos projetos de demonstração disponíveis no site avr-libc são muito bons e abrangentes.

O simples programa de ligar / desligar o LED abaixo leva 62 bytes em um ATtiny13 com "-Os" no avr-gcc 4.3.3. do CrossPack-AVR:

#include <avr / io.h>
#include <avr / delay.h>

int main (vazio)
{
    DDRB | = _BV (PB3);
    enquanto (1) { 
        PORTB | = _BV (PB3);
        _delay_ms (200);
        PORTB & = ~ _BV (PB3);
        _delay_ms (200);
    }
}

A remoção das chamadas _delay_ms () faz 46 bytes.

Um exemplo maior no ATtiny13 são meus protótipos de LEDs inteligentes . Esse código contém um software PWM de 3 canais, uma conversão de cores HSV para RGB, uma máquina de estado e lê dois botões. Não está escrito particularmente bem e tem 864 bytes. No avr-gcc 3.x, era ainda menor. (por algum motivo, o avr-gcc 4 aumentou quase todos os programas em alguns bytes)

todbot
fonte
avr-gcc -std=c99 -Wall -Os -mmcu=attiny13 -o hello.out helloworld.cé a linha relevante no meu makefile (criado por você). e eu usar o código quase idênticos, exceto para virar o uso LED I PORTB &= ~(1 << LED);e tal
Earlz
E sim, o tamanho é constante, mas até 46 bytes parece meio pesado se tudo o que tem a fazer é configurar um StackFrame
Earlz
2

Se você está com pouco espaço, experimente o ambiente de trabalho incorporado do IAR - a versão gratuita do 'kickstart' tem um limite de tamanho de código de palavra em 4K, o que é suficiente para o ATTiny e provavelmente a otimização melhor que o gcc

mikeselectricstuff
fonte
11
As comparações de otimização são um assunto de alta contenção. Eu não iria lá.
tyblu
11
@tyblu Eu concordo, mas o IAR é conhecido por produzir binários menores que o avr-gcc, por exemplo. Eu também concordaria com o mikeselectricstuff e acho que é um conselho sensato.
Morten Jensen
1

Dispositivos como esse geralmente são programados no assembler, resultando em executáveis ​​menores. Vale a pena fazer um esforço e aprender a usá-lo.

Leon Heller
fonte
11
Eu concordo, mas IMHO o objetivo não é programar dispositivos inteiros na montagem (eu sei que isso é feito com frequência e também fiz isso), mas ser capaz de decodificar e verificar o que o compilador C está fazendo nas suas costas. Isso também significa que você frequentemente poderá adivinhar o compilador e otimizar o código que escreve em C para obter um tamanho pequeno de executável.
JPC