Cheddar 1.3p5 user's guide
LYSIC Team
LYSIC technical report number singhoff-01-03
First publication : september 2003
Last update : 1st november 2005
Frank Singhoff, Jérôme Legrand, Laurent Nana, Lionel Marcé
You will find here a short user's guide of Cheddar.
Cheddar is a free real time scheduling framework. Cheddar
is designed for checking task temporal constraints of a real time
application/system. It can also help you for quick prototyping of real
time schedulers. Finally, it can be used for educational purpose. Cheddar
is a free software, and you are welcome to redistribute it under certain
conditions; See the GNU General Public License for details. Cheddar
is developped by the LYSIC Team, University of Brest.
WARNING : this user's guide supposes that you have a
minimum background on real time applications/systems and real time scheduling.
If it's not your case, you can see this link which includes
some very basic articles or book references. This link also provides a descreption
of the analytical methods developped into Cheddar and gives some publications which show how to use Cheddar.
You want to improve this page ? Your english is better of
mine (that's certainly the case...) ? Feel free to contact us :-)))
In this chapter, you will find a description of the most important scheduling
and feasibility services provided by Cheddar in the case of independent tasks.
This section will show you how to call the simpliest
features of Cheddar.
Cheddar provides tools to check temporal constraints of
real time tasks. These tools are based on classical results from
real time scheduling theory. Before calling such tools, you have to
define a system which is mainly composed of several processors and tasks.
To define a processor, choose the "Edit/Add/Add a processor"
submenu. The window below is then displayed :
Figure 1.1 Adding a processor
A processor is defined by the following fields (see Figure 1.1) :
- The name of the processor. A processor name
can be any combinaison of literal characters including underscore.
Space is forbidden. Each processor must have a unique name.
- The scheduler hosted by the processor. Basically,
you can choose from a various set of schedulers such as (to get a detailed description
on these schedulers, see your prefered real time books of the publications
provided with this program) :
- Earliest Deadline First (EDF). Tasks can be periodic
or not and are scheduled according to their deadline.
- Least Laxity First (LLF). Tasks can be periodic
or not and are scheduled according to their laxity.
- Rate Monotonic. Tasks have to be periodic and
deadline must be equal to period. Tasks are scheduled according
to their period. You have to be warned that the value of the priority
field of the tasks is ignored here.
- Deadline Monotonic. Tasks have to be periodic
and are scheduled according to their deadline. You have to
be warned that the value of the priority field of the tasks is
ignored here.
- POSIX 1003.1b scheduler. Tasks can be periodic or not.
Tasks are scheduled according to the priority and the policy
of the tasks. (Rate Monotonic and Deadline Monotonic use the same
scheduler engine except that priorities are automaticly computed
from task period or deadline). POSIX 1003.1b scheduler supports SCHED_RR,
SCHED_FIFO and SCHED_OTHERS queueing policies.
SCHED_OTHERS is a time sharing policy.
SCHED_RR and SCHED_FIFO tasks must have
priorities range from 255 to 1. Priority level 0 is reserved to
SCHED_OTHERS tasks. The highiest priority level is
255.
- User-defined scheduler (called "Parametric" scheduler). This last one allows user
to define their own scheduler into Cheddar (see section V for details).
- ...
- If the scheduler is preemptive or not. By default,
the scheduler is set to be preemptive.
- At the time we're speaking, the network field is not used (plan to be used in order
to simulate message scheduling).
- The quantum value associated to the scheduler.
This information is useful if a scheduler have to manage several
tasks with the same dynamic or static priority : in this case, the
simulator has to choose how to share the processor between these
tasks. The quantum is a bound on the delay a task can hold the processor
(if the quantum is equal to zero, there is no bound on the processor
holding time). At the time we're speaking, the quantum value can
e used with the POSIX 1003.1b scheduler (only with SCHED_RR tasks) and the round robin scheduler.
With POSIX 1003.1b, two SCHED_RR
tasks with the same priority level should share the processor with a
POSIX round-robin policy. In this case, the quantum value is the slot time
of this round-robin scheduler. Finally, the quantum value could
also be used for user-defined scheduler (see part V for details).
- The file name : it's the name of a file
which contains the source code of a user-defined scheduler
(see section V for details).
Warning : with Cheddar, to add a processor (or any
object), you have to push the Add button before pushing
the Close button. That's allow you to quickly define several
objects whithout closing the "Add" window (you should then
push Add for each defined object).
Figure 1.2 Adding an address space
The next step in order to run a simulation, is to define an address space.
Choose the "Edit/Add/Add an address space" submenu.
An address
space models a piece of memory which
can contain tasks, buffers or shared resources.
The Figure 1.2 shows the widget used to define such feature. At the time we speaking,
the only releveant information you have to provide is :
- A name. An address space name
can be any combinaison of literal characters including underscore.
Space is forbidden. Each address space name have
to be unique.
- A processor name. This is the processor which hosts the address space.
-
The fields related to memory size will be used in the next Cheddar's release in order to perform
a global memory analysis.
Figure 1.3 Adding a task
Let's see now how to define a task, the last feature required to perform
the most simpliest performance analysis. Choose the "Edit/Add/Add a task" submenu.
The window of Figure 1.3 is then displayed.
This window is composed of 3 sub-windows : the "main page", the "offset page"
and the "user's defined parameters page". The main page contains the following
information :
- At least, a task is defined by a name (the
task name should be unique), a capacity (bound on its execution
time) and a place to run it (a processor name). The other
parameters are optionnals but can be required for a particular scheduler
- A type of task . It describes the way the task
is activated. An aperiodic task is only activated once time. A
periodic task is activated many times and the delay between two
activations is a fixed one. A poisson process task is activated
many times and the delay between two activations is a random delay
: the random pattern used to generated these delays is an exponential one (poisson
process). If the task type is "user-defined", the task activation
pattern is defined by the user (see section
V.2 of this user's guide).
- The period. It is the time between two task
activations. The period is a constant delay for periodic task. It's
a average delay for poisson process task. If you selected a processor
that owns a Rate Monotonic or a Deadline Monotonic scheduler, you
have to give a period for each of its task
- A start time. It is the time when the task
arrives in the system (its first activation time).
- A deadline. The task must finish its activation
before its deadline. A deadline is a relative information : to get
the absolute date at which a task must end an activation, you should
add to the task deadline the time when the task was awoken/activated.
Warning : the deadline must be equal to the period if you
define a Rate Monotonic scheduler.
- A priority and a policy. These parameters
are dedicated to the POSIX 1003.1b/Highest Priority First scheduler. Priority
is the fixed priority of a task. Policy can be SCHED_RR,
SCHED_FIFO or SCHED_OTHERS and described how
the scheduler choose a task when several tasks have the same priority
level. Warning : the priority and the policy
are ignored by a Rate Monotonic and a Deadline Monotonic scheduler.
- A blocking time. It's a bound on shared resource
waiting time. This delay could be set by the user but could also
be computed by Cheddar if you described how shared resources are
accessed.
- An activation rule. The name of the rule which
defines the way the task should be activated. Only used with user-defined task.
(see section V for details).
- A seed . If you defined a poisson process task or
a user-defined task (also called "Parametric" task),
you can set here how random activation delay should be generated
(in a deterministic way or not). The "Seed" button proposes
you a randomly generated seed value but of course, you can give any seed
value. This seed value is used only if the Predictable button is pushed.
If the Unpredictable button is pushed instead, the seed is initialized at
simulation time with "gettimeofday".
The second and the third page stored task information which
are less used by user's.
The offsets page is made of a table. Each entry of the table
stores two information : an activation number and and value. The offset
page allows the user to change the wake up time of a task on a given
activation number. For each activation number stored in the "Activations:"
fields, the task wake up time will be delayed by the amount of time given
in the "Values" fields.
Finally, the third page (the "User's defined parameters" page)
contains task parameters (similar to the deadline, the period, he capacity
...) used by specific scheduler. The "User's defined parameters
page" is described in section V.5
.
Warning : when you create tasks, in most of cases, Cheddar does
not check if your task parameters are erronous according to the scheduler
you previously selected : these checks are done at task analysis/scheduling.
Of course, you can always change task and processor parameters with
"Edit/Update ", "Edit/Delete" and "Edit/Duplicate"
menus.
When tasks and processors are defined, we can start task
analysis. Cheddar provides two kind of analysis tools :
- Feasibility analysis tools : these tools computes
much information without scheduling the set of the tasks. Equation
references used to compute theses feasibility informations are
always provided with the results. Feasibility services are provided
for tasks and buffers.
- Simulation analysis tools : With these tools,
scheduling have to be computed first. When the scheduling is computing
(of course, this step can be long to proceed ...), the resulting
scheduling is drawn in the top part of the window and information
is computed and displayed in the bottom part of the window. Information
retrived there are only valid in the computed scheduling.The simpliest
tools provided by Cheddar checks if a set of tasks meet their temporal
constraints. Simulation services are also provided for other resources
(for buffers for instance).
All these tools can be called from the "Tools"
Menu and from some toolbar Buttons :
- From the submenu "Tools/Scheduling/Customized scheduling
simulation", the scheduling of each processor is drawn on
the top of the Cheddar main window (see below). From the drawn scheduling,
missed deadlines are shown and some statistics are displayed (number
of preemption for instance).
- From the submenu "Tools/Scheduling/Customize scheduling
feasibility", response time, base period and processor utilization
level are computed and displayed on the bottom of the Cheddar main
window (see Figure 1.4).
Figure 1.4 The Cheddar's main window
In the top part of this window, each resource, buffer, message and task is shown by a
time line :
- For a task time line :
- Each vertical red line means that the task is activated (waken
up) at this time.
- Each black rectangle means that the task is running at this time.
- For a resource time line :
- Each vertical blue line means that the resource is allocated by a
task at this time.
- Each black rectangle means that the resource is used by the task which
is running at this time.
- For a message time line :
each black rectangle means that the message is beeing transmitted by
the
network.
- For a buffer time line :
- Each write rectangle means that a task writes data into a buffer.
- Each red rectangle means that a task reads data from a buffer.
To get a summary of the tools provided by Cheddar, see
section VI .
In Cheddar, you will find several schedulers. Some of them are directly
implemented into the framework ; the others can be defined by the user.
This list above describes the currently built-in schedulers you may find in the 1.3p15
release :
- Rate Monotonic : run the task with the smallest period first.
- Deadline Monotonic : run the task with the smallest static deadline first.
- Earliest Deadline First : run the task with the smallest dynamic deadline first.
- Least Laxity First : run the task with the smallest laxity first.
- POSIX 1003.1b scheduler : run the task withthe highest fixed priority first. Support
SCHED_RR, SCHED_FIFO and SCHED_OTHERS policy. SCHED_OTHERS is a time sharing scheduler.
- Maximum Urgency First scheduler
[STE 91] : run the tasks according to a mixed static and dynamic priority.
- D-over dynamic scheduler
[KOR 92] : run the tasks as EDF but with a safe policy in case of transient
overload.
- Round robin scheduler : give the processor during a fixed delay to each task at a fixed order
(allows the use of a given quantum).
- Time sharing scheduler based on task waiting time (scheduler similar to the one
provided by Linux) : run the task which waits since the oldest date.
- Time sharing scheduler based on cpu usage : run the task which had consumed the least
cpu time.
The current Cheddar's release also provide some User-defined/parametric scheduler stores
in some .sc files (see project_examples sub-directory and section V). These schedulers are :
- arinc.sc : modeling of an ARINC 653 partition and task scheduler
- schedule_according_to_criticity.parametric-cpu.sc :
schedule tasks
according a task criticity level
- non_preemptive_llf.sc :
example of a LLF scheduler with no preemption when tasks have the
same laxity value
- ts.sc : the processor is given to the task which
ran the least frequently.
- fcfs.sc : first come/first served scheduling policy.
- short.sc : schedule the shortest task first
(with the smallest capacity)
- dvd0.parametric-cpu.sc : Dynamic value density scheduler of the York
University
[ALD 98].
- mllf.sc : Modified Least Laxity First scheduler with f=0.5
[OVE 97].
- muf.sc : Maximum Urgency First scheduler
[STE 91].
In the same way, Cheddar provides a set of built-in task arrival patterns. The built-in
task arrival patterns are :
- Aperiodic tasks : this kind of task arrives in the system at a given time
(the start time, see the "Add Task" widget), run a job and leaves the system.
- Periodic tasks : this kind of task periodically run a job. A periodic
task has a start time. The period field (see the "Add Task" wiget) stores the fixed delay
between two successive task woken up.
- Poisson process tasks : this kind of task periodically run a job. A periodic
task has a start time. The period field (see the "Add Task" wiget) stores the average delay
between two successive task woken up. The effective delay between two woken time is computed
with an exponential random
generator.
Some examples of User-defined/Parametric task provided with this Cheddar release can be found in the files :
- sporadic.sc : tasks are woken up with a minimal inter-woken up period delay. The miminum delay is stored
in the period field and the woken up delay is
randomly generated (exponential distribution).
- random_capacity.sc : task with a randomly generated capacity.
- increasing_capacity.sc : tasks with a growing capacity.
- activations.sc : various task models.
Figure 1.5 Scheduling options windows
The submenu "Tools/Scheduling/Options" allows
you to tune the way all next scheduling simulations will be done (see Figure 1.5) :
- If you push the "Offsets" button, the simulation
engine takes care of the task offsets given at task definition time :
task activations can be then delayed if you provided offset values at task
definition time.
- If you push the "Precedencies" button, task
scheduling will be done so that task precedencies will be meet. By
default, task precedencies are ignored.
- If you push the "Resource" button, access
to shared ressources will be done during simulation. By default, all shared
resources are ignored.
- Cheddar allows you to randomly activated tasks. If you want
to do simulations with this kind of task, the simulator engive have to compute
some random values. From this window, you can tune the way random activation
delays are generated.
A seed value can be associated for each task but you also can use only one seed for all
tasks. In the two cases, you can do "predictable" or "unpredictable" simulations.
If you choose "predictable" simulation, the seed will be initialized by a given value.
In the other case, the seed is initialized with "gettimeofday".
. Pushing
the "Predictable for all tasks" radio button leads to take the seed
value of the Option window during simulation for all tasks. If the "Task specific seed"
radio button is pushed instead, the seed of each task is used to generate
task activation delays. You should notice that by default, 0 is given to
the seed value, but of course, you can choose any value. Pushing the "Seed"
button give you a random value for the seed.
- The check button of the window on the right side allow the user to define with events
will be generated into the event table at simulation time (see section V).
Figure 1.6 Scheduling options windows (both feasibility and simulation)
The submenu "Tools/Scheduling/Scheduling simulation" allows
you to tune the way the next scheduling simulation
and the next feasibility test
will be done (see the Figure 1.6).
Options related to
which information the engine has to compute
when the scheduling sequence is built are :
- Pushing the "Schedule all processors" check button imply that the scheduling
simulation will be computed on all defined processors. If this button stay uncheck, the user
has to choose a given processor.
- Pushing the "Number of context switch" imply to compute the number
of context switch from the computed scheduling sequence.
- Pushing the "Number of preemption" imply to compute the number
of preemption from the computed scheduling sequence.
- Pushing the "Task response time " imply to compute the worst/best/average
task response time from the computed scheduling sequence.
- Pushing the "Blocking time" imply to compute the worst/best/average
task blocking time on shared resources from the computed scheduling sequence.
- Pushing the "Run event analyzers" will imply to perform,
on the computed scheduling sequence, the user-defined code (see section V).
- The Display event table, Automatically export event table and Event table file name
options are related to the computing scheduling sequence. These options allow you to save
in a file or display on the screen the computed scheduling in a XML format.
Options related to
which information the feasibility tests
will compute are :
- Pushing the "Feasibility on all processors" check button imply that the
feasibility tests
will be computed on all defined processors. If this button stay uncheck, the user
has to choose a given processor.
- Pushing the "Feasibility test based on the processor utilization factor"
will imply to compute such test.
- Pushing the "Feasibility test based on worst case task response time"
will imply to compute such test.
Information stored during a simulation can be saved into project
files. A project file is a XML file defined by this DTD. By the way, you do not need
a deep understanding of the layout of cheddar project files except if
you want to edit project files by hand. If so, you should check if your
project files are correctly structured by the tool xml2xml (xml2xml
just read, parses and displays to the screen the content of a XML Cheddar
project file).
All Cheddar XML files can be displayed with an Internet Browser if you
put in the directory hosting your XML Cheddar files the following
XSLT file and the following.CSS file. To do so, you should use a recent
release of Internet Explorer (version 6.0 or later), Netscape (version
7.0 or later) or Mozilla (version 1.0 or later).
From Cheddar, there is two ways to load a project file :
Saving a project can be done with the same "File"
menu.
Cheddar can also import AADL specification
[SAE 04].
This service can be accessed throught
the submenu "File/AADL/Import AADL".
In the same wat, an XML project can be exported towards an AADL
specification (see the "File/AADL/Export AADL" sub-menu).
As with XML files, you can launch Cheddar with an AADL file given from the command line. To launch Cheddar
and automatically read the foo.aadl AADL specification file, do :
my_shell$cheddar -a foo.aadl
This chapter describes services provided by Cheddar when the system
you want to study has task dependencies. By task dependencies, we mean resources
shared by several tasks (ex : semaphores) or precedencies relationship between
several tasks (due to buffer access or message exchange or also constraints
between the end of a task and the start of another one).
With Cheddar, you can define shared resources. Shared resources
can be seen as semaphores. They can be accessed by several tasks.
Tasks which require access to an already allocated semaphore are blocked
(and then, unscheduled). To define a shared resource in a Cheddar project,
call the submenu "Edit/Add/Add a resource". The window below
is then displayed :
Figure 3.1 Add a new shared reousrce
Before adding a shared resource, at least one processor
and one task must already exist in your project. A resource is defined
by the following information :
- An unique name.
- An initial value/state (simular to a semaphore initial
value). During a scheduling simulation, at a given time, if a resource
value is equal or less than zero, the requesting tasks are blocked up to the semaphore/shared resource
will be released. An initial value equal to 1 allows you to design
a shared resource which is initially free and which can be used by only
one task at a given time.
- A protocol. Currently, you can choose between PCP
(for Priority Ceiling Protocol),
PIP (for Priority Inheritance Protocol)
or "No protocol". With PCP or PIP, accessing shared resources may change
task priorities
[SHA 90].
The "No protocol"
just means that no task prioriy will be changed at shared resource access.
- A processor name. Each shared resource has to be hosted
by a given processor.
- Finally, we must give information on tasks who need
the resource. Tasks held resources in critical section.
Each critical section has to be defined by :
- The task name requering the shared resource.
- The start time of the critical section.
- The end time of the critical section.
Of course, you can define several critical sections for a given task of a given
shared resource.
By default, shared resources analysis tools are not included in the scheduling
simulation engine of Cheddar. See "Tools/Scheduling/Options"
if you want to take care of shared resources during scheduling simulation and if you want to display shared resources time line.
Blocking time on shared resources can be computed from scheduling simulation analysis if scheduling simulation is invoked from the sub-menu "Tools/Scheduling/Scheduling Simulation".
Finally, from the "Tools/Resources/Blocking time" sub-menu, you will find services
to compute bounds on blocking time of each tasks. These bounds
are computed without assumption on the scheduling actually generated for the
analyzed system. To compute blocking time bound, shared resources have
to used PCP or PIP protocols.
With Cheddar, dependencies are links between at least two
tasks. There are three different types of dependencies : precedencies,
message and buffer dependencies. Precendencies express order constraints
between end or begin of task execution. Message dependencies expressed
relationship between a sender and a receiver task of a given message.
Buffer dependencies expressed relationship between producer and consumer
of data in a given buffer.
To create a dependency, you may open the "Precedencies Graph" window by
clicking on the appropriate icon on the main Cheddar window. By the
way, all Cheddar objects are imported in this window. All items are
represented by geometrical forms :
- Tasks -> circles.
- Messages -> rectangles.
- Buffers -> enveloppes.
If there is no item on the canvas, it means that you don't have created
any dependency before... So you can create one by entering in "create"
mode (on the top left corner) and selecting 'task' on the radio button.
After that, create a task by clicking on the canvas, then a popup
appears asking you to create a new or import one task from Cheddar
(cf Part One). It's the same succession
of operations to create others types of items (buffers and messages).
Figure 3.2 Add a new task dependency
To create a dependencie, enter in "create" mode and select
'arrow' on the radio button. Then click on a first item. This item
is the source of the dependencie. Click on a second item which will
be the destination of the dependencie. You can see a screenshot of
such a dependencie between a task and and a buffer below :
Figure 3.3 Delete a task dependency
If you want to delete nodes or arrows, enter in "delete"
mode and select the object (task, arrow, message, buffer) on the
radio button. Click on a task, buffer or message should delete all
dependencies of the clicked object. Delete operations do not delete
tasks, buffers and messages from the project files but only dependencies
of the project. To delete tasks, messages or buffers from the
project, choose the "Edit/Delete" submenu.
With precedencies, some simple scheduling tools are provided.
See the submenu "Tools/Precedencies"
End to end response time includes
message transmission delay and buffer memorisation delay.
Cheddar allows you to define buffers shared by tasks. If
you want to define a buffer, a processor and a least one task have
to be defined before. At the time we writte this user's guide, buffers
can only shared by periodics or aperiodicss tasks. Buffers are defined
as follow :
- A buffer has a unique name, a size
and is hosted by a processor.
- Two type of tasks can access a buffer : producer
and consumer . We suppose that a producer/consumer
writes/reads a fixed size of information in the buffer each time
the task is activated (in the periodic case for instance). For each
producer or consumer, the size of the information produced or consummed
have to be defined.
A buffer can be added to a Cheddar project with the submenu
"Edit/Add/Add a buffer". The window below is then displayed :
Figure 3.4 Add anew buffer
Like tasks, from a buffer, two kind of tools can be invoke
by the user : simulation and feasibility tools. At
first, simulation of the task scheduling can help the user to see
how the buffer is filled or not with messages (see "Tools/Buffer/Buffer
simulation" submenu). In this case, a scheduling simulation must
be previously run. The result is then displayed in a window as below
:
Buffer Feasibility mainly consits to
compute buffer bounds. Bounds computed here suppose that each task
which is defined as "procuder", produces one message per periodic
activation. In the same manner, each "consumer" extracts one message
during each of its periodic activation.
Figure 3.5 Display buffer utilization factor computed from scheduling simulation
The picture contains for each time the buffer utilization
level.
Second, the feasibility tool provides a way to compute
bound on buffer utilization level. At the time we write this User's
guide, bounds do not depend on the type of the scheduler. Bounds
can be computed from the "Tools/Buffer/Buffer feasibility"
submenu.
Multiprocessor system is good for heavy computing demands.
It is sometimes only way to provide sufficient processing
power to meet critical real-time deadlines. Multiprocessor
systems are also in generally more reliable than uni-processor systems.
Scheduling of multiprocessor systems is proven to be NP-hard
(Non-deterministic Polynomial-time) problem [LEU 82].
The complexity class NP is the set of decision problems that
can be solved by a non-deterministic machine in polynomial time.
Complexity theory is part of the theory of computation dealing
with the resources required during computation to solve a given problem.
The most common resources are time (how many steps does it
take to solve a problem) and space (how much memory does it take
to solve a problem). Other resources can also be considered,
such as how many parallel processors are needed to solve a problem in parallel.
There are many scheduling heuristics to solve this.
Rate-monotonic scheduling is good for numerous reasons: Rate-monotonic
algorithm is optimal for fixed priority assignment
of periodic tasks on a processor so it’s easy to design predictable real-time system.
Also it’s easy to implement and takes minimal scheduling overhead.
Cheddar has four algorithms: RMNF, RMFF, RMBF, RMST and RMGT.
Each of these are off-line schemes, so
entire task set must be known before starting task assignment.
Bounds for functions are calculated using
.
Rate-Monotonic-Next-Fit [SON 93]
Upper bound for this algorithm is 2.67. Tasks are sorted non-decreasing order of
periods. Then tasks
are placed on processors, according to Condition IP(Increasing Period).
First task is placed on first processor. Then second task is placed on first
processor, if it meets
Condition IP. Otherwise it is placed on new processor. This continues until
all tasks are scheduled.
Rate-Monotonic-First-Fit [SON 93]
Upper bound for this algorithm is 2.33 [SON 93]
(original study by
Liu and Dhall had wrong bound of 2.23). Tasks are sorted non-decreasing
order of periods. Condition IP is used to verify schedulability of
tasks on processors.
First task is placed on first processor. Then second task is placed
on first processor, if it meets Condition IP. Otherwise it is placed on
new processor. Third task is tried to place on first processor according
to Condition IP. If it does not meet condition, task is tried to place on
second processor. Otherwise new processor is selected for third task…
Rate-Monotonic-Best-Fit [SON 93]
Upper bound for this algorithm is 2.33. Tasks are sorted non-decreasing
order of periods.
First task is placed on first processor. For second task, function
checks all processors, if it meets Condition IP. For processors which
satisfy condition, it checks
where kj is number of tasks already assigned to processor and Uj
total utilization of the kj tasks. And task is assigned to processor
which has smallest value. If condition is not met, new processor is
selected for task.
Rate-Monotonic Small-Tasks [BUR 94]
Upper bound for RMST is
, a = max Ui, i = 1,…,K and U is
utilization of all tasks. Tasks are sorted increasing Si. Si =
log2(Ti). Main idea of RMST is to minimize value of
b for each processor. ß = max Si – min Si, 1= i =K.
Rate-Monotonic General-Tasks [BUR 94]
Upper bound for RMGT is 1.75.
RMGT uses RMST algorithm for task s = 1/3 and First-Fit heuristics for rest of tasks.
Example of use :
- First, Define processors for tasks. They have to be Rate Monotonic type.
- Second, Define tasks (host tasks on any processors)
- Third, compute partitioning :
Usual feasibility tests are limited to only few task models (mainly periodic tasks) and
to only few schedulers.
When an application
built with a particular task activation pattern or scheduled with a particular scheduler
has to be checked, feasibility tests are not necessarily available.
In this case, the only solution consists in
analyzing the scheduling simulation.
Cheddar allows the user to design and easily
build framework extensions to do simulation
of user-defined schedulers or task activation patterns.
By easy, we mean quickly write and test
framework extensions without a deep understanding of the
framework design and of the Ada language.
We propose the use of a simple language
to describe framework extensions.
Framework extensions
are interpreted at simulation time.
As a consequence, they can be changed
and tested
without recompiling the framework itself.
Figure 5.1 How an user-defined code is run by the
scheduling engine
Figure 5.1 gives an idea on the way the simulation engine is implemented
in the framework.
Running a simulation with Cheddar is a three-steps
process.
The
first step consists in
computing the
scheduling : we have to
decide for each unit of time
which events occur.
Events
can be allocating/releasing shared resources,
writing/reading buffers, sending/receiving
messages and of course running a task at a given
time.
At the end of this step, a table
is built which stores all the generated events.
The event table is built according to the XML description
file of the studied application and according to
a set of task activation patterns and schedulers.
Usual task activation patterns and
schedulers are predefined in the Cheddar framework
but users can add their own schedulers and task
activation patterns.
In the second step, the analysis of the event
table is performed.
The table is scanned by "event analyzers"
to find properties on the studied system.
At this step, some standard information can be
extracted by predefined event analyzers
(worst/best/average blocking time, missed deadlines ..)
but users can also define their own
event analyzers to
look for ad-hoc properties (ex : synchronization constraints between two
tasks, shared resources access order, ...).
The results produced during this step are XML formatted
and can be exported towards other programs.
Finally, the last step consists in displaying
XML results in the Cheddar main window (see Figure
1.4).
Now, let's see how user-defined schedulers
or task activation patterns can
be added into the framework.
Basically, all tasks are stored in a set of arrays.
Each array stores for all tasks a given information (ex : deadline, capacity,
start time, ...).
The job of a scheduler is to find a task
to run from a set of ready tasks. To achieve this job, Cheddar models a
scheduler with a 3 stages pipe-line which is similar to the POSIX.1003b scheduler (see [GAL 95]). These 3 stages are
:
- The priority stage. For each ready task, a priority is computed.
-
The queueing stage. Ready tasks are inserted into different queues. There is one queue per priority level. Each queue contains all the ready tasks with the same priority value.
Queues are managed like POSIX scheduling queues : if a quantum is associated to the scheduler, queues
work like the SCHED_RR scheduling queueing policy.
Otherwise, the SCHED_FIFO queueing policy is applied.
- The election stage. The scheduler looks for the non empty queue with the highest priority level and allocates the processor to the task at the head of this queue. The elected task keeps the processor during one unit of time if the designed scheduler is preemptive or during all its capacity if the scheduler is not preemptive.
Defining a new scheduler is simply giving piece of code
for some of the pipe-line stages we described above. Each of these
stages can be defined by a user without the need to have a deep kwonledge
of the way the scheduling simulator works.
User-defined schedulers are stored in text files. These files are
organized in several sections :
- The start section. In this section, you may declare
variables needed to schedule your tasks. Many variables are already predefined
in Cheddar.
Some of them are those defined at task/processor/buffer/message definition (ex : period, deadline,
capacity ...).
This set of predefined variables can be extended
with the "Edit/Add/Add a Task" submenu (see user-defined parameters).
The others are managed by the
simulator engine and describes the state of tasks/processors/buffers/messages
at simulation time.
See section V.5 for a list of all predefined
variables.
All variables used in a scheduler should have a type. The framework provides
two type families : scalar types
and arrays.
One can define variable with scalar type of double,
integer, boolean, string and also of random (a random
is a type which
allows the user to generate ramdom values).
An array is a type which stores one scalar data per task, message, buffer or shared resource.
Arrays are declared as usual Ada Table.
Vectorial operations can be done on this kind of variable.
- The priority section.
The section contains the code necessary to compute task priorities.
The code given here is called
each time a scheduling decision have to be token (at each unit of time
for preemptive scheduler and when a task run during all his capacity
for non preemptive scheduler). The code given here can be composed of
many differents statements described in section V.5
- The election section. This section just decides
which task should receive the processor for next units of time.
This section should only contain one return statement.
- The task activation section. This section describes how
tasks could be activated during a simulation. In Cheddar, 3 kinds of tasks
exists : aperiodic tasks which are activated only one time and periodic
or poissons process tasks which are activated several times. In the case
of periodic tasks, two successive task activations are delayed by an amount
of fixed time called period. In the case of poisson process tasks, two
successive task activations are delayed by a exponential random delay.
The task activation section allows you to define new kinds of task activation patterns
(ex : sporadic task, randomly activated task, burst of activations, ...). .
In the sequel, we first give you some simple examples of user-defined
schedulers. Then, we explain how to use this kind of scheduler to do scheduling
simulation with Cheddar. The list of statements and the list of predefined
variables is given at the end of this section.
In this section, we give some user-defined scheduler examples. We first show
that user-defined scheduler can be built with two kinds of statements : high-level
and low-level statements. Second, we present how to add new task parameters
with User's defined task parameters.
Let's see now some very simple user-defined schedulers.
The most simple user-defined scheduler can be defined like below :
election_section:
return min_to_index(tasks.period);
|
Figure 5.2 a simple Rate Monotonic scheduler
This first example shows you how to give the processor to
the task with the smallest period. This scheduler is equivalent to
the Rate monotonic implemented into Cheddar. tasks.period is a predefined
variable initialized at task definition time by the user. To implement
a Rate Monotonic scheduler, no dynamic priorities are computed and no variable
is necessary. Then, the scheduler designer does not have to redefine the start
and priority sections. The only section which is defined is the election
one. The election section contains an unique return statement
to inform the scheduling simulator engine which task should be run for the
next unit of time. The return statement uses the high level min_to_index
operator. This operator scans the task array to find the ready task with
the minimum value for the variable tasks.period. In Cheddar,
the scheduler designer can use two kinds of statements : high-level and low-level
statements. High level statements like min_to_index, hides the data
type organization of the scheduling simulator engine. For example, the scheduler
designer do not need to give statement into its user-defined scheduler to scan
manually the task array. Writting scheduler with high-level statements is
then an easy work. At contrary, low-level statements assume that the user
have a deeper idea of the design of the scheduling engine simulator. By the
way, these statements are sometimes necessary when the scheduler designer
want code a too much specific scheduler.
Let's see now how to define an EDF like scheduler :
1 start_section:
2 dynamic_priority : array (tasks_range) of integer;
3
4
5 priority_section:
6 dynamic_priority := tasks.start_time + tasks.deadline
7 + ((tasks.activation_number-1)*tasks.period);
8
9 election_section:
10 return min_to_index(dynamic_priority);
|
Figure 5.3 an EDF like scheduler using vectorial operators
EDF is a dynamic scheduler which computes a dynamic priority for
each task. This dynamic priority is in fact a deadline. EDF just gives the
processor to the task with the shortest deadline. In our example, this deadline
is stored in a variable called dynamic_deadline. Since we need one
value per task, the type of this variable is integer array.
With this example the priority_section is not empty any more and
contains (lines 5 to 7) the necessary code to compute EDF dynamic priorities.
You should notice that the code in line 6/7 is in fact a vectorial operation
: the arithmetic operation to compute the deadline is done for each item
of the table dynamic_priority ranging from 1 to nb_tasks (nb_tasks
is a static predefined variable initialized by the number of tasks in the
current processor). To compute the dynamic priorities of our example,
we used many predefined variables :
- tasks.deadline, tasks.start_time and tasks.period : it's the deadline, start
time and period values given by the user at task definition time (in
the window Edit/Add/Add a task).
- tasks.activation_number : it's a variable updated by the
simulation engine. The simulator increments this variable each time a periodic
or a poisson process task start a new activation. For instance, if tasks.activation_number(i)
is equal to 3, it means that the task i has started its 4th activation.
You can find in V.5 a
list of all predefined variables and all available statements you can
used to build your user-defined scheduler.
The example of the Figure 5.3 is built with vectorial operators : each arithmetic
operation is done for all tak of the system. The scheduler designer do not
need to take care of the task array and just give rules to computed the EDF
dynamic deadline. As max_to_index/min_to_index, these statements
are High-level ones because they do not required to directly access to the
data type organization of the scheduling engine of Cheddar (mainly the
task arrays).
Now, let's see a third example:
start_section:
to_run : integer;
current_priority : integer;
priority_section:
current_priority:=0;
for i in tasks_range loop
if (tasks.ready(i) = true) and (tasks.priority(i)>current_priority)
then to_run:=i;
current_priority:=tasks.priority(i);
end if;
end loop;
election_section:
return to_run;
|
Figure 5.4 Building an user-defined with low-level statement
This scheduler looks for the highest priority ready task of a processor
and is fully equivalent to the scheduler described by :
election_section:
return max_to_index(tasks.priority);
|
Figure 5.5 a HPF scheduler built with hight-level statements
but, in the example of Figure 5.4, the code scans itself the
task array to find a ready task to run. To achieve this, the example of
Figure 5.4 built with low-level instructions : a for loop and
an if statement. The priority_section is then composed of a
loop which do a test on each task entry entry. This loop is made with a
for statement, a loop which run the inner statement for each
task defined in the task array. To contrary to a high-level implementation,
a scheduler made of low-level statements have to do more tests. For instance,
the example of the Figure 5.4 checks with the ready dynamic variable
if tasks are ready at the time the scheduler is called. Low-level scheduler
are then more complicated and more difficult to test. The reader will find
in section V.3 some tips
to help testing of complicated user-defined scheduler.
In the previous examples, data used to built user-defined scheduler was
either static variable initialized at task definition time, either dynamic
variables predefined or declared in the start section. A last type
of data exists in Cheddar : User's defined task parameters. This kinds of
data are static ones and are defined at task definition time. User's defined
task parameters allow the user to extend the set of static variables. Since
they describe new task parameters, User's defined task parameters are table
type. Today, User's defined task parameters can be boolean, integer, double
or string table type. To define User's defined task parameters, you have
to update the second page of the book displayed from the submenus "Edit/Add/Add
a task" or "Edit/Update/Update a task" :
Figure 5.6 Adding an Users's Defined Task Parameter
The example above shows you a system composed of 3 tasks (T1, T2 and
T3) where a criticity level is defined. Like usual task parameters, you should
give a value to an User's defined task parameter (ex : the criticity level
for task T1 is 1) but you also have to set a type to the parameter (integer
in our example). When tasks are created, as usually, you can call the scheduling
simulation services of Cheddar. The next window is a snapshoot of the resulting
scheduling of our example composed of 3 tasks scheduled according to their
criticity level. (T2 is the most critical task and T1 the less critical).
Figure 5.7 Scheduling according to a criticity level.
To conclude this chapter, let's take a look on a more complex example of
user-defined scheduler which summarises all the features presented before.
This example is an ARINC 653 scheduler
(see [ARI 97]). An ARINC 653 system is composed of several partitions.
A partition is an unit of software and is itself composed of processes
and memory spaces. A processor can hosts several partitions so that two
levels of scheduling exists in an ARINC653 system : partition scheduling
and process scheduling.
- Process scheduling. In one partition, process are scheduled
according to their fixed priority. The scheduler is preemptive and always
gives the processor to the highiest fixed priority task of the partition which
is ready to run. When several tasks of a partition has the same priority
level, the oldest one is elected.
- Partition scheduling. Partitions share the processor in a
predefined way. On each processor partitions are activated according to
an activation table. This table is built at design time and defines a
cycle of partition scheduling. The table describes for each partition when
it has to be activated and how much time it has to run for each of its activation.
Figure 5.8 An example of ARINC 653 scheduling
The Figure 5.8 displays an example of ARINC 653 scheduling
(see the XML project file project_examples/arinc653.xml). The studied
system is made of 3 tasks hosted by one processor. The processor owns 2 partitions
: partition number P0 and partition number P1. The task T1 run into the partition
P0 and the two others run into the partition P1. Each task has a fixed priority
level : the T1 priority is 1, the T2 priority is 5 and the T3 priority is
4. The cyclic partition scheduling should be done so that P0 run before P1.
In each cycle, P0 should be run during 2 units of time and P1 should run during
4 units of time. The user-defined scheduler source code
used to
compute the scheduling displayed in Figure 7 is given bellow :
start_section:
partition_duration : array (tasks_range) of integer;
dynamic_priority : array (tasks_range) of integer;
number_of_partition : integer :=2;
current_partition : integer :=0;
time_partition : integer :=0;
i : integer;
partition_duration(0):=2;
partition_duration(1):=4;
time_partition:=partition_duration(current_partition);
priority_section:
if time_partition=0
then current_partition:=(current_partition+1)
mod number_of_partition;
time_partition:=partition_duration(current_partition);
end if;
for i in tasks_range loop
if tasks.task_partition(i)=current_partition
then dynamic_priority(i]:=priority(i);
else dynamic_priority(i):=0; tasks.ready(i):=false;
end if;
end loop;
time_partition:=time_partition-1;
election_section:
return max_to_index(dynamic_priority);
|
Figure 5.9 Processes and partitions scheduling into an ARINC
653 system
In this code, tasks.task_partition is an User's defined task
parameter. tasks.task_partition stores the partition number hosting the
associated task. The variable partition_duration stores the partition
cyclic activation table.
In the same way you can define specific schedulers,
you can also define specific task activation patterns. By default, 3 kinds of
task activation pattern are defined in Cheddar :
- Periodic task : a fixed amount of time exists between two successive
task activations.
- Aperiodic task : the task is activated only one time at a given time.
- Poisson process task : tasks are activated several times and the
delay between two successive activations is a random delay. The static variable
period in this case is the average time between two successive activations.
Delay between activations are generetad according to a random poisson process.
If the application you want to study can not be modeled with this 3
kinds of activation rules above, a possible solution is to explain your own
task activation pattern with a user-defined scheduler. The description of task
activation pattern is done in .sc files in a particulary section which
is called task_activation_section. In this section, you can define
named activation rules with set statements. The set statement just
link a name/identifier (the left part ot the set statement) and an expression
(the right part of the set statement). The expression explained the amount
of time the scheduling simulator engine have to wait between two activation
of a given task.
start_section:
gen1 : random;
gen2 : random;
exponential(gen1, 200);
uniform(gen2, 0, 100);
election_section:
return max_to_index(tasks.priority);
task_activation_section:
set activation_rule1 10;
set activation_rule2 2*tasks.capacity;
set activation_rule3 gen1*20;
set activation_rule4 gen2;
|
Figure 5.10 Defining new task activation patterns : how to run simulation
with specific task models
The example of the Figure 5.10 describes a Highiest Priority First scheduler
which hosts tasks activated with different patterns. Each pattern is described by
a set statement :
- The pattern activation_rule1 describes periodic
tasks
with a period equal to 10.
- The pattern activation_rule2 describes
periodic tasks
with a period equal to twice time their capacity.
- The pattern activation_rule3 describes randomly activated tasks.
Two successive activations are delayed by a amount of time which is randomly
computed. Delays are computed according to a random exponential distribution
pattern with a mean value of 400. 400 is then the average period value
of the tasks. The seed used during random delay generation
depend of the scheduling options set at simulation time
(see section I.3 ) : the user can choose to associate a seed
per task or a seed for all the tasks. Seeds can be initialized in a predictable way
or in an unpredictable way.
In the case of a predictable seed, the random generator is initialized with the
seed value given at task definition time or in the scheduling option window.
In the case of a unpredictable seed, the seed
is initialized by the "gettimeofday" at simulation time.
- The pattern activation_rule4 describes randomly activated tasks.
Two successive activations are delayed by a amount of time which is randomly
computed. Delays are computed according to a random uniform distribution pattern
with a mean value of 50. At each periodic task
activation, the period can has a value between 0 and 100.
The seed used during random delay generation is
managed in the same way that activation_rule3.
When task activation rules are defined, task activation names (ex :
activation_rule1) have to be associated to "real" task. The picture
bellow shows you an "Edit/Add/Add a task" window :
Figure 5.11 Assigning activation rules to tasks
In this example (see the XML project file project_examples/test_user-defined6.xml),
the task activation rule activation_rule1 is
associated to the task T1. The task activation rule activation_rule2 is
associated to the task T2.
The task activation rule activation_rule3 is
associated to the tasks T3, T4 and T5.
Finally, the task activation rule activation_rule4 is
associated to the tasks T6, T7 and T8.
Then,
a possible resulting scheduling can be :
Figure 5.12 Scheduling tasks with user-defined task activation pattern
Let's see how to run a simulation with one or several user-defined
schedulers. First, you have to add a scheduler by selecting the submenu
"Edit/Add/Add a processor". The following window is then launched :
Figure 5.13 Define a processor with an user-defined scheduler
To add a user-defined scheduler into a Cheddar project, select the right
item of the Combo Box and give a name to your scheduler. You should then
provide the code of your user-defined scheduler. This operation can be done
either by pushing the "Read"
button.
In this case, the following window is spawned and you should
give a file name containing the code of your user-defined scheduler :
Figure 5.14 Selecting the .sc file which contains the user-defined scheduler
By convention, files which contain user-defined scheduler code should
be prefixed by .sc. For example, the file rm.sc in our
example should almost contains an election section and of course,
can also contains a start and a priority sections.
When a processor is defined, you have to add tasks on it. To
do so, select the submenu "Edit/Add/Add a task" like in section
I. Just place the task on the previously defined processor. Finally,
you can run scheduling simulations as the usual case.
Since an user-defined scheduler is also a piece of code, you sometimes
need to debug it. To do so, you can use the following tips :
- First, a special instruction can be used to display at the screen
the value of a variable : the put statement. For instance,
running the following user-defined code will display each time the scheduler
is called, the value of the dynamic variable to_run :
--!TRACE
start_section:
to_run : integer;
current_priority : integer;
priority_section:
current_priority:=0;
for i in tasks_range loop
if (tasks.ready(i)=true) and (tasks.priority(i)>current_priority)
then to_run:=i;
put(to_run);
current_priority:=tasks.priority(i);
end if;
end loop;
election_section:
return to_run;
|
Figure 5.15 Using the put statement
- A second tip can help you to test if the syntax of your user-defined
scheduler is correct. In all .sc file, you can add else where the
line --!TRACE. If you add this line, the parser will give
extra information during the syntax analysis of your user-defined scheduler.
It's usefull if you want to test a .sc file before using it in
a Cheddar project file. You can also test it with sc, a program designed
to read, parse and check .sc files.
start_section:
i : integer;
nb_T2 : integer;
nb_T1 : integer;
bound_on_jitter : integer;
max_delay : integer;
min_delay : integer;
tmp : integer;
T1_end_time : array (time_units_range) of integer;
T2_end_time : array (time_units_range) of integer;
min_delay:=integer'last;
max_delay:=integer'first;
i:=0;
nb_T1:=0; nb_T2:=0;
gather_event_analyzer_section:
if (events.type = "end_of_task_capacity")
then
if (events.task_name = "T1")
then
T1_end_time(nb_T1):=events.time;
nb_T1:=nb_T1+1;
end if;
if (events.task_name = "T2")
then
T2_end_time(nb_T2):=events.time;
nb_T2:=nb_T2+1;
end if;
end if;
display_event_analyzer_section:
while (i < nb_T1) and (i < nb_T2) loop
tmp:=abs(T1_end_time(i)-T2_end_time(i));
min_delay:=min(tmp, min_delay);
max_delay:=max(tmp, max_delay);
i:=i+1;
end loop;
bound_on_jitter:=abs(max_delay-min_delay);
put(min_delay);
put(max_delay);
put(bound_on_jitter);
|
Figure 5.16 Example of user-defined event analyzer : computing task termination jitter bound
In the same way that users can define new
schedulers, Cheddar makes it
possible to create user-defined event analyzers.
These event analyzers are also
written with an Ada-like language and interpreted
at simulation time.
The event table produced by the simulator records
events related to task execution and related
to objects
that tasks access.
Event examples stored in this table can be :
- Events produced when
a task becomes ready to run
(event task_activation),
when a task starts
or ends
running its capacity
(events start_of_task_capacity
and end_of_task_capacity),
- Events produced when a task reads or
writes data from/to
a buffer (events
write_to_buffer and
read_from_buffer),
- Events produced when a task sends or receives
a message
(events
send_message and
receive_message),
- Events produced when a task starts waiting for a
busy resource
(event wait_for_a_resource),
allocates or releases a given
resource (events allocate_resource
and release_resource).
Each of these events is stored with the time
it occurs and with information related to the event
itself (eg. name of the resource, of the buffer,
of the message, of the task ...).
The event table is scanned sequentially
by event analyzers. User-defined event analyzers
are composed of several sections : a start
section,
a data gathering section and an analyze and display
section.
- As user-defined schedulers, the
start section is devoted to variable declarations
and initializations.
- The gathering section contains
code which is called for each
item of the event table. Most of the time, this section
contains statements which extract usefull
data
from the event table,
and store them for the event analyzer.
- Finally, the display section performs
analysis on data previously saved
by the gathering section and
displays the results in the
main window of the Cheddar Editor.
Figure \ref{jitter} gives an example of user-defined
event analyzer. From
an ARINC 653 scheduling
this event analyzer computes
the minimum, the maximum and the
jitter on the delay between
end times of two tasks owned by different
partitions
(tasks T1_P0 and
T2_P1 ; see Figure
\ref{arinc_simulation}).
The tables bellow list all predefined variables which are
available when
you write an user-defined code:
Name |
Type
|
Is updated by the simulator engine |
Can be changed by user code |
Meaning
|
Variables related to processors |
nb_processors
|
integer
|
no
|
no
|
Given the number of processors of the current analyzed system.
|
Variables related to tasks |
tasks.period
|
array (tasks_range) of integer
|
yes
| yes
|
Store the value of the parameter given
at task definition time. For the meaning of this variable, see section
I.
|
tasks.name
|
array (tasks_range) of string
|
no
|
no
|
Name of the task
|
tasks.processor_name
|
array (tasks_range) of string
|
no
|
no
|
Store the processor name of the cpu hosting the corresponding task.
|
tasks.blocking_time
|
array (tasks_range) of integer
|
no
|
yes
|
Store the sum of the bounded time the task has to wait on shared resources accesses.
|
tasks.deadline
|
array (tasks_range) of integer
|
yes
|
yes
|
Store the value of the parameter given
at task definition time. For the meaning of this variable, see section
I.
|
tasks.capacity
|
array (tasks_range) of integer
|
yes
|
yes
|
Store the value of the parameter given
at task definition time. For the meaning of this variable, see section
I.
|
tasks.start_time
|
array (tasks_range) of integer
|
yes
|
yes
|
Store the value of the parameter given
at task definition time. For the meaning of this variable, see section
I.
|
tasks.used_cpu
|
array (tasks_range) of integer
|
yes
|
no
|
Store the amount of processor time
wasted by the associated task.
|
tasks.activation_number
|
array (tasks_range) of integer
|
yes
|
no
|
Store the activation number of the
associated task. Of course, using this
variable is meaningless for aperiodic tasks.
|
tasks.jitter
|
array (tasks_range) of integer
|
yes
|
yes
|
Store the value of the parameter given
at task definition time. For the meaning of this variable, see section
I.
|
tasks.priority
|
array (tasks_range) of integer
|
yes
|
yes
|
Store the value of the parameter given
at task definition time. For the meaning of this variable, see section
I.
|
tasks.rest_of_capacity
|
array (tasks_range) of integer
|
yes
|
no
|
For each task activation, this variable
is reseted to the capacity each
time the associated task starts a new activation. If rest_of_capacity
is equal to zero, the task had over its current activation and then,
is blocked.
|
nb_tasks
|
integer
|
no
|
no
|
Given the number of tasks of the current
analyzed system.
|
tasks.ready
|
array (tasks_range) of boolean
|
yes
|
no
|
Store the state of the task : this
boolean is true if the task is ready ; it means the task has a capacity
to run, does not wait for a shared resource, does not wait for a delay,
does not wait for a offset constraint and does not wait for a precedency
constraint.
|
Variables related to messages |
nb_messages
|
integer
|
no
|
no
|
Gives the number of messages of the current analyzed system.
|
messages.name
|
array (messages_range) of string
|
no
|
no
|
Gives the names of each message.
|
messages.jitter
|
array (messages_range) of integer
|
no
|
no
|
Jitter on the time the periodic message becomes ready to be sent.
|
messages.period
|
array (messages_range) of integer
|
no
|
no
|
Gives the sending period if the message is a periodic one.
|
messages.delay
|
array (messages_range) of integer
|
no
|
no
|
time needed by a message to go from the sendrer to the receiver node.
|
messages.deadline
|
array (messages_range) of integer
|
no
|
no
|
Stores the deadline is the message has to meet one.
|
messages.size
|
array (messages_range) of integer
|
no
|
no
|
Stores the size of the message.
|
messages.users.time
|
array (messages_range) of integer
|
no
|
no
|
Stores the time when the task should send or receive the message.
|
messages.users.task_name
|
array (messages_range) of string
|
no
|
no
|
Stores the task name which sends/receives the message.
|
messages.users.type
|
array (messages_range) of string
|
no
|
no
|
Stores sender if the corresponding task sends the message or stores receiver if the task receives it.
|
Variables related to buffers |
nb_buffers
|
integer
|
no
|
no
|
Gives the number of buffers of the current analyzed system.
|
buffers.max_size
|
array (buffers_range) of integer
|
no
|
no
|
The maximum size of a given buffer.
|
buffers.processor_name
|
array (buffers_range) of string
|
no
|
no
|
Gives the processor name which owns the buffer.
|
buffers.name
|
array (buffers_range) of string
|
no
|
no
|
Unique name of the buffer.
|
buffers.users.time
|
array (buffers_range) of integer
|
no
|
no
|
Store the time a given task consumes/produces a message from/into a buffer.
|
buffers.users.size
|
array (buffers_range) of integer
|
no
|
no
|
Store the size of the message produced/consumed into/from a buffer by a given task.
|
buffers.users.task_name
|
array (buffers_range) of string
|
no
|
no
|
Store the task name which procudes/consumes messages into/from a given buffer.
|
buffers.users.type
|
array (buffers_range) of string
|
no
|
no
|
Stores consumer if the corresponding task consumes messages from the buffer or stores producer if the task produces messages.
|
Variables related to shared resources |
nb_resources
|
integer
|
no
|
no
|
Gives the number of shared resources of the current analyzed system.
|
resources.initial_state
|
array (resources_range) of integer
|
no
|
no
|
Stores the state of the resource when the simulation is started. If this integer is equal of less than zero, the first allocation request will block the requesting task.
|
resources.current_state
|
array (resources_range) of integer
|
no
|
no
|
Stores the current state of the resource.
If this integer is equal of less than zero, the first allocation request will block the requesting task. After an allocation of the resource, this counter is
decremented. After the task release the resource, this counter is incremented.
|
resources.processor_name
|
array (resources_range) of string
|
no
|
no
|
Stores the name of the processors hosting the shared resource.
|
resources.protocol
|
array (resources_range) of string
|
no
|
no
|
Contains the protocol name used to manage the resource allocation request. Could be either no_protocol, priority_ceiling_protocol or priority_inheritance_protocol
|
resources.name
|
array (resources_range) of integer
|
no
|
no
|
Unique name of the shared resource
|
resources.users.task_name
|
array (resources_range) of string
|
no
|
no
|
Gives the name of a task which can access the shared resource.
|
resources.users.start_time
|
array (resources_range) of integer
|
no
|
no
|
Gives the time the task starts accessing the shared resource during its capacity.
|
resources.users.end_time
|
array (resources_range) of integer
|
no
|
no
|
Gives the time the task ends accessing the shared resource during its capacity.
|
Variables related to the scheduling simulation |
previously_elected
|
integer
|
yes
|
no
|
At the time the user-defined scheduler
run,
this variable store the TCB index of the task elected at the
previous simulation time
|
simulation_time
|
integer
|
yes
|
no
|
Store the current simulation time .
|
Variables related to the event table |
events.type
|
string
|
no
|
no
|
Type of event on the current index table. Can be task_activation, running_task, write_to_buffer, read_from_buffer, send_message, receive_message, start_of_task_capacity, end_of_task_capacity, allocate_resource, release_resource, wait_for_resource.
|
events.time
|
integer
|
no
|
no
|
The time when the event occurs.
|
events.processor_name
|
string
|
no
|
no
|
The processor name hosting the task/resource/buffer related to the current event.
|
events.task_name
|
string
|
no
|
no
|
The task name related to the current event.
|
events.message_name
|
string
|
no
|
no
|
The message name related to the current event.
|
events.buffer_name
|
string
|
no
|
no
|
The buffer name related to the current event.
|
events.resource_name
|
string
|
no
|
no
|
The resource name related to the current event.
|
The BNF syntax of a .sc file is given bellow :
entry := start_rule priority_rule election_rule
task_activation_rule gather_event_analyzer display_event_analyzer
declare_rule := "start_section:" statements
priority_rule := "priority_section:" statements
election_rule := "election_section:" statements
task_activation_rule := "task_activation_section" statements
gather_event_analyzer := "gather_event_analyzer_section" statements
display_event_analyzer:= "display_event_analyzer_section" statements
statements := statement {statement}
statement :=
"put" "(" identifier [, integer] [, integer]")" ";"
| identifier ":" data_type [ ":=" expression ] ";"
| identifier ":=" expression ";"
| "if" expression "then" statements [ "else"
statements ] "end" "if" ";"
| "return" expr ";"
| "for" identifier "in" ranges "loop" statements "end" "loop"
";"
| "while" expression "loop" statements "end" "loop" ";"
| "set" identifier expression ";"
| "uniform" "(" identifier "," expression "," expression ")" ";"
| "exponential" "(" identifier "," expression ")" ";"
data_type := scalar_data_type
| "array" "(" ranges ")" "of" scalar_data_type
ranges := "tasks_range" | "buffers_range" | "messages_range" | "resources_range" | "processors_range" | "time_units_range"
scalar_data_type := "double" | "integer" | "boolean" | "string" | "random"
operator := "and" | "or" | "mod" | "<" | ">"
| "<=" | ">=" | "/=" | "=" | "+" | "/" | "-" |
"*" | "**"
expression := expression operator expression
| "(" expression ")"
| "not" expression
| "-" expression
| "max_to_index" "(" expression ")"
| "min_to_index" "(" expression ")"
| "max" "(" expression "," expression
")"
| "min" "(" expression "," expression
")"
| "lcm" "(" expression "," expression
")"
| "abs" "(" expression ")"
| identifier "[" expression "]"
| identifier
| integer_value
| double_value
| boolean_value
|
Notes on the BNF of .sc file syntax :
- entry is the entry point of the grammar.
- The data_type rule describes all data types available
in a .sc file
- The operator rule lists all binary operators.
- The expression rule give all possibles expressions
that you can use to define your scheduler.
- The statement rule contains all statements which
can be used in a .sc file.
- identifier is a string constant.
- integer_value is a integer constant.
- double_value is a double constant.
- boolean_value is a boolean constant.
Two kinds of statements exist to build your user-defined scheduler
: low-level and high-level statements. high-level statementsoperate
on all task informations. low-level statements operate only on one
information of a task at a time. all these statements work as follow :
- The if statement : works like in Ada or most
of programming languages : run the else or the then statement
branch according to the value of the if expression.
- The while statement : works like in Ada or most
of programming languages : run the statements inclosed in the loop/end loop
block until the while condition become false.
- The for statement : it's an Ada loop with a predefined iterator
index. With a for statement, the statements enclosed in the loop
is run for each task defined in the TCB table. At each iteration, the
variable defined in the for statement is incremented.
Then, in the case os task loop for instance (use keyword tasks_range
in this case), its
value range from 1 to nb_tasks (nb_tasks is a predefined static
variable initiliazed to the number of tasks hosted by the
currently analyzed processor).
- The return statement. You can use a return statement
in two cases :
- With any argument in any section except in the election_section.
In this case, the return statement just end the code of the section.
- With a integer argument and only in the election_section
. Then, the return statement give the task number to be run.
- The put(p,a,b) statement : displays to the screen
the value of the variable p. This statement is useful to debug your user-defined
scheduler. If a and b are not equal to zero and
if p is an array type, put(p,a,b)
displays entries of the table with index between a
and b.
If a and b are equal to zero and if p is an array, all entries if the
arrao are displayed.
- The exponential(a,b) statement :
intializes the
random generator a to generate exponential random values with
an average value of b.
- The uniform(a,b,c) statement :
intializes the
random generator a to generate uniformly random values
between
b
and c.
- The set statement : description of new task activation model : assign an expression which shows how to compute task wake up time with
an identifier.
The predefined operators work as follow :
- abs(a) : return the unsigned value of
a.
- lcm(a,b) : return the last common multiplier of a
and b.
- max(a,b) : return the maximum value between a
and b.
- min(a,b) : return the minimum value between a and
b.
- max_to_index (v) : firstly find the task in the TCB
with the maximum value of v and then, return its position in the
TCB table. Only ready tasks are considered by this operator.
- min_to_index(v) : firstly find the task in the TCB with
the minimum value of v and then, return its position in the TCB
table Only ready tasks are considered by this operator.
- a mod b : compute the modulo of a on b (rest
of the integer division).
All Cheddar analysis tools are called from the "Tools"
menu. This section gives a short description of them. Some
of them compute
tasks parameters, and then are composed of two submenus
: "Compute and update tasks set"
and "Compute and display".
Choose "Compute and update tasks set" submenu
if you want to save computed parameters into your project tasks
set.
Choose "Compute and display" if you only want to
display computed parameters on the bottom of the main Cheddar window.
Menus and Sub-menus of the Cheddar's editor :
- File Menu :
- New sub-menu : create a new XML project.
- Open sub-menu : load a XML project file into the editor.
- Save sub-menu : save the current XML project into a file
with the current XML project file name.
- Save as sub-menu : save the current XML project into a file
with a new XML project file name.
- AADL sub-menu : provides ane features related to AADL specifications.
- AADL import : read an AADL specification into Cheddar.
- AADL export : translate a Cheddar specification towards an AADL specification.
- Export property sets used by Cheddar : write to files in the current directory
the Cheddar's property sets.
- Export standard AADL property set : write to files in the current directory
the standard AADL property set.
- Customize how AADL services work : allows the user to set some options related
to the AADL services provided by Cheddar.
- Exit sub-menu : Quit the Cheddar's editor.
- Edit menu :
- Add sub-menu : create a new object in the current XML project.
Object can be a processor, a task, a message, a buffer, a network or
an event analyzer.
- Delete sub-menu : remove an object from the current XML project.
Object can be a processor, a task, a message, a buffer, a network or
an event analyzer.
- Update sub-menu : change parameters of
an already defined object in the current XML project.
Object can be a processor, a task, a message, a buffer, a network or
an event analyzer.
- Duplicate sub-menu : copy an objet to a new one
in the current XML project.
Object can be a processor, a task, a message, a buffer, a network or
an event analyzer.
- View menu :
- List sub-menu : show a set of object
of the current XML project.
Set of object can be the processor set, the task set, the message set, the buffer set, the network set or
the event analyzer set.
- Tools menu :
- Clear work space sub-menu : clean the working area
(main window). Do not change anything on the project itself.
- Scheduling sub-menu :
- Customized scheduling simulation sub-menu :
compute and draw scheduling simulation. This sub-menu allows the user to
customize the way the scheduling is computed.
- Customized scheduling feasibility sub-menu : compute some
basics feasibility tests on all processors. Feasibility test computed there are
utilization factor test and response time test.
- Set priorities according to Rate Monotonic sub-menu : change priority
of the task according to the period (Tasks with the smallest period become
tasks with the highest priority).
- Set priorities according to Deadline Monotonic sub-menu : change priority
of the task according to the deadline (Tasks with the smallest deadline become
tasks with the highest priority).
- Partition sub-menu : provide some services to assign tasks on a set of processor.
- With Best Fit sub-sub-menu : assign tasks on the set of processor according to the Best Fit algorithm.
- With General Task sub-sub-menu : assign tasks on the set of processor according to the General Task algorithm.
- With Next Fit sub-sub-menu : assign tasks on the set of processor according to the Next Fit algorithm.
- With First Fit sub-sub-menu : assign tasks on the set of processor according to the First Fit algorithm.
- With Small Task sub-sub-menu : assign tasks on the set of processor according to the Small Task algorithm.
- Event table services sub-menu : provide some basic services on event tables.
- Compute scheduling and generate event table sub-sub-menu :compute the scheduling and produce the event table.
- Draw time line from event table sub-sub-menu : draw time line from the last computed or loaded scheduling/event table.
- Run analysis on event table sub-sub-menu : perform analysis on the last computed or loaded scheduling/event table.
- Export event table sub-sub-menu : save the last scheduling/event table on a file in a XML format.
- Options sub-menu : describe how the scheduling simulation will be done.
- Resource sub-menu : from there, you
- Bound on blocking time sub-sub-menu :
compute bound on shared resources blocking time according
to
PCP and PIP protocols without computing the scheduling
- Looking for priority inversion from simulation sub-sub-menu : run analysis on a previously computed scheduling to look for hight priority tasks blocked
by lower priority task at shared resource access.
- Looking for priority inversion from simulation sub-sub-menu : run analysis on a previously computed scheduling to look for tasks blocked for ever on
shared reousrces.
- Buffer sub-menu : this submenu can
help you to study buffers shared by tasks.
- Buffer simulation sub-sub-menu : compute buffer utilization factor and message waiting time from a given scheduling simulation.
- Buffer feasibility tests sub-sub-menu :
compute bound on buffer utilization factor and essage waiting time without computing scheduling.
- Precedency sub-menu : . You will find here some
heuristics/algorithms that can schedule or check feasibility of
a tasks set with dependencies.
- Chetto/Blazewicz modifications on priorities sub-sub-menu : This service create independent task set from a dependent task set by
a modification oe task priorities according to precedency constraints.
- Chetto/Blazewicz modifications on deadlines sub-sub-menu : This service create independent task set from a dependent task set by
a modification oe task deadlines according to precedency constraints.
- End to End response time : compute response time from a set of task (which have precendency relationship) with the
Holistic method.
- Random sub-menu : this submenu should provide necessary tools to do
simulations with random events.
- Compute response
time density sub-menu :
compute statistic distribution of task
response time from a scheduling simulation.
- Help Menu :
- About Cheddar sub-menu : provide version number of the Cheddar's binaries.
- Manual sub-menu : contains the text given in this section.
- Scheduling references sub-menu : gives all paper references
used to compute feasibility tests and simulation results.
Contact : Frank Singhoff mailto:singhoff@univ-brest.fr
Last update : november the 5th, 2005