From 69f4aee33c07134a651dd032f28944e30ab5b322 Mon Sep 17 00:00:00 2001 From: Andreas Sandberg Date: Tue, 19 Jan 2021 10:09:56 +0000 Subject: [PATCH] base, python: Add a Temperature type and associated param Add a class to represent a temperature. The class stores temperatures in Kelvin and provides helper methods to convert to/from Celsius. The corresponding param type automatically converts from Kelvin, Celsius, and Fahrenheit to the underlying C++ type. Change-Id: I5783cc4f4fecbea5aba9821dfc71bfa77c3f75a9 Signed-off-by: Andreas Sandberg Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/39218 Maintainer: Jason Lowe-Power Reviewed-by: Daniel Carvalho Reviewed-by: Gabe Black Tested-by: kokoro --- src/base/SConscript | 2 + src/base/temperature.cc | 69 ++++++++++++ src/base/temperature.hh | 176 ++++++++++++++++++++++++++++++ src/base/temperature.test.cc | 123 +++++++++++++++++++++ src/python/m5/params.py | 40 ++++++- src/python/m5/util/convert.py | 22 ++++ src/python/pybind11/core.cc | 35 +++++- tests/pyunit/util/test_convert.py | 13 +++ 8 files changed, 478 insertions(+), 2 deletions(-) create mode 100644 src/base/temperature.cc create mode 100644 src/base/temperature.hh create mode 100644 src/base/temperature.test.cc diff --git a/src/base/SConscript b/src/base/SConscript index 393731449..204ed3cdb 100644 --- a/src/base/SConscript +++ b/src/base/SConscript @@ -72,6 +72,8 @@ Source('str.cc') GTest('str.test', 'str.test.cc', 'str.cc') Source('time.cc') Source('version.cc') +Source('temperature.cc') +GTest('temperature.test', 'temperature.test.cc', 'temperature.cc') Source('trace.cc') GTest('trie.test', 'trie.test.cc') Source('types.cc') diff --git a/src/base/temperature.cc b/src/base/temperature.cc new file mode 100644 index 000000000..b1d9c9a29 --- /dev/null +++ b/src/base/temperature.cc @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 Arm Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "base/temperature.hh" + +Temperature +Temperature::fromKelvin(double _value) +{ + return Temperature(_value); +} + +Temperature +Temperature::fromCelsius(double _value) +{ + return Temperature(273.15 + _value); +} + +Temperature +Temperature::fromFahrenheit(double _value) +{ + return Temperature((_value + 459.67) / 1.8); +} + +double +Temperature::toFahrenheit() const +{ + return value * 1.8 - 459.67; +} + +std::ostream & +operator<<(std::ostream &out, const Temperature &temp) +{ + out << temp.value << "K"; + return out; +} diff --git a/src/base/temperature.hh b/src/base/temperature.hh new file mode 100644 index 000000000..bcb51993d --- /dev/null +++ b/src/base/temperature.hh @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2021 Arm Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __BASE_TEMPERATURE_HH__ +#define __BASE_TEMPERATURE_HH__ + +#include + +/** + * The class stores temperatures in Kelvin and provides helper methods + * to convert to/from Celsius. + */ +class Temperature +{ + + private: + /** Temperature in Kelvin */ + double value; + + public: + /** Explicit constructor assigning a value. */ + explicit constexpr Temperature(double _value=0.0) + : value(_value) + { + } + + static Temperature fromKelvin(double _value); + static Temperature fromCelsius(double _value); + static Temperature fromFahrenheit(double _value); + + constexpr double toKelvin() const { return value; } + constexpr double toCelsius() const { return value - 273.15; } + double toFahrenheit() const; + + constexpr bool + operator>(const Temperature &rhs) const + { + return value > rhs.value; + } + + constexpr bool + operator>=(const Temperature &rhs) const + { + return value >= rhs.value; + } + + constexpr bool + operator<(const Temperature &rhs) const + { + return value < rhs.value; + } + + constexpr bool + operator<=(const Temperature &rhs) const + { + return value <= rhs.value; + } + + constexpr bool + operator==(const Temperature &rhs) const + { + return value == rhs.value; + } + + constexpr bool + operator!=(const Temperature &rhs) const + { + return value != rhs.value; + } + + constexpr Temperature + operator+(const Temperature &rhs) const + { + return Temperature(value + rhs.value); + } + + constexpr Temperature + operator-(const Temperature &rhs) const + { + return Temperature(value - rhs.value); + } + + friend constexpr Temperature operator*( + const Temperature &lhs, const double &rhs); + + friend constexpr Temperature operator*( + const double &lhs, const Temperature &rhs); + + friend constexpr Temperature operator/( + const Temperature &lhs, const double &rhs); + + Temperature & + operator+=(const Temperature &rhs) + { + value += rhs.value; + return *this; + } + + Temperature & + operator-=(const Temperature &rhs) + { + value -= rhs.value; + return *this; + } + + Temperature & + operator*=(const double &rhs) + { + value *= rhs; + return *this; + } + + Temperature & + operator/=(const double &rhs) + { + value /= rhs; + return *this; + } + + friend std::ostream &operator<<(std::ostream &out, const Temperature &t); +}; + +constexpr Temperature +operator*(const Temperature &lhs, const double &rhs) +{ + return Temperature(lhs.value * rhs); +} + +constexpr Temperature +operator*(const double &lhs, const Temperature &rhs) +{ + return Temperature(lhs * rhs.value); +} + +constexpr Temperature +operator/(const Temperature &lhs, const double &rhs) +{ + return Temperature(lhs.value / rhs); +} + + +#endif // __BASE_TEMPERATURE_HH__ diff --git a/src/base/temperature.test.cc b/src/base/temperature.test.cc new file mode 100644 index 000000000..1d7b04895 --- /dev/null +++ b/src/base/temperature.test.cc @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021 Arm Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include "base/temperature.hh" + +TEST(TemperatureTest, Constructor) +{ + Temperature temp; + EXPECT_EQ(temp, Temperature(0.0)); +} + +TEST(TemperatureTest, Conversion) +{ + Temperature freezing(273.15); + + EXPECT_EQ(Temperature::fromKelvin(42.0), Temperature(42.0)); + EXPECT_EQ(Temperature::fromCelsius(0.0), freezing); + EXPECT_EQ(Temperature::fromFahrenheit(32.0), freezing); + + EXPECT_EQ(freezing.toKelvin(), 273.15); + EXPECT_EQ(freezing.toCelsius(), 0.0); + EXPECT_EQ(Temperature(0).toFahrenheit(), -459.67); +} + +TEST(TemperatureTest, Comparison) +{ + EXPECT_TRUE(Temperature(2.0) < Temperature(3.0)); + EXPECT_FALSE(Temperature(2.0) < Temperature(2.0)); + EXPECT_FALSE(Temperature(2.0) < Temperature(1.0)); + + EXPECT_FALSE(Temperature(2.0) > Temperature(3.0)); + EXPECT_FALSE(Temperature(2.0) > Temperature(2.0)); + EXPECT_TRUE(Temperature(2.0) > Temperature(1.0)); + + EXPECT_TRUE(Temperature(2.0) <= Temperature(3.0)); + EXPECT_TRUE(Temperature(2.0) <= Temperature(2.0)); + EXPECT_FALSE(Temperature(2.0) <= Temperature(1.0)); + + EXPECT_FALSE(Temperature(2.0) >= Temperature(3.0)); + EXPECT_TRUE(Temperature(2.0) >= Temperature(2.0)); + EXPECT_TRUE(Temperature(2.0) >= Temperature(1.0)); + + EXPECT_TRUE(Temperature(2.0) == Temperature(2.0)); + EXPECT_FALSE(Temperature(2.0) == Temperature(3.0)); + + EXPECT_FALSE(Temperature(2.0) != Temperature(2.0)); + EXPECT_TRUE(Temperature(2.0) != Temperature(3.0)); +} + +TEST(TemperatureTest, BinaryOperators) +{ + EXPECT_EQ(Temperature(2.0) + Temperature(1.0), Temperature(3.0)); + EXPECT_EQ(Temperature(2.0) - Temperature(1.0), Temperature(1.0)); + + EXPECT_EQ(Temperature(8.0) * 2.0, Temperature(16.0)); + EXPECT_EQ(2.0 * Temperature(8.0), Temperature(16.0)); + EXPECT_EQ(Temperature(8.0) / 2.0, Temperature(4.0)); +} + +TEST(TemperatureTest, AssignmentOperators) +{ + Temperature temp1(0); + temp1 += Temperature(1.0); + EXPECT_EQ(temp1, Temperature(1.0)); + + Temperature temp2(1.0); + temp2 -= Temperature(1.0); + EXPECT_EQ(temp2, Temperature(0.0)); + + Temperature temp3(32.0); + temp3 *= 2.0; + EXPECT_EQ(temp3, Temperature(64.0)); + + Temperature temp4(32.0); + temp4 /= 2.0; + EXPECT_EQ(temp4, Temperature(16.0)); +} + +TEST(TemperatureTest, OutStream) +{ + Temperature temp(42); + std::ostringstream ss; + ss << "T: " << temp << std::endl; + EXPECT_EQ("T: 42K\n", ss.str()); +} diff --git a/src/python/m5/params.py b/src/python/m5/params.py index 6227fad4c..43fd0d97c 100644 --- a/src/python/m5/params.py +++ b/src/python/m5/params.py @@ -1,4 +1,4 @@ -# Copyright (c) 2012-2014, 2017-2019 ARM Limited +# Copyright (c) 2012-2014, 2017-2019, 2021 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -1693,6 +1693,43 @@ class Energy(Float): value = convert.toEnergy(value) super(Energy, self).__init__(value) +class Temperature(ParamValue): + cxx_type = 'Temperature' + cmd_line_settable = True + ex_str = "1C" + + def __init__(self, value): + self.value = convert.toTemperature(value) + + def __call__(self, value): + self.__init__(value) + return value + + def getValue(self): + from _m5.core import Temperature + return Temperature.fromKelvin(self.value) + + def config_value(self): + return self + + @classmethod + def cxx_predecls(cls, code): + code('#include "base/temperature.hh"') + + @classmethod + def cxx_ini_predecls(cls, code): + # Assume that base/str.hh will be included anyway + # code('#include "base/str.hh"') + pass + + @classmethod + def cxx_ini_parse(self, code, src, dest, ret): + code('double _temp;') + code('bool _ret = to_number(%s, _temp);' % src) + code('if (_ret)') + code(' %s = Temperature(_temp);' % dest) + code('%s _ret;' % ret) + class NetworkBandwidth(float,ParamValue): cxx_type = 'float' ex_str = "1Gbps" @@ -2231,6 +2268,7 @@ __all__ = ['Param', 'VectorParam', 'IpAddress', 'IpNetmask', 'IpWithPort', 'MemorySize', 'MemorySize32', 'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy', + 'Temperature', 'NetworkBandwidth', 'MemoryBandwidth', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory', diff --git a/src/python/m5/util/convert.py b/src/python/m5/util/convert.py index 8f70d01a7..1d78f82d5 100644 --- a/src/python/m5/util/convert.py +++ b/src/python/m5/util/convert.py @@ -292,3 +292,25 @@ def toCurrent(value): def toEnergy(value): return toMetricFloat(value, 'energy', 'J') + +def toTemperature(value): + """Convert a string value specified to a temperature in Kelvin""" + + magnitude, unit = toNum(value, + target_type='temperature', + units=('K', 'C', 'F'), + prefixes=metric_prefixes, + converter=float) + if unit == 'K': + kelvin = magnitude + elif unit == 'C': + kelvin = magnitude + 273.15 + elif unit == 'F': + kelvin = (magnitude + 459.67) / 1.8 + else: + raise ValueError(f"'{value}' needs a valid temperature unit.") + + if kelvin < 0: + raise ValueError(f"{value} is an invalid temperature") + + return kelvin diff --git a/src/python/pybind11/core.cc b/src/python/pybind11/core.cc index 8655d736d..030246b60 100644 --- a/src/python/pybind11/core.cc +++ b/src/python/pybind11/core.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019 ARM Limited + * Copyright (c) 2017, 2019, 2021 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -52,6 +52,7 @@ #include "base/logging.hh" #include "base/random.hh" #include "base/socket.hh" +#include "base/temperature.hh" #include "base/types.hh" #include "sim/core.hh" #include "sim/drain.hh" @@ -220,6 +221,38 @@ pybind_init_core(py::module &m_native) .def("__sub__", &Cycles::operator-) ; + py::class_(m_core, "Temperature") + .def(py::init<>()) + .def(py::init()) + .def_static("from_celsius", &Temperature::fromCelsius) + .def_static("from_kelvin", &Temperature::fromKelvin) + .def_static("from_fahrenheit", &Temperature::fromFahrenheit) + .def("celsius", &Temperature::toCelsius) + .def("kelvin", &Temperature::toKelvin) + .def("fahrenheit", &Temperature::toFahrenheit) + .def(py::self == py::self) + .def(py::self != py::self) + .def(py::self < py::self) + .def(py::self <= py::self) + .def(py::self > py::self) + .def(py::self >= py::self) + .def(py::self + py::self) + .def(py::self - py::self) + .def(py::self * float()) + .def(float() * py::self) + .def(py::self / float()) + .def("__str__", [](const Temperature &t) { + std::stringstream s; + s << t; + return s.str(); + }) + .def("__repr__", [](const Temperature &t) { + std::stringstream s; + s << "Temperature(" << t.toKelvin() << ")"; + return s.str(); + }) + ; + py::class_(m_core, "tm") .def_static("gmtime", [](std::time_t t) { return *std::gmtime(&t); }) .def_readwrite("tm_sec", &tm::tm_sec) diff --git a/tests/pyunit/util/test_convert.py b/tests/pyunit/util/test_convert.py index 6d02b51c2..a9c9d4642 100644 --- a/tests/pyunit/util/test_convert.py +++ b/tests/pyunit/util/test_convert.py @@ -275,3 +275,16 @@ class ConvertTestSuite(unittest.TestCase): self.assertEqual(conv('42'), 42) self.assertEqual(conv('42J'), 42) self.assertEqual(conv('42kJ'), 42e3) + + def test_temperature(self): + conv = convert.toTemperature + + self.assertEqual(conv("1.0K"), 1.0) + self.assertEqual(conv("1.0mK"), 1.0e-3) + + self.assertEqual(conv("0C"), 273.15) + self.assertEqual(conv("-1C"), 272.15) + self.assertRaises(ValueError, conv, "1.0") + self.assertRaises(ValueError, conv, "-1K") + + self.assertEqual(conv("32F"), 273.15) -- 2.30.2