Contagem de ciclos com CPUs modernas (por exemplo, ARM)


14

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.

Respostas:


7

Eu voto no DMA. É realmente flexível no Cortex-M3 e superior - e você pode fazer todo tipo de loucura, como obter dados automaticamente de um local e enviar para outro com taxa especificada ou em alguns eventos sem gastar QUALQUER ciclo de CPU. O DMA é muito mais confiável.

Mas pode ser bem difícil de entender em detalhes.

Outra opção são os núcleos flexíveis no FPGA com a implementação de hardware dessas coisas restritas.


1
Eu gosto da noção de DMA. Mas não acho que o núcleo do Cortex M3 tenha DMA - isso é função dos chips de fabricantes individuais, e todos parecem implementá-lo de maneira diferente. Uma coisa que eu acho incômoda com pelo menos a implementação com a qual realmente joguei (STM32L152) é que não consigo encontrar nenhuma maneira de ter um strobe de pinos quando os dados do DMA são emitidos. Também não está claro quais fatores podem afetar a pontualidade do DMA.
30811

1
De qualquer forma, em relação a uma das primeiras aplicações em que eu estava pensando em bater de bicicleta com precisão, publiquei mais informações na pergunta original. Estou curioso para o que você pensa. Outra situação em que eu estava pensando em bater o ciclo seria enviar dados de exibição para um LCD colorido. Os dados seriam armazenados em buffer na RAM usando cores de 8 bits, mas a tela precisa de cores de 16 bits. A maneira mais rápida em que pensei em produzir dados seria usar o hardware para gerar os strobes de gravação, para que a CPU tivesse apenas que registrar os dados. Seria bom converter 8-> 16 bits em um pequeno buffer ... #
308

1
... e, em seguida, organize o DMA para transferir isso, ou qual seria a melhor abordagem?
307

4

As informações de tempo estão disponíveis, mas, como você apontou, às vezes pode ser vago. Há muitas informações de tempo na Seção 18.2 e na Tabela 18.1 do Manual de Referência Técnica do Cortex-M3, por exemplo, ( pdf aqui ) e um trecho aqui:

excerto de 18,2

que fornecem uma lista de condições para o tempo máximo. O momento para muitas instruções depende de fatores externos, alguns dos quais deixam ambiguidades. Eu destaquei cada uma das ambiguidades que encontrei no seguinte trecho dessa seção:

[1] Os ramos tomam um ciclo para obter instruções e, em seguida, recarregam o pipeline para obter instruções de destino. As ramificações não obtidas são um total de 1 ciclo. As ramificações capturadas com um imediato são normalmente 1 ciclo de recarga de tubulação (total de 2 ciclos). As ramificações capturadas com operando de registro são normalmente 2 ciclos de recarga de tubulação (total de 3 ciclos). A recarga do pipeline é mais longa [Quanto tempo?] Ao ramificar para instruções de 32 bits desalinhadas, além de acessos à memória mais lenta. Uma dica de ramificação é emitida no barramento de código que permite que um sistema mais lento [Quanto mais lento?] Seja pré-carregado. Isso pode [Isso é opcional?] Reduzir [Em quanto?] A penalidade do alvo da ramificação por memória mais lenta, mas nunca menos do que a mostrada aqui.

[2] Geralmente, as instruções de armazenamento de carga levam dois ciclos para o primeiro acesso e um ciclo para cada acesso adicional. Lojas com compensações imediatas levam um ciclo.

[3] UMULL / SMULL / UMLAL / SMLAL usam terminação antecipada, dependendo do tamanho dos valores de origem [Quais tamanhos?]. Estes são interruptíveis (abandonados / reiniciados), com pior latência de um ciclo. As versões MLAL levam de quatro a sete ciclos e as versões MULL, de três a cinco ciclos . Para o MLAL, a versão assinada é um ciclo a mais que a não assinada.

[4] As instruções de TI podem ser dobradas . [Quando? Ver comentários.]

[5] Os tempos de DIV dependem do dividendo e do divisor . [O mesmo problema que MUL] DIV é interrompível (abandonado / reiniciado), com pior latência de um ciclo. Quando dividendo e divisor são semelhantes [Como semelhante?] Em tamanho, dividir termina rapidamente. O tempo mínimo é para casos de divisor maior que dividendo e divisor de zero. Um divisor de zero retorna zero (não é uma falha), embora uma interceptação de depuração esteja disponível para capturar esse caso. [Quais são os intervalos fornecidos para MUL?]

[6] O sono é um ciclo para a instrução mais quantos ciclos de sono forem apropriados. O WFE usa apenas um ciclo quando o evento já passou. O WFI é normalmente mais de um ciclo, a menos que ocorra uma interrupção exatamente ao entrar no WFI.

