简体   繁体   中英

Inline C assembler macros for an AVR RTOS

I am trying to write a preemptive scheduler for AVR and therefore I need some assembler code ... but I have no experience with assembler. However, I wrote all the assembler code I think I need in some C macros. At compilation I get some errors related to assembler (constant value required and garbage at and of line), which makes me think that something is not correct in my macros ...

The following macro, load_SP(some_unsigned_char, some_unsigned_char), sets the stack pointer to a known memory location ... I am keeping the location in the global struct aux_SP;

Something similar is with load_PC(...) which is loading on the stack, a program counter: "func_pointer" which is actually, as the name suggest, a pointer to a function. I assume here that the program counter as well as the function pointer are represented on 2 bytes (because the flash is small enough)

For this I am using processor register R16. In order to leave this register untouched, I am saving its value first with the macro "save_R16(tempR)" and the restoring its value with the macro "load_R16(tempR)" where "tempR" as can be seen is a global C variable.

This is simply written in a header file. This along with another two macros (not written here because of their size) "pushRegs()" and "popRegs()" which are basically pushing and then popping all processors registers is ALL my assembler code ...

What should I do to correct my 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"   \
                );

Your main source of reference for this should be http://www.nongnu.org/avr-libc/user-manual/inline_asm.html

Avoid using "unsigned char" - use "uint8_t", as it is shorter and more explicit. Avoid macros - use static inline functions instead whenever possible. Don't invent your own struct for auxSP, especially not using a different endian ordering than the target normally uses - just use uint16_t. Don't write things in assembly when you can write them in C. And don't split up asm statements that need to be combined together (such as preserving R16 in one statement, then using it in a second statement).

Where does that leave us?

It's a long time since I have done much AVR programming, but this might get you started:

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();
}

You will probably also want to make sure you disable interrupts before using any of these.

Here is an example of C code I did on an AVR platform. It is not macros but functions because it was more adapted for me.

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;
}

Also try without your \\n\\t this may be the "garbage at and of line"

The problem of constant value required may come from here :

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

For this I am less sure but STS (and other) requires an address that may need to be fixed at compile time. So depending on how you use the macro it may not compile. If tempR is not fixed, you may use functions instead of macro.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM