| [258] | 1 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 2 | // File     : mwmr_channel_.c | 
|---|
|  | 3 | // Date     : 01/04/2012 | 
|---|
|  | 4 | // Author   : alain greiner | 
|---|
|  | 5 | // Copyright (c) UPMC-LIP6 | 
|---|
|  | 6 | /////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 7 | // The mwmr_channel.c and mwmr_channel.h files are part of the GIET nano-kernel. | 
|---|
|  | 8 | // This  middleware implements a user level Multi-Writers / Multi-Readers | 
|---|
|  | 9 | // communication channel, that can be used by parallel multi-tasks applications | 
|---|
|  | 10 | // respecting the TCG (Tasks and Communications Graph) formalism. | 
|---|
|  | 11 | // | 
|---|
|  | 12 | // The mwmr_read() and mwmr_write() functions do not require a system call. | 
|---|
|  | 13 | // The channel itself must have been allocated in a non cacheable segment, | 
|---|
|  | 14 | // if the platform does not provide hardware cache coherence. | 
|---|
|  | 15 | // | 
|---|
|  | 16 | // ALL MWMR channels must be defined in the mapping_info data structure, | 
|---|
|  | 17 | // to be initialised by the GIET in the boot phase. | 
|---|
|  | 18 | // The vobj_get_vbase() system call (defined in stdio.c and stdio.h files) | 
|---|
|  | 19 | // can be used to get the virtual base address of the channel from it's name. | 
|---|
|  | 20 | // | 
|---|
|  | 21 | // An MWMR transaction transfer    an integer number of items, and an item is | 
|---|
|  | 22 | // an integer number of unsigned int (32 bits words). | 
|---|
|  | 23 | // The max number of words that can be stored in a MWMR channel is defined by the | 
|---|
|  | 24 | // "depth" parameter, and the "width" parameter define the minimal number of | 
|---|
|  | 25 | // word contained in an atomic item. Therefore, the "depth" parameter must be | 
|---|
|  | 26 | // a multiple of the "width" parameter. | 
|---|
|  | 27 | // | 
|---|
|  | 28 | // Both the mwmr_read() and mwmr_write() functions are blocking functions. | 
|---|
|  | 29 | // A private lock provides exclusive access to the MWMR channel, that can have | 
|---|
|  | 30 | // a variable number of producers and a variable number of consumers. | 
|---|
|  | 31 | /////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 32 |  | 
|---|
|  | 33 | #include <mwmr_channel.h> | 
|---|
|  | 34 | #include <stdio.h> | 
|---|
|  | 35 |  | 
|---|
|  | 36 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 37 | //  mwmr_lock_aquire() | 
|---|
|  | 38 | // This blocking function returns only when the lock has been taken. | 
|---|
|  | 39 | // If the lock is already taken a fixed delay is introduced before retry. | 
|---|
|  | 40 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 41 | void mwmr_lock_acquire(unsigned int * lock_address) { | 
|---|
|  | 42 | register unsigned int * plock = lock_address; | 
|---|
|  | 43 | register unsigned int delay = 100; | 
|---|
|  | 44 | asm volatile ( | 
|---|
|  | 45 | "mwmr_lock_try:                    \n" | 
|---|
|  | 46 | "ll   $2,    0(%0)                \n" /* $2 <= lock current value */ | 
|---|
|  | 47 | "bnez $2,    mwmr_lock_delay    \n" /* retry after delay if lock busy */ | 
|---|
|  | 48 | "li   $3,    1                    \n" /* $3 <= argument for sc */ | 
|---|
|  | 49 | "sc   $3,    0(%0)                \n" /* try to get lock */ | 
|---|
|  | 50 | "bnez $3,    mwmr_lock_ok        \n" /* exit if atomic */ | 
|---|
|  | 51 | "mwmr_lock_delay:                \n" | 
|---|
|  | 52 | "move $4,    %1                 \n" /* $4 <= delay */ | 
|---|
|  | 53 | "mwmr_lock_loop:                \n" | 
|---|
|  | 54 | "beqz $4,    mwmr_lock_loop        \n" /* test end delay */ | 
|---|
|  | 55 | "addi $4,    $4,  -1            \n" /* $4 <= $4 - 1 */ | 
|---|
|  | 56 | "j           mwmr_lock_try        \n" /* retry ll */ | 
|---|
|  | 57 | "nop                            \n" | 
|---|
|  | 58 | "mwmr_lock_ok:                    \n" | 
|---|
|  | 59 | : | 
|---|
|  | 60 | :"r"(plock), "r"(delay) | 
|---|
|  | 61 | :"$2", "$3", "$4"); | 
|---|
|  | 62 | } | 
|---|
|  | 63 |  | 
|---|
|  | 64 |  | 
|---|
|  | 65 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 66 | //       nb_mwmr_write() | 
|---|
|  | 67 | // This is a non-blocking function. | 
|---|
|  | 68 | // The nitems parameter is the number of items to be transfered. | 
|---|
|  | 69 | // The requested transfer is therefore (nitems * width) words. | 
|---|
|  | 70 | // It takes the lock for exclusive access before testing the channel state. | 
|---|
|  | 71 | // If there is not enough data in mwmr channel to read nitems, | 
|---|
|  | 72 | // it reads as many items as possible, releases the lock, and returns | 
|---|
|  | 73 | // the number of read items (it can be 0). | 
|---|
|  | 74 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 75 | unsigned int nb_mwmr_write(mwmr_channel_t * mwmr, unsigned int * buffer, unsigned int nitems) { | 
|---|
|  | 76 | unsigned int x; | 
|---|
|  | 77 | unsigned int spaces; // number of empty slots (in words) | 
|---|
|  | 78 | unsigned int nwords; // requested transfer length (in words) | 
|---|
|  | 79 | unsigned int depth;  // channel depth (in words) | 
|---|
|  | 80 | unsigned int width;  // channel width (in words) | 
|---|
|  | 81 | unsigned int sts;    // channel sts | 
|---|
|  | 82 | unsigned int ptw;    // channel ptw | 
|---|
|  | 83 |  | 
|---|
|  | 84 | if (nitems == 0) { | 
|---|
|  | 85 | return 0; | 
|---|
|  | 86 | } | 
|---|
|  | 87 |  | 
|---|
|  | 88 | // get the lock | 
|---|
|  | 89 | mwmr_lock_acquire(&mwmr->lock); | 
|---|
|  | 90 |  | 
|---|
|  | 91 | // access fifo status | 
|---|
|  | 92 | depth = mwmr->depth; | 
|---|
|  | 93 | width = mwmr->width; | 
|---|
|  | 94 | sts = mwmr->sts; | 
|---|
|  | 95 | ptw = mwmr->ptw; | 
|---|
|  | 96 | spaces = depth - sts; | 
|---|
|  | 97 | nwords = width * nitems; | 
|---|
|  | 98 |  | 
|---|
|  | 99 | if (spaces >= nwords) { // transfer nitems, release lock and return | 
|---|
|  | 100 | for (x = 0; x < nwords; x++) { | 
|---|
|  | 101 | mwmr->data[ptw] = buffer[x]; | 
|---|
|  | 102 | if ((ptw + 1) == depth) { | 
|---|
|  | 103 | ptw = 0; | 
|---|
|  | 104 | } | 
|---|
|  | 105 | else { | 
|---|
|  | 106 | ptw = ptw + 1; | 
|---|
|  | 107 | } | 
|---|
|  | 108 | } | 
|---|
|  | 109 | mwmr->sts = mwmr->sts + nwords; | 
|---|
|  | 110 | mwmr->ptw = ptw; | 
|---|
|  | 111 | mwmr->lock = 0; | 
|---|
|  | 112 | return nitems; | 
|---|
|  | 113 | } | 
|---|
|  | 114 | else if (spaces < width) { | 
|---|
|  | 115 | // release lock and return | 
|---|
|  | 116 | mwmr->lock = 0; | 
|---|
|  | 117 | return 0; | 
|---|
|  | 118 | } | 
|---|
|  | 119 | else { | 
|---|
|  | 120 | // transfer as many items as possible, release lock and return | 
|---|
|  | 121 | nwords = (spaces / width) * width;    // integer number of items | 
|---|
|  | 122 | for (x = 0; x < nwords; x++) { | 
|---|
|  | 123 | mwmr->data[ptw] = buffer[x]; | 
|---|
|  | 124 | if ((ptw + 1) == depth) { | 
|---|
|  | 125 | ptw = 0; | 
|---|
|  | 126 | } | 
|---|
|  | 127 | else { | 
|---|
|  | 128 | ptw = ptw + 1; | 
|---|
|  | 129 | } | 
|---|
|  | 130 | } | 
|---|
|  | 131 | mwmr->sts = sts + nwords; | 
|---|
|  | 132 | mwmr->ptw = ptw; | 
|---|
|  | 133 | mwmr->lock = 0; | 
|---|
|  | 134 | return (nwords / width); | 
|---|
|  | 135 | } | 
|---|
|  | 136 | } // end nb_mwmr_write() | 
|---|
|  | 137 |  | 
|---|
|  | 138 |  | 
|---|
|  | 139 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 140 | //    mwmr_write() | 
|---|
|  | 141 | // This blocking function returns only when the transfer is completed. | 
|---|
|  | 142 | // The nitems parameter is the number of items to be transfered. | 
|---|
|  | 143 | // The requested transfer is therefore (nitems * width) words. | 
|---|
|  | 144 | // It takes the lock for exclusive access before testing the channel state. | 
|---|
|  | 145 | // If there is not enough space in mwmr channel to write nitems, | 
|---|
|  | 146 | // it writes as many items as possible, releases the lock, and retry | 
|---|
|  | 147 | // after a random delay. | 
|---|
|  | 148 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 149 | void mwmr_write(mwmr_channel_t * mwmr, unsigned int * buffer, unsigned int nitems) { | 
|---|
|  | 150 | unsigned int x; | 
|---|
|  | 151 | unsigned int spaces; // number of empty slots (in words) | 
|---|
|  | 152 | unsigned int nwords; // requested transfer length (in words) | 
|---|
|  | 153 | unsigned int depth;  // channel depth (in words) | 
|---|
|  | 154 | unsigned int width;  // channel width (in words) | 
|---|
|  | 155 | unsigned int sts;    // channel sts | 
|---|
|  | 156 | unsigned int ptw;    // channel ptw | 
|---|
|  | 157 |  | 
|---|
|  | 158 | if (nitems == 0) { | 
|---|
|  | 159 | return; | 
|---|
|  | 160 | } | 
|---|
|  | 161 |  | 
|---|
|  | 162 | while (1) { | 
|---|
|  | 163 | // get the lock | 
|---|
|  | 164 | mwmr_lock_acquire(&mwmr->lock); | 
|---|
|  | 165 |  | 
|---|
|  | 166 | // compute spaces and nwords | 
|---|
|  | 167 | depth = mwmr->depth; | 
|---|
|  | 168 | width = mwmr->width; | 
|---|
|  | 169 | sts  = mwmr->sts; | 
|---|
|  | 170 | ptw  = mwmr->ptw; | 
|---|
|  | 171 | spaces = depth - sts; | 
|---|
|  | 172 | nwords = width * nitems; | 
|---|
|  | 173 |  | 
|---|
|  | 174 | if (spaces >= nwords) { | 
|---|
|  | 175 | // write nwords, release lock and return | 
|---|
|  | 176 | for (x = 0; x < nwords; x++) { | 
|---|
|  | 177 | mwmr->data[ptw] = buffer[x]; | 
|---|
|  | 178 | if ((ptw + 1) == depth) { | 
|---|
|  | 179 | ptw = 0; | 
|---|
|  | 180 | } | 
|---|
|  | 181 | else { | 
|---|
|  | 182 | ptw = ptw + 1; | 
|---|
|  | 183 | } | 
|---|
|  | 184 | } | 
|---|
|  | 185 | mwmr->ptw = ptw; | 
|---|
|  | 186 | mwmr->sts = sts + nwords; | 
|---|
|  | 187 | mwmr->lock = 0; | 
|---|
|  | 188 | return; | 
|---|
|  | 189 | } | 
|---|
|  | 190 | else if (spaces < width) { | 
|---|
|  | 191 | // release lock and retry after delay | 
|---|
|  | 192 | mwmr->lock = 0; | 
|---|
|  | 193 | } | 
|---|
|  | 194 | else { | 
|---|
|  | 195 | // write as many items as possible, release lock and retry after delay | 
|---|
|  | 196 | nwords = (spaces / width) * width;  // integer number of items | 
|---|
|  | 197 | for (x = 0; x < nwords; x++) { | 
|---|
|  | 198 | mwmr->data[ptw] = buffer[x]; | 
|---|
|  | 199 | if ((ptw + 1) == depth) { | 
|---|
|  | 200 | ptw = 0; | 
|---|
|  | 201 | } | 
|---|
|  | 202 | else { | 
|---|
|  | 203 | ptw = ptw + 1; | 
|---|
|  | 204 | } | 
|---|
|  | 205 | } | 
|---|
|  | 206 | mwmr->sts = sts + nwords; | 
|---|
|  | 207 | mwmr->ptw = ptw; | 
|---|
|  | 208 | buffer = buffer + nwords; | 
|---|
|  | 209 | nitems = nitems - (nwords/width); | 
|---|
|  | 210 | mwmr->lock = 0; | 
|---|
|  | 211 | } | 
|---|
|  | 212 | giet_context_switch(); | 
|---|
|  | 213 | } | 
|---|
|  | 214 | } // end mwmr_write() | 
|---|
|  | 215 |  | 
|---|
|  | 216 |  | 
|---|
|  | 217 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 218 | //       nb_mwmr_read() | 
|---|
|  | 219 | // This is a non-blocking function. | 
|---|
|  | 220 | // The nitems parameter is the number of items to be transfered. | 
|---|
|  | 221 | // The requested transfer is therefore (nitems * width) words. | 
|---|
|  | 222 | // It takes the lock for exclusive access before testing the channel state. | 
|---|
|  | 223 | // If there is not enough data in mwmr channel to read nitems, | 
|---|
|  | 224 | // it reads as many items as possible, releases the lock, and returns | 
|---|
|  | 225 | // the number of read items (it can be 0). | 
|---|
|  | 226 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 227 | unsigned int nb_mwmr_read(mwmr_channel_t * mwmr, unsigned int * buffer, unsigned int nitems) { | 
|---|
|  | 228 | unsigned int x; | 
|---|
|  | 229 | unsigned int nwords; // requested transfer length (in words) | 
|---|
|  | 230 | unsigned int depth;  // channel depth (in words) | 
|---|
|  | 231 | unsigned int width;  // channel width (in words) | 
|---|
|  | 232 | unsigned int sts;    // channel sts | 
|---|
|  | 233 | unsigned int ptr;    // channel ptr | 
|---|
|  | 234 |  | 
|---|
|  | 235 | if (nitems == 0) { | 
|---|
|  | 236 | return 0; | 
|---|
|  | 237 | } | 
|---|
|  | 238 |  | 
|---|
|  | 239 | // get the lock | 
|---|
|  | 240 | mwmr_lock_acquire(&mwmr->lock); | 
|---|
|  | 241 |  | 
|---|
|  | 242 | // access fifo status | 
|---|
|  | 243 | depth = mwmr->depth; | 
|---|
|  | 244 | width = mwmr->width; | 
|---|
|  | 245 | sts = mwmr->sts; | 
|---|
|  | 246 | ptr = mwmr->ptr; | 
|---|
|  | 247 | nwords = width * nitems; | 
|---|
|  | 248 |  | 
|---|
|  | 249 | if (sts >= nwords) { | 
|---|
|  | 250 | // transfer nitems, release lock and return | 
|---|
|  | 251 | for (x = 0; x < nwords; x++) { | 
|---|
|  | 252 | buffer[x] = mwmr->data[ptr]; | 
|---|
|  | 253 | if ((ptr + 1) == depth) { | 
|---|
|  | 254 | ptr = 0; | 
|---|
|  | 255 | } | 
|---|
|  | 256 | else { | 
|---|
|  | 257 | ptr = ptr + 1; | 
|---|
|  | 258 | } | 
|---|
|  | 259 | } | 
|---|
|  | 260 | mwmr->sts = mwmr->sts - nwords; | 
|---|
|  | 261 | mwmr->ptr = ptr; | 
|---|
|  | 262 | mwmr->lock = 0; | 
|---|
|  | 263 | return nitems; | 
|---|
|  | 264 | } | 
|---|
|  | 265 | else if (sts < width) { | 
|---|
|  | 266 | // release lock and return | 
|---|
|  | 267 | mwmr->lock = 0; | 
|---|
|  | 268 | return 0; | 
|---|
|  | 269 | } | 
|---|
|  | 270 | else { | 
|---|
|  | 271 | // transfer as many items as possible, release lock and return | 
|---|
|  | 272 | nwords = (sts / width) * width; // integer number of items | 
|---|
|  | 273 | for (x = 0 ; x < nwords ; x++) { | 
|---|
|  | 274 | buffer[x] = mwmr->data[ptr]; | 
|---|
|  | 275 | if ((ptr + 1) == depth) { | 
|---|
|  | 276 | ptr = 0; | 
|---|
|  | 277 | } | 
|---|
|  | 278 | else { | 
|---|
|  | 279 | ptr = ptr + 1; | 
|---|
|  | 280 | } | 
|---|
|  | 281 | } | 
|---|
|  | 282 | mwmr->sts = sts - nwords; | 
|---|
|  | 283 | mwmr->ptr = ptr; | 
|---|
|  | 284 | mwmr->lock = 0; | 
|---|
|  | 285 | return (nwords / width); | 
|---|
|  | 286 | } | 
|---|
|  | 287 | } // nb_mwmr_read() | 
|---|
|  | 288 |  | 
|---|
|  | 289 |  | 
|---|
|  | 290 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 291 | //    mwmr_read() | 
|---|
|  | 292 | // This blocking function returns only when the transfer is completed. | 
|---|
|  | 293 | // The nitems parameter is the number of items to be transfered. | 
|---|
|  | 294 | // The requested transfer is therefore (nitems * width) words. | 
|---|
|  | 295 | // It takes the lock for exclusive access before testing the channel state. | 
|---|
|  | 296 | // If there is not enough data in mwmr channel to read nitems, | 
|---|
|  | 297 | // it reads as many items as possible, releases the lock, and retry | 
|---|
|  | 298 | // after a random delay. | 
|---|
|  | 299 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 300 | void mwmr_read( mwmr_channel_t * mwmr, unsigned int * buffer, unsigned int nitems) { | 
|---|
|  | 301 | unsigned int x; | 
|---|
|  | 302 | unsigned int nwords; // requested transfer length (in words) | 
|---|
|  | 303 | unsigned int depth;  // channel depth (in words) | 
|---|
|  | 304 | unsigned int width;  // channel width (in words) | 
|---|
|  | 305 | unsigned int sts;    // channel sts | 
|---|
|  | 306 | unsigned int ptr;    // channel ptr | 
|---|
|  | 307 |  | 
|---|
|  | 308 | if (nitems == 0) { | 
|---|
|  | 309 | return; | 
|---|
|  | 310 | } | 
|---|
|  | 311 |  | 
|---|
|  | 312 | while (1) { | 
|---|
|  | 313 | // get the lock | 
|---|
|  | 314 | mwmr_lock_acquire(&mwmr->lock); | 
|---|
|  | 315 |  | 
|---|
|  | 316 | // compute nwords | 
|---|
|  | 317 | depth = mwmr->depth; | 
|---|
|  | 318 | width = mwmr->width; | 
|---|
|  | 319 | sts = mwmr->sts; | 
|---|
|  | 320 | ptr = mwmr->ptr; | 
|---|
|  | 321 | nwords = width * nitems; | 
|---|
|  | 322 |  | 
|---|
|  | 323 | if (sts >= nwords) { | 
|---|
|  | 324 | // read nwords, release lock and return | 
|---|
|  | 325 | for (x = 0; x < nwords; x++) { | 
|---|
|  | 326 | buffer[x] = mwmr->data[ptr]; | 
|---|
|  | 327 | if ((ptr + 1) == depth) { | 
|---|
|  | 328 | ptr = 0; | 
|---|
|  | 329 | } | 
|---|
|  | 330 | else { | 
|---|
|  | 331 | ptr = ptr + 1; | 
|---|
|  | 332 | } | 
|---|
|  | 333 | } | 
|---|
|  | 334 | mwmr->sts = mwmr->sts - nwords; | 
|---|
|  | 335 | mwmr->ptr = ptr; | 
|---|
|  | 336 | mwmr->lock = 0; | 
|---|
|  | 337 | return; | 
|---|
|  | 338 | } | 
|---|
|  | 339 | else if (sts < width) { | 
|---|
|  | 340 | // release lock and retry after delay | 
|---|
|  | 341 | mwmr->lock = 0; | 
|---|
|  | 342 | } | 
|---|
|  | 343 | else {   // read as many items as possible, release lock and retry after delay | 
|---|
|  | 344 | nwords = (sts / width) * width; // integer number of items | 
|---|
|  | 345 | for (x = 0; x < nwords; x++) { | 
|---|
|  | 346 | buffer[x] = mwmr->data[ptr]; | 
|---|
|  | 347 | if ((ptr + 1) == depth) { | 
|---|
|  | 348 | ptr = 0; | 
|---|
|  | 349 | } | 
|---|
|  | 350 | else { | 
|---|
|  | 351 | ptr = ptr + 1; | 
|---|
|  | 352 | } | 
|---|
|  | 353 | } | 
|---|
|  | 354 | mwmr->sts = sts - nwords; | 
|---|
|  | 355 | mwmr->ptr = ptr; | 
|---|
|  | 356 | buffer = buffer + nwords; | 
|---|
|  | 357 | nitems = nitems - (nwords/width); | 
|---|
|  | 358 | mwmr->lock = 0; | 
|---|
|  | 359 | } | 
|---|
|  | 360 | giet_context_switch(); | 
|---|
|  | 361 | } | 
|---|
|  | 362 | } // end mwmr_read() | 
|---|
|  | 363 |  | 
|---|
|  | 364 |  | 
|---|
|  | 365 | // Local Variables: | 
|---|
|  | 366 | // tab-width: 4 | 
|---|
|  | 367 | // c-basic-offset: 4 | 
|---|
|  | 368 | // c-file-offsets:((innamespace . 0)(inline-open . 0)) | 
|---|
|  | 369 | // indent-tabs-mode: nil | 
|---|
|  | 370 | // End: | 
|---|
|  | 371 | // vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4 | 
|---|
|  | 372 |  | 
|---|