diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-20 12:08:42 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-20 13:52:19 +0200 |
commit | e0dd8a5ad830798bc8082b03b8c42c32fb9660e0 (patch) | |
tree | d147bfc4d670fcdfbd2e2d2e75eb209f92e07df1 /bsps | |
parent | bsps: Move startup files to bsps (diff) | |
download | rtems-e0dd8a5ad830798bc8082b03b8c42c32fb9660e0.tar.bz2 |
bsps: Move benchmark timer to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps')
48 files changed, 5705 insertions, 0 deletions
diff --git a/bsps/arm/csb336/btimer/btimer.c b/bsps/arm/csb336/btimer/btimer.c new file mode 100644 index 0000000000..66ff4a0bf4 --- /dev/null +++ b/bsps/arm/csb336/btimer/btimer.c @@ -0,0 +1,93 @@ +/** + * @file + * @brief Cogent CSB336 Timer driver + * + * This uses timer 2 for timing measurments. + */ + +/* + * Copyright (c) 2004 Cogent Computer Systems + * Written by Jay Monkman <jtm@lopingdog.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/btimer.h> +#include <mc9328mxl.h> + +uint32_t g_start; +uint32_t g_freq; + +bool benchmark_timer_find_average_overhead; + + +/* + * Set up Timer 1 + */ +void benchmark_timer_initialize( void ) +{ + MC9328MXL_TMR2_TCTL = (MC9328MXL_TMR_TCTL_CLKSRC_PCLK1 | + MC9328MXL_TMR_TCTL_FRR | + MC9328MXL_TMR_TCTL_TEN); + /* set prescaler to 1 (register value + 1) */ \ + MC9328MXL_TMR2_TPRER = 0; + + /* get freq of counter in KHz */ + g_freq = get_perclk1_freq() / 1000; + + g_start = MC9328MXL_TMR2_TCN; +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 0 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 1 /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read( void ) +{ + uint32_t t; + unsigned long long total; + + t = MC9328MXL_TMR2_TCN; + /* + * Total is calculated by taking into account the number of timer overflow + * interrupts since the timer was initialized and clicks since the last + * interrupts. + */ + + total = (t - g_start); + + /* convert to nanoseconds */ + total = (total * 1000)/ g_freq; + + if ( benchmark_timer_find_average_overhead == 1 ) { + return (int) total; + } else if ( total < LEAST_VALID ) { + return 0; + } + /* + * Somehow convert total into microseconds + */ + + return (total - AVG_OVERHEAD); +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ + benchmark_timer_find_average_overhead = find_flag; +} + diff --git a/bsps/arm/csb337/btimer/btimer.c b/bsps/arm/csb337/btimer/btimer.c new file mode 100644 index 0000000000..c65dd8096a --- /dev/null +++ b/bsps/arm/csb337/btimer/btimer.c @@ -0,0 +1,91 @@ +/** + * @file + * @brief Cogent CSB337 Timer driver + * + * This uses timer 0 for timing measurments. + */ + +/* + * Copyright (c) 2004 by Jay Monkman <jtm@lopingdog.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <bsp.h> +#include <rtems.h> +#include <rtems/btimer.h> +#include <at91rm9200.h> +#include <at91rm9200_pmc.h> + +uint16_t tstart; +bool benchmark_timer_find_average_overhead; +uint32_t tick_time; +/* + * Set up TC0 - + * timer_clock2 (MCK/8) + * capture mode - this shouldn't matter + */ +void benchmark_timer_initialize( void ) +{ + uint32_t tmr_freq; + + /* since we are using timer_clock2, divide mck by 8 */ + tmr_freq = at91rm9200_get_mck() / 8; + + TC_TC0_REG(TC_CMR) = TC_CMR_TCCLKS(1); /* timer_clock2 */ + TC_TC0_REG(TC_CCR) = (TC_CCR_CLKEN /* enable the counter */ + | TC_CCR_SWTRG); /* start it up */ + + /* tick time in nanoseconds */ + tick_time = 1000000000/tmr_freq; + +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 0 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 1 /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read( void ) +{ + uint16_t t; + uint32_t total; + t = TC_TC0_REG(TC_CV); + + /* + * Total is calculated by taking into account the number of timer overflow + * interrupts since the timer was initialized and clicks since the last + * interrupts. + */ + + total = t * tick_time; + + if ( benchmark_timer_find_average_overhead == 1 ) + return total; /* in nanosecond units */ + else { + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + /* + * Somehow convert total into microseconds + */ + return (total - AVG_OVERHEAD); + } +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ + benchmark_timer_find_average_overhead = find_flag; +} + diff --git a/bsps/arm/edb7312/btimer/btimer.c b/bsps/arm/edb7312/btimer/btimer.c new file mode 100644 index 0000000000..b795122ff3 --- /dev/null +++ b/bsps/arm/edb7312/btimer/btimer.c @@ -0,0 +1,80 @@ +/* + * Cirrus EP7312 Timer driver + * + * Copyright (c) 2002 by Jay Monkman <jtm@smoothsmoothie.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * Notes: + * This file manages the benchmark timer used by the RTEMS Timing Test + * Suite. Each measured time period is demarcated by calls to + * benchmark_timer_initialize() and benchmark_timer_read(). benchmark_timer_read() usually returns + * the number of microseconds since benchmark_timer_initialize() exitted. + * + * It is important that the timer start/stop overhead be determined + * when porting or modifying this code. +*/ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/btimer.h> +#include <ep7312.h> + +uint16_t tstart; +bool benchmark_timer_find_average_overhead; + +void benchmark_timer_initialize( void ) +{ + *EP7312_SYSCON1 |= EP7312_SYSCON1_TC2_512KHZ; + *EP7312_TC2D = 0xffff; +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 0 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 1 /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read( void ) +{ + uint16_t t; + uint32_t total; + t = *EP7312_TC2D; + + /* + * Total is calculated by taking into account the number of timer overflow + * interrupts since the timer was initialized and clicks since the last + * interrupts. + */ + + total = (uint32_t)0x0000ffff - t; /* result is 1/512000 = ~2 uS */ + total = (total * 1953) / 1000; /* convert to uS */ + if ( benchmark_timer_find_average_overhead == true ) + return total; /* in XXX microsecond units */ + else { + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + /* + * Somehow convert total into microseconds + */ + return (total - AVG_OVERHEAD); + } +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/arm/gumstix/btimer/btimer.c b/bsps/arm/gumstix/btimer/btimer.c new file mode 100644 index 0000000000..7eb3d5b811 --- /dev/null +++ b/bsps/arm/gumstix/btimer/btimer.c @@ -0,0 +1,75 @@ +/** + * @file + * @brief PXA255 timer + */ + +/* + * PXA255 timer by Yang Xi <hiyangxi@gmail.com> + * Copyright (c) 2004 by Jay Monkman <jtm@lopingdog.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <bsp.h> +#include <rtems.h> +#include <rtems/btimer.h> +#include <pxa255.h> + +uint32_t tstart; +static uint32_t tick_time; +bool benchmark_timer_find_average_overhead; + +bool benchmark_timer_is_initialized = false; + +/* + * Use the timer count register to measure. + * The frequency of it is 3.4864MHZ + * The longest period we are able to capture is 4G/3.4864MHZ + */ +void benchmark_timer_initialize(void) +{ + tick_time = XSCALE_OS_TIMER_TCR; +} + +/* + * The following controls the behavior of Read_timer(). + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 0 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 1 /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read(void) +{ + + uint32_t total; + total = XSCALE_OS_TIMER_TCR; + if(total>=tick_time) + total -= tick_time; + else + total += 0xffffffff - tick_time; /*Round up but not overflow*/ + + if ( benchmark_timer_find_average_overhead == true ) + return total; /*Counter cycles*/ + + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + + return total; +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/arm/lpc176x/btimer/btimer.c b/bsps/arm/lpc176x/btimer/btimer.c new file mode 100644 index 0000000000..36c720b4fb --- /dev/null +++ b/bsps/arm/lpc176x/btimer/btimer.c @@ -0,0 +1,407 @@ +/** + * @file timer.c + * + * @ingroup lpc176x + * + * @brief Timer controller for the mbed lpc1768 board. + */ + +/* + * Copyright (c) 2014 Taller Technologies. + * + * @author Boretto Martin (martin.boretto@tallertechnologies.com) + * @author Diaz Marcos (marcos.diaz@tallertechnologies.com) + * @author Lenarduzzi Federico (federico.lenarduzzi@tallertechnologies.com) + * @author Daniel Chicco (daniel.chicco@tallertechnologies.com) + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <stdio.h> +#include <rtems/status-checks.h> +#include <bsp.h> +#include <bsp/irq.h> +#include <bsp/io.h> +#include <bsp/timer.h> + +/** + * @brief Represents all the timers. + */ +const lpc176x_timer timers[ LPC176X_TIMER_COUNT ] = +{ + { + .device = (lpc176x_timer_device *) LPC176X_TMR0_BASE_ADDR, + .module = LPC176X_MODULE_TIMER_0, + .pinselcap = LPC176X_TIMER0_CAPTURE_PORTS, + .pinselemat = LPC176X_TIMER0_EMATCH_PORTS, + }, + { + .device = (lpc176x_timer_device *) LPC176X_TMR1_BASE_ADDR, + .module = LPC176X_MODULE_TIMER_1, + .pinselcap = LPC176X_TIMER1_CAPTURE_PORTS, + .pinselemat = LPC176X_TIMER1_EMATCH_PORTS, + }, + { + .device = (lpc176x_timer_device *) LPC176X_TMR2_BASE_ADDR, + .module = LPC176X_MODULE_TIMER_2, + .pinselcap = LPC176X_TIMER2_CAPTURE_PORTS, + .pinselemat = LPC176X_TIMER2_EMATCH_PORTS, + }, + { + .device = (lpc176x_timer_device *) LPC176X_TMR3_BASE_ADDR, + .module = LPC176X_MODULE_TIMER_3, + .pinselcap = LPC176X_TIMER3_CAPTURE_PORTS, + .pinselemat = LPC176X_TIMER3_EMATCH_PORTS, + } +}; + +/** + * @brief Represents all the functions according to the timers. + */ +lpc176x_timer_functions functions_vector[ LPC176X_TIMER_COUNT ] = +{ + { + .funct_vector = NULL + }, + { + .funct_vector = NULL + }, + { + .funct_vector = NULL + }, + { + .funct_vector = NULL + } +}; + +/** + * @brief Calls the corresponding interrupt function and pass the timer + * as parameter. + * + * @param timer The specific device. + * @param interruptnumber Interrupt number. + */ +static inline void lpc176x_call_desired_isr( + const lpc176x_timer_number number, + const lpc176x_isr_function interruptfunction +) +{ + if ( ( *functions_vector[ number ].funct_vector )[ interruptfunction ] != + NULL ) { + ( *functions_vector[ number ].funct_vector )[ interruptfunction ]( number ); + } + + /* else implies that the function vector points NULL. Also, + there is nothing to do. */ +} + +/** + * @brief Gets true if the selected interrupt is pending + * + * @param number: the number of the timer. + * @param interrupt: the interrupt we are checking for. + * @return TRUE if the interrupt is pending. + */ +static inline bool lpc176x_timer_interrupt_is_pending( + const lpc176x_timer_number tnumber, + const lpc176x_isr_function function +) +{ + assert( ( tnumber < LPC176X_TIMER_COUNT ) + && ( function < LPC176X_ISR_FUNCTIONS_COUNT ) ); + + return ( timers[ tnumber ].device->IR & + LPC176X_TIMER_INTERRUPT_SOURCE_BIT( function ) ); +} + +/** + * @brief Resets interrupt status for the selected interrupt + * + * @param tnumber: the number of the timer + * @param interrupt: the interrupt we are resetting + */ +static inline void lpc176x_timer_reset_interrupt( + const lpc176x_timer_number tnumber, + const lpc176x_isr_function function +) +{ + assert( ( tnumber < LPC176X_TIMER_COUNT ) + && ( function < LPC176X_ISR_FUNCTIONS_COUNT ) ); + timers[ tnumber ].device->IR = + LPC176X_TIMER_INTERRUPT_SOURCE_BIT( function ); +} + +inline rtems_status_code lpc176x_timer_reset( + const lpc176x_timer_number tnumber ) +{ + rtems_status_code status_code = RTEMS_INVALID_NUMBER; + + if ( tnumber < LPC176X_TIMER_COUNT ) { + timers[ tnumber ].device->TCR = LPC176X_TIMER_RESET; + status_code = RTEMS_SUCCESSFUL; + } + + /* else implies that the timer number is invalid. Also, + an invalid number is returned. */ + + return status_code; +} + +inline rtems_status_code lpc176x_timer_set_mode( + const lpc176x_timer_number tnumber, + const lpc176x_timer_mode mode +) +{ + rtems_status_code status_code = RTEMS_INVALID_NUMBER; + + if ( tnumber < LPC176X_TIMER_COUNT ) { + timers[ tnumber ].device->CTCR = mode; + status_code = RTEMS_SUCCESSFUL; + } + + /* else implies that the timer number is invalid. Also, + an invalid number is returned. */ + + return status_code; +} + +inline rtems_status_code lpc176x_timer_start( + const lpc176x_timer_number tnumber ) +{ + rtems_status_code status_code = RTEMS_INVALID_NUMBER; + + if ( tnumber < LPC176X_TIMER_COUNT ) { + timers[ tnumber ].device->TCR = LPC176X_TIMER_START; + status_code = RTEMS_SUCCESSFUL; + } + + /* else implies that the timer number is invalid. Also, + an invalid number is returned. */ + + return status_code; +} + +inline rtems_status_code lpc176x_timer_is_started( + const lpc176x_timer_number tnumber, + bool *is_started +) +{ + rtems_status_code status_code = RTEMS_INVALID_NUMBER; + + if ( tnumber < LPC176X_TIMER_COUNT ) { + *is_started = ( timers[ tnumber ].device->TCR & LPC176X_TIMER_START ) == + LPC176X_TIMER_START; + status_code = RTEMS_SUCCESSFUL; + } + + /* else implies that the timer number is invalid. Also, + an invalid number is returned. */ + + return status_code; +} + +inline rtems_status_code lpc176x_timer_set_resolution( + const lpc176x_timer_number tnumber, + const lpc176x_microseconds resolution +) +{ + rtems_status_code status_code = RTEMS_INVALID_NUMBER; + + if ( tnumber < LPC176X_TIMER_COUNT ) { + timers[ tnumber ].device->PR = ( LPC176X_CCLK / + LPC176X_TIMER_PRESCALER_DIVISOR ) * + resolution; + status_code = RTEMS_SUCCESSFUL; + } + + /* else implies that the timer number is invalid. Also, + an invalid number is returned. */ + + return status_code; +} + +rtems_status_code lpc176x_timer_match_config( + const lpc176x_timer_number tnumber, + const lpc176x_match_port match_port, + const lpc176x_match_function function, + const uint32_t match_value +) +{ + rtems_status_code status_code = RTEMS_INVALID_NUMBER; + + if ( ( tnumber < LPC176X_TIMER_COUNT ) + && ( match_port < LPC176X_EMATCH_PORTS_COUNT ) + && ( function < LPC176X_TIMER_MATCH_FUNCTION_COUNT ) ) { + timers[ tnumber ].device->MCR = + LPC176X_SET_MCR( timers[ tnumber ].device->MCR, + match_port, function ); + timers[ tnumber ].device->MR[ match_port ] = match_value; + status_code = RTEMS_SUCCESSFUL; + } + + /* else implies that the timer number, or a match port or a function + is invalid. Also, an invalid number is returned. */ + + return status_code; +} + +inline rtems_status_code lpc176x_timer_capture_config( + const lpc176x_timer_number tnumber, + const lpc176x_capture_port capture_port, + const lpc176x_capture_function function +) +{ + rtems_status_code status_code = RTEMS_INVALID_NUMBER; + + if ( ( tnumber < LPC176X_TIMER_COUNT ) + && ( capture_port < LPC176X_CAPTURE_PORTS_COUNT ) + && ( function < LPC176X_TIMER_CAPTURE_FUNCTION_COUNT ) ) { + timers[ tnumber ].device->CCR = + LPC176X_SET_CCR( timers[ tnumber ].device->CCR, + capture_port, function ); + lpc176x_pin_select( timers[ tnumber ].pinselcap[ capture_port ], + LPC176X_PIN_FUNCTION_11 ); + } + + /* else implies that the timer number or the capture port is invalid. Also, + an invalid number is returned. */ + + return status_code; +} + +inline rtems_status_code lpc176x_timer_external_match_config( + const lpc176x_timer_number number, + const lpc176x_match_port match_port, + const lpc176x_ext_match_function function +) +{ + rtems_status_code status_code = RTEMS_INVALID_NUMBER; + + if ( ( number < LPC176X_TIMER_COUNT ) + && ( match_port < LPC176X_EMATCH_PORTS_COUNT ) ) { + timers[ number ].device->EMR = + LPC176X_SET_EMR( timers[ number ].device->EMR, + match_port, function ); + lpc176x_pin_select( timers[ number ].pinselemat[ match_port ], + LPC176X_PIN_FUNCTION_11 ); + status_code = RTEMS_SUCCESSFUL; + } + + /* else implies that the timer number or the match port is invalid. Also, + an invalid number is returned. */ + + return status_code; +} + +inline uint32_t lpc176x_timer_get_capvalue( + const lpc176x_timer_number number, + const lpc176x_capture_port capture_port +) +{ + assert( ( number < LPC176X_TIMER_COUNT ) + && ( capture_port < LPC176X_CAPTURE_PORTS_COUNT ) ); + + return timers[ number ].device->CR[ capture_port ]; +} + +inline uint32_t lpc176x_timer_get_timer_value( + const lpc176x_timer_number tnumber ) +{ + assert( tnumber < LPC176X_TIMER_COUNT ); + + return timers[ tnumber ].device->TC; +} + +inline rtems_status_code lpc176x_timer_set_timer_value( + const lpc176x_timer_number tnumber, + const uint32_t timer_value +) +{ + rtems_status_code status_code = RTEMS_INVALID_NUMBER; + + if ( tnumber < LPC176X_TIMER_COUNT ) { + timers[ tnumber ].device->TC = timer_value; + status_code = RTEMS_SUCCESSFUL; + } + + /* else implies that the timer number is invalid. Also, + an invalid number is returned. */ + + return status_code; +} + +void lpc176x_timer_isr( void *arg ) +{ + const lpc176x_timer_number tnumber = (lpc176x_timer_number) arg; + + if ( tnumber < LPC176X_TIMER_COUNT ) { + lpc176x_isr_function i; + + for ( i = 0; i < LPC176X_ISR_FUNCTIONS_COUNT; ++i ) { + if ( lpc176x_timer_interrupt_is_pending( tnumber, i ) ) { + lpc176x_call_desired_isr( tnumber, i ); + lpc176x_timer_reset_interrupt( tnumber, i ); + } + + /* else implies that the current timer is not pending. Also, + there is nothing to do. */ + } + } + + /* else implies that the timer number is not valid. Also, + there is nothing to do. */ +} + +rtems_status_code lpc176x_timer_init( const lpc176x_timer_number tnumber ) +{ + rtems_status_code status_code = RTEMS_INVALID_NUMBER; + + if ( tnumber < LPC176X_TIMER_COUNT ) { + status_code = lpc176x_module_enable( timers[ tnumber ].module, + LPC176X_MODULE_PCLK_DEFAULT ); + RTEMS_CHECK_SC( status_code, "Enabling the timer module." ); + + status_code = lpc176x_timer_reset( tnumber ); + status_code = lpc176x_timer_set_mode( tnumber, + LPC176X_TIMER_MODE_TIMER ); + status_code = lpc176x_timer_set_resolution( tnumber, + LPC176X_TIMER_DEFAULT_RESOLUTION ); + + timers[ tnumber ].device->MCR = LPC176X_TIMER_CLEAR_FUNCTION; + timers[ tnumber ].device->CCR = LPC176X_TIMER_CLEAR_FUNCTION; + timers[ tnumber ].device->EMR = LPC176X_TIMER_CLEAR_FUNCTION; + } + + /* else implies that the timer number is not valid. Also, + an invalid number is returned. */ + + return status_code; +} + +rtems_status_code lpc176x_timer_init_with_interrupt( + const lpc176x_timer_number tnumber, + const lpc176x_isr_funct_vector *const vector +) +{ + rtems_status_code status_code = RTEMS_INVALID_NUMBER; + + char isrname[ LPC176X_ISR_NAME_STRING_SIZE ]; + + snprintf( isrname, LPC176X_ISR_NAME_STRING_SIZE, "TimerIsr%d", tnumber ); + + if ( tnumber < LPC176X_TIMER_COUNT && vector != NULL ) { + functions_vector[ tnumber ].funct_vector = vector; + + status_code = lpc176x_timer_init( tnumber ); + status_code = rtems_interrupt_handler_install( + LPC176X_TIMER_VECTOR_NUMBER( tnumber ), + isrname, + RTEMS_INTERRUPT_UNIQUE, + lpc176x_timer_isr, + (void *) tnumber ); + } + + return status_code; +}
\ No newline at end of file diff --git a/bsps/arm/rtl22xx/btimer/btimer.c b/bsps/arm/rtl22xx/btimer/btimer.c new file mode 100644 index 0000000000..ad73aeda3c --- /dev/null +++ b/bsps/arm/rtl22xx/btimer/btimer.c @@ -0,0 +1,65 @@ +/** + * @file + * @brief RTL22xx board Timer driver + * + * This uses Timer1 for timing measurments. + */ + +/* + * By Ray Xu <rayx.cn@gmail.com>, modify form Mc9328mxl RTEMS DSP + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <bsp.h> +#include <rtems.h> +#include <rtems/btimer.h> +#include <lpc22xx.h> +#include "lpc_timer.h" + +uint32_t g_start; +uint32_t g_freq; + +bool benchmark_timer_find_average_overhead; + + +/* + * Set up Timer 1 + */ +void benchmark_timer_initialize( void ) +{ + g_freq = LPC22xx_Fpclk / 1000; +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 0 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 1 /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read( void ) +{ + return (T0TC/(LPC22xx_Fpclk/1000000)); + /* + * Total is calculated by taking into account the number of timer overflow + * interrupts since the timer was initialized and clicks since the last + * interrupts. + */ +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ + benchmark_timer_find_average_overhead = find_flag; +} + diff --git a/bsps/arm/rtl22xx/btimer/lpc_timer.h b/bsps/arm/rtl22xx/btimer/lpc_timer.h new file mode 100644 index 0000000000..364812ddb8 --- /dev/null +++ b/bsps/arm/rtl22xx/btimer/lpc_timer.h @@ -0,0 +1,48 @@ +#ifndef __LPC_TIMER_H +#define __LPC_TIMER_H + +/************************************************************************* + * + * File name : Timer.h + * + **************************************************************************/ + +/* Timer Control register bit descriptions */ +#define TCR_ENABLE_BIT 0 +#define TCR_RESET_BIT 1 + +// The channel name which is used in matching, in fact they represent +// corresponding Match Register +#define CH_MAXNUM 4 +#define CH0 0 +#define CH1 1 +#define CH2 2 +#define CH3 3 + +// The channel name which is used in capturing, in fact they represent +// corresponding Capture Register +#define CPCH_MAXNUM 4 +#define CPCH0 0 +#define CPCH1 1 +#define CPCH2 2 +#define CPCH3 3 + +//The actions when matching +#define TimerAction_Interrupt 0x1 +#define TimerAction_ResetTimer 0x2 +#define TimerAction_StopTimer 0x4 + +//Interrupt source type +#define TIMERMR0Int 0x01 +#define TIMERMR1Int 0x02 +#define TIMERMR2Int 0x04 +#define TIMERMR3Int 0x08 +#define TIMERCR0Int 0x10 +#define TIMERCR1Int 0x20 +#define TIMERCR2Int 0x40 +#define TIMERCR3Int 0x80 + +#define TIMERALLInt 0xFF + +#endif //__LPC_Timer_H + diff --git a/bsps/arm/smdk2410/btimer/btimer.c b/bsps/arm/smdk2410/btimer/btimer.c new file mode 100644 index 0000000000..2bc599df75 --- /dev/null +++ b/bsps/arm/smdk2410/btimer/btimer.c @@ -0,0 +1,100 @@ +/** + * @file + * @brief S3C2400 Timer driver + * + * This uses timer 1 for timing measurments. + */ + +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <bsp.h> +#include <rtems.h> +#include <rtems/btimer.h> +#include <s3c24xx.h> + +uint32_t g_start; +uint32_t g_freq; + +bool benchmark_timer_find_average_overhead; + + +/* + * Set up Timer 1 + */ +void benchmark_timer_initialize( void ) +{ + uint32_t cr; + + /* stop TIMER1*/ + cr=rTCON & 0xFFFFF0FF; + rTCON=(cr | (0x0 << 8)); + + /* set MUX for Timer1 to 1/2 */ + cr=rTCFG1 & 0xFFFFFF0F; + rTCFG1=(cr | (0<<4)); + + /* input freq=PLCK/2 Mhz*/ + g_freq = get_PCLK() / 2000; + rTCNTB1 = 0xFFFF; + + /* start TIMER1 with manual reload */ + cr=rTCON & 0xFFFFF0FF; + rTCON=(cr | (0x1 << 9)); + rTCON=(cr | (0x1 << 8)); + + g_start = rTCNTO1; +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 0 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 1 /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read( void ) +{ + uint32_t t; + unsigned long long total; + + t = rTCNTO1; + /* + * Total is calculated by taking into account the number of timer overflow + * interrupts since the timer was initialized and clicks since the last + * interrupts. + */ + + total = (g_start - t); + + /* convert to microseconds */ + total = (total*1000) / g_freq; + + if ( benchmark_timer_find_average_overhead == 1 ) { + return (int) total; + } else if ( total < LEAST_VALID ) { + return 0; + } + + /* + * Somehow convert total into microseconds + */ + return (total - AVG_OVERHEAD); +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ + benchmark_timer_find_average_overhead = find_flag; +} + diff --git a/bsps/epiphany/epiphany_sim/btimer/btimer.c b/bsps/epiphany/epiphany_sim/btimer/btimer.c new file mode 100644 index 0000000000..5df9448e01 --- /dev/null +++ b/bsps/epiphany/epiphany_sim/btimer/btimer.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2015 University of York. + * Hesham ALMatary <hmka501@york.ac.uk> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/btimer.h> +#include <rtems/score/epiphany-utility.h> + +extern char bsp_start_vector_table_begin[]; + +bool benchmark_timer_find_average_overhead; + +static void benchmark_timer1_interrupt_handler(void) +{ + unsigned int val = 0xFFFFFFFF; + unsigned int event_type = 0x1; + + /* Embed assembly code for setting timer1 */ + __asm__ __volatile__ ("movts ctimer1, %[val] \t \n" :: [val] "r" (val)); + + __asm__ __volatile__ ("movfs r16, config; \t \n" + "mov r17, %%low(0xfffff0ff);\t \n" + "movt r17, %%high(0xffff0ff);\t \n" + "lsl r18, %[event_type], 0x8; \t \n" + "and r16, r16, r17; \t \n" + "orr r16, r16, r18; \t \n" + "movts config, r16; \t \n" + :: [event_type] "r" (event_type)); +} + +/* Start eCore tiemr 1 usef for profiling and timing analysis */ +void benchmark_timer_initialize( void ) +{ + /* Install interrupt handler for timer 1 */ + + proc_ptr *table = + (proc_ptr *) bsp_start_vector_table_begin; + + table[TIMER1] = benchmark_timer1_interrupt_handler; + + benchmark_timer1_interrupt_handler(); +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 0 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 1 /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read( void ) +{ + uint32_t timer_val = 0; + uint32_t total; + + __asm__ __volatile__ ("movfs %[timer_val], ctimer1 \t \n" + :[timer_val] "=r" (timer_val):); + + total = (0xFFFFFFFF - timer_val); + + if ( benchmark_timer_find_average_overhead == true ) + return total; + else { + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + /* + * Somehow convert total into microseconds + */ + return (total - AVG_OVERHEAD); + } +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/i386/pc386/btimer/btimer.c b/bsps/i386/pc386/btimer/btimer.c new file mode 100644 index 0000000000..16d3d66176 --- /dev/null +++ b/bsps/i386/pc386/btimer/btimer.c @@ -0,0 +1,552 @@ +/* + * This file contains the PC386 timer package. + * + * Rosimildo daSilva -ConnectTel, Inc - Fixed infinite loop in the Calibration + * routine. I've seen this problems with faster machines ( pentiums ). Sometimes + * RTEMS just hangs at startup. + * + * Joel 9 May 2010: This is now seen sometimes on qemu. + * + * Modifications by: + * (C) Copyright 1997 - + * NavIST Group - Real-Time Distributed Systems and Industrial Automation + * http://pandora.ist.utl.pt + * Instituto Superior Tecnico * Lisboa * PORTUGAL + * + * This file is provided "AS IS" without warranty of any kind, either + * expressed or implied. + * + * Based upon code by + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <stdlib.h> +#include <bsp.h> +#include <rtems/btimer.h> +#include <bsp/irq-generic.h> +#include <libcpu/cpuModel.h> + +/* + * Constants + */ +#define AVG_OVERHEAD 0 /* 0.1 microseconds to start/stop timer. */ +#define LEAST_VALID 1 /* Don't trust a value lower than this. */ +#define SLOW_DOWN_IO 0x80 /* io which does nothing */ + +#define TWO_MS (uint32_t)(2000) /* TWO_MS = 2000us (sic!) */ + +#define MSK_NULL_COUNT 0x40 /* bit counter available for reading */ + +#define CMD_READ_BACK_STATUS 0xE2 /* command read back status */ + +RTEMS_INTERRUPT_LOCK_DEFINE( /* visible global variable */ , + rtems_i386_i8254_access_lock, "rtems_i386_i8254_access_lock" ); + +/* + * Global Variables + */ +volatile uint32_t Ttimer_val; +bool benchmark_timer_find_average_overhead = true; +volatile unsigned int fastLoop1ms, slowLoop1ms; + +void (*benchmark_timer_initialize_function)(void) = 0; +benchmark_timer_t (*benchmark_timer_read_function)(void) = 0; +void (*Timer_exit_function)(void) = 0; + +/* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */ +extern void timerisr(void); + +void Timer_exit(void); + +/* + * Pentium optimized timer handling. + */ + +/* + * Timer cleanup routine at RTEMS exit. + * + * NOTE: This routine is not really necessary, since there will be + * a reset at exit. + */ +static void tsc_timer_exit(void) +{ +} + +static void tsc_timer_initialize(void) +{ + static bool First = true; + + if (First) { + First = false; + + atexit(Timer_exit); /* Try not to hose the system at exit. */ + } + Ttimer_val = rdtsc(); /* read starting time */ +} + +/* + * Read TSC timer value. + */ +static uint32_t tsc_read_timer(void) +{ + register uint32_t total; + + total = (uint32_t)(rdtsc() - Ttimer_val); + + if (benchmark_timer_find_average_overhead) + return total; + + if (total < LEAST_VALID) + return 0; /* below timer resolution */ + + return (total - AVG_OVERHEAD); +} + +/* + * Non-Pentium timer handling. + */ +#define US_PER_ISR 250 /* Number of micro-seconds per timer interruption */ + +/* + * Timer cleanup routine at RTEMS exit. NOTE: This routine is + * not really necessary, since there will be a reset at exit. + */ +static void timerOff(const rtems_raw_irq_connect_data* used) +{ + rtems_interrupt_lock_context lock_context; + /* + * disable interrrupt at i8259 level + */ + bsp_interrupt_vector_disable(used->idtIndex - BSP_IRQ_VECTOR_BASE); + + rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context); + + /* reset timer mode to standard (DOS) value */ + outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN); + outport_byte(TIMER_CNTR0, 0); + outport_byte(TIMER_CNTR0, 0); + + rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context); +} + +static void timerOn(const rtems_raw_irq_connect_data* used) +{ + rtems_interrupt_lock_context lock_context; + + rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context); + + /* load timer for US_PER_ISR microsecond period */ + outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN); + outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 0 & 0xff); + outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 8 & 0xff); + + rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context); + + /* + * enable interrrupt at i8259 level + */ + bsp_interrupt_vector_enable(used->idtIndex - BSP_IRQ_VECTOR_BASE); +} + +static rtems_raw_irq_connect_data timer_raw_irq_data = { + BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE, + timerisr, + timerOn, + timerOff, + NULL +}; + +/* + * Timer cleanup routine at RTEMS exit. + * + * NOTE: This routine is not really necessary, since there will be + * a reset at exit. + */ +static void i386_timer_exit(void) +{ + i386_delete_idt_entry (&timer_raw_irq_data); +} + +extern void rtems_irq_prologue_0(void); +static void i386_timer_initialize(void) +{ + static bool First = true; + + if (First) { + rtems_raw_irq_connect_data raw_irq_data = { + BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE, + rtems_irq_prologue_0, + NULL, + NULL, + NULL + }; + + First = false; + i386_delete_idt_entry (&raw_irq_data); + + atexit(Timer_exit); /* Try not to hose the system at exit. */ + if (!i386_set_idt_entry (&timer_raw_irq_data)) { + printk("raw handler connection failed\n"); + rtems_fatal_error_occurred(1); + } + } + /* wait for ISR to be called at least once */ + Ttimer_val = 0; + while (Ttimer_val == 0) + continue; + Ttimer_val = 0; +} + +/* + * Read hardware timer value. + */ +static uint32_t i386_read_timer(void) +{ + register uint32_t total, clicks; + register uint8_t lsb, msb; + rtems_interrupt_lock_context lock_context; + + rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context); + outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH); + inport_byte(TIMER_CNTR0, lsb); + inport_byte(TIMER_CNTR0, msb); + rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context); + + clicks = (msb << 8) | lsb; + total = (Ttimer_val * US_PER_ISR) + (US_PER_ISR - TICK_TO_US(clicks)); + + if (benchmark_timer_find_average_overhead) + return total; + + if (total < LEAST_VALID) + return 0; /* below timer resolution */ + + return (total - AVG_OVERHEAD); +} + +/* + * General timer functions using either TSC-based implementation + * or interrupt-based implementation + */ + +void benchmark_timer_initialize(void) +{ + static bool First = true; + + if (First) { + if (x86_has_tsc()) { +#if defined(DEBUG) + printk("TSC: timer initialization\n"); +#endif /* DEBUG */ + benchmark_timer_initialize_function = &tsc_timer_initialize; + benchmark_timer_read_function = &tsc_read_timer; + Timer_exit_function = &tsc_timer_exit; + } else { +#if defined(DEBUG) + printk("ISR: timer initialization\n"); +#endif /* DEBUG */ + benchmark_timer_initialize_function = &i386_timer_initialize; + benchmark_timer_read_function = &i386_read_timer; + Timer_exit_function = &i386_timer_exit; + } + First = false; + } + (*benchmark_timer_initialize_function)(); +} + +uint32_t benchmark_timer_read(void) +{ + return (*benchmark_timer_read_function)(); +} + +void Timer_exit(void) +{ + if ( Timer_exit_function ) + return (*Timer_exit_function)(); +} + +/* + * Set internal benchmark_timer_find_average_overhead flag value. + */ +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ + benchmark_timer_find_average_overhead = find_flag; +} + +static unsigned short lastLoadedValue; + +/* + * Loads timer 0 with value passed as arguemnt. + * + * Returns: Nothing. Loaded value must be a number of clock bits... + */ +static void loadTimerValue( unsigned short loadedValue ) +{ + rtems_interrupt_lock_context lock_context; + rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context); + lastLoadedValue = loadedValue; + outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_SQWAVE); + outport_byte(TIMER_CNTR0, loadedValue & 0xff); + outport_byte(TIMER_CNTR0, (loadedValue >> 8) & 0xff); + rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context); +} + +/* + * Reads the current value of the timer, and converts the + * number of ticks to micro-seconds. + * + * Returns: number of clock bits elapsed since last load. + */ +static unsigned int readTimer0(void) +{ + unsigned short lsb, msb; + unsigned char status; + unsigned int count; + rtems_interrupt_lock_context lock_context; + rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context); + + outport_byte( + TIMER_MODE, + (TIMER_RD_BACK | (RB_COUNT_0 & ~(RB_NOT_STATUS | RB_NOT_COUNT))) + ); + inport_byte(TIMER_CNTR0, status); + inport_byte(TIMER_CNTR0, lsb); + inport_byte(TIMER_CNTR0, msb); + + rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context); + + count = ( msb << 8 ) | lsb ; + if (status & RB_OUTPUT ) + count += lastLoadedValue; + + return (2*lastLoadedValue - count); +} + +static void Timer0Reset(void) +{ + loadTimerValue(0xffff); + readTimer0(); +} + +static void fastLoop (unsigned int loopCount) +{ + unsigned int i; + for( i=0; i < loopCount; i++ )outport_byte( SLOW_DOWN_IO, 0 ); +} + +static void slowLoop (unsigned int loopCount) +{ + unsigned int j; + for (j=0; j <100 ; j++) { + fastLoop (loopCount); + } +} + +/* + * #define DEBUG_CALIBRATE + */ +void +Calibrate_loop_1ms(void) +{ + unsigned int offset, offsetTmp, emptyCall, emptyCallTmp, res, i, j; + unsigned int targetClockBits, currentClockBits; + unsigned int slowLoopGranularity, fastLoopGranularity; + rtems_interrupt_level level; + int retries = 0; + + /* + * This code is designed to run before interrupt management + * is enabled and running it on multiple CPUs and or after + * secondary CPUs are bring up seems really broken. + * Disabling of local interrupts is enough. + */ + rtems_interrupt_local_disable(level); + +retry: + if ( ++retries >= 5 ) { + printk( "Calibrate_loop_1ms: too many attempts. giving up!!\n" ); + while (1); + } +#ifdef DEBUG_CALIBRATE + printk("Calibrate_loop_1ms is starting, please wait (but not too long.)\n"); +#endif + targetClockBits = US_TO_TICK(1000); + /* + * Fill up the cache to get a correct offset + */ + Timer0Reset(); + readTimer0(); + /* + * Compute the minimal offset to apply due to read counter register. + */ + offset = 0xffffffff; + for (i=0; i <1000; i++) { + Timer0Reset(); + offsetTmp = readTimer0(); + offset += offsetTmp; + } + offset = offset / 1000; /* compute average */ + /* + * calibrate empty call + */ + fastLoop (0); + emptyCall = 0; + j = 0; + for (i=0; i <10; i++) { + Timer0Reset(); + fastLoop (0); + res = readTimer0(); + /* res may be inferior to offset on fast + * machine because we took an average for offset + */ + if (res > offset) { + ++j; + emptyCallTmp = res - offset; + emptyCall += emptyCallTmp; + } + } + if (j == 0) emptyCall = 0; + else emptyCall = emptyCall / j; /* compute average */ + /* + * calibrate fast loop + */ + Timer0Reset(); + fastLoop (10000); + res = readTimer0() - offset; + if (res < emptyCall) { + printk( + "Problem #1 in offset computation in Calibrate_loop_1ms " + " in file libbsp/i386/pc386/timer/timer.c\n" + ); + goto retry; + } + fastLoopGranularity = (res - emptyCall) / 10000; + /* + * calibrate slow loop + */ + Timer0Reset(); + slowLoop(10); + res = readTimer0(); + if (res < offset + emptyCall) { + printk( + "Problem #2 in offset computation in Calibrate_loop_1ms " + " in file libbsp/i386/pc386/timer/timer.c\n" + ); + goto retry; + } + slowLoopGranularity = (res - offset - emptyCall)/ 10; + + if (slowLoopGranularity == 0) { + printk( + "Problem #3 in offset computation in Calibrate_loop_1ms " + " in file libbsp/i386/pc386/timer/timer.c\n" + ); + goto retry; + } + + targetClockBits += offset; +#ifdef DEBUG_CALIBRATE + printk("offset = %u, emptyCall = %u, targetClockBits = %u\n", + offset, emptyCall, targetClockBits); + printk("slowLoopGranularity = %u fastLoopGranularity = %u\n", + slowLoopGranularity, fastLoopGranularity); +#endif + slowLoop1ms = (targetClockBits - emptyCall) / slowLoopGranularity; + if (slowLoop1ms != 0) { + fastLoop1ms = targetClockBits % slowLoopGranularity; + if (fastLoop1ms > emptyCall) fastLoop1ms -= emptyCall; + } + else + fastLoop1ms = targetClockBits - emptyCall / fastLoopGranularity; + + if (slowLoop1ms != 0) { + /* + * calibrate slow loop + */ + + while(1) + { + int previousSign = 0; /* 0 = unset, 1 = incrementing, 2 = decrementing */ + Timer0Reset(); + slowLoop(slowLoop1ms); + currentClockBits = readTimer0(); + if (currentClockBits > targetClockBits) { + if ((currentClockBits - targetClockBits) < slowLoopGranularity) { + /* decrement loop counter anyway to be sure slowLoop(slowLoop1ms) < targetClockBits */ + --slowLoop1ms; + break; + } + else { + --slowLoop1ms; + if (slowLoop1ms == 0) break; + if (previousSign == 0) previousSign = 2; + if (previousSign == 1) break; + } + } + else { + if ((targetClockBits - currentClockBits) < slowLoopGranularity) { + break; + } + else { + ++slowLoop1ms; + if (previousSign == 0) previousSign = 1; + if (previousSign == 2) break; + } + } + } + } + /* + * calibrate fast loop + */ + + if (fastLoopGranularity != 0 ) { + while(1) { + int previousSign = 0; /* 0 = unset, 1 = incrementing, 2 = decrementing */ + Timer0Reset(); + if (slowLoop1ms != 0) slowLoop(slowLoop1ms); + fastLoop(fastLoop1ms); + currentClockBits = readTimer0(); + if (currentClockBits > targetClockBits) { + if ((currentClockBits - targetClockBits) < fastLoopGranularity) + break; + else { + --fastLoop1ms; + if (previousSign == 0) previousSign = 2; + if (previousSign == 1) break; + } + } + else { + if ((targetClockBits - currentClockBits) < fastLoopGranularity) + break; + else { + ++fastLoop1ms; + if (previousSign == 0) previousSign = 1; + if (previousSign == 2) break; + } + } + } + } +#ifdef DEBUG_CALIBRATE + printk("slowLoop1ms = %u, fastLoop1ms = %u\n", slowLoop1ms, fastLoop1ms); +#endif + rtems_interrupt_local_enable(level); + +} + +/* + * loop which waits at least timeToWait ms + */ +void Wait_X_ms( unsigned int timeToWait) +{ + unsigned int j; + + for (j=0; j<timeToWait ; j++) { + if (slowLoop1ms != 0) slowLoop(slowLoop1ms); + fastLoop(fastLoop1ms); + } +} diff --git a/bsps/i386/pc386/btimer/timerisr.S b/bsps/i386/pc386/btimer/timerisr.S new file mode 100644 index 0000000000..da2a07845a --- /dev/null +++ b/bsps/i386/pc386/btimer/timerisr.S @@ -0,0 +1,56 @@ +/*-------------------------------------------------------------------------+ +| timerisr.s v1.1 - PC386 BSP - 1997/08/07 ++--------------------------------------------------------------------------+ +| This file contains the PC386 timer interrupt handler. ++--------------------------------------------------------------------------+ +| (C) Copyright 1997 - +| - NavIST Group - Real-Time Distributed Systems and Industrial Automation +| +| http://pandora.ist.utl.pt +| +| Instituto Superior Tecnico * Lisboa * PORTUGAL ++--------------------------------------------------------------------------+ +| Disclaimer: +| +| This file is provided "AS IS" without warranty of any kind, either +| expressed or implied. ++--------------------------------------------------------------------------+ +| This code is base on: +| timerisr.s,v 1.5 1995/12/19 20:07:45 joel Exp - go32 BSP +| With the following copyright notice: +| ************************************************************************** +| * COPYRIGHT (c) 1989-1999. +| * On-Line Applications Research Corporation (OAR). +| * +| * The license and distribution terms for this file may be +| * found in the file LICENSE in this distribution or at +| * http://www.rtems.org/license/LICENSE. +| ************************************************************************** ++--------------------------------------------------------------------------*/ + +#include <rtems/asm.h> + +BEGIN_CODE + + EXTERN(Ttimer_val) + +/*-------------------------------------------------------------------------+ +| Function: rtems_isr timerisr(rtems_vector_number); +| Description: ISR for the timer. The timer is set up to generate an +| interrupt at maximum intervals. +| Global Variables: None. +| Arguments: standard - see RTEMS documentation. +| Returns: standard return value - see RTEMS documentation. ++--------------------------------------------------------------------------*/ + PUBLIC(timerisr) +SYM (timerisr): + incl Ttimer_val # another tick + pushl eax + movb $0x20, al + outb al, $0x20 # signal generic End Of Interrupt (EOI) to PIC + popl eax + iret + +END_CODE + +END diff --git a/bsps/lm32/shared/btimer/btimer.c b/bsps/lm32/shared/btimer/btimer.c new file mode 100644 index 0000000000..e1980a7077 --- /dev/null +++ b/bsps/lm32/shared/btimer/btimer.c @@ -0,0 +1,111 @@ +/* timer.c + * + * This file manages the benchmark timer used by the RTEMS Timing + * Test Suite. Each measured time period is demarcated by calls to + * benchmark_timer_initialize() and benchmark_timer_read(). + * benchmark_timer_read() usually returns the number of microseconds + * since benchmark_timer_initialize() exitted. + * + * NOTE: It is important that the timer start/stop overhead be + * determined when porting or modifying this code. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * Jukka Pietarinen <jukka.pietarinen@mrf.fi>, 2008, + * Micro-Research Finland Oy + */ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/btimer.h> +#include <rtems/bspIo.h> +#include "../include/system_conf.h" +#include "../../shared/clock/clock.h" + +static inline int timerread(unsigned int reg) +{ +#if ON_SIMULATOR && defined(TIMER0_BASE_ADDRESS) + return *((int*)(TIMER0_BASE_ADDRESS + reg)); +#elif defined(TIMER1_BASE_ADDRESS) + return *((int*)(TIMER1_BASE_ADDRESS + reg)); +#else +#warning "Benchmarking timer not available!" + return 0; +#endif +} + +static inline void timerwrite(unsigned int reg, int value) +{ +#if ON_SIMULATOR && defined(TIMER0_BASE_ADDRESS) + *((int*)(TIMER0_BASE_ADDRESS + reg)) = value; +#elif defined(TIMER1_BASE_ADDRESS) + *((int*)(TIMER1_BASE_ADDRESS + reg)) = value; +#endif +} + +bool benchmark_timer_find_average_overhead; + +void benchmark_timer_initialize( void ) +{ + /* Set timer period */ + timerwrite(LM32_CLOCK_PERIOD, 0xffffffff); + /* Stop timer */ + timerwrite(LM32_CLOCK_CR, LM32_CLOCK_CR_STOP); + /* Clear status register */ + timerwrite(LM32_CLOCK_SR, 0); + /* Start timer */ + timerwrite(LM32_CLOCK_CR, LM32_CLOCK_CR_START); +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 4 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 4 /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read( void ) +{ + uint32_t ticks; + uint32_t total; + uint32_t status; + + ticks = 0xffffffff - timerread(LM32_CLOCK_SNAPSHOT); + status = timerread(LM32_CLOCK_SR); + if (status & LM32_CLOCK_SR_TO) + printk("Timer overflow!\n"); + + total = ticks / (CPU_FREQUENCY / 1000000); + + if ( benchmark_timer_find_average_overhead == true ) + return total; /* in XXX microsecond units */ + else + { + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + /* + * Somehow convert total into microseconds + */ + return (total - AVG_OVERHEAD); + } +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/m32c/m32cbsp/btimer/btimer.c b/bsps/m32c/m32cbsp/btimer/btimer.c new file mode 100644 index 0000000000..a27d9ffb1b --- /dev/null +++ b/bsps/m32c/m32cbsp/btimer/btimer.c @@ -0,0 +1,69 @@ +/* + * This file implements a stub benchmark timer that is sufficient to + * satisfy linking the RTEMS Benchmarks. + */ + +/* + * COPYRIGHT (c) 1989-2001. + * On-Line Applications Research Corporation (OAR). + */ + +#include <bsp.h> +#include <rtems/btimer.h> + +#include <varvects.h> + +bool benchmark_timer_find_average_overhead; +uint32_t benchmark_timer_overhead = 10; + +#define TABSR *((uint8_t *)0x340) +#define TA0MR *((uint8_t *)0x356) +#define TA0 *((uint16_t *)0x346) +#define TA0IC *((uint8_t *)0x6c) + +static int benchmark_timer_interrupts; + +static void __attribute__((interrupt)) timer_ra_interrupt(void); + +#define ivec_timer_a0 12 + +void __attribute__((interrupt)) +timer_ra_interrupt(void) +{ + benchmark_timer_interrupts++; + TA0IC = 0x05; + +} + +void benchmark_timer_initialize(void) +{ + benchmark_timer_interrupts = 0; + _set_var_vect (timer_ra_interrupt, ivec_timer_a0); + TA0MR = 0x00; + TA0 = 0xffff; + TA0IC = 0x05; + TABSR = 0x55; +} + +benchmark_timer_t benchmark_timer_read(void) +{ + uint32_t count; + + count = 0xFFFF - TA0; + count += benchmark_timer_interrupts * 0xFFFFL; + + if (!benchmark_timer_find_average_overhead) { + if ( count > benchmark_timer_overhead ) + count -= benchmark_timer_overhead; + else + count = 0; + } + return count; +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/m68k/av5282/btimer/btimer.c b/bsps/m68k/av5282/btimer/btimer.c new file mode 100644 index 0000000000..8a8323e547 --- /dev/null +++ b/bsps/m68k/av5282/btimer/btimer.c @@ -0,0 +1,34 @@ +/* + * Timer Init + * + * Use the last DMA timer (DTIM3) as the diagnostic timer. + */ + +#include <rtems.h> +#include <rtems/btimer.h> +#include <bsp.h> + +void +benchmark_timer_initialize(void) +{ + int preScaleDivisor = 58; + int div = MCF5282_TIMER_DTMR_CLK_DIV1; + MCF5282_TIMER3_DTRR = 0x2710; + MCF5282_TIMER3_DTMR = 0; + MCF5282_TIMER3_DTMR = MCF5282_TIMER_DTMR_PS(preScaleDivisor - 1) | div | + MCF5282_TIMER_DTMR_RST; +} + +/* + * Return timer value in microsecond units + */ +uint32_t +benchmark_timer_read(void) +{ + return MCF5282_TIMER3_DTCN; +} + +void +benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ +} diff --git a/bsps/m68k/gen68340/btimer/btimer.c b/bsps/m68k/gen68340/btimer/btimer.c new file mode 100644 index 0000000000..23cddd95b0 --- /dev/null +++ b/bsps/m68k/gen68340/btimer/btimer.c @@ -0,0 +1,256 @@ +/* + * ATTENTION: As MC68349 has no built-in Timer, the following code doesn't work + * in a MC68349. You can't use FIFO full mode for the moment, but + * it should be easy to fix this by using an external timer. + * + * Use TIMER 1 for TIMEOUT when using FIFO FULL mode in UART driver + * Use TIMER 2 for timing test suites + * + * NOTE: It is important that the timer start/stop overhead be + * determined when porting or modifying this code. + */ + +/* + * Geoffroy Montel + * France Telecom - CNET/DSM/TAM/CAT + * 4, rue du Clos Courtel + * 35512 CESSON-SEVIGNE + * FRANCE + * + * e-mail: g_montel@yahoo.com + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/btimer.h> +#include <m340uart.h> +#include <m340timer.h> +#include <m68340.h> + +#define TIMER1_VECTOR 122 +#define TIMER1_IRQ_LEVEL 5 +#define TIMER1_INTERRUPT_ARBITRATION 5 + +#define TIMER2_VECTOR 123 +#define TIMER2_IRQ_LEVEL 4 +#define TIMER2_INTERRUPT_ARBITRATION 4 + +#define CLOCK_SPEED 25 /* in Mhz */ + +#define max(a,b) (((a)>(b)) ? (a) : (b)) + +void (*Restart_Fifo_Full_A_Timer)(void); +void (*Restart_Check_A_Timer)(void); +void (*Restart_Fifo_Full_B_Timer)(void); +void (*Restart_Check_B_Timer)(void); + +int preload = 0; + +/* + * __Restart_Fifo_Full_Timer + * + * When a character is received, sets the TIMER to raise an interrupt at + * TIMEOUT. It's necessary to prevent from not getting n-1 characters + * (with n the Uart Fifo size). + */ +static void __Restart_Fifo_Full_Timer (void) +{ + TSR1 |= m340_TO; + TCR1 &= ~m340_CPE; + WPREL11 = preload; + TCR1 |= m340_CPE; +} + +/* + * __Restart_Check_Timer + * + * When no character has been received recently, check now and then if whether + * a there's a character in the FIFO + */ +static void __Restart_Check_Timer (void) +{ + TSR1 |= m340_TO; + TCR1 &= ~m340_CPE; + WPREL11 = 0xFFFF; + TCR1 |= m340_CPE; +} + +/* + * __do_nothing + * + * We always restart the fifo full timer with a call to Restart_*_Timer + * if we do not use FIFO full, Restart_X_Timer are set to do __do_nothing + */ +static void __do_nothing (void) +{ +} + +#define Fifo_Full_on_A \ + (m340_uart_config[UART_CHANNEL_A].rx_mode==UART_FIFO_FULL && \ + m340_uart_config[UART_CHANNEL_A].enable && \ + m340_uart_config[UART_CHANNEL_A].mode==UART_INTERRUPTS) +#define Fifo_Full_on_B \ + (m340_uart_config[UART_CHANNEL_B].rx_mode==UART_FIFO_FULL && \ + m340_uart_config[UART_CHANNEL_B].enable && \ + m340_uart_config[UART_CHANNEL_B].mode==UART_INTERRUPTS) + +/* + * Fifo_Full_benchmark_timer_initialize + * + * initialize Timer 1 for FIFO full mode + */ +void Fifo_Full_benchmark_timer_initialize (void) +{ + float max_baud_rate; + int prescaler_output_tap = -1; + int nb_of_clock_ticks = 0; + rtems_isr_entry old_handler; + + /* + * USE TIMER 1 for UART FIFO FULL mode + */ + if ( Fifo_Full_on_A || Fifo_Full_on_B ) { + /* Disable the timer */ + TCR1 &= ~m340_SWR; + + /* Reset the interrupts */ + TSR1 &= ~(m340_TO | m340_TG | m340_TC); + + /* Init the stop bit for normal operation, ignore FREEZE, user privileges, + * set interrupt arbitration. + */ + TMCR1 = TIMER1_INTERRUPT_ARBITRATION; + + /* interrupt priority level and interrupt vector */ + TIR1 = TIMER1_VECTOR | (TIMER1_IRQ_LEVEL << 8); + + /* compute prescaler */ + if ( Fifo_Full_on_A && Fifo_Full_on_B) { + max_baud_rate = max( + m340_uart_config[UART_CHANNEL_A].rx_baudrate, + m340_uart_config[UART_CHANNEL_B].rx_baudrate + ); + } else if ( Fifo_Full_on_A ) { + max_baud_rate = m340_uart_config[UART_CHANNEL_A].rx_baudrate; + } else + max_baud_rate = m340_uart_config[UART_CHANNEL_B].rx_baudrate; + + /* find out config */ + nb_of_clock_ticks = (10/max_baud_rate)*(CLOCK_SPEED*1000000)*1.2; + if (nb_of_clock_ticks < 0xFFFF) { + preload = nb_of_clock_ticks; + prescaler_output_tap = -1; + } else if (nb_of_clock_ticks/2 < 0xFFFF) { + preload = nb_of_clock_ticks/2; + prescaler_output_tap = m340_Divide_by_2; + } else if (nb_of_clock_ticks/4 < 0xFFFF) { + preload = nb_of_clock_ticks/4; + prescaler_output_tap = m340_Divide_by_4; + } else if (nb_of_clock_ticks/8 < 0xFFFF) { + preload = nb_of_clock_ticks/8; + prescaler_output_tap = m340_Divide_by_16; + } else if (nb_of_clock_ticks/16 < 0xFFFF) { + preload = nb_of_clock_ticks/16; + prescaler_output_tap = m340_Divide_by_16; + } else if (nb_of_clock_ticks/32 < 0xFFFF) { + preload = nb_of_clock_ticks/32; + prescaler_output_tap = m340_Divide_by_32; + } else if (nb_of_clock_ticks/64 < 0xFFFF) { + preload = nb_of_clock_ticks/64; + prescaler_output_tap = m340_Divide_by_64; + } else if (nb_of_clock_ticks/128 < 0xFFFF) { + preload = nb_of_clock_ticks/128; + prescaler_output_tap = m340_Divide_by_128; + } else if (nb_of_clock_ticks/256 < 0xFFFF) { + preload = nb_of_clock_ticks/256; + prescaler_output_tap = m340_Divide_by_256; + } + + /* Input Capture/Output Compare (ICOC) */ + TCR1 = m340_SWR | m340_TO_Enabled | m340_ICOC; + if (prescaler_output_tap!=-1) TCR1 |= prescaler_output_tap | m340_PSE; + + /* install interrupt vector */ + rtems_interrupt_catch(InterruptHandler, TIMER1_VECTOR, &old_handler); + + } /* fifo full mode on a uart */ + + /* install routines */ + if ( Fifo_Full_on_A ) { + Restart_Check_A_Timer = __Restart_Check_Timer; + Restart_Fifo_Full_A_Timer = __Restart_Fifo_Full_Timer; + } else { + Restart_Check_A_Timer = __do_nothing; + Restart_Fifo_Full_A_Timer = __do_nothing; + } + + if ( Fifo_Full_on_B ) { + Restart_Check_B_Timer = __Restart_Check_Timer; + Restart_Fifo_Full_B_Timer = __Restart_Fifo_Full_Timer; + } else { + Restart_Check_B_Timer = __do_nothing; + Restart_Fifo_Full_B_Timer = __do_nothing; + } + + /* start checking timer */ + Restart_Check_A_Timer(); + Restart_Check_B_Timer(); +} + +/* + * benchmark_timer_initialize + * + * init Timer for timing test suites + */ +void benchmark_timer_initialize (void) +{ + /* Disable the timer */ + TCR2 &= ~m340_SWR; + + /* Reset the interrupts */ + TSR2 &= ~(m340_TO | m340_TG | m340_TC); + + /* Init the stop bit for normal operation, ignore FREEZE, user privileges, + set interrupt arbitration */ + TMCR1 = TIMER2_INTERRUPT_ARBITRATION; + + /* interrupt priority level and interrupt vector */ + TIR1 = TIMER2_VECTOR | (TIMER2_IRQ_LEVEL << 8); + + /* Init the stop bit for normal operation, ignore FREEZE, user privileges, + set interrupt arbitration */ + TMCR2 = TIMER2_INTERRUPT_ARBITRATION; + + /* Preload register 1 */ + WPREL21 = 0xFFFF; + + /* Input Capture/Output Compare (ICOC) */ + TCR2 = m340_SWR | m340_ICOC | m340_PSE | m340_Divide_by_16 | m340_CPE; +} + +/* + * benchmark_timer_read + * + * Return timer value in microsecond units + */ +uint32_t benchmark_timer_read (void) +{ + /* there's CLOCK_SPEED / 16 micro seconds between two timer + * register decrements. + */ + return (((0xFFFF - TCNTR2) * CLOCK_SPEED) / 16); +} + +/* + * benchmark_timer_disable_subtracting_average_overhead + */ +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ +} diff --git a/bsps/m68k/gen68360/btimer/btimer.c b/bsps/m68k/gen68360/btimer/btimer.c new file mode 100644 index 0000000000..40a3e0a4c1 --- /dev/null +++ b/bsps/m68k/gen68360/btimer/btimer.c @@ -0,0 +1,83 @@ +/* + * Use TIMER 1 and TIMER 2 for Timing Test Suite + * The hardware on the MC68360 makes these routines very simple. + * + * Based on the `gen68302' board support package, and covered by the + * original distribution terms. + * + * W. Eric Norum + * Saskatchewan Accelerator Laboratory + * University of Saskatchewan + * Saskatoon, Saskatchewan, CANADA + * eric@skatter.usask.ca + */ + +/* + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * NOTE: It is important that the timer start/stop overhead be + * determined when porting or modifying this code. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <rtems/btimer.h> +#include <bsp.h> +#include <rtems/m68k/m68360.h> + +void +benchmark_timer_initialize (void) +{ + /* + * Reset timers 1 and 2 + */ + m360.tgcr &= ~0x00FF; + m360.tcn1 = 0; + m360.tcn2 = 0; + m360.ter1 = 0xFFFF; + m360.ter2 = 0xFFFF; + + /* + * Cascade timers 1 and 2 + */ + m360.tgcr |= 0x0080; + + /* + * Configure timers 1 and 2 to a single 32-bit, 1 MHz timer. + * HARDWARE: + * Change the `25' to match your processor clock + */ + m360.tmr2 = ((25-1) << 8) | 0x2; + m360.tmr1 = 0; + + /* + * Start the timers + */ + m360.tgcr |= 0x0011; +} + +/* + * Return timer value in microsecond units + */ +uint32_t +benchmark_timer_read (void) +{ + unsigned short val; + + val = m360.tcn1; + return val; +} + +void +benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ +} diff --git a/bsps/m68k/genmcf548x/btimer/btimer.c b/bsps/m68k/genmcf548x/btimer/btimer.c new file mode 100644 index 0000000000..acac6f8f9b --- /dev/null +++ b/bsps/m68k/genmcf548x/btimer/btimer.c @@ -0,0 +1,83 @@ +/*===============================================================*\ +| Project: RTEMS generic mcf548x BSP | ++-----------------------------------------------------------------+ +| File: timer.c | ++-----------------------------------------------------------------+ +| The file contains the diagnostic timer code of generic MCF548x | +| BSP. | ++-----------------------------------------------------------------+ +| Copyright (c) 2007 | +| Embedded Brains GmbH | +| Obere Lagerstr. 30 | +| D-82178 Puchheim | +| Germany | +| rtems@embedded-brains.de | ++-----------------------------------------------------------------+ +| | +| Parts of the code has been derived from the "dBUG source code" | +| package Freescale is providing for M548X EVBs. The usage of | +| the modified or unmodified code and it's integration into the | +| generic mcf548x BSP has been done according to the Freescale | +| license terms. | +| | +| The Freescale license terms can be reviewed in the file | +| | +| Freescale_license.txt | +| | ++-----------------------------------------------------------------+ +| | +| The generic mcf548x BSP has been developed on the basic | +| structures and modules of the av5282 BSP. | +| | ++-----------------------------------------------------------------+ +| | +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| | +| http://www.rtems.org/license/LICENSE. | +| | ++-----------------------------------------------------------------+ +| | +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 12.11.07 1.0 ras | +| | +\*===============================================================*/ + +/* + * Timer Init + * + * Use the second slice timer (SLT1) as the diagnostic timer. + */ + +#include <rtems.h> +#include <rtems/btimer.h> +#include <bsp.h> +#include <mcf548x/mcf548x.h> + +bool benchmark_timer_find_average_overhead; +static uint32_t microsecond_timer_value = 0; + +void benchmark_timer_initialize(void) +{ + MCF548X_SLT_SLTCNT1 = 0xFFFFFFFF; + MCF548X_SLT_SCR1 |= (MCF548X_SLT_SCR_TEN | MCF548X_SLT_SCR_RUN); +} + +/* + * Return timer value in microsecond units + * XLB clock 100 MHz / 1us is equal to 100 counts + */ +uint32_t +benchmark_timer_read(void) +{ + microsecond_timer_value = (0xFFFFFFFF - MCF548X_SLT_SCNT1)/100; + + return microsecond_timer_value; +} + +void +benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/m68k/mcf52235/btimer/btimer.c b/bsps/m68k/mcf52235/btimer/btimer.c new file mode 100644 index 0000000000..f9122542c2 --- /dev/null +++ b/bsps/m68k/mcf52235/btimer/btimer.c @@ -0,0 +1,39 @@ +/* + * Timer Init + * + * Use the last DMA timer (DTIM3) as the diagnostic timer. + * + * Author: W. Eric Norum <norume@aps.anl.gov> + * + * COPYRIGHT (c) 2005. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <rtems/btimer.h> +#include <bsp.h> + +void benchmark_timer_initialize(void) +{ + uint32_t preScaleDivisor = bsp_get_CPU_clock_speed() / 1000000; + + MCF_DTIM3_DTMR = 0; + MCF_DTIM3_DTMR = MCF_DTIM_DTMR_PS(preScaleDivisor - 1) | + MCF_DTIM_DTMR_CLK_DIV1 | MCF_DTIM_DTMR_RST; +} + +/* + * Return timer value in microsecond units + */ +benchmark_timer_t benchmark_timer_read(void) +{ + return MCF_DTIM3_DTCN; +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ +} diff --git a/bsps/m68k/mcf5225x/btimer/btimer.c b/bsps/m68k/mcf5225x/btimer/btimer.c new file mode 100644 index 0000000000..ac0ca6d7d0 --- /dev/null +++ b/bsps/m68k/mcf5225x/btimer/btimer.c @@ -0,0 +1,39 @@ +/* + * Timer Init + * + * Use the last DMA timer (DTIM3) as the diagnostic timer. + * + * Author: W. Eric Norum <norume@aps.anl.gov> + * + * COPYRIGHT (c) 2005-2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <rtems/btimer.h> +#include <bsp.h> + +void benchmark_timer_initialize(void) +{ + uint32_t preScaleDivisor = bsp_get_CPU_clock_speed() / 1000000; + + MCF_DTIM3_DTMR = 0; + MCF_DTIM3_DTMR = MCF_DTIM_DTMR_PS(preScaleDivisor - 1) | + MCF_DTIM_DTMR_CLK_DIV1 | MCF_DTIM_DTMR_RST; +} + +/* + * Return timer value in microsecond units + */ +benchmark_timer_t benchmark_timer_read(void) +{ + return MCF_DTIM3_DTCN; +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ +} diff --git a/bsps/m68k/mcf5235/btimer/btimer.c b/bsps/m68k/mcf5235/btimer/btimer.c new file mode 100644 index 0000000000..6aae8e99e6 --- /dev/null +++ b/bsps/m68k/mcf5235/btimer/btimer.c @@ -0,0 +1,34 @@ +/* + * Timer Init + * + * Use the last DMA timer (DTIM3) as the diagnostic timer. + */ + +#include <rtems.h> +#include <rtems/btimer.h> +#include <bsp.h> + +void +benchmark_timer_initialize(void) +{ + int preScaleDivisor = 0x4A; + int div = MCF5235_TIMER_DTMR_CLK_DIV1; + MCF5235_TIMER_DTRR3 = 0x2710; + MCF5235_TIMER3_DTMR = 0; + MCF5235_TIMER3_DTMR = MCF5235_TIMER_DTMR_PS(preScaleDivisor) | div | + MCF5235_TIMER_DTMR_RST; +} + +/* + * Return timer value in microsecond units + */ +uint32_t +benchmark_timer_read(void) +{ + return MCF5235_TIMER3_DTCN; +} + +void +benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ +} diff --git a/bsps/m68k/mcf5329/btimer/btimer.c b/bsps/m68k/mcf5329/btimer/btimer.c new file mode 100644 index 0000000000..0f762c0a6a --- /dev/null +++ b/bsps/m68k/mcf5329/btimer/btimer.c @@ -0,0 +1,39 @@ +/* + * Timer Init + * + * Use the last DMA timer (DTIM3) as the diagnostic timer. + * + * Author: W. Eric Norum <norume@aps.anl.gov> + * + * COPYRIGHT (c) 2005. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <rtems/btimer.h> +#include <bsp.h> + +void benchmark_timer_initialize(void) +{ + uint32_t preScaleDivisor = bsp_get_BUS_clock_speed() / 1000000; + + MCF_DTIM3_DTMR = 0; + MCF_DTIM3_DTMR = MCF_DTIM_DTMR_PS(preScaleDivisor - 1) | + MCF_DTIM_DTMR_CLK_DIV1 | MCF_DTIM_DTMR_RST; +} + +/* + * Return timer value in microsecond units + */ +benchmark_timer_t benchmark_timer_read(void) +{ + return MCF_DTIM3_DTCN; +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ +} diff --git a/bsps/m68k/mrm332/btimer/btimer.c b/bsps/m68k/mrm332/btimer/btimer.c new file mode 100644 index 0000000000..8c14f8fe58 --- /dev/null +++ b/bsps/m68k/mrm332/btimer/btimer.c @@ -0,0 +1,60 @@ +/* + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <bsp.h> +#include <rtems/btimer.h> + +bool benchmark_timer_find_average_overhead; + +extern rtems_isr Clock_isr(void); + +void benchmark_timer_initialize( void ) +{ +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * FIND_AVG_OVERHEAD * instructs the routine to return the "raw" count. + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 0 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 1 /* Don't trust a clicks value lower than this */ + +/* + * Return timer value in 1/2-microsecond units + */ +benchmark_timer_t benchmark_timer_read( void ) +{ + uint32_t total; + total = 0; + + if ( benchmark_timer_find_average_overhead == true ) + return total; /* in XXX microsecond units */ + + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + + return (total - AVG_OVERHEAD); +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/m68k/mvme147/btimer/btimer.c b/bsps/m68k/mvme147/btimer/btimer.c new file mode 100644 index 0000000000..9d48fa7075 --- /dev/null +++ b/bsps/m68k/mvme147/btimer/btimer.c @@ -0,0 +1,67 @@ +/* + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * MVME147 port for TNI - Telecom Bretagne + * by Dominique LE CAMPION (Dominique.LECAMPION@enst-bretagne.fr) + * May 1996 + */ + +#include <rtems/btimer.h> +#include <bsp.h> + +#define TIMER_INT_LEVEL 6 + +#define COUNTDOWN_VALUE 0 +/* Allows 0.4096 second delay betwin ints */ +/* Each tick is 6.25 us */ + +int Ttimer_val; +bool benchmark_timer_find_average_overhead; + +rtems_isr timerisr(rtems_vector_number); + +void benchmark_timer_initialize(void) +{ + (void) set_vector(timerisr, TIMER_1_VECTOR, 0); /* install ISR */ + + Ttimer_val = 0; /* clear timer ISR count */ + pcc->timer1_int_control = 0x00; /* Disable T1 Interr. */ + pcc->timer1_preload = COUNTDOWN_VALUE; + /* write countdown preload value */ + pcc->timer1_control = 0x00; /* load preload value */ + pcc->timer1_control = 0x07; /* clear T1 overflow counter, enable counter */ + pcc->timer1_int_control = TIMER_INT_LEVEL|0x08; + /* Enable Timer 1 and set its int. level */ + +} + +#define AVG_OVERHEAD 0 /* No need to start/stop the timer to read + its value on the MVME147 PCC: reads are not + synchronized whith the counter updates*/ +#define LEAST_VALID 10 /* Don't trust a value lower than this */ + +benchmark_timer_t benchmark_timer_read(void) +{ + uint32_t total; + uint16_t counter_value; + + counter_value = pcc->timer1_count; /* read the counter value */ + + total = ((Ttimer_val * 0x10000) + counter_value); /* in 6.25 us units */ + /* DC note : just look at the assembly generated + to see gcc's impressive optimization ! */ + return total; + +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/m68k/mvme147/btimer/timerisr.S b/bsps/m68k/mvme147/btimer/timerisr.S new file mode 100644 index 0000000000..418cf64108 --- /dev/null +++ b/bsps/m68k/mvme147/btimer/timerisr.S @@ -0,0 +1,26 @@ +/* timer_isr() + * + * This routine provides the ISR for the PCC timer on the MVME147 + * board. The timer is set up to generate an interrupt at maximum + * intervals. + * + * MVME147 port for TNI - Telecom Bretagne + * by Dominique LE CAMPION (Dominique.LECAMPION@enst-bretagne.fr) + * May 1996 + */ + +#include <rtems/asm.h> + +BEGIN_CODE + +.set T1_CONTROL_REGISTER, 0xfffe1018 | timer 1 control register + + PUBLIC (timerisr) +SYM (timerisr): + orb #0x80, T1_CONTROL_REGISTER | clear T1 int status bit + addql #1, SYM (Ttimer_val) | increment timer value +end_timerisr: + rte + +END_CODE +END diff --git a/bsps/m68k/mvme162/btimer/btimer.c b/bsps/m68k/mvme162/btimer/btimer.c new file mode 100644 index 0000000000..c7dce0475d --- /dev/null +++ b/bsps/m68k/mvme162/btimer/btimer.c @@ -0,0 +1,73 @@ +/* + * This routine initializes the Tick Timer 1 on the MVME162 board. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * Modifications of respective RTEMS file: COPYRIGHT (c) 1994. + * EISCAT Scientific Association. M.Savitski + * + * This material is a part of the MVME162 Board Support Package + * for the RTEMS executive. Its licensing policies are those of the + * RTEMS above. + */ + +#include <rtems.h> +#include <rtems/btimer.h> +#include <bsp.h> + +/* Periodic tick interval */ +#define TICK_INTERVAL 0x10000U +#define TIMER_INT_LEVEL 6 + +uint32_t Ttimer_val; +bool benchmark_timer_find_average_overhead; + +rtems_isr timerisr(rtems_vector_number vector); + +void benchmark_timer_initialize(void) +{ + (void) set_vector( timerisr, VBR0 * 0x10 + 0x8, 0 ); + + Ttimer_val = 0; /* clear timer ISR count */ + lcsr->vector_base |= MASK_INT; /* unmask VMEchip2 interrupts */ + lcsr->intr_clear |= 0x01000000; /* clear pending interrupt */ + lcsr->to_ctl = 0xE7; /* prescaler to 1 MHz (see Appendix A1) */ + lcsr->timer_cmp_1 = TICK_INTERVAL; + lcsr->timer_cnt_1 = 0; /* clear counter */ + lcsr->board_ctl |= 7; /* increment, reset-on-compare, */ + /* and clear-overflow-cnt */ + + lcsr->intr_level[0] |= TIMER_INT_LEVEL; /* set int level */ + lcsr->intr_ena |= 0x01000000; /* enable tick timer 1 interrupt */ +} + +#define AVG_OVERHEAD 3U /* It typically takes 3.0 microseconds */ + /* (3 countdowns) to start/stop the timer. */ +#define LEAST_VALID 10U /* Don't trust a value lower than this */ + +benchmark_timer_t benchmark_timer_read(void) +{ + uint32_t total; + + total = (Ttimer_val * TICK_INTERVAL) + lcsr->timer_cnt_1; + + if ( benchmark_timer_find_average_overhead == true ) + return total; /* in one-half microsecond units */ + + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + + return (total-AVG_OVERHEAD) >> 1; +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/m68k/mvme162/btimer/timerisr.S b/bsps/m68k/mvme162/btimer/timerisr.S new file mode 100644 index 0000000000..c433d91c59 --- /dev/null +++ b/bsps/m68k/mvme162/btimer/timerisr.S @@ -0,0 +1,44 @@ +/* timer_isr() + * + * This routine provides the ISR for the Z8036 timer on the MVME136 + * board. The timer is set up to generate an interrupt at maximum + * intervals. + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * Modifications of respective RTEMS file: COPYRIGHT (c) 1994. + * EISCAT Scientific Association. M.Savitski + * + * This material is a part of the MVME162 Board Support Package + * for the RTEMS executive. Its licensing policies are those of the + * RTEMS above. + */ + +#include <rtems/asm.h> + +BEGIN_CODE + +.set INTR_CLEAR_REG, 0xfff40074 | interrupt clear register +.set RELOAD, 0x01000000 | clear tick 1 interrupt + + PUBLIC (Ttimer_val) + PUBLIC (timerisr) +SYM (timerisr): + move.l a0, -(a7) | save a0 + movea.l #INTR_CLEAR_REG, a0 | a0 = addr of cmd status reg + ori.l #RELOAD, (a0) | reload countdown + addq.l #1, SYM (Ttimer_val) | increment timer value + move.l (a7)+, a0 | restore a0 + rte + +END_CODE +END diff --git a/bsps/m68k/mvme167/btimer/btimer.c b/bsps/m68k/mvme167/btimer/btimer.c new file mode 100644 index 0000000000..ed2ce26e51 --- /dev/null +++ b/bsps/m68k/mvme167/btimer/btimer.c @@ -0,0 +1,139 @@ +/** + * @file + * + * This file manages the benchmark timer used by the RTEMS Timing Test Suite. + * Each measured time period is demarcated by calls to + * benchmark_timer_initialize() and benchmark_timer_read(). + * benchmark_timer_read() usually returns the number of microseconds + * since benchmark_timer_initialize() exitted. + * + * These functions are prototyped in rtems/btimer.h and + * must be implemented as part of the BSP. + * + * This port does not allow the application to select which timer on the + * MVME167 to use for the timer, nor does it allow the application to + * configure the timer. The timer uses the VMEchip2 Tick Timer #1. This timer + * is distinct from the clock, which uses Tick Timer #2 in the VMEchip2. + * + * All page references are to the MVME166/MVME167/MVME187 Single Board + * Computer Programmer's Reference Guide (MVME187PG/D2) with the April 1993 + * supplements/addenda (MVME187PG/D2A1). + */ + +/* + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * Modifications of respective RTEMS file: + * Copyright (c) 1998, National Research Council of Canada + */ + +#include <rtems.h> +#include <rtems/btimer.h> +#include <bsp.h> + +/* Periodic tick interval */ +#define TICK_INTERVAL 10000UL /* T1's countdown constant (10 ms) */ +#define TIMER_INT_LEVEL 6 /* T1's interrupt level */ +#define TIMER_VECTOR (VBR0 * 0x10 + 0x8) /* T1 is vector $X8 (p. 2-71)*/ + +/* Number of interrupts since timer was re-initialized */ +uint32_t Ttimer_val; + +/* + * Set to true to return raw value. Normally zero. Depends on being allocated + * in the .bss section and on that section being explicitly zeroed at boot + * time. + */ +bool benchmark_timer_find_average_overhead; + +rtems_isr timerisr(rtems_vector_number); + +/* + * This routine initializes the Tick Timer 1 on the MVME167 board. + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * NOTE: This routine may not work if the optimizer is enabled for some + * compilers. The multiple writes may be optimized away. + * + * It is important that the timer start/stop overhead be + * determined when porting or modifying this code. + * + * THE VMECHIP2 PRESCALER REGISTER IS ASSUMED TO BE SET! + * The prescaler is used by all VMEchip2 timers, including the VMEbus grant + * timeout counter, the DMAC time off timer, the DMAC timer on timer, and the + * VMEbus global timeout timer. The prescaler value is normally set by the + * boot ROM to provide a 1 MHz clock to the timers. For a 25 MHz MVME167, the + * prescaler value should be 0xE7 (page 2-63). + */ +void benchmark_timer_initialize(void) +{ + (void) set_vector( timerisr, TIMER_VECTOR, 0 ); + + Ttimer_val = 0; /* clear timer ISR count */ + lcsr->intr_ena &= 0xFEFFFFFF; /* disable tick timer 1 interrupt */ + lcsr->intr_clear |= 0x01000000; /* clear tick timer 1 interrupt */ + lcsr->intr_level[0] = /* set int level */ + (lcsr->intr_level[0] & 0xFFFFFFF0) | TIMER_INT_LEVEL; + lcsr->timer_cmp_1 = TICK_INTERVAL; /* period in compare register */ + lcsr->timer_cnt_1 = 0; /* clear tick timer 1 counter */ + lcsr->board_ctl |= 7; /* start tick timer 1, reset-on-compare, */ + /* and clear overflow counter */ + + lcsr->intr_ena |= 0x01000000; /* enable tick timer 1 interrupt */ + lcsr->vector_base |= MASK_INT; /* unmask VMEchip2 interrupts */ +} + +#define AVG_OVERHEAD 3UL /* It typically takes 3.0 microseconds */ + /* (3 countdowns) to start/stop the timer. */ +#define LEAST_VALID 3UL /* Don't trust a value lower than this */ + +/* + * This routine reads the Tick Timer 1 on the MVME167 board. + * + * Input parameters: NONE + * + * Output parameters: time in microseconds + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ +benchmark_timer_t benchmark_timer_read(void) +{ + uint32_t total; + + total = (Ttimer_val * TICK_INTERVAL) + lcsr->timer_cnt_1; + + if ( benchmark_timer_find_average_overhead ) + return total; /* in one microsecond units */ + + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + + return total - AVG_OVERHEAD; +} + +/* + * This routine sets the benchmark_timer_find_average_overhead flag in this + * module. + * + * Input parameters: NONE + * + * Output parameters: time in microseconds + */ +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/m68k/mvme167/btimer/timerisr.S b/bsps/m68k/mvme167/btimer/timerisr.S new file mode 100644 index 0000000000..d96f0996a5 --- /dev/null +++ b/bsps/m68k/mvme167/btimer/timerisr.S @@ -0,0 +1,50 @@ +/** + * @file + * + * This ISR is used to bump a count of interval "overflow" interrupts which + * have occurred since the timer was started. The number of overflows is taken + * into account in the benchmark_timer_read() routine. + */ + +/* + * COPYRIGHT (c) 1989-2014. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * Modifications of respective RTEMS file: COPYRIGHT (c) 1994. + * Copyright (c) 1998, National Research Council of Canada + */ + +#include <rtems/asm.h> + +BEGIN_CODE + +.set INTR_CLEAR_REG, 0xfff40074 | interrupt clear register +.set T1_CNTRL_REG, 0xfff40060 | tick timer 1 control register +.set CLEAR_INT, 0x01000000 | clear tick 1 interrupt +.set CLEAR_OVF, 0x00000004 | clear tick 1 overflow counter + + PUBLIC (Ttimer_val) + PUBLIC (timerisr) +SYM (timerisr): + move.l a0, -(a7) | save a0 + move.l d0, -(a7) | save d0 + move.w sr, -(a7) | save ccr + movea.l #INTR_CLEAR_REG, a0 | a0 = addr of intr clr reg + ori.l #CLEAR_INT, (a0) | clear tick timer 1 intr + movea.l #T1_CNTRL_REG, a0 | a0 = addr of t1 cntrl reg + move.l (a0), d0 | read overflow counter + lsr.l #4, d0 | put overflow in low order bits + andi.l #0xF, d0 | keep only overflow + add.l d0, SYM (Ttimer_val) | increment timer value + ori.l #CLEAR_OVF, (a0) | clear overflow counter + move.w (a7)+, sr | restore ccr + move.l (a7)+, d0 | restore d0 + move.l (a7)+, a0 | restore a0 + rte + +END_CODE +END diff --git a/bsps/m68k/uC5282/btimer/btimer.c b/bsps/m68k/uC5282/btimer/btimer.c new file mode 100644 index 0000000000..e01b9c8269 --- /dev/null +++ b/bsps/m68k/uC5282/btimer/btimer.c @@ -0,0 +1,47 @@ +/* + * Timer Init + * + * Use the last DMA timer (DTIM3) as the diagnostic timer. + * + * Author: W. Eric Norum <norume@aps.anl.gov> + * + * COPYRIGHT (c) 2005. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/btimer.h> + +void +benchmark_timer_initialize(void) +{ + int preScaleDivisor = bsp_get_CPU_clock_speed() / 1000000; + int div = MCF5282_TIMER_DTMR_CLK_DIV1; + + if (preScaleDivisor > 256) { + preScaleDivisor /= 16; + div = MCF5282_TIMER_DTMR_CLK_DIV16; + } + MCF5282_TIMER3_DTMR = 0; + MCF5282_TIMER3_DTMR = MCF5282_TIMER_DTMR_PS(preScaleDivisor - 1) | div | + MCF5282_TIMER_DTMR_RST; +} + +/* + * Return timer value in microsecond units + */ +uint32_t +benchmark_timer_read(void) +{ + return MCF5282_TIMER3_DTCN; +} + +void +benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ +} diff --git a/bsps/mips/csb350/btimer/btimer.c b/bsps/mips/csb350/btimer/btimer.c new file mode 100644 index 0000000000..30dbfe836b --- /dev/null +++ b/bsps/mips/csb350/btimer/btimer.c @@ -0,0 +1,57 @@ +/* + * This file implements a benchmark timer using the count/compare + * CP0 registers. + * + * Copyright (c) 2005 by Cogent Computer Systems + * Written by Jay Monkman <jtm@lopingdog.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <assert.h> + +#include <bsp.h> +#include <rtems/btimer.h> + +bool benchmark_timer_find_average_overhead; +uint32_t tstart; + +void benchmark_timer_initialize(void) +{ + __asm__ volatile ("mfc0 %0, $9\n" : "=r" (tstart)); + /* tick time in picooseconds */ +} + +#define AVG_OVERHEAD 0 /* It typically takes N instructions */ + /* to start/stop the timer. */ +#define LEAST_VALID 1 /* Don't trust a value lower than this */ + /* tx39 simulator can count instructions. :) */ + +benchmark_timer_t benchmark_timer_read(void) +{ + uint32_t total; + uint32_t cnt; + + __asm__ volatile ("mfc0 %0, $9\n" : "=r" (cnt)); + + total = cnt - tstart; + total = (total * 1000) / 396; /* convert to nanoseconds */ + + + if ( benchmark_timer_find_average_overhead == true ) + return total; /* in one microsecond units */ + + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + + return total - AVG_OVERHEAD; +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/mips/jmr3904/btimer/btimer.c b/bsps/mips/jmr3904/btimer/btimer.c new file mode 100644 index 0000000000..ca97cd3a0b --- /dev/null +++ b/bsps/mips/jmr3904/btimer/btimer.c @@ -0,0 +1,67 @@ +/* + * This file implements a benchmark timer using a TX39 timer. + * + * NOTE: On the simulator, the count directly reflects instructions. + * + * COPYRIGHT (c) 1989-2000. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <assert.h> + +#include <bsp.h> +#include <rtems/btimer.h> + +bool benchmark_timer_find_average_overhead; + +void benchmark_timer_initialize(void) +{ + /* + * Programming the compare register as the maximum value should let + * it run long enough and accurate enough not to require an interrupt. + * but if it ever does generate an interrupt, we will simply fault. + * + * NOTE: This is similar to the clock driver initialization + * with the exception that the divider is disabled and + * the compare register is set to the maximum value. + */ + + TX3904_TIMER_WRITE( TX3904_TIMER1_BASE, TX3904_TIMER_TCR, 0x20 ); + TX3904_TIMER_WRITE( TX3904_TIMER1_BASE, TX3904_TIMER_CCDR, 0x3 ); + TX3904_TIMER_WRITE( TX3904_TIMER1_BASE, TX3904_TIMER_TRR, 0x0 ); + TX3904_TIMER_WRITE( TX3904_TIMER1_BASE, TX3904_TIMER_CPRA, 0xFFFFFFFF ); + TX3904_TIMER_WRITE( TX3904_TIMER1_BASE, TX3904_TIMER_TISR, 0x00 ); + TX3904_TIMER_WRITE( TX3904_TIMER1_BASE, TX3904_TIMER_ITMR, 0x0001 ); + TX3904_TIMER_WRITE( TX3904_TIMER1_BASE, TX3904_TIMER_TCR, 0xe0 ); +} + +#define AVG_OVERHEAD 0 /* It typically takes N instructions */ + /* to start/stop the timer. */ +#define LEAST_VALID 1 /* Don't trust a value lower than this */ + /* tx39 simulator can count instructions. :) */ + +benchmark_timer_t benchmark_timer_read(void) +{ + uint32_t total; + + total = TX3904_TIMER_READ( TX3904_TIMER1_BASE, TX3904_TIMER_TRR ); + + if ( benchmark_timer_find_average_overhead == true ) + return total; /* in one microsecond units */ + + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + + return total - AVG_OVERHEAD; +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/nios2/nios2_iss/btimer/btimer.c b/bsps/nios2/nios2_iss/btimer/btimer.c new file mode 100644 index 0000000000..fbb20d892b --- /dev/null +++ b/bsps/nios2/nios2_iss/btimer/btimer.c @@ -0,0 +1,137 @@ +/* + * This file manages the benchmark timer used by the RTEMS Timing Test + * Suite. Each measured time period is demarcated by calls to + * benchmark_timer_initialize() and benchmark_timer_read(). + * benchmark_timer_read() usually returns the number of microseconds + * since benchmark_timer_initialize() exitted. + * + * NOTE: It is important that the timer start/stop overhead be + * determined when porting or modifying this code. + */ + +/* + * COPYRIGHT (c) 2005-2006 Kolja Waschk rtemsdev/ixo.de + * Derived from no_cpu/no_bsp/timer/timer.c 1.9, + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#define TIMER_WRAPS_AFTER_1MS 0 + +#include <rtems.h> +#include <rtems/btimer.h> +#include <rtems/score/cpu.h> +#include <bsp.h> + +volatile uint32_t Timer_interrupts; +bool benchmark_timer_find_average_overhead; + +#define TIMER_REGS ((altera_avalon_timer_regs*)NIOS2_IO_BASE(TIMER_BASE)) + +static rtems_isr timerisr(rtems_vector_number vector) +{ + TIMER_REGS->status = 0; + Timer_interrupts++; +} + +void benchmark_timer_initialize( void ) +{ + /* Disable timer interrupt, stop timer */ + + TIMER_REGS->control = ALTERA_AVALON_TIMER_CONTROL_STOP_MSK; + + set_vector(timerisr, TIMER_VECTOR, 1); + + /* Enable interrupt processing */ + + NIOS2_IENABLE(1 << TIMER_VECTOR); + +#if TIMER_WRAPS_AFTER_1MS + /* Writing to periodl/h resets the counter and eventually + stops it. If the timer hasn't been configured with fixed + period, set it to 1 ms now */ + + TIMER_REGS->period_hi = (TIMER_FREQ/1000)>>16; + TIMER_REGS->period_lo = (TIMER_FREQ/1000)&0xFFFF; +#else + /* Writing to periodl/h resets the counter and eventually + stops it. Set max period */ + + TIMER_REGS->period_hi = 0xFFFF; + TIMER_REGS->period_lo = 0xFFFF; +#endif + + /* For timers that can be stopped, writing to periodl/h + also stopped the timer and we have to manually start it. */ + + TIMER_REGS->control = ALTERA_AVALON_TIMER_CONTROL_ITO_MSK | + ALTERA_AVALON_TIMER_CONTROL_CONT_MSK | + ALTERA_AVALON_TIMER_CONTROL_START_MSK; + + /* This is the most safe place for resetting the overflow + counter - just _after_ we reset the timer. Depending + on the SOPC configuration, the counter may not be + stoppable and it doesn't make sense to assume that + there is any "safe" period before resetting. */ + + Timer_interrupts = 0; +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 2 /* It typically takes 2 microseconds */ + /* to start/stop the timer. */ + +#define LEAST_VALID AVG_OVERHEAD /* Don't trust a value lower than this */ + +benchmark_timer_t benchmark_timer_read( void ) +{ + uint32_t timer_wraps; + uint32_t timer_snap; + uint32_t timer_ticks; + uint32_t total; + + /* Hold timer */ + TIMER_REGS->control = ALTERA_AVALON_TIMER_CONTROL_STOP_MSK; + + /* Write to request snapshot of timer value */ + TIMER_REGS->snap_lo = 0; + /* Get snapshot */ + timer_snap = ((TIMER_REGS->snap_hi)<<16) | TIMER_REGS->snap_lo; + timer_wraps = Timer_interrupts; + + /* Restart timer */ + TIMER_REGS->control = ALTERA_AVALON_TIMER_CONTROL_START_MSK; + +#if TIMER_WRAPS_AFTER_1MS + timer_ticks = (TIMER_FREQ / 1000) - 1 - timer_snap; + total = timer_wraps * 1000; +#else + timer_ticks = 0xFFFFFFFF - timer_snap; + total = timer_wraps * 0x80000000 / (TIMER_FREQ / 2000000L); +#endif + total += timer_ticks / (TIMER_FREQ / 1000000L); + + if(total < LEAST_VALID) return 0; + + if(benchmark_timer_find_average_overhead != TRUE) total-= AVG_OVERHEAD; + + return total; +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/no_cpu/no_bsp/btimer/btimer.c b/bsps/no_cpu/no_bsp/btimer/btimer.c new file mode 100644 index 0000000000..e9b59dc756 --- /dev/null +++ b/bsps/no_cpu/no_bsp/btimer/btimer.c @@ -0,0 +1,92 @@ +/* timer.c + * + * This file manages the benchmark timer used by the RTEMS Timing Test + * Suite. Each measured time period is demarcated by calls to + * benchmark_timer_initialize() and benchmark_timer_read(). benchmark_timer_read() usually returns + * the number of microseconds since benchmark_timer_initialize() exitted. + * + * NOTE: It is important that the timer start/stop overhead be + * determined when porting or modifying this code. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <rtems/btimer.h> +#include <bsp.h> + +uint32_t Timer_interrupts; +bool benchmark_timer_find_average_overhead; + +void benchmark_timer_initialize( void ) +{ + + /* + * Timer has never overflowed. This may not be necessary on some + * implemenations of timer but .... + */ + + Timer_interrupts = 0; + + /* + * Somehow start the timer + */ +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 0 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 1 /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read( void ) +{ + uint32_t clicks; + uint32_t total; + + /* + * Read the timer and see how many clicks it has been since we started. + */ + + clicks = 0; /* XXX: read some HW here */ + + /* + * Total is calculated by taking into account the number of timer overflow + * interrupts since the timer was initialized and clicks since the last + * interrupts. + */ + + total = clicks * 0; + + if ( benchmark_timer_find_average_overhead == true ) + return total; /* in XXX microsecond units */ + else { + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + /* + * Somehow convert total into microseconds + */ + return (total - AVG_OVERHEAD); + } +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/no_cpu/no_bsp/btimer/timerisr.c b/bsps/no_cpu/no_bsp/btimer/timerisr.c new file mode 100644 index 0000000000..5ca7152a54 --- /dev/null +++ b/bsps/no_cpu/no_bsp/btimer/timerisr.c @@ -0,0 +1,38 @@ +/** + * @file + * + * If required this ISR is used to bump a count of interval "overflow" + * interrupts which have occurred since the timer was started. The + * number of overflows is taken into account in the benchmark_timer_read() + * routine if necessary. + * + * To reduce overhead this is best to be the "rawest" hardware interupt + * handler you can write. This should be the only interrupt which can + * occur during the measured time period. + * + * @note This file is USUALLY in assembly and is LEAN AND MEAN. + * Any code in this isr is pure overhead which can perturb + * the accuracy of the Timing Test Suite. + */ + +/* + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <rtems/btimer.h> + +extern uint32_t _Timer_interrupts; + +void timerisr( void ) +{ + /* + * _Timer_interrupts += TIMER_BETWEEN_OVERFLOWS (usually in microseconds) + * return from interrupt + */ +} diff --git a/bsps/or1k/generic_or1k/btimer/btimer.c b/bsps/or1k/generic_or1k/btimer/btimer.c new file mode 100644 index 0000000000..872f1af7be --- /dev/null +++ b/bsps/or1k/generic_or1k/btimer/btimer.c @@ -0,0 +1,64 @@ +/** + * @file + * + * @ingroup generic_or1k + * + * @brief Benchmark timer support. + */ + +/* + * Copyright (c) 2014-2015 by Hesham ALMatary + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE + */ + +#include <rtems.h> +#include <rtems/btimer.h> +#include <bsp/generic_or1k.h> +#include <rtems/score/or1k-utility.h> + +#define OR1K_NANOSECONDS_PER_CLK_CYCLE 10 + +static bool benchmark_timer_find_average_overhead = false; +static uint64_t benchmark_timer_base; + +void benchmark_timer_initialize(void) +{ + benchmark_timer_base = _OR1K_mfspr(CPU_OR1K_SPR_TTCR); +} + +#define AVG_OVERHEAD 0 +#define LEAST_VALID 1 + +benchmark_timer_t benchmark_timer_read( void ) +{ + uint64_t clicks; + uint64_t total; + uint64_t delta; + /* + * Read the timer and see how many clicks (clock cycles) + * has passed since timer initialization. + */ + clicks = _OR1K_mfspr(CPU_OR1K_SPR_TTCR); + + delta = clicks - benchmark_timer_base; + + /* total in nanoseconds */ + total = OR1K_NANOSECONDS_PER_CLK_CYCLE * (delta); + + if ( benchmark_timer_find_average_overhead == true ) + return total; /* in nanoseconds microsecond units */ + else { + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + + return (total - AVG_OVERHEAD); + } +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/powerpc/qoriq/btimer/btimer.c b/bsps/powerpc/qoriq/btimer/btimer.c new file mode 100644 index 0000000000..e8927c1194 --- /dev/null +++ b/bsps/powerpc/qoriq/btimer/btimer.c @@ -0,0 +1,42 @@ +/** + * @file + * + * @ingroup QorIQ + * + * @brief QorIQ benchmark timer. + */ + +/* + * Copyright (c) 2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems/btimer.h> + +#include <libcpu/powerpc-utility.h> + +static benchmark_timer_t benchmark_timer_base; + +void benchmark_timer_initialize(void) +{ + benchmark_timer_base = ppc_alternate_time_base(); +} + +benchmark_timer_t benchmark_timer_read(void) +{ + return ppc_alternate_time_base() - benchmark_timer_base; +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_average_overhead) +{ + /* VOID */ +} diff --git a/bsps/powerpc/tqm8xx/btimer/btimer.c b/bsps/powerpc/tqm8xx/btimer/btimer.c new file mode 100644 index 0000000000..06c43c56be --- /dev/null +++ b/bsps/powerpc/tqm8xx/btimer/btimer.c @@ -0,0 +1,101 @@ +/*===============================================================*\ +| Project: RTEMS TQM8xx BSP | ++-----------------------------------------------------------------+ +| This file has been adapted to MPC8xx by | +| Thomas Doerfler <Thomas.Doerfler@embedded-brains.de> | +| Copyright (c) 2008 | +| Embedded Brains GmbH | +| Obere Lagerstr. 30 | +| D-82178 Puchheim | +| Germany | +| rtems@embedded-brains.de | +| | +| See the other copyright notice below for the original parts. | ++-----------------------------------------------------------------+ +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| | +| http://www.rtems.org/license/LICENSE. | +| | ++-----------------------------------------------------------------+ +| this file contains the console driver | +\*===============================================================*/ +/* + * benchmark_timer_initialize() + * + * Use TIMER 1 and TIMER 2 for Timing Test Suite + * + * this is derived from "timer.c" available in the m68k/gen68360 BSP + * adapted by Thomas Doerfler <Thomas.Doerfler@embedded-brains.de> + */ + +/* + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * NOTE: It is important that the timer start/stop overhead be + * determined when porting or modifying this code. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/btimer.h> +#include <mpc8xx.h> + +bool benchmark_timer_find_average_overhead; + +void +benchmark_timer_initialize (void) +{ + /* + * Reset timers 1 and 2 + */ + m8xx.tgcr &= ~0x00FF; + m8xx.tcn1 = 0; + m8xx.tcn2 = 0; + m8xx.ter1 = 0xFFFF; + m8xx.ter2 = 0xFFFF; + + /* + * Cascade timers 1 and 2 + */ + m8xx.tgcr |= M8xx_TGCR_CAS2; + + /* + * Configure timers 1 and 2 to a single 32-bit, BUS_clock timer. + */ + m8xx.tmr2 = (0 << 8) | 0x2; + m8xx.tmr1 = 0; + + /* + * Start the timers + */ + m8xx.tgcr |= 0x0011; +} + +/* + * Return timer value in microsecond units + */ +benchmark_timer_t benchmark_timer_read(void) +{ + uint32_t retval; + retval = *(uint32_t*)&m8xx.tcn1; + retval = retval * 1000000LL / BSP_bus_frequency; + return retval; +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/riscv/riscv_generic/btimer/btimer.c b/bsps/riscv/riscv_generic/btimer/btimer.c new file mode 100644 index 0000000000..4dd3193685 --- /dev/null +++ b/bsps/riscv/riscv_generic/btimer/btimer.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015 University of York. + * Hesham Almatary <hesham@alumni.york.ac.uk> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/btimer.h> +#include <rtems/score/riscv-utility.h> + +extern char bsp_start_vector_table_begin[]; + +bool benchmark_timer_find_average_overhead; + +static void benchmark_timer1_interrupt_handler(void) +{ +} + +/* Start eCore tiemr 1 usef for profiling and timing analysis */ +void benchmark_timer_initialize( void ) +{ + /* Install interrupt handler for timer 1 */ +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 0 /* It typically takes X.X microseconds */ +/* (Y countdowns) to start/stop the timer. */ +/* This value is in microseconds. */ +#define LEAST_VALID 1 /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read( void ) +{ +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/sh/gensh1/btimer/btimer.c b/bsps/sh/gensh1/btimer/btimer.c new file mode 100644 index 0000000000..94a834cb1e --- /dev/null +++ b/bsps/sh/gensh1/btimer/btimer.c @@ -0,0 +1,195 @@ +/** + * @file + * @brief Timer for the Hitachi SH 703X + */ + +/* + * Authors: Ralf Corsepius (corsepiu@faw.uni-ulm.de) and + * Bernd Becker (becker@faw.uni-ulm.de) + * + * COPYRIGHT (c) 1997-1998, FAW Ulm, Germany + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * COPYRIGHT (c) 1998. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <rtems/btimer.h> + +#include <rtems/score/sh_io.h> +#include <rtems/score/ispsh7032.h> +#include <rtems/score/iosh7032.h> + +extern uint32_t bsp_clicks_per_second; + +#define I_CLK_PHI_1 0 +#define I_CLK_PHI_2 1 +#define I_CLK_PHI_4 2 +#define I_CLK_PHI_8 3 + +/* + * Set I_CLK_PHI to one of the I_CLK_PHI_X values from above to choose + * a PHI/X clock rate. + */ + +#define I_CLK_PHI I_CLK_PHI_4 +#define CLOCK_SCALE (1<<I_CLK_PHI) + +#define ITU1_STARTMASK 0xfd +#define ITU1_SYNCMASK 0xfd +#define ITU1_MODEMASK 0xfd +#define ITU1_TCRMASK (0x00 | I_CLK_PHI) +#define ITU1_TIORMASK 0x88 +#define ITU1_STAT_MASK 0xf8 +#define ITU1_TIERMASK 0xfc +#define IPRC_ITU1_MASK 0xfff0 + +#ifndef ITU1_PRIO +#define ITU1_PRIO 15 +#endif + +#define ITU1_VECTOR OVI1_ISP_V + +extern rtems_isr timerisr(void); + +static uint32_t Timer_interrupts; + +bool benchmark_timer_find_average_overhead; + +static uint32_t Timer_HZ ; + +void benchmark_timer_initialize( void ) +{ + uint8_t temp8; + uint16_t temp16; + rtems_interrupt_level level; + rtems_isr *ignored; + + Timer_HZ = bsp_clicks_per_second / CLOCK_SCALE ; + + /* + * Timer has never overflowed. This may not be necessary on some + * implemenations of timer but .... + */ + + Timer_interrupts /* .i */ = 0; + rtems_interrupt_disable( level ); + + /* + * Somehow start the timer + */ + /* stop Timer 1 */ + temp8 = read8(ITU_TSTR) & ITU1_STARTMASK; + write8( temp8, ITU_TSTR ); + + /* initialize counter 1 */ + write16( 0, ITU_TCNT1 ); + + /* Timer 1 is independent of other timers */ + temp8 = read8(ITU_TSNC) & ITU1_SYNCMASK; + write8( temp8, ITU_TSNC ); + + /* Timer 1, normal mode */ + temp8 = read8(ITU_TMDR) & ITU1_MODEMASK; + write8( temp8, ITU_TMDR ); + + /* Use a Phi/X counter */ + write8( ITU1_TCRMASK, ITU_TCR1 ); + + /* gra and grb are not used */ + write8( ITU1_TIORMASK, ITU_TIOR1 ); + + /* reset all status flags */ + temp8 = read8(ITU_TSR1) & ITU1_STAT_MASK; + write8( temp8, ITU_TSR1 ); + + /* enable overflow interrupt */ + write8( ITU1_TIERMASK, ITU_TIER1 ); + + /* set interrupt priority */ + temp16 = read16(INTC_IPRC) & IPRC_ITU1_MASK; + temp16 |= ITU1_PRIO; + write16( temp16, INTC_IPRC ); + + /* initialize ISR */ + _CPU_ISR_install_raw_handler( ITU1_VECTOR, timerisr, &ignored ); + rtems_interrupt_enable( level ); + + /* start timer 1 */ + temp8 = read8(ITU_TSTR) | ~ITU1_STARTMASK; + write8( temp8, ITU_TSTR ); +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVERHEAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 1 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 0 /* 20 */ /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read( void ) +{ + uint32_t cclicks; + uint32_t total ; + /* + * Read the timer and see how many clicks it has been since we started. + */ + + + cclicks = read16( ITU_TCNT1 ); /* XXX: read some HW here */ + + /* + * Total is calculated by taking into account the number of timer overflow + * interrupts since the timer was initialized and clicks since the last + * interrupts. + */ + + total = cclicks + Timer_interrupts * 65536; + + if ( benchmark_timer_find_average_overhead ) + return total / CLOCK_SCALE; /* in XXX microsecond units */ + else + { + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + /* + * Somehow convert total into microseconds + */ + return (total / CLOCK_SCALE - AVG_OVERHEAD); + } +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ + benchmark_timer_find_average_overhead = find_flag; +} + +/* Timer 1 is used */ + +#pragma interrupt +void timerisr( void ) +{ + uint8_t temp8; + + /* reset the flags of the status register */ + temp8 = read8(ITU_TSR1) & ITU1_STAT_MASK; + write8( temp8, ITU_TSR1 ); + + Timer_interrupts += 1; +} diff --git a/bsps/sh/gensh2/btimer/btimer.c b/bsps/sh/gensh2/btimer/btimer.c new file mode 100644 index 0000000000..152c99b1aa --- /dev/null +++ b/bsps/sh/gensh2/btimer/btimer.c @@ -0,0 +1,191 @@ +/** + * @file + * @brief Timer for the Hitachi SH 704X + */ + +/* + * Authors: Ralf Corsepius (corsepiu@faw.uni-ulm.de) and + * Bernd Becker (becker@faw.uni-ulm.de) + * + * COPYRIGHT (c) 1997-1998, FAW Ulm, Germany + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * COPYRIGHT (c) 1998. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <rtems/btimer.h> + +#include <rtems/score/sh_io.h> +#include <rtems/score/iosh7045.h> + +extern uint32_t bsp_clicks_per_second; + +/* + * We use a Phi/4 timer + */ +#define SCALE (Timer_MHZ/4) + +#define MTU1_STARTMASK 0xfd +#define MTU1_SYNCMASK 0xfd +#define MTU1_MODEMASK 0xc0 +#define MTU1_TCRMASK 0x01 +#define MTU1_TIORMASK 0x88 +#define MTU1_STAT_MASK 0xf8 +#define MTU1_TIERMASK 0xfc +#define IPRC_MTU1_MASK 0xfff0 + +#ifndef MTU1_PRIO +#define MTU1_PRIO 15 +#endif + +#define MTU1_VECTOR 86 + +extern rtems_isr timerisr(void); + +static uint32_t Timer_interrupts; + +bool benchmark_timer_find_average_overhead; + +static uint32_t Timer_MHZ ; + +void benchmark_timer_initialize( void ) +{ + uint8_t temp8; + uint16_t temp16; + rtems_interrupt_level level; + rtems_isr *ignored; + + Timer_MHZ = bsp_clicks_per_second / 1000000 ; + + /* + * Timer has never overflowed. This may not be necessary on some + * implemenations of timer but .... + */ + + Timer_interrupts /* .i */ = 0; + rtems_interrupt_disable( level ); + + /* + * Somehow start the timer + */ + /* stop Timer 1 */ + temp8 = read8(MTU_TSTR) & MTU1_STARTMASK; + write8( temp8, MTU_TSTR ); + + /* initialize counter 1 */ + write16( 0, MTU_TCNT1); + + /* Timer 1 is independent of other timers */ + temp8 = read8(MTU_TSYR) & MTU1_SYNCMASK; + write8( temp8, MTU_TSYR ); + + /* Timer 1, normal mode */ + temp8 = read8(MTU_TMDR1) & MTU1_MODEMASK; + write8( temp8, MTU_TMDR1 ); + + /* x0000000 + * |||||+++--- Internal Clock + * |||++------ Count on rising edge + * |++-------- disable TCNT clear + * +---------- don`t care + */ + write8( MTU1_TCRMASK, MTU_TCR1 ); + + /* gra and grb are not used */ + write8( MTU1_TIORMASK, MTU_TIOR1 ); + + /* reset all status flags */ + temp8 = read8(MTU_TSR1) & MTU1_STAT_MASK; + write8( temp8, MTU_TSR1 ); + + /* enable overflow interrupt */ + write8( MTU1_TIERMASK, MTU_TIER1 ); + + /* set interrupt priority */ + temp16 = read16(INTC_IPRC) & IPRC_MTU1_MASK; + temp16 |= MTU1_PRIO; + write16( temp16, INTC_IPRC); + + /* initialize ISR */ + _CPU_ISR_install_raw_handler( MTU1_VECTOR, timerisr, &ignored ); + rtems_interrupt_enable( level ); + + /* start timer 1 */ + temp8 = read8(MTU_TSTR) | ~MTU1_STARTMASK; + write8( temp8, MTU_TSTR ); +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVERHEAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 1 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 0 /* 20 */ /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read( void ) +{ + uint32_t clicks; + uint32_t total ; + /* + * Read the timer and see how many clicks it has been since we started. + */ + + + clicks = read16( MTU_TCNT1 ); /* XXX: read some HW here */ + + /* + * Total is calculated by taking into account the number of timer overflow + * interrupts since the timer was initialized and clicks since the last + * interrupts. + */ + + total = clicks + Timer_interrupts * 65536; + + if ( benchmark_timer_find_average_overhead ) + return total / SCALE; /* in XXX microsecond units */ + else + { + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + /* + * Somehow convert total into microseconds + */ + return (total / SCALE - AVG_OVERHEAD) ; + } +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ + benchmark_timer_find_average_overhead = find_flag; +} + +/* Timer 1 is used */ + +#pragma interrupt +void timerisr( void ) +{ + uint8_t temp8; + + /* reset the flags of the status register */ + temp8 = read8(MTU_TSR1) & MTU1_STAT_MASK; + write8( temp8, MTU_TSR1 ); + + Timer_interrupts += 1; +} diff --git a/bsps/sh/gensh4/btimer/btimer.c b/bsps/sh/gensh4/btimer/btimer.c new file mode 100644 index 0000000000..ef462c780c --- /dev/null +++ b/bsps/sh/gensh4/btimer/btimer.c @@ -0,0 +1,269 @@ +/** + * @file + * @brief Timer driver for the Hitachi SH 7750 + */ + +/* + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Victor V. Vengerov <vvv@oktet.ru> + * + * COPYRIGHT (c) 1998. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <rtems/btimer.h> + +#include <rtems/score/sh_io.h> +#include <rtems/score/iosh7750.h> + +extern uint32_t bsp_clicks_per_second; + +#ifndef TIMER_PRIO +#define TIMER_PRIO 15 +#endif + +/* Timer prescaler division ratio */ +#define TIMER_PRESCALER 4 +#define TCR1_TPSC SH7750_TCR_TPSC_DIV4 + +#define TIMER_VECTOR SH7750_EVT_TO_NUM(SH7750_EVT_TUNI1) + +extern rtems_isr timerisr(void); + +static uint32_t Timer_interrupts; + +/* Counter should be divided to this value to obtain time in microseconds */ +static uint32_t microseconds_divider; + +/* Interrupt period in microseconds */ +static uint32_t microseconds_per_int; + +bool benchmark_timer_find_average_overhead; + +/* benchmark_timer_initialize -- + * Initialize Timer 1 to operate as a RTEMS benchmark timer: + * - determine timer clock frequency + * - install timer interrupt handler + * - configure the Timer 1 hardware + * - start the timer + * + * PARAMETERS: + * none + * + * RETURNS: + * none + */ +void +benchmark_timer_initialize(void) +{ + uint8_t temp8; + uint16_t temp16; + rtems_interrupt_level level; + rtems_isr *ignored; + int cpudiv = 1; + int tidiv = 1; + + Timer_interrupts = 0; + rtems_interrupt_disable(level); + + /* Get CPU frequency divider from clock unit */ + switch (read16(SH7750_FRQCR) & SH7750_FRQCR_IFC) + { + case SH7750_FRQCR_IFCDIV1: + cpudiv = 1; + break; + + case SH7750_FRQCR_IFCDIV2: + cpudiv = 2; + break; + + case SH7750_FRQCR_IFCDIV3: + cpudiv = 3; + break; + + case SH7750_FRQCR_IFCDIV4: + cpudiv = 4; + break; + + case SH7750_FRQCR_IFCDIV6: + cpudiv = 6; + break; + + case SH7750_FRQCR_IFCDIV8: + cpudiv = 8; + break; + + default: + rtems_fatal_error_occurred( RTEMS_NOT_CONFIGURED); + } + + /* Get peripheral module frequency divider from clock unit */ + switch (read16(SH7750_FRQCR) & SH7750_FRQCR_PFC) + { + case SH7750_FRQCR_PFCDIV2: + tidiv = 2 * TIMER_PRESCALER; + break; + + case SH7750_FRQCR_PFCDIV3: + tidiv = 3 * TIMER_PRESCALER; + break; + + case SH7750_FRQCR_PFCDIV4: + tidiv = 4 * TIMER_PRESCALER; + break; + + case SH7750_FRQCR_PFCDIV6: + tidiv = 6 * TIMER_PRESCALER; + break; + + case SH7750_FRQCR_PFCDIV8: + tidiv = 8 * TIMER_PRESCALER; + break; + + default: + rtems_fatal_error_occurred( RTEMS_NOT_CONFIGURED); + } + + microseconds_divider = bsp_clicks_per_second * cpudiv / (tidiv * 1000000); + microseconds_per_int = 0xFFFFFFFF / microseconds_divider; + + /* + * Hardware specific initialization + */ + + /* Stop the Timer 0 */ + temp8 = read8(SH7750_TSTR); + temp8 &= ~SH7750_TSTR_STR1; + write8(temp8, SH7750_TSTR); + + /* Establish interrupt handler */ + _CPU_ISR_install_raw_handler( TIMER_VECTOR, timerisr, &ignored ); + + /* Reset timer constant and counter */ + write32(0xFFFFFFFF, SH7750_TCOR1); + write32(0xFFFFFFFF, SH7750_TCNT1); + + /* Select timer mode */ + write16( + SH7750_TCR_UNIE | /* Enable Underflow Interrupt */ + SH7750_TCR_CKEG_RAISE | /* Count on rising edge */ + TCR1_TPSC, /* Timer prescaler ratio */ + SH7750_TCR1); + + /* Set timer interrupt priority */ + temp16 = read16(SH7750_IPRA); + temp16 = (temp16 & ~SH7750_IPRA_TMU1) | (TIMER_PRIO << SH7750_IPRA_TMU1_S); + write16(temp16, SH7750_IPRA); + + + rtems_interrupt_enable(level); + + /* Start the Timer 1 */ + temp8 = read8(SH7750_TSTR); + temp8 |= SH7750_TSTR_STR1; + write8(temp8, SH7750_TSTR); + +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVERHEAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 0 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 0 /* 20 */ /* Don't trust a clicks value lower than this */ + +/* benchmark_timer_read -- + * Read timer value in microsecond units since timer start. + * + * PARAMETERS: + * none + * + * RETURNS: + * number of microseconds since timer has been started + */ +benchmark_timer_t +benchmark_timer_read(void) +{ + uint32_t clicks; + uint32_t ints; + uint32_t total; + rtems_interrupt_level level; + uint32_t tcr; + + + rtems_interrupt_disable(level); + + clicks = 0xFFFFFFFF - read32(SH7750_TCNT1); + tcr = read32(SH7750_TCR1); + ints = Timer_interrupts; + + rtems_interrupt_enable(level); + + /* Handle the case when timer overflowed but interrupt was not processed */ + if ((clicks > 0xFF000000) && ((tcr & SH7750_TCR_UNF) != 0)) + { + ints++; + } + + total = microseconds_per_int * ints + (clicks / microseconds_divider); + + if ( benchmark_timer_find_average_overhead ) + return total; /* in microsecond units */ + else + { + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + /* + * Somehow convert total into microseconds + */ + return (total - AVG_OVERHEAD) ; + } +} + +/* benchmark_timer_disable_subtracting_average_overhead -- + * This routine is invoked by the "Check Timer" (tmck) test in the + * RTEMS Timing Test Suite. It makes the benchmark_timer_read routine not + * subtract the overhead required to initialize and read the benchmark + * timer. + * + * PARAMETERS: + * find_flag - boolean flag, true if overhead must not be subtracted. + * + * RETURNS: + * none + */ +void +benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ + benchmark_timer_find_average_overhead = find_flag; +} + +/* timerisr -- + * Timer interrupt handler routine. This function invoked on timer + * underflow event; once per 2^32 clocks. It should reset the timer + * event and increment timer interrupts counter. + */ +void +timerisr(void) +{ + uint8_t temp8; + + /* reset the flags of the status register */ + temp8 = read8(SH7750_TCR1) & ~SH7750_TCR_UNF; + write8(temp8, SH7750_TCR1); + + Timer_interrupts += 1; +} diff --git a/bsps/sparc/erc32/btimer/btimer.c b/bsps/sparc/erc32/btimer/btimer.c new file mode 100644 index 0000000000..05728f8acc --- /dev/null +++ b/bsps/sparc/erc32/btimer/btimer.c @@ -0,0 +1,81 @@ +/* timer.c + * + * This file implements a benchmark timer using the General Purpose Timer on + * the MEC. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * Ported to ERC32 implementation of the SPARC by On-Line Applications + * Research Corporation (OAR) under contract to the European Space + * Agency (ESA). + * + * ERC32 modifications of respective RTEMS file: COPYRIGHT (c) 1995. + * European Space Agency. + */ + +#include <bsp.h> +#include <rtems/btimer.h> + +bool benchmark_timer_find_average_overhead; + +bool benchmark_timer_is_initialized = false; + +void benchmark_timer_initialize(void) +{ + /* + * Timer runs long and accurate enough not to require an interrupt. + */ + + if ( benchmark_timer_is_initialized == false ) { + + /* approximately 1 us per countdown */ + ERC32_MEC.General_Purpose_Timer_Scalar = CLOCK_SPEED - 1; + ERC32_MEC.General_Purpose_Timer_Counter = 0xffffffff; + + } else { + benchmark_timer_is_initialized = true; + } + + ERC32_MEC_Set_General_Purpose_Timer_Control( + ERC32_MEC_TIMER_COUNTER_ENABLE_COUNTING | + ERC32_MEC_TIMER_COUNTER_LOAD_COUNTER + ); + + ERC32_MEC_Set_General_Purpose_Timer_Control( + ERC32_MEC_TIMER_COUNTER_ENABLE_COUNTING + ); + +} + +#define AVG_OVERHEAD 12 /* It typically takes 3.0 microseconds */ + /* to start/stop the timer. */ +#define LEAST_VALID 13 /* Don't trust a value lower than this */ + +benchmark_timer_t benchmark_timer_read(void) +{ + uint32_t total; + + total = ERC32_MEC.General_Purpose_Timer_Counter; + + total = 0xffffffff - total; + + if ( benchmark_timer_find_average_overhead == true ) + return total; /* in one microsecond units */ + + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + + return total - AVG_OVERHEAD; +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/sparc/leon2/btimer/btimer.c b/bsps/sparc/leon2/btimer/btimer.c new file mode 100644 index 0000000000..de115c965a --- /dev/null +++ b/bsps/sparc/leon2/btimer/btimer.c @@ -0,0 +1,83 @@ +/** + * @file + * @ingroup sparc_leon2 + * @brief Implement a benchmark timer using timer 2 + */ + +/* timer.c + * + * This file implements a benchmark timer using timer 2. + * + * COPYRIGHT (c) 1989-1998. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * Ported to LEON implementation of the SPARC by On-Line Applications + * Research Corporation (OAR) under contract to the European Space + * Agency (ESA). + * + * LEON modifications of respective RTEMS file: COPYRIGHT (c) 1995. + * European Space Agency. + */ + + +#include <bsp.h> +#include <rtems/btimer.h> + +bool benchmark_timer_find_average_overhead; + +bool benchmark_timer_is_initialized = false; + +void benchmark_timer_initialize(void) +{ + /* + * Timer runs long and accurate enough not to require an interrupt. + */ + + if ( benchmark_timer_is_initialized == false ) { + + /* approximately 1 us per countdown */ + LEON_REG.Timer_Counter_2 = 0xffffff; + LEON_REG.Timer_Reload_2 = 0xffffff; + + } else { + benchmark_timer_is_initialized = true; + } + + LEON_REG.Timer_Control_2 = ( + LEON_REG_TIMER_COUNTER_ENABLE_COUNTING | + LEON_REG_TIMER_COUNTER_LOAD_COUNTER + ); + +} + +#define AVG_OVERHEAD 3 /* It typically takes 3.0 microseconds */ + /* to start/stop the timer. */ +#define LEAST_VALID 2 /* Don't trust a value lower than this */ + +benchmark_timer_t benchmark_timer_read(void) +{ + uint32_t total; + + total = LEON_REG.Timer_Counter_2; + + total = 0xffffff - total; + + if ( benchmark_timer_find_average_overhead == true ) + return total; /* in one microsecond units */ + + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + + return total - AVG_OVERHEAD; +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/sparc/leon3/btimer/btimer.c b/bsps/sparc/leon3/btimer/btimer.c new file mode 100644 index 0000000000..40bdeb87bb --- /dev/null +++ b/bsps/sparc/leon3/btimer/btimer.c @@ -0,0 +1,85 @@ +/* timer.c + * + * This file implements a benchmark timer using timer 2. + * + * COPYRIGHT (c) 1989-1998. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * Ported to LEON implementation of the SPARC by On-Line Applications + * Research Corporation (OAR) under contract to the European Space + * Agency (ESA). + * + * LEON modifications of respective RTEMS file: COPYRIGHT (c) 1995. + * European Space Agency. + */ + + +#include <bsp.h> +#include <rtems/btimer.h> + +#if defined(RTEMS_MULTIPROCESSING) + #define LEON3_TIMER_INDEX \ + ((rtems_configuration_get_user_multiprocessing_table()) ? \ + (rtems_configuration_get_user_multiprocessing_table()->node) - 1 : 1) +#else + #define LEON3_TIMER_INDEX 0 +#endif + +bool benchmark_timer_find_average_overhead; + +bool benchmark_timer_is_initialized = false; + +extern volatile struct gptimer_regs *LEON3_Timer_Regs; + +void benchmark_timer_initialize(void) +{ + /* + * Timer runs long and accurate enough not to require an interrupt. + */ + if (LEON3_Timer_Regs) { + if ( benchmark_timer_is_initialized == false ) { + /* approximately 1 us per countdown */ + LEON3_Timer_Regs->timer[LEON3_TIMER_INDEX].reload = 0xffffff; + LEON3_Timer_Regs->timer[LEON3_TIMER_INDEX].value = 0xffffff; + } else { + benchmark_timer_is_initialized = true; + } + LEON3_Timer_Regs->timer[LEON3_TIMER_INDEX].ctrl = + GPTIMER_TIMER_CTRL_EN | GPTIMER_TIMER_CTRL_LD; + } +} + +#define AVG_OVERHEAD 3 /* It typically takes 3.0 microseconds */ + /* to start/stop the timer. */ +#define LEAST_VALID 2 /* Don't trust a value lower than this */ + +benchmark_timer_t benchmark_timer_read(void) +{ + uint32_t total; + + if (LEON3_Timer_Regs) { + total = LEON3_Timer_Regs->timer[LEON3_TIMER_INDEX].value; + + total = 0xffffff - total; + + if ( benchmark_timer_find_average_overhead == true ) + return total; /* in one microsecond units */ + + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + + return total - AVG_OVERHEAD; + } + return 0; +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/sparc/leon3/btimer/watchdog.c b/bsps/sparc/leon3/btimer/watchdog.c new file mode 100644 index 0000000000..5ce8e32354 --- /dev/null +++ b/bsps/sparc/leon3/btimer/watchdog.c @@ -0,0 +1,90 @@ +/* GPTIMER Watchdog timer routines. On some systems the first GPTIMER + * core's last Timer instance underflow signal is connected to system + * reset. + * + * COPYRIGHT (c) 2012. + * Cobham Gaisler AB. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <bsp.h> +#include <bsp/watchdog.h> +#include <grlib.h> + +extern volatile struct gptimer_regs *LEON3_Timer_Regs; + +struct gptimer_watchdog_priv { + struct gptimer_regs *regs; + struct gptimer_timer_regs *timer; + int timerno; +}; + +struct gptimer_watchdog_priv bsp_watchdogs[1]; +int bsp_watchdog_count = 0; + +int bsp_watchdog_init(void) +{ + int timercnt; + + if (!LEON3_Timer_Regs) + return 0; + + /* Get Watchdogs in system, this is implemented for one GPTIMER core + * only. + * + * First watchdog is a special case, we can get the first timer core by + * looking at LEON3_Timer_Regs, the watchdog within a timer core is + * always the last timer. Unfortunately we can not know it the watchdog + * functionality is available or not, we assume that it is if we + * reached this function. + */ + bsp_watchdogs[0].regs = (struct gptimer_regs *)LEON3_Timer_Regs; + + /* Find Timer that has watchdog functionality */ + timercnt = bsp_watchdogs[0].regs->cfg & 0x7; + if (timercnt < 2) /* First timer system clock timer */ + return 0; + + bsp_watchdogs[0].timerno = timercnt - 1; + bsp_watchdogs[0].timer = &bsp_watchdogs[0].regs->timer[bsp_watchdogs[0].timerno]; + + bsp_watchdog_count = 1; + return bsp_watchdog_count; +} + +void bsp_watchdog_reload(int watchdog, unsigned int reload_value) +{ + if (bsp_watchdog_count == 0) + bsp_watchdog_init(); + + if (bsp_watchdog_count <= watchdog) + return; + + /* Kick watchdog, and clear interrupt pending bit */ + bsp_watchdogs[watchdog].timer->reload = reload_value; + bsp_watchdogs[watchdog].timer->ctrl = + (GPTIMER_TIMER_CTRL_LD | GPTIMER_TIMER_CTRL_EN) | + (bsp_watchdogs[watchdog].timer->ctrl & ~(1<<4)); +} + +void bsp_watchdog_stop(int watchdog) +{ + if (bsp_watchdog_count == 0) + bsp_watchdog_init(); + + if (bsp_watchdog_count <= watchdog) + return; + + /* Stop watchdog timer */ + bsp_watchdogs[watchdog].timer->ctrl = 0; +} + +/* Use watchdog timer to reset system */ +void bsp_watchdog_system_reset(void) +{ + sparc_disable_interrupts(); + bsp_watchdog_reload(0, 1); +} diff --git a/bsps/sparc/shared/btimer/gptimer.c b/bsps/sparc/shared/btimer/gptimer.c new file mode 100644 index 0000000000..08e498178e --- /dev/null +++ b/bsps/sparc/shared/btimer/gptimer.c @@ -0,0 +1,544 @@ +/* This file contains the driver for the GRLIB GPTIMER timers port. The driver + * is implemented by using the tlib.c simple timer layer and the Driver + * Manager. + * + * The Driver can be configured using driver resources: + * + * - timerStart Timer Index if first Timer, this parameters is typically used + * in AMP systems for resource allocation. The Timers before + * timerStart will not be accessed. + * - timerCnt Number of timers that the driver will use, this parameters is + * typically used in AMP systems for resource allocation between + * OS instances. + * - prescaler Base prescaler, normally set by bootloader but can be + * overridden. The default scaler reload value set by bootloader + * is so that Timers operate in 1MHz. Setting the prescaler to a + * lower value increase the accuracy of the timers but shortens + * the time until underflow happens. + * - clockTimer Used to select a particular timer to be the system clock + * timer. This is useful when multiple GPTIMERs cores are + * available, or in AMP systems. By default the TLIB selects the + * first timer registered as system clock timer. + * + * The BSP define APBUART_INFO_AVAIL in order to add the info routine + * used for debugging. + * + * COPYRIGHT (c) 2010. + * Cobham Gaisler AB. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <bsp.h> +#include <stdlib.h> +#include <string.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <grlib.h> +#include <bsp/gptimer.h> +#include <bsp/tlib.h> + +#if defined(LEON3) +#include <leon.h> +#endif + +#ifdef GPTIMER_INFO_AVAIL +#include <stdio.h> +#endif + +#ifdef RTEMS_SMP +#include <rtems/score/processormask.h> +#include <rtems/score/smpimpl.h> +#endif + +/* GPTIMER Core Configuration Register (READ-ONLY) */ +#define GPTIMER_CFG_TIMERS_BIT 0 +#define GPTIMER_CFG_IRQ_BIT 3 +#define GPTIMER_CFG_SI_BIT 8 +#define GPTIMER_CFG_DF_BIT 9 + +#define GPTIMER_CFG_TIMERS (0x7<<GPTIMER_CFG_TIMERS_BIT) +#define GPTIMER_CFG_IRQ (0x1f<<GPTIMER_CFG_IRQ_BIT) +#define GPTIMER_CFG_SI (1<<GPTIMER_CFG_SI_BIT) +#define GPTIMER_CFG_DF (1<<GPTIMER_CFG_DF_BIT) + +/* GPTIMER Timer Control Register */ +#define GPTIMER_CTRL_EN_BIT 0 +#define GPTIMER_CTRL_RS_BIT 1 +#define GPTIMER_CTRL_LD_BIT 2 +#define GPTIMER_CTRL_IE_BIT 3 +#define GPTIMER_CTRL_IP_BIT 4 +#define GPTIMER_CTRL_CH_BIT 5 +#define GPTIMER_CTRL_DH_BIT 6 + +#define GPTIMER_CTRL_EN (1<<GPTIMER_CTRL_EN_BIT) +#define GPTIMER_CTRL_RS (1<<GPTIMER_CTRL_RS_BIT) +#define GPTIMER_CTRL_LD (1<<GPTIMER_CTRL_LD_BIT) +#define GPTIMER_CTRL_IE (1<<GPTIMER_CTRL_IE_BIT) +#define GPTIMER_CTRL_IP (1<<GPTIMER_CTRL_IP_BIT) +#define GPTIMER_CTRL_CH (1<<GPTIMER_CTRL_CH_BIT) +#define GPTIMER_CTRL_DH (1<<GPTIMER_CTRL_DH_BIT) + +#define DBG(x...) + +/* GPTIMER timer private */ +struct gptimer_timer { + struct tlib_dev tdev; /* Must be first in struct */ + struct gptimer_timer_regs *tregs; + char index; /* Timer Index in this driver */ + char tindex; /* Timer Index In Hardware */ + unsigned char irq_ack_mask; +}; + +/* GPTIMER Core private */ +struct gptimer_priv { + struct drvmgr_dev *dev; + struct gptimer_regs *regs; + unsigned int base_clk; + unsigned int base_freq; + unsigned int widthmask; + char separate_interrupt; + char isr_installed; + + /* Structure per Timer unit, the core supports up to 8 timers */ + int timer_cnt; + struct gptimer_timer timers[0]; +}; + +void gptimer_isr(void *data); + +#if 0 +void gptimer_tlib_irq_register(struct tlib_drv *tdrv, tlib_isr_t func, void *data) +{ + struct gptimer_priv *priv = (struct gptimer_priv *)tdrv; + + if ( SHARED ...) + + + drvmgr_interrupt_register(); +} +#endif + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +static struct tlib_drv gptimer_tlib_drv; +int gptimer_device_init(struct gptimer_priv *priv); + +int gptimer_init1(struct drvmgr_dev *dev); +#ifdef GPTIMER_INFO_AVAIL +static int gptimer_info( + struct drvmgr_dev *dev, + void (*print_line)(void *p, char *str), + void *p, int, char *argv[]); +#define GTIMER_INFO_FUNC gptimer_info +#else +#define GTIMER_INFO_FUNC NULL +#endif + +struct drvmgr_drv_ops gptimer_ops = +{ + .init = {gptimer_init1, NULL, NULL, NULL}, + .remove = NULL, + .info = GTIMER_INFO_FUNC, +}; + +struct amba_dev_id gptimer_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GPTIMER}, + {VENDOR_GAISLER, GAISLER_GRTIMER}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info gptimer_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GPTIMER_ID,/* Driver ID */ + "GPTIMER_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &gptimer_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &gptimer_ids[0] +}; + +void gptimer_register_drv (void) +{ + DBG("Registering GPTIMER driver\n"); + drvmgr_drv_register(&gptimer_drv_info.general); +} + +int gptimer_init1(struct drvmgr_dev *dev) +{ + struct gptimer_priv *priv; + struct gptimer_regs *regs; + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + int timer_hw_cnt, timer_cnt, timer_start; + int i, size; + struct gptimer_timer *timer; + union drvmgr_key_value *value; + unsigned char irq_ack_mask; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + regs = (struct gptimer_regs *)pnpinfo->apb_slv->start; + + DBG("GPTIMER[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + + /* Get number of Timers */ + timer_hw_cnt = regs->cfg & GPTIMER_CFG_TIMERS; + + /* Let user spelect a range of timers to be used. In AMP systems + * it is sometimes neccessary to leave timers for other CPU instances. + * + * The default operation in AMP is to shared the timers within the + * first GPTIMER core as below. This can of course be overrided by + * driver resources. + */ + timer_cnt = timer_hw_cnt; + timer_start = 0; +#if defined(RTEMS_MULTIPROCESSING) && defined(LEON3) + if ((dev->minor_drv == 0) && drvmgr_on_rootbus(dev)) { + timer_cnt = 1; + timer_start = LEON3_Cpu_Index; + } +#endif + value = drvmgr_dev_key_get(dev, "timerStart", DRVMGR_KT_INT); + if ( value) { + timer_start = value->i; + timer_cnt = timer_hw_cnt - timer_start; + } + value = drvmgr_dev_key_get(dev, "timerCnt", DRVMGR_KT_INT); + if ( value && (value->i < timer_cnt) ) { + timer_cnt = value->i; + } + + /* Allocate Common Timer Description, size depends on how many timers + * are present. + */ + size = sizeof(struct gptimer_priv) + + timer_cnt*sizeof(struct gptimer_timer); + priv = dev->priv = (struct gptimer_priv *)malloc(size); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, size); + priv->dev = dev; + priv->regs = regs; + + /* The Base Frequency of the GPTIMER core is the same as the + * frequency of the AMBA bus it is situated on. + */ + drvmgr_freq_get(dev, DEV_APB_SLV, &priv->base_clk); + + /* This core will may provide important Timer functionality + * to other drivers and the RTEMS kernel, the Clock driver + * may for example use this device. So the Timer driver must be + * initialized in the first iiitialization stage. + */ + + /*** Initialize Hardware ***/ + + /* If user request to set prescaler, we will do that. However, note + * that doing so for the Root-Bus GPTIMER may affect the RTEMS Clock + * so that Clock frequency is wrong. + */ + value = drvmgr_dev_key_get(priv->dev, "prescaler", DRVMGR_KT_INT); + if ( value ) + regs->scaler_reload = value->i; + + /* Get Frequency that the timers are operating in (after prescaler) */ + priv->base_freq = priv->base_clk / (priv->regs->scaler_reload + 1); + + /* Stop Timer and probe Pending bit. In newer hardware the + * timer has pending bit is cleared by writing a one to it, + * whereas older versions it is cleared with a zero. + */ + priv->regs->timer[timer_start].ctrl = GPTIMER_CTRL_IP; + if ((priv->regs->timer[timer_start].ctrl & GPTIMER_CTRL_IP) != 0) + irq_ack_mask = ~GPTIMER_CTRL_IP; + else + irq_ack_mask = ~0; + + /* Probe timer register width mask */ + priv->regs->timer[timer_start].value = 0xffffffff; + priv->widthmask = priv->regs->timer[timer_start].value; + + priv->timer_cnt = timer_cnt; + for (i=0; i<timer_cnt; i++) { + timer = &priv->timers[i]; + timer->index = i; + timer->tindex = i + timer_start; + timer->tregs = ®s->timer[(int)timer->tindex]; + timer->tdev.drv = &gptimer_tlib_drv; + timer->irq_ack_mask = irq_ack_mask; + + /* Register Timer at Timer Library */ + tlib_dev_reg(&timer->tdev); + } + + /* Check Interrupt support implementation, two cases: + * A. All Timers share one IRQ + * B. Each Timer have an individual IRQ. The number is: + * BASE_IRQ + timer_index + */ + priv->separate_interrupt = (regs->cfg & GPTIMER_CFG_SI) != 0; + + return DRVMGR_OK; +} + +#ifdef GPTIMER_INFO_AVAIL +static int gptimer_info( + struct drvmgr_dev *dev, + void (*print_line)(void *p, char *str), + void *p, int argc, char *argv[]) +{ + struct gptimer_priv *priv = dev->priv; + struct gptimer_timer *timer; + char buf[64]; + int i; + + if (priv == NULL || argc != 0) + return -DRVMGR_EINVAL; + + sprintf(buf, "Timer Count: %d", priv->timer_cnt); + print_line(p, buf); + sprintf(buf, "REGS: 0x%08x", (unsigned int)priv->regs); + print_line(p, buf); + sprintf(buf, "BASE SCALER: %d", priv->regs->scaler_reload); + print_line(p, buf); + sprintf(buf, "BASE FREQ: %dkHz", priv->base_freq / 1000); + print_line(p, buf); + sprintf(buf, "SeparateIRQ: %s", priv->separate_interrupt ? "YES":"NO"); + print_line(p, buf); + + for (i=0; i<priv->timer_cnt; i++) { + timer = &priv->timers[i]; + sprintf(buf, " - TIMER HW Index %d -", timer->tindex); + print_line(p, buf); + sprintf(buf, " TLIB Index: %d", timer->index); + print_line(p, buf); + sprintf(buf, " RELOAD REG: %d", timer->tregs->reload); + print_line(p, buf); + sprintf(buf, " CTRL REG: %d", timer->tregs->ctrl); + print_line(p, buf); + } + + return DRVMGR_OK; +} +#endif + +static inline struct gptimer_priv *priv_from_timer(struct gptimer_timer *t) +{ + return (struct gptimer_priv *) + ((unsigned int)t - + sizeof(struct gptimer_priv) - + t->index * sizeof(struct gptimer_timer)); +} + +static int gptimer_tlib_int_pend(struct tlib_dev *hand, int ack) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + unsigned int ctrl = timer->tregs->ctrl; + + if ((ctrl & (GPTIMER_CTRL_IP | GPTIMER_CTRL_IE)) == + (GPTIMER_CTRL_IP | GPTIMER_CTRL_IE)) { + /* clear Pending IRQ ? */ + if (ack) + timer->tregs->ctrl = ctrl & timer->irq_ack_mask; + return 1; /* timer generated IRQ */ + } else + return 0; /* was not timer causing IRQ */ +} + +void gptimer_isr(void *data) +{ + struct gptimer_priv *priv = data; + int i; + + /* Check all timers for IRQ */ + for (i=0;i<priv->timer_cnt; i++) { + if (gptimer_tlib_int_pend((void *)&priv->timers[i], 0)) { + /* IRQ Was generated by Timer and Pending flag has *not* + * yet been cleared, this is to allow ISR to look at + * pending bit. Call ISR registered. Clear pending bit. + */ + if (priv->timers[i].tdev.isr_func) { + priv->timers[i].tdev.isr_func( + priv->timers[i].tdev.isr_data); + } + gptimer_tlib_int_pend((void *)&priv->timers[i], 1); + } + } +} + +static void gptimer_tlib_reset(struct tlib_dev *hand) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + + timer->tregs->ctrl = (timer->tregs->ctrl & timer->irq_ack_mask) & + GPTIMER_CTRL_IP; + timer->tregs->reload = 0xffffffff; + timer->tregs->ctrl = GPTIMER_CTRL_LD; +} + +static void gptimer_tlib_get_freq( + struct tlib_dev *hand, + unsigned int *basefreq, + unsigned int *tickrate) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + struct gptimer_priv *priv = priv_from_timer(timer); + + /* Calculate base frequency from Timer Clock and Prescaler */ + if ( basefreq ) + *basefreq = priv->base_freq; + if ( tickrate ) + *tickrate = timer->tregs->reload + 1; +} + +static int gptimer_tlib_set_freq(struct tlib_dev *hand, unsigned int tickrate) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + + timer->tregs->reload = tickrate - 1; + + /*Check that value was allowed (Timer may not be as wide as expected)*/ + if ( timer->tregs->reload != (tickrate - 1) ) + return -1; + else + return 0; +} + +static void gptimer_tlib_irq_reg(struct tlib_dev *hand, tlib_isr_t func, void *data, int flags) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + struct gptimer_priv *priv = priv_from_timer(timer); + + if ( priv->separate_interrupt ) { + drvmgr_interrupt_register(priv->dev, timer->tindex, + "gptimer", func, data); + } else { + if (priv->isr_installed == 0) { + /* Shared IRQ handler */ + drvmgr_interrupt_register( + priv->dev, + 0, + "gptimer_shared", + gptimer_isr, + priv); + } + priv->isr_installed++; + } + +#if RTEMS_SMP + if (flags & TLIB_FLAGS_BROADCAST) { + int tindex = 0; + + if (priv->separate_interrupt) { + /* Offset interrupt number with HW subtimer index */ + tindex = timer->tindex; + } + drvmgr_interrupt_set_affinity(priv->dev, tindex, + _SMP_Get_online_processors()); + } +#endif + + timer->tregs->ctrl |= GPTIMER_CTRL_IE; +} + +static void gptimer_tlib_irq_unreg(struct tlib_dev *hand, tlib_isr_t func, void *data) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + struct gptimer_priv *priv = priv_from_timer(timer); + + /* Turn off IRQ at source, unregister IRQ handler */ + timer->tregs->ctrl &= ~GPTIMER_CTRL_IE; + + if ( priv->separate_interrupt ) { + drvmgr_interrupt_unregister(priv->dev, timer->tindex, + func, data); + } else { + timer->tdev.isr_func = NULL; + priv->isr_installed--; + if (priv->isr_installed == 0) { + drvmgr_interrupt_unregister(priv->dev, 0, + gptimer_isr, priv); + } + } +} + +static void gptimer_tlib_start(struct tlib_dev *hand, int once) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + unsigned int ctrl; + + /* Load the selected frequency before starting Frequency */ + ctrl = GPTIMER_CTRL_LD | GPTIMER_CTRL_EN; + if ( once == 0 ) + ctrl |= GPTIMER_CTRL_RS; /* Restart Timer */ + timer->tregs->ctrl = ctrl | (timer->tregs->ctrl & timer->irq_ack_mask & + ~GPTIMER_CTRL_RS); +} + +static void gptimer_tlib_stop(struct tlib_dev *hand) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + + /* Load the selected Frequency */ + timer->tregs->ctrl &= ~(GPTIMER_CTRL_EN|GPTIMER_CTRL_IP); +} + +static void gptimer_tlib_restart(struct tlib_dev *hand) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + + timer->tregs->ctrl |= GPTIMER_CTRL_LD | GPTIMER_CTRL_EN; +} + +static void gptimer_tlib_get_counter( + struct tlib_dev *hand, + unsigned int *counter) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + + *counter = timer->tregs->value; +} + +static void gptimer_tlib_get_widthmask( + struct tlib_dev *hand, + unsigned int *widthmask) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + struct gptimer_priv *priv = priv_from_timer(timer); + + *widthmask = priv->widthmask; +} + +static struct tlib_drv gptimer_tlib_drv = +{ + .reset = gptimer_tlib_reset, + .get_freq = gptimer_tlib_get_freq, + .set_freq = gptimer_tlib_set_freq, + .irq_reg = gptimer_tlib_irq_reg, + .irq_unreg = gptimer_tlib_irq_unreg, + .start = gptimer_tlib_start, + .stop = gptimer_tlib_stop, + .restart = gptimer_tlib_restart, + .get_counter = gptimer_tlib_get_counter, + .custom = NULL, + .int_pend = gptimer_tlib_int_pend, + .get_widthmask = gptimer_tlib_get_widthmask, +}; diff --git a/bsps/sparc/shared/btimer/tlib.c b/bsps/sparc/shared/btimer/tlib.c new file mode 100644 index 0000000000..a0525d072d --- /dev/null +++ b/bsps/sparc/shared/btimer/tlib.c @@ -0,0 +1,77 @@ +/* + * Timer Library (TLIB) + * + * COPYRIGHT (c) 2011. + * Cobham Gaisler AB. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <bsp/tlib.h> + +struct tlib_dev *tlib_dev_head = NULL; +struct tlib_dev *tlib_dev_tail = NULL; +static int tlib_dev_cnt = 0; + +/* Register Timer device to Timer Library */ +int tlib_dev_reg(struct tlib_dev *newdev) +{ + /* Reset device */ + newdev->status = 0; + newdev->isr_func = NULL; + newdev->index = tlib_dev_cnt; + + /* Insert last in queue */ + newdev->next = NULL; + if ( tlib_dev_tail == NULL ) { + tlib_dev_head = newdev; + } else { + tlib_dev_tail->next = newdev; + } + tlib_dev_tail = newdev; + + /* Return Index of Registered Timer */ + return tlib_dev_cnt++; +} + +void *tlib_open(int timer_no) +{ + struct tlib_dev *dev; + + if ( timer_no < 0 ) + return NULL; + + dev = tlib_dev_head; + while ( (timer_no > 0) && dev ) { + timer_no--; + dev = dev->next; + } + if ( dev ) { + if ( dev->status ) + return NULL; + dev->status = 1; + /* Reset Timer to initial state */ + tlib_reset(dev); + } + return dev; +} + +void tlib_close(void *hand) +{ + struct tlib_dev *dev = hand; + + /* Stop any ongoing timer operation and unregister IRQ if registered */ + tlib_stop(dev); + tlib_irq_unregister(dev); + + /* Mark not open */ + dev->status = 0; +} + +int tlib_ntimer(void) +{ + return tlib_dev_cnt; +} diff --git a/bsps/sparc/shared/btimer/tlib_ckinit.c b/bsps/sparc/shared/btimer/tlib_ckinit.c new file mode 100644 index 0000000000..3f56d725d9 --- /dev/null +++ b/bsps/sparc/shared/btimer/tlib_ckinit.c @@ -0,0 +1,453 @@ +/* + * Clock Tick Device Driver using Timer Library implemented + * by the GRLIB GPTIMER / LEON2 Timer drivers. + * + * COPYRIGHT (c) 2010 - 2017. + * Cobham Gaisler AB. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + */ + +/* + * This is an implementation of the RTEMS "clockdrv_shell" interface for + * LEON2/3/4 systems using the Driver Manager. It is clock hardware agnostic + * and compatible with SMP and UP. Availability of free running counters is + * probed and selected as needed. + */ +#include <rtems.h> +#include <rtems/timecounter.h> +#include <rtems/clockdrv.h> +#include <stdlib.h> +#include <bsp.h> +#include <bsp/tlib.h> + +#ifdef RTEMS_DRVMGR_STARTUP + +#if defined(LEON3) +#include <leon.h> +#endif + +struct ops { + /* + * Set up the free running counter using the Timecounter or Simple + * Timecounter interface. + */ + rtems_device_driver (*initialize_counter)(void); + + /* + * Hardware-specific support at tick interrupt which runs early in Clock_isr. + * It can for example be used to check if interrupt was actually caused by + * the timer hardware. If return value is not RTEMS_SUCCESSFUL then Clock_isr + * returns immediately. at_tick can be initialized with NULL. + */ + rtems_device_driver (*at_tick)(void); + + /* + * Typically calls rtems_timecounter_tick(). A specialized clock driver may + * use for example rtems_timecounter_tick_simple() instead. + */ + void (*timecounter_tick)(void); + + /* + * Called when the clock driver exits. It can be used to stop functionality + * started by initialize_counter. The tick timer is stopped by default. + * shutdown_hardware can be initialized with NULL + */ + void (*shutdown_hardware)(void); +}; + +/* + * Different implementation depending on available free running counter for the + * timecounter. + * + * NOTE: The clock interface is not compatible with shared interrupts on the + * clock (tick) timer in SMP configuration. + */ + +/* "simple timecounter" interface. Only for non-SMP. */ +static const struct ops ops_simple; +/* Hardware support up-counter using LEON3 %asr23. */ +static const struct ops ops_timetag; +/* Timestamp counter available in some IRQ(A)MP instantiations. */ +static const struct ops ops_irqamp; +/* Separate GPTIMER subtimer as timecounter */ +static const struct ops ops_subtimer; + +struct clock_priv { + const struct ops *ops; + /* + * Timer number in Timer Library for tick timer used by this interface. + * Defaults to the first Timer in the System. + */ + int tlib_tick_index; + /* Timer number for timecounter timer if separate GPTIMER subtimer is used */ + int tlib_counter_index; + void *tlib_tick; + void *tlib_counter; + rtems_timecounter_simple tc_simple; + struct timecounter tc; +}; +static struct clock_priv priv; + +/** Common interface **/ + +/* Set system clock timer instance */ +void Clock_timer_register(int timer_number) +{ + priv.tlib_tick_index = timer_number; + priv.tlib_counter_index = timer_number + 1; +} + +static rtems_device_driver tlib_clock_find_timer(void) +{ + /* Take Timer that should be used as system timer. */ + priv.tlib_tick = tlib_open(priv.tlib_tick_index); + if (priv.tlib_tick == NULL) { + /* System Clock Timer not found */ + return RTEMS_NOT_DEFINED; + } + + /* Select which operation set to use */ +#ifndef RTEMS_SMP + priv.ops = &ops_simple; +#else + /* When on LEON3 try to use dedicated hardware free running counter. */ + leon3_up_counter_enable(); + if (leon3_up_counter_is_available()) { + priv.ops = &ops_timetag; + return RTEMS_SUCCESSFUL; + } else { + volatile struct irqmp_timestamp_regs *irqmp_ts; + + irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0]; + if (leon3_irqmp_has_timestamp(irqmp_ts)) { + priv.ops = &ops_irqamp; + return RTEMS_SUCCESSFUL; + } + } + + /* Take another subtimer as the final option. */ + priv.ops = &ops_subtimer; +#endif + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver tlib_clock_initialize_hardware(void) +{ + /* Set tick rate in number of "Base-Frequency ticks" */ + tlib_set_freq(priv.tlib_tick, rtems_configuration_get_microseconds_per_tick()); + priv.ops->initialize_counter(); + tlib_start(priv.tlib_tick, 0); + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver tlib_clock_at_tick(void) +{ + if (priv.ops->at_tick) { + return priv.ops->at_tick(); + } + + return RTEMS_SUCCESSFUL; +} + +static void tlib_clock_timecounter_tick(void) +{ + priv.ops->timecounter_tick(); +} + +/* Return a value not equal to RTEMS_SUCCESFUL to make Clock_initialize fail. */ +static rtems_device_driver tlib_clock_install_isr(rtems_isr *isr) +{ + int flags = 0; + +#ifdef RTEMS_SMP + /* We shall broadcast the clock interrupt to all processors. */ + flags = TLIB_FLAGS_BROADCAST; +#endif + tlib_irq_register(priv.tlib_tick, isr, NULL, flags); + + return RTEMS_SUCCESSFUL; +} + +static void tlib_clock_shutdown_hardware(void) +{ + if (priv.tlib_tick) { + tlib_stop(priv.tlib_tick); + priv.tlib_tick = NULL; + } + if (priv.ops->shutdown_hardware) { + priv.ops->shutdown_hardware(); + } +} + +/** Simple counter **/ +static uint32_t simple_tlib_tc_get(rtems_timecounter_simple *tc) +{ + unsigned int clicks = 0; + + if (priv.tlib_tick != NULL) { + tlib_get_counter(priv.tlib_tick, &clicks); + } + + return clicks; +} + +static bool simple_tlib_tc_is_pending(rtems_timecounter_simple *tc) +{ + bool pending = false; + + if (priv.tlib_tick != NULL) { + pending = tlib_interrupt_pending(priv.tlib_tick, 0) != 0; + } + + return pending; +} + +static uint32_t simple_tlib_tc_get_timecount(struct timecounter *tc) +{ + return rtems_timecounter_simple_downcounter_get( + tc, + simple_tlib_tc_get, + simple_tlib_tc_is_pending + ); +} + +static rtems_device_driver simple_initialize_counter(void) +{ + uint64_t frequency; + unsigned int tick_hz; + + frequency = 1000000; + tick_hz = rtems_configuration_get_microseconds_per_tick(); + + rtems_timecounter_simple_install( + &priv.tc_simple, + frequency, + tick_hz, + simple_tlib_tc_get_timecount + ); + + return RTEMS_NOT_DEFINED; +} + +static void simple_tlib_tc_at_tick(rtems_timecounter_simple *tc) +{ + /* Nothing to do */ +} + +/* + * Support for shared interrupts. Ack IRQ at source, only handle interrupts + * generated from the tick-timer. This is called early in Clock_isr. + */ +static rtems_device_driver simple_at_tick(void) +{ + if (tlib_interrupt_pending(priv.tlib_tick, 1) == 0) { + return RTEMS_NOT_DEFINED; + } + return RTEMS_SUCCESSFUL; +} + +static void simple_timecounter_tick(void) +{ + rtems_timecounter_simple_downcounter_tick( + &priv.tc_simple, + simple_tlib_tc_get, + simple_tlib_tc_at_tick + ); +} + +static const struct ops ops_simple = { + .initialize_counter = simple_initialize_counter, + .at_tick = simple_at_tick, + .timecounter_tick = simple_timecounter_tick, + .shutdown_hardware = NULL, +}; + +/** Subtimer as counter **/ +static uint32_t subtimer_get_timecount(struct timecounter *tc) +{ + unsigned int counter; + + tlib_get_counter(priv.tlib_counter, &counter); + + return 0xffffffff - counter; +} + +static rtems_device_driver subtimer_initialize_counter(void) +{ + unsigned int mask; + unsigned int basefreq; + + if (priv.tlib_counter_index == priv.tlib_tick_index) { + priv.tlib_counter_index = priv.tlib_tick_index + 1; + } + /* Take Timer that should be used as timecounter upcounter timer. */ + priv.tlib_counter = tlib_open(priv.tlib_counter_index); + if (priv.tlib_counter == NULL) { + /* Timecounter timer not found */ + return RTEMS_NOT_DEFINED; + } + + /* Configure free running counter: GPTIMER */ + tlib_get_freq(priv.tlib_counter, &basefreq, NULL); + tlib_get_widthmask(priv.tlib_counter, &mask); + + priv.tc.tc_get_timecount = subtimer_get_timecount; + priv.tc.tc_counter_mask = mask; + priv.tc.tc_frequency = basefreq; + priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER; + rtems_timecounter_install(&priv.tc); + /* Start free running counter */ + tlib_start(priv.tlib_counter, 0); + + return RTEMS_SUCCESSFUL; +} + +static void subtimer_timecounter_tick(void) +{ + rtems_timecounter_tick(); +} + +static void subtimer_shutdown_hardware(void) +{ + if (priv.tlib_counter) { + tlib_stop(priv.tlib_counter); + priv.tlib_counter = NULL; + } +} + +static const struct ops ops_subtimer = { + .initialize_counter = subtimer_initialize_counter, + .timecounter_tick = subtimer_timecounter_tick, + .shutdown_hardware = subtimer_shutdown_hardware, +}; + +#if defined(LEON3) +/** DSU timetag as counter **/ +static uint32_t timetag_get_timecount(struct timecounter *tc) +{ + return leon3_up_counter_low(); +} + +static rtems_device_driver timetag_initialize_counter(void) +{ + /* Configure free running counter: timetag */ + priv.tc.tc_get_timecount = timetag_get_timecount; + priv.tc.tc_counter_mask = 0xffffffff; + priv.tc.tc_frequency = leon3_up_counter_frequency(); + priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER; + rtems_timecounter_install(&priv.tc); + + return RTEMS_SUCCESSFUL; +} + +static void timetag_timecounter_tick(void) +{ + rtems_timecounter_tick(); +} + +static const struct ops ops_timetag = { + .initialize_counter = timetag_initialize_counter, + .at_tick = NULL, + .timecounter_tick = timetag_timecounter_tick, + .shutdown_hardware = NULL, +}; +#endif + +#if defined(LEON3) +/** IRQ(A)MP timestamp as counter **/ +static uint32_t irqamp_get_timecount(struct timecounter *tc) +{ + return LEON3_IrqCtrl_Regs->timestamp[0].counter; +} + +static rtems_device_driver irqamp_initialize_counter(void) +{ + volatile struct irqmp_timestamp_regs *irqmp_ts; + static const uint32_t A_TSISEL_FIELD = 0xf; + + /* Configure free running counter: timetag */ + priv.tc.tc_get_timecount = irqamp_get_timecount; + priv.tc.tc_counter_mask = 0xffffffff; + priv.tc.tc_frequency = leon3_up_counter_frequency(); + priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER; + rtems_timecounter_install(&priv.tc); + + /* + * The counter increments whenever a TSISEL field in a Timestamp Control + * Register is non-zero. + */ + irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0]; + irqmp_ts->control = A_TSISEL_FIELD; + + return RTEMS_SUCCESSFUL; +} + +static void irqamp_timecounter_tick(void) +{ + rtems_timecounter_tick(); +} + +static const struct ops ops_irqamp = { + .initialize_counter = irqamp_initialize_counter, + .at_tick = NULL, + .timecounter_tick = irqamp_timecounter_tick, + .shutdown_hardware = NULL, +}; +#endif + +/** Interface to the Clock Driver Shell (dev/clock/clockimpl.h) **/ +#define Clock_driver_support_find_timer() \ + do { \ + rtems_device_driver ret; \ + ret = tlib_clock_find_timer(); \ + if (RTEMS_SUCCESSFUL != ret) { \ + return ret; \ + } \ + } while (0) + +#define Clock_driver_support_install_isr( isr ) \ + do { \ + rtems_device_driver ret; \ + ret = tlib_clock_install_isr( isr ); \ + if (RTEMS_SUCCESSFUL != ret) { \ + return ret; \ + } \ + } while (0) + +#define Clock_driver_support_set_interrupt_affinity(online_processors) \ + /* Done by tlib_clock_install_isr() */ + +#define Clock_driver_support_initialize_hardware() \ + do { \ + rtems_device_driver ret; \ + ret = tlib_clock_initialize_hardware(); \ + if (RTEMS_SUCCESSFUL != ret) { \ + return ret; \ + } \ + } while (0) + +#define Clock_driver_support_shutdown_hardware() \ + tlib_clock_shutdown_hardware() + +#define Clock_driver_timecounter_tick() \ + tlib_clock_timecounter_tick() + +#define Clock_driver_support_at_tick() \ + do { \ + rtems_device_driver ret; \ + ret = tlib_clock_at_tick(); \ + if (RTEMS_SUCCESSFUL != ret) { \ + return; \ + } \ + } while (0) + +#include "../../../shared/dev/clock/clockimpl.h" + +#endif /* RTEMS_DRVMGR_STARTUP */ + |