Pisca-led avancado (acima de 20 MHz) com OMAP3530

Software e Hardware para linha ARM

Moderadores: 51, guest2003, Renie, gpenga

Pisca-led avancado (acima de 20 MHz) com OMAP3530

Mensagempor albertorcneto » 28 Abr 2010 11:36

Um amigo esta tentando usar um clone confiavel (EBV) da BeagleBoard para leitura de um sensor. Ele precisa gerar um clock para o AD (objetivo de 100 MHz, mas qualquer coisa acima de 20 MHz ja esta de bom sinal) e armazenar os 1000 dados (16 bits word), que chegam de forma paralela, atraves de DMA para a memoria, para processamento posterior.

Tentei ajudar ele mas o maximo que a gente consegue piscar o led eh 1,1 MHz, mesmo com o ARM trabalhando teoricamente a 720 MHz.

Sera que tem como melhorar isso? Pedi para ele compilar deixando o assembly para ver se nao tem vai-e-vems que atrasam a geracao do clock. Sera que tem como ter acesso mais rapido a um endereco de memoria especifico pelo linux sem ser atraves da forma que nos fizemos (tipo, assembly direto)? O Linux permite isso (Ubuntu)?

O fonte ta anexado.

Código: Selecionar todos
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

int main()
{
   int fd = open("/dev/mem", O_RDWR | O_SYNC);

   if (fd < 0) {
      printf("Could not open memory.\n");
      return(1);
   }

   // Device Identification according to page 179 of spruf98f.pdf OMAP3530's Manual
   volatile ulong *deviceid;
   deviceid = (ulong*) mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x4830A000);
   if (deviceid == MAP_FAILED) {
      printf("DeviceID Mapping failed.\n");
      close(fd);
      return(2);
   }
   printf("0x4830 A204: 0x%08lx \n0x4830 A218: 0x%08lx \n ", deviceid[0x204/4], deviceid[0x218/4]);
   printf("0x4830 A20C: 0x%08lx.\n\n====================================\n", deviceid[0x20C/4]);

   // Pad configuration
   volatile ulong *pinconf;
   pinconf = (ulong*) mmap(NULL, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x48000000);
   if (pinconf == MAP_FAILED) {
      printf("Pinconf Mapping failed.\n");
      close(fd);
      return(2);
   }

   // Configure Expansion header pins as input/output.
   pinconf[0x2158/4] = 0x011C011C;
   pinconf[0x215C/4] = 0x011C011C;
   pinconf[0x2160/4] = 0x011C011C;
   pinconf[0x2164/4] = 0x011C011C;
   pinconf[0x2168/4] = 0x011C011C;
   pinconf[0x216C/4] = 0x011C011C;
   pinconf[0x2170/4] = 0x011C011C;
   pinconf[0x2174/4] = 0x011C011C;
   pinconf[0x2178/4] = 0x011C011C;
   pinconf[0x218C/4] = 0x011C011C;
   pinconf[0x2190/4] = 0x011C011C;
   pinconf[0x2194/4] = 0x011C011C;
   pinconf[0x2198/4] = 0x011C011C;
   close(fd);
   printf("0x4800 244C: 0x%08lx.\n", pinconf[0x244C/4]);

   fd = open("/dev/mem", O_RDWR | O_SYNC);
   
   // GPIO Configuration
   volatile ulong *gpio;
   gpio = (ulong*) mmap(NULL, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x49050000);
   if (gpio == MAP_FAILED) {
      printf("GPIO Mapping failed.\n");
      close(fd);
      return(3);
   }

   // Configure all GPIO pins on bank 5 but gpio_157 as input.
   // gpio[0x6034/4] = 0xFFFFFFFF;
   gpio[0x6034/4] = 0xDFFFFFFF;
   printf("GPIO5_OE: 0x%08lx. \t\t\t GPIO6_OE: 0x%08lx.\n", gpio[0x6034/4], gpio[8034/4]);
 
   int c=0;
/*
   // Print the state of GPIO5 and GPIO6
   for(c=0;c<40000;c++) {
      printf("%5d) gpio5: 0x%08lx. \t\t gpio6: 0x%08lx.\n", c, gpio[0x6038/4], gpio[0x8038/4]);
      sleep(1);
   }
*/
   // Switch on and off gpio_157 indefinitely
   for(;;) {
      gpio[0x603C/4] = gpio[0x6038/4] | 0x20000000;
      gpio[0x603C/4] = gpio[0x6038/4] & 0xDFFFFFFF;
   }
}

"Nothing travels faster than the speed of light, with the possible exception of bad news, which obeys its own set of laws" ~ Douglas Adams
albertorcneto
Byte
 
Mensagens: 269
Registrado em: 28 Mar 2007 14:08

Mensagempor Ander_sil » 28 Abr 2010 12:01

Bom nunca mexi com OMAP nem nada parecido com linux, mas não seria melhor usar um pino de saida de timer e configura o timer pra 100mhz.
Anderson Chrispim da Silva
chrispimdasilva@gmail.com
Ander_sil
Byte
 
Mensagens: 368
Registrado em: 30 Out 2006 09:58
Localização: Campinas - SP

Mensagempor xultz » 28 Abr 2010 13:08

Eu chutaria que você teria que carregar o kernel em modo monousuário, assim tua aplicação ficaria sozinha e não ia mais depender do timeslice do kernel.

Só que a última vez que fiz isso fazem uns 10 anos atrás, se der sorte do Polesapart ou o Marcelo Sam lerem este post, eles vão saber como faz isso com certeza.

Agoa, talvez o melhor mesmo seria você rodar tua aplicação sem SO, mas não sei como faz isso no OMAP, mas deve ser divertido... Pior que eu tenho uma Beagleboard e nunca sobra tempo prá brincar com ela, sempre tem algo mais importante passando na TV...
98% das vezes estou certo, e não estou nem aí pros outros 3%.
Avatar do usuário
xultz
Dword
 
Mensagens: 3001
Registrado em: 13 Out 2006 18:41
Localização: Curitiba

Mensagempor MarcusPonce » 28 Abr 2010 21:02

Você está pensando em um clock para o A/D, nesta aplicação quase sempre tem que ser um clock com frequência definida e com o mínimo de jitter. Portanto, como o Ander_sil citou, o melhor seria ajustar um timer do ARM para gerar este clock a partir do clock do sistema.

Para ficar colocando 0 e 1 no pino por software, seria mais rápido comandar o pino de dentro de um driver, carregado como módulo do kernel do Linux.

O motivo é que o kernel pode tudo e o seu software de usuário só pode fazer o que o kernel deixa fazer.

Como você já está pensando em DMA, seria melhor mesmo fazer um driver que controlasse o DMA também, além do pino de clock.
MarcusPonce
Byte
 
Mensagens: 166
Registrado em: 12 Fev 2007 13:58
Localização: Campinas - SP

Mensagempor msamsoniuk » 29 Abr 2010 00:49

bom, antes de mais nada, vou assumir que vc quer um clock continuo! :)

o que eh possivel fazer eh sincronizar a geracao do clock com o escalonamento de processos. no caso do linux, o tempo tipico eh 100Hz, mas vc consegue ajustar para velocidades maiores se quiser. eu jah vi operando com 1KHz. mas no fundo, vc vai perdendo performance, pq escalonar significa rodar um monte de instrucoes para trocar contexto e isso eh muito pesado! outro problema grave eh que essa troca varia muito em termos de tempo de execucao, entao o seu clock fica uma m****, cheio de jitter.

bom, eu posso ver q vc nao esta sincronizando nada e precisa de um clock muito maior do que eh possivel fazer durante o escalonamento..

uma possibilidade seria interrupcao e um modulo no kernel, mas meio que nao ajuda muito... tive boa performance com isso jah, mas francamente, nao dah para pensar em frequencias elevadas... tem que ir por outro caminho.

entao dae fica a questao de saber se o clock eh continuo!

