On Monday 28 Jun 2004 7:47 am, Nick Brent wrote:
Hello All,
I'm trying to write a small RTOS and I'm having a bit of trouble
with re-enabling tasks when they're ready. The question,
specifically, that I have is what happens when you call a
sub-function from main or another function? If I had to guess, I'd
assume the new address of the function being called is pushed onto
the stack, and when the function exits, the address is popped off and
the original address is loaded. Am I right?
No.
I'm sure it's a little
more involved than that.
No ;-)
I couldn't seem to find an explanation in
the avr-libc user manual,
You wont. It might be in the datasheet for the processor, or the
instruction set, though.
so I'm hoping someone can explain it to me.
When a function is called some of the parameters are pushed onto the
stack, others may be passed in registers. Then the CALL instruction
pushes the currently executing address onto the stack and then jumps to
the new function.
When the function finishes the RET instruction pops the currently
executing address off the stack. This means the processor jumps to the
instruction after the CALL instruction in the original function. The
original function then removes any parameters it had pushed.
The problem that I'm running into is that I'm using lngjmp() to
restore the context of a task, which is working just fine, but when
it leaves the sub-function it just jumped back to, it goes to the
last function that was left, not the one that was left before the
context was saved. I don't know if that makes much sense, but a brief
look at setjmp() and lngjmp() in the avr-libc user manual should
explain it a little better.
I'm not convinced that you can use setjmp and longjmp like that. setjmp
remembers the stack state as it is. Subsequently a longjmp will return
to that state, but if you return from the function that called setjmp,
or longjmp up past it, then that setjmp data is no longer valid.
Say your stack looked like this:
top->dataF
dataE
setjmpY
dataD
dataC
setjmpX
dataB
dataA
If you execute a longjmp(X,v), then the stack looks like this:
top->dataB
dataA
So you can never thereafter execute longjmp(Y,v).
Note that the stack actually looks like this:
dataF
dataE
setjmpY
dataD
dataC
setjmpX
top->dataB
dataA
So longjmp(Y,v) might still appear to work, (depending on how it was
implemented.) But any interrupts and any pushing onto the stack between
the longjmp(X,v) and the longjmp(Y,v) would corrupt the stack. Never,
ever, use data that has been popped off the stack. (By Murphy's Law, it
may work while you're testing, but it wont work in the field, and it's
a really nasty bug to find.)
So if your code for context switching looks like:
setjmp(TASKA);
longjmp(TASKB,v);
It wont work.