/* * Copyright 2018 Google, Inc. * * 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. * * Authors: Gabe Black */ #include #include #include "base/logging.hh" #include "base/types.hh" #include "python/pybind11/pybind.hh" #include "sim/core.hh" #include "systemc/core/python.hh" #include "systemc/core/time.hh" #include "systemc/ext/core/messages.hh" #include "systemc/ext/core/sc_main.hh" #include "systemc/ext/core/sc_time.hh" #include "systemc/ext/utils/sc_report_handler.hh" namespace sc_core { namespace { bool timeFixed = false; bool pythonReady = false; struct SetInfo { SetInfo(::sc_core::sc_time *time, double d, ::sc_core::sc_time_unit tu) : time(time), d(d), tu(tu) {} ::sc_core::sc_time *time; double d; ::sc_core::sc_time_unit tu; }; std::vector toSet; void setWork(sc_time *time, double d, ::sc_core::sc_time_unit tu) { double scale = sc_gem5::TimeUnitScale[tu] * SimClock::Float::s; // Accellera claims there is a linux bug, and that these next two // lines work around them. volatile double tmp = d * scale + 0.5; *time = sc_time::from_value(static_cast(tmp)); } void fixTime() { auto ticks = pybind11::module::import("m5.ticks"); auto fix_global_frequency = ticks.attr("fixGlobalFrequency"); fix_global_frequency(); for (auto &t: toSet) setWork(t.time, t.d, t.tu); toSet.clear(); } void attemptToFixTime() { // Only fix time once. if (!timeFixed) { timeFixed = true; // If we've run, python is working and we haven't fixed time yet. if (pythonReady) fixTime(); } } void setGlobalFrequency(Tick ticks_per_second) { auto ticks = pybind11::module::import("m5.ticks"); auto set_global_frequency = ticks.attr("setGlobalFrequency"); set_global_frequency(ticks_per_second); fixTime(); } void set(::sc_core::sc_time *time, double d, ::sc_core::sc_time_unit tu) { if (d != 0) attemptToFixTime(); if (pythonReady) { // Time should be working. Set up this sc_time. setWork(time, d, tu); } else { // Time isn't set up yet. Defer setting up this sc_time. toSet.emplace_back(time, d, tu); } } class TimeSetter : public ::sc_gem5::PythonReadyFunc { public: TimeSetter() : ::sc_gem5::PythonReadyFunc() {} void run() override { // Record that we've run and python/pybind should be usable. pythonReady = true; // If time is already fixed, let python know. if (timeFixed) fixTime(); } } timeSetter; double defaultUnit = 1.0e-9; } // anonymous namespace sc_time::sc_time() : val(0) {} sc_time::sc_time(double d, sc_time_unit tu) { val = 0; set(this, d, tu); } sc_time::sc_time(const sc_time &t) { val = t.val; } sc_time::sc_time(double d, const char *unit) { sc_time_unit tu; for (tu = SC_FS; tu <= SC_SEC; tu = (sc_time_unit)(tu + 1)) { if (strcmp(unit, sc_gem5::TimeUnitNames[tu]) == 0 || strcmp(unit, sc_gem5::TimeUnitConstantNames[tu]) == 0) { break; } } if (tu > SC_SEC) { SC_REPORT_ERROR(SC_ID_TIME_CONVERSION_FAILED_,"invalid unit given"); val = 0; return; } set(this, d, tu); } sc_time::sc_time(double d, bool scale) { double scaler = scale ? defaultUnit : SimClock::Float::Hz; set(this, d * scaler, SC_SEC); } sc_time::sc_time(sc_dt::uint64 v, bool scale) { double scaler = scale ? defaultUnit : SimClock::Float::Hz; set(this, static_cast(v) * scaler, SC_SEC); } sc_time & sc_time::operator = (const sc_time &t) { val = t.val; return *this; } sc_dt::uint64 sc_time::value() const { return val; } double sc_time::to_double() const { return static_cast(val); } double sc_time::to_seconds() const { return to_double() * SimClock::Float::Hz; } const std::string sc_time::to_string() const { std::ostringstream ss; print(ss); return ss.str(); } bool sc_time::operator == (const sc_time &t) const { return val == t.val; } bool sc_time::operator != (const sc_time &t) const { return val != t.val; } bool sc_time::operator < (const sc_time &t) const { return val < t.val; } bool sc_time::operator <= (const sc_time &t) const { return val <= t.val; } bool sc_time::operator > (const sc_time &t) const { return val > t.val; } bool sc_time::operator >= (const sc_time &t) const { return val >= t.val; } sc_time & sc_time::operator += (const sc_time &t) { val += t.val; return *this; } sc_time & sc_time::operator -= (const sc_time &t) { val -= t.val; return *this; } sc_time & sc_time::operator *= (double d) { val = static_cast(static_cast(val) * d + 0.5); return *this; } sc_time & sc_time::operator /= (double d) { val = static_cast(static_cast(val) / d + 0.5); return *this; } void sc_time::print(std::ostream &os) const { os << sc_time_tuple(*this).to_string(); } sc_time sc_time::from_value(sc_dt::uint64 u) { if (u) attemptToFixTime(); sc_time t; t.val = u; return t; } sc_time sc_time::from_seconds(double d) { sc_time t; set(&t, d, SC_SEC); return t; } sc_time sc_time::from_string(const char *str) { char *end = nullptr; double d = str ? std::strtod(str, &end) : 0.0; if (str == end || d < 0.0) { SC_REPORT_ERROR(SC_ID_TIME_CONVERSION_FAILED_, "invalid value given"); return SC_ZERO_TIME; } while (*end && std::isspace(*end)) end++; return sc_time(d, end); } const sc_time operator + (const sc_time &a, const sc_time &b) { return sc_time::from_value(a.value() + b.value()); } const sc_time operator - (const sc_time &a, const sc_time &b) { return sc_time::from_value(a.value() - b.value()); } const sc_time operator * (const sc_time &t, double d) { volatile double tmp = static_cast(t.value()) * d + 0.5; return sc_time::from_value(static_cast(tmp)); } const sc_time operator * (double d, const sc_time &t) { volatile double tmp = d * static_cast(t.value()) + 0.5; return sc_time::from_value(static_cast(tmp)); } const sc_time operator / (const sc_time &t, double d) { volatile double tmp = static_cast(t.value()) / d + 0.5; return sc_time::from_value(static_cast(tmp)); } double operator / (const sc_time &t1, const sc_time &t2) { return t1.to_double() / t2.to_double(); } std::ostream & operator << (std::ostream &os, const sc_time &t) { t.print(os); return os; } const sc_time SC_ZERO_TIME; void sc_set_time_resolution(double d, sc_time_unit tu) { if (d <= 0.0) SC_REPORT_ERROR(SC_ID_SET_TIME_RESOLUTION_, "value not positive"); double dummy; if (modf(log10(d), &dummy) != 0.0) { SC_REPORT_ERROR(SC_ID_SET_TIME_RESOLUTION_, "value not a power of ten"); } if (sc_is_running()) SC_REPORT_ERROR(SC_ID_SET_TIME_RESOLUTION_, "simulation running"); static bool specified = false; if (specified) SC_REPORT_ERROR(SC_ID_SET_TIME_RESOLUTION_, "already specified"); // This won't detect the timescale being fixed outside of systemc, but // it's at least some protection. if (timeFixed) { SC_REPORT_ERROR(SC_ID_SET_TIME_RESOLUTION_, "sc_time object(s) constructed"); } double seconds = d * sc_gem5::TimeUnitScale[tu]; if (seconds < sc_gem5::TimeUnitScale[SC_FS]) SC_REPORT_ERROR(SC_ID_SET_TIME_RESOLUTION_, "value smaller than 1 fs"); if (seconds > defaultUnit) { SC_REPORT_WARNING(SC_ID_DEFAULT_TIME_UNIT_CHANGED_, ""); defaultUnit = seconds; } // Get rid of fractional parts of d. while (d < 1.0 && tu > SC_FS) { d *= 1000; tu = (sc_time_unit)(tu - 1); } Tick ticks_per_second = sc_gem5::TimeUnitFrequency[tu] / static_cast(d); setGlobalFrequency(ticks_per_second); specified = true; } sc_time sc_get_time_resolution() { return sc_time::from_value(1); } const sc_time & sc_max_time() { static const sc_time MaxScTime = sc_time::from_value(MaxTick); return MaxScTime; } void sc_set_default_time_unit(double d, sc_time_unit tu) { if (d < 0.0) SC_REPORT_ERROR(SC_ID_SET_DEFAULT_TIME_UNIT_, "value not positive"); double dummy; if (modf(log10(d), &dummy) != 0.0) { SC_REPORT_ERROR(SC_ID_SET_DEFAULT_TIME_UNIT_, "value not a power of ten"); } if (sc_is_running()) SC_REPORT_ERROR(SC_ID_SET_DEFAULT_TIME_UNIT_, "simulation running"); static bool specified = false; if (specified) { SC_REPORT_ERROR(SC_ID_SET_DEFAULT_TIME_UNIT_, "already specified"); } // This won't detect the timescale being fixed outside of systemc, but // it's at least some protection. if (timeFixed) { SC_REPORT_ERROR(SC_ID_SET_DEFAULT_TIME_UNIT_, "sc_time object(s) constructed"); } // Normalize d to seconds. defaultUnit = d * sc_gem5::TimeUnitScale[tu]; specified = true; double resolution = SimClock::Float::Hz; if (resolution == 0.0) resolution = sc_gem5::TimeUnitScale[SC_PS]; if (defaultUnit < resolution) { SC_REPORT_ERROR(SC_ID_SET_DEFAULT_TIME_UNIT_, "value smaller than time resolution"); } } sc_time sc_get_default_time_unit() { return sc_time(defaultUnit, SC_SEC); } sc_time_tuple::sc_time_tuple(const sc_time &t) : _value(), _unit(SC_SEC), _set(true) { if (!t.value()) return; Tick frequency = SimClock::Frequency; // Shrink the frequency by scaling down the time period, ie converting // it from cycles per second to cycles per millisecond, etc. while (_unit > 1 && (frequency % 1000 == 0)) { _unit = (sc_time_unit)((int)_unit - 1); frequency /= 1000; } // Convert the frequency into a period. Tick period; if (frequency > 1) { _unit = (sc_time_unit)((int)_unit - 1); period = 1000 / frequency; } else { period = frequency; } // Scale our integer value by the period. _value = t.value() * period; // Shrink the scaled time value by increasing the size of the units // it's measured by, avoiding fractional parts. while (_unit < SC_SEC && (_value % 1000) == 0) { _unit = (sc_time_unit)((int)_unit + 1); _value /= 1000; } } bool sc_time_tuple::has_value() const { return _set; } sc_dt::uint64 sc_time_tuple::value() const { return _value; } const char * sc_time_tuple::unit_symbol() const { return sc_gem5::TimeUnitNames[_unit]; } double sc_time_tuple::to_double() const { return static_cast(_value); } std::string sc_time_tuple::to_string() const { std::ostringstream ss; ss << _value << ' ' << unit_symbol(); return ss.str(); } } // namespace sc_core