se eh continuo, esqueca, nao tem como fazer no linux. ele eh um sistema tipo shared time, onde sua task roda ateh estourar o tempo, daih ele escalona para outra task e a sua para, o q significa que seu clock ser gerado em bursts e, de quebra, cheios de jitter...entao se vc precisa de clock continuo, esqueca, nao tem como fazer no linux.

se os bursts jah servem, entao daria para otimizar trocando as referencias gpio[offset] por ponteiros diretos para os enderecos que vc vai acessar e declarando estes ponteiros como register (o arm tem registros suficientes para otimizar), algo como:

Código: Selecionar todos
register unsigned long *port;
register unsigned long mask;

gpio = mmap(blablabla)

port  = &gpio[offset];
mask = mask;

for(;;) *port ^= mask;


bom, eu nao manjo de asm arm, mas num 68k provavelmente isso geraria algo como:

Código: Selecionar todos
loop:
  move.l a0@,d0 // *port => d0
  eor.l d1,d0 // xor mask,d0 => d0
  move.l d0,a0@ // d0 => *port
  bra loop


supondo ae 1 clock por instrucao, acesso ao bus com zero wait states e interrupcoes totalmente desligadas... vc tem pelo menos 4 clocks para fazer uma inversao... para a onda completa, sao 8 clocks, entao para gerar 100MHz jah precisaria de uma cpu de 800MHz trabalhando 100% do tempo nesse loop! supondo que vc nao quisesse 100MHz, mas apenas 10, nao ia melhorar muito... teria que inserir delays ae de modo a gastar 80 clocks por ciclo. e para isso ser deterministico, valeriam as mesmas regras: sem interrupcoes! soh que isso eh algo totalmente irreal e, no fundo, um completo desperdicio de recursos.

vc pode fazer melhor usando um simples oscilador ttl de 4 pinos. note que usar um timer interno eh uma possibilidade apenas se o clock do PLL principal tem um divisor inteiro para o clock que vc quer gerar. no caso que vc citou, 720MHz para gerar 100MHz nao tem como, pq nao eh inteiro. e se vc precisa de ciclo de trabalho de 50%, ajustar o PLL para 700MHz e dividir por 7 no timer nao vai funcionar, pq tem que dividir por 2 com um flip-flop para obter o ciclo de trabalho correto, portanto tem que gerar 200MHz. uma opcao seria 800MHz (divide por 4 no timer) ou 600MHz (divide por 3).

isso lembra um amigo meu que me criticou em relacao a estar projetando uma FPGA para tratar HDLC com clock de 2MHz... ele dizia q hoje em dia um processador de PC podia fazer isso com facilidade em funcao do elevado clock! eh fato que pode, afinal 2MHz nao eh muito, soh q em cada clock de 2MHz eu faco uma infinidade de coisas em paralelo na FPGA! alem de ficar uma m**** cheia de jitter e glitches em funcao do indeterminismo ao qual os sistemas operacionais estao sujeitos e a falta de paralelismo no processamento, eu no fundo estaria desperdicando uma gigantesca placa de 500 dolares para fazer o trampo de um chip de 3 dolares!?! dae nao dah neh...

tem coisas q sao faceis e baratas de fazer em software e dificeis e caras de fazer em hardware... e sempre tem o contrario tb! no seu caso um oscilador resolve melhor! :)
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Mensagempor MarcusPonce » 29 Abr 2010 08:24

Só esclarecendo: Quando eu falei de usar um timer não era para disparar uma interrupção, era para gerar um clock só no hardware. É verdade que talvez não seja possível exatamente 100MHz com o clock que está na sua placa.
MarcusPonce
Byte
 
Mensagens: 166
Registrado em: 12 Fev 2007 13:58
Localização: Campinas - SP

Mensagempor guest2003 » 29 Abr 2010 09:17

Não sei se este chip tem PWM em hardware... mas se tivesse...

Talvez seria mais facil... configura um PWM com duty em 50% e pronto...

[]'s

