| /* | |
| * 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 <cmath> | |
| #include <cstring> | |
| #include <sstream> | |
| #include <vector> | |
| #include "base/types.hh" | |
| #include "sim/core.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 | |
| { | |
| void | |
| set(::sc_core::sc_time *time, double d, ::sc_core::sc_time_unit tu) | |
| { | |
| if (d != 0) | |
| fixClockFrequency(); | |
| 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<uint64_t>(tmp)); | |
| } | |
| 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<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 | |
| { | |
| os << sc_time_tuple(*this).to_string(); | |
| } | |
| sc_time | |
| sc_time::from_value(sc_dt::uint64 u) | |
| { | |
| if (u) | |
| fixClockFrequency(); | |
| 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<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(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 (clockFrequencyFixed()) { | |
| 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<Tick>(d); | |
| setClockFrequency(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 (clockFrequencyFixed()) { | |
| 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<double>(_value); } | |
| std::string | |
| sc_time_tuple::to_string() const | |
| { | |
| std::ostringstream ss; | |
| ss << _value << ' ' << unit_symbol(); | |
| return ss.str(); | |
| } | |
| } // namespace sc_core |