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
If the two functions aren't calling each other and the compiler/linker can see that the two variables will never be needed at the same time - what is then wrong with optimizing the memory usage by using the same space for the two variables?
I see. In my case one of these functions is called by an ISR. So I changed my function declaration to
static U8 InhalationSampleHandler( volatile U16 u16Sample )
and now the locator keeps the data separate. Any idea if this is a guarantee that the linker will do the right thing?
Thanks for the help.
Just because you've managed to avoid the problems does not mean that there are no problems.
Just like crossing the road has many dangers - but most of us manage to do it every day by being aware of the dangers, and acting accordingly.
But saying there are NO dangers is untrue and unhelpful.
"Just because you've managed to avoid the problems does not mean that there are no problems."
Andy, what problems are you talking about? I certainly didn't have to avoid any problems. There weren't any problems in what I did. Why? because I understood how the 51 works, how the assembler works, what the compiler produced, how the linker works, and what I wanted to achieve. There were no problems and no gotchas.
"Just like crossing the road has many dangers ..."
That's an extremely poor example. Processors are (in general) highly predictable. Crossing the road is totally different. You are not in control of all the variables - Most importantly there is that unknown variable sitting behind the wheel of a large mass.
"But saying there are NO dangers is untrue and unhelpful."
That seems to lack a certain amount of context. Go back and you'll see my post ended with:
No problem, so long as you know what you're doing, period.
So ... going back to the full post and full context, what I was saying is that there is no danger in doing it per se. You (and others) might consider it a danger in the context of Keil's implementation of C - But in itself I stand by what I said.
"Just like crossing the road has many dangers - but most of us manage to do it every day by being aware of the dangers, and acting accordingly."
which of the following statement would you agree more?
1) don't cross the road (because there is danger); 2) it is OK to cross the road, as long as you understand the danger.
I would rephrase it like this:
"Would you like to establish a _habit_ of crossing the road at a dangerous spot for 10 other people, when you know that the risk involved is significant"?
To me, the question is not if it is dangerous or not. Risk can be managed, at least on the short term. The question is whether this is sustainable on the long term.
my post was triggered by it is in fact possible to write inherently re-entrant functions in Keil C51 (and I don't mean by using the reentrant keyword). Of course there are limitations. The functions would generally be small, take few parameters and use few few is not none local variables
thus
"pray show a function that "don't use the reentrant keyword" and "use few local variables" and is "inherently re-entrant"."
Erik, I really expected better of you.
Anyway - As a starting point, take the simple/obvious example:
void foo(void) { ; } You'd find it difficult to get fewer local variables. It doesn't need reentrant. It doesn't require any overlaying of data (simply because there isn't any).
of course not, with no local variables what I asked you to do was to show it "with a few local variables" as you stated would be no problem
Erik
"of course not, with no local variables what I asked you to do was to show it "with a few local variables" as you stated would be no problem"
So I take it that you didn't pursue the finding of a solution yourself.
I did say that my little code snippet was the starting point. Just consider/remember that parameters and variables do not have to be stored in memory - Hint, no mention of registers.
Please remember, I did say in the same paragraph:
"Of course there are limitations."
And note that for this part, at least, I didn't say there would be 'no problem'.
Anyway ... I do not consider this part of the discussion to be particularly important and I'm not going to get dragged any further into it.
the answer to that would depend on many different factors:
1) if the pay-off is sufficient, I would; 2) if the 10 other people understand their risks they are taking, I would; 3) if we are prepared for the risk, we would; ...
it is stupid to say "there is risk so don't do it". we all take risks, one way or another. sometimes wisely and others not so.
there is nothing wrong with taking risks. you just need to plan out the pros / cons and be good at it. that's essentially what IB said, in a more generic form.
It is not necessarily stupid to say "there is risk so don't do it". There is a risk going very fast on a curvy road, to an inexperienced driver I'd say "there is risk so don't do it", to a NASCAR driver I would not.
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.
"There is a risk going very fast on a curvy road, to an inexperienced driver I'd say "there is risk so don't do it", to a NASCAR driver I would not."
but that's precisely what IB said: "there is nothing wrong with doing X if you understand what you are doing" aka your NASCAR example.
the converse of what IB said would be "don't do X if you are an inexperienced driver".
you guys really really need to improve your reading comprehension.
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.
"you guys really really need to improve your reading comprehension."
So now it's "you guys"? Why the generalization? It seldom helps an argument. How much credibility would you give to the statement: "Everyone knows that all bikers are criminals"?
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.
:)
View all questions in Keil forum