1 //
2 // Copyright (c) 2010-2023 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.Logging;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Peripherals.Miscellaneous;
13 
14 namespace Antmicro.Renode.Peripherals.Timers
15 {
16     public class OpenTitan_AonTimer: BasicDoubleWordPeripheral, IKnownSize
17     {
OpenTitan_AonTimer(IMachine machine, OpenTitan_PowerManager powerManager, OpenTitan_ResetManager resetManager, long frequency = 200000)18         public OpenTitan_AonTimer(IMachine machine, OpenTitan_PowerManager powerManager, OpenTitan_ResetManager resetManager, long frequency = 200000): base(machine)
19         {
20             this.powerManager = powerManager;
21             this.resetManager = resetManager;
22 
23             powerManager.LowPowerStateChanged += HandleLowPowerTransition;
24 
25             WakeupTimerExpired = new GPIO();
26             WatchdogTimerBark = new GPIO();
27             FatalAlert = new GPIO();
28 
29             wkupTimer = new ComparingTimer(machine.ClockSource, frequency, this, "wkup_timer", workMode: Time.WorkMode.Periodic, eventEnabled: true);
30             wkupTimer.CompareReached += UpdateWakeupInterrupts;
31 
32             wdogBarkInnerTimer = new ComparingTimer(machine.ClockSource, frequency, this, "wdog_bark_inner_timer", workMode: Time.WorkMode.Periodic, eventEnabled: true);
33             wdogBarkInnerTimer.CompareReached += UpdateWdogBarkInterrupts;
34             wdogBiteInnerTimer = new ComparingTimer(machine.ClockSource, frequency, this, "wdog_bite_inner_timer", workMode: Time.WorkMode.Periodic, eventEnabled: true);
35             wdogBiteInnerTimer.CompareReached += UpdateWdogBiteInterrupts;
36 
37             DefineRegisters();
38         }
39 
Reset()40         public override void Reset()
41         {
42             base.Reset();
43             FatalAlert.Unset();
44 
45             wkupTimer.Reset();
46             wdogBarkInnerTimer.Reset();
47             wdogBiteInnerTimer.Reset();
48             UpdateWakeupInterrupts();
49             UpdateWdogBarkInterrupts();
50             UpdateWdogBiteInterrupts();
51 
52             lowPowerState = false;
53         }
54 
55         public long Size => 0x30;
56 
57         public GPIO WakeupTimerExpired { get; }
58         public GPIO WatchdogTimerBark { get; }
59         public GPIO FatalAlert { get; }
60 
HandleLowPowerTransition(bool lowPower)61         private void HandleLowPowerTransition(bool lowPower)
62         {
63             if(lowPowerState == lowPower)
64             {
65                 return;
66             }
67 
68             if(pauseInSleep.Value)
69             {
70                 wdogBarkInnerTimer.Enabled = !lowPower;
71                 wdogBiteInnerTimer.Enabled = !lowPower;
72             }
73 
74             lowPowerState = lowPower;
75             this.Log(LogLevel.Debug, "Low power state set to {0}", lowPower);
76         }
77 
DefineRegisters()78         private void DefineRegisters()
79         {
80             Registers.AlertTest.Define(this)
81                 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalAlert.Blink(); }, name: "fatal_fault")
82                 .WithReservedBits(1, 31);
83 
84             Registers.WakeupTimerControl.Define(this)
85                 .WithFlag(0, name: "enable", writeCallback: (_, val) =>
86                 {
87                     wkupTimer.Enabled = val;
88                     UpdateWakeupInterrupts();
89                 })
90                 .WithValueField(1, 12, name: "prescaler",
91                     valueProviderCallback: _ => wkupTimer.Divider - 1,
92                     writeCallback: (_, val) => wkupTimer.Divider = (uint)(val + 1u))
93                 .WithReservedBits(13, 19);
94 
95             Registers.WakeupTimerThreshold.Define(this)
96                 .WithValueField(0, 32, name: "threshold",
97                     valueProviderCallback: _ => (uint)wkupTimer.Compare,
98                     writeCallback: (_, val) =>
99                     {
100                         wkupTimer.Compare = val;
101                         UpdateWakeupInterrupts();
102                     });
103 
104             Registers.WakeupTimerCount.Define(this)
105                 .WithValueField(0, 32, name: "count",
106                     valueProviderCallback: _ => (uint)wkupTimer.Value,
107                     writeCallback: (_, val) =>
108                     {
109                         wkupTimer.Value = val;
110                         UpdateWakeupInterrupts();
111                     });
112 
113             Registers.WatchdogTimerWriteEnable.Define(this, 0x1)
114                 .WithFlag(0, out wdogConfigNotLocked, FieldMode.Read | FieldMode.WriteZeroToClear, name: "regwen")
115                 .WithReservedBits(1, 31);
116 
117             Registers.WatchdogTimerControl.Define(this)
118                 .WithFlag(0, name: "enable", writeCallback: (_, val) =>
119                 {
120                     if(!wdogConfigNotLocked.Value)
121                     {
122                         this.Log(LogLevel.Warning, "Watchdog timer configuration is locked");
123                         return;
124                     }
125 
126                     wdogBiteInnerTimer.Enabled = val;
127                     UpdateWdogBiteInterrupts();
128                     wdogBarkInnerTimer.Enabled = val;
129                     UpdateWdogBarkInterrupts();
130                 })
131                 .WithFlag(1, out pauseInSleep, name: "pause_in_sleep")
132                 .WithReservedBits(2, 30);
133 
134             Registers.WatchdogTimerBarkThreshold.Define(this)
135                 .WithValueField(0, 32, name: "threshold",
136                     valueProviderCallback: _ => (uint)wdogBarkInnerTimer.Compare,
137                     writeCallback: (_, val) =>
138                     {
139                         if(!wdogConfigNotLocked.Value)
140                         {
141                             this.Log(LogLevel.Warning, "Watchdog timer configuration is locked");
142                             return;
143                         }
144 
145                         wdogBarkInnerTimer.Compare = val;
146                         UpdateWdogBarkInterrupts();
147                     });
148 
149             Registers.WatchdogTimerBiteThreshold.Define(this)
150                 .WithValueField(0, 32, name: "threshold",
151                     valueProviderCallback: _ => (uint)wdogBiteInnerTimer.Compare,
152                     writeCallback: (_, val) =>
153                     {
154                         if(!wdogConfigNotLocked.Value)
155                         {
156                             this.Log(LogLevel.Warning, "Watchdog timer configuration is locked");
157                             return;
158                         }
159 
160                         wdogBiteInnerTimer.Compare = val;
161                         UpdateWdogBiteInterrupts();
162                     });
163 
164             Registers.WatchdogTimerCount.Define(this)
165                 .WithValueField(0, 32, name: "count",
166                     valueProviderCallback: _ => (uint)wdogBiteInnerTimer.Value,
167                     writeCallback: (_, val) =>
168                     {
169                         if(!wdogConfigNotLocked.Value)
170                         {
171                             this.Log(LogLevel.Warning, "Watchdog timer configuration is locked");
172                             return;
173                         }
174 
175                         wdogBiteInnerTimer.Value = val;
176                         UpdateWdogBiteInterrupts();
177                         wdogBarkInnerTimer.Value = val;
178                         UpdateWdogBarkInterrupts();
179                     });
180 
181             Registers.InterruptState.Define(this)
182                 .WithFlag(0, FieldMode.Read | FieldMode.WriteOneToClear, name: "wkup_timer_expired",
183                     valueProviderCallback: _ => WakeupTimerExpired.IsSet,
184                     writeCallback: (_, val) => { if(val) WakeupTimerExpired.Set(false); })
185                 .WithFlag(1, FieldMode.Read | FieldMode.WriteOneToClear, name: "wdog_timer_bark",
186                     valueProviderCallback: _ => WatchdogTimerBark.IsSet,
187                     writeCallback: (_, val) => { if(val) WatchdogTimerBark.Set(false); })
188                 .WithReservedBits(2, 30);
189 
190             Registers.InterruptTest.Define(this)
191                 .WithFlag(0, FieldMode.Write, name: "wkup_timer_expired",
192                     writeCallback: (_, newValue) =>
193                     {
194                         if(newValue)
195                         {
196                             WakeupTimerExpired.Set(true);
197                         }
198                     })
199                 .WithFlag(1, FieldMode.Write, name: "wdog_timer_bark",
200                     writeCallback: (_, newValue) =>
201                     {
202                         if(newValue)
203                         {
204                             WatchdogTimerBark.Set(true);
205                         }
206                     })
207                 .WithReservedBits(2, 30);
208 
209             Registers.WakeupRequestStatus.Define(this)
210                 .WithFlag(0, out wakeupLevelSignal, FieldMode.Read | FieldMode.WriteZeroToClear, name: "cause")
211                 .WithReservedBits(1, 31);
212         }
213 
UpdateWakeupInterrupts()214         private void UpdateWakeupInterrupts()
215         {
216             var isActive = wkupTimer.Enabled && wkupTimer.Value >= wkupTimer.Compare;
217             wakeupLevelSignal.Value = isActive;
218             if(isActive && lowPowerState)
219             {
220                 // Set wakeup level signal
221                 powerManager.RequestWakeup();
222             }
223             WakeupTimerExpired.Set(isActive);
224         }
225 
UpdateWdogBarkInterrupts()226         private void UpdateWdogBarkInterrupts()
227         {
228             var isActive = wdogBarkInnerTimer.Enabled && wdogBarkInnerTimer.Value >= wdogBarkInnerTimer.Compare;
229             WatchdogTimerBark.Set(isActive);
230             this.Log(LogLevel.Debug, "Watchdog bark interrupt is {0}", isActive ? "active" : "inactive");
231         }
232 
UpdateWdogBiteInterrupts()233         private void UpdateWdogBiteInterrupts()
234         {
235             var isActive = wdogBiteInnerTimer.Enabled && wdogBiteInnerTimer.Value >= wdogBiteInnerTimer.Compare;
236             if(isActive)
237             {
238                 // Set bite level signal
239                 this.Log(LogLevel.Info, "Watchdog timer bite");
240                 resetManager.PeripheralRequestedReset(OpenTitan_ResetManager.HardwareResetReason.Watchdog, lowPowerState);
241             }
242         }
243 
244         private readonly ComparingTimer wkupTimer, wdogBarkInnerTimer, wdogBiteInnerTimer;
245 
246         private IFlagRegisterField pauseInSleep;
247         private IFlagRegisterField wakeupLevelSignal;
248         private IFlagRegisterField wdogConfigNotLocked;
249 
250         private readonly OpenTitan_PowerManager powerManager;
251         private readonly OpenTitan_ResetManager resetManager;
252         private bool lowPowerState;
253 
254         public enum Registers
255         {
256             AlertTest = 0x0,
257             WakeupTimerControl = 0x4,
258             WakeupTimerThreshold = 0x8,
259             WakeupTimerCount = 0xc,
260             WatchdogTimerWriteEnable = 0x10,
261             WatchdogTimerControl = 0x14,
262             WatchdogTimerBarkThreshold = 0x18,
263             WatchdogTimerBiteThreshold = 0x1c,
264             WatchdogTimerCount = 0x20,
265             InterruptState = 0x24,
266             InterruptTest = 0x28,
267             WakeupRequestStatus = 0x2c,
268         }
269     }
270 }
271