Skip to content

Commit 3be6e8d

Browse files
committed
Improve virtual timer implementation
1 parent 3b3ba69 commit 3be6e8d

14 files changed

+119
-58
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +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/priority_queue.h
17+
ETL_INCLUDES += ./external/etl/include/etl/set.h
1818

1919
# Source and header files
2020
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: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ enum class DriverCommand {
88
LED_DISPLAY = 1,
99
BUTTONS = 2,
1010
TERMINAL_OUTPUT = 3,
11-
TIMER_START = 4,
12-
TIMER_CANCEL = 5,
11+
TIMER_CANCEL = 4,
1312
};
1413

15-
enum class DriverSubscribe { NOTIFY_BUTTON_PRESS = 0 };
14+
enum class DriverSubscribe { NOTIFY_BUTTON_PRESS = 0, TIMER_START = 1 };
1615

1716
enum class GPIOConfiguration { OUT, IN_NORES, IN_PDR, IN_PUR };
1817

include/drivers/virtual_timer_controller.hpp

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "nrf_timer.h"
44
#include "stdint.h"
5+
#include "util.hpp"
56

67
namespace edge::drivers {
78
extern "C" {
@@ -11,33 +12,36 @@ void TIMER3_IRQHandler(void);
1112
struct timer {
1213
uint32_t id;
1314

14-
void* callback;
15+
ProcessCallbackPtr callback;
1516

1617
uint32_t timer_value;
17-
18-
bool operator<(const timer& other) const { return timer_value < other.timer_value; }
18+
ProcessId process_id;
19+
uint32_t duration;
20+
bool periodic;
1921
};
2022

23+
inline bool operator<(const timer& first, const timer& second)
24+
{
25+
return first.timer_value < second.timer_value;
26+
}
27+
2128
// Reserves Timer 3
2229
class VirtualTimerController {
2330
static constexpr nrf_timer_frequency_t TIMER_FREQUENCY = NRF_TIMER_FREQ_16MHz;
2431
inline static NRF_TIMER_Type* const TIMER = NRF_TIMER3;
25-
etl::priority_queue<timer, 32> timers_;
26-
27-
void trigger_ready_timers();
28-
bool has_ready_timer() const;
32+
static constexpr size_t MAX_TIMERS = 512;
33+
etl::set<timer, MAX_TIMERS> timers_;
34+
uint32_t virtual_timer_start(const timer& timer);
2935

3036
public:
31-
uint32_t read_timer() const;
32-
33-
uint32_t virtual_timer_start(uint32_t microseconds, void* callback);
34-
37+
uint32_t virtual_timer_start(
38+
uint32_t microseconds, ProcessCallbackPtr callback, ProcessId timer_creator,
39+
bool periodic
40+
);
3541
void virtual_timer_cancel(uint32_t timer_id);
3642

3743
static VirtualTimerController& get();
3844

39-
uint32_t timer_start(uint32_t microseconds, void* callback);
40-
4145
VirtualTimerController(VirtualTimerController&) = delete;
4246
VirtualTimerController(VirtualTimerController&&) = delete;
4347
VirtualTimerController& operator=(VirtualTimerController&) = delete;
@@ -46,8 +50,20 @@ class VirtualTimerController {
4650
friend void TIMER3_IRQHandler(void);
4751

4852
private:
49-
~VirtualTimerController() = default;
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+
5065
VirtualTimerController();
66+
~VirtualTimerController() = default;
5167
};
5268

5369
} // 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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ void subscribe_ipc(const char* name, void (*callback)(int message));
2727

2828
void set_fault_handler(void (*callback)(FaultType));
2929

30-
uint32_t start_timer(void (*callback)(uint32_t), uint32_t microseconds);
30+
uint32_t
31+
start_timer(void (*callback)(uint32_t), uint32_t microseconds, bool periodic = false);
3132

3233
void cancel_timer(uint32_t timer_id);
3334

src/drivers/driver_commands.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ etl::optional<int> handle_command(DriverCommand type, int arg1, int arg2, int ar
3131
return ButtonController::get().get_button_pressed(
3232
static_cast<ButtonType>(arg1)
3333
);
34-
case DriverCommand::TIMER_START:
35-
return VirtualTimerController::get().virtual_timer_start(
36-
static_cast<uint32_t>(arg2), (void*)arg1
37-
);
3834
case DriverCommand::TIMER_CANCEL:
3935
VirtualTimerController::get().virtual_timer_cancel(
4036
static_cast<uint32_t>(arg1)
@@ -44,7 +40,7 @@ etl::optional<int> handle_command(DriverCommand type, int arg1, int arg2, int ar
4440
return etl::nullopt;
4541
}
4642

47-
void handle_subscribe(
43+
etl::optional<int> handle_subscribe(
4844
DriverSubscribe type, ProcessCallbackPtr callback, int arg1, int arg2,
4945
ProcessId process_id
5046
)
@@ -55,7 +51,12 @@ void handle_subscribe(
5551
static_cast<ButtonType>(arg1), callback, process_id
5652
);
5753
break;
54+
case DriverSubscribe::TIMER_START:
55+
return VirtualTimerController::get().virtual_timer_start(
56+
static_cast<uint32_t>(arg1), callback, process_id, arg2
57+
);
5858
}
59+
return etl::nullopt;
5960
}
6061

6162
} // namespace edge::drivers

src/drivers/virtual_timer_controller.cpp

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "nrf52833.h"
44
#include "scheduler/pending_process_callbacks.hpp"
5+
#include "util.hpp"
56

67
namespace edge::drivers {
78

@@ -24,20 +25,38 @@ VirtualTimerController::VirtualTimerController()
2425
NVIC_EnableIRQ(TIMER3_IRQn);
2526
}
2627

27-
bool VirtualTimerController::has_ready_timer() const
28+
etl::optional<timer> VirtualTimerController::get_ready_timer()
2829
{
29-
return !timers_.empty() && timers_.top().timer_value <= read_timer();
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;
3037
};
3138

39+
void VirtualTimerController::enqueue_next_timer() const
40+
{
41+
if (!timers_.empty())
42+
TIMER->CC[2] = timers_.begin()->timer_value;
43+
}
44+
3245
void VirtualTimerController::trigger_ready_timers()
3346
{
34-
while (has_ready_timer()) {
35-
timer t{timers_.top()};
36-
timers_.pop();
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();
3750
PendingProcessCallbacks::get().add_ready_callback(
38-
0, reinterpret_cast<void (*)(int, int)>(t.callback)
51+
ready_timer.process_id, ready_timer.callback, ready_timer.id
3952
);
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();
4058
}
59+
enqueue_next_timer();
4160
}
4261

4362
VirtualTimerController& VirtualTimerController::get()
@@ -52,20 +71,38 @@ uint32_t VirtualTimerController::read_timer() const
5271
return TIMER->CC[1];
5372
}
5473

55-
uint32_t VirtualTimerController::timer_start(uint32_t microseconds, void* cb)
74+
uint32_t VirtualTimerController::virtual_timer_start(const timer& timer)
5675
{
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+
5788
uint32_t curr_time = read_timer();
58-
timers_.emplace(0, cb, curr_time + microseconds);
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};
5993

60-
TIMER->CC[2] = timers_.top().timer_value;
61-
return 0;
94+
return virtual_timer_start(new_timer);
6295
}
6396

