cassemblymicrocontrollermsp430

MSP430F5xxx RTOS restore context assembler not clear


I'm trying to port a FunkOS RTOS from MSP430F2xxx to MSP430F5529. I'm using CCS 10.4 with TI v20.2.5 LTS compiler. I ported most of the code but I have problem with the RTOS taking over the control. After I initialize all the tasks I call Task_StartTasks function. My problem is with the assembler part of this function.

void Task_StartTasks(void)
{   
    Task_SetScheduler(TRUE);
    Task_Switch();
    // Restore the context...
    asm(" mov.w  &pstCurrentTask, r12");
    asm(" mov.w  @r12, r1");
    asm(" pop  r15");
    asm(" pop  r14");
    asm(" pop  r13");
    asm(" pop  r12");
    asm(" pop  r11");
    asm(" pop  r10");
    asm(" pop  r9");
    asm(" pop  r8");
    asm(" pop  r7");
    asm(" pop  r6");
    asm(" pop  r5");
    asm(" pop  r4");
    asm(" bic.w  #0x00F0, 0(SP)");
    asm(" reti");
}

pstCurrentTask is a global pointer to following structure:

typedef struct Task_Struct
{
/*! This is the basic task control block in the RTOS.  It contains parameters
    and state information required for a task, including stack, priority, 
    timeouts, entry funcitons, and task pending semaphore.
*/  
    //--[Task Control Block Entries]-----------------------------------------
    WORD *pwTopStack;       //!< Pointer to current stack top 
    WORD *pwStack;          //!< Stack pointer, defined by the task.    
    USHORT usStackSize;     //!< Size of the stack in MAU
    //--[Task Definitions]---------------------------------------------------
    BYTE *pacName;          //!< Pointer to the name of the task (ASCII)
    TASK_FUNC pfTaskFunc;   //!< Pointer to the entry function
    UCHAR ucPriority;       //!< Task priority
    TASK_STATE eState;      //!< Current task state
    USHORT usTimeLeft;      //!< Ticks remaining in blocked/sleep state 
    BOOL bTimeout;          //!< Indicates that an IO operation timed out
    struct Task_Struct *pstNext;        //!< Pointer to the next task (handled by scheduler)
} TASK_STRUCT;

Task_SetScheduler and Task_Switch make sure that pstCurrentTask is pointing to the correct task structure. As far as I understand this:

    asm(" mov.w  &pstCurrentTask, r12");
    asm(" mov.w  @r12, r1");

Moves the value of pstCurrentTask (in this case this is just an address to the structure?) to the R1 which for MSP430 is stack pointer (Why?). Then all registers are popped and the magic happens here.

I don't understand what is going on here:

asm(" bic.w  #0x00F0, 0(SP)");

It would be great if someone could explaing the assembler here.


Solution

  • Don't miss the @. The stack pointer is (re)set to pstCurrentTask->pwTopStack (since it's the first field in the structure, dereferencing the pointer to the structure will do the trick without any extra offset needed), presumably the pointer to the original stack was stored here after the registers were pushed and is now put back into place.

    Then the registers are popped. At the very end reti causes two more registers to be popped: the status register and the program counter (instruction pointer), the latter resulting in a jump/return to the stored value.

    But just before that happens, the bic.w clears some bits in this value on the stack, namely it turns off low-power-mode by clearing CPUOFF, OSCOFF, SCG0, SCG1. It operates on the value that is currently on the top of the stack (dereferencing SP with offset zero) which is the soon-to-be status register. This means that even if the stored status register had those bits set, indicating low power mode, they won't be set anymore when it is popped again as part of reti.

    If you were to translate that line to C, this is what it would look like:

    SP[0] &= ~0x00f0;
    // 0x00f0 comes from (CPUOFF | OSCOFF | SCG0 | SCG1) 
    

    Note that the 0x00f0 isn't a peripheral or anything like that. It's just a bitmask used on the status register. In the manual, check chapter 2.3.1 on page 40 (entering and exiting lower power mode). A very similar command is used there, but with a sum of named constants instead of a numerical value, in our case that would be CPUOFF+OSCOFF+SCG0+SCG1 instead of 0x00f0. If you look at chapter 3.2.3 on page 46 (status register) you can see why - those 4 flags are at bits 4-7 of the status register, i.e. their values are 0x0010, 0x0020, 0x0040 and 0x0080 respectively, which when added or ORed together gives you 0x00f0.