Index: /soft/giet_vm/applications/mjpeg/Makefile
===================================================================
--- /soft/giet_vm/applications/mjpeg/Makefile	(revision 723)
+++ /soft/giet_vm/applications/mjpeg/Makefile	(revision 723)
@@ -0,0 +1,52 @@
+
+CC = mipsel-unknown-elf-gcc
+AS = mipsel-unknown-elf-as
+LD = mipsel-unknown-elf-ld
+DU = mipsel-unknown-elf-objdump
+AR = mipsel-unknown-elf-ar
+
+APP_NAME = mjpeg
+
+OBJS = mjpeg.o \
+       tg.o    \
+       demux.o \
+       vld.o   \
+       iqzz.o  \
+       idct.o  \
+       libu.o
+
+CFLAGS = -G0
+
+LIBS= -L../../build/libs -luser
+
+INCLUDES = -I.  -I../..  -I../../giet_libs  -I../../giet_xml  
+
+LIB_DEPS = ../../build/libs/libuser.a
+
+appli.elf: $(OBJS) $(APP_NAME).ld $(LIBS_DEPS) 
+	$(LD) -o $@ -T $(APP_NAME).ld $(OBJS) $(LIBS)
+	$(DU) -D $@ > $@.txt
+
+mjpeg.o: mjpeg.c 
+	$(CC)  $(INCLUDES) $(CFLAGS) -c -o  $@ $<
+
+tg.o: tg.c
+	$(CC)  $(INCLUDES) $(CFLAGS) -c -o  $@ $<
+
+demux.o: demux.c
+	$(CC)  $(INCLUDES) $(CFLAGS) -c -o  $@ $<
+
+vld.o: vld.c
+	$(CC)  $(INCLUDES) $(CFLAGS) -c -o  $@ $<
+
+iqzz.o: iqzz.c
+	$(CC)  $(INCLUDES) $(CFLAGS) -c -o  $@ $<
+
+idct.o: idct.c
+	$(CC)  $(INCLUDES) $(CFLAGS) -c -o  $@ $<
+
+libu.o: libu.c
+	$(CC)  $(INCLUDES) $(CFLAGS) -c -o  $@ $<
+
+clean:
+	rm -f *.o *.elf *.txt core *~
Index: /soft/giet_vm/applications/mjpeg/demux.c
===================================================================
--- /soft/giet_vm/applications/mjpeg/demux.c	(revision 723)
+++ /soft/giet_vm/applications/mjpeg/demux.c	(revision 723)
@@ -0,0 +1,381 @@
+////////////////////////////////////////////////////////////////////////////////////////
+// File   : demux.c  
+// Date   : octobre 2015
+// author : Alain Greiner
+////////////////////////////////////////////////////////////////////////////////////////
+// This file define the code of the DEMUX  thread for the MJPEG application.
+// This function makes the analysis of the MJPEG stream of bytes: For each 
+// compressed image arriving on the <in> input MWMR channel, it dispathch the stream
+// on three output MWMR channels:
+// - the <out_quanti> channel receive the quantisation table segment.
+// - the <out_huff> channel receive the huffman tables.
+// - the <out_data> channel receive the compressed bit stream.
+// It uses four BUFIO local buffers, connected to the four MWMR channels.
+// It search specifically the following 16 bits markers : 
+// - the SOI_MK : Start of Image marquer
+// - the DQT_MK : Quantization Table marker 
+// - the DHT_MK : Huffman Table marker
+// - the SOS_MK : Start of Scan marker
+// - the EOI_MK : REnd of Image marker
+////////////////////////////////////////////////////////////////////////////////////////
+
+#include <stdio.h>
+#include <stdint.h>
+#include <mwmr_channel.h>
+#include "mjpeg.h"
+
+#define SOI_MK	0xFFD8		// start of image
+#define APP_MK	0xFFE0		// custom, up to FFEF
+#define COM_MK	0xFFFE		// commment segment
+#define SOF_MK	0xFFC0		// start of frame
+#define SOS_MK	0xFFDA		// start of scan
+#define DHT_MK	0xFFC4		// Huffman table
+#define DQT_MK	0xFFDB		// Quant. table	
+#define EOI_MK	0xFFD9		// end of image		
+#define MK_MSK	0xFFF0
+
+// macro to use a shared TTY
+#define PRINTF(...)    lock_acquire( &tty_lock ); \
+                       giet_tty_printf(__VA_ARGS__);  \
+                       lock_release( &tty_lock );
+
+///////////////////////////////////////////////////////////
+// This function discard all bytes from an input <bufio>, 
+// until it found a marker, and returns the marker value.
+///////////////////////////////////////////////////////////
+uint16_t get_next_marker( mwmr_bufio_t* bufio )
+{
+    uint16_t   marker;
+    uint8_t    byte;
+    uint8_t    ff_found = 0;
+
+    do 
+    {
+        byte = mwmr_bufio_read_byte( bufio );
+
+        if ( ff_found )
+        {
+            ff_found = 0;
+            marker = byte | 0xFF00;
+
+            return marker;
+        }
+        else if ( byte == 0xFF ) 
+        {
+            ff_found = 1;
+        }
+    } while (1);
+}
+      
+
+//////////////////////////////////////////////////////////////
+__attribute__ ((constructor)) void demux( uint32_t index )
+//////////////////////////////////////////////////////////////
+{
+    // get platform parameters
+    uint32_t  x_size;
+    uint32_t  y_size;
+    uint32_t  nprocs;
+    giet_procs_number( &x_size , &y_size , &nprocs );
+
+    // get processor coordinates
+    uint32_t x, y, p;
+    giet_proc_xyp( &x , &y , &p );
+ 
+    // private TTY allocation
+    // giet_tty_alloc( 0 );
+
+    PRINTF("\n[MJPEG] thread DEMUX[%d] starts on P[%d,%d,%d]\n", index, x, y, p )
+
+    // initialise BUFIO for MWMR channel <in>
+    mwmr_channel_t*   mwmr_in = tg_2_demux[index];
+    mwmr_bufio_t      bufio_in;
+    uint8_t           in_buffer[64];
+    mwmr_bufio_init( &bufio_in , in_buffer , 64 , 1 , mwmr_in );
+
+#if (DEBUG_DEMUX > 1)
+PRINTF("\nDEMUX[%d] <in> : &mwmr = %x / &bufio = %x\n", 
+       index , mwmr_in , &bufio_in )
+#endif
+
+    // initialise BUFIO for MWMR channel <out_quanti>
+    mwmr_channel_t*   mwmr_out_quanti = demux_2_iqzz[index];
+    mwmr_bufio_t      bufio_out_quanti;
+    uint8_t           out_quanti_buffer[64];
+    mwmr_bufio_init( &bufio_out_quanti , out_quanti_buffer , 64 , 0 , mwmr_out_quanti );
+
+#if (DEBUG_DEMUX > 1)
+PRINTF("\nDEMUX[%d] : <out_quanti> : mwmr = %x / &bufio = %x\n",
+       index , mwmr_out_quanti , &bufio_out_quanti )
+#endif
+
+    // initialise BUFIO for MWMR channel <out_huff>
+    mwmr_channel_t*   mwmr_out_huff = demux_2_vld_huff[index];
+    mwmr_bufio_t      bufio_out_huff;
+    uint8_t           out_huff_buffer[64];
+    mwmr_bufio_init( &bufio_out_huff , out_huff_buffer , 64 , 0 , mwmr_out_huff );
+
+#if (DEBUG_DEMUX > 1)
+PRINTF("\nDEMUX[%d] : <out_huff> : mwmr = %x / &bufio = %x\n",
+       index , mwmr_out_huff , &bufio_out_huff )
+#endif
+
+    // initialise BUFIO for MWMR channel <out_data>
+    mwmr_channel_t*   mwmr_out_data = demux_2_vld_data[index];
+    mwmr_bufio_t      bufio_out_data;
+    uint8_t           out_data_buffer[64];
+    mwmr_bufio_init( &bufio_out_data , out_data_buffer , 64 , 0 , mwmr_out_data );
+
+#if (DEBUG_DEMUX > 1)
+PRINTF("\nDEMUX[%d] : <out_data> : mwmr = %x / &bufio = %x\n",
+       index , mwmr_out_data , &bufio_out_data )
+#endif
+
+    uint32_t   found_marker;
+    uint32_t   image_done;
+
+    uint16_t   marker;          // 16 bits marker read from bufio_in
+    uint32_t   word;            // 32 bits word read from bufio_in
+    uint8_t    byte;            // one byte read from bufio_in
+    uint32_t   byte_count;      // byte counter in a given compressed image
+    
+    uint32_t   image = index;
+
+    // infinite loop : one image per iteration
+    while ( image < MAX_IMAGES ) 
+    {
+        // search start of image marker
+        do 
+        {
+            marker = get_next_marker( &bufio_in );
+
+#if (DEBUG_DEMUX > 1)
+if ( marker == SOI_MK ) PRINTF("\nDEMUX[%x] found Start of Image marker\n", index )
+#endif
+        } 
+        while ( marker != SOI_MK );
+
+        found_marker = 0;
+        image_done   = 0;
+
+        // analyse image
+        while ( !image_done ) 
+        {
+            // search next marker if required
+            if ( !found_marker ) 
+            {
+                marker = get_next_marker( &bufio_in );
+            }
+
+            ////////////////////////////////////////////
+            if ( marker == SOF_MK )   // start of frame
+            {
+
+#if (DEBUG_DEMUX > 1)
+PRINTF("\nDEMUX[%x] found Start of Frame marker\n", index )
+#endif
+                // Only one component per image is supported
+                // we check the image size (must fit Frame Buffer),
+                // and skip the fixed length frame header
+
+                // skip 3 bytes (Lf , P) from bufio_in
+                mwmr_bufio_skip( &bufio_in , 3 );
+
+                // read width & height from bufio_in
+                uint32_t height = (uint32_t)mwmr_bufio_read_byte( &bufio_in );
+                height = (height<<8) | mwmr_bufio_read_byte( &bufio_in );
+
+                uint32_t width  = (uint32_t)mwmr_bufio_read_byte( &bufio_in );
+                width  = (width<<8)  | mwmr_bufio_read_byte( &bufio_in );
+
+                giet_pthread_assert( (fbf_width == width) && (fbf_height == height) ,
+                "ERROR in demux() : image size doesn't fit frame buffer size" );
+
+                // read one byte (number of components) from bufio_in
+                uint8_t nf  = mwmr_bufio_read_byte( &bufio_in );
+
+                giet_pthread_assert( (nf==1) ,
+                "ERROR in demux() : only one component supported" );
+
+                // skip 3 bytes (C0,H0,V0,TQ0)
+                mwmr_bufio_skip( &bufio_in , 3 );
+
+                found_marker = 0;
+            }
+            ///////////////////////////////////////////////////////////
+            else if ( marker == DQT_MK )   // quantization table marker
+            {
+
+#if (DEBUG_DEMUX > 1)
+PRINTF("\nDEMUX[%x] found Quantization Table marker\n", index )
+#endif
+                // The  quantisation table segment being fixed length, 
+                // we skip the header and write the 64 coefs to out_quanti channel.
+
+                uint32_t  n;
+
+                // skip three first bytes (Lq , PqTq) from bufio_in
+                mwmr_bufio_skip( &bufio_in , 3 );
+
+                // write 64 coefs (one byte each) 
+                for ( n = 0 ; n < 64 ; n++ )
+                {
+                    uint8_t byte = mwmr_bufio_read_byte( &bufio_in );
+                    mwmr_bufio_write_byte( &bufio_out_quanti , byte );
+                }
+
+                // flush out_quanti buffer
+                mwmr_bufio_flush( &bufio_out_quanti );
+
+                found_marker = 0;
+            }
+            ///////////////////////////////////////////////////////
+            else if ( marker == DHT_MK )   // Huffman table marker
+            {
+
+#if (DEBUG_DEMUX > 1)
+PRINTF("\nDEMUX[%x] found Huffman Table marker\n", index )
+#endif
+                // The Huffman Table segment being variable length, 
+                // we search the next marker, and transfer the complete
+                // segment (without marker) to the out_huff channel.
+
+                found_marker = 0;
+
+                while ( !found_marker ) 
+                {
+                    // read one byte from bufio_in
+                    byte = mwmr_bufio_read_byte( &bufio_in );
+
+                    if ( byte == 0xFF )  // potential marker
+                    {
+                        // read another byte from bufio_in
+                        byte = mwmr_bufio_read_byte( &bufio_in );
+
+                        if (byte == 0)  // not a real marker
+                        {
+                            // write one byte to bufio_out_huff
+                            mwmr_bufio_write_byte( &bufio_out_huff, 0xFF );
+                        }
+                        else            // it's a marker 
+                        {
+                            marker       = 0xFF00 | byte;
+                            found_marker = 1;
+                        }
+                    }
+                    else                // normal byte 
+                    {
+                        // write one byte to bufio_out_huff
+                        mwmr_bufio_write_byte( &bufio_out_huff , byte );
+                    }
+                }
+
+                // flush out_huff bufio
+                mwmr_bufio_flush( &bufio_out_huff );
+            } 
+            ///////////////////////////////////////////////////////
+            else if ( marker == SOS_MK )    // start of scan marker 
+            {
+
+#if (DEBUG_DEMUX > 1)
+PRINTF("\nDEMUX[%x] found Start of Scan marker\n", index )
+#endif
+                // The scan segment has a variable length: 
+                // we skip the header, and search the next marker,
+                // to transfer the segment (without header) to out_data channel.
+
+                // read 2 bytes (scan header length) from bufio_in
+                uint32_t length = (uint32_t)mwmr_bufio_read_byte( &bufio_in );
+                length = (length<<8) | mwmr_bufio_read_byte( &bufio_in );
+
+                // skip scan segment header 
+                mwmr_bufio_skip( &bufio_in , length-2 );
+
+                found_marker = 0;
+                 
+                while ( !found_marker ) 
+                {
+                    // read one byte from bufio_in
+                    byte = mwmr_bufio_read_byte( &bufio_in );
+
+                    if ( byte == 0xFF )  // potential marker
+                    {
+                        // read another byte from bufio_in
+                        byte = mwmr_bufio_read_byte( &bufio_in );
+
+                        if ( byte == 0x00 )   // not a real marker
+                        {
+                            // write one byte to bufio_out_data
+                            mwmr_bufio_write_byte( &bufio_out_data, 0xFF);
+                        }
+                        else                  // it is a marker
+                        {
+                            marker       = 0xFF00 | byte;
+                            found_marker = 1;
+                        }
+                    }
+                    else   // normal byte
+                    {
+                        // write one byte to bufio_out_data
+                        mwmr_bufio_write_byte( &bufio_out_data , byte );
+                    }
+                }
+
+                // flush out_data bufio
+                mwmr_bufio_flush( &bufio_out_data );
+            }
+            //////////////////////////////////////////////////////
+            else if ( marker == EOI_MK )    // end of image marker
+            {
+
+#if (DEBUG_DEMUX > 1)
+PRINTF("\nDEMUX[%x] found End of Image marker\n", index )
+#endif
+                mwmr_bufio_flush( &bufio_out_data );
+                image_done = 1;
+            }
+            ///////////////////////////////////////////////////////////////////////////
+            else if ( ((marker & MK_MSK) == APP_MK) ||   // application specific marker 
+                      (marker == COM_MK)            )    // comment marker
+            {
+
+#if (DEBUG_DEMUX > 1)
+PRINTF("\nDEMUX[%x] found Comment or Application marker\n", index )
+#endif
+                // read segment length from bufio_in
+                uint32_t length = (uint32_t)mwmr_bufio_read_byte( &bufio_in );
+                length = (length<<8) | mwmr_bufio_read_byte( &bufio_in );
+
+                // skip segment from bufio_in
+                mwmr_bufio_skip( &bufio_in , length - 2 );
+
+                found_marker = 0;
+            }
+            /////////////////////////////////////////////////////////////////////
+            else if ( marker & 0xFFF0 == 0xFFC0 )  // other Start of Frame marker
+            {
+                giet_pthread_assert( 0 ,
+                "ERROR in demux() : only baseline DCT supported");
+            }        
+            ///////////////////////////
+            else   // any other marker
+            {
+                giet_pthread_assert( 0 ,
+                "ERROR in demux() : unsupported marker in MJPEG stream");
+            }
+        }  // end while ( image_done )
+
+#if DEBUG_DEMUX
+PRINTF("\nDEMUX[%d] completes image %d at cycle %d\n", index , image , giet_proctime() )
+#endif
+        image = image + x_size * y_size;
+
+    }  // end while on images
+
+    giet_pthread_exit( "demux completed" );
+
+}  // end demux()
+
+
+
+
Index: /soft/giet_vm/applications/mjpeg/idct.c
===================================================================
--- /soft/giet_vm/applications/mjpeg/idct.c	(revision 723)
+++ /soft/giet_vm/applications/mjpeg/idct.c	(revision 723)
@@ -0,0 +1,246 @@
+////////////////////////////////////////////////////////////////////////////////////////
+// File   : idct.c  
+// Date   : octobre 2015
+// author : Alain Greiner
+////////////////////////////////////////////////////////////////////////////////////////
+// This file define the code of the IDCT (Inverse Discrete Cosinus Transform) thread
+// for the MJPEG application.
+// It read blocks of 8*8 pixels (one int32_t per pixel) from the <in> MWMR channel.
+// It write blocks of 8*8 pixels (one uint8_t per pixel) to the <out> MWMR channel.
+////////////////////////////////////////////////////////////////////////////////////////
+
+#include <mwmr_channel.h>
+#include <stdio.h>
+#include <stdint.h>
+#include "mjpeg.h"
+
+#define   INT_MAX   ((int32_t)0x7fffffff)
+#define   INT_MIN   ((int32_t)0x80000000)
+
+// macro to use a shared TTY
+#define PRINTF(...)    lock_acquire( &tty_lock ); \
+                       giet_tty_printf(__VA_ARGS__);  \
+                       lock_release( &tty_lock );
+
+// Useful constants 
+
+/* ck = cos(k*pi/16) = s8-k = sin((8-k)*pi/16) times 1 << C_BITS and rounded */
+#define c0_1  16384
+#define c0_s2 23170
+#define c1_1  16069
+#define c1_s2 22725
+#define c2_1  15137
+#define c2_s2 21407
+#define c3_1  13623
+#define c3_s2 19266
+#define c4_1  11585
+#define c4_s2 16384
+#define c5_1  9102
+#define c5_s2 12873
+#define c6_1  6270
+#define c6_s2 8867
+#define c7_1  3196
+#define c7_s2 4520
+#define c8_1  0
+#define c8_s2 0
+#define sqrt2 c0_s2
+
+// The number of bits of accuracy in all (signed) integer operations
+
+#define ARITH_BITS      16
+
+// The minimum signed integer value that fits in ARITH_BITS
+#define ARITH_MIN       (-1 << (ARITH_BITS-1))
+#define ARITH_MAX       (~ARITH_MIN)
+
+// The number of bits coefficients are scaled up before 2-D idct
+#define S_BITS           3
+
+// The number of bits in the fractional part of a fixed point constant
+#define C_BITS          14
+
+// This version is vital in passing overall mean error test.
+#define descale(x, n) (((x) + (1 << ((n) - 1)) - ((x) < 0)) >> (n))
+
+static const int32_t COS[2][8] = 
+{
+   {c0_1 , c1_1 , c2_1 , c3_1 , c4_1 , c5_1 , c6_1 , c7_1 },
+   {c0_s2, c1_s2, c2_s2, c3_s2, c4_s2, c5_s2, c6_s2, c7_s2}
+};
+
+////////////////////////////////////
+static inline void rot( int32_t   f, 
+                        int32_t   k,
+                        int32_t   x, 
+                        int32_t   y,
+                        int32_t*  rx,
+                        int32_t*  ry )
+{
+#define Cos(k)  COS[f][k]
+#define Sin(k)  Cos(8-k)
+   *rx = (Cos(k) * x - Sin(k) * y) >> C_BITS;
+   //    r = (r + (1 << (C_BITS - 1))) >> C_BITS;
+   *ry = (Sin(k) * x + Cos(k) * y) >> C_BITS;
+   //    r = (r + (1 << (C_BITS - 1))) >> C_BITS;
+#undef Cos
+#undef Sin
+}
+
+/* Butterfly: but(a,b,x,y) = rot(sqrt(2),4,a,b,x,y) */
+#define but(a,b,x,y)   do { x = a - b; y = a + b; } while(0)
+
+// Inverse 1-D Discrete Cosine Transform.
+// Result Y is scaled up by factor sqrt(8).
+// Original Loeffler algorithm.
+static inline void idct_1d( int32_t* Y )
+{
+   int32_t z1[8], z2[8], z3[8];
+
+   /* Stage 1: */
+   but(Y[0], Y[4], z1[1], z1[0]);
+   rot(1, 6, Y[2], Y[6], &z1[2], &z1[3]);
+   but(Y[1], Y[7], z1[4], z1[7]);
+   z1[5] = (sqrt2 * Y[3]) >> C_BITS;
+   //    r = (r + (1 << (C_BITS - 1))) >> C_BITS;
+   z1[6] = (sqrt2 * Y[5]) >> C_BITS;
+   //    r = (r + (1 << (C_BITS - 1))) >> C_BITS;
+
+   /* Stage 2: */
+   but(z1[0], z1[3], z2[3], z2[0]);
+   but(z1[1], z1[2], z2[2], z2[1]);
+   but(z1[4], z1[6], z2[6], z2[4]);
+   but(z1[7], z1[5], z2[5], z2[7]);
+
+   /* Stage 3: */
+   z3[0] = z2[0];
+   z3[1] = z2[1];
+   z3[2] = z2[2];
+   z3[3] = z2[3];
+   rot(0, 3, z2[4], z2[7], &z3[4], &z3[7]);
+   rot(0, 1, z2[5], z2[6], &z3[5], &z3[6]);
+
+   /* Final stage 4: */
+   but(z3[0], z3[7], Y[7], Y[0]);
+   but(z3[1], z3[6], Y[6], Y[1]);
+   but(z3[2], z3[5], Y[5], Y[2]);
+   but(z3[3], z3[4], Y[4], Y[3]);
+}
+
+//////////////////////////////////////////////////////////////
+__attribute__ ((constructor)) void idct( unsigned int index )
+//////////////////////////////////////////////////////////////
+{
+    mwmr_channel_t* input  = iqzz_2_idct[index];
+    mwmr_channel_t* output = idct_2_libu[index];
+
+    int32_t row;
+    int32_t column;
+    int32_t block;
+
+    int32_t  bin[64];
+    uint8_t  bout[64];
+    int32_t  Y[64];
+
+    // get platform parameters
+    uint32_t  x_size;
+    uint32_t  y_size;
+    uint32_t  nprocs;
+    giet_procs_number( &x_size , &y_size , &nprocs );
+
+    // get processor coordinates
+    unsigned int x , y , p;
+    giet_proc_xyp( &x ,&y , &p );
+
+    // private TTY allocation
+    // giet_tty_alloc( 0 );
+
+    PRINTF("\n[MJPEG] thread IDCT[%d] starts on P[%d,%d,%d]\n", index , x , y , p )
+
+    uint32_t  image = index;
+    uint32_t  nblocks = nblocks_h * nblocks_w;
+
+    while( image < MAX_IMAGES )   // one image per iteration
+    {
+        for ( block = 0 ; block < nblocks ; block++ )
+        {
+            uint32_t begin;
+
+            // read obe block of coefficients (4 bytes per pixel)
+            mwmr_read( input, (uint32_t*)bin , 64 );
+
+            for ( row = 0; row < 8 ; row++ )
+            {
+                for ( column = 0 ; column < 8 ; column++ )
+                {
+                    Y[row * 8 + column] = bin[row * 8 + column] << S_BITS;
+                }
+
+                idct_1d( &Y[8*row] );
+
+                // Result Y is scaled up by factor sqrt(8)*2^S_BITS.
+            }
+
+            for ( column = 0 ; column < 8 ; column++ )
+            {
+                int32_t Yc[8];
+
+                for ( row = 0 ; row < 8 ; row++ )
+                {
+                    Yc[row] = Y[8 * row + column];
+                }
+
+                idct_1d( Yc );
+       
+                for ( row = 0 ; row < 8 ; row++ ) 
+                {
+                    // Result is once more scaled up by a factor sqrt(8). 
+                    int32_t r = 128 + descale(Yc[row], 2 * S_BITS);
+
+                    // Clip to 8 bits unsigned
+                    r = r > 0 ? (r < 255 ? r : 255) : 0;
+
+                    bout[8*row+column] = r;
+
+                    giet_pthread_assert( ((r & 0xFF) == r ) ,
+                    "ERROR in idct() : pixel overflow" ); 
+                }
+            }
+
+            // write one block to output MWMR channel (one byte per pixel)
+            mwmr_write( output, (uint32_t*)bout , 16 );
+
+#if (DEBUG_IDCT > 1) 
+PRINTF("\nIDCT[%d] completes block %d/%d in image %d\n", 
+       index , block , nblocks , image )
+PRINTF("  %x  %x  %x  %x  %x  %x  %x  %x\n"
+       "  %x  %x  %x  %x  %x  %x  %x  %x\n"
+       "  %x  %x  %x  %x  %x  %x  %x  %x\n"
+       "  %x  %x  %x  %x  %x  %x  %x  %x\n"
+       "  %x  %x  %x  %x  %x  %x  %x  %x\n"
+       "  %x  %x  %x  %x  %x  %x  %x  %x\n"
+       "  %x  %x  %x  %x  %x  %x  %x  %x\n"
+       "  %x  %x  %x  %x  %x  %x  %x  %x\n",
+       bout[0] , bout[1] , bout[2] , bout[3] , bout[4] , bout[5] , bout[6] , bout[7] ,
+       bout[8] , bout[9] , bout[10], bout[11], bout[12], bout[13], bout[14], bout[15],
+       bout[16], bout[17], bout[18], bout[19], bout[20], bout[21], bout[22], bout[23],
+       bout[24], bout[25], bout[26], bout[27], bout[28], bout[29], bout[30], bout[31],
+       bout[32], bout[33], bout[34], bout[35], bout[36], bout[37], bout[38], bout[39],
+       bout[40], bout[41], bout[42], bout[43], bout[44], bout[45], bout[46], bout[47],
+       bout[48], bout[49], bout[50], bout[51], bout[52], bout[53], bout[54], bout[55],
+       bout[56], bout[57], bout[58], bout[59], bout[60], bout[61], bout[62], bout[63])
+}
+#endif
+        }  // end for blocks
+
+#if DEBUG_IDCT
+PRINTF("IDCT[%d] completes image %d at cycle %d\n", index , image , giet_proctime() )
+#endif
+
+        image = image + x_size*y_size;
+
+    }  // end while (1) on images
+
+    giet_pthread_exit( "idct completed" );
+
+}  // end idct()
+
Index: /soft/giet_vm/applications/mjpeg/iqzz.c
===================================================================
--- /soft/giet_vm/applications/mjpeg/iqzz.c	(revision 723)
+++ /soft/giet_vm/applications/mjpeg/iqzz.c	(revision 723)
@@ -0,0 +1,117 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// File   : iqzz.c   
+// Date   : octobre 2015
+// author : Alain Greiner
+/////////////////////////////////////////////////////////////////////////////////////////
+// This file define the code of the IQZZ (Invert Quantisation) thread for MJPEG.
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#include <stdio.h>
+#include <mwmr_channel.h>
+#include <stdint.h>
+#include "mjpeg.h"
+
+// macro to use a shared TTY
+#define PRINTF(...)    lock_acquire( &tty_lock ); \
+                       giet_tty_printf(__VA_ARGS__);  \
+                       lock_release( &tty_lock );
+
+//////////////////////////////////////////////////////////////
+__attribute__ ((constructor)) void iqzz( unsigned int index )
+//////////////////////////////////////////////////////////////
+{
+    const uint8_t G_ZZ[64] = 
+    {
+        0 ,  1,  8, 16,  9,  2,  3, 10,
+        17, 24, 32, 25, 18, 11,  4,  5,
+        12, 19, 26, 33, 40, 48, 41, 34,
+        27, 20, 13,  6,  7, 14, 21, 28,
+        35, 42, 49, 56, 57, 50, 43, 36,
+        29, 22, 15, 23, 30, 37, 44, 51,
+        58, 59, 52, 45, 38, 31, 39, 46,
+        53, 60, 61, 54, 47, 55, 62, 63
+    };
+
+    mwmr_channel_t*   mwmr_in_data   = vld_2_iqzz[index];
+    mwmr_channel_t*   mwmr_in_quanti = demux_2_iqzz[index];
+    mwmr_channel_t*   mwmr_out_data  = iqzz_2_idct[index];
+
+    uint32_t    block;
+    uint32_t    i;
+    uint8_t     QTable[64];    // Quantisation Table / 1 byte per pixel
+    int16_t     bufin[64];     // Input data buffer  / 2 bytes per pixel
+    int32_t     bufout[64];    // Output data buffer / 4 bytes per pixel 
+
+    uint32_t    nblocks = nblocks_w * nblocks_h;
+
+    // get platform parameters
+    uint32_t  x_size;
+    uint32_t  y_size;
+    uint32_t  nprocs;
+    giet_procs_number( &x_size , &y_size , &nprocs );
+
+    // get processor coordinates
+    uint32_t    x, y, p;
+    giet_proc_xyp( &x , &y , &p );
+
+    PRINTF("\n[MJPEG] thread IQZZ[%d] starts on P[%d,%d,%d]\n", index, x, y, p )
+
+    uint32_t image = index;
+
+    while ( image < MAX_IMAGES ) // one image per iteration
+    {
+        // read the quantization coefs from mwmr_in_quanti (one byte per coef) 
+        mwmr_read( mwmr_in_quanti , (uint32_t*)QTable , 16 );
+
+#if (DEBUG_IQZZ > 1)
+PRINTF("\nIQZZ[%d] get quantisation coefs for image %d\n", index , image )
+#endif
+
+        for ( block = 0 ; block < nblocks ; ++block ) 
+        {
+            // read one block from mwmr_in_data (2 bytes per pixel)
+            mwmr_read( mwmr_in_data , (uint32_t*)bufin , 32 );
+
+            // unquantify & UnZZ each pixel
+            for ( i = 0 ; i < 64 ; ++i ) 
+            {
+                bufout[G_ZZ[i]] = bufin[i] * QTable[i];
+            }
+    
+            // write one block to IDCT / 4 bytes per pixel
+            mwmr_write( mwmr_out_data , (uint32_t*)bufout , 64 );
+
+#if (DEBUG_IQZZ > 1) 
+PRINTF("\nIQZZ[%d] completes block %d/%d in image %d\n", 
+       index , block , nblocks , image )
+PRINTF("  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n",
+       bufout[0] , bufout[1] , bufout[2] , bufout[3] , bufout[4] , bufout[5] , bufout[6] , bufout[7] ,
+       bufout[8] , bufout[9] , bufout[10], bufout[11], bufout[12], bufout[13], bufout[14], bufout[15],
+       bufout[16], bufout[17], bufout[18], bufout[19], bufout[20], bufout[21], bufout[22], bufout[23],
+       bufout[24], bufout[25], bufout[26], bufout[27], bufout[28], bufout[29], bufout[30], bufout[31],
+       bufout[32], bufout[33], bufout[34], bufout[35], bufout[36], bufout[37], bufout[38], bufout[39],
+       bufout[40], bufout[41], bufout[42], bufout[43], bufout[44], bufout[45], bufout[46], bufout[47],
+       bufout[48], bufout[49], bufout[50], bufout[51], bufout[52], bufout[53], bufout[54], bufout[55],
+       bufout[56], bufout[57], bufout[58], bufout[59], bufout[60], bufout[61], bufout[62], bufout[63])
+#endif
+        }  // end for blocks
+
+#if DEBUG_IQZZ
+PRINTF("\nIQZZ[%d] completes image %d at cycle %d\n", index , image , giet_proctime() )
+#endif
+
+        image = image + x_size* y_size;
+
+    } // end while(1) on images
+
+    giet_pthread_exit( "iqzz completed" );
+
+} // end iqzz()
+
Index: /soft/giet_vm/applications/mjpeg/libu.c
===================================================================
--- /soft/giet_vm/applications/mjpeg/libu.c	(revision 723)
+++ /soft/giet_vm/applications/mjpeg/libu.c	(revision 723)
@@ -0,0 +1,111 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// File   : libu.c   
+// Date   : octobre 2015
+// author : Alain Greiner
+/////////////////////////////////////////////////////////////////////////////////////////
+// This file define the code of the LIBU (line building) thread for MJPEG application.
+// This function execute an infinite loop on a stream of decompressed images.
+// The image size is fbf_width * fbf_height pixels. Each pixel is one byte.
+// The LIBU[index] thread read all blocks of a given image to fill its private
+// bufout[index] buffer, that is part of a FBF_CMA chbuf.
+// For each image:
+// - it checks that buf_out[index] is empty (released by the CMA peripheral).
+// - it move all blocks fom the input MWMR channel to the bufout[index] buffer.
+// - it request the CMA peripheral to display the image stored in bufout[index]
+// - It register the date of display in the date[] array for intrumentation.
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#include <stdio.h>
+#include <mwmr_channel.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "mjpeg.h"
+
+// macro to use a shared TTY
+#define PRINTF(...)    lock_acquire( &tty_lock ); \
+                       giet_tty_printf(__VA_ARGS__);  \
+                       lock_release( &tty_lock );
+
+//////////////////////////////////////////////////////////////
+__attribute__ ((constructor)) void libu( unsigned int index )
+//////////////////////////////////////////////////////////////
+{
+    mwmr_channel_t* input = idct_2_libu[index];
+
+    uint32_t time;
+
+    uint8_t  bufin[64];
+    uint32_t line;
+    uint32_t column;
+
+    // get platform parameters
+    uint32_t  x_size;
+    uint32_t  y_size;
+    uint32_t  nprocs;
+    giet_procs_number( &x_size , &y_size , &nprocs );
+
+    // get processor coordinates
+    unsigned int x , y , p;
+    giet_proc_xyp( &x ,&y , &p );
+
+    // private TTY allocation
+    // giet_tty_alloc( 0 );
+
+    PRINTF("\n[MJPEG] thread LIBU[%d] starts on P[%d,%d,%d]\n", index , x , y , p )
+
+    uint32_t image = index;
+
+    while( image < MAX_IMAGES )  // one image per iteration
+    {
+        // check CMA buffer empty
+        giet_fbf_cma_check( index );
+
+        // two loops on blocks to build image
+        for ( line = 0 ; line < nblocks_h ; ++line ) 
+        {
+            for ( column = 0 ; column < nblocks_w ; ++column )
+            {
+                // move one block from input MWMR channel (one byte per pixel
+                mwmr_read( input , (uint32_t*)bufin , 16 );
+
+                // copy block to cma_buf
+                unsigned int i;
+                unsigned int src;
+                unsigned int dst;
+                for ( i = 0; i < 8 ; ++i ) 
+                {
+                    src = (i * 8);
+                    dst = ((line * 8 + i) * fbf_width) + (column * 8);
+
+                    // copy 8 bytes to cma_buf[index]
+                    memcpy( &cma_buf[index][dst] , &bufin[src] , 8 );
+                }
+
+#if (DEBUG_LIBU > 1)
+PRINTF("\nLIBU[%d] copy block[%d] for image %d\n", 
+index, line * nblocks_w + column , image )
+#endif 
+            }
+
+        } // end loops on blocks
+
+        // request CMA to display image
+        giet_fbf_cma_display( index );
+
+        // get date of display
+        time = giet_proctime();
+
+#if DEBUG_LIBU
+PRINTF("\nLIBU[%d] completes image %d at cycle %d\n", index , image , time )
+#endif
+        // register date of display for instrumentation
+        date[image] = time;
+
+        image = image + x_size*y_size;
+
+    }  // end while on images
+
+    giet_pthread_exit( "libu completed" );
+
+}  // end libu()
+
Index: /soft/giet_vm/applications/mjpeg/mjpeg.c
===================================================================
--- /soft/giet_vm/applications/mjpeg/mjpeg.c	(revision 723)
+++ /soft/giet_vm/applications/mjpeg/mjpeg.c	(revision 723)
@@ -0,0 +1,274 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// File   : mjpeg.c   
+// Date   : octobre 2015
+// author : Alain Greiner
+/////////////////////////////////////////////////////////////////////////////////////////
+// This multi-threaded application illustrates "pipe-line" parallelism, and message
+// passing programming model, on top of the POSIX threads API.
+// It makes the parallel decompression of a MJPEG bitstream contained in a file.
+// The application is described as a TCG (Task and Communication Graph), and all
+// communications between threads uses MWMR channels,.
+// It uses the chained buffer DMA component to display the images on the graphic display.
+// It contains 6 types of threads (plus the "main" thread, that makes initialisation)
+// and 7 types of MWMR communication channels:
+// - the TG thread is only mapped in cluster[0,0], but all other threads
+//   (DEMUX, VLD, IQZZ, IDCT, LIBU) are replicated in all clusters.
+// - all MWMR channels are replicated in all clusters.
+// The number of cluster cannot be larger than 16*16.
+// The number of processors per cluster is not constrained.
+// The frame buffer size must fit the decompressed images size.
+// It uses one TTY terminal shared by all tasks.
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#include <stdio.h>
+#include <mwmr_channel.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include "mjpeg.h"
+
+
+// macro to use a shared TTY
+#define PRINTF(...)    lock_acquire( &tty_lock ); \
+                       giet_tty_printf(__VA_ARGS__);  \
+                       lock_release( &tty_lock );
+
+///////////////////////////////////////////////
+//       Global variables
+///////////////////////////////////////////////
+
+uint32_t         fd;    // file descriptor for the file containing the MJPEG stream
+
+mwmr_channel_t*  tg_2_demux[256];         // one per cluster
+mwmr_channel_t*  demux_2_vld_data[256];   // one per cluster
+mwmr_channel_t*  demux_2_vld_huff[256];   // one per cluster
+mwmr_channel_t*  demux_2_iqzz[256];       // one per cluster
+mwmr_channel_t*  vld_2_iqzz[256];         // one per cluster
+mwmr_channel_t*  iqzz_2_idct[256];        // one per cluster
+mwmr_channel_t*  idct_2_libu[256];        // one per cluster
+
+user_lock_t      tty_lock;                // lock protecting shared TTY
+
+uint8_t*         cma_buf[256];            // CMA buffers (one per cluster)
+void*            cma_sts[256];            // CMA buffers status
+
+uint32_t         fbf_width;               // Frame Buffer width
+uint32_t         fbf_height;              // Frame Buffer height
+
+uint32_t         nblocks_h;               // number of blocks in a column
+uint32_t         nblocks_w;               // number of blocks in a row   
+
+uint32_t         date[MAX_IMAGES];        // date of libu completion
+
+////////////////////////////////////////////////
+// declare thread functions
+////////////////////////////////////////////////
+
+extern void tg( );
+extern void demux( uint32_t index );
+extern void vld( uint32_t index );
+extern void iqzz( uint32_t index );
+extern void idct( uint32_t index );
+extern void libu( uint32_t index );
+
+/////////////////////////////////////////
+__attribute__ ((constructor)) void main()
+/////////////////////////////////////////
+{
+    // get platform parameters
+    uint32_t  x_size;
+    uint32_t  y_size;
+    uint32_t  nprocs;
+    giet_procs_number( &x_size , &y_size , &nprocs );
+
+    // shared TTY allocation
+    giet_tty_alloc( 1 );
+    lock_init( &tty_lock );
+
+    // check platform parameters
+    giet_pthread_assert( (nprocs <= 6),
+                         "[MJPEG ERROR] nprocs cannot be larger than 4");
+
+    giet_pthread_assert( (x_size <= 16),
+                         "[MJPEG ERROR] x_size cannot be larger than 16");
+
+    giet_pthread_assert( (y_size <= 16),
+                         "[MJPEG ERROR] y_size cannot be larger than 16");
+
+    giet_pthread_assert( (MAX_IMAGES >= (x_size*y_size)),
+                         "MJPEG ERROR] number of images smaller than x_size * y_size");
+
+    // check frame buffer size
+    giet_fbf_size( &fbf_width , &fbf_height );
+
+    giet_pthread_assert( ((fbf_width & 0x7) == 0) && ((fbf_height & 0x7) == 0) ,
+                         "[MJPEG ERROR] image width and height must be multiple of 8");
+
+    // request frame buffer and CMA channel allocation 
+    giet_fbf_alloc();
+    giet_fbf_cma_alloc( x_size * y_size );
+
+    // file name and image size acquisition
+    char          file_pathname[256];
+    uint32_t      image_width;
+    uint32_t      image_height;
+
+    PRINTF("\n[MJPEG] enter path for JPEG stream file\n> ");  
+    giet_tty_gets( file_pathname , 256 );
+
+    if ( file_pathname[0] == 0 )
+    {
+        strcpy( file_pathname , "/misc/plan_48.mjpg" );
+        image_width  = 48;
+        image_height = 48;
+        PRINTF("\n\n[MJPEG] use /misc/plan_48.mjpg\n" );
+    }
+    else
+    {
+        PRINTF("\n[MJPEG] enter image width\n> ");  
+        giet_tty_getw( &image_width );
+        PRINTF("\n[MJPEG] enter image height\n> ");  
+        giet_tty_getw( &image_height );
+        PRINTF("\n"); 
+    }
+
+    giet_pthread_assert( (image_width == fbf_width) && (image_height == fbf_height) ,
+                         "[MJPEG ERROR] image size doesn't fit frame buffer size");
+ 
+    // compute nblocks_h & nblocks_w
+    nblocks_w = fbf_width / 8;
+    nblocks_h = fbf_height / 8;
+
+    // open file containing the MJPEG bit stream
+    int fd = giet_fat_open( file_pathname , 0 );
+
+    giet_pthread_assert( (fd >= 0),
+                         "[MJPEG ERROR] cannot open MJPEG stream file");
+
+    // index for loops
+    uint32_t x;
+    uint32_t y;
+    uint32_t n;
+
+    uint32_t*  buffer;  
+
+    // initialise distributed heap, 
+    // allocate MWMR channels
+    // allocate buffers for CMA
+    for ( x = 0 ; x < x_size ; x++ ) 
+    {
+        for ( y = 0 ; y < y_size ; y++ ) 
+        {
+            uint32_t index = x*y_size + y;
+
+            // initialise heap[x][y]
+            heap_init( x , y );
+
+            // allocate MWMR channels in cluster[x][y] 
+            tg_2_demux[index]       = remote_malloc( sizeof( mwmr_channel_t ) , x , y );
+            buffer                  = remote_malloc( 4 * TG_2_DEMUX_DEPTH , x , y );
+            mwmr_init( tg_2_demux[index] , buffer , 1 , TG_2_DEMUX_DEPTH );
+
+            demux_2_vld_data[index] = remote_malloc( sizeof( mwmr_channel_t ) , x , y );
+            buffer                  = remote_malloc( 4 * DEMUX_2_VLD_DATA_DEPTH , x , y );
+            mwmr_init( demux_2_vld_data[index] , buffer , 1 , DEMUX_2_VLD_DATA_DEPTH );
+
+            demux_2_vld_huff[index] = remote_malloc( sizeof( mwmr_channel_t ) , x , y );
+            buffer                  = remote_malloc( 4 * DEMUX_2_VLD_HUFF_DEPTH , x , y );
+            mwmr_init( demux_2_vld_huff[index] , buffer , 1 , DEMUX_2_VLD_HUFF_DEPTH );
+
+            demux_2_iqzz[index]     = remote_malloc( sizeof( mwmr_channel_t ) , x , y );
+            buffer                  = remote_malloc( 4 * DEMUX_2_IQZZ_DEPTH , x , y );
+            mwmr_init( demux_2_iqzz[index] , buffer , 1 , DEMUX_2_IQZZ_DEPTH );
+
+            vld_2_iqzz[index]       = remote_malloc( sizeof( mwmr_channel_t ) , x , y );
+            buffer                  = remote_malloc( 4 * VLD_2_IQZZ_DEPTH , x , y );
+            mwmr_init( vld_2_iqzz[index] , buffer , 1 , VLD_2_IQZZ_DEPTH );
+
+            iqzz_2_idct[index]      = remote_malloc( sizeof( mwmr_channel_t ) , x , y );
+            buffer                  = remote_malloc( 4 * IQZZ_2_IDCT_DEPTH , x , y );
+            mwmr_init( iqzz_2_idct[index] , buffer , 1 , IQZZ_2_IDCT_DEPTH );
+
+            idct_2_libu[index]      = remote_malloc( sizeof( mwmr_channel_t ) , x , y );
+            buffer                  = remote_malloc( 4 * IDCT_2_LIBU_DEPTH , x , y );
+            mwmr_init( idct_2_libu[index] , buffer , 1 , IDCT_2_LIBU_DEPTH );
+
+            // allocate and register CMA buffers in cluster[x][y]
+            cma_buf[index] = remote_malloc( fbf_width * fbf_height , x , y );
+            cma_sts[index] = remote_malloc( 64 , x , y );
+            giet_fbf_cma_init_buf( index , cma_buf[index] , cma_sts[index] );
+        }
+    }
+
+    // start CMA channel
+    giet_fbf_cma_start( );
+
+    PRINTF("\n[MJPEG] main thread completes initialisation for %d cores\n", 
+           x_size * y_size * nprocs )
+
+    // thread trdid for pthread_create() and pthread_join()
+    pthread_t   trdid_tg; 
+    pthread_t   trdid_demux[256]; 
+    pthread_t   trdid_vld[256]; 
+    pthread_t   trdid_iqzz[256]; 
+    pthread_t   trdid_idct[256]; 
+    pthread_t   trdid_libu[256]; 
+
+    uint32_t index;
+
+    // launch all threads : precise mapping is defined in the mjpeg.py file
+
+    if ( giet_pthread_create( &trdid_tg, NULL, &tg , NULL ) )
+    giet_pthread_exit( "error launching thread tg\n");
+
+    for ( index = 0 ; index < (x_size * y_size) ; index++ )
+    {
+        if ( giet_pthread_create( &trdid_demux[index], NULL, &demux , (void*)index ) )
+        giet_pthread_exit( "error launching thread demux\n");
+
+        if ( giet_pthread_create( &trdid_vld[index], NULL, &vld , (void*)index ) )
+        giet_pthread_exit( "error launching thread vld\n");
+
+        if ( giet_pthread_create( &trdid_iqzz[index], NULL, &iqzz , (void*)index ) )
+        giet_pthread_exit( "error launching thread iqzz");
+
+        if ( giet_pthread_create( &trdid_idct[index], NULL, &idct , (void*)index ) )
+        giet_pthread_exit( "error launching thread idct\n");
+
+        if ( giet_pthread_create( &trdid_libu[index], NULL, &libu , (void*)index ) )
+        giet_pthread_exit( "error launching thread libu\n");
+    }
+
+    // wait all threads completion
+
+    if ( giet_pthread_join( trdid_tg , NULL ) )
+    { PRINTF("\n[MJPEG ERROR] calling giet_pthread_join() for tg\n" ) }
+
+    for ( index = 0 ; index < (x_size * y_size) ; index++ )
+    {
+        if ( giet_pthread_join( trdid_demux[index] , NULL ) )
+        { PRINTF("\n[MJPEG ERROR] calling giet_pthread_join() for demux[%d]\n", index ) }
+
+        if ( giet_pthread_join( trdid_vld[index] , NULL ) )
+        { PRINTF("\n[MJPEG ERROR] calling giet_pthread_join() for vld[%d]\n", index ) }
+
+        if ( giet_pthread_join( trdid_iqzz[index] , NULL ) )
+        { PRINTF("\n[MJPEG ERROR] calling giet_pthread_join() for iqzz[%d]\n", index ) }
+
+        if ( giet_pthread_join( trdid_idct[index] , NULL ) )
+        { PRINTF("\n[MJPEG ERROR] calling giet_pthread_join() for idct[%d]\n", index ) }
+
+        if ( giet_pthread_join( trdid_libu[index] , NULL ) )
+        { PRINTF("\n[MJPEG ERROR] calling giet_pthread_join() for libu[%d]\n", index ) }
+    }
+
+    // instrumentation
+
+    uint32_t image;
+    PRINTF("\n[MJPEG] Instumentation Results\n" )
+    for ( image = 0 ; image < MAX_IMAGES ; image++ )
+    { PRINTF(" - Image %d : completed at cycle %d\n", image , date[image]) }
+
+    giet_pthread_exit( "main completed" );
+    
+} // end main()
+
Index: /soft/giet_vm/applications/mjpeg/mjpeg.h
===================================================================
--- /soft/giet_vm/applications/mjpeg/mjpeg.h	(revision 723)
+++ /soft/giet_vm/applications/mjpeg/mjpeg.h	(revision 723)
@@ -0,0 +1,73 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// File   : mjpeg.h   
+// Date   : octobre 2015
+// author : Alain Greiner
+/////////////////////////////////////////////////////////////////////////////////////////
+// This file contains all global variables allocated in the mjpeg.c file,
+// and used by the threads of the MJPEG application.
+// It defines also the debug directives.
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#ifndef MJPEG_GLOBALS_H
+#define MJPEG_GLOBALS_H
+
+#include <stdint.h>
+#include <mwmr_channel.h>
+#include <user_lock.h>
+
+#define   MAX_IMAGES  64
+
+////////////////////////////////////////////////////////////////////////////////////////
+//  MWMMR channels depths (number of 32 bits words)
+////////////////////////////////////////////////////////////////////////////////////////
+
+#define   TG_2_DEMUX_DEPTH          256 
+#define   DEMUX_2_VLD_DATA_DEPTH    256
+#define   DEMUX_2_VLD_HUFF_DEPTH    256
+#define   DEMUX_2_IQZZ_DEPTH        256
+#define   VLD_2_IQZZ_DEPTH          256
+#define   IQZZ_2_IDCT_DEPTH         256
+#define   IDCT_2_LIBU_DEPTH         256 
+
+////////////////////////////////////////////////////////////////////////////////////////
+// debug variables 
+// O : No trace 
+// 1 : simple debug
+// 2 : detailed debug
+////////////////////////////////////////////////////////////////////////////////////////
+
+#define DEBUG_TG      1
+#define DEBUG_DEMUX   0
+#define DEBUG_VLD     0
+#define DEBUG_IQZZ    0
+#define DEBUG_IDCT    0
+#define DEBUG_LIBU    0
+
+////////////////////////////////////////////////////////////////////////////////////////
+//       Global variables
+////////////////////////////////////////////////////////////////////////////////////////
+
+extern uint32_t         fd;    // file descriptor for the file containing MJPEG stream
+
+extern mwmr_channel_t*  tg_2_demux[256];         // one per cluster
+extern mwmr_channel_t*  demux_2_vld_data[256];   // one per cluster
+extern mwmr_channel_t*  demux_2_vld_huff[256];   // one per cluster
+extern mwmr_channel_t*  demux_2_iqzz[256];       // one per cluster
+extern mwmr_channel_t*  vld_2_iqzz[256];         // one per cluster
+extern mwmr_channel_t*  iqzz_2_idct[256];        // one per cluster
+extern mwmr_channel_t*  idct_2_libu[256];        // one per cluster
+
+extern user_lock_t      tty_lock;                // lock protecting shared TTY
+
+extern uint8_t*         cma_buf[256];            // CMA buffers (one per cluster)
+extern void*            cma_sts[256];            // CMA buffers status
+
+extern uint32_t         fbf_width;               // Frame Buffer width
+extern uint32_t         fbf_height;              // Frame Buffer height
+
+extern uint32_t         nblocks_h;               // number of blocks in a column
+extern uint32_t         nblocks_w;               // number of blocks in a row   
+
+extern uint32_t         date[MAX_IMAGES];        // date of completion in libu
+
+#endif
Index: /soft/giet_vm/applications/mjpeg/mjpeg.ld
===================================================================
--- /soft/giet_vm/applications/mjpeg/mjpeg.ld	(revision 723)
+++ /soft/giet_vm/applications/mjpeg/mjpeg.ld	(revision 723)
@@ -0,0 +1,40 @@
+/****************************************************************************
+* Definition of the base address for all virtual segments
+*****************************************************************************/
+
+seg_code_base      = 0x10000000;
+seg_data_base      = 0x20000000;
+
+/***************************************************************************
+* Grouping sections into segments for code and data
+***************************************************************************/
+
+SECTIONS
+{
+    . = seg_code_base;
+    seg_code : 
+    {
+        *(.text)
+        *(.text.*)
+    }
+    . = seg_data_base;
+    seg_data : 
+    {
+        *(.ctors)
+        *(.rodata)
+        /* . = ALIGN(4); */
+        *(.rodata.*)
+        /* . = ALIGN(4); */
+        *(.data)
+        /* . = ALIGN(4); */
+        *(.lit8)
+        *(.lit4)
+        *(.sdata)
+        /* . = ALIGN(4); */
+        *(.bss)
+        *(COMMON)
+        *(.sbss)
+        *(.scommon)
+    }
+}
+
Index: /soft/giet_vm/applications/mjpeg/mjpeg.py
===================================================================
--- /soft/giet_vm/applications/mjpeg/mjpeg.py	(revision 723)
+++ /soft/giet_vm/applications/mjpeg/mjpeg.py	(revision 723)
@@ -0,0 +1,245 @@
+#!/usr/bin/env python
+
+from mapping import *
+
+###################################################################################
+#   file   : mjpeg.py 
+#   date   : november 2015
+#   author : Alain Greiner
+###################################################################################
+#  This file describes the mapping of the multi-threaded "mjpeg" 
+#  application on a multi-clusters, multi-processors architecture.
+#
+#  The mapping of threads on processors is the following:
+#    - the "main" thread, on P[0,0,0] launches all others threads and exit.
+#    - the "tg" thread is only running on P[0,0,0].
+#    - the "demux", "iqzz", "idct", "vld", and "libu" threads, implementing
+#      a block-level pipe-line, are replicated in all clusters.
+#  In each cluster the actual mapping depends on the <nprocs> parameter.
+#
+#  The mapping of virtual segments is the following:
+#    - There is one shared data vseg in cluster[0][0]
+#    - The code vsegs are replicated in all clusters.
+#    - There is one heap vseg per cluster (containing MWMR channels).
+#    - The stacks vsegs are distibuted in all clusters.
+##################################################################################
+
+######################
+def extend( mapping ):
+
+    x_size    = mapping.x_size
+    y_size    = mapping.y_size
+    nprocs    = mapping.nprocs
+
+    assert (nprocs >= 1)
+
+    # define vsegs base & size
+    code_base  = 0x10000000     
+    code_size  = 0x00010000     # 64 Kbytes (per cluster)
+    
+    data_base  = 0x20000000
+    data_size  = 0x00010000     # 64 Kbytes (non replicated) 
+
+    heap_base  = 0x30000000
+    heap_size  = 0x00100000     # 1M bytes (per cluster)      
+
+    stack_base = 0x40000000 
+    stack_size = 0x00020000     # 128 Kbytes (per thread)
+
+    # create vspace
+    vspace = mapping.addVspace( name = 'mjpeg', 
+                                startname = 'data', 
+                                active = True )
+    
+    # data vseg : shared / cluster[0][0]
+    mapping.addVseg( vspace, 'data', data_base , data_size, 
+                     'C_WU', vtype = 'ELF', x = 0, y = 0, pseg = 'RAM', 
+                     binpath = 'bin/mjpeg/appli.elf',
+                     local = False )
+
+    # heap vsegs : shared (one per cluster) 
+    for x in xrange (x_size):
+        for y in xrange (y_size):
+            cluster_id = (x * y_size) + y
+            if ( mapping.clusters[cluster_id].procs ):
+                size  = heap_size
+                base  = heap_base + (cluster_id * size)
+
+                mapping.addVseg( vspace, 'heap_%d_%d' %(x,y), base , size, 
+                                 'C_WU', vtype = 'HEAP', x = x, y = y, pseg = 'RAM', 
+                                 local = False, big = True )
+
+    # code vsegs : local (one copy per cluster)
+    for x in xrange (x_size):
+        for y in xrange (y_size):
+            cluster_id = (x * y_size) + y
+            if ( mapping.clusters[cluster_id].procs ):
+
+                mapping.addVseg( vspace, 'code_%d_%d' %(x,y), 
+                                 code_base , code_size,
+                                 'CXWU', vtype = 'ELF', x = x, y = y, pseg = 'RAM', 
+                                 binpath = 'bin/mjpeg/appli.elf',
+                                 local = True )
+
+    # stacks vsegs: local (one stack per thread => 5 stacks per cluster)
+    # ... plus main_stack and tg_stack in cluster[0][0]
+    base = stack_base
+    mapping.addVseg( vspace, 'main_stack',
+                     base, stack_size, 'C_WU', vtype = 'BUFFER', 
+                     x = 0 , y = 0 , pseg = 'RAM',
+                     local = True )
+
+    base += stack_size
+
+    mapping.addVseg( vspace, 'tg_stack',
+                     base , stack_size, 'C_WU', vtype = 'BUFFER', 
+                     x = 0 , y = 0 , pseg = 'RAM',
+                     local = True )
+
+    base += stack_size
+
+    for x in xrange (x_size):
+        for y in xrange (y_size):
+            if ( mapping.clusters[cluster_id].procs ):
+
+                mapping.addVseg( vspace, 'demux_stack_%d_%d' % (x,y), 
+                                 base, stack_size, 'C_WU', vtype = 'BUFFER', 
+                                 x = x , y = y , pseg = 'RAM',
+                                 local = True )
+
+                base += stack_size
+
+                mapping.addVseg( vspace, 'vld_stack_%d_%d' % (x,y), 
+                                 base, stack_size, 'C_WU', vtype = 'BUFFER', 
+                                 x = x , y = y , pseg = 'RAM',
+                                 local = True )
+
+                base += stack_size
+
+                mapping.addVseg( vspace, 'iqzz_stack_%d_%d' % (x,y), 
+                                 base, stack_size, 'C_WU', vtype = 'BUFFER', 
+                                 x = x , y = y , pseg = 'RAM',
+                                 local = True )
+
+                base += stack_size
+
+                mapping.addVseg( vspace, 'idct_stack_%d_%d' % (x,y), 
+                                 base, stack_size, 'C_WU', vtype = 'BUFFER', 
+                                 x = x , y = y , pseg = 'RAM',
+                                 local = True )
+
+                base += stack_size
+
+                mapping.addVseg( vspace, 'libu_stack_%d_%d' % (x,y), 
+                                 base, stack_size, 'C_WU', vtype = 'BUFFER', 
+                                 x = x , y = y , pseg = 'RAM',
+                                 local = True )
+
+                base += stack_size
+
+    # threads mapping: demux, vld, iqzz, idct, libu replicated in all clusters
+    # ... plus main & tg on P[0,0,0]
+    mapping.addThread( vspace, 'main', True, 0, 0, 0,
+                       'main_stack',
+                       'heap_0_0',
+                       0 )                      # index in start_vector
+
+    if ( nprocs == 1 ):
+        p_tg    = 0
+        p_demux = 0
+        p_vld   = 0
+        p_iqzz  = 0
+        p_idct  = 0
+        p_libu  = 0
+    elif ( nprocs == 2 ):
+        p_tg    = 0
+        p_demux = 1
+        p_vld   = 1
+        p_iqzz  = 1
+        p_idct  = 1
+        p_libu  = 1
+    elif ( nprocs == 3 ):
+        p_tg    = 0
+        p_demux = 1
+        p_vld   = 1
+        p_iqzz  = 1
+        p_idct  = 2
+        p_libu  = 1
+    elif ( nprocs == 4 ):
+        p_tg    = 0
+        p_demux = 1
+        p_vld   = 2
+        p_iqzz  = 2
+        p_idct  = 3
+        p_libu  = 2
+    elif ( nprocs == 5 ):
+        p_tg    = 0
+        p_demux = 1
+        p_vld   = 2
+        p_iqzz  = 3
+        p_idct  = 4
+        p_libu  = 3
+    else:
+        p_tg    = 0
+        p_demux = 1
+        p_vld   = 2
+        p_iqzz  = 3
+        p_idct  = 4
+        p_libu  = 5
+    
+    mapping.addThread( vspace, 'tg', False, 0, 0, p_tg,
+                       'tg_stack',
+                       'heap_0_0',
+                       1 )                      # index in start_vector
+
+    for x in xrange (x_size):
+        for y in xrange (y_size):
+            if ( mapping.clusters[cluster_id].procs ):
+
+                mapping.addThread( vspace, 'demux_%d_%d' % (x,y), False , x, y, p_demux,
+                                   'demux_stack_%d_%d' % (x,y), 
+                                   'heap_%d_%d' % (x,y),
+                                   2 )   # start_index  
+
+                mapping.addThread( vspace, 'vld_%d_%d' % (x,y), False , x, y, p_vld,
+                                   'vld_stack_%d_%d' % (x,y), 
+                                   'heap_%d_%d' % (x,y),
+                                   3 )   # start_index  
+
+                mapping.addThread( vspace, 'iqzz_%d_%d' % (x,y), False , x, y, p_iqzz,
+                                   'iqzz_stack_%d_%d' % (x,y), 
+                                   'heap_%d_%d' % (x,y),
+                                   4 )   # start_index  
+
+                mapping.addThread( vspace, 'idct_%d_%d' % (x,y), False , x, y, p_idct,
+                                   'idct_stack_%d_%d' % (x,y), 
+                                   'heap_%d_%d' % (x,y),
+                                   5 )   # start_index  
+
+                mapping.addThread( vspace, 'libu_%d_%d' % (x,y), False , x, y, p_libu,
+                                   'libu_stack_%d_%d' % (x,y), 
+                                   'heap_%d_%d' % (x,y),
+                                   6 )   # start_index  
+
+    # extend mapping name
+    mapping.name += '_mjpeg'
+
+    return vspace  # useful for test
+            
+################################ test ############################################
+
+if __name__ == '__main__':
+
+    vspace = extend( Mapping( 'test', 2, 2, 4 ) )
+    print vspace.xml()
+
+
+# Local Variables:
+# tab-width: 4;
+# c-basic-offset: 4;
+# c-file-offsets:((innamespace . 0)(inline-open . 0));
+# indent-tabs-mode: nil;
+# End:
+#
+# vim: filetype=python:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
+
Index: /soft/giet_vm/applications/mjpeg/tg.c
===================================================================
--- /soft/giet_vm/applications/mjpeg/tg.c	(revision 723)
+++ /soft/giet_vm/applications/mjpeg/tg.c	(revision 723)
@@ -0,0 +1,138 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// File   : tg.c   
+// Date   : octobre 2015
+// author : Alain Greiner
+/////////////////////////////////////////////////////////////////////////////////////////
+// This file define the code of the TG (trafic generator) thread for MJPEG application.
+// It transfer the byte stream from the file identified by the fd argument to a 1024 
+// bytes local buffer. It analyses the byte stream to detect the End_of_Image markers.
+// All the bytes corresponding to a single image (from the first byte, to the EOI marker
+// included) is written in the TG_2_DEMUX[index] channel of a single cluster, 
+// in increasing order in of the cluster index.
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#include <stdio.h>
+#include <mwmr_channel.h>
+#include <stdint.h>
+#include "mjpeg.h"
+
+// macro to use a shared TTY
+#define PRINTF(...)    lock_acquire( &tty_lock ); \
+                       giet_tty_printf(__VA_ARGS__);  \
+                       lock_release( &tty_lock );
+
+//////////////////////////////////////
+__attribute__ ((constructor)) void tg()
+///////////////////////////////////////
+{ 
+    // get platform parameters
+    uint32_t  x_size;
+    uint32_t  y_size;
+    uint32_t  nprocs;
+    giet_procs_number( &x_size , &y_size , &nprocs );
+
+    // get processor coordinates
+    uint32_t x , y , p;
+    giet_proc_xyp( &x , &y , &p );
+
+    // private TTY allocation
+    //  giet_tty_alloc( 0 );
+
+    PRINTF("\n[MJPEG] thread TG starts on P[%d,%d,%d]\n", x , y , p )
+
+   // allocate input buffer : 1024 bytes
+    uint8_t        bufin[1024];
+
+    // allocate output bufio to access MWMR channel : 64 bytes == 16 words
+    mwmr_bufio_t  bufio;
+    uint8_t       bufout[64]; 
+    mwmr_bufio_init( &bufio , bufout , 64 , 0 , tg_2_demux[0] );
+
+    uint32_t  image;           // image index
+    uint32_t  cluster;         // cluster index / modulo x_size*y_size
+    uint32_t  ptr;             // byte pointer in input buffer
+    uint32_t  eoi_found;       // boolean : End-of-Image found
+    uint32_t  ff_found;        // boolean : 0xFF value found
+    uint32_t  bytes_count;     // mumber of bytes in compressed image
+       
+    // initialise image and cluster index, and bufin pointer
+    image   = 0;
+    cluster = 0;
+    ptr     = 0;
+
+    while( image < MAX_IMAGES )  // one compressed image per iteration
+    {
+        // initialise image specific variables
+        eoi_found   = 0;
+        ff_found    = 0;
+        bytes_count = 0; 
+       
+        // re-initialise the destination buffer for each image
+        bufio.mwmr = tg_2_demux[cluster];
+
+        // scan bit stream until EOI found
+        // transfer one byte per iteration from input buffer to output bufio
+        while ( eoi_found == 0 )
+        {
+            // - tranfer 1024 bytes from file to input buffer when input buffer empty.
+            // - return to first byte in input file when EOF found, 
+            //   to emulate an infinite stream of images.
+            if ( ptr == 0 )
+            {
+                uint32_t r = giet_fat_read( fd , bufin , 1024 );
+                if ( r < 1024 ) 
+                {
+                    giet_fat_lseek( fd , 0 , SEEK_SET );
+                    giet_fat_read( fd , bufin + r , 1024 - r );
+                }
+            }
+
+            // transfer one byte from input buffer to output bufio
+            mwmr_bufio_write_byte( &bufio , bufin[ptr] );
+
+            // analyse this byte to find EOI marker OxFFD8
+            // flush the output buffer when EOI found
+            if ( ff_found )  // possible End of Image
+            { 
+                ff_found = 0;
+                if ( bufin[ptr] == 0xD9 )   // End of Image found
+                {
+                    // exit current image
+                    eoi_found = 1;
+
+                    // flush output bufio
+                    mwmr_bufio_flush( &bufio );
+                }
+            }
+            else           // test if first byte of a marker
+            {
+                if ( bufin[ptr] == 0xFF )  ff_found = 1;
+            }        
+
+            // increment input buffer pointer modulo 1024
+            ptr++;
+            if ( ptr == 1024 ) ptr = 0;
+                
+            // increment bytes_count for current image
+            bytes_count++;
+
+        } // end while (eoi)
+ 
+#if DEBUG_TG
+PRINTF("\nTG send image %d to cluster %d at cycle %d : %d bytes\n",
+       image , cluster , giet_proctime() , bytes_count )
+#endif
+        // increment image index
+        image++;
+
+        // increment cluster index modulo (x_size*y_size)   
+        cluster++; 
+        if (cluster == x_size * y_size) cluster = 0;    
+
+    } // end while on images
+
+    giet_pthread_exit("TG completed");
+
+}  // end tg()
+
+
Index: /soft/giet_vm/applications/mjpeg/vld.c
===================================================================
--- /soft/giet_vm/applications/mjpeg/vld.c	(revision 723)
+++ /soft/giet_vm/applications/mjpeg/vld.c	(revision 723)
@@ -0,0 +1,499 @@
+////////////////////////////////////////////////////////////////////////////////////////
+// File   : vld.c  
+// Date   : octobre 2015
+// author : Alain Greiner
+////////////////////////////////////////////////////////////////////////////////////////
+// This file define the code of the VLD (Variable Length Decoder) thread for the MJPEG
+// application. This function makes the analysis of the variable length bit stream, 
+// resulting from the Huffman entropy coder.
+// For each image:
+// - It read the Huffman Table parameters from the <in_huff> MWMR channel.
+// - It read the bit stream from the <in_data> MWMR channel.
+// - It write output pixels (two bytes per pixel) to the <out> MWMR channel.
+// It uses MWMR_BUFIO buffers for the input channels, but not for the output channel.
+////////////////////////////////////////////////////////////////////////////////////////
+
+#include <stdio.h>
+#include <stdint.h>
+#include <mwmr_channel.h>
+#include "mjpeg.h"
+
+#define HUFF_EOB               0x00
+#define HUFF_ZRL               0xF0
+
+// macro to use a shared TTY
+#define PRINTF(...)    lock_acquire( &tty_lock ); \
+                       giet_tty_printf(__VA_ARGS__);  \
+                       lock_release( &tty_lock );
+
+////////////////////////////////////////////////////////////////////////////////////////
+// bitreader_t : data structure and access functions to analyse a bit-stream.
+////////////////////////////////////////////////////////////////////////////////////////
+
+typedef struct 
+{
+    mwmr_bufio_t*     bufio;         // associated bufio 
+    uint8_t           current;       // temporary buffer of one byte
+    uint8_t           available;     // number of bits to read in current byte
+} bitreader_t;
+
+////////////////////////////////////////////////////
+// returns <number> bits from the associated bufio.
+////////////////////////////////////////////////////
+uint32_t bitreader_get( bitreader_t*  stream,
+                        uint32_t      number )
+{
+    uint32_t  ret = 0;
+
+    giet_pthread_assert( (number <= 16) ,
+    "ERROR in bitreader_get() : illegal number argument");
+
+    if (stream->available) 
+    {
+        stream->current &= (1<<stream->available)-1;
+    }
+
+    while (number) 
+    {
+        if ( stream->available == 0 )  // current buffer empty => refill
+        {
+            stream->current = mwmr_bufio_read_byte( stream->bufio );
+            stream->available = 8;
+        }
+        if ( number == stream->available ) 
+        {
+            stream->available = 0;
+            ret = (ret<<number) | stream->current;
+            break;
+        }
+        if ( number < stream->available ) 
+        {
+            ret = (ret<<number) | (stream->current>>(stream->available-number));
+            stream->available -= number;
+            break;
+        }
+        if ( number > stream->available ) 
+        {
+            ret = (ret<<stream->available) | stream->current;
+            number -= stream->available;
+            stream->available = 0;
+        }
+    }
+
+    return ret;
+}
+
+///////////////////////////////////////////////
+// returns one bit from the associated bufio.
+///////////////////////////////////////////////
+uint8_t bitreader_get_one( bitreader_t*  stream )
+{
+    if ( stream->available == 0 ) // current buffer empty => refill
+    {
+        stream->current = mwmr_bufio_read_byte( stream->bufio );
+        stream->available = 7;
+    }
+    else
+    {
+        --(stream->available);
+    }
+
+    uint32_t tmp = 1<<(stream->available);
+
+    return ( !!(stream->current & tmp) );
+}
+
+/////////////////////////////////////////
+void bitreader_init( bitreader_t*   stream,
+                     mwmr_bufio_t*  bufio )
+{
+    stream->available = 0;
+    stream->current   = 0;
+    stream->bufio     = bufio;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//       data structures and access functions for the Huffman tables
+// - We have two tables (DC and AC), and 16 possible code lengths (from 1 to 16).
+// - DC_Table[12]  : 12 possible symbol values for the DC Table.
+// - AC_Table[162] : 162 possible symbol values for the AC Table.
+// - ValPtr[t][l]  : index in Table <t> for the first code of length <l>
+// - MinCode[t][l] : min value for codes of length <l> 
+// - MaxCode[t][l] : max value for codes of length <l> / (-1) if no code of length <l> 
+//////////////////////////////////////////////////////////////////////////////////////////
+
+typedef struct 
+{
+    uint8_t*  HT[2];                  // HT[0] == DC_Table / HT[1] == AC_Table 
+    int32_t   MinCode[2][16];         // two types of tables / 16 code lengths
+    int32_t   MaxCode[2][16];         // two types of tables / 16 code lengths
+    int32_t   ValPtr[2][16];          // two types of tables / 16 code lengths
+    uint8_t   DC_Table[12];           // at most 12 values
+    uint8_t   AC_Table[162];          // at most 162 values
+} huff_tables_t;
+
+/////////////////////////////////////////////
+void huff_tables_init( huff_tables_t*  huff )
+{
+    int32_t i, j;
+
+    for ( j=0; j<16; ++j ) 
+    {
+        for ( i=0; i<2; ++i ) 
+        {
+            huff->MinCode[i][j] = 0;
+            huff->MaxCode[i][j] = 0;
+            huff->ValPtr[i][j] = 0;
+        }
+    }
+
+    for ( i=0; i<14; ++i )    huff->DC_Table[i] = 0;
+
+    for ( i=0; i<162; ++i )   huff->AC_Table[i] = 0;
+
+    huff->HT[0] = huff->DC_Table;
+    huff->HT[1] = huff->AC_Table;
+}
+
+////////////////////////////////////////////
+void huff_tables_dump( huff_tables_t* huff,
+                       uint32_t       is_ac )
+{
+    uint32_t j;
+    int32_t  code;
+
+    uint32_t max  = ( is_ac ) ? 162 : 12;
+    uint32_t type = ( is_ac ) ? 1 : 0;
+
+    if ( is_ac ) { PRINTF("\n AC Huffman Table\n\n") }
+    else         { PRINTF("\n DC Huffman Table\n\n") }
+
+    for ( j = 0; j < 16; j++ ) // j = code_length - 1
+    {
+        PRINTF(" length = %d / mincode = %x / maxcode = %x / valptr = %d\n",
+        j+1 , huff->MinCode[type][j] , huff->MaxCode[type][j] , huff->ValPtr[type][j] )
+    }
+
+    PRINTF("\n")
+
+    for ( j = 0 ; j < 16 ; j++ )  // j == code_length - 1
+    {
+        for ( code = huff->MinCode[type][j] ; code <= huff->MaxCode[type][j] ; code++ )
+        {
+            uint32_t index = huff->ValPtr[type][j] + code - huff->MinCode[type][j];
+
+            giet_pthread_assert( (index<max) , "ERROR in huff_tables_dump() : overflow");
+
+            PRINTF(" Symbol[%d] = %x / code[%d] = %x\n", 
+            index , huff->HT[type][index] , index , code )
+        }
+    }
+ 
+    PRINTF("\n")
+}
+
+////////////////////////////////////////////
+void huff_tables_load( huff_tables_t*  huff,
+                       mwmr_bufio_t*   bufio )
+{
+    uint8_t    byte;
+    uint32_t   is_ac;          // AC Table if non zero
+
+    uint8_t    LeavesN;        // number of codes of length N (from 1 to 16)
+    uint8_t    LeavesT;        // cumulated total number of codes
+    uint32_t   AuxCode;        // used to compute code values
+
+    // read length (2 bytes) from bufio
+    uint32_t length = (uint32_t)mwmr_bufio_read_byte( bufio );
+    length = (length<<8) | mwmr_bufio_read_byte( bufio );
+
+    // read Tc/Th  (1 byte) from bufio 
+    // Th must be null, Tc must be 0 or 1
+    byte = mwmr_bufio_read_byte( bufio );
+
+    giet_pthread_assert( ((byte & 0xEF) == 0) ,
+    "ERROR in huff_load_table() : non supported HT header");
+
+    if (byte == 0) is_ac = 0;
+    else           is_ac = 1;
+ 
+    uint32_t max_size = ( is_ac ) ? 162 : 12;
+
+    // get the 16 LeavesN values from bufio
+    uint32_t   i;
+    LeavesT = 0;
+    AuxCode = 0;
+    for ( i=0; i<16; i++ ) 
+    {
+        // read one byte from bufio
+        LeavesN = mwmr_bufio_read_byte( bufio );
+
+        huff->ValPtr[is_ac][i] = LeavesT;
+        huff->MinCode[is_ac][i] = AuxCode<<1;
+        AuxCode = huff->MinCode[is_ac][i] + LeavesN;
+        huff->MaxCode[is_ac][i] = (LeavesN) ? (AuxCode - 1) : (-1);
+        LeavesT += LeavesN;
+    }
+
+    giet_pthread_assert( (length ==  19 + LeavesT) ,
+    "ERROR in huff_load_table() : illegal HT length");
+
+    giet_pthread_assert( (LeavesT <= max_size) , 
+    "ERROR in huff_load_table() : too much symbols");
+
+    // get the symbol values from bufio (one byte per symbol)
+    // complete table with zero values if LeavesT < max_size
+    for ( i=0 ; i<max_size ; ++i ) 
+	   {
+        if ( i < LeavesT )  huff->HT[is_ac][i] = mwmr_bufio_read_byte( bufio );
+        else                huff->HT[is_ac][i] = 0; 
+    }
+
+    // align bufio pointer on next item
+    mwmr_bufio_align( bufio );
+    
+#if (DEBUG_VLD > 2) 
+huff_tables_dump( huff , is_ac );
+#endif
+
+}  // end huff_tables_load()
+
+//////////////////////////////////////////////////////////
+// extract a symbol (8 bits) from an Huffman encoded
+// bit-stream, using the specified huffman table 
+/////////////////////////////////////////////////////////
+int8_t huff_get_symbol( bitreader_t*     stream,
+                        huff_tables_t*   huff,
+                        int32_t          select )   // DC if zero / AC if non zero
+{
+    uint32_t length;
+    int32_t  code = 0;
+
+    uint32_t is_ac    = (select) ? 1 : 0;
+    uint32_t max_size = (select) ? 162 : 12;
+    
+    for ( length = 0 ; length < 16 ; ++length ) 
+    {
+        code = (code<<1) | bitreader_get_one( stream );
+        if ( code <= huff->MaxCode[select][length] )   break;
+    }
+
+    uint32_t index = huff->ValPtr[is_ac][length] + code - huff->MinCode[is_ac][length];
+
+    giet_pthread_assert( (index <= max_size) ,
+    "ERROR in huff_get_symbol() : Huffman table overflow");
+
+    return huff->HT[is_ac][index];
+} 
+
+///////////////////////////////////////////////////////////////////////////
+// transform JPEG encoded positive/negative value coded as ( S , nbits )
+// to a standard 16 bits 2's complement number (int16_t).
+// - nbits is the magnitude (number of significant bits in S
+// - most significant bit in S is 0 for positive / 1 for negative
+// - other bits in S define the value in 2**(nbits-1) possible values
+///////////////////////////////////////////////////////////////////////////
+static int16_t reformat( uint32_t S , int32_t nbits )
+{
+    uint32_t  ext;
+    uint32_t  sign;
+    int16_t   value;
+
+    if ( nbits == 0 )  return 0;
+
+    sign = !( (1 << (nbits - 1)) & S );
+    ext = 0 - (sign << nbits);
+    value = (S | ext) + sign;
+
+    return value;
+}
+
+////////////////////////////////////////////////////////
+// unpack a 8*8 pixels block with 2 bytes per pixel
+//////////////////////////////////////////////////////////
+static int16_t vld_unpack_block( bitreader_t*      stream, 
+                                 mwmr_channel_t*   mwmr_out, 
+                                 huff_tables_t*    huff,
+                                 int16_t           prev_dc )
+{
+    uint32_t temp;
+    uint32_t i;
+    uint32_t run;
+    uint32_t cat;
+    int32_t  value;
+    uint8_t  symbol;
+    int16_t  buf[64];       // buffer for the 64 resulting pixels (2 bytes per pixel)
+
+    // set default values
+    for (i = 0; i < 64; i++) buf[i] = 0;
+
+    // compute the DC coefficient
+    symbol   = huff_get_symbol( stream , huff , 0 );   // use DC Huffman Table
+
+    temp     = bitreader_get( stream , symbol );
+    value    = reformat( temp , symbol );
+    buf[0]   = value + prev_dc;
+
+#if (DEBUG_VLD > 1)
+PRINTF("\nDC[0] = %d / reformat( %x , %d ) = %d\n", buf[0], temp , symbol , value )
+#endif
+
+    // compute the 63 AC coefficients
+    for (i = 1; i < 64; i++) 
+    {
+        symbol = huff_get_symbol( stream , huff , 1 );   // use AC Huffman Table
+
+        // in case of HUFF_EOB symbol, all other pixels are zero
+        if ( symbol == HUFF_EOB )
+        {
+
+#if (DEBUG_VLD > 1)
+PRINTF("EOB found at i = %d\n", i );
+#endif 
+            break;
+        }
+ 
+        // in case of HUFF_ZRL symbol (0xF0) , 15 next pixels are zero
+        if (symbol == HUFF_ZRL) 
+        {
+            i += 15;
+            continue;
+        }
+
+        cat = symbol & 0xf;
+        run = symbol >> 4;
+        i += run;
+
+        temp   = bitreader_get (stream , cat );
+        value  = reformat( temp , cat );
+        buf[i] = value;
+
+#if (DEBUG_VLD > 2)
+PRINTF("AC[%d] = %d / reformat( %x , %d ) = %d\n", i , buf[i] , temp , cat , value )
+#endif
+
+    }
+
+    // write one block to mwmr_out channel ( 2 bytes per pixel)
+    mwmr_write( mwmr_out, (uint32_t*)buf , 32 );
+
+#if (DEBUG_VLD > 1 )
+PRINTF("  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n"
+       "  %d  %d  %d  %d  %d  %d  %d  %d\n",
+       buf[0] , buf[1] , buf[2] , buf[3] , buf[4] , buf[5] , buf[6] , buf[7] ,
+       buf[8] , buf[9] , buf[10], buf[11], buf[12], buf[13], buf[14], buf[15],
+       buf[16], buf[17], buf[18], buf[19], buf[20], buf[21], buf[22], buf[23],
+       buf[24], buf[25], buf[26], buf[27], buf[28], buf[29], buf[30], buf[31],
+       buf[32], buf[33], buf[34], buf[35], buf[36], buf[37], buf[38], buf[39],
+       buf[40], buf[41], buf[42], buf[43], buf[44], buf[45], buf[46], buf[47],
+       buf[48], buf[49], buf[50], buf[51], buf[52], buf[53], buf[54], buf[55],
+       buf[56], buf[57], buf[58], buf[59], buf[60], buf[61], buf[62], buf[63])
+#endif
+
+    return buf[0];
+
+} // end vld_unpack_block()
+
+
+//////////////////////////////////////////////////////////////
+__attribute__ ((constructor)) void vld( uint32_t index )
+//////////////////////////////////////////////////////////////
+{
+    mwmr_channel_t*  mwmr_in_data = demux_2_vld_data[index];
+    mwmr_channel_t*  mwmr_in_huff = demux_2_vld_huff[index];
+    mwmr_channel_t*  mwmr_out     = vld_2_iqzz[index];
+
+    huff_tables_t    huff;       // huffman tables
+    bitreader_t      stream;     // bit stream buffer
+    int16_t          prev_dc;    // previous block DC value
+    uint32_t         block;      // block index 
+
+    // get platform parameters
+    uint32_t  x_size;
+    uint32_t  y_size;
+    uint32_t  nprocs;
+    giet_procs_number( &x_size , &y_size , &nprocs );
+
+    // get processor coordinates
+    uint32_t         x, y, p;
+    giet_proc_xyp( &x , &y , &p );
+
+    // private TTY allocation
+    // giet_tty_alloc( 0 );
+
+    PRINTF("\n[MJPEG] thread VLD[%d] starts on P[%d,%d,%d]\n" , index, x, y ,p )
+
+    // initialise BUFIO for MWMR channel <in_data>
+    uint8_t       in_data_buffer[64];
+    mwmr_bufio_t  bufio_in_data;
+    mwmr_bufio_init( &bufio_in_data , in_data_buffer , 64 , 1 , mwmr_in_data );
+
+#if (DEBUG_VLD > 1)
+PRINTF("\nVLD[%d] <in_data> : &mwmr = %x / &bufio = %x\n",
+       index , mwmr_in_data , &bufio_in_data )
+#endif
+
+    // initialise BUFIO for MWMR channel <in_huff>
+    uint8_t       in_huff_buffer[64];
+    mwmr_bufio_t  bufio_in_huff;
+    mwmr_bufio_init( &bufio_in_huff , in_huff_buffer , 64 , 1 , mwmr_in_huff );
+
+#if (DEBUG_VLD > 1)
+PRINTF("\nVLD[%d] <in_huff> : &mwmr = %x / &bufio = %x\n",
+       index , mwmr_in_huff , &bufio_in_huff )
+#endif
+
+    // initialise Huffman Tables
+    huff_tables_init( &huff );
+
+    uint32_t  image = index;
+    uint32_t  nblocks = nblocks_h * nblocks_w;
+
+    while ( image < MAX_IMAGES )  // one image per iteration
+    {
+        // load first Huffman Table from bufio_in_huff
+        huff_tables_load( &huff , &bufio_in_huff );
+
+        // load second Huffman Table from bufio_in_huff
+        huff_tables_load( &huff , &bufio_in_huff );
+
+#if (DEBUG_VLD > 1)
+PRINTF("\nVLD[%d] load Huffman tables for image %d\n", index , image )
+#endif
+
+        // (re)initializes DC value for each image
+        prev_dc = 0;
+
+        // (re)align data bufio for each image
+        mwmr_bufio_align( &bufio_in_data );
+
+        // (re)initializes bit-stream for each image
+        bitreader_init( &stream, &bufio_in_data );
+    
+        // loop on the blocks in current image
+        for ( block = 0 ; block < nblocks ; block++ )
+        {
+
+#if (DEBUG_VLD > 1) 
+PRINTF("\nVLD[%d] uncompress block %d/%d in image %d\n", index, block, nblocks, image )
+#endif
+            prev_dc = vld_unpack_block( &stream , mwmr_out, &huff , prev_dc );
+        }  // end for blocks
+
+#if DEBUG_VLD 
+PRINTF("\nVLD[%d] completes image %d at cycle %d\n", index , image , giet_proctime() )
+#endif
+        image = image + x_size*y_size;
+
+    }  // end while on images
+
+    giet_pthread_exit( "vld completed" );
+
+}  // end vld()
+
