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 
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 
13 namespace Antmicro.Renode.Peripherals.Timers
14 {
15     public class Cadence_WDT : BasicDoubleWordPeripheral, IKnownSize
16     {
Cadence_WDT(IMachine machine, long frequency)17         public Cadence_WDT(IMachine machine, long frequency) : base(machine)
18         {
19             IRQ = new GPIO();
20 
21             watchdogTimer = new LimitTimer(machine.ClockSource, frequency, this, "watchdog", enabled: false, eventEnabled: true);
22             watchdogTimer.LimitReached += () =>
23             {
24                 this.Log(LogLevel.Noisy, "Limit reached");
25                 if(interruptRequestEnable.Value)
26                 {
27                     IRQ.Blink();
28                 }
29 
30                 if(resetEnable.Value)
31                 {
32                     this.Log(LogLevel.Info, "Watchdog timed out. Resetting...");
33                     machine.RequestReset();
34                 }
35                 LimitReached?.Invoke(this);
36             };
37             DefineRegisters();
38         }
39 
Reset()40         public override void Reset()
41         {
42             base.Reset();
43             watchdogTimer.Reset();
44         }
45 
46         public GPIO IRQ { get; }
47         public long Size => 0x1000;
48 
49         public event Action<Cadence_WDT> LimitReached;
50 
DefineRegisters()51         private void DefineRegisters()
52         {
53             Register.ZeroMode.Define(this, resetValue: 0x1C3)
54                 .WithFlag(0, out watchdogEnable, writeCallback: (oldVal, newVal) =>
55                 {
56                     if(zeroAccessKey.Value != ZeroAccessKey)
57                     {
58                         watchdogEnable.Value = oldVal;
59                         return;
60                     }
61                     watchdogTimer.Enabled = newVal;
62                 }, name: "WDEN")
63                 .WithFlag(1, out resetEnable, writeCallback: (oldVal, newVal) =>
64                 {
65                     if(zeroAccessKey.Value != ZeroAccessKey)
66                     {
67                         resetEnable.Value = oldVal;
68                         return;
69                     }
70                 }, name: "RSTEN")
71                 .WithFlag(2, out interruptRequestEnable, writeCallback: (oldVal, newVal) =>
72                 {
73                     if(zeroAccessKey.Value != ZeroAccessKey)
74                     {
75                         interruptRequestEnable.Value = oldVal;
76                         return;
77                     }
78                 }, name: "IRQEN")
79                 .WithTaggedFlag("EXTEN", 3)
80                 .WithTag("RSTLN", 4, 3)
81                 .WithTag("IRQLN", 7, 2)
82                 .WithTag("EXLN", 9, 3)
83                 .WithValueField(12, 12, out zeroAccessKey, FieldMode.Write, name: "ZKEY")
84                 .WithReservedBits(24, 8)
85                 .WithWriteCallback((_, __) =>
86                 {
87                     if(zeroAccessKey.Value != ZeroAccessKey)
88                     {
89                         this.Log(LogLevel.Warning, "Write to the register is invalid because of the wrong access key");
90                     }
91                 });
92 
93             Register.CounterControl.Define(this, resetValue: 0b111100)
94                 .WithValueField(0, 2, out counterClockPrescale, writeCallback: (oldVal, newVal) =>
95                 {
96                     if(counterAccessKey.Value != CounterAccessKey)
97                     {
98                         counterClockPrescale.Value = oldVal;
99                         return;
100                     }
101                     watchdogTimer.Divider = 1 << (int)(3 * (newVal + 1));
102                 }, name: "CLKSEL")
103                 .WithValueField(2, 12, out counterRestartValue, writeCallback: (oldVal, newVal) =>
104                 {
105                     if(counterAccessKey.Value != CounterAccessKey)
106                     {
107                         counterRestartValue.Value = oldVal;
108                         return;
109                     }
110                 }, name: "CRV")
111                 .WithValueField(14, 12, out counterAccessKey, FieldMode.Write, name: "CKEY")
112                 .WithReservedBits(26, 6)
113                 .WithWriteCallback((_, __) =>
114                 {
115                     if(counterAccessKey.Value != CounterAccessKey)
116                     {
117                         this.Log(LogLevel.Warning, "Write to the register is invalid because of the wrong access key");
118                     }
119                 });
120 
121             Register.Restart.Define(this)
122                 .WithValueField(0, 16, FieldMode.Write, writeCallback: (_, val) =>
123                 {
124                     if(val != RestartKey)
125                     {
126                         this.Log(LogLevel.Warning, "Write to the register is invalid because of the wrong access key");
127                         return;
128                     }
129                     watchdogTimer.Value = (counterRestartValue.Value << 12) | 0xFFF;
130                 })
131                 .WithReservedBits(16, 16);
132 
133             Register.Status.Define(this)
134                 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => WatchdogZero)
135                 .WithReservedBits(1, 31);
136         }
137 
138         private bool WatchdogZero => watchdogTimer.Value == watchdogTimer.Limit;
139 
140         private readonly LimitTimer watchdogTimer;
141 
142         private IFlagRegisterField watchdogEnable;
143         private IFlagRegisterField resetEnable;
144         private IFlagRegisterField interruptRequestEnable;
145         private IValueRegisterField zeroAccessKey;
146         private IValueRegisterField counterClockPrescale;
147         private IValueRegisterField counterRestartValue;
148         private IValueRegisterField counterAccessKey;
149 
150         private const ushort ZeroAccessKey = 0xABC;
151         private const ushort CounterAccessKey = 0x248;
152         private const ushort RestartKey = 0x1999;
153 
154         private enum Register
155         {
156             ZeroMode = 0x0,
157             CounterControl = 0x4,
158             Restart = 0x8,
159             Status = 0xC,
160         }
161     }
162 }
163