CS452 - Real-Time Programming - Spring 2013
Lecture 4 - Tasks & Kernels
Pubilc Service Annoucements
- Due date for kernel 1: 27 May.
- Partners, groups
Kernel of a Real-time Operating System
Introduction
The base unit of a polling loop is
Think of action as the performance of a task, such as washing
dishes. Think of condition as a signal that tells you it's time
to perform the task.
Actions are not independent of one another: washing dishes requires hot
water, dish soap, etc., which are provided by other actions. Communication is
needed.
Thus tasks need some support to do what they do.
- the ability to execute instructions
- code with the pc pointing into it
- state, in the form of memory
- the ability to communicate with one another
- the ability to receive information from the real world
- acquire data
- possibly nothing but symchronization
These basic needs are provided by the kernel of an operating system. The
kernel we create in cs452 is a microkernel, because it provides these
capabilities and nothing more.
We build the microkernel in four assignments
- task creation and scheduling
- inter-task communication
- an interrupt primitive
- complex servers
Really, the first three are the microkernel, plus a little stuff in
userland to exercise the microkernel. The fourth develops non trivial service
tasks that live in userland.
The real-time operating system we build consists of
- an uninterruptible microkernel, plus
- interruptible device-handling server tasks that run in user-space with
permissions allowing them to access hardware.
Permissions that allow user-space tasks to access hardware are not
unusual
What Does a Microkernel Provide?
Tasks
Communication
Communication has two aspects
- sharing information, requesting service
- synchronization
We use Send/Receive/Reply (SRR) to do both.
- Send blocks
- Receive blocks: is synchronous with the call to send
- Reply doesn't block: is synchronous with the return from send
Synchronization
- Between tasks
- Coordination of execution in co-operating tasks
- Uses SRR
- With internal events
- Real-time by synchronizing with a real-time clock: e.g. clock
server
- Ordering execution: e.g. name server, bounded buffer
- Uses SRR
- With external events
Interrupts
Input from the outside world
- Provide the information you polled for
- ISR OS design, which is essentially a jump table, which separates
testing from acting
interrupt entry point:
calculate action_entry_point;
jump to act_entry_point;
entry_point.1:
action.1;
entry_point.2:
action.2;
...
entry_point.n:
action.n;
These are the same actions you implemented in your polling loop,
- and the have all the same problems, plus some others.
- Somthing to think about
- Polling loop was single-threaded
- You were guaranteed not to be in the middle of a
computation when you got the signal to start another one.
- ISRs are not necessarily single-threaded
- You could get back the polling loop by turning off
interrupts during the action.
- No hierarchy of importance among ISRs
- Polling loop hierarchy was in the polling structure
- Selective interrupt masking can reproduce the
hierarchy,
- But then you have to save state
Tasks
What is a task?
- A set of instructions
- Current state, which is changed by executing instructions, which
includes
- values of its variables, which are automatic variables maintained
on the stack
- contents of its registers
- other processor state such as the PSR
- processor mode
- condition codes
- etc.
- its run state and other things maintained by the kernel
Two tasks can use the same set of instructions, but
- every task has its own state
- Therefore, no static variables
The kernel keeps track of every task's state
- In essence, servicing a request amounts to changing the state of one or
more tasks.
- Kernel maintains a task descriptor (TD) for each created task.
- That is, to create a task the kernel must allocate a TD and initialize
it.
The TD normally contains
- The task's stack pointer, which points to a private stack, in the
memory of the task, containing
- PC
- other registers
- local variables
all ready to be reloaded whenever the task next runs.
- Possibly the return value for when the task is next activated
- The task's parent
- The task's state
- Links to queues on which the task is located
- The kernel uses these to find the task when it is servicing a
request
Possible states of the task
- Active: running or about to run
- On a single processor system only one task can ever be active.
- But we would like to generalize smoothly to more than one
processor.
- Ready: can run if scheduled
- Need a queue used by the scheduler when deciding which task should
be the next active task
- Blocked: waiting for something to happen
- Need several queues, one for each thing that could happen
Kernel Structure
The kernel is just a function like any other, but which runs forever.
kernel( ) {
initialize( ); // includes starting the first user task
FOREVER {
request = getNextRequest( );
handle( request );
}
}
Where is the OS?
- requests come from running user tasks
- one type of request creates a task
- There needs to be a first task that gets everything going
All the interesting stuff inside done by the kernel is hidden inside
getNextRequest.
int getNextRequest( ) {
return activate( schedule( ) ); //the active task doesn't change
}
What's inside activate( active )?
- transfer of control to the active task
- execution to completion of the active task . To completion means
- until the active task sends a request to the kernel, or
- until a hardware interrupt occurs
- transfer of control back to the kernel
- getting the request
The hard part to get right is transfer of control
- which we call a context switch
The Hardware/Software Available in the Lab
Provided and maintained by CSCF
- Linux systems
- cross compiler: runs on 86_64, produces code for ARM
- GNU toolchain: compiler, assembler, link editor
- You will notice that my makefile separates
- compilation to assembly code,
- assembling the assembly code, and
- link editing.
- need to login explicitly to
linux.student.cs
- TFTP servers
- need to type IP number explicitly
TS-7200
Specific documentation from Technologic
System on Chip (SoC)
EP9302, designed and manufactured by Cirrus semiconductor
Memory
Byte addressable, word size 32 bits
- 32 Mbytes of RAM, starting at
0x00000000
- 4 Mbytes of flash RAM, starting at
0x60000000
- Contains RedBoot, which is loaded into RAM at startup
- Special locations at low addresses
- Special locations above
0x80000000
Two types of special location
- Supplied by Technologic:
0x80840000 to
0x80840047
- Suppied by Cirrus:
0x80010000 to 0x8081ffff
0x808a0000 to 0x80900023
Separate instruction and data caches
`COM' ports
Connected to UARTs
- RS-232
- Actual UART hardware is on the EP9302
Only really two
Ethernet port
Busy wait ethernet code in RedBoot
- used by loader to execute TFTP protocol
- used by RedBoot, which was customized by Technologic and installed in
the Flash RAM
Reset switch
- red, even though documentation says black
- actually, some are black
EP-9302
Specific documentation from Cirrus
System on chip
- ARM 920T core, implementing ARM v4T instruction set
- Specific documentation from ARM
- Two co-processors
- System controller, MMU
- Maverick Crunch floating point unit
- Two interrupt controllers
- Both ARM and Cirrus documentation
- An ARM-designed part, the PL-190
- Peripherals
- UARTs
- Timers
- DIO
- A/D
- etc.
Software
Compiler
GNU tool chain
- when you are getting started optimizing is usually a bad idea
- software multiplication, division, floating point from libgcc.a
- gcc uses a couple of functions like memcpy
- Makefile
- target.ld
RedBoot
Partial implementation
- fconfig :: NOT
- load (tftp)
- examine, copy, fill memory
Returns when program terminates
Busy-wait IO
COM2 uses monitor; COM1 goes to train
- initialization
- output
input
Return to: