Em muitas aplicações, uma CPU cuja execução de instrução possui uma relação de tempo conhecida com estímulos de entrada esperados pode lidar com tarefas que exigiriam uma CPU muito mais rápida se o relacionamento fosse desconhecido. Por exemplo, em um projeto que eu fiz usando um PSOC para gerar vídeo, usei código para gerar um byte de dados de vídeo a cada 16 relógios da CPU. Como o teste do dispositivo SPI está pronto e a ramificação do IIRC levaria 13 relógios, e o carregamento e armazenamento dos dados de saída levaria 11, não havia como testar o dispositivo quanto à prontidão entre bytes; em vez disso, simplesmente organizei para que o processador executasse o código com precisão de 16 ciclos para cada byte após o primeiro (acredito que usei uma carga indexada real, uma carga indexada fictícia e uma loja). A primeira gravação SPI de cada linha ocorreu antes do início do vídeo, e para cada gravação subsequente, havia uma janela de 16 ciclos em que a gravação poderia ocorrer sem saturação ou saturação de buffer. O loop de ramificação gerou uma janela de 13 ciclos de incerteza, mas a execução previsível de 16 ciclos significou que a incerteza para todos os bytes subseqüentes caberia na mesma janela de 13 ciclos (que por sua vez cabia na janela de 16 ciclos em que a gravação poderia ser aceitavelmente ocorrer).
Para CPUs mais antigas, as informações de tempo das instruções eram claras, disponíveis e inequívocas. Para ARMs mais recentes, as informações de tempo parecem muito mais vagas. Entendo que, quando o código está sendo executado a partir do flash, o comportamento do cache pode tornar as coisas muito mais difíceis de prever, portanto, espero que qualquer código contado em ciclo seja executado a partir da RAM. Mesmo ao executar o código da RAM, as especificações parecem um pouco vagas. O uso de código contado em ciclo ainda é uma boa idéia? Em caso afirmativo, quais são as melhores técnicas para fazê-lo funcionar de maneira confiável? Até que ponto alguém pode supor com segurança que um fornecedor de chips não vai deslizar silenciosamente em um chip "novo e melhorado" que reduz um ciclo a execução de determinadas instruções em certos casos?
Supondo que o loop a seguir inicie em um limite de palavras, como determinar com base nas especificações exatamente quanto tempo levaria (suponha o Cortex-M3 com memória de estado de espera zero; nada mais sobre o sistema deve importar neste exemplo).
myloop: mov r0, r0; Instruções simples e breves para permitir a pré-busca de mais instruções mov r0, r0; Instruções simples e breves para permitir a pré-busca de mais instruções mov r0, r0; Instruções simples e breves para permitir a pré-busca de mais instruções mov r0, r0; Instruções simples e breves para permitir a pré-busca de mais instruções mov r0, r0; Instruções simples e breves para permitir a pré-busca de mais instruções mov r0, r0; Instruções simples e breves para permitir a pré-busca de mais instruções adiciona r2, r1, # 0x12000000; Instrução de 2 palavras ; Repita o seguinte, possivelmente com operandos diferentes ; Continuará adicionando valores até que um transporte ocorra itcc adicionacc r2, r2, # 0x12000000; Instrução de 2 palavras, além de "palavra" extra para itcc itcc adicionacc r2, r2, # 0x12000000; Instrução de 2 palavras, além de "palavra" extra para itcc itcc adicionacc r2, r2, # 0x12000000; Instrução de 2 palavras, além de "palavra" extra para itcc itcc adicionacc r2, r2, # 0x12000000; Instrução de 2 palavras, além de "palavra" extra para itcc ; ... etc, com mais instruções condicionais de duas palavras sub r8, r8, # 1 bpl myloop
Durante a execução das seis primeiras instruções, o núcleo teria tempo para buscar seis palavras, das quais três seriam executadas, para que houvesse até três pré-buscadas. As próximas instruções são todas as três palavras cada, portanto, não seria possível que o núcleo buscasse instruções tão rapidamente quanto elas estão sendo executadas. Eu esperaria que algumas das instruções "it" levassem um ciclo, mas não sei como prever quais.
Seria bom se o ARM pudesse especificar certas condições sob as quais o tempo da instrução "it" seria determinístico (por exemplo, se não houver estados de espera ou contenção de barramento de código, e as duas instruções anteriores forem instruções de registro de 16 bits, etc.) mas eu não vi nenhuma dessas especificações.
Aplicativo de amostra
Suponha que alguém esteja tentando projetar uma placa-filha para um Atari 2600 para gerar saída de vídeo componente em 480P. O 2600 possui um relógio de pixel de 3,579 MHz e um relógio de CPU de 1,19 MHz (dot clock / 3). Para vídeo componente 480P, cada linha deve ser impressa duas vezes, implicando uma saída de clock de ponto de 7,158 MHz. Como o chip de vídeo da Atari (TIA) emite uma de 128 cores usando como sinal luma de 3 bits mais um sinal de fase com resolução de aproximadamente 18ns, seria difícil determinar com precisão a cor apenas observando as saídas. Uma abordagem melhor seria interceptar gravações nos registros de cores, observar os valores gravados e alimentar cada registro nos valores de luminância TIA correspondentes ao número do registro.
Tudo isso poderia ser feito com um FPGA, mas alguns dispositivos ARM bem rápidos podem ser comprados muito mais baratos que um FPGA com RAM suficiente para lidar com o buffer necessário (sim, eu sei que para os volumes que uma coisa dessas pode ser produzida, o custo não é '' um fator real). Exigir que o ARM observe o sinal do relógio recebido, no entanto, aumentaria significativamente a velocidade da CPU necessária. Contagens previsíveis de ciclos podem tornar as coisas mais limpas.
Uma abordagem de design relativamente simples seria fazer com que um CPLD observasse a CPU e o TIA e gerasse um sinal de sincronização RGB + de 13 bits, e então fizesse com que o ARM DMA capturasse valores de 16 bits de uma porta e os gravasse em outra com o tempo adequado. Seria um desafio interessante de design, no entanto, ver se um ARM barato poderia fazer tudo. O DMA pode ser um aspecto útil de uma abordagem tudo-em-um se for possível prever seus efeitos nas contagens de ciclos da CPU (especialmente se os ciclos do DMA puderem ocorrer em ciclos quando o barramento de memória estiver inativo), mas em algum momento do processo o ARM teria que executar suas funções de busca de tabela e observação de ônibus. Observe que, diferentemente de muitas arquiteturas de vídeo em que os registros de cores são gravados durante intervalos de apagamento, o Atari 2600 grava frequentemente em registros de cores durante a parte exibida de um quadro,
Talvez a melhor abordagem seja usar alguns chips de lógica discreta para identificar gravações em cores e forçar os bits mais baixos dos registros de cores para os valores adequados e, em seguida, usar dois canais DMA para amostrar os dados de saída do barramento da CPU e da saída TIA e um terceiro canal DMA para gerar os dados de saída. A CPU ficaria livre para processar todos os dados de ambas as fontes para cada linha de varredura, executar a tradução necessária e armazená-la em buffer para a saída. O único aspecto das tarefas do adaptador que teria que acontecer em "tempo real" seria a substituição de dados gravados no COLUxx, e isso poderia ser resolvido com o uso de dois chips lógicos comuns.