This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

crash with indexed CALLA instruction from flash

Other Parts Discussed in Thread: CC430F6137, CC430F5137

Hello,

today I just discovered a strange behaviour of my code when I transfered a jump table from RAM to Flash on the CC430F6137 controller.

When the code wants to jump to an entry of the table with a CALLA instruction, it crashes (sets PC to 0x0F3FFE)

If the table sits in RAM then everything works as expected. When the same table is placed in FLASH, the app crashes.

This is the code with the table placed in ROM (at address 0xc272):

0x08226:   4F6F                MOV.B   @R15,R15

0x08228:   118F                SXT     R15

0x0822A:   065F                RLAM.W  #2,R15

0x0822C:>  135F C272           CALLA   0xc272(R15)

At the time of the CALLA instruction, the R15 has the content 0x10. But even with 0 the app crashes.

Coil_module = 0xc272

B372     0000     B3A4     0000     B378     0000     B37E     0000     B352     0000     B242     0000     B390     0000     B3A8     0000

The intended jump address is 0xB352, which I verified to hold the wanted function.

First I thought it is the alignment, which in this case is aligned only to 2 bytes, not to 4 bytes. But even with an alignment of 4 Bytes for the table the app is crashing.

-----------------------

Now, when I place the same table in RAM (here also with alignment 2), the code is working:

0x08228:   118F                SXT     R15

0x0822A:   065F                RLAM.W  #2,R15

0x0822C:   135F 28DE           CALLA   0x28de(R15)

Now, for security reasons and for restricted RAM space, I just want to place the jump table in ROM. Is there any way to make the call work?

Is this a hardware bug? Is this documented somewhere?

Well, the errata CPU33 of the document SLAZ052J, looks similar, but is states that this is only applicable when

Quote:

 This erratum applies only when the instruction sequence is: PUSH or
PUSHX followed by CALLA index(SP).
This erratum does not apply if the PUSH or PUSHX instruction is used in
the Register or Immediate addressing mode.
This erratum applies only when SP is used as the destination register in
the CALLA index(Rdst) instruction.

I use the CCS  Version: 4.1.3.00038  and use the CCS compiler.n The original code is in C.

Greetings and happy easter,