PS: Não sei tambem se chegaria a freq tao altas...
http://www.sethi.com.br (Institucional)
http://www.sethi3d.com.br (Impressoras 3d)
http://www.sethi.com.br/blog (Blog Impressoras 3d)
Avatar do usuário
guest2003
Word
 
Mensagens: 746
Registrado em: 13 Out 2006 11:48
Localização: Campinas - SP

Mensagempor albertorcneto » 11 Mai 2010 11:03

Bom, so para dar um feedback pra quem tiver problema parecido.

Frequencia do clock medida:
Anteriormente: 1,1 MHz
Com a ideia do Marcelo de usar xor: 1,15 MHz (4 instrucoes ASM ao inves de 18)
Com timer 0xFFFF FFFE com estouro em 0xFFFF FFFF: 13 MHz (Clock de referencia eh 26 MHz mas nao consegui achar o outro divisor)
Programando o pino sys_clkout2 com o clock principal multiplicado: 96 MHz


Resultado: Da pra gerar o clock que eu preciso. Mesmo o clock de 13 MHz da pro gasto ja que a conversao AD se da nos dois flankes.

Agora preciso ler sobre como funciona DMA e como setar pelo menos 2 canais pra conseguir transferir o valor de 2 portas para um endereco de memoria com uma velocidade alta.

Obrigado a todos.
"Nothing travels faster than the speed of light, with the possible exception of bad news, which obeys its own set of laws" ~ Douglas Adams
albertorcneto
Byte
 
Mensagens: 269
Registrado em: 28 Mar 2007 14:08

Mensagempor msamsoniuk » 11 Mai 2010 14:07

apenas 1.15MHz? entao acho que esse seu core nao roda a 720MHz como vc falou... pq se forem 4 instrucoes mesmo, daria 150 clocks por instrucao! tem algo errado hein! :)

albertorcneto escreveu:Bom, so para dar um feedback pra quem tiver problema parecido.

Frequencia do clock medida:
Anteriormente: 1,1 MHz
Com a ideia do Marcelo de usar xor: 1,15 MHz (4 instrucoes ASM ao inves de 18)
Com timer 0xFFFF FFFE com estouro em 0xFFFF FFFF: 13 MHz (Clock de referencia eh 26 MHz mas nao consegui achar o outro divisor)
Programando o pino sys_clkout2 com o clock principal multiplicado: 96 MHz


Resultado: Da pra gerar o clock que eu preciso. Mesmo o clock de 13 MHz da pro gasto ja que a conversao AD se da nos dois flankes.

Agora preciso ler sobre como funciona DMA e como setar pelo menos 2 canais pra conseguir transferir o valor de 2 portas para um endereco de memoria com uma velocidade alta.

Obrigado a todos.
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Mensagempor albertorcneto » 12 Mai 2010 04:03

Marcelo,

Tambem achei muito estranho. Mas, como eh a primeira vez que estou mexendo com ARM e primeira vez com Beagleboard tambem, ainda estou apanhando. O ARM (pelo menos o OMAP3530) tem diferentes clocks para diferentes dominios. Entao, pode ser que o clock para o interfaceamento com as GPIOs esteja setado em uma frequencia baixa ou com um divisor alto. Ainda estou aprendendo a questao do clock no ARM. O HC908 que eu aprendi na faculdade era muito mais facil. :lol: :lol: :lol: :lol: :lol: :lol: :lol: :lol: :lol: :lol:

Como o problema central ja foi resolvido de forma mais ou menos satisfatoria (com o timer ou com o pino sys_clkout2, que ainda nao consomem o processador), acho que o responsavel pelo projeto nao vai querer mexer mais nao.

O desafio agora eh conseguir coletar as informacoes do AD que vem de forma paralela para dois GPIO ports diferentes. Provavelmente vai ter que ser via DMA, mas ainda estou aprendendo o conceito pra depois tentar implementar um canal de DMA entre o endereco de memoria dos GPIOs e a SRAM.
"Nothing travels faster than the speed of light, with the possible exception of bad news, which obeys its own set of laws" ~ Douglas Adams
albertorcneto
Byte
 
