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 using System.Collections.Generic;
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Structure.Registers;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Time;
13 using TimeDirection = Antmicro.Renode.Time.Direction;
14 
15 namespace Antmicro.Renode.Peripherals.Timers
16 {
17     public class RenesasRZG_Watchdog : IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize
18     {
RenesasRZG_Watchdog(IMachine machine, long clockFrequency)19         public RenesasRZG_Watchdog(IMachine machine, long clockFrequency)
20         {
21             this.machine = machine;
22 
23             IRQ = new GPIO();
24 
25             this.timer = new LimitTimer(machine.ClockSource, clockFrequency, this, "Watchdog Timer",
26                 DefaultCycle, TimeDirection.Ascending, workMode: WorkMode.Periodic, eventEnabled: true
27             );
28             this.timer.LimitReached += OnLimitReached;
29 
30             RegistersCollection = new DoubleWordRegisterCollection(this, BuildRegisterMap());
31 
32             Reset();
33         }
34 
Reset()35         public void Reset()
36         {
37             RegistersCollection.Reset();
38             IRQ.Unset();
39             timer.Reset();
40             forceStop = false;
41             timerEnabled = false;
42             SystemResetEnabled = false;
43             if(!keepGeneratedResetValue)
44             {
45                 generatedReset = false;
46             }
47             keepGeneratedResetValue = false;
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             RegistersCollection.Write(offset, value);
58         }
59 
60         public GPIO IRQ { get; }
61         public long Size => 0x400;
62         public DoubleWordRegisterCollection RegistersCollection { get; }
63         public bool ForceStop
64         {
65             get => forceStop;
66             set
67             {
68                 forceStop = value;
69                 UpdateTimerStatus();
70             }
71         }
72         public bool SystemResetEnabled { get; set; } = false;
73         public bool GeneratedReset
74         {
75             get => generatedReset;
76             set
77             {
78                 // Only allow to reset the flag, not set it
79                 if(!value)
80                 {
81                     generatedReset = false;
82                 }
83             }
84         }
85 
OnLimitReached()86         private void OnLimitReached()
87         {
88             if(IRQ.IsSet && SystemResetEnabled)
89             {
90                 keepGeneratedResetValue = true;
91                 generatedReset = true;
92                 machine.RequestReset();
93             }
94             else
95             {
96                 IRQ.Set();
97             }
98         }
99 
BuildRegisterMap()100         private Dictionary<long, DoubleWordRegister> BuildRegisterMap()
101         {
102             var registerMap = new Dictionary<long, DoubleWordRegister>
103             {
104                 {(long)Registers.Control, new DoubleWordRegister(this)
105                     .WithFlag(0, name: "WDTEN",
106                         valueProviderCallback: _ => timerEnabled,
107                         writeCallback: (_, value) =>
108                         {
109                             timerEnabled = value;
110                             UpdateTimerStatus();
111                         }
112                     )
113                     .WithReservedBits(1, 31)
114                 },
115                 {(long)Registers.PeriodSetting, new DoubleWordRegister(this)
116                     .WithReservedBits(0, 20)
117                     .WithValueField(20, 12, name: "WDTTIME",
118                         valueProviderCallback: _ => (timer.Limit >> 20) - 1,
119                         writeCallback: (_, value) =>
120                         {
121                             if(timer.Enabled)
122                             {
123                                 this.Log(LogLevel.Warning, "Setting WDTTIME while the timer is running. Ignoring");
124                                 return;
125                             }
126                             timer.Limit = (value + 1) << 20;
127                         }
128                     )
129                 },
130                 {(long)Registers.ElapsedTime, new DoubleWordRegister(this)
131                     .WithValueField(0, 32, name: "CRTTIME",
132                         valueProviderCallback: _ => timer.Value,
133                         writeCallback: (_, value) =>
134                         {
135                             if(timer.Enabled)
136                             {
137                                 this.Log(LogLevel.Warning, "Setting CRTTIME while the timer is running. Ignoring");
138                                 return;
139                             }
140                             timer.Value = value;
141                         }
142                     )
143                 },
144                 {(long)Registers.InterruptControl, new DoubleWordRegister(this)
145                     .WithFlag(0, FieldMode.WriteOneToClear | FieldMode.Read, name: "INTDISP",
146                         valueProviderCallback: _ => IRQ.IsSet,
147                         writeCallback: (_, value) =>
148                         {
149                             if(value)
150                             {
151                                 IRQ.Unset();
152                             }
153                         }
154                     )
155                     .WithReservedBits(1, 31)
156                 },
157                 {(long)Registers.ParityErrorControl, new DoubleWordRegister(this)
158                     .WithTaggedFlags("PECR", 0, 32)
159                 },
160                 {(long)Registers.ParityErrorForcedEnable, new DoubleWordRegister(this)
161                     .WithTaggedFlag("PEEN", 0)
162                     .WithReservedBits(1, 31)
163                 },
164                 {(long)Registers.ParityStatus, new DoubleWordRegister(this)
165                     .WithTaggedFlags("PESR", 0, 32)
166                 },
167                 {(long)Registers.ParityErrorEnable, new DoubleWordRegister(this)
168                     .WithTaggedFlags("PEER", 0, 32)
169                 },
170                 {(long)Registers.ParityErrorPolarity, new DoubleWordRegister(this)
171                     .WithTaggedFlags("PEPO", 0, 32)
172                 },
173             };
174 
175             return registerMap;
176         }
177 
UpdateTimerStatus()178         private void UpdateTimerStatus()
179         {
180             timer.Enabled = timerEnabled && !forceStop;
181         }
182 
183         private bool forceStop = false;
184         private bool timerEnabled = false;
185         private bool generatedReset = false;
186         private bool keepGeneratedResetValue = false;
187 
188         private readonly IMachine machine;
189         private readonly LimitTimer timer;
190 
191         private const ulong DefaultCycle = 1ul << 20;
192 
193         public enum Registers : long
194         {
195             Control = 0x00,
196             PeriodSetting = 0x04,
197             ElapsedTime = 0x08,
198             InterruptControl = 0x0C,
199             ParityErrorControl = 0x10,
200             ParityErrorForcedEnable = 0x14,
201             ParityStatus = 0x18,
202             ParityErrorEnable = 0x1C,
203             ParityErrorPolarity = 0x20,
204         }
205     }
206 }
207