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

Locating 2 parameters at the same address

I am using an 8051 (C51/BL51) with no off-chip memory. I have two functions with parameters:

void Detect( U8 iLed )

and

static U8 INHSampleHandler( U16 u16Sample )


Now I understand that Keil will allocate a variable (in DATA) for these. The problem seems to be that the locator is using the same memory location for both. I cannot understand why.

Below are excerpts from the scratchpad showing 2 "D:0026H". These are the only places these symbols are declared. Any ideas what I'm doing wrong?

Thanks,
Jeff

BL51 BANKED LINKER/LOCATER V5.12              07/14/2011  09:36:23  PAGE 1


BL51 BANKED LINKER/LOCATER V5.12, INVOKED BY:
Z:\TOOLS\SOFTWARE\KEIL\BL51.EXE Z:\Software\FB_CPU_Init.obj,
>> Z:\Software\Settings.obj, Z:\Software\Glo
>> bals.obj, Z:\Software\Devices\Clock.obj, Z:\ 
>> Software\Devices\Flash.obj, Z:\Software\Devices\HMI.obj
>> , Z:\Software\Devices\INH.obj, Z:\ 
>> Software\Devices\ADC.obj, Z:\Software\Devices\Timer.obj, Z
>> :\Software\Builds\TestINH - 06-00039-21-09\Main.obj
>> , Z:\Software\Test\Test_Button.obj, Z:\So
>> ftware\Builds\TestINH - 06-00039-21-09\Version.obj TO Z:\ 
>> Software\Builds\TestINH - 06-00039-21-09\06-00039-21-09-xx.wsp
>>  RS (256) PL (68) PW (78) XDATA (?XD?SETTINGS (0X0)) CODE (?CO?VERSION (0X7
>> FC0))


MEMORY MODEL: SMALL

Deleted for brevity

  -------         PROC          _INHSAMPLEHANDLER
  D:0026H         SYMBOL        u16Sample
  C:0BF1H         LINE#         150
  C:0BF5H         LINE#         151
  C:0BF5H         LINE#         207
  C:0BF7H         LINE#         208
  -------         ENDPROC       _INHSAMPLEHANDLER
  -------         ENDMOD        INH

Deleted for brevity

  C:09FEH         PUBLIC        _Detect
  C:074EH         PUBLIC        main
  -------         PROC          _DETECT
  D:0026H         SYMBOL        iLed

Parents
  • Ron,

    I'm the original poster here and I thank you for your post of AP note 129. While my original post didn't confess that I'm using function pointers, this clearly explains how I can get into trouble invoking (sorry, that's a C# term I've adopted) a function from a pointer. This also explains how to fix it.

    I don't really follow all the discussion above regarding ISRs. Does the linker make sure that ISRs (and their associated call trees) never overlay anything else? It seems like it would have to.

Reply
  • Ron,

    I'm the original poster here and I thank you for your post of AP note 129. While my original post didn't confess that I'm using function pointers, this clearly explains how I can get into trouble invoking (sorry, that's a C# term I've adopted) a function from a pointer. This also explains how to fix it.

    I don't really follow all the discussion above regarding ISRs. Does the linker make sure that ISRs (and their associated call trees) never overlay anything else? It seems like it would have to.

