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

Last change on this file since 1039 was 810, checked in by cfuguet, 10 years ago

giet_tsar: updating giet_tsar to use new hard_config format

Other optimizations:

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