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 System.Collections.Generic;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Time;
13 
14 namespace Antmicro.Renode.Peripherals.Timers
15 {
16     public class S32K3XX_PeriodicInterruptTimer : BasicDoubleWordPeripheral, IKnownSize
17     {
S32K3XX_PeriodicInterruptTimer(IMachine machine, long oscillatorFrequency, bool hasRealTimeInterrupt = false, bool hasLifetimeTimer = false, bool supportsTimersChaining = false)18         public S32K3XX_PeriodicInterruptTimer(IMachine machine, long oscillatorFrequency, bool hasRealTimeInterrupt = false, bool hasLifetimeTimer = false, bool supportsTimersChaining = false) : base(machine)
19         {
20             clockChannels = new SortedList<Registers, ClockChannel>();
21 
22             IRQ = new GPIO();
23 
24             DefineRegisters(oscillatorFrequency, hasRealTimeInterrupt, hasLifetimeTimer, supportsTimersChaining);
25             Reset();
26         }
27 
Reset()28         public override void Reset()
29         {
30             base.Reset();
31             IRQ.Unset();
32 
33             foreach(var clockChannel in clockChannels.Values)
34             {
35                 clockChannel.Reset();
36             }
37         }
38 
39         public long Size => 0x4000;
40         public GPIO IRQ { get; }
41 
UpdateInterrupts()42         private void UpdateInterrupts()
43         {
44             var interrupt = clockChannels.Values.Any(clockChannel => clockChannel.InterruptEnable && clockChannel.InterruptFlag);
45             IRQ.Set(interrupt);
46         }
47 
DefineRegisters(long oscillatorFrequency, bool hasRealTimeInterrupt, bool hasLifetimeTimer, bool supportsTimersChaining)48         private void DefineRegisters(long oscillatorFrequency, bool hasRealTimeInterrupt, bool hasLifetimeTimer, bool supportsTimersChaining)
49         {
50             var moduleControl = Registers.ModuleControl.Define(this)
51                 .WithReservedBits(3, 29)
52                 .WithTaggedFlag("ModuleDisableForPIT", 1)
53                 .WithTaggedFlag("Freeze", 0)
54             ;
55 
56             if(hasRealTimeInterrupt)
57             {
58                 moduleControl.WithTaggedFlag("ModuleDisableForRTI", 2);
59             }
60             else
61             {
62                 moduleControl.WithReservedBits(2, 1);
63             }
64 
65             if(hasLifetimeTimer)
66             {
67                 // NOTE: Technically value of those two registers is only valid when PIT has configured lifetimer,
68                 // that is Channel 1 has enabled chain mode, and both Channel 0 and Channel 1 start values are set to
69                 // 0xFFFF_FFFF, but documentation doesn't specify what we should read from lifetimer registers when that's
70                 // not the case
71 
72                 Registers.LowerLifetimer.Define(this)
73                     .WithValueField(0, 32, out var lowerLifetimerValue, FieldMode.Read, name: "LifetimerValue");
74                 ;
75 
76                 Registers.UpperLifetimer.Define(this)
77                     .WithValueField(0, 32, FieldMode.Read, name: "LifetimerValue",
78                         valueProviderCallback: _ =>
79                         {
80                             var channel0 = clockChannels[Registers.Control0];
81                             var channel1 = clockChannels[Registers.Control1];
82                             var lifetimerValue = ulong.MaxValue - ((channel0.Value << 32) | channel1.Value);
83 
84                             lowerLifetimerValue.Value = lifetimerValue;
85                             return lifetimerValue >> 32;
86                         })
87                 ;
88             }
89 
90             if(hasRealTimeInterrupt)
91             {
92                 DefineChannelRegisters(oscillatorFrequency, null, Registers.ControlRTI, Registers.FlagRTI, Registers.LoadValueRTI, Registers.CurrentValueRTI);
93             }
94 
95             DefineChannelRegisters(oscillatorFrequency, null, Registers.Control0, Registers.Flag0, Registers.LoadValue0, Registers.CurrentValue0);
96             DefineChannelRegisters(oscillatorFrequency, Registers.Control0, Registers.Control1, Registers.Flag1, Registers.LoadValue1, Registers.CurrentValue1);
97             DefineChannelRegisters(oscillatorFrequency, Registers.Control1, Registers.Control2, Registers.Flag2, Registers.LoadValue2, Registers.CurrentValue2);
98             DefineChannelRegisters(oscillatorFrequency, Registers.Control2, Registers.Control3, Registers.Flag3, Registers.LoadValue3, Registers.CurrentValue3);
99 
100             if(hasRealTimeInterrupt)
101             {
102                 Registers.LoadValueSyncStatusRTI.Define(this)
103                     .WithReservedBits(1, 31)
104                     .WithTaggedFlag("SyncStatus", 0)
105                 ;
106             }
107         }
108 
DefineChannelRegisters(long oscillatorFrequency, Registers? chainedTo, Registers control, Registers flag, Registers load, Registers currentValue)109         private void DefineChannelRegisters(long oscillatorFrequency, Registers? chainedTo, Registers control, Registers flag, Registers load, Registers currentValue)
110         {
111             var clockChannel = new ClockChannel(machine.ClockSource, this, oscillatorFrequency, Enum.GetName(typeof(Registers), control));
112             clockChannel.OnInterrupt += UpdateInterrupts;
113 
114             var controlRegister = control.Define(this)
115                 .WithReservedBits(3, 29)
116                 .WithFlag(1, name: "InterruptEnable",
117                     valueProviderCallback: _ => clockChannel.InterruptEnable,
118                     changeCallback: (_, value) =>
119                     {
120                         clockChannel.InterruptEnable = value;
121                         UpdateInterrupts();
122                     })
123                 .WithFlag(0, name: "TimerEnable",
124                     valueProviderCallback: _ => clockChannel.Enabled,
125                     changeCallback: (_, value) => clockChannel.Enabled = value)
126             ;
127 
128             if(chainedTo != null)
129             {
130                 controlRegister.WithFlag(2, name: "ChainMode",
131                     valueProviderCallback: _ => clockChannel.ChainMode,
132                     writeCallback: (_, value) => clockChannel.ChainMode = value)
133                 ;
134 
135                 var chainedTimer = clockChannels[chainedTo.Value];
136                 chainedTimer.OnInterrupt += () =>
137                 {
138                     if(clockChannel.ChainMode)
139                     {
140                         clockChannel.Step();
141                     }
142                 };
143             }
144             else
145             {
146                 controlRegister.WithReservedBits(2, 1);
147             }
148 
149             flag.Define(this)
150                 .WithReservedBits(1, 31)
151                 .WithFlag(0, name: "InterruptFlag",
152                     valueProviderCallback: _ => clockChannel.InterruptFlag,
153                     writeCallback: (_, value) => { if(value) clockChannel.InterruptFlag = false; } )
154                 .WithWriteCallback((_, __) => UpdateInterrupts())
155             ;
156 
157             load.Define(this)
158                 .WithValueField(0, 32, name: "StartValue",
159                     valueProviderCallback: _ => clockChannel.StartValue,
160                     writeCallback: (_, value) => clockChannel.StartValue = value)
161             ;
162 
163             currentValue.Define(this)
164                 .WithValueField(0, 32, FieldMode.Read, name: "CurrentValue",
165                     valueProviderCallback: _ => clockChannel.Value)
166             ;
167 
168             clockChannels.Add(control, clockChannel);
169         }
170 
171         private readonly IDictionary<Registers, ClockChannel> clockChannels;
172 
173         private class ClockChannel
174         {
ClockChannel(IClockSource clockSource, IPeripheral parent, long frequency, string name)175             public ClockChannel(IClockSource clockSource, IPeripheral parent, long frequency, string name)
176             {
177                 underlyingTimer = new LimitTimer(clockSource, frequency, parent, name);
178                 underlyingTimer.LimitReached += () =>
179                 {
180                     InterruptFlag = true;
181                     OnInterrupt?.Invoke();
182                 };
183             }
184 
Step()185             public void Step()
186             {
187                 if(Value == 0)
188                 {
189                     Value = StartValue;
190                 }
191                 else if(--Value == 0)
192                 {
193                     InterruptFlag = true;
194                     OnInterrupt?.Invoke();
195                 }
196             }
197 
Reset()198             public void Reset()
199             {
200                 Enabled = false;
201                 InterruptEnable = false;
202                 InterruptFlag = false;
203                 Value = ulong.MaxValue;
204                 StartValue = Value;
205                 ChainMode = false;
206             }
207 
208             public event Action OnInterrupt;
209 
210             public bool Enabled
211             {
212                 get => timerEnabled;
213                 set
214                 {
215                     timerEnabled = value;
216                     underlyingTimer.Enabled = value && !ChainMode;
217                 }
218             }
219 
220             public bool InterruptEnable
221             {
222                 get => underlyingTimer.EventEnabled;
223                 set => underlyingTimer.EventEnabled = value;
224             }
225 
226             public bool InterruptFlag { get; set; }
227 
228             public ulong Value
229             {
230                 get => underlyingTimer.Value;
231                 private set => underlyingTimer.Value = value;
232             }
233 
234             public ulong StartValue
235             {
236                 get => underlyingTimer.Limit;
237                 set
238                 {
239                     underlyingTimer.Limit = value;
240                     underlyingTimer.Value = value;
241                 }
242             }
243 
244             public bool ChainMode
245             {
246                 get => chainMode;
247                 set
248                 {
249                     chainMode = value;
250                     Enabled = timerEnabled;
251                 }
252             }
253 
254             private readonly LimitTimer underlyingTimer;
255 
256             private bool chainMode;
257             private bool timerEnabled;
258         }
259 
260         private enum Registers
261         {
262             ModuleControl = 0x0, // MCR
263             UpperLifetimer = 0xE0, // LTMR64H
264             LowerLifetimer = 0xE4, // LTMR64L
265             LoadValueSyncStatusRTI = 0xEC, // RTI_LDVAL_STAT
266             LoadValueRTI = 0xF0, // RTI_LDVAL
267             CurrentValueRTI = 0xF4, // RTI_CVAL
268             ControlRTI = 0xF8, // RTI_TCTRL
269             FlagRTI = 0xFC, // RTI_TFLG
270             LoadValue0 = 0x100, // LDVAL0
271             CurrentValue0 = 0x104, // CVAL0
272             Control0 = 0x108, // TCTRL0
273             Flag0 = 0x10C, // TFLG0
274             LoadValue1 = 0x110, // LDVAL1
275             CurrentValue1 = 0x114, // CVAL1
276             Control1 = 0x118, // TCTRL1
277             Flag1 = 0x11C, // TFLG1
278             LoadValue2 = 0x120, // LDVAL2
279             CurrentValue2 = 0x124, // CVAL2
280             Control2 = 0x128, // TCTRL2
281             Flag2 = 0x12C, // TFLG2
282             LoadValue3 = 0x130, // LDVAL3
283             CurrentValue3 = 0x134, // CVAL3
284             Control3 = 0x138, // TCTRL3
285             Flag3 = 0x13C, // TFLG3
286         }
287     }
288 }
289