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 Antmicro.Renode.Core; 8 using Antmicro.Renode.Logging; 9 using Antmicro.Renode.Utilities; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Peripherals.Timers; 12 using Antmicro.Renode.Time; 13 14 namespace Antmicro.Renode.Peripherals.Timers 15 { 16 public class NXP_OsTimer : BasicDoubleWordPeripheral, IKnownSize 17 { NXP_OsTimer(IMachine machine, long frequency)18 public NXP_OsTimer(IMachine machine, long frequency) : base(machine) 19 { 20 innerTimer = new ComparingTimer(machine.ClockSource, frequency, this, nameof(innerTimer), workMode: WorkMode.Periodic, eventEnabled: true, direction: Direction.Ascending, enabled: true); 21 innerTimer.CompareReached += () => 22 { 23 this.Log(LogLevel.Debug, "Compare value reached"); 24 irqFlag.Value = true; 25 UpdateInterrupt(); 26 }; 27 28 DefineRegisters(); 29 } 30 31 public GPIO IRQ { get; } = new GPIO(); 32 33 public long Size => 0x100; 34 DefineRegisters()35 private void DefineRegisters() 36 { 37 Registers.MatchLow.Define(this) 38 .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, val) => 39 { 40 // it's not fully clear if HW does the latching, 41 // but the SDK driver is always acccessing the Low register first 42 // hence this implementation 43 matchLowLatched = (uint)val; 44 }); 45 46 Registers.MatchHigh.Define(this) 47 .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, val) => 48 { 49 // here we assume that the driver will first write the Match Low 50 // register and set the correct `matchLowLatched` value 51 if(!matchLowLatched.HasValue) 52 { 53 this.Log(LogLevel.Error, "Match low register expected to be set before the match high one"); 54 matchLowLatched = (uint)Misc.BinaryToGray((uint)innerTimer.Compare); 55 } 56 57 var nextCompare = ((ulong)val << 32) + matchLowLatched.Value; 58 nextCompare = Misc.GrayToBinary(nextCompare); 59 60 this.Log(LogLevel.Debug, "Changing compare value from 0x{0:X} to 0x{0:X}", innerTimer.Compare, nextCompare); 61 innerTimer.Compare = nextCompare; 62 matchLowLatched = null; 63 }); 64 65 Registers.CaptureLow.Define(this) 66 .WithReservedBits(0, 32); 67 68 Registers.CaptureHigh.Define(this) 69 .WithReservedBits(0, 32); 70 71 Registers.EventTimerHigh.Define(this) 72 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => 73 { 74 SyncTime(); 75 return Misc.BinaryToGray(innerTimer.Value) >> 32; 76 }); 77 78 Registers.EventTimerLow.Define(this) 79 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => 80 { 81 SyncTime(); 82 return Misc.BinaryToGray(innerTimer.Value); 83 }); 84 85 Registers.Control.Define(this) 86 .WithFlag(0, out irqFlag, FieldMode.WriteOneToClear | FieldMode.Read) 87 .WithFlag(1, out irqEnabled) 88 .WithFlag(2, name: "MATCH_WR_RDY - EVTimer Match Write Ready", valueProviderCallback: _ => false) // by looking at the driver, it seems this needs to be 0 to allow for match register to be set 89 .WithWriteCallback((_, __) => UpdateInterrupt()); 90 } 91 SyncTime()92 private void SyncTime() 93 { 94 if(machine.GetSystemBus(this).TryGetCurrentCPU(out var cpu)) 95 { 96 cpu.SyncTime(); 97 } 98 } 99 UpdateInterrupt()100 private void UpdateInterrupt() 101 { 102 var flag = irqFlag.Value && irqEnabled.Value; 103 104 this.Log(LogLevel.Debug, "Setting IRQ to {0}", flag); 105 IRQ.Set(flag); 106 } 107 108 private IFlagRegisterField irqEnabled; 109 private IFlagRegisterField irqFlag; 110 private uint? matchLowLatched; 111 112 private readonly ComparingTimer innerTimer; 113 114 private enum Registers 115 { 116 EventTimerLow = 0x0, 117 EventTimerHigh = 0x4, 118 CaptureLow = 0x8, 119 CaptureHigh = 0xc, 120 MatchLow = 0x10, 121 MatchHigh = 0x14, 122 // reserved 123 Control = 0x1c, 124 } 125 } 126 } 127