Skip to content

NCS36510: Fix the sporadic semaphore timing issue #3779

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

Merged
merged 4 commits into from
Feb 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion TESTS/events/queue/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ SIMPLE_POSTS_TEST(0)


void time_func(Timer *t, int ms) {
TEST_ASSERT_INT_WITHIN(2, ms, t->read_ms());
TEST_ASSERT_INT_WITHIN(5, ms, t->read_ms());
t->reset();
}

Expand Down
122 changes: 122 additions & 0 deletions TESTS/events/timing/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include "mbed_events.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

license missing here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker, we should definitely add licenses to our tests (more files are without as I noticed).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry, not sure how I ended up with a lot of licenseless test files.

#include "mbed.h"
#include "rtos.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include <cstdlib>
#include <cmath>

using namespace utest::v1;


// Test delay
#ifndef TEST_EVENTS_TIMING_TIME
#define TEST_EVENTS_TIMING_TIME 20000
#endif

#ifndef TEST_EVENTS_TIMING_MEAN
#define TEST_EVENTS_TIMING_MEAN 25
#endif

#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif

// Random number generation to skew timing values
float gauss(float mu, float sigma) {
float x = (float)rand() / ((float)RAND_MAX+1);
float y = (float)rand() / ((float)RAND_MAX+1);
float x2pi = x*2.0*M_PI;
float g2rad = sqrt(-2.0 * log(1.0-y));
float z = cos(x2pi) * g2rad;
return mu + z*sigma;
}

float chisq(float sigma) {
return pow(gauss(0, sqrt(sigma)), 2);
}


Timer timer;
DigitalOut led(LED1);

equeue_sema_t sema;

// Timer timing test
void timer_timing_test() {
timer.reset();
timer.start();
int prev = timer.read_us();

while (prev < TEST_EVENTS_TIMING_TIME*1000) {
int next = timer.read_us();
if (next < prev) {
printf("backwards drift %d -> %d (%08x -> %08x)\r\n",
prev, next, prev, next);
}
TEST_ASSERT(next >= prev);
prev = next;
}
}

// equeue tick timing test
void tick_timing_test() {
unsigned start = equeue_tick();
int prev = 0;

while (prev < TEST_EVENTS_TIMING_TIME) {
int next = equeue_tick() - start;
if (next < prev) {
printf("backwards drift %d -> %d (%08x -> %08x)\r\n",
prev, next, prev, next);
}
TEST_ASSERT(next >= prev);
prev = next;
}
}

// equeue semaphore timing test
void semaphore_timing_test() {
srand(0);
timer.reset();
timer.start();

int err = equeue_sema_create(&sema);
TEST_ASSERT_EQUAL(0, err);

while (timer.read_ms() < TEST_EVENTS_TIMING_TIME) {
int delay = chisq(TEST_EVENTS_TIMING_MEAN);

int start = timer.read_us();
equeue_sema_wait(&sema, delay);
int taken = timer.read_us() - start;

printf("delay %dms => error %dus\r\n", delay, abs(1000*delay - taken));
TEST_ASSERT_INT_WITHIN(5000, taken, delay * 1000);

led = !led;
}

equeue_sema_destroy(&sema);
}


// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases) {
GREENTEA_SETUP((number_of_cases+1)*TEST_EVENTS_TIMING_TIME, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}

const Case cases[] = {
Case("Testing accuracy of timer", timer_timing_test),
Case("Testing accuracy of equeue tick", tick_timing_test),
Case("Testing accuracy of equeue semaphore", semaphore_timing_test),
};

Specification specification(test_setup, cases);

int main() {
return !Harness::run(specification);
}

17 changes: 12 additions & 5 deletions events/equeue/equeue_mbed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@

// Ticker operations
static bool equeue_tick_inited = false;
static unsigned equeue_minutes = 0;
static volatile unsigned equeue_minutes = 0;
static unsigned equeue_timer[
(sizeof(Timer)+sizeof(unsigned)-1)/sizeof(unsigned)];
static unsigned equeue_ticker[
(sizeof(Ticker)+sizeof(unsigned)-1)/sizeof(unsigned)];

static void equeue_tick_update() {
equeue_minutes += reinterpret_cast<Timer*>(equeue_timer)->read_ms();
reinterpret_cast<Timer*>(equeue_timer)->reset();
equeue_minutes += 1;
}

static void equeue_tick_init() {
Expand All @@ -48,7 +48,7 @@ static void equeue_tick_init() {
equeue_minutes = 0;
reinterpret_cast<Timer*>(equeue_timer)->start();
reinterpret_cast<Ticker*>(equeue_ticker)
->attach_us(equeue_tick_update, (1 << 16)*1000);
->attach_us(equeue_tick_update, 1000 << 16);

equeue_tick_inited = true;
}
Expand All @@ -58,8 +58,15 @@ unsigned equeue_tick() {
equeue_tick_init();
}

unsigned equeue_ms = reinterpret_cast<Timer*>(equeue_timer)->read_ms();
return (equeue_minutes << 16) + equeue_ms;
unsigned minutes;
unsigned ms;

do {
minutes = equeue_minutes;
ms = reinterpret_cast<Timer*>(equeue_timer)->read_ms();
} while (minutes != equeue_minutes);

return minutes + ms;
}


Expand Down
27 changes: 16 additions & 11 deletions targets/TARGET_ONSEMI/TARGET_NCS36510/ncs36510_us_ticker_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ static int us_ticker_inited = 0;

static void us_timer_init(void);

static uint32_t us_ticker_int_counter = 0;
static uint32_t us_ticker_target = 0;
static volatile uint32_t msb_counter = 0;

void us_ticker_init(void)
Expand Down Expand Up @@ -168,20 +168,25 @@ extern void us_ticker_isr(void)
/* Clear IRQ flag */
TIM1REG->CLEAR = 0;

/* If this is a longer timer it will take multiple full hw counter cycles */
if (us_ticker_int_counter > 0) {
ticker_set(0xFFFF);
us_ticker_int_counter--;
} else {
int32_t delta = us_ticker_target - us_ticker_read();
if (delta <= 0) {
TIM1REG->CONTROL.BITS.ENABLE = False;
us_ticker_irq_handler();
} else {
// Clamp at max value of timer
if (delta > 0xFFFF) {
delta = 0xFFFF;
}

ticker_set(delta);
}
}

/* Set timer 1 ticker interrupt */
void us_ticker_set_interrupt(timestamp_t timestamp)
{
int32_t delta = (uint32_t)timestamp - us_ticker_read();
us_ticker_target = (uint32_t)timestamp;
int32_t delta = us_ticker_target - us_ticker_read();

if (delta <= 0) {
/* This event was in the past */
Expand All @@ -195,10 +200,10 @@ void us_ticker_set_interrupt(timestamp_t timestamp)
return;
}

/* Calculate how much delta falls outside the 16-bit counter range. */
/* You will have to perform a full timer overflow for each bit above */
/* that range. */
us_ticker_int_counter = (uint32_t)(delta >> 16);
// Clamp at max value of timer
if (delta > 0xFFFF) {
delta = 0xFFFF;
}

ticker_set(delta);
}