/* * soclib_mty.c - soclib mty driver implementation. * * Author Alain Greiner (2016,2017,2018) * * 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 extern chdev_directory_t chdev_dir; // allocated in the kernel_init.c file. //////////////////////////////////////////////////////////////////////////////////// // These global variables implement the MTY_RX FIFOs (one per channel) //////////////////////////////////////////////////////////////////////////////////// // Implementation note: // We allocate - in each cluster - two arrays of FIFOs containing as many entries // as the total number of TXT channels, but all entries are not used in all // clusters: for a given cluster K, a given entry corresponding to a given channel // and a given direction is only used if the associated chdev is in cluster K. // With this policy, the driver can ignore the actual placement of chdevs. //////////////////////////////////////////////////////////////////////////////////// __attribute__((section(".kdata"))) mty_fifo_t mty_rx_fifo[CONFIG_MAX_TXT_CHANNELS]; __attribute__((section(".kdata"))) mty_fifo_t mty_tx_fifo[CONFIG_MAX_TXT_CHANNELS]; //////////////////////////////////////////////////////////////////////////////////// // These global variables define the physical channel RX and TX state, // as required by the virtual channels handling. //////////////////////////////////////////////////////////////////////////////////// // Implementation note: // These state variables are required, because the ISR is called twice for each // character on the physical channel (for index and value), and does not the same // actions for index and value. They are also used to implement the round-robin // policy between TX_FIFOs. //////////////////////////////////////////////////////////////////////////////////// __attribute__((section(".kdata"))) bool_t mty_rx_not_new; // RX first byte if false __attribute__((section(".kdata"))) uint32_t mty_rx_channel; // RX virtual channel index __attribute__((section(".kdata"))) bool_t mty_tx_not_new; // TX first byte if false __attribute__((section(".kdata"))) uint32_t mty_tx_value; // TX character ascii value __attribute__((section(".kdata"))) uint32_t mty_tx_last; // TX last selected virtual channel /////////////////////////////////////// void soclib_mty_init( chdev_t * chdev ) { xptr_t reg_xp; // initialise function pointers in chdev chdev->cmd = &soclib_mty_cmd; chdev->isr = &soclib_mty_isr; chdev->aux = &soclib_mty_aux; // get MTY channel and extended pointer on MTY peripheral base address xptr_t mty_xp = chdev->base; uint32_t channel = chdev->channel; bool_t is_rx = chdev->is_rx; // get SOCLIB_MTY device cluster and local pointer cxy_t mty_cxy = GET_CXY( mty_xp ); uint32_t * mty_ptr = GET_PTR( mty_xp ); // enable interruptions for RX reg_xp = XPTR( mty_cxy , mty_ptr + MTY_CONFIG ); hal_remote_s32( reg_xp , MTY_CONFIG_RX_ENABLE ); // reset relevant FIFO if( is_rx ) { mty_rx_fifo[channel].sts = 0; mty_rx_fifo[channel].ptr = 0; mty_rx_fifo[channel].ptw = 0; } else { mty_tx_fifo[channel].sts = 0; mty_tx_fifo[channel].ptr = 0; mty_tx_fifo[channel].ptw = 0; } } // end soclib_mty_init() ////////////////////////////////////////////////////////////// void __attribute__ ((noinline)) soclib_mty_cmd( xptr_t th_xp ) { mty_fifo_t * fifo; // MTY_RX or MTY_TX FIFO char byte; // byte value uint32_t done; // number of bytes moved // get client thread cluster and local pointer cxy_t th_cxy = GET_CXY( th_xp ); thread_t * th_ptr = GET_PTR( th_xp ); // get command arguments uint32_t type = hal_remote_l32 ( XPTR( th_cxy , &th_ptr->txt_cmd.type ) ); xptr_t buf_xp = hal_remote_l64( XPTR( th_cxy , &th_ptr->txt_cmd.buf_xp ) ); uint32_t count = hal_remote_l32 ( XPTR( th_cxy , &th_ptr->txt_cmd.count ) ); xptr_t error_xp = XPTR( th_cxy , &th_ptr->txt_cmd.error ); // get TXT device cluster and pointers xptr_t dev_xp = (xptr_t)hal_remote_l64( XPTR( th_cxy , &th_ptr->txt_cmd.dev_xp ) ); cxy_t dev_cxy = GET_CXY( dev_xp ); chdev_t * dev_ptr = GET_PTR( dev_xp ); // get MTY channel index and channel base address uint32_t channel = hal_remote_l32( XPTR( dev_cxy , &dev_ptr->channel ) ); /////////////////////// if( type == TXT_WRITE ) // write bytes to MTY_TX FIFO { fifo = &mty_tx_fifo[channel]; done = 0; while( done < count ) { if( fifo->sts < MTY_FIFO_DEPTH ) // put one byte to FIFO if TX_FIFO not full { // get one byte from command buffer byte = hal_remote_lb( buf_xp + done ); #if DEBUG_HAL_TXT_TX uint32_t tx_cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_TXT_TX < tx_cycle ) printk("\n[DBG] %s : thread %x put character <%c> to TXT%d_TX fifo / cycle %d\n", __FUNCTION__, CURRENT_THREAD, byte, channel, tx_cycle ); #endif // write byte to FIFO fifo->data[fifo->ptw] = byte; // prevent race hal_fence(); // update FIFO state fifo->ptw = (fifo->ptw + 1) % MTY_FIFO_DEPTH; hal_atomic_add( &fifo->sts , 1 ); // udate number of bytes moved done++; } else // block & deschedule if TX_FIFO full { // block on ISR thread_block( XPTR( local_cxy , CURRENT_THREAD ) , THREAD_BLOCKED_ISR ); // deschedule sched_yield( "MTY_TX_FIFO full" ); } } // set error status in command and return hal_remote_s32( error_xp , 0 ); } /////////////////////////// else if( type == TXT_READ ) // read bytes from MTY_RX FIFO { fifo = &mty_rx_fifo[channel]; done = 0; while( done < count ) { if( fifo->sts > 0 ) // get byte from FIFO if not empty { // get one byte from FIFO char byte = fifo->data[fifo->ptr]; #if DEBUG_HAL_TXT_RX uint32_t rx_cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_TXT_RX < rx_cycle ) printk("\n[DBG] %s : thread %x get character <%c> from TXT%d_RX fifo / cycle %d\n", __FUNCTION__, CURRENT_THREAD, byte, channel, rx_cycle ); #endif // update FIFO state fifo->ptr = (fifo->ptr + 1) % MTY_FIFO_DEPTH; hal_atomic_add( &fifo->sts , -1 ); // set byte to command buffer hal_remote_sb( buf_xp + done , byte ); // udate number of bytes done++; } else // deschedule if FIFO empty { // block on ISR thread_block( XPTR( local_cxy , CURRENT_THREAD ) , THREAD_BLOCKED_ISR ); // deschedule sched_yield( "MTY_RX_FIFO empty" ); } } // end while // set error status in command hal_remote_s32( error_xp , 0 ); } else { assert( __FUNCTION__, false , "illegal TXT command\n" ); } } // end soclib_mty_cmd() ///////////////////////////////////////////////////////////////// void __attribute__ ((noinline)) soclib_mty_isr( chdev_t * chdev ) { thread_t * server; // pointer on TXT chdev server thread lid_t server_lid; // local index of core running the server thread uint32_t channel; // TXT chdev channel (virtual channel index) bool_t is_rx; // TXT chdev direction char byte; // byte value xptr_t owner_xp; // extended pointer on TXT owner process cxy_t owner_cxy; // TXT owner process cluster process_t * owner_ptr; // local pointer on TXT owner process pid_t owner_pid; // TXT owner process identifier mty_fifo_t * fifo; // pointer on MTY_TX or MTY_RX FIFO cxy_t mty_cxy; // soclib_mty cluster uint32_t * mty_ptr; // soclib_mty segment base address xptr_t status_xp; // extended pointer on MTY_STATUS register xptr_t write_xp; // extended pointer on MTY_WRITE register xptr_t read_xp; // extended pointer on MTY_READ register xptr_t parent_xp; // extended pointer on parent process cxy_t parent_cxy; // parent process cluster process_t * parent_ptr; // local pointer on parent process thread_t * parent_main_ptr; // extended pointer on parent process main thread xptr_t parent_main_xp; // local pointer on parent process main thread uint32_t n; // index in loop bool_t found; #if DEBUG_HAL_TXT_RX uint32_t rx_cycle = (uint32_t)hal_get_cycles(); #endif #if DEBUG_HAL_TXT_TX uint32_t tx_cycle = (uint32_t)hal_get_cycles(); #endif // get TXT chdev channel, direction and server thread is_rx = chdev->is_rx; server = chdev->server; server_lid = server->core->lid; // get SOCLIB_MTY peripheral cluster and local pointer mty_cxy = GET_CXY( chdev->base ); mty_ptr = GET_PTR( chdev->base ); // get extended pointer on MTY registers status_xp = XPTR( mty_cxy , mty_ptr + MTY_STATUS ); write_xp = XPTR( mty_cxy , mty_ptr + MTY_WRITE ); read_xp = XPTR( mty_cxy , mty_ptr + MTY_READ ); /////////////////////////// handle RX ////////////////////// if( is_rx ) { // check one byte available in MTY_READ register if( hal_remote_l32( status_xp ) & MTY_STATUS_RX_FULL ) { // get one byte from MTY_READ register & acknowledge RX_IRQ byte = (char)hal_remote_lb( read_xp ); // test physical RX channel state if( mty_rx_not_new == false ) // get first byte (virtual channel index) { // register virtual channel index mty_rx_channel = (uint32_t)byte; // update physical RX channel state mty_rx_not_new = true; } else // get second byte (character value) { // get virtual channel index registered channel = mty_rx_channel; // get destination TX_FIFO fifo = &mty_rx_fifo[channel]; // filter special character ^Z => block TXT owner process if( byte == 0x1A ) { #if DEBUG_HAL_TXT_RX if( DEBUG_HAL_TXT_RX < rx_cycle ) printk("\n[DBG] %s : read ^Z character from TXT%d\n", __FUNCTION__, channel ); #endif // get pointers on TXT owner process in owner cluster owner_xp = process_txt_get_owner( channel ); // check process exist assert( __FUNCTION__, (owner_xp != XPTR_NULL) , "TXT owner process not found\n" ); // get relevant infos on TXT owner process owner_cxy = GET_CXY( owner_xp ); owner_ptr = GET_PTR( owner_xp ); owner_pid = hal_remote_l32( XPTR( owner_cxy , &owner_ptr->pid ) ); // block TXT owner process only if it is not the INIT process if( owner_pid != 1 ) { // get parent process descriptor pointers parent_xp = hal_remote_l64( XPTR( owner_cxy , &owner_ptr->parent_xp ) ); parent_cxy = GET_CXY( parent_xp ); parent_ptr = GET_PTR( parent_xp ); // get pointers on the parent process main thread parent_main_ptr = hal_remote_lpt( XPTR(parent_cxy, &parent_ptr->th_tbl[0])); parent_main_xp = XPTR( parent_cxy , parent_main_ptr ); // transfer TXT ownership process_txt_transfer_ownership( owner_xp ); // block all threads in all clusters, but the main thread process_sigaction( owner_pid , BLOCK_ALL_THREADS ); // block the main thread xptr_t main_xp = XPTR( owner_cxy , &owner_ptr->th_tbl[0] ); thread_block( main_xp , THREAD_BLOCKED_GLOBAL ); // atomically update owner process termination state hal_remote_atomic_or( XPTR( owner_cxy , &owner_ptr->term_state ) , PROCESS_TERM_STOP ); // take the children lock and unblock the parent process main thread thread_unblock( parent_main_xp , THREAD_BLOCKED_WAIT ); return; } } // filter special character ^C => kill TXT owner process if( byte == 0x03 ) { #if DEBUG_HAL_TXT_RX if( DEBUG_HAL_TXT_RX < rx_cycle ) printk("\n[DBG] %s : read ^C character from TXT%d\n", __FUNCTION__, channel ); #endif // get pointer on TXT owner process in owner cluster owner_xp = process_txt_get_owner( channel ); // check process exist assert( __FUNCTION__, (owner_xp != XPTR_NULL) , "TXT owner process not found\n" ); // get relevant infos on TXT owner process owner_cxy = GET_CXY( owner_xp ); owner_ptr = GET_PTR( owner_xp ); owner_pid = hal_remote_l32( XPTR( owner_cxy , &owner_ptr->pid ) ); // kill TXT owner process only if it is not the INIT process if( owner_pid != 1 ) { // get parent process descriptor pointers parent_xp = hal_remote_l64( XPTR( owner_cxy, &owner_ptr->parent_xp ) ); parent_cxy = GET_CXY( parent_xp ); parent_ptr = GET_PTR( parent_xp ); // get pointers on the parent process main thread parent_main_ptr = hal_remote_lpt( XPTR( parent_cxy, &parent_ptr->th_tbl[0])); parent_main_xp = XPTR( parent_cxy , parent_main_ptr ); // remove process from TXT list process_txt_detach( owner_xp ); // mark for delete all thread in all clusters, but the main process_sigaction( owner_pid , DELETE_ALL_THREADS ); // block main thread xptr_t main_xp = XPTR( owner_cxy , &owner_ptr->th_tbl[0] ); thread_block( main_xp , THREAD_BLOCKED_GLOBAL ); // atomically update owner process termination state hal_remote_atomic_or( XPTR( owner_cxy , &owner_ptr->term_state ) , PROCESS_TERM_KILL ); // take the children lock and unblock the parent process main thread thread_unblock( parent_main_xp , THREAD_BLOCKED_WAIT ); return; } } // write byte in RX FIFO if not full / discard byte if full if ( fifo->sts < MTY_FIFO_DEPTH ) { #if DEBUG_HAL_TXT_RX if( DEBUG_HAL_TXT_RX < rx_cycle ) printk("\n[DBG] %s : put character <%c> to TXT%d_RX fifo\n", __FUNCTION__, byte, channel ); #endif // store byte into FIFO fifo->data[fifo->ptw] = (char)byte; // avoid race hal_fence(); // update RX_FIFO state fifo->ptw = (fifo->ptw + 1) % MTY_FIFO_DEPTH; hal_atomic_add( &fifo->sts , 1 ); // unblock TXT_RX server thread thread_unblock( XPTR( local_cxy , server ) , THREAD_BLOCKED_ISR ); // send IPI to core running server thread dev_pic_send_ipi( local_cxy , server_lid ); } else { printk("\n[WARNING] %s : MTY_RX_FIFO[%d] full => discard char <%x>\n", __FUNCTION__, channel, (uint32_t)byte ); } // update physical RX channel state mty_rx_not_new = true; } // end get character value } // end if byte available in MTY_READ register } // end RX /////////////////////// handle TX ///////////////////////////// else { // test physical TX channel state if( mty_tx_not_new == false ) // send first byte (virtual channel index) { // scan the set of the TX_FIFO for( n = 0 , found = false ; n < CONFIG_MAX_TXT_CHANNELS ; n++ ) { // implement round-robin policy channel = (n + mty_tx_last + 1) % CONFIG_MAX_TXT_CHANNELS; // get pointer on TX_FIFO[channel] fifo = &mty_tx_fifo[channel]; if( fifo->sts > 0 ) { found = true; break; } } // get one byte from TX_FIFO if found and send channel index if( found ) { // get one byte from selected TX_FIFO byte = fifo->data[fifo->ptr]; #if DEBUG_HAL_TXT_TX if( DEBUG_HAL_TXT_TX < tx_cycle ) printk("\n[DBG] %s : get character <%c> from TXT%d_TX fifo\n", __FUNCTION__, byte, channel ); #endif // update TX_FIFO state fifo->ptr = (fifo->ptr + 1) % MTY_FIFO_DEPTH; hal_atomic_add( &fifo->sts , -1 ); // update TX physical channel state mty_tx_value = (uint32_t)byte; mty_tx_not_new = true; mty_tx_last = channel; // write virtual channel index to TX_WRITE register hal_remote_sb( write_xp , (uint8_t)channel ); // unblock TXT_TX server thread thread_unblock( XPTR( local_cxy , server ) , THREAD_BLOCKED_ISR ); // send IPI to core running server thread dev_pic_send_ipi( local_cxy , server_lid ); } } else // send second byte (character value) { // write registered character value to TX_WRITE register hal_remote_sb( write_xp , (uint8_t)mty_tx_value ); // update TX physical channel state mty_tx_not_new = false; } // end if MTY_WRITE register empty } // end TX hal_fence(); } // end soclib_mty_isr() ///////////////////////////////////////////////////////////// void __attribute__ ((noinline)) soclib_mty_aux( void * args ) { uint32_t i; xptr_t dev_xp = ((txt_sync_args_t *)args)->dev_xp; const char * buffer = ((txt_sync_args_t *)args)->buffer; uint32_t count = ((txt_sync_args_t *)args)->count; uint32_t channel = ((txt_sync_args_t *)args)->channel; // get chdev cluster and local pointer cxy_t dev_cxy = GET_CXY( dev_xp ); chdev_t * dev_ptr = GET_PTR( dev_xp ); // get extended pointer on MTY channel base address xptr_t mty_xp = (xptr_t)hal_remote_l64( XPTR( dev_cxy , &dev_ptr->base ) ); // get MTY channel segment cluster and local pointer cxy_t mty_cxy = GET_CXY( mty_xp ); uint32_t * mty_ptr = GET_PTR( mty_xp ); // get extended pointers on MTY_WRITE & MTY_STATUS registers xptr_t write_xp = XPTR( mty_cxy , mty_ptr + MTY_WRITE ); // loop on characters (two bytes per character) for( i = 0 ; i < count ; i++ ) { // write virtual channel index to MTY_WRITE register hal_remote_sb( write_xp , (uint8_t)channel ); // write character value to MTY_WRITE register hal_remote_sb( write_xp , buffer[i] ); } } // end soclib_mty_aux()