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