sc_time.cc revision 13317:36c574a4036e
1/*
2 * Copyright 2018 Google, Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met: redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer;
8 * redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution;
11 * neither the name of the copyright holders nor the names of its
12 * contributors may be used to endorse or promote products derived from
13 * this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * Authors: Gabe Black
28 */
29
30#include <sstream>
31#include <vector>
32
33#include "base/logging.hh"
34#include "base/types.hh"
35#include "python/pybind11/pybind.hh"
36#include "sim/core.hh"
37#include "systemc/core/python.hh"
38#include "systemc/core/time.hh"
39#include "systemc/ext/core/messages.hh"
40#include "systemc/ext/core/sc_main.hh"
41#include "systemc/ext/core/sc_time.hh"
42#include "systemc/ext/utils/sc_report_handler.hh"
43
44namespace sc_core
45{
46
47namespace
48{
49
50bool timeFixed = false;
51bool pythonReady = false;
52
53struct SetInfo
54{
55    SetInfo(::sc_core::sc_time *time, double d, ::sc_core::sc_time_unit tu) :
56        time(time), d(d), tu(tu)
57    {}
58
59    ::sc_core::sc_time *time;
60    double d;
61    ::sc_core::sc_time_unit tu;
62};
63std::vector<SetInfo> toSet;
64
65void
66setWork(sc_time *time, double d, ::sc_core::sc_time_unit tu)
67{
68    double scale = sc_gem5::TimeUnitScale[tu] * SimClock::Float::s;
69    // Accellera claims there is a linux bug, and that these next two
70    // lines work around them.
71    volatile double tmp = d * scale + 0.5;
72    *time = sc_time::from_value(static_cast<uint64_t>(tmp));
73}
74
75void
76fixTime()
77{
78    auto ticks = pybind11::module::import("m5.ticks");
79    auto fix_global_frequency = ticks.attr("fixGlobalFrequency");
80    fix_global_frequency();
81
82    for (auto &t: toSet)
83        setWork(t.time, t.d, t.tu);
84    toSet.clear();
85}
86
87void
88attemptToFixTime()
89{
90    // Only fix time once.
91    if (!timeFixed) {
92        timeFixed = true;
93
94        // If we've run, python is working and we haven't fixed time yet.
95        if (pythonReady)
96            fixTime();
97    }
98}
99
100void
101setGlobalFrequency(Tick ticks_per_second)
102{
103    auto ticks = pybind11::module::import("m5.ticks");
104    auto set_global_frequency = ticks.attr("setGlobalFrequency");
105    set_global_frequency(ticks_per_second);
106    fixTime();
107}
108
109void
110set(::sc_core::sc_time *time, double d, ::sc_core::sc_time_unit tu)
111{
112    if (d != 0)
113        attemptToFixTime();
114    if (pythonReady) {
115        // Time should be working. Set up this sc_time.
116        setWork(time, d, tu);
117    } else {
118        // Time isn't set up yet. Defer setting up this sc_time.
119        toSet.emplace_back(time, d, tu);
120    }
121}
122
123class TimeSetter : public ::sc_gem5::PythonReadyFunc
124{
125  public:
126    TimeSetter() : ::sc_gem5::PythonReadyFunc() {}
127
128    void
129    run() override
130    {
131        // Record that we've run and python/pybind should be usable.
132        pythonReady = true;
133
134        // If time is already fixed, let python know.
135        if (timeFixed)
136            fixTime();
137    }
138} timeSetter;
139
140double defaultUnit = 1.0e-9;
141
142} // anonymous namespace
143
144sc_time::sc_time() : val(0) {}
145
146sc_time::sc_time(double d, sc_time_unit tu)
147{
148    val = 0;
149    set(this, d, tu);
150}
151
152sc_time::sc_time(const sc_time &t)
153{
154    val = t.val;
155}
156
157sc_time::sc_time(double d, const char *unit)
158{
159    sc_time_unit tu;
160    for (tu = SC_FS; tu <= SC_SEC; tu = (sc_time_unit)(tu + 1)) {
161        if (strcmp(unit, sc_gem5::TimeUnitNames[tu]) == 0 ||
162            strcmp(unit, sc_gem5::TimeUnitConstantNames[tu]) == 0) {
163            break;
164        }
165    }
166
167    if (tu > SC_SEC) {
168        SC_REPORT_ERROR(SC_ID_TIME_CONVERSION_FAILED_,"invalid unit given");
169        val = 0;
170        return;
171    }
172    set(this, d, tu);
173}
174
175sc_time::sc_time(double d, bool scale)
176{
177    double scaler = scale ? defaultUnit : SimClock::Float::Hz;
178    set(this, d * scaler, SC_SEC);
179}
180
181sc_time::sc_time(sc_dt::uint64 v, bool scale)
182{
183    double scaler = scale ? defaultUnit : SimClock::Float::Hz;
184    set(this, static_cast<double>(v) * scaler, SC_SEC);
185}
186
187sc_time &
188sc_time::operator = (const sc_time &t)
189{
190    val = t.val;
191    return *this;
192}
193
194sc_dt::uint64
195sc_time::value() const
196{
197    return val;
198}
199
200double
201sc_time::to_double() const
202{
203    return static_cast<double>(val);
204}
205double
206sc_time::to_seconds() const
207{
208    return to_double() * SimClock::Float::Hz;
209}
210
211const std::string
212sc_time::to_string() const
213{
214    std::ostringstream ss;
215    print(ss);
216    return ss.str();
217}
218
219bool
220sc_time::operator == (const sc_time &t) const
221{
222    return val == t.val;
223}
224
225bool
226sc_time::operator != (const sc_time &t) const
227{
228    return val != t.val;
229}
230
231bool
232sc_time::operator < (const sc_time &t) const
233{
234    return val < t.val;
235}
236
237bool
238sc_time::operator <= (const sc_time &t) const
239{
240    return val <= t.val;
241}
242
243bool
244sc_time::operator > (const sc_time &t) const
245{
246    return val > t.val;
247}
248
249bool
250sc_time::operator >= (const sc_time &t) const
251{
252    return val >= t.val;
253}
254
255sc_time &
256sc_time::operator += (const sc_time &t)
257{
258    val += t.val;
259    return *this;
260}
261
262sc_time &
263sc_time::operator -= (const sc_time &t)
264{
265    val -= t.val;
266    return *this;
267}
268
269sc_time &
270sc_time::operator *= (double d)
271{
272    val = static_cast<int64_t>(static_cast<double>(val) * d + 0.5);
273    return *this;
274}
275
276sc_time &
277sc_time::operator /= (double d)
278{
279    val = static_cast<int64_t>(static_cast<double>(val) / d + 0.5);
280    return *this;
281}
282
283void
284sc_time::print(std::ostream &os) const
285{
286    os << sc_time_tuple(*this).to_string();
287}
288
289sc_time
290sc_time::from_value(sc_dt::uint64 u)
291{
292    if (u)
293        attemptToFixTime();
294    sc_time t;
295    t.val = u;
296    return t;
297}
298
299sc_time
300sc_time::from_seconds(double d)
301{
302    sc_time t;
303    set(&t, d, SC_SEC);
304    return t;
305}
306
307sc_time
308sc_time::from_string(const char *str)
309{
310    char *end = nullptr;
311
312    double d = str ? std::strtod(str, &end) : 0.0;
313    if (str == end || d < 0.0) {
314        SC_REPORT_ERROR(SC_ID_TIME_CONVERSION_FAILED_, "invalid value given");
315        return SC_ZERO_TIME;
316    }
317
318    while (*end && std::isspace(*end))
319        end++;
320
321    return sc_time(d, end);
322}
323
324const sc_time
325operator + (const sc_time &a, const sc_time &b)
326{
327    return sc_time::from_value(a.value() + b.value());
328}
329
330const sc_time
331operator - (const sc_time &a, const sc_time &b)
332{
333    return sc_time::from_value(a.value() - b.value());
334}
335
336const sc_time
337operator * (const sc_time &t, double d)
338{
339    volatile double tmp = static_cast<double>(t.value()) * d + 0.5;
340    return sc_time::from_value(static_cast<int64_t>(tmp));
341}
342
343const sc_time
344operator * (double d, const sc_time &t)
345{
346    volatile double tmp = d * static_cast<double>(t.value()) + 0.5;
347    return sc_time::from_value(static_cast<int64_t>(tmp));
348}
349
350const sc_time
351operator / (const sc_time &t, double d)
352{
353    volatile double tmp = static_cast<double>(t.value()) / d + 0.5;
354    return sc_time::from_value(static_cast<int64_t>(tmp));
355}
356
357double
358operator / (const sc_time &t1, const sc_time &t2)
359{
360    return t1.to_double() / t2.to_double();
361}
362
363std::ostream &
364operator << (std::ostream &os, const sc_time &t)
365{
366    t.print(os);
367    return os;
368}
369
370const sc_time SC_ZERO_TIME;
371
372void
373sc_set_time_resolution(double d, sc_time_unit tu)
374{
375    if (d <= 0.0)
376        SC_REPORT_ERROR(SC_ID_SET_TIME_RESOLUTION_, "value not positive");
377
378    double dummy;
379    if (modf(log10(d), &dummy) != 0.0) {
380        SC_REPORT_ERROR(SC_ID_SET_TIME_RESOLUTION_,
381                "value not a power of ten");
382    }
383    if (sc_is_running())
384        SC_REPORT_ERROR(SC_ID_SET_TIME_RESOLUTION_, "simulation running");
385
386    static bool specified = false;
387    if (specified)
388        SC_REPORT_ERROR(SC_ID_SET_TIME_RESOLUTION_, "already specified");
389
390    // This won't detect the timescale being fixed outside of systemc, but
391    // it's at least some protection.
392    if (timeFixed) {
393        SC_REPORT_ERROR(SC_ID_SET_TIME_RESOLUTION_,
394                "sc_time object(s) constructed");
395    }
396
397    double seconds = d * sc_gem5::TimeUnitScale[tu];
398    if (seconds < sc_gem5::TimeUnitScale[SC_FS])
399        SC_REPORT_ERROR(SC_ID_SET_TIME_RESOLUTION_, "value smaller than 1 fs");
400
401    if (seconds > defaultUnit) {
402        SC_REPORT_WARNING(SC_ID_DEFAULT_TIME_UNIT_CHANGED_, "");
403        defaultUnit = seconds;
404    }
405
406    // Get rid of fractional parts of d.
407    while (d < 1.0 && tu > SC_FS) {
408        d *= 1000;
409        tu = (sc_time_unit)(tu - 1);
410    }
411
412    Tick ticks_per_second =
413        sc_gem5::TimeUnitFrequency[tu] / static_cast<Tick>(d);
414    setGlobalFrequency(ticks_per_second);
415    specified = true;
416}
417
418sc_time
419sc_get_time_resolution()
420{
421    return sc_time::from_value(1);
422}
423
424const sc_time &
425sc_max_time()
426{
427    static const sc_time MaxScTime = sc_time::from_value(MaxTick);
428    return MaxScTime;
429}
430
431void
432sc_set_default_time_unit(double d, sc_time_unit tu)
433{
434    if (d < 0.0)
435        SC_REPORT_ERROR(SC_ID_SET_DEFAULT_TIME_UNIT_, "value not positive");
436
437    double dummy;
438    if (modf(log10(d), &dummy) != 0.0) {
439        SC_REPORT_ERROR(SC_ID_SET_DEFAULT_TIME_UNIT_,
440                "value not a power of ten");
441    }
442    if (sc_is_running())
443        SC_REPORT_ERROR(SC_ID_SET_DEFAULT_TIME_UNIT_, "simulation running");
444
445    static bool specified = false;
446    if (specified) {
447        SC_REPORT_ERROR(SC_ID_SET_DEFAULT_TIME_UNIT_, "already specified");
448    }
449    // This won't detect the timescale being fixed outside of systemc, but
450    // it's at least some protection.
451    if (timeFixed) {
452        SC_REPORT_ERROR(SC_ID_SET_DEFAULT_TIME_UNIT_,
453                "sc_time object(s) constructed");
454    }
455
456    // Normalize d to seconds.
457    defaultUnit = d * sc_gem5::TimeUnitScale[tu];
458    specified = true;
459
460    double resolution = SimClock::Float::Hz;
461    if (resolution == 0.0)
462        resolution = sc_gem5::TimeUnitScale[SC_PS];
463    if (defaultUnit < resolution) {
464        SC_REPORT_ERROR(SC_ID_SET_DEFAULT_TIME_UNIT_,
465                "value smaller than time resolution");
466    }
467}
468
469sc_time
470sc_get_default_time_unit()
471{
472    return sc_time(defaultUnit, SC_SEC);
473}
474
475sc_time_tuple::sc_time_tuple(const sc_time &t) :
476    _value(), _unit(SC_SEC), _set(true)
477{
478    if (!t.value())
479        return;
480
481    Tick frequency = SimClock::Frequency;
482
483    // Shrink the frequency by scaling down the time period, ie converting
484    // it from cycles per second to cycles per millisecond, etc.
485    while (_unit > 1 && (frequency % 1000 == 0)) {
486        _unit = (sc_time_unit)((int)_unit - 1);
487        frequency /= 1000;
488    }
489
490    // Convert the frequency into a period.
491    Tick period;
492    if (frequency > 1) {
493        _unit = (sc_time_unit)((int)_unit - 1);
494        period = 1000 / frequency;
495    } else {
496        period = frequency;
497    }
498
499    // Scale our integer value by the period.
500    _value = t.value() * period;
501
502    // Shrink the scaled time value by increasing the size of the units
503    // it's measured by, avoiding fractional parts.
504    while (_unit < SC_SEC && (_value % 1000) == 0) {
505        _unit = (sc_time_unit)((int)_unit + 1);
506        _value /= 1000;
507    }
508}
509
510bool
511sc_time_tuple::has_value() const
512{
513    return _set;
514}
515
516sc_dt::uint64 sc_time_tuple::value() const { return _value; }
517
518const char *
519sc_time_tuple::unit_symbol() const
520{
521    return sc_gem5::TimeUnitNames[_unit];
522}
523
524double sc_time_tuple::to_double() const { return static_cast<double>(_value); }
525
526std::string
527sc_time_tuple::to_string() const
528{
529    std::ostringstream ss;
530    ss << _value << ' ' << unit_symbol();
531    return ss.str();
532}
533
534} // namespace sc_core
535