This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

printf function overwritting RAM

Hi All, I've been lurking on this board for about 12 months now, but this is my first post...

I have traced a memory corruption problem to a single assembler statement. The statement is the first statement in the Keil printf() function. The invocation of the printf() function is this: printf("\n");

From my limited knowledge of the XC161's segmented addressing, I can't figure out why the Keil C compiler is generating the assembler that it has. Please refer to this image during the following description: 68.178.219.18/.../printf_segment_problem.png

From what I can tell the assembler statement that was just executed: MOV [-R0],R12 is moving the contents of register R12 (0x0BF2) to the absolute address 0x80BFE.

It is using DPP2 to calculate that absolute address as follows (correct me if I'm wrong):
DPP2 = 0x20 = 100000
+ 0x8BFE & 0x3FFF = 0x0BFE = 00101111111110b
= 100000b 00101111111110b
= 10000000101111111110b
= 0x80BFE

My hardware has 512K of RAM memory located at address 0x80000.
NDATA is (I presume) indexed using DPP2 which puts the 16K of near data at 0x80000.

After compiling the code I can see in the *.M66 file that:
- NDATA is located at 0x80000..0x8004E
- HDATA is located at 0x80050 through to 0x87A6F.

I have an 8192 byte array declared, it is located by the compiler/linker at: 0x80050..0x8204F

My question is this:
Why does the compiler produce code in the printf() function that writes to memory address 0x80BFE which is in the middle of my array.
Or if there is no answer to the "why" question, then what can I do to stop this memory overwritting from occuring.

I guess it probably has something to do with the memory model and near data definitions. For what it's worth I have selected the HLarge memory model and 16K near RAM and 16K near ROM.

There is probably a lot more info I can provide. If you can help and need more info just tell me what info you need and I'll get it.

Regards (and thanks in advance, this has had me stumped for 2 days now).
Paul

  • Look through the map file to see where the user stack is located. If the memory location 0x80BFE is not part of the user stack, then something corrupted the user stack pointer R0. Or maybe you just didn't allocate enough memory for the user stack.

    Regards,
    - mike

  • Hi Mike,

    Thanks for the reply. The first thing I did was to suspect a stack overflow. I increased the stack sizes in the startup file to:
    System stack: 0x0800
    User Stack: 0x0400
    User stack reg bank 1: 0x400
    User stack reg bank 2: 0x400

    According to the *.M66 file, they are located here:

    START     STOP      LENGTH    TYPE  RTYP  ALIGN  TGR  GRP  COMB  CLASS   SECTION NAME
    =====================================================================================
    00C000H   00C7FFH   000800H   DATA  REL   WORD   ---    2  PUBL  SDATA   ?C_SYSSTACK
    00C800H   00CBFFH   000400H   DATA  REL   WORD   ---    1  PUBL  SDATA   ?C_USERSTACK
    00CC00H   00CFFFH   000400H   DATA  REL   WORD   ---    1  PUBL  SDATA   ?C_USERSTACK1
    00F600H   00F9FFH   000400H   DATA  REL   WORD   ---    1  PUBL  SDATA   ?C_USERSTACK2
    00FC00H   00FC1FH   000020H   DATA  ---   BYTE   ---  ---  ---   *REG*   ?C_MAINREGISTERS
    080000H   08001DH   00001EH   DATA  REL   WORD   ---    1  PUBL  NDATA0  ?ND0?TIM
    ...
    

    Prior to executing the Keil startup code:
    R0=0x0001
    DPP0=0
    DPP1=1
    DPP2=2
    DPP3=3
    i.e. 16 bit addresses will be the same as their absolute addresses.

    After the Keil startup code has been executed and before even the first statement in main() has been exceuted:
    R0=0x8C00 (which does not appear to be correct - I think it should be 0xC800. The fact that the two addresses are similar 8C v's C8 is just a distracting coincidence)
    DPP0=0
    DPP1=0x0301 (segment at 0xC04000)
    DPP2=0x0020 (segment at 0x080000)
    DPP3=0x0003 (segment at 0x00C000)
    That tells me that the user stack is located at 0x80C00, which is right in the middle of my array.

    DPP3 is 0x03 which points to the correct segment for the user stack as shown in the *.M66 file. So if R0 was set to 0xCC00, then that would make more sense than having it set to 0x8C00. (But even then, I think it should be 0xC800, not 0xCC00)

    I don't know where to go from here, what am I missing?

    Regards
    Paul

  • Ok, after much searching, I think I can see the cause of the problem. I just don't know why...

    I modified the following lines in the Start_V2.A66 file from this:

    ?C_USERSTACK    SECTION DATA PUBLIC 'NDATA'
    ...
    ?C_USERSTACK1   SECTION DATA PUBLIC 'NDATA'
    ...
    ?C_USERSTACK2   SECTION DATA PUBLIC 'NDATA'
    ...
    ?C_SYSSTACK     SECTION DATA PUBLIC 'IDATA'
    
    to this:
    ?C_USERSTACK    SECTION DATA PUBLIC 'SDATA'
    ...
    ?C_USERSTACK1   SECTION DATA PUBLIC 'SDATA'
    ...
    ?C_USERSTACK2   SECTION DATA PUBLIC 'SDATA'
    ...
    ?C_SYSSTACK     SECTION DATA PUBLIC 'SDATA'
    

    Without the modification, the stacks were being placed in my external RAM. I want the stacks to be placed at 0xC000..0xCFFF.

    So the question boils down to, what is the correct way to force the stacks to be in DSRAM at 0xC000?

    Regards
    Paul

  • what is the correct way to force the stacks to be in DSRAM at 0xC000?

    I can't be 100% sure, but it looks like you just found the correct way of putting the stacks where you want them. The original version of Start_V2.A66 puts the user stacks into the NDATA class. Clearly you don't want them there. So you just put them into a different class. SDATA should do it, because we know that DPP3 is properly initialized for accessing SDATA. For a description of classes that the C166 compiler uses, see here:

    http://www.keil.com/support/man/docs/l166/l166_in_classes.htm

    Best of luck,
    - mike

  • I appreciate your response Mike, thanks for taking the time. I probably wasn't clear in my last message.

    I originally had changed the stacks from NDATA to SDATA and that is what had caused the initial memory conflict with the printf("\n") statement. Doing so, correctly put the stacks into memory at 0xCxxxx, but incorrectly left the stack pointer R0 pointing to someplace in 0x80xxx.

    After your comment about stacks overflowing I put the stacks back to NDATA (the default config in the Start_V2.A66 file) and that fixed the problem. But only because the stacks are now where R0 is initialised to be.

    So my question should really be expanded to: How do I force the stacks into memory at 0xCxxxx (answer: probably by setting them into SDATA). BUT MORE IMPORTANTLY how do I make the stack pointer R0 point to them at 0xCxxx instead of incorrectly to someplace in 0x80xxxx. You see when I place the stacks into SDATA, R0 is incorrectly set to 0x8xxx (using DPP2) when it should be 0xCxxx (using DPP3).

    I would have thought the compiler/linker would be clever enough to set R0 so that it correctly used DPP3 when the stacks are in the segment referenced by DPP3.

    Regards
    Paul

  • Oops, my mistake. I have Startup.A66 within my reach at the moment. It is written for the C167, but I think that Start_V2.A66 should be similar. In this file the initialization of R0 is hardcoded as

    MOV     R0,#DPP2:?C_USERSTKTOP
    
    which is consistent with user stack sections being placed in the NDATA class. Basically, the startup code is hardcoded for user stacks in NDATA.
    I don't see why you can't modify the startup code to place user stacks in SDATA and modify the initialization of R0 accordingly (use DPP3 instead of DPP2.)
    There is an overview of DPP register usage here:

    http://www.keil.com/support/man/docs/c166/c166_ap_dppreg.htm

    Regards,
    - mike

  • It is probably easiest to do it as Mike says, but you can also do it without hard coding the DPP register to the instruction loading the user stack top to R0.

    1. Place the user stacks to SDATA as you have done.

    2. Change the

    ASSUME DPP3:SYSTEM
    line to read
    ASSUME DPP3:SDATA

    3. Move the user stack sections from the NDATA group (NDATA DGROUP ...) to the SDATA group, adding the SYSTEM section to SDATA group too:
    SDATA DGROUP ?C_SYSSTACK, ?C_USERSTACK, ?C_USERSTACK1, ?C_USERSTACK2, SYSTEM

    That should do it.

    Sauli

  • Please see:

    http://www.keil.com/support/docs/1695.htm

    It should be noted that you need also the USERSTACKDPP3 directive.

  • Thanks everyone for your replies.
    It all makes sense, now back to work... :)

    Regards
    Paul