/////////////////////////////////////////////////////////////////////////////////// // 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 #if NB_TIM_CHANNELS extern volatile unsigned char _user_timer_event[NB_CLUSTERS * NB_TIM_CHANNELS] ; #endif /////////////////////////////////////////////////////////////////////////////////// // _irq_demux() // 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 three bits fields: // - isr_id : defines the type of ISR to be executed. // - type_id : HWI if zero / PTI if non zero // - channel_id : defines the specific channel for multi-channels peripherals. // // 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 pid = _procid(); unsigned int irq_id; // get the highest priority active IRQ index if (_icu_get_index( pid / NB_PROCS_MAX, pid % NB_PROCS_MAX, &irq_id)) { _get_lock(&_tty_put_lock); _puts("\n[GIET ERROR] Strange... Wrong _icu_read in _irq_demux()\n"); _release_lock(&_tty_put_lock); } // do nothing if no interrupt active if (irq_id < 32) { static_scheduler_t* psched = _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) & 0x0000FFFF; if(type_id == 0) // HARD irq type { if ( isr_id == ISR_SWITCH) _isr_switch(channel_id); else if ( isr_id == ISR_IOC ) _isr_ioc(); else if ( isr_id == ISR_DMA ) _isr_dma(channel_id); else if ( isr_id == ISR_TTY ) _isr_tty(channel_id); else if ( isr_id == ISR_TIMER ) _isr_timer(channel_id); else _isr_default(); } else // PTI irq type { if ( isr_id == ISR_SWITCH) _isr_switch(irq_id); else if ( isr_id == ISR_TIMER ) _isr_timer(irq_id); } } } /////////////////////////////////////////////////////////////////////////////////// // _isr_default() // 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() { _get_lock(&_tty_put_lock); _puts("\n[GIET ERROR] Strange... Default ISR activated for processor "); _putd( _procid() ); _puts("\n"); _release_lock(&_tty_put_lock); } /////////////////////////////////////////////////////////////////////////////////// // _isr_dma() // This ISR handles all IRQs generated by the multi-channels DMA controlers. // The multi_dma components can be distributed in the clusters. // The channel_id argument is the local DMA channel index. // dma_global_id = cluster_id*NB_DMA_CHANNELS + channel_id // - The ISR saves the transfert status in _dma_status[dma_global_id]. // - It acknowledges the interrupt to reinitialize the DMA controler. // - it resets the synchronisation variable _dma_busy[dma_global_id]. /////////////////////////////////////////////////////////////////////////////////// void _isr_dma(unsigned int channel_id) { #if NB_DMA_CHANNELS > 0 // compute cluster_id unsigned int cluster_id = _procid() / NB_PROCS_MAX; // compute dma_global_id unsigned int dma_global_id = cluster_id * NB_DMA_CHANNELS + channel_id; // save DMA channel status if (_dma_get_status(cluster_id, channel_id, (unsigned int *) &_dma_status[dma_global_id])) { _get_lock(&_tty_put_lock); _puts("[GIET ERROR] illegal DMA channel detected by _isr_dma\n"); _release_lock(&_tty_put_lock); return; } // reset DMA channel irq if (_dma_reset_irq(cluster_id, channel_id)) { _get_lock(&_tty_put_lock); _puts("[GIET ERROR] illegal DMA channel detected by _isr_dma\n"); _release_lock(&_tty_put_lock); return; } // release DMA channel _dma_done[dma_global_id] = 1; #else _get_lock(&_tty_put_lock); _puts("[GIET ERROR] NB_DMA_CHANNELS is set to zero\n"); _release_lock(&_tty_put_lock); #endif } /////////////////////////////////////////////////////////////////////////////////// // _isr_ioc() // There is only one IOC controler shared by all tasks. // - The ISR save the status and acknowledge the IRQ. // - It sets the _ioc_done variable to signal completion. /////////////////////////////////////////////////////////////////////////////////// void _isr_ioc() { // save status & reset IRQ if (_ioc_get_status((unsigned int *) &_ioc_status)) { _get_lock(&_tty_put_lock); _puts("[GIET ERROR] bad access to IOC status detected by _isr_ioc\n"); _release_lock(&_tty_put_lock); return; } // signals completion _ioc_done = 1; } /////////////////////////////////////////////////////////////////////////////////// // _isr_timer() // 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 = _procid() / NB_PROCS_MAX; // aknowledge IRQ if (_timer_reset_irq( cluster_id, timer_id)) { _get_lock(&_tty_put_lock); _puts("[GIET ERROR] illegal timer index detected by _isr_timer\n"); _release_lock(&_tty_put_lock); 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 _get_lock(&_tty_put_lock); _puts("\n[GIET] User Timer IRQ at cycle "); _putd(_proctime()); _puts("\n - cluster_id = "); _putd(cluster_id); _puts("\n - timer_id = "); _putd(timer_id); _puts("\n"); _release_lock(&_tty_put_lock); } /////////////////////////////////////////////////////////////////////////////////// // _isr_tty() // 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) { // save character and reset IRQ if (_tty_get_char( tty_id, (unsigned char *) &_tty_get_buf[tty_id])) { _get_lock(&_tty_put_lock); _puts("[GIET ERROR] illegal tty index detected by _isr_tty\n"); _release_lock(&_tty_put_lock); return; } // signals character available _tty_get_full[tty_id] = 1; } ///////////////////////////////////////////////////////////////////////////////////// // _isr_switch // 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 and proc local index unsigned int cluster_id = _procid() / NB_PROCS_MAX; // acknowledge IRQ if (_timer_reset_irq(cluster_id, timer_id)) { _get_lock(&_tty_put_lock); _puts("[GIET ERROR] illegal proc index detected by _isr_switch\n"); _release_lock(&_tty_put_lock); return; } // 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