1 // 2 // Copyright (c) 2010-2023 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 using System; 9 using Antmicro.Renode.Peripherals.Bus; 10 using Antmicro.Renode.Logging; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Time; 14 using System.Collections.Generic; 15 using System.Collections.ObjectModel; 16 17 namespace Antmicro.Renode.Peripherals.Timers 18 { 19 public sealed class SunxiHighSpeedTimer : IDoubleWordPeripheral, IKnownSize, INumberedGPIOOutput 20 { SunxiHighSpeedTimer(IMachine machine, long frequency)21 public SunxiHighSpeedTimer(IMachine machine, long frequency) 22 { 23 irqEnableRegister = new DoubleWordRegister(this); 24 irqStatusRegister = new DoubleWordRegister(this); 25 26 timers = new SunxiHighSpeedTimerUnit[4]; 27 interruptFlags = new IFlagRegisterField[4]; 28 enableFlags = new IFlagRegisterField[4]; 29 30 for(var i = 0; i < 4; ++i) 31 { 32 var j = i; 33 timers[i] = new SunxiHighSpeedTimerUnit(machine, frequency); 34 timers[i].LimitReached += () => OnTimerLimitReached(j); 35 interruptFlags[i] = irqStatusRegister.DefineFlagField(i, FieldMode.WriteOneToClear, name: "Tx_IRQ_PEND"); 36 enableFlags[i] = irqEnableRegister.DefineFlagField(i, name: "Tx_INT_EN"); 37 } 38 39 var innerConnections = new Dictionary<int, IGPIO>(); 40 for(var i = 0; i < 4; ++i) 41 { 42 innerConnections[i] = new GPIO(); 43 } 44 Connections = new ReadOnlyDictionary<int, IGPIO>(innerConnections); 45 } 46 ReadDoubleWord(long offset)47 public uint ReadDoubleWord(long offset) 48 { 49 SunxiHighSpeedTimerUnit timerUnit; 50 var offsetPerTimer = (Registers)GetTimerRegisterByOffset(offset, out timerUnit); 51 switch(offsetPerTimer) 52 { 53 case Registers.IRQEnable: 54 return irqEnableRegister.Read(); 55 case Registers.IRQStatus: 56 return irqStatusRegister.Read(); 57 case Registers.Control: 58 return timerUnit.ControlRegister; 59 case Registers.CurrentValueHigh: 60 return timerUnit.ValueRegisterHigh; 61 case Registers.CurrentValueLow: 62 return timerUnit.ValueRegisterLow; 63 case Registers.IntervalHigh: 64 return timerUnit.IntervalRegisterHigh; 65 case Registers.IntervalLow: 66 return timerUnit.IntervalRegisterLow; 67 default: 68 this.LogUnhandledRead(offset); 69 return 0; 70 } 71 } 72 WriteDoubleWord(long offset, uint value)73 public void WriteDoubleWord(long offset, uint value) 74 { 75 SunxiHighSpeedTimerUnit timerUnit; 76 var offsetPerTimer = (Registers)GetTimerRegisterByOffset(offset, out timerUnit); 77 switch(offsetPerTimer) 78 { 79 case Registers.IRQEnable: 80 irqEnableRegister.Write(offset, value); 81 Update(); 82 break; 83 case Registers.IRQStatus: 84 irqStatusRegister.Write(offset, value); 85 Update(); 86 break; 87 case Registers.Control: 88 timerUnit.ControlRegister = value; 89 break; 90 case Registers.CurrentValueHigh: 91 timerUnit.ValueRegisterHigh = value; 92 break; 93 case Registers.CurrentValueLow: 94 timerUnit.ValueRegisterLow = value; 95 break; 96 case Registers.IntervalHigh: 97 timerUnit.IntervalRegisterHigh = value; 98 break; 99 case Registers.IntervalLow: 100 timerUnit.IntervalRegisterLow = value; 101 break; 102 default: 103 this.LogUnhandledWrite(offset, value); 104 break; 105 } 106 } 107 Reset()108 public void Reset() 109 { 110 for(var i = 0; i < 4; ++i) 111 { 112 timers[i].Reset(); 113 } 114 irqEnableRegister.Reset(); 115 irqStatusRegister.Reset(); 116 } 117 118 public long Size 119 { 120 get 121 { 122 return 0x2000; 123 } 124 } 125 126 public IReadOnlyDictionary<int, IGPIO> Connections { get; private set; } 127 GetTimerRegisterByOffset(long offset, out SunxiHighSpeedTimerUnit timer)128 private int GetTimerRegisterByOffset(long offset, out SunxiHighSpeedTimerUnit timer) 129 { 130 if(offset < 0x10) 131 { 132 timer = null; 133 return (int)offset; 134 } 135 136 var offsetPerTimer = ((offset - 0x10) % 0x20) + 0x10; 137 var timerNumber = (uint)(offset - 0x10) / 0x20; 138 timer = timers[timerNumber]; 139 return (int)offsetPerTimer; 140 } 141 OnTimerLimitReached(int timerId)142 private void OnTimerLimitReached(int timerId) 143 { 144 this.Log(LogLevel.Noisy, "HSTimer {0} limit reached.", timerId); 145 if(enableFlags[timerId].Value) 146 { 147 interruptFlags[timerId].Value = true; 148 Update(); 149 } 150 } 151 Update()152 private void Update() 153 { 154 for(var i = 0; i < 4; ++i) 155 { 156 if(enableFlags[i].Value && interruptFlags[i].Value) 157 { 158 Connections[i].Set(); 159 } 160 else 161 { 162 Connections[i].Unset(); 163 } 164 } 165 } 166 167 private readonly SunxiHighSpeedTimerUnit[] timers; 168 private readonly IFlagRegisterField[] interruptFlags, enableFlags; 169 private readonly DoubleWordRegister irqEnableRegister, irqStatusRegister; 170 171 private enum Registers 172 { 173 IRQEnable = 0x00, 174 IRQStatus = 0x04, 175 Control = 0x10, 176 IntervalLow = 0x14, 177 IntervalHigh = 0x18, 178 CurrentValueLow = 0x1c, 179 CurrentValueHigh = 0x20 180 } 181 182 private sealed class SunxiHighSpeedTimerUnit : LimitTimer 183 { SunxiHighSpeedTimerUnit(IMachine machine, long frequency)184 public SunxiHighSpeedTimerUnit(IMachine machine, long frequency) : base(machine.ClockSource, frequency, direction: Direction.Descending, enabled: false) 185 { 186 controlRegister = new DoubleWordRegister(this); 187 controlRegister.DefineFlagField(7, changeCallback: OnModeChange, name: "MODE"); 188 controlRegister.DefineValueField(4, 3, writeCallback: (prevVal, val) => OnPrescalerChange((uint)prevVal, (uint)val), name: "PRESC"); 189 controlRegister.DefineFlagField(1, writeCallback: OnReload, name: "RELOAD"); 190 controlRegister.DefineFlagField(0, writeCallback: OnEnableChange, name: "ENABLE"); 191 EventEnabled = true; 192 } 193 Reset()194 public override void Reset() 195 { 196 base.Reset(); 197 controlRegister.Reset(); 198 } 199 200 public uint IntervalRegisterLow 201 { 202 get 203 { 204 var currentLimt = Limit; 205 savedIntervalHigh = (uint)(currentLimt >> 32) & 0x00ffffff; 206 return (uint)currentLimt; 207 } 208 set 209 { 210 intervalRegisterLow = value; 211 } 212 } 213 214 public uint IntervalRegisterHigh 215 { 216 get 217 { 218 return savedIntervalHigh; 219 } 220 set 221 { 222 Limit = ((ulong)value << 32) | intervalRegisterLow; 223 } 224 } 225 226 public uint ValueRegisterLow 227 { 228 get 229 { 230 var currentValue = Value; 231 savedValueHigh = (uint)(currentValue >> 32) & 0x00ffffff; 232 return (uint)currentValue; 233 } 234 set 235 { 236 valueRegisterLow = value; 237 } 238 } 239 240 public uint ValueRegisterHigh 241 { 242 get 243 { 244 return savedValueHigh; 245 } 246 set 247 { 248 Value = ((ulong)value << 32) | valueRegisterLow; 249 } 250 } 251 252 public uint ControlRegister 253 { 254 get 255 { 256 return controlRegister.Read(); 257 } 258 set 259 { 260 controlRegister.Write((long)Registers.Control, value); 261 } 262 } 263 264 // THIS IS A WORKAROUND FOR A BUG IN MONO 265 // https://bugzilla.xamarin.com/show_bug.cgi?id=39444 OnLimitReached()266 protected override void OnLimitReached() 267 { 268 base.OnLimitReached(); 269 } 270 OnModeChange(bool oldValue, bool newValue)271 private void OnModeChange(bool oldValue, bool newValue) 272 { 273 Mode = newValue ? WorkMode.OneShot : WorkMode.Periodic; 274 } 275 OnPrescalerChange(ulong oldValue, ulong newValue)276 private void OnPrescalerChange(ulong oldValue, ulong newValue) 277 { 278 if(newValue < 4) 279 { 280 Divider = 1 << (int)newValue; 281 } 282 else 283 { 284 this.Log(LogLevel.Warning, "Invalid prescaler value: {0}.", newValue); 285 } 286 } 287 OnReload(bool oldValue, bool newValue)288 private void OnReload(bool oldValue, bool newValue) 289 { 290 if(newValue) 291 { 292 Value = Limit; 293 } 294 } 295 OnEnableChange(bool oldValue, bool newValue)296 private void OnEnableChange(bool oldValue, bool newValue) 297 { 298 Enabled = newValue; 299 } 300 301 private uint savedIntervalHigh, savedValueHigh, intervalRegisterLow, valueRegisterLow; 302 private readonly DoubleWordRegister controlRegister; 303 } 304 } 305 } 306 307