Como a pilha de chamadas opera durante uma interrupção no AVR?

8

(Específico para o Arduino Uno ...)

O que acontece com a pilha quando ocorre uma interrupção em um microcontrolador AVR e eu chamo uma função? O compilador embutiu o código? Ele armazena em cache a pilha em algum lugar e redefine o ponteiro da pilha? Ele possui uma pilha secundária apenas para interrupções?

Pelo que entendi, o vetor para a interrupção é um comando GOTO direto na montagem. Além disso, não acho que o microcontrolador mexa automaticamente com a pilha, por isso provavelmente é deixado sozinho. No entanto, isso ainda não explica como as funções funcionam durante um ISR.

Pinguim anônimo
fonte
A resposta abaixo é grande, assim Por favor, note que o compilador não pode chamadas de linha de interrupção, porque ... eles são chamadas de interrupção: D
Vladimir Cravero
1
@VladimirCravero eu quis dizer em linha as funções chamadas dentro da interrupção (se eu chamar foo () no ISR ele inline por isso não é realmente uma chamada de função?)
Pinguim Anonymous
Portanto, a resposta abaixo não responde à sua pergunta, ou responde? homem das cavernas explica o que acontece quando ocorre uma interrupção, mas não o que acontece quando uma função é chamada em um contexto interrompido. a resposta para o último seria: nada de especial, a função é chamada e é isso. a mágica acontece quando a interrupção é acionada, o que acontece depois (antes da iret) é apenas código normal, geralmente ininterrupto.
precisa saber é o seguinte
@VladimirCravero sim, faz (indiretamente). Eu estava falando sobre como a pilha foi modificada para um ISR, pensando que tinha que ser modificada para usar funções em primeiro lugar. Eu acho que as funções funcionam da mesma maneira exata após a instalação do ISR.
Anonymous Penguin
bem, você entendeu então. depois do salto para o vetor de interrupção, tudo fica bem e você pode ir aonde quiser.
Vladimir Cravero

Respostas:

16

O AVR é ​​uma arquitetura RISC, portanto, possui um gerenciamento bastante básico de interrupções no hardware. A maioria dos processadores mexe com a pilha durante as interrupções, embora existam alguns, principalmente o ARM e o PowerPC, que usam métodos diferentes.

De qualquer forma, é isso que o AVR faz para interrupções:

Quando ocorre uma interrupção, o hardware do processador executa estas etapas, que não são apenas um simples GOTO:

  1. Conclua a instrução atual.
  2. Desative o sinalizador de interrupção global.
  3. Empurre o endereço da próxima instrução na pilha.
  4. Copie o endereço no vetor de interrupção correto (de acordo com a interrupção que ocorreu) no contador do programa.

Agora, neste momento, o hardware fez tudo o que está fazendo. O software deve ser escrito corretamente para não quebrar as coisas. Normalmente, as próximas etapas são nesse sentido.

  1. Empurre o registro de status para a pilha. (Isso deve ser feito primeiro antes de ser alterado).

  2. Envie todos os registradores da CPU que serão (ou podem ser) alterados para a pilha. Quais registros que precisam ser salvos dessa maneira são definidos pelo modelo de programação. O modelo de programação é definido pelo compilador.

Agora o código de interrupção de trabalho pode ser executado. Para responder ao caso na questão de chamar uma função, ele apenas faz o que sempre faz, pressiona o valor de retorno na pilha e, em seguida, retorna quando terminar. Isso não afeta nenhum desses valores anteriores que salvamos na pilha até agora.

  1. Execute o código de trabalho do ISR.

Agora terminamos e queremos retornar da interrupção. Primeiro, precisamos fazer a limpeza do software.

  1. Coloque os registros da CPU que pressionamos na etapa 6.
  2. Retorne o valor do status salvo para o registro de status. Depois disso, devemos ter cuidado para não executar nenhuma instrução que possa alterar o registro de status.
  3. Execute a instrução RTI. O hardware executa estas etapas para esta instrução:

    uma. Habilite o sinalizador de interrupção global. (Observe que pelo menos uma instrução deve ser executada antes que a próxima interrupção seja respeitada. Isso impede que interrupções pesadas bloqueiem totalmente o trabalho em segundo plano.)

    b. Coloque o endereço de retorno salvo no PC.

Agora estamos de volta ao código normal.

Observe que há alguns pontos em que precisamos ter muito cuidado, principalmente em torno do registro de status e do salvamento de registros que podem ser alterados. Felizmente, se você estiver usando um compilador C, tudo isso será tratado sob as cobertas.

Além disso, você deve observar a profundidade da pilha. A qualquer momento em que as interrupções são ativadas, um ISR pode usar mais da pilha do que é óbvio, observando o código local. Claro, isso realmente não aparece muito, a menos que você esteja levando a memória ao limite.

Aqui está um link que descreve esse processo, se você desejar uma referência.

homem das cavernas
fonte
Qual é o objetivo das etapas 5/6? Parece-me bobagem não modificar diretamente os registros, embora eu ache que mexer com alguns registros durante interrupções possa criar alguns resultados desagradáveis.
Anonymous Penguin
1
As etapas 5 e 6 salvam o estado atual (antes da interrupção) do sistema, para que possa ser restaurado (etapas 8 e 9) após a interrupção, para permitir que o programa principal continue como se não tivesse sido interrompido.
Peter Bennett
1
Este é um excelente resumo.
Connor Lobo
3
Também é importante notar que, se você realmente, realmente sabe o que está fazendo, você pode desativar a gravação automática do estado e outros registros, com uma bandeira GCC ( ISR_NAKED). Isso pode permitir que você pule as etapas 5,6,8,9, em um contexto em que você realmente precisa desses ciclos. A desvantagem é que você deve estar absolutamente certo de que preservará quaisquer registros relevantes ou poderá substituí-los sem causar danos.
Connor Lobo
2
É interessante saber, mas eu faria a programação de montagem antes de usar esse sinalizador. Tão perigoso ...
homem das cavernas