GPIO

Software e Hardware para uC da Qualcomm, NXP, FreeScale e Motorola

Moderadores: 51, guest2003

GPIO

Mensagempor mastk » 31 Mar 2011 11:42

Ola pessoal, estava testando um MCF52259 e esta feliz até o momento que tentei fazer uma barramento externo com GPIO, a velocidade maxima que consegui com esse codifco foi de 4Mhz e com o CORE rodando em 80Mhz.

Código: Selecionar todos
         
unsigned char counter++;
for (;;){
  counter++;
  PORTAN = counter;
}


Asm:
0x000008a8 <main+16>: moveq #0,d0
0x000008aa <main+18>: move.b -4(a6),d0
0x000008ae <main+22>: addq.l #1,d0
0x000008b0 <main+24>: move.b d0,-4(a6)
//PORTUA = 0x02;
PORTAN = counter;
0x000008b4 <main+28>: move.b -4(a6),d0
0x000008b8 <main+32>: move.b d0,0x4010000A (0x4010000a)
0x000008be <main+38>: bra.s main+0x10 (0x8a8) ; 0x000008a8

Qual a limitacao? O CORE? A documentação deixa a impressão que os GPIOs podem chegar ao BUS interno, desde que o mesmo esteja abaixo de 100Mhz.
Avatar do usuário
mastk
Dword
 
Mensagens: 4407
Registrado em: 14 Out 2006 20:43

Re: GPIO

Mensagempor msamsoniuk » 31 Mar 2011 18:22

soh remodelando a resposta: verifique se esse core tem caches e se elas estao ligadas, pois a diferenca de performance de um v2 com e sem caches eh da ordem de quase 5x! bom, pelo codigo asm vejo que vc nao esta usando gcc, entao talvez algumas otimizacoes nem funcionem da mesma forma. mas imagino eu que uma versao ainda melhor do codigo seria:

Código: Selecionar todos
volatile unsigned char *port;
register unsigned char a = 0, b = 1;

port = &(PORTAN);

loop:
  *port = a;
  *port = b;
  *port = a;
  *port = b;
  *port = a;
  *port = b;
  *port = a;
  *port = b;
  goto loop;


compilando com o gcc e usando a opcao -O1, ele gerou a seguinte saida:

Código: Selecionar todos
main:
        link.w %a6,#0
        clr.b %d1
        moveq #1,%d0
        move.l #305419896,%a0
        .align  2
.L2:
        move.b %d1,(%a0)
        move.b %d0,(%a0)
        move.b %d1,(%a0)
        move.b %d0,(%a0)
        move.b %d1,(%a0)
        move.b %d0,(%a0)
        move.b %d1,(%a0)
        move.b %d0,(%a0)
        jbra .L2


obviamente eu nao tenho a definicao de PORTAN, entao usei um valor aleatorio mesmo. e note que os multiplos move.b dispersam o delay do branch. neste caso, cada move.b deve consumir recursos infimos, pq o endereco da porta esta em um registro e todos dados possiveis estao em um registro. porem para chegar nisso deu um certo trampo: testei com -O3, -O2 e finalmente com -O1, chegando entao ao codigo que eu gostaria.

se vc tiver oportunidade de testar em asm, experimente o codigo acima. acredito que da sintaxe do gcc para o padrao motorola eh soh tirar os % dos nomes dos registros.

mastk escreveu:Ola pessoal, estava testando um MCF52259 e esta feliz até o momento que tentei fazer uma barramento externo com GPIO, a velocidade maxima que consegui com esse codifco foi de 4Mhz e com o CORE rodando em 80Mhz.

Código: Selecionar todos
         
unsigned char counter++;
for (;;){
  counter++;
  PORTAN = counter;
}


Asm:
0x000008a8 <main+16>: moveq #0,d0
0x000008aa <main+18>: move.b -4(a6),d0
0x000008ae <main+22>: addq.l #1,d0
0x000008b0 <main+24>: move.b d0,-4(a6)
//PORTUA = 0x02;
PORTAN = counter;
0x000008b4 <main+28>: move.b -4(a6),d0
0x000008b8 <main+32>: move.b d0,0x4010000A (0x4010000a)
0x000008be <main+38>: bra.s main+0x10 (0x8a8) ; 0x000008a8

Qual a limitacao? O CORE? A documentação deixa a impressão que os GPIOs podem chegar ao BUS interno, desde que o mesmo esteja abaixo de 100Mhz.
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Mensagempor mastk » 01 Abr 2011 10:22

