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