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:
Norman Feske
2019-01-03 18:01:49 +01:00
parent 14cd115c82
commit bf62d6b896
222 changed files with 272 additions and 454 deletions

View 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;
}

View 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_ */

View 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)

View 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>()) ;
}

View 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))); }

View 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_ */

View 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_ */

View 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_ */

View 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_ */

View 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); }

View 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();
}

View 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_ */

View 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

View 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);
}

View 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_ */

View 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))