Skip to content
This repository was archived by the owner on Jan 7, 2025. It is now read-only.

Commit cf39511

Browse files
EM-337: Handle timer_offset overflow and add timer unit tests
1 parent 731313a commit cf39511

File tree

14 files changed

+475
-13
lines changed

14 files changed

+475
-13
lines changed

.gitlab-ci.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ create_docker_image:
4848
- docker build --cache-from registry.gitlab.com/aloxy/$CI_PROJECT_NAME/builder:$DOCKER_TAG --pull -t registry.gitlab.com/aloxy/$CI_PROJECT_NAME/builder:$DOCKER_TAG . && docker push registry.gitlab.com/aloxy/$CI_PROJECT_NAME/builder:$DOCKER_TAG
4949

5050

51-
job 1:
51+
build primary apps:
5252
image: "registry.gitlab.com/aloxy/$CI_PROJECT_NAME/builder:$DOCKER_TAG"
5353
tags:
5454
- docker
@@ -73,7 +73,7 @@ job 1:
7373
- build/apps/gateway/*
7474
- build/apps/modem/*
7575
- build/apps/sensor_push/*
76-
job 1b:
76+
run unit tests:
7777
image: "registry.gitlab.com/aloxy/$CI_PROJECT_NAME/builder:$DOCKER_TAG"
7878
tags:
7979
- docker
@@ -87,18 +87,20 @@ job 1b:
8787
script:
8888
- mkdir build && cd build
8989
- platform="NATIVE"
90-
- cmake ../stack/ -DPLATFORM=$platform -DCMAKE_TOOLCHAIN_FILE="../stack/cmake/toolchains/gcc.cmake" -DBUILD_UNIT_TESTS=y -DFRAMEWORK_CONSOLE_ENABLED=n -DTEST_AES=y -DTEST_FEC=y -DTEST_ALP=y -DTEST_FIFO=y -DTEST_SCHEDULER=y -DMODULE_D7AP=n -DFRAMEWORK_MODEM_INTERFACE_ENABLED=n -DFRAMEWORK_USE_POWER_TRACKING=n
90+
- cmake ../stack/ -DPLATFORM=$platform -DCMAKE_TOOLCHAIN_FILE="../stack/cmake/toolchains/gcc.cmake" -DBUILD_UNIT_TESTS=y -DFRAMEWORK_CONSOLE_ENABLED=n -DTEST_AES=y -DTEST_FEC=y -DTEST_ALP=y -DTEST_FIFO=y -DTEST_SCHEDULER=y -DTEST_TIMER=y -DMODULE_D7AP=n -DFRAMEWORK_MODEM_INTERFACE_ENABLED=n -DFRAMEWORK_USE_POWER_TRACKING=n
9191
- make -j
9292
- ./tests/aes/test_aes &> results_aes.txt
9393
- ./tests/alp/test_alp &> results_alp.txt
9494
- ./tests/fec/test_fec &> results_fec.txt
9595
- ./tests/fifo/test_fifo &> results_fifo.txt
9696
- ./tests/scheduler/test_scheduler &> results_scheduler.txt
97+
- ./tests/timer/test_timer &> results_timer.txt
9798
- if ! grep -q 'AES all unit tests OK !' "results_aes.txt"; then exit 1; fi
9899
- if ! grep -q 'Unit-tests for ALP completed' "results_alp.txt"; then exit 1; fi
99100
- if ! grep -q 'Input was decoded successfully' "results_fec.txt"; then exit 1; fi
100101
- if ! grep -q 'All FIFO tests passed!' "results_fifo.txt"; then exit 1; fi
101102
- if ! grep -q 'All scheduler tests passed!' "results_scheduler.txt"; then exit 1; fi
103+
- if ! grep -q 'All timer tests passed!' "results_timer.txt"; then exit 1; fi
102104
- cd ..
103105
- mv build /builds/aloxy/$CI_PROJECT_NAME/build
104106
artifacts:
@@ -141,12 +143,12 @@ build sample apps:
141143
- cmake ../stack/ -DAPP_SENSOR_MULTIMODAL=y -DAPP_SENSOR_PUSH_LORAWAN=y -DPLATFORM=$platform -DMODULE_LORAWAN=y -DFRAMEWORK_MODEM_INTERFACE_ENABLED=n -DMODULE_LORAWAN_REGION=MODEM_REGION_EU868
142144
- make -j
143145

144-
job 2:
146+
flash devkits:
145147
tags:
146148
- testsuite
147149
stage: flash test boards
148150
dependencies:
149-
- job 1
151+
- build primary apps
150152
script:
151153
- if $(lsof -i -P -n | grep -q JLinkExe) ; then exit 1; fi
152154
- if pgrep -f "python3 run.py" ; then exit 1; fi
@@ -157,7 +159,7 @@ job 2:
157159
- cat flashOutput2.txt
158160
- if grep -q 'FAILED\|ERROR' "flashOutput1.txt"; then exit 1; fi
159161
- if grep -q 'FAILED\|ERROR' "flashOutput2.txt"; then exit 1; fi
160-
job 3:
162+
run testsuite:
161163
image: python:3.8
162164
tags:
163165
- testsuite

stack/framework/components/timer/timer.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
#define HW_TIMER_ID 0
4141

42+
#define COUNTER_EVENTTIME_LIMIT TIMER_TICKS_PER_SEC * 20
4243
#define COUNTER_OVERFLOW_INCREASE (UINT32_C(1) << (8*sizeof(hwtimer_tick_t)))
4344

4445
// define inline functions from timer.h as extern
@@ -302,7 +303,8 @@ static bool configure_next_event()
302303
if(NG(next_event) != NO_EVENT)
303304
{
304305
next_fire_time = NG(timers)[NG(next_event)].next_event;
305-
if ( (((int32_t)next_fire_time) - ((int32_t)current_time) - timer_info->min_delay_ticks) <= 0 )
306+
// fire if the fire time is less than 20 seconds ago since this means we should have fired already (timer_calculate_difference takes overflow into account)
307+
if(timer_calculate_difference(next_fire_time, current_time + timer_info->min_delay_ticks) < COUNTER_EVENTTIME_LIMIT)
306308
{
307309
DPRINT("will be late, sched immediately\n\n");
308310
if(NG(timers)[NG(next_event)].f == 0)
@@ -375,7 +377,7 @@ timer_tick_t timer_calculate_difference(timer_tick_t start_time, timer_tick_t st
375377
{
376378
if(start_time <= stop_time)
377379
return stop_time - start_time;
378-
// if a rolloved happened, add both parts together
380+
// if a rollover happened, add both parts together
379381
else
380382
return (UINT32_MAX - start_time) + 1 + stop_time;
381383
}
@@ -385,11 +387,10 @@ static void timer_overflow()
385387
NG(timer_offset) += COUNTER_OVERFLOW_INCREASE;
386388
if(NG(next_event) != NO_EVENT && //there is an event scheduled at THIS timer level
387389
(!NG(hw_event_scheduled)) && //but NOT at the hw timer level
388-
NG(timers)[NG(next_event)].next_event <= (NG(timer_offset) + COUNTER_OVERFLOW_INCREASE) //and the next trigger will happen before the next overflow
390+
( (NG(timers)[NG(next_event)].next_event >= NG(timer_offset))
391+
&& (NG(timers)[NG(next_event)].next_event <= (NG(timer_offset) + COUNTER_OVERFLOW_INCREASE - 1) ) ) //and the next trigger will happen before the next overflow
389392
)
390393
{
391-
//normally this shouldn't happen. Put an assert here just to make sure
392-
assert(NG(timers)[NG(next_event)].next_event >= NG(timer_offset));
393394
timer_tick_t fire_time = (NG(timers)[NG(next_event)].next_event - NG(timer_offset));
394395

395396
//fire time already passed

stack/tests/error_event_file/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ cmake_minimum_required(VERSION 2.8)
2121

2222
add_executable(${PROJECT_NAME} main.c )
2323

24+
#link with the framework library that includes the error_event_file component
2425
target_link_libraries (${PROJECT_NAME} d7ap_fs framework )

stack/tests/fifo/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ cmake_minimum_required(VERSION 2.8)
2121

2222
add_executable(${PROJECT_NAME} main.c)
2323

24-
#link with the framework library that includes the AES library
24+
#link with the framework library that includes the fifo component
2525
target_link_libraries (${PROJECT_NAME} framework)

stack/tests/scheduler/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ cmake_minimum_required(VERSION 2.8)
2121

2222
add_executable(${PROJECT_NAME} main.c)
2323

24-
#link with the framework library that includes the AES library
24+
#link with the framework library that includes the scheduler component
2525
target_link_libraries (${PROJECT_NAME} framework)

stack/tests/timer/CMakeLists.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#[[
2+
Copyright (c) 2015-2021 University of Antwerp, Aloxy NV.
3+
4+
This file is part of Sub-IoT.
5+
See https://github.com/Sub-IoT/Sub-IoT-Stack for further info.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
]]
19+
project(test_timer)
20+
cmake_minimum_required(VERSION 2.8)
21+
22+
add_subdirectory(./mocks)
23+
24+
#create a lib out of the scheduler
25+
add_library(scheduler STATIC ../../framework/components/scheduler/scheduler.c)
26+
set_target_properties(scheduler PROPERTIES LINKER_LANGUAGE C)
27+
target_include_directories(scheduler PUBLIC ../../framework/inc ../../framework/hal/inc ./) #to include framework_defs.h
28+
29+
add_executable(${PROJECT_NAME} main.c ) #the main includes timer.c directly
30+
31+
target_include_directories(${PROJECT_NAME} PUBLIC ../../framework/components/timer ../../framework/inc ../../framework/hal/inc ./)
32+
target_link_libraries (${PROJECT_NAME} scheduler mocks)

stack/tests/timer/framework_defs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#define FRAMEWORK_TIMER_RESOLUTION 1MS
2+
#define FRAMEWORK_TIMER_STACK_SIZE 10
3+
#define FRAMEWORK_SCHEDULER_MAX_TASKS 41
4+
#define FRAMEWORK_SCHEDULER_LP_MODE 0
5+
#define FRAMEWORK_SCHEDULER_MAX_ACTIVE_TIME 0

stack/tests/timer/main.c

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/*
2+
* Copyright (c) 2015-2021 University of Antwerp, Aloxy NV.
3+
*
4+
* This file is part of Sub-IoT.
5+
* See https://github.com/Sub-IoT/Sub-IoT-Stack for further info.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
#include "stdio.h"
20+
#include <stdlib.h>
21+
#include "timer.c"
22+
23+
#define COUNTER_OVERFLOW_MAX 0xFFFF0000
24+
25+
static timer_event dummy_tester_event;
26+
static timer_event dummy_tester_event2;
27+
static timer_event dummy_tester_event3;
28+
static timer_event dummy_tester_event4;
29+
static timer_event dummy_tester_event5;
30+
static timer_event dummy_tester_event6;
31+
32+
void dummy_function(){}
33+
void dummy_function2(){}
34+
void dummy_function3(){}
35+
void dummy_function4(){}
36+
void dummy_function5(){}
37+
void dummy_function6(){}
38+
void dummy_function7(){}
39+
void dummy_function8(){}
40+
41+
void test_timer_events() {
42+
//can init event
43+
assert(timer_init_event(&dummy_tester_event, &dummy_function) == SUCCESS);
44+
//cannot init same event multiple times
45+
assert(timer_init_event(&dummy_tester_event, &dummy_function) == -EALREADY);
46+
//manually conf next_event
47+
dummy_tester_event.next_event = TIMER_TICKS_PER_SEC * 1;
48+
// can post a task from an event
49+
assert(timer_add_event(&dummy_tester_event) == SUCCESS);
50+
// after adding event, associated task is scheduled
51+
assert(timer_is_task_scheduled(dummy_tester_event.f));
52+
// cancel event
53+
timer_cancel_event(&dummy_tester_event);
54+
// after cancelling event, associated task is no longer scheduled
55+
assert(timer_is_task_scheduled(dummy_tester_event.f) == false);
56+
}
57+
58+
void test_timer_post_tasks() {
59+
// cannot post task with less than min priority
60+
assert(timer_post_task_prio_delay(&dummy_function2, TIMER_TICKS_PER_SEC * 5, MIN_PRIORITY + 1) == EINVAL);
61+
// can post task
62+
assert(timer_post_task_prio_delay(&dummy_function2, TIMER_TICKS_PER_SEC * 5, DEFAULT_PRIORITY) == SUCCESS);
63+
// after posting, task is scheduled
64+
assert(timer_is_task_scheduled(&dummy_function2));
65+
// can modify the fire time by posting the same task again
66+
assert(timer_post_task_prio_delay(&dummy_function2, TIMER_TICKS_PER_SEC * 7, DEFAULT_PRIORITY) == SUCCESS);
67+
// cannot modify the priority by posting the same task again
68+
assert(timer_post_task_prio_delay(&dummy_function2, TIMER_TICKS_PER_SEC * 7, DEFAULT_PRIORITY-1) == EALREADY);
69+
// can cancel the task
70+
assert(timer_cancel_task(&dummy_function2) == SUCCESS);
71+
// after cancelling, task is no longer scheduled
72+
assert(timer_is_task_scheduled(&dummy_function2) == false);
73+
// cannot cancel an unscheduled task
74+
assert(timer_cancel_task(&dummy_function2) == EALREADY);
75+
76+
// task posted with delay of 0 is scheduled immediately
77+
sched_register_task(&dummy_function3);
78+
assert(timer_post_task_prio_delay(&dummy_function3, TIMER_TICKS_PER_SEC * 0, DEFAULT_PRIORITY) == SUCCESS);
79+
assert(timer_is_task_scheduled(&dummy_function3) == false);
80+
}
81+
82+
void run_overflow_test(timer_tick_t timer_offset, timer_tick_t next_event_time) {
83+
NG(timer_offset) = timer_offset;
84+
NG(timers)[NG(next_event)].next_event = next_event_time;
85+
NG(hw_event_scheduled) = false;
86+
run_overflow_c();
87+
}
88+
void test_timer_overflow() {
89+
set_hw_timer_value(0);
90+
//regular cases:
91+
//create an event
92+
assert(timer_init_event(&dummy_tester_event2, &dummy_function4) == SUCCESS);
93+
dummy_tester_event2.next_event = TIMER_TICKS_PER_SEC * 1;
94+
assert(timer_add_event(&dummy_tester_event2) == SUCCESS);
95+
96+
// event is in the next period
97+
run_overflow_test(COUNTER_OVERFLOW_INCREASE * 10, COUNTER_OVERFLOW_INCREASE * 11 + 3000);
98+
assert(NG(hw_event_scheduled) == true); //i.e. event gets scheduled for the next period
99+
100+
// event is in the far future
101+
run_overflow_test(COUNTER_OVERFLOW_INCREASE * 10, COUNTER_OVERFLOW_INCREASE * 12 + 3000);
102+
assert( (NG(hw_event_scheduled) == false) && NG(timers)[NG(next_event)].f != 0x0 ); //i.e. event should not be scheduled yet
103+
104+
// event is in the distant past
105+
run_overflow_test(COUNTER_OVERFLOW_INCREASE * 10, 0);
106+
assert( (NG(hw_event_scheduled) == false) && NG(timers)[NG(next_event)].f != 0x0 ); //i.e. event doesn't get fired
107+
108+
// event is in this period but in near past (behind hw_timer time)
109+
set_hw_timer_value(4000);
110+
run_overflow_test(COUNTER_OVERFLOW_INCREASE * 10, COUNTER_OVERFLOW_INCREASE * 11 + 3000);
111+
assert( (NG(hw_event_scheduled) == false) && get_next_event() == NO_EVENT ); //i.e. event got fired immediately
112+
set_hw_timer_value(0);
113+
114+
timer_cancel_event(&dummy_tester_event2);
115+
116+
//overflow cases:
117+
//create another event
118+
assert(timer_init_event(&dummy_tester_event3, &dummy_function5) == SUCCESS);
119+
dummy_tester_event3.next_event = TIMER_TICKS_PER_SEC * 1;
120+
assert(timer_add_event(&dummy_tester_event3) == SUCCESS);
121+
122+
// event is in the next period
123+
run_overflow_test(COUNTER_OVERFLOW_MAX, 3000);
124+
assert(NG(hw_event_scheduled) == true); //i.e. event gets scheduled for the next period
125+
126+
// event is in the far future
127+
run_overflow_test(COUNTER_OVERFLOW_MAX, COUNTER_OVERFLOW_INCREASE + 3000);
128+
assert( (NG(hw_event_scheduled) == false) && NG(timers)[NG(next_event)].f != 0x0 ); //i.e. event should not be scheduled yet
129+
130+
// event is in the distant past
131+
set_hw_timer_value(0);
132+
run_overflow_test(0, COUNTER_OVERFLOW_MAX);
133+
assert( (NG(hw_event_scheduled) == false) && NG(timers)[NG(next_event)].f != 0x0 ); //i.e. event doesn't get fired
134+
135+
// event is in this period but in near past (behind hw_timer time)
136+
set_hw_timer_value(4000);
137+
run_overflow_test(COUNTER_OVERFLOW_MAX, 3000);
138+
assert( (NG(hw_event_scheduled) == false) && get_next_event() == NO_EVENT ); //i.e. event got fired immediately
139+
140+
timer_cancel_event(&dummy_tester_event3);
141+
}
142+
143+
void setup_counter_test(timer_tick_t timer_offset, uint16_t hw_timer_value, bool is_overflow_pending) {
144+
NG(timer_offset) = timer_offset;
145+
set_hw_timer_value(hw_timer_value);
146+
set_overflow_pending(is_overflow_pending);
147+
}
148+
149+
void test_timer_get_counter_value() {
150+
//regular cases:
151+
setup_counter_test(COUNTER_OVERFLOW_INCREASE, 0x00ff, false);
152+
assert(timer_get_counter_value() == COUNTER_OVERFLOW_INCREASE + 0x00ff);
153+
setup_counter_test(COUNTER_OVERFLOW_INCREASE, 0x00ff, true);
154+
assert(timer_get_counter_value() == COUNTER_OVERFLOW_INCREASE + 0x00ff + COUNTER_OVERFLOW_INCREASE);
155+
156+
//overflow cases:
157+
setup_counter_test(COUNTER_OVERFLOW_MAX, 0x00ff, false);
158+
assert(timer_get_counter_value() == COUNTER_OVERFLOW_MAX + 0x00ff);
159+
setup_counter_test(COUNTER_OVERFLOW_MAX, 0x00ff, true);
160+
assert(timer_get_counter_value() == COUNTER_OVERFLOW_MAX + 0x00ff + COUNTER_OVERFLOW_INCREASE);
161+
}
162+
163+
void test_timer_timed_events() {
164+
//reset time to 0
165+
NG(timer_offset) = 0;
166+
set_hw_timer_value(0);
167+
set_overflow_pending(false);
168+
169+
// - schedule three events of different times
170+
assert(timer_init_event(&dummy_tester_event4, &dummy_function6) == SUCCESS);
171+
dummy_tester_event4.next_event = TIMER_TICKS_PER_SEC * 1;
172+
assert(timer_init_event(&dummy_tester_event5, &dummy_function7) == SUCCESS);
173+
dummy_tester_event5.next_event = TIMER_TICKS_PER_SEC * 3;
174+
assert(timer_init_event(&dummy_tester_event6, &dummy_function8) == SUCCESS);
175+
dummy_tester_event6.next_event = TIMER_TICKS_PER_SEC * 5;
176+
177+
// post tasks from the events
178+
assert(timer_add_event(&dummy_tester_event4) == SUCCESS);
179+
assert(timer_add_event(&dummy_tester_event5) == SUCCESS);
180+
assert(timer_add_event(&dummy_tester_event6) == SUCCESS);
181+
182+
assert(timer_is_task_scheduled(dummy_tester_event4.f));
183+
// check event returned is as expected
184+
uint32_t next_event = get_next_event();
185+
assert(NG(timers)[next_event].f == dummy_tester_event4.f);
186+
// move time forward till after that event
187+
NG(timer_offset) = TIMER_TICKS_PER_SEC * 2;
188+
// events from the past should still get returned if they haven't fired yet
189+
next_event = get_next_event();
190+
assert(NG(timers)[next_event].f == dummy_tester_event4.f);
191+
timer_cancel_event(&dummy_tester_event4);
192+
next_event = get_next_event();
193+
assert(NG(timers)[next_event].f == dummy_tester_event5.f);
194+
timer_cancel_event(&dummy_tester_event5);
195+
next_event = get_next_event();
196+
assert(NG(timers)[next_event].f == dummy_tester_event6.f);
197+
timer_cancel_event(&dummy_tester_event6);
198+
//when no events listed, should return NO_EVENT
199+
next_event = get_next_event();
200+
assert(next_event == NO_EVENT);
201+
}
202+
203+
int main(int argc, char *argv[]){
204+
timer_init();
205+
scheduler_init();
206+
207+
test_timer_events();
208+
209+
test_timer_post_tasks();
210+
211+
test_timer_overflow();
212+
213+
test_timer_get_counter_value();
214+
215+
test_timer_timed_events();
216+
217+
printf("All timer tests passed!\n");
218+
219+
exit(0);
220+
}

0 commit comments

Comments
 (0)