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.Linq;
8 
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Time;
13 
14 namespace Antmicro.Renode.Peripherals.Timers
15 {
16     public class S32K_LPIT : BasicDoubleWordPeripheral, IKnownSize, IGPIOReceiver
17     {
S32K_LPIT(IMachine machine, long frequency)18         public S32K_LPIT(IMachine machine, long frequency) : base(machine)
19         {
20             IRQ = new GPIO();
21             TimerOutput0 = new GPIO();
22             TimerOutput1 = new GPIO();
23             TimerOutput2 = new GPIO();
24             TimerOutput3 = new GPIO();
25 
26             for(var channel = 0; channel < ChannelCount; channel++)
27             {
28                 // The frequency typically depends on PCC_LPIT register.
29                 timers[channel] = new LPITTimer(machine.ClockSource, frequency, this, $"ch{channel}");
30             }
31             // Timers have to be initialized before calling 'DefineRegisters'.
32             DefineRegisters();
33         }
34 
PrintTimerInformation(int channel)35         public string PrintTimerInformation(int channel)
36         {
37             return $"{timers[channel]}";
38         }
39 
OnGPIO(int number, bool value)40         public void OnGPIO(int number, bool value)
41         {
42             // For external triggers.
43             // If and which external trigger each LPIT timer uses depends on its TRG_SRC and TRG_SEL fields.
44             // These should be connected via TRGMUX_LPIT0 register.
45             this.Log(LogLevel.Error, "External triggers aren't currently supported!");
46         }
47 
ReadDoubleWord(long offset)48         public override uint ReadDoubleWord(long offset)
49         {
50             if(!IsRegisterAccessValid(offset))
51             {
52                 this.Log(LogLevel.Error, "Reading from the {0} register with module disabled is forbidden!", (Registers)offset);
53                 // "generate a transfer error"
54                 return 0;
55             }
56             return base.ReadDoubleWord(offset);
57         }
58 
Reset()59         public override void Reset()
60         {
61             base.Reset();
62             IRQ.Unset();
63             for(var channel = 0; channel < ChannelCount; channel++)
64             {
65                 timers[channel].Reset();
66                 GetTimerOutput(channel).Unset();
67             }
68         }
69 
WriteDoubleWord(long offset, uint value)70         public override void WriteDoubleWord(long offset, uint value)
71         {
72             if(!IsRegisterAccessValid(offset))
73             {
74                 this.Log(LogLevel.Error, "Writing to the {0} register with module disabled is forbidden! Value: {1:X}", (Registers)offset, value);
75                 // "generate a transfer error"
76                 return;
77             }
78             base.WriteDoubleWord(offset, value);
79         }
80 
81         public GPIO IRQ { get; }
82 
83         public long Size => 0x1000;
84 
85         public GPIO TimerOutput0 { get; }
86         public GPIO TimerOutput1 { get; }
87         public GPIO TimerOutput2 { get; }
88         public GPIO TimerOutput3 { get; }
89 
DefineChannelRegisters(int channel, Registers valueRegister, Registers currentValueRegister, Registers controlRegister)90         private void DefineChannelRegisters(int channel, Registers valueRegister, Registers currentValueRegister, Registers controlRegister)
91         {
92             valueRegister.Define(this)
93                 .WithValueField(0, 32, name: "TMR_VAL - Timer Value",
94                     changeCallback: (x, value) => timers[channel].ValueSet = (uint)value,
95                     valueProviderCallback: (value) => timers[channel].ValueSet);
96 
97             currentValueRegister.Define(this)
98                 .WithValueField(0, 32, FieldMode.Read, name: "TMR_CUR_VAL - Current Timer Value",
99                     valueProviderCallback: (value) => timers[channel].Enabled ? (uint)timers[channel].Value : uint.MaxValue);
100 
101             controlRegister.Define(this)
102                 .WithFlag(0, name: "T_EN - Timer Enable",
103                     changeCallback: (x, value) => timers[channel].SetTimerEnabled(value),
104                     valueProviderCallback: (value) => timers[channel].Enabled)
105                 .WithTaggedFlag("CHAIN - Chain Channel", 1)
106                 .WithEnumField<DoubleWordRegister, OperationModes>(2, 2, name: "MODE - Timer Operation Mode",
107                     changeCallback: (x, value) => timers[channel].OperationMode = value,
108                     valueProviderCallback: (value) => timers[channel].OperationMode)
109                 .WithReservedBits(4, 12)
110                 .WithTaggedFlag("TSOT - Timer Start On Trigger", 16)
111                 .WithFlag(17, name: "TSOI - Timer Stop On Interrupt",
112                     changeCallback: (x, value) => timers[channel].Mode = value ? WorkMode.OneShot : WorkMode.Periodic,
113                     valueProviderCallback: (value) => timers[channel].Mode == WorkMode.OneShot)
114                 .WithTaggedFlag("TROT - Timer Reload On Trigger", 18)
115                 .WithReservedBits(19, 4)
116                 .WithEnumField<DoubleWordRegister, TriggerModes>(23, 1, name: "TRG_SRC - Trigger Source",
117                     changeCallback: (x, value) => timers[channel].TriggerMode = value,
118                     valueProviderCallback: (value) => timers[channel].TriggerMode)
119                 .WithTag("TRG_SEL - Trigger Select", 24, 4)
120                 .WithReservedBits(28, 4);
121         }
122 
DefineRegisters()123         private void DefineRegisters()
124         {
125             Registers.VersionID.Define(this)
126                 .WithValueField(0, 16, FieldMode.Read, name: "FEATURE - Feature Number", valueProviderCallback: (x) => 0u)
127                 .WithValueField(16, 8, FieldMode.Read, name: "MINOR - Minor Version Number", valueProviderCallback: (x) => 0u)
128                 .WithValueField(24, 8, FieldMode.Read, name: "MAJOR - Major Version Number", valueProviderCallback: (x) => 1u);
129 
130             Registers.Parameter.Define(this)
131                 .WithValueField(0, 8, FieldMode.Read, name: "CHANNEL - Number of Timer Channels", valueProviderCallback: (x) => ChannelCount)
132                 .WithValueField(8, 8, FieldMode.Read, name: "EXT_TRIG - Number of External Trigger Inputs", valueProviderCallback: (x) => ChannelCount)
133                 .WithReservedBits(16, 16);
134 
135             Registers.ModuleControl.Define(this)
136                 .WithFlag(0, out moduleEnabled, name: "M_CEN - Module Clock Enable", changeCallback: (x, value) => UpdateInterrupts())
137                 // TODO: Software Reset shouldn't reset the Module Control register.
138                 .WithFlag(1, name: "SW_RST - Software Reset", changeCallback: (x, value) => { if(value) Reset(); })
139                 .WithTaggedFlag("DOZE_EN - DOZE Mode Enable", 2)
140                 .WithTaggedFlag("DBG_EN - Debug Enable", 3)
141                 .WithReservedBits(4, 28);
142 
143             Registers.ModuleStatus.Define(this)
144                 .WithFlags(0, 4, name: "TIFn - Channel n Timer Interrupt Flag",
145                     writeCallback: (channel, x, value) => { if(value) timers[channel].ClearInterrupt(); UpdateInterrupts(); },
146                     valueProviderCallback: (channel, value) => timers[channel].RawInterrupt)
147                 .WithReservedBits(4, 28);
148 
149             Registers.ModuleInterruptEnable.Define(this)
150                 .WithFlags(0, 4, name: "TIEn - Channel n Timer Interrupt Enable",
151                     changeCallback: (channel, x, value) => { timers[channel].EventEnabled = value; UpdateInterrupts(); },
152                     valueProviderCallback: (channel, value) => timers[channel].EventEnabled)
153                 .WithReservedBits(4, 28);
154 
155             Registers.SetTimerEnable.Define(this)
156                 .WithFlags(0, 4, name: "SET_T_EN_n - Set Timer n Enable",
157                     // Writing 0 has no effect.
158                     writeCallback: (channel, x, value) => { if(value) timers[channel].SetTimerEnabled(true); },
159                     valueProviderCallback: (channel, x) => timers[channel].Enabled)
160                 .WithReservedBits(4, 28);
161 
162             Registers.ClearTimerEnable.Define(this)
163                 // TODO: Does disabling a timer clears its interrupt flag?
164                 .WithFlags(0, 4, FieldMode.Write, name: "CLR_T_EN_n - Clear Timer n Enable",
165                     // Writing 0 has no effect.
166                     writeCallback: (channel, x, value) => { if(value) timers[channel].SetTimerEnabled(false); })
167                 .WithReservedBits(4, 28);
168 
169             DefineChannelRegisters(0, Registers.TimerValue0, Registers.CurrentTimerValue0, Registers.TimerControl0);
170             DefineChannelRegisters(1, Registers.TimerValue1, Registers.CurrentTimerValue1, Registers.TimerControl1);
171             DefineChannelRegisters(2, Registers.TimerValue2, Registers.CurrentTimerValue2, Registers.TimerControl2);
172             DefineChannelRegisters(3, Registers.TimerValue3, Registers.CurrentTimerValue3, Registers.TimerControl3);
173         }
174 
GetTimerOutput(int channel)175         private GPIO GetTimerOutput(int channel)
176         {
177             switch(channel)
178             {
179                 case 0:
180                     return TimerOutput0;
181                 case 1:
182                     return TimerOutput1;
183                 case 2:
184                     return TimerOutput2;
185                 case 3:
186                     return TimerOutput3;
187             }
188             throw new System.ArgumentException($"No such channel: {channel}! LPIT has only {ChannelCount} channels!");
189         }
190 
IsRegisterAccessValid(long register)191         private bool IsRegisterAccessValid(long register)
192         {
193             switch((Registers)register)
194             {
195                 case Registers.ModuleStatus:
196                 case Registers.SetTimerEnable:
197                 case Registers.ClearTimerEnable:
198                 case Registers.TimerValue0:
199                 case Registers.TimerValue1:
200                 case Registers.TimerValue2:
201                 case Registers.TimerValue3:
202                 case Registers.TimerControl0:
203                 case Registers.TimerControl1:
204                 case Registers.TimerControl2:
205                 case Registers.TimerControl3:
206                     return moduleEnabled.Value;
207             }
208             return true;
209         }
210 
UpdateInterrupts()211         private void UpdateInterrupts()
212         {
213             // None of these should be set if Module Enabled field is cleared.
214             IRQ.Set(moduleEnabled.Value && timers.Any((timer) => timer.Interrupt));
215             for(int channel = 0; channel < ChannelCount; channel++)
216             {
217                 // For output it doesn't matter whether interrupts are enabled (hence RawInterrupt).
218                 GetTimerOutput(channel).Set(moduleEnabled.Value && timers[channel].RawInterrupt);
219             }
220         }
221 
222         private IFlagRegisterField moduleEnabled;
223         private readonly LPITTimer[] timers = new LPITTimer[ChannelCount];
224 
225         private const uint ChannelCount = 4;
226 
227         private class LPITTimer : LimitTimer
228         {
LPITTimer(IClockSource clockSource, long frequency, S32K_LPIT owner, string name)229             public LPITTimer(IClockSource clockSource, long frequency, S32K_LPIT owner, string name) : base(clockSource, frequency, owner, name,
230                     limit: uint.MaxValue, direction: Direction.Descending, enabled: false, workMode: WorkMode.Periodic, eventEnabled: false, autoUpdate: true)
231             {
232                 lpit = owner;
233                 this.name = name;
234                 LimitReached += LimitReachedHandle;
235 
236                 Reset();
237             }
238 
ToString()239             public override string ToString()
240             {
241                 return $"{name}: L {Limit} (VS={valueSet}) /V {Value} /E {Enabled} /EE {EventEnabled} /M {OperationMode} /RI {RawInterrupt} /I {Interrupt}";
242             }
243 
Reset()244             public override void Reset()
245             {
246                 base.Reset();
247                 operationMode = 0;
248                 TriggerMode = TriggerModes.External;
249                 valueSet = uint.MaxValue;  // Has to match the initial limit value.
250             }
251 
SetTimerEnabled(bool value)252             public void SetTimerEnabled(bool value)
253             {
254                 DebugLog("Setting Timer Enabled: {0}", value);
255                 if(value && TriggerMode == TriggerModes.External)
256                 {
257                     Log(LogLevel.Error, "External trigger sources aren't currently supported, treating as internal!");
258                 }
259 
260                 Enabled = value;
261                 if(!value)
262                 {
263                     if(Limit == valueSet)
264                     {
265                         ResetValue();
266                     }
267                     else
268                     {
269                         Limit = valueSet;
270                     }
271                 }
272             }
273 
274             public OperationModes OperationMode
275             {
276                 get => operationMode;
277                 set
278                 {
279                     DebugLog("Setting Operation Mode: {0}", value);
280                     switch(value)
281                     {
282                         case OperationModes.DualPeriodicCounter:
283                         case OperationModes.TriggerAccumulator:
284                         case OperationModes.TriggerInputCapture:
285                             Log(LogLevel.Error, "{0}: The {1} mode isn't currently supported, still operating as {2}!", name, value);
286                             return;
287                     }
288                     operationMode = value;
289                 }
290             }
291 
292             public uint ValueSet
293             {
294                 // In capture mode "the inverse of the counter value" should be returned.
295                 get => valueSet;
296                 set
297                 {
298                     DebugLog("Setting Value: {0:X}", value);
299                     if(IsInCompareMode())
300                     {
301                         if(value < 2)
302                         {
303                             Log(LogLevel.Error, "Invalid load value in compare mode: {1}", value);
304                             return;
305                         }
306 
307                         valueSet = value;
308                         if(!Enabled)
309                         {
310                             DebugLog("Timer disabled, setting value.");
311                             Limit = value;
312                         }
313                         else
314                         {
315                             DebugLog("Timer enabled, value will be set after the timer is disabled or 0 is reached.");
316                         }
317                     }
318                 }
319             }
320 
321             public TriggerModes TriggerMode { get; set; }
322 
LimitReachedHandle()323             private void LimitReachedHandle()
324             {
325                 this.DebugLog("Limit reached!");
326                 // TODO: Reaching Limit should generate Pre-Trigger and Trigger outputs.
327                 if(IsInCompareMode())
328                 {
329                     lpit.UpdateInterrupts();
330                     if(Limit != valueSet)
331                     {
332                         Limit = valueSet;
333                     }
334                 }
335             }
336 
IsInCompareMode()337             private bool IsInCompareMode()
338             {
339                 return operationMode == OperationModes.PeriodicCounter || operationMode == OperationModes.DualPeriodicCounter;
340             }
341 
342             // Convenience functions to prepend timer name
DebugLog(string message, params object[] args)343             private void DebugLog(string message, params object[] args)
344             {
345                 Log(LogLevel.Debug, message, args);
346             }
347 
Log(LogLevel level, string message, params object[] args)348             private void Log(LogLevel level, string message, params object[] args)
349             {
350                 lpit.Log(level, $"{name}: {message}", args);
351             }
352 
353             private readonly S32K_LPIT lpit;
354             private readonly string name;
355             private OperationModes operationMode;
356             private uint valueSet;
357         }
358 
359         private enum OperationModes
360         {
361             PeriodicCounter,
362             DualPeriodicCounter,
363             TriggerAccumulator,
364             TriggerInputCapture,
365         }
366 
367         private enum Registers
368         {
369             VersionID = 0x0,
370             Parameter = 0x4,
371             ModuleControl = 0x8,
372             ModuleStatus = 0xC,
373             ModuleInterruptEnable = 0x10,
374             SetTimerEnable = 0x14,
375             ClearTimerEnable = 0x18,
376             TimerValue0 = 0x20,
377             CurrentTimerValue0 = 0x24,
378             TimerControl0 = 0x28,
379             TimerValue1 = 0x30,
380             CurrentTimerValue1 = 0x34,
381             TimerControl1 = 0x38,
382             TimerValue2 = 0x40,
383             CurrentTimerValue2 = 0x44,
384             TimerControl2 = 0x48,
385             TimerValue3 = 0x50,
386             CurrentTimerValue3 = 0x54,
387             TimerControl3 = 0x58,
388         }
389 
390         private enum TriggerModes
391         {
392             External,
393             Internal,
394         }
395     }
396 }
397