1 //
2 // Copyright (c) 2010-2023 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.Collections.Generic;
9 using System.Collections.ObjectModel;
10 using System.Linq;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Exceptions;
14 using Antmicro.Renode.Peripherals.CPU;
15 using Antmicro.Renode.Peripherals.IRQControllers;
16 using Antmicro.Renode.Time;
17 using Antmicro.Renode.Utilities;
18 
19 namespace Antmicro.Renode.Peripherals.Timers
20 {
21     public class ARM_GlobalTimer : BasicDoubleWordPeripheral, INumberedGPIOOutput, IKnownSize
22     {
ARM_GlobalTimer(IMachine machine, long frequency, IARMCPUsConnectionsProvider irqController)23         public ARM_GlobalTimer(IMachine machine, long frequency, IARMCPUsConnectionsProvider irqController)
24             : base(machine)
25         {
26             BuildRegisters();
27             globalTimer = new LimitTimer(machine.ClockSource, frequency, this, "coreTimer", direction: Direction.Ascending);
28             connections = new Dictionary<int, IGPIO>();
29             comparators = new Dictionary<ICPU, PrivateComparator>();
30             lock(locker)
31             {
32                 irqController.CPUAttached += AddCPU;
33                 foreach(var cpu in irqController.AttachedCPUs)
34                 {
35                     AddCPU(cpu);
36                 }
37             }
38         }
39 
40         public long Size => 0x100;
41 
42         public IReadOnlyDictionary<int, IGPIO> Connections
43         {
44             get
45             {
46                 lock(locker)
47                 {
48                     connectionsLocked = true;
49                     return connections;
50                 }
51             }
52         }
53 
Reset()54         public override void Reset()
55         {
56             lock(locker)
57             {
58                 base.Reset();
59                 foreach(var cmp in comparators.Values)
60                 {
61                     cmp.Reset();
62                 }
63             }
64         }
65 
ReadDoubleWord(long offset)66         public override uint ReadDoubleWord(long offset)
67         {
68             lock(locker)
69             {
70                 return base.ReadDoubleWord(offset);
71             }
72         }
73 
WriteDoubleWord(long offset, uint value)74         public override void WriteDoubleWord(long offset, uint value)
75         {
76             lock(locker)
77             {
78                 base.WriteDoubleWord(offset, value);
79             }
80         }
81 
ReadDoubleWord(long offset, ICPU cpu)82         public uint ReadDoubleWord(long offset, ICPU cpu)
83         {
84             lock(locker)
85             {
86                 providedCpu = cpu;
87                 var value = base.ReadDoubleWord(offset);
88                 providedCpu = null;
89                 return value;
90             }
91         }
92 
WriteDoubleWord(long offset, uint value, ICPU cpu)93         public void WriteDoubleWord(long offset, uint value, ICPU cpu)
94         {
95             lock(locker)
96             {
97                 providedCpu = cpu;
98                 base.WriteDoubleWord(offset, value);
99                 providedCpu = null;
100             }
101         }
102 
BuildRegisters()103         private void BuildRegisters()
104         {
105             Registers.CounterLow.Define(this)
106                 .WithValueField(0, 32, name: "Counter [0:31]",
107                     writeCallback: (_, value) => CounterLow = value,
108                     valueProviderCallback: (_) => CounterLow
109                 )
110             ;
111             Registers.CounterHigh.Define(this)
112                 .WithValueField(0, 32, name: "Counter [32:63]",
113                     writeCallback: (_, value) => CounterHigh = value,
114                     valueProviderCallback: (_) => CounterHigh
115                 )
116             ;
117             Registers.Control.Define(this)
118                 .WithFlag(0, name: "Timer enable",
119                     writeCallback: (_, value) => Enabled = value,
120                     valueProviderCallback: (_) => Enabled
121                 )
122                 .WithFlag(1, name: "Comp enable",
123                     writeCallback: (_, value) => GetComparator().ComparatorEnabled = value,
124                     valueProviderCallback: (_) => GetComparator().ComparatorEnabled
125                 )
126                 .WithFlag(2, name: "IRQ enable",
127                     writeCallback: (_, value) => GetComparator().IrqEnabled = value,
128                     valueProviderCallback: (_) => GetComparator().IrqEnabled
129                 )
130                 .WithFlag(3, name: "Auto-increment",
131                     writeCallback: (_, value) => GetComparator().AutoIncrementEnabled = value,
132                     valueProviderCallback: (_) => GetComparator().AutoIncrementEnabled
133                 )
134                 .WithReservedBits(4, 4)
135                 .WithValueField(8, 8, name: "Prescaler",
136                     writeCallback: (_, value) => Prescaler = value,
137                     valueProviderCallback: (_) => Prescaler
138                 )
139                 .WithReservedBits(16, 16)
140             ;
141             Registers.InterruptStatus.Define(this)
142                 .WithFlag(0, name: "Event",
143                     writeCallback: (_, value) => GetComparator().EventFlag = value,
144                     valueProviderCallback: (_) => GetComparator().EventFlag
145                 )
146                 .WithReservedBits(1, 31)
147             ;
148             Registers.CompareValueLow.Define(this)
149                 .WithValueField(0, 32, name: "Comparator Value [0:31]",
150                     writeCallback: (_, value) => GetComparator().CompareValueLow = value,
151                     valueProviderCallback: (_) => GetComparator().CompareValueLow
152                 )
153             ;
154             Registers.CompareValueHigh.Define(this)
155                 .WithValueField(0, 32, name: "Comparator Value [32:63]",
156                     writeCallback: (_, value) => GetComparator().CompareValueHigh = value,
157                     valueProviderCallback: (_) => GetComparator().CompareValueHigh
158                 )
159             ;
160             Registers.AutoIncrement.Define(this)
161                 .WithValueField(0, 32, name: "Auto-increment",
162                     writeCallback: (_, value) => GetComparator().AutoIncrement = value,
163                     valueProviderCallback: (_) => GetComparator().AutoIncrement
164                 )
165             ;
166         }
167 
AddCPU(ICPU cpu)168         private void AddCPU(ICPU cpu)
169         {
170             lock(locker)
171             {
172                 if(connectionsLocked)
173                 {
174                     throw new RecoverableException($"CPU (connection #{cpu.MultiprocessingId}) attached to IRQ Controller after Global Timer's GPIO initialization");
175                 }
176                 var comparator = new PrivateComparator(machine.ClockSource, globalTimer, this, $"{cpu.MultiprocessingId}");
177                 connections.Add((int)cpu.MultiprocessingId, comparator.IRQ);
178                 comparators.Add(cpu, comparator);
179             }
180         }
181 
GetComparator(ICPU cpu = null)182         private PrivateComparator GetComparator(ICPU cpu = null)
183         {
184             cpu = cpu ?? providedCpu;
185             if(cpu == null && !sysbus.TryGetCurrentCPU(out cpu))
186             {
187                 throw new RecoverableException("Attempted to access a core specific feature, but no CPU is selected nor detected");
188             }
189             if(comparators.TryGetValue(cpu, out var cmp))
190             {
191                 return cmp;
192             }
193             throw new RecoverableException($"Detected CPU {machine.GetLocalName(cpu)} is not connected to this peripheral");
194         }
195 
196         private ulong CounterLow
197         {
198             get => (uint)Counter;
199             set => Counter = BitHelper.SetMaskedValue(Counter, value, 0, 32);
200         }
201 
202         private ulong CounterHigh
203         {
204             get => (uint)(Counter >> 32);
205             set => Counter = BitHelper.SetMaskedValue(Counter, value, 32, 32);
206         }
207 
208         private ulong Counter
209         {
210             get
211             {
212                 var cpu = providedCpu;
213                 if(cpu != null || sysbus.TryGetCurrentCPU(out cpu))
214                 {
215                     cpu.SyncTime();
216                 }
217                 return globalTimer.Value;
218             }
219             set
220             {
221                 if(globalTimer.Value == value)
222                 {
223                     return;
224                 }
225                 globalTimer.Value = value;
226                 foreach(var cmp in comparators.Values)
227                 {
228                     cmp.Value = value;
229                 }
230             }
231         }
232 
233         private bool Enabled
234         {
235             get => globalTimer.Enabled;
236             set
237             {
238                 if(globalTimer.Enabled == value)
239                 {
240                     return;
241                 }
242                 globalTimer.Enabled = value;
243                 foreach(var cmp in comparators.Values)
244                 {
245                     cmp.Enabled = value;
246                 }
247             }
248         }
249 
250         private ulong Prescaler
251         {
252             get => (ulong)globalTimer.Divider;
253             set
254             {
255                 if((ulong)globalTimer.Divider == value + 1)
256                 {
257                     return;
258                 }
259                 globalTimer.Divider = (int)value + 1;
260                 foreach(var cmp in comparators.Values)
261                 {
262                     cmp.Divider = (uint)value + 1;
263                 }
264             }
265         }
266 
267         private bool connectionsLocked;
268         private ICPU providedCpu;
269         private readonly LimitTimer globalTimer;
270         private readonly Dictionary<ICPU, PrivateComparator> comparators;
271         private readonly Dictionary<int, IGPIO> connections;
272         private readonly object locker = new Object();
273 
274         public enum Registers
275         {
276             CounterLow       = 0x00,
277             CounterHigh      = 0x04,
278             Control          = 0x08,
279             InterruptStatus  = 0x0C,
280             CompareValueLow  = 0x10,
281             CompareValueHigh = 0x14,
282             AutoIncrement    = 0x18,
283         }
284 
285         private class PrivateComparator
286         {
PrivateComparator(IClockSource clockSource, LimitTimer coreTimer, IPeripheral owner, string coreName)287             public PrivateComparator(IClockSource clockSource, LimitTimer coreTimer, IPeripheral owner, string coreName)
288             {
289                 innerTimer = new ComparingTimer(clockSource, coreTimer.Frequency, owner, $"compareTimer-{coreName}", direction: Direction.Ascending, compare: 0, workMode: WorkMode.Periodic);
290                 innerTimer.Value = coreTimer.Value;
291                 innerTimer.Enabled = coreTimer.Enabled;
292                 innerTimer.Divider = (uint)coreTimer.Divider;
293                 innerTimer.CompareReached += HandleCompareEvent;
294                 IRQ = new GPIO();
295             }
296 
Reset()297             public void Reset()
298             {
299                 innerTimer.Reset();
300                 eventFlag = false;
301                 irqEnabled = false;
302                 UpdateInterrupt();
303             }
304 
305             public IGPIO IRQ { get; }
306 
307             public ulong CompareValueLow
308             {
309                 get => (uint)innerTimer.Compare;
310                 set
311                 {
312                     innerTimer.Compare = BitHelper.SetMaskedValue(innerTimer.Compare, value, 0, 32);
313                     UpdateEventFlag();
314                     UpdateInterrupt();
315                 }
316             }
317 
318             public ulong CompareValueHigh
319             {
320                 get => (uint)(innerTimer.Compare >> 32);
321                 set
322                 {
323                     innerTimer.Compare = BitHelper.SetMaskedValue(innerTimer.Compare, value, 32, 32);
324                     UpdateEventFlag();
325                     UpdateInterrupt();
326                 }
327             }
328 
329             public ulong AutoIncrement { get; set; }
330 
331             public bool EventFlag
332             {
333                 get => eventFlag;
334                 set
335                 {
336                     if(!value)
337                     {
338                         return;
339                     }
340                     eventFlag = false;
341                     UpdateEventFlag();
342                     UpdateInterrupt();
343                 }
344             }
345 
346             public bool Enabled
347             {
348                 set => innerTimer.Enabled = value;
349             }
350 
351             public bool ComparatorEnabled
352             {
353                 get => innerTimer.EventEnabled;
354                 set
355                 {
356                     innerTimer.EventEnabled = value;
357                     UpdateEventFlag();
358                     UpdateInterrupt();
359                 }
360             }
361 
362             public bool IrqEnabled
363             {
364                 get => irqEnabled;
365                 set
366                 {
367                     if(irqEnabled == value)
368                     {
369                         return;
370                     }
371                     irqEnabled = value;
372                     UpdateInterrupt();
373                 }
374             }
375 
376             public bool AutoIncrementEnabled { get; set; }
377 
378             public ulong Value
379             {
380                 set
381                 {
382                     innerTimer.Value = value;
383                     UpdateEventFlag();
384                     UpdateInterrupt();
385                 }
386             }
387 
388             public uint Divider
389             {
390                 set => innerTimer.Divider = value;
391             }
392 
HandleCompareEvent()393             private void HandleCompareEvent()
394             {
395                 eventFlag = true;
396                 if(AutoIncrementEnabled)
397                 {
398                     innerTimer.Compare += AutoIncrement;
399                 }
400                 UpdateInterrupt();
401             }
402 
UpdateEventFlag()403             private void UpdateEventFlag()
404             {
405                 eventFlag |= ComparatorEnabled && innerTimer.Value >= innerTimer.Compare;
406             }
407 
UpdateInterrupt()408             private void UpdateInterrupt()
409             {
410                 IRQ.Set(IrqEnabled && EventFlag);
411             }
412 
413             private bool eventFlag;
414             private bool irqEnabled;
415             private readonly ComparingTimer innerTimer;
416         }
417     }
418 }
419