RTOS para HCS08

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

Moderadores: 51, guest2003

Mensagempor msamsoniuk » 13 Fev 2011 00:38

Aquino escreveu:Eu entendi a tua idéia, é basicamente uma autômato com troca de estados por processamento de mensagens, não é?
É interessante também, eu costumo implementar coisas simples utilizando autômatos, porém, não utilizo filas de mensagens.


pois eh, eu realmente nao sei se tem vantagem ou nao! em teoria, sim, deveria ser um negocio bacana, pq vc monta um software grande sem pensar muito e tem tudo padronizado... mas sera que realmente eh? se for pensar em um sistema de comunicacao simples, por exemplo, um gateway entre duas interfaces ethernet.

uma abordagem direta seria na propria interrupcao de tratamento do frame recebido chamar as funcoes necessarias (firewall e roteamento, por exemplo) ateh o frame ser encaminhado para fora, algo como:

Código: Selecionar todos
interrupt void ethernet_recv()
{
  frame *p;
  while(p = get_frame())
  {
    firewall(p);
  }
}

void firewall(frame *p)
{
  if(check(p)
  {
    forward(p);
  }
}

void forward(frame *p)
{
  ethernet_xmit(p, get_iface_by_route(p));
}


o ruim disso eh que o processo eh completamente atomico do inicio ao fim, pq o tratamento de interrupcao nao eh quebrado. e no caso de um sistema SMP eh dificil fazer o hardware espalhar o processamento de interrupcoes. no caso de um sistema com dois cores e duas interfaces, eh mais facil atribuir um core por interface. mas se o trafego for assimetrico, temos perda de performance em funcao da assimetria.

colocar uma fila de mensagens permitiria eliminar essa atomizacao do processamento de pacote na interrupcao e granularizar melhor o processamento em um sistema SMP:

Código: Selecionar todos
interrupt void ethernet_recv()
{
  frame *p;
  while(p = get_frame())
  {
    send(p,firewall);
  }
}

void firewall()
{
  frame *p = recv();
  if(check(p))
  {
    send(p,forward);
  }
}

void forward()
{
  frame *p = recv();
  send(p, get_iface_by_route(p));
}


desta forma, o tempo preso na interrupcao diminui. a interrupcao vai colocar algumas mensagens no escalonador de processos e eles vao ser processados pelo firewall, que vai devolver mensagens para o roteamento e assim por diante. assim, mesmo que as interrupcoes fiquem fixas em um determinado core, o processamento pode ser espalhado nos outros cores.

Agora, como ficaria na tua implementação quando uma das tasks precisa de temporizações? Utilizaria diretamente os timers ou teria que entrar dentro do switch, processar a mensagem e verificar que a contagem ainda não chegou a zero e pular fora? Dessa forma o escalonador ganha, pois a fila de tarefas bloqueada, dependendo da implementação, decrementa apenas o primeiro nodo e os outros vem atrás...


pois eh, se for necessario funcionalidade de tempo real, nao tem jeito, tem que ter um timer de hardware associado. eu acho que o modelo ideal nesse caso eh cada processo enviar uma mensagem para um processo de gerenciamento de timer se registrando. o processo de gerenciamento de timer entao utiliza uma interrupcao de timer para gerar uma referencia de tempo e decrementa cada timer registrado, enviando uma mensagem para o processo que se registrou originalmente.

eu pensei em algo como:

Código: Selecionar todos
interrupt rtc_recv()
{
  static frame p = RTC_WAKEUP;
  send(p,rtc);
}

vod rtc()
{
  frame *aux, *p = recv();
  static frame *clients = NULL;
 
  switch(p->event)
  {
    RTC_WAKEUP_EVENT:
      for(aux = clients; aux; aux = aux->next;
        if(!aux->counter--)
          send(aux,aux->src);
      break;
    RTC_REGISTER:
      add(p,clients);
      break;
  }
}


os processos se registram com uma mensagem que contem o valor do contador. se a referencia do timer eh 1ms, o valor de counter vai ser um multiplo e vai ser decrementado cada vez q a interrupcao do timer enviar uma mensagem para o processo principal de timer. quando counter chega a zero, ele envia uma mensagem para o processo que pediu.

daih com isso vc tem uma especie de soft-timer, onde um timer de referencia pode ser compartilhado por varios processos. por outro lado, em processadores muito pequenos este conceito ainda eh abstrado demais e consome memoria demais. daih acho que algo mais simples acaba sendo necessario.
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Mensagempor Red Neck Guy » 13 Fev 2011 09:46

Em processadores pequenos, tipo os 9S08SH4, 9S08QA2 e etc... Eu tenho implementado o sistema utilizando autômatos da seguinte forma:

Código: Selecionar todos
typedef enum{
  inicio,
  LedLigado,
  ContandoFlash,
  LedDesligado,
  ContandoDark 
}eAppStates;

typedef enum{
  semSinal,
  ContadorFlashZerado,
  ContadorDarkZerado
}eAppSignals;

#define   SET_LED                     //DEFINIÇÃO PARA SETAR O PINO ONDE ESTÁ O LED
#define   CLR_LED                     //DEFINIÇÃO PARA RESETAR O PINO ONDE ESTÁ O LED

unsigned int tempo=0;
///Só pra ilustrar,
//Função chamada a cada 1ms pelo timer de sincronismo do sistema
#pragma interrupt
void appTimer(void){
   if(tempo)
     tempo--;
}

void main(void){
  eAppStates estadoAtual=inicio;
  eAppSignals sinal=semSinal;

  for(;;){
      switch(estadoAtual){
         case inicio      :  estadoAtual = LedLigado;                         
                        break;   
         case LedLigado   :  SET_LED;
                             tempo = 200;
                        estadoAtual = ContandoFlash;
                        break;
         case ContandoFlash: if(!tempo)
                               estadoAtual = LedDesligado;
                        break;
         case LedDesligado:  CLR_LED;
                             tempo = 1000;
                        estadoAtual = ContandoDark;
                        break;
         case ContandoDark:  if(!tempo)
                               estadoAtual = LedLigado;
                        break;
      }
   }
 }
}


Meu autômato está bem tosco, mas foi feito em 2 minutos...
O teu é coisa pra coisas bem mais complexas, ou meu é um bem mais humilde que se destina a facilitar o desenvolvimento de aplicações para as coisas toscas do dia-a-dia de 90% dos desenvolvedores do mercado.
Com esse método, eu fiz semana passada, uma sirene com comunicação serial, multi-toque e mais umas frescuras. Tudo num 9S08SH4.


Obs.Muitos irão observar o código e dizer que dá pra fazer como demonstrado abaixo, porém, isso é só um exemplo. Perceba que pra começar, o processador não fica parado em loop contando tempo. Dessa forma tempos CPU livre para outras tarefas
Código: Selecionar todos

void main(void){
   SET_LED;
   delay(200);
   CLR_LED;
   delay(200);
}

ASM51 descanse em paz!
Avatar do usuário
Red Neck Guy
Dword
 
Mensagens: 1968
Registrado em: 12 Out 2006 22:24

Mensagempor proex » 13 Fev 2011 13:39

Isso ai pra mim chama-se maquina de estado.

.
proex
Dword
 
Mensagens: 2101
Registrado em: 11 Out 2006 14:05
Localização: São Paulo

Mensagempor Red Neck Guy » 13 Fev 2011 14:06

proex escreveu:Isso ai pra mim chama-se maquina de estado.

.


Também.
ASM51 descanse em paz!
Avatar do usuário
Red Neck Guy
Dword
 
Mensagens: 1968
Registrado em: 12 Out 2006 22:24

Mensagempor msamsoniuk » 13 Fev 2011 15:24

tem uma sutileza interessante entre estado e evento. no inicio eu pensei que cada processo teria uma maquina de estados que mudaria conforme o evento, entao cheguei a comecar fazendo algo um switch para estados e dentro de cada estado um switch para os eventos naquele estado:

Código: Selecionar todos
void processo()
{
  static state = HALT;
  message *p = recv();

  switch(state)
  {
    case HALT:
      switch(p->event)
      {
        EVENT_START:
          state = START;
          break;
        ...
      }
      break;
    ...
  }
}


eh interessante em alguns casos, mas parece nao ser mandatorio. ter apenas um switch para os eventos parece ser suficiente na maioria dos casos, pois a logica soh roda se chegar uma mensagem, portanto a maquina soh anda se existirem eventos! no caso de ter estados internos, pareceu entao ser mais logico inverter:

Código: Selecionar todos
void processo()
{
  static state = HALT;
  message *p = recv();

  switch(p->event)
  {
    EVENT_RESET:
      state = HALT;
      break;
    EVENT_RX:
      switch(state)
      {
        HALT:
          state = START;
          break;
        ...
      }
  }
}


acho isso pq eventos parecem ter precedencia sobre estados!

por exemplo, em um controlador de comunicacao que esta recebendo um frame, os estados mudam conforme o andamento da comunicacao (preambulo, enderecos, comandos, payload, checksum, etc). se chegar um evento pedindo um reset, o evento tem precedencia.

mas eh meio filosofico isso neh?
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Mensagempor polesapart » 13 Fev 2011 21:47

Eu bolei um esqueminha parecido aqui. Os eventos são despachados pra handlers, que internamente costumam ser FSMs (finite-state machines ou máquinas de estados finitos), mas nem sempre isto fica explícito, pois tem casos em que só há um estado :P

O bacana é que quanto a timers, minhas necessidades de análise temporal podem ser feitas assincronamente, então o que eu faço é colocar um timestamp na captura do evento. Depois, o handler usa esta informação para analizar coisas como o tempo entre um evento e outro.

Um exemplo bacana disso é um periférico com a função de capture, quando o nível lógico em um pino muda, o periférico captura o valor do timer e dispara uma irq, assincronamente. Quando a irq é atendida, eu simplesmente faço o forward destes dados como um evento direcionado a uma task. Quando a task for rodar, ela analisa os eventos e, combinando o nível lógico com o timestamp, usa isto como estímulo para uma máquina de estados que reage em função do nível lógico e do tempo estar dentro de certos parâmetros; Eis por exemplo uma tarefa pra decodificar protocolos seriais por software que não consome tanta cpu, pois o que mata nas implementações comuns é que elas geralmente são síncronas, mascarando irqs e ficando em loops toscos durante boa parte do tempo. Claro que olhando assim, por cima, pra isto funcionar precisaria ou ter o mínimo necessário de slots de tempo de cpu livres garantidos, ou uma fila de eventos bem grande. Neste último caso há um atraso considerável no processamento dos dados e as vezes isto não é aceitável.

Isto é ajustável até próximo dos limites absolutos, como o sistema é preemptivo e as tarefas tem diferentes prioridades, uma tarefa de alta prioridade ainda consegue "consumir" eventos sem que eles acumulem na fila.

E apesar de ter dois timers rodando com diferentes bases de tempo, nenhum deles gera irqs periódicas.

É um sistema 100% reativo, do ponto de vista da cpu, até o momento ela não precisou fazer polling de nada; Na verdade, habilitei um flag da cpu que faz ela entrar em sleep quando retorna pra tarefa de mais baixa prioridade (a tarefa "ociosa").


Isto vai mudar um pouco quando eu agregar a pilha tcp/ip, pois pra operar marromenos dentro das especificações vou precisar de um timer de 30ms pra fazer os retransmits do tcp e vou usar um múltiplo disso pra fazer o polling do status da porcaria da PHY conectada a porta ethernet, pra saber se tem link, pq este desgramado não tem como informar o MAC disto, e o mac tampouco gera irq, o que achei uma cagada tremenda do povo da IEEE (alguns phy tem um pino de irq externo pra compensar isto, infelizmente não é o caso do desenho aqui) :P

Ainda assim, 30ms ou mesmo 1 ms não seria grande coisa, pois os eventos de timer periódicos não são deadlines, ou seja, eles podem atrasar sem problemas, o que significa que eles ainda podem ser despachados pelo sistema de passagem de mensagens, sendo priorizados, e chegar com leve atraso e jitter sem que o sistema sofra nenhum efeito colateral com isto (em tese, na prática, poderiam existir heisenbugs no meio desta salada).

Se eu precisasse de algo hard real time não teria problema, bastaria agregar parte da lógica numa irq de timer e parte dela numa tarefa de altíssima prioridade. Mas tem coisas que é melhor usar outra abordagem hehe
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 polesapart » 13 Fev 2011 22:03

Acaba de me ocorrer que dá pra usar algo semelhante a implementação de timers atualmente em uso no linux 2.6 ...

ao invés de ter algo assim:

periodic_irq_timer()
{
timer *p;
for (p = timer_list[0]; p; p = p->next)
if (current_time() > p->deadline)
timer_task_dispatch(p);

}

... que faz polling de timeouts a cada vez que a irq cicla ...


o que a implementação atual faz é agendar a irq para disparar apenas no evento mais velho da lista ... e ajustar isto a medida em que as irqs vao ocorrendo.

Existem umas questões problemáticas aí, mas em tese, dá pra eliminar pollings totalmente: o timer ocorre como um evento programado apenas se necessário, existindo apenas uma pequena janela para eventos desnecessários.
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 » 14 Fev 2011 00:18

eu nao entendi pq vc precisa de polling! no caso de um sistema orientado a mensagens, nao vejo pq as interrupcoes nao possam enviar mensagens. assim, o loop principal ficaria da seguinte forma:

Código: Selecionar todos
for(;;)
{
  if(ipc_queue)
    ipc_queue->to();
  else
    mcu_sleep();
}


ou seja, se nao existem mensagem na fila, o mcu dorme e espera! se ocorrer uma interrupcao, a interrupcao acorda o mcu, que processa a fila e escalona o processo destino. uma vez processada a mensagem, se nada mais eh necessario a fila acaba vazia e o mcu dorme novamente.

mesmo no caso dos timers, isso nao seria problema. no meu conceito todos os timers sao soft timers derivados de um timer principal, digamos de 1ms. isso significa que cada vez que ocorre uma interrupcao do timer de 1ms uma mensagem eh enviada para o processo que gerencia os soft timers. cada timer registrado eh decrementado e, se atingir seu timeout, o processo que gerencia os timers envia uma mensagem para o processo que registrou o timer. parece estranho o processo ser acordado 1000x segundo apenas para processar timers, mas na realidade o tempo em que o mcu realmente fica acordado eh menor que 1%. eu acho isso interessante pq nos sistemas de comunicacao costumam existir muitos timers, entao nao dah para esperar existir timers no hardware capazes de suprir toda essa demanda.

bom, o problema que poderia acontecer no sistema com mensagens o tempo gasto gerando mensagens ser maior que o tempo processando. mas neste caso eu tenho uma solucao simples: as mensagens vem de um pool fixo de mensagens! se o pool acabar, a interrupcao que estava gerando mensagens bloqueia a comunicacao via controle de fluxo (o que significa que nao havera mais interrupcoes) e envia uma mensagem para ela mesma. quando as mensagens que estiverem na fila terminarem de ser processadas, provavelmente o pool de mensagens estara cheio novamente e a ultima mensagem da fila sera justamente uma mensagem enviada pela propria interrupcao solicitando o desbloqueio da comunicacao via controle de fluxo (o que significa que havera novas interrupcoes).

desta forma nenhuma mensagem importante eh perdida.
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Mensagempor polesapart » 14 Fev 2011 07:39

Mas foi o que eu quis dizer: Um timer que dispara uma irq que tem que consultar se há tarefas esperando mensagens ("eventos de timer"), ou pior ainda, simplesmente mandar mensagens pra todas as tarefas registradas para recebê-las, é um mecanismo de polling disfarçado, e é exatamente disto que eu quero fugir! :P Já que as necessidades de realtime por enquanto são bem relaxadas, eu não preciso de uma interrupção de timer periódica. E quando tava pensando em precisar, lembrei da forma como o linux gerencia isso recentemente , e bom, com isto eu realmente não preciso :D

Vou usar uma versão ultra-simplificada da idéia do dynticks, descrita aqui: http://kerneltrap.org/node/6750

No teu caso, vc pode precisar de muitos soft timers, no meu caso, ainda na pior aplicação que tenho planejada, não vai ser o caso, então a irq acordando a cpu (ainda que fosse a cada 10ms) quando o sistema estiver ocioso representa um pequeno aumento no consumo de corrente. Nesta outra aplicação, a coisa vai mover a bateria, e ao invés de simplesmente colocar a cpu em modo sleep, tou pensando em usar um deep power down ou algo, onde não dá pra processar timers com esta granularidade, até pq rebootar a danada neste ritmo consumiria mais tempo e mais bateria que a alternativa hehe.

Eu tou fazendo isto mais pra testar este conceito, pois a aplicação atual não será movida a bateria, apenas a que farei em seguida, mas assim já afogo dois coelhos com uma caixa d'água só :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 polesapart » 14 Fev 2011 07:49

Outra coisa,

Um dos testes que faço pra ver como o rtos se comporta sob pressão é justamente ter um timer de irq disparando numa sucessão muito rápida (centenas de KHz) e enviando mensagens para uma ou mais tarefas. Descobri um bug no mecanismo de preempção graças a isso ehhehe
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 » 14 Fev 2011 14:25

polling disfarcado foi F***! depois dessa vou largar tudo e vou para casa chorar...

polesapart escreveu:Mas foi o que eu quis dizer: Um timer que dispara uma irq que tem que consultar se há tarefas esperando mensagens ("eventos de timer"), ou pior ainda, simplesmente mandar mensagens pra todas as tarefas registradas para recebê-las, é um mecanismo de polling disfarçado, e é exatamente disto que eu quero fugir! :P Já que as necessidades de realtime por enquanto são bem relaxadas, eu não preciso de uma interrupção de timer periódica. E quando tava pensando em precisar, lembrei da forma como o linux gerencia isso recentemente , e bom, com isto eu realmente não preciso :D

Vou usar uma versão ultra-simplificada da idéia do dynticks, descrita aqui: http://kerneltrap.org/node/6750

No teu caso, vc pode precisar de muitos soft timers, no meu caso, ainda na pior aplicação que tenho planejada, não vai ser o caso, então a irq acordando a cpu (ainda que fosse a cada 10ms) quando o sistema estiver ocioso representa um pequeno aumento no consumo de corrente. Nesta outra aplicação, a coisa vai mover a bateria, e ao invés de simplesmente colocar a cpu em modo sleep, tou pensando em usar um deep power down ou algo, onde não dá pra processar timers com esta granularidade, até pq rebootar a danada neste ritmo consumiria mais tempo e mais bateria que a alternativa hehe.

Eu tou fazendo isto mais pra testar este conceito, pois a aplicação atual não será movida a bateria, apenas a que farei em seguida, mas assim já afogo dois coelhos com uma caixa d'água só :P
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Mensagempor polesapart » 14 Fev 2011 14:48

Marcelo Samsoniuk escreveu:polling disfarcado foi F***! depois dessa vou largar tudo e vou para casa chorar...


Nah, vai ao banheiro e volta, ninguém vai perceber ahuehuhuae :D
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 polesapart » 14 Fev 2011 15:20

Marcelo Samsoniuk escreveu:... mas demorou muito para chegar e a inercia dominou a minha mente! :(


Isto soa a desnutrição: falta de pizza.



Tou dando uma zoiada nisso e tou gostando :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 Red Neck Guy » 17 Fev 2011 16:40

Minha alteração do Helium com as duas uarts incluidas e um teste de semáforos:

http://www.chavedigital.com.br/helio.rar
ASM51 descanse em paz!
Avatar do usuário
Red Neck Guy
Dword
 
Mensagens: 1968
Registrado em: 12 Out 2006 22:24

Anterior

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

Quem está online

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

x