2017-08-19 3 views
0

J'essaye d'écrire un programmateur préemptif pour AVR et donc j'ai besoin d'un peu de code assembleur ... mais je n'ai aucune expérience avec l'assembleur. Cependant, j'ai écrit tout le code assembleur dont je pense avoir besoin dans certaines macros C. A la compilation j'obtiens quelques erreurs liées à l'assembleur (valeur constante requise et garbage at et of line), ce qui me fait penser que quelque chose n'est pas correct dans mes macros ...Macros en ligne assembleur C pour un AVR RTOS

La macro suivante, load_SP (some_unsigned_char, some_unsigned_char) , positionne le pointeur de la pile sur un emplacement de mémoire connu ... Je garde l'emplacement dans la structure globale aux aux; Quelque chose de similaire est avec load_PC (...) qui charge sur la pile, un compteur de programme: "func_pointer" qui est en fait, comme son nom l'indique, un pointeur vers une fonction. Je suppose ici que le compteur de programme ainsi que le pointeur de fonction sont représentés sur 2 octets (car le flash est assez petit)

Pour cela j'utilise le registre de processeur R16. Afin de ne pas toucher à ce registre, j'enregistre d'abord sa valeur avec la macro "save_R16 (tempR)" et la restauration de sa valeur avec la macro "load_R16 (tempR)" où "tempR" peut être vu comme une variable C globale .

Ceci est simplement écrit dans un fichier d'en-tête. Ceci avec deux autres macros (pas écrites ici en raison de leur taille) "pushRegs()" et "popRegs()" qui poussent et puis éclatent tous les registres des processeurs est TOUS mon code assembleur ...

Je fais pour corriger mes macros?

// used to store the current StackPointer when creating a new task until it is restored at the 
// end of createNewTask function. 
struct auxSP 
{ 
    unsigned char auxSPH; 
    unsigned char auxSPL; 
}; 

struct auxSP cSP = {0,0}; 

// used to restore processor register when using load_SP or load_PC macros to perform 
// a Stack Pointer or Program Counter load. 
unsigned char tempReg = 0; 

//////////////////////////////////////////////////////////////////////////////////////////// 
//////////////////////////// assembler macros begin //////////////////////////////////////// 
//////////////////////////////////////////////////////////////////////////////////////////// 

// save processor register R16 
#define save_R16(tempR)         \ 
    asm volatile(          \ 
      "STS tempR, R16      \n\t" \ 
       ); 

// load processor register R16 
#define load_R16(tempR)         \ 
     asm volatile(         \ 
        "LDS R16, tempR    \n\t" \ 
        ); 

// load the Stack Pointer.    Warning: Alters the processor registers 
#define load_SP(new_SP_H, new_SP_L)      \ 
    asm volatile(          \ 
       "LDI R16, new_SP_H    \n\t" \ 
       "OUT SPH, R16     \n\t" \ 
       "LDI R16, new_SP_L    \n\t" \ 
       "OUT SPL, R16     \n\t" \ 
       ); 

// load the Program Counter on stack. Warning: Alters the processor registers 
#define load_PC(func_pointer)       \ 
    asm volatile(          \ 
       "LDS r16, LOW(func_pointer)  \n\t" \ 
       "PUSH r16      \n\t" \ 
       "LDS r16, HIGH(func_pointer) \n\t" \ 
       "PUSH r16      \n\t" \ 
       ); 
+0

Lisez dans la documentation de gcc comment utiliser les paramètres C et variobles dans l'assemblage en ligne. –

+0

Pour un changement de thread réussi, vous devez restaurer ** tous ** les registres à l'état dans lequel ils se trouvaient lorsque vous avez interrompu le thread. Cela signifie que votre routine d'interruption de minuterie doit non seulement enregistrer tous les registres qu'elle voit, mais inspecter la pile pour récupérer les valeurs qui y sont poussées par la logique matérielle d'interruption (la bonne valeur pour le PC est là). Le prologue ISR généré par le compilateur pousse également certaines valeurs sur la pile. L'ordre dans lequel les registres sont restaurés est également d'une importance cruciale. –

+0

