/* * soclib_nic.c - VCI_MASTER_NIC (Network Interface Controler) driver implementation. * * Author Alain Greiner (2016,2017,2018,2019,2020) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MKH. * * ALMOS-MKH.is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-MKH.is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-MKH.; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include //////////////////////////////////////////////////////////////////////////////////////// // 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 : cxy %x / ptr %x / wid %d / rid %d *****\n", name, local_cxy , chbuf, chbuf->wid, chbuf->rid ); for( i = 0 ; i < CONFIG_SOCK_QUEUES_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 / plen %d\n", i, chbuf->cont_ptr[i], container[510] ); } else { nolock_printk(" - %d : EMPTY / cont_ptr %x\n", i, chbuf->cont_ptr[i] ); } } // release TXT0 lock remote_busylock_release( lock_xp ); } // end soclib_nic_chbuf_display() #endif /////////////////////////////////////// void soclib_nic_init( chdev_t * chdev ) { uint32_t * container; // local pointer on one container uint32_t cont_per_page; // number of containers per page uint32_t cont_gid; // container global index (in chbuf) bool_t cont_error; // not enough memory for chbuf containers ppn_t ppn; // used for both the chbuf descriptor and the containers uint64_t padr; // used for both the chbuf descriptor and the containers assert( __FUNCTION__ , (chdev->func == DEV_FUNC_NIC), "bad func argument" ); assert( __FUNCTION__ , (sizeof(nic_cont_t) == 2048), "container size must be 2048 bytes" ); assert( __FUNCTION__ , (CONFIG_PPM_PAGE_ORDER >= 11 ), "page size cannot be smaller than container size" ); // 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 NIC device cluster and local pointer cxy_t nic_cxy = GET_CXY( chdev->base ); 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 / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, channel, chdev, cycle ); if( is_rx && DEBUG_HAL_NIC_RX < cycle ) printk("\n[%s] thread[%x,%x] enter : NIC_RX channel %d / chdev %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, channel, chdev, cycle ); #endif // get number of channels from NIC hardware register 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[ERROR] in %s illegal channel index\n", __FUNCTION__ ); return; } // allocate memory for chbuf descriptor nic_chbuf_t * chbuf = kmem_alloc( bits_log2( sizeof(nic_chbuf_t) ) , AF_KERNEL ); if( chbuf == NULL ) { printk("\n[ERROR] 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 ); cont_error = false; cont_gid = 0; cont_per_page = 1 << (CONFIG_PPM_PAGE_ORDER - 11); // allocate containers & complete chbuf initialisation // depending on the PPM page size, we pack several // 248 bytes containers in one single page. // lopp on containers while( cont_gid < CONFIG_SOCK_QUEUES_DEPTH ) { if( (cont_gid & (cont_per_page - 1)) == 0 ) // allocate one PPM page { container = kmem_alloc( CONFIG_PPM_PAGE_ORDER , AF_KERNEL ); if( container == NULL ) { cont_error = true; break; } } else // increment container base address { container = container + 512; } // 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_ORDER) | ((intptr_t)container & CONFIG_PPM_PAGE_MASK); // complete chbuf initialisation chbuf->cont_ptr[cont_gid] = container; chbuf->cont_pad[cont_gid] = padr; // increment container index cont_gid++; } // release allocated containers and chbuf if not enough memory if( cont_error ) { // loop on allocated containers while( cont_gid ) { // release container when required if( (cont_gid & (cont_per_page - 1)) == 0 ) kmem_free( chbuf->cont_ptr[cont_gid] , CONFIG_PPM_PAGE_ORDER ); // decrement container index cont_gid--; } // release chbuf descriptor kmem_free( chbuf , bits_log2(sizeof(nic_chbuf_t)) ); return; } // 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 = CONFIG_SOCK_QUEUES_DEPTH; // compute chbuf physical address ppn = ppm_base2ppn( XPTR( local_cxy , chbuf ) ); padr = ((uint64_t)ppn << CONFIG_PPM_PAGE_ORDER) | ((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; #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() ////////////////////////////////////////////////////////////////// void __attribute__ ((noinline)) soclib_nic_cmd( xptr_t thread_xp ) { uint32_t type; // command type uint8_t * buffer; // pointer on command buffer uint32_t length; // Ethernet packet length 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 index; // index of current container in chbuf uint32_t * container; // pointer on container (array of uint32_t) thread_t * this = CURRENT_THREAD; assert( __FUNCTION__, (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_WRITE: // move one packet from command buffer to TX queue { // check chdev is local assert( __FUNCTION__, (dev_cxy == local_cxy), "illegal cluster for a WRITE command"); // get command arguments buffer = this->nic_cmd.buffer; length = this->nic_cmd.length; // check packet length assert( __FUNCTION__, (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 / %s / chbuf (%x,%x) / len %d / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, dev_ptr->name, local_cxy, chbuf, length, cycle ); #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] exit / WRITE failure : NIC_TX[%d] queue full / cycle %d\n", __FUNCTION__, this->process->pid , this->trdid , dev_ptr->channel , cycle ); #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) % CONFIG_SOCK_QUEUES_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] exit / WRITE success on NIC_TX[%d] / len %d / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, dev_ptr->channel , length, cycle ); if((DEBUG_HAL_NIC_TX < cycle) && (DEBUG_HAL_NIC_TX & 1)) putb( "64 first bytes moved to TX queue by NIC driver" , buffer , 64 ); #endif } } break; // end WRITE ///////////////////////////////////////////////////////////////////////// case NIC_CMD_READ: // move one packet from RX queue to kernel buffer { // check chdev is local assert( __FUNCTION__, (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 / %s / chbuf (%x,%x) / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, dev_ptr->name, local_cxy, chbuf, cycle ); #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] exit / READ failure : NIC_RX[%d] queue empty / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, dev_ptr->channel , cycle ); #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) % CONFIG_SOCK_QUEUES_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] exit / READ success on NIC_RX[%d] queue / len %d / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid , dev_ptr->channel , length , cycle ); if((DEBUG_HAL_NIC_RX < cycle) && (DEBUG_HAL_NIC_RX & 1)) putb("64 first bytes moved from RX queue by NIC driver" , buffer , 64 ); #endif } } break; // end READ ///////////////////////////////////////////////////////////////////// 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" arguments 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( __FUNCTION__, false, "Unknown command <%x>\n", type ); } } } // end soclib_nic_cmd() ///////////////////////////////////////////////////////////////// void __attribute__ ((noinline)) soclib_nic_isr( chdev_t * chdev ) { // get base, size, channel, is_rx, name, and server from NIC chdev xptr_t base_xp = chdev->base; uint32_t channel = chdev->channel; bool_t is_rx = chdev->is_rx; thread_t * server = chdev->server; // get NIC peripheral cluster and local pointer 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( nic_cxy , ptr ) ); // check status value if( status == NIC_CHANNEL_STATUS_ERROR ) // error reported { #if (DEBUG_HAL_NIC_RX || DEBUG_HAL_NIC_TX) uint32_t cycle = (uint32_t)hal_get_cycles(); printk("\n[%s] error reported for %s / status %d / cycle %d\n", __FUNCTION__ , chdev->name , status , cycle ); #endif server->nic_cmd.error = 1; } else if( status != NIC_CHANNEL_STATUS_IDLE) // no error but DMA BUSY { #if (DEBUG_HAL_NIC_RX || DEBUG_HAL_NIC_TX) uint32_t cycle = (uint32_t)hal_get_cycles(); printk("\n[%s] warning reported for %s / status %d / cycle %d\n", __FUNCTION__ , chdev->name , status , cycle ); #endif server->nic_cmd.error = 0; } else { #if (DEBUG_HAL_NIC_RX || DEBUG_HAL_NIC_TX) uint32_t cycle = (uint32_t)hal_get_cycles(); printk("\n[%s] irq reported for %s / status %d / cycle %d\n", __FUNCTION__ , chdev->name , status , cycle ); #endif server->nic_cmd.error = 0; } // unblock server thread server->nic_cmd.status = status; thread_unblock( XPTR( local_cxy , server ) , THREAD_BLOCKED_ISR ); } // end soclib_nic_isr()