1. Review
Two
kinds of communication in our system:
Something
like a socket/pipe
And
something like shared memory
Two
policies for shared memory updates
Copy
on read
Copy
on write
2. An Example: A stack to provide shared memory across multiple processors over I2C
Application: Responsible for the application
semantics: what does the value of the shared variable mean?
Transport: Implements “shared memory” iterface by
using the datalink system to send guaranteed messages
between transport layers running on different
processors. Might also implement fifos, semaphores, etc.
Exports to application:
publish(addr);
pubscribe(addr); post(addr,var); update(addr,&var);
Exports to datalink :
transport_recv(message);
DataLink: Guarantees error free delivery of messages from
one transport to another using the available
physical layer. Can implement a wide
variety of retransmit schemes.
Exports to transport layer:
datalink_send(message);
Exports to physical layer:
datalink_recv(packet);
Physical: Converts a packet into a set of frames
for transmission over the bus. Frames are reconstructed and
passed back to datalink layer at other end.
Knows how to drive the physical bus.
Exports to datalink_layer
physical_send(packet);
Exports to physical layer
ISRs to deal with events on the bus
such as start, stop, byte transmission
7. Questions for Monday Discussion:
what does subscribe() do in the transport layer ?
sends
SUBSCRIBE message if published (even if published by me)
what does publish do in the transport() layer ?
sends
PUBLISH message if not already published
what does post do in the transport layer?
How does the transport layer process incoming data
from the data-link layer?
It
looks at first byte to determine message type and processes accordingly. Looks
for transport level communication errors.
How does the data-link layer process incoming data
from the transport layer?
if
the destination address is SELF then call recv(message);
otherwise,
add message to queue of outgoing messages.
/////////// here is the complete stack ////////////
///////////////// The Transport Layer /////////////////
///////////////// Shows how one Transport
Layer
talks to another Transport Layer //////////
//
queue ID’s for communication between transport and data-link
#define
SELF 1 //
different for each processor
struct
shared_mem {
char
value;
int
policy;
int
id;
boolean
postOK;
int
publisher;
list *subscribers;
boolean update; // flag for use in copy-on-read
}
shared_mem
globals[N];
transport_init()
{
g = 0; // number of currently known shared globals
}
///// Outgoing Messages
//////////////////////
int
publish(int dataID, int policy) { //msg = <PUB, publisher, data ID,
policy>, broadcast
message[0]
= PUBLISH; //
Message Type
message[1]
= SELF; // ID of Publisher
message[2]
= dataID; // ‘Address’ of the shared variable
message[3]
= policy; // update policy …
datalink_send(0,message,
4); // broadcast
this message (can this block?)
return(0);
}
int
subscribe(int dataID, int policy) { //msg = <SUB, subscriber, data
ID, policy>, to publisher
startTimer();
while
( (!tmp=lookup(globals,dataID)) && readTimer() < TIMEOUT); // block until published or timeout
if
(tmp) { // publisher found
message[0]
= SUBSCRIBE; //
Message Type
message[1]
= SELF; // I am the subscriber
message[2] = dataID;
message[3] = policy; //
subscription update policy
datalink_send(tmp.publisher,
message, 4) ; // send message to publisher
return(0)
}
send_signal(NETWORKTIMEOUT); // signal to all tasks on this SELFessor for
error handling
}
int post(int dataID, char value) {
if (!tmp = lookup(dataID)) return (-ENOTPUBLISHED); // trying to post to unpublished data item
if (tmp.publisher != SELF) return(-ENOTPUBLISHER); // can't write if SELF is not publisher
tmp.value = value; // finally, write the value
if (tmp.policy == CR) return(0); // no need to do anything!
foreach(p in tmp.subscribers) {
message[0] = POST;
message[1] = dataID;
message[2] = SELF; // so that receiver can verify!
message[3] = tmp.value; // send the data
datalink_send(p, message, 4); // send post message to p
}
} // behavior depends on policy
int update(int dataID, char *value) {
if (!tmp = lookup(dataID)) return (-ENOTPUBLISHED); // trying to post to unpublished data item
if (SELF not in tmp.subscribers) return(-ENOTSUBSCRIBER);
if (tmp.policy = CW) {
*value = tmp.value;
return(0);
}
// its copy on read, so send a message!
message[0] = UPDATE;
message[1] = dataID;
message[2] = SELF; // so that receiver can verify
datalink_send(tmp.publisher, message, 3); // send post message to p
tmp.update = false;
sleep_on(update_q, TIMEOUT); // blocking
if (!tmp.update) return (-EREADTIMEOUT);
else *value = tmp.value;
}
///// Incoming Transport Layer Messages
//////////////////////
int transport_recv(char
*message, int len) { // called by data link layer when a message is received
if (len < 4) transport_error();
switch (message[0])
SUBSCRIBE:
if (!tmp =
lookup(message[2])) transport_error(message); // not published
if
(tmp.publisher != SELF) transport_error(message); // by me
ir
(tmp.policy != message[3]) transport_error(message); // policy mismatch
add_subscriber(tmp.subcribers,
message[1]); // add to subscribers list
return(0);
PUBLISH:
if (tmp =
lookup(message[2])) transport_error(message); // already
published
tmp =
globals[g++]; // fill in data structure
tmp.policy
= message[3];
tmp.id
= message[2];
tmp.publisher
= message[1];
return(0);
POST:
if (!tmp = lookup(message[1]) transport_error(message); // not published
if (SELF not in tmp.subscribers) transport_errror(mesage); // not subscribed
if (message[2] != tmp.publisher) transport_error(message); // wrong publisher
tmp.value = message[3];
if (tmp.policy == CR) {
wake_on(update_q); // wake up reader
tmp.update = true;
}
UPDATE:
if (!tmp = lookup(message[1]) transport_error(message); // not published
if (tmp.publisher != SELF) transport_error(message); // by me
if (message[2] not in tmp.subscribers) transport_error(message); // not sub
subscriber = message[2]; // save subscriber info
message = make_message(POST, tmp.dataID, SELF, tmp.value);
transport_send(subscriber, message, 4);
default: transport_error(message); // have to deal with errors
}
////////////////// Data-link
layer //////////////////////////
// keep a
packet counter so that we can uniquely tag each packet for a period of time
datalink_packet_counter =
0;
char
*datalink_packet; // packet
= <dst, src, number, checksum, length, transport_msg>
// or it
can be <dst,src,number,0,0> = NACK
/////// Outgoing DataLink Packets
//////////
datalink_send(int dst, char *transport_msg, int length) { // called from transport layer with a transport
message
packet =
new datalink_packet(transport_msg, dst, length);
if (dst ==
SELF || dst == 0) datalink_recv(packet); // loopback in
the event of SELF or Broadcast
if (dst
!= SELF) { // if not self,
send to physical layer
physical_send(packet);
save_until
_timeout(packet);
}
}
//////// Incoming DataLink Packets
////////
datalink_recv(char *packet, int length) { // called from
physical layer when a packet is received.
if
(isNACK(packet)) { // receiver had a problem so retransmit if still saved
resend
= get_saved _packet(get_packet_number(packet)); // lookup saved packet for resend
if
(resend) physical_send(resend);
else
datalink_error(packet); // can no longer retransmit, so datalink error has occurred
}
else { // it
must be normal packet
char
*transport_msg = datalink_verify(packet, length); //
validates header and checksum, extracts transport message
If
(transport_msg) //
if null, then assume that verify failed so send a NACK
transport_recv(transport_msg); // pass extracted
message to transport layer
else
physical_send(make_NACK_packet(packet));
// send a NACK to the src of the packet.
// a NACK to a
NACK should cause error, so don’t save
}
}
////////////////////////// Physical
Layer ////////////////////////
/////////// what datalink needs to know
about physical ////////
physical_send(char *packet) {
// this was the old version
// enq(sendQ,
packet);
// i2C_send_start(); // if bus is busy, this will retry when stop
condition is received
//
here is the new version: a packet is converted into a set of frames appropriate
for the physical interface
//
The frame format contains information needed to reconstruct the packet along with
the dst address needed by i2C
// <packet> becomes
<dst, seq#, packet#, [part of packet]> <dst, seq#, packet#, [part of packet]> ... <dst, seq#,
packet#, [part of packet]>
// it should be something like this
make_frames(packet);
// chops packet up into frames and puts them on and
internal queue.
I2C_send_start(); // try to
send
}
physical_init() {
master
= false; // we
are not currently bus master
recv = send = null; // clear the send and receive frame buffers
// not shown: make queue of outgoing frames
// see below for plug and play version of this function
}
////////// incoming events from the
network interface /////////
// In I2C, frames can be any length. So
each packet is converted directly to a frame
// other physical layers might have
limited frame sizes so each packet might get split
// up into multiple frames.
i2C_recv_start_isr() { // start of a frame
if (init) return;
if
(!master) { //
if I’m master then ignore (was sent by SELF)
recv = null; // otherwise, get ready to receive
recv_ix
= 0;
wait_for_stop
= false;
}
else { // if
I’m master then put address on bus
*PORT
= send[send_ix++]; // start sending current frame
}
}
i2C_send_start() {
if
(tryStart()) { // not guaranteed to succeed in i2C
master
= true; // if
successful the become master and get a frame to send
send
= get_frame(); // get a frame to send from the queue
send_ix
= 0; //
initialize send counter
send_length
= get_packet_length(send);
}
}
i2C_recv_stop_isr() {
if (init) return;
if
(wait_for_stop) { // not our frame so ignore
wait_for_stop
= false;
return;
}
if (!master) { // just received a frame so send up to datalink layer (frame == packet in i2C)
if (physical_control_frame(recv)) process_control_frame(recv); // for
plug-and-play
else
packet = assemble_packet(recv,
recv_ix); // reassembles frames into packets,
if packet is complete, return packet else null
if
(packet) datalink_recv(recv, recv_ix);
}
master
= false;
if
(frames_to_send()) { // if outgoing frames are
pending, then try to get bus
i2C_send_start();
}
}
i2C_recv_byte_isr() {
if (init) {ack = true; return;}
if
(wait_for_stop) return; // ignore these events if message is not for me
if
(!master) { // must be slave so add to the incoming frame buffer
if (recv_ix == 0) { // first byte of
incoming frame so process header
if (*PORT != SELF) { // check if frame
is for me
wait_for_stop
= true; // set to ignore until stop if not
return;
}
//
otherwise, this frame is for me
//
throw away first byte (physical header)
(or use to authenticate packet!)
recv =
new packet; //
start a new frame, which is a packet
}
else
recv[recv_ix++] = *PORT; // we’re in the middle of a frame so add new byte to buffer
}
else { // we are
master so send another byte from send frame buffer
if
(send_ix == send_length) i2C_send_stop();
// frame is finished, so send a stop condition
else
{ // we are in
the middle of a send frame so continue
*PORT
= send[send_ix++];
}
}
}
i2C_byte_error_isr() {
if (init) { nack = true; return; }
if (wait_for_stop) return;
if (!master) { // the error was that this processor did not send an acknowledgement!
// but there doesn't seem to be anything we can do about it
wait_for_stop = true; // may as well ignore rest of message and hope for retransmit
return;
} else { // the receiver did not acknowledge the byte so we need to retransmit the frame
if (get_xmit_count(send) >= MAXRETRY) physical_error(frame);
else physical_frame_send(send) ; // just puts this frame back on the internal queue and increments
// frame's retransmit counter
i2C_send_stop() ; // abort this frame
}
Question 1: How can we add plug and play to this?
1. In init, get the bus and probe each address until no ack is received then claim the address (SELF = i)
boolean ack,nack;
i2C_init {
init = true;
for (i = 0; (i < MAX & !ack); i++) {
while (! i2C_try_start()); // get control of the bus
ack = nak = false; // variables to be byte or error isr's
*PORT = i; // send the byte
while (!ack & !nack); // wait for ack or nack
}
if (ack) {
SELF = i;
physical_frame_send(make_control_frame(0,NEWADDRESS, SELF)); // broadcast new addres
}
else physical_error();
init = false;
}
Question 2: How can we add watchdog to the system?
should this be at the physical, transport, or datalink level?
periodically send, if after a certain number of sends some flag is not cleared, then there is an error
in the system.
Question 3: How to I do the pipes
open(pipeID, READ) // OPEN message, create local buffer for incoming data
open( pipeID, WRITE) // OPEN message
read(pipeID); // no message (assume copy on write), return item from buffer or -1
write(pipeID); // DATA message