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