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

eorror of multiple call to segment

Hi,

The compile error is:

WARNING L15: MULTIPLE CALL TO SEGMENT
SEGMENT: ?PR?_STRTONUM?PARSE
CALLER1: ?PR?TC0?PERIPHERAL
CALLER2: ?C_C51STARTUP

The callers TC0 is an ISR using reg bank 2 and I don't know about the c51startup. The called function STRTONUM is using the directive of #pragma NOAREGS so can be called for any functions. The question is why I still got the multiple call error? Thanks for answering my question.

By the way, C51 is V6.21, BL51 is V4.21 and LIB51 is V4.20.

chao.

Parents
  • Function main() also calls STRTONUM function. As the result I have two different functions using different reg banks to call STRTONUM. But still, why would I get multiple call warning since the directive of NOAREGS is used on STRTONUM? Can this warning be ignored?

    You are calling a function from main AND an interrrupt.
    1) this is bad practice
    2) if you insist on using this bad practice, you must also make the code reentranr.

    Erik

Reply
  • Function main() also calls STRTONUM function. As the result I have two different functions using different reg banks to call STRTONUM. But still, why would I get multiple call warning since the directive of NOAREGS is used on STRTONUM? Can this warning be ignored?

    You are calling a function from main AND an interrrupt.
    1) this is bad practice
    2) if you insist on using this bad practice, you must also make the code reentranr.

    Erik

