Index: trunk/hal/tsar_mips32/drivers/soclib_nic.c
===================================================================
--- trunk/hal/tsar_mips32/drivers/soclib_nic.c	(revision 657)
+++ trunk/hal/tsar_mips32/drivers/soclib_nic.c	(revision 658)
@@ -2,5 +2,5 @@
  * soclib_nic.c - SOCLIB_NIC (Network Interface Controler) driver implementation.
  *
- * Author     Alain Greiner (2016,2017,2018,2019,2020:)
+ * Author     Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -33,4 +33,69 @@
 #include <soclib_nic.h>
 
+
+////////////////////////////////////////////////////////////////////////////////////////
+// Extern global variables
+////////////////////////////////////////////////////////////////////////////////////////
+
+extern chdev_directory_t  chdev_dir;     // allocated in kernel_init.c
+
+#if DEBUG_HAL_NIC_TX || DEBUG_HAL_NIC_RX
+
+////////////////////////////////////////////////////////////////////////////////////////
+//          static function used for SOCLIB_NIC driver debug 
+////////////////////////////////////////////////////////////////////////////////////////
+static void soclib_nic_chbuf_display( nic_chbuf_t * chbuf,
+                                      char        * name )
+{
+    uint32_t i;
+
+    // software L2/L3 cache coherence for chbuf WID & RID read
+    if( chdev_dir.iob )  dev_mmc_inval( XPTR ( local_cxy , chbuf ) , 8 );
+
+    // get pointers on TXT0 chdev
+    xptr_t    txt0_xp  = chdev_dir.txt_tx[0];
+    cxy_t     txt0_cxy = GET_CXY( txt0_xp );
+    chdev_t * txt0_ptr = GET_PTR( txt0_xp );
+
+    // get extended pointer on remote TXT0 chdev lock
+    xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
+
+    // get TXT0 lock 
+    remote_busylock_acquire( lock_xp );
+
+    nolock_printk("\n***** chbuf %s : ptr %x / wid %d / rid %d *****\n",
+    name, chbuf, chbuf->wid, chbuf->rid );
+
+    for( i = 0 ; i < SOCLIB_NIC_CHBUF_DEPTH ; i++ )
+    {
+        uint32_t * container = chbuf->cont_ptr[i];
+
+        // software L2/L3 cache coherence for container STS & PLEN read
+        if( chdev_dir.iob ) dev_mmc_inval( XPTR( local_cxy , container + 510 ), 8 );
+
+        if( container[511] )
+        {
+            nolock_printk(" - %d : FULL  / cont_ptr %x / cont_pad [%x,%x] / plen %d\n",
+            i, chbuf->cont_ptr[i],
+            (uint32_t)(chbuf->cont_pad[i]>>32),
+            (uint32_t)chbuf->cont_pad[i],
+            container[510] );
+        }
+        else
+        {
+            nolock_printk(" - %d : EMPTY / cont_ptr %x / cont_pad [%x,%x]\n",
+            i, chbuf->cont_ptr[i],
+            (uint32_t)(chbuf->cont_pad[i]>>32),
+            (uint32_t)chbuf->cont_pad[i] );
+        }
+    }
+
+    // release TXT0 lock 
+    remote_busylock_release( lock_xp );
+
+}  // end soclib_nic_chbuf_display()
+
+#endif
+
 ///////////////////////////////////////
 void soclib_nic_init( chdev_t * chdev )
@@ -38,55 +103,152 @@
     uint32_t    i;
     kmem_req_t  req;
+    ppn_t       ppn;
+    uint64_t    padr;
 
     // set driver specific fields in chdev descriptor
     chdev->cmd = &soclib_nic_cmd;
     chdev->isr = &soclib_nic_isr;
+
+    // get chdev channel & direction
+    bool_t   is_rx   = chdev->is_rx;
+    uint32_t channel = chdev->channel;
     
-    // get hardware device cluster and local pointer
+    // get NIC device cluster and local pointer
     cxy_t      nic_cxy  = GET_CXY( chdev->base );
-    uint32_t * nic_ptr  = (uint32_t *)GET_PTR( chdev->base );
-
-    // initialize Soclib NIC global registers 
-    hal_remote_s32( XPTR( nic_cxy , nic_ptr + NIC_GLOBAL_SPAN + NIC_G_BC_ENABLE ) , 0 );
-    hal_remote_s32( XPTR( nic_cxy , nic_ptr + NIC_GLOBAL_SPAN + NIC_G_RUN       ) , 0 );
-
-    // allocate memory for chbuf descriptor (one page)
-    assert( (sizeof(nic_chbuf_t) <= CONFIG_PPM_PAGE_SIZE ) ,
-            "chbuf descriptor exceeds one page" );
-
-    req.type   = KMEM_PPM;
-    req.order  = 0;
+    uint32_t * nic_ptr  = GET_PTR( chdev->base );
+
+#if DEBUG_HAL_NIC_TX || DEBUG_HAL_NIC_RX
+thread_t * this  = CURRENT_THREAD;
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( (is_rx == false) && DEBUG_HAL_NIC_RX < cycle )
+printk("\n[%s] thread[%x,%x] enter : NIC_TX channel %d / chdev %x / base %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, channel, chdev, nic_ptr, cycle );
+if( is_rx && DEBUG_HAL_NIC_RX < cycle )
+printk("\n[%s] thread[%x,%x] enter : NIC_RX channel %d / chdev %x / base %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, channel, chdev, nic_ptr, cycle );
+#endif
+
+    // get number of channels from hardware
+    uint32_t channels = hal_remote_l32( XPTR( nic_cxy,
+                        nic_ptr + NIC_GLOBAL_OFFSET + NIC_G_CHANNELS )); 
+
+    // check value registered in cluster descriptor
+    if( LOCAL_CLUSTER->nb_nic_channels != channels )
+    {
+        printk("\n[PANIC] in %s : channels[soft] (%d) != channels[hard] (%d)\n",
+        __FUNCTION__, LOCAL_CLUSTER->nb_nic_channels, channels );
+        return;
+    }
+
+    // check channel index 
+    if( channel >= channels ) 
+    {
+        printk("\n[PANIC] in %s illegal channel index\n", __FUNCTION__ );
+        return;
+    }
+
+    // allocate memory for chbuf descriptor
+    req.type   = KMEM_KCM;
+    req.order  = bits_log2( sizeof(nic_chbuf_t) );
     req.flags  = AF_KERNEL;
-
     nic_chbuf_t * chbuf = kmem_alloc( &req );
 
     if( chbuf == NULL )
     {
-        printk("\n[PANIC] in %s : cannot allocate chbuf descriptor\n",
-        __FUNCTION__ );
-    }
-
-    // initialise chbuf state
-    chbuf->cont_id  = 0;
-    chbuf->pkt_id   = 0;
-    chbuf->word_id  = 34;
+        printk("\n[PANIC] in %s : cannot allocate chbuf descriptor\n", __FUNCTION__ );
+        return;
+    }
+
+    // initialise chbuf indexes
+    chbuf->wid  = 0;
+    chbuf->rid  = 0;
+     
+    // software L2/L3 cache coherence for chbuf WID & RID     
+    if( chdev_dir.iob ) dev_mmc_sync( XPTR( local_cxy , chbuf ) , 8 );
     
-    // allocate containers (one page per container)
-    // and complete chbuf descriptor initialization
-    assert( (CONFIG_PPM_PAGE_SIZE == 4096) ,
-            "chbuf container must be 4 Kbytes" );
-
-    for( i = 0 ; i < CONFIG_NIC_CHBUF_DEPTH ; i++ )
-    {
-        uint32_t * container = kmem_alloc( &req );   
-
-        assert( (container != NULL) ,
-                "cannot allocate container" );
+    // allocate containers and complete chbuf initialisation
+    for( i = 0 ; i < SOCLIB_NIC_CHBUF_DEPTH ; i++ )
+    {
+        // 2048 bytes per container
+        req.type   = KMEM_KCM;
+        req.order  = 11;
+        req.flags  = AF_KERNEL;
+        uint32_t * container  = kmem_alloc( &req );
+
+        if( container == NULL )
+        {
+            printk("\n[PANIC] in %s : cannot allocate container\n", __FUNCTION__ );
+            return;
+        }
+
+        // initialize container as empty
+        container[511] = 0;
+
+        // software L2/L3 cache coherence for container STS       
+        if( chdev_dir.iob ) dev_mmc_sync( XPTR( local_cxy , &container[511] ) , 4 );
+
+        // compute container physical address
+        ppn  = ppm_base2ppn( XPTR( local_cxy , container ) );
+        padr = ((uint64_t)ppn << CONFIG_PPM_PAGE_SHIFT) |
+               ((intptr_t)container & CONFIG_PPM_PAGE_MASK);
+
+        // complete chbuf initialisation        
+        chbuf->cont_ptr[i] = container;
+        chbuf->cont_pad[i] = padr;
+    }
+
+    // software L2/L3 cache coherence for chbuf descriptor
+    if( chdev_dir.iob ) dev_mmc_sync( XPTR( local_cxy , chbuf ),
+                                      sizeof(nic_chbuf_t) );
+
+    // get NIC channel segment base and chbuf depth
+    uint32_t * channel_base = nic_ptr + NIC_CHANNEL_SPAN * channel;
+    uint32_t   nbufs        = SOCLIB_NIC_CHBUF_DEPTH;
+    
+    // compute chbuf physical address 
+    ppn  = ppm_base2ppn( XPTR( local_cxy , chbuf ) );
+    padr = ((uint64_t)ppn  << CONFIG_PPM_PAGE_SHIFT) |
+           ((intptr_t)chbuf & CONFIG_PPM_PAGE_MASK);
+
+    uint32_t low  = (uint32_t)(padr); 
+    uint32_t high = (uint32_t)(padr >> 32); 
+
+    // initialize the NIC channel registers
+    if( is_rx ) 
+    {
+        hal_remote_s32( XPTR( nic_cxy , channel_base + NIC_RX_CHBUF_DESC_LO ) , low );
+        hal_remote_s32( XPTR( nic_cxy , channel_base + NIC_RX_CHBUF_DESC_HI ) , high );
+        hal_remote_s32( XPTR( nic_cxy , channel_base + NIC_RX_CHBUF_NBUFS   ) , nbufs );
+
+        hal_fence();
+
+        hal_remote_s32( XPTR( nic_cxy , channel_base + NIC_RX_CHANNEL_RUN   ) , 1 );
+    }
+    else
+    {
+        hal_remote_s32( XPTR( nic_cxy , channel_base + NIC_TX_CHBUF_DESC_LO ) , low );
+        hal_remote_s32( XPTR( nic_cxy , channel_base + NIC_TX_CHBUF_DESC_HI ) , high );
+        hal_remote_s32( XPTR( nic_cxy , channel_base + NIC_TX_CHBUF_NBUFS   ) , nbufs );
+
+        hal_fence();
+
+        hal_remote_s32( XPTR( nic_cxy , channel_base + NIC_TX_CHANNEL_RUN   ) , 1 );
+    }
+
+    // register chbuf pointer in chdev descriptor extension
+    chdev->ext.nic.queue = chbuf;
         
-        chbuf->cont[i] = container;
-        chbuf->full[i] = (paddr_t)XPTR( local_cxy , container );
-    }
+#if DEBUG_HAL_NIC_TX || DEBUG_HAL_NIC_RX
+cycle = (uint32_t)hal_get_cycles();
+if( (is_rx == false) && DEBUG_HAL_NIC_RX < cycle )
+printk("\n[%s] thread[%x,%x] exit / NIC_TX channel %d / chbuf %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, channel, chbuf, cycle );
+if( is_rx && DEBUG_HAL_NIC_RX < cycle )
+printk("\n[%s] thread[%x,%x] exit / NIC_RX channel %d / chbuf %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, channel, chbuf, cycle );
+soclib_nic_chbuf_display( chbuf , chdev->name );
+#endif
+
 } // end soclib_nic_init()
-
 
 //////////////////////////////////////////////////////////////////
@@ -94,184 +256,352 @@
 {
     uint32_t       type;         // command type    
-    char         * buffer;       // pointer on command buffer   
+    uint8_t      * buffer;       // pointer on command buffer   
     uint32_t       length;       // Ethernet packet length
-    xptr_t         dev_xp;       // extended pointer on NIC device
+    xptr_t         dev_xp;       // extended pointer on NIC chdev
+    chdev_t      * dev_ptr;      // local pointer on NIC chdev
+    cxy_t          dev_cxy;      // NIC chdev cluster identifier
     nic_chbuf_t  * chbuf;        // pointer on chbuf descriptor
-    uint32_t       cont_id;      // index of current container in chbuf
-    uint32_t       pkt_id;       // index of current packet in container
-    uint32_t       word_id;      // index of first word of current packet in container
+    uint32_t       index;        // index of current container in chbuf
     uint32_t     * container;    // pointer on container (array of uint32_t)
-    uint16_t     * header;       // pointer on container header (array of uint16_t)
-    uint32_t       npackets;     // number of packets in current container
-
-    // get local pointer for client thread
-    thread_t * thread_ptr = (thread_t *)GET_PTR( thread_xp );
-
-    // get command arguments
-    type   = thread_ptr->nic_cmd.type;
-    buffer = thread_ptr->nic_cmd.buffer;
-    length = thread_ptr->nic_cmd.length;
-    dev_xp = thread_ptr->nic_cmd.dev_xp;
-
-    // get local pointer for device
-    chdev_t * dev_ptr = (chdev_t *)GET_PTR( dev_xp );
-
-    // get chbuf descriptor pointer
-    chbuf = (nic_chbuf_t *)dev_ptr->ext.nic.queue;
+
+    thread_t * this = CURRENT_THREAD;
+
+// check calling thread == client thread
+assert( (thread_xp == XPTR( local_cxy , this )), "calling thread must be the client thread");
+ 
+    // get command type
+    type    = this->nic_cmd.type;
+
+    // get chdev pointers for device
+    dev_xp  = this->nic_cmd.dev_xp;
+    dev_ptr = GET_PTR( dev_xp );
+    dev_cxy = GET_CXY( dev_xp );
 
     // analyse command type
     switch( type )
     {
-        /////////////////////////////////////////////////////////////////////////////
-        case NIC_CMD_READ:   // transfer one packet from RX queue to command buffer
-        {
-            // get current container index
-            cont_id = chbuf->cont_id;
+        //////////////////////////////////////////////////////////////////////////
+        case NIC_CMD_WRITE:  // move one packet from command buffer to TX queue
+        {
+
+// check chdev is local
+assert( (dev_cxy == local_cxy), "illegal cluster for a WRITE command");
             
-            assert( chbuf->full[cont_id] == 0 , "Read an empty container\n" );
-
-            // get pointer on container and header 
-            container = chbuf->cont[cont_id];
-            header    = (uint16_t *)container;
-
-            // get  expected packet index and first word index in container
-            pkt_id  = chbuf->pkt_id;
-            word_id = chbuf->word_id;
-
-            // get packet length and number of packets from container header
-            length    = header[pkt_id + 2];
-            npackets  = header[0];
-
-            assert( pkt_id >= npackets,
-                "Read a non readable container, packet index too large\n");
-
-            // move the packet from container to buffer
-            memcpy( buffer , container + word_id , length );
-
-            // update current packet index and first word index
-            chbuf->pkt_id  = pkt_id + 1;
-            if( length & 0x3 ) chbuf->word_id = word_id + (length>>2) + 1;
-            else               chbuf->word_id = word_id + (length>>2);
+            // get command arguments
+            buffer = this->nic_cmd.buffer;
+            length = this->nic_cmd.length;
+
+// check packet length
+assert( (length <= 2040), "packet length too large");
+
+            // get chbuf descriptor pointer
+            chbuf = (nic_chbuf_t *)dev_ptr->ext.nic.queue;
+
+            // software L2/L3 cache coherence for chbuf WID read
+            if( chdev_dir.iob )  dev_mmc_inval( XPTR ( local_cxy , chbuf ) , 8 );
+
+            // get container write index
+            index = chbuf->wid;
+
+            // get pointer on container (no L2/L3 cache coherence required)
+            container = chbuf->cont_ptr[index];
+
+            // software L2/L3 cache coherence for container STS read
+            if( chdev_dir.iob )  dev_mmc_inval( XPTR ( local_cxy , &container[511]) , 4 );
+
+#if DEBUG_HAL_NIC_TX
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_HAL_NIC_TX < cycle )
+printk("\n[%s] thread[%x,%x] enter / WRITE / chdev %x / chbuf %x / len %d / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, dev_ptr, chbuf, length, cycle );
+soclib_nic_chbuf_display( chbuf , dev_ptr->name );
+#endif
+            // check container STS
+            if( container[511] != 0 )   // container full
+            {
+                // return failure
+                this->nic_cmd.status = 0;
+                this->nic_cmd.error  = 0;
+
+#if DEBUG_HAL_NIC_TX
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_HAL_NIC_TX < cycle )
+printk("\n[%s] thread[%x,%x] WRITE failure : NIC_TX[%d] queue full / cycle %d\n",
+__FUNCTION__, this->process->pid , this->trdid , dev_ptr->channel , cycle );
+soclib_nic_chbuf_display( chbuf , dev_ptr->name );
+#endif
+            }
+            else                                       // container empty
+            {
+                // move the packet from buffer to container
+                memcpy( container , buffer , length );
+
+                // update packet length in container header
+                container[510] = length;
+
+                hal_fence();
+
+                // update container STS
+                container[511] = 1;
+
+                // update current container WID
+                chbuf->wid = (index + 1) % SOCLIB_NIC_CHBUF_DEPTH;
+
+                // software L2/L3 cache coherence for container DATA write
+                if( chdev_dir.iob ) dev_mmc_sync( XPTR( local_cxy , container ), length );
+
+                // software L2/L3 cache coherence for container LENGTH and STS write
+                if( chdev_dir.iob ) dev_mmc_sync( XPTR( local_cxy , &container[510] ) , 8 );
+                
+                // software L2/L3 cache coherence for chbuf WID write
+                if( chdev_dir.iob ) dev_mmc_sync( XPTR( local_cxy , chbuf ) , 8 );
+
+                // return success
+                this->nic_cmd.status = length;
+                this->nic_cmd.error  = 0;
+
+#if DEBUG_HAL_NIC_TX
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_HAL_NIC_TX < cycle )
+printk("\n[%s] thread[%x,%x] WRITE success on NIC_TX[%d] / len %d / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, dev_ptr->channel , length, cycle );
+soclib_nic_chbuf_display( chbuf , dev_ptr->name );
+#endif
+            }
+        }
+        break;  // end WRITE
+
+        /////////////////////////////////////////////////////////////////////////
+        case NIC_CMD_READ:   // move one packet from RX queue to kernel buffer
+        {
+
+// check chdev is local
+assert( (dev_cxy == local_cxy), "illegal cluster for a READ command");
+            
+            // get target buffer
+            buffer = this->nic_cmd.buffer;
+
+            // get chbuf descriptor pointer
+            chbuf = (nic_chbuf_t *)dev_ptr->ext.nic.queue;
+
+            // software L2/L3 cache coherence for chbuf WID & RID read
+            if( chdev_dir.iob )  dev_mmc_inval( XPTR ( local_cxy , chbuf ) , 8 );
+
+            // get container read index
+            index = chbuf->rid;
+            
+            // get pointer on container (no L2/L3 cache coherence required) 
+            container = chbuf->cont_ptr[index];
+
+            // software L2/L3 cache coherence for container STS & PLEN read
+            if( chdev_dir.iob ) dev_mmc_inval( XPTR( local_cxy , container + 510 ), 8 );
+
+#if DEBUG_HAL_NIC_RX 
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_HAL_NIC_RX < cycle )
+printk("\n[%s] thread[%x,%x] enter / READ / chdev %x / chbuf %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, dev_ptr, chbuf, cycle );
+soclib_nic_chbuf_display( chbuf , dev_ptr->name );
+#endif
+            // check container state
+            if( container[511] == 0 )   // container empty
+            {
+                // return failure
+                this->nic_cmd.status = 0;
+                this->nic_cmd.error  = 0;
+
+#if DEBUG_HAL_NIC_RX
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_HAL_NIC_RX < cycle )
+printk("\n[%s] thread[%x,%x] READ failure : NIC_RX[%d] queue empty / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, dev_ptr->channel , cycle );
+soclib_nic_chbuf_display( chbuf , dev_ptr->name );
+#endif
+            }
+            else                      // container full
+            {
+                // get packet length from container
+                length = container[510];
+
+                // software L2/L3 cache coherence for container DATA
+                if( chdev_dir.iob ) dev_mmc_inval( XPTR( local_cxy , container) , length );
+
+                // move the packet from container to buffer
+                memcpy( buffer , container , length );
+
+                hal_fence();
+
+                // update container STS
+                container[511] = 0;
+
+                // update current container WID 
+                chbuf->rid = (index + 1) % SOCLIB_NIC_CHBUF_DEPTH;
+
+                // software L2/L3 cache coherence for container STS write
+                if( chdev_dir.iob ) dev_mmc_sync( XPTR( local_cxy , &container[511] ), 4 );
+
+                // software L2/L3 cache coherence for chbuf RID write
+                if( chdev_dir.iob )  dev_mmc_sync( XPTR ( local_cxy , chbuf ) , 8 );
+
+                // return success
+                this->nic_cmd.status = length;
+                this->nic_cmd.error  = 0;
+
+#if DEBUG_HAL_NIC_RX 
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_HAL_NIC_RX < cycle )
+printk("\n[%s] thread[%x,%x] READ success on NIC_RX[%d] queue / len %d / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid , dev_ptr->channel , length , cycle );
+soclib_nic_chbuf_display( chbuf , dev_ptr->name );
+#endif
+            }
         }
         break;    // end READ
             
-        //////////////////////////////////////////////////////////////////////////
-        case NIC_CMD_WRITE:  // move one packet from command buffer to TX queue
-        {
-            // get current TX container indexes
-            cont_id = chbuf->cont_id;
-            pkt_id  = chbuf->pkt_id;
-            word_id = chbuf->word_id;
-
-            assert( chbuf->full[cont_id] != 0, "Write to a full container\n" );
-
-            // get pointer on container and header 
-            container = chbuf->cont[cont_id];
-            header    = (uint16_t *)container;
-
-            assert( length > ((1024 - word_id) << 2),
-                "Write to a non writable container, packet length too large\n");
-
-            // update packet length in container header
-            header[pkt_id + 2] = (uint16_t)length;
-
-            // move the packet from buffer to container
-            memcpy( container + word_id , buffer , length );
-
-            // update current packet index and first word index
-            chbuf->pkt_id  = pkt_id + 1;
-            if( length & 0x3 ) chbuf->word_id = word_id + (length>>2) + 1;
-            else               chbuf->word_id = word_id + (length>>2);
-        }
-        break;  // end WRITE
-
-        ////////////////////////////////////////////////////////////////////////////
-        case NIC_CMD_WRITABLE:  // analyse chbuf status / update status if required
-        {
-            // get current container state 
-            cont_id = chbuf->cont_id;
-            word_id = chbuf->word_id;
-
-            // compute current container writable
-            bool_t ok = ( chbuf->full[cont_id] == 0 ) &&
-                        ( length <= ((1024 - word_id)<<2) );
-
-            if( ok )                // current container writable
-            {
-                // return chbuf writable
-                thread_ptr->nic_cmd.status = true;
-            }
-            else                    // current container not writable
-            {
-                // release current container
-                chbuf->full[cont_id] = 1;
-
-                // check next container 
-                cont_id = (cont_id + 1) % CONFIG_NIC_CHBUF_DEPTH;
-
-                if( chbuf->full[cont_id] == 0 ) // next container empty
-                {
-                    // update chbuf status
-                    chbuf->word_id = 34;
-                    chbuf->cont_id = cont_id;
-                    chbuf->pkt_id  = 0;
-                     
-                    // return chbuf writable
-                    thread_ptr->nic_cmd.status = true;
-                }
-                else                            // next container full     
-                {
-                    // return chbuf non writable
-                    thread_ptr->nic_cmd.status = false;
-                }
-            }
-        }
-        break;  // end WRITABLE
-
-        /////////////////////////////////////////////////////////////////////////////
-        case NIC_CMD_READABLE:  // analyse chbuf status / update status if required
-        {
-            // get current container state
-            cont_id  = chbuf->cont_id;
-            pkt_id   = chbuf->pkt_id;
-            npackets = chbuf->cont[cont_id][0] & 0x0000FFFF; 
-            
-            // compute current container readable
-            bool_t ok = ( chbuf->full[cont_id] == 1 ) &&
-                        ( pkt_id < npackets );
-
-            if( ok )                    // current container readable
-            {
-                // return chbuf readable     
-                thread_ptr->nic_cmd.status = true;
-            }
-            else                        // current container non readable
-            {
-                // release current container
-                chbuf->full[cont_id] = 0;
-
-                // check next container
-                cont_id = (cont_id + 1) % CONFIG_NIC_CHBUF_DEPTH;
-
-                if( chbuf->full[cont_id] == 1 ) // next container full 
-                {
-                    // update chbuf status
-                    chbuf->word_id = 34;
-                    chbuf->cont_id = cont_id;
-                    chbuf->pkt_id  = 0;
-                     
-                    // return chbuf readable
-                    thread_ptr->nic_cmd.status = true;
-                }
-                else                            // next container empty    
-                {
-                    // return chbuf non readable
-                    thread_ptr->nic_cmd.status = false;
-                }
-            }
-   
-        }
-        break;  // end READABLE
-        default: {
+        /////////////////////////////////////////////////////////////////////
+        case NIC_CMD_GET_KEY:      // return channel from IP addr & port
+        {
+            // get number of NIC channels
+            uint32_t channels = LOCAL_CLUSTER->nb_nic_channels;
+ 
+            // get IP address and port from command in thread descriptor
+            uint32_t addr = (intptr_t)this->nic_cmd.buffer;
+            uint16_t port = (uint16_t)this->nic_cmd.length;
+ 
+            // compute NIC channel index
+            uint32_t key = ( ((addr     ) & 0xFF) +
+                             ((addr > 8 ) & 0xFF) +
+                             ((addr > 16) & 0xFF) +
+                             ((addr > 24) & 0xFF) +
+                             ((port     ) & 0xFF) +
+                             ((port > 8 ) & 0xFF) ) % channels;
+
+            // return key in "status" and return "error"
+            this->nic_cmd.status = key;
+            this->nic_cmd.error  = 0;
+        }
+        break;  // end GET_KEY
+
+        /////////////////////////////////////////////////////////////////////
+        case NIC_CMD_SET_RUN:       // activate/desactivate one NIC channel
+        {
+            // get pointers on NIC peripheral
+            xptr_t     base_xp  = dev_ptr->base;
+            uint32_t * base_ptr = GET_PTR( base_xp );
+            cxy_t      base_cxy = GET_CXY( base_xp );
+
+            // get channel and run from the "length" and "status" arguments
+            uint32_t channel = this->nic_cmd.length;
+            uint32_t run     = this->nic_cmd.status;
+
+            // build pointers on channel base
+            uint32_t * channel_ptr = base_ptr + NIC_CHANNEL_SPAN * channel;
+
+            // set new value in NIC_RX_CHANNEL_RUN & NIC_TX_CHANNEL_RUN registers
+            hal_remote_s32( XPTR( base_cxy , channel_ptr + NIC_RX_CHANNEL_RUN ) , run );
+            hal_remote_s32( XPTR( base_cxy , channel_ptr + NIC_TX_CHANNEL_RUN ) , run );
+
+            // return "error"
+            this->nic_cmd.error  = 0;
+        }
+        break;  // end SET_RUN
+
+        /////////////////////////////////////////////////////////////////////
+        case NIC_CMD_GET_INSTRU:     // diplay packets counters on TXT0
+        {
+            // get pointers on NIC peripheral
+            xptr_t     base_xp  = dev_ptr->base;
+            uint32_t * base_ptr = GET_PTR( base_xp );
+            cxy_t      base_cxy = GET_CXY( base_xp );
+
+            // build pointer on global register base 
+            uint32_t * global_ptr = base_ptr + NIC_GLOBAL_OFFSET;
+
+            uint32_t rx_g2s_received      = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_RX_G2S_RECEIVED ));
+            uint32_t rx_g2s_discarded     = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_RX_G2S_DISCARDED ));
+            uint32_t rx_des_success       = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_RX_DES_SUCCESS ));
+            uint32_t rx_des_too_small     = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_RX_DES_TOO_SMALL ));
+            uint32_t rx_des_too_big       = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_RX_DES_TOO_BIG ));
+            uint32_t rx_des_mfifo_full    = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_RX_DES_MFIFO_FULL ));
+            uint32_t rx_des_crc_fail      = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_RX_DES_CRC_FAIL ));
+            uint32_t rx_disp_received     = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_RX_DISP_RECEIVED ));
+            uint32_t rx_disp_dst_fail     = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_RX_DISP_DST_FAIL ));
+            uint32_t rx_disp_ch_full      = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_RX_DISP_CH_FULL ));
+
+            uint32_t tx_disp_received     = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_TX_DISP_RECEIVED ));
+            uint32_t tx_disp_too_small    = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_TX_DISP_TOO_SMALL ));
+            uint32_t tx_disp_too_big      = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_TX_DISP_TOO_BIG ));
+            uint32_t tx_disp_transmit     = hal_remote_l32( XPTR( base_cxy , 
+                                            global_ptr + NIC_G_NPKT_TX_DISP_TRANSMIT ));
+
+            printk("\n*** NIC device Instrumentation ***\n\n"
+                   " - rx_g2s_received   = %d\n"
+                   " - rx_g2s_discarded  = %d\n"
+                   " - rx_des_success    = %d\n"
+                   " - rx_des_too_small  = %d\n"
+                   " - rx_des_too_big    = %d\n"
+                   " - rx_des_mfifo_full = %d\n"
+                   " - rx_des_crc_fail   = %d\n"
+                   " - rx_disp_received  = %d\n"
+                   " - rx_disp_dsp_fail  = %d\n"
+                   " - rx_disp_ch_full   = %d\n\n"
+                   " - tx_disp_received  = %d\n"
+                   " - tx_disp_too_small = %d\n"
+                   " - tx_disp_too_big   = %d\n"
+                   " - tx_disp_transmit  = %d\n",
+                   rx_g2s_received,
+                   rx_g2s_discarded,
+                   rx_des_success,
+                   rx_des_too_small,
+                   rx_des_too_big,
+                   rx_des_mfifo_full,
+                   rx_des_crc_fail,
+                   rx_disp_received,
+                   rx_disp_dst_fail, 
+                   rx_disp_ch_full,
+                   tx_disp_received,
+                   tx_disp_too_small,
+                   tx_disp_too_big, 
+                   tx_disp_transmit );
+
+            // return "error"
+            this->nic_cmd.error  = 0;
+        }
+        break;  // end CLEAR_INSTRU
+
+        /////////////////////////////////////////////////////////////////////
+        case NIC_CMD_CLEAR_INSTRU:  // reset instrumentation registers 
+        {
+            // get pointers on NIC peripheral
+            xptr_t     base_xp  = dev_ptr->base;
+            uint32_t * base_ptr = GET_PTR( base_xp );
+            cxy_t      base_cxy = GET_CXY( base_xp );
+
+            // build pointer on relevant NIC register 
+            uint32_t * reset_ptr = base_ptr + NIC_GLOBAL_OFFSET + NIC_G_NPKT_RESET;
+
+            // reset all NIC instrumentation registers
+            hal_remote_s32( XPTR( base_cxy , reset_ptr ) , 0 );
+
+            // return "error"
+            this->nic_cmd.error  = 0;
+        }
+        break;  // end GET_INSTRU
+
+        default:
+        {
             assert( false, "Unknown command <%x>\n", type );
         }
@@ -284,28 +614,40 @@
 {
     // get base, size, channel, is_rx from NIC channel device NIC 
-    xptr_t     base    = chdev->base;
+    xptr_t     base_xp = chdev->base;
     uint32_t   channel = chdev->channel;
     bool_t     is_rx   = chdev->is_rx;
 
     // get NIC peripheral cluster and local pointer
-    cxy_t      cxy_nic = GET_CXY( base );
-    uint32_t * ptr_nic = (uint32_t *)GET_PTR( base );
-
-    // compute local pointer on status register 
-    uint32_t * offset;
-    if( is_rx ) offset = ptr_nic + (NIC_CHANNEL_SPAN * (channel + 1)) + NIC_RX_STATUS;
-    else        offset = ptr_nic + (NIC_CHANNEL_SPAN * (channel + 1)) + NIC_TX_STATUS;
+    cxy_t      nic_cxy = GET_CXY( base_xp );
+    uint32_t * nic_ptr = GET_PTR( base_xp );
+
+    // compute local pointer on state register 
+    uint32_t  * ptr;
+    if( is_rx ) ptr = nic_ptr + (NIC_CHANNEL_SPAN * channel) + NIC_RX_CHANNEL_STATE;
+    else        ptr = nic_ptr + (NIC_CHANNEL_SPAN * channel) + NIC_TX_CHANNEL_STATE;
 
     // read NIC channel status and acknowledge IRQ
-    uint32_t status = hal_remote_l32( XPTR( cxy_nic , offset ) );
-
-    assert( status != 0, "Illegal address: \n" );
+    uint32_t status = hal_remote_l32( XPTR( nic_cxy , ptr ) );
+
+// check status value
+if( is_rx &&  (status != NIC_CHANNEL_STATUS_IDLE) )
+printk("\n[PANIC] in %s : error reported by NIC_RX[%d]\n", __FUNCTION__, channel );
+if( (is_rx == false) &&  (status != NIC_CHANNEL_STATUS_IDLE) )
+printk("\n[PANIC] in %s : error reported by NIC_TX[%d]\n", __FUNCTION__, channel );
 
     // unblock server thread 
     thread_t * server = chdev->server;
-    thread_unblock( XPTR( local_cxy , server ) , THREAD_BLOCKED_IO );
+    thread_unblock( XPTR( local_cxy , server ) , THREAD_BLOCKED_ISR );
+
+#if (DEBUG_HAL_NIC_RX || DEBUG_HAL_NIC_TX) 
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( is_rx && DEBUG_HAL_NIC_RX < cycle )
+printk("\n[%s] ISR unblocks NIC_RX[%d] server thread / cycle %d\n",
+__FUNCTION__, channel, cycle );
+if( (is_rx == false) && DEBUG_HAL_NIC_TX < cycle )
+printk("\n[%s] ISR unblocks NIC_TX[%d] server thread / cycle %d\n",
+__FUNCTION__, channel, cycle );
+#endif
 
 } // end soclib_nic_isr()
 
-
-