Entendi Sam, mas nao deixei de ficar descepcionado.

Veja, estou com uma M52259EVB e apesar do kit ser bom, o FlexBus não esta externado em nenhum conector.
As unicas opções que me restaram foram, QSPI, que é lenta 20Mhz, se nao me falha a memoria ou GPIO. As GPIOs são rapidas, mas a CPU não faz uma instrução por clock.
Avatar do usuário
mastk
Dword
 
Mensagens: 4407
Registrado em: 14 Out 2006 20:43

Mensagempor msamsoniuk » 01 Abr 2011 14:04

opa! mas *nenhuma* cpu consegue fazer uma instrucao por clock se a instrucao nao for puramente de registro para registro! o barramento eh um gargalo e qualquer operacao em memoria (ou io) tera uma penalidade.

alem disso, o seu codigo nao esta bom! nos comentarios abaixo, eu chuto quanto cada instrucao consome:

Código: Selecionar todos
0x000008a8 <main+16>: moveq #0,d0 // 1 clock
0x000008aa <main+18>: move.b -4(a6),d0 // 2 clocks
0x000008ae <main+22>: addq.l #1,d0 // 1 clock
0x000008b0 <main+24>: move.b d0,-4(a6) // 2
//PORTUA = 0x02;
PORTAN = counter;
0x000008b4 <main+28>: move.b -4(a6),d0 // 2 clocks
0x000008b8 <main+32>: move.b d0,0x4010000A (0x4010000a) // 1
0x000008be <main+38>: bra.s main+0x10 (0x8a8) ; 0x000008a8 // 1


o codigo inteiro consome entao, uns 10 clocks. para um ciclo completo de GPIO, seriam 20 clocks. rodando com 80MHz, vc vai ter exatamente 4MHz! :)

agora, no codigo mais otimizado que eu mostrei, temos:

Código: Selecionar todos
.L2:
        move.b %d1,(%a0)  // 2 clocks
        move.b %d0,(%a0)  // 2 clocks 
        move.b %d1,(%a0)  // 2 clocks
        move.b %d0,(%a0)  // 2 clocks
        move.b %d1,(%a0)  // 2 clocks
        move.b %d0,(%a0)  // 2 clocks
        move.b %d1,(%a0)  // 2 clocks
        move.b %d0,(%a0)  // 2 clocks
        jbra .L2 // 1 clock


assim, temos 17 clocks no total. rodando com 80MHz, vamos ter 4.7 milhoes de loops. mas lembrando que em cada loop temos 4 ciclos completos, o que vc efetivamente vai medir no pino de GPIO eh 18.8MHz, ou seja, o codigo pode ficar pelo menos 4 vezes melhor.

e definitivamente isso nao pode ser considerado um bom benchmark! suponha que vc fosse usar essa cpu para graficos. uma das operacoes que mais consomem recursos de software eh justamente o preenchimento de areas e existem truques para otimizar isso. por exemplo, para um display 640x480 podemos pensar em algo para otimizar o preenchimento de retangulos:

Código: Selecionar todos
for(y=y0;y!=y1;y++)
{
    *vram = lines[y];
    switch(x1-x0)
    {
       case 640: *vram++ = pixel;
       case 639: *vram++ = pixel;
       case 638: *vram++ = pixel;
       case 637: *vram++ = pixel;
       ...
       case 2: *vram++ = pixel;
       case 1: *vram = pixel;
    }
}


efetivamente, o que o codigo faz eh procurar em cada linha quantas atribuicoes faltam para completar a largura do retangulo (eh facil adaptar isso tb para triangulos, o que permite extender para qq poligono!). vamos supor que cada pixel eh um byte, entao, na pratica, o codigo em asm disso vai virar uma sequencia do tipo:

Código: Selecionar todos
case_640: move.b %d0,(%a0)+
case_639: move.b %d0,(%a0)+
case_638: move.b %d0,(%a0)+
case_637: move.b %d0,(%a0)+
...
case_2: move.b %d0,(%a0)+
case_1: move.b %d0,(%a0)


o que significa que o codigo que faz o for das linhas, calcula o endereco base da vram e controla o switch impacta muito pouco. se cada linha destas consome 2 clocks, temos entao ateh 1280 clocks para transferir 640 bytes. desta forma, o codigo que controla o loop poderia ter ateh 128 clocks e estaria impactando em menos de 10% na performance total.

