1 // 2 // Copyright (c) 2021 Zisis Adamos 3 // Copyright (c) 2010-2023 Antmicro 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 9 using System; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Time; 13 using Antmicro.Renode.Logging; 14 15 namespace Antmicro.Renode.Peripherals.Timers 16 { 17 public class STM32_IndependentWatchdog : BasicDoubleWordPeripheral, IKnownSize 18 { 19 //TODO: Stop timer on debug stop. 20 //TODO: Use RCC to set restart cause. STM32_IndependentWatchdog(IMachine machine, long frequency, bool windowOption = true, uint defaultPrescaler = 0)21 public STM32_IndependentWatchdog(IMachine machine, long frequency, bool windowOption = true, uint defaultPrescaler = 0) : base(machine) 22 { 23 watchdogTimer = new LimitTimer(machine.ClockSource, frequency, this, "STM32_IWDG", DefaultReloadValue, workMode: WorkMode.OneShot, enabled: false, eventEnabled: true, autoUpdate: true, divider: DefaultPrescalerValue); 24 watchdogTimer.LimitReached += TimerLimitReachedCallback; 25 this.defaultPrescaler = defaultPrescaler; 26 this.windowOption = windowOption; 27 DefineRegisters(); 28 Reset(); 29 } 30 Reset()31 public override void Reset() 32 { 33 base.Reset(); 34 watchdogTimer.Reset(); 35 registersUnlocked = false; 36 reloadValue = DefaultReloadValue; 37 window = DefaultWindow; 38 windowEnabled = false; 39 } 40 41 public long Size => 0x400; 42 DefineRegisters()43 private void DefineRegisters() 44 { 45 Registers.Key.Define(this) 46 .WithEnumField<DoubleWordRegister, Key>(0, 16, FieldMode.Write, writeCallback: (_, value) => 47 { 48 registersUnlocked = false; 49 switch(value) 50 { 51 case Key.Reload: 52 if(windowEnabled && watchdogTimer.Value > window) 53 { 54 this.Log(LogLevel.Warning, "Watchdog reloaded outside of window, triggering reset!"); 55 machine.RequestReset(); 56 } 57 else 58 { 59 Reload(); 60 } 61 break; 62 case Key.Start: 63 watchdogTimer.Enabled = true; 64 break; 65 case Key.Unlock: 66 registersUnlocked = true; 67 break; 68 } 69 }, name: "KEY") 70 .WithReservedBits(16, 16); 71 72 Registers.Prescaler.Define(this, defaultPrescaler) 73 .WithValueField(0, 3, writeCallback: (_, value) => 74 { 75 if(registersUnlocked) 76 { 77 var divider = (int)Math.Pow(2, (2 + value)); 78 if(divider > 256) 79 { 80 divider = 256; 81 } 82 watchdogTimer.Divider = divider; 83 } 84 else 85 { 86 this.Log(LogLevel.Warning, "Trying to change watchdog prescaler value without unlocking it"); 87 } 88 }, name: "PR") 89 .WithReservedBits(3, 29); 90 91 Registers.Reload.Define(this, DefaultReloadValue) 92 .WithValueField(0, 12, writeCallback: (_, value) => 93 { 94 if(registersUnlocked) 95 { 96 reloadValue = (uint)value; 97 } 98 else 99 { 100 this.Log(LogLevel.Warning, "Trying to change watchdog reload value without unlocking it"); 101 } 102 }, name: "RL") 103 .WithReservedBits(12, 20); 104 105 Registers.Status.Define(this) 106 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => false, name: "PVU") 107 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => false, name: "RVU") 108 .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => false, name: "WVU") 109 .WithReservedBits(3, 29); 110 111 if(windowOption) 112 { 113 Registers.Window.Define(this, DefaultWindow) 114 .WithValueField(0, 12, writeCallback: (_, value) => 115 { 116 if(registersUnlocked) 117 { 118 windowEnabled = true; 119 window = (uint)value; 120 Reload(); 121 } 122 else 123 { 124 this.Log(LogLevel.Warning, "Trying to change watchdog window without unlocking it"); 125 } 126 }, name: "WIN") 127 .WithReservedBits(12, 20); 128 } 129 } 130 Reload()131 private void Reload() 132 { 133 watchdogTimer.Limit = reloadValue; 134 } 135 TimerLimitReachedCallback()136 private void TimerLimitReachedCallback() 137 { 138 this.Log(LogLevel.Warning, "Watchdog reset triggered!"); 139 machine.RequestReset(); 140 } 141 142 private bool registersUnlocked; 143 private uint reloadValue; 144 private uint window; 145 private bool windowEnabled; 146 147 private readonly LimitTimer watchdogTimer; 148 private readonly uint defaultPrescaler; 149 private readonly bool windowOption; 150 151 private const uint DefaultReloadValue = 0xFFF; 152 private const uint DefaultWindow = 0xFFF; 153 private const int DefaultPrescalerValue = 4; 154 155 private enum Key 156 { 157 Unlock = 0x5555, 158 Reload = 0xAAAA, 159 Start = 0xCCCC 160 } 161 162 private enum Registers 163 { 164 Key = 0x0, 165 Prescaler = 0x4, 166 Reload = 0x8, 167 Status = 0xC, 168 Window = 0x10, 169 } 170 } 171 } 172