////////////////////////////////////////////////////////////////////////////////////// // File : timer_driver.c // Date : 23/05/2013 // Author : alain greiner // Copyright (c) UPMC-LIP6 ////////////////////////////////////////////////////////////////////////////////////// // The timer_driver.c and timer_driver.h files are part ot the GIET-VM nano-kernel. // This driver supports the SoCLib vci_multi_timer component. // // It can exist several multi_timers in the architecture (at most one per cluster), // and each one can contain several timers (called channels). // // There is two types of timers: // - "system" timers : one per processor, used for context switch. // local_id in [0, NB_PROCS_MAX-1], // - "user" timers : requested by the task in the mapping_info data structure. // For each user timer, the timer_id is stored in the context of the task. // The global index is cluster_xy * (NB_PROCS_MAX + NB_TIM_CHANNELS) + local_id // // The NB_PROCS_MAX and NB_TIM_CHANNELS values must be defined in hard_config.h file. ///////////////////////////////////////////////////////////////////////////////////// // The virtual base address of the segment associated to a channel is: // // seg_tim_base + cluster_xy * vseg_cluster_increment + TIMER_SPAN * timer_id // // The seg_tim_base and vseg_cluster_increment values must be defined // in the giet_vsegs.ld file. ///////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #if !defined(X_SIZE) # error: You must define X_SIZE in the hard_config.h file #endif #if !defined(Y_SIZE) # error: You must define X_SIZE in the hard_config.h file #endif #if !defined(X_WIDTH) # error: You must define X_WIDTH in the hard_config.h file #endif #if !defined(Y_WIDTH) # error: You must define X_WIDTH in the hard_config.h file #endif #if !defined(NB_PROCS_MAX) # error: You must define NB_PROCS_MAX in the hard_config.h file #endif #if !defined(NB_TIM_CHANNELS) #define NB_TIM_CHANNELS 0 #endif #if ( (NB_TIM_CHANNELS + NB_PROC_MAX) > 32 ) # error: NB_TIM_CHANNELS + NB_PROCS_MAX cannot be larger than 32 #endif /////////////////// Timer global variables //////////////////////////////////////// #define in_unckdata __attribute__((section (".unckdata"))) #if (NB_TIM_CHANNELS > 0) in_unckdata volatile unsigned char _user_timer_event[(1<> Y_WIDTH; unsigned int y = cluster_xy & ((1<= X_SIZE) || (y >= Y_SIZE) || (local_id >= NB_TIM_CHANNELS) ) { _printf("[GIET ERROR] in _timer_start()\n"); _exit(); } unsigned int* timer_address = (unsigned int *) ((unsigned int)&seg_tim_base + (cluster_xy * (unsigned int)&vseg_cluster_increment)); timer_address[local_id * TIMER_SPAN + TIMER_PERIOD] = period; timer_address[local_id * TIMER_SPAN + TIMER_MODE] = 0x3; #else _printf("[GIET ERROR] _timer_start() should not be called when NB_TIM_CHANNELS is 0\n"); _exit(); #endif } ////////////////////////////////////////////////////////////////////////////// // This function desactivates a timer in the vci_timer component // by writing in the proper register. // Returns 0 if success, > 0 if error. ////////////////////////////////////////////////////////////////////////////// void _timer_stop( unsigned int cluster_xy, unsigned int local_id) { #if NB_TIM_CHANNELS // parameters checking unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<= X_SIZE) || (y >= Y_SIZE) || (local_id >= NB_TIM_CHANNELS) ) { _printf("[GIET ERROR] in _timer_stop()\n"); _exit(); } unsigned int* timer_address = (unsigned int *) ((unsigned int)&seg_tim_base + (cluster_xy * (unsigned int)&vseg_cluster_increment)); timer_address[local_id * TIMER_SPAN + TIMER_MODE] = 0; #else _printf("[GIET ERROR] _timer_stop() should not be called when NB_TIM_CHANNELS is 0\n"); _exit(); #endif } ////////////////////////////////////////////////////////////////////////////// // This function acknowlegge a timer interrupt in the vci_timer // component by writing in the proper register. // It can be used by both the isr_switch() for a "system" timer, // or by the _isr_timer() for an "user" timer. // Returns 0 if success, > 0 if error. ////////////////////////////////////////////////////////////////////////////// void _timer_reset_irq( unsigned int cluster_xy, unsigned int local_id ) { #if NB_TIM_CHANNELS // parameters checking unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<= X_SIZE) || (y >= Y_SIZE) || (local_id >= NB_TIM_CHANNELS) ) { _printf("[GIET ERROR] in _timer_reset_irq()\n"); _exit(); } unsigned int * timer_address = (unsigned int *) ((unsigned int)&seg_tim_base + (cluster_xy * (unsigned int)&vseg_cluster_increment)); timer_address[local_id * TIMER_SPAN + TIMER_RESETIRQ] = 0; #else _printf("[GIET ERROR] _timer_reset_irq() should not be called when NB_TIM_CHANNELS is 0\n"); _exit(); #endif } ///////////////////////////////////////////////////////////////////////////// // This function resets the timer counter. To do so, we re-write the period // in the proper register, what causes the count to restart. // The period value is read from the same (TIMER_PERIOD) register, // this is why in appearance we do nothing useful (read a value // from a register and write this value in the same register) // This function is called during a context switch (user or preemptive) //////////////////////////////////////////////////////////////////////i////// void _timer_reset_cpt( unsigned int cluster_xy, unsigned int local_id) { #if NB_TIM_CHANNELS // parameters checking unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<= X_SIZE) || (y >= Y_SIZE) || (local_id >= NB_TIM_CHANNELS) ) { _printf("[GIET ERROR in _timer_reset_cpt()\n"); _exit(); } // We suppose that the TIMER_MODE register value is 0x3 unsigned int * timer_address = (unsigned int *) ((unsigned int)&seg_tim_base + (cluster_xy * (unsigned int)&vseg_cluster_increment)); unsigned int period = timer_address[local_id * TIMER_SPAN + TIMER_PERIOD]; timer_address[local_id * TIMER_SPAN + TIMER_PERIOD] = period; #else _printf("[GIET ERROR] _timer_reset_cpt should not be called when NB_TIM_CHANNELS is 0\n"); _exit(); #endif } /////////////////////////////////////////////////////////////////////////////////// // This ISR handles the IRQs generated by the "user" timers that are // replicated in all clusters. // The IRQs generated by the "system" timers should be handled by _isr_switch(). // It can be a HWI or a PTI. // The channel argument is the user timer local index. // timer_global_id = cluster_id*(NB_TIM_CHANNELS) + channel // The ISR acknowledges the IRQ and registers the event in the proper entry // of the _user_timer_event[] array, and a log message is displayed on TTY0. /////////////////////////////////////////////////////////////////////////////////// void _timer_isr( unsigned int irq_type, // HWI / PTI unsigned int irq_id, // index returned by XCU unsigned int channel ) // user timer index { #if NB_TIM_CHANNELS unsigned int cluster_xy = _get_procid() / NB_PROCS_MAX; // acknowledge IRQ depending on type if ( irq_type == IRQ_TYPE_HWI ) _timer_reset_irq( cluster_xy, channel ); else _xcu_timer_reset_irq( cluster_xy, irq_id ); // register the event _user_timer_event[cluster_xy * NB_TIM_CHANNELS + channel] = 1; // display a message on TTY 0 _printf("\n[GIET WARNING] User Timer IRQ at cycle %d / cluster = %x / channel = %d\n", _get_proctime(), cluster_xy, channel ); #else _printf("[GIET ERROR] _timer_isr() should not be called when NB_TIM_CHANNELS == 0\n"); _exit(); #endif } // 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