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