/////////////////////////////////////////////////////////////////////////////////// // 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 /////////////////////////////////////////////////////////////////////////////////// // _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 two 16 bits fields: // - isr_id : defines the type of ISR to be executed. // - 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); } if ( irq_id < 32 ) // do nothing if no interrupt active { unsigned int entry = _get_interrupt_vector_entry(irq_id); unsigned int isr_id = entry & 0x000000FF; unsigned int channel_id = (entry>>16) & 0x0000FFFF; if ( isr_id == ISR_SWITCH ) _isr_switch(); 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(); } } /////////////////////////////////////////////////////////////////////////////////// // _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 global DMA channel index. // channel_id = cluster_id*NB_DMAS_MAX + loc_id // - The ISR saves the transfert status in _dma_status[channel_id]. // - It acknowledges the interrupt to reinitialize the DMA controler. // - it resets the synchronisation variable _dma_busy[channel_id]. /////////////////////////////////////////////////////////////////////////////////// void _isr_dma( unsigned int channel_id ) { // compute cluster_id and loc_id unsigned int cluster_id = channel_id / NB_DMAS_MAX; unsigned int loc_id = channel_id % NB_DMAS_MAX; // compute DMA channel address unsigned int* dma_address = (unsigned int*)&seg_dma_base + (loc_id * DMA_SPAN) + (cluster_id * CLUSTER_SPAN); // save DMA channel status _dma_status[channel_id] = dma_address[DMA_LEN]; // reset DMA channel dma_address[DMA_RESET] = 0; // release DMA channel _dma_done[channel_id] = 1; } /////////////////////////////////////////////////////////////////////////////////// // _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() { unsigned int* ioc_address = (unsigned int*)&seg_ioc_base; _ioc_status = ioc_address[BLOCK_DEVICE_STATUS]; // save status & reset IRQ _ioc_done = 1; // signals completion } /////////////////////////////////////////////////////////////////////////////////// // _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 a global index: // timer_id = cluster_id*(NB_TIMERS_MAX+NB_PROCS_MAX) + local_id // The user timer local index is (loc_id - NB_PROCS_MAX). // // 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) { unsigned int cluster_id = timer_id / (NB_TIMERS_MAX + NB_PROCS_MAX); unsigned int local_id = timer_id % (NB_TIMERS_MAX + NB_PROCS_MAX); // checking timer type if (local_id < NB_PROCS_MAX ) { _get_lock(&_tty_put_lock); _puts("[GIET ERROR] Strange... User timer ISR for a system timer\n"); _release_lock(&_tty_put_lock); } // aknowledge IRQ _timer_reset_irq( cluster_id, local_id ); #if NB_TIMERS_MAX // register the event _timer_event[(cluster_id*NB_TIMERS_MAX) + (loc_id - NB_PROCS_MAX)] = 1; #endif // display a message on TTY 0 _get_lock(&_tty_put_lock); _puts("[GIET] User Timer IRQ at cycle "); _putd( _proctime() ); _puts(" / index = "); _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) { // compute terminal base address unsigned int *tty_address = (unsigned int*)&seg_tty_base + (tty_id * TTY_SPAN); // save character and reset IRQ _tty_get_buf[tty_id] = (unsigned char)tty_address[TTY_READ]; // 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() { // get cluster index and proc local index unsigned int pid = _procid(); unsigned int local_id = pid % NB_PROCS_MAX; unsigned int cluster_id = pid / NB_PROCS_MAX; // acknowledge IRQ _timer_reset_irq( cluster_id, local_id ); // performs the context switch _ctx_switch(); }