Children
  • I don't really follow all the discussion above regarding ISRs. Does the linker make sure that ISRs (and their associated call trees) never overlay anything else? It seems like it would have to.

    it does, except when it runs into it's trouble with function pointers.

    There is nothing inherintly wrong with using function pointers with C51 (or any code for the '51) but the mechanisms required makes it bug prone and less maintainable. Thus I (and many others) have a '51 specific coding style that avoids function pointers.

    Erik

  • Forums are good places for blanket statements, but blanket exceptions always miss caveats. If you included every caveat in the post, you would reproduce the manual ;)

    an 8051 is not a re-entrant device. If you call a function with local variables, say foo(), and an ISR occurs, and calls foo() - the ISR's version of foo() could stomp on the values in Registers R0 - R7 if you are not careful.

    So calling a function from both main and an interrupt, or a low priority and high priority interrupt you introduce a possible failure point in your code. It is generally not recommended - but there are situation where it will make sense.

    Common workaround are (from least to most preferred):

    -Create a re-entrant Stack - every time the function is called, its registered are handled by a separate stack

    http://www.keil.com/support/man/docs/c51/c51_le_reentrantfuncs.htm

    - Create a foo_for_ISR and a foo_for_main

    - Make all variables global for that foo()

    - Try to hand code the function in assembly, and attempt to put safeguards in

    - Switch to a RTOS, such as RTX, and instead have the ISR send a signal, and have a task which calls foo() handle the timing.

  • I find it curious that the App Note's method (using the OVERLAY command) doesn't even make it to your Top 5 Workarounds.

    Re-entrancy is not my problem. I'm not calling "foo()" from two places. The issue is that foo()'s parameters are overlaid with bar()'s parameters. And in the middle of running bar() an interrupt happens. Unbeknownst to the linker, the ISR called foo().

    In my situation, where all the design and 98% of the coding is complete, the OVERLAY command seems the easiest to implement. Next next best option would be to call the functions explicitly rather than by pointer.

  • "I find it curious that the App Note's method (using the OVERLAY command) doesn't even make it to your Top 5 Workarounds."

    but why use such a simple solution when more convoluted ones exist? how can we possibly justify our existence if a simple OVERLAY command does what we can do in a month's time?

    not to mention our ego for sophistication.

    :)

  • Overlay solves the 'Linker missed a relationship between 2 functions" issue.

    Once you solve that - you may not know it yet - you will have to solve the re-entrancy issue.

    If you are calling foo() via main() and via an ISR, (and if foo has local variables) you have introduced a failure point.

    To see start the debugger and run to foo()

    while in foo, go to Peripherals -> Interrupt. Find your Interrupt and set its flag.

    Pay attention to the Registers window, especially the values of R0-R7. Note how the ISR's version of foo will step on these values.

  • I understand that the OVERLAY command doesn't solve re-entrancy. I understand that the C51 is basically non-re-entrant. Ok. I get it. As I said, I do not think re-entrancy is my problem.

    The OVERLAY command should work very nicely for me. However I still don't have it working. I feel like a 2nd grader who keeps getting is homework returned "incomplete".

    Without posting lengthy code, here is the function I'm calling via pointer from an ISR. (I don't need the local variable - I just threw it in there to ensure that the problem isn't limited to parameters.)

    static U8 InhalationSampleHandler( U16 u16Sample )
    {
            U8 u8InhalationSampleHandlerLocal;
    ...
    

    Below are excepts from the linker and you'll see that addresses 26H & 28H are overlaid among several functions. I did include the Overlay command to specify that my ISR calls InhalationSampleHandler(). You'll also see that I included ?C_C51STARTUP ~ ?PR?_INHALATIONSAMPLEHANDLER?INHALATION as an attempt to solve the warning WARNING L15: MULTIPLE CALL TO SEGMENT, but it didn't help much.

    BL51 BANKED LINKER/LOCATER V5.12              07/18/2011  11:39:17  PAGE 1
    
    
    BL51 BANKED LINKER/LOCATER V5.12, INVOKED BY:
    Z:\TOOLS\SOFTWARE\KEIL\BL51.EXE Z:\MicroDose\Phase2\Software\FB_CPU_Init.obj,
    Deleted for bevity
    >> -21-02-xx.out RS (256) PL (68) PW (78) XDATA (?XD?SETTINGS (0X0)) CODE (?CO
    >> ?VERSION (0X7FC0)) OVERLAY (?PR?ADC_ISR?ADC ! ?PR?_INHALATIONSAMPLEHANDLER?
    >> INHALATION, ?C_C51STARTUP ~ ?PR?_INHALATIONSAMPLEHANDLER?INHALATION)
    
    
    Deleted for bevity
    
      -------         ENDPROC       FLASH_VERIFY
      -------         PROC          _FLASH_WRITE_BYTE
      D:0026H         SYMBOL        pu8Address
      D:0028H         SYMBOL        u8Data
    
    Deleted for bevity
    
      -------         ENDPROC       INHALATION_DETECTED
      -------         PROC          _INHALATIONSAMPLEHANDLER
      D:0026H         SYMBOL        u16Sample
      -------         DO
      D:0028H         SYMBOL        u8InhalationSampleHandlerLocal
      -------         ENDDO
    
    Deleted for bevity
    
      -------         PROC          SLEEP_NOW
      -------         DO
      D:0026H         SYMBOL        u8Save_ADC0CN
      D:0027H         SYMBOL        u8Save_P0MDIN
      D:0028H         SYMBOL        u8Save_P1MDIN
    
    
    *** WARNING L12: NO REFERENCE BETWEEN SEGMENTS
        SEGMENT1: ?PR?_INHALATIONSAMPLEHANDLER?INHALATION
        SEGMENT2: ?C_C51STARTUP
    
    
    *** WARNING L15: MULTIPLE CALL TO SEGMENT
        SEGMENT: ?PR?_INHALATIONSAMPLEHANDLER?INHALATION
        CALLER1: ?PR?ADC_ISR?ADC
        CALLER2: ?C_C51STARTUP
    

  • Found the problem and fixed it. I had to remove the connection between the function that passed the function pointer from the function itself. This is explained in the App Note. (I was thrown off by the cryptic warning from the linker.)

    Now both the parameter and local are unique in the data segment.

    Call me happy.

  • "Found the problem and fixed it."

    how about this little piece of code:

    #include <regx51.h>
    #include <intrins.h>
    
    //test to see if var1/var2 overlay creates problems.
    //if var1/var2 overlaid incorrectly, P2 will be pulled down to 0 periodically.
    //otherwise, P2 = 0x22+1;
    
    #define NOP()                   _nop_()
    #define NOP2()                  {NOP(); NOP();}
    #define NOP4()                  {NOP2(); NOP2();}
    #define NOP8()                  {NOP4(); NOP4();}
    #define NOP16()                 {NOP8(); NOP8();}
    #define NOP32()                 {NOP16(); NOP16();}
    #define NOP64()                 {NOP32(); NOP32();}
    #define NOP128()                {NOP64(); NOP64();}
    #define NOP256()                {NOP128(); NOP128();}
    
    
    void sub1(unsigned char var1) {
            var1=0;                                                 //reset var1/var2
            if (var1) var1=0;                               //make sure var1 is always 0
            //P2=var1;                                              //make sure tmr0 is working
    }
    
    void sub2(unsigned char var2) {
            var2=var2+1;                                    //increment var1/var2
            if (var2==0) var2=1;                    //make sure var2 is never zero
            NOP256(); NOP256();                             //delay 512 ticks - create opportunity for tmr0 to fire at least once
            P2=var2;                                                //output var2 on P0
    }
    
    void tmr0_isr(void) interrupt TF0_VECTOR {
            sub1(0);                                                //call sub1 periodically to reset var1/var2
    }
    
    int main(void) {
            TR0=0;                                                  //turn off tmr0
            TMOD = (TMOD & 0xf0) | 0x02;        //tmr0 in mode 2 (auto reload tl0 with th0
            TH0=-100;                                               //tmr strikes every 100 ticks
            TL0=TH0;
            ET0=1;                                                  //enable tmr0 interrupt
            TR0=1;                                                  //turn on tmr0
            EA=1;                                                   //enable global interrupt
    
            while (1) {
                    sub2(35);
            }
    }
    

    it follows the example you gave earlier, except that it uses a tmr isr to fire sub1() periodically.

    you can play with the compiler settings to get var1/var2 to overlay. what do you think you will get on P2?

  • Having mocked you of having less curiosity than me, I could hardly ignore your request. (I had to change your header files as I don't have those - I'm using an 8051F930 - so perhaps it's not an apples-apples comparison.)

    With optimization 4+ var1/var2 are at the same address. However the actual assembler code doesn't reference these and uses registers. Not a fair test.

    With optimization at 2, var1/var2 do not overlay each other. I would not expect this program to write anything other than 36 to P2.

    I have to change the code as follows to get the data to overlay:

    static void (*mHandler)(unsigned int uData);
    
    void sub1(unsigned char var1) {
            var1=0;                                                 //reset var1/var2
            if (var1) var1=0;                               //make sure var1 is always 0
            //P2=var1;                                              //make sure tmr0 is working
    }
    
    void sub2(unsigned char var2) {
            var2=var2+1;                                    //increment var1/var2
            if (var2==0) var2=1;                    //make sure var2 is never zero
            NOP256(); NOP256();                             //delay 512 ticks - create opportunity for tmr0 to fire at least once
            P2=var2;                                                //output var2 on P0
            if ( var2 == 0 )
            {
                    P2 = 1;
            }
    }
    
    void tmr0_isr(void) interrupt 1 {
    //              sub1(0);
            (*mHandler)(0);                                                //call sub1 periodically to reset var1/var2
    }
    
    int main(void) {
                    mHandler = sub1;
            TR0=0;                                                  //turn off tmr0
            TMOD = (TMOD & 0xf0) | 0x02;        //tmr0 in mode 2 (auto reload tl0 with th0
            TH0=-100;                                               //tmr strikes every 100 ticks
            TL0=TH0;
            ET0=1;                                                  //enable tmr0 interrupt
            TR0=1;                                                  //turn on tmr0
            EA=1;                                                   //enable global interrupt
    
            while (1) {
                    sub2(35);
            }
    }
    

    This code seems to suffer from the same problem as my program. In fact, from within Sub2 I got var2 == 0.

  • "This code seems to suffer from the same problem as my program. In fact, from within Sub2 I got var2 == 0."

    if you really had curiosity, you probably would have run it on different platforms and observed the differences in behavior and asked yourself why.

  • I actually claimed to have a lack of curiosity - and proud of it.

    Thanks to everyone here to for their contributions to a case solved. Especially to Ron who, strangely enough, found the solution but didn't recommend it.