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:
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 orPUSHX followed by CALLA index(SP).This erratum does not apply if the PUSH or PUSHX instruction is used inthe Register or Immediate addressing mode.This erratum applies only when SP is used as the destination register inthe 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 Potschadtkeit crashes (sets PC to 0x0F3FFE)
Alignment is not a problem, for reading 16 or 20 bit access, only word alignment is required.
Jens Potschadtkeerrata CPU33 of the document SLAZ052J, looks similar,
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,R15CALLA 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.
_____________________________________Before posting bug reports or ask for help, do at least quick scan over this article. It applies to any kind of problem reporting. On any forum. And/or look here.If you cannot discuss your problem in the public, feel free to start a private conversation: click on my name and then 'start conversation'. But please do so only if you really cannot do it in a public thread, as I usually read all threads. And I prefer to answer where others can profit from it (or contribute to it) too.
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 ?
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 PotschadtkeI 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 TenneyJens PotschadtkeI use the CCS Version: 4.1.3.00038 and use the CCS compiler. The original code is in C.
Well, the original code would be interesting.
Jeff TenneyI think this really is a CCS bug.
Jeff TenneyI'm guessing when he removes the "const" his code works just fine.
Jeff Tenneythere is no CALLX.A, so one must restructure the code
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,
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.
Jens-Michael Gross ... Jeff TenneyI 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) ...
Are you sure it won't use CALLA in small code model? (I do not have nor want to have CCS.)
Jens-Michael Gross ... Jeff TenneyI'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 :) ...
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 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_yellowThat 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.
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 TenneyFull disclosure, I don't actually use CCS either. Just IAR.
Jeff TenneyCCS should know that indexes larger than 0x7FFF are treated as negatives when used with CALLA.
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 belowfsmfunct * const functionList [] ={ func1, func2, func1, func2};//a dummy counterint counter=0;
//a dummy functionvoid func1(void){ //do something counter++;}//a dummy functionvoid 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 PotschadtkeThe compiled code for calling entries from the table is now much more complicated and twice as big, but works.
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 :)