mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-19 23:53:55 +00:00
Move timer from os to base repository
Since the timer and timeout handling is part of the base library (the dynamic linker), it belongs to the base repository. Besides moving the timer and its related infrastructure (alarm, timeout libs, tests) to the base repository, this patch also moves the timer from the 'drivers' subdirectory directly to 'src' and disamibuates the timer's build locations for the various kernels. Otherwise the different timer implementations could interfere with each other when using one build directory with multiple kernels. Note that this patch changes the include paths for the former os/timer, os/alarm.h, os/duration.h, and os/timed_semaphore.h to base/. Issue #3101
This commit is contained in:
293
repos/base/src/lib/alarm/alarm.cc
Normal file
293
repos/base/src/lib/alarm/alarm.cc
Normal file
@ -0,0 +1,293 @@
|
||||
/*
|
||||
* \brief Timed event scheduler
|
||||
* \date 2005-10-24
|
||||
* \author Norman Feske
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2005-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <base/log.h>
|
||||
#include <base/alarm.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Alarm_scheduler::_unsynchronized_enqueue(Alarm *alarm)
|
||||
{
|
||||
if (alarm->_active) {
|
||||
error("trying to insert the same alarm twice!");
|
||||
return;
|
||||
}
|
||||
|
||||
alarm->_active++;
|
||||
|
||||
/* if alarmlist is empty add first element */
|
||||
if (!_head) {
|
||||
alarm->_next = 0;
|
||||
_head = alarm;
|
||||
return;
|
||||
}
|
||||
|
||||
/* if deadline is smaller than any other deadline, put it on the head */
|
||||
if (alarm->_raw.is_pending_at(_head->_raw.deadline, _head->_raw.deadline_period)) {
|
||||
alarm->_next = _head;
|
||||
_head = alarm;
|
||||
return;
|
||||
}
|
||||
|
||||
/* find list element with a higher deadline */
|
||||
Alarm *curr = _head;
|
||||
while (curr->_next &&
|
||||
curr->_next->_raw.is_pending_at(alarm->_raw.deadline, alarm->_raw.deadline_period))
|
||||
{
|
||||
curr = curr->_next;
|
||||
}
|
||||
|
||||
/* if end of list is reached, append new element */
|
||||
if (curr->_next == 0) {
|
||||
curr->_next = alarm;
|
||||
return;
|
||||
}
|
||||
|
||||
/* insert element in middle of list */
|
||||
alarm->_next = curr->_next;
|
||||
curr->_next = alarm;
|
||||
}
|
||||
|
||||
|
||||
void Alarm_scheduler::_unsynchronized_dequeue(Alarm *alarm)
|
||||
{
|
||||
if (!_head) return;
|
||||
|
||||
if (_head == alarm) {
|
||||
_head = alarm->_next;
|
||||
alarm->_reset();
|
||||
return;
|
||||
}
|
||||
|
||||
/* find predecessor in alarm queue */
|
||||
Alarm *curr;
|
||||
for (curr = _head; curr && (curr->_next != alarm); curr = curr->_next);
|
||||
|
||||
/* alarm is not enqueued */
|
||||
if (!curr) return;
|
||||
|
||||
/* remove alarm from alarm queue */
|
||||
curr->_next = alarm->_next;
|
||||
alarm->_reset();
|
||||
}
|
||||
|
||||
|
||||
bool Alarm::Raw::is_pending_at(unsigned long time, bool time_period) const
|
||||
{
|
||||
return (time_period == deadline_period &&
|
||||
time >= deadline) ||
|
||||
(time_period != deadline_period &&
|
||||
time < deadline);
|
||||
}
|
||||
|
||||
|
||||
Alarm *Alarm_scheduler::_get_pending_alarm()
|
||||
{
|
||||
Lock::Guard lock_guard(_lock);
|
||||
|
||||
if (!_head || !_head->_raw.is_pending_at(_now, _now_period)) {
|
||||
return nullptr; }
|
||||
|
||||
/* remove alarm from head of the list */
|
||||
Alarm *pending_alarm = _head;
|
||||
_head = _head->_next;
|
||||
|
||||
/*
|
||||
* Acquire dispatch lock to defer destruction until the call of 'on_alarm'
|
||||
* is finished
|
||||
*/
|
||||
pending_alarm->_dispatch_lock.lock();
|
||||
|
||||
/* reset alarm object */
|
||||
pending_alarm->_next = nullptr;
|
||||
pending_alarm->_active--;
|
||||
|
||||
return pending_alarm;
|
||||
}
|
||||
|
||||
|
||||
void Alarm_scheduler::handle(Alarm::Time curr_time)
|
||||
{
|
||||
/*
|
||||
* Raise the time counter and if it wraps, update also in which
|
||||
* period of the time counter we are.
|
||||
*/
|
||||
if (_now > curr_time) {
|
||||
_now_period = !_now_period;
|
||||
}
|
||||
_now = curr_time;
|
||||
|
||||
if (!_min_handle_period.is_pending_at(_now, _now_period)) {
|
||||
return;
|
||||
}
|
||||
Alarm::Time const deadline = _now + _min_handle_period.period;
|
||||
_min_handle_period.deadline = deadline;
|
||||
_min_handle_period.deadline_period = _now > deadline ?
|
||||
!_now_period : _now_period;
|
||||
|
||||
Alarm *curr;
|
||||
while ((curr = _get_pending_alarm())) {
|
||||
|
||||
unsigned long triggered = 1;
|
||||
|
||||
if (curr->_raw.period) {
|
||||
Alarm::Time deadline = curr->_raw.deadline;
|
||||
|
||||
/* schedule next event */
|
||||
if (deadline == 0)
|
||||
deadline = curr_time;
|
||||
|
||||
triggered += (curr_time - deadline) / curr->_raw.period;
|
||||
}
|
||||
|
||||
/* do not reschedule if alarm function returns 0 */
|
||||
bool reschedule = curr->on_alarm(triggered);
|
||||
|
||||
if (reschedule) {
|
||||
|
||||
/*
|
||||
* At this point, the alarm deadline normally is somewhere near
|
||||
* the current time but If the alarm had no deadline by now,
|
||||
* initialize it with the current time.
|
||||
*/
|
||||
if (curr->_raw.deadline == 0) {
|
||||
curr->_raw.deadline = _now;
|
||||
curr->_raw.deadline_period = _now_period;
|
||||
}
|
||||
/*
|
||||
* Raise the deadline value by one period of the alarm and
|
||||
* if the deadline value wraps thereby, update also in which
|
||||
* period it is located.
|
||||
*/
|
||||
Alarm::Time const deadline = curr->_raw.deadline +
|
||||
triggered * curr->_raw.period;
|
||||
if (curr->_raw.deadline > deadline) {
|
||||
curr->_raw.deadline_period = !curr->_raw.deadline_period;
|
||||
}
|
||||
curr->_raw.deadline = deadline;
|
||||
|
||||
/* synchronize enqueue operation */
|
||||
Lock::Guard lock_guard(_lock);
|
||||
_unsynchronized_enqueue(curr);
|
||||
}
|
||||
|
||||
/* release alarm, resume concurrent destructor operation */
|
||||
curr->_dispatch_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Alarm_scheduler::_setup_alarm(Alarm &alarm, Alarm::Time period, Alarm::Time deadline)
|
||||
{
|
||||
/*
|
||||
* If the alarm is already present in the queue, re-consider its queue
|
||||
* position because its deadline might have changed. I.e., if an alarm is
|
||||
* rescheduled with a new timeout before the original timeout triggered.
|
||||
*/
|
||||
if (alarm._active)
|
||||
_unsynchronized_dequeue(&alarm);
|
||||
|
||||
alarm._assign(period, deadline, _now > deadline ? !_now_period : _now_period, this);
|
||||
|
||||
_unsynchronized_enqueue(&alarm);
|
||||
}
|
||||
|
||||
|
||||
void Alarm_scheduler::schedule_absolute(Alarm *alarm, Alarm::Time timeout)
|
||||
{
|
||||
Lock::Guard alarm_list_lock_guard(_lock);
|
||||
|
||||
_setup_alarm(*alarm, 0, timeout);
|
||||
}
|
||||
|
||||
|
||||
void Alarm_scheduler::schedule(Alarm *alarm, Alarm::Time period)
|
||||
{
|
||||
Lock::Guard alarm_list_lock_guard(_lock);
|
||||
|
||||
/*
|
||||
* Refuse to schedule a periodic timeout of 0 because it would trigger
|
||||
* infinitely in the 'handle' function. To account for the case where the
|
||||
* alarm object was already scheduled, we make sure to remove it from the
|
||||
* queue.
|
||||
*/
|
||||
if (period == 0) {
|
||||
_unsynchronized_dequeue(alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
/* first deadline is overdue */
|
||||
_setup_alarm(*alarm, period, _now);
|
||||
}
|
||||
|
||||
|
||||
void Alarm_scheduler::discard(Alarm *alarm)
|
||||
{
|
||||
/*
|
||||
* Make sure that nobody is inside the '_get_pending_alarm' when
|
||||
* grabbing the '_dispatch_lock'. This is important when this function
|
||||
* is called from the 'Alarm' destructor. Without the '_dispatch_lock',
|
||||
* we could take the lock and proceed with destruction just before
|
||||
* '_get_pending_alarm' tries to grab the lock. When the destructor is
|
||||
* finished, '_get_pending_alarm' would proceed with operating on a
|
||||
* dangling pointer.
|
||||
*/
|
||||
Lock::Guard alarm_list_lock_guard(_lock);
|
||||
|
||||
if (alarm) {
|
||||
Lock::Guard alarm_lock_guard(alarm->_dispatch_lock);
|
||||
_unsynchronized_dequeue(alarm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Alarm_scheduler::next_deadline(Alarm::Time *deadline)
|
||||
{
|
||||
Lock::Guard alarm_list_lock_guard(_lock);
|
||||
|
||||
if (!_head) return false;
|
||||
|
||||
if (deadline)
|
||||
*deadline = _head->_raw.deadline;
|
||||
|
||||
if (deadline && *deadline < _min_handle_period.deadline) {
|
||||
*deadline = _min_handle_period.deadline;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Alarm_scheduler::~Alarm_scheduler()
|
||||
{
|
||||
Lock::Guard lock_guard(_lock);
|
||||
|
||||
while (_head) {
|
||||
|
||||
Alarm *next = _head->_next;
|
||||
|
||||
/* reset alarm object */
|
||||
_head->_reset();
|
||||
|
||||
/* remove from list */
|
||||
_head = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Alarm::~Alarm()
|
||||
{
|
||||
if (_scheduler)
|
||||
_scheduler->discard(this);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include <util/string.h>
|
||||
#include <base/thread.h>
|
||||
#include <base/heap.h>
|
||||
#include <os/timed_semaphore.h>
|
||||
#include <base/timed_semaphore.h>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/unmanaged_singleton.h>
|
||||
|
35
repos/base/src/lib/timed_semaphore/timed_semaphore.cc
Normal file
35
repos/base/src/lib/timed_semaphore/timed_semaphore.cc
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* \brief Semaphore implementation with timeout facility.
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2010-03-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <base/timed_semaphore.h>
|
||||
|
||||
|
||||
Genode::Env *Genode::Timeout_thread::_env = nullptr;
|
||||
|
||||
|
||||
void Genode::Timeout_thread::entry()
|
||||
{
|
||||
while (true) {
|
||||
Signal s = _receiver.wait_for_signal();
|
||||
|
||||
/* handle timouts of this point in time */
|
||||
Genode::Alarm_scheduler::handle(_timer.elapsed_ms());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Genode::Timeout_thread *Genode::Timeout_thread::alarm_timer()
|
||||
{
|
||||
static Timeout_thread _alarm_timer;
|
||||
return &_alarm_timer;
|
||||
}
|
32
repos/base/src/lib/timeout/arm/timer_connection_time.cc
Normal file
32
repos/base/src/lib/timeout/arm/timer_connection_time.cc
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* \brief Connection to timer service and timeout scheduler
|
||||
* \author Martin Stein
|
||||
* \date 2016-11-04
|
||||
*
|
||||
* On ARM, we do not have a component-local hardware time-source. The ARM
|
||||
* performance counter has no reliable frequency as the ARM idle command
|
||||
* halts the counter. Thus, we do not do local time interpolation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <timer_session/connection.h>
|
||||
#include <base/internal/globals.h>
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Genode::Trace;
|
||||
|
||||
Timestamp Timer::Connection::_timestamp() { return 0ULL; }
|
||||
|
||||
void Timer::Connection::_update_real_time() { }
|
||||
|
||||
Duration Timer::Connection::curr_time()
|
||||
{
|
||||
return Duration(Microseconds(elapsed_us()));
|
||||
}
|
90
repos/base/src/lib/timeout/duration.cc
Normal file
90
repos/base/src/lib/timeout/duration.cc
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* \brief A duration type for both highly precise and long durations
|
||||
* \author Martin Stein
|
||||
* \date 2017-03-21
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/duration.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Duration::_raise_hours(unsigned long hours)
|
||||
{
|
||||
unsigned long const old_hours = _hours;
|
||||
_hours += hours;
|
||||
if (_hours < old_hours) {
|
||||
throw Overflow(); }
|
||||
}
|
||||
|
||||
|
||||
void Duration::_add_us_less_than_an_hour(unsigned long us)
|
||||
{
|
||||
unsigned long const us_until_next_hr =
|
||||
(unsigned long)US_PER_HOUR - _microseconds;
|
||||
|
||||
if (us >= us_until_next_hr) {
|
||||
_raise_hours(1);
|
||||
_microseconds = us - us_until_next_hr;
|
||||
} else {
|
||||
_microseconds += us;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Duration::add(Microseconds us)
|
||||
{
|
||||
/* filter out hours if any */
|
||||
if (us.value >= (unsigned long)US_PER_HOUR) {
|
||||
unsigned long const hours = us.value / US_PER_HOUR;
|
||||
_raise_hours(hours);
|
||||
us.value -= hours * US_PER_HOUR;
|
||||
}
|
||||
/* add the rest */
|
||||
_add_us_less_than_an_hour(us.value);
|
||||
}
|
||||
|
||||
|
||||
void Duration::add(Milliseconds ms)
|
||||
{
|
||||
/* filter out hours if any */
|
||||
if (ms.value >= MS_PER_HOUR) {
|
||||
unsigned long const hours = ms.value / MS_PER_HOUR;
|
||||
_raise_hours(hours);
|
||||
ms.value -= hours * MS_PER_HOUR;
|
||||
}
|
||||
/* add the rest as microseconds value */
|
||||
_add_us_less_than_an_hour(ms.value * US_PER_MS);
|
||||
}
|
||||
|
||||
|
||||
bool Duration::less_than(Duration const &other) const
|
||||
{
|
||||
if (_hours != other._hours) {
|
||||
return _hours < other._hours; }
|
||||
|
||||
if (_microseconds != other._microseconds) {
|
||||
return _microseconds < other._microseconds; }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Microseconds Duration::trunc_to_plain_us() const
|
||||
{
|
||||
return Microseconds(_microseconds + (_hours ? _hours * US_PER_HOUR : 0));
|
||||
}
|
||||
|
||||
|
||||
Milliseconds Duration::trunc_to_plain_ms() const
|
||||
{
|
||||
return Milliseconds(trunc_to_plain_us().value / 1000);
|
||||
}
|
29
repos/base/src/lib/timeout/hw/timer_connection_timestamp.cc
Normal file
29
repos/base/src/lib/timeout/hw/timer_connection_timestamp.cc
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* \brief Timestamp implementation for the Genode Timer
|
||||
* \author Martin Stein
|
||||
* \date 2016-11-04
|
||||
*
|
||||
* On ARM, we do not have a component-local hardware time-source. The ARM
|
||||
* performance counter has no reliable frequency as the ARM idle command
|
||||
* halts the counter. However, on the HW kernel, we use a syscall that reads
|
||||
* out the kernel time instead.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <kernel/interface.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
Trace::Timestamp Timer::Connection::_timestamp()
|
||||
{
|
||||
return Kernel::time();
|
||||
}
|
424
repos/base/src/lib/timeout/timeout.cc
Normal file
424
repos/base/src/lib/timeout/timeout.cc
Normal file
@ -0,0 +1,424 @@
|
||||
/*
|
||||
* \brief Multiplexing one time source amongst different timeout subjects
|
||||
* \author Martin Stein
|
||||
* \date 2016-11-04
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <timer/timeout.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/*************
|
||||
** Timeout **
|
||||
*************/
|
||||
|
||||
void Timeout::schedule_periodic(Microseconds duration, Handler &handler)
|
||||
{
|
||||
_alarm.handler = &handler;
|
||||
_alarm.periodic = true;
|
||||
_alarm.timeout_scheduler._schedule_periodic(*this, duration);
|
||||
}
|
||||
|
||||
|
||||
void Timeout::schedule_one_shot(Microseconds duration, Handler &handler)
|
||||
{
|
||||
_alarm.handler = &handler;
|
||||
_alarm.periodic = false;
|
||||
_alarm.timeout_scheduler._schedule_one_shot(*this, duration);
|
||||
}
|
||||
|
||||
|
||||
void Timeout::discard()
|
||||
{
|
||||
_alarm.timeout_scheduler._discard(*this);
|
||||
_alarm.handler = nullptr;
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
** Timeout::Alarm **
|
||||
********************/
|
||||
|
||||
bool Timeout::Alarm::_on_alarm(unsigned)
|
||||
{
|
||||
if (handler) {
|
||||
Handler *current = handler;
|
||||
if (!periodic) {
|
||||
handler = nullptr;
|
||||
}
|
||||
current->handle_timeout(timeout_scheduler.curr_time());
|
||||
}
|
||||
return periodic;
|
||||
}
|
||||
|
||||
|
||||
Timeout::Alarm::~Alarm()
|
||||
{
|
||||
if (_scheduler)
|
||||
_scheduler->_alarm_discard(this);
|
||||
}
|
||||
|
||||
|
||||
bool Timeout::Alarm::Raw::is_pending_at(unsigned long time, bool time_period) const
|
||||
{
|
||||
return (time_period == deadline_period &&
|
||||
time >= deadline) ||
|
||||
(time_period != deadline_period &&
|
||||
time < deadline);
|
||||
}
|
||||
|
||||
|
||||
/*****************************
|
||||
** Alarm_timeout_scheduler **
|
||||
*****************************/
|
||||
|
||||
void Alarm_timeout_scheduler::handle_timeout(Duration duration)
|
||||
{
|
||||
unsigned long const curr_time_us = duration.trunc_to_plain_us().value;
|
||||
|
||||
_alarm_handle(curr_time_us);
|
||||
|
||||
/* sleep time is either until the next deadline or the maximum timout */
|
||||
unsigned long sleep_time_us;
|
||||
Alarm::Time deadline_us;
|
||||
if (_alarm_next_deadline(&deadline_us)) {
|
||||
sleep_time_us = deadline_us - curr_time_us;
|
||||
} else {
|
||||
sleep_time_us = _time_source.max_timeout().value; }
|
||||
|
||||
/* limit max timeout to a more reasonable value, e.g. 60s */
|
||||
if (sleep_time_us > 60000000) {
|
||||
sleep_time_us = 60000000;
|
||||
} else if (sleep_time_us == 0) {
|
||||
sleep_time_us = 1; }
|
||||
|
||||
_time_source.schedule_timeout(Microseconds(sleep_time_us), *this);
|
||||
}
|
||||
|
||||
|
||||
Alarm_timeout_scheduler::Alarm_timeout_scheduler(Time_source &time_source,
|
||||
Microseconds min_handle_period)
|
||||
:
|
||||
_time_source(time_source)
|
||||
{
|
||||
Alarm::Time const deadline = _now + min_handle_period.value;
|
||||
_min_handle_period.period = min_handle_period.value;
|
||||
_min_handle_period.deadline = deadline;
|
||||
_min_handle_period.deadline_period = _now > deadline ?
|
||||
!_now_period : _now_period;
|
||||
}
|
||||
|
||||
|
||||
Alarm_timeout_scheduler::~Alarm_timeout_scheduler()
|
||||
{
|
||||
Lock::Guard lock_guard(_lock);
|
||||
while (_active_head) {
|
||||
Alarm *next = _active_head->_next;
|
||||
_active_head->_alarm_reset();
|
||||
_active_head = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Alarm_timeout_scheduler::_enable()
|
||||
{
|
||||
_time_source.schedule_timeout(Microseconds(0), *this);
|
||||
}
|
||||
|
||||
|
||||
void Alarm_timeout_scheduler::_schedule_one_shot(Timeout &timeout,
|
||||
Microseconds duration)
|
||||
{
|
||||
/* raise timeout duration by the age of the local time value */
|
||||
unsigned long us = _time_source.curr_time().trunc_to_plain_us().value;
|
||||
if (us >= _now) {
|
||||
us = duration.value + (us - _now); }
|
||||
else {
|
||||
us = duration.value + (~0UL - _now) + us; }
|
||||
if (us >= duration.value) {
|
||||
duration.value = us; }
|
||||
|
||||
/* insert timeout into scheduling queue */
|
||||
_alarm_schedule_absolute(&timeout._alarm, duration.value);
|
||||
|
||||
/* if new timeout is the closest to now, update the time-source timeout */
|
||||
if (_alarm_head_timeout(&timeout._alarm)) {
|
||||
_time_source.schedule_timeout(Microseconds(0), *this); }
|
||||
}
|
||||
|
||||
|
||||
void Alarm_timeout_scheduler::_schedule_periodic(Timeout &timeout,
|
||||
Microseconds duration)
|
||||
{
|
||||
_alarm_schedule(&timeout._alarm, duration.value);
|
||||
|
||||
if (_alarm_head_timeout(&timeout._alarm)) {
|
||||
_time_source.schedule_timeout(Microseconds(0), *this); }
|
||||
}
|
||||
|
||||
|
||||
void Alarm_timeout_scheduler::_alarm_unsynchronized_enqueue(Alarm *alarm)
|
||||
{
|
||||
if (alarm->_active) {
|
||||
error("trying to insert the same alarm twice!");
|
||||
return;
|
||||
}
|
||||
|
||||
alarm->_active++;
|
||||
|
||||
/* if active alarm list is empty add first element */
|
||||
if (!_active_head) {
|
||||
alarm->_next = 0;
|
||||
_active_head = alarm;
|
||||
return;
|
||||
}
|
||||
|
||||
/* if deadline is smaller than any other deadline, put it on the head */
|
||||
if (alarm->_raw.is_pending_at(_active_head->_raw.deadline, _active_head->_raw.deadline_period)) {
|
||||
alarm->_next = _active_head;
|
||||
_active_head = alarm;
|
||||
return;
|
||||
}
|
||||
|
||||
/* find list element with a higher deadline */
|
||||
Alarm *curr = _active_head;
|
||||
while (curr->_next &&
|
||||
curr->_next->_raw.is_pending_at(alarm->_raw.deadline, alarm->_raw.deadline_period))
|
||||
{
|
||||
curr = curr->_next;
|
||||
}
|
||||
|
||||
/* if end of list is reached, append new element */
|
||||
if (curr->_next == 0) {
|
||||
curr->_next = alarm;
|
||||
return;
|
||||
}
|
||||
|
||||
/* insert element in middle of list */
|
||||
alarm->_next = curr->_next;
|
||||
curr->_next = alarm;
|
||||
}
|
||||
|
||||
|
||||
void Alarm_timeout_scheduler::_alarm_unsynchronized_dequeue(Alarm *alarm)
|
||||
{
|
||||
if (!_active_head) return;
|
||||
|
||||
if (_active_head == alarm) {
|
||||
_active_head = alarm->_next;
|
||||
alarm->_alarm_reset();
|
||||
return;
|
||||
}
|
||||
|
||||
/* find predecessor in alarm queue */
|
||||
Alarm *curr;
|
||||
for (curr = _active_head; curr && (curr->_next != alarm); curr = curr->_next);
|
||||
|
||||
/* alarm is not enqueued */
|
||||
if (!curr) return;
|
||||
|
||||
/* remove alarm from alarm queue */
|
||||
curr->_next = alarm->_next;
|
||||
alarm->_alarm_reset();
|
||||
}
|
||||
|
||||
|
||||
Timeout::Alarm *Alarm_timeout_scheduler::_alarm_get_pending_alarm()
|
||||
{
|
||||
Lock::Guard lock_guard(_lock);
|
||||
|
||||
if (!_active_head || !_active_head->_raw.is_pending_at(_now, _now_period)) {
|
||||
return nullptr; }
|
||||
|
||||
/* remove alarm from head of the list */
|
||||
Alarm *pending_alarm = _active_head;
|
||||
_active_head = _active_head->_next;
|
||||
|
||||
/*
|
||||
* Acquire dispatch lock to defer destruction until the call of '_on_alarm'
|
||||
* is finished
|
||||
*/
|
||||
pending_alarm->_dispatch_lock.lock();
|
||||
|
||||
/* reset alarm object */
|
||||
pending_alarm->_next = nullptr;
|
||||
pending_alarm->_active--;
|
||||
|
||||
return pending_alarm;
|
||||
}
|
||||
|
||||
|
||||
void Alarm_timeout_scheduler::_alarm_handle(Alarm::Time curr_time)
|
||||
{
|
||||
/*
|
||||
* Raise the time counter and if it wraps, update also in which
|
||||
* period of the time counter we are.
|
||||
*/
|
||||
if (_now > curr_time) {
|
||||
_now_period = !_now_period;
|
||||
}
|
||||
_now = curr_time;
|
||||
|
||||
if (!_min_handle_period.is_pending_at(_now, _now_period)) {
|
||||
return;
|
||||
}
|
||||
Alarm::Time const deadline = _now + _min_handle_period.period;
|
||||
_min_handle_period.deadline = deadline;
|
||||
_min_handle_period.deadline_period = _now > deadline ?
|
||||
!_now_period : _now_period;
|
||||
|
||||
/*
|
||||
* Dequeue all pending alarms before starting to re-schedule. Otherwise,
|
||||
* a long-lasting alarm that has a deadline in the next now_period might
|
||||
* get scheduled as head of this now_period falsely because the code
|
||||
* thinks that it belongs to the last now_period.
|
||||
*/
|
||||
while (Alarm *curr = _alarm_get_pending_alarm()) {
|
||||
|
||||
/* enqueue alarm into list of pending alarms */
|
||||
curr->_next = _pending_head;
|
||||
_pending_head = curr;
|
||||
}
|
||||
while (Alarm *curr = _pending_head) {
|
||||
|
||||
/* dequeue alarm from list of pending alarms */
|
||||
_pending_head = _pending_head->_next;
|
||||
curr->_next = nullptr;
|
||||
|
||||
unsigned long triggered = 1;
|
||||
|
||||
if (curr->_raw.period) {
|
||||
Alarm::Time deadline = curr->_raw.deadline;
|
||||
|
||||
/* schedule next event */
|
||||
if (deadline == 0)
|
||||
deadline = curr_time;
|
||||
|
||||
triggered += (curr_time - deadline) / curr->_raw.period;
|
||||
}
|
||||
|
||||
/* do not reschedule if alarm function returns 0 */
|
||||
bool reschedule = curr->_on_alarm(triggered);
|
||||
|
||||
if (reschedule) {
|
||||
|
||||
/*
|
||||
* At this point, the alarm deadline normally is somewhere near
|
||||
* the current time but If the alarm had no deadline by now,
|
||||
* initialize it with the current time.
|
||||
*/
|
||||
if (curr->_raw.deadline == 0) {
|
||||
curr->_raw.deadline = _now;
|
||||
curr->_raw.deadline_period = _now_period;
|
||||
}
|
||||
/*
|
||||
* Raise the deadline value by one period of the alarm and
|
||||
* if the deadline value wraps thereby, update also in which
|
||||
* period it is located.
|
||||
*/
|
||||
Alarm::Time const deadline = curr->_raw.deadline +
|
||||
triggered * curr->_raw.period;
|
||||
if (curr->_raw.deadline > deadline) {
|
||||
curr->_raw.deadline_period = !curr->_raw.deadline_period;
|
||||
}
|
||||
curr->_raw.deadline = deadline;
|
||||
|
||||
/* synchronize enqueue operation */
|
||||
Lock::Guard lock_guard(_lock);
|
||||
_alarm_unsynchronized_enqueue(curr);
|
||||
}
|
||||
|
||||
/* release alarm, resume concurrent destructor operation */
|
||||
curr->_dispatch_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Alarm_timeout_scheduler::_alarm_setup_alarm(Alarm &alarm, Alarm::Time period, Alarm::Time first_duration)
|
||||
{
|
||||
/*
|
||||
* If the alarm is already present in the queue, re-consider its queue
|
||||
* position because its deadline might have changed. I.e., if an alarm is
|
||||
* rescheduled with a new timeout before the original timeout triggered.
|
||||
*/
|
||||
if (alarm._active)
|
||||
_alarm_unsynchronized_dequeue(&alarm);
|
||||
|
||||
Alarm::Time deadline = _now + first_duration;
|
||||
alarm._alarm_assign(period, deadline, _now > deadline ? !_now_period : _now_period, this);
|
||||
|
||||
_alarm_unsynchronized_enqueue(&alarm);
|
||||
}
|
||||
|
||||
|
||||
void Alarm_timeout_scheduler::_alarm_schedule_absolute(Alarm *alarm, Alarm::Time duration)
|
||||
{
|
||||
Lock::Guard alarm_list_lock_guard(_lock);
|
||||
|
||||
_alarm_setup_alarm(*alarm, 0, duration);
|
||||
}
|
||||
|
||||
|
||||
void Alarm_timeout_scheduler::_alarm_schedule(Alarm *alarm, Alarm::Time period)
|
||||
{
|
||||
Lock::Guard alarm_list_lock_guard(_lock);
|
||||
|
||||
/*
|
||||
* Refuse to schedule a periodic timeout of 0 because it would trigger
|
||||
* infinitely in the 'handle' function. To account for the case where the
|
||||
* alarm object was already scheduled, we make sure to remove it from the
|
||||
* queue.
|
||||
*/
|
||||
if (period == 0) {
|
||||
_alarm_unsynchronized_dequeue(alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
/* first deadline is overdue */
|
||||
_alarm_setup_alarm(*alarm, period, 0);
|
||||
}
|
||||
|
||||
|
||||
void Alarm_timeout_scheduler::_alarm_discard(Alarm *alarm)
|
||||
{
|
||||
/*
|
||||
* Make sure that nobody is inside the '_alarm_get_pending_alarm' when
|
||||
* grabbing the '_dispatch_lock'. This is important when this function
|
||||
* is called from the 'Alarm' destructor. Without the '_dispatch_lock',
|
||||
* we could take the lock and proceed with destruction just before
|
||||
* '_alarm_get_pending_alarm' tries to grab the lock. When the destructor is
|
||||
* finished, '_alarm_get_pending_alarm' would proceed with operating on a
|
||||
* dangling pointer.
|
||||
*/
|
||||
Lock::Guard alarm_list_lock_guard(_lock);
|
||||
|
||||
if (alarm) {
|
||||
Lock::Guard alarm_lock_guard(alarm->_dispatch_lock);
|
||||
_alarm_unsynchronized_dequeue(alarm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Alarm_timeout_scheduler::_alarm_next_deadline(Alarm::Time *deadline)
|
||||
{
|
||||
Lock::Guard alarm_list_lock_guard(_lock);
|
||||
|
||||
if (!_active_head) return false;
|
||||
|
||||
if (deadline)
|
||||
*deadline = _active_head->_raw.deadline;
|
||||
|
||||
if (*deadline < _min_handle_period.deadline) {
|
||||
*deadline = _min_handle_period.deadline;
|
||||
}
|
||||
return true;
|
||||
}
|
172
repos/base/src/lib/timeout/timer_connection.cc
Normal file
172
repos/base/src/lib/timeout/timer_connection.cc
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* \brief Connection to timer service and timeout scheduler
|
||||
* \author Martin Stein
|
||||
* \date 2016-11-04
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <timer_session/connection.h>
|
||||
#include <base/internal/globals.h>
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Genode::Trace;
|
||||
|
||||
|
||||
void Timer::Connection::_update_interpolation_quality(unsigned long min_factor,
|
||||
unsigned long max_factor)
|
||||
{
|
||||
/*
|
||||
* If the factor gets adapted less than 12.5%, we raise the
|
||||
* interpolation-quality value. Otherwise, we reset it to zero.
|
||||
*/
|
||||
if ((max_factor - min_factor) < (max_factor >> 3)) {
|
||||
if (_interpolation_quality < MAX_INTERPOLATION_QUALITY) {
|
||||
_interpolation_quality++; }
|
||||
} else if (_interpolation_quality) {
|
||||
_interpolation_quality = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned long Timer::Connection::_ts_to_us_ratio(Timestamp ts,
|
||||
unsigned long us,
|
||||
unsigned shift)
|
||||
{
|
||||
/*
|
||||
* If the timestamp difference is to big to do the following
|
||||
* factor calculation without an overflow, scale both timestamp
|
||||
* and time difference down equally. This should neither happen
|
||||
* often nor have much effect on the resulting factor.
|
||||
*/
|
||||
Timestamp const max_ts = ~(Timestamp)0ULL >> shift;
|
||||
while (ts > max_ts) {
|
||||
warning("timestamp value too big");
|
||||
ts >>= 1;
|
||||
us >>= 1;
|
||||
}
|
||||
if (!us) { us = 1; }
|
||||
if (!ts) { ts = 1; }
|
||||
|
||||
/*
|
||||
* To make the result more precise, we scale up the numerator of
|
||||
* the calculation. This upscaling must be considered when using
|
||||
* the result.
|
||||
*/
|
||||
Timestamp const result = (ts << shift) / us;
|
||||
unsigned long const result_ul = (unsigned long)result;
|
||||
if (result != result_ul) {
|
||||
warning("Timestamp-to-time ratio too big");
|
||||
return ~0UL;
|
||||
}
|
||||
return result_ul;
|
||||
}
|
||||
|
||||
|
||||
Duration Timer::Connection::_update_interpolated_time(Duration &interpolated_time)
|
||||
{
|
||||
/*
|
||||
* The new interpolated time value may be smaller than a
|
||||
* previously interpolated time value (based on an older real time
|
||||
* value and factor). In this case, we don't want the user time to
|
||||
* jump back but to freeze at the higher value until the new
|
||||
* interpolation has caught up.
|
||||
*/
|
||||
if (_interpolated_time.less_than(interpolated_time)) {
|
||||
_interpolated_time = interpolated_time; }
|
||||
|
||||
return _interpolated_time;
|
||||
}
|
||||
|
||||
|
||||
void Timer::Connection::_handle_timeout()
|
||||
{
|
||||
unsigned long const us = elapsed_us();
|
||||
if (us - _us > REAL_TIME_UPDATE_PERIOD_US) {
|
||||
_update_real_time();
|
||||
}
|
||||
if (_handler) {
|
||||
_handler->handle_timeout(curr_time());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Timer::Connection::schedule_timeout(Microseconds duration,
|
||||
Timeout_handler &handler)
|
||||
{
|
||||
if (duration.value < MIN_TIMEOUT_US)
|
||||
duration.value = MIN_TIMEOUT_US;
|
||||
|
||||
if (duration.value > REAL_TIME_UPDATE_PERIOD_US)
|
||||
duration.value = REAL_TIME_UPDATE_PERIOD_US;
|
||||
|
||||
_handler = &handler;
|
||||
trigger_once(duration.value);
|
||||
}
|
||||
|
||||
|
||||
void Timer::Connection::_enable_modern_mode()
|
||||
{
|
||||
if (_mode == MODERN) {
|
||||
return;
|
||||
}
|
||||
_mode = MODERN;
|
||||
_sigh(_signal_handler);
|
||||
_scheduler._enable();
|
||||
|
||||
/* do initial calibration burst to make interpolation available earlier */
|
||||
for (unsigned i = 0; i < NR_OF_INITIAL_CALIBRATIONS; i++) {
|
||||
_update_real_time();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Timer::Connection::Connection(Genode::Env &env, char const *label)
|
||||
:
|
||||
Genode::Connection<Session>(env, session(env.parent(),
|
||||
"ram_quota=10K, cap_quota=%u, label=\"%s\"",
|
||||
CAP_QUOTA, label)),
|
||||
Session_client(cap()),
|
||||
_signal_handler(env.ep(), *this, &Connection::_handle_timeout)
|
||||
{
|
||||
/* register default signal handler */
|
||||
Session_client::sigh(_default_sigh_cap);
|
||||
}
|
||||
|
||||
|
||||
Timer::Connection::Connection()
|
||||
:
|
||||
Genode::Connection<Session>(session("ram_quota=10K")),
|
||||
Session_client(cap()),
|
||||
_signal_handler(internal_env().ep(), *this, &Connection::_handle_timeout)
|
||||
{
|
||||
/* register default signal handler */
|
||||
Session_client::sigh(_default_sigh_cap);
|
||||
}
|
||||
|
||||
|
||||
void Timer::Connection::_schedule_one_shot(Timeout &timeout, Microseconds duration)
|
||||
{
|
||||
_enable_modern_mode();
|
||||
_scheduler._schedule_one_shot(timeout, duration);
|
||||
};
|
||||
|
||||
|
||||
void Timer::Connection::_schedule_periodic(Timeout &timeout, Microseconds duration)
|
||||
{
|
||||
_enable_modern_mode();
|
||||
_scheduler._schedule_periodic(timeout, duration);
|
||||
};
|
||||
|
||||
|
||||
void Timer::Connection::_discard(Timeout &timeout)
|
||||
{
|
||||
_enable_modern_mode();
|
||||
_scheduler._discard(timeout);
|
||||
}
|
211
repos/base/src/lib/timeout/timer_connection_time.cc
Normal file
211
repos/base/src/lib/timeout/timer_connection_time.cc
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* \brief Connection to timer service and timeout scheduler
|
||||
* \author Martin Stein
|
||||
* \date 2016-11-04
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <timer_session/connection.h>
|
||||
#include <base/internal/globals.h>
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Genode::Trace;
|
||||
|
||||
|
||||
void Timer::Connection::_update_real_time()
|
||||
{
|
||||
Lock_guard<Lock> lock_guard(_real_time_lock);
|
||||
|
||||
|
||||
/*
|
||||
* Update timestamp, time, and real-time value
|
||||
*/
|
||||
|
||||
Timestamp ts = 0UL;
|
||||
unsigned long us = 0UL;
|
||||
unsigned long latency_us = ~0UL;
|
||||
|
||||
/*
|
||||
* We retry reading out timestamp plus remote time until the result
|
||||
* fulfills a given latency. If the maximum number of trials is
|
||||
* reached, we take the results that has the lowest latency.
|
||||
*/
|
||||
for (unsigned remote_time_trials = 0;
|
||||
remote_time_trials < MAX_REMOTE_TIME_TRIALS; )
|
||||
{
|
||||
/* read out the two time values close in succession */
|
||||
Timestamp volatile new_ts = _timestamp();
|
||||
unsigned long volatile new_us = elapsed_us();
|
||||
|
||||
/* do not proceed until the time difference is at least 1 us */
|
||||
if (new_us == _us) { continue; }
|
||||
remote_time_trials++;
|
||||
|
||||
/*
|
||||
* If interpolation is not ready, yet we cannot determine a latency
|
||||
* and take the values as they are.
|
||||
*/
|
||||
if (_interpolation_quality < MAX_INTERPOLATION_QUALITY) {
|
||||
us = new_us;
|
||||
ts = new_ts;
|
||||
break;
|
||||
}
|
||||
/* determine latency between reading out timestamp and time value */
|
||||
Timestamp const ts_diff = _timestamp() - new_ts;
|
||||
unsigned long const new_latency_us =
|
||||
_ts_to_us_ratio(ts_diff, _us_to_ts_factor, _us_to_ts_factor_shift);
|
||||
|
||||
/* remember results if the latency was better than on the last trial */
|
||||
if (new_latency_us < latency_us) {
|
||||
us = new_us;
|
||||
ts = new_ts;
|
||||
|
||||
/* take the results if the latency fulfills the given maximum */
|
||||
if (latency_us < MAX_REMOTE_TIME_LATENCY_US) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* determine timestamp and time difference */
|
||||
unsigned long const us_diff = us - _us;
|
||||
Timestamp ts_diff = ts - _ts;
|
||||
|
||||
/* overwrite timestamp, time, and real time member */
|
||||
_us = us;
|
||||
_ts = ts;
|
||||
_real_time.add(Microseconds(us_diff));
|
||||
|
||||
|
||||
/*
|
||||
* Update timestamp-to-time factor and its shift
|
||||
*/
|
||||
|
||||
unsigned factor_shift = _us_to_ts_factor_shift;
|
||||
unsigned long old_factor = _us_to_ts_factor;
|
||||
Timestamp max_ts_diff = ~(Timestamp)0ULL >> factor_shift;
|
||||
Timestamp min_ts_diff_shifted = ~(Timestamp)0ULL >> 1;
|
||||
|
||||
/*
|
||||
* If the calculation type is bigger than the resulting factor type,
|
||||
* we have to apply further limitations to avoid a loss at the final cast.
|
||||
*/
|
||||
if (sizeof(Timestamp) > sizeof(unsigned long)) {
|
||||
|
||||
Timestamp limit_ts_diff_shifted = (Timestamp)~0UL * us_diff;
|
||||
Timestamp const limit_ts_diff = limit_ts_diff_shifted >>
|
||||
factor_shift;
|
||||
|
||||
/*
|
||||
* Avoid that we leave the factor shift on such a high level that
|
||||
* casting the factor to its final type causes a loss.
|
||||
*/
|
||||
if (max_ts_diff > limit_ts_diff) {
|
||||
max_ts_diff = limit_ts_diff;
|
||||
}
|
||||
/*
|
||||
* Avoid that we raise the factor shift such that casting the factor
|
||||
* to its final type causes a loss.
|
||||
*/
|
||||
limit_ts_diff_shifted >>= 1;
|
||||
if (min_ts_diff_shifted > limit_ts_diff_shifted) {
|
||||
min_ts_diff_shifted = limit_ts_diff_shifted;
|
||||
}
|
||||
}
|
||||
|
||||
struct Factor_update_failed : Genode::Exception { };
|
||||
try {
|
||||
/* meet the timestamp-difference limit before applying the shift */
|
||||
while (ts_diff > max_ts_diff) {
|
||||
|
||||
/* if possible, lower the shift to meet the limitation */
|
||||
if (!factor_shift) {
|
||||
error("timestamp difference too big");
|
||||
throw Factor_update_failed();
|
||||
}
|
||||
factor_shift--;
|
||||
max_ts_diff = (max_ts_diff << 1) | 1;
|
||||
old_factor >>= 1;
|
||||
}
|
||||
/*
|
||||
* Apply current shift to timestamp difference and try to even
|
||||
* raise the shift successively to get as much precision as possible.
|
||||
*/
|
||||
Timestamp ts_diff_shifted = ts_diff << factor_shift;
|
||||
while (ts_diff_shifted < us_diff << MIN_FACTOR_LOG2)
|
||||
{
|
||||
factor_shift++;
|
||||
ts_diff_shifted <<= 1;
|
||||
old_factor <<= 1;
|
||||
}
|
||||
/*
|
||||
* The cast to unsigned long does not cause a loss because of the
|
||||
* limitations we applied to the timestamp difference. We also took
|
||||
* care that the time difference cannot become null.
|
||||
*/
|
||||
unsigned long const new_factor =
|
||||
(unsigned long)((Timestamp)ts_diff_shifted / us_diff);
|
||||
|
||||
/* update interpolation-quality value */
|
||||
if (old_factor > new_factor) { _update_interpolation_quality(new_factor, old_factor); }
|
||||
else { _update_interpolation_quality(old_factor, new_factor); }
|
||||
|
||||
/* overwrite factor and factor-shift member */
|
||||
_us_to_ts_factor_shift = factor_shift;
|
||||
_us_to_ts_factor = new_factor;
|
||||
|
||||
} catch (Factor_update_failed) {
|
||||
|
||||
/* disable interpolation */
|
||||
_interpolation_quality = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Duration Timer::Connection::curr_time()
|
||||
{
|
||||
_enable_modern_mode();
|
||||
|
||||
Reconstructible<Lock_guard<Lock> > lock_guard(_real_time_lock);
|
||||
Duration interpolated_time(_real_time);
|
||||
|
||||
/*
|
||||
* Interpolate with timestamps only if the factor value
|
||||
* remained stable for some time. If we would interpolate with
|
||||
* a yet unstable factor, there's an increased risk that the
|
||||
* interpolated time falsely reaches an enourmous level. Then
|
||||
* the value would stand still for quite some time because we
|
||||
* can't let it jump back to a more realistic level.
|
||||
*/
|
||||
if (_interpolation_quality == MAX_INTERPOLATION_QUALITY)
|
||||
{
|
||||
/* buffer interpolation related members and free the lock */
|
||||
Timestamp const ts = _ts;
|
||||
unsigned long const us_to_ts_factor = _us_to_ts_factor;
|
||||
unsigned const us_to_ts_factor_shift = _us_to_ts_factor_shift;
|
||||
|
||||
lock_guard.destruct();
|
||||
|
||||
/* interpolate time difference since the last real time update */
|
||||
Timestamp const ts_diff = _timestamp() - ts;
|
||||
unsigned long const us_diff = _ts_to_us_ratio(ts_diff, us_to_ts_factor,
|
||||
us_to_ts_factor_shift);
|
||||
|
||||
interpolated_time.add(Microseconds(us_diff));
|
||||
|
||||
} else {
|
||||
|
||||
/* use remote timer instead of timestamps */
|
||||
interpolated_time.add(Microseconds(elapsed_us() - _us));
|
||||
|
||||
lock_guard.destruct();
|
||||
}
|
||||
return _update_interpolated_time(interpolated_time);
|
||||
}
|
24
repos/base/src/lib/timeout/timer_connection_timestamp.cc
Normal file
24
repos/base/src/lib/timeout/timer_connection_timestamp.cc
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* \brief Timestamp implementation for the Genode Timer
|
||||
* \author Martin Stein
|
||||
* \date 2016-11-04
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <trace/timestamp.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
Trace::Timestamp Timer::Connection::_timestamp()
|
||||
{
|
||||
return Trace::timestamp();
|
||||
}
|
78
repos/base/src/test/timed_semaphore/main.cc
Normal file
78
repos/base/src/test/timed_semaphore/main.cc
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* \brief Test for the timed-semaphore
|
||||
* \author Stefan Kalkowski
|
||||
* \author Martin Stein
|
||||
* \date 2010-03-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <timer_session/connection.h>
|
||||
#include <base/timed_semaphore.h>
|
||||
#include <base/thread.h>
|
||||
#include <base/component.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
struct Test : Thread
|
||||
{
|
||||
struct Failed : Exception { };
|
||||
|
||||
unsigned id;
|
||||
Timer::Connection wakeup_timer;
|
||||
unsigned const wakeup_period;
|
||||
Timed_semaphore sem { };
|
||||
bool stop_wakeup { false };
|
||||
Lock wakeup_stopped { Lock::LOCKED };
|
||||
bool got_timeouts { false };
|
||||
|
||||
void entry()
|
||||
{
|
||||
do {
|
||||
wakeup_timer.msleep(wakeup_period);
|
||||
sem.up();
|
||||
} while (!stop_wakeup);
|
||||
wakeup_stopped.unlock();
|
||||
}
|
||||
|
||||
Test(Env &env, bool timeouts, unsigned id, char const *brief)
|
||||
: Thread(env, "wakeup", 1024 * sizeof(addr_t)), id(id), wakeup_timer(env),
|
||||
wakeup_period(timeouts ? 1000 : 100)
|
||||
{
|
||||
log("\nTEST ", id, ": ", brief, "\n");
|
||||
Thread::start();
|
||||
try { for (int i = 0; i < 10; i++) { sem.down(timeouts ? 100 : 1000); } }
|
||||
catch (Timeout_exception) { got_timeouts = true; }
|
||||
if (timeouts != got_timeouts) {
|
||||
throw Failed(); }
|
||||
|
||||
stop_wakeup = true;
|
||||
wakeup_stopped.lock();
|
||||
}
|
||||
|
||||
~Test() { log("\nTEST ", id, " finished\n"); }
|
||||
};
|
||||
|
||||
|
||||
struct Main
|
||||
{
|
||||
Constructible<Test> test { };
|
||||
|
||||
Main(Env &env)
|
||||
{
|
||||
log("--- Timed semaphore test ---");
|
||||
test.construct(env, false, 1, "without timeouts"); test.destruct();
|
||||
test.construct(env, true, 2, "with timeouts"); test.destruct();
|
||||
log("--- Timed semaphore test finished ---");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env) { static Main main(env); }
|
3
repos/base/src/test/timed_semaphore/target.mk
Normal file
3
repos/base/src/test/timed_semaphore/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = test-timed_semaphore
|
||||
SRC_CC = main.cc
|
||||
LIBS = base timed_semaphore
|
220
repos/base/src/test/timer/main.cc
Normal file
220
repos/base/src/test/timer/main.cc
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* \brief Test for timer service
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2009-06-22
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <base/registry.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
struct Lazy_test
|
||||
{
|
||||
struct Faster_timer_too_slow : Exception { };
|
||||
|
||||
Env &env;
|
||||
Signal_transmitter done;
|
||||
Timer::Connection slow_timer { env };
|
||||
Signal_handler<Lazy_test> slow_handler { env.ep(), *this,
|
||||
&Lazy_test::handle_slow_timer };
|
||||
Timer::Connection fast_timer { env };
|
||||
Signal_handler<Lazy_test> fast_handler { env.ep(), *this,
|
||||
&Lazy_test::handle_fast_timer };
|
||||
Timer::Connection faster_timer { env };
|
||||
Signal_handler<Lazy_test> faster_handler { env.ep(), *this,
|
||||
&Lazy_test::handle_faster_timer };
|
||||
|
||||
enum { RUN_TIME_US = 2 * 1000 * 1000, TIMEOUT_US = 50*1000, FACTOR = 2 };
|
||||
unsigned fast = 0;
|
||||
unsigned faster = 0;
|
||||
|
||||
void handle_slow_timer()
|
||||
{
|
||||
log("timeout fired - ", fast, "/", faster, "/",
|
||||
RUN_TIME_US / TIMEOUT_US * FACTOR);
|
||||
|
||||
if (fast)
|
||||
throw Faster_timer_too_slow();
|
||||
|
||||
done.submit();
|
||||
}
|
||||
|
||||
void handle_fast_timer() {
|
||||
fast ++;
|
||||
if (faster <= fast)
|
||||
throw Faster_timer_too_slow();
|
||||
}
|
||||
|
||||
void handle_faster_timer() { set_fast_timers(); }
|
||||
|
||||
void set_fast_timers()
|
||||
{
|
||||
fast_timer.trigger_once(TIMEOUT_US);
|
||||
faster_timer.trigger_once(TIMEOUT_US/FACTOR);
|
||||
faster ++;
|
||||
}
|
||||
|
||||
Lazy_test(Env &env, Signal_context_capability done) : env(env), done(done)
|
||||
{
|
||||
slow_timer.sigh(slow_handler);
|
||||
fast_timer.sigh(fast_handler);
|
||||
faster_timer.sigh(faster_handler);
|
||||
|
||||
log("register two-seconds timeout...");
|
||||
slow_timer.trigger_once(RUN_TIME_US);
|
||||
set_fast_timers();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Stress_test
|
||||
{
|
||||
enum { DURATION_SEC = 10 };
|
||||
enum { MAX_SLV_PERIOD_US = 33000 };
|
||||
|
||||
struct Starvation : Exception { };
|
||||
struct Violation_of_timer_rate_limit : Exception { };
|
||||
|
||||
struct Slave
|
||||
{
|
||||
enum { DURATION_US = DURATION_SEC * 1000 * 1000 };
|
||||
enum { MIN_TIMER_PERIOD_US = 1000 };
|
||||
enum { MAX_CNT_BASE = DURATION_US / MIN_TIMER_PERIOD_US };
|
||||
enum { MAX_CNT_TOLERANCE = MAX_CNT_BASE / 9 };
|
||||
enum { MAX_CNT = MAX_CNT_BASE + MAX_CNT_TOLERANCE };
|
||||
enum { MIN_CNT = DURATION_US / MAX_SLV_PERIOD_US / 2 };
|
||||
|
||||
Signal_handler<Slave> timer_handler;
|
||||
Timer::Connection timer;
|
||||
unsigned long us;
|
||||
unsigned count { 0 };
|
||||
|
||||
Slave(Env &env, unsigned us)
|
||||
: timer_handler(env.ep(), *this, &Slave::handle_timer),
|
||||
timer(env), us(us) { timer.sigh(timer_handler); }
|
||||
|
||||
virtual ~Slave() { }
|
||||
|
||||
void handle_timer()
|
||||
{
|
||||
count++;
|
||||
timer.trigger_once(us);
|
||||
}
|
||||
|
||||
void dump(unsigned &starvation, unsigned &rate_violation)
|
||||
{
|
||||
log("timer (period ", us, " us) triggered ", count,
|
||||
" times (min ", (unsigned)MIN_CNT,
|
||||
" max ", (unsigned)MAX_CNT, ") -> slept ",
|
||||
((unsigned long)us * count) / 1000, " ms");
|
||||
|
||||
/* detect starvation of timeouts */
|
||||
if (count < MIN_CNT) {
|
||||
error("triggered less than ", (unsigned)MIN_CNT,
|
||||
" times");
|
||||
starvation ++;
|
||||
}
|
||||
/* detect violation of timer rate limitation */
|
||||
if (count > MAX_CNT) {
|
||||
error("triggered more than ", (unsigned)MAX_CNT,
|
||||
" times");
|
||||
rate_violation ++;
|
||||
}
|
||||
}
|
||||
|
||||
void start() { timer.trigger_once(us); }
|
||||
void stop() { timer.sigh(Signal_context_capability()); }
|
||||
};
|
||||
|
||||
Env &env;
|
||||
Signal_transmitter done;
|
||||
Heap heap { &env.ram(), &env.rm() };
|
||||
Timer::Connection timer { env };
|
||||
unsigned count { 0 };
|
||||
Signal_handler<Stress_test> handler { env.ep(), *this, &Stress_test::handle };
|
||||
Registry<Registered<Slave> > slaves { };
|
||||
|
||||
void handle()
|
||||
{
|
||||
if (count < DURATION_SEC) {
|
||||
count++;
|
||||
log("wait ", count, "/", (unsigned)DURATION_SEC);
|
||||
timer.trigger_once(1000UL * 1000);
|
||||
} else {
|
||||
unsigned starvation = 0;
|
||||
unsigned rate_violation = 0;
|
||||
|
||||
slaves.for_each([&] (Slave &timer) { timer.stop(); });
|
||||
slaves.for_each([&] (Slave &timer) {
|
||||
timer.dump(starvation, rate_violation);
|
||||
});
|
||||
|
||||
if (starvation)
|
||||
throw Starvation();
|
||||
if (rate_violation)
|
||||
throw Violation_of_timer_rate_limit();
|
||||
|
||||
done.submit();
|
||||
}
|
||||
}
|
||||
|
||||
Stress_test(Env &env, Signal_context_capability done) : env(env), done(done)
|
||||
{
|
||||
timer.sigh(handler);
|
||||
|
||||
for (unsigned long us_1 = 1; us_1 < MAX_SLV_PERIOD_US; us_1 *= 2) {
|
||||
new (heap) Registered<Slave>(slaves, env, us_1 - us_1 / 3);
|
||||
new (heap) Registered<Slave>(slaves, env, us_1);
|
||||
}
|
||||
|
||||
slaves.for_each([&] (Slave &slv) { slv.start(); });
|
||||
timer.trigger_once(1000 * 1000);
|
||||
}
|
||||
|
||||
~Stress_test() {
|
||||
slaves.for_each([&] (Registered<Slave> &slv) { destroy(heap, &slv); }); }
|
||||
};
|
||||
|
||||
|
||||
struct Main
|
||||
{
|
||||
Env &env;
|
||||
Constructible<Lazy_test> test_1 { };
|
||||
Signal_handler<Main> test_1_done { env.ep(), *this, &Main::handle_test_1_done };
|
||||
Constructible<Stress_test> test_2 { };
|
||||
Signal_handler<Main> test_2_done { env.ep(), *this, &Main::handle_test_2_done };
|
||||
|
||||
void handle_test_1_done()
|
||||
{
|
||||
test_1.destruct();
|
||||
test_2.construct(env, test_2_done);
|
||||
}
|
||||
|
||||
void handle_test_2_done()
|
||||
{
|
||||
log("--- timer test finished ---");
|
||||
env.parent().exit(0);
|
||||
}
|
||||
|
||||
Main(Env &env) : env(env)
|
||||
{
|
||||
log("--- timer test ---");
|
||||
test_1.construct(env, test_1_done);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Env &env) { static Main main(env); }
|
3
repos/base/src/test/timer/target.mk
Normal file
3
repos/base/src/test/timer/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = test-timer
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
43
repos/base/src/test/timer_accuracy/main.cc
Normal file
43
repos/base/src/test/timer_accuracy/main.cc
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* \brief Timer accuracy test
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2010-03-09
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <timer_session/connection.h>
|
||||
#include <base/component.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
struct Main
|
||||
{
|
||||
Timer::Connection timer;
|
||||
Signal_handler<Main> timer_handler;
|
||||
unsigned duration_us { 0 };
|
||||
|
||||
void handle_timer()
|
||||
{
|
||||
duration_us += 1000 * 1000;
|
||||
timer.trigger_once(duration_us);
|
||||
log("");
|
||||
}
|
||||
|
||||
Main(Env &env) : timer(env), timer_handler(env.ep(), *this, &Main::handle_timer)
|
||||
{
|
||||
timer.sigh(timer_handler);
|
||||
handle_timer();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Env &env) { static Main main(env); }
|
3
repos/base/src/test/timer_accuracy/target.mk
Normal file
3
repos/base/src/test/timer_accuracy/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = test-timer_accuracy
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
55
repos/base/src/timer/epit/time_source.cc
Normal file
55
repos/base/src/timer/epit/time_source.cc
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* \brief Time source that uses the Enhanced Periodic Interrupt Timer (Freescale)
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \author Stefan Kalkowski
|
||||
* \author Alexander Boettcher
|
||||
* \date 2009-06-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <time_source.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Timer::Time_source::schedule_timeout(Genode::Microseconds duration,
|
||||
Timeout_handler &handler)
|
||||
{
|
||||
unsigned long const ticks = (1ULL * duration.value * TICKS_PER_MS) / 1000;
|
||||
_handler = &handler;
|
||||
_timer_irq.ack_irq();
|
||||
_cleared_ticks = 0;
|
||||
|
||||
/* disable timer */
|
||||
write<Cr::En>(0);
|
||||
|
||||
/* clear interrupt and install timeout */
|
||||
write<Sr::Ocif>(1);
|
||||
write<Cr>(Cr::prepare_one_shot());
|
||||
write<Cmpr>(Cnt::MAX - ticks);
|
||||
|
||||
/* start timer */
|
||||
write<Cr::En>(1);
|
||||
}
|
||||
|
||||
|
||||
Duration Timer::Time_source::curr_time()
|
||||
{
|
||||
unsigned long const uncleared_ticks = Cnt::MAX - read<Cnt>() - _cleared_ticks;
|
||||
unsigned long const uncleared_us = timer_ticks_to_us(uncleared_ticks, TICKS_PER_MS);
|
||||
|
||||
/* update time only on IRQs and if rate is under 1000 per second */
|
||||
if (_irq || uncleared_us > 1000) {
|
||||
_curr_time.add(Genode::Microseconds(uncleared_us));
|
||||
_cleared_ticks += uncleared_ticks;
|
||||
}
|
||||
return _curr_time;
|
||||
}
|
78
repos/base/src/timer/epit/time_source.h
Normal file
78
repos/base/src/timer/epit/time_source.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* \brief Time source that uses the Enhanced Periodic Interrupt Timer (Freescale)
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2009-06-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _TIME_SOURCE_H_
|
||||
#define _TIME_SOURCE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <irq_session/connection.h>
|
||||
#include <os/attached_mmio.h>
|
||||
#include <drivers/timer/util.h>
|
||||
|
||||
/* local includes */
|
||||
#include <signalled_time_source.h>
|
||||
|
||||
namespace Timer { class Time_source; }
|
||||
|
||||
|
||||
class Timer::Time_source : private Genode::Attached_mmio,
|
||||
public Genode::Signalled_time_source
|
||||
{
|
||||
private:
|
||||
|
||||
enum { TICKS_PER_MS = 66000 };
|
||||
|
||||
struct Cr : Register<0x0, 32>
|
||||
{
|
||||
struct En : Bitfield<0, 1> { };
|
||||
struct En_mod : Bitfield<1, 1> { enum { RELOAD = 1 }; };
|
||||
struct Oci_en : Bitfield<2, 1> { };
|
||||
struct Swr : Bitfield<16, 1> { };
|
||||
struct Clk_src : Bitfield<24, 2> { enum { HIGH_FREQ_REF_CLK = 2 }; };
|
||||
|
||||
static access_t prepare_one_shot()
|
||||
{
|
||||
access_t cr = 0;
|
||||
En_mod::set(cr, En_mod::RELOAD);
|
||||
Oci_en::set(cr, 1);
|
||||
Clk_src::set(cr, Clk_src::HIGH_FREQ_REF_CLK);
|
||||
return cr;
|
||||
}
|
||||
};
|
||||
|
||||
struct Sr : Register<0x4, 32> { struct Ocif : Bitfield<0, 1> { }; };
|
||||
struct Cmpr : Register<0xc, 32> { };
|
||||
struct Cnt : Register<0x10, 32> { enum { MAX = ~(access_t)0 }; };
|
||||
|
||||
Genode::Irq_connection _timer_irq;
|
||||
Genode::Duration _curr_time { Genode::Microseconds(0) };
|
||||
Genode::Microseconds const _max_timeout { Genode::timer_ticks_to_us(Cnt::MAX / 2, TICKS_PER_MS) };
|
||||
unsigned long _cleared_ticks { 0 };
|
||||
|
||||
public:
|
||||
|
||||
Time_source(Genode::Env &env);
|
||||
|
||||
|
||||
/*************************
|
||||
** Genode::Time_source **
|
||||
*************************/
|
||||
|
||||
Genode::Duration curr_time() override;
|
||||
void schedule_timeout(Genode::Microseconds duration, Timeout_handler &handler) override;
|
||||
Genode::Microseconds max_timeout() const override { return _max_timeout; };
|
||||
};
|
||||
|
||||
#endif /* _TIME_SOURCE_H_ */
|
9
repos/base/src/timer/epit/wand_quad/target.inc
Normal file
9
repos/base/src/timer/epit/wand_quad/target.inc
Normal file
@ -0,0 +1,9 @@
|
||||
TARGET = wand_quad_timer_drv
|
||||
REQUIRES = wand_quad
|
||||
GEN_DIR := $(dir $(call select_from_repositories,src/timer/main.cc))
|
||||
INC_DIR += $(GEN_DIR)/epit
|
||||
SRC_CC += epit/time_source.cc epit/wand_quad/timer.cc
|
||||
|
||||
include $(GEN_DIR)/target.inc
|
||||
|
||||
vpath %.cc $(GEN_DIR)
|
33
repos/base/src/timer/epit/wand_quad/timer.cc
Normal file
33
repos/base/src/timer/epit/wand_quad/timer.cc
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* \brief Time source for Wandboard Quad i.MX6
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \author Stefan Kalkowski
|
||||
* \author Alexander Boettcher
|
||||
* \date 2009-06-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* base include */
|
||||
#include <drivers/defs/wand_quad.h>
|
||||
|
||||
/* local include */
|
||||
#include <time_source.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
Timer::Time_source::Time_source(Env &env)
|
||||
:
|
||||
Attached_mmio(env, Wand_quad::EPIT_2_MMIO_BASE, Wand_quad::EPIT_2_MMIO_SIZE),
|
||||
Signalled_time_source(env),
|
||||
_timer_irq(env, Wand_quad::EPIT_2_IRQ)
|
||||
{
|
||||
_timer_irq.sigh(_signal_handler);
|
||||
while (read<Cr::Swr>()) ;
|
||||
}
|
93
repos/base/src/timer/fiasco/time_source.cc
Normal file
93
repos/base/src/timer/fiasco/time_source.cc
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* \brief Time source that uses sleeping by the means of the kernel
|
||||
* \author Christian Helmuth
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2006-08-30
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/misc_math.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
|
||||
/* Fiasco includes */
|
||||
namespace Fiasco {
|
||||
#include <l4/sys/ipc.h>
|
||||
#include <l4/sys/kip.h>
|
||||
}
|
||||
|
||||
/*
|
||||
* On L4/Fiasco, the KIP layout is defined in 'kernel.h', which does not exist
|
||||
* on Fiasco.OC. We test for 'L4_SYS_KIP_H__' to check for the L4/Fiasco case
|
||||
* and include 'kernel.h'. This works because the Fiasco.OC headers do not use
|
||||
* include guards ('L4_SYS_KIP_H__' is undefined on Fiasco.OC).
|
||||
*/
|
||||
#ifdef L4_SYS_KIP_H__
|
||||
namespace Fiasco {
|
||||
#include <l4/sys/kernel.h>
|
||||
}
|
||||
#endif /* L4_SYS_KIP_H__ */
|
||||
|
||||
/* local includes */
|
||||
#include <time_source.h>
|
||||
|
||||
using namespace Fiasco;
|
||||
using Microseconds = Genode::Microseconds;
|
||||
using Duration = Genode::Duration;
|
||||
|
||||
|
||||
static l4_timeout_s mus_to_timeout(unsigned long mus)
|
||||
{
|
||||
if (mus == 0)
|
||||
return L4_IPC_TIMEOUT_0;
|
||||
else if (mus == ~0UL)
|
||||
return L4_IPC_TIMEOUT_NEVER;
|
||||
|
||||
long e = Genode::log2(mus) - 7;
|
||||
unsigned long m;
|
||||
if (e < 0) e = 0;
|
||||
m = mus / (1UL << e);
|
||||
|
||||
/* check corner case */
|
||||
if ((e > 31 ) || (m > 1023)) {
|
||||
Genode::warning("invalid timeout ", mus, ", using max. values");
|
||||
e = 0;
|
||||
m = 1023;
|
||||
}
|
||||
return l4_timeout_rel(m, e);
|
||||
}
|
||||
|
||||
|
||||
Microseconds Timer::Time_source::max_timeout() const
|
||||
{
|
||||
Genode::Lock::Guard lock_guard(_lock);
|
||||
return Microseconds(1000 * 1000 * 100);
|
||||
}
|
||||
|
||||
|
||||
Duration Timer::Time_source::curr_time()
|
||||
{
|
||||
Genode::Lock::Guard lock_guard(_lock);
|
||||
static Genode::Attached_rom_dataspace kip_ds(_env, "l4v2_kip");
|
||||
static Fiasco::l4_kernel_info_t * const kip =
|
||||
kip_ds.local_addr<Fiasco::l4_kernel_info_t>();
|
||||
|
||||
#ifdef L4_SYS_KIP_H__
|
||||
Fiasco::l4_cpu_time_t const clock = kip->clock;
|
||||
#else
|
||||
Fiasco::l4_cpu_time_t const clock = Fiasco::l4_kip_clock(kip);
|
||||
#endif
|
||||
|
||||
return Duration(Microseconds(clock));
|
||||
}
|
||||
|
||||
|
||||
void Timer::Time_source::_usleep(unsigned long usecs) {
|
||||
l4_ipc_sleep(l4_timeout(L4_IPC_TIMEOUT_NEVER, mus_to_timeout(usecs))); }
|
67
repos/base/src/timer/include/root_component.h
Normal file
67
repos/base/src/timer/include/root_component.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* \brief Root interface to timer service
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2006-08-15
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _ROOT_COMPONENT_H_
|
||||
#define _ROOT_COMPONENT_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <root/component.h>
|
||||
|
||||
/* local includes */
|
||||
#include <time_source.h>
|
||||
#include <session_component.h>
|
||||
|
||||
namespace Timer { class Root_component; }
|
||||
|
||||
|
||||
class Timer::Root_component : public Genode::Root_component<Session_component>
|
||||
{
|
||||
private:
|
||||
|
||||
enum { MIN_TIMEOUT_US = 1000 };
|
||||
|
||||
Time_source _time_source;
|
||||
Genode::Alarm_timeout_scheduler _timeout_scheduler;
|
||||
|
||||
|
||||
/********************
|
||||
** Root_component **
|
||||
********************/
|
||||
|
||||
Session_component *_create_session(const char *args)
|
||||
{
|
||||
using namespace Genode;
|
||||
size_t const ram_quota =
|
||||
Arg_string::find_arg(args, "ram_quota").ulong_value(0);
|
||||
|
||||
if (ram_quota < sizeof(Session_component)) {
|
||||
throw Insufficient_ram_quota(); }
|
||||
|
||||
return new (md_alloc())
|
||||
Session_component(_timeout_scheduler);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Root_component(Genode::Env &env, Genode::Allocator &md_alloc)
|
||||
:
|
||||
Genode::Root_component<Session_component>(&env.ep().rpc_ep(), &md_alloc),
|
||||
_time_source(env),
|
||||
_timeout_scheduler(_time_source, Microseconds(MIN_TIMEOUT_US))
|
||||
{
|
||||
_timeout_scheduler._enable();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _ROOT_COMPONENT_H_ */
|
98
repos/base/src/timer/include/session_component.h
Normal file
98
repos/base/src/timer/include/session_component.h
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* \brief Instance of the timer session interface
|
||||
* \author Norman Feske
|
||||
* \author Markus Partheymueller
|
||||
* \author Martin Stein
|
||||
* \date 2006-08-15
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2017 Genode Labs GmbH
|
||||
* Copyright (C) 2012 Intel Corporation
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SESSION_COMPONENT_
|
||||
#define _SESSION_COMPONENT_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/list.h>
|
||||
#include <timer_session/timer_session.h>
|
||||
#include <base/rpc_server.h>
|
||||
#include <timer/timeout.h>
|
||||
|
||||
namespace Timer {
|
||||
|
||||
using Microseconds = Genode::Microseconds;
|
||||
using Duration = Genode::Duration;
|
||||
class Session_component;
|
||||
}
|
||||
|
||||
|
||||
class Timer::Session_component : public Genode::Rpc_object<Session>,
|
||||
private Genode::List<Session_component>::Element,
|
||||
private Genode::Timeout::Handler
|
||||
{
|
||||
private:
|
||||
|
||||
friend class Genode::List<Session_component>;
|
||||
|
||||
Genode::Timeout _timeout;
|
||||
Genode::Timeout_scheduler &_timeout_scheduler;
|
||||
Genode::Signal_context_capability _sigh { };
|
||||
|
||||
unsigned long const _init_time_us =
|
||||
_timeout_scheduler.curr_time().trunc_to_plain_us().value;
|
||||
|
||||
void handle_timeout(Duration) {
|
||||
Genode::Signal_transmitter(_sigh).submit(); }
|
||||
|
||||
public:
|
||||
|
||||
Session_component(Genode::Timeout_scheduler &timeout_scheduler)
|
||||
: _timeout(timeout_scheduler), _timeout_scheduler(timeout_scheduler) { }
|
||||
|
||||
|
||||
/********************
|
||||
** Timer::Session **
|
||||
********************/
|
||||
|
||||
void trigger_once(unsigned us) override {
|
||||
|
||||
/*
|
||||
* FIXME Workaround for the problem that Alarm scheduler may
|
||||
* categorize big timeouts into the wrong time counter
|
||||
* period due to its outdated internal time. This is needed
|
||||
* only because the Alarm framework solely takes absolute
|
||||
* time values on one-shot timeouts. and thus As soon as the
|
||||
* Alarm framework takes solely relative time values, please
|
||||
* remove this.
|
||||
*/
|
||||
Microseconds typed_us((us > ~0U >> 1) ? ~0U >> 1 : us);
|
||||
_timeout.schedule_one_shot(typed_us, *this);
|
||||
}
|
||||
|
||||
void trigger_periodic(unsigned us) override {
|
||||
_timeout.schedule_periodic(Microseconds(us), *this); }
|
||||
|
||||
void sigh(Signal_context_capability sigh) override
|
||||
{
|
||||
_sigh = sigh;
|
||||
if (!sigh.valid())
|
||||
_timeout.discard();
|
||||
}
|
||||
|
||||
unsigned long elapsed_ms() const override {
|
||||
return elapsed_us() / 1000; }
|
||||
|
||||
unsigned long elapsed_us() const override {
|
||||
return _timeout_scheduler.curr_time().trunc_to_plain_us().value -
|
||||
_init_time_us; }
|
||||
|
||||
void msleep(unsigned) override { /* never called at the server side */ }
|
||||
void usleep(unsigned) override { /* never called at the server side */ }
|
||||
};
|
||||
|
||||
#endif /* _SESSION_COMPONENT_ */
|
62
repos/base/src/timer/include/signalled_time_source.h
Normal file
62
repos/base/src/timer/include/signalled_time_source.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* \brief Time source that handles timeouts via a signal handler
|
||||
* \author Martin Stein
|
||||
* \date 2016-11-04
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SIGNALLED_TIME_SOURCE_H_
|
||||
#define _SIGNALLED_TIME_SOURCE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/env.h>
|
||||
#include <timer/timeout.h>
|
||||
#include <base/rpc_client.h>
|
||||
|
||||
namespace Genode { class Signalled_time_source; }
|
||||
|
||||
|
||||
class Genode::Signalled_time_source : public Time_source
|
||||
{
|
||||
private:
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Signalled_time_source(Signalled_time_source const &);
|
||||
Signalled_time_source &operator = (Signalled_time_source const &);
|
||||
|
||||
protected:
|
||||
|
||||
using Signal_handler = Genode::Signal_handler<Signalled_time_source>;
|
||||
|
||||
Signal_handler _signal_handler;
|
||||
Timeout_handler *_handler = nullptr;
|
||||
bool _irq = false;
|
||||
|
||||
void _handle_timeout()
|
||||
{
|
||||
if (_handler) {
|
||||
_irq = true;
|
||||
Duration time(curr_time());
|
||||
_irq = false;
|
||||
_handler->handle_timeout(time);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Signalled_time_source(Env &env)
|
||||
:
|
||||
_signal_handler(env.ep(), *this,
|
||||
&Signalled_time_source::_handle_timeout)
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* _SIGNALLED_TIME_SOURCE_H_ */
|
110
repos/base/src/timer/include/threaded_time_source.h
Normal file
110
repos/base/src/timer/include/threaded_time_source.h
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* \brief Time source that uses an extra thread for timeout handling
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2009-06-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _THREADED_TIME_SOURCE_H_
|
||||
#define _THREADED_TIME_SOURCE_H_
|
||||
|
||||
/* Genode inludes */
|
||||
#include <base/env.h>
|
||||
#include <base/rpc_client.h>
|
||||
#include <timer/timeout.h>
|
||||
|
||||
namespace Timer {
|
||||
|
||||
using Genode::Microseconds;
|
||||
using Genode::Duration;
|
||||
class Threaded_time_source;
|
||||
}
|
||||
|
||||
|
||||
class Timer::Threaded_time_source : public Genode::Time_source,
|
||||
protected Genode::Thread
|
||||
{
|
||||
private:
|
||||
|
||||
struct Irq_dispatcher : Genode::Interface
|
||||
{
|
||||
GENODE_RPC(Rpc_do_dispatch, void, do_dispatch);
|
||||
GENODE_RPC_INTERFACE(Rpc_do_dispatch);
|
||||
};
|
||||
|
||||
struct Irq_dispatcher_component : Genode::Rpc_object<Irq_dispatcher,
|
||||
Irq_dispatcher_component>
|
||||
{
|
||||
private:
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Irq_dispatcher_component(Irq_dispatcher_component const &);
|
||||
Irq_dispatcher_component &operator = (Irq_dispatcher_component const &);
|
||||
|
||||
public:
|
||||
|
||||
Timeout_handler *handler = nullptr;
|
||||
Threaded_time_source &ts;
|
||||
|
||||
Irq_dispatcher_component(Threaded_time_source &ts) : ts(ts) { }
|
||||
|
||||
/********************
|
||||
** Irq_dispatcher **
|
||||
********************/
|
||||
|
||||
void do_dispatch()
|
||||
{
|
||||
/* call curr_time in ep and not in ts (no locks in use!) */
|
||||
ts._irq = true;
|
||||
Duration us = ts.curr_time();
|
||||
ts._irq = false;
|
||||
|
||||
if (handler)
|
||||
handler->handle_timeout(us);
|
||||
}
|
||||
|
||||
} _irq_dispatcher_component;
|
||||
|
||||
Genode::Capability<Irq_dispatcher> _irq_dispatcher_cap;
|
||||
|
||||
virtual void _wait_for_irq() = 0;
|
||||
|
||||
/***********************
|
||||
** Thread_deprecated **
|
||||
***********************/
|
||||
|
||||
void entry()
|
||||
{
|
||||
while (true) {
|
||||
_wait_for_irq();
|
||||
_irq_dispatcher_cap.call<Irq_dispatcher::Rpc_do_dispatch>();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
bool _irq { false };
|
||||
|
||||
public:
|
||||
|
||||
Threaded_time_source(Genode::Env &env)
|
||||
:
|
||||
Thread(env, "threaded_time_source", 8 * 1024 * sizeof(Genode::addr_t)),
|
||||
_irq_dispatcher_component(*this),
|
||||
_irq_dispatcher_cap(env.ep().rpc_ep().manage(&_irq_dispatcher_component))
|
||||
{ }
|
||||
|
||||
void handler(Timeout_handler &handler) {
|
||||
_irq_dispatcher_component.handler = &handler; }
|
||||
};
|
||||
|
||||
#endif /* _THREADED_TIME_SOURCE_H_ */
|
43
repos/base/src/timer/main.cc
Normal file
43
repos/base/src/timer/main.cc
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* \brief Provides the Timer service to multiple clients
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2006-08-15
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/heap.h>
|
||||
#include <base/component.h>
|
||||
|
||||
/* local includes */
|
||||
#include <root_component.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
class Main
|
||||
{
|
||||
private:
|
||||
|
||||
Sliced_heap _sliced_heap;
|
||||
Timer::Root_component _root;
|
||||
|
||||
public:
|
||||
|
||||
Main(Env &env) : _sliced_heap(env.ram(), env.rm()),
|
||||
_root(env, _sliced_heap)
|
||||
{
|
||||
env.parent().announce(env.ep().manage(_root));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
size_t Component::stack_size() { return 4*1024*sizeof(addr_t); }
|
||||
void Component::construct(Env &env) { static Main main(env); }
|
52
repos/base/src/timer/periodic/time_source.cc
Normal file
52
repos/base/src/timer/periodic/time_source.cc
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* \brief Time source that uses sleeping by the means of the kernel
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2009-06-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <time_source.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Timer::Time_source::schedule_timeout(Microseconds duration,
|
||||
Timeout_handler &handler)
|
||||
{
|
||||
Genode::Lock::Guard lock_guard(_lock);
|
||||
Threaded_time_source::handler(handler);
|
||||
_next_timeout_us = duration.value;
|
||||
}
|
||||
|
||||
|
||||
void Timer::Time_source::_wait_for_irq()
|
||||
{
|
||||
enum { SLEEP_GRANULARITY_US = 1000UL };
|
||||
unsigned long last_time_us = curr_time().trunc_to_plain_us().value;
|
||||
_lock.lock();
|
||||
while (_next_timeout_us > 0) {
|
||||
_lock.unlock();
|
||||
|
||||
try { _usleep(SLEEP_GRANULARITY_US); }
|
||||
catch (Blocking_canceled) { }
|
||||
|
||||
unsigned long curr_time_us = curr_time().trunc_to_plain_us().value;
|
||||
unsigned long sleep_duration_us = curr_time_us - last_time_us;
|
||||
last_time_us = curr_time_us;
|
||||
|
||||
_lock.lock();
|
||||
if (_next_timeout_us >= sleep_duration_us)
|
||||
_next_timeout_us -= sleep_duration_us;
|
||||
else
|
||||
break;
|
||||
}
|
||||
_lock.unlock();
|
||||
}
|
58
repos/base/src/timer/periodic/time_source.h
Normal file
58
repos/base/src/timer/periodic/time_source.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* \brief Time source that uses sleeping by the means of the kernel
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2009-06-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _TIME_SOURCE_H_
|
||||
#define _TIME_SOURCE_H_
|
||||
|
||||
/* local includes */
|
||||
#include <threaded_time_source.h>
|
||||
|
||||
namespace Timer { class Time_source; }
|
||||
|
||||
|
||||
class Timer::Time_source : public Threaded_time_source
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Env &_env;
|
||||
|
||||
Genode::Lock mutable _lock { };
|
||||
unsigned long _curr_time_us = 0;
|
||||
unsigned long _next_timeout_us = max_timeout().value;
|
||||
|
||||
void _usleep(unsigned long us);
|
||||
|
||||
|
||||
/**************************
|
||||
** Threaded_time_source **
|
||||
**************************/
|
||||
|
||||
void _wait_for_irq();
|
||||
|
||||
public:
|
||||
|
||||
Time_source(Genode::Env &env)
|
||||
: Threaded_time_source(env), _env(env) { start(); }
|
||||
|
||||
|
||||
/*************************
|
||||
** Genode::Time_source **
|
||||
*************************/
|
||||
|
||||
Duration curr_time() override;
|
||||
Microseconds max_timeout() const override;
|
||||
void schedule_timeout(Microseconds duration, Timeout_handler &handler) override;
|
||||
};
|
||||
|
||||
#endif /* _TIME_SOURCE_H_ */
|
9
repos/base/src/timer/pit/target.inc
Normal file
9
repos/base/src/timer/pit/target.inc
Normal file
@ -0,0 +1,9 @@
|
||||
TARGET = pit_timer_drv
|
||||
REQUIRES = x86
|
||||
GEN_DIR := $(dir $(call select_from_repositories,src/timer/main.cc))
|
||||
INC_DIR += $(GEN_DIR)/pit
|
||||
SRC_CC += time_source.cc
|
||||
|
||||
include $(GEN_DIR)/target.inc
|
||||
|
||||
vpath time_source.cc $(GEN_DIR)/pit
|
210
repos/base/src/timer/pit/time_source.cc
Normal file
210
repos/base/src/timer/pit/time_source.cc
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* \brief Time source that uses the Programmable Interval Timer (PIT)
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2009-06-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <drivers/timer/util.h>
|
||||
|
||||
/* local includes */
|
||||
#include <time_source.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Timer::Time_source::_set_counter(uint16_t value)
|
||||
{
|
||||
_handled_wrap = false;
|
||||
_io_port.outb(PIT_DATA_PORT_0, value & 0xff);
|
||||
_io_port.outb(PIT_DATA_PORT_0, (value >> 8) & 0xff);
|
||||
}
|
||||
|
||||
|
||||
uint16_t Timer::Time_source::_read_counter(bool *wrapped)
|
||||
{
|
||||
/* read-back count and status of counter 0 */
|
||||
_io_port.outb(PIT_CMD_PORT, PIT_CMD_READ_BACK |
|
||||
PIT_CMD_RB_COUNT |
|
||||
PIT_CMD_RB_STATUS |
|
||||
PIT_CMD_RB_CHANNEL_0);
|
||||
|
||||
/* read status byte from latch register */
|
||||
uint8_t status = _io_port.inb(PIT_DATA_PORT_0);
|
||||
|
||||
/* read low and high bytes from latch register */
|
||||
uint16_t lo = _io_port.inb(PIT_DATA_PORT_0);
|
||||
uint16_t hi = _io_port.inb(PIT_DATA_PORT_0);
|
||||
|
||||
*wrapped = status & PIT_STAT_INT_LINE ? true : false;
|
||||
return (hi << 8) | lo;
|
||||
}
|
||||
|
||||
|
||||
void Timer::Time_source::schedule_timeout(Microseconds duration,
|
||||
Timeout_handler &handler)
|
||||
{
|
||||
_handler = &handler;
|
||||
unsigned long duration_us = duration.value;
|
||||
|
||||
/* timeout '0' is trigger to cancel the current pending, if required */
|
||||
if (!duration.value) {
|
||||
duration_us = max_timeout().value;
|
||||
Signal_transmitter(_signal_handler).submit();
|
||||
} else {
|
||||
/* limit timer-interrupt rate */
|
||||
enum { MAX_TIMER_IRQS_PER_SECOND = 4*1000 };
|
||||
if (duration_us < 1000 * 1000 / MAX_TIMER_IRQS_PER_SECOND)
|
||||
duration_us = 1000 * 1000 / MAX_TIMER_IRQS_PER_SECOND;
|
||||
|
||||
if (duration_us > max_timeout().value)
|
||||
duration_us = max_timeout().value;
|
||||
}
|
||||
|
||||
_counter_init_value = (PIT_TICKS_PER_MSEC * duration_us) / 1000;
|
||||
_set_counter(_counter_init_value);
|
||||
|
||||
if (duration.value)
|
||||
_timer_irq.ack_irq();
|
||||
}
|
||||
|
||||
|
||||
uint32_t Timer::Time_source::_ticks_since_update_no_wrap(uint16_t curr_counter)
|
||||
{
|
||||
/*
|
||||
* The counter did not wrap since the last update of _counter_init_value.
|
||||
* This means that _counter_init_value is equal to or greater than
|
||||
* curr_counter and that the time that passed is simply the difference
|
||||
* between the two.
|
||||
*/
|
||||
return _counter_init_value - curr_counter;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Timer::Time_source::_ticks_since_update_one_wrap(uint16_t curr_counter)
|
||||
{
|
||||
/*
|
||||
* The counter wrapped since the last update of _counter_init_value.
|
||||
* This means that the time that passed is the whole _counter_init_value
|
||||
* plus the time that passed since the counter wrapped.
|
||||
*/
|
||||
return _counter_init_value + PIT_MAX_COUNT - curr_counter;
|
||||
}
|
||||
|
||||
|
||||
Duration Timer::Time_source::curr_time()
|
||||
{
|
||||
/* read out and update curr time solely if running in context of irq */
|
||||
if (_irq)
|
||||
_curr_time();
|
||||
|
||||
return Duration(Microseconds(_curr_time_us));
|
||||
}
|
||||
|
||||
|
||||
Duration Timer::Time_source::_curr_time()
|
||||
{
|
||||
/* read PIT counter and wrapped status */
|
||||
uint32_t ticks;
|
||||
bool wrapped;
|
||||
uint16_t const curr_counter = _read_counter(&wrapped);
|
||||
|
||||
if (!wrapped) {
|
||||
|
||||
/*
|
||||
* The counter did not wrap since the last call to scheduled_timeout
|
||||
* which means that it did not wrap since the last update of
|
||||
* _counter_init_time.
|
||||
*/
|
||||
ticks = _ticks_since_update_no_wrap(curr_counter);
|
||||
}
|
||||
else if (wrapped && !_handled_wrap) {
|
||||
|
||||
/*
|
||||
* The counter wrapped at least once since the last call to
|
||||
* schedule_timeout (wrapped) and curr_time (!_handled_wrap) which
|
||||
* means that it definitely did wrap since the last update of
|
||||
* _counter_init_time. We cannot determine whether it wrapped only
|
||||
* once but we have to assume it. Even if it wrapped multiple times,
|
||||
* the error that results from the assumption that it did not is pretty
|
||||
* innocuous ((nr_of_wraps - 1) * 53 ms at a max).
|
||||
*/
|
||||
ticks = _ticks_since_update_one_wrap(curr_counter);
|
||||
_handled_wrap = true;
|
||||
}
|
||||
else { /* wrapped && _handled_wrap */
|
||||
|
||||
/*
|
||||
* The counter wrapped at least once since the last call to
|
||||
* schedule_timeout (wrapped) but may not have wrapped since the last
|
||||
* call to curr_time (_handled_wrap).
|
||||
*/
|
||||
|
||||
if (_counter_init_value >= curr_counter) {
|
||||
|
||||
/*
|
||||
* We cannot determine whether the counter wrapped since the last
|
||||
* call to curr_time but we have to assume that it did not. Even if
|
||||
* it wrapped, the error that results from the assumption that it
|
||||
* did not is pretty innocuous as long as _counter_init_value is
|
||||
* not greater than curr_counter (nr_of_wraps * 53 ms at a max).
|
||||
*/
|
||||
ticks = _ticks_since_update_no_wrap(curr_counter);
|
||||
|
||||
} else {
|
||||
|
||||
/*
|
||||
* The counter definitely wrapped multiple times since the last
|
||||
* call to schedule_timeout and at least once since the last call
|
||||
* to curr_time. It is the only explanation for the fact that
|
||||
* curr_counter became greater than _counter_init_value again
|
||||
* after _counter_init_value was updated with a wrapped counter
|
||||
* by curr_time (_handled_wrap). This means two things:
|
||||
*
|
||||
* First, the counter wrapped at least once since the last update
|
||||
* of _counter_init_value. We cannot determine whether it wrapped
|
||||
* only once but we have to assume it. Even if it wrapped multiple
|
||||
* times, the error that results from the assumption that it
|
||||
* did not is pretty innocuous ((nr_of_wraps - 1) * 53 ms at a max).
|
||||
*
|
||||
* Second, we have to warn the user as it is a sure indication of
|
||||
* insufficient activation latency if the counter wraps multiple
|
||||
* times between two schedule_timeout calls.
|
||||
*/
|
||||
warning("PIT wrapped multiple times, timer-driver latency too big");
|
||||
ticks = _ticks_since_update_one_wrap(curr_counter);
|
||||
}
|
||||
}
|
||||
|
||||
/* use current counter as the reference for the next update */
|
||||
_counter_init_value = curr_counter;
|
||||
|
||||
/* translate counter to microseconds and update time value */
|
||||
static_assert(PIT_TICKS_PER_MSEC >= (unsigned)TIMER_MIN_TICKS_PER_MS,
|
||||
"Bad TICS_PER_MS value");
|
||||
_curr_time_us += timer_ticks_to_us(ticks, PIT_TICKS_PER_MSEC);
|
||||
|
||||
return Duration(Microseconds(_curr_time_us));
|
||||
}
|
||||
|
||||
|
||||
Timer::Time_source::Time_source(Env &env)
|
||||
:
|
||||
Signalled_time_source(env),
|
||||
_io_port(env, PIT_DATA_PORT_0, PIT_CMD_PORT - PIT_DATA_PORT_0 + 1),
|
||||
_timer_irq(env, IRQ_PIT)
|
||||
{
|
||||
/* operate PIT in one-shot mode */
|
||||
_io_port.outb(PIT_CMD_PORT, PIT_CMD_SELECT_CHANNEL_0 |
|
||||
PIT_CMD_ACCESS_LO_HI | PIT_CMD_MODE_IRQ);
|
||||
|
||||
_timer_irq.sigh(_signal_handler);
|
||||
}
|
102
repos/base/src/timer/pit/time_source.h
Normal file
102
repos/base/src/timer/pit/time_source.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* \brief Time source that uses the Programmable Interval Timer (PIT)
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2009-06-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _TIME_SOURCE_H_
|
||||
#define _TIME_SOURCE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <io_port_session/connection.h>
|
||||
#include <irq_session/connection.h>
|
||||
#include <base/duration.h>
|
||||
|
||||
/* local includes */
|
||||
#include <signalled_time_source.h>
|
||||
|
||||
namespace Timer {
|
||||
|
||||
using Microseconds = Genode::Microseconds;
|
||||
using Duration = Genode::Duration;
|
||||
class Time_source;
|
||||
}
|
||||
|
||||
|
||||
class Timer::Time_source : public Genode::Signalled_time_source
|
||||
{
|
||||
private:
|
||||
|
||||
|
||||
enum {
|
||||
PIT_TICKS_PER_SECOND = 1193182,
|
||||
PIT_TICKS_PER_MSEC = PIT_TICKS_PER_SECOND/1000,
|
||||
PIT_MAX_COUNT = 65535,
|
||||
PIT_DATA_PORT_0 = 0x40, /* data port for PIT channel 0,
|
||||
connected to the PIC */
|
||||
PIT_CMD_PORT = 0x43, /* PIT command port */
|
||||
|
||||
PIT_MAX_USEC = (PIT_MAX_COUNT*1000)/(PIT_TICKS_PER_MSEC),
|
||||
|
||||
IRQ_PIT = 0, /* timer interrupt at the PIC */
|
||||
|
||||
/*
|
||||
* Bit definitions for accessing the PIT command port
|
||||
*/
|
||||
PIT_CMD_SELECT_CHANNEL_0 = 0 << 6,
|
||||
PIT_CMD_ACCESS_LO = 1 << 4,
|
||||
PIT_CMD_ACCESS_LO_HI = 3 << 4,
|
||||
PIT_CMD_MODE_IRQ = 0 << 1,
|
||||
PIT_CMD_MODE_RATE = 2 << 1,
|
||||
|
||||
PIT_CMD_READ_BACK = 3 << 6,
|
||||
PIT_CMD_RB_COUNT = 0 << 5,
|
||||
PIT_CMD_RB_STATUS = 0 << 4,
|
||||
PIT_CMD_RB_CHANNEL_0 = 1 << 1,
|
||||
|
||||
/*
|
||||
* Bit definitions of the PIT status byte
|
||||
*/
|
||||
PIT_STAT_INT_LINE = 1 << 7,
|
||||
};
|
||||
|
||||
Genode::Io_port_connection _io_port;
|
||||
Genode::Irq_connection _timer_irq;
|
||||
unsigned long mutable _curr_time_us = 0;
|
||||
Genode::uint16_t mutable _counter_init_value = 0;
|
||||
bool mutable _handled_wrap = false;
|
||||
|
||||
void _set_counter(Genode::uint16_t value);
|
||||
|
||||
Genode::uint16_t _read_counter(bool *wrapped);
|
||||
|
||||
Genode::uint32_t _ticks_since_update_one_wrap(Genode::uint16_t curr_counter);
|
||||
|
||||
Genode::uint32_t _ticks_since_update_no_wrap(Genode::uint16_t curr_counter);
|
||||
|
||||
Duration _curr_time();
|
||||
|
||||
public:
|
||||
|
||||
Time_source(Genode::Env &env);
|
||||
|
||||
|
||||
/*************************
|
||||
** Genode::Time_source **
|
||||
*************************/
|
||||
|
||||
Duration curr_time() override;
|
||||
void schedule_timeout(Microseconds duration, Timeout_handler &handler) override;
|
||||
Microseconds max_timeout() const override {
|
||||
return Microseconds(PIT_MAX_USEC); }
|
||||
};
|
||||
|
||||
#endif /* _TIME_SOURCE_H_ */
|
5
repos/base/src/timer/target.inc
Normal file
5
repos/base/src/timer/target.inc
Normal file
@ -0,0 +1,5 @@
|
||||
SRC_CC += main.cc
|
||||
LIBS += base
|
||||
INC_DIR += $(call select_from_repositories,src/timer/include)
|
||||
|
||||
vpath main.cc $(dir $(call select_from_repositories,src/timer/main.cc))
|
Reference in New Issue
Block a user