|
Arquitetura do ARM Cortex M3:
E/S, Interrupções
MC404 2o semestre de 2014 |
|
Atualizado em 17 Nov 2014
Prof. Célio
Os processadores ARM das famílias M e R (usados em sistemas embarcados e de tempo real como o Cortex M3 e M4)
e da família A (usados em sistemas operacionais multiusuário/ multitarefa como Linux e Mac OS), usam E/S "mapeada em memória" ao contrário, por exemplo, dos processadores
AVR (8 bits) e Intel (32,64 bits), que usam instruções especiais para E/S, do tipo "in" e "out".
O "espaço de endereçamento" do Cortex M3 possui 32 bits, podendo endereçar, portanto, até 2**32
bytes (4 GBytes) de memória. Esse espaço é dividido em regiões de 512 MB conforme mostrado
no mapa de memória do Cortex M3,
onde apenas uma pequena quantidade existe físicamente em cada região:
no chip que estamos usando, por exemplo, a região de memória flash
(CODE) ocupa os primeiros 120 KB, a de SRAM os primeiros 20 KB (a partir do endereço 0x20000000),
e a de periféricos ~ 0xFFF00000 bytes a partir do endereço 0x40000000.
O que significa "E/S mapeada em memória"? - significa que os diversos registradores de E/S
associados a periféricos implementados no chip possuem endereços prefixados na "região de
periféricos" conforme se pode ver
no mapa de endereços de E/S do Cortex M3.
Note nesse mapa a grande quantidade de periféricos (alguns complexos como USB, Ethernet, SPI),
incorporados no chip da CPU.
O acesso a esses registradores se faz através das instruções convencionais LDR (para executar
uma operação de "entrada") e STR (para executar uma operação de "saida").
Passos para fazer E/S sobre um periférico genérico:
- Configuração (inicialização) do periférico:
uma ou mais operações de saída (via instrução STR) para configurar o "modo de operação" do periférico.
- Leitura do estado do periférico:
uma ou mais operações de entrada (via instrução LDR) para ler o o estado do periférico.
- Acionamento do periférico:
uma ou mais operações de saída (instrução STR) para "acionar" o periférico.
Quando múltiplas operações de E/S são feitas sobre o mesmo periférico, usualmente existe
um laço no passo 2 acima, para verificar se o periférico está "pronto" para a próxima operação,
seguido pelo passo 3 e de volta ao passo 2.
Este tipo de E/S é chamado de "busy waiting" pois a CPU vai executar "ociosamente" muitas
instruções (milhares e até milhões) no laço de espera para o periférico ficar "pronto para a
próxima operação". Isto pode ser inconveniente se a aplicação possui vários periféricos
que poderiam ser acionados em paralelo: nesses casos o "busy waiting" pode ser eliminado com o
uso de interrupções, assunto a ser discutido em seguida.
Interrupções no Cortex M3
Definição: uma interrupção é um evento assíncrono que desvia o fluxo de execução de
instruções da CPU para
uma posição prefixada de memória onde uma "rotina de interrupção" associada ao tipo do evento
é executada (por exemplo, para fazer uma "operação de entrada" em um periférico).
Quando a rotina de interrupção termina, a instrução imediatamente após aquela onde ocorreu o
desvio é executada "como se a interrupção não tivesse ocorrido": este é um requisito essencial
pois esta instrução poderia ser, por exemplo, um salto condicional
(lembrando que saltos condicionais, ao serem executados, consultam os bits de "flags" do
registrador de estado (PSR), que foram atualizados em instrução anterior ao salto.
É essencial, portanto, que esses bits sejam preservados e restaurados pela interrupção.
Fica claro, portanto, que algumas operações necessárias à preservação do "contexto do
programa interrompido" sejam executadas pelo hardware quando ocorre uma interrupção,
(o "contexto de um programa" inclui
pelo menos os "flags" e os registradores sendo usados pelo programa).
Para que uma interrupção possa ocorrer vários requisitos são necessários:
(i) um "pedido de interrupção" deve ser sinalizado (se advindo de um periférico externo,
por exemplo, por um sinal em algum pino da CPU),
(ii) interrupções devem estar globalmente habilitadas desligando o bit 7 da palavra de estado xPSR
(via instrução cpsie i).
(iii) o "tipo da interrupção" não deve estar inibido em algum dos registradores especiais
de "máscara de exceções".
(iv) a interrupção deve ser habilitada programando um dos registradores de E/S do periférico para este fim
(v)deve ser tambem habilitada para o nivel associado ao periférico no vetor de interrupções
via programação de registrador do controlador de interrupções NVIC (Nested Vector Interrupt Controller).
(vi) nenhum pedido mais prioritário de interrupção existe (pois pedidos concorrentes podem
ocorrer durante o tempo em que a instrução corrente está sendo executada).
Interrupções usualmente ocorrem no final da execução de uma instrução.
Existe a possibilidade de uma rotina de interrupção ser interrompida por uma interrupção
mais prioritária que a atual (nesses casos se diz que temos "interrupções aninhadas" ("nested
interrupts") e tanto o SW quanto o HW devem ter sido cuidadosamente projetados. Existem
muitas situações a serem consideradas no Cortex M3 e que não serão abordadas aqui. Essas
situações são exepcionais e podem ser evitadas pelo SW. Um caso em que elas podem ocorrer
são os pedidos do tipo NMI ("Non Maskable Interrupt") em que um periférico muito rápido
precisa ter seu "pedido de interrupção" atendido com o mínimo atraso.
Este tipo de interrupção veio substituir de forma mais econômica a interrupção FIQ ("Fast Interrupt") da família A.
Definição: "mecanismo de interrupção" é o conjunto de "ações indivisíveis" (isto é,
não interrompíveis) tomadas pelo HW no momento em que uma interrupção ocorre e antes que a primeira
instrução da rotina de interrupção seja executada.
Definição: quando existem posições distintas de memória para cada tipo de interrupção
para onde o fluxo de execução é desviado, se diz que o sistema implementa interrupções vetoradas.
Essas posições usualmente ocupam palavras consecutivas no início da memória flash e são
denominadas de vetor de interrupção.
Elas contêm uma instrução de salto para a rotina de
interrupção propriamente dita (que pode estar em qualquer posição da memória flash).
Em algumas CPUs (AVR, por exemplo), a posição no vetor determina a prioridade da interrupção
(quanto menor a posição, maior a prioridade).
Veremos a seguir que o Cortex M3 tem um esquema mais flexível de prioridades.
Os processadores ARM das famílias M e R
possuem "modos de operação" e um sistema de interrupções bem diferente dos da família A.
- Existem apenas dois modos de operação: "thread" (corresponde ao modo "usuário" na terminologia convencional)
e "handle" (modo "supervisor" na terminologia convencional).
- Apenas o "stack pointer" é replicado nos dois modos; todos os outros registradores
são comuns aos dois modos (ver a figura
registradores do Cortex M3).
- Interrupções vetoradas com prioridades programáveis conforme pode ser visto na figura
vetor de interrupções do Cortex M3.
O vetor de interrupções está localizado no início da memória flash (endereço 0).
Nesta figura nota-se uma peculiaridade interessante do Cortex M3: a posição 0 do vetor não
corresponde a nenhuma interrupção. Nela deve estar gravado o endereço da última palavra da
memória RAM: quando a CPU dá partida ("reset") o HW carrega esse valor no registrador
"Stack Pointer" (SP) e executa a instrução localizada na palavra seguinte ("Reset") que contem
um salto para a rotina que vai inicializar a aplicação gravada na memória flash.
Para obter o endereço da entrada correspondente na coluna "Exception Number" multiplique o valor
por 4 pois cada entrada ocupa 4 bytes. Por exemplo, o vetor de interrupção correspondente
ao relógio interno SYSTICK está localizado no endereço 4*15= 60 = 0x3C da memória flash.
- Dois "níveis de privilégio" para execução de instruções:
- "nível não privilegiado" - no qual a instrução MSR que altera registradores de estado, ver
registradores especiais , ou a instrução CPS que
muda o modo de operação da CPU, não são permitidas (geram uma exceção ao serem executadas).
- "nível privilegiado" - no qual todas as instruções são permitidas. No modo "handle" a execução de instruções
é sempre no nivel privilegiado. No modo "thread" pode ser privilegiado dependendo da configuração
do registrador especial CONTROL.
Mecanismo de interrupção do Cortex M3
- Empilha na pilha corrente os registradores: xPSR, PC, LR, R12, R3, R2, R1, R0 (nesta ordem, de forma que
o topo da pilha passa a apontar para R0, lembrando que a pilha "cresce" na direção de endereços
decrescentes). Observe que o valor do PC empilhado é o endereço da próxima instrução do programa
interrompido.
As 8 palavras empilhadas são denominadas de "stack frame".
- Carrega o LR com um código que indica um apontador para o "stack frame" e o modo de operação
da CPU quando ocorreu a interrupção (para ser usado quando ocorrer o retorno da interrupção).
- Busca no vetor de interrupção a instrução associada à interrupção.
(Essas operações são feitas em paralelo).
Nesse ponto a instrução contida no vetor é executada (salto para a rotina de interrupção).
Observe que a rotina de interrupção pode usar livremente os registradores R0 a R3 e R12, pois os mesmos
foram salvos na pilha pelo mecanismo de interrupção.
Se ela precisar de mais registradores de "rascunho" precisará salvá-los.
Retorno da rotina de interrupção
Quando uma das instruções POP, BX ou LDR carrega o PC, o endereço do "stack frame"
armazenado no LR é usado para desempilhar os registradores do "stack frame", retornando à
próxima instrução do programa interrompido.