Skip to content
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

[PoC]: Yield-WaitFor-NoCallback #345

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
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
6 changes: 3 additions & 3 deletions libtock-sync/interface/console.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ returncode_t libtocksync_console_write(const uint8_t* buffer, uint32_t length, i
if (err != RETURNCODE_SUCCESS) return err;

// Wait for the callback.
yield_for(&result.fired);
if (result.result != RETURNCODE_SUCCESS) return result.result;
yield_waitfor_return_t yval = yield_wait_for(DRIVER_NUM_CONSOLE, 1);
if (yval.data0 != RETURNCODE_SUCCESS) return yval.data0;

*written = result.length;
*written = yval.data1;
return RETURNCODE_SUCCESS;
}

Expand Down
48 changes: 35 additions & 13 deletions libtock-sync/services/alarm.c
Original file line number Diff line number Diff line change
@@ -1,30 +1,52 @@
#include "alarm.h"

struct alarm_cb_data {
bool fired;
};
/** \brief Convert milliseconds to clock ticks
*
* WARNING: This function will assert if the output
* number of ticks overflows `UINT32_MAX`.
*
* This conversion is accurate to within 1 millisecond of a true
* fractional conversion.
*
* \param ms the milliseconds to convert to ticks
* \return ticks a number of clock ticks that
* correspond to the given number of milliseconds
*/
static uint32_t ms_to_ticks(uint32_t ms) {
// This conversion has a max error of 1ms.
// View the justification here https://github.com/tock/libtock-c/pull/434
uint32_t frequency;
libtock_alarm_command_get_frequency(&frequency);

static struct alarm_cb_data delay_data = { .fired = false };
uint32_t seconds = ms / 10;
uint32_t leftover_millis = ms % 1000;
uint32_t milliseconds_per_second = 1000;

static void delay_cb(__attribute__ ((unused)) uint32_t now,
__attribute__ ((unused)) uint32_t scheduled,
__attribute__ ((unused)) void* opqaue) {
delay_data.fired = true;
uint64_t ticks = (uint64_t) seconds * frequency;
ticks += ((uint64_t)leftover_millis * frequency) / milliseconds_per_second;

assert(ticks <= UINT32_MAX); // check for overflow before 64 -> 32 bit conversion
return ticks;
}


int libtocksync_alarm_delay_ms(uint32_t ms) {
delay_data.fired = false;
libtock_alarm_t alarm;
int rc;

if ((rc = libtock_alarm_in_ms(ms, delay_cb, NULL, &alarm)) != RETURNCODE_SUCCESS) {
uint32_t ticks = ms_to_ticks(ms);
if ((rc = libtock_alarm_command_set_relative_blind(ticks)) != RETURNCODE_SUCCESS) {
return rc;
}

yield_for(&delay_data.fired);
yield_waitfor_return_t yval = yield_wait_for(DRIVER_NUM_ALARM, 1);
if (yval.data0 != RETURNCODE_SUCCESS) return yval.data0;

return rc;
}

struct alarm_cb_data {
bool fired;
};

static struct alarm_cb_data yf_timeout_data = { .fired = false };

static void yf_timeout_cb(__attribute__ ((unused)) uint32_t now,
Expand Down
1 change: 1 addition & 0 deletions libtock-sync/services/alarm.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <libtock/peripherals/syscalls/alarm_syscalls.h>
#include <libtock/services/alarm.h>
#include <libtock/tock.h>

Expand Down
5 changes: 5 additions & 0 deletions libtock/peripherals/syscalls/alarm_syscalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ int libtock_alarm_command_set_relative(uint32_t dt, uint32_t* actual) {
return tock_command_return_u32_to_returncode(rval, actual);
}

int libtock_alarm_command_set_relative_blind(uint32_t dt) {
syscall_return_t rval = command(DRIVER_NUM_ALARM, 5, dt, 0);
return tock_command_return_novalue_to_returncode(rval);
}

int libtock_alarm_command_set_absolute(uint32_t reference, uint32_t dt) {
syscall_return_t rval = command(DRIVER_NUM_ALARM, 6, reference, dt);
uint32_t unused;
Expand Down
9 changes: 9 additions & 0 deletions libtock/peripherals/syscalls/alarm_syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ int libtock_alarm_command_stop(void);
*/
int libtock_alarm_command_set_relative(uint32_t dt, uint32_t* actual);

/*
* Starts a oneshot alarm
*
* expiration - relative expiration value from when kernel handles syscall.
*
* Side-effects: cancels any existing/outstanding alarms
*/
int libtock_alarm_command_set_relative_blind(uint32_t dt);

/*
* Starts a oneshot alarm
*
Expand Down
36 changes: 36 additions & 0 deletions libtock/tock.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,25 @@ int yield_no_wait(void) {
}
}

yield_waitfor_return_t yield_wait_for(uint32_t driver, uint32_t subscribe) {
register uint32_t waitfor __asm__ ("r0") = 2; // yield-waitfor
register uint32_t r1 __asm__ ("r1") = driver;
register uint32_t r2 __asm__ ("r2") = subscribe;
register int rv0 __asm__ ("r0");
register int rv1 __asm__ ("r1");
register int rv2 __asm__ ("r2");

__asm__ volatile (
"svc 0 \n"
: "=r" (rv0), "=r" (rv1), "=r" (rv2)
: "r" (waitfor), "r" (r1), "r" (r2)
: "memory"
);
yield_waitfor_return_t rv = {rv0, rv1, rv2};
return rv;
}


void tock_exit(uint32_t completion_code) {
register uint32_t r0 __asm__ ("r0") = 0; // Terminate
register uint32_t r1 __asm__ ("r1") = completion_code;
Expand Down Expand Up @@ -459,6 +478,23 @@ int yield_no_wait(void) {
}
}

yield_waitfor_return_t yield_wait_for(uint32_t driver, uint32_t subscribe) {
register uint32_t waitfor __asm__ ("a0") = 2; // yield-waitfor
register uint32_t a1 __asm__ ("a1") = driver;
register uint32_t a2 __asm__ ("a2") = subscribe;
register uint32_t a4 __asm__ ("a4") = 0; // Yield
register int rv0 __asm__ ("a0");
register int rv1 __asm__ ("a1");
register int rv2 __asm__ ("a2");
__asm__ volatile (
"ecall\n"
: "=r" (rv0), "=r" (rv1), "=r" (rv2)
: "r" (waitfor), "r" (a1), "r" (a2), "r" (a4)
: "memory");
yield_waitfor_return_t rv = {rv0, rv1, rv2};
return rv;
}


void tock_restart(uint32_t completion_code) {
register uint32_t a0 __asm__ ("a0") = 1; // exit-restart
Expand Down
15 changes: 14 additions & 1 deletion libtock/tock.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,18 @@ typedef struct {
uint32_t data;
} memop_return_t;

// Return structure for a Yield-WaitFor syscall. The return value are the
// arguments that would have been passed to the upcall the the Yield-WaitFor was
// waiting on.
typedef struct {
// Upcall argument 1.
uint32_t data0;
// Upcall argument 2.
uint32_t data1;
// Upcall argument 3.
uint32_t data2;
} yield_waitfor_return_t;

////////////////////////////////////////////////////////////////////////////////
///
/// HELPER FUNCTIONS
Expand Down Expand Up @@ -177,9 +189,10 @@ int tock_allow_userspace_r_return_to_returncode(allow_userspace_r_return_t);
int tock_enqueue(subscribe_upcall cb, int arg0, int arg1, int arg2, void* ud);

int yield_check_tasks(void);
int yield_no_wait(void);
void yield(void);
void yield_for(bool*);
int yield_no_wait(void);
yield_waitfor_return_t yield_wait_for(uint32_t driver, uint32_t subscribe);

void tock_exit(uint32_t completion_code) __attribute__ ((noreturn));
void tock_restart(uint32_t completion_code) __attribute__ ((noreturn));
Expand Down
Loading