Jens

  • Hi Jens,

    Really good post, I say.  Seems pretty clear you have a silicon bug on your hands.  I've never seen that one documented before.

    Hey TI, somebody needs to look at this one.

    I guess I'm not too surprised that nobody has ever found it before.  It is an odd thing to have a const array of function pointers.

    The workaround should be simple even if you leave the table in flash.  Please post again if you can't seem to work around it.

    Jeff

  • Jens Potschadtke said:
    it crashes (sets PC to 0x0F3FFE)

    This looks like reading from vacant memory location 0x3fff, 0x3fff

    Alignment is not a problem, for reading 16 or 20 bit access, only word alignment is required.

    Jens Potschadtke said:
    errata CPU33 of the document SLAZ052J, looks similar,

    This erratum is due to the fact that both, push and call, are writing to the stack and modifying the SP. IF SP is then also used as index for the CALLA target, things go wrong. In your case, the index register is R15, so no problems apply.

    Actually I don't know what's wrong with your code. Initially, I though about a problem with 64k range, with the table being above 64k or such.

    However, why do you use CALLA? the 6137 doesn't have more than 64k, so using CALLA is a waste of space and clock cycles. A normal CALL will do.
    And why do you use SXT? The index is definitely always positive, so there must not be any sign to extend. And the MOV.B instruction will clear the high-byte implicitely.

    The MOV.B R15,@R15 looks a bit strange. Why do you pass the parameter by reference and not by value (if this is what happens here)? But it shouldn't have any influence. If you say that R15 contains 0x10 (hmm, the table you posted is only 0x10 bytes long, so the index is out of range. But maybe real table is longer than you posted) and the problem also appears with R15 == 0, the parameter isn't the problem.

    What if you use

    MOVA @R15,R15
    CALLA R15

    You can check R15 with a breakpoint on CALLA

    Ah, I guess I know what happened: From the 5x family users guide:

    "Indexed mode: Call a subroutine at the 20-bit address contained in the address pointed to by register (R5 + X); for example, a table with addresses starting at X. (R5 + X) points to the LSBs, (R5 + X + 2) points to the MSBs of the word address. X is within R5 + 32 K. Indirect, indirect (R5 + X)."

    In indirect mode, the register is the base address and the static value is the index. The two roles are usually exchangeable. But not in this case. It address is not fetched from 0xC272+R15, but from R15 + 0xC272, and 0xC272 is a signed value. Since you're using CALLA (20 bit addressing), the generated address is R15-0x03d8e which is (for R15==0) = 0xFC272. Which is vacant memory and returns 0xF3FFF as result.

    Sicne CALL is a 16 bit instruction, it wouldn't happen with CALL.

    It's easy to forget that the constant part of an indexed instruction is the index, not the register. And that the index is signed. And on 20 bit instructions, the signed rollover leaves the 64k range. (I must admit that it took quite some time for me to figure it out)

    Solution: either move the destination in to R15 as I suggested, by using word instead of address instructions, use CALL (which would shrink the jumptable as well as speed up and shrink your code) or calculate the index address in R15 first with word instructions and then use indirect mode CALLA @R15.

  • Very nice catch, JMG. No silicon bug after all.

    I believe the OP Jens said his compiler generated this code, so I think you found a bug in CCS.

    What makes this so strange is that it's a 20-bit instruction with only a 16-bit index!  Only CALLA and MOVA are like that.  All other 20-bit instructions use a 20-bit index for indexed-mode addressing.  It's a little bit confusing.

    So would MOVA 0xC272(R15), xyz  still read from the wrong address like CALLA ?

    Jeff

  • Actually, CALLA, MOVA and RETA are the only instruciton dealing with 20 bit address range (except for plain register/register instructions) but not having the Prefix word that stores the upper 4 bits of 20 bit arguments.

    For MOVA, you can use MOVX.A and get a 20 bit argument. At the cost of an additional cycle and an additional instruction word. (well, better than having 32 bit operands)

    I guess, nobody thought that a CALLX.A instruction would be needed.

    However, the OP didn't say anything about compiler, and the code looks like hand-crafted assembly. I don't think the compiler will generate a jumptable like this. And the linker just links what is given to it for linking.

    There are several other ways to do it. Use MOVX.A (not MOVA) to load the target address into R15, then CALLA R15. Or call the jumptable itself directly. The table is preceded by an  ADD R15, PC,  and the jumptable contains branch instructions, not just addresses. And more.
    I agree, that the probelmatic way is the most obvious. But unfortunately a non-working one if the table is above 0x8000.

  • Jens Potschadtke said:
    I use the CCS  Version: 4.1.3.00038  and use the CCS compiler. The original code is in C.

    I think this really is a CCS bug.

    I'm guessing the OP has a const array of function pointers.  I'm guessing when he removes the "const" his code works just fine.

    Also, because RETA is really just MOVA @SP+, PC, it really is just the two instructions CALLA and MOVA that are limited this way.  They are 20-bit instructions, but when using indexed addressing they use only a 16-bit index.  They are the oddballs.  As JMG pointed out, MOVX.A provides full functionality, but there is no CALLX.A, so one must restructure the code.

    Jeff

  • Jeff Tenney said:
    I use the CCS  Version: 4.1.3.00038  and use the CCS compiler. The original code is in C.

    [/quote]Well, my eyes aren't getting better by staring at other people's code :)
    I didn't see this even when I was specifically lookign for it.

    Well, the original code would be interesting.

    Jeff Tenney said:
    I think this really is a CCS bug.

    Yes, it if was C code then indeed this is a bug in large code model (small code model won't use CALLA, and for CALL it works)

    Jeff Tenney said:
    I'm guessing when he removes the "const" his code works just fine.

    Definitely, as the array then is copied to ram and I don't know any MSP with RAM above 0x8000 :)

    Jeff Tenney said:
    there is no CALLX.A, so one must restructure the code

    Yep. A possible restructuring is to separate the function call from the table by first fetching the destination into a variable and then doung the call using the already fetched variable. However, compile roptimization might undo this workaroun (sometiems, compilers can be too smart)

  • Hello,

    it`s me again, back from a short vacation around easter holidays.

    After all those explanations in this thread I think it is really a CCS bug.

    The C-compiler and linker should be aware about the limitation of using an indirect offset bigger than 0x8000 in a CALLA instruction results in a negative sign extended address value. So either it should prepare the index register accordingly to offset this effect or use any of the other proposed workarounds.

    Just removing the "const" from the table places it in RAM at lower addresses, so the error does not appear.

    When I want the table in ROM, then I will use an additional step and hope that the compiler does not optimize the code too much. I didn't have the time yet to try this out, since at the same time I was chasing down a rare DMA error, which corrupted exactly this jump table in RAM. But that is an other story and I seem to have found my error there by now.

    I will try to produce a small sample code snippet. The current code is part of a larger state machine.

    Greetings,

    Jens

  • Hi Jens,

    As JMG mentioned, you can select the small code model in CCS (if they still support it).

    That should make a nice work-around for your problem until they fix the bug.  In the small code model, CCS uses CALL instead of CALLA.

    Jeff

  • Jens-Michael Gross said:
    ...
    I think this really is a CCS bug.

    Yes, it if was C code then indeed this is a bug in large code model (small code model won't use CALLA, and for CALL it works) ... [/quote]

    Are you sure it won't use CALLA in small code model? (I do not have nor want to have CCS.)

    Jens-Michael Gross said:
    ...
    I'm guessing when he removes the "const" his code works just fine.

    Definitely, as the array then is copied to ram and I don't know any MSP with RAM above 0x8000 :) ... [/quote]

    The OP showed code at 0x82xx and table at 0xC2xx (does not work). He also showed code at 0x8200 and table at 0x28xx (works). CC430F6137 has Flash from 0x8000 to 0xFFFF and RAM from 0x1A00 to 0x2BFF.

    Based on what the OP said, I must conclude that there is a hardware bug. I do not think the c-compiler caused the crash in this case.

  • Jeff Tenney said:

    Hi Jens,

    As JMG mentioned, you can select the small code model in CCS (if they still support it).

    That should make a nice work-around for your problem until they fix the bug.  In the small code model, CCS uses CALL instead of CALLA.

    Jeff

    That chip has only 32 KB Flash below 0x10000. Can he use large code model in CCS?

  • old_cow_yellow said:
    That chip has only 32 KB Flash below 0x10000. Can he use large code model in CCS?

    Yes, I think so.  Somewhere I heard that CCS defaults to the large code model for any MCU with CPUX.  It's a waste of code space, stack space, etc, on MCUs with flash only up to 0xFFFF.

    Full disclosure, I don't actually use CCS either.  Just IAR.

    As to the bug, I think the OP's most-recent post summarized the problem pretty well.  CCS should know that indexes larger than 0x7FFF are treated as negatives when used with CALLA.  The effective address ends up at 0xFC272 instead of 0x0C272 as intended.

    Jeff

  • old_cow_yellow said:
    That chip has only 32 KB Flash below 0x10000. Can he use large code model in CCS?

    The CPU has the instructions, so the compiler generates it. Actually, the compiler doesn't know how much flash the MSP has. It may or may not have flash above 64k., This is the linkers job to know.

    Problem is that for large and small code model, you'll have to provide different versions of the standard library, different startup code etc. THis might be the reason if you cannot switch over to small code model for these CPUs. Less variants of the libraries needed.

    Jeff Tenney said:
    Full disclosure, I don't actually use CCS either.  Just IAR.

    I use neither. :)

    Jeff Tenney said:
    CCS should know that indexes larger than 0x7FFF are treated as negatives when used with CALLA.

    Yep, so it shouldn't use indexed CALLA at all, because only the linker will know what the final index will be. The compiler cannot make any assumption.

  • Hello,

    below is a condensed code snippet to reproduce the described error:

    If one removes the "const" in the definition of functionList, then the table is placed in RAM and the error goes away.


    typedef void (fsmfunct) (void);         /*  Address of FSM function          */

    void func1(void);
    void func2(void);


    //the list of functions
    //if placed in ROM, the code crashes below
    fsmfunct * const functionList [] =
    {
        func1,
        func2,
        func1,
        func2
    };
    //a dummy counter
    int counter=0;


    //a dummy function
    void func1(void)
    {
      //do something
      counter++;
    }

    //a dummy function
    void func2(void)
    {
      //do something
      counter += 2;
    }


    void main(void)
    {
      int i;
     
      for (i=0;i<3;i++)
      {
        //call the function from the table
        (*functionList [i]) (); //crashes when table is in ROM
      }
     
    }


  • I found the workaround, which works for me: See below.

    The compiled code for calling entries from the table is now much more complicated and twice as big, but works.

    void main(void)
    {
      int i;
      fsmfunct *targetFunction;
      for (i=0;i<3;i++)
      {
        targetFunction = functionList [i]; //fetches address to R15
        (*targetFunction)(); //compiles to CALLA R15
       
      }
     
    }

  • Jens Potschadtke said:
    The compiled code for calling entries from the table is now much more complicated and twice as big, but works.

    Can you post the assembly code?

    Normally, it shouldn't differ from the original one much.

    Just one additional MOVX.A (just like I proposed). Unless, of course, the handling of function pointers in local variables is handled ineffective, using 2 16bit registers for the fetch (with MOV instructions) instead of a 20bit register, then combine them before the CALLA. But even if so, it's not a bug anymore :)

  • Hi Jens,

    In addition to posting the assembly code, can you also tell us which data and code models you are using?  There are typically three choices for data model (small, medium, large) and two choices for code model (small, large).  The choices are buried in the IDE somewhere.

    Can you also tell us if your original code works when you switch to the small code model?

    Jeff

  • Ok, below is the assembly code of the code inside the for loop  of my workaround.

    Apparently it is so complicated, since it uses a variable on the stack for holding the address.

             C$DW$L$main$2$B, C$L1:
    0x0C056:   412F                MOV.W   @SP,R15
    0x0C058:   065F                RLAM.W  #2,R15
    0x0C05A:   1800 4FD1 C0CC 0002 MOVX.A  0x0c0cc(R15),0x00002(SP)
    0x0C062:   013F 0002           MOVA    0x0002(SP),R15
    0x0C066:   134F                CALLA   R15

    When I use the same workaround on my real code and not the example, then the code is more efficient, since it just uses a register for holding the address. So there is not much overhead there.

    These are the settings of my sample project, where I verified that the error occurs, when I don't use the workaround. So, it seems not to be the large data model but just the MSPX CPU.

    This is what the all options field contains:

    --silicon_version=mspx -g --include_path="D:/Programme/Texas Instruments/ccsv4/msp430/include" --include_path="D:/Programme/Texas Instruments/ccsv4/tools/compiler/MSP430 Code Generation Tools 3.2.3/include" --diag_warning=225 --printf_support=minimal

  • Jens Potschadtke said:
    0x0C05A:   1800 4FD1 C0CC 0002 MOVX.A  0x0c0cc(R15),0x00002(SP)
    0x0C062:   013F 0002           MOVA    0x0002(SP),R15

    It's as I thought, the compiler doesn't use a 20bit register for the pointer, it reserves a 32 bit area on stack, first moves the data from the table to stack and then from stack to R15.

    MOVX.A 0x0c0cc(R15), R15 would have been sufficient. (6 bytes, 4 bytes stack and 6 cycles less)

    Or CALLA 0x0002(SP) and no use of R15 at all. (4 bytes and 4 cycles less)

    I don't know whether CCS allows specification of 'register' variables? Maybe it then generates a more efficient code?

    Currently, it looks as if the 20 bit support of the C compiler still requires some improvements. (same for IAR and MSPGCC).

  • Jens Potschadtke said:
    below is a condensed code snippet to reproduce the described error

    Using CCS 5.1 I created a new project for a CC430F5137 and used compiler version 4.0.2 (the latest). The program ran successfully.

    I changed to use compiler version 3.2.3 (the version you appear to be using). The program then crashed when attempted to single step the (*functionList [i]) ();

    i.e. the crash appears to be fixed in a later version of the compiler. 

  • OK,

    it's good to hear that the new compiler has no longer this bug.

    So, I have a reason to switch to the new CCS.

    Thanks.

**Attention** This is a public forum