1 //
2 // Copyright (c) 2010-2025 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Structure.Registers;
10 using Antmicro.Renode.Exceptions;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 using Antmicro.Renode.Peripherals.Miscellaneous;
14 using Antmicro.Renode.Time;
15 
16 namespace Antmicro.Renode.Peripherals.Timers
17 {
18     public class SAM4S_WDT : LimitTimer, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize
19     {
SAM4S_WDT(IMachine machine, SAM4S_RSTC resetController, long slowClockFrequency = 32768)20         public SAM4S_WDT(IMachine machine, SAM4S_RSTC resetController, long slowClockFrequency = 32768)
21             : base(machine.ClockSource, slowClockFrequency, enabled: false, divider: 128, limit: MaximumWatchdogValue, workMode: WorkMode.Periodic, autoUpdate: true, eventEnabled: true)
22         {
23             if(resetController == null)
24             {
25                 throw new ConstructionException($"'{nameof(resetController)}' was null");
26             }
27 
28             RegistersCollection = new DoubleWordRegisterCollection(this);
29             this.resetController = resetController;
30             this.machine = machine;
31 
32             DefineRegisters();
33 
34             LimitReached += () =>
35             {
36                 underflowPending.Value = true;
37                 TriggerWatchdogFault();
38             };
39         }
40 
Reset()41         public override void Reset()
42         {
43             base.Reset();
44 
45             modeRegisterLocked = false;
46             RegistersCollection.Reset();
47             IRQ.Unset();
48         }
49 
ReadDoubleWord(long offset)50         public uint ReadDoubleWord(long offset)
51         {
52             return RegistersCollection.Read(offset);
53         }
54 
WriteDoubleWord(long offset, uint value)55         public void WriteDoubleWord(long offset, uint value)
56         {
57             if(offset == (uint)Registers.Mode && modeRegisterLocked)
58             {
59                 this.Log(LogLevel.Warning, "Tried to write WDT_MR register after it has been locked");
60                 return;
61             }
62 
63             RegistersCollection.Write(offset, value);
64         }
65 
66         public DoubleWordRegisterCollection RegistersCollection { get; }
67 
68         public long Size => 0x10;
69 
70         public GPIO IRQ { get; } = new GPIO();
71 
TriggerWatchdogFault()72         private void TriggerWatchdogFault()
73         {
74             if(interruptEnabled.Value)
75             {
76                 IRQ.Set();
77             }
78 
79             if(watchdogReset.Value)
80             {
81                 resetController.InvokeReset(SAM4S_RSTC.ResetType.WatchdogReset, resetProcessor: true, resetPeripherals: !resetOnlyProcessor.Value);
82             }
83         }
84 
DefineRegisters()85         private void DefineRegisters()
86         {
87             Registers.Control.Define(this)
88                 .WithFlag(0, out var resetPending, name: "WDRSTT")
89                 .WithReservedBits(1, 23)
90                 .WithValueField(24, 8, out var providedKey, name: "KEY")
91                 .WithWriteCallback((_, __) =>
92                 {
93                     if(providedKey.Value != WatchdogPassword)
94                     {
95                         this.Log(LogLevel.Debug, "Software tried to modify WDT_CR with invalid password: {0:X} instead of {1:X}",
96                             providedKey.Value, WatchdogPassword);
97                         return;
98                     }
99 
100                     if(machine.SystemBus.TryGetCurrentCPU(out var cpu))
101                     {
102                         cpu.SyncTime();
103                     }
104 
105                     if(Value > watchdogDelta.Value)
106                     {
107                         this.Log(LogLevel.Info, "Tried to refresh watchdog while not in correct interval: {0} > {1}", Value, watchdogDelta.Value);
108                         errorPending.Value = true;
109                         TriggerWatchdogFault();
110                         return;
111                     }
112 
113                     if(resetPending.Value)
114                     {
115                         Value = Limit;
116                     }
117 
118                     resetPending.Value = false;
119                 })
120             ;
121 
122             // NOTE: Only first write is accepted by this register,
123             //       after first write has been handled this register behaves as read-only.
124             Registers.Mode.Define(this, 0x3FFF2FFF)
125                 .WithValueField(0, 12, name: "WDV",
126                     valueProviderCallback: _ => Value,
127                     writeCallback: (_, value) => Limit = value)
128                 // NOTE: We don't have to update IRQ on interruptEnabled as this value
129                 //       can be only changed once
130                 .WithFlag(12, out interruptEnabled, name: "WDIFEN")
131                 .WithFlag(13, out watchdogReset, name: "WDRSTEN")
132                 .WithFlag(14, out resetOnlyProcessor, name: "WDRPROC")
133                 .WithFlag(15, name: "WDDIS",
134                     valueProviderCallback: _ => !Enabled,
135                     writeCallback: (_, value) => Enabled = !value)
136                 .WithValueField(16, 12, out watchdogDelta, name: "WDD")
137                 .WithTaggedFlag("WDDBGHLT", 28)
138                 .WithTaggedFlag("WDIDLEHLT", 29)
139                 .WithReservedBits(30, 2)
140                 .WithWriteCallback((_, __) => modeRegisterLocked = true)
141             ;
142 
143             Registers.Status.Define(this, 0x00000000)
144                 .WithFlag(0, out underflowPending, FieldMode.ReadToClear, name: "WDUNF")
145                 .WithFlag(1, out errorPending, FieldMode.ReadToClear, name: "WDERR")
146                 .WithReservedBits(2, 30)
147                 .WithReadCallback((_, __) =>
148                 {
149                     IRQ.Unset();
150                 })
151             ;
152         }
153 
154         private bool modeRegisterLocked;
155         private IValueRegisterField watchdogDelta;
156         private IFlagRegisterField interruptEnabled;
157         private IFlagRegisterField watchdogReset;
158         private IFlagRegisterField resetOnlyProcessor;
159         private IFlagRegisterField underflowPending;
160         private IFlagRegisterField errorPending;
161 
162         private readonly SAM4S_RSTC resetController;
163         private readonly IMachine machine;
164 
165         private const int MaximumWatchdogValue = 0xFFF;
166         private const int WatchdogPassword = 0xA5;
167 
168         public enum Registers
169         {
170             Control = 0x00,
171             Mode = 0x04,
172             Status = 0x08,
173         }
174     }
175 }
176