portSTACK - 20-bits

jwestmoreland wrote on Friday, August 03, 2007:

Richard,

Yep - I think …but may be right here.

I’ll revisit this when I get time - or when trying to help others like Penn gives me those ‘oh yeah’ moments thus causing me to check things out…

Regards,
John W.

jwestmoreland wrote on Friday, August 03, 2007:

Richard,

I think I may have mentioned this before - I do get 166 ticks on the clock before the <kablam!> happens - so that indicates a lot is working OK.

Regards,
John

plinder13 wrote on Wednesday, August 08, 2007:

I’m not sure where you are at now, but I have a few thoughts - which may just be re-iterating what has been said so far.

Since it is possible for a context switch to occur while the PC is in 20-bit space, the portSAVE_CONTEXT and portRESTORE_CONTEXT macros probably have to handle by default the 20-bit case.  This means that all of the push and pops need to be the .A versions so that the 20-bit registers are pushed onto the stack. Since this requires 32-bits per register, this requires twice as much stack space relative to the 16-bit version.  This means that the CCE compiler needs to use the mspx core as the msp core does not support the extended instruction set.

In the portYIELD() function, the stack will have to be manipulated to combine the upper 4 bits of the PC with the SR as well as reversing the word order.  This will be difficult as it seems that the PC19:16 needs to be shifted to the left and have the SR added to it, without affecting the SR in the process (if you figure this out before me, please post the code), followed by a swap of this location and the location on the stack containing PC15:0. It may end up being easier to push the SR onto the stack, and use portRESTORE_CONTEXT with the RETA instruction (however, this would still require some manipulation for the case of the the portTICK_ISR(), since the stack coming in due to an interrupt is not correct for a RETA instruction). 

All of this assumes that portYIELD() is called with a CALLA instruction (not a CALL instruction). This also means that you need to make sure the compiler is using the CALLA instruction when calling portYIELD() so that the stack is as expected.  I think the Large Memory Model option in the CCE compiler forces all functions to use CALLA and RETA.

Hope this at least spurs some thoughts. I probably also will be working on a port for CCE as I just had to shift from the F449 to the FG4618 due to a need for RAM.

Penn

jwestmoreland wrote on Wednesday, August 08, 2007:

Penn,

I might suggest just using the 'FG4618 in 430 mode for now vs. 430X mode.  You will have access to all peripherals save FLASH
above 0xFFFF.

I’m busy working on a C8051 IAR port at the moment so I won’t be back on this for a while.

I do have a stable 430-mode port running on the 'FG4618 (IAR).

Regards,
John

plinder13 wrote on Wednesday, August 08, 2007:

I have a port attempted - figured out the assembly code I need.  However, when I start the schedular, and step through the taskRESTORE_CONTEXT() macro, it resets when a popx.w r15 instruction executes.  The SP seems to be pointing to a valid, even, RAM location.  Any ideas?

Penn

jwestmoreland wrote on Wednesday, August 08, 2007:

You’re not running in ‘true’ 430X mode (I just made that ‘definition’ up - so it’s somewhat tongue-in-cheek) - but the pop
instruction should be:

popx.a   r15

If 430 & 430X modes get mixed - you get bits ‘lurking’ in the upper 4 positions - and end up with a situation that doesn’t
work - which sounds like what is happening.

You may have to some assembler to get this to work - look at the Rowley MSP430 port for some starting ideas or look at the
MSP430 ports on my site.

I don’t think you will have 100% control on when the compiler decides to use CALL vs. CALLA - which could lead to the above -
which is why you may need to convert some routines to assembler.

HTH,
John W.

jwestmoreland wrote on Wednesday, August 08, 2007:

Maybe a way to ‘fix’ this is to issue CLR instructions at the appropriate time - would be useful if TI had supplied a
‘CLEAR MULTIPLE’ command - i.e. - CLRM.A command.

This in fact may be a way to fix the problem. 

The 20 vs. 16 bit issue is particularly ugly if the PC gets corrupted in 16 vs. 20-bit mode - meaning the upper 4 bits are set when the PC is restored to a ‘near’ address making the PC happily jump into weedland - somewhere > 0x10000 (running in 430 mode).
20-bit push occurring when a 16-bit should’ve occurred - or 20-bit pop occuring where 16-bit should’ve occurred - or vice-versa.

Regards,
John W.

plinder13 wrote on Wednesday, August 08, 2007:

