/////////////////////////////////////////////////////////////////////////////////// // File : irq_handler.c // Date : 01/04/2012 // Author : alain greiner // Copyright (c) UPMC-LIP6 /////////////////////////////////////////////////////////////////////////////////// // The irq_handler.c and irq_handler.h files are part of the GIET-VM nano-kernel. // They contain the code of the _irq_demux() function that access the XICU or // ICU component (Interupt Controler Unit), and the various ISRs (Interrupt // Service Routine) associated to the peripherals. /////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #if !defined( USE_XICU ) # error: You must define USE_XICU in the hard_config.h file #endif #if NB_TIM_CHANNELS extern volatile unsigned char _user_timer_event[NB_CLUSTERS * NB_TIM_CHANNELS] ; #endif /////////////////////////////////////////////////////////////////////////////////// // This function uses the ICU or XICU component (Interrupt Controler Unit) // to get the interrupt vector entry. There is one ICU or XICU component per // cluster, and this component can support up to NB_PROCS_MAX output IRQs. // It returns the highest priority active interrupt index (smaller // indexes have the highest priority). // Any value larger than 31 means "no active interrupt", and no ISR is executed. // // There is one interrupt vector per processor (stored in the scheduler associated // to the processor. Each interrupt vector entry contains four bits fields: // - isr_id bits[7:0] : defines the type of ISR to be executed. // - type_id bits[15:8] : IRQ_TYPE_HWI / IRQ_TYPE_SWI / IRQ_TYPE_PTI // - channel_id bits[30:16] : defines the channel for multi-channels peripherals. // - valid bit 31 : valid interrupt vector entry // If the peripheral is replicated in clusters (TIMER or DMA), the channel_id is // a global index : channel_id = cluster_id * NB_CHANNELS_MAX + loc_id /////////////////////////////////////////////////////////////////////////////////// void _irq_demux() { unsigned int gpid = _get_procid(); unsigned int cluster_id = gpid / NB_PROCS_MAX; unsigned int local_id = gpid % NB_PROCS_MAX; unsigned int irq_id; unsigned int ko; // get the highest priority active IRQ index #if USE_XICU ko = _xcu_get_index( cluster_id, local_id, &irq_id ); #else ko = _icu_get_index( cluster_id, local_id, &irq_id ); #endif if ( ko ) { _tty_get_lock( 0 ); _puts("\n[GIET ERROR] Wrong _icu_read in _irq_demux()\n"); _tty_release_lock( 0 ); } // do nothing if no interrupt active, or if (irq_id < 32) { static_scheduler_t* psched = (static_scheduler_t*)_get_sched(); unsigned int entry = psched->interrupt_vector[irq_id]; unsigned int isr_id = (entry ) & 0x000000FF; unsigned int type_id = (entry>> 8) & 0x000000FF; unsigned int channel_id = (entry>>16) & 0x00007FFF; if(type_id == IRQ_TYPE_HWI) // HWI { if ( isr_id == ISR_SWITCH) _isr_switch(channel_id); else if ( isr_id == ISR_IOC ) _isr_ioc(); else if ( isr_id == ISR_TTY ) _isr_tty(channel_id); else if ( isr_id == ISR_TIMER ) _isr_timer(channel_id); else if ( isr_id == ISR_WAKUP ) _isr_timer(channel_id); else _isr_default( irq_id, isr_id, type_id ); } else if(type_id == IRQ_TYPE_PTI) // PTI { if ( isr_id == ISR_SWITCH) _isr_switch(irq_id); else if ( isr_id == ISR_TIMER ) _isr_timer(irq_id); else _isr_default( irq_id, isr_id, type_id ); } else if(type_id == IRQ_TYPE_SWI) // SWI { if ( isr_id == ISR_WAKUP ) return; else _isr_default( irq_id, isr_id, type_id ); } } } /////////////////////////////////////////////////////////////////////////////////// // The default ISR is called when no specific ISR has been installed in the // interrupt vector. It simply displays an error message on kernel TTY[0]. /////////////////////////////////////////////////////////////////////////////////// void _isr_default( unsigned int irq_id, unsigned int isr_id, unsigned int type_id ) { _tty_get_lock( 0 ); _puts("\n[GIET ERROR] Undefined ISR index = "); _putd( isr_id ); if(type_id == IRQ_TYPE_HWI) _puts(" / type = HWI "); if(type_id == IRQ_TYPE_SWI) _puts(" / type = SWI "); if(type_id == IRQ_TYPE_PTI) _puts(" / type = PTI "); _puts(" / IRQ index = "); _putd( irq_id ); _puts(" / processor = "); _putd( _get_procid() ); _puts("\n"); _tty_release_lock( 0 ); _exit(); } /////////////////////////////////////////////////////////////////////////////////// // This ISR is executed when a processor wakes up after a SWI interrup. /////////////////////////////////////////////////////////////////////////////////// void _isr_wakup() { _tty_get_lock( 0 ); _puts("\n Processor "); _putd( _get_procid() ); _puts(" wake up\n"); _tty_release_lock( 0 ); } /////////////////////////////////////////////////////////////////////////////////// // There is only one IOC controler shared by all tasks. // This ISR save the status, acknowledge the IRQ. // and activates the task waiting on IO transfer. // // TODO the _set_task_slot access should be replaced by an atomic LL/SC // when the CTX_RUN bool will be replaced by a bit_vector. /////////////////////////////////////////////////////////////////////////////////// void _isr_ioc() { // save status in _ioc_status variable and reset IRQ _ioc_status = _ioc_get_status(); // reactivate task waiting on IOC unsigned int gpid = _ioc_gtid>>16; unsigned int ltid = _ioc_gtid & 0xFFFF; _set_task_slot( gpid, // global processor index ltid, // local task index (on processor) CTX_RUN_ID, // CTX_RUN slot 1 ); // running } /////////////////////////////////////////////////////////////////////////////////// // This ISR handles the IRQs generated by the "user" timers (the IRQs generated // by the "system" timers should be handled by the _isr_switch(). // These timers are distributed in all clusters, and can be implemented // in a vci_multi_timer component, or in a vci_xicu component. // The timer_id argument is the user timer local index. // timer_globa_id = cluster_id*(NB_TIM_CHANNELS) + timer_id // The ISR acknowledges the IRQ and registers the event in the proper entry // of the _timer_event[] array, and a log message is displayed on kernel terminal. /////////////////////////////////////////////////////////////////////////////////// void _isr_timer(unsigned int timer_id) { // compute cluster_id unsigned int cluster_id = _get_procid() / NB_PROCS_MAX; // aknowledge IRQ if (_timer_reset_irq( cluster_id, timer_id)) { _tty_get_lock( 0 ); _puts("[GIET ERROR] illegal timer index detected by _isr_timer\n"); _tty_release_lock( 0 ); return; } #if NB_TIM_CHANNELS // register the event unsigned int timer_global_id = cluster_id * NB_TIM_CHANNELS + timer_id; _user_timer_event[timer_global_id] = 1; #endif // display a message on TTY 0 _tty_get_lock( 0 ); _puts("\n[GIET] User Timer IRQ at cycle "); _putd(_get_proctime()); _puts("\n - cluster_id = "); _putd(cluster_id); _puts("\n - timer_id = "); _putd(timer_id); _puts("\n"); _tty_release_lock( 0 ); } /////////////////////////////////////////////////////////////////////////////////// // This ISR handles the IRQs generated by the multi_tty controler, // signaling that a character is available. // There is one single multi_tty component controling all TTYs, // and the tty_id argument is the global TTY index. // There is one communication buffer _tty_buf[tty_id] per terminal. // The sychronisation variable _tty_full[tty_id], is set by the ISR, // and reset by the OS. // A character is lost if the buffer is full when the ISR is executed. /////////////////////////////////////////////////////////////////////////////////// void _isr_tty(unsigned int tty_id) { // read character and reset IRQ in one read access _tty_get_buf[tty_id] = _tty_read_data( tty_id ); // signals character available _tty_get_full[tty_id] = 1; } ///////////////////////////////////////////////////////////////////////////////////// // This ISR is in charge of context switch, and handle the IRQs generated by // the "system" timers. // The IRQs can be generated by the MULTI_TIMER component or by the XICU component, // that are distributed in all clusters. // The ISR acknowledges the IRQ and calls the _ctx_switch() function. ///////////////////////////////////////////////////////////////////////////////////// void _isr_switch( unsigned int timer_id) { // get cluster index unsigned int cluster_id = _get_procid() / NB_PROCS_MAX; // acknowledge IRQ #if USE_XICU if ( _xcu_timer_reset_irq( cluster_id, timer_id) ) { _tty_get_lock( 0 ); _puts("[GIET ERROR] illegal proc index detected by _isr_switch\n"); _tty_release_lock( 0 ); return; } #else if (_timer_reset_irq(cluster_id, timer_id)) { _tty_get_lock( 0 ); _puts("[GIET ERROR] illegal proc index detected by _isr_switch\n"); _tty_release_lock( 0 ); return; } #endif // performs the context switch _ctx_switch(); } // Local Variables: // tab-width: 4 // c-basic-offset: 4 // c-file-offsets:((innamespace . 0)(inline-open . 0)) // indent-tabs-mode: nil // End: // vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4