1 //
2 // Copyright (c) 2010-2022 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.Collections.Generic;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 using Antmicro.Renode.Peripherals.Miscellaneous;
14 
15 namespace Antmicro.Renode.Peripherals.Timers
16 {
17     public class NRF52840_Watchdog : LimitTimer, IDoubleWordPeripheral, IKnownSize, INRFEventProvider
18     {
NRF52840_Watchdog(IMachine machine)19         public NRF52840_Watchdog(IMachine machine) : base(machine.ClockSource, InitialFrequency, eventEnabled: true)
20         {
21             IRQ = new GPIO();
22 
23             this.machine = machine;
24             requestRegisterEnabled = new bool[NumberOfRegisters];
25             requestRegisterStatus = new IFlagRegisterField[NumberOfRegisters];
26 
27             LimitReached += TriggerReset;
28 
29             DefineRegisters();
30         }
31 
Reset()32         override public void Reset()
33         {
34             base.Reset();
35             registers.Reset();
36             IRQ.Unset();
37             readyToReset = false;
38             interruptEnabled = false;
39             for(var i = 0; i < NumberOfRegisters; i++)
40             {
41                 requestRegisterEnabled[i] = false;
42             }
43         }
44 
ReadDoubleWord(long offset)45         public uint ReadDoubleWord(long offset)
46         {
47             return registers.Read(offset);
48         }
49 
WriteDoubleWord(long offset, uint value)50         public void WriteDoubleWord(long offset, uint value)
51         {
52             registers.Write(offset, value);
53         }
54 
55         public GPIO IRQ { get; }
56 
57         public long Size => 0x1000;
58 
59         public event Action<uint> EventTriggered;
60 
DefineRegisters()61         private void DefineRegisters()
62         {
63             var registersMap = new Dictionary<long, DoubleWordRegister>
64             {
65                 {(long)Register.Start, new DoubleWordRegister(this)
66                     .WithFlag(0, FieldMode.Write, name: "TASK_START",
67                         writeCallback: (_, value) =>
68                         {
69                             if(value)
70                             {
71                                 Value = Limit + 1;
72                                 Enabled = true;
73                             }
74                         })
75                     .WithReservedBits(1, 31)
76                 },
77                 {(long)Register.Timeout, new DoubleWordRegister(this)
78                     .WithFlag(0, out eventTimeoutEnabled, name: "EVENTS_TIMEOUT", writeCallback: (_, value) =>
79                     {
80                         if(value && interruptEnabled)
81                         {
82                             IRQ.Set(true);
83                         }
84                     })
85                     .WithReservedBits(1, 31)
86                 },
87                 {(long)Register.InterruptSet, new DoubleWordRegister(this)
88                     .WithFlag(0, name: "INTENSET",
89                         writeCallback: (_, value) =>
90                         {
91                             if(value)
92                             {
93                                 interruptEnabled = true;
94                             }
95                         },
96                         valueProviderCallback: _ => interruptEnabled)
97                     .WithReservedBits(1, 31)
98                 },
99                 {(long)Register.InterruptClear, new DoubleWordRegister(this)
100                     .WithFlag(0, name: "INTENCLR",
101                         writeCallback: (_, value) =>
102                         {
103                             if(value)
104                             {
105                                 interruptEnabled = false;
106                             }
107                         },
108                         valueProviderCallback: _ => interruptEnabled)
109                     .WithReservedBits(1, 31)
110                 },
111                 {(long)Register.RunStatus, new DoubleWordRegister(this)
112                     .WithFlag(0, FieldMode.Read, name: "RUNSTATUS",
113                         valueProviderCallback: _ => Enabled)
114                     .WithReservedBits(1, 31)
115                 },
116                 {(long)Register.RequestStatus, new DoubleWordRegister(this, 0x1)
117                     .WithFlags(0, NumberOfRegisters, out requestRegisterStatus, FieldMode.Read, name: "REQSTATUS")
118                     .WithReservedBits(NumberOfRegisters, 32 - NumberOfRegisters)
119                 },
120                 {(long)Register.CounterReloadValue, new DoubleWordRegister(this, 0xFFFFFFFF)
121                     .WithValueField(0, 32, name: "CRV",
122                         changeCallback: (_, value) =>
123                         {
124                             if(Enabled)
125                             {
126                                 this.Log(LogLevel.Warning, "Tried to change CRV while watchdog is running, ignored");
127                                 return;
128                             }
129                             if(value < 0xf)
130                             {
131                                 this.Log(LogLevel.Warning, $"Tried to set CRV to illegal value ({value} < 15)");
132                                 return;
133                             }
134                             Limit = value;
135                         },
136                         valueProviderCallback: _ => (uint)Limit)
137                 },
138                 {(long)Register.EnableReloadRequest, new DoubleWordRegister(this, 0x1)
139                     .WithFlags(0, NumberOfRegisters, name: "RREN",
140                         writeCallback: (j, _, value) =>
141                         {
142                             if(Enabled)
143                             {
144                                 this.Log(LogLevel.Warning, $"Tried to write RREN while watchdog is running, ignored");
145                                 return;
146                             }
147                             requestRegisterEnabled[j] = value;
148                             requestRegisterStatus[j].Value = value;
149                         },
150                         valueProviderCallback: (j, _) => requestRegisterEnabled[j])
151                     .WithReservedBits(NumberOfRegisters, 32 - NumberOfRegisters)
152                 },
153                 {(long)Register.Config, new DoubleWordRegister(this)
154                     .WithFlag(0, name: "SLEEP")
155                     .WithReservedBits(1, 2)
156                     .WithFlag(3, name: "HALT")
157                     .WithReservedBits(4, 28)
158                     .WithWriteCallback((_, value) => this.Log(LogLevel.Warning, $"Write to a dummy implementation of the Config register, value: 0x{value:X}"))
159                     .WithReadCallback((_, value) => this.Log(LogLevel.Warning, $"Read from a dummy implementation of the Config register, returned: 0x{value:X}"))
160                 }
161             };
162 
163             for(var i = 0; i < NumberOfRegisters; i++)
164             {
165                 var j = i;
166                 registersMap.Add((long)Register.ReloadRequest1 + i * 0x4, new DoubleWordRegister(this)
167                     .WithValueField(0, 32, FieldMode.Write, name: $"RR{i}", writeCallback: (_, value) =>
168                     {
169                         if(value == ReloadRegisterResetValue)
170                         {
171                             requestRegisterStatus[j].Value = false;
172                             Reload();
173                         }
174                     })
175                 );
176             }
177 
178             registers = new DoubleWordRegisterCollection(this, registersMap);
179         }
180 
TriggerReset()181         private void TriggerReset()
182         {
183             if(!interruptEnabled || readyToReset)
184             {
185                 this.Log(LogLevel.Info, "Reseting machine");
186                 machine.RequestReset();
187                 return;
188             };
189 
190             this.Log(LogLevel.Info, "Timeout triggered with interrupt enabled, waiting 2 cycles to reset");
191             EventTriggered?.Invoke((uint)Register.Timeout);
192             eventTimeoutEnabled.Value = true;
193             IRQ.Set(true);
194 
195             readyToReset = true;
196             Value = 2;
197             Enabled = true;
198         }
199 
Reload()200         private void Reload()
201         {
202             if(readyToReset)
203             {
204                 // readyToReset flag is true, which means that we already timeouted, ignore reload
205                 this.Log(LogLevel.Warning, "Trying to reload after timeout, ignored");
206                 return;
207             }
208 
209             var reload = true;
210             for(var i = 0; i < NumberOfRegisters; i++)
211             {
212                 if(requestRegisterEnabled[i])
213                 {
214                     reload &= !requestRegisterStatus[i].Value;
215                 }
216             }
217 
218             if(reload)
219             {
220                 for(var i = 0; i < NumberOfRegisters; i++)
221                 {
222                     requestRegisterStatus[i].Value = requestRegisterEnabled[i];
223                 }
224                 Value = Limit + 1;
225                 this.NoisyLog("Counter reloaded");
226             }
227         }
228 
229         private DoubleWordRegisterCollection registers;
230         private IFlagRegisterField eventTimeoutEnabled;
231         private IFlagRegisterField[] requestRegisterStatus;
232         private bool[] requestRegisterEnabled;
233         private bool interruptEnabled;
234         private bool readyToReset;
235 
236         private readonly IMachine machine;
237 
238         private const int InitialFrequency = 32768;
239         private const int NumberOfRegisters = 8;
240         private const ulong ReloadRegisterResetValue = 0x6E524635;
241 
242         private enum Register : long
243         {
244             Start = 0x000,
245             Timeout = 0x100,
246 
247             InterruptSet = 0x304,
248             InterruptClear = 0x308,
249 
250             RunStatus = 0x400,
251             RequestStatus = 0x404,
252             CounterReloadValue = 0x504,
253             EnableReloadRequest = 0x508,
254             Config = 0x50C,
255 
256             ReloadRequest1 = 0x600,
257             ReloadRequest2 = 0x604,
258             ReloadRequest3 = 0x608,
259             ReloadRequest4 = 0x60C,
260             ReloadRequest5 = 0x610,
261             ReloadRequest6 = 0x614,
262             ReloadRequest7 = 0x618,
263             ReloadRequest8 = 0x61C
264         }
265     }
266 }
267