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;
}
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) {
} // behavior depends
on policy
int update(int dataID, char *value) {
} // behavior depends
on policy
///// 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:
UDATE:
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() { // queue
of outgoing frames
master
= false; // we
are not currently bus master
recv
= send = null; // clear the send and receive frame
buffers
}
////////// 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
(!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
(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)
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
(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++];
}
}
}