1 // 2 // Copyright (c) 2010-2024 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.Exceptions; 11 using Antmicro.Renode.Time; 12 using Antmicro.Renode.Logging; 13 14 namespace Antmicro.Renode.Peripherals.Timers 15 { 16 public class ARM_GenericTimer : IPeripheral 17 { 18 // defaultCounterFrequencyRegister is the value that CNTFRQ will be set to at reset. 19 // It can be used to configure the value configured by an earlier-stage bootloader not 20 // run under Renode in the platform file. ARM_GenericTimer(IMachine machine, ulong frequency, uint defaultCounterFrequencyRegister = 0)21 public ARM_GenericTimer(IMachine machine, ulong frequency, uint defaultCounterFrequencyRegister = 0) 22 { 23 if(frequency > long.MaxValue) 24 { 25 throw new ConstructionException($"Timer doesn't support frequency greater than {long.MaxValue}, given {frequency}."); 26 } 27 28 Frequency = (long)frequency; 29 this.defaultCounterFrequencyRegister = defaultCounterFrequencyRegister; 30 clockSource = machine.ClockSource; 31 32 // The names used are based on Generic Timer documentation: https://developer.arm.com/documentation/102379/0101/The-processor-timers 33 el1PhysicalTimer = new TimerUnit(clockSource, this, "EL1PhysicalTimer", EL1PhysicalTimerIRQ, Frequency); 34 el1VirtualTimer = new TimerUnit(clockSource, this, "EL1VirtualTimer", EL1VirtualTimerIRQ, Frequency); 35 el3PhysicalTimer = new TimerUnit(clockSource, this, "EL3PhysicalTimer", EL3PhysicalTimerIRQ, Frequency); 36 nonSecureEL2PhysicalTimer = new TimerUnit(clockSource, this, "NonSecureEL2PhysicalTimer", NonSecureEL2PhysicalTimerIRQ, Frequency); 37 nonSecureEL2VirtualTimer = new TimerUnit(clockSource, this, "NonSecureEL2VirtualTimer", NonSecureEL2VirtualTimerIRQ, Frequency); 38 39 registersAArch64 = new QuadWordRegisterCollection(this, BuildRegisterAArch64Map()); 40 doubleWordRegistersAArch32 = new DoubleWordRegisterCollection(this, BuildDoubleWordRegisterAArch32Map()); 41 quadWordRegistersAArch32 = new QuadWordRegisterCollection(this, BuildQuadWordRegisterAArch32Map()); 42 43 Reset(); 44 } 45 WriteRegisterAArch64(uint offset, ulong value)46 public void WriteRegisterAArch64(uint offset, ulong value) 47 { 48 this.Log(LogLevel.Debug, "Write to {0} (0x{1:X}) AArch64 register, value 0x{2:X}", (RegistersAArch64)offset, offset, value); 49 registersAArch64.Write(offset, value); 50 } 51 ReadRegisterAArch64(uint offset)52 public ulong ReadRegisterAArch64(uint offset) 53 { 54 var register = (RegistersAArch64)offset; 55 var value = registersAArch64.Read(offset); 56 57 // There can be lots and lots of '*Count' register reads. 58 if((register != RegistersAArch64.PhysicalCount && register != RegistersAArch64.VirtualCount) || EnableCountReadLogs) 59 { 60 this.Log(LogLevel.Debug, "Read from {0} (0x{1:X}) AArch64 register, value 0x{2:X}", register, offset, value); 61 } 62 return value; 63 } 64 WriteDoubleWordRegisterAArch32(uint offset, uint value)65 public void WriteDoubleWordRegisterAArch32(uint offset, uint value) 66 { 67 this.Log(LogLevel.Debug, "Write to {0} (0x{1:X}) AArch32 register, value 0x{2:X}", (DoubleWordRegistersAArch32)offset, offset, value); 68 doubleWordRegistersAArch32.Write(offset, value); 69 } 70 ReadDoubleWordRegisterAArch32(uint offset)71 public uint ReadDoubleWordRegisterAArch32(uint offset) 72 { 73 var value = doubleWordRegistersAArch32.Read(offset); 74 this.Log(LogLevel.Debug, "Read from {0} (0x{1:X}) AArch32 register, value 0x{2:X}", (DoubleWordRegistersAArch32)offset, offset, value); 75 return value; 76 } 77 WriteQuadWordRegisterAArch32(uint offset, ulong value)78 public void WriteQuadWordRegisterAArch32(uint offset, ulong value) 79 { 80 this.Log(LogLevel.Debug, "Write to {0} (0x{1:X}) AArch32 64-bit register, value 0x{2:X}", (QuadWordRegistersAArch32)offset, offset, value); 81 quadWordRegistersAArch32.Write(offset, value); 82 } 83 ReadQuadWordRegisterAArch32(uint offset)84 public ulong ReadQuadWordRegisterAArch32(uint offset) 85 { 86 var register = (QuadWordRegistersAArch32)offset; 87 var value = quadWordRegistersAArch32.Read(offset); 88 89 // There can be lots and lots of '*Count' register reads. 90 if((register != QuadWordRegistersAArch32.PhysicalCount && register != QuadWordRegistersAArch32.VirtualCount) || EnableCountReadLogs) 91 { 92 this.Log(LogLevel.Debug, "Read from {0} (0x{1:X}) AArch32 64-bit register, value 0x{2:X}", register, offset, value); 93 } 94 return value; 95 } 96 Reset()97 public void Reset() 98 { 99 el1PhysicalTimer.Reset(); 100 el1VirtualTimer.Reset(); 101 el3PhysicalTimer.Reset(); 102 nonSecureEL2PhysicalTimer.Reset(); 103 nonSecureEL2VirtualTimer.Reset(); 104 105 CounterFrequencyRegister = defaultCounterFrequencyRegister; 106 registersAArch64.Reset(); 107 doubleWordRegistersAArch32.Reset(); 108 quadWordRegistersAArch32.Reset(); 109 } 110 111 public bool EnableCountReadLogs { get; set; } 112 public long Frequency { get; } 113 // There is the counter frequency register used by a software to discover a frequency of a timer 114 // In the model we allow to preset it using the `CounterFrequencyRegister` property to support simulation scenarios without a bootloader. 115 public uint CounterFrequencyRegister { set; get; } 116 117 public GPIO EL1PhysicalTimerIRQ { get; } = new GPIO(); 118 public GPIO EL1VirtualTimerIRQ { get; } = new GPIO(); 119 public GPIO EL3PhysicalTimerIRQ { get; } = new GPIO(); 120 public GPIO NonSecureEL2PhysicalTimerIRQ { get; } = new GPIO(); 121 public GPIO NonSecureEL2VirtualTimerIRQ { get; } = new GPIO(); 122 123 // Some physical timers names in the ARMv7 specification are different than names in the ARMv8-A specification. 124 public GPIO PL1PhysicalTimerIRQ => EL1PhysicalTimerIRQ; 125 public GPIO PL2PhysicalTimerIRQ => NonSecureEL2PhysicalTimerIRQ; 126 public GPIO VirtualTimerIRQ => EL1VirtualTimerIRQ; 127 BuildRegisterAArch64Map()128 private Dictionary<long, QuadWordRegister> BuildRegisterAArch64Map() 129 { 130 var registersMap = new Dictionary<long, QuadWordRegister> 131 { 132 {(long)RegistersAArch64.Frequency, 133 BuildFrequencyRegister(new QuadWordRegister(this), "CounterFrequency", 64) 134 }, 135 136 {(long)RegistersAArch64.PhysicalCount, 137 BuildTimerCountValueRegister(el1PhysicalTimer, "EL1Physical") 138 }, 139 {(long)RegistersAArch64.VirtualCount, 140 BuildTimerCountValueRegister(el1VirtualTimer, "EL1Virtual") 141 }, 142 143 // There is no implementation of the PhysicalTimerOffset register, because it require the Enhanced Counter Virtualization support. 144 145 {(long)RegistersAArch64.VirtualOffset, 146 BuildTimerOffsetRegister(el1VirtualTimer, "EL1Virtual") 147 }, 148 149 {(long)RegistersAArch64.EL1PhysicalTimerControl, 150 BuildTimerControlRegister(new QuadWordRegister(this), el1PhysicalTimer, "EL1PhysicalTimer") 151 }, 152 {(long)RegistersAArch64.EL1VirtualTimerControl, 153 BuildTimerControlRegister(new QuadWordRegister(this), el1VirtualTimer, "EL1VirtualTimer") 154 }, 155 {(long)RegistersAArch64.EL3PhysicalTimerControl, 156 BuildTimerControlRegister(new QuadWordRegister(this), el3PhysicalTimer, "EL3PhysicalTimer") 157 }, 158 {(long)RegistersAArch64.NonSecureEL2PhysicalTimerControl, 159 BuildTimerControlRegister(new QuadWordRegister(this), nonSecureEL2PhysicalTimer, "NonSecureEL2PhysicalTimer") 160 }, 161 {(long)RegistersAArch64.NonSecureEL2VirtualTimerControl, 162 BuildTimerControlRegister(new QuadWordRegister(this), nonSecureEL2VirtualTimer, "NonSecureEL2VirtualTimer") 163 }, 164 165 {(long)RegistersAArch64.EL1PhysicalTimerCompareValue, 166 BuildTimerCompareValueRegister(el1PhysicalTimer, "EL1PhysicalTimer") 167 }, 168 {(long)RegistersAArch64.EL1VirtualTimerCompareValue, 169 BuildTimerCompareValueRegister(el1VirtualTimer, "EL1VirtualTimer") 170 }, 171 {(long)RegistersAArch64.EL3PhysicalTimerCompareValue, 172 BuildTimerCompareValueRegister(el3PhysicalTimer, "EL3PhysicalTimer") 173 }, 174 {(long)RegistersAArch64.NonSecureEL2PhysicalTimerCompareValue, 175 BuildTimerCompareValueRegister(nonSecureEL2PhysicalTimer, "NonSecureEL2PhysicalTimer") 176 }, 177 {(long)RegistersAArch64.NonSecureEL2VirtualTimerCompareValue, 178 BuildTimerCompareValueRegister(nonSecureEL2VirtualTimer, "NonSecureEL2VirtualTimer") 179 }, 180 181 {(long)RegistersAArch64.EL1PhysicalTimerValue, 182 BuildTimerCountDownValueRegister(new QuadWordRegister(this), el1PhysicalTimer, "EL1PhysicalTimer", 64) 183 }, 184 {(long)RegistersAArch64.EL1VirtualTimerValue, 185 BuildTimerCountDownValueRegister(new QuadWordRegister(this), el1VirtualTimer, "EL1VirtualTimer", 64) 186 }, 187 {(long)RegistersAArch64.EL3PhysicalTimerValue, 188 BuildTimerCountDownValueRegister(new QuadWordRegister(this), el3PhysicalTimer, "EL3PhysicalTimer", 64) 189 }, 190 {(long)RegistersAArch64.NonSecureEL2PhysicalTimerValue, 191 BuildTimerCountDownValueRegister(new QuadWordRegister(this), nonSecureEL2PhysicalTimer, "NonSecureEL2PhysicalTimer", 64) 192 }, 193 {(long)RegistersAArch64.NonSecureEL2VirtualTimerValue, 194 BuildTimerCountDownValueRegister(new QuadWordRegister(this), nonSecureEL2VirtualTimer, "NonSecureEL2VirtualTimer", 64) 195 }, 196 }; 197 198 // Specultative access doesn't occure in Renode. 199 // Self synchronized registers can be just mapped to normal registers. 200 registersMap[(long)RegistersAArch64.PhysicalSelfSynchronizedCount] = 201 registersMap[(long)RegistersAArch64.PhysicalCount]; 202 registersMap[(long)RegistersAArch64.VirtualSelfSynchronizedCount] = 203 registersMap[(long)RegistersAArch64.VirtualCount]; 204 205 return registersMap; 206 } 207 BuildDoubleWordRegisterAArch32Map()208 private Dictionary<long, DoubleWordRegister> BuildDoubleWordRegisterAArch32Map() 209 { 210 var registersMap = new Dictionary<long, DoubleWordRegister> 211 { 212 {(long)DoubleWordRegistersAArch32.Frequency, 213 BuildFrequencyRegister(new DoubleWordRegister(this), "CounterFrequency", 32) 214 }, 215 216 {(long)DoubleWordRegistersAArch32.PL1PhysicalTimerControl, 217 BuildTimerControlRegister(new DoubleWordRegister(this), el1PhysicalTimer, "PL1PhysicalTimer") 218 }, 219 {(long)DoubleWordRegistersAArch32.PL2PhysicalTimerControl, 220 BuildTimerControlRegister(new DoubleWordRegister(this), nonSecureEL2PhysicalTimer, "PL2PhysicalTimer") 221 }, 222 {(long)DoubleWordRegistersAArch32.VirtualTimerControl, 223 BuildTimerControlRegister(new DoubleWordRegister(this), el1VirtualTimer, "VirtualTimer") 224 }, 225 226 {(long)DoubleWordRegistersAArch32.PL1PhysicalTimerValue, 227 BuildTimerCountDownValueRegister(new DoubleWordRegister(this), el1PhysicalTimer, "PL1PhysicalTimer", 32) 228 }, 229 {(long)DoubleWordRegistersAArch32.PL2PhysicalTimerValue, 230 BuildTimerCountDownValueRegister(new DoubleWordRegister(this), nonSecureEL2PhysicalTimer, "PL2PhysicalTimer", 32) 231 }, 232 {(long)DoubleWordRegistersAArch32.VirtualTimerValue, 233 BuildTimerCountDownValueRegister(new DoubleWordRegister(this), el1VirtualTimer, "VirtualTimer", 32) 234 } 235 }; 236 return registersMap; 237 } 238 BuildQuadWordRegisterAArch32Map()239 private Dictionary<long, QuadWordRegister> BuildQuadWordRegisterAArch32Map() 240 { 241 var registersMap = new Dictionary<long, QuadWordRegister> 242 { 243 {(long)QuadWordRegistersAArch32.PhysicalCount, 244 BuildTimerCountValueRegister(el1PhysicalTimer, "Physical") 245 }, 246 {(long)QuadWordRegistersAArch32.VirtualCount, 247 BuildTimerCountValueRegister(el1VirtualTimer, "Virtual") 248 }, 249 250 {(long)QuadWordRegistersAArch32.VirtualOffset, 251 BuildTimerOffsetRegister(el1VirtualTimer, "Virtual") 252 }, 253 254 {(long)QuadWordRegistersAArch32.PL1PhysicalTimerCompareValue, 255 BuildTimerCompareValueRegister(el1PhysicalTimer, "PL1PhysicalTimer") 256 }, 257 {(long)QuadWordRegistersAArch32.PL2PhysicalTimerCompareValue, 258 BuildTimerCompareValueRegister(nonSecureEL2PhysicalTimer, "PL2PhysicalTimer") 259 }, 260 {(long)QuadWordRegistersAArch32.VirtualTimerCompareValue, 261 BuildTimerCompareValueRegister(el1VirtualTimer, "VirtualTimer") 262 } 263 }; 264 return registersMap; 265 } 266 267 private T BuildFrequencyRegister<T>(T register, string name, int registerWidth) where T : PeripheralRegister 268 { 269 // According to the documentation "the value of the register is not interpreted by hardware", 270 // it's there for software to discover a frequency of a timer set earlier by a bootloader. 271 return register 272 .WithReservedBits(32, registerWidth - 32) 273 .WithValueField(0, 32, name: name, 274 valueProviderCallback: _ => CounterFrequencyRegister, 275 writeCallback: (_, val) => 276 { 277 CounterFrequencyRegister = (uint)val; 278 if(Frequency != CounterFrequencyRegister) 279 { 280 this.Log(LogLevel.Warning, "Setting the counter frequency register to 0x{0:X} ({0}Hz), which is different than timer frequency ({1}Hz).", val, Frequency); 281 } 282 } 283 ); 284 } 285 BuildTimerControlRegister(QuadWordRegister register, TimerUnit timer, string namePrefix)286 private QuadWordRegister BuildTimerControlRegister(QuadWordRegister register, TimerUnit timer, string namePrefix) 287 { 288 return BuildTimerControlGenericRegister(register, timer, namePrefix, 64) 289 .WithWriteCallback((_, __) => timer.UpdateInterrupt()); 290 } 291 BuildTimerControlRegister(DoubleWordRegister register, TimerUnit timer, string namePrefix)292 private DoubleWordRegister BuildTimerControlRegister(DoubleWordRegister register, TimerUnit timer, string namePrefix) 293 { 294 return BuildTimerControlGenericRegister(register, timer, namePrefix, 32) 295 .WithWriteCallback((_, __) => timer.UpdateInterrupt()); 296 } 297 298 private T BuildTimerControlGenericRegister<T>(T register, TimerUnit timer, string namePrefix, int registerWidth) where T : PeripheralRegister 299 { 300 return register 301 .WithReservedBits(3, registerWidth - 3) 302 .WithFlag(2, FieldMode.Read, name: $"{namePrefix}InterruptStatus", 303 valueProviderCallback: _ => timer.InterruptStatus 304 ) 305 .WithFlag(1, name: $"{namePrefix}InterruptMask", 306 writeCallback: (_, val) => timer.InterruptMask = val, 307 valueProviderCallback: _ => timer.InterruptMask 308 ) 309 .WithFlag(0, name: $"{namePrefix}InterruptEnable", 310 writeCallback: (_, val) => timer.InterruptEnable = val, 311 valueProviderCallback: _ => timer.InterruptEnable 312 ); 313 } 314 315 private T BuildTimerCountDownValueRegister<T>(T register, TimerUnit timer, string namePrefix, int registerWidth) where T : PeripheralRegister 316 { 317 return register 318 .WithReservedBits(32, registerWidth - 32) 319 .WithValueField(0, 32, name: $"{namePrefix}Value", 320 writeCallback: (_, val) => 321 { 322 timer.CountDownValue = (int)val; 323 timer.UpdateInterrupt(); 324 }, 325 valueProviderCallback: _ => (ulong)timer.CountDownValue 326 ); 327 } 328 BuildTimerCountValueRegister(TimerUnit timer, string namePrefix)329 private QuadWordRegister BuildTimerCountValueRegister(TimerUnit timer, string namePrefix) 330 { 331 return new QuadWordRegister(this) 332 .WithValueField(0, 64, FieldMode.Read, name: $"{namePrefix}Count", 333 valueProviderCallback: _ => timer.Value 334 ); 335 } 336 BuildTimerOffsetRegister(TimerUnit timer, string namePrefix)337 private QuadWordRegister BuildTimerOffsetRegister(TimerUnit timer, string namePrefix) 338 { 339 return new QuadWordRegister(this) 340 .WithValueField(0, 64, name: $"{namePrefix}Offset", 341 writeCallback: (_, val) => 342 { 343 timer.Offset = val; 344 timer.UpdateInterrupt(); 345 }, 346 valueProviderCallback: _ => timer.Offset 347 ); 348 } 349 BuildTimerCompareValueRegister(TimerUnit timer, string namePrefix)350 private QuadWordRegister BuildTimerCompareValueRegister(TimerUnit timer, string namePrefix) 351 { 352 return new QuadWordRegister(this) 353 .WithValueField(0, 64, name: $"{namePrefix}CompareValue", 354 writeCallback: (_, val) => 355 { 356 timer.CompareValue = val; 357 timer.UpdateInterrupt(); 358 }, 359 valueProviderCallback: _ => timer.CompareValue 360 ); 361 } 362 363 private readonly TimerUnit el1PhysicalTimer; 364 private readonly TimerUnit el1VirtualTimer; 365 private readonly TimerUnit el3PhysicalTimer; 366 private readonly TimerUnit nonSecureEL2PhysicalTimer; 367 private readonly TimerUnit nonSecureEL2VirtualTimer; 368 private readonly QuadWordRegisterCollection registersAArch64; 369 private readonly DoubleWordRegisterCollection doubleWordRegistersAArch32; 370 private readonly QuadWordRegisterCollection quadWordRegistersAArch32; 371 private readonly IClockSource clockSource; 372 private readonly uint defaultCounterFrequencyRegister; 373 374 private enum RegistersAArch64 375 { 376 // Enum values are created from op0, op1, CRn, CRm and op2 fields of the MRS instruction. 377 Frequency = 0xdf00, // CNTFRQ_EL0 378 HypervisorControl = 0xe708, // CNTHCTL_EL2 379 KernelControl = 0xc708, // CNTKCTL_EL1 380 381 PhysicalCount = 0xdf01, // CNTPCT_EL0 382 PhysicalOffset = 0xe706, // CNTPOFF_EL2 383 PhysicalSelfSynchronizedCount = 0xdf05, // CNTPCTSS_EL0 384 385 VirtualCount = 0xdf02, // CNTVCT_EL0 386 VirtualOffset = 0xe703, // CNTVOFF_EL2 387 VirtualSelfSynchronizedCount = 0xdf06, // CNTVCTSS_EL0 388 389 EL1PhysicalTimerValue = 0xdf10, // CNTP_TVAL_EL0 390 EL1PhysicalTimerControl = 0xdf11, // CNTP_CTL_EL0 391 EL1PhysicalTimerCompareValue = 0xdf12, // CNTP_CVAL_EL0 392 EL1VirtualTimerValue = 0xdf18, // CNTV_TVAL_EL0 393 EL1VirtualTimerControl = 0xdf19, // CNTV_CTL_EL0 394 EL1VirtualTimerCompareValue = 0xdf1a, // CNTV_CVAL_EL0 395 396 EL3PhysicalTimerValue = 0xff10, // CNTPS_TVAL_EL1 397 EL3PhysicalTimerControl = 0xff11, // CNTPS_CTL_EL1 398 EL3PhysicalTimerCompareValue = 0xff12, // CNTPS_CVAL_EL1 399 400 NonSecureEL2PhysicalTimerValue = 0xe710, // CNTHP_TVAL_EL2 401 NonSecureEL2PhysicalTimerControl = 0xe711, // CNTHP_CTL_EL2 402 NonSecureEL2PhysicalTimerCompareValue = 0xe712, // CNTHP_CVAL_EL2 403 NonSecureEL2VirtualTimerValue = 0xe718, // CNTHV_TVAL_EL2 404 NonSecureEL2VirtualTimerControl = 0xe719, // CNTHV_CTL_EL2 405 NonSecureEL2VirtualTimerCompareValue = 0xe71a, // CNTHV_CVAL_EL2 406 407 // Secure EL2 timers are added by ARMv8.4-SecEL2 extension. 408 SecureEL2PhysicalTimerValue = 0xe728, // CNTHPS_TVAL_EL2 409 SecureEL2PhysicalTimerControl = 0xe729, // CNTHPS_CTL_EL2 410 SecureEL2PhysicalTimerCompareValue = 0xe72a, // CNTHPS_CVAL_EL2 411 SecureEL2VirtualTimerValue = 0xe720, // CNTHVS_TVAL_EL2 412 SecureEL2VirtualTimerControl = 0xe721, // CNTHVS_CTL_EL2 413 SecureEL2VirtualTimerCompareValue = 0xe722, // CNTHVS_CVAL_EL2 414 } 415 416 private enum DoubleWordRegistersAArch32 : uint 417 { 418 // Enum values are created from opc1, CRn, opc2 and CRm fields of the MRC instruction. 419 Frequency = 0x0e0000, // CNTFRQ 420 PL1PhysicalControl = 0x0e0001, // CNTKCTL 421 PL1PhysicalTimerValue = 0x0e0002, // CNTP_TVAL 422 PL1PhysicalTimerControl = 0x0e0022, // CNTP_CTL 423 VirtualTimerValue = 0x0e0003, // CNTV_TVAL 424 VirtualTimerControl = 0x0e0023, // CNTV_CTL 425 PL2PhysicalControl = 0x8e0001, // CNTHCTL 426 PL2PhysicalTimerValue = 0x8e0002, // CNTHP_TVAL 427 PL2PhysicalTimerControl = 0x8e0022// CNTHP_CTL PL2 428 }; 429 430 private enum QuadWordRegistersAArch32 : uint 431 { 432 // Enum values are created from the opc1 and CRm fields of the MRRC instruction. 433 PhysicalCount = 0x0e, // CNTPCT 434 VirtualCount = 0x1e, // CNTVCT 435 PL1PhysicalTimerCompareValue = 0x2e, // CNTP_CVAL 436 VirtualTimerCompareValue = 0x3e, // CNTV_CVAL 437 VirtualOffset = 0x4e, // CNTVOFF 438 PL2PhysicalTimerCompareValue = 0x6e // CNTHP_CVAL 439 }; 440 441 private class TimerUnit 442 { TimerUnit(IClockSource clockSource, IPeripheral parent, string name, GPIO irq, long frequency)443 public TimerUnit(IClockSource clockSource, IPeripheral parent, string name, GPIO irq, long frequency) 444 { 445 this.clockSource = clockSource; 446 this.name = name; 447 this.parent = parent; 448 IRQ = irq; 449 450 timer = new ComparingTimer(clockSource, frequency, parent, name, limit: ulong.MaxValue, compare: ulong.MaxValue, enabled: true, eventEnabled: true); 451 timer.CompareReached += OnCompareReached; 452 } 453 Reset()454 public void Reset() 455 { 456 offset = 0; 457 timer.Reset(); 458 459 InterruptMask = false; 460 InterruptEnable = false; 461 UpdateStatus(); 462 UpdateInterrupt(); 463 } 464 UpdateInterrupt()465 public void UpdateInterrupt() 466 { 467 var value = InterruptStatus && !InterruptMask && InterruptEnable; 468 if(value != IRQ.IsSet) 469 { 470 parent.Log(LogLevel.Debug, "{0}: {1} IRQ", name, value ? "setting" : "unsetting"); 471 IRQ.Set(value); 472 } 473 } 474 475 public GPIO IRQ { get; } 476 477 public ulong Value => timer.Value; 478 479 // The offset property is defined as the difference between value properties of a some parent timer and the timer itself. 480 // For example the Virtual timer has an offset over the Physical timer. 481 public ulong Offset 482 { 483 get => offset; 484 set 485 { 486 clockSource.ExecuteInLock(() => 487 { 488 var offsetDiff = value - offset; 489 timer.Value -= offsetDiff; 490 offset += offsetDiff; 491 UpdateStatus(); 492 }); 493 } 494 } 495 496 public ulong CompareValue 497 { 498 get => timer.Compare; 499 set 500 { 501 clockSource.ExecuteInLock(() => 502 { 503 timer.Compare = value; 504 UpdateStatus(); 505 }); 506 } 507 } 508 509 public int CountDownValue 510 { 511 get 512 { 513 int value = 0; 514 clockSource.ExecuteInLock(() => 515 { 516 value = (int)(CompareValue - Value); 517 }); 518 return value; 519 } 520 set 521 { 522 clockSource.ExecuteInLock(() => 523 { 524 CompareValue = Value + (ulong)value; 525 }); 526 } 527 } 528 529 public bool InterruptStatus { get; private set; } 530 public bool InterruptMask { get; set; } 531 public bool InterruptEnable { get; set; } 532 UpdateStatus()533 private void UpdateStatus() 534 { 535 InterruptStatus = Value >= CompareValue; 536 // This method is typically called inside the IClockSource.ExecuteInLock() helper. 537 // To prevent deadlock it doesn't call the UpdateInterrupt() method. 538 } 539 OnCompareReached()540 private void OnCompareReached() 541 { 542 InterruptStatus = true; 543 UpdateInterrupt(); 544 } 545 546 private ulong offset; 547 548 private readonly IClockSource clockSource; 549 private readonly string name; 550 private readonly IPeripheral parent; 551 private readonly ComparingTimer timer; 552 } 553 } 554 } 555