1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Globalization; 9 using System.Text.RegularExpressions; 10 using Antmicro.Renode.Exceptions; 11 using Antmicro.Renode.Debugging; 12 using Antmicro.Renode.Utilities; 13 14 namespace Antmicro.Renode.Time 15 { 16 /// <summary> 17 /// Represents time interval. 18 /// Right now it has the resolution of 10^-9 second, but is intended for future extension. 19 /// </summary> 20 public struct TimeInterval : IComparable<TimeInterval>, IEquatable<TimeInterval> 21 { 22 // this method is required by a parsing mechanism in the monitor operator TimeIntervalAntmicro.Renode.Time.TimeInterval23 public static explicit operator TimeInterval(string s) 24 { 25 if(!TryParse(s, out var output)) 26 { 27 throw new RecoverableException("Could not parse ${s} to time interval. Provide input in form 00:00:00.0000"); 28 } 29 return output; 30 } 31 TryParseAntmicro.Renode.Time.TimeInterval32 public static bool TryParse(string input, out TimeInterval output) 33 { 34 var m = Regex.Match(input, @"(((?<hours>[0-9]+):)?(?<minutes>[0-9]+):)?(?<seconds>[0-9]+)(?<decimals>\.[0-9]+)?"); 35 if(!m.Success) 36 { 37 output = Empty; 38 return false; 39 } 40 41 var hours = m.Groups["hours"].Success ? ulong.Parse(m.Groups["hours"].Value) : 0; 42 var minutes = m.Groups["minutes"].Success ? ulong.Parse(m.Groups["minutes"].Value) : 0; 43 var seconds = ulong.Parse(m.Groups["seconds"].Value); 44 // For convenience we parse "decimals" as fraction of a second, and so we multiply this by the number of ticks in a second 45 var decimals = m.Groups["decimals"].Success ? (ulong)(double.Parse($"0{m.Groups["decimals"].Value}", CultureInfo.InvariantCulture) * TicksPerSecond) : 0; 46 47 ulong ticks = 0; 48 ticks += decimals; 49 ticks += seconds * TicksPerSecond; 50 ticks += minutes * (60 * TicksPerSecond); 51 ticks += hours * (3600 * TicksPerSecond); 52 53 output = new TimeInterval(ticks); 54 return true; 55 } 56 MinAntmicro.Renode.Time.TimeInterval57 public static TimeInterval Min(TimeInterval t1, TimeInterval t2) 58 { 59 return (t1.ticks <= t2.ticks) ? t1 : t2; 60 } 61 FromNanosecondsAntmicro.Renode.Time.TimeInterval62 public static TimeInterval FromNanoseconds(ulong v) 63 { 64 return FromTicks(v * TicksPerNanosecond); 65 } 66 FromMicrosecondsAntmicro.Renode.Time.TimeInterval67 public static TimeInterval FromMicroseconds(ulong v) 68 { 69 return FromTicks(v * TicksPerMicrosecond); 70 } 71 FromMillisecondsAntmicro.Renode.Time.TimeInterval72 public static TimeInterval FromMilliseconds(ulong v) 73 { 74 return FromTicks(v * TicksPerMillisecond); 75 } 76 FromMillisecondsAntmicro.Renode.Time.TimeInterval77 public static TimeInterval FromMilliseconds(float v) 78 { 79 return FromTicks((ulong)(v * TicksPerMillisecond)); 80 } 81 FromSecondsAntmicro.Renode.Time.TimeInterval82 public static TimeInterval FromSeconds(ulong v) 83 { 84 return FromTicks(v * TicksPerSecond); 85 } 86 FromSecondsAntmicro.Renode.Time.TimeInterval87 public static TimeInterval FromSeconds(float v) 88 { 89 return FromTicks((ulong)(v * TicksPerSecond)); 90 } 91 FromSecondsAntmicro.Renode.Time.TimeInterval92 public static TimeInterval FromSeconds(double v) 93 { 94 return FromTicks((ulong)(v * TicksPerSecond)); 95 } 96 FromMinutesAntmicro.Renode.Time.TimeInterval97 public static TimeInterval FromMinutes(ulong v) 98 { 99 return FromSeconds(v * 60); 100 } 101 FromMinutesAntmicro.Renode.Time.TimeInterval102 public static TimeInterval FromMinutes(float v) 103 { 104 return FromSeconds(v * 60); 105 } 106 FromTicksAntmicro.Renode.Time.TimeInterval107 public static TimeInterval FromTicks(ulong ticks) 108 { 109 return new TimeInterval(ticks); 110 } 111 FromTimeSpanAntmicro.Renode.Time.TimeInterval112 public static TimeInterval FromTimeSpan(TimeSpan span) 113 { 114 // 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`. 115 return FromNanoseconds(((ulong)span.Ticks) * 100); 116 } 117 FromTimeSpanAntmicro.Renode.Time.TimeInterval118 public static TimeInterval FromTimeSpan(TimeSpan span, uint nsResiduum) 119 { 120 // 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`. 121 return FromNanoseconds(((ulong)span.Ticks) * 100 + nsResiduum); 122 } 123 FromCPUCyclesAntmicro.Renode.Time.TimeInterval124 public static TimeInterval FromCPUCycles(ulong cycles, uint performanceInMips, out ulong cyclesResiduum) 125 { 126 checked 127 { 128 var residuumModulus = performanceInMips / Misc.GCD(performanceInMips, TicksPerMicrosecond); 129 cyclesResiduum = cycles % residuumModulus; 130 cycles -= cyclesResiduum; 131 132 ulong ticks = cycles * TicksPerMicrosecond / performanceInMips; 133 return FromTicks(ticks); 134 } 135 } 136 operator +Antmicro.Renode.Time.TimeInterval137 public static TimeInterval operator +(TimeInterval t1, TimeInterval t2) 138 { 139 return new TimeInterval(checked(t1.ticks + t2.ticks)); 140 } 141 operator -Antmicro.Renode.Time.TimeInterval142 public static TimeInterval operator -(TimeInterval t1, TimeInterval t2) 143 { 144 return new TimeInterval(checked(t1.ticks - t2.ticks)); 145 } 146 operator <Antmicro.Renode.Time.TimeInterval147 public static bool operator <(TimeInterval t1, TimeInterval t2) 148 { 149 return t1.ticks < t2.ticks; 150 } 151 operator >Antmicro.Renode.Time.TimeInterval152 public static bool operator >(TimeInterval t1, TimeInterval t2) 153 { 154 return t1.ticks > t2.ticks; 155 } 156 operator <=Antmicro.Renode.Time.TimeInterval157 public static bool operator <=(TimeInterval t1, TimeInterval t2) 158 { 159 return t1.ticks <= t2.ticks; 160 } 161 operator >=Antmicro.Renode.Time.TimeInterval162 public static bool operator >=(TimeInterval t1, TimeInterval t2) 163 { 164 return t1.ticks >= t2.ticks; 165 } 166 operator ==Antmicro.Renode.Time.TimeInterval167 public static bool operator ==(TimeInterval t1, TimeInterval t2) 168 { 169 return t1.ticks == t2.ticks; 170 } 171 operator !=Antmicro.Renode.Time.TimeInterval172 public static bool operator !=(TimeInterval t1, TimeInterval t2) 173 { 174 return t1.ticks != t2.ticks; 175 } 176 177 public static readonly TimeInterval Empty = FromTicks(0); 178 public static readonly TimeInterval Maximal = FromTicks(ulong.MaxValue); 179 CompareToAntmicro.Renode.Time.TimeInterval180 public int CompareTo(TimeInterval other) 181 { 182 return ticks.CompareTo(other.ticks); 183 } 184 ToTimeSpanAntmicro.Renode.Time.TimeInterval185 public TimeSpan ToTimeSpan() 186 { 187 return TimeSpan.FromTicks((long)ticks / 100); 188 } 189 ToTimeSpanAntmicro.Renode.Time.TimeInterval190 public TimeSpan ToTimeSpan(out uint nsResiduum) 191 { 192 nsResiduum = (uint)(ticks % 100); 193 return TimeSpan.FromTicks((long)ticks / 100); 194 } 195 EqualsAntmicro.Renode.Time.TimeInterval196 public override bool Equals(object obj) 197 { 198 return (obj is TimeInterval ts) && this.ticks == ts.ticks; 199 } 200 EqualsAntmicro.Renode.Time.TimeInterval201 public bool Equals(TimeInterval ts) 202 { 203 return this.ticks == ts.ticks; 204 } 205 GetHashCodeAntmicro.Renode.Time.TimeInterval206 public override int GetHashCode() 207 { 208 return (int)ticks; 209 } 210 ToStringAntmicro.Renode.Time.TimeInterval211 public override string ToString() 212 { 213 var decimals = ticks % TicksPerSecond; 214 var seconds = (long)(ticks / TicksPerSecond); 215 var hours = Math.DivRem(seconds, 3600, out seconds); 216 var minutes = Math.DivRem(seconds, 60, out seconds); 217 return $"{hours:00}:{minutes:00}:{seconds:00}.{decimals:000000000}"; 218 } 219 WithTicksMinAntmicro.Renode.Time.TimeInterval220 public TimeInterval WithTicksMin(ulong ticks) 221 { 222 return new TimeInterval(Math.Min(this.ticks, ticks)); 223 } 224 WithScaledTicksAntmicro.Renode.Time.TimeInterval225 public TimeInterval WithScaledTicks(double factor) 226 { 227 return new TimeInterval((ulong)(ticks * factor)); 228 } 229 ToCPUCyclesAntmicro.Renode.Time.TimeInterval230 public ulong ToCPUCycles(uint performanceInMips, out ulong ticksCountResiduum) 231 { 232 var maxTicks = FromCPUCycles(ulong.MaxValue / TicksPerMicrosecond, performanceInMips, out var unused).Ticks; 233 if(ticks >= maxTicks) 234 { 235 ticksCountResiduum = ticks - maxTicks; 236 return ulong.MaxValue; 237 } 238 239 checked 240 { 241 var nanoSeconds = ticks / TicksPerNanosecond; 242 ticksCountResiduum = ticks % TicksPerNanosecond; 243 return nanoSeconds * performanceInMips / TicksPerMicrosecond; 244 } 245 } 246 247 public ulong Ticks => ticks; 248 public ulong TotalNanoseconds => ticks / TicksPerNanosecond; 249 public double TotalMicroseconds => ticks / (double)TicksPerMicrosecond; 250 public double TotalMilliseconds => ticks / (double)TicksPerMillisecond; 251 public double TotalSeconds => ticks / (double)TicksPerSecond; 252 253 public const ulong TicksPerSecond = TicksPerMillisecond * 1000; 254 public const ulong TicksPerMillisecond = TicksPerMicrosecond * 1000; 255 public const ulong TicksPerMicrosecond = TicksPerNanosecond * 1000; 256 257 // WARNING: when changing the resolution of TimeInterval update methods: 'TryParse', 'FromTimeSpan', 'ToTimeSpan' and 'FromCPUCycles' accordingly 258 public const ulong TicksPerNanosecond = 1; 259 TimeIntervalAntmicro.Renode.Time.TimeInterval260 static TimeInterval() 261 { 262 DebugHelper.Assert(TimeSpan.TicksPerSecond == 10000000L, "Number of Ticks in TimeSpan mismatch!"); 263 } 264 TimeIntervalAntmicro.Renode.Time.TimeInterval265 private TimeInterval(ulong ticks) 266 { 267 this.ticks = ticks; 268 } 269 270 private ulong ticks; 271 } 272 } 273