summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/nios2/nios2_iss/timer/timer.c
blob: d51c606e5a8c05e41a1e2210c99c60c0dfb883aa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*  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) 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.com/license/LICENSE.
 *
 *  $Id$
 */

#define TIMER_WRAPS_AFTER_1MS 0

#include <rtems.h>
#include <rtems/score/cpu.h>
#include <bsp.h>

volatile uint32_t Timer_interrupts;
rtems_boolean    benchmark_timer_find_average_overhead;

#define TIMER_REGS ((altera_avalon_timer_regs*)NIOS2_IO_BASE(TIMER_BASE))

void timerisr( void )
{
  TIMER_REGS->status = 0;
  Timer_interrupts++;
}

void benchmark_timer_initialize( void )
{
  uint32_t old_ie;

  /* Disable timer interrupt, stop timer */

  TIMER_REGS->control = ALTERA_AVALON_TIMER_CONTROL_STOP_MSK;

  set_vector((nios2_isr_entry *)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 */

int benchmark_timer_read( void )
{
  uint32_t timer_wraps;
  uint32_t timer_snap;
  uint32_t timer_ticks;
  uint32_t total;
  uint32_t old_ie;

  /* 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(
  rtems_boolean find_flag
)
{
  benchmark_timer_find_average_overhead = find_flag;
}