////////////////////////////////////////////////////////////////////////////////// // File : mwmr_channel_.c // Date : 01/04/2012 // Author : alain greiner // Copyright (c) UPMC-LIP6 /////////////////////////////////////////////////////////////////////////////////// // The mwmr_channel.c and mwmr_channel.h files are part of the GIET nano-kernel. // This middleware implements a user level Multi-Writers / Multi-Readers // communication channel, that can be used by parallel multi-tasks applications // respecting the TCG (Tasks and Communications Graph) formalism. // // The mwmr_read() and mwmr_write() functions do not require a system call. // The channel itself must have been allocated in a non cacheable segment, // if the platform does not provide hardware cache coherence. // // ALL MWMR channels must be defined in the mapping_info data structure, // to be initialised by the GIET in the boot phase. // The vobj_get_vbase() system call (defined in stdio.c and stdio.h files) // can be used to get the virtual base address of the channel from it's name. // // An MWMR transaction transfer an integer number of items, and an item is // an integer number of unsigned int (32 bits words). // The max number of words that can be stored in a MWMR channel is defined by the // "depth" parameter, and the "width" parameter define the minimal number of // word contained in an atomic item. Therefore, the "depth" parameter must be // a multiple of the "width" parameter. // // Both the mwmr_read() and mwmr_write() functions are blocking functions. // A private lock provides exclusive access to the MWMR channel, that can have // a variable number of producers and a variable number of consumers. /////////////////////////////////////////////////////////////////////////////////// #include #include ////////////////////////////////////////////////////////////////////////////// // mwmr_lock_aquire() // This blocking function returns only when the lock has been taken. // If the lock is already taken a fixed delay is introduced before retry. ////////////////////////////////////////////////////////////////////////////// void mwmr_lock_acquire(unsigned int * lock_address) { register unsigned int * plock = lock_address; register unsigned int delay = 100; asm volatile ( "1: \n" "ll $2, 0(%0) \n" /* $2 <= lock current value */ "bnez $2, 2f \n" /* retry after delay if lock busy */ "li $3, 1 \n" /* $3 <= argument for sc */ "sc $3, 0(%0) \n" /* try to get lock */ "bnez $3, 3f \n" /* exit if atomic */ "2: \n" "move $4, %1 \n" /* $4 <= delay */ "4: \n" "beqz $4, 4b \n" /* test end delay */ "addi $4, $4, -1 \n" /* $4 <= $4 - 1 */ "j 1b \n" /* retry ll */ "nop \n" "3: \n" : :"r"(plock), "r"(delay) :"$2", "$3", "$4"); } ////////////////////////////////////////////////////////////////////////////// // nb_mwmr_write() // This is a non-blocking function. // The nitems parameter is the number of items to be transfered. // The requested transfer is therefore (nitems * width) words. // It takes the lock for exclusive access before testing the channel state. // If there is not enough data in mwmr channel to read nitems, // it reads as many items as possible, releases the lock, and returns // the number of read items (it can be 0). ////////////////////////////////////////////////////////////////////////////// unsigned int nb_mwmr_write(mwmr_channel_t * mwmr, unsigned int * buffer, unsigned int nitems) { unsigned int x; unsigned int spaces; // number of empty slots (in words) unsigned int nwords; // requested transfer length (in words) unsigned int depth; // channel depth (in words) unsigned int width; // channel width (in words) unsigned int sts; // channel sts unsigned int ptw; // channel ptw if (nitems == 0) { return 0; } // get the lock mwmr_lock_acquire(&mwmr->lock); // access fifo status depth = mwmr->depth; width = mwmr->width; sts = mwmr->sts; ptw = mwmr->ptw; spaces = depth - sts; nwords = width * nitems; if (spaces >= nwords) { // transfer nitems, release lock and return for (x = 0; x < nwords; x++) { mwmr->data[ptw] = buffer[x]; if ((ptw + 1) == depth) { ptw = 0; } else { ptw = ptw + 1; } } mwmr->sts = mwmr->sts + nwords; mwmr->ptw = ptw; mwmr->lock = 0; return nitems; } else if (spaces < width) { // release lock and return mwmr->lock = 0; return 0; } else { // transfer as many items as possible, release lock and return nwords = (spaces / width) * width; // integer number of items for (x = 0; x < nwords; x++) { mwmr->data[ptw] = buffer[x]; if ((ptw + 1) == depth) { ptw = 0; } else { ptw = ptw + 1; } } mwmr->sts = sts + nwords; mwmr->ptw = ptw; mwmr->lock = 0; return (nwords / width); } } // end nb_mwmr_write() ////////////////////////////////////////////////////////////////////////////// // mwmr_write() // This blocking function returns only when the transfer is completed. // The nitems parameter is the number of items to be transfered. // The requested transfer is therefore (nitems * width) words. // It takes the lock for exclusive access before testing the channel state. // If there is not enough space in mwmr channel to write nitems, // it writes as many items as possible, releases the lock, and retry // after a random delay. ////////////////////////////////////////////////////////////////////////////// void mwmr_write(mwmr_channel_t * mwmr, unsigned int * buffer, unsigned int nitems) { unsigned int x; unsigned int spaces; // number of empty slots (in words) unsigned int nwords; // requested transfer length (in words) unsigned int depth; // channel depth (in words) unsigned int width; // channel width (in words) unsigned int sts; // channel sts unsigned int ptw; // channel ptw if (nitems == 0) { return; } while (1) { // get the lock mwmr_lock_acquire(&mwmr->lock); // compute spaces and nwords depth = mwmr->depth; width = mwmr->width; sts = mwmr->sts; ptw = mwmr->ptw; spaces = depth - sts; nwords = width * nitems; if (spaces >= nwords) { // write nwords, release lock and return for (x = 0; x < nwords; x++) { mwmr->data[ptw] = buffer[x]; if ((ptw + 1) == depth) { ptw = 0; } else { ptw = ptw + 1; } } mwmr->ptw = ptw; mwmr->sts = sts + nwords; mwmr->lock = 0; return; } else if (spaces < width) { // release lock and retry after delay mwmr->lock = 0; } else { // write as many items as possible, release lock and retry after delay nwords = (spaces / width) * width; // integer number of items for (x = 0; x < nwords; x++) { mwmr->data[ptw] = buffer[x]; if ((ptw + 1) == depth) { ptw = 0; } else { ptw = ptw + 1; } } mwmr->sts = sts + nwords; mwmr->ptw = ptw; buffer = buffer + nwords; nitems = nitems - (nwords/width); mwmr->lock = 0; } giet_context_switch(); } } // end mwmr_write() ////////////////////////////////////////////////////////////////////////////// // nb_mwmr_read() // This is a non-blocking function. // The nitems parameter is the number of items to be transfered. // The requested transfer is therefore (nitems * width) words. // It takes the lock for exclusive access before testing the channel state. // If there is not enough data in mwmr channel to read nitems, // it reads as many items as possible, releases the lock, and returns // the number of read items (it can be 0). ////////////////////////////////////////////////////////////////////////////// unsigned int nb_mwmr_read(mwmr_channel_t * mwmr, unsigned int * buffer, unsigned int nitems) { unsigned int x; unsigned int nwords; // requested transfer length (in words) unsigned int depth; // channel depth (in words) unsigned int width; // channel width (in words) unsigned int sts; // channel sts unsigned int ptr; // channel ptr if (nitems == 0) { return 0; } // get the lock mwmr_lock_acquire(&mwmr->lock); // access fifo status depth = mwmr->depth; width = mwmr->width; sts = mwmr->sts; ptr = mwmr->ptr; nwords = width * nitems; if (sts >= nwords) { // transfer nitems, release lock and return for (x = 0; x < nwords; x++) { buffer[x] = mwmr->data[ptr]; if ((ptr + 1) == depth) { ptr = 0; } else { ptr = ptr + 1; } } mwmr->sts = mwmr->sts - nwords; mwmr->ptr = ptr; mwmr->lock = 0; return nitems; } else if (sts < width) { // release lock and return mwmr->lock = 0; return 0; } else { // transfer as many items as possible, release lock and return nwords = (sts / width) * width; // integer number of items for (x = 0 ; x < nwords ; x++) { buffer[x] = mwmr->data[ptr]; if ((ptr + 1) == depth) { ptr = 0; } else { ptr = ptr + 1; } } mwmr->sts = sts - nwords; mwmr->ptr = ptr; mwmr->lock = 0; return (nwords / width); } } // nb_mwmr_read() ////////////////////////////////////////////////////////////////////////////// // mwmr_read() // This blocking function returns only when the transfer is completed. // The nitems parameter is the number of items to be transfered. // The requested transfer is therefore (nitems * width) words. // It takes the lock for exclusive access before testing the channel state. // If there is not enough data in mwmr channel to read nitems, // it reads as many items as possible, releases the lock, and retry // after a random delay. ////////////////////////////////////////////////////////////////////////////// void mwmr_read( mwmr_channel_t * mwmr, unsigned int * buffer, unsigned int nitems) { unsigned int x; unsigned int nwords; // requested transfer length (in words) unsigned int depth; // channel depth (in words) unsigned int width; // channel width (in words) unsigned int sts; // channel sts unsigned int ptr; // channel ptr if (nitems == 0) { return; } while (1) { // get the lock mwmr_lock_acquire(&mwmr->lock); // compute nwords depth = mwmr->depth; width = mwmr->width; sts = mwmr->sts; ptr = mwmr->ptr; nwords = width * nitems; if (sts >= nwords) { // read nwords, release lock and return for (x = 0; x < nwords; x++) { buffer[x] = mwmr->data[ptr]; if ((ptr + 1) == depth) { ptr = 0; } else { ptr = ptr + 1; } } mwmr->sts = mwmr->sts - nwords; mwmr->ptr = ptr; mwmr->lock = 0; return; } else if (sts < width) { // release lock and retry after delay mwmr->lock = 0; } else { // read as many items as possible, release lock and retry after delay nwords = (sts / width) * width; // integer number of items for (x = 0; x < nwords; x++) { buffer[x] = mwmr->data[ptr]; if ((ptr + 1) == depth) { ptr = 0; } else { ptr = ptr + 1; } } mwmr->sts = sts - nwords; mwmr->ptr = ptr; buffer = buffer + nwords; nitems = nitems - (nwords/width); mwmr->lock = 0; } giet_context_switch(); } } // end mwmr_read() // Local Variables: // tab-width: 4 // c-basic-offset: 4 // c-file-offsets:((innamespace . 0)(inline-open . 0)) // indent-tabs-mode: nil // End: // vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4