Software APIs
rv_timer_systick_test.c
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
8 #include "sw/device/lib/runtime/irq.h"
10 #include "sw/device/lib/testing/test_framework/check.h"
12 
14 
15 static dif_rv_timer_t timer;
16 
17 enum {
19  kComparator = 0,
20  kReferenceTimeMillis = 5,
21  kWrapTimeMillis = 5,
22 };
23 
24 OTTF_DEFINE_TEST_CONFIG();
25 
26 static status_t set_tick(uint32_t tick_hz) {
27  dif_rv_timer_tick_params_t tick_params;
29  &tick_params));
30  TRY(dif_rv_timer_set_tick_params(&timer, kHart, tick_params));
31  return OK_STATUS();
32 }
33 
34 static status_t test_tick(uint32_t tick_hz) {
35  LOG_INFO("%s: tick_hz = %u", __func__, tick_hz);
36 
37  TRY(set_tick(tick_hz));
38 
39  uint64_t counter = 0;
40  TRY(dif_rv_timer_counter_write(&timer, kHart, counter));
41  TRY(dif_rv_timer_counter_read(&timer, kHart, &counter));
42  TRY_CHECK(counter == 0, "Failed to write the counter");
43 
45  busy_spin_micros(kReferenceTimeMillis * 1000);
47 
48  TRY(dif_rv_timer_counter_read(&timer, kHart, &counter));
49  const uint64_t elapsed_millis = udiv64_slow(counter * 1000, tick_hz, NULL);
50 
51  // Verify that `n * T ~= 5 milliseconds` within 3% of tolerance.
52  TRY_CHECK((elapsed_millis >= (uint64_t)(kReferenceTimeMillis * 0.97)) &&
53  (elapsed_millis <= (uint64_t)(kReferenceTimeMillis * 1.03)),
54  "Unexpected elapsed time, expected: %u, got: %u",
55  (uint32_t)kReferenceTimeMillis, (uint32_t)elapsed_millis);
56 
57  return OK_STATUS();
58 }
59 
60 static status_t test_wrap(uint32_t tick_hz) {
61  LOG_INFO("%s: tick_hz = %u", __func__, tick_hz);
62 
63  TRY(dif_rv_timer_irq_set_enabled(
64  &timer, kDifRvTimerIrqTimerExpiredHart0Timer0, kDifToggleEnabled));
65 
66  TRY(set_tick(tick_hz));
67 
68  uint64_t start_counter = UINT64_MAX - (kWrapTimeMillis * tick_hz) / 1000;
69  TRY(dif_rv_timer_counter_write(&timer, kHart, start_counter));
70  TRY(dif_rv_timer_arm(&timer, kHart, 0, UINT64_MAX));
71 
73  busy_spin_micros(kWrapTimeMillis * 1000 * 2);
75 
76  uint64_t counter = UINT64_MAX;
77  TRY(dif_rv_timer_counter_read(&timer, kHart, &counter));
78  // Verify that the counter wrapped.
79  TRY_CHECK(counter < start_counter,
80  "Unexpected elapsed time, expected: %08x%08x, got: %08x%08x",
81  (uint32_t)(start_counter >> 32), (uint32_t)start_counter,
82  (uint32_t)(counter >> 32), (uint32_t)counter);
83 
84  bool irq_pending = false;
85  TRY(dif_rv_timer_irq_is_pending(&timer, kDifRvTimerIrqTimerExpiredHart0Timer0,
86  &irq_pending));
87  TRY_CHECK(irq_pending, "Expected timer IRQ");
88  return OK_STATUS();
89 }
90 
91 /**
92  * Verify that the timer can be configured to generate a system tick.
93  * - Configure the timer to generate a tick of `T` microseconds long.
94  * - Enable the timer.
95  * - Start a busy loop of 5 milliseconds based on the `mcycleh` CSR.
96  * - Read the number of ticks `n`.
97  * - Verify that `n * T ~= 5 milliseconds` within 3% of tolerance.
98  * - Repeat for T in:
99  * [1, 5, 25, 100, 125]
100  */
101 bool test_main(void) {
102  CHECK_DIF_OK(dif_rv_timer_init(
104  CHECK_DIF_OK(dif_rv_timer_reset(&timer));
105 
106  const uint32_t kTickHz[] = {
107  1 * 1000 * 1000, // 1MHz - 1us.
108  200 * 1000, // 200kHz - 5us.
109  40 * 1000, // 40kHz - 25us.
110  10 * 1000, // 10kHz - 100us.
111  8 * 1000, // 8kHz - 125us.
112  };
113 
114  status_t result = OK_STATUS();
115  for (size_t i = 0; i < ARRAYSIZE(kTickHz); ++i) {
116  EXECUTE_TEST(result, test_tick, kTickHz[i]);
117  }
118 
119  EXECUTE_TEST(result, test_wrap, 10000);
120 
121  return status_ok(result);
122 }