source: trunk/softs/giet_tsar/stdio.c @ 670

Last change on this file since 670 was 631, checked in by alain, 11 years ago

Introducing support for RAMDISK in giet_tsar

File size: 51.4 KB
RevLine 
[622]1////////////////////////////////////////////////////////////////////////////////////////
2// File : stdio.c
3// Written by Alain Greiner
4// Date : janvier 2014
5//
[629]6// This file defines various functions that can be used by applications to access
[622]7// peripherals, for the TSAR multi-processors multi_clusters architecture.
8// There is NO separation between application code and system code, as the
9// application are running in kernel mode without system calls.
10// This basic GIET does not support virtual memory, and does not support multi-tasking.
11//
12// The supported peripherals are:
13// - the SoClib multi_tty
14// - The SoCLib frame_buffer
15// - The SoCLib block_device
16//
17// The following parameters must be defined in the hard_config.h file.
18// - X_SIZE          : number of clusters in a row
19// - Y_SIZE          : number of clusters in a column
20// - X_WIDTH         : number of bits for X field in proc_id
21// - Y_WIDTH         : number of bits for Y field in proc_id
22// - NB_PROCS_MAX    : max number of processor per cluster
23// - NB_TTY_CHANNELS : max number of TTY channels
[629]24// - USE_EXT_IO      : use external peripherals if not zero
[622]25//
26// The follobing base addresses must be defined in the ldscript
27// - seg_tty_base
28// - seg_fbf_base
29// - seg_ioc_base
30////////////////////////////////////////////////////////////////////////////////////////
[158]31
[622]32#include "stdio.h"
[158]33
[629]34#if !defined(NB_PROCS_MAX)
35#error: you must define NB_PROCS_MAX in the hard_config.h file
36#endif
37
38#if !defined(USE_EXT_IO)
39#error: you must define USE_EXT_IO in the hard_config.h file
40#endif
41
42#if !defined(X_SIZE)
43#error: you must define X_SIZE in the hard_config.h file
44#endif
45
46#if !defined(Y_SIZE)
47#error: you must define Y_SIZE in the hard_config.h file
48#endif
49
50#if !defined(X_WIDTH)
51#error: you must define X_WIDTH in the hard_config.h file
52#endif
53
54#if (X_WIDTH != 4)
55#error: The X_WIDTH parameter must be equal to 4
56#endif
57
58#if !defined(Y_WIDTH)
59#error: you must define X_WIDTH in the hard_config.h file
60#endif
61
62#if (X_WIDTH != 4)
63#error: The Y_WIDTH parameter must be equal to 4
64#endif
65
66#if !defined(NB_TTY_CHANNELS)
67#error: you must define NB_TTY_CHANNELS in the hard_config.h file
68#endif
69
70
71
72
[622]73#define NB_LOCKS      256
74#define NB_BARRIERS   16
[158]75
[622]76#define in_drivers __attribute__((section (".drivers")))
77#define in_unckdata __attribute__((section (".unckdata")))
[158]78
[622]79//////////////////////////////////////////////////////////////
80// various informations that must be defined in ldscript
81//////////////////////////////////////////////////////////////
[158]82
[622]83struct plouf;
84
85extern struct plouf seg_tty_base;
86extern struct plouf seg_fbf_base;
87extern struct plouf seg_ioc_base;
88extern struct plouf seg_mmc_base;
[631]89extern struct plouf seg_ramdisk_base;
[622]90
91////////////////////////////////////////////////////////////////////////////////////////
92//  Global uncachable variables for synchronization between drivers and ISRs
93////////////////////////////////////////////////////////////////////////////////////////
94
95in_unckdata int volatile    _ioc_lock    = 0;
96in_unckdata int volatile    _ioc_done    = 0;
97in_unckdata int volatile    _ioc_status;
98
99in_unckdata char volatile   _tty_get_buf[NB_TTY_CHANNELS];
100in_unckdata int volatile    _tty_get_full[NB_TTY_CHANNELS] = { [0 ... NB_TTY_CHANNELS-1] = 0 };
101
102////////////////////////////////////////////////////////////////////////////////////////
103//  Global uncachable variables for inter-task barriers
104////////////////////////////////////////////////////////////////////////////////////////
105
106in_unckdata int volatile    _barrier_value[NB_BARRIERS]  = { [0 ... NB_BARRIERS-1] = 0 };
107in_unckdata int volatile    _barrier_count[NB_BARRIERS]  = { [0 ... NB_BARRIERS-1] = 0 };
108in_unckdata int volatile    _barrier_lock[NB_BARRIERS]   = { [0 ... NB_BARRIERS-1] = 0 };
109
110////////////////////////////////////////////////////////////////////////////////////////
111//  Global uncachable variables for spin_locks using LL/C instructions
112////////////////////////////////////////////////////////////////////////////////////////
113
114in_unckdata int volatile    _spin_lock[NB_LOCKS] =    { [0 ... NB_LOCKS-1] = 0 };
115
116////////////////////////////////////////////////////////////////////////////////////////
[629]117// Memcopy taken from MutekH.
[622]118////////////////////////////////////////////////////////////////////////////////////////
119in_drivers void* _memcpy( void*        _dst, 
120                          const void*  _src, 
121                          unsigned int size )
[158]122{
[622]123    unsigned int *dst = _dst;
124    const unsigned int *src = _src;
125    if ( ! ((unsigned int)dst & 3) && ! ((unsigned int)src & 3) )
126    {
127        while (size > 3) 
128        {
129            *dst++ = *src++;
130            size -= 4;
131        }
132    }
[158]133
[622]134    unsigned char *cdst = (unsigned char*)dst;
135    unsigned char *csrc = (unsigned char*)src;
136
137    while (size--) 
138    {
139        *cdst++ = *csrc++;
140    }
141    return _dst;
[158]142}
[629]143////////////////////////////////////////////////////////////////////////////////////////
144// Memcopy using extended addresses
145////////////////////////////////////////////////////////////////////////////////////////
146in_drivers void  _extended_memcpy( unsigned int dst_cluster,
147                                   unsigned int dst_address,
148                                   unsigned int src_cluster,
149                                   unsigned int src_address,
150                                   unsigned int length )
151{
152    if ( (dst_address & 0x3) || (src_address & 0x3) || (length & 0x3) )
153    {
154        _tty_get_lock( 0 );
155        _tty_puts( "ERROR in _extended_memcpy()" );
156        _tty_release_lock( 0 );
157        _exit();
158    }
[158]159
[629]160    unsigned int i;
161    unsigned int word;
162
163    for ( i = 0 ; i < length ; i = i+4 )
164    {
165        word = _word_extended_read( src_cluster, (src_address + i) );
166        _word_extended_write( dst_cluster, (dst_address + i), word );
167    }
168}
[622]169////////////////////////////////////////////////////////////////////////////////////////
170// Access CP0 and returns processor ident
171// No more than 1024 processors...
172////////////////////////////////////////////////////////////////////////////////////////
173in_drivers unsigned int _procid()
[158]174{
[622]175    unsigned int ret;
176    asm volatile( "mfc0 %0, $15, 1": "=r"(ret) );
177    return (ret & 0x3FF);
[158]178}
[622]179////////////////////////////////////////////////////////////////////////////////////////
180// Access CP0 and returns processor time
181////////////////////////////////////////////////////////////////////////////////////////
182in_drivers unsigned int _proctime()
[158]183{
[622]184    unsigned int ret;
185    asm volatile( "mfc0 %0, $9": "=r"(ret) );
186    return ret;
[158]187}
[622]188////////////////////////////////////////////////////////////////////////////////////////
189// Returns the number of processsors controled by the GIET
190////////////////////////////////////////////////////////////////////////////////////////
191in_drivers unsigned int _procnumber()
[158]192{
[622]193    return (unsigned int)(NB_PROCS_MAX * X_SIZE * Y_SIZE);
[158]194}
[622]195////////////////////////////////////////////////////////////////////////////////////////
[626]196// Returns pseudo-random number
197////////////////////////////////////////////////////////////////////////////////////////
198in_drivers unsigned int _rand()
199{
200    unsigned int x = _proctime();
201    if((x & 0xF) > 7)
202        return (x*x & 0xFFFF);
203    else
204        return (x*x*x & 0xFFFF);
205}
206////////////////////////////////////////////////////////////////////////////////////////
[622]207// Access CP0 and mask IRQs
208////////////////////////////////////////////////////////////////////////////////////////
209in_drivers void _it_mask()
[158]210{
[622]211    int tmp;
212    asm volatile("mfc0  %0, $12"    : "=r" (tmp) );
213    asm volatile("ori   %0, %0, 1"  : "=r" (tmp) );
214    asm volatile("mtc0  %0, $12"    : "=r" (tmp) );
[158]215}
[622]216////////////////////////////////////////////////////////////////////////////////////////
217// Access CP0 and enable IRQs
218////////////////////////////////////////////////////////////////////////////////////////
219in_drivers void _it_enable()
[158]220{
[622]221    int tmp;
222    asm volatile("mfc0  %0, $12"    : "=r" (tmp) );
223    asm volatile("addi  %0, %0, -1" : "=r" (tmp) );
224    asm volatile("mtc0  %0, $12"    : "=r" (tmp) );
[158]225}
[622]226//////////////////////////////////////////////////////////////////////
227// Invalidate all cache lines corresponding to a memory buffer.
228// This is used by the block_device driver.
229/////////////////////////////////////////////////////////////////////////
230in_drivers void _dcache_buf_invalidate(const void * buffer, size_t size)
231{
232    size_t i;
233    size_t dcache_line_size;
[158]234
[622]235    // retrieve dcache line size from config register (bits 12:10)
236    asm volatile("mfc0 %0, $16, 1" : "=r" (dcache_line_size));
237
238    dcache_line_size = 2 << ((dcache_line_size>>10) & 0x7);
239
240    // iterate on lines to invalidate each one of them
241    for ( i=0; i<size; i+=dcache_line_size )
242        asm volatile(" cache %0, %1"
243                :
244                :"i" (0x11), "R" (*((char*)buffer+i)));
245}
246
[629]247////////////////////////////////////////////////////////////////////////////
248// This function makes a physical read access to a 32 bits word in memory,
249// after a temporary paddr extension.
250////////////////////////////////////////////////////////////////////////////
251in_drivers unsigned int _word_extended_read( unsigned int  cluster,
252                                             unsigned int  address )
253{
254    unsigned int value;
255    asm volatile(
256            "li      $3,        0xFFFFFFFE    \n"
257            "mfc0    $2,        $12           \n"
258            "and     $3,        $2, $3        \n"
259            "mtc0    $3,        $12           \n"     /* IRQ disabled     */
260
261            "mtc2    %2,        $24           \n"     /* PADDR_EXT <= msb */   
262            "lw      %0,        0(%1)         \n"     /* value <= *paddr  */
263            "mtc2    $0,        $24           \n"     /* PADDR_EXT <= 0   */
264
265            "li      $3,        0x00000001    \n"
266            "mfc0    $2,        $12           \n"
267            "or      $3,        $3, $2        \n"
268            "mtc0    $3,        $12           \n"     /* IRQ enabled      */
269            : "=r" (value)
270            : "r" (address), "r" (cluster)
271            : "$2", "$3" );
272    return value;
273}
274////////////////////////////////////////////////////////////////////////////
275// This function makes a physical read access to a single byte in memory,
276// after a temporary paddr extension.
277////////////////////////////////////////////////////////////////////////////
278in_drivers unsigned char _byte_extended_read( unsigned int  cluster,
279                                              unsigned int  address )
280{
281    unsigned int value;
282    asm volatile(
283            "li      $3,        0xFFFFFFFE    \n"
284            "mfc0    $2,        $12           \n"
285            "and     $3,        $2, $3        \n"
286            "mtc0    $3,        $12           \n"     /* IRQ disabled     */
287
288            "mtc2    %2,        $24           \n"     /* PADDR_EXT <= msb */   
289            "lb      %0,        0(%1)         \n"     /* value <= *paddr  */
290            "mtc2    $0,        $24           \n"     /* PADDR_EXT <= 0   */
291
292            "li      $3,        0x00000001    \n"
293            "mfc0    $2,        $12           \n"
294            "or      $3,        $3, $2        \n"
295            "mtc0    $3,        $12           \n"     /* IRQ enabled      */
296            : "=r" (value)
297            : "r" (address), "r" (cluster)
298            : "$2", "$3" );
299    return (unsigned char)value;
300}
301////////////////////////////////////////////////////////////////////////////
302// This function makes a physical write access to a 32 bits word in memory,
303// after a temporary DTLB address extension.
304////////////////////////////////////////////////////////////////////////////
305in_drivers void _word_extended_write( unsigned int  cluster, 
306                                      unsigned int  address,
307                                      unsigned int  word ) 
308{
309    asm volatile(
310            "li      $3,        0xFFFFFFFE    \n"
311            "mfc0    $2,        $12           \n"
312            "and     $3,        $2, $3        \n"
313            "mtc0    $3,        $12           \n"     /* IRQ disabled     */
314
315            "mtc2    %2,        $24           \n"     /* PADDR_EXT <= msb */   
316            "sw      %0,        0(%1)         \n"     /* *paddr <= value  */
317            "mtc2    $0,        $24           \n"     /* PADDR_EXT <= 0   */   
318
319            "li      $3,        0x00000001    \n"
320            "mfc0    $2,        $12           \n"
321            "or      $3,        $2, $3        \n"
322            "mtc0    $3,        $12           \n"     /* IRQ enabled      */
323            :
324            : "r" (word), "r" (address), "r" (cluster)
325            : "$2", "$3");
326}
327////////////////////////////////////////////////////////////////////////////
328// This function makes a physical write access to single byte in memory,
329// after a temporary DTLB de-activation and address extension.
330////////////////////////////////////////////////////////////////////////////
331in_drivers void _byte_extended_write( unsigned int  cluster, 
332                                      unsigned int  address,
333                                      unsigned char byte ) 
334{
335    asm volatile(
336            "li      $3,        0xFFFFFFFE    \n"
337            "mfc0    $2,        $12           \n"
338            "and     $3,        $2, $3        \n"
339            "mtc0    $3,        $12           \n"     /* IRQ disabled     */
340
341            "mtc2    %2,        $24           \n"     /* PADDR_EXT <= msb */   
342            "sb      %0,        0(%1)         \n"     /* *paddr <= value  */
343            "mtc2    $0,        $24           \n"     /* PADDR_EXT <= 0   */   
344
345            "li      $3,        0x00000001    \n"
346            "mfc0    $2,        $12           \n"
347            "or      $3,        $2, $3        \n"
348            "mtc0    $3,        $12           \n"     /* IRQ enabled      */
349            :
350            : "r" (byte), "r" (address), "r" (cluster)
351            : "$2", "$3");
352}
353
[622]354///////////////////////////////////////////////////////////////////////////////////////
[629]355// Exit (suicide) after printing message on TTY0
[622]356///////////////////////////////////////////////////////////////////////////////////////
357in_drivers void _exit()
[158]358{
[622]359    unsigned int proc_id = _procid();
360    unsigned int l       = proc_id % NB_PROCS_MAX;
361    unsigned int x       = (proc_id / NB_PROCS_MAX) >> Y_WIDTH;
362    unsigned int y       = (proc_id / NB_PROCS_MAX) & ((1<<Y_WIDTH) - 1);
363
[629]364    _tty_get_lock( 0 );
365    _tty_puts("\n !!! exit proc[");
366    _tty_putd( x );
367    _tty_puts(",");
368    _tty_putd( y );
369    _tty_puts(",");
370    _tty_putd( l );
371    _tty_puts("]  !!!\n");
372    _tty_release_lock( 0 );
[622]373
374    while(1) asm volatile("nop");   // infinite loop...
[158]375}
[622]376
377/////////////////////////////////////////////////////////////////////////
378// convert a 32 bits unsigned int to a string of 10 decimal characters.
379/////////////////////////////////////////////////////////////////////////
380in_drivers void _itoa_dec(unsigned val, char* buf)
[158]381{
[622]382    const char  DecTab[] = "0123456789";
383    unsigned int i;
384    for( i=0 ; i<10 ; i++ )
385    {
386        if( (val!=0) || (i==0) ) buf[9-i] = DecTab[val % 10];
387        else                     buf[9-i] = 0x20;
388        val /= 10;
[158]389    }
390}
[622]391//////////////////////////////////////////////////////////////////////////
392// convert a 32 bits unsigned int to a string of 8 hexadecimal characters.
393///////////////////////////////////////////////////////////////////////////
394in_drivers void _itoa_hex(unsigned int val, char* buf)
[158]395{
[622]396    const char  HexaTab[] = "0123456789ABCD";
397    unsigned int i;
398    for( i=0 ; i<8 ; i++ )
399    {
400        buf[7-i] = HexaTab[val % 16];
401        val /= 16;
[158]402    }
403}
[622]404
405
406///////////////////////////////////////////////////////////////////////////////////////
407// VCI MULTI_TTY
408///////////////////////////////////////////////////////////////////////////////////////
409//  The total number of TTY terminals is defined by NB_TTY_CHANNELS.
[629]410//  - If there is only one terminal, it is supposed to be shared, and used by
411//    all processors: a lock must be taken before display.
412//  - If there is several terminals, and the number of processors is smaller
413//    than the number of terminals, there is one terminal per processor, but
414//    the TTY index is not equal to the proc_id, due to cluster indexing policy:
415//    proc_id = cluster_xy * NB_PROCS_MAX + local_id (with cluster_xy = x << Y_WIDTH + y)
416//    tty_id  = cluster_id * NB_PROCS_MAX + local_id (with cluster_id = x * Y_SIZE + y)
417//  - If the computed tty_id is larger than NB_TTY_CHANNELS, an error is returned.
[622]418///////////////////////////////////////////////////////////////////////////////////////
[629]419//  If USE_EXT_IO is set, we use the TTY controler implemented in cluster_io
420//  (x = X_SIZE-1 / y = Y_SIZE), which requires and extended address access.
421//  If USE_EXT_IO not set, we use the single channel TTY contrÃŽler in cluster (0,0).
422///////////////////////////////////////////////////////////////////////////////////////
423
424///////////////////////////////////////////////////////////////////////////////////////
[622]425// Write one or several characters directly from a fixed length user buffer
426// to the TTY_WRITE register of the TTY controler.
[629]427// The channel index must be checked by the calling function.
[622]428// This is a non blocking call : it test the TTY_STATUS register.
429// If the TTY_STATUS_WRITE bit is set, the transfer stops and the function
430// returns  the number of characters that have been actually written.
431///////////////////////////////////////////////////////////////////////////////////////
432in_drivers int _tty_write( char*           buffer, 
433                           unsigned int    length, 
434                           unsigned int    channel )
[158]435{
[629]436    unsigned int    base       = (unsigned int)&seg_tty_base + channel*TTY_SPAN*4;
437    unsigned int    nwritten   = 0;
438    unsigned int    cluster_io = ((X_SIZE-1)<<Y_WIDTH) + Y_SIZE;
439    unsigned int    status;
440    unsigned int        i;
[622]441
442    for ( i=0 ; i < length ; i++ )
[158]443    {
[629]444        if( USE_EXT_IO )    // extended addressing to reach cluster_io
[622]445        {
[629]446            status = _word_extended_read( cluster_io, base + TTY_STATUS*4 );
447            if ( (status & 0x2) == 0x2 ) break;
448            else
449            {
450                _byte_extended_write( cluster_io, base + TTY_WRITE*4 , buffer[i] );
451                nwritten++;
452            }
[622]453        }
[629]454        else                // direct addressing to cluster(0,0)
455        {
456            char* tty = (char*)base;
457            if ( (tty[TTY_STATUS*4] & 0x2) == 0x2 )  break;
458            else
459            {
460                tty[TTY_WRITE*4] = buffer[i]; // write character
461                nwritten++;
462            }
463        }
[158]464    }
[622]465
466    return nwritten;
[158]467}
[629]468
[622]469///////////////////////////////////////////////////////////////////////////////////////
470// Fetch one character directly from the TTY_READ register of the TTY controler,
471// and writes this character to the user buffer.
[629]472// The channel index must be checked by the calling function.
[622]473// This is a non blocking call : it returns 0 if the register is empty,
474// and returns 1 if the register is full.
475///////////////////////////////////////////////////////////////////////////////////////
476in_drivers int _tty_read( char*          buffer, 
477                          unsigned int   channel )
[158]478{
[629]479    unsigned int    base       = (unsigned int)&seg_tty_base + channel*TTY_SPAN*4;
480    unsigned int    cluster_io = ((X_SIZE-1)<<Y_WIDTH) + Y_SIZE;
481    unsigned int    status;
[622]482
[629]483    if( USE_EXT_IO )
[158]484    {
[629]485        status = _word_extended_read( cluster_io, base + TTY_STATUS*4 );
486        if ( (status & 0x1) == 0x1 )
487        {
488            buffer[0] = (char)_word_extended_read( cluster_io, base + TTY_READ*4 );
489            return 1;
490        }
491        else
492        {
493            return 0;
494        }
[158]495    }
[622]496    else
497    {
[629]498        char* tty = (char*)base;
499
500        if((tty[TTY_STATUS*4] & 0x1) == 0x1)
501        {
502            buffer[0] = tty[TTY_READ*4];
503            return 1;
504        }
505        else
506        {
507            return 0;
508        }
[622]509    }
[158]510}
[629]511
[622]512//////////////////////////////////////////////////////////////////////////////
513// This function displays a string on TTY0.
514// The string must be terminated by a NUL character.
515//////////////////////////////////////////////////////////////////////////////
516in_drivers void _tty_puts( char* string )
[158]517{
[622]518    int length = 0;
519    while (string[length] != 0) length++;
520    _tty_write( string, length, 0 );
521}
[158]522
[622]523///////////////////////////////////////////////////////////////////////////////
524// This function displays a 32 bits unsigned int as an hexa string on TTY0.
525///////////////////////////////////////////////////////////////////////////////
526in_drivers void _tty_putx(unsigned int val) 
527{
528    static const char HexaTab[] = "0123456789ABCDEF";
529    char buf[11];
530    unsigned int c;
531
532    buf[0] = '0';
533    buf[1] = 'x';
534    buf[10] = 0;
535
536    for (c = 0; c < 8; c++) 
537    { 
538        buf[9 - c] = HexaTab[val & 0xF];
539        val = val >> 4;
540    }
541    _tty_puts( buf );
542}
543
544///////////////////////////////////////////////////////////////////////////////
545// This function displays a 32 bits unsigned int as a decimal string on TTY0.
546///////////////////////////////////////////////////////////////////////////////
547in_drivers void _tty_putd( unsigned int val ) 
548{
549    static const char DecTab[] = "0123456789";
550    char buf[11];
551    unsigned int i;
552    unsigned int first;
553
554    buf[10] = 0;
555
556    for (i = 0; i < 10; i++) 
[158]557    {
[622]558        if ((val != 0) || (i == 0)) 
559        {
560            buf[9 - i] = DecTab[val % 10];
561            first = 9 - i;
562        }
563        else 
564        {
565            break;
566        }
567        val /= 10;
568    }
569    _tty_puts( &buf[first] );
570}
[158]571
[622]572//////////////////////////////////////////////////////////////////////////////
573// This function try to take the hardwired lock protecting exclusive access
574// to TTY terminal identified by the channel argument.
575// It returns only when the lock has been successfully taken.
576//////////////////////////////////////////////////////////////////////////////
577in_drivers void _tty_get_lock( unsigned int channel )
578{
[629]579    if ( USE_EXT_IO )  // extended addressing to cluster_io
580    {
581        unsigned int    cluster_io = ((X_SIZE-1)<<Y_WIDTH) + Y_SIZE;
582        unsigned int    address    = (unsigned int)&seg_tty_base
583                                     + ((TTY_CONFIG + channel*TTY_SPAN)*4);
584        while ( _word_extended_read( cluster_io, address ) ) asm volatile("nop");
585    }
586    else               // direct addressing to cluster(0,0)
587    {
588        unsigned int* tty = (unsigned int *) &seg_tty_base;
589        while ( tty[channel * TTY_SPAN + TTY_CONFIG] ) asm volatile("nop"); 
590    }
[622]591}
[158]592
[622]593//////////////////////////////////////////////////////////////////////////////
594// This function releases the hardwired lock protecting exclusive access
595// to TTY terminal identified by the channel argument.
596//////////////////////////////////////////////////////////////////////////////
597in_drivers void _tty_release_lock( unsigned int channel )
598{
[629]599    if ( USE_EXT_IO )  // extended addressing to cluster_io
600    {
601        unsigned int    cluster_io = ((X_SIZE-1)<<Y_WIDTH) + Y_SIZE;
602        unsigned int    address    = (unsigned int)&seg_tty_base
603                                     + ((TTY_CONFIG + channel*TTY_SPAN)*4);
604        _word_extended_write( cluster_io, address, 0 );
605    }
606    else               // direct addressing to cluster(0,0)
607    {
608        unsigned int* tty_address = (unsigned int *) &seg_tty_base;
609        tty_address[channel * TTY_SPAN + TTY_CONFIG] = 0;
610    }
[622]611}
612
613//////////////////////////////////////////////////////////////////////////////
614// This function fetch a single ascii character from a terminal
615// implicitely defined by the processor ID.
616// It is a blocking function.
617//////////////////////////////////////////////////////////////////////////////
618in_drivers void _tty_getc( char* buf )
619{
620    unsigned int proc_id = _procid();
621    unsigned int channel;
622    unsigned int l;
623    unsigned int x;
624    unsigned int y;
625
[629]626    // check TTY channel
627    l           = (proc_id % NB_PROCS_MAX);
628    x           = (proc_id / NB_PROCS_MAX) >> Y_WIDTH; 
629    y           = (proc_id / NB_PROCS_MAX) & ((1<<Y_WIDTH) - 1);
630    channel = (x * Y_SIZE + y) * NB_PROCS_MAX + l;
631    if (channel >= NB_TTY_CHANNELS )
[622]632    {
[629]633        _tty_get_lock( 0 );
634        _tty_puts( "ERROR in _tty_getc(): TTY index too large\n" );
635        _tty_release_lock( 0 );
636        _exit();
[622]637    }
638
639    while( _tty_read( buf, channel ) == 0 ) asm volatile("nop");
[158]640}
[622]641
642//////////////////////////////////////////////////////////////////////////////
643//  Fetch a string of decimal characters (most significant digit first)
644//  to build a 32 bits unsigned int.
645//  The terminal index is implicitely defined by the processor ID.
646//  This is a blocking function.
647//  The decimal characters are written in a 32 characters buffer
648//  until a <LF> or <CR> character is read.
649//  The <DEL> character is interpreted, and previous characters can be
650//  cancelled. All others characters are ignored.
651//  When the <LF> or <CR> character is received, the string is converted
652//  to an unsigned int value. If the number of decimal digit is too large
653//  for the 32 bits range, the zero value is returned.
654//////////////////////////////////////////////////////////////////////////////
655in_drivers void _tty_getw( unsigned int* word_buffer )
[158]656{
[622]657    char          buf[32];
658    char          byte;
659    char          cancel_string[3] = { 0x08, 0x20, 0x08 };
660    char          zero             = 0x30;
661    unsigned int  save = 0;
662    unsigned int  val = 0;
663    unsigned int  done = 0;
664    unsigned int  overflow = 0;
665    unsigned int  max = 0;
666    unsigned int  proc_id = _procid();
667    unsigned int  i;
668    unsigned int  channel;
669    unsigned int  x;
670    unsigned int  y;
[629]671    unsigned int  l;
[158]672
[629]673    // check TTY channel
674    l           = (proc_id % NB_PROCS_MAX);
675    x           = (proc_id / NB_PROCS_MAX) >> Y_WIDTH; 
676    y           = (proc_id / NB_PROCS_MAX) & ((1<<Y_WIDTH) - 1);
677    channel = (x * Y_SIZE + y) * NB_PROCS_MAX + l;
678    if (channel >= NB_TTY_CHANNELS )
[158]679    {
[629]680        _tty_get_lock( 0 );
681        _tty_puts( "ERROR in _tty_getw(): TTY index too large\n" );
682        _tty_release_lock( 0 );
683        _exit();
[622]684    }
[158]685
[622]686    while( done == 0 )
687    {
688        _tty_read( &byte, channel );
689
690        if (( byte > 0x2F) && (byte < 0x3A))  // decimal character
[158]691        {
[622]692            buf[max] = byte;
693            max++;
694            _tty_write( &byte, 1, channel );
695        }
696        else if ( (byte == 0x0A) || (byte == 0x0D) ) // LF or CR character
697        {
698            done = 1;
699        }
700        else if ( byte == 0x7F )        // DEL character
701        {
702            if (max > 0)
[158]703            {
[622]704                max--;          // cancel the character
705                _tty_write( cancel_string, 3, channel );
[158]706            }
707        }
708    } // end while
709
710    // string conversion
711    for( i=0 ; i<max ; i++ )
712    {
713        val = val*10 + (buf[i] - 0x30);
714        if (val < save) overflow = 1;
715        save = val;
716    }
717    if (overflow == 0)
718    {
719        *word_buffer = val;     // return decimal value
720    }
721    else
722    {
723        for( i=0 ; i<max ; i++)     // cancel the string
724        {
[622]725            _tty_write( cancel_string, 3, channel );
[158]726        }
[622]727        _tty_write( &zero, 1, channel );
[158]728        *word_buffer = 0;       // return 0 value
729    }
730}
[622]731
732//////////////////////////////////////////////////////////////////////////////
733//  This function is a simplified version of the mutek_printf() function.
734//  It takes the TTY lock on the selected channel for exclusive access.
735//  Only a limited number of formats are supported:
736//  - %d : signed decimal
737//  - %u : unsigned decimal
738//  - %x : hexadecimal
739//  - %c : char
740//  - %s : string
741//////////////////////////////////////////////////////////////////////////////
742in_drivers void _tty_printf( char *format, ...)
[158]743{
744    va_list ap;
[622]745    va_start( ap, format );
[158]746
[622]747    unsigned int channel;
748    unsigned int x;
749    unsigned int y;
750    unsigned int proc_id = _procid();
751
[629]752    // compute TTY channel :
753    // if the number of TTY channels is smaller
754    // than the number of clusters, use TTY_0_0
755    // else, TTY channel <= cluster index
756    if ( NB_TTY_CHANNELS < (X_SIZE * Y_SIZE) )
[622]757    {
758        channel = 0;
759    }
760    else
761    {
762        x           = (proc_id / NB_PROCS_MAX) >> Y_WIDTH; 
763        y           = (proc_id / NB_PROCS_MAX) & ((1<<Y_WIDTH) - 1);
[629]764        channel     = (x * Y_SIZE + y);
[622]765    }
766
767    // take the TTY lock
768    _tty_get_lock( channel );
769
[158]770printf_text:
771
[622]772    while (*format) 
773    {
[158]774        unsigned int i;
775        for (i = 0; format[i] && format[i] != '%'; i++)
776            ;
[622]777        if (i) 
778        {
779            _tty_write( format, i, channel );
[158]780            format += i;
781        }
[622]782        if (*format == '%') 
783        {
[158]784            format++;
785            goto printf_arguments;
786        }
787    } // end while
788
[622]789    va_end( ap );
[158]790
[622]791    // release lock
792    _tty_release_lock( 0 );
793
794    return;
795
[158]796printf_arguments:
797
798    {
[622]799        int                 val = va_arg(ap, long);
800        char                buf[20];
801        char*               pbuf;
[158]802        unsigned int        len = 0;
803        static const char   HexaTab[] = "0123456789ABCDEF";
804        unsigned int        i;
805
806        switch (*format++) {
807            case ('c'):             // char conversion
808                len = 1;
809                buf[0] = val;
810                pbuf = buf;
811                break;
812            case ('d'):             // decimal signed integer
[622]813                if (val < 0) 
814                {
[158]815                    val = -val;
[622]816                    _tty_write( "_" , 1, channel );
[158]817                }
818            case ('u'):             // decimal unsigned integer
[622]819                for( i=0 ; i<10 ; i++) 
820                {
[158]821                    buf[9-i] = HexaTab[val % 10];
822                    if (!(val /= 10)) break;
823                }
824                len =  i+1;
825                pbuf = &buf[9-i];
826                break;
827            case ('x'):             // hexadecimal integer
[622]828                _tty_write( "0x", 2, channel );
829                for( i=0 ; i<8 ; i++) 
830                {
[158]831                    buf[7-i] = HexaTab[val % 16U];
832                    if (!(val /= 16U)) break;
833                }
834                len =  i+1;
835                pbuf = &buf[7-i];
836                break;
837            case ('s'):             // string
838                {
839                    char *str = (char*)val;
840                    while ( str[len] ) len++;
841                    pbuf = (char*)val;
842                }
843                break;
844            default:
845                goto printf_text;
846        } // end switch
847
[622]848        _tty_write( pbuf, len, channel );
[158]849        goto printf_text;
850    }
851} // end printf()
852
[622]853//////////////////////////////////////////////////////////////////////////////////////
854//  These functions are the ISRs that must be executed when an IRQ is activated
[629]855//  by the TTY: _tty_isr_XX is associated to TTY channel [XX].
856//  It save the character in the communication buffer _tty_get_buf[XX],
857//  and set the set/reset variable _tty_get_full[XX].
[622]858//  A character is lost if the buffer is full when the ISR is executed.
859//////////////////////////////////////////////////////////////////////////////////////
860in_drivers void _tty_isr_indexed(size_t index)
[158]861{
[629]862    if ( USE_EXT_IO )   // extended addressing to TTY in cluster_io
863    {
864        unsigned int  cluster = ((X_SIZE-1)<<Y_WIDTH) + Y_SIZE;
865        unsigned int  base    = (unsigned int)&seg_tty_base +
866                                ((index*TTY_SPAN + TTY_READ)*4); 
[622]867
[629]868        _tty_get_buf[index] = (char)_word_extended_read( cluster, base );
869    }
870    else                // direct addressing to TTY in cluster(0,0)
871    {
872        char* tty = (char*)&seg_tty_base + index*TTY_SPAN*4;
873
874        _tty_get_buf[index] = tty[TTY_READ*4];  // save character and reset IRQ
875    }
876    _tty_get_full[index] = 1;               // signals character available
[158]877}
[622]878
[629]879in_drivers void _tty_isr()    { _tty_isr_indexed(0); }
880
[622]881in_drivers void _tty_isr_00() { _tty_isr_indexed(0); }
882in_drivers void _tty_isr_01() { _tty_isr_indexed(1); }
883in_drivers void _tty_isr_02() { _tty_isr_indexed(2); }
884in_drivers void _tty_isr_03() { _tty_isr_indexed(3); }
885in_drivers void _tty_isr_04() { _tty_isr_indexed(4); }
886in_drivers void _tty_isr_05() { _tty_isr_indexed(5); }
887in_drivers void _tty_isr_06() { _tty_isr_indexed(6); }
888in_drivers void _tty_isr_07() { _tty_isr_indexed(7); }
889in_drivers void _tty_isr_08() { _tty_isr_indexed(8); }
890in_drivers void _tty_isr_09() { _tty_isr_indexed(9); }
891in_drivers void _tty_isr_10() { _tty_isr_indexed(10); }
892in_drivers void _tty_isr_11() { _tty_isr_indexed(11); }
893in_drivers void _tty_isr_12() { _tty_isr_indexed(12); }
894in_drivers void _tty_isr_13() { _tty_isr_indexed(13); }
895in_drivers void _tty_isr_14() { _tty_isr_indexed(14); }
896in_drivers void _tty_isr_15() { _tty_isr_indexed(15); }
897in_drivers void _tty_isr_16() { _tty_isr_indexed(16); }
898in_drivers void _tty_isr_17() { _tty_isr_indexed(17); }
899in_drivers void _tty_isr_18() { _tty_isr_indexed(18); }
900in_drivers void _tty_isr_19() { _tty_isr_indexed(19); }
901in_drivers void _tty_isr_20() { _tty_isr_indexed(20); }
902in_drivers void _tty_isr_21() { _tty_isr_indexed(21); }
903in_drivers void _tty_isr_22() { _tty_isr_indexed(22); }
904in_drivers void _tty_isr_23() { _tty_isr_indexed(23); }
905in_drivers void _tty_isr_24() { _tty_isr_indexed(24); }
906in_drivers void _tty_isr_25() { _tty_isr_indexed(25); }
907in_drivers void _tty_isr_26() { _tty_isr_indexed(26); }
908in_drivers void _tty_isr_27() { _tty_isr_indexed(27); }
909in_drivers void _tty_isr_28() { _tty_isr_indexed(28); }
910in_drivers void _tty_isr_29() { _tty_isr_indexed(29); }
911in_drivers void _tty_isr_30() { _tty_isr_indexed(30); }
912in_drivers void _tty_isr_31() { _tty_isr_indexed(31); }
913
914
915//////////////////////////////////////////////////////////////////////////////////////////
[629]916//   BLOCK_DEVICE (IOC)
917//////////////////////////////////////////////////////////////////////////////////////////
[631]918// The block size is 512 bytes.
[629]919// The functions below use the three variables _ioc_lock _ioc_done,
[622]920// and _ioc_status for synchronisation.
921// - As the IOC component can be used by several programs running in parallel,
922// the _ioc_lock variable guaranties exclusive access to the device.
923// The _ioc_read() and _ioc_write() functions use atomic LL/SC to get the lock.
924// and set _ioc_lock to a non zero value.
925// The _ioc_write() and _ioc_read() functions are blocking, polling the _ioc_lock
926// variable until the device is available.
927// - When the tranfer is completed, the ISR routine activated by the IOC IRQ
928// set the _ioc_done variable to a non-zero value. Possible address errors detected
929// by the IOC peripheral are reported by the ISR in the _ioc_status variable.
930// The _ioc_completed() function is polling the _ioc_done variable, waiting for
931// tranfer conpletion. When the completion is signaled, the _ioc_completed() function
932// reset the _ioc_done variable to zero, and releases the _ioc_lock variable.
933///////////////////////////////////////////////////////////////////////////////////////
[629]934//  If USE_EXT_IO is set, we use the IOC controler implemented in cluster_io
935//  (x = X_SIZE-1 / y = Y_SIZE), which requires and extended address access.
936//  If USE_EXT_IO not set, we use the IOC contrÃŽler in cluster (0,0).
[631]937//
938//  If USE_RAMDISK is set, we access a "virtual" block device controler  implemented
939//  as a memory-mapped segment in cluster [0,0] at address seg_ramdisk_base.
940//  The tranfer being fully synchronous, the IOC interrupt is not activated.
[629]941///////////////////////////////////////////////////////////////////////////////////////
942
943///////////////////////////////////////////////////////////////////////////////////////
[622]944// This blocking function is used by the _ioc_read() and _ioc_write() functions
945// to get _ioc_lock using LL/SC.
946///////////////////////////////////////////////////////////////////////////////////////
947in_drivers void _ioc_get_lock()
[158]948{
[622]949    register unsigned int*      plock = (unsigned int*)&_ioc_lock;                     
950
951    asm volatile ("_ioc_llsc:                       \n"
952                  "ll   $2,    0(%0)                \n" // $2 <= _ioc_lock
953                  "bnez $2,    _ioc_llsc            \n" // retry  if busy
954                  "li   $3,    1                    \n" // prepare argument for sc 
955                  "sc   $3,    0(%0)                \n" // try to set _ioc_busy
956                  "beqz $3,    _ioc_llsc            \n" // retry if not atomic
957                  ::"r"(plock):"$2","$3");
[158]958}
[629]959
[622]960//////////////////////////////////////////////////////////////////////////////////////
961// Transfer data from a memory buffer to the block_device.
962// - lba    : first block index on the disk
963// - buffer : base address of the memory buffer
964// - count  : number of blocks to be transfered
[629]965// - ext    : cluster index for the memory buffer
[622]966///////////////////////////////////////////////////////////////////////////////////////
967in_drivers void _ioc_write( size_t   lba, 
968                            void*    buffer, 
969                            size_t   count,
970                            size_t   ext )
[158]971{
[622]972    // get the lock
973    _ioc_get_lock();
974
[631]975    if ( USE_RAMDISK )  // we use an extended_memcpy
[629]976    {
[631]977        unsigned int  src_address = (unsigned int)buffer;
978        unsigned int  src_cluster = ext;
979        unsigned int  dst_address = (unsigned int)&seg_ramdisk_base + lba*512;
980        unsigned int  dst_cluster = 0;
981
982        _extended_memcpy( dst_cluster,
983                          dst_address,
984                          src_cluster,
985                          src_address,
986                          count*512 );
987       
988        _ioc_status = BLOCK_DEVICE_WRITE_SUCCESS;
989        _ioc_done   = 1;
990    }
991    else if ( USE_EXT_IO )   // extended addressing to cluster_io
992    {
[629]993        unsigned int    cluster = ((X_SIZE-1)<<Y_WIDTH) + Y_SIZE;
994        unsigned int    base    = (unsigned int)&seg_ioc_base;
995
996        _word_extended_write( cluster, base + BLOCK_DEVICE_BUFFER*4,     (unsigned int)buffer );
997        _word_extended_write( cluster, base + BLOCK_DEVICE_BUFFER_EXT*4, ext );
998        _word_extended_write( cluster, base + BLOCK_DEVICE_COUNT*4,      count );
999        _word_extended_write( cluster, base + BLOCK_DEVICE_LBA*4,        lba );
1000        _word_extended_write( cluster, base + BLOCK_DEVICE_IRQ_ENABLE*4, 1 );
1001        _word_extended_write( cluster, base + BLOCK_DEVICE_OP*4,         BLOCK_DEVICE_WRITE );
1002    }
1003    else                // direct addressing to cluster(0,0)
1004    {
1005        unsigned int* ioc = (unsigned int*)&seg_ioc_base;
1006
1007        ioc[BLOCK_DEVICE_BUFFER]     = (unsigned int)buffer;
1008        ioc[BLOCK_DEVICE_BUFFER_EXT] = ext;
1009        ioc[BLOCK_DEVICE_COUNT]      = count;
1010        ioc[BLOCK_DEVICE_LBA]        = lba;
1011        ioc[BLOCK_DEVICE_IRQ_ENABLE] = 1;
1012        ioc[BLOCK_DEVICE_OP]         = BLOCK_DEVICE_WRITE;
1013    }
[158]1014}
[629]1015
[622]1016///////////////////////////////////////////////////////////////////////////////////////
1017// Transfer data from a file on the block device to a memory buffer.
1018// - lba    : first block index on the disk
1019// - buffer : base address of the memory buffer
1020// - count  : number of blocks to be transfered
[629]1021// - ext    : cluster index for the memory buffer
[622]1022///////////////////////////////////////////////////////////////////////////////////////
1023in_drivers void _ioc_read( size_t   lba, 
1024                           void*    buffer, 
1025                           size_t   count,
1026                           size_t   ext )
[158]1027{
[622]1028    // get the lock
1029    _ioc_get_lock();
1030
[631]1031    if ( USE_RAMDISK )  // we use an extended_memcpy
[629]1032    {
[631]1033        unsigned int  dst_address = (unsigned int)buffer;
1034        unsigned int  dst_cluster = ext;
1035        unsigned int  src_address = (unsigned int)&seg_ramdisk_base + lba*512;
1036        unsigned int  src_cluster = 0;
1037
1038        _extended_memcpy( dst_cluster,
1039                          dst_address,
1040                          src_cluster,
1041                          src_address,
1042                          count*512 );
1043
1044        _ioc_status = BLOCK_DEVICE_READ_SUCCESS;
1045        _ioc_done   = 1;
1046    }
1047    else if ( USE_EXT_IO )   // extended addressing to cluster_io
1048    {
[629]1049        unsigned int    cluster = ((X_SIZE-1)<<Y_WIDTH) + Y_SIZE;
1050        unsigned int    base    = (unsigned int)&seg_ioc_base;
1051
1052        _word_extended_write( cluster, base + BLOCK_DEVICE_BUFFER*4,     (unsigned int)buffer );
1053        _word_extended_write( cluster, base + BLOCK_DEVICE_BUFFER_EXT*4, ext );
1054        _word_extended_write( cluster, base + BLOCK_DEVICE_COUNT*4,      count );
1055        _word_extended_write( cluster, base + BLOCK_DEVICE_LBA*4,        lba );
1056        _word_extended_write( cluster, base + BLOCK_DEVICE_IRQ_ENABLE*4, 1 );
1057        _word_extended_write( cluster, base + BLOCK_DEVICE_OP*4,         BLOCK_DEVICE_READ );
1058    }
1059    else                // direct addressing to cluster(0,0)
1060    {
1061        unsigned int* ioc = (unsigned int*)&seg_ioc_base;
1062
1063        ioc[BLOCK_DEVICE_BUFFER]     = (unsigned int)buffer;
1064        ioc[BLOCK_DEVICE_BUFFER_EXT] = ext;
1065        ioc[BLOCK_DEVICE_COUNT]      = count;
1066        ioc[BLOCK_DEVICE_LBA]        = lba;
1067        ioc[BLOCK_DEVICE_IRQ_ENABLE] = 1;
1068        ioc[BLOCK_DEVICE_OP]         = BLOCK_DEVICE_READ;
1069    }
[158]1070}
[629]1071
[622]1072///////////////////////////////////////////////////////////////////////////////////////
1073// This blocking function cheks completion of an I/O transfer and reports errors.
1074// It returns 0 if the transfer is successfully completed.
1075// It returns -1 if an error has been reported.
1076///////////////////////////////////////////////////////////////////////////////////////
1077in_drivers void _ioc_completed()
[158]1078{
[622]1079    // waiting for completion
1080    while (_ioc_done == 0)  asm volatile("nop"); 
1081   
1082    // reset synchronisation variables
1083    _ioc_done = 0;
1084    _ioc_lock = 0;
1085
1086    if( (_ioc_status != BLOCK_DEVICE_READ_SUCCESS) &&
1087        (_ioc_status != BLOCK_DEVICE_WRITE_SUCCESS) )
1088    {
1089        _tty_get_lock( 0 );
1090        _tty_puts( "ERROR in _ioc_completed()\n");
1091        _tty_release_lock( 0 );
1092        _exit();
1093    }
[158]1094}
[629]1095
[622]1096//////////////////////////////////////////////////////////////////////////////////////
1097//  This ISR must be executed when an IRQ is activated by IOC to signal completion.
1098//  It acknowledge the IRQ using the ioc base address, save the status in _ioc_status,
1099//  and set the _ioc_done variable to signal completion.
1100//  This variable is defined in the drivers.c file.
1101//////////////////////////////////////////////////////////////////////////////////////
1102in_drivers void _ioc_isr()
[158]1103{
[629]1104    if ( USE_EXT_IO )  // extended addressing to cluster_io
1105    {
1106        unsigned int    cluster = ((X_SIZE-1)<<Y_WIDTH) + Y_SIZE;
1107        unsigned int    base    = (unsigned int)&seg_ioc_base;
1108
1109        _ioc_status = _word_extended_read( cluster, base + BLOCK_DEVICE_STATUS*4 );
1110    }
1111    else               // direct addressing to cluster(à,0)
1112    {
1113        unsigned int* ioc = (unsigned int*)&seg_ioc_base;
[622]1114   
[629]1115        _ioc_status = ioc[BLOCK_DEVICE_STATUS]; // save status & reset IRQ
1116    }
1117    _ioc_done   = 1;       // signals completion
[158]1118}
[622]1119
1120//////////////////////////////////////////////////////////////////////////////////////
1121//  FRAME_BUFFER
[629]1122//////////////////////////////////////////////////////////////////////////////////////
[622]1123// The _fb_sync_write & _fb_sync_read functions use a memcpy strategy to implement
1124// the transfer between a data buffer and the frame buffer.
1125// They are blocking until completion of the transfer.
1126//////////////////////////////////////////////////////////////////////////////////////
[629]1127
1128//////////////////////////////////////////////////////////////////////////////////////
[622]1129//  _fb_sync_write()
1130// Transfer data from an user buffer to the frame_buffer device with a memcpy.
[629]1131// - offset : offset (in bytes) in the frame buffer
[622]1132// - buffer : base address of the memory buffer
1133// - length : number of bytes to be transfered
[629]1134// - ext    : cluster_xy for the user buffer
[622]1135//////////////////////////////////////////////////////////////////////////////////////
[629]1136in_drivers void _fb_sync_write( unsigned int  offset, 
1137                                unsigned int  buffer, 
1138                                unsigned int  length,
1139                                unsigned int  ext )
[158]1140{
[629]1141    unsigned int  src_address = buffer;
1142    unsigned int  src_cluster = ext;
1143    unsigned int  dst_address = (unsigned int)&seg_fbf_base + offset;
1144    unsigned int  dst_cluster = ((X_SIZE-1)<<Y_WIDTH) + Y_SIZE;  // cluster_xy for I/O
[158]1145
[629]1146    _extended_memcpy( dst_cluster,
1147                      dst_address,
1148                      src_cluster,
1149                      src_address,
1150                      length );
[158]1151}
[629]1152
[622]1153///////////////////////////////////////////////////////////////////////////////////////
1154//  _fb_sync_read()
1155// Transfer data from the frame_buffer device to an user buffer with a memcpy.
[629]1156// - offset : offset (in bytes) in the frame buffer
[622]1157// - buffer : base address of the memory buffer
1158// - length : number of bytes to be transfered
[629]1159// - ext    : cluster_xy for the user buffer
[622]1160//////////////////////////////////////////////////////////////////////////////////////
[629]1161in_drivers void  _fb_sync_read( unsigned int  offset, 
1162                                unsigned int  buffer, 
1163                                unsigned int  length,
1164                                unsigned int  ext )
[158]1165{
[629]1166    unsigned int  dst_address = buffer;
1167    unsigned int  dst_cluster = ext;
1168    unsigned int  src_address = (unsigned int)&seg_fbf_base + offset;
1169    unsigned int  src_cluster = ((X_SIZE-1)<<Y_WIDTH) + Y_SIZE;  // cluster_xy for I/O
[622]1170
[629]1171    _extended_memcpy( dst_cluster,
1172                      dst_address,
1173                      src_cluster,
1174                      src_address,
1175                      length );
[158]1176}
[622]1177
[631]1178//////////////////////////////////////////////////////////////////////////////////////
1179//  This ISR must be executed when an IRQ is activated by MEMC to signal
1180//  an error detected by the TSAR memory cache after a write transaction.
1181//  It displays an error message on the TTY terminal allocated to the processor
1182//  executing the ISR.
1183//////////////////////////////////////////////////////////////////////////////////////
1184in_drivers void _mmc_isr()
1185{
1186    int*         mmc_address = (int*)&seg_mmc_base;
1187    unsigned int cluster_xy  = _procid() / NB_PROCS_MAX;
1188   
1189    _tty_printf( "WRITE ERROR signaled by Memory Cache in cluster %x\n", cluster_xy );
1190}
1191
[622]1192///////////////////////////////////////////////////////////////////////////////////////
1193// Release a software spin-lock
1194///////////////////////////////////////////////////////////////////////////////////////
1195in_drivers void _release_lock(size_t index)
1196
[158]1197{
[622]1198    if( index >= NB_LOCKS ) 
1199    {
1200        _tty_get_lock( 0 );
1201        _tty_puts( "ERROR in _release_lock()" );
1202        _tty_release_lock( 0 );
1203        _exit();
1204    }
1205   
1206    _spin_lock[index] = 0;
[158]1207}
[629]1208
[622]1209///////////////////////////////////////////////////////////////////////////////////////
1210// Try to take a software spin-lock.
1211// This is a blocking call, as there is a busy-waiting loop,
1212// until the lock is granted to the requester.
1213// There is an internal delay of about 100 cycles between
1214// two successive lock read, to avoid bus saturation.
1215///////////////////////////////////////////////////////////////////////////////////////
1216in_drivers void _get_lock(size_t index)
[158]1217{
[622]1218    if( index >= NB_LOCKS )
1219    {
1220        _tty_get_lock( 0 );
1221        _tty_puts( "ERROR in _get_lock()" );
1222        _tty_release_lock( 0 );
1223        _exit();
1224    }
[158]1225
[622]1226    register int   delay = ((_proctime() +_procid()) & 0xF) << 4;
1227    register int * plock = (int *) &_spin_lock[index];                 
[158]1228
[622]1229    asm volatile ("_locks_llsc:                 \n"
1230                  "ll   $2,    0(%0)            \n"     // $2 <= _locks_lock
1231                  "bnez $2,    _locks_delay     \n"     // random delay if busy
1232                  "li   $3,    1            \n"     // prepare argument for sc 
1233                  "sc   $3,    0(%0)            \n"     // try to set _locks_busy
1234                  "bnez $3,    _locks_ok    \n"     // exit if atomic
1235                  "_locks_delay:            \n"
1236                  "move $4,    %1           \n"     // $4 <= delay
1237                  "_locks_loop:             \n"
1238                  "addi $4,    $4,    -1    \n"     // $4 <= $4 - 1
1239                  "beqz $4,    _locks_loop  \n"     // test end delay
1240                  "j           _locks_llsc  \n"     // retry
1241                  "_locks_ok:                   \n"
1242                  ::"r"(plock),"r"(delay):"$2","$3","$4");
[158]1243}
1244
[622]1245
1246//////////////////////////////////////////////////////////////////////////////////////
1247// This function makes a cooperative initialisation of the barrier:
1248// - barrier_count[index] <= N
1249// - barrier_lock[index]  <= 0
1250// All tasks try to initialize the barrier, but the initialisation
1251// is done by only one task, using LL/SC instructions.
1252// This cooperative initialisation is questionnable,
1253// because the barrier can ony be initialised once...
1254//////////////////////////////////////////////////////////////////////////////////////
1255in_drivers void _barrier_init(unsigned int index, unsigned int value)
[158]1256{
1257
[622]1258    register int* pinit         = (int*)&_barrier_value[index];
1259    register int* pcount        = (int*)&_barrier_count[index];
1260    register int* plock         = (int*)&_barrier_lock[index];
1261
1262    if ( index >= NB_BARRIERS )
1263    {
1264        _tty_get_lock( 0 );
1265        _tty_puts( "ERROR in _barrier_init()" );
1266        _tty_release_lock( 0 );
1267        _exit();
1268    }
1269
1270    // parallel initialisation using atomic instructions LL/SC
1271    asm volatile ("_barrier_init_test:                  \n"
1272                  "ll   $2,     0(%0)                   \n"     // read barrier_value
1273                  "bnez $2,     _barrier_init_done      \n"
1274                  "move $3,     %3                              \n"
1275                  "sc   $3,     0(%0)                   \n"     // try to write barrier_value
1276                  "beqz $3,     _barrier_init_test      \n"
1277                  "move $3,     %3                                  \n" 
1278                  "sw   $3,     0(%1)                           \n"     // barrier_count <= barrier_value
1279                  "move $3, $0                      \n" //
1280                  "sw   $3,     0(%2)                           \n"     // barrier_lock <= 0
1281                  "_barrier_init_done:                  \n"
1282                  ::"r"(pinit),"r"(pcount),"r"(plock),"r"(value):"$2","$3");
[158]1283}
[629]1284
[622]1285//////////////////////////////////////////////////////////////////////////////////////
1286// This blocking function uses a busy_wait technics (on the barrier_lock value),
1287// because the GIET does not support dynamic scheduling/descheduling of tasks.
1288// The barrier state is actually defined by two variables:
1289// _barrier_count[index] define the number of particpants that are waiting
1290// _barrier_lock[index] define the bool variable whose value is polled
1291// The last participant change the value of _barrier_lock[index] to release the barrier...
1292// There is at most 16 independant barriers, and an error is returned
1293// if the barrier index is larger than 15.
1294//////////////////////////////////////////////////////////////////////////////////////
1295in_drivers void _barrier_wait(unsigned int index)
[158]1296{
[622]1297    register int*       pcount          = (int*)&_barrier_count[index];         
1298    register int        count;
[158]1299
[622]1300    int                lock             = _barrier_lock[index];         
1301
1302    if ( index >= NB_BARRIERS )
1303    {
1304        _tty_get_lock( 0 );
1305        _tty_puts( "ERROR in _barrier_wait()" );
1306        _tty_release_lock( 0 );
1307        _exit();
1308    }
1309   
1310    // parallel decrement _barrier_count[index] using atomic instructions LL/SC
1311    // input : pointer on _barrier_count[index]
1312    // output : count = _barrier_count[index] (before decrementation)
1313    asm volatile ("_barrier_decrement:                          \n"
1314                  "ll   %0,     0(%1)                           \n"
1315                  "addi $3,     %0,     -1                      \n"
1316                  "sc   $3,     0(%1)                           \n"
1317                  "beqz $3,     _barrier_decrement              \n"
1318                  :"=&r"(count)
1319                  :"r"(pcount)
1320                  :"$2","$3");
1321
1322    // the last task re-initializes the barrier_ count variable
1323    // and the barrier_lock variable, waking up all other waiting tasks
1324
1325    if ( count == 1 )    // last task
1326    {
1327        _barrier_count[index] = _barrier_value[index];
1328        asm volatile( "sync" );
1329        _barrier_lock[index]   = (lock == 0) ? 1 : 0;
1330    }
1331    else                // other tasks
1332    {
1333        while ( lock == _barrier_lock[index] )  asm volatile("nop");
1334    }
1335} 
1336
1337
[158]1338// Local Variables:
1339// tab-width: 4;
1340// c-basic-offset: 4;
1341// c-file-offsets:((innamespace . 0)(inline-open . 0));
1342// indent-tabs-mode: nil;
1343// End:
1344//
1345// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
1346
Note: See TracBrowser for help on using the repository browser.