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