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.Linq;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Time;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 
14 namespace Antmicro.Renode.Peripherals.Timers
15 {
16     public class ARM_SP804_Timer : BasicDoubleWordPeripheral, IKnownSize
17     {
ARM_SP804_Timer(IMachine machine, long frequency = 1000000)18         public ARM_SP804_Timer(IMachine machine, long frequency = 1000000) : base(machine)
19         {
20             DefineRegisters();
21             for(var i = 0; i < NumberOfTimers; i++)
22             {
23                 var j = i;
24                 innerTimers[j] = new LimitTimer(machine.ClockSource, frequency, this, ((Timer)j).ToString(), limit: uint.MaxValue, eventEnabled: true, autoUpdate: true);
25                 innerTimers[j].LimitReached += delegate
26                 {
27                     this.Log(LogLevel.Noisy, "{0}: limit reached", (Timer)j);
28 
29                     if(backgroundLimitSet[j])
30                     {
31                         innerTimers[j].Limit = backgroundLimit[j];
32                         backgroundLimitSet[j] = false;
33                     }
34 
35                     interruptPending[j].Value = true;
36                     UpdateInterrupts();
37                 };
38             }
39         }
40 
Reset()41         public override void Reset()
42         {
43             base.Reset();
44             for(var i = 0; i < NumberOfTimers; i++)
45             {
46                 innerTimers[i].Reset();
47                 innerTimers[i].Limit = TimerLimitResetValue;
48                 backgroundLimit[i] = TimerLimitResetValue;
49                 backgroundLimitSet[i] = false;
50             }
51             UpdateInterrupts();
52         }
53 
54         public long Size => 0x1000;
55 
56         public GPIO IRQ { get; } = new GPIO();
57 
DefineRegisters()58         private void DefineRegisters()
59         {
60             Registers.Timer1Load.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, setup: (reg, idx) =>
61             {
62                 reg.WithValueField(0, 32, name: "Load",
63                     valueProviderCallback: _ => innerTimers[idx].Limit,
64                     writeCallback: (_, value) =>
65                     {
66                         innerTimers[idx].Limit = value;
67                     }
68                 );
69             });
70 
71             Registers.Timer1Value.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, resetValue: 0xFFFFFFFF, setup: (reg, idx) =>
72             {
73                 reg.WithValueField(0, 32, FieldMode.Read, name: "Value",
74                     valueProviderCallback: _ =>
75                     {
76                         if(machine.SystemBus.TryGetCurrentCPU(out var cpu))
77                         {
78                             // being here means we are on the CPU thread
79                             cpu.SyncTime();
80                         }
81                         return (uint)innerTimers[idx].Value;
82                     }
83                 );
84             });
85 
86             Registers.Timer1Control.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, resetValue: 0x20, setup: (reg, idx) =>
87             {
88                 reg.WithFlag(0, name: "One Shot",
89                     writeCallback: (_, val) =>
90                     {
91                         innerTimers[idx].Mode = val ? WorkMode.OneShot : WorkMode.Periodic;
92                     });
93                 reg.WithTaggedFlag("Timer Size", 1);
94                 reg.WithEnumField<DoubleWordRegister, PrescalerMode>(2, 2, name: "Timer Prescaler",
95                     writeCallback: (_, val) =>
96                     {
97                         switch(val)
98                         {
99                             case PrescalerMode.NoPrescaler:
100                                 innerTimers[idx].Divider = 1;
101                                 break;
102                             case PrescalerMode.Prescaler16:
103                                 innerTimers[idx].Divider = 16;
104                                 break;
105                             case PrescalerMode.Prescaler256:
106                                 innerTimers[idx].Divider = 256;
107                                 break;
108                             case PrescalerMode.Undefined:
109                                 this.Log(LogLevel.Error, "Timer{0} prescaler set to an undefined value!", idx);
110                                 break;
111                         }
112                     });
113                 reg.WithReservedBits(4, 1);
114                 reg.WithFlag(5, out interruptEnable[idx], name: "Interrupt Enable",
115                     writeCallback: (_, val) =>
116                     {
117                         UpdateInterrupts();
118                     });
119                 reg.WithTaggedFlag("Timer Mode", 6);
120                 reg.WithFlag(7, name: "Timer Enable",
121                     writeCallback: (_, val) =>
122                     {
123                         innerTimers[idx].Enabled = val;
124                     });
125                 reg.WithReservedBits(8, 24);
126                 reg.WithWriteCallback((_, __) =>
127                 {
128                     if(innerTimers[idx].Enabled)
129                     {
130                         this.Log(LogLevel.Warning, "Timer{0}: Writing to the Control register while the timer is running!", idx);
131                     }
132                 });
133             });
134 
135             Registers.Timer1InterruptClear.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, setup: (reg, idx) =>
136             {
137                 reg.WithFlag(0, out interruptPending[idx], FieldMode.WriteOneToClear, name: "Interrupt Clear Mask",
138                     writeCallback: (_, val) =>
139                     {
140                         UpdateInterrupts();
141                     });
142                 reg.WithReservedBits(1, 31);
143             });
144 
145             Registers.Timer1RawInterruptStatus.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, setup: (reg, idx) =>
146             {
147                 reg.WithFlag(0, FieldMode.Read, name: "Raw Interrupt Status",
148                     valueProviderCallback: _ => interruptPending[idx].Value);
149                 reg.WithReservedBits(1, 31);
150             });
151 
152             Registers.Timer1MaskedInterruptStatus.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, setup: (reg, idx) =>
153             {
154                 reg.WithFlag(0, FieldMode.Read, name: "Masked Interrupt Status",
155                     valueProviderCallback: _ => interruptEnable[idx].Value && interruptPending[idx].Value);
156                 reg.WithReservedBits(1, 31);
157             });
158 
159             Registers.Timer1BackgroundLoad.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, setup: (reg, idx) =>
160             {
161                 reg.WithValueField(0, 32, name: "Background Load",
162                     // As per the SP804 TRM, reading from Background Load returns the same value as the Load register
163                     valueProviderCallback: _ => (uint)innerTimers[idx].Limit,
164                     writeCallback: (_, value) =>
165                     {
166                         backgroundLimit[idx] = value;
167                         backgroundLimitSet[idx] = true;
168                     }
169                 );
170             });
171 
172             Registers.IntegrationTestControl.Define(this)
173                 .WithTaggedFlag("Integration Test Enable", 0)
174                 .WithReservedBits(1, 31);
175 
176             Registers.IntegrationTestOutputSet.Define(this)
177                 .WithTaggedFlag("Timer Interrupt 1", 0)
178                 .WithReservedBits(1, 31);
179         }
180 
UpdateInterrupts()181         private void UpdateInterrupts()
182         {
183             var anyPending = interruptEnable.Select(x => x.Value)
184                 .Zip(interruptPending.Select(x => x.Value), (enabled, pending) => enabled && pending)
185                 .Any(x => x);
186 
187             this.Log(LogLevel.Noisy, "Setting IRQ to: {0}", anyPending);
188             IRQ.Set(anyPending);
189         }
190 
191         private readonly IFlagRegisterField[] interruptEnable = new IFlagRegisterField[NumberOfTimers];
192         private readonly IFlagRegisterField[] interruptPending = new IFlagRegisterField[NumberOfTimers];
193         private readonly LimitTimer[] innerTimers = new LimitTimer[NumberOfTimers];
194         private readonly ulong[] backgroundLimit = new ulong[NumberOfTimers];
195         private readonly bool[] backgroundLimitSet = new bool[NumberOfTimers];
196 
197         private const int NumberOfTimers = 2;
198         private const uint TimersRegistersOffset = 0x20;
199         private const uint TimerLimitResetValue = 0xFFFFFFFF;
200 
201         private enum PrescalerMode
202         {
203             NoPrescaler,
204             Prescaler16,
205             Prescaler256,
206             Undefined
207         }
208 
209         private enum Timer
210         {
211             Timer1,
212             Timer2
213         }
214 
215         private enum Registers
216         {
217             Timer1Load = 0x00,
218             Timer1Value = 0x04,
219             Timer1Control = 0x08,
220             Timer1InterruptClear = 0x0C,
221             Timer1RawInterruptStatus = 0x10,
222             Timer1MaskedInterruptStatus = 0x14,
223             Timer1BackgroundLoad = 0x18,
224 
225             Timer2Load = 0x20,
226             Timer2Value = 0x24,
227             Timer2Control = 0x28,
228             Timer2InterruptClear = 0x2C,
229             Timer2RawInterruptStatus = 0x30,
230             Timer2MaskedInterruptStatus = 0x34,
231             Timer2BackgroundLoad = 0x38,
232 
233             IntegrationTestControl = 0xF00,
234             IntegrationTestOutputSet = 0xF04,
235         }
236     }
237 }
238