CREEM(tm)

Concurrent Realtime Embedded Executive for Microcontrollers
CREEM is the "cream" of the baby real-time operating systems!
CREEM is free, with source code

STOP
PRESS!
  CREEM is now designed by the GOOFEE Diagrammer, so is 100% C code. Look in GOO-CREE\ directory.

Read on for an introduction to CREEM and GOOFEE, and a description of the original assembly language version of CREEM, at only 650 bytes...

What about TERSE?

I originally wrote TERSE for the 8051, a tiny operating system (as small as 150 bytes) than relies on cooperative scheduling. Its greatest feature is totally deterministic operation. "Cooperative" means that it executes a node (i.e., task, module) right through to completion until it voluntarily returns to the O.S., and no other node can execute while it is. Cooperative scheduling requires a lot of thought at the design time, but results in very predictable performance, and with the Signature scheduling used in TERSE results in "bounded runtime" determinism (i.e. runtime variation in ordering of execution of the nodes, but within known bounds).

What is CREEM?

CREEM is a concurrent O.S., which means that it timeslices between nodes. That is, nodes can run concurrently, at the same time. The great advantage of CREEM over TERSE is that there is no scheduling table (Signature table) to setup. In fact, CREEM has the advantage over every other RTOS, including commercial baby microcontroller RTOSs, in that there are only three operating system services for an application programmer to worry about. CREEM is a dream to use. Let me itemise some major features of CREEM:

CREEM is for implementation of designs done using GOOFEE diagrams, as introduced in my book Flow Design for Embedded Systems, but any analysis/design process in which you adopt a "dataflow orientation" can be implemented in CREEM. We have a freeware graphical Windows program for drawing GOOFEE diagrams and project management. Let me illustrate with a hypothetical design, using GOOFEE notation:

[Image]

Basically, GOOFEE enables you to draw unambiguous designs of a program, that can express anything that you might like to do. CREEM always starts by executing node 1, which can launch any other initial threads. I have left out any wires from node 1, as the semantics of the diagram imply that the initial threads are nodes 2, 6 and 7.

Thus, initially we have three nodes running concurrently. The diagram uses the concept of synchronous dataflow, and nodes 2, 5, 6, and 7 are called Iterative nodes, as they have more than one ring. The concepts are simple, but this is not the place for detailed description of the notation and rules. Briefly, taking node 6 as an example, as there are no synchronous wires to the outer ring of node 6 it is immediately eligible to fire. Node 6 can exit from any one of its rings, which means, effectively, that it is a state machine. If it exits from the inner ring, posting a message back to itself, the inner ring then becomes the active one and all synchronous messages must arrive at that ring before it becomes eligible again.

As node 6 posts a message to itself, it keeps re-executing. Note node 5 -- it has two input wires, which means that it cannot fire until both have arrived -- this is the principle of synchronous dataflow. However, the wire to node 6 from node 4 is asynchronous, indicated by the little bar near the arrowhead, which means that it has no impact on whether node 6 fires or not, but it is still delivered whenever node 6 does fire.

Nodes 8 and 9 illustrate automatic resource sharing and mutual exclusion. Nodes touching each other (or bound by what is called a Joining Bar) cannot execute concurrently. Anything that has to be shared, such as memory block or peripheral device, can make use of this.

Looking at the diagram overall, you can see three loops, node 6, nodes 7--10, and nodes 2--3--4--5. This is not solving any particular problem, just showing a diagram with various types of synchronous dataflow, state machine, asynchronous flow, synchronisation, mutual exclusion, messaging, and multiple concurrent execution loops with different cycle times. GOOFEE notation does have some more adornments for further clarification if desired, but the diagram as shown serves the purpose for explaining CREEM.

Focusing just on node 5. It requires messages from nodes 3 and 4 before it is eligible to fire, and in the normal rules for GOOFEE diagrams they would be posted when nodes 3 and 4 exit. So, CREEM fires (executes) node 5, and can then exit from either the inner or outer ring. If it exits from the inner ring, it posts a message to node 8, and when a message comes back, node 5 is eligible to execute again. Node 5 has accessed the resource, without having to worry about other parts of the program sharing the same resource. When node 5 exits from its outer ring, a message is posted back to node 2, thus starting the 2--3--4--5 loop again. Note that the wire from node 5 to 2 could be left out for brevity, as it is also implied by the semantics.

Focusing on node 2. When it exits, it makes both nodes 3 and 4 eligible to fire, and CREEM will run them concurrently (unless a mutual exclusion relationship is defined, as is done with nodes 8 and 9). Note that CREEM partitions the 128 nodes into 16 groups of 8 nodes, and any nodes (from two nodes to 8 nodes in the group) within a group can be declared as being mutually exclusive, or what CREEM terminology refers to as in the same exclusion zone.

Focusing on node 7. When it exits, it fires both nodes 9 and 10, and when both return messages have arrived, node 7 re-fires.

Any kind of complex real-time problem that you can think about, you can express in GOOFEE notation, and CREEM can execute it. To see the above diagram in action, look at CREEM51.ASM in CREEM\ directory on the disk.

This is the first version of CREEM, written in assembly language for the 8051. It has the example application, implementing the above diagram, in the same file. If you don't have an 8051 to run it on, the evaluation version of Franklin C allows a program size up to 4K, has the A51 assembler and fully functional simulator. The simulator is a work of art, and simulates just like the real thing, including the interrupt-driven concurrency. It can be downloaded from http://www.fsinc.com/ but beware, the zip file is 13M.

Using the Franklin product, Create Project/New and add CREEM51.ASM to it, then Project/Build All, then Debug/Start. Its as simple as that. To see something happening, set breakpoints at the start of "node2", "node3", "node4", and "node5". Bring up the Watch window and add the variables "first", "second", "third', "fourth", and "msgcnt". As you Go to each breakpoint, you'll be able to see that the nodes execute in the required order, and what nodes are executing concurrently at a particular point in time.

Feel free to experiment!

Further technical details

Limitations

To make CREEM work on a 8051 with only 128 bytes of RAM required compromises!

CREEM does not fall in a heap if you try to execute too many nodes concurrently. The example in the above diagram could have up to 4 nodes running concurrently, but CREEM will still manage if the design has more -- it will just juggle them, so they won't all be actually running concurrently. The main problem with too much concurrency is that of posting too many messages than the buffer can hold -- CREEM prevents this from happening, but on the current version this introduces a small possibility of a deadlock. This can be prevented at the design stage.

Data passed with each message is 16 bits. If CREEM is to be used in a bigger system with external RAM, the data field of a message could be a pointer, allowing large memory blocks to be passed around.

The future

Lots of things are happening. We have written a C version of CREEM, and also looking at linking the assembly version into the application written in C. Grant Gorddard has added CAN networking -- see CAN-CREE\ directory.  We are looking at further developing Grant's version with a stack for each thread, and an all-C version.




CREEM FAQ

Just starting this.

Q
Most RTOSs have priority levels for the tasks. Why not CREEM?

A
This is part of the excessive overhead of the way most RTOSs and applications for such RTOSs are designed. With a dataflow design, a node automatically runs when it is ready. Different parts, or threads of a dataflow diagram cycle at their own natural rate. Really, the priorities are assigned automatically, without having to worry about them with yet another O.S. service.

Q
Isn't it possible to post messages to a node faster than it can consume them, thus overloading the message buffer?

A
Yes, but it is quite simple to avoid at the design stage. Also, you could have an automatic invocation of the error handler node if this occurs. When designing the flow diagrams, observe the loops -- you will see the example diagram, that there are three independent loops. Make sure that you always post messages from a loop with a slower cycle time than the one you're posting to. You'll see that loop 2--3--4--5 is slower than node 6, so node 6 will always be able to consume whatever is sent to it. Alternatively, in the case of asynchronous messages, the GOOFEE rule is that it is optional whether a node posts these when it exits, so even if you post from a fast loop to a slow loop, as long asd the async msgs are not posted faster than the slow loop can absorb. In practice it is quite easy.