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