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