Atividade 03¶
Registradores usados¶
SIM_SCGC5 (Endereço: 0x40048038)¶
SIM_SCGC5 |= 1 << 10;
System Clock Gating Control Register 5, tem a função de habilitar os clocks dos módulos dos PORTn (nesse caso do PORTB, bit 10).
Campo |
Descrição |
|---|---|
bit 10 |
Port B Clock Gate Control |
|
This bit controls the clock gate to the Port B module. |
0 |
Clock disabled |
1 |
Clock enabled |
PORTB_PCR19 (Endereço: 0x4004A04C)¶
PORTB_PCR19 = 1 << 8;
Pin Control Register n (n = 19 nesse caso), uma das funções é configurar o multiplexador de cada pino do PORTB definindo o recurso que será utilizado (nesse caso GPIO, portanto os bits 10:8 = \(001_2\)).
Campo |
Descrição |
|---|---|
bits 10:8 |
Pin Mux Control |
\(000_2\) |
Pin disabled (analog) |
\(\mathbf{001_2}\) |
Alternative 1 (GPIO) |
\(010_2\) |
Alternative 2 (chip-specific) |
\(011_2\) |
Alternative 3 (chip-specific) |
\(100_2\) |
Alternative 4 (chip-specific) |
\(101_2\) |
Alternative 5 (chip-specific) |
\(110_2\) |
Alternative 6 (chip-specific) |
\(111_2\) |
Alternative 7 (chip-specific) |
GPIOB_PDDR (Endereço: 0x400FF054)¶
GPIOB_PDDR |= 1 << 19;
Port Data Direction Register, configura a direção de cada um dos pinos do PORTB, se vai ser input ou output (configura o bit 19 como 1, definindo o pino correspondente como saída digital).
Campo |
Descrição |
|---|---|
bits 31:0 |
Port Data Direction |
0 |
Pin is configured as general-purpose input |
1 |
Pin is configured as general-purpose output |
GPIOB_PSOR (Endereço: 0x400FF044)¶
GPIOB_PSOR = 1 << 19;
Port Set Output Register, define os pinos do PORTB como nível lógico alto (nesse caso apenas o bit 19), porém pelo fato dos LEDs serem Active Low eles são desligados com esse registrador.
Campo |
Descrição |
|---|---|
bits 31:0 0 |
Port Set Output Corresponding bit in PDORn does not change |
1 |
Corresponding bit in PDORn is set to logic 1 |
GPIOB_PCOR (Endereço: 0x400FF048)¶
GPIOB_PCOR = 1 << 19;
Port Clear Output Register, define os pinos do PORTB como nível lógico baixo (nesse caso apenas o bit 19), porém pelo fato dos LEDs serem Active Low eles são ligados com esse registrador.
Campo |
Descrição |
|---|---|
bits 31:0 |
Port Clear Output |
0 |
Corresponding bit in PDORn does not change |
1 |
Corresponding bit in PDORn is cleared to logic 0 |
Fonte: [1]
Análise da função de atraso¶
O compilador otimizou o laço interno substituindo a condição original j < 7000 por um contador regressivo iniciado em 7000 e decrementado até zero, reduzindo o número de instruções necessárias para o teste da condição de parada.
0void delay_ms (int n) {
1 int i;
2 int j;
3 for (i = 0; i < n; i++)
4 for (j = 0; j < 7000; j++)
5 __asm volatile ("nop");
6}
Foram obtidas as seguintes instruções geradas pelo compilador para a função delay_ms:
578: movs r3, #0 ; i = 0
57a: cmp r3, r0 ; compara i com n
57c: blt.n 580 ; continua se i < n
57e: bx lr ; retorno da função
580: ldr r2, [pc, #12] ; carrega 7000 em j
582: nop ; atraso definido na linha 5 da função
584: subs r2, #1 ; j--
586: cmp r2, #0 ; compara j com zero
588: bne.n 582 ; repete enquanto j != 0
58a: adds r3, #1 ; i++
58c: b.n 57a ; repete laço externo
58e: nop
590: .word 0x00001b58 ; constante 7000
...
5a6: movs r5, #250
...
5b4: lsls r5, r5, #2 ; 250 << 2 = 1000
...
5c6: movs r0, r5
Nas linhas 5a6, 5b4 e 5c6, o compilador prepara o argumento da chamada à função delay_ms. Inicialmente, o valor decimal 250 é carregado no registrador r5; em seguida, a instrução lsls realiza um deslocamento lógico de dois bits para a esquerda, produzindo o valor 1000. Por fim, esse valor é copiado para o registrador r0, utilizado pela convenção de chamadas ARM para passagem do primeiro argumento de função.
Observa-se que as variáveis i e j foram mantidas inteiramente em registradores (r3 e r2, respectivamente), sem necessidade de armazenamento na pilha. Essa otimização elimina acessos à memória durante as iterações, reduzindo o número de ciclos de execução da rotina de atraso.
Obtendo o número de ciclos para cada instrução, temos [2]:
Instrução |
Ciclos |
|---|---|
|
2 |
|
1 |
|
1 |
|
1 |
|
1 |
|
1 ou 2* |
|
2 |
* 2 ciclos se o desvio for tomado, 1 ciclo caso contrário.
Portanto, para o laço interno são necessários 5 ciclos (linhas 582 a 588) para as primeiras 6999 iterações, sendo que a última executa um ciclo a menos.
Sendo necessário adicionar mais dois ciclos para a instrução que carrega o valor 7000 para o j (ldr r2, [pc, #12]), obtém-se:
Obtendo o tempo de uma iteração externa, acrescentando o controle do laço externo, considerando apenas as primeiras \(n - 1\), em que o desvio blt é tomado, na última iteração o custo é ligeiramente menor, pois o desvio não é executado, temos:
Sabendo que:
temos:
Logo:
Considerando que o parâmetro n é igual a 1000:
delay_ms(1000);
obtém-se:
Sendo obtido um erro de:
Ou seja, para o clock de 48 MHz configurado pelo Zephyr, o valor 7000 do laço interno não corresponde a 1 ms.
A análise do código assembly mostrou que o compilador otimizou os laços utilizando registradores para as variáveis de controle e substituindo o contador crescente interno por um contador regressivo. Considerando a frequência de 48 MHz configurada pelo Zephyr e a quantidade de ciclos estimada para cada instrução, obteve-se um atraso aproximado de 729 ms para a chamada delay_ms(1000). Assim, a constante 7000 utilizada no laço interno não produz um atraso de 1 ms por iteração nesse ambiente de execução, resultando em um erro aproximado de 27.1 %.
Referências