diff --git a/repos/base/run/timer_rate.run b/repos/base/run/timer_rate.run new file mode 100644 index 0000000000..e18fbf3bd1 --- /dev/null +++ b/repos/base/run/timer_rate.run @@ -0,0 +1,43 @@ +build { core init timer test/timer_rate } + +create_boot_directory + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + + +} +build_boot_image { core ld.lib.so init timer test-timer_rate } + +append qemu_args " -nographic" + +run_genode_until "child \"test\" exited with exit value.*\n" 120 +grep_output {\[init\] child "test" exited with exit value} +compare_output_to {[init] child "test" exited with exit value 0} + diff --git a/repos/base/src/test/timer_rate/main.cc b/repos/base/src/test/timer_rate/main.cc new file mode 100644 index 0000000000..04177043b1 --- /dev/null +++ b/repos/base/src/test/timer_rate/main.cc @@ -0,0 +1,182 @@ +/* + * \brief Test multiprocessor support of Timeout framework + * \author Martin Stein + * \date 2020-09-10 + */ + +/* + * Copyright (C) 2020 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 ve sion 3. + */ + +/* Genode includes */ +#include +#include +#include + +using namespace Genode; + +class Measurement +{ + private: + + Env &_env; + Signal_transmitter _done_transmitter; + uint64_t _set_period_us; + uint64_t _duration_us; + unsigned long volatile _count { 0 }; + uint64_t volatile _elapsed_ms[3] { 0, 0, 0 }; + uint64_t volatile *volatile _elapsed_ms_ptr { _elapsed_ms }; + Timer::Connection _timer { _env }; + Signal_handler _handler { _env.ep(), *this, &Measurement::_handle }; + uint64_t _nr_of_periods { _duration_us / _set_period_us }; + double _error_pc { 0 }; + double _avg_period_us { 0 }; + uint64_t _elapsed_us { 0 }; + + void _handle() + { + _count++; + if (_count % _nr_of_periods != 1) { + return; + } + *_elapsed_ms_ptr = _timer.elapsed_ms(); + _elapsed_ms_ptr = (uint64_t *)((addr_t)_elapsed_ms_ptr + sizeof(uint64_t)); + if (_count == 1) { + return; + } + _timer.sigh(Signal_context_capability()); + + _elapsed_us = (_elapsed_ms[1] - _elapsed_ms[0]) * 1'000; + _avg_period_us = (double)_elapsed_us / (double)(_count - 1); + _error_pc = ((double)_avg_period_us * + (double)100 / (double)_set_period_us) - 100; + + _done_transmitter.submit(); + } + + Measurement(const Measurement&); + const Measurement& operator=(const Measurement&); + + public: + + Measurement( + Env &env, + Signal_context_capability done_sigh, + uint64_t set_period_us, + uint64_t duration_us) + : + _env { env }, + _done_transmitter { done_sigh }, + _set_period_us { set_period_us }, + _duration_us { duration_us } + { + log(" Measure: set period: ", _set_period_us, + " us, periods: ", _nr_of_periods); + + _timer.sigh(_handler); + _timer.trigger_periodic(_set_period_us); + } + + double error_pc() const { return _error_pc; } + double avg_period_us() const { return _avg_period_us; } + uint64_t elapsed_us() const { return _elapsed_us; } +}; + +class Test +{ + private: + + Env &_env; + Signal_transmitter _done_transmitter; + Attached_rom_dataspace _config_rom { _env, "config" }; + uint64_t _max_abs_error_pc { _config_rom.xml().attribute_value("max_abs_error_pc", (uint64_t)5) }; + uint64_t _measure_duration_us { _config_rom.xml().attribute_value("measure_duration_us", (uint64_t)3'000'000) }; + uint64_t _min_good_bad_diff_us { _config_rom.xml().attribute_value("min_good_bad_diff_us", (uint64_t)10) }; + uint64_t _good_period_us { _measure_duration_us }; + uint64_t _bad_period_us { 0 }; + uint64_t _set_period_us { (_good_period_us - _bad_period_us) / 2 }; + Constructible _measurement { }; + Signal_handler _measurement_done_sigh { _env.ep(), *this, + &Test::_handle_measurement_done }; + + void _handle_measurement_done() + { + if (_measurement.constructed()) { + + if (_measurement->error_pc() > (double)_max_abs_error_pc || + _measurement->error_pc() < (double)_max_abs_error_pc * -1) { + + log(" Bad: avg period: ", _measurement->avg_period_us(), + " us, measure duration: ", _measurement->elapsed_us(), + " us, error: ", _measurement->error_pc(), " %"); + + _bad_period_us = _set_period_us; + + } else { + + log(" Good: avg period: ", _measurement->avg_period_us(), + " us, measure duration: ", _measurement->elapsed_us(), + " us, error: ", _measurement->error_pc(), " %"); + + _good_period_us = _set_period_us; + } + if (_good_period_us - _bad_period_us < _min_good_bad_diff_us) { + + log("Test result: lowest period value with error < ", + _max_abs_error_pc , "% is ", _good_period_us, " us"); + + _done_transmitter.submit(); + return; + } + _set_period_us = _bad_period_us + ((_good_period_us - _bad_period_us) / 2); + } + _measurement.construct(_env, _measurement_done_sigh, _set_period_us, _measure_duration_us); + } + + public: + + Test(Env &env, + Signal_context_capability done_sigh) + : + _env { env }, + _done_transmitter { done_sigh } + { + log("Test: find lowest period value with error < ", + _max_abs_error_pc, " %, measure duration: ", + _measure_duration_us, " us, min good-bad diff: ", + _min_good_bad_diff_us, " us"); + + _handle_measurement_done(); + } +}; + +class Main +{ + private: + + Env &_env; + Constructible _test { }; + Signal_handler
_test_done_sigh { _env.ep(), *this, &Main::_handle_test_done }; + + void _handle_test_done() + { + _env.parent().exit(0); + } + + public: + + Main(Env &env) + : + _env { env } + { + _test.construct(_env, _test_done_sigh); + } +}; + +void Component::construct(Env & env) +{ + static Main main { env }; +} diff --git a/repos/base/src/test/timer_rate/target.mk b/repos/base/src/test/timer_rate/target.mk new file mode 100644 index 0000000000..c63e39defc --- /dev/null +++ b/repos/base/src/test/timer_rate/target.mk @@ -0,0 +1,3 @@ +TARGET = test-timer_rate +SRC_CC = main.cc +LIBS = base