[7] O ISB leva um ciclo (atua como ramificação). DMB e DSB levam um ciclo, a menos que haja dados pendentes no buffer de gravação ou LSU. Se uma interrupção ocorre durante uma barreira, ela é abandonada / reiniciada.

Para todos os casos de uso, será mais complexo que o "Esta instrução é um ciclo, esta instrução é dois ciclos, este é um ciclo ...", contando possível em processadores mais simples, lentos e mais antigos. Para alguns casos de uso, você não encontrará nenhuma ambiguidade. Se você encontrar ambiguidades, sugiro:

  1. Entre em contato com seu fornecedor e pergunte qual é o tempo das instruções para o seu caso de uso.
  2. Teste para especificar o comportamento ambíguo
  3. Teste novamente todas as revisões do processador e, especialmente, ao passar por alterações no fornecedor.

Esses requisitos provavelmente respondem à sua pergunta: "Não, não é uma boa ideia, a menos que as dificuldades encontradas valam o custo" - mas você já sabia disso.


1
Considero que o seguinte é vago: "O recarregamento do pipeline é mais longo ao ramificar para instruções de 32 bits desalinhadas, além de acessos à memória mais lenta" não diz se ele adiciona exatamente um ciclo e "Instruções de TI podem ser dobradas" não não especifica sob quais condições eles serão ou não serão.
Supercat

1
O tempo de "TI" parece especialmente preocupante, já que é uma instrução que costuma ser usada em um loop contado por ciclo e tenho certeza de que nem sempre pode ser dobrada. Eu acho que se alguém sempre ramifica para o início de um loop sensível ao tempo, força o loop a iniciar em um limite de palavra, evita quaisquer cargas ou armazenamentos condicionais dentro do loop e não se coloca imediatamente nenhuma instrução "IT" após o carregamento ou armazenamento de atualização de registro, os tempos de "TI" seriam consistentes, mas as especificações não deixam isso claro.
Supercat

1
Meu palpite seria que a TI provavelmente (sinceramente) notaria algo como: "Na ausência de estados de espera ou contenção de barramento de código, a dobragem da TI será garantida se (1) a instrução anterior for uma instrução de 16 bits que não acessou memória ou o contador de programa e (2) a instrução seguinte é uma instrução de 16 bits ou a instrução anterior não foi o alvo de uma ramificação "não alinhada". O dobramento da TI também pode ocorrer em outras circunstâncias não especificadas ". Essa especificação permitiria escrever programas com tempo previsível de instruções de TI, garantindo que o código fosse organizado conforme indicado.
Supercat

1
Uau - confesso que só havia passado por simples contagens de ciclos dos piores casos, em vez de realmente ter lutado com as advertências sob a mesa. Minha resposta atualizada destaca algumas outras ambiguidades.
9137 Kevin Vermeer

1
Existem muitas situações em que alguém está interessado nas contagens de pior caso e um número razoável em que está interessado em contagens de melhor caso (por exemplo, se uma porta SPI puder gerar um byte a cada 16 ciclos, a geração de cada byte levaria 14 ciclos na melhor das hipóteses, e a verificação da prontidão levaria 5 ciclos, a verificação da prontidão de cada byte limitaria a velocidade a uma byte a cada 19 ciclos de melhor caso; escrever cegamente com dois NOPs adicionados permitiria uma velocidade de um byte a cada 16 ciclos de melhor caso ) Os casos em que é necessário um tempo preciso não são tão comuns, mas podem surgir.
21811

3

Uma maneira de contornar esse problema é usar dispositivos com intervalos determinísticos ou previsíveis, como o Parallax Propeller e os chips XMOS:

http://www.parallaxsemiconductor.com/multicoreconcept

http://www.xmos.com/

A contagem de ciclos funciona muito bem com a hélice (a linguagem de montagem precisa ser usada), enquanto os dispositivos XMOS têm um utilitário de software muito poderoso, o XMOS Timing Analyzer, que trabalha com aplicativos escritos na linguagem de programação XC:

https://www.xmos.com/download/public/XMOS-Timing-Analyzer-Whitepaper%281%29.pdf


1
Eu estou começando a pensar que Leon tem ações em XMOS ... ;-)
Federico Russo

1
Eu apenas gosto das fichas e das pessoas que trabalham lá. A Parallax também é uma boa companhia com bons produtos.
21711 Leon Heller

1
Sim, sem ofensa. Parece-me que todas as respostas (exceto uma) em que o XMOS é mencionado são suas. Não há nada de errado em estar entusiasmado com alguma coisa.
Federico Russo

@Federico, @Leon - Isso é exatamente o que me preocupa um pouco sobre o XMOS: por que existe apenas um usuário no mundo (pelo menos é o que parece)? Se é tão bom, por que não é o assunto da cidade? Eu nunca ouvi alguém falar sobre isso, menos usá-lo.
Stevenvh

Experimente os fóruns XMOS: xcore.com
Leon Heller

2

A contagem de ciclos fica mais problemática à medida que você se afasta dos microcontroladores de baixo nível e entra em processadores de computação de uso geral. O primeiro geralmente tem um tempo de instrução bem especificado, em parte pelos motivos pelos quais você site. Também é porque a arquitetura deles é bastante simples, portanto os tempos de instrução são fixos e conhecíveis.

Um bom exemplo disso são a maioria dos PICs de Microchip. As séries 10, 12, 16 e 18 têm um tempo de instrução muito bem documentado e previsível. Isso pode ser um recurso útil no tipo de pequenas aplicações de controle para as quais esses chips se destinam.

À medida que você se afasta do custo ultra baixo, o designer pode gastar um pouco mais de área de chip para obter maior velocidade de uma arquitetura mais exótica, além de previsibilidade. Dê uma olhada nas variantes modernas do x86 como exemplos extremos disso. Existem vários níveis de caches, vitualização da memória, busca à vista, pipelining e muito mais, o que torna quase impossível os ciclos de instruções de contagem. Nesta aplicação, não importa, já que o cliente está interessado em alta velocidade, não na previsibilidade do tempo das instruções.

Você pode ver esse efeito trabalhando em modelos mais avançados de microchips. O núcleo de 24 bits (séries 24, 30 e 33) possui um tempo de instruções amplamente previsível, exceto por algumas exceções quando há contenções de barramento de registro. Por exemplo, em alguns casos, a máquina insere uma parada quando a próxima instrução usa um registrador com alguns modos de endereçamento indireto cujo valor foi alterado na instrução anterior. Esse tipo de paralisação é incomum em um dsPIC e, na maioria das vezes, você pode ignorá-lo, mas mostra como essas coisas surgem devido aos designers que tentam fornecer um processador mais rápido e capaz.

Portanto, a resposta básica é que isso faz parte da troca quando você escolhe um processador. Para aplicações de controle pequeno, você pode escolher algo pequeno, barato, baixo consumo de energia e com tempo de instrução previsível. À medida que você exige mais poder de processamento, a arquitetura muda para que você precise renunciar ao tempo previsível das instruções. Felizmente, isso é menos problemático, pois você obtém aplicativos de uso geral e intensivos em computação, então acho que as compensações funcionam razoavelmente bem.


Concordo que, em geral, os aplicativos que exigem mais computação tornam-se menos sensíveis ao tempo microscópico, mas há alguns cenários em que é possível precisar de um pouco mais de capacidade de processamento do que o PIC-18, mas também precisa de previsibilidade. Estou me perguntando até que ponto devo me esforçar para aprender coisas como as arquiteturas PIC de 16 bits ou até que ponto devo imaginar que o ARM provavelmente será adequado.
21811

0

Sim, você ainda pode fazê-lo, mesmo em um ARM. O maior problema com isso em um ARM é que o ARM vende núcleos, não chips, e o tempo do núcleo é conhecido, mas o que o fornecedor de chips envolve envolve variando de fornecedor para fornecedor e, às vezes, da família de chips para outro dentro do fornecedor. Portanto, um chip específico de um fornecedor específico pode ser bastante determinístico (se você não usa caches, por exemplo), mas fica mais difícil de portar. Ao lidar com 5 relógios aqui e 11 relógios lá usando cronômetros, é problemático, pois o número de instruções necessárias para amostrar o cronômetro e descobrir se o tempo limite expirou. Pelos sons de sua experiência anterior em programação, estou disposto a apostar que você provavelmente depura com um osciloscópio como eu, para que você possa experimentar um loop apertado no chip na velocidade do relógio, observar o spi ou i2c ou qualquer forma de onda, adicionar ou remover nops, altere o número de vezes no loop e basicamente ajuste. Como em qualquer plataforma, o não uso de interrupções ajuda muito a natureza determinística da execução das instruções.

Não, não é tão simples quanto um PIC, mas ainda é bastante viável, especialmente se o atraso / sincronismo se aproximar da taxa de clock do processador. Vários fornecedores baseados em ARM permitem multiplicar a taxa de clock e obter 60MHz de uma referência de 8 MHz, por isso, se você precisar de alguma interface de 2 MHz em vez de fazer algo a cada 4 instruções, poderá aumentar o relógio (se tiver o orçamento de energia) e, em seguida, use um cronômetro e ofereça muitos relógios para fazer outras coisas também.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.