em outras palavras, a 80MHz, vc estaria conseguindo trabalhar com uma performance de 40 Mpixel/s se fosse utilizada uma memoria capaz de operar com 2 clocks. na pratica, tem o impacto do loop de controle, que deve reduzir esse total em uns 10%. mas por outro lado, nao usamos a largura total do barramento. se a memoria de video for de 32 bits, podemos refazer o loop para otimizar transferencias de 4 em 4 bytes.

isso requer um tratamento das bordas de inicio e fim, transferindo bytes e words para alinhar e entao fazendo o grosso da transferencia com longs. por exemplo, um poligono nas coordenadas 3,3 e 129,129 requer um byte no inicio para alinhar com multiplo de 4. daih ele consegue preencher otimizado ateh o pixel 128 e entao temos mais um byte para fechar o valor 129. neste caso, a transferencia otimizada seria 4x mais veloz, chegando entao a 160Mpixel/s.

mas otimizar o codigo nem sempre eh facil! :)

eu acho mais facil acreditar que o software sempre eh uma penalidade. se vc precisa de performance, vc precisa de hardware, motivo pelo qual DMA eh sempre essencial.

mastk escreveu:Entendi Sam, mas nao deixei de ficar descepcionado.

Veja, estou com uma M52259EVB e apesar do kit ser bom, o FlexBus não esta externado em nenhum conector.
As unicas opções que me restaram foram, QSPI, que é lenta 20Mhz, se nao me falha a memoria ou GPIO. As GPIOs são rapidas, mas a CPU não faz uma instrução por clock.
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Mensagempor mastk » 05 Abr 2011 09:15

Sem problemas Sam, já imaginava que era isso, mas com 76Dmips, sonhei em poder substituir o FlexBus, sem grandes penalidades, talvez ate seja possivel mediante o uso do DMA.
Avatar do usuário
mastk
Dword
 
Mensagens: 4407
Registrado em: 14 Out 2006 20:43

Mensagempor msamsoniuk » 05 Abr 2011 15:50

mastk escreveu:Sem problemas Sam, já imaginava que era isso, mas com 76Dmips, sonhei em poder substituir o FlexBus, sem grandes penalidades, talvez ate seja possivel mediante o uso do DMA.


eh bem dificil conseguir substituir um bus de forma eficiente! :(

a forma mais eficiente seria conseguida utilizando pelo menos 10 pinos de GPIO, com 8 pinos para dados, um para clock e um para delimitar a transferencia. os 10 pinos teriam que obrigatoriamente ser agrupados no mesmo registro de IO, de modo que uma unica escrita de 16 bits permitisse alterar arbitrariamente os valores dos 10 pinos ao mesmo tempo. com isso, cada escrita via DMA faria a transferencia de um byte.

no caso do pino de clock, seria necessario adicionar uma linha de delay, cascateando varios TTLs, de modo que as bordas de clock ocorram com tempos de setup/hold bons nos pinos de dados e frame. as transferencias seriam feitas tanto na borda de subida quanto descida, o que significa que a interface tem que operar em modo DDR (double data rate). mas eh possivel decodificar isso facil usando 2 latches com clocks invertidos.

um buffer de dados transferindo ABCDEF seria como:

00000000_00000000 // nada
00000011_01100101 // A
00000010_01100110 // B
00000011_01100111 // C
00000010_01101000 // D
00000011_01101001 // E
00000010_01101010 // F
00000000_00000000 // nada

no primeiro byte temos seis zeros q nao significam nada e entao dois bits que sao realmente os pinos de GPIO. o primeiro bit eh o delimitador de frame, ou seja, se for zero qq transferencia eh ignorada. o segundo bit eh o bit de clock e precisa ser invertido a cada word transferida. no segundo byte temos os dados que queremos transferir. lah do outro lado, entao, vamos ter nos latches a sequencia ABCDEF.

se o DMA consegue transferir 80Mword/s, vc vai ter uma performance de 80Mbytes/s neste tipo de interface. mas isso somente na escrita, pois na leitura a coisa comeca a complicar um pouco mais. isso ae mostra pq um processador com bus externo eh infinitamente superior em performance de IO e infinitamente mais simples! :)

outra opcao interessante de interface eh via ethernet, que na pratica eh como o pci-express funciona hoje. eh bem eficiente pq usa ring-buffers e DMA, porem como no 52259 opera apenas a 100Mbps, vc ficaria limitado a taxa de 12,5MB/s.
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04


Voltar para NXP (ex-FreeScale (ex-Motorola))

Quem está online

Usuários navegando neste fórum: Nenhum usuário registrado e 1 visitante

cron

x