The ARM Cortex multitasker follows the model introduced with the v6.1 compilers. A few extensions are also provided.
The code in MultiCortex.fth only works with the Cortex-M3 register set. When using the a Cortex-M0 device with its register allocation, use MultiCM0.fth. If you need the interworking (ARM32) register set, please contact MPE or modify the code yourself.
The configuration equates can be defined before this file is compiled.
1 equ lazy-save? \ -- flag
If previously undefined, LAZY-SAVE?
is set to one
and registers are only saved if they have to be.
0 equ norun-sleep? \ -- flag
If previously undefined, NORUN-SLEEP?
is set to zero.
If set to one, the processor is put to sleep using a WFI
instruction if there are no other tasks to run. This can
save a lot of power, but requires regular interrupts and
care that tasks are properly enabled. For this equate to
be used LAZY-SAVE?
must be non-zero.
0 equ test-multi? \ -- flag ; true to compile test code
If previously undefined, TEST-MULTI?
is set to zero
and test code is not compiled.
cell LINK link to next task
cell SSP Saved Stack Pointer
cell STAT Bit 0 1 = running, 0 = halted
Bit 1 1 = message pending
Bit 2 1 = event triggered
Bit 3 1 = event handler run
Bit 4..7 Reserved
others 1 = set to run task, available to user
cell TASK Task that sent message here
cell MESG Message address
cell EVNTw CFA of word run as event handler
This structure is allocated at the start of the USER
area. Consequently the TCB
of the current task is given
by UP
.
struct /TCB \ -- size
The structure used by the code that matches the
description above.
init-u0 constant main \ -- addr ; tcb of main task
Returns the base address of the main task's USER
area.
0 value multi? \ -- flag
Returns true if the tasker is enabled.
: single \ --
Disable scheduler.
: multi \ --
Enable scheduler.
CODE pause \ -- ; the scheduler itself
The software scheduler itself - with lazy register save.
CODE pause \ -- ; the scheduler itself
The software scheduler itself - without lazy register save.
: status \ -- task-status
Returns the current task's status cell, but with the run
bit masked out.
: restart \ task -- ; mark task TCB as running
Sets the RUN bit in the task's status cell.
: halt \ task -- ; reset running bit in TCB
Clears the RUN bit in the task's status cell.
: stop \ -- ; halt oneself
HALT
s the current task, and executes PAUSE
.
Event handling is only compiled if the equate
EVENT-HANDLER?
is set non-zero in the control
file.
: set-event \ task --
Set the event trigger in the TCB of task.
: event? \ task -- flag
Returns true if true if task has processed an event
trigger and the event-run flag has not been cleared yet.
Use clr-event-run
below in the event handler to
clear the event-run flag.
: clr-event-run \ --
Reset the current task's EVENT_RUN
flag.
: to-event \ xt task -- ; define action of a task
Sets XT as the event handler for the task.
Message handling is only compiled if the equate
MESSAGE-HANDLER?
is set non-zero in the control
file.
: msg? \ task -- flag
Returns true if task has received a message.
: send-message \ addr task --
Send a message to a task.
: get-message \ -- addr task
Wait for any message and return the message and the task it
came from.
: wait-event/msg \ --
Wait for a message or an event trigger.
code init-task \ xt task -- ; Initialise a task stack
Initialise a task's stack before running it and
set it to execute the word whose XT is given.
: add-task \ task -- ; insert into list
Add the task to the list of tasks after the current task.
: sub-task \ task -- ; remove task from chain
Remove the task from the task list.
: initiate \ xt task -- ; start task from scratch
Start the given task executing the word whose XT is given, e.g.
['] <name> <task> INITIATE
: sleeper \ xt task --
Start task from scratch, but leave it HALT
ed. Use in
the form:
['] <action> <taskname> SLEEPER
to put a task on the active task list, but as if HALT
ed.
SLEEPER
allows you to make a task ready for waking up
later, perhaps by another task. This avoids having to put
STOP
as the first word in a task. Note that SLEEPER
does not call PAUSE
. See also INITIATE
.
: terminate \ task --
Stop a task, and remove it from the list.
: init-multi \ -- ; initialisation with multi-tasking
Initialise the multitasker and start it. If tasking is
selected by setting the equate TASKING?
in the control
file, KERNEL62.FTH will automatically run this word.
Make sure that your initialisation code includes
INIT-MULTI
or your code will crash.
: his \ task uservar -- addr
Given a task id and a USER
variable, returns the
address of that variable in the given task. This word is
used to set up USER
variables in other tasks.
The semaphore code is only compiled if the equate
SEMAPHORES?
is set non-zero in the control
file.
A SEMAPHORE
is an extended variable used for signalling
between tasks, and for resource allocation. It contains two
cells, a counter and an arbiter. The counter field
is used as a count of the number of times the resource may
be used, and the arbiter field contains the TCB of the task
that currently owns it. This field can be used for priority
arbitration and deadlock detection/arbitration. The count
field allows the semaphore to be used as acounted
semaphore or as an exclusive access semaphore.
For example a character buffer may be used where the semaphore counter contains the number of available characters.
An exclusive access semaphore is used to share resources.
The semaphore is initialised to one, usually by SIGNAL
.
The first task to REQUEST
it gains access, and all
other tasks must wait until the accessing task SIGNAL
s
that it has finished with the resource.
: semaphore \ -- ; -- addr [child]
Creates a semaphore which returns its address at runtime.
The count field is initialised to zero for use as counted
semaphore. Use in the form:
Semaphore <name>
If you want this to be an exclusive access semaphore, follow this with:
1 <name> !
: signal \ addr --
SIGNAL
increments the counter field of a semaphore, indicating
either that another item has been allocated to the resource, or
that it is available for use again, 0 indicating in use by a task.
: request \ sem -- ; get access to resource, wait if count = 0
REQUEST
waits until the counter field of a semaphore
is non-zero, and then decrements the counter field by one.
TASK <name>
builds a named task user area.
The action of a task is assigned and the task started
by the word INITIATE
['] <action> <task> INITIATE
START:
is used inside a colon definition. The code
before START:
is the task's initialisation, performed
by the current task. The code after START:
up to the
closing ;
is the action of the task. For example:
TASK FOO
: RUN-FOO
...
FOO START:
...
begin ... pause again
;
All tasks must run in an endless loop, except for initialisation
code.
When RUN-FOO
is executed, the code after START:
is set up as the action of task FOO
and started.
RUN-FOO
then exits.
If you want to perform additional actions after starting the task, you
should use INITIATE
to start the task.
variable task-chain \ -- addr
Anchors list of all tasks created by TASK
and friends.
: task \ -- ; -- task ; TASK <name> builds a task
Note that the cross-interpreter's version of TASK
has
been modified from v6.2 onwards to leave the current
section as CDATA
.
: task \ -- ; -- task ; TASK <name> builds a task
Creates a new task and data area, returning the address
of the user area at run time. The task is also linked into
the task chain anchored by TASK-CHAIN
.
: start: \ task -- ; exits from caller
Used inside a colon definition. The code following
START:
up to the ending semi-colon forms the action
of the task. The word containing START:
finishes at
START:
.
: .task \ task --
Display task's name if it has one, otherwise display its
address.
: .tasks \ -- ; display all task names
Display all the tasks anchored by TASK-CHAIN
.
: .running \ --
Display all the running tasks.