| /* |
| * 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 <sstream> |
| #include <vector> |
| |
| #include "base/logging.hh" |
| #include "base/types.hh" |
| #include "python/pybind11/pybind.hh" |
| #include "sim/core.hh" |
| #include "systemc/core/python.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 |
| { |
| |
| const char *TimeUnitNames[] = { |
| [SC_FS] = "fs", |
| [SC_PS] = "ps", |
| [SC_NS] = "ns", |
| [SC_US] = "us", |
| [SC_MS] = "ms", |
| [SC_SEC] = "s" |
| }; |
| |
| double TimeUnitScale[] = { |
| [SC_FS] = 1.0e-15, |
| [SC_PS] = 1.0e-12, |
| [SC_NS] = 1.0e-9, |
| [SC_US] = 1.0e-6, |
| [SC_MS] = 1.0e-3, |
| [SC_SEC] = 1.0 |
| }; |
| |
| Tick TimeUnitFrequency[] = { |
| [SC_FS] = 1ULL * 1000 * 1000 * 1000 * 1000 * 1000, |
| [SC_PS] = 1ULL * 1000 * 1000 * 1000 * 1000, |
| [SC_NS] = 1ULL * 1000 * 1000 * 1000, |
| [SC_US] = 1ULL * 1000 * 1000, |
| [SC_MS] = 1ULL * 1000, |
| [SC_SEC] = 1ULL |
| }; |
| |
| 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<SetInfo> toSet; |
| |
| void |
| setWork(sc_time *time, double d, ::sc_core::sc_time_unit tu) |
| { |
| double scale = 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<uint64_t>(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, 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<double>(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<double>(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<int64_t>(static_cast<double>(val) * d + 0.5); |
| return *this; |
| } |
| |
| sc_time & |
| sc_time::operator /= (double d) |
| { |
| val = static_cast<int64_t>(static_cast<double>(val) / d + 0.5); |
| return *this; |
| } |
| |
| void |
| sc_time::print(std::ostream &os) const |
| { |
| if (val == 0) { |
| os << "0 s"; |
| } else { |
| 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. |
| sc_time_unit tu = SC_SEC; |
| while (tu > 1 && (frequency % 1000 == 0)) { |
| tu = (sc_time_unit)((int)tu - 1); |
| frequency /= 1000; |
| } |
| |
| // Convert the frequency into a period. |
| Tick period; |
| if (frequency > 1) { |
| tu = (sc_time_unit)((int)tu - 1); |
| period = 1000 / frequency; |
| } else { |
| period = frequency; |
| } |
| |
| // Scale our integer value by the period. |
| uint64_t scaled = val * period; |
| |
| // Shrink the scaled time value by increasing the size of the units |
| // it's measured by, avoiding fractional parts. |
| while (tu < SC_SEC && (scaled % 1000) == 0) { |
| tu = (sc_time_unit)((int)tu + 1); |
| scaled /= 1000; |
| } |
| |
| os << scaled << ' ' << TimeUnitNames[tu]; |
| } |
| } |
| |
| 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) |
| { |
| warn("%s not implemented.\n", __PRETTY_FUNCTION__); |
| return sc_time(); |
| } |
| |
| 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<double>(t.value()) * d + 0.5; |
| return sc_time::from_value(static_cast<int64_t>(tmp)); |
| } |
| |
| const sc_time |
| operator * (double d, const sc_time &t) |
| { |
| volatile double tmp = d * static_cast<double>(t.value()) + 0.5; |
| return sc_time::from_value(static_cast<int64_t>(tmp)); |
| } |
| |
| const sc_time |
| operator / (const sc_time &t, double d) |
| { |
| volatile double tmp = static_cast<double>(t.value()) / d + 0.5; |
| return sc_time::from_value(static_cast<int64_t>(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("(E514) set time resolution failed", |
| "value not positive"); |
| } |
| double dummy; |
| if (modf(log10(d), &dummy) != 0.0) { |
| SC_REPORT_ERROR("(E514) set time resolution failed", |
| "value not a power of ten"); |
| } |
| if (sc_is_running()) { |
| SC_REPORT_ERROR("(E514) set time resolution failed", |
| "simulation running"); |
| } |
| static bool specified = false; |
| if (specified) { |
| SC_REPORT_ERROR("(E514) set time resolution failed", |
| "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("(E514) set time resolution failed", |
| "sc_time object(s) constructed"); |
| } |
| |
| double seconds = d * TimeUnitScale[tu]; |
| if (seconds < TimeUnitScale[SC_FS]) { |
| SC_REPORT_ERROR("(E514) set time resolution failed", |
| "value smaller than 1 fs"); |
| } |
| |
| if (seconds > defaultUnit) { |
| SC_REPORT_WARNING( |
| "(W516) default time unit changed to time resolution", ""); |
| 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 = TimeUnitFrequency[tu] / static_cast<Tick>(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("(E515) set default time unit failed", |
| "value not positive"); |
| } |
| double dummy; |
| if (modf(log10(d), &dummy) != 0.0) { |
| SC_REPORT_ERROR("(E515) set default time unit failed", |
| "value not a power of ten"); |
| } |
| if (sc_is_running()) { |
| SC_REPORT_ERROR("(E515) set default time unit failed", |
| "simulation running"); |
| } |
| static bool specified = false; |
| if (specified) { |
| SC_REPORT_ERROR("(E515) set default time unit failed", |
| "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("(E515) set default time unit failed", |
| "sc_time object(s) constructed"); |
| } |
| |
| // Normalize d to seconds. |
| defaultUnit = d * TimeUnitScale[tu]; |
| specified = true; |
| } |
| |
| sc_time |
| sc_get_default_time_unit() |
| { |
| return sc_time(defaultUnit, SC_SEC); |
| } |
| |
| sc_time_tuple::sc_time_tuple(const sc_time &) |
| { |
| warn("%s not implemented.\n", __PRETTY_FUNCTION__); |
| } |
| |
| bool |
| sc_time_tuple::has_value() const |
| { |
| warn("%s not implemented.\n", __PRETTY_FUNCTION__); |
| return false; |
| } |
| |
| sc_dt::uint64 |
| sc_time_tuple::value() const |
| { |
| warn("%s not implemented.\n", __PRETTY_FUNCTION__); |
| return 0; |
| } |
| |
| const char * |
| sc_time_tuple::unit_symbol() const |
| { |
| warn("%s not implemented.\n", __PRETTY_FUNCTION__); |
| return ""; |
| } |
| |
| double |
| sc_time_tuple::to_double() const |
| { |
| warn("%s not implemented.\n", __PRETTY_FUNCTION__); |
| return 0.0; |
| } |
| |
| std::string |
| sc_time_tuple::to_string() const |
| { |
| warn("%s not implemented.\n", __PRETTY_FUNCTION__); |
| return ""; |
| } |
| |
| } // namespace sc_core |