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.Collections.Generic;
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Structure.Registers;
10 using Antmicro.Renode.Exceptions;
11 using Antmicro.Renode.Time;
12 using Antmicro.Renode.Logging;
13 
14 namespace Antmicro.Renode.Peripherals.Timers
15 {
16     public class ARM_GenericTimer : IPeripheral
17     {
18         // defaultCounterFrequencyRegister is the value that CNTFRQ will be set to at reset.
19         // It can be used to configure the value configured by an earlier-stage bootloader not
20         // run under Renode in the platform file.
ARM_GenericTimer(IMachine machine, ulong frequency, uint defaultCounterFrequencyRegister = 0)21         public ARM_GenericTimer(IMachine machine, ulong frequency, uint defaultCounterFrequencyRegister = 0)
22         {
23             if(frequency > long.MaxValue)
24             {
25                 throw new ConstructionException($"Timer doesn't support frequency greater than {long.MaxValue}, given {frequency}.");
26             }
27 
28             Frequency = (long)frequency;
29             this.defaultCounterFrequencyRegister = defaultCounterFrequencyRegister;
30             clockSource = machine.ClockSource;
31 
32             // The names used are based on Generic Timer documentation: https://developer.arm.com/documentation/102379/0101/The-processor-timers
33             el1PhysicalTimer = new TimerUnit(clockSource, this, "EL1PhysicalTimer", EL1PhysicalTimerIRQ, Frequency);
34             el1VirtualTimer = new TimerUnit(clockSource, this, "EL1VirtualTimer", EL1VirtualTimerIRQ, Frequency);
35             el3PhysicalTimer = new TimerUnit(clockSource, this, "EL3PhysicalTimer", EL3PhysicalTimerIRQ, Frequency);
36             nonSecureEL2PhysicalTimer = new TimerUnit(clockSource, this, "NonSecureEL2PhysicalTimer", NonSecureEL2PhysicalTimerIRQ, Frequency);
37             nonSecureEL2VirtualTimer = new TimerUnit(clockSource, this, "NonSecureEL2VirtualTimer", NonSecureEL2VirtualTimerIRQ, Frequency);
38 
39             registersAArch64 = new QuadWordRegisterCollection(this, BuildRegisterAArch64Map());
40             doubleWordRegistersAArch32 = new DoubleWordRegisterCollection(this, BuildDoubleWordRegisterAArch32Map());
41             quadWordRegistersAArch32 = new QuadWordRegisterCollection(this, BuildQuadWordRegisterAArch32Map());
42 
43             Reset();
44         }
45 
WriteRegisterAArch64(uint offset, ulong value)46         public void WriteRegisterAArch64(uint offset, ulong value)
47         {
48             this.Log(LogLevel.Debug, "Write to {0} (0x{1:X}) AArch64 register, value 0x{2:X}", (RegistersAArch64)offset, offset, value);
49             registersAArch64.Write(offset, value);
50         }
51 
ReadRegisterAArch64(uint offset)52         public ulong ReadRegisterAArch64(uint offset)
53         {
54             var register = (RegistersAArch64)offset;
55             var value = registersAArch64.Read(offset);
56 
57             // There can be lots and lots of '*Count' register reads.
58             if((register != RegistersAArch64.PhysicalCount && register != RegistersAArch64.VirtualCount) || EnableCountReadLogs)
59             {
60                 this.Log(LogLevel.Debug, "Read from {0} (0x{1:X}) AArch64 register, value 0x{2:X}", register, offset, value);
61             }
62             return value;
63         }
64 
WriteDoubleWordRegisterAArch32(uint offset, uint value)65         public void WriteDoubleWordRegisterAArch32(uint offset, uint value)
66         {
67             this.Log(LogLevel.Debug, "Write to {0} (0x{1:X}) AArch32 register, value 0x{2:X}", (DoubleWordRegistersAArch32)offset, offset, value);
68             doubleWordRegistersAArch32.Write(offset, value);
69         }
70 
ReadDoubleWordRegisterAArch32(uint offset)71         public uint ReadDoubleWordRegisterAArch32(uint offset)
72         {
73             var value = doubleWordRegistersAArch32.Read(offset);
74             this.Log(LogLevel.Debug, "Read from {0} (0x{1:X}) AArch32 register, value 0x{2:X}", (DoubleWordRegistersAArch32)offset, offset, value);
75             return value;
76         }
77 
WriteQuadWordRegisterAArch32(uint offset, ulong value)78         public void WriteQuadWordRegisterAArch32(uint offset, ulong value)
79         {
80             this.Log(LogLevel.Debug, "Write to {0} (0x{1:X}) AArch32 64-bit register, value 0x{2:X}", (QuadWordRegistersAArch32)offset, offset, value);
81             quadWordRegistersAArch32.Write(offset, value);
82         }
83 
ReadQuadWordRegisterAArch32(uint offset)84         public ulong ReadQuadWordRegisterAArch32(uint offset)
85         {
86             var register = (QuadWordRegistersAArch32)offset;
87             var value = quadWordRegistersAArch32.Read(offset);
88 
89             // There can be lots and lots of '*Count' register reads.
90             if((register != QuadWordRegistersAArch32.PhysicalCount && register != QuadWordRegistersAArch32.VirtualCount) || EnableCountReadLogs)
91             {
92                 this.Log(LogLevel.Debug, "Read from {0} (0x{1:X}) AArch32 64-bit register, value 0x{2:X}", register, offset, value);
93             }
94             return value;
95         }
96 
Reset()97         public void Reset()
98         {
99             el1PhysicalTimer.Reset();
100             el1VirtualTimer.Reset();
101             el3PhysicalTimer.Reset();
102             nonSecureEL2PhysicalTimer.Reset();
103             nonSecureEL2VirtualTimer.Reset();
104 
105             CounterFrequencyRegister = defaultCounterFrequencyRegister;
106             registersAArch64.Reset();
107             doubleWordRegistersAArch32.Reset();
108             quadWordRegistersAArch32.Reset();
109         }
110 
111         public bool EnableCountReadLogs { get; set; }
112         public long Frequency { get; }
113         // There is the counter frequency register used by a software to discover a frequency of a timer
114         // In the model we allow to preset it using the `CounterFrequencyRegister` property to support simulation scenarios without a bootloader.
115         public uint CounterFrequencyRegister { set; get; }
116 
117         public GPIO EL1PhysicalTimerIRQ { get; } = new GPIO();
118         public GPIO EL1VirtualTimerIRQ { get; } = new GPIO();
119         public GPIO EL3PhysicalTimerIRQ { get; } = new GPIO();
120         public GPIO NonSecureEL2PhysicalTimerIRQ { get; } = new GPIO();
121         public GPIO NonSecureEL2VirtualTimerIRQ { get; } = new GPIO();
122 
123         // Some physical timers names in the ARMv7 specification are different than names in the ARMv8-A specification.
124         public GPIO PL1PhysicalTimerIRQ => EL1PhysicalTimerIRQ;
125         public GPIO PL2PhysicalTimerIRQ => NonSecureEL2PhysicalTimerIRQ;
126         public GPIO VirtualTimerIRQ => EL1VirtualTimerIRQ;
127 
BuildRegisterAArch64Map()128         private Dictionary<long, QuadWordRegister> BuildRegisterAArch64Map()
129         {
130             var registersMap = new Dictionary<long, QuadWordRegister>
131             {
132                 {(long)RegistersAArch64.Frequency,
133                     BuildFrequencyRegister(new QuadWordRegister(this), "CounterFrequency", 64)
134                 },
135 
136                 {(long)RegistersAArch64.PhysicalCount,
137                     BuildTimerCountValueRegister(el1PhysicalTimer, "EL1Physical")
138                 },
139                 {(long)RegistersAArch64.VirtualCount,
140                     BuildTimerCountValueRegister(el1VirtualTimer, "EL1Virtual")
141                 },
142 
143                 // There is no implementation of the PhysicalTimerOffset register, because it require the Enhanced Counter Virtualization support.
144 
145                 {(long)RegistersAArch64.VirtualOffset,
146                     BuildTimerOffsetRegister(el1VirtualTimer, "EL1Virtual")
147                 },
148 
149                 {(long)RegistersAArch64.EL1PhysicalTimerControl,
150                     BuildTimerControlRegister(new QuadWordRegister(this), el1PhysicalTimer, "EL1PhysicalTimer")
151                 },
152                 {(long)RegistersAArch64.EL1VirtualTimerControl,
153                     BuildTimerControlRegister(new QuadWordRegister(this), el1VirtualTimer, "EL1VirtualTimer")
154                 },
155                 {(long)RegistersAArch64.EL3PhysicalTimerControl,
156                     BuildTimerControlRegister(new QuadWordRegister(this), el3PhysicalTimer, "EL3PhysicalTimer")
157                 },
158                 {(long)RegistersAArch64.NonSecureEL2PhysicalTimerControl,
159                     BuildTimerControlRegister(new QuadWordRegister(this), nonSecureEL2PhysicalTimer, "NonSecureEL2PhysicalTimer")
160                 },
161                 {(long)RegistersAArch64.NonSecureEL2VirtualTimerControl,
162                     BuildTimerControlRegister(new QuadWordRegister(this), nonSecureEL2VirtualTimer, "NonSecureEL2VirtualTimer")
163                 },
164 
165                 {(long)RegistersAArch64.EL1PhysicalTimerCompareValue,
166                     BuildTimerCompareValueRegister(el1PhysicalTimer, "EL1PhysicalTimer")
167                 },
168                 {(long)RegistersAArch64.EL1VirtualTimerCompareValue,
169                     BuildTimerCompareValueRegister(el1VirtualTimer, "EL1VirtualTimer")
170                 },
171                 {(long)RegistersAArch64.EL3PhysicalTimerCompareValue,
172                     BuildTimerCompareValueRegister(el3PhysicalTimer, "EL3PhysicalTimer")
173                 },
174                 {(long)RegistersAArch64.NonSecureEL2PhysicalTimerCompareValue,
175                     BuildTimerCompareValueRegister(nonSecureEL2PhysicalTimer, "NonSecureEL2PhysicalTimer")
176                 },
177                 {(long)RegistersAArch64.NonSecureEL2VirtualTimerCompareValue,
178                     BuildTimerCompareValueRegister(nonSecureEL2VirtualTimer, "NonSecureEL2VirtualTimer")
179                 },
180 
181                 {(long)RegistersAArch64.EL1PhysicalTimerValue,
182                     BuildTimerCountDownValueRegister(new QuadWordRegister(this), el1PhysicalTimer, "EL1PhysicalTimer", 64)
183                 },
184                 {(long)RegistersAArch64.EL1VirtualTimerValue,
185                     BuildTimerCountDownValueRegister(new QuadWordRegister(this), el1VirtualTimer, "EL1VirtualTimer", 64)
186                 },
187                 {(long)RegistersAArch64.EL3PhysicalTimerValue,
188                     BuildTimerCountDownValueRegister(new QuadWordRegister(this), el3PhysicalTimer, "EL3PhysicalTimer", 64)
189                 },
190                 {(long)RegistersAArch64.NonSecureEL2PhysicalTimerValue,
191                     BuildTimerCountDownValueRegister(new QuadWordRegister(this), nonSecureEL2PhysicalTimer, "NonSecureEL2PhysicalTimer", 64)
192                 },
193                 {(long)RegistersAArch64.NonSecureEL2VirtualTimerValue,
194                     BuildTimerCountDownValueRegister(new QuadWordRegister(this), nonSecureEL2VirtualTimer, "NonSecureEL2VirtualTimer", 64)
195                 },
196             };
197 
198             // Specultative access doesn't occure in Renode.
199             // Self synchronized registers can be just mapped to normal registers.
200             registersMap[(long)RegistersAArch64.PhysicalSelfSynchronizedCount] =
201                 registersMap[(long)RegistersAArch64.PhysicalCount];
202             registersMap[(long)RegistersAArch64.VirtualSelfSynchronizedCount] =
203                 registersMap[(long)RegistersAArch64.VirtualCount];
204 
205             return registersMap;
206         }
207 
BuildDoubleWordRegisterAArch32Map()208         private Dictionary<long, DoubleWordRegister> BuildDoubleWordRegisterAArch32Map()
209         {
210             var registersMap = new Dictionary<long, DoubleWordRegister>
211             {
212                 {(long)DoubleWordRegistersAArch32.Frequency,
213                     BuildFrequencyRegister(new DoubleWordRegister(this), "CounterFrequency", 32)
214                 },
215 
216                 {(long)DoubleWordRegistersAArch32.PL1PhysicalTimerControl,
217                     BuildTimerControlRegister(new DoubleWordRegister(this), el1PhysicalTimer, "PL1PhysicalTimer")
218                 },
219                 {(long)DoubleWordRegistersAArch32.PL2PhysicalTimerControl,
220                     BuildTimerControlRegister(new DoubleWordRegister(this), nonSecureEL2PhysicalTimer, "PL2PhysicalTimer")
221                 },
222                 {(long)DoubleWordRegistersAArch32.VirtualTimerControl,
223                     BuildTimerControlRegister(new DoubleWordRegister(this), el1VirtualTimer, "VirtualTimer")
224                 },
225 
226                 {(long)DoubleWordRegistersAArch32.PL1PhysicalTimerValue,
227                     BuildTimerCountDownValueRegister(new DoubleWordRegister(this), el1PhysicalTimer, "PL1PhysicalTimer", 32)
228                 },
229                 {(long)DoubleWordRegistersAArch32.PL2PhysicalTimerValue,
230                     BuildTimerCountDownValueRegister(new DoubleWordRegister(this), nonSecureEL2PhysicalTimer, "PL2PhysicalTimer", 32)
231                 },
232                 {(long)DoubleWordRegistersAArch32.VirtualTimerValue,
233                     BuildTimerCountDownValueRegister(new DoubleWordRegister(this), el1VirtualTimer, "VirtualTimer", 32)
234                 }
235             };
236             return registersMap;
237         }
238 
BuildQuadWordRegisterAArch32Map()239         private Dictionary<long, QuadWordRegister> BuildQuadWordRegisterAArch32Map()
240         {
241             var registersMap = new Dictionary<long, QuadWordRegister>
242             {
243                 {(long)QuadWordRegistersAArch32.PhysicalCount,
244                     BuildTimerCountValueRegister(el1PhysicalTimer, "Physical")
245                 },
246                 {(long)QuadWordRegistersAArch32.VirtualCount,
247                     BuildTimerCountValueRegister(el1VirtualTimer, "Virtual")
248                 },
249 
250                 {(long)QuadWordRegistersAArch32.VirtualOffset,
251                     BuildTimerOffsetRegister(el1VirtualTimer, "Virtual")
252                 },
253 
254                 {(long)QuadWordRegistersAArch32.PL1PhysicalTimerCompareValue,
255                     BuildTimerCompareValueRegister(el1PhysicalTimer, "PL1PhysicalTimer")
256                 },
257                 {(long)QuadWordRegistersAArch32.PL2PhysicalTimerCompareValue,
258                     BuildTimerCompareValueRegister(nonSecureEL2PhysicalTimer, "PL2PhysicalTimer")
259                 },
260                 {(long)QuadWordRegistersAArch32.VirtualTimerCompareValue,
261                     BuildTimerCompareValueRegister(el1VirtualTimer, "VirtualTimer")
262                 }
263             };
264             return registersMap;
265         }
266 
267         private T BuildFrequencyRegister<T>(T register, string name, int registerWidth) where T : PeripheralRegister
268         {
269             // According to the documentation "the value of the register is not interpreted by hardware",
270             // it's there for software to discover a frequency of a timer set earlier by a bootloader.
271             return register
272                 .WithReservedBits(32, registerWidth - 32)
273                 .WithValueField(0, 32, name: name,
274                     valueProviderCallback: _ => CounterFrequencyRegister,
275                     writeCallback: (_, val) =>
276                         {
277                             CounterFrequencyRegister = (uint)val;
278                             if(Frequency != CounterFrequencyRegister)
279                             {
280                                 this.Log(LogLevel.Warning, "Setting the counter frequency register to 0x{0:X} ({0}Hz), which is different than timer frequency ({1}Hz).", val, Frequency);
281                             }
282                         }
283                 );
284         }
285 
BuildTimerControlRegister(QuadWordRegister register, TimerUnit timer, string namePrefix)286         private QuadWordRegister BuildTimerControlRegister(QuadWordRegister register, TimerUnit timer, string namePrefix)
287         {
288             return BuildTimerControlGenericRegister(register, timer, namePrefix, 64)
289                 .WithWriteCallback((_, __) => timer.UpdateInterrupt());
290         }
291 
BuildTimerControlRegister(DoubleWordRegister register, TimerUnit timer, string namePrefix)292         private DoubleWordRegister BuildTimerControlRegister(DoubleWordRegister register, TimerUnit timer, string namePrefix)
293         {
294             return BuildTimerControlGenericRegister(register, timer, namePrefix, 32)
295                 .WithWriteCallback((_, __) => timer.UpdateInterrupt());
296         }
297 
298         private T BuildTimerControlGenericRegister<T>(T register, TimerUnit timer, string namePrefix, int registerWidth) where T : PeripheralRegister
299         {
300             return register
301                 .WithReservedBits(3, registerWidth - 3)
302                 .WithFlag(2, FieldMode.Read, name: $"{namePrefix}InterruptStatus",
303                     valueProviderCallback: _ => timer.InterruptStatus
304                 )
305                 .WithFlag(1, name: $"{namePrefix}InterruptMask",
306                     writeCallback: (_, val) => timer.InterruptMask = val,
307                     valueProviderCallback: _ => timer.InterruptMask
308                 )
309                 .WithFlag(0, name: $"{namePrefix}InterruptEnable",
310                     writeCallback: (_, val) => timer.InterruptEnable = val,
311                     valueProviderCallback: _ => timer.InterruptEnable
312                 );
313         }
314 
315         private T BuildTimerCountDownValueRegister<T>(T register, TimerUnit timer, string namePrefix, int registerWidth) where T : PeripheralRegister
316         {
317             return register
318                 .WithReservedBits(32, registerWidth - 32)
319                 .WithValueField(0, 32, name: $"{namePrefix}Value",
320                     writeCallback: (_, val) =>
321                         {
322                             timer.CountDownValue = (int)val;
323                             timer.UpdateInterrupt();
324                         },
325                     valueProviderCallback: _ => (ulong)timer.CountDownValue
326                 );
327         }
328 
BuildTimerCountValueRegister(TimerUnit timer, string namePrefix)329         private QuadWordRegister BuildTimerCountValueRegister(TimerUnit timer, string namePrefix)
330         {
331             return new QuadWordRegister(this)
332                 .WithValueField(0, 64, FieldMode.Read, name: $"{namePrefix}Count",
333                     valueProviderCallback: _ => timer.Value
334                 );
335         }
336 
BuildTimerOffsetRegister(TimerUnit timer, string namePrefix)337         private QuadWordRegister BuildTimerOffsetRegister(TimerUnit timer, string namePrefix)
338         {
339             return new QuadWordRegister(this)
340                 .WithValueField(0, 64, name: $"{namePrefix}Offset",
341                     writeCallback: (_, val) =>
342                         {
343                             timer.Offset = val;
344                             timer.UpdateInterrupt();
345                         },
346                     valueProviderCallback: _ => timer.Offset
347                 );
348         }
349 
BuildTimerCompareValueRegister(TimerUnit timer, string namePrefix)350         private QuadWordRegister BuildTimerCompareValueRegister(TimerUnit timer, string namePrefix)
351         {
352             return new QuadWordRegister(this)
353                 .WithValueField(0, 64, name: $"{namePrefix}CompareValue",
354                     writeCallback: (_, val) =>
355                         {
356                             timer.CompareValue = val;
357                             timer.UpdateInterrupt();
358                         },
359                     valueProviderCallback: _ => timer.CompareValue
360                 );
361         }
362 
363         private readonly TimerUnit el1PhysicalTimer;
364         private readonly TimerUnit el1VirtualTimer;
365         private readonly TimerUnit el3PhysicalTimer;
366         private readonly TimerUnit nonSecureEL2PhysicalTimer;
367         private readonly TimerUnit nonSecureEL2VirtualTimer;
368         private readonly QuadWordRegisterCollection registersAArch64;
369         private readonly DoubleWordRegisterCollection doubleWordRegistersAArch32;
370         private readonly QuadWordRegisterCollection quadWordRegistersAArch32;
371         private readonly IClockSource clockSource;
372         private readonly uint defaultCounterFrequencyRegister;
373 
374         private enum RegistersAArch64
375         {
376             // Enum values are created from op0, op1, CRn, CRm and op2 fields of the MRS instruction.
377             Frequency = 0xdf00, // CNTFRQ_EL0
378             HypervisorControl = 0xe708, // CNTHCTL_EL2
379             KernelControl = 0xc708, // CNTKCTL_EL1
380 
381             PhysicalCount = 0xdf01, // CNTPCT_EL0
382             PhysicalOffset = 0xe706, // CNTPOFF_EL2
383             PhysicalSelfSynchronizedCount = 0xdf05, // CNTPCTSS_EL0
384 
385             VirtualCount = 0xdf02, // CNTVCT_EL0
386             VirtualOffset = 0xe703, // CNTVOFF_EL2
387             VirtualSelfSynchronizedCount = 0xdf06, // CNTVCTSS_EL0
388 
389             EL1PhysicalTimerValue = 0xdf10, // CNTP_TVAL_EL0
390             EL1PhysicalTimerControl = 0xdf11, // CNTP_CTL_EL0
391             EL1PhysicalTimerCompareValue = 0xdf12, // CNTP_CVAL_EL0
392             EL1VirtualTimerValue = 0xdf18, // CNTV_TVAL_EL0
393             EL1VirtualTimerControl = 0xdf19, // CNTV_CTL_EL0
394             EL1VirtualTimerCompareValue = 0xdf1a, // CNTV_CVAL_EL0
395 
396             EL3PhysicalTimerValue = 0xff10, // CNTPS_TVAL_EL1
397             EL3PhysicalTimerControl = 0xff11, // CNTPS_CTL_EL1
398             EL3PhysicalTimerCompareValue = 0xff12, // CNTPS_CVAL_EL1
399 
400             NonSecureEL2PhysicalTimerValue = 0xe710, // CNTHP_TVAL_EL2
401             NonSecureEL2PhysicalTimerControl = 0xe711, // CNTHP_CTL_EL2
402             NonSecureEL2PhysicalTimerCompareValue = 0xe712, // CNTHP_CVAL_EL2
403             NonSecureEL2VirtualTimerValue = 0xe718, // CNTHV_TVAL_EL2
404             NonSecureEL2VirtualTimerControl = 0xe719, // CNTHV_CTL_EL2
405             NonSecureEL2VirtualTimerCompareValue = 0xe71a, // CNTHV_CVAL_EL2
406 
407             // Secure EL2 timers are added by ARMv8.4-SecEL2 extension.
408             SecureEL2PhysicalTimerValue = 0xe728, // CNTHPS_TVAL_EL2
409             SecureEL2PhysicalTimerControl = 0xe729, // CNTHPS_CTL_EL2
410             SecureEL2PhysicalTimerCompareValue = 0xe72a, // CNTHPS_CVAL_EL2
411             SecureEL2VirtualTimerValue = 0xe720, // CNTHVS_TVAL_EL2
412             SecureEL2VirtualTimerControl = 0xe721, // CNTHVS_CTL_EL2
413             SecureEL2VirtualTimerCompareValue = 0xe722, // CNTHVS_CVAL_EL2
414         }
415 
416         private enum DoubleWordRegistersAArch32 : uint
417         {
418             // Enum values are created from opc1, CRn, opc2 and CRm fields of the MRC instruction.
419             Frequency = 0x0e0000, // CNTFRQ
420             PL1PhysicalControl = 0x0e0001, // CNTKCTL
421             PL1PhysicalTimerValue = 0x0e0002, // CNTP_TVAL
422             PL1PhysicalTimerControl = 0x0e0022, // CNTP_CTL
423             VirtualTimerValue = 0x0e0003, // CNTV_TVAL
424             VirtualTimerControl = 0x0e0023, // CNTV_CTL
425             PL2PhysicalControl = 0x8e0001, // CNTHCTL
426             PL2PhysicalTimerValue = 0x8e0002, // CNTHP_TVAL
427             PL2PhysicalTimerControl = 0x8e0022// CNTHP_CTL PL2
428         };
429 
430         private enum QuadWordRegistersAArch32 : uint
431         {
432             // Enum values are created from the opc1 and CRm fields of the MRRC instruction.
433             PhysicalCount = 0x0e, // CNTPCT
434             VirtualCount = 0x1e, // CNTVCT
435             PL1PhysicalTimerCompareValue = 0x2e, // CNTP_CVAL
436             VirtualTimerCompareValue = 0x3e, // CNTV_CVAL
437             VirtualOffset = 0x4e, // CNTVOFF
438             PL2PhysicalTimerCompareValue = 0x6e // CNTHP_CVAL
439         };
440 
441         private class TimerUnit
442         {
TimerUnit(IClockSource clockSource, IPeripheral parent, string name, GPIO irq, long frequency)443             public TimerUnit(IClockSource clockSource, IPeripheral parent, string name, GPIO irq, long frequency)
444             {
445                 this.clockSource = clockSource;
446                 this.name = name;
447                 this.parent = parent;
448                 IRQ = irq;
449 
450                 timer = new ComparingTimer(clockSource, frequency, parent, name, limit: ulong.MaxValue, compare: ulong.MaxValue, enabled: true, eventEnabled: true);
451                 timer.CompareReached += OnCompareReached;
452             }
453 
Reset()454             public void Reset()
455             {
456                 offset = 0;
457                 timer.Reset();
458 
459                 InterruptMask = false;
460                 InterruptEnable = false;
461                 UpdateStatus();
462                 UpdateInterrupt();
463             }
464 
UpdateInterrupt()465             public void UpdateInterrupt()
466             {
467                 var value = InterruptStatus && !InterruptMask && InterruptEnable;
468                 if(value != IRQ.IsSet)
469                 {
470                     parent.Log(LogLevel.Debug, "{0}: {1} IRQ", name, value ? "setting" : "unsetting");
471                     IRQ.Set(value);
472                 }
473             }
474 
475             public GPIO IRQ { get; }
476 
477             public ulong Value => timer.Value;
478 
479             // The offset property is defined as the difference between value properties of a some parent timer and the timer itself.
480             // For example the Virtual timer has an offset over the Physical timer.
481             public ulong Offset
482             {
483                 get => offset;
484                 set
485                 {
486                     clockSource.ExecuteInLock(() =>
487                         {
488                             var offsetDiff = value - offset;
489                             timer.Value -= offsetDiff;
490                             offset += offsetDiff;
491                             UpdateStatus();
492                         });
493                 }
494             }
495 
496             public ulong CompareValue
497             {
498                 get => timer.Compare;
499                 set
500                 {
501                     clockSource.ExecuteInLock(() =>
502                         {
503                             timer.Compare = value;
504                             UpdateStatus();
505                         });
506                 }
507             }
508 
509             public int CountDownValue
510             {
511                 get
512                 {
513                     int value = 0;
514                     clockSource.ExecuteInLock(() =>
515                         {
516                             value = (int)(CompareValue - Value);
517                         });
518                     return value;
519                 }
520                 set
521                 {
522                     clockSource.ExecuteInLock(() =>
523                         {
524                             CompareValue = Value + (ulong)value;
525                         });
526                 }
527             }
528 
529             public bool InterruptStatus { get; private set; }
530             public bool InterruptMask { get; set; }
531             public bool InterruptEnable { get; set; }
532 
UpdateStatus()533             private void UpdateStatus()
534             {
535                 InterruptStatus = Value >= CompareValue;
536                 // This method is typically called inside the IClockSource.ExecuteInLock() helper.
537                 // To prevent deadlock it doesn't call the UpdateInterrupt() method.
538             }
539 
OnCompareReached()540             private void OnCompareReached()
541             {
542                 InterruptStatus = true;
543                 UpdateInterrupt();
544             }
545 
546             private ulong offset;
547 
548             private readonly IClockSource clockSource;
549             private readonly string name;
550             private readonly IPeripheral parent;
551             private readonly ComparingTimer timer;
552         }
553     }
554 }
555