64-
uint32_t VirtualTimerController::virtual_timer_start(uint32_t microseconds, void* cb)
97+
void VirtualTimerController::virtual_timer_cancel(uint32_t timer_id)
6598
{
66-
return timer_start(microseconds, cb);
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();
67106
}
68107

69-
void VirtualTimerController::virtual_timer_cancel(uint32_t timer_id) {}
70-
71108
} // 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: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22

33
#include <cstdio>
44

5-
void static callback(uint32_t id)
5+
void callback(uint32_t id)
66
{
7-
edge::userlib::set_led(3, 3, true);
8-
edge::userlib::debug_print("Callback!\n");
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);
915
}
1016

1117
void exception_task(void)
@@ -37,9 +43,9 @@ void exception_task(void)
3743
set_fault_handler(fault_handler);
3844

3945
trigger_faults();
40-
auto id = start_timer(&callback, 1000000);
41-
// cancel_timer(id);
42-
id = start_timer(&callback, 2000000);
46+
start_timer(callback, 100000, true);
47+
auto id2 = start_timer(callback2, 750000, true);
48+
cancel_timer(id2);
4349

4450
set_led(2, 2, true);
4551

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: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,14 @@ int USER_CODE get_time_us()
8585
RETURN_REGISTER(r0);
8686
}
8787

88-
uint32_t USER_CODE start_timer(void (*callback)(uint32_t), uint32_t microseconds)
88+
uint32_t USER_CODE
89+
start_timer(void (*callback)(uint32_t), uint32_t microseconds, bool periodic)
8990
{
90-
SET_REGISTER(r0, (int)drivers::DriverCommand::TIMER_START);
91+
SET_REGISTER(r0, (int)drivers::DriverSubscribe::TIMER_START);
9192
SET_REGISTER(r1, (int)callback);
9293
SET_REGISTER(r2, (int)microseconds);
93-
TRIGGER_SVC(SystemCallType::COMMAND);
94+
SET_REGISTER(r3, (int)periodic);
95+
TRIGGER_SVC(SystemCallType::SUBSCRIBE);
9496
RETURN_REGISTER(r0);
9597
}
9698

0 commit comments

Comments
 (0)