1 //
2 // Copyright (c) 2010-2023 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 System.Collections.ObjectModel;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 using Antmicro.Renode.Time;
14 
15 namespace Antmicro.Renode.Peripherals.Timers
16 {
17     public class S32K_LPTMR : BasicDoubleWordPeripheral, IKnownSize
18     {
S32K_LPTMR(IMachine machine, long frequency)19         public S32K_LPTMR(IMachine machine, long frequency) : base(machine)
20         {
21             innerTimer = new ComparingTimer(machine.ClockSource, frequency, this, "lptmr", limit: 0xFFFF, direction: Direction.Ascending,
22                 enabled: false, eventEnabled: true, workMode: WorkMode.Periodic, compare: 0xFFFF, divider: 2);
23 
24             innerTimer.CompareReached += CompareReached;
25 
26             IRQ = new GPIO();
27 
28             DefineRegisters();
29         }
30 
Reset()31         public override void Reset()
32         {
33             base.Reset();
34             innerTimer.Reset();
35             prescaleValue = 0;
36             prescalerBypass = false;
37             compare = 0;
38             latchedTimerValue = 0;
39             UpdateInterrupt();
40         }
41 
42         public GPIO IRQ { get; }
43 
44         public long Size => 0x1000;
45 
UpdateInterrupt()46         private void UpdateInterrupt()
47         {
48             var value = true;
49             value &= compareFlag.Value;
50             value &= interruptEnable.Value;
51             IRQ.Set(value);
52         }
53 
CompareReached()54         private void CompareReached()
55         {
56             compareFlag.Value = true;
57             if(!freeRunningCounter.Value)
58             {
59                 innerTimer.Value = 0;
60             }
61             UpdateInterrupt();
62         }
63 
UpdateDivider()64         private void UpdateDivider()
65         {
66             if(!prescalerBypass)
67             {
68                 innerTimer.Divider = (uint)System.Math.Pow(2, prescaleValue + 1);
69             }
70         }
71 
DefineRegisters()72         private void DefineRegisters()
73         {
74             Registers.ControlStatus.Define(this)
75                 .WithFlag(0, out enabled, name: "TEN", changeCallback: (_, value) =>
76                     {
77                         innerTimer.Enabled = value;
78                         if(!value)
79                         {
80                             innerTimer.Value = 0;
81                             compareFlag.Value = false;
82                         }
83                     })
84                 .WithTaggedFlag("TMS", 1)
85                 .WithFlag(2, out freeRunningCounter, name: "TFC")
86                 .WithTaggedFlag("TPP", 3)
87                 .WithTag("TPS", 4, 2)
88                 .WithFlag(6, out interruptEnable, name: "TIE")
89                 .WithFlag(7, out compareFlag, FieldMode.Read | FieldMode.WriteOneToClear, name: "TCF")
90                 .WithTaggedFlag("TDRE", 8)
91                 .WithReservedBits(9, 23)
92                 .WithWriteCallback((_, __) => UpdateInterrupt())
93             ;
94 
95             Registers.Prescale.Define(this)
96                 .WithTag("PCS", 0, 2)
97                 .WithFlag(2, name: "PBYP", changeCallback: (_, value) =>
98                     {
99                         if(enabled.Value)
100                         {
101                             this.Log(LogLevel.Warning, "Trying to update the prescaler bypass value with LPTIMR enabled - ignoring...");
102                             return;
103                         }
104                         prescalerBypass = value;
105                     })
106                 .WithValueField(3, 4, name: "PRESCALE", changeCallback: (_, value) =>
107                     {
108                         if(enabled.Value)
109                         {
110                             this.Log(LogLevel.Warning, "Trying to update the prescale value with LPTIMR enabled - ignoring...");
111                             return;
112                         }
113                         prescaleValue = (uint)value;
114                     }, valueProviderCallback: _ => prescaleValue) // we keep the prescaleValue not to have to calculate logarithms
115                 .WithReservedBits(7, 25)
116                 .WithWriteCallback((_, __) => UpdateDivider())
117             ;
118 
119             Registers.Compare.Define(this)
120                 .WithValueField(0, 16, name: "CMR", writeCallback: (_, value) =>
121                     {
122                         if(enabled.Value && !compareFlag.Value)
123                         {
124                             this.Log(LogLevel.Warning, "Trying to update the compare value while the timer is enabled and TCF is not set, ignoring...");
125                             return;
126                         }
127                         if(value == 0)
128                         {
129                             //set tcf until disabled. May be unhandled, because setting compare to 0 can be difficult
130                             this.Log(LogLevel.Warning, "Trying to set CMR to 0, this is currently not handled");
131                         }
132                         compare = (uint)value;
133 
134                     })
135                 .WithReservedBits(16, 16)
136             ;
137 
138             // value written is not relevant - used only to latch the current value
139             Registers.Counter.Define(this)
140                 .WithValueField(0, 16, writeCallback: (_, __) => latchedTimerValue = (uint)innerTimer.Value, valueProviderCallback: _ => latchedTimerValue, name: "CNR")
141                 .WithReservedBits(16, 16)
142             ;
143         }
144 
145         private IFlagRegisterField enabled;
146         private IFlagRegisterField compareFlag;
147         private IFlagRegisterField interruptEnable;
148         private IFlagRegisterField freeRunningCounter;
149 
150         private uint compare;
151         private uint latchedTimerValue;
152         private bool prescalerBypass;
153         private uint prescaleValue;
154 
155         private readonly ComparingTimer innerTimer;
156 
157         private enum Registers
158         {
159             ControlStatus = 0x0,
160             Prescale = 0x4,
161             Compare = 0x8,
162             Counter = 0xC,
163         }
164     }
165 }
166