1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdbool.h>
8 #include <stdint.h>
9 #include "nsi_cpu0_interrupts.h"
10 #include "irq_ctrl.h"
11 #include "nsi_tasks.h"
12 #include "nsi_hws_models_if.h"
13
14 static uint64_t hw_counter_timer;
15
16 static bool counter_running;
17 static uint64_t counter_value;
18 static uint64_t counter_target;
19 static uint64_t counter_period;
20 static uint64_t counter_wrap;
21
22 /**
23 * Initialize the counter with prescaler of HW
24 */
hw_counter_init(void)25 static void hw_counter_init(void)
26 {
27 hw_counter_timer = NSI_NEVER;
28 counter_target = NSI_NEVER;
29 counter_value = 0;
30 counter_running = false;
31 counter_period = NSI_NEVER;
32 counter_wrap = NSI_NEVER;
33 }
34
35 NSI_TASK(hw_counter_init, HW_INIT, 10);
36
hw_counter_triggered(void)37 static void hw_counter_triggered(void)
38 {
39 if (!counter_running) {
40 hw_counter_timer = NSI_NEVER;
41 return;
42 }
43
44 hw_counter_timer = nsi_hws_get_time() + counter_period;
45 counter_value = (counter_value + 1) % counter_wrap;
46
47 if (counter_value == counter_target) {
48 hw_irq_ctrl_set_irq(COUNTER_EVENT_IRQ);
49 }
50 }
51
52 NSI_HW_EVENT(hw_counter_timer, hw_counter_triggered, 20);
53
54 /**
55 * Configures the counter period.
56 * The counter will be incremented every 'period' microseconds.
57 */
hw_counter_set_period(uint64_t period)58 void hw_counter_set_period(uint64_t period)
59 {
60 counter_period = period;
61 }
62
63 /*
64 * Set the count value at which the counter will wrap
65 * The counter will count up to (counter_wrap-1), i.e.:
66 * 0, 1, 2,.., (counter_wrap - 1), 0
67 */
hw_counter_set_wrap_value(uint64_t wrap_value)68 void hw_counter_set_wrap_value(uint64_t wrap_value)
69 {
70 counter_wrap = wrap_value;
71 }
72
73 /**
74 * Starts the counter. It must be previously configured with
75 * hw_counter_set_period() and hw_counter_set_target().
76 */
hw_counter_start(void)77 void hw_counter_start(void)
78 {
79 if (counter_running) {
80 return;
81 }
82
83 counter_running = true;
84
85 hw_counter_timer = nsi_hws_get_time() + counter_period;
86 nsi_hws_find_next_event();
87 }
88
89 /**
90 * Stops the counter at current value.
91 * On the next call to hw_counter_start, the counter will
92 * start from the value at which it was stopped.
93 */
hw_counter_stop(void)94 void hw_counter_stop(void)
95 {
96 counter_running = false;
97 hw_counter_timer = NSI_NEVER;
98 nsi_hws_find_next_event();
99 }
100
hw_counter_is_started(void)101 bool hw_counter_is_started(void)
102 {
103 return counter_running;
104 }
105
106 /**
107 * Returns the current counter value.
108 */
hw_counter_get_value(void)109 uint64_t hw_counter_get_value(void)
110 {
111 return counter_value;
112 }
113
114 /**
115 * Resets the counter value.
116 */
hw_counter_reset(void)117 void hw_counter_reset(void)
118 {
119 counter_value = 0;
120 }
121
122 /**
123 * Configures the counter to generate an interrupt
124 * when its count value reaches target.
125 */
hw_counter_set_target(uint64_t target)126 void hw_counter_set_target(uint64_t target)
127 {
128 counter_target = target;
129 }
130