From what I understand, it is ok to “mix” popx.w and popx.a.  I believe that when popx.w Rn is used, it automatically clears bits 19:16 of Rn while placing the word on the stack into 15:0, and decrements the stack pointer by 2. I am stepping through the code and the stack pointer is in RAM - the upper 4 bits of the SP are clear - right before the “popx.w R15” sends my debugger into the weeds.  Issuing a popx.w R15 command shouldn’t affect either the SP or the PC, so I am confused why stepping into this instruction is causing my debugger to go into the weeds.  I will change it to a popx.a instruction just for a test to see if it improves my situation (knowing I will have to change the corresponding pushx.w instruction to pushx.a as well).  I suspect that there is something else going on that I am missing and the debugger is not showing me good information.

Penn

plinder13 wrote on Wednesday, August 08, 2007:

BTW, I think the CCE compiler uses strictly CALLA and RETA when using the mspx core.

jwestmoreland wrote on Thursday, August 09, 2007:

Wouldn’t count on it.  If you’re operating in the lower 64 - then CALL’s work fine.  I would think a ‘short’ call would only
use CALL’s.  I wouldn’t trust any compiler until I looked at 100% of the assembly in this case.

plinder13 wrote on Thursday, August 09, 2007:

CCE says that to mix assembler and C, always use CALLA and RETA in the assembler.  This implies that all the functions use this, as otherwise a C function could be called from assembler with a CALLA that returned with a RET.

I figured out my bug - I was writing to an undefined are of the SFRs - pointer issue.  The debugger was masking it as it is slow and awkward with the MSP430X.

Here are some snippets of my port.  Note that the portSTACK_TYPE should be portSTACK_SHORT as in other MSP430 ports, even though some function pointers are 20-bits.  This works because all stack pointers are in RAM and are 16-bit.

#define portSAVE_CONTEXT()                           
    asm (    " pushm.a    #12,r15");                   
    asm    (    " movx.w    usCriticalNesting,r15");   
    asm (    " pushx.w    r15");                       
    asm (   " movx.w    pxCurrentTCB,r12");           
    asm (   " movx.w    r1,0(r12)");

#define portRESTORE_CONTEXT()                           
    asm (    " movx.w    pxCurrentTCB, r12");           
    asm    (    " movx.w    0(r12),r1");                   
    asm    (    " popx.w    r15");                           
    asm    (    " movx.w    r15,usCriticalNesting");       
    asm    (    " popm.a    #12,r15");                       
    asm (    " reti"    );

void vPortYield( void )
{
    /* We want the stack of the task being saved to look exactly as if the task
    was saved during a pre-emptive RTOS tick ISR.  Before calling an ISR the
    msp430 places the status register onto the stack.  As this is a function
    call and not an ISR we have to do this manually. */
    asm ( " pushx.w     r2" );
    _DINT();
        /* Make stack look like we entered form an ISR instead of a CALLA */
    asm ( " bicx.w         #0xF000,0(r1)");
    asm ( " swpbx.w        +4(r1)");
    asm ( " rlax.w        +4(r1)");
    asm ( " rlax.w        +4(r1)");
    asm ( " rlax.w        +4(r1)");
    asm ( " rlax.w        +4(r1)");
    asm ( " addx.w         +4(r1),0(r1)");
    asm ( " movx.w         +2(r1),+4(r1)");
    asm ( " movx.w         0(r1),+2(r1)" );
    asm ( " incdx.a        r1");

The pushm.x #n,Rx instruction and corresponding popm.x instruction, while placing n registers on the stack with one instruction, place the registers on the stack in the opposite order of pushing and popping one at a time.  Therefore, the pxPortInitialiseStack function needs to account for this and reverse the R4 to R15 initialization values.

portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )
{
    /*
        Place a few bytes of known values on the bottom of the stack.
        This is just useful for debugging and can be included if required.

        *pxTopOfStack = ( portSTACK_TYPE ) 0x1111;
        pxTopOfStack–;
        *pxTopOfStack = ( portSTACK_TYPE ) 0x2222;
        pxTopOfStack–;
        *pxTopOfStack = ( portSTACK_TYPE ) 0x3333;
        pxTopOfStack–;
    */

    /* The msp430 automatically pushes the PC then SR onto the stack before
    executing an ISR.  We want the stack to look just as if this has happened
    so place a pointer to the start of the task on the stack first - followed
    by the flags we want the task to use when it starts up. */
    *pxTopOfStack = ( portSTACK_TYPE ) pxCode;
    pxTopOfStack–;
    *pxTopOfStack = ((( portSTACK_TYPE )((((unsigned long)(pxCode)) >>4) & (0x0000F000))) | portFLAGS_INT_ENABLED);
    //*pxTopOfStack = portFLAGS_INT_ENABLED;
    pxTopOfStack–;

    /* Next the general purpose registers. */
    /* When the task starts is will expect to find the function parameter in
    R15. */
    *pxTopOfStack = (portSTACK_TYPE) ((((unsigned long)pvParameters)>>16) & (0x0000000F));
    pxTopOfStack–;   
    *pxTopOfStack = ( portSTACK_TYPE ) pvParameters;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x000e;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0xeeee;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x000d;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0xdddd;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x000c;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0xcccc;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x000b;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0xbbbb;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x000a;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0xaaaa;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x0009;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x9999;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x0008;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x8888;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x0007;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x7777;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x0006;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x6666;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x0005;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x5555;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x0004;
    pxTopOfStack–;
    *pxTopOfStack = ( portSTACK_TYPE ) 0x4444;
    pxTopOfStack–;

    /* The code generated by the mspgcc compiler does not maintain separate
    stack and frame pointers. The portENTER_CRITICAL macro cannot therefore
    use the stack as per other ports.  Instead a variable is used to keep
    track of the critical section nesting.  This variable has to be stored
    as part of the task context and is initially set to zero. */
    *pxTopOfStack = ( portSTACK_TYPE ) portNO_CRITICAL_SECTION_NESTING;   

    /* Return a pointer to the top of the stack we have generated so this can
    be stored in the task control block for the task. */
    return pxTopOfStack;
}