Mensagens: 269
Registrado em: 28 Mar 2007 14:08

Mensagempor polesapart » 12 Mai 2010 10:14

albertorcneto escreveu:Marcelo,

Tambem achei muito estranho. Mas, como eh a primeira vez que estou mexendo com ARM e primeira vez com Beagleboard tambem, ainda estou apanhando. O ARM (pelo menos o OMAP3530) tem diferentes clocks para diferentes dominios.

(cortei aqui)

De fato, o controlador de GPIO costuma ficar em um barramento fora da CPU, que costuma ter um clock menor e/ou latência hehe.

Ao menos em processadores com geradores de clock mais espertos, pois aí não faz sentido usar GPIO mesmo. Eu não manjo de OMAP, mas é comum, além de controlador PWM, ter um (ou mais) pino(s) no periférico de timer, que pode fazer toggle quando alcança um determinado valor. Dá pra fazer coisas como ter uma interrupção quando o pino faz toggle, ou então, mais interessante ainda, uma interrupção quando o timer alcança um valor mais alto: se vc não tiver um pino de dma request ou algo do gênero, vc pode fazer essa interrupção te notificar que "x" samples já foram copiados por DMA ou algo do gênero.


Porém, pra tratar dma no linux, a menos que tenham inventado uma interface que eu desconheço, tem que ser via kernel, então um módulo simples seria uma boa. Pq a alternativa seria acessar via /dev/mem mas talvez vc tenha que mapear uma janela tão grande de memória que tenha problemas, acabe dereferenciando uns ponteiros inválidos ou coisa pior.
Warning: time of day goes back (-163479us), taking countermeasures. :)
Avatar do usuário
polesapart
Byte
 
Mensagens: 477
Registrado em: 19 Nov 2007 12:56
Localização: Curitiba

Mensagempor msamsoniuk » 12 Mai 2010 11:25

depende do tamanho do buffer... ateh 4KB eh soh alocar, jogar o endereco fisico para o controlador de DMA e retornar via mmap o endereco logico para a aplicacao. tanto kernel, quando aplicacao quanto DMA vao ver numeros separados, mas sera a mesma pagina de memoria... no caso de buffers maiores, tem que usar alocacao continua e daih complica um pouco mais para o kernel, mas tambem eh perfeitamente possivel.

normalmente nao tem tantos dominios de clock assim... depende de quantas tensoes tem no chip. normalmente vc tem uma tensao pequena para o core e uma tensao grande para a periferia. isso implica em usar transistores compactos e rapidos no core, deixando a periferia com transistores grandes e lentos. de qq forma, existe uma relacao inteira entre o clock do core e da periferia, de modo a ficarem sincronos e evitarem violacoes de setup/hold.

eu diria que o pratico para bus clock eh algo da ordem de 50MHz.

polesapart escreveu:
albertorcneto escreveu:Marcelo,

Tambem achei muito estranho. Mas, como eh a primeira vez que estou mexendo com ARM e primeira vez com Beagleboard tambem, ainda estou apanhando. O ARM (pelo menos o OMAP3530) tem diferentes clocks para diferentes dominios.

(cortei aqui)

De fato, o controlador de GPIO costuma ficar em um barramento fora da CPU, que costuma ter um clock menor e/ou latência hehe.

Ao menos em processadores com geradores de clock mais espertos, pois aí não faz sentido usar GPIO mesmo. Eu não manjo de OMAP, mas é comum, além de controlador PWM, ter um (ou mais) pino(s) no periférico de timer, que pode fazer toggle quando alcança um determinado valor. Dá pra fazer coisas como ter uma interrupção quando o pino faz toggle, ou então, mais interessante ainda, uma interrupção quando o timer alcança um valor mais alto: se vc não tiver um pino de dma request ou algo do gênero, vc pode fazer essa interrupção te notificar que "x" samples já foram copiados por DMA ou algo do gênero.


Porém, pra tratar dma no linux, a menos que tenham inventado uma interface que eu desconheço, tem que ser via kernel, então um módulo simples seria uma boa. Pq a alternativa seria acessar via /dev/mem mas talvez vc tenha que mapear uma janela tão grande de memória que tenha problemas, acabe dereferenciando uns ponteiros inválidos ou coisa pior.
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Mensagempor polesapart » 12 Mai 2010 11:40

Marcelo Samsoniuk escreveu:depende do tamanho do buffer... ateh 4KB eh soh alocar, jogar o endereco fisico para o controlador de DMA e retornar via mmap o endereco logico para a aplicacao. tanto kernel, quando aplicacao quanto DMA vao ver numeros separados, mas sera a mesma pagina de memoria... no caso de buffers maiores, tem que usar alocacao continua e daih complica um pouco mais para o kernel, mas tambem eh perfeitamente possivel.



Tá certo, eu tava pensando complicado (scatter-gatter ou algo do gênero). Mas me parece meio xunxo fazer isso via /dev/mem, tudo bem que o X faz exatamente isso até hj (ok, existe o dri2 + kms, mas é outra história) :P
Warning: time of day goes back (-163479us), taking countermeasures. :)
Avatar do usuário
polesapart
Byte
 
Mensagens: 477
Registrado em: 19 Nov 2007 12:56
Localização: Curitiba

Mensagempor msamsoniuk » 12 Mai 2010 11:46

ou o lance eh partir para o uclinux: garantia de que mmap() sempre retorna blocos continuos e DMA-ready. para que complicar? :D

polesapart escreveu:
Marcelo Samsoniuk escreveu:depende do tamanho do buffer... ateh 4KB eh soh alocar, jogar o endereco fisico para o controlador de DMA e retornar via mmap o endereco logico para a aplicacao. tanto kernel, quando aplicacao quanto DMA vao ver numeros separados, mas sera a mesma pagina de memoria... no caso de buffers maiores, tem que usar alocacao continua e daih complica um pouco mais para o kernel, mas tambem eh perfeitamente possivel.



Tá certo, eu tava pensando complicado (scatter-gatter ou algo do gênero). Mas me parece meio xunxo fazer isso via /dev/mem, tudo bem que o X faz exatamente isso até hj (ok, existe o dri2 + kms, mas é outra história) :P
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Mensagempor polesapart » 12 Mai 2010 11:47

Marcelo Samsoniuk escreveu:normalmente nao tem tantos dominios de clock assim...


Bom, eu não disse que tem, e se entendi direito, o povo tbm não hehe. Geralmente nos ARM só a ram (e as vezes a flash) tão no barramento local, o resto passa por um bridge e fica num barramento com clock mais baixo (num de de 400mhz que eu tava olhando, tem + 2 bus: o clock pros bancos de ram externos é de 100mhz e tem um outro de 33mhz pros perifericos). Mas é até excessão ter 2 bus extras, o mais simples seria ter só uma.

O que eu falei são dos periféricos específicos pra gerar timers de software, este costumam ter seus registradores nesta bus de periféricos, de clock menor, já que geralmente vc apenas os programa raramente e deixa eles rodando autônomos. Os controladores de GPIO são controversos, eu vi mais de uma solução em que o clock deles era bem inferior ao da CPU. No caso da NXP o povo reclamou que era muiiiiito inferior, o que ela fez foi colocar no barramento mais rápido, o que aumentou em 3.5 vezes a performance, mas ainda assim, não alcançam metade do clock mesmo num loop bem construido.

EDITADO: o clock pra gerar os pulsos em si estes periféricos costumam pegar da saída do PLL, que pode ser igual ou maior que o do core da cpu...
Warning: time of day goes back (-163479us), taking countermeasures. :)
Avatar do usuário
polesapart
Byte
 
Mensagens: 477
Registrado em: 19 Nov 2007 12:56
Localização: Curitiba


Voltar para ARM

Quem está online

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

cron

x