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 Antmicro.Renode.Core; 9 using Antmicro.Renode.Core.Structure.Registers; 10 using Antmicro.Renode.Time; 11 using Antmicro.Renode.Logging; 12 13 namespace Antmicro.Renode.Peripherals.Timers 14 { 15 public class S32K3XX_RealTimeClock : BasicDoubleWordPeripheral, IKnownSize 16 { S32K3XX_RealTimeClock(IMachine machine, long externalFastCrystalOscillatorFrequency = 2097152, long externalSlowCrystalOscillatorFrequency = 32768)17 public S32K3XX_RealTimeClock(IMachine machine, long externalFastCrystalOscillatorFrequency = 2097152, long externalSlowCrystalOscillatorFrequency = 32768) : base(machine) 18 { 19 this.externalFastCrystalOscillatorFrequency = externalFastCrystalOscillatorFrequency; 20 this.externalSlowCrystalOscillatorFrequency = externalSlowCrystalOscillatorFrequency; 21 22 IRQ = new GPIO(); 23 24 DefineRegisters(); 25 26 internalClock = new InternalClock(machine.ClockSource, this, GetClockFrequency()); 27 internalClock.OnOverflowInterrupt += () => 28 { 29 rolloverInterruptPending.Value = true; 30 UpdateInterrupts(); 31 }; 32 internalClock.OnAPIInterrupt += () => 33 { 34 apiInterruptPending.Value = true; 35 UpdateInterrupts(); 36 }; 37 internalClock.OnRTCInterrupt += () => 38 { 39 rtcInterruptPending.Value = true; 40 UpdateInterrupts(); 41 }; 42 } 43 UpdateInterrupts()44 private void UpdateInterrupts() 45 { 46 var interrupt = false; 47 48 interrupt |= rtcInterruptEnabled.Value && rtcInterruptPending.Value; 49 interrupt |= apiInterruptEnabled.Value && apiInterruptPending.Value; 50 interrupt |= rolloverInterruptEnabled.Value && rolloverInterruptPending.Value; 51 52 IRQ.Set(interrupt); 53 } 54 Reset()55 public override void Reset() 56 { 57 base.Reset(); 58 IRQ.Unset(); 59 60 clockDividerFlags = ClockDivider.None; 61 } 62 63 public long Size => 0x4000; 64 public GPIO IRQ { get; } 65 SetClockDivider(bool state, ClockDivider divideBy)66 private void SetClockDivider(bool state, ClockDivider divideBy) 67 { 68 if(internalClock.Enabled) 69 { 70 this.Log(LogLevel.Warning, "Trying to change clock divider when counter is enabled. Operation ignored"); 71 return; 72 } 73 74 if(state) 75 { 76 clockDividerFlags |= divideBy; 77 } 78 else 79 { 80 clockDividerFlags &= ~divideBy; 81 } 82 } 83 GetClockFrequency()84 private long GetClockFrequency() 85 { 86 long clockFrequency; 87 switch(clockSource.Value) 88 { 89 case ClockSource.SXOSC: 90 clockFrequency = this.externalSlowCrystalOscillatorFrequency; 91 break; 92 case ClockSource.SIRC: 93 clockFrequency = 32000; // 32kHz 94 break; 95 case ClockSource.FIRC: 96 clockFrequency = 48000000; // 48MHz 97 break; 98 case ClockSource.FXOSC: 99 clockFrequency = this.externalFastCrystalOscillatorFrequency; 100 break; 101 default: 102 throw new Exception("unreachable code"); 103 } 104 105 if(clockDividerFlags.HasFlag(ClockDivider.DivideBy32)) 106 { 107 clockFrequency >>= 5; 108 } 109 110 if(clockDividerFlags.HasFlag(ClockDivider.DivideBy512)) 111 { 112 clockFrequency >>= 9; 113 } 114 115 return clockFrequency; 116 } 117 DefineRegisters()118 private void DefineRegisters() 119 { 120 Registers.RTCSupervisorControl.Define(this, 0x80000000) 121 .WithReservedBits(0, 31) 122 .WithTaggedFlag("RTCSupervisorBit", 31) 123 ; 124 125 Registers.RTCControl.Define(this) 126 .WithTaggedFlag("TriggerEnableForAnalogComparator", 0) 127 .WithReservedBits(1, 9) 128 .WithFlag(10, name: "DivideBy32enable", 129 valueProviderCallback: _ => clockDividerFlags.HasFlag(ClockDivider.DivideBy32), 130 changeCallback: (_, value) => SetClockDivider(value, ClockDivider.DivideBy32)) 131 .WithFlag(11, name: "DivideBy512enable", 132 valueProviderCallback: _ => clockDividerFlags.HasFlag(ClockDivider.DivideBy512), 133 changeCallback: (_, value) => SetClockDivider(value, ClockDivider.DivideBy512)) 134 .WithEnumField(12, 2, out clockSource, name: "ClockSelect") 135 // NOTE: This flag enables writes to APIInterruptFlag field in RTCStatus 136 .WithFlag(14, name: "APIInterruptEnable", 137 valueProviderCallback: _ => internalClock.APIInterruptEnabled, 138 changeCallback: (_, value) => internalClock.APIInterruptEnabled = value) 139 // NOTE: This flag 'connects' APIInterruptFlag to IRQ output 140 .WithFlag(15, out apiInterruptEnabled, name: "AutonomousPeriodicInterruptEnable") 141 .WithReservedBits(16, 12) 142 .WithFlag(28, out rolloverInterruptEnabled, name: "CounterRollOverInterruptEnable") 143 .WithTaggedFlag("FreezeEnableBit", 29) 144 .WithFlag(30, out rtcInterruptEnabled, name: "RTCInterruptEnable") 145 .WithFlag(31, name: "CounterEnable", 146 valueProviderCallback: _ => internalClock.Enabled, 147 changeCallback: (_, value) => internalClock.Enabled = value) 148 .WithChangeCallback((_, __) => 149 { 150 UpdateInterrupts(); 151 internalClock.Frequency = GetClockFrequency(); 152 }) 153 ; 154 155 Registers.RTCStatus.Define(this) 156 .WithReservedBits(0, 9) 157 .WithFlag(10, out rolloverInterruptPending, FieldMode.WriteOneToClear | FieldMode.Read, name: "CounterRollOverInterruptFlag") 158 .WithReservedBits(11, 2) 159 .WithFlag(13, out apiInterruptPending, FieldMode.WriteOneToClear | FieldMode.Read, name: "APIInterruptFlag") 160 .WithReservedBits(14, 2) 161 .WithReservedBits(16, 1) 162 .WithTaggedFlag("InvalidAPIVALWrite", 17) 163 .WithTaggedFlag("InvalidRTCWrite", 18) 164 .WithReservedBits(19, 10) 165 .WithFlag(29, out rtcInterruptPending, FieldMode.WriteOneToClear | FieldMode.Read, name: "RTCInterruptFlag") 166 .WithReservedBits(30, 2) 167 .WithChangeCallback((_, __) => UpdateInterrupts()) 168 ; 169 170 Registers.RTCCounter.Define(this) 171 .WithValueField(0, 32, FieldMode.Read, name: "RTCCounterValue", 172 valueProviderCallback: _ => internalClock.Value) 173 ; 174 175 Registers.APICompareValue.Define(this) 176 .WithValueField(0, 32, name: "APICompareValue", 177 valueProviderCallback: _ => internalClock.APICompareValue, 178 writeCallback: (_, value) => internalClock.APICompareValue = value) 179 ; 180 181 Registers.RTCCompareValue.Define(this) 182 .WithValueField(0, 32, name: "RTCCompareValue", 183 valueProviderCallback: _ => internalClock.RTCCompareValue, 184 writeCallback: (_, value) => internalClock.RTCCompareValue = value) 185 ; 186 } 187 188 private readonly InternalClock internalClock; 189 private readonly long externalSlowCrystalOscillatorFrequency; 190 private readonly long externalFastCrystalOscillatorFrequency; 191 192 private ClockDivider clockDividerFlags; 193 194 private IEnumRegisterField<ClockSource> clockSource; 195 196 private IFlagRegisterField rtcInterruptEnabled; 197 private IFlagRegisterField apiInterruptEnabled; 198 private IFlagRegisterField rolloverInterruptEnabled; 199 200 private IFlagRegisterField rtcInterruptPending; 201 private IFlagRegisterField apiInterruptPending; 202 private IFlagRegisterField rolloverInterruptPending; 203 204 private class InternalClock 205 { InternalClock(IClockSource clockSource, IPeripheral parent, long frequency)206 public InternalClock(IClockSource clockSource, IPeripheral parent, long frequency) 207 { 208 mainClock = new LimitTimer(clockSource, frequency, parent, "main_clk", limit: uint.MaxValue, direction: Direction.Ascending, eventEnabled: true); 209 mainClock.LimitReached += HandleOverflow; 210 211 apiInterruptClock = new LimitTimer(clockSource, frequency, parent, "api_int_clk", limit: uint.MaxValue, direction: Direction.Ascending, eventEnabled: false); 212 apiInterruptClock.LimitReached += OnAPIInterrupt; 213 214 rtcInterruptClock = new LimitTimer(clockSource, frequency, parent, "rtc_int_clk", limit: uint.MaxValue, direction: Direction.Ascending, eventEnabled: false); 215 rtcInterruptClock.LimitReached += OnRTCInterrupt; 216 } 217 HandleOverflow()218 private void HandleOverflow() 219 { 220 if(APIInterruptEnabled) 221 { 222 apiInterruptClock.Value = 0; 223 apiInterruptClock.Enabled = true; 224 } 225 226 if(RTCInterruptEnabled) 227 { 228 rtcInterruptClock.Value = 0; 229 rtcInterruptClock.Enabled = true; 230 } 231 232 OnOverflowInterrupt?.Invoke(); 233 } 234 235 public ulong Value => mainClock.Value; 236 237 public long Frequency 238 { 239 get => mainClock.Frequency; 240 set 241 { 242 if(value == mainClock.Frequency) 243 { 244 return; 245 } 246 247 mainClock.Frequency = value; 248 apiInterruptClock.Frequency = value; 249 rtcInterruptClock.Frequency = value; 250 } 251 } 252 253 public bool AutonomusPeriodicInterruptEnable 254 { 255 get => apiInterruptClock.Enabled; 256 } 257 258 public ulong APICompareValue 259 { 260 get => apiInterruptClock.Limit; 261 set => apiInterruptClock.Limit = value; 262 } 263 264 public ulong RTCCompareValue 265 { 266 get => rtcInterruptClock.Limit; 267 set => rtcInterruptClock.Limit = value; 268 } 269 270 public bool Enabled 271 { 272 get => mainClock.Enabled; 273 set 274 { 275 mainClock.Enabled = value; 276 if(!value) 277 { 278 apiInterruptClock.Enabled = false; 279 rtcInterruptClock.Enabled = false; 280 } 281 } 282 } 283 284 public bool APIInterruptEnabled 285 { 286 get => apiInterruptClock.EventEnabled; 287 set 288 { 289 apiInterruptClock.EventEnabled = value; 290 if(value && APICompareValue > Value) 291 { 292 apiInterruptClock.Value = Value; 293 apiInterruptClock.Enabled = true; 294 } 295 } 296 } 297 298 public bool RTCInterruptEnabled 299 { 300 get => rtcInterruptClock.EventEnabled; 301 set 302 { 303 rtcInterruptClock.EventEnabled = value; 304 if(value && RTCCompareValue > Value) 305 { 306 rtcInterruptClock.Value = Value; 307 rtcInterruptClock.Enabled = true; 308 } 309 } 310 } 311 312 public event Action OnOverflowInterrupt; 313 public event Action OnRTCInterrupt; 314 public event Action OnAPIInterrupt; 315 316 private readonly LimitTimer mainClock; 317 private readonly LimitTimer apiInterruptClock; 318 private readonly LimitTimer rtcInterruptClock; 319 } 320 321 [Flags] 322 private enum ClockDivider 323 { 324 None = (0 << 0), 325 DivideBy32 = (1 << 0), 326 DivideBy512 = (1 << 1), 327 } 328 329 private enum ClockSource 330 { 331 SXOSC, // NOTE: Not available on: S32K311; instead SIRC will be used 332 SIRC, 333 FIRC, 334 FXOSC, 335 } 336 337 private enum Registers 338 { 339 RTCSupervisorControl = 0x0, // RTCSUPV 340 RTCControl = 0x4, // RTCC 341 RTCStatus = 0x8, // RTCS 342 RTCCounter = 0xC, // RTCCNT 343 APICompareValue = 0x10, // APIVAL 344 RTCCompareValue = 0x14 // RTCVAL 345 } 346 } 347 } 348