Penn

jwestmoreland wrote on Thursday, August 09, 2007:

Penn,

Looks like you have solved this - great job!

As long as a vPortYield will always occur during a CALLA/RETA frame - this should always work.  I wasn’t 100% sure this could be relied upon since I wasn’t sure the compiler wouldn’t ‘mix’ CALL and CALLA frames.

I will code up a test and try to force calls with function pointers > 0x10000 to be yielded to make sure this works in all instances.

Thanks!
John W.

plinder13 wrote on Thursday, August 09, 2007:

I’ll try to remember to send you my port.c and portmacro.h tomorrow when I get back to work.

Penn

jwestmoreland wrote on Friday, August 10, 2007:

Penn,

OK - please do that.

I just tried this - plus I tried some ideas I had -

definitely got the system running with some tasks running - but -

I have a RX_IRQ that caused the system to crash only after a few times of it being used - so evidently there’s still some work to do.

This is good progress though.

Thanks,
John W.

plinder13 wrote on Friday, August 10, 2007:

How many times is your TICK running before it crashes (ie, is it crashing on the first tick or have you concluded it is definately related to the RX_IRQ)?

jwestmoreland wrote on Friday, August 10, 2007:

Penn,

As I mentioned - several tasks are actively running - LED’s blinking, several IRQ’s running, etc. The xTickCount counter has rolled many times before this happens.

Please check this if you would on your end with a serial ISR maybe doing something simple like copying received chars and just echoing them back out.

The same code I have runs fine in 430 mode - so the error is more than likely related to the port (430X).

Are you able to run the demo code in 430X mode - including the Serial ISR demo, with no problem?

Thanks,
John

plinder13 wrote on Friday, August 10, 2007:

I have not tried the demo code as I have my own hardware and software - a completely different application.

I have TIMERB interrupts going - they are yielding and resuming tasks.  I also have a simple serial ISR that collects data and places into a buffer - but I haven’t tried it yet as I am waiting on some other hardware.

Could you send me or post a copy of your serial ISR so I can take a quick look at it?

Also, the stack size needs to increase when using the 430X because the full 20-bits of the registers are saved on the stack (consuming 4-bytes) instead of just the lower 16-bits.  Perhaps your ISR is interrupting a task that is near the end of the stack.

Penn

jwestmoreland wrote on Thursday, February 14, 2008:

Penn, et. al,

I have the IAR (4.10D) port running on the TI MSP430(X) Experimeter Board - using the latest posted v.4.7.1 FreeRTOS ‘core’ code.
There are a few differences with my board(s) - I have the '4619 on my board(s) vs. the '4618 - and I’m using an 8MHz clk (X1)
on my board(s).

I have the LED demo running - LED’s 1, 2, and 4 - and I have the Com Test Running - 115.2KB - running out of the 9-Pin DB-9
(RS-232) port.

If anyone is interested in the port - you can send me an e-mail and/or download it from my web-site.  I will try to post it soon -
so be patient if you want to download it from my site. 

The port seems to be stable so far - I want to thank Penn for his dialogue and suggestions on this port.

Regards,
John C. Westmoreland
www.WestmorelandEngineering.com
john at westmorelandengineering dot com