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

Ram corruption

Hello,

I have the following piece of code:

static void SETBIT( Pcf8574 xdata *Object, Nat8 Pin, Nat8 Active )
{
    ASSERT( ( 0 <= Pin )
          &&( Pin <= 7 )
          );

    ASSERT( ( Active == TRUE )
          ||( Active == FALSE )
          );

    if ( (Bool)Active )
    {
        Object->Buffer |= (_crol_( 0x01, Pin ) & 0xFF);
    }
    else
    {
        Object->Buffer &= (~_crol_( 0x01, Pin ) & 0xFF);
    }

    WriteI2c( Object );
}

This function receives a pointer to an object (object is a structure with two elements of type unsigned char). The object is located in XDATA, hence the memory specifier.

After some processing on the object (changing a bit in the Buffer member of the structure), the object is passed as an argument to the function WriteI2c().
The prototype of this function is:
static void WriteI2c( Pcf8574 xdata *Object ).

When the function SETBIT is called, the address of Object is (in my case) X:0x000002 and points to location X:0x00014D in Ram, where my array of structures is located (in the example given, the pointer points to the second element of the array).

Up until the call to WriteI2c( Object );, everything is ok. However, when I enter the function WriteI2c(), all of a sudden the address of the object Object is changed from X:0x000002 to D:0x04 (still points to the Ram location X:0x00014D). How can this change of memory space from XDATA to DATA be explained for the address of Object???.

I also encounter another problem:

Below, you can find the implementation of the function WriteI2c():

static void WriteI2c( Pcf8574 xdata *Object )
{
    i2c_swi2c_Write( Object->I2cAddress
                   , &( Object->Buffer )
                   , 1
                   );
}

The assembly code of this function is:

   129: static void WriteI2c( Pcf8574 xdata *Object )
C:0x45AC    8F82     MOV      DPL(0x82),R7
C:0x45AE    8E83     MOV      DPH(0x83),R6
   130: {
C:0x45B0    E0       MOVX     A,@DPTR
C:0x45B1    FF       MOV      R7,A
C:0x45B2    AC83     MOV      R4,DPH(0x83)
C:0x45B4    AD82     MOV      R5,DPL(0x82)
C:0x45B6    ED       MOV      A,R5
C:0x45B7    2401     ADD      A,#0x01
C:0x45B9    FD       MOV      R5,A
C:0x45BA    E4       CLR      A
C:0x45BB    3C       ADDC     A,R4
C:0x45BC    FA       MOV      R2,A
C:0x45BD    A905     MOV      R1,0x05
C:0x45BF    7B01     MOV      R3,#0x01
C:0x45C1    90000A   MOV      DPTR,#0x000A
C:0x45C4    7401     MOV      A,#0x01
C:0x45C6    F0       MOVX     @DPTR,A			<===!!!
C:0x45C7    0231D2   LJMP     i2c_swi2c_Write(C:31D2)

This function also gives me problems: when the command on the line indicated by "<===!!!" is executed, I get a Ram corruption.

Earlier in the process, there has been declared a buffer in XDATA as follows:
xdata Nat8 buf[ 40 ];.

This buffer is filled with the result of the following command:
sprintf( buf, "Date: %s - Time: %s", __DATE__, __TIME__ );

This buffer is (again in my situation) located on address X:000008. When the line with the indication above is executed, it overwrites (among other locations) the content of my buffer on address X:0x00000A.

What happens is that the value #0x000A is loaded into the DPTR (third last assembly line). After that, the value 0x01 is loaded into A and then the content of A is written to locaion X:0x00000A via a MOVX instruction, so to XDATA.
But I have a buffer which is already assigned to this memory location!!!
"buf" is positioned between the locations X:0x000008 and X:0x000038, so why is the code generating instructions to write on location X:0x00000A (so, overwriting other memory areas)??? Can someone explain this? To my humble opinion, this should not happen!

Also creating temporary variables like given in the code below, doesn't solve the issue:
static void WriteI2c( Pcf8574 xdata *Object )
{
    Nat8 TempAddress = Object->I2cAddress;
    Nat8 TempData    = Object->Buffer;

    i2c_swi2c_Write( TempAddress
                   , &TempData
                   , 1
                   );
}

I've looked already for hours to this problem but can't find neither an explanation nor a solution.

Any help is highly appreciated.

Rgds,

Geert

PS.: uVision2, compiler = V7.02b

Parents

  • I think the key phrase here is "function pointer". Using function pointers on the 8051 brings some interesting new challenges (ahem) to writing C.

    Remember that the 8051 has no stack to speak of. So, the compiler is very clever and does compile-time analysis of memory usage, and creates code that does direct memory accesses to data that would normally be placed on the stack for most processors. Both function parameters that don't fit into registers as well as local ("auto") variables get treated this way.

    Since assigning every parameter and local its own memory location would eat up a lot of memory, the compiler reduces the memory consumption by analyzing the call tree of the program. If, say, main calls f1(), and then calls f2(), you know that f1 and f2 won't use their memory at the same time, and thus you can overlay their two "stack" areas. (Note that the same thing happens on a stack-based architecture. f1 runs, and data gets pushed and popped on the stack. Then f2 runs, and data again gets pushed and popped on the stack, overwriting the same memory locations f1 was using earlier. The difference is just that with most processors, the "overlaying" is happening at run time; with the 8051, the overlaying is determined at compile time.)

    All that said, the compiler thus has to know which functions call which other functions. Function pointers make this difficult, because when you call a function though a function pointer, it can be difficult to tell at compile time what function the pointer might point to.

    The linker provides an OVERLAY directive so that you can fix up the call tree to tell the linker which functions really call which other functions. Study the sections of the manual on function pointers and overlaying, and take a look at your call tree in the map file. I think perhaps you'll find that the call tree is incorrect, and thus the overlaying is incorrect, which is why when that last parameter gets bumped to memory location 000A, it's overwriting your other data. The compiler thinks 000A is currently free at that point in the call tree, but the function pointers have deceived it.

    As an aside, with all due respect to Phillipe, I think the object-oriented virtual function emulation is a bit much for this problem. Certainly, you don't want ten copies of the code for ten devices. But you can probably manage with static (in the C++ sense) routines that take a parameter indicating which device to affect. Just pass the I/O address/buffer structure to the routines that drive the I2C devices. You shouldn't need any function pointers at all, which would avoid the whole problem.


Reply

  • I think the key phrase here is "function pointer". Using function pointers on the 8051 brings some interesting new challenges (ahem) to writing C.

    Remember that the 8051 has no stack to speak of. So, the compiler is very clever and does compile-time analysis of memory usage, and creates code that does direct memory accesses to data that would normally be placed on the stack for most processors. Both function parameters that don't fit into registers as well as local ("auto") variables get treated this way.

    Since assigning every parameter and local its own memory location would eat up a lot of memory, the compiler reduces the memory consumption by analyzing the call tree of the program. If, say, main calls f1(), and then calls f2(), you know that f1 and f2 won't use their memory at the same time, and thus you can overlay their two "stack" areas. (Note that the same thing happens on a stack-based architecture. f1 runs, and data gets pushed and popped on the stack. Then f2 runs, and data again gets pushed and popped on the stack, overwriting the same memory locations f1 was using earlier. The difference is just that with most processors, the "overlaying" is happening at run time; with the 8051, the overlaying is determined at compile time.)

    All that said, the compiler thus has to know which functions call which other functions. Function pointers make this difficult, because when you call a function though a function pointer, it can be difficult to tell at compile time what function the pointer might point to.

    The linker provides an OVERLAY directive so that you can fix up the call tree to tell the linker which functions really call which other functions. Study the sections of the manual on function pointers and overlaying, and take a look at your call tree in the map file. I think perhaps you'll find that the call tree is incorrect, and thus the overlaying is incorrect, which is why when that last parameter gets bumped to memory location 000A, it's overwriting your other data. The compiler thinks 000A is currently free at that point in the call tree, but the function pointers have deceived it.

    As an aside, with all due respect to Phillipe, I think the object-oriented virtual function emulation is a bit much for this problem. Certainly, you don't want ten copies of the code for ten devices. But you can probably manage with static (in the C++ sense) routines that take a parameter indicating which device to affect. Just pass the I/O address/buffer structure to the routines that drive the I2C devices. You shouldn't need any function pointers at all, which would avoid the whole problem.


Children