source: trunk/softs/giet_tsar/stdio.c

Last change on this file was 810, checked in by cfuguet, 12 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
Line 
1////////////////////////////////////////////////////////////////////////////////////////
2// File : stdio.c
3// Written by Alain Greiner
4// Date : janvier 2014
5//
6// This file defines various functions that can be used by applications to access
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////////////////////////////////////////////////////////////////////////////////////////
30
31#include "stdio.h"
32
33#if !defined(NB_PROCS_MAX)
34#error: you must define NB_PROCS_MAX in the hard_config.h file
35#endif
36
37#if !defined(X_IO) || !defined(Y_IO)
38#error: you must define X_IO and Y_IO in the hard_config.h file
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
69#define NB_LOCKS 256
70#define NB_BARRIERS 16
71
72#define in_drivers __attribute__((section (".drivers")))
73#define in_unckdata __attribute__((section (".unckdata")))
74#define cacheline_aligned __attribute__((aligned(64)))
75
76//////////////////////////////////////////////////////////////
77// various informations that must be defined in ldscript
78//////////////////////////////////////////////////////////////
79
80struct plouf;
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;
86
87////////////////////////////////////////////////////////////////////////////////////////
88// Global uncachable variables for synchronization between drivers and ISRs
89////////////////////////////////////////////////////////////////////////////////////////
90
91static in_unckdata int volatile _ioc_lock = 0;
92static in_unckdata int volatile _ioc_done = 0;
93static in_unckdata int volatile _ioc_status;
94
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];
101
102////////////////////////////////////////////////////////////////////////////////////////
103// Global uncachable variables for inter-task barriers
104////////////////////////////////////////////////////////////////////////////////////////
105
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 };
109
110////////////////////////////////////////////////////////////////////////////////////////
111// Global uncachable variables for spin_locks using LL/C instructions
112////////////////////////////////////////////////////////////////////////////////////////
113
114static in_unckdata int volatile _spin_lock[NB_LOCKS] = { [0 ... NB_LOCKS-1] = 0 };
115
116////////////////////////////////////////////////////////////////////////////////////////
117// Memcopy taken from MutekH.
118////////////////////////////////////////////////////////////////////////////////////////
119in_drivers void* _memcpy( void* _dst,
120 const void* _src,
121 unsigned int size )
122{
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 }
133
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;
142}
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 }
159
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}
169////////////////////////////////////////////////////////////////////////////////////////
170// Access CP0 and returns processor ident
171// No more than 1024 processors...
172////////////////////////////////////////////////////////////////////////////////////////
173in_drivers unsigned int _procid()
174{
175 unsigned int ret;
176 asm volatile( "mfc0 %0, $15, 1": "=r"(ret) );
177 return (ret & 0x3FF);
178}
179////////////////////////////////////////////////////////////////////////////////////////
180// Access CP0 and returns processor time
181////////////////////////////////////////////////////////////////////////////////////////
182in_drivers unsigned int _proctime()
183{
184 unsigned int ret;
185 asm volatile( "mfc0 %0, $9": "=r"(ret) );
186 return ret;
187}
188////////////////////////////////////////////////////////////////////////////////////////
189// Returns the number of processsors controled by the GIET
190////////////////////////////////////////////////////////////////////////////////////////
191in_drivers inline unsigned int _procnumber()
192{
193 return (unsigned int)(NB_PROCS_MAX * X_SIZE * Y_SIZE);
194}
195////////////////////////////////////////////////////////////////////////////////////////
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////////////////////////////////////////////////////////////////////////////////////////
207// Access CP0 and enable IRQs
208////////////////////////////////////////////////////////////////////////////////////////
209in_drivers inline void _it_enable()
210{
211 asm volatile(
212 "mfc0 $8, $12 \n"
213 "ori $8, $8, 1 \n"
214 "mtc0 $8, $12 \n"
215 ::: "$8");
216}
217////////////////////////////////////////////////////////////////////////////////////////
218// Access CP0 and mask IRQs
219////////////////////////////////////////////////////////////////////////////////////////
220in_drivers inline void _it_disable()
221{
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");
228}
229
230////////////////////////////////////////////////////////////////////////////////////////
231// Access CP0 and mask IRQs
232////////////////////////////////////////////////////////////////////////////////////////
233in_drivers inline void _sr_write(int sr)
234{
235 asm volatile("mtc0 %0, $12 \n" : /* no outputs */ : "r" (sr));
236}
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
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;
253
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
266////////////////////////////////////////////////////////////////////////////
267// This function makes a physical read access to a 32 bits word in memory,
268// after a temporary paddr extension.
269////////////////////////////////////////////////////////////////////////////
270in_drivers volatile unsigned int _word_extended_read( unsigned int cluster,
271 unsigned int address )
272{
273 int sr = _sr_read();
274 volatile unsigned int value;
275 asm volatile(
276 "li $3, 0xFFFFFFFE \n"
277 "and $3, %3, $3 \n"
278 "mtc0 $3, $12 \n" /* IRQ disabled */
279
280 "mtc2 %2, $24 \n" /* PADDR_EXT <= msb */
281 "lw %0, 0(%1) \n" /* value <= *paddr */
282 "mtc2 $0, $24 \n" /* PADDR_EXT <= 0 */
283
284 : "=r" (value)
285 : "r" (address), "r" (cluster), "r" (sr)
286 : "$2", "$3", "memory" );
287
288 _sr_write(sr);
289 return value;
290}
291////////////////////////////////////////////////////////////////////////////
292// This function makes a physical read access to a single byte in memory,
293// after a temporary paddr extension.
294////////////////////////////////////////////////////////////////////////////
295in_drivers volatile unsigned char _byte_extended_read( unsigned int cluster,
296 unsigned int address )
297{
298 int sr = _sr_read();
299 volatile unsigned char value;
300 asm volatile(
301 "li $3, 0xFFFFFFFE \n"
302 "and $3, %3, $3 \n"
303 "mtc0 $3, $12 \n" /* IRQ disabled */
304
305 "mtc2 %2, $24 \n" /* PADDR_EXT <= msb */
306 "lb %0, 0(%1) \n" /* value <= *paddr */
307 "mtc2 $0, $24 \n" /* PADDR_EXT <= 0 */
308
309 : "=r" (value)
310 : "r" (address), "r" (cluster), "r" (sr)
311 : "$2", "$3", "memory" );
312
313 _sr_write(sr);
314 return value;
315}
316////////////////////////////////////////////////////////////////////////////
317// This function makes a physical write access to a 32 bits word in memory,
318// after a temporary DTLB address extension.
319////////////////////////////////////////////////////////////////////////////
320in_drivers void _word_extended_write( unsigned int cluster,
321 unsigned int address,
322 unsigned int word )
323{
324 int sr = _sr_read();
325 asm volatile(
326 "li $3, 0xFFFFFFFE \n"
327 "and $3, %3, $3 \n"
328 "mtc0 $3, $12 \n" /* IRQ disabled */
329
330 "mtc2 %2, $24 \n" /* PADDR_EXT <= msb */
331 "sw %0, 0(%1) \n" /* *paddr <= value */
332 "mtc2 $0, $24 \n" /* PADDR_EXT <= 0 */
333
334 "sync \n"
335 :
336 : "r" (word), "r" (address), "r" (cluster), "r" (sr)
337 : "$2", "$3", "memory");
338
339 _sr_write(sr);
340}
341////////////////////////////////////////////////////////////////////////////
342// This function makes a physical write access to single byte in memory,
343// after a temporary DTLB de-activation and address extension.
344////////////////////////////////////////////////////////////////////////////
345in_drivers void _byte_extended_write( unsigned int cluster,
346 unsigned int address,
347 unsigned char byte )
348{
349 int sr = _sr_read();
350 asm volatile(
351 "li $3, 0xFFFFFFFE \n"
352 "and $3, %3, $3 \n"
353 "mtc0 $3, $12 \n" /* IRQ disabled */
354
355 "mtc2 %2, $24 \n" /* PADDR_EXT <= msb */
356 "sb %0, 0(%1) \n" /* *paddr <= value */
357 "mtc2 $0, $24 \n" /* PADDR_EXT <= 0 */
358
359 "sync \n"
360 :
361 : "r" (byte), "r" (address), "r" (cluster), "r" (sr)
362 : "$2", "$3", "memory");
363
364 _sr_write(sr);
365}
366
367///////////////////////////////////////////////////////////////////////////////////////
368// Exit (suicide) after printing message on TTY0
369///////////////////////////////////////////////////////////////////////////////////////
370in_drivers void _exit()
371{
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
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 );
386
387 while(1) asm volatile("nop"); // infinite loop...
388}
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)
394{
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;
402 }
403}
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)
408{
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;
415 }
416}
417
418
419///////////////////////////////////////////////////////////////////////////////////////
420// VCI MULTI_TTY
421///////////////////////////////////////////////////////////////////////////////////////
422// The total number of TTY terminals is defined by NB_TTY_CHANNELS.
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.
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
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.
431///////////////////////////////////////////////////////////////////////////////////////
432
433///////////////////////////////////////////////////////////////////////////////////////
434// Write one or several characters directly from a fixed length user buffer
435// to the TTY_WRITE register of the TTY controler.
436// The channel index must be checked by the calling function.
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///////////////////////////////////////////////////////////////////////////////////////
441in_drivers int _tty_write( char* buffer,
442 unsigned int length,
443 unsigned int channel )
444{
445 unsigned int base = (unsigned int)&seg_tty_base + channel*TTY_SPAN*4;
446 unsigned int nwritten = 0;
447 unsigned int status;
448 unsigned int i;
449
450 for ( i=0 ; i < length ; i++ )
451 {
452 status = _word_extended_read( CLUSTER_IO, base + TTY_STATUS*4 );
453 if ( (status & 0x2) == 0x2 ) break;
454 else
455 {
456 _byte_extended_write( CLUSTER_IO, base + TTY_WRITE*4 , buffer[i] );
457 nwritten++;
458 }
459 }
460
461 return nwritten;
462}
463
464///////////////////////////////////////////////////////////////////////////////////////
465// Fetch one character directly from the TTY_READ register of the TTY controler,
466// and writes this character to the user buffer.
467// The channel index must be checked by the calling function.
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///////////////////////////////////////////////////////////////////////////////////////
471in_drivers int _tty_read( char* buffer,
472 unsigned int channel )
473{
474 unsigned int base = (unsigned int)&seg_tty_base + channel*TTY_SPAN*4;
475 unsigned int status;
476
477 status = _word_extended_read( CLUSTER_IO, base + TTY_STATUS*4 );
478 if ( (status & 0x1) == 0x1 )
479 {
480 buffer[0] = (char)_word_extended_read( CLUSTER_IO, base + TTY_READ*4 );
481 return 1;
482 }
483 return 0;
484}
485
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 )
491{
492 int length = 0;
493 while (string[length] != 0) length++;
494 _tty_write( string, length, 0 );
495}
496
497///////////////////////////////////////////////////////////////////////////////
498// This function displays a 32 bits unsigned int as an hexa string on TTY0.
499///////////////////////////////////////////////////////////////////////////////
500in_drivers void _tty_putx(unsigned int val)
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
510 for (c = 0; c < 8; c++)
511 {
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///////////////////////////////////////////////////////////////////////////////
521in_drivers void _tty_putd( unsigned int val )
522{
523 static const char DecTab[] = "0123456789";
524 char buf[11];
525 unsigned int i;
526 unsigned int first = 0;
527
528 buf[10] = 0;
529
530 for (i = 0; i < 10; i++)
531 {
532 if ((val != 0) || (i == 0))
533 {
534 buf[9 - i] = DecTab[val % 10];
535 first = 9 - i;
536 }
537 else
538 {
539 break;
540 }
541 val /= 10;
542 }
543 _tty_puts( &buf[first] );
544}
545
546//////////////////////////////////////////////////////////////////////////////
547// This function try to take the hardwired lock protecting exclusive access
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{
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");
563}
564
565//////////////////////////////////////////////////////////////////////////////
566// This function releases the hardwired lock protecting exclusive access
567// to TTY terminal identified by the channel argument.
568//////////////////////////////////////////////////////////////////////////////
569in_drivers void _tty_release_lock( unsigned int channel )
570{
571 _tty_channel[channel].lock = 0;
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
587 // check TTY channel
588 l = (proc_id % NB_PROCS_MAX);
589 x = (proc_id / NB_PROCS_MAX) >> Y_WIDTH;
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 )
593 {
594 _tty_get_lock( 0 );
595 _tty_puts( "ERROR in _tty_getc(): TTY index too large\n" );
596 _tty_release_lock( 0 );
597 _exit();
598 }
599
600 while( _tty_read( buf, channel ) == 0 ) asm volatile("nop");
601}
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.
612// When the <LF> or <CR> character is received, the string is converted
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 )
617{
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;
632 unsigned int l;
633
634 // check TTY channel
635 l = (proc_id % NB_PROCS_MAX);
636 x = (proc_id / NB_PROCS_MAX) >> Y_WIDTH;
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 )
640 {
641 _tty_get_lock( 0 );
642 _tty_puts( "ERROR in _tty_getw(): TTY index too large\n" );
643 _tty_release_lock( 0 );
644 _exit();
645 }
646
647 while( done == 0 )
648 {
649 _tty_read( &byte, channel );
650
651 if (( byte > 0x2F) && (byte < 0x3A)) // decimal character
652 {
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)
664 {
665 max--; // cancel the character
666 _tty_write( cancel_string, 3, channel );
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 {
686 _tty_write( cancel_string, 3, channel );
687 }
688 _tty_write( &zero, 1, channel );
689 *word_buffer = 0; // return 0 value
690 }
691}
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, ...)
704{
705 va_list ap;
706 va_start( ap, format );
707
708 unsigned int channel;
709 unsigned int x;
710 unsigned int y;
711 unsigned int proc_id = _procid();
712
713 // compute TTY channel :
714 // if the number of TTY channels is smaller
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) )
718 {
719 channel = 0;
720 }
721 else
722 {
723 x = (proc_id / NB_PROCS_MAX) >> Y_WIDTH;
724 y = (proc_id / NB_PROCS_MAX) & ((1<<Y_WIDTH) - 1);
725 channel = (x * Y_SIZE + y);
726 }
727
728 // take the TTY lock
729 _tty_get_lock( channel );
730
731printf_text:
732
733 while (*format)
734 {
735 unsigned int i;
736 for (i = 0; format[i] && format[i] != '%'; i++)
737 ;
738 if (i)
739 {
740 _tty_write( format, i, channel );
741 format += i;
742 }
743 if (*format == '%')
744 {
745 format++;
746 goto printf_arguments;
747 }
748 } // end while
749
750 va_end( ap );
751
752 // release lock
753 _tty_release_lock( 0 );
754
755 return;
756
757printf_arguments:
758
759 {
760 int val = va_arg(ap, long);
761 char buf[20];
762 char* pbuf;
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
774 if (val < 0)
775 {
776 val = -val;
777 _tty_write( "_" , 1, channel );
778 }
779 case ('u'): // decimal unsigned integer
780 for( i=0 ; i<10 ; i++)
781 {
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
789 _tty_write( "0x", 2, channel );
790 for( i=0 ; i<8 ; i++)
791 {
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
809 _tty_write( pbuf, len, channel );
810 goto printf_text;
811 }
812} // end printf()
813
814//////////////////////////////////////////////////////////////////////////////////////
815// These functions are the ISRs that must be executed when an IRQ is activated
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].
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)
822{
823 unsigned int base = (unsigned int)&seg_tty_base;
824 unsigned int offset = (index*TTY_SPAN + TTY_READ) << 2;
825
826 _tty_channel[index].get_buf = _byte_extended_read(CLUSTER_IO, base + offset);
827 _tty_channel[index].get_full = 1; // signals character available
828}
829
830in_drivers void _tty_isr() { _tty_isr_indexed(0); }
831
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//////////////////////////////////////////////////////////////////////////////////////////
867// BLOCK_DEVICE (IOC)
868//////////////////////////////////////////////////////////////////////////////////////////
869// The block size is 512 bytes.
870// The functions below use the three variables _ioc_lock _ioc_done,
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.
875// and set _ioc_lock to a non zero value.
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///////////////////////////////////////////////////////////////////////////////////////
885// If USE_IOC_RDK is set, we access a "virtual" block device controler implemented
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.
888///////////////////////////////////////////////////////////////////////////////////////
889
890///////////////////////////////////////////////////////////////////////////////////////
891// This blocking function is used by the _ioc_read() and _ioc_write() functions
892// to get _ioc_lock using LL/SC.
893///////////////////////////////////////////////////////////////////////////////////////
894in_drivers void _ioc_get_lock()
895{
896 register unsigned int* plock = (unsigned int*)&_ioc_lock;
897
898 asm volatile (
899 "1: \n"
900 "ll $2, 0(%0) \n" // $2 <= _ioc_lock
901 "bnez $2, 1b \n" // retry if busy
902 "li $3, 1 \n" // prepare argument for sc
903 "sc $3, 0(%0) \n" // try to set _ioc_busy
904 "beqz $3, 1b \n" // retry if not atomic
905 ::"r"(plock) :"$2","$3");
906}
907
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
913// - ext : cluster index for the memory buffer
914///////////////////////////////////////////////////////////////////////////////////////
915in_drivers void _ioc_write( size_t lba,
916 void* buffer,
917 size_t count,
918 size_t ext )
919{
920 // get the lock
921 _ioc_get_lock();
922
923 if ( USE_IOC_RDK ) // we use an extended_memcpy
924 {
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 );
935
936 _ioc_status = BLOCK_DEVICE_WRITE_SUCCESS;
937 _ioc_done = 1;
938
939 return;
940 }
941
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 );
949}
950
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
956// - ext : cluster index for the memory buffer
957///////////////////////////////////////////////////////////////////////////////////////
958in_drivers void _ioc_read( size_t lba,
959 void* buffer,
960 size_t count,
961 size_t ext )
962{
963 // get the lock
964 _ioc_get_lock();
965
966 if ( USE_IOC_RDK ) // we use an extended_memcpy
967 {
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;
981
982 return;
983 }
984
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 );
992}
993
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
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()
1005{
1006 // waiting for completion
1007 while (_ioc_done == 0) asm volatile("nop");
1008
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 }
1021}
1022
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()
1030{
1031 unsigned int base = (unsigned int)&seg_ioc_base;
1032
1033 _ioc_status = _word_extended_read( CLUSTER_IO, base + BLOCK_DEVICE_STATUS*4 );
1034 _ioc_done = 1; // signals completion
1035}
1036
1037//////////////////////////////////////////////////////////////////////////////////////
1038// FRAME_BUFFER
1039//////////////////////////////////////////////////////////////////////////////////////
1040// The _fb_sync_write & _fb_sync_read functions use a memcpy strategy to implement
1041// the transfer between a data buffer and the frame buffer.
1042// They are blocking until completion of the transfer.
1043//////////////////////////////////////////////////////////////////////////////////////
1044
1045//////////////////////////////////////////////////////////////////////////////////////
1046// _fb_sync_write()
1047// Transfer data from an user buffer to the frame_buffer device with a memcpy.
1048// - offset : offset (in bytes) in the frame buffer
1049// - buffer : base address of the memory buffer
1050// - length : number of bytes to be transfered
1051// - ext : cluster_xy for the user buffer
1052//////////////////////////////////////////////////////////////////////////////////////
1053in_drivers void _fb_sync_write( unsigned int offset,
1054 unsigned int buffer,
1055 unsigned int length,
1056 unsigned int ext )
1057{
1058 unsigned int src_address = buffer;
1059 unsigned int src_cluster = ext;
1060 unsigned int dst_address = (unsigned int)&seg_fbf_base + offset;
1061
1062 _extended_memcpy( CLUSTER_IO,
1063 dst_address,
1064 src_cluster,
1065 src_address,
1066 length );
1067}
1068
1069///////////////////////////////////////////////////////////////////////////////////////
1070// _fb_sync_read()
1071// Transfer data from the frame_buffer device to an user buffer with a memcpy.
1072// - offset : offset (in bytes) in the frame buffer
1073// - buffer : base address of the memory buffer
1074// - length : number of bytes to be transfered
1075// - ext : cluster_xy for the user buffer
1076//////////////////////////////////////////////////////////////////////////////////////
1077in_drivers void _fb_sync_read( unsigned int offset,
1078 unsigned int buffer,
1079 unsigned int length,
1080 unsigned int ext )
1081{
1082 unsigned int dst_address = buffer;
1083 unsigned int dst_cluster = ext;
1084 unsigned int src_address = (unsigned int)&seg_fbf_base + offset;
1085
1086 _extended_memcpy( dst_cluster,
1087 dst_address,
1088 CLUSTER_IO,
1089 src_address,
1090 length );
1091}
1092
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{
1101 //int* mmc_address = (int*)&seg_mmc_base;
1102 unsigned int cluster_xy = _procid() / NB_PROCS_MAX;
1103
1104 _tty_printf( "WRITE ERROR signaled by Memory Cache in cluster %x\n", cluster_xy );
1105}
1106
1107///////////////////////////////////////////////////////////////////////////////////////
1108// Release a software spin-lock
1109///////////////////////////////////////////////////////////////////////////////////////
1110in_drivers void _release_lock(size_t index)
1111
1112{
1113 if( index >= NB_LOCKS )
1114 {
1115 _tty_get_lock( 0 );
1116 _tty_puts( "ERROR in _release_lock()" );
1117 _tty_release_lock( 0 );
1118 _exit();
1119 }
1120
1121 _spin_lock[index] = 0;
1122}
1123
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)
1132{
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 }
1140
1141 register int delay = ((_proctime() +_procid()) & 0xF) << 4;
1142 register int * plock = (int *) &_spin_lock[index];
1143
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
1147 "li $3, 1 \n" // prepare argument for sc
1148 "sc $3, 0(%0) \n" // try to set _locks_busy
1149 "bnez $3, _locks_ok \n" // exit if atomic
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
1156 "_locks_ok: \n"
1157 ::"r"(plock),"r"(delay):"$2","$3","$4");
1158}
1159
1160
1161//////////////////////////////////////////////////////////////////////////////////////
1162// This function makes a cooperative initialisation of the barrier:
1163// - barrier_count[index] <= N
1164// - barrier_lock[index] <= 0
1165// All tasks try to initialize the barrier, but the initialisation
1166// is done by only one task, using LL/SC instructions.
1167// This cooperative initialisation is questionnable,
1168// because the barrier can ony be initialised once...
1169//////////////////////////////////////////////////////////////////////////////////////
1170in_drivers void _barrier_init(unsigned int index, unsigned int value)
1171{
1172 register int* pinit = (int*)&_barrier_value[index];
1173 register int* pcount = (int*)&_barrier_count[index];
1174 register int* plock = (int*)&_barrier_lock[index];
1175
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
1185 asm volatile ("_barrier_init_test: \n"
1186 "ll $2, 0(%0) \n" // read barrier_value
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"
1191 "move $3, %3 \n"
1192 "sw $3, 0(%1) \n" // barrier_count <= barrier_value
1193 "move $3, $0 \n" //
1194 "sw $3, 0(%2) \n" // barrier_lock <= 0
1195 "_barrier_init_done: \n"
1196 ::"r"(pinit),"r"(pcount),"r"(plock),"r"(value):"$2","$3");
1197}
1198
1199//////////////////////////////////////////////////////////////////////////////////////
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.
1202// The barrier state is actually defined by two variables:
1203// _barrier_count[index] define the number of particpants that are waiting
1204// _barrier_lock[index] define the bool variable whose value is polled
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)
1210{
1211 register int* pcount = (int*)&_barrier_count[index];
1212 register int count;
1213 int lock = _barrier_lock[index];
1214
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 }
1222
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)
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"
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
1238 if ( count == 1 ) // last task
1239 {
1240 _barrier_count[index] = _barrier_value[index];
1241 asm volatile( "sync" );
1242 _barrier_lock[index] = (lock == 0) ? 1 : 0;
1243 }
1244 else // other tasks
1245 {
1246 while ( lock == _barrier_lock[index] );
1247 }
1248}
1249
1250
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.