segunda-feira, 1 de julho de 2013

SDCC: Relocação de Código - PIC (Parte 1)

Vamos voltar ao nosso assunto de desenvolver um bootloader em C para PIC18F. (Apesar de ser um assunto batido, eu prefiro subir o Everest a ver fotos da paisagens de quem já foi lá! Entendeu a analogia?)

O SDCC pobremente suporta relocação de código. Ele traz os switches --ivt-loc e --code-loc (endereço das tabelas de vetores de interrupções e código) mas eles são ineficientes, incapazes, moribundos!

Recentemente descobri uma diretiva de compilação (#pragma) que pareceu ser promissor e tem feito o que eu preciso. Mas funciona de forma pobre, pois não consigo relocar o start point default do programa (main) para o ponto que eu preciso... então vamos para os artifícios.

Entendendo a Relocação

Quando o sistema possui um pré-inicializador, que pode ser um bootloader. O programa do usuário não precisa assumir o endereço absoluto. E para que não acabe apagando o bootloader, o seu código normalmente é adicionado no final daquele setor.

Os compiladores normalmente irão gerar seus códigos executáveis almejando o endereçamento padrão e farão a sua linkedição com as bibliotecas de inicialização necessários (crt, c0t, c0rt...).

No caso da relocação, o compilador terá que gerar nova origem (ORG) e todos os branches, jumps, goto's, skip tests, memory access deverão observar esse novo endereço e calcular os seus "saltos" baseado nessa nova origem.

Na compilação, um dos arquivos gerados é o .LST. Ele possui a listagem em assembly, com a referencia de endereço de cada linha de código.

Vamos pegar o nosso programinha de blink!
#define __18F252
#include "pic18fregs.h"
#pragma config OSC=HS, OSCS=OFF, PWRT=OFF, BOR=OFF, WDT=OFF, CCP2MUX=OFF, LVP=OFF, CP0=OFF, CP1=OFF, CP2=OFF, CP3=OFF
int main()
{
TRISB=0xFD;
while(1) {
PORTB=(PORTB^0x02);
}
}

Veja o código da função "main()" sem relocação - in natura.

                                           ; ; Starting pCode block
                                           S_blink__main code
                                           _main:
                                           ; .line 20; blink.c TRISB=0xFD;
00000a   0efd     movlw   0xfd             MOVLW 0xfd
00000c   6e93     movwf   0x93, 0           MOVWF _TRISB
                                           _00106_DS_:
                                           ; .line 22; blink.c PORTB=(PORTB^0x02);
00000e   7281     btg     0x81, 0x1, 0     BTG _PORTB, 1
000010   d7fe     bra     0xe               BRA _00106_DS_
000012   0012     return  0                 RETURN

No código acima, eu removi os preâmbulos de inicialização CRT/C0T do sdcc (sdcc_gsinit...). Veja que a função main, começa no endereço 0x0a; o loop e o branch (BRA) apontam para 0x0e;
O label __0106_DS_ marca o inicio e fim do loop "while (1)..."

Vou relocar o código para o endereço 0x800

                                           ; ; Starting pCode block
                                           S_blink___main code 0X000800
                                           __main:
                                           ; .line 18; blink.c int _main()
000800   cfd9     movff   0xfd9, 0xfe5     MOVFF FSR2L, POSTDEC1
000802   ffe5
000804   cfe1     movff   0xfe1, 0xfd9     MOVFF FSR1L, FSR2L
000806   ffd9
                                           ; .line 20; blink.c TRISB=0xFD;
000808   0efd     movlw   0xfd             MOVLW 0xfd
00080a   6e93     movwf   0x93, 0           MOVWF _TRISB
                                           _00106_DS_:
                                           ; .line 22; blink.c PORTB=(PORTB^0x02);
00080c   7281     btg     0x81, 0x1, 0     BTG _PORTB, 1
00080e   d7fe     bra     0x80c             BRA _00106_DS_
000810   cfe4     movff   0xfe4, 0xfd9     MOVFF PREINC1, FSR2L
000812   ffd9
000814   0012     return  0                 RETURN

Eu marquei em amarelo, as referências de endereços. Veja que o compilador teve que relocar o código. Isto é, atribuir novos endereços e corrigir os saltos para a nova faixa.

Problemas à vista

Estou usando o SDCC 3.3. Portanto o que eu disser aqui, é somente associado ao SDCC 3.3 (não testei em outras versões).

Como eu disse, o suporte de relocação de código para PIC no SDCC é pobre. Com muita pesquisa você vai cair nas seguintes dicas:
1) --code-loc
2) --ivt-loc
3) usar #pragma code
4) alterar o .lkr do gputils

Cada uma delas tem a sua particularidade.

--code-loc

--code-loc não faz o que diz fazer. Ele relocaria o código para o endereço desejado. Mas isso não ocorre. NUNCA! :( (you boor!)
(sorry, SDCC team, --code-loc is would never be in PIC)


--ivt-loc

--ivt-loc faz o que tem de fazer, mas é somente para tabelas de vetores. Se você tiver alguma função tipo __interrupt. Ele irá relocar o código para os endereços de vetores a partir do ponto informado.

Adicione o trecho de código abaixo ao nosso blink.c

void isr_high()  __critical __interrupt 1 {
PORTA=!PORTA;
}

compile-o
sdcc --use-non-free -p18f252 -mpic16 blink.c

Por fim abra o blink.lst e inspecione o seu conteúdo...
                                           S_blink__isr_high code 0X000008
                                           _isr_high:
                                           ; .line 15; blink.c PORTA=!PORTA;
000008   5080     movf    0x80, 0, 0       MOVF _PORTA, W
00000a   80d8     bsf     0xd8, 0, 0       BSF STATUS, 0
00000c   66e8     tstfsz  0xe8, 0           TSTFSZ WREG
00000e   90d8     bcf     0xd8, 0, 0       BCF STATUS, 0
000010   6a80     clrf    0x80, 0           CLRF _PORTA
000012   3680     rlcf    0x80, 0x1, 0     RLCF _PORTA, F

Agora compile com essa linha de comando:

sdcc --use-non-free -p18f252 -mpic16 --ivt-loc=0x200 blink.c 

O código será relocado:

_isr_high:
                                           ; .line 15; blink.c PORTA=!PORTA;
000208   5080     movf    0x80, 0, 0       MOVF _PORTA, W
00020a   80d8     bsf     0xd8, 0, 0       BSF STATUS, 0
00020c   66e8     tstfsz  0xe8, 0           TSTFSZ WREG
00020e   90d8     bcf     0xd8, 0, 0       BCF STATUS, 0
000210   6a80     clrf    0x80, 0           CLRF _PORTA
000212   3680     rlcf    0x80, 0x1, 0     RLCF _PORTA, F

I FOUND A BUG! SDCC não está gerando RETFIE (return from Interrupt) no final das ISR's quando se usa __naked!  Bom, nada que um __asm RETFIE __endasm; não resolva. Mas fica o alerta!
o --ivt-loc não reloca o main... então falta somente essa parte.

--//--
Nos próximos artigos vamos explorar o #pragma e o .lkr! Tem coisa boa vindo aí!

Nenhum comentário:

Postar um comentário