// // Copyright (c) 2010-2024 Antmicro // // This file is licensed under the MIT License. // Full license text is available in 'licenses/MIT.txt'. // using System; using System.Globalization; using System.Text.RegularExpressions; using Antmicro.Renode.Exceptions; using Antmicro.Renode.Debugging; using Antmicro.Renode.Utilities; namespace Antmicro.Renode.Time { /// /// Represents time interval. /// Right now it has the resolution of 10^-9 second, but is intended for future extension. /// public struct TimeInterval : IComparable, IEquatable { // this method is required by a parsing mechanism in the monitor public static explicit operator TimeInterval(string s) { if(!TryParse(s, out var output)) { throw new RecoverableException("Could not parse ${s} to time interval. Provide input in form 00:00:00.0000"); } return output; } public static bool TryParse(string input, out TimeInterval output) { var m = Regex.Match(input, @"(((?[0-9]+):)?(?[0-9]+):)?(?[0-9]+)(?\.[0-9]+)?"); if(!m.Success) { output = Empty; return false; } var hours = m.Groups["hours"].Success ? ulong.Parse(m.Groups["hours"].Value) : 0; var minutes = m.Groups["minutes"].Success ? ulong.Parse(m.Groups["minutes"].Value) : 0; var seconds = ulong.Parse(m.Groups["seconds"].Value); // For convenience we parse "decimals" as fraction of a second, and so we multiply this by the number of ticks in a second var decimals = m.Groups["decimals"].Success ? (ulong)(double.Parse($"0{m.Groups["decimals"].Value}", CultureInfo.InvariantCulture) * TicksPerSecond) : 0; ulong ticks = 0; ticks += decimals; ticks += seconds * TicksPerSecond; ticks += minutes * (60 * TicksPerSecond); ticks += hours * (3600 * TicksPerSecond); output = new TimeInterval(ticks); return true; } public static TimeInterval Min(TimeInterval t1, TimeInterval t2) { return (t1.ticks <= t2.ticks) ? t1 : t2; } public static TimeInterval FromNanoseconds(ulong v) { return FromTicks(v * TicksPerNanosecond); } public static TimeInterval FromMicroseconds(ulong v) { return FromTicks(v * TicksPerMicrosecond); } public static TimeInterval FromMilliseconds(ulong v) { return FromTicks(v * TicksPerMillisecond); } public static TimeInterval FromMilliseconds(float v) { return FromTicks((ulong)(v * TicksPerMillisecond)); } public static TimeInterval FromSeconds(ulong v) { return FromTicks(v * TicksPerSecond); } public static TimeInterval FromSeconds(float v) { return FromTicks((ulong)(v * TicksPerSecond)); } public static TimeInterval FromSeconds(double v) { return FromTicks((ulong)(v * TicksPerSecond)); } public static TimeInterval FromMinutes(ulong v) { return FromSeconds(v * 60); } public static TimeInterval FromMinutes(float v) { return FromSeconds(v * 60); } public static TimeInterval FromTicks(ulong ticks) { return new TimeInterval(ticks); } public static TimeInterval FromTimeSpan(TimeSpan span) { // since the number of ticks per second in `TimeSpan` is 10^7 (which gives 100 ns per tick) we must multiply here by 100 to get the number of `ns`. return FromNanoseconds(((ulong)span.Ticks) * 100); } public static TimeInterval FromTimeSpan(TimeSpan span, uint nsResiduum) { // since the number of ticks per second in `TimeSpan` is 10^7 (which gives 100 ns per tick) we must multiply here by 100 to get the number of `ns`. return FromNanoseconds(((ulong)span.Ticks) * 100 + nsResiduum); } public static TimeInterval FromCPUCycles(ulong cycles, uint performanceInMips, out ulong cyclesResiduum) { checked { var residuumModulus = performanceInMips / Misc.GCD(performanceInMips, TicksPerMicrosecond); cyclesResiduum = cycles % residuumModulus; cycles -= cyclesResiduum; ulong ticks = cycles * TicksPerMicrosecond / performanceInMips; return FromTicks(ticks); } } public static TimeInterval operator +(TimeInterval t1, TimeInterval t2) { return new TimeInterval(checked(t1.ticks + t2.ticks)); } public static TimeInterval operator -(TimeInterval t1, TimeInterval t2) { return new TimeInterval(checked(t1.ticks - t2.ticks)); } public static bool operator <(TimeInterval t1, TimeInterval t2) { return t1.ticks < t2.ticks; } public static bool operator >(TimeInterval t1, TimeInterval t2) { return t1.ticks > t2.ticks; } public static bool operator <=(TimeInterval t1, TimeInterval t2) { return t1.ticks <= t2.ticks; } public static bool operator >=(TimeInterval t1, TimeInterval t2) { return t1.ticks >= t2.ticks; } public static bool operator ==(TimeInterval t1, TimeInterval t2) { return t1.ticks == t2.ticks; } public static bool operator !=(TimeInterval t1, TimeInterval t2) { return t1.ticks != t2.ticks; } public static readonly TimeInterval Empty = FromTicks(0); public static readonly TimeInterval Maximal = FromTicks(ulong.MaxValue); public int CompareTo(TimeInterval other) { return ticks.CompareTo(other.ticks); } public TimeSpan ToTimeSpan() { return TimeSpan.FromTicks((long)ticks / 100); } public TimeSpan ToTimeSpan(out uint nsResiduum) { nsResiduum = (uint)(ticks % 100); return TimeSpan.FromTicks((long)ticks / 100); } public override bool Equals(object obj) { return (obj is TimeInterval ts) && this.ticks == ts.ticks; } public bool Equals(TimeInterval ts) { return this.ticks == ts.ticks; } public override int GetHashCode() { return (int)ticks; } public override string ToString() { var decimals = ticks % TicksPerSecond; var seconds = (long)(ticks / TicksPerSecond); var hours = Math.DivRem(seconds, 3600, out seconds); var minutes = Math.DivRem(seconds, 60, out seconds); return $"{hours:00}:{minutes:00}:{seconds:00}.{decimals:000000000}"; } public TimeInterval WithTicksMin(ulong ticks) { return new TimeInterval(Math.Min(this.ticks, ticks)); } public TimeInterval WithScaledTicks(double factor) { return new TimeInterval((ulong)(ticks * factor)); } public ulong ToCPUCycles(uint performanceInMips, out ulong ticksCountResiduum) { var maxTicks = FromCPUCycles(ulong.MaxValue / TicksPerMicrosecond, performanceInMips, out var unused).Ticks; if(ticks >= maxTicks) { ticksCountResiduum = ticks - maxTicks; return ulong.MaxValue; } checked { var nanoSeconds = ticks / TicksPerNanosecond; ticksCountResiduum = ticks % TicksPerNanosecond; return nanoSeconds * performanceInMips / TicksPerMicrosecond; } } public ulong Ticks => ticks; public ulong TotalNanoseconds => ticks / TicksPerNanosecond; public double TotalMicroseconds => ticks / (double)TicksPerMicrosecond; public double TotalMilliseconds => ticks / (double)TicksPerMillisecond; public double TotalSeconds => ticks / (double)TicksPerSecond; public const ulong TicksPerSecond = TicksPerMillisecond * 1000; public const ulong TicksPerMillisecond = TicksPerMicrosecond * 1000; public const ulong TicksPerMicrosecond = TicksPerNanosecond * 1000; // WARNING: when changing the resolution of TimeInterval update methods: 'TryParse', 'FromTimeSpan', 'ToTimeSpan' and 'FromCPUCycles' accordingly public const ulong TicksPerNanosecond = 1; static TimeInterval() { DebugHelper.Assert(TimeSpan.TicksPerSecond == 10000000L, "Number of Ticks in TimeSpan mismatch!"); } private TimeInterval(ulong ticks) { this.ticks = ticks; } private ulong ticks; } }