2016-06-01 1 views
1

J'essaie d'écrire un commutateur de contexte dans un gestionnaire d'interruption de minuterie. Actuellement, le commutateur de contexte est capable de basculer entre les contextes sur commande (coopérative). Dans le gestionnaire d'interruption, je tentais de:ARM v6 Interrupteur de contexte IRQ

  1. Enregistrez le compteur de programme actuel comme le lieu le vieux fil doit continuer d'exécuter
  2. Passer en mode SVC pour effectuer réellement le changement de contexte
  3. Repassez en le mode IRQ et modifier le registre de lien pour être le PC sauvé du nouveau thread
  4. Retour du gestionnaire IRQ au lien IRQ registre

Je crois que je peux faire les deux premiers correctement, mais je me demandais: comment puis-je revenir en mode interruption, ou au moins modifier le SVC R13 et R15 à partir du gestionnaire d'interruption? J'utilise un processeur ARM v6; Merci beaucoup pour l'aide!

Edit: voici en gros ce que mon commutateur est:

void interrupt_yield() { 
    unsigned int old_mode; 
    __asm__("mrs %0, cpsr" : "=r" (old_mode)); 
    __asm__("msr cpsr_c, %0" : : "r" (MODE_SVC)); 

    PUSH_ALL; // Macro for push {r0-r12, lr} 
    __asm__("mov %0, sp" : "=r"(sp)); 
    manager->threads[manager->current_thread].sp = sp; 

    unsigned nt = (manager->current_thread + 1) % manager->thread_counter; 
    if (CURRENT_THREAD.status == ACTIVE) { 
     CURRENT_THREAD.status = INACTIVE; 
    } 
    manager->current_thread = nt; 

    CURRENT_THREAD.status = ACTIVE; 
    SET_SP(CURRENT_THREAD.sp); 
    POP_ALL; 

    __asm__("msr cpsr, %0" : : "r" (old_mode)); 
} 


void timer_vector() { // This is called by assembly in interrupt mode 
    armtimer_clear_interrupt(); // clear timer interrupt 
    interrupt_yield(); // Calls above function 
} 

L'objectif est de modifier le registre de lien IRQ pour revenir à la nouvelle fonction. Cependant, je ne peux pas sembler revenir en mode d'interruption pour ce faire.

1 édition de plus: Je ne change jamais réellement le registre de liaison IRQ; Je me rends compte de cela, mais je ne suis même pas revenir en mode IRQ donc c'est un problème plus tard à réparer.

+1

Pouvez-vous partager ce que vous avez fait/essayé jusqu'à présent? Cela aiderait à répondre à votre question. – PhilDulac

+0

Pourquoi auriez-vous besoin de basculer vers le gestionnaire SVC pour effectuer le changement de contexte? Il n'a pas plus de privilège. – Dric512

+0

@ Dric512 Je veux le faire en mode SVC afin de changer le pointeur de la pile en mode SVC, pas celui de l'IRQ. Je ne peux pas simplement basculer en mode SVC et me connecter au registre de liaison, car, lorsqu'une interruption se produit, je veux sauvegarder r15 du thread en cours d'exécution et passer au compteur de programme stocké. –

Répondre

1

Pour l'ARMv6, vous devez changer de mode pour obtenir les registres en banque. Votre exemple de code contient déjà de nombreux détails nécessaires.

#define MODE_IRQ 0x12 
    #define MODE_SVC 0x13 
    unsigned int mode; /* original mode */ 

    /* target data... */ 
    unsigned int lr_irq; 
    unsigned int sp_irq; 
    unsigned int spsr; 

    asm (" mrs %0, cpsr\n" /* Save mode. */ 
     " msr cpsr_c,%4 \n" /* to irq mode */ 
     " mov %1, lr\n"  /* Get lr_irq */ 
     " mov %2, sp\n"  /* Get sp_irq */ 
     " mrs %3, spsr\n" /* Get spsr_irq */ 
     " msr cpsr, %0\n" /* back to old mode */ 
     : "=&r" (mode), "=r"(lr_irq), 
      "=r"(sp_irq), "=r"(spsr) 
     : "I" (MODE_IRQ)); 

gcc attribuera les lr_irq etc à des registres généraux (non-bancarisées) et vous pouvez transférer les données entre les différents modes. L'ARMv7 avec extensions de virtualisation a une instruction pour éviter ce changement.

Vous devez savoir que l'interruption de la minuterie peut se produire dans de nombreux contextes. Il est probablement prudent de vérifier au moins le spsr depuis le mode IRQ et d'avoir un débogage (affirmer comme) qui vérifie qu'il est en mode utilisateur. Si cela ne se déclenche jamais et que vous pensez qu'un IRQ ne peut se produire qu'en mode utilisateur, le 'debug' peut être supprimé.

Une autre méthode consiste à le faire dans l'assembleur du gestionnaire IRQ et de les passer à la routine interrupt_yield() dans r0-r2 par exemple. L'ARM EABI place les paramètres dans r0-r2 afin d'interrompre les paramètres de besoin de rendement. Une fois que vous avez ces données, il ne devrait pas être nécessaire de revenir en mode IRQ. Je recommande fortement cette méthode pour le code de production. Ce qui précède est bon pour le prototypage.


connexes: Explicitly accessing banked registers on ARM

+0

Merci, cela aide beaucoup. Comment puis-je changer lr_irq et sp_irq? Je crois que j'ai besoin de faire ces choses pour être en mesure de revenir à un nouveau sujet. –

+0

Je vois ceci: /var/folders/qb/njcg85_123s4jqr_n0kkt71w0000gn/T//ccStXA95.s:39: Erreur: le processeur sélectionné ne supporte pas le mode ARM 'cpsid aif, # 18 ' –

+0

Y at-il moyen de contourner le problème? @ artless-noise –