dol: initial dol commit
[jump.git] / dol / src / dol / visitor / cell / lib / ppu / FastCommunication.cpp
diff --git a/dol/src/dol/visitor/cell/lib/ppu/FastCommunication.cpp b/dol/src/dol/visitor/cell/lib/ppu/FastCommunication.cpp
new file mode 100644 (file)
index 0000000..70a3bbd
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * FastCommunication.cpp / Version PPE
+ *
+ *  Created on: Mar 3, 2009
+ *      Author: lschor
+ *
+ * For remarks to this file, please refer to the SPE variant.
+ *
+ * Completion of the SPEs:
+ * Is coordinated by this process. Each SPE has to send a request to
+ * the PPE if it is allowed to finish.
+ * If all SPEs have requested to complete, the PPE sends to the SPEs a
+ * message that they can complete, thus return.
+ */
+
+#include "FastCommunication.h"
+
+/*
+ * Constructor
+ */
+FastCommunication::FastCommunication(int nrOfQueues,
+               uint64_t *context_all, uint64_t * ea_ls_base,
+               int32_t * queueFromSPEIn, int32_t * queueOnSPEIn,
+               uint64_t *fifoTails) {
+
+       _context_all = context_all;
+       queueFromSPE = queueFromSPEIn;
+       queueOnSPE = queueOnSPEIn;
+       _ea_ls_base = ea_ls_base;
+       _fifoTails = fifoTails;
+       _nrSpeComplete = 0;
+
+       _nrOfQueues = nrOfQueues;
+       try { _fifos = new fifoCollection[nrOfQueues]; }
+        catch(std::bad_alloc &e) {
+            fprintf(stderr, "[FastCommunication] Memory allocation failure\n");
+           exit(1);
+        }
+
+       for (int i = 0; i < MAXNOREQ; i++) {
+               _request[i].valid = false;
+       }
+}
+
+/*
+ * Deconstructor
+ */
+FastCommunication::~FastCommunication() {
+       delete _fifos;
+}
+
+/*
+ * Register an additional FIFO in the Communication
+ */
+bool FastCommunication::addFifo(int fifoNr, Fifo* fifo, int type,
+               int queue) {
+       _fifos[fifoNr].fifo = fifo;
+       _fifos[fifoNr].queue = queue;
+       _fifos[fifoNr].type = type;
+       _fifos[fifoNr].iswfifo = false;
+
+       // Per FIFO queue, we need two requests, but we can store at max MAXNOREQ of them
+       if (_nrOfRequest < MAXNOREQ) {
+               _nrOfRequest += 2;
+       }
+
+       return true;
+}
+
+/*
+ * Register an additional WindowedFIFO in the Communication
+ */
+bool FastCommunication::addWFifo(int fifoNr, WindowedFifo* wfifo,
+               int type, int queue) {
+       _fifos[fifoNr].wfifo = wfifo;
+       _fifos[fifoNr].queue = queue;
+       _fifos[fifoNr].type = type;
+       _fifos[fifoNr].iswfifo = true;
+
+       // Per FIFO queue, we need two requests, but we can store at max MAXNOREQ of them
+       if (_nrOfRequest < MAXNOREQ) {
+               _nrOfRequest += 2;
+       }
+
+       return true;
+}
+
+/*
+ * Communication: Main update procedure to update
+ */
+bool FastCommunication::update() {
+       // Check if you have received a new message
+       for (int processNr = 0; processNr < NUM_SPES; processNr++) {
+               if (!spe_out_mbox_status((spe_context*) _context_all[processNr]))
+                       continue;
+
+               uint32_t messageIn;
+               spe_out_mbox_read((spe_context*) _context_all[processNr],
+                               &messageIn, 1);
+
+               uint32_t code = GETFASTCODE(messageIn);
+               uint32_t queue = GETFASTQUEUE(messageIn);
+               uint32_t len = GETFASTLEN(messageIn);
+
+               /***************************************************************************************************************/
+               if (code == SPE_READ_DEMAND) // Have to send a write address (1) --> (2)
+               {
+                       // Find out for which fifo the request is
+                       fifoCollection *fifocol = NULL;
+                       int i;
+                       for (i = 0; i < _nrOfQueues; i++) {
+                               if (_fifos[i].queue == queue) {
+                                       fifocol = &_fifos[i];
+                                       break;
+                               }
+                       }
+
+                       // The queue was not found
+                       if (fifocol == NULL) {
+                               printf("PPU COM> ERROR, this queue does not exists!\n");
+                               return false;
+                       }
+
+                       // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+                       // Windowed FIFO
+                       if (_fifos[i].iswfifo) {
+                               WindowedFifo* wfifo = NULL;
+                               wfifo = _fifos[i].wfifo;
+
+                               uint32_t inTail = wfifo->_inTail;
+
+                               // This is the len we like to read
+                               len = len > (wfifo->unused()) ? wfifo->unused() : len;
+
+                               // We can read something
+                               if (len > 0) {
+                                       // Write all information we used to the request-memory
+                                       comRequest* request = newRequest();
+
+                                       // Has no memory to store a new request
+                                       if (request == NULL) {
+                                               // not possible to start a request for this SPE --> Send len = 0
+                                               uint32_t message = CREATEFASTMESSAGE(
+                                                               SPE_READ_COMPLETE, queue, 0);
+                                               sendMessage(message, queueFromSPE[queue]);
+                                               return false;
+                                       }
+
+                                       uint64_t baseAddress = _fifoTails[queue] + inTail;
+                                       while (baseAddress % ALIGNMENT_FACTOR_POWER2 != 0)
+                                               baseAddress--;
+
+                                       // Offset
+                                       uint32_t offset = _fifoTails[queue] + inTail
+                                                       - baseAddress;
+
+                                       // Store all data in the request buffer
+                                       request->data = (char *) _malloc_align(roundDMA(len
+                                                       + offset), ALIGNMENT_FACTOR);
+                                       request->len = len;
+                                       request->wfifo = wfifo;
+                                       request->iswfifo = true;
+                                       request->status = read_started;
+                                       request->queue = queue;
+                                       request->offset = offset;
+
+                                       int ret;
+                                       do {
+                                               ret = spe_mfcio_put(
+                                                               (spe_context*) _context_all[processNr],
+                                                               baseAddress, (void *) &(request->data[0]),
+                                                               roundDMA(len + offset), request->tag_id,
+                                                               0, 0);
+                                       } while (ret != 0);
+                               } else {
+#ifndef STORE_REQUESTS                // Send len = 0 back to the sender, this one should try it in a later phase
+                                       uint32_t message = CREATEFASTMESSAGE(
+                                                       SPE_READ_COMPLETE, queue, 0);
+                                       sendMessage(message, queueFromSPE[queue]);
+#else                                // I store the request and may try in a later time to start the transfer
+                                       comRequest* request = newRequest();
+
+                                       // Has no memory to store a new request
+                                       if (request == NULL)
+                                       {
+                                               // not possible to start a request for this SPE --> Send len = 0
+                                               uint32_t message = CREATEFASTMESSAGE(SPE_READ_COMPLETE, queue, 0);
+                                               sendMessage(message, queueFromSPE[queue]);
+                                               return false;
+                                       }
+
+                                       request->len = len;
+                                       request->wfifo = wfifo;
+                                       request->iswfifo = true;
+                                       request->status = read_pending;
+                                       request->queue = queue;
+#endif
+                               }
+
+                       }
+
+                       // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+                       // Classic FIFO
+                       else {
+                               Fifo* fifo = NULL;
+                               fifo = _fifos[i].fifo;
+
+                               uint32_t inTail = fifo->_inTail;
+
+                               // This is the len we like to read
+                               len = len > (fifo->unused()) ? fifo->unused() : len;
+
+                               // We can read something
+                               if (len > 0) {
+                                       // Write all information we used to the request-memory
+                                       comRequest* request = newRequest();
+
+                                       // Has no memory to store a new request
+                                       if (request == NULL) {
+                                               // not possible to start a request for this SPE --> Send len = 0
+                                               uint32_t message = CREATEFASTMESSAGE(
+                                                               SPE_READ_COMPLETE, queue, 0);
+                                               sendMessage(message, queueFromSPE[queue]);
+                                               return false;
+                                       }
+
+                                       uint64_t baseAddress = _fifoTails[queue] + inTail;
+                                       while (baseAddress % ALIGNMENT_FACTOR_POWER2 != 0)
+                                               baseAddress--;
+
+                                       // Offset
+                                       uint32_t offset = _fifoTails[queue] + inTail
+                                                       - baseAddress;
+
+                                       // Store all data in the request buffer
+                                       request->data = (char *) _malloc_align(roundDMA(len
+                                                       + offset), ALIGNMENT_FACTOR);
+                                       request->len = len;
+                                       request->fifo = fifo;
+                                       request->iswfifo = false;
+                                       request->status = read_started;
+                                       request->queue = queue;
+                                       request->offset = offset;
+
+                                       int ret;
+                                       do {
+                                               ret = spe_mfcio_put(
+                                                               (spe_context*) _context_all[processNr],
+                                                               baseAddress, (void *) &(request->data[0]),
+                                                               roundDMA(len + offset), request->tag_id,
+                                                               0, 0);
+                                       } while (ret != 0);
+                               }
+
+                               // Len == 0
+                               else {
+#ifndef STORE_REQUESTS                // Send len = 0 back to the sender, this one should try it in a later phase
+                                       uint32_t message = CREATEFASTMESSAGE(
+                                                       SPE_READ_COMPLETE, queue, 0);
+                                       sendMessage(message, queueFromSPE[queue]);
+#else                                // I store the request and may try in a later time to start the transfer
+                                       comRequest* request = newRequest();
+
+                                       // Has no memory to store a new request
+                                       if (request == NULL) {
+                                               // not possible to start a request for this SPE --> Send len = 0
+                                               uint32_t message = CREATEFASTMESSAGE(SPE_READ_COMPLETE, queue, 0);
+                                               sendMessage(message, queueFromSPE[queue]);
+                                               return false;
+                                       }
+
+                                       request->len = len;
+                                       request->fifo = fifo;
+                                       request->iswfifo = false;
+                                       request->status = read_pending;
+                                       request->queue = queue;
+#endif
+                               }
+                       }
+               }
+
+               /***************************************************************************************************************/
+               else if (code == SPE_READ_COMPLETE) // Can finish a write request (4) --> (5)
+               {
+                       // Get the stored request
+                       comRequest* request = getRequest(read_request_sent, queue);
+
+                       if (request == NULL) {
+                               printf("PPU Communicate> Couldn't find the request\n");
+                               return 0;
+                       }
+
+                       // Inform my FIFO that the request is completed
+                       if (request->iswfifo)
+                               request->wfifo->dmaRead(len);
+                       else
+                               request->fifo->dmaRead(len);
+
+                       // Request free
+                       deleteRequest(request);
+               }
+
+               /***************************************************************************************************************/
+               else if (code == SPE_COMPLETE) // One SPE has finished
+               {
+                       _nrSpeComplete++;
+               }
+       }
+
+       /***************************************************************************************************************/
+       // Check if some active write processes have finished (4)
+       uint8_t req = _currentRequest;
+
+       for (int i = 0; i < _nrOfRequest; i++) {
+               if (_request[req].valid) {
+                       if (_request[req].status == read_started) {
+                               if (!(testMessage(queueFromSPE[_request[req].queue]) > 0))
+                                       continue;
+
+                               uint32_t tag_id = _request[req].tag_id;
+                               uint32_t ret;
+                               uint32_t
+                                               test =
+                                                               spe_mfcio_tag_status_read(
+                                                                               (spe_context*) _context_all[queueFromSPE[_request[req].queue]],
+                                                                               0, SPE_TAG_IMMEDIATE, &ret);
+
+                               if (((ret & (1 << tag_id)) == 0)) {
+                                       // Have to write the data into the fifo
+                                       if (_request[req].iswfifo) { // WFIFO
+
+                                               _request[req].wfifo->dmaWrite(
+                                                               (char *) _request[req].data
+                                                                               + _request[req].offset,
+                                                               _request[req].len);
+                                               _free_align(_request[req].data);
+
+                                               // Increase the inTail value (used to know where the last request was started)
+
+                                               _request[req].wfifo->_inTail
+                                                               = (_request[req].wfifo->_inTail
+                                                                               + _request[req].len) % (FIFO_SIZE[_request[req].queue]);
+                                       } else { // FIFO
+                                               _request[req].fifo->write(
+                                                               (char *) _request[req].data
+                                                                               + _request[req].offset,
+                                                               _request[req].len);
+                                               _free_align(_request[req].data);
+
+                                               _request[req].fifo->_inTail
+                                                               = (_request[req].fifo->_inTail
+                                                                               + _request[req].len) % (FIFO_SIZE[_request[req].queue]);
+
+                                       }
+
+                                       uint32_t message = CREATEFASTMESSAGE(
+                                                       SPE_READ_COMPLETE, _request[req].queue,
+                                                       _request[req].len);
+                                       sendMessage(message, queueFromSPE[_request[req].queue]);
+
+                                       deleteRequest(&_request[req]);
+                                       _currentRequest = (req + 1) % _nrOfRequest;
+
+                                       break;
+                               }
+                       } else if (_request[req].status == read_pending) { // Has an open request for sending
+
+                               // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+                               // Windowed FIFO
+                               if (_request[req].iswfifo) {
+
+                                       if (_request[req].wfifo->unused() > 0) {
+                                               comRequest *request = &_request[req];
+
+                                               // This is the len we like to read
+                                               uint32_t len = request->len > (request->wfifo->unused()) ? request->wfifo->unused()
+                                                               : request->len;
+
+                                               uint32_t inTail = request->wfifo->_inTail;
+                                               uint64_t baseAddress = _fifoTails[request->queue]
+                                                               + inTail;
+
+                                               while (baseAddress % ALIGNMENT_FACTOR_POWER2 != 0)
+                                                       baseAddress--;
+
+                                               // Offset --> How much we had to align
+                                               uint32_t offset = _fifoTails[request->queue]
+                                                               + inTail - baseAddress;
+
+                                               request->data = (char *) _malloc_align(roundDMA(
+                                                               len + offset), ALIGNMENT_FACTOR);
+                                               request->len = len;
+                                               request->status = read_started;
+                                               request->offset = offset;
+
+                                               // Set up the request in the MFC
+                                               int ret;
+                                               do {
+                                                       ret = spe_mfcio_put(
+                                                                       (spe_context*) _context_all[queueFromSPE[_request[req].queue]],
+                                                                       baseAddress,
+                                                                       (void *) &(request->data[0]),
+                                                                       roundDMA(len + offset),
+                                                                       request->tag_id, 0, 0);
+                                               } while (ret != 0);
+                                       }
+                               }
+
+                               // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+                               // Classic FIFO
+                               else {
+
+                                       if (_request[req].fifo->unused() > 0) {
+                                               comRequest *request = &_request[req];
+
+                                               // This is the len we like to read
+                                               uint32_t len =  request->len > (request->fifo->unused()) ? request->fifo->unused() : request->len;
+
+                                               uint32_t inTail = request->fifo->_inTail;
+                                               uint64_t baseAddress = _fifoTails[request->queue]
+                                                               + inTail;
+
+                                               while (baseAddress % 16 != 0)
+                                                       baseAddress--;
+
+                                               // Offset
+                                               uint32_t offset = _fifoTails[request->queue]
+                                                               + inTail - baseAddress;
+
+                                               // Store all data in the request buffer
+                                               request->data = (char *) _malloc_align(roundDMA(
+                                                               len + offset), ALIGNMENT_FACTOR);
+                                               request->len = len;
+                                               request->status = read_started;
+                                               request->offset = offset;
+
+                                               // Start the DMA transfer
+                                               int ret;
+                                               do {
+                                                       ret
+                                                                       = spe_mfcio_put(
+                                                                                       (spe_context*) _context_all[queueFromSPE[_request[req].queue]],
+                                                                                       baseAddress,
+                                                                                       (void *) &(request->data[0]),
+                                                                                       roundDMA(len + offset),
+                                                                                       request->tag_id, 0, 0);
+                                               } while (ret != 0);
+                                       }
+                               }
+                       }
+               }
+               req = (req + 1) % _nrOfRequest;
+       }
+
+       /***************************************************************************************************************/
+       // Have some out - queue some data to send
+       for (int i = 0; i < _nrOfQueues; i++) {
+               if (_fifos[i].type == this->out) {
+                       if (_fifos[i].iswfifo) { // WFIFO
+                               // Start only a write process if there is really enough place in the outbound mailbox
+                               if (_fifos[i].wfifo->dmaAllowed()
+                                               && _fifos[i].wfifo->used() > 0) {
+                                       uint32_t queue = _fifos[i].queue;
+
+                                       // Can we send a message to this processor or is it blocked?
+                                       if (testMessage(queueOnSPE[queue]) <= 0) {
+                                               continue;
+                                       }
+
+                                       // Write all information we used to the request-memory
+                                       comRequest* request = newRequest();
+
+                                       // Has no memory to store a new request
+                                       if (request == NULL) {
+                                       } else {
+                                               uint32_t len = _fifos[i].wfifo->dmaStart();
+                                               // Create a write-demand message
+                                               uint32_t message = CREATEFASTMESSAGE(
+                                                               SPE_READ_DEMAND, queue, len);
+
+                                               request->len = len;
+                                               request->queue = queue;
+                                               request->status = read_request_sent;
+                                               request->wfifo = _fifos[i].wfifo;
+                                               request->iswfifo = true;
+
+                                               sendMessage(message, queueOnSPE[queue]);
+                                       }
+                                       break;
+                               }
+                       } else { // FIFO
+                               // Start only a write process if there is really enough place in the outbound mailbox
+                               if (_fifos[i].fifo->dmaAllowed() && _fifos[i].fifo->used()
+                                               > 0) {
+                                       uint32_t queue = _fifos[i].queue;
+
+                                       if (testMessage(queueOnSPE[queue]) <= 0)
+                                               continue;
+
+                                       // Write all information we used to the request-memory
+                                       comRequest* request = newRequest();
+
+                                       // Has no memory to store a new request
+                                       if (request == NULL) {
+                                       } else {
+                                               uint32_t len = _fifos[i].fifo->dmaStart();
+                                               uint32_t message = CREATEFASTMESSAGE(
+                                                               SPE_READ_DEMAND, queue, len);
+                                               request->len = len;
+                                               request->queue = queue;
+                                               request->status = read_request_sent;
+                                               request->fifo = _fifos[i].fifo;
+                                               request->iswfifo = false;
+
+                                               sendMessage(message, queueOnSPE[queue]);
+                                       }
+                                       break;
+                               }
+                       }
+               }
+       }
+       return true;
+}
+
+/*
+ * Create a new request to store in the cache
+ *
+ */
+comRequest* FastCommunication::newRequest() {
+       for (int i = 0; i < _nrOfRequest; i++) {
+               if (!_request[i].valid) {
+                       _request[i].tag_id = i + 3;
+                       _request[i].valid = true;
+                       return &(_request[i]);
+               }
+       }
+
+       return NULL;
+}
+
+/*
+ * Delete the request
+ *
+ */
+void FastCommunication::deleteRequest(comRequest* request) {
+       request->valid = false;
+       request->tag_id = 0;
+}
+
+/*
+ * Returns the request one like to get
+ */
+comRequest* FastCommunication::getRequest(uint8_t status, uint32_t queue) {
+       uint8_t req = _currentRequest;
+
+       for (int i = 0; i < _nrOfRequest; i++) {
+               if (_request[req].valid && _request[req].status == status
+                               && _request[req].queue == queue) {
+                       _currentRequest = (req + 1) % _nrOfRequest;
+                       return &(_request[req]);
+               }
+               req = (req + 1) % _nrOfRequest;
+       }
+
+       return NULL;
+}
+
+/**
+ * True if no communication is necessary, false if there is active communication
+ */
+bool FastCommunication::empty() { /////////////////////////////////////////////////////////////////
+       // check if one fifo would like to send something to another SPE
+       for (int i = 0; i < _nrOfQueues; i++) {
+               if (_fifos[i].type == this->out) {
+                       if (_fifos[i].iswfifo) { // WFIFO
+                               if (_fifos[i].wfifo->used() > 0) 
+                               {
+                                       return false;
+                               }
+                       } else {
+                               if (_fifos[i].fifo->used() > 0) 
+                               {
+                                       return false;
+                               }               
+                       }
+               }
+       }
+
+       // are open sendings?
+       for (int i = 0; i < _nrOfRequest; i++) {
+               if (_request[i].valid) {
+                       return false;
+               }
+       }
+
+       if (_nrSpeComplete < NUM_SPES) {
+               return false;
+       }
+
+       return true;
+}
+
+/**
+ * Send a message to a SPE
+ */
+void FastCommunication::sendMessage(uint32_t message, int32_t process) {
+       spe_in_mbox_write((spe_context*) _context_all[process],
+                       (uint32_t*) &message, 1, SPE_MBOX_ANY_NONBLOCKING);
+}
+
+/**
+ * Test if we can send a message to an SPE without stalling
+ */
+int FastCommunication::testMessage(int32_t process) {
+       return spe_in_mbox_status((spe_context*) _context_all[process]);
+}
+