Sat Oct 20 14:00:30 CEST 2007

message passing interface

Since I2C is a shared bus architecture, care needs to be taken to
place operation in a sane highlevel framework. The interface i want is
asynchronous message passing. Messages should either be bytes, or a
sequence of bytes (in which case 'message' contains the size, and the
a/f regs contain the message)

      message address i2c-send

Let's suppose for now there is only a single process per machine, and
build multiple process dispatch on top of single process.

To do this bi--directionally, an event loop needs to poll for
messages. Dispatching of highlevel messages (internal addresses) can
be done as a layer on top of single message passing. So i need a send
and receive task, and make sure they don't collide

   * it's always possible to RECEIVE, so that should be the background
     task. this simply waits until a message arrives.

   * it's only possible to SEND if the bus is free, so a SEND might

The problem is that a message might come in while waiting to send out
a message. Therefore messages need to be queued. The moral of the

     A send can never block a process, only a receive can.

So what is a task? It is a function that maps a single input message
to zero or more output messages. The output can be zero in a
meaningful way, because the task has internal state. So basically, a
task is a closure, or an object.

The driver routine can be a single task, since the hardware is
half-duplex. See pic18/message.f for the iplementation attempt.

Something to think about: the ISR needs to be completely decoupled
from the tasks that generate output messages. This is the whole point
of buffering: if there is straight line code from RX interrupt ->
computation task, the tasks that might run a long time will not be
pre-empted. So:

      The RX ISR and the dispatch loop are distinct.

what it looks like (yes i need to pick up hoare's book again..)

\ Message buffering for a shared bus architecture. The topology looks
\ like this:

\                            wire
\                              |
\                              | G
\         A             E      v      F
\  wire ----> [ LRX ] ----> [ LTX ] ----> wire
\                |             ^
\  . . . . . . . | B . . . . . | D . . . . . . .
\                v      C      |
\             [ HRX ] ----> [ HTX ]
\ Code above the dotted line runs with interrupts disabled, and
\ pre--empts the code below the line. Communication between the two
\ priority levels uses single reader - single writer buffers. The 6
\ different events are:
\ A) PIC hardware interrupt
\ B) RX buffer full condition
\ C) TX buffer full condition (execute task which writes to buffer)
\ D) wakeup lowlevel TX task from userspace
\ E) wakeup lowlevel TX task from kernelspace
\ F) PIC hardware send
\ G) wakeup lowlevel TX task from bus idle event
\ A task is an 'event converter'. The 4 different tasks are:
\ LRX) convert interrupt (A) to tx buffer full B and tx wakeup E
\ HRX) convert tx buffer full (B) to rx buffer full (C)
\ HTX) convert tx buffer full (C) to tx wakeup (D)
\ LTX) convert wakeup (data ready: D,E) to hardware send.
\ The pre--emption point is A: this causes no problems for the
\ low--priority task because of the decoupling provided by the receive
\ buffer. The only point that needs special attention is the LTX task,
\ which can be woken up by different events D, E and G, and care needs
\ to be taken to properly serialize message handling. To do this, both
\ D and E should invoke LTX with interrupts disabled. For E this is
\ trivial: just call the LTX task, for G is is already ok since it's
\ an isr, so D needs to explicitly disable interrupts.