Em um aplicativo em tempo real¹ em um ARM Cortex M3 (semelhante ao STM32F101), preciso pesquisar um pouco do registro de um periférico interno até que seja zero, o mais estreito possível. Eu uso a banda de bits para acessar o bit apropriado. O código C (de trabalho) é
while (*(volatile uint32_t*)kMyBit != 0);
Esse código é copiado na RAM executável no chip. Após alguma otimização manual², o ciclo de polling é reduzido para o seguinte, que eu cronometrei³ a 6 ciclos:
0x00600200 681A LDR r2,[r3,#0x00]
0x00600202 2A00 CMP r2,#0x00
0x00600204 D1FC BNE 0x00600200
Como a incerteza da votação pode ser reduzida? Um loop de 5 ciclos seria adequado ao meu objetivo: amostrar o mesmo bit o mais próximo possível de 15,5 ciclos depois de zero.
Minhas especificações exigem a detecção confiável de um pulso baixo, pelo menos, 6,5 ciclos de clock da CPU; classificá-lo de forma confiável como curta se durar menos de 12,5 ciclos; e classificá-lo de forma confiável desde que dure mais de 18,5 ciclos. Os pulsos não têm relação de fase definida com o relógio da CPU, que é minha única referência de tempo precisa. Isso requer um loop de votação de no máximo 5 horas. Na verdade, estou emulando código que rodava em uma CPU de 8 bits de décadas que poderia pesquisar com um ciclo de 5 horas, e o que isso fez se tornou a especificação.
Tentei compensar o alinhamento do código inserindo NOP antes do loop, nas muitas variantes que tentei, mas nunca observei uma alteração.
Tentei inverter o CMP e o LDR, mas ainda recebo 6 ciclos:
0x00600200 681A LDR r2,[r3,#0x00]
; we loop here
0x00600202 2A00 CMP r2,#0x00
0x00600204 681A LDR r2,[r3,#0x00]
0x00600206 D1FC BNE 0x00600202
Este é 8 ciclos
0x00600200 681A LDR r2,[r3,#0x00]
0x00600202 681A LDR r2,[r3,#0x00]
0x00600204 2A00 CMP r2,#0x00
0x00600206 D1FB BNE 0x00600200
Mas este é 9 ciclos:
0x00600200 681A LDR r2,[r3,#0x00]
0x00600202 2A00 CMP r2,#0x00
0x00600204 681A LDR r2,[r3,#0x00]
0x00600206 D1FB BNE 0x00600200
¹ Medir quanto tempo o bit está baixo, em um contexto em que nenhuma interrupção ocorre.
² O código inicial gerado pelo compilador usou r12 como o registro de destino e adicionou 4 bytes de código ao loop, custando 1 ciclo.
³ Os números fornecidos são obtidos com um emulador STIce em tempo real com precisão de ciclo supostamente preciso e seu recurso de acionador de emulador quando lido no endereço do registro. Anteriormente, tentei o contador "States" com um ponto de interrupção no loop, mas o resultado depende da localização do ponto de interrupção. A etapa única é ainda pior: sempre dá 4 ciclos para o LDR, quando isso é pelo menos em algum momento até 3.
gcc -Os -mcpu=cortex-m3
?
ldr
/ cbz reg, end_of_loop
para os internos e ainda assim cmp
/ bnz
na parte inferior. Mas isso daria a você um intervalo de pesquisa não uniforme, por exemplo, 1 em cada 8 pesquisas, caso isso importe.