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 Antmicro.Renode.Core;
8 using Antmicro.Renode.Core.Structure.Registers;
9 using Antmicro.Renode.Exceptions;
10 using Antmicro.Renode.Time;
11 using Antmicro.Renode.Peripherals.Bus;
12 using System.Collections.Generic;
13 using System.Threading.Tasks;
14 
15 namespace Antmicro.Renode.Peripherals.Timers
16 {
17     public class ARM_PrivateTimer : BasicDoubleWordPeripheral, IKnownSize
18     {
ARM_PrivateTimer(IMachine machine, ulong frequency)19         public ARM_PrivateTimer(IMachine machine, ulong frequency) : base(machine)
20         {
21             if(frequency > long.MaxValue)
22             {
23                 throw new ConstructionException($"Timer doesn't support frequency greater than {long.MaxValue}, given {frequency}.");
24             }
25             Frequency = (long)frequency;
26 
27             BuildRegisters();
28             timer = new LimitTimer(machine.ClockSource, Frequency, this, "Timer",
29                 limit: uint.MaxValue, direction: Direction.Descending, workMode: WorkMode.OneShot, autoUpdate: true);
30             timer.LimitReached += UpdateInterrupt;
31         }
32 
Reset()33         public override void Reset()
34         {
35             base.Reset();
36             timer.Reset();
37             UpdateInterrupt();
38         }
39 
40         public long Size => 0x200;
41 
42         public GPIO IRQ { get; } = new GPIO();
43         public long Frequency { get; }
44 
BuildRegisters()45         private void BuildRegisters()
46         {
47             Registers.Load.Define(this)
48                 .WithValueField(0, 32, name: "Load",
49                     writeCallback: (_, val) => timer.Limit = val,
50                     valueProviderCallback: (_) => (uint)timer.Limit
51                 );
52             Registers.Counter.Define(this)
53                 .WithValueField(0, 32, name: "Counter",
54                     writeCallback: (_, val) =>
55                     {
56                         if(machine.SystemBus.TryGetCurrentCPU(out var cpu))
57                         {
58                             cpu.SyncTime();
59                         }
60                         timer.Value = val;
61                     },
62                     valueProviderCallback: (_) => (uint)timer.Value
63                 );
64             Registers.Control.Define(this)
65                 .WithReservedBits(16, 16)
66                 .WithValueField(8, 8, name: "Prescaler",
67                     writeCallback: (_, val) => timer.Divider = (int)val + 1,
68                     valueProviderCallback: (_) => (ulong)timer.Divider - 1
69                 )
70                 .WithReservedBits(3, 5)
71                 .WithFlag(2, name: "InterruptEnabled",
72                     writeCallback: (_, val) => timer.EventEnabled = val,
73                     valueProviderCallback: (_) => timer.EventEnabled
74                 )
75                 .WithFlag(1, name: "AutoLoad",
76                     writeCallback: (_, val) => timer.Mode = val ? WorkMode.Periodic : WorkMode.OneShot,
77                     valueProviderCallback: (_) => timer.Mode == WorkMode.Periodic
78                 )
79                 .WithFlag(0, name: "Enabled",
80                     writeCallback: (_, val) => timer.Enabled = val,
81                     valueProviderCallback: (_) => timer.Enabled
82                 )
83                 .WithWriteCallback(
84                     (_, __) => UpdateInterrupt()
85                 );
86             Registers.InterruptStatus.Define(this)
87                 .WithReservedBits(1, 31)
88                 .WithFlag(0, name: "InterruptStatus",
89                     writeCallback: (_, val) => { if(val) timer.ClearInterrupt(); },
90                     valueProviderCallback: (_) => timer.RawInterrupt
91                 )
92                 .WithWriteCallback(
93                     (_, __) => UpdateInterrupt()
94                 );
95         }
96 
UpdateInterrupt()97         private void UpdateInterrupt()
98         {
99             IRQ.Set(timer.Interrupt);
100         }
101 
102         private readonly LimitTimer timer;
103 
104         private enum Registers : long
105         {
106             Load = 0x00,
107             Counter = 0x04,
108             Control = 0x08,
109             InterruptStatus = 0x0C
110         }
111     }
112 }
113