friendev EtherDune TCP/IP library
Public Member Functions | List of all members
SharedBuffer Class Reference

Implements a "shared" circular buffer using spare ENC28J60 memory. More...

#include <SharedBuffer.h>

Inheritance diagram for SharedBuffer:
ListItem

Public Member Functions

uint16_t write (uint16_t len, const byte *data)
 Writes a fragment to the shared buffer More...
 
uint16_t release ()
 Releases one fragment of data More...
 
 SharedBuffer ()
 
 ~SharedBuffer ()
 
void flush ()
 Releases all data in this buffer, freeing up space. More...
 
bool isEmpty ()
 Determines whether this buffer is empty More...
 
uint16_t fillTxBuffer (uint16_t dstOffset, uint16_t &checksum, uint16_t count=0xFFFF)
 Copies up to count fragments to the transmit buffer via DMA, concatenating them starting at given offset. More...
 

Detailed Description

Implements a "shared" circular buffer using spare ENC28J60 memory.

This class takes the memory that is not used for the receive/transmit buffer and turns it into a circular buffer shared by all sockets or any network service that needs to assemble packets before they are put on the network, or in the case of TCP, buffer data in case a retransmit is necessary.

The concept of "shared circular buffer" is an attempt to provide each network service with its own circular buffer but without having to statically assign a chunk of dedicated memory to each possible service. Instead, all services share the same memory chunk, which is about 4kB in length by default. See the ENC28J60 Hardware configuration section to see how the ENC28J60 8kB memory is distributed among TX buffer, RX buffer and this shared buffer.

This "shared circular buffer" indeed creates a virtual private circular buffer for each client. To do this, the implementation keeps track of the head of the circular buffer and all tails. Every time a SharedBuffer writes, writes to the head. Every time a SharedBuffer releases data it does not need any more, data is only actually freed if that last release happened to be the last tail.

Consider the following memory area we want to share for 3 "virtual" circular buffers A, B and C. In the example, tail pointers A, B, C point each to the first byte to be read.

+---------------------------------------+
| |
| free |
| |
+---------------------------------------+
^
head
tail=head

Now, let's say A writes a fragment of data to the buffer:

+---+-----------------------------------+
| | |
|A0 | free |
| | |
+---+-----------------------------------+
^ ^
A head
tail=A

And then B and C write to the buffer

+---+-----+---+---------------------------+
| | | | |
|A0 | B0 |C0 | free |
| | | | |
+---+-----+---+---------------------------+
^ ^ ^ ^
A B C head
tail=A

Then B writes again:

+---+-----+---+---+-----------------------+
| | | | | |
|A0 | B0 |C0 |B1 | free |
| | | | | |
+---+-----+---+---+-----------------------+
^ ^ ^ ^
A B C head
tail=A

At this point, lets say B decides to read out (release) the first fragment. Since this is a circular buffer, only the first fragment (the one pointed out by tail pointer B) may be released:

+---+-----+---+---+-----------------------+
| | | | | |
|A0 |free |C0 |B1 | free |
| | | | | |
+---+-----+---+---+-----------------------+
^ ^ ^ ^
A C B head
tail=A

Fragment B0 is released and B's tail pointer is updated to point to B1

This has fragmented our memory space. The "shared" circular buffer implementation does not keep track of free fragments that exist between used chunks of memory. The implementation makes an optimistic assumption that free chunks will eventually coalesce since the buffer is supposed to be used to keep data for a short amount of time. This assumption saves a lot of overhead in keeping track of these fragments and maximizes usage of the limited memory available. The caveat here is that if a service does not free a fragment in a timely manner it will eventually block all services. This is mitigated by 1) not running many services simultaneously (at the end, we're in a microcontroller!) and 2) careful service design: making sure that services release data frequently or time out and release in case of errors.

Back to our example, when A releases its only fragment, we end up with this:

+---------+---+---+-----------------------+
| | | | |
| free |C0 |B1 | free |
| | | | |
+---------+---+---+-----------------------+
^ ^ ^
C B head
tail=C

The global tail now points to C and all the free space is consolidated in one chunk (remember this is a circular buffer and therefore it wraps around).

You can think of this shared circular buffer as a regular circular buffer in which the head is shared for all and the tail is the one that is most behind.

This mechanism is implemented in the SharedBuffer class. Each instance of SharedBuffer represents an independent buffer (like A, B or C in the example) in the shared memory area. SharedBuffer also takes care of writing a small header for each fragment that contains a pointer to the next fragment, the length of the fragment and a checksum of the data in the fragment that can be combined to calculate the checksum of the concatenation.

The class includes a method, fillTxBuffer() that reads out fragments and concatenates them into the transmit buffer, calculating the resulting checksum.

Definition at line 158 of file SharedBuffer.h.

Constructor & Destructor Documentation

SharedBuffer::SharedBuffer ( )

Definition at line 40 of file SharedBuffer.cpp.

SharedBuffer::~SharedBuffer ( )

Definition at line 45 of file SharedBuffer.cpp.

Member Function Documentation

uint16_t SharedBuffer::fillTxBuffer ( uint16_t  dstOffset,
uint16_t &  checksum,
uint16_t  count = 0xFFFF 
)

Copies up to count fragments to the transmit buffer via DMA, concatenating them starting at given offset.

This function may copy fewer fragments than count to make sure not to overflow the TX buffer

Parameters
dstOffsetDestination ENC28J60 memory address, encoded as an offset within the tx buffer.
checksum[out] Resulting checksum of copied data
countMaximum number of fragments to concatenate
Returns
Total number of bytes copied to the tx buffer

Definition at line 212 of file SharedBuffer.cpp.

void SharedBuffer::flush ( )

Releases all data in this buffer, freeing up space.

Definition at line 198 of file SharedBuffer.cpp.

bool SharedBuffer::isEmpty ( )

Determines whether this buffer is empty

Returns
true if the buffer is empty. false otherwise

Definition at line 263 of file SharedBuffer.cpp.

uint16_t SharedBuffer::release ( )

Releases one fragment of data

Returns
The amount of data released

Definition at line 162 of file SharedBuffer.cpp.

uint16_t SharedBuffer::write ( uint16_t  len,
const byte *  data 
)

Writes a fragment to the shared buffer

Parameters
lenlength of the data pointed by data
datapointer to the data to write
Returns
Number of bytes actually written

Definition at line 126 of file SharedBuffer.cpp.


The documentation for this class was generated from the following files: