B
Brian Gregory
Guest
Oops. I seem to have messed up the quoting.
Hopefully it\'s still legible.
--
Brian Gregory (in England).
Hopefully it\'s still legible.
--
Brian Gregory (in England).
Follow along with the video below to see how to install our site as a web app on your home screen.
Note: This feature may not be available in some browsers.
On 4/20/2023 10:35 PM, Jasen Betts wrote:
On 2023-04-20, Brian Gregory <void-invalid-dead-dontuse@email.invalid
wrote:
On 20/04/2023 19:55, Don Y wrote:
On 4/20/2023 11:48 AM, Brian Gregory wrote:
On 20/04/2023 15:10, Don Y wrote:
On small (resource starved) processors, I often implement
multitasking using
a structure like:
Main:
      Call task1
      Call task2
      Call task3
      Jmp Main
And, within any task:
      Call Yield
Here:
unwinds the stack (to get the address IN THE TASK at which the
Call was
executed) and plugs the return address to replace the \"taskX\" address
field
of the associated \"Call\" within Main. So, the next time around the
loop,
\"Call task1\" may execute as \"Call Here\".
[This makes for incredibly fast, low-overhead context switches as the
new PC
is \"restored\" directly *in* the invoking Call]
Doesn\'t work with XIP, though.
I can\'t see how that works. Do you save the task\'s stack somewhere
too?
In truly \"resource starved\" applications (think \"a few KB of RAM -- or
less,
total\"), you don\'t have *space* for a stack per task. And, some really
small
devices don\'t have an \"accessible\" stack that one can preserve (and
restore)
as part of a context switch.
I\'d just write a proper task switcher, either interrupt driven or not
as suites the situation. It\'s usually no more than a handful of pages
of C and/or assembler.
It\'s not that it is difficult. Rather, that it consumes runtime
resources.
But, being able to break an application into smaller, concurrent pieces
is too valuable a technique to discard just because \"you can\'t
afford it\".
Then I don\'t get it. Unless maybe the calls to the tasks are just from
habit and could more straightforwardly be replaced by jumps or gotos.
The use of call puts the address of the instruction after the call on
the stack from where it can be fetched by the yield() call.
I\'m guessing that Yield() pops the return address of the stack and
saves it in a register
then pops the next return address off the stack, substracts some
constant, and puts it in a pointer register and and then saves the
first register there,
Or, encodes the contents of the first register into a valid instruction
that references that location -- depends on the specifics of the
instruction set encoding.
The point is, the only bit of task state that is preserved is
the PC (for the location after the yield) AND that it is
preserved in an instruction that can efficiently dispatch
to that address when next encountered.
[Storing the entire state of a task typically has the
PC \"restored\" as the last step in reestablishing the
task\'s state just prior to next invocation with the
PC value (as well as the rest of the state) retrieved
from some TCB structure in general purpose memory
(here, it is stored in \"program memory\")]
adds the constant back on and does jump to that
address.
Because you don\'t have to restore any of the rest of the
machine state, you are free to make that \"jump\" in whatever
means is easiest (consuming the entire machine state in the
process, if necessary)
This all makes perfect sense and is in line with techniques I have used on
occasion except this:
Main:
     Call task1
     Call task2
     Call task3
     Jmp Main
Because you don\'t have to restore any of the rest of the
machine state, you are free to make that \"jump\" in whatever
means is easiest (consuming the entire machine state in the
process, if necessary)
Okay, this makes more sense now.
On 4/21/2023 5:24 PM, Brian Gregory wrote:
Because you don\'t have to restore any of the rest of the
machine state, you are free to make that \"jump\" in whatever
means is easiest (consuming the entire machine state in the
process, if necessary)
Okay, this makes more sense now.
I would sure hope so -- there are many products that have been
built on this approach! :
The real value, though, is in illustrating that there is often
value to challenging your preconceived notions of what you \"need\"
in a solution space. People who \"just write code\" tend not to
examine these issues -- often completely ignorant of the
choices involved!
Do you really need 32-bit floats? Would 24-bit suffice? Maybe
shrink the exponent field to gain another bit in the mantissa?
Do you need support for overflow/underflow/denormalized values?
Do you need to be able to print floats? Hex/octal constants?
Long longs? Limit the width of a printed field? Zero-pad? etc.
Often, these assumptions have (relatively) high associated costs.
E.g., add a printf() invocation to a binary and notice how much
bigger the executable becomes! Note how costly a taskswitch is
*when* you\'ve used a floating point calculation vs. not (most
reschedules are smart enough NOT to save/restore the FPU state
if you\'re not using it... many are smart enough to know not
to do so it if you haven\'t used it *recently*!)
On Fri, 21 Apr 2023 19:14:00 -0700, Don Y
blockedofcourse@foo.invalid> wrote:
On 4/21/2023 5:24 PM, Brian Gregory wrote:
Because you don\'t have to restore any of the rest of the
machine state, you are free to make that \"jump\" in whatever
means is easiest (consuming the entire machine state in the
process, if necessary)
Okay, this makes more sense now.
I would sure hope so -- there are many products that have been
built on this approach! :
The real value, though, is in illustrating that there is often
value to challenging your preconceived notions of what you \"need\"
in a solution space. People who \"just write code\" tend not to
examine these issues -- often completely ignorant of the
choices involved!
Do you really need 32-bit floats? Would 24-bit suffice? Maybe
shrink the exponent field to gain another bit in the mantissa?
Do you need support for overflow/underflow/denormalized values?
Do you need to be able to print floats? Hex/octal constants?
Long longs? Limit the width of a printed field? Zero-pad? etc.
Often, these assumptions have (relatively) high associated costs.
E.g., add a printf() invocation to a binary and notice how much
bigger the executable becomes! Note how costly a taskswitch is
*when* you\'ve used a floating point calculation vs. not (most
reschedules are smart enough NOT to save/restore the FPU state
if you\'re not using it... many are smart enough to know not
to do so it if you haven\'t used it *recently*!)
Can you even get 24 bit floating point processors ?
All of them that are built into my processors are 32 bit or if wanted,
more expensive 64 bit double precision IEEE FP.
If 32 bit FP is single precision, why would you want 24 bit ?