Skip to content

Commit f11fc00

Browse files
authored
Merge pull request #44 from stevenewald/timer
Add virtual timer implementation
2 parents 08c0e94 + 3be6e8d commit f11fc00

14 files changed

+244
-14
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ ETL_INCLUDES += ./external/etl/include/etl/string_stream.h
1414
ETL_INCLUDES += ./external/etl/include/etl/to_string.h
1515
ETL_INCLUDES += ./external/etl/include/etl/delegate.h
1616
ETL_INCLUDES += ./external/etl/include/etl/unordered_map.h
17+
ETL_INCLUDES += ./external/etl/include/etl/set.h
1718

1819
# Source and header files
1920
APP_HEADER_PATHS += ./include

include/drivers/driver_commands.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ void do_async_work();
1111

1212
etl::optional<int> handle_command(DriverCommand type, int arg1, int arg2, int arg3);
1313

14-
void handle_subscribe(
14+
etl::optional<int> handle_subscribe(
1515
DriverSubscribe type, ProcessCallbackPtr callback, int arg1, int arg2,
1616
ProcessId process_id
1717
);

include/drivers/driver_enums.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ enum class DriverCommand {
77
GET_TIME = 0,
88
LED_DISPLAY = 1,
99
BUTTONS = 2,
10-
TERMINAL_OUTPUT = 3
10+
TERMINAL_OUTPUT = 3,
11+
TIMER_CANCEL = 4,
1112
};
1213

13-
enum class DriverSubscribe { NOTIFY_BUTTON_PRESS = 0 };
14+
enum class DriverSubscribe { NOTIFY_BUTTON_PRESS = 0, TIMER_START = 1 };
1415

1516
enum class GPIOConfiguration { OUT, IN_NORES, IN_PDR, IN_PUR };
1617

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#pragma once
2+
3+
#include "nrf_timer.h"
4+
#include "stdint.h"
5+
#include "util.hpp"
6+
7+
namespace edge::drivers {
8+
extern "C" {
9+
void TIMER3_IRQHandler(void);
10+
}
11+
12+
struct timer {
13+
uint32_t id;
14+
15+
ProcessCallbackPtr callback;
16+
17+
uint32_t timer_value;
18+
ProcessId process_id;
19+
uint32_t duration;
20+
bool periodic;
21+
};
22+
23+
inline bool operator<(const timer& first, const timer& second)
24+
{
25+
return first.timer_value < second.timer_value;
26+
}
27+
28+
// Reserves Timer 3
29+
class VirtualTimerController {
30+
static constexpr nrf_timer_frequency_t TIMER_FREQUENCY = NRF_TIMER_FREQ_16MHz;
31+
inline static NRF_TIMER_Type* const TIMER = NRF_TIMER3;
32+
static constexpr size_t MAX_TIMERS = 512;
33+
etl::set<timer, MAX_TIMERS> timers_;
34+
uint32_t virtual_timer_start(const timer& timer);
35+
36+
public:
37+
uint32_t virtual_timer_start(
38+
uint32_t microseconds, ProcessCallbackPtr callback, ProcessId timer_creator,
39+
bool periodic
40+
);
41+
void virtual_timer_cancel(uint32_t timer_id);
42+
43+
static VirtualTimerController& get();
44+
45+
VirtualTimerController(VirtualTimerController&) = delete;
46+
VirtualTimerController(VirtualTimerController&&) = delete;
47+
VirtualTimerController& operator=(VirtualTimerController&) = delete;
48+
VirtualTimerController& operator=(VirtualTimerController&&) = delete;
49+
50+
friend void TIMER3_IRQHandler(void);
51+
52+
private:
53+
void trigger_ready_timers();
54+
55+
etl::optional<timer> get_ready_timer();
56+
57+
void enqueue_next_timer() const;
58+
59+
uint32_t read_timer() const;
60+
61+
uint32_t timer_start(
62+
uint32_t microseconds, ProcessCallbackPtr callback, ProcessId timer_creator
63+
);
64+
65+
VirtualTimerController();
66+
~VirtualTimerController() = default;
67+
};
68+
69+
} // namespace edge::drivers

include/ipc/ipc_manager.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ class IPCManager {
2020
static IPCManager& get();
2121

2222
void register_callback(
23-
ProcessId process_id, const ProcessName& process_name, ProcessCallbackPtr callback
23+
ProcessId process_id, const ProcessName& process_name,
24+
ProcessCallbackPtr callback
2425
);
2526
void send_message(const ProcessName& destination_name, int value);
2627
};

include/userlib/syscalls.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include "drivers/driver_enums.hpp"
4+
#include "stdint.h"
45
#include "util.hpp"
56

67
namespace edge::userlib {
@@ -26,6 +27,11 @@ void subscribe_ipc(const char* name, void (*callback)(int message));
2627

2728
void set_fault_handler(void (*callback)(FaultType));
2829

30+
uint32_t
31+
start_timer(void (*callback)(uint32_t), uint32_t microseconds, bool periodic = false);
32+
33+
void cancel_timer(uint32_t timer_id);
34+
2935
// We need a syscall for this because SVC will not be preempted by SysTick
3036
// Technically this is insecure - it's mostly for debugging
3137
void debug_print(const char* string);

src/drivers/driver_commands.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "drivers/driver_enums.hpp"
55
#include "drivers/led_display.hpp"
66
#include "drivers/timer.hpp"
7+
#include "drivers/virtual_timer_controller.hpp"
78

89
#include <stdio.h>
910

@@ -30,11 +31,16 @@ etl::optional<int> handle_command(DriverCommand type, int arg1, int arg2, int ar
3031
return ButtonController::get().get_button_pressed(
3132
static_cast<ButtonType>(arg1)
3233
);
34+
case DriverCommand::TIMER_CANCEL:
35+
VirtualTimerController::get().virtual_timer_cancel(
36+
static_cast<uint32_t>(arg1)
37+
);
38+
break;
3339
}
3440
return etl::nullopt;
3541
}
3642

37-
void handle_subscribe(
43+
etl::optional<int> handle_subscribe(
3844
DriverSubscribe type, ProcessCallbackPtr callback, int arg1, int arg2,
3945
ProcessId process_id
4046
)
@@ -45,7 +51,12 @@ void handle_subscribe(
4551
static_cast<ButtonType>(arg1), callback, process_id
4652
);
4753
break;
54+
case DriverSubscribe::TIMER_START:
55+
return VirtualTimerController::get().virtual_timer_start(
56+
static_cast<uint32_t>(arg1), callback, process_id, arg2
57+
);
4858
}
59+
return etl::nullopt;
4960
}
5061

5162
} // namespace edge::drivers
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#include "drivers/virtual_timer_controller.hpp"
2+
3+
#include "nrf52833.h"
4+
#include "scheduler/pending_process_callbacks.hpp"
5+
#include "util.hpp"
6+
7+
namespace edge::drivers {
8+
9+
extern "C" {
10+
void TIMER3_IRQHandler(void)
11+
{
12+
VirtualTimerController& vtc = VirtualTimerController::get();
13+
vtc.TIMER->EVENTS_COMPARE[2] = 0;
14+
vtc.trigger_ready_timers();
15+
}
16+
}
17+
18+
VirtualTimerController::VirtualTimerController()
19+
{
20+
TIMER->BITMODE = 0x3;
21+
TIMER->MODE = 0x0;
22+
TIMER->PRESCALER = 0x4;
23+
TIMER->TASKS_START = 1;
24+
TIMER->INTENSET = 0x40000;
25+
NVIC_EnableIRQ(TIMER3_IRQn);
26+
}
27+
28+
etl::optional<timer> VirtualTimerController::get_ready_timer()
29+
{
30+
auto begin = timers_.begin();
31+
if (!timers_.empty() && begin->timer_value <= read_timer()) {
32+
auto ret = etl::make_optional<timer>(*begin);
33+
timers_.erase(begin);
34+
return ret;
35+
}
36+
return etl::nullopt;
37+
};
38+
39+
void VirtualTimerController::enqueue_next_timer() const
40+
{
41+
if (!timers_.empty())
42+
TIMER->CC[2] = timers_.begin()->timer_value;
43+
}
44+
45+
void VirtualTimerController::trigger_ready_timers()
46+
{
47+
etl::optional<timer> ready_timer_opt = get_ready_timer();
48+
while (ready_timer_opt.has_value()) {
49+
timer& ready_timer = ready_timer_opt.value();
50+
PendingProcessCallbacks::get().add_ready_callback(
51+
ready_timer.process_id, ready_timer.callback, ready_timer.id
52+
);
53+
if (ready_timer.periodic) {
54+
ready_timer.timer_value += ready_timer.duration;
55+
virtual_timer_start(ready_timer);
56+
}
57+
ready_timer_opt = get_ready_timer();
58+
}
59+
enqueue_next_timer();
60+
}
61+
62+
VirtualTimerController& VirtualTimerController::get()
63+
{
64+
static VirtualTimerController controller;
65+
return controller;
66+
}
67+
68+
uint32_t VirtualTimerController::read_timer() const
69+
{
70+
TIMER->TASKS_CAPTURE[1] = 1;
71+
return TIMER->CC[1];
72+
}
73+
74+
uint32_t VirtualTimerController::virtual_timer_start(const timer& timer)
75+
{
76+
timers_.insert(timer);
77+
enqueue_next_timer();
78+
return timer.id;
79+
}
80+
81+
uint32_t VirtualTimerController::virtual_timer_start(
82+
uint32_t microseconds, ProcessCallbackPtr cb, ProcessId timer_creator, bool periodic
83+
)
84+
{
85+
static uint32_t timer_offset = 0;
86+
++timer_offset;
87+
88+
uint32_t curr_time = read_timer();
89+
uint32_t timer_id = curr_time + timer_offset;
90+
91+
timer new_timer{timer_id, cb, curr_time + microseconds,
92+
timer_creator, microseconds, periodic};
93+
94+
return virtual_timer_start(new_timer);
95+
}
96+
97+
void VirtualTimerController::virtual_timer_cancel(uint32_t timer_id)
98+
{
99+
for (auto it = timers_.begin(); it != timers_.end(); ++it) {
100+
if (it->id == timer_id) {
101+
timers_.erase(it);
102+
return;
103+
}
104+
}
105+
enqueue_next_timer();
106+
}
107+
108+
} // namespace edge::drivers

src/ipc/ipc_manager.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ IPCManager& IPCManager::get()
1212
}
1313

1414
void IPCManager::register_callback(
15-
ProcessId process_id, const ProcessName& new_process_name, ProcessCallbackPtr callback
15+
ProcessId process_id, const ProcessName& new_process_name,
16+
ProcessCallbackPtr callback
1617
)
1718
{
1819
ipc_communicators.set_callback(process_id, callback);

src/scheduler/user_callback_storage.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ void UserCallbackStorage::set_callback(ProcessId id, ProcessCallbackPtr ptr)
2020
void UserCallbackStorage::call_callback(ProcessId id, int arg1, int arg2)
2121
{
2222
if (!has_callback(id)) {
23-
panic("Attempted to call callback when it has not been set");
23+
panic("Attempted to call callback when it has not been set");
2424
}
2525

2626
PendingProcessCallbacks::get().add_ready_callback(id, callbacks_[id], arg1, arg2);

src/svc/svc.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ etl::optional<int> handle_driver_command(exception_stack_registers* stack_regs)
2929
);
3030
}
3131

32-
void handle_driver_subscribe(exception_stack_registers* stack_regs)
32+
etl::optional<int> handle_driver_subscribe(exception_stack_registers* stack_regs)
3333
{
3434
auto type = static_cast<drivers::DriverSubscribe>(stack_regs->R0);
3535
auto callback = reinterpret_cast<ProcessCallbackPtr>(stack_regs->R1);
@@ -82,8 +82,7 @@ etl::optional<int> handle_call(exception_stack_registers* stack_regs)
8282
case SystemCallType::COMMAND:
8383
return handle_driver_command(stack_regs);
8484
case SystemCallType::SUBSCRIBE:
85-
handle_driver_subscribe(stack_regs);
86-
break;
85+
return handle_driver_subscribe(stack_regs);
8786
case edge::SystemCallType::IPC:
8887
handle_ipc(stack_regs);
8988
break;
@@ -106,8 +105,8 @@ __attribute__((used)) void SVC_Handler(void)
106105
exception_stack_registers* stack_regs;
107106
asm("MRS %0,PSP" : "=r"(stack_regs));
108107
auto ret_opt = handle_call(stack_regs);
109-
if (ret_opt) {
110-
stack_regs->R0 = *ret_opt;
108+
if (ret_opt.has_value()) {
109+
stack_regs->R0 = ret_opt.value();
111110
}
112111
}
113112
}

src/user_programs/user_program_exception.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
#include "userlib/syscalls.hpp"
22

3+
#include <cstdio>
4+
5+
void callback(uint32_t id)
6+
{
7+
static int b = 0;
8+
edge::userlib::set_led(3, 3, ++b % 2 == 0);
9+
}
10+
11+
void callback2(uint32_t id)
12+
{
13+
static int b = 0;
14+
edge::userlib::set_led(1, 1, ++b % 2 == 0);
15+
}
16+
317
void exception_task(void)
418
{
519
auto trigger_faults = []() {
@@ -29,6 +43,9 @@ void exception_task(void)
2943
set_fault_handler(fault_handler);
3044

3145
trigger_faults();
46+
start_timer(callback, 100000, true);
47+
auto id2 = start_timer(callback2, 750000, true);
48+
cancel_timer(id2);
3249

3350
set_led(2, 2, true);
3451

src/user_programs/user_program_ipc_part_1.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#include "nrf52833.h"
2-
#include "nrf_delay.h"
31
#include "userlib/syscalls.hpp"
42

53
#include <stdio.h>

src/userlib/syscalls.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,24 @@ int USER_CODE get_time_us()
8585
RETURN_REGISTER(r0);
8686
}
8787

88+
uint32_t USER_CODE
89+
start_timer(void (*callback)(uint32_t), uint32_t microseconds, bool periodic)
90+
{
91+
SET_REGISTER(r0, (int)drivers::DriverSubscribe::TIMER_START);
92+
SET_REGISTER(r1, (int)callback);
93+
SET_REGISTER(r2, (int)microseconds);
94+
SET_REGISTER(r3, (int)periodic);
95+
TRIGGER_SVC(SystemCallType::SUBSCRIBE);
96+
RETURN_REGISTER(r0);
97+
}
98+
99+
void USER_CODE cancel_timer(uint32_t timer_id)
100+
{
101+
SET_REGISTER(r0, (int)drivers::DriverCommand::TIMER_CANCEL);
102+
SET_REGISTER(r1, (int)timer_id);
103+
TRIGGER_SVC(SystemCallType::COMMAND);
104+
}
105+
88106
void USER_CODE debug_print(const char* val)
89107
{
90108
SET_REGISTER(r0, (int)drivers::DriverCommand::TERMINAL_OUTPUT);

0 commit comments

Comments
 (0)