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.Logging; 11 12 namespace Antmicro.Renode.Peripherals.Timers 13 { 14 public class S32K3XX_SoftwareWatchdogTimer : BasicDoubleWordPeripheral, IKnownSize 15 { S32K3XX_SoftwareWatchdogTimer(IMachine machine)16 public S32K3XX_SoftwareWatchdogTimer(IMachine machine) : base(machine) 17 { 18 IRQ = new GPIO(); 19 20 // HACK: There is a bug in LimitTimer which requires to set limit argument to be bigger than any limit given later 21 internalTimer = new LimitTimer(machine.ClockSource, 32000 , this, "wdt", eventEnabled: true); 22 internalTimer.Limit = DefaultWatchdogTimeout; 23 internalTimer.LimitReached += HandleTimeout; 24 25 DefineRegisters(); 26 } 27 Reset()28 public override void Reset() 29 { 30 base.Reset(); 31 32 IRQ.Unset(); 33 34 previousServiceKeyWrite = 0; 35 pendingReset = false; 36 } 37 WriteDoubleWord(long offset, uint value)38 public override void WriteDoubleWord(long offset, uint value) 39 { 40 if(!registerSoftLock.Value) 41 { 42 base.WriteDoubleWord(offset, value); 43 return; 44 } 45 46 switch((Registers)offset) 47 { 48 case Registers.Control: 49 case Registers.Timeout: 50 case Registers.Window: 51 case Registers.ServiceKey: 52 this.Log(LogLevel.Warning, "Tried to write {0} while in soft lock", (Registers)offset); 53 return; 54 default: 55 base.WriteDoubleWord(offset, value); 56 break; 57 } 58 } 59 60 public long Size => 0x4000; 61 public GPIO IRQ { get; } 62 63 public ushort ServiceKey 64 { 65 get => (ushort)serviceKey.Value; 66 private set => serviceKey.Value = value; 67 } 68 69 public ushort NextServiceKey => (ushort)(ServiceKey * 17 + 3); 70 HandleTimeout()71 private void HandleTimeout() 72 { 73 if(!generateInterrupt.Value || pendingReset) 74 { 75 machine.RequestReset(); 76 return; 77 } 78 79 pendingReset = true; 80 interruptPending.Value = true; 81 UpdateInterrupt(); 82 } 83 UpdateInterrupt()84 private void UpdateInterrupt() 85 { 86 IRQ.Set(interruptPending.Value); 87 } 88 HandleServiceKey(ushort value)89 private void HandleServiceKey(ushort value) 90 { 91 var keyGiven = new Tuple<ushort, ushort>(previousServiceKeyWrite, value); 92 previousServiceKeyWrite = value; 93 94 if(windowMode.Value && windowStartValue.Value > internalTimer.Value) 95 { 96 this.Log(LogLevel.Warning, "Tried to write service key while window mode is enabled and before window period"); 97 if(resetOnInvalidAccess.Value) 98 { 99 machine.RequestReset(); 100 } 101 return; 102 } 103 104 if(keyGiven.Equals(softLockSequenceServiceKey)) 105 { 106 registerSoftLock.Value = false; 107 // NOTE: Soft lock sequence can still be valid keyed sequence for servicing watchdog 108 // thus we allow to pass-through 109 } 110 111 var resetCondition = false; 112 switch(serviceMode.Value) 113 { 114 case ServiceMode.FixedSequence: 115 if(keyGiven.Equals(fixedSequenceServiceKey)) 116 { 117 resetCondition = true; 118 } 119 break; 120 121 case ServiceMode.KeyedSequence: 122 if(keyGiven.Equals(KeyedSequenceServiceKey)) 123 { 124 resetCondition = true; 125 } 126 if(value == NextServiceKey) 127 { 128 ServiceKey = NextServiceKey; 129 } 130 break; 131 } 132 133 if(resetCondition) 134 { 135 internalTimer.Value = internalTimer.Limit; 136 pendingReset = false; 137 } 138 } 139 DefineRegisters()140 private void DefineRegisters() 141 { 142 Registers.Control.Define(this, 0xFF00010A) 143 .WithFlag(0, name: "WatchdogEnable", 144 valueProviderCallback: _ => internalTimer.Enabled, 145 changeCallback: (_, value) => 146 { 147 if(value) 148 { 149 internalTimer.Value = internalTimer.Limit; 150 } 151 internalTimer.Enabled = value; 152 }) 153 .WithTaggedFlag("DebugModeControl", 1) 154 .WithTaggedFlag("StopModeControl", 2) 155 .WithReservedBits(3, 1) 156 .WithFlag(4, out registerSoftLock, name: "SoftLock") 157 .WithTaggedFlag("HardLock", 5) 158 .WithFlag(6, out generateInterrupt, name: "InterruptThenResetRequest") 159 .WithFlag(7, out windowMode, name: "WindowMode") 160 .WithFlag(8, out resetOnInvalidAccess, name: "ResetOnInvalidAccess") 161 .WithEnumField(9, 2, out serviceMode, name: "ServiceMode") 162 .WithReservedBits(11, 13) 163 .WithTaggedFlag("MAP7", 24) 164 .WithTaggedFlag("MAP6", 25) 165 .WithTaggedFlag("MAP5", 26) 166 .WithTaggedFlag("MAP4", 27) 167 .WithTaggedFlag("MAP3", 28) 168 .WithTaggedFlag("MAP2", 29) 169 .WithTaggedFlag("MAP1", 30) 170 .WithTaggedFlag("MAP0", 31) 171 ; 172 173 Registers.Interrupt.Define(this) 174 .WithFlag(0, out interruptPending, FieldMode.WriteOneToClear | FieldMode.Read, name: "TimeoutInterruptFlag") 175 .WithReservedBits(1, 31) 176 .WithChangeCallback((_, __) => UpdateInterrupt()) 177 ; 178 179 Registers.Timeout.Define(this, DefaultWatchdogTimeout) 180 .WithValueField(0, 32, name: "WatchdogTimeout", 181 valueProviderCallback: _ => internalTimer.Limit, 182 writeCallback: (_, value) => internalTimer.Limit = Math.Max(MinimalWatchdogTimeout, value)) 183 ; 184 185 Registers.Window.Define(this) 186 .WithValueField(0, 32, out windowStartValue, name: "WindowStartValue") 187 ; 188 189 Registers.Service.Define(this) 190 .WithValueField(0, 16, name: "WatchdogServiceCode", 191 valueProviderCallback: _ => 0, 192 writeCallback: (_, value) => HandleServiceKey((ushort)value)) 193 .WithReservedBits(16, 16) 194 ; 195 196 Registers.CounterOutput.Define(this) 197 .WithValueField(0, 32, FieldMode.Read, name: "WatchdogCount", 198 valueProviderCallback: _ => internalTimer.Value) 199 ; 200 201 Registers.ServiceKey.Define(this) 202 .WithValueField(0, 16, out serviceKey, name: "ServiceKey") 203 .WithReservedBits(16, 16) 204 ; 205 206 Registers.EventRequest.Define(this) 207 .WithFlag(0, out requestInternalReset, name: "ResetRequestFlag") 208 .WithReservedBits(1, 31) 209 ; 210 } 211 212 private Tuple<ushort, ushort> KeyedSequenceServiceKey => 213 new Tuple<ushort, ushort>(ServiceKey, NextServiceKey); 214 215 private readonly LimitTimer internalTimer; 216 private readonly Tuple<ushort, ushort> fixedSequenceServiceKey = 217 new Tuple<ushort, ushort>(0xA602, 0xB480); 218 private readonly Tuple<ushort, ushort> softLockSequenceServiceKey = 219 new Tuple<ushort, ushort>(0xC520, 0xD928); 220 221 private const int MinimalWatchdogTimeout = 3; 222 private const uint DefaultWatchdogTimeout = 0x320; 223 224 private IFlagRegisterField generateInterrupt; 225 private IFlagRegisterField windowMode; 226 private IFlagRegisterField interruptPending; 227 private IFlagRegisterField requestInternalReset; 228 private IFlagRegisterField resetOnInvalidAccess; 229 private IFlagRegisterField registerSoftLock; 230 231 private IEnumRegisterField<ServiceMode> serviceMode; 232 private IValueRegisterField serviceKey; 233 private IValueRegisterField windowStartValue; 234 235 private ushort previousServiceKeyWrite; 236 private bool pendingReset; 237 238 private enum ServiceMode 239 { 240 FixedSequence, 241 KeyedSequence 242 } 243 244 private enum Registers 245 { 246 Control = 0x0, // CR 247 Interrupt = 0x4, // IR 248 Timeout = 0x8, // TO 249 Window = 0xC, // WN 250 Service = 0x10, // SR 251 CounterOutput = 0x14, // CO 252 ServiceKey = 0x18, // SK 253 EventRequest = 0x1C // RRR 254 } 255 } 256 } 257