/////////////////////////////////////////////////////////////////////////////////// // 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 GIET_USE_XICU #else if ( _icu_read( pid / NB_PROCS_MAX, pid % NB_PROCS_MAX, ICU_IT_VECTOR, &irq_id ) ) { _puts("\n[GIET ERROR] wrong _icu_read in _irq_demux() function\n"); _exit(); } #endif 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 a message on kernel TTY[0]. /////////////////////////////////////////////////////////////////////////////////// void _isr_default() { _puts("\n\n!!! Strange... Default ISR activated !!!\n"); } /////////////////////////////////////////////////////////////////////////////////// // _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 channel_id argument is the global channel index: // channel_id = cluster_id*(NB_TIMERS_MAX+NB_PROCS_MAX) + loc_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. // A log message is displayed on the kernel terminal. /////////////////////////////////////////////////////////////////////////////////// void _isr_timer(unsigned int channel_id) { unsigned int cluster_id = channel_id / (NB_TIMERS_MAX + NB_PROCS_MAX); unsigned int loc_id = channel_id % (NB_TIMERS_MAX + NB_PROCS_MAX); if (loc_id < NB_PROCS_MAX ) { _puts("[GIET ERROR] Receiving a user timer IRQ for a system timer\n"); _puts(" cluster = "); _putw(cluster_id); _puts(" / local_id = "); _putw(loc_id); } #if GIET_USE_XICU // TODO #else // compute Timer address unsigned int* timer_address = (unsigned int*)&seg_timer_base + (loc_id * TIMER_SPAN) + (cluster_id * CLUSTER_SPAN); // reset IRQ timer_address[TIMER_RESETIRQ] = 0; #endif #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 _puts("[GIET] User Timer IRQ / cluster = "); _putw(cluster_id); _puts(" / timer = "); _putw(loc_id - NB_PROCS_MAX); _puts("\n"); } /////////////////////////////////////////////////////////////////////////////////// // _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 loc_id = pid % NB_PROCS_MAX; unsigned int cluster_id = pid / NB_PROCS_MAX; #if GIET_USE_XICU unsigned int* timer_address = // TODO #else // compute Timer address unsigned int* timer_address = (unsigned int*)&seg_timer_base + (loc_id * TIMER_SPAN) + (cluster_id * CLUSTER_SPAN); // reset IRQ timer_address[TIMER_RESETIRQ] = 0; #endif // performs the context switch _ctx_switch(); }