Children
  • erik,

    First, thank you for your help and thanks to Andy too. I agress with your points. Since it is a bad practice, is there any way to get around other than using reentrant?

    chao.

  • is there any way to get around other than using reentrant?

    Yep. I do this all the time. Here are the main points to consider.

    • The function may be called from both the main program and the ISR, but it will not be invoked simultaneously. In other words, when the function is called by the main program, the interrupt cannot invoke it. Therefore, you must disable interrupts when this function is entered. If all arguments are passed in registers, you can do this using #pragma disable. Otherwise, in the main program you must disable interrupts, call the function, and restore interrupts.
    • The function should not call other functions. If it does, this gets a little more complex. Basically, you'll have to do all of this stuff for those functions, too.
    • Some of the arguments and local variables are stored in fixed memory locations. This is important because the linker builds a call tree and inserts this function somewhere in it. If the function is in the main program call tree and if it is invoked by an ISR, it will trash the arguments or locals of some other function. The best way to handle this problem is to remove the function from the call tree. Use the overlay linker directive to do that.
    The following knowledgebase article describes exactly what to do.

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

    Jon

  • Jon,

    Thanks, got your help. As I understand you gave me two ways to workaround, disable the interrupt and disable overlay. Here is my project structure:

    1. Data is received from uart (interrupt is used).
    2. Main() is to analyze the received data and display the result on a screen (LCM). The analyzed data will be shown in different pages, selected by a user.
    3. Another ISR (timer 0 to detect a keypad) is also to display the analyzed data on the screen for quickly responding to a user's input.

    In short, both main and the ISR functions are doing the same thing. The only difference is that ISR can quickly response to a user, simple as that. So my program would not work if I disable the interrupt. On the other hand, if I disable overlay it would take lots of xdata space due to the display subroutines. Do you have any suggestion?

    chao.

  • Presumably, this STRTONUM function is just doing some sort of formatting for the display?

    Why not just keep the formatted result in a buffer, so that both main and the interrupt can access the buffer when they need to display it?

    This also has the advantage of relieving the ISR of the task of doing the formatting - which is generally a Good Thing.

  • Writing to an LCD is a very slow (relatively) operation, and not the sort of thing you should be doing inside an interrupt service.
    Your ISR would be better off setting a flag to note that the LCD requires updating, and regularly check this flag in your non-interrupt code.

  • Could you not just set a flag in the ISR indicating that main() should call the function? This is the usual sort of technique to avoid these kind of problems.

    Stefan

  • In short, both main and the ISR functions are doing the same thing. The only difference is that ISR can quickly response to a user, simple as that.

    OK. So what happens if main is in the middle of displaying the data and the interrupt occurs to display the data?

    So my program would not work if I disable the interrupt.

    Why? I only suggest disabling the interrupt when the main program calls the function. I do not suggest that you completely and forever disable the interrupt.

    if I disable overlay it would take lots of xdata space due to the display subroutines

    Really? Have you tried this? Overlaying, in this case, prevents the display routine from corrupting variables in other functions (that were overlaid with the display function).

    Do you have any suggestion?

    Yes. I already gave you THE best suggestion. But, since you don't like it, here are several software kludges that you can try:

    1. Make 2 copies of the display function. One to be called from the main program and the other to be called from the interrupt.

    2. Make the display function reentrant and define a reentrant stack.

    3. Only call the display routine from the main function if a flag is set. Then, in the main program and in the interrupt set this flag whenever you want to display the information.

    4. In the main program, rather than call the display function, invoke the interrupt by manually setting the interrupt request bit in software.

    5. Connect another inexpensive 8051 to your hardware design to run the display function. Trigger it by connecting an output pin from the primary 8051 to the external interrupt input. Then, in your main program and interrupt you can simply toggle the output pin.

    Maybe one of these is helpful.

    Jon

  • #pragma disable seems to generate code thus:

    ; prologue
           SETB    C
           JBC     EA,?C0069
           CLR     C
    ?C0069:
           PUSH    PSW
    
    ; epilogue
           POP     PSW
           MOV     EA,C
           RET
    

    That is, it's remembering the state of EA in the carry bit, and storing the carry bit (as part of PSW) on the stack. In my 8051 assembler naivete, I wrote code for the same purpose thus:

    ; prologue
           PUSH    IE
           CLR     EA
    
    ; epilogue
           POP     IE
           RET
    

    which seems more straightforward. So what's going to go wrong with this method? Why do we need to get the carry bit involved? Or is it just that the compiler "owns" the carry bit, and that way it doesn't have to worry about the C routine changing other bits in IE?

  • So what's going to go wrong with this method?


    This works!

    #pragma disable
    void init_serial_isr (void)
    {
    ES = 1;  /* enable serial interrupts */
    }


    This does not!

    ; prologue
           PUSH    IE
           CLR     EA
    
           SETB    ES
    
    ; epilogue
           POP     IE
           RET

    Jon

  • That's the sort of thing I had in mind in the last sentence (the C routine making other changes to IE). The compiler has to play it safe for the general case.

    In my case, I have the advantage of some extra information about what the routines are doing, so preserving IE modification isn't a problem.

    I suddenly panicked that I had overlooked some subtle 8051-ism about disabling interrupts and just hadn't noticed the error in the lab yet.

  • Thanks for all of you who try to help me.

    I have three layers of buffer for data. The first layer is for UART raw data. The second layer is a copy of the raw data for the main() to analyze and not to "disturb" the work of UART. The third layer is for the result, the analyzed data for the displaying purpose. So Neil's concern has been taking care of, unless I misunderstand his point.

    A LCM is slow relatively. Got Collett's point. I will keep that in mind.

    The reason I need to do display things in an ISR is to quickly display the page an user is selected. If I only use a flag to signal the main() then display, it will be "slow" to react to an user's request. This approach was my first attempt. The good thing about this approach is save, simple and won't have problem like Jon mentioned, "what happens if main is in the middle of displaying the data and the interrupt occurs to display the data" which I realized last night.

    Jon, it was not I didn't take your solution. Maybe I tried to be "perfect" (in my own way) on this project since this was the first project I had and not fully understood both my project and the compiler. You know, it is the "first time" thing.

    Please keep posting your new ideas. I really need that. Thank you very much.

    chao.

  • Jon,

    Can reentrant prevent the problem of "what happens if main is in the middle of displaying the data and the interrupt occurs to display the data?" Is this a displaying order problem? For instance, when main() is in the middle of displaying page 1, timer 0 interrupt fires and an user wants to see page 2. After the interrupt is done page 2 is on the screen already, execution goes back to main() and continue displaying page 1 (the old job, before the interrupt). This is the major problem of my project structure. I am wondering what if I can somehow make the execution not to run the old job after the interrupt. Is it possible to do that in c level? I need help on this "how". If not maybe I need to re-construct my entire program.

    chao.

  • "maybe I need to re-construct my entire program"

    That would seem to be your solution.

    Despite the fact that Jon sees it as a software cludge I'd still recommend setting a flag in your ISR and doing the 'real' work from main. If nothing else, you'll find this a lot easier to maintain when you look at your code again in a couple of years time.

    Stefan

  • Despite the fact that Jon sees it as a software cludge I'd still recommend setting a flag in your ISR and doing the 'real' work from main.

    Well, I see EVERYTHING as a software kludge!

    If the ISR is already implemented and working, I'd lean more towards setting the interrupt request flag in the main program and letting the ISR handle things. I think this is less code, does not require a flag or other variables, and has fewer design issues than checking a flag in the main loop. Note that doing real work in an ISR is not my first choice either.

    As far as making the display function reentrant...if it is invoked from the main program and, while displaying, is invoked by the ISR, the display may show strange stuff (the first part displayed by main, the whole info displayed by the ISR, and the last part displayed by main). Is this a problem?

    This is why I suggest disabling the ISR when making the call to the display routine from the main program.

    Jon