@MichaelRoy Comme mentionné, il y a les macros pushRegs() qui poussent tous les registres de la pile et popRegs() qui font apparaître tous les registres de la pile. Ces macros sont utilisées dans d'autres fonctions ... –

Répondre

-1

Voici un exemple de code C fait sur une plate-forme AVR. Ce n'est pas des macros mais des fonctions car c'était plus adapté pour moi.

void call_xxx(uint32_t address, uint16_t data) 
{ 
    asm volatile("push r15"); //has to be saved 
    asm volatile("ldi r18, 0x01"); //r15 will be copied to SPMCSR 
    asm volatile("mov r15, r18"); //copy r18 to r15 (cannot be done directly) 
    asm volatile("movw r0, r20"); //r1:r0 <= r21:r20 //should conatain "data" parameter 
    asm volatile("movw r30, r22"); //31:r30<=r23:r22 // should contain "address" parameter ... 
    asm volatile("sts 0x5b, r24"); //RAMPZ 
    asm volatile("rcall .+0"); //push PC on top of stack and never pop it 
    asm volatile("jmp 0x3ecb7"); //secret function 
    asm volatile("eor r1, r1"); //null r1 
    asm volatile("pop r15"); //restore value 

    return; 
} 

Essayez aussi sans votre \n\t cela peut être la « poubelle à et de la ligne »

Le problème de la valeur constante requise peut venir d'ici:

#define save_R16(tempR)         \ 
    asm volatile(          \ 
      "STS tempR, R16      \n\t" \ 
       ); 

Pour cela, je suis moins Bien sûr, mais STS (et autre) nécessite une adresse qui peut avoir besoin d'être corrigée au moment de la compilation. Donc, selon la façon dont vous utilisez la macro, il ne peut pas compiler. Si tempR n'est pas corrigé, vous pouvez utiliser des fonctions au lieu de macro.

+0

Si vous allez faire cela, il suffit d'écrire toute votre fonction dans ASM. Cela va casser si/quand il insiste sur quelque chose. Si vous avez besoin d'une regre scratch, vous devriez simplement demander au compilateur pour celui avec une contrainte de sortie factice. (Voir https://stackoverflow.com/tags/inline-assembly/info pour plus d'informations sur l'écriture d'inline asm qui permet au compilateur d'optimiser autant que possible sans gaspiller d'instructions.) –

1

Votre principale source de référence pour ce qui devrait être http://www.nongnu.org/avr-libc/user-manual/inline_asm.html

Évitez d'utiliser « unsigned char » - utiliser « uint8_t », car il est plus court et plus explicite. Évitez les macros - utilisez plutôt des fonctions statiques en ligne lorsque cela est possible. N'inventez pas votre propre structure pour auxSP, en particulier si vous n'utilisez pas un ordre endian différent de celui normalement utilisé par la cible. Utilisez simplement uint16_t. N'écrivez pas les choses dans l'assemblage quand vous pouvez les écrire en C. Et ne divisez pas les instructions asm qui doivent être combinées ensemble (comme conserver R16 dans une instruction, puis l'utiliser dans une deuxième instruction).

Où cela nous mène-t-il?

Il y a longtemps que je l'ai fait beaucoup de programmation AVR, mais cela pourrait vous aider à démarrer:

static inline uint16_t read_SP(void) { 
    uint16_t sp; 
    asm volatile(
    "in %A[sp], __SP_L__ \n\t" 
    "in %B[sp], __SP_H__ \n\t" 
    : [sp] "=r" (sp) ::); 
    return sp; 
} 

static inline void write_SP(uint16_t sp) { 
    asm volatile(
    "out __SP_L__, %A[sp] \n\t" 
    "out __SP_H__, %B[sp] \n\t" 
    :: [sp] "r" (sp) :); 
} 

typedef void (*FVoid)(void); 
static inline void load_PC(FVoid f) __attribute__((noreturn)); 
static inline void load_PC(FVoid f) { 
    asm volatile(
    "ijmp" 
    :: "z" (f)); 
    __builtin_unreachable(); 
} 

Vous voudrez probablement aussi pour vous assurer de désactiver les interruptions avant d'utiliser l'un de ces.