sc_time.cc revision 13265:6cde60ee99fb
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/sc_main.hh"
40#include "systemc/ext/core/sc_time.hh"
41#include "systemc/ext/utils/sc_report_handler.hh"
42
43namespace sc_core
44{
45
46namespace
47{
48
49bool timeFixed = false;
50bool pythonReady = false;
51
52struct SetInfo
53{
54    SetInfo(::sc_core::sc_time *time, double d, ::sc_core::sc_time_unit tu) :
55        time(time), d(d), tu(tu)
56    {}
57
58    ::sc_core::sc_time *time;
59    double d;
60    ::sc_core::sc_time_unit tu;
61};
62std::vector<SetInfo> toSet;
63
64void
65setWork(sc_time *time, double d, ::sc_core::sc_time_unit tu)
66{
67    double scale = sc_gem5::TimeUnitScale[tu] * SimClock::Float::s;
68    // Accellera claims there is a linux bug, and that these next two
69    // lines work around them.
70    volatile double tmp = d * scale + 0.5;
71    *time = sc_time::from_value(static_cast<uint64_t>(tmp));
72}
73
74void
75fixTime()
76{
77    auto ticks = pybind11::module::import("m5.ticks");
78    auto fix_global_frequency = ticks.attr("fixGlobalFrequency");
79    fix_global_frequency();
80
81    for (auto &t: toSet)
82        setWork(t.time, t.d, t.tu);
83    toSet.clear();
84}
85
86void
87attemptToFixTime()
88{
89    // Only fix time once.
90    if (!timeFixed) {
91        timeFixed = true;
92
93        // If we've run, python is working and we haven't fixed time yet.
94        if (pythonReady)
95            fixTime();
96    }
97}
98
99void
100setGlobalFrequency(Tick ticks_per_second)
101{
102    auto ticks = pybind11::module::import("m5.ticks");
103    auto set_global_frequency = ticks.attr("setGlobalFrequency");
104    set_global_frequency(ticks_per_second);
105    fixTime();
106}
107
108void
109set(::sc_core::sc_time *time, double d, ::sc_core::sc_time_unit tu)
110{
111    if (d != 0)
112        attemptToFixTime();
113    if (pythonReady) {
114        // Time should be working. Set up this sc_time.
115        setWork(time, d, tu);
116    } else {
117        // Time isn't set up yet. Defer setting up this sc_time.
118        toSet.emplace_back(time, d, tu);
119    }
120}
121
122class TimeSetter : public ::sc_gem5::PythonReadyFunc
123{
124  public:
125    TimeSetter() : ::sc_gem5::PythonReadyFunc() {}
126
127    void
128    run() override
129    {
130        // Record that we've run and python/pybind should be usable.
131        pythonReady = true;
132
133        // If time is already fixed, let python know.
134        if (timeFixed)
135            fixTime();
136    }
137} timeSetter;
138
139double defaultUnit = 1.0e-9;
140
141} // anonymous namespace
142
143sc_time::sc_time() : val(0) {}
144
145sc_time::sc_time(double d, sc_time_unit tu)
146{
147    val = 0;
148    set(this, d, tu);
149}
150
151sc_time::sc_time(const sc_time &t)
152{
153    val = t.val;
154}
155
156sc_time::sc_time(double d, const char *unit)
157{
158    sc_time_unit tu;
159    for (tu = SC_FS; tu <= SC_SEC; tu = (sc_time_unit)(tu + 1)) {
160        if (strcmp(unit, sc_gem5::TimeUnitNames[tu]) == 0 ||
161            strcmp(unit, sc_gem5::TimeUnitConstantNames[tu]) == 0) {
162            break;
163        }
164    }
165
166    if (tu > SC_SEC) {
167        SC_REPORT_ERROR("(E567) sc_time conversion failed",
168                "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("(E567) sc_time conversion failed",
315                "invalid value given");
316        return SC_ZERO_TIME;
317    }
318
319    while (*end && std::isspace(*end))
320        end++;
321
322    return sc_time(d, end);
323}
324
325const sc_time
326operator + (const sc_time &a, const sc_time &b)
327{
328    return sc_time::from_value(a.value() + b.value());
329}
330
331const sc_time
332operator - (const sc_time &a, const sc_time &b)
333{
334    return sc_time::from_value(a.value() - b.value());
335}
336
337const sc_time
338operator * (const sc_time &t, double d)
339{
340    volatile double tmp = static_cast<double>(t.value()) * d + 0.5;
341    return sc_time::from_value(static_cast<int64_t>(tmp));
342}
343
344const sc_time
345operator * (double d, const sc_time &t)
346{
347    volatile double tmp = d * static_cast<double>(t.value()) + 0.5;
348    return sc_time::from_value(static_cast<int64_t>(tmp));
349}
350
351const sc_time
352operator / (const sc_time &t, double d)
353{
354    volatile double tmp = static_cast<double>(t.value()) / d + 0.5;
355    return sc_time::from_value(static_cast<int64_t>(tmp));
356}
357
358double
359operator / (const sc_time &t1, const sc_time &t2)
360{
361    return t1.to_double() / t2.to_double();
362}
363
364std::ostream &
365operator << (std::ostream &os, const sc_time &t)
366{
367    t.print(os);
368    return os;
369}
370
371const sc_time SC_ZERO_TIME;
372
373void
374sc_set_time_resolution(double d, sc_time_unit tu)
375{
376    if (d <= 0.0) {
377        SC_REPORT_ERROR("(E514) set time resolution failed",
378                "value not positive");
379    }
380    double dummy;
381    if (modf(log10(d), &dummy) != 0.0) {
382        SC_REPORT_ERROR("(E514) set time resolution failed",
383                "value not a power of ten");
384    }
385    if (sc_is_running()) {
386        SC_REPORT_ERROR("(E514) set time resolution failed",
387                "simulation running");
388    }
389    static bool specified = false;
390    if (specified) {
391        SC_REPORT_ERROR("(E514) set time resolution failed",
392                "already specified");
393    }
394    // This won't detect the timescale being fixed outside of systemc, but
395    // it's at least some protection.
396    if (timeFixed) {
397        SC_REPORT_ERROR("(E514) set time resolution failed",
398                "sc_time object(s) constructed");
399    }
400
401    double seconds = d * sc_gem5::TimeUnitScale[tu];
402    if (seconds < sc_gem5::TimeUnitScale[SC_FS]) {
403        SC_REPORT_ERROR("(E514) set time resolution failed",
404                "value smaller than 1 fs");
405    }
406
407    if (seconds > defaultUnit) {
408        SC_REPORT_WARNING(
409                "(W516) default time unit changed to time resolution", "");
410        defaultUnit = seconds;
411    }
412
413    // Get rid of fractional parts of d.
414    while (d < 1.0 && tu > SC_FS) {
415        d *= 1000;
416        tu = (sc_time_unit)(tu - 1);
417    }
418
419    Tick ticks_per_second =
420        sc_gem5::TimeUnitFrequency[tu] / static_cast<Tick>(d);
421    setGlobalFrequency(ticks_per_second);
422    specified = true;
423}
424
425sc_time
426sc_get_time_resolution()
427{
428    return sc_time::from_value(1);
429}
430
431const sc_time &
432sc_max_time()
433{
434    static const sc_time MaxScTime = sc_time::from_value(MaxTick);
435    return MaxScTime;
436}
437
438void
439sc_set_default_time_unit(double d, sc_time_unit tu)
440{
441    if (d < 0.0) {
442        SC_REPORT_ERROR("(E515) set default time unit failed",
443                "value not positive");
444    }
445    double dummy;
446    if (modf(log10(d), &dummy) != 0.0) {
447        SC_REPORT_ERROR("(E515) set default time unit failed",
448                "value not a power of ten");
449    }
450    if (sc_is_running()) {
451        SC_REPORT_ERROR("(E515) set default time unit failed",
452                "simulation running");
453    }
454    static bool specified = false;
455    if (specified) {
456        SC_REPORT_ERROR("(E515) set default time unit failed",
457                "already specified");
458    }
459    // This won't detect the timescale being fixed outside of systemc, but
460    // it's at least some protection.
461    if (timeFixed) {
462        SC_REPORT_ERROR("(E515) set default time unit failed",
463                "sc_time object(s) constructed");
464    }
465
466    // Normalize d to seconds.
467    defaultUnit = d * sc_gem5::TimeUnitScale[tu];
468    specified = true;
469
470    double resolution = SimClock::Float::Hz;
471    if (resolution == 0.0)
472        resolution = sc_gem5::TimeUnitScale[SC_PS];
473    if (defaultUnit < resolution) {
474        SC_REPORT_ERROR("(E515) set default time unit failed",
475                "value smaller than time resolution");
476    }
477}
478
479sc_time
480sc_get_default_time_unit()
481{
482    return sc_time(defaultUnit, SC_SEC);
483}
484
485sc_time_tuple::sc_time_tuple(const sc_time &t) :
486    _value(), _unit(SC_SEC), _set(true)
487{
488    if (!t.value())
489        return;
490
491    Tick frequency = SimClock::Frequency;
492
493    // Shrink the frequency by scaling down the time period, ie converting
494    // it from cycles per second to cycles per millisecond, etc.
495    while (_unit > 1 && (frequency % 1000 == 0)) {
496        _unit = (sc_time_unit)((int)_unit - 1);
497        frequency /= 1000;
498    }
499
500    // Convert the frequency into a period.
501    Tick period;
502    if (frequency > 1) {
503        _unit = (sc_time_unit)((int)_unit - 1);
504        period = 1000 / frequency;
505    } else {
506        period = frequency;
507    }
508
509    // Scale our integer value by the period.
510    _value = t.value() * period;
511
512    // Shrink the scaled time value by increasing the size of the units
513    // it's measured by, avoiding fractional parts.
514    while (_unit < SC_SEC && (_value % 1000) == 0) {
515        _unit = (sc_time_unit)((int)_unit + 1);
516        _value /= 1000;
517    }
518}
519
520bool
521sc_time_tuple::has_value() const
522{
523    return _set;
524}
525
526sc_dt::uint64 sc_time_tuple::value() const { return _value; }
527
528const char *
529sc_time_tuple::unit_symbol() const
530{
531    return sc_gem5::TimeUnitNames[_unit];
532}
533
534double sc_time_tuple::to_double() const { return static_cast<double>(_value); }
535
536std::string
537sc_time_tuple::to_string() const
538{
539    std::ostringstream ss;
540    ss << _value << ' ' << unit_symbol();
541    return ss.str();
542}
543
544} // namespace sc_core
545