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