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.Peripherals.Miscellaneous;
11 
12 namespace Antmicro.Renode.Peripherals.Timers
13 {
14     public class MAX32650_WDT : BasicDoubleWordPeripheral, IKnownSize
15     {
MAX32650_WDT(IMachine machine, MAX32650_GCR gcr)16         public MAX32650_WDT(IMachine machine, MAX32650_GCR gcr) : base(machine)
17         {
18             IRQ = new GPIO();
19 
20             interruptTimer = new LimitTimer(machine.ClockSource, gcr.SysClk / 2, this, "interrupt_timer", InitialLimit, eventEnabled: true);
21             interruptTimer.LimitReached += () =>
22             {
23                 interruptPending.Value = true;
24                 UpdateInterrupts();
25             };
26 
27             resetTimer = new LimitTimer(machine.ClockSource, gcr.SysClk / 2, this, "reset_timer", InitialLimit, eventEnabled: true);
28             resetTimer.LimitReached += () =>
29             {
30                 if(BeforeReset?.Invoke() ?? false)
31                 {
32                     return;
33                 }
34 
35                 systemReset = true;
36                 machine.RequestReset();
37             };
38 
39             gcr.SysClkChanged += (newFrequency) =>
40             {
41                 interruptTimer.Frequency = newFrequency / 2;
42                 resetTimer.Frequency = newFrequency / 2;
43             };
44 
45             DefineRegisters();
46         }
47 
Reset()48         public override void Reset()
49         {
50             base.Reset();
51             interruptTimer.Reset();
52             resetTimer.Reset();
53             IRQ.Unset();
54 
55             resetSequence = ResetSequence.WaitForFirstByte;
56 
57             // We are intentionally not clearing systemReset variable
58             // as it should persist after watchdog-triggered reset.
59         }
60 
61         public long Size => 0x400;
62 
63         public GPIO IRQ { get; }
64 
65         public Func<bool> BeforeReset { get; set; }
66 
UpdateInterrupts()67         private void UpdateInterrupts()
68         {
69             IRQ.Set(interruptTimer.EventEnabled && interruptPending.Value);
70         }
71 
DefineRegisters()72         private void DefineRegisters()
73         {
74             Registers.Control.Define(this)
75                 .WithValueField(0, 4, name: "CTRL.int_period",
76                     changeCallback: (_, value) =>
77                     {
78                         interruptTimer.Limit = 1UL << (31 - (int)value);
79                     })
80                 .WithValueField(4, 4, name: "CTRL.rst_period",
81                     changeCallback: (_, value) =>
82                     {
83                         resetTimer.Limit = 1UL << (31 - (int)value);
84                     })
85                 .WithFlag(8, name: "CTRL.wdt_en",
86                     writeCallback: (_, value) =>
87                     {
88                         interruptTimer.Enabled = value;
89                         resetTimer.Enabled = value;
90                     })
91                 .WithFlag(9, out interruptPending, FieldMode.WriteOneToClear, name: "CTRL.int_flag",
92                     writeCallback: (_, __) => UpdateInterrupts())
93                 .WithFlag(10, name: "CTRL.int_en",
94                     valueProviderCallback: _ => interruptTimer.EventEnabled,
95                     writeCallback: (_, value) =>
96                     {
97                         interruptTimer.EventEnabled = value;
98                         UpdateInterrupts();
99                     })
100                 .WithFlag(11, name: "CTRL.rst_en",
101                     valueProviderCallback: _ => resetTimer.EventEnabled,
102                     changeCallback: (_, value) => resetTimer.EventEnabled = value)
103                 .WithReservedBits(12, 19)
104                 .WithFlag(31, name: "CTRL.rst_flag",
105                     valueProviderCallback: _ => systemReset,
106                     changeCallback: (_, value) => systemReset = value)
107             ;
108 
109             Registers.Reset.Define(this)
110                 .WithValueField(0, 8, name: "RST.wdt_rst",
111                     writeCallback: (_, value) =>
112                     {
113                         if(resetSequence == ResetSequence.WaitForFirstByte && value == FirstResetByte)
114                         {
115                             resetSequence = ResetSequence.WaitForSecondByte;
116                         }
117                         else if(resetSequence == ResetSequence.WaitForSecondByte && value == SecondResetByte)
118                         {
119                             resetSequence = ResetSequence.WaitForFirstByte;
120                             interruptTimer.Value = interruptTimer.Limit;
121                             resetTimer.Value = resetTimer.Limit;
122                         }
123                         else
124                         {
125                             resetSequence = ResetSequence.WaitForFirstByte;
126                         }
127                     })
128                 .WithReservedBits(8, 24)
129             ;
130         }
131 
132         private ResetSequence resetSequence;
133         private bool systemReset;
134 
135         private IFlagRegisterField interruptPending;
136 
137         private readonly LimitTimer interruptTimer;
138         private readonly LimitTimer resetTimer;
139 
140         private const ulong InitialLimit = (1UL << 31);
141         private const byte FirstResetByte = 0xA5;
142         private const byte SecondResetByte = 0x5A;
143 
144         private enum ResetSequence
145         {
146             WaitForFirstByte,
147             WaitForSecondByte
148         }
149 
150         private enum Registers
151         {
152             Control = 0x00,
153             Reset = 0x04,
154         }
155     }
156 }
157