1 //
2 // Copyright (c) 2010-2018 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 
9 using System;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 using Antmicro.Renode.Time;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.Peripherals.Timers
17 {
18     public class Atmel91SystemTimer : IDoubleWordPeripheral, IKnownSize
19     {
Atmel91SystemTimer(IMachine machine)20         public Atmel91SystemTimer(IMachine machine)
21         {
22             IRQ = new GPIO();
23 
24             PeriodIntervalTimer = new LimitTimer(machine.ClockSource, 32768, this, nameof(PeriodIntervalTimer), int.MaxValue); // long.MaxValue couses crashes
25             PeriodIntervalTimer.Value = 0x00000000;
26             PeriodIntervalTimer.AutoUpdate = true;
27             PeriodIntervalTimer.LimitReached += PeriodIntervalTimerAlarmHandler;
28 
29             WatchdogTimer = new LimitTimer(machine.ClockSource, 32768, this, nameof(WatchdogTimer), int.MaxValue);
30             WatchdogTimer.Value = 0x00020000;
31             WatchdogTimer.AutoUpdate = true;
32             WatchdogTimer.Divider = 128;
33             WatchdogTimer.LimitReached += WatchdogTimerAlarmHandler;
34 
35             RealTimeTimer = new AT91_InterruptibleTimer(machine, 32768, this, nameof(RealTimeTimer), (ulong)BitHelper.Bit(20), Direction.Ascending);
36             RealTimeTimer.Divider = 0x00008000;
37             RealTimeTimer.OnUpdate += () => {
38                 lock (localLock)
39                 {
40                     if (RealtimeTimerIncrementInterruptMask)
41                     {
42                         RealTimeTimerIncrement = true;
43                     }
44                 }
45             };
46         }
47 
PeriodIntervalTimerAlarmHandler()48         private void PeriodIntervalTimerAlarmHandler()
49         {
50             lock (localLock)
51             {
52             //this.Log(LogLevel.Noisy, "Period Interval Timer Alarm");
53 
54             if (PeriodIntervalTimerStatusInterruptMask)
55             {
56                 PeriodIntervalTimerStatus = true;
57                 if (!IRQ.IsSet)
58                 {
59                     this.Log(LogLevel.Noisy, "Setting IRQ due to PeriodIntervalTimerAlarm");
60                 }
61                 //IRQ.Set(false);
62                 IRQ.Set(true);
63             }
64             }
65         }
66 
WatchdogTimerAlarmHandler()67         private void WatchdogTimerAlarmHandler()
68         {
69             lock (localLock)
70             {
71             this.Log(LogLevel.Noisy, "Watchdog Timer Alarm");
72 
73             WatchdogOverflow = true;
74             }
75         }
76 
77         public GPIO IRQ { get; private set; }
78 
79         #region IPeripheral implementation
80 
Reset()81         public void Reset()
82         {
83             throw new NotImplementedException();
84         }
85 
86         #endregion
87 
88         #region IDoubleWordPeripheral implementation
89 
ReadDoubleWord(long offset)90         public uint ReadDoubleWord(long offset)
91         {
92             lock (localLock)
93             {
94             switch ((Register)offset)
95             {
96             case Register.StatusRegister:
97                 var val = statusRegister;
98                 statusRegister = 0;
99                 IRQ.Unset();
100                 return val;
101 
102             case Register.PeriodIntervalModeRegister:
103                 return (uint)PeriodIntervalTimer.Limit;
104 
105             case Register.CurrentRealtimeRegister:
106                 return (uint)RealTimeTimer.Value;
107 
108             case Register.WatchdogModeRegister:
109                 return (uint)WatchdogTimer.Limit;
110 
111             default:
112                 this.LogUnhandledRead(offset);
113                 return 0u;
114             }
115             }
116         }
117 
WriteDoubleWord(long offset, uint value)118         public void WriteDoubleWord(long offset, uint value)
119         {
120             lock (localLock)
121             {
122             switch ((Register)offset)
123             {
124             case Register.ControlRegister:
125                 if (value == 0x1)
126                 {
127                     WatchdogTimer.ResetValue();
128                 }
129                 break;
130 
131             case Register.PeriodIntervalModeRegister:
132                 PeriodIntervalTimer.Limit = value;
133                 PeriodIntervalTimer.Enabled = true;
134                 break;
135 
136             case Register.InterruptDisableRegister:
137                 this.Log(LogLevel.Noisy, "Disabling interrupt 0x{0:X}", value);
138                 interruptMaskRegister &= ~value;
139                 break;
140 
141             case Register.InterruptEnableRegister:
142                 this.Log(LogLevel.Noisy, "Enabling interrupt 0x{0:X}", value);
143                 interruptMaskRegister |= value;
144                 break;
145 
146             case Register.RealTimeModeRegister:
147                 RealTimeTimer.Divider = (int)value;
148                 break;
149 
150             default:
151                 this.LogUnhandledWrite(offset, value);
152                 return;
153             }
154             }
155         }
156 
157         #endregion
158 
159         #region IKnownSize implementation
160 
161         public long Size {
162             get {
163                 return 256;
164             }
165         }
166 
167         #endregion
168 
169         private LimitTimer PeriodIntervalTimer; // PIT
170         private LimitTimer WatchdogTimer;       // WDT
171         private AT91_InterruptibleTimer RealTimeTimer;       // RTT
172 
173         private uint interruptMaskRegister;             // TODO: uses only 4 bits
174         private uint statusRegister;                    // TODO: uses only 4 bits
175 
176         private object localLock = new object();
177 
178         #region Bits
179 
180         private bool PeriodIntervalTimerStatus
181         {
182             get { return BitHelper.IsBitSet(statusRegister, 0); }
183             set { BitHelper.SetBit(ref statusRegister, 0, value); }
184         }
185         private bool WatchdogOverflow
186         {
187             get { return BitHelper.IsBitSet(statusRegister, 1); }
188             set { BitHelper.SetBit(ref statusRegister, 1, value); }
189         }
190         private bool RealTimeTimerIncrement
191         {
192             get { return BitHelper.IsBitSet(statusRegister, 2); }
193             set { BitHelper.SetBit(ref statusRegister, 2, value); }
194         }
195         private bool AlarmStatus
196         {
197             get { return BitHelper.IsBitSet(statusRegister, 3); }
198             set { BitHelper.SetBit(ref statusRegister, 3, value); }
199         }
200 
201         private bool PeriodIntervalTimerStatusInterruptMask
202         {
203             get { return BitHelper.IsBitSet(interruptMaskRegister, 0); }
204         }
205         private bool WatchdogOverflowInterruptMask
206         {
207             get { return BitHelper.IsBitSet(interruptMaskRegister, 1); }
208         }
209         private bool RealtimeTimerIncrementInterruptMask
210         {
211             get { return BitHelper.IsBitSet(interruptMaskRegister, 2); }
212         }
213         private bool AlarmStatusInterruptMask
214         {
215             get { return BitHelper.IsBitSet(interruptMaskRegister, 3); }
216         }
217 
218         #endregion
219 
220         private enum Register:uint
221         {
222             ControlRegister             = 0x0000,   // CR
223             PeriodIntervalModeRegister  = 0x0004,   // PIMR
224             WatchdogModeRegister        = 0x0008,   // WDMR - TODO: there is RSTEN bit mentioned in documentation, but not mapped to WDMR register
225             RealTimeModeRegister        = 0x000C,   // RTMR
226             StatusRegister              = 0x0010,   // SR
227             InterruptEnableRegister     = 0x0014,   // IER
228             InterruptDisableRegister    = 0x0018,   // IDR
229             RealTimeAlarmRegister       = 0x0020,   // RTAR
230             CurrentRealtimeRegister     = 0x0024,   // CRTR
231         }
232 
233         private class AT91_InterruptibleTimer
234         {
235             private LimitTimer timer;
236             private ulong? prevValue;
237             private object lockobj = new object();
238 
239             public event Action OnUpdate;
240 
AT91_InterruptibleTimer(IMachine machine, long frequency, IPeripheral owner, string localName, ulong limit = ulong.MaxValue, Direction direction = Direction.Descending, bool enabled = false)241             public AT91_InterruptibleTimer(IMachine machine, long frequency, IPeripheral owner, string localName, ulong limit = ulong.MaxValue, Direction direction = Direction.Descending, bool enabled = false)
242             {
243                 timer = new LimitTimer(machine.ClockSource, frequency, owner, localName, limit, direction, enabled);
244                 timer.LimitReached += () => { if (OnUpdate != null) OnUpdate(); };
245             }
246 
247             public ulong Value
248             {
249                 get
250                 {
251                     lock (lockobj)
252                     {
253                         if (!prevValue.HasValue)
254                         {
255                             prevValue = timer.Value;
256                             return prevValue.Value;
257                         }
258                         else
259                         {
260                             var result = prevValue.Value;
261                             prevValue = null;
262                             return result;
263                         }
264                     }
265                 }
266                 set
267                 {
268                     lock (lockobj)
269                     {
270                         prevValue = null;
271                         timer.Value = value;
272                     }
273                 }
274             }
275 
276             public int Divider
277             {
278                 get { return timer.Divider; }
279                 set { timer.Divider = value; }
280             }
281 
Enable()282             public void Enable()
283             {
284                 timer.Enabled = true;
285             }
286         }
287     }
288 }
289 
290