-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP/POC: start a test where xtimer uses a fake 'periph_timer' mock implementation #10321
Draft
cladmi
wants to merge
3
commits into
RIOT-OS:master
Choose a base branch
from
cladmi:pr/wip/tests_xtimer_set_absolute
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
include ../Makefile.tests_common | ||
|
||
TEST_ON_CI_WHITELIST += all | ||
|
||
USEMODULE += embunit | ||
|
||
# Replace enabling xtimer through the build system to manually enabling it | ||
# It allows replacing periph_timer implementation for xtimer by including c | ||
# files | ||
## USEMODULE += xtimer | ||
|
||
# Dependencies | ||
INCLUDES += -I$(RIOTBASE)/sys/xtimer | ||
USEMODULE += div | ||
# Ignore periph_timer dependency it will be mocked | ||
|
||
DISABLE_MODULE += auto_init | ||
|
||
include $(RIOTBASE)/Makefile.include |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
/* | ||
* Copyright (C) 2018 Freie Universität Berlin | ||
* | ||
* This file is subject to the terms and conditions of the GNU Lesser | ||
* General Public License v2.1. See the file LICENSE in the top level | ||
* directory for more details. | ||
*/ | ||
|
||
/** | ||
* @ingroup tests | ||
* @{ | ||
* | ||
* @file | ||
* @brief xtimer mocked periph_timer test application | ||
* | ||
* Test xtimer using a mocked implementation of periph_timer | ||
* | ||
* @author Gaëtan Harter <gaetan.harter@fu-berlin.de> | ||
* @} | ||
*/ | ||
#include <stdio.h> | ||
|
||
#define ENABLE_DEBUG 0 | ||
#include "debug.h" | ||
|
||
#include "embUnit.h" | ||
|
||
/* | ||
* Mock 'xtimer' to use our periph_timer mock implementation | ||
* The '.c' files must be included to have access to private functions and | ||
* mock with the macros | ||
*/ | ||
#define timer_init timer_init_mock | ||
#define timer_read timer_read_mock | ||
#define timer_set_absolute timer_set_absolute_mock | ||
#include "xtimer.h" | ||
#include "xtimer.c" | ||
#include "xtimer_core.c" | ||
|
||
/* Allow configuring the mock through here */ | ||
#include "periph_timer_mock.h" | ||
|
||
|
||
static void test_timer_cb(void *arg) | ||
{ | ||
printf("test_timer_cb(%p)\n", arg); | ||
} | ||
|
||
|
||
|
||
/* This is a magic that should tell that it is the first call in | ||
* `_xtimer_set` | ||
*/ | ||
#define TIMER_SET_TICK_COUNT (1) | ||
|
||
/* | ||
* A magic value that is meant to make it work with the current | ||
* implementation | ||
*/ | ||
#define TIMER_SET_XTIMER_SET_OFFSET (XTIMER_BACKOFF) | ||
#define TIMER_SET_DESCHEDULE_TIME_TICK (TIMER_SET_XTIMER_SET_OFFSET +1) | ||
|
||
/* Simulate descheduling the task for XTIMER_BACKOFF after `_xtimer_now` in | ||
* `_xtimer_set_absolute` | ||
* | ||
* Simulating the de-scheduling with this method assumes we are not in masked | ||
* interrupts, which we currently are. | ||
* A proper test should just unlock a thread but this is for next step. | ||
*/ | ||
static int timer_set_deschedule_triggered = 0; | ||
static void _simulate_deschedule_in_timer_set(uint32_t now) | ||
{ | ||
if (now == TIMER_SET_TICK_COUNT && !timer_set_deschedule_triggered) { | ||
/* This is countered by being in an interrupt context */ | ||
DEBUG("De-schedule the thread at %" PRIu32 " for %u\n", | ||
now, TIMER_SET_DESCHEDULE_TIME_TICK); | ||
timer_set_deschedule_triggered = 1; | ||
timer_mock_set(now + TIMER_SET_DESCHEDULE_TIME_TICK); | ||
} | ||
|
||
} | ||
static void test_show_xtimer_set_call_target_in_the_past(void) | ||
{ | ||
xtimer_init(); | ||
timer_mock_set_post_step_cb(_simulate_deschedule_in_timer_set); | ||
|
||
xtimer_t timer_low_offset = {.callback = test_timer_cb}; | ||
|
||
/* TODO I only handle Freq 1MHz for now in this test */ | ||
TEST_ASSERT_MESSAGE(XTIMER_HZ == 1000000, | ||
"Only 1Mhz handled here for this first test"); | ||
|
||
|
||
xtimer_set(&timer_low_offset, TIMER_SET_XTIMER_SET_OFFSET); | ||
|
||
/* The test should not be in the `long_list_head` as it is close by */ | ||
int was_in_list = _remove_timer_from_list(&long_list_head, &timer_low_offset); | ||
TEST_ASSERT_MESSAGE(was_in_list == 0, | ||
"The timer has been placed in the long_list_head for a small sleep time"); | ||
} | ||
|
||
|
||
|
||
/* This is a magic that should tell that it is the first call in | ||
* `_xtimer_set_absolute` | ||
*/ | ||
#define SET_ABSOLUTE_TICK_COUNT (2) | ||
|
||
/* | ||
* A magic value that is meant to make it work with the current | ||
* implementation | ||
*/ | ||
#define SET_ABSOLUTE_XTIMER_SET_OFFSET (XTIMER_BACKOFF) | ||
#define SET_ABSOLUTE_DESCHEDULE_TIME_TICK (SET_ABSOLUTE_XTIMER_SET_OFFSET) | ||
|
||
/* Simulate descheduling the task for XTIMER_BACKOFF after `_xtimer_now` in | ||
* `_xtimer_set_absolute` | ||
* | ||
* Simulating the de-scheduling with this method assumes we are not in masked | ||
* interrupts, which we currently are. | ||
* A proper test should just unlock a thread but this is for next step. | ||
*/ | ||
static int set_absolute_deschedule_triggered = 0; | ||
static int set_absolute_full_loop = 0; | ||
static void _simulate_deschedule_in_set_absolute(uint32_t now) | ||
{ | ||
if (now == SET_ABSOLUTE_TICK_COUNT && !set_absolute_deschedule_triggered) { | ||
/* This is countered by being in an interrupt context */ | ||
DEBUG("De-schedule the thread at %" PRIu32 " for %u\n", | ||
now, SET_ABSOLUTE_DESCHEDULE_TIME_TICK); | ||
set_absolute_deschedule_triggered = 1; | ||
timer_mock_set(now + SET_ABSOLUTE_DESCHEDULE_TIME_TICK); | ||
} | ||
|
||
if (now == 200) { | ||
puts("Stuck in xtimer_spin_until for a full loop, jump out"); | ||
set_absolute_full_loop = 1; | ||
/* Step a lot in the future */ | ||
timer_mock_set((((uint64_t)1) << XTIMER_WIDTH) - 2); | ||
} | ||
} | ||
|
||
|
||
|
||
/* This test adds delay after the _xtimer_now() in `_xtimer_set_absolute` as if | ||
* it was de-scheduled enough time to make `target` in the past. | ||
* | ||
* This makes `xtimer_spin_until` be called with a target already elapsed. | ||
*/ | ||
static void test_show_xtimer_set_does_not_handle_overhead(void) | ||
{ | ||
xtimer_init(); | ||
timer_mock_set_post_step_cb(_simulate_deschedule_in_set_absolute); | ||
|
||
xtimer_t timer_low_offset = {.callback = test_timer_cb}; | ||
|
||
/* TODO I only handle Freq 1MHz for now in this test */ | ||
TEST_ASSERT_MESSAGE(XTIMER_HZ == 1000000, | ||
"Only 1Mhz handled here for this first test"); | ||
|
||
|
||
xtimer_set(&timer_low_offset, SET_ABSOLUTE_XTIMER_SET_OFFSET); | ||
DEBUG("Outside of xtimer_set\n"); | ||
TEST_ASSERT(!set_absolute_full_loop); | ||
} | ||
|
||
|
||
|
||
static TestRef tests_xtimer_low_level(void) | ||
{ | ||
EMB_UNIT_TESTFIXTURES(fixtures) { | ||
new_TestFixture(test_show_xtimer_set_call_target_in_the_past), | ||
new_TestFixture(test_show_xtimer_set_does_not_handle_overhead), | ||
|
||
}; | ||
EMB_UNIT_TESTCALLER(xtimer_low_level, NULL, NULL, fixtures); | ||
|
||
return (Test *)&xtimer_low_level; | ||
} | ||
|
||
int main(void) | ||
{ | ||
printf("XTIMER_WIDTH: %u\n", XTIMER_WIDTH); | ||
printf("XTIMER_HZ: %lu\n", XTIMER_HZ); | ||
printf("XTIMER_BACKOFF: %u\n", XTIMER_BACKOFF); | ||
|
||
TESTS_START(); | ||
TESTS_RUN(tests_xtimer_low_level()); | ||
TESTS_END(); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
#include <stdint.h> | ||
|
||
#define ENABLE_DEBUG 0 | ||
#include "debug.h" | ||
|
||
#include "periph/timer.h" | ||
#include "periph_timer_mock.h" | ||
|
||
|
||
static struct { | ||
tim_t dev; | ||
unsigned long freq; | ||
timer_cb_t cb; | ||
void *cb_arg; | ||
/* Handle only one channel for now */ | ||
int channel; | ||
unsigned int timeout_value; | ||
|
||
uint32_t current; | ||
|
||
void (*post_step_cb)(uint32_t current); | ||
} mock_timer; | ||
|
||
|
||
void timer_mock_set_post_step_cb(void (*post_step_cb)(uint32_t current)) | ||
{ | ||
mock_timer.post_step_cb = post_step_cb; | ||
} | ||
|
||
|
||
static void _timer_mock_step_one(void) | ||
{ | ||
/* TODO handle callbacks for timer */ | ||
mock_timer.current = (mock_timer.current == mock_timer.timeout_value ? | ||
0 : mock_timer.current + 1); | ||
if (mock_timer.current == mock_timer.timeout_value) { | ||
printf("periph_timer_mock: TODO Callback not handled\n"); | ||
} | ||
} | ||
|
||
|
||
uint32_t timer_mock_step(uint32_t ticks) | ||
{ | ||
DEBUG("timer_mock_step(%" PRIu32 ") from %" PRIu32 "\n", ticks, mock_timer.current); | ||
for (uint32_t i = 0; i < ticks; i++) { | ||
_timer_mock_step_one(); | ||
} | ||
|
||
uint32_t now = mock_timer.current; | ||
|
||
|
||
if (mock_timer.post_step_cb) { | ||
mock_timer.post_step_cb(mock_timer.current); | ||
} | ||
|
||
return now; | ||
} | ||
|
||
void timer_mock_set(uint32_t ticks) | ||
{ | ||
DEBUG("timer_mock_set(%" PRIu32 ")\n", ticks); | ||
/* TODO This should be lower than `mock_timer.timeout_value` to be handled | ||
* properly.*/ | ||
mock_timer.current = ticks; | ||
} | ||
|
||
|
||
static void timer_cb_mock(void *arg, int channel) | ||
{ | ||
(void)arg; | ||
(void)channel; | ||
} | ||
|
||
int timer_init_mock(tim_t dev, unsigned long freq, timer_cb_t cb, void *arg) | ||
{ | ||
DEBUG("timer_init_mock(%u): %" PRIu32 ", %p, %p\n", dev, freq, cb, arg); | ||
|
||
|
||
mock_timer.dev = dev; | ||
mock_timer.freq = freq; | ||
mock_timer.cb = cb; | ||
mock_timer.cb_arg = arg; | ||
|
||
mock_timer.current = 0; | ||
|
||
mock_timer.post_step_cb = NULL; | ||
|
||
(void)timer_cb_mock; | ||
|
||
return 0; | ||
} | ||
|
||
|
||
unsigned int timer_read_mock(tim_t dev) | ||
{ | ||
DEBUG("timer_read_mock(%u)\n", dev); | ||
return timer_mock_step(1); | ||
} | ||
|
||
int timer_set_absolute_mock(tim_t dev, int channel, unsigned int value) | ||
{ | ||
assert(channel == 0); /* Not handled more than one channel for the moment */ | ||
DEBUG("timer_set_absolute_mock(%u): channel %i, value %u\n", dev, channel, value); | ||
mock_timer.channel = channel; | ||
mock_timer.timeout_value = value; | ||
return 1; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#ifndef PERIPH_TIMER_MOCK_H | ||
#define PERIPH_TIMER_MOCK_H | ||
|
||
/* Put here any functions that could need to help communicate with the mock */ | ||
|
||
/* | ||
* Step the timer counter by 'ticks' and return the current ticks count after. | ||
*/ | ||
uint32_t timer_mock_step(uint32_t ticks); | ||
|
||
/* | ||
* Set the internal timer without handling any callbacks | ||
* Should fit the internal precisiong | ||
*/ | ||
void timer_mock_set(uint32_t ticks); | ||
|
||
/* | ||
* This simulates a function that would be called after the value of | ||
* `xtimer_now` is calculated | ||
* | ||
* And so deschedule after xtimer_now. | ||
*/ | ||
void timer_mock_set_post_step_cb(void (*post_step_cb)(uint32_t current)); | ||
|
||
#endif /* PERIPH_TIMER_MOCK_H */ |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it's still WIP, but there's a typo here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed