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;
8 using System.Collections.Generic;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Exceptions;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Peripherals.Miscellaneous;
15 
16 namespace Antmicro.Renode.Peripherals.Timers
17 {
18     public class NRF52840_RTC : IDoubleWordPeripheral, IKnownSize, INRFEventProvider
19     {
NRF52840_RTC(IMachine machine, int numberOfEvents)20         public NRF52840_RTC(IMachine machine, int numberOfEvents)
21         {
22             IRQ = new GPIO();
23 
24             if(numberOfEvents > MaxNumberOfEvents)
25             {
26                 throw new ConstructionException($"Cannot create {nameof(NRF52840_Timer)} with {numberOfEvents} events (must be less than {MaxNumberOfEvents})");
27             }
28             this.numberOfEvents = numberOfEvents;
29             compareEventEnabled = new IFlagRegisterField[numberOfEvents];
30             compareReached = new IFlagRegisterField[numberOfEvents];
31             compareInterruptEnabled = new IFlagRegisterField[numberOfEvents];
32 
33             innerTimers = new ComparingTimer[numberOfEvents];
34             for(var i = 0u; i < innerTimers.Length; i++)
35             {
36                 var j = i;
37                 // counters are 24-bits
38                 innerTimers[i] = new ComparingTimer(machine.ClockSource, InitialFrequency, this, $"compare{i}", eventEnabled: true, limit: 0xFFFFFF, compare: 0xFFFFFF);
39                 innerTimers[i].CompareReached += () =>
40                 {
41                     this.Log(LogLevel.Noisy, "IRQ #{0} triggered", j);
42                     compareReached[j].Value = true;
43                     if(compareEventEnabled[j].Value)
44                     {
45                        EventTriggered?.Invoke((uint)Register.Compare0EventPending + j * 4);
46                     }
47                     UpdateInterrupts();
48                 };
49             }
50 
51             tickTimer = new LimitTimer(machine.ClockSource, InitialFrequency, this, "tick", eventEnabled: true, limit: 0x1);
52             tickTimer.LimitReached += () =>
53             {
54                 tickEvent.Value = true;
55                 UpdateInterrupts();
56             };
57 
58             DefineRegisters();
59         }
60 
ReadDoubleWord(long offset)61         public uint ReadDoubleWord(long offset)
62         {
63             return registers.Read(offset);
64         }
65 
WriteDoubleWord(long offset, uint value)66         public void WriteDoubleWord(long offset, uint value)
67         {
68             registers.Write(offset, value);
69         }
70 
Reset()71         public void Reset()
72         {
73             registers.Reset();
74             foreach(var timer in innerTimers)
75             {
76                 timer.Reset();
77             }
78             tickTimer.Reset();
79             IRQ.Unset();
80         }
81 
82         public GPIO IRQ { get; }
83 
84         public long Size => 0x1000;
85 
86         public event Action<uint> EventTriggered;
87 
88         private void UpdateTimersEnable(bool? global = null, bool? tick = null)
89         {
90             if(global.HasValue)
91             {
92                 foreach(var timer in innerTimers)
93                 {
94                     timer.Enabled = global.Value;
95                 }
96             }
97 
98             // due to optimization reasons we try to keep
99             // the tick timer disabled as long as possible
100             // - we enable it only when the global timer is
101             // enabled and the tick event is unmasked
102             if(tick.HasValue || (global.HasValue && !global.Value))
103             {
104                 tickTimer.Enabled = innerTimers[0].Enabled && tick.Value;
105             }
106         }
107 
DefineRegisters()108         private void DefineRegisters()
109         {
110             var registersMap = new Dictionary<long, DoubleWordRegister>
111             {
112                 {(long)Register.Start, new DoubleWordRegister(this)
113                     .WithFlag(0, FieldMode.Write, name: "TASKS_START", writeCallback: (_, value) =>
114                     {
115                         if(value)
116                         {
117                             UpdateTimersEnable(global: true);
118                         }
119                     })
120                     .WithReservedBits(1, 31)
121                 },
122                 {(long)Register.Stop, new DoubleWordRegister(this)
123                     .WithFlag(0, FieldMode.Write, name: "TASKS_STOP", writeCallback: (_, value) =>
124                     {
125                         if(value)
126                         {
127                             UpdateTimersEnable(global: false);
128                         }
129                     })
130                     .WithReservedBits(1, 31)
131                 },
132                 {(long)Register.Clear, new DoubleWordRegister(this)
133                     .WithFlag(0, FieldMode.Write, name: "TASKS_CLEAR", writeCallback: (_, value) =>
134                     {
135                         if(value)
136                         {
137                             foreach(var timer in innerTimers)
138                             {
139                                 timer.Value = 0;
140                             }
141                             tickTimer.Value = 0;
142                         }
143                     })
144                     .WithReservedBits(1, 31)
145                 },
146                 {(long)Register.InterruptEnableSet, new DoubleWordRegister(this)
147                     .WithFlag(0, out tickInterruptEnabled, FieldMode.Set | FieldMode.Read, name: "TICK")
148                     .WithTaggedFlag("OVRFLW", 1)
149                     .WithReservedBits(2, 14)
150                     .WithFlags(16, numberOfEvents, out compareInterruptEnabled, FieldMode.Set | FieldMode.Read, name: "COMPARE")
151                     .WithChangeCallback((_, __) =>
152                     {
153                         UpdateTimersEnable(tick: tickInterruptEnabled.Value);
154                         UpdateInterrupts();
155                     })
156                 },
157                 {(long)Register.InterruptEnableClear, new DoubleWordRegister(this)
158                     .WithFlag(0, name: "TICK",
159                           writeCallback: (_, value) => tickInterruptEnabled.Value &= !value,
160                           valueProviderCallback: _ => tickInterruptEnabled.Value)
161                     .WithTaggedFlag("OVRFLW", 1)
162                     .WithReservedBits(2, 14)
163                     .WithFlags(16, numberOfEvents, name: "COMPARE",
164                           writeCallback: (j, _, value) => compareInterruptEnabled[j].Value &= !value,
165                           valueProviderCallback: (j, value) => compareInterruptEnabled[j].Value)
166                     //missing register fields defined below
167                     .WithChangeCallback((_, __) =>
168                     {
169                         UpdateTimersEnable(tick: tickInterruptEnabled.Value);
170                         UpdateInterrupts();
171                     })
172                 },
173                 {(long)Register.Counter, new DoubleWordRegister(this)
174                     .WithValueField(0, 24, FieldMode.Read, name: "COUNTER", valueProviderCallback: _ =>
175                     {
176                         // all timers have the same value, so let's just pick the first one
177                         return (uint)innerTimers[0].Value;
178                     })
179                     .WithReservedBits(24, 8)
180                 },
181                 {(long)Register.Prescaler, new DoubleWordRegister(this)
182                     .WithValueField(0, 12, out prescaler, name: "PRESCALER", writeCallback: (_, value) =>
183                     {
184                         foreach(var timer in innerTimers)
185                         {
186                             timer.Divider = (uint)(value + 1u);
187                         }
188                         tickTimer.Divider = (int)(value + 1);
189                     })
190                     .WithReservedBits(12, 20)
191                 },
192                 {(long)Register.EventEnable, new DoubleWordRegister(this)
193                    .WithFlags(16, numberOfEvents, out compareEventEnabled, name: "COMPARE")
194                 },
195                 {(long)Register.EventSet, new DoubleWordRegister(this)
196                    .WithTaggedFlag("TICK", 0)
197                    .WithTaggedFlag("OVRFLW", 1)
198                    .WithReservedBits(2, 14)
199                    .WithFlags(16, numberOfEvents,
200                          writeCallback: (i, _, val) => compareEventEnabled[i].Value |= val,
201                          valueProviderCallback: (i, _) => compareEventEnabled[i].Value)
202                 },
203                 {(long)Register.EventClear, new DoubleWordRegister(this)
204                    .WithFlags(16, numberOfEvents,
205                          writeCallback: (i, _, val) => compareEventEnabled[i].Value &= !val,
206                          valueProviderCallback: (i, _) => compareEventEnabled[i].Value)
207                 },
208                 {(long)Register.Tick, new DoubleWordRegister(this)
209                    .WithFlag(0, out tickEvent, name: "EVENTS_TICK")
210                    .WithReservedBits(1, 31)
211                    .WithWriteCallback((_, __) => UpdateInterrupts())
212                 }
213             };
214 
215             for(var i = 0; i < numberOfEvents; i++)
216             {
217                 var j = i;
218                 registersMap.Add((long)Register.Compare0 + j * 4, new DoubleWordRegister(this)
219                     .WithValueField(0, 24, name: $"COMPARE[{j}]", writeCallback: (_, value) =>
220                     {
221                         compareReached[j].Value = false;
222                         UpdateInterrupts();
223                         innerTimers[j].Compare = value;
224                     },
225                     valueProviderCallback: _ =>
226                     {
227                         return (uint)innerTimers[j].Compare;
228                     })
229                     .WithReservedBits(24, 8)
230                 );
231 
232                 registersMap.Add((long)Register.Compare0EventPending + j * 4, new DoubleWordRegister(this)
233                     .WithFlag(0, out compareReached[j], name: $"EVENTS_COMPARE[{j}]", writeCallback: (_, __) =>
234                     {
235                         UpdateInterrupts();
236                     })
237                     .WithReservedBits(1, 31)
238                 );
239             }
240 
241             registers = new DoubleWordRegisterCollection(this, registersMap);
242         }
243 
UpdateInterrupts()244         private void UpdateInterrupts()
245         {
246             var flag = false;
247 
248             for(var i = 0; i < numberOfEvents; i++)
249             {
250                 var thisEventSet = compareInterruptEnabled[i].Value && compareReached[i].Value;
251                 if(thisEventSet)
252                 {
253                    this.Log(LogLevel.Noisy, "Interrupt set by CC{0} interruptEnable={1} compareSet={2} compareEventEnable={3}",
254                          i, compareInterruptEnabled[i].Value, compareReached[i].Value, compareEventEnabled[i].Value);
255                 }
256                 flag |= thisEventSet;
257             }
258 
259             flag |= tickEvent.Value && tickInterruptEnabled.Value;
260             IRQ.Set(flag);
261         }
262 
263         private DoubleWordRegisterCollection registers;
264         private IFlagRegisterField[] compareEventEnabled;
265         private IFlagRegisterField[] compareReached;
266         private IFlagRegisterField[] compareInterruptEnabled;
267         private IValueRegisterField prescaler;
268         private IFlagRegisterField tickInterruptEnabled;
269         private IFlagRegisterField tickEvent;
270 
271         private readonly LimitTimer tickTimer;
272         private readonly ComparingTimer[] innerTimers;
273 
274         private readonly int numberOfEvents;
275         private const int InitialFrequency = 32768;
276         private const int MaxNumberOfEvents = 4;
277 
278         private enum Register : long
279         {
280             Start = 0x000,
281             Stop = 0x004,
282             Clear = 0x008,
283             TriggerOverflow = 0x00C,
284             Tick = 0x100,
285             Overflow = 0x104,
286             Compare0EventPending = 0x140,
287             Compare1EventPending = 0x144,
288             Compare2EventPending = 0x148,
289             Compare3EventPending = 0x14C,
290             InterruptEnableSet = 0x304,
291             InterruptEnableClear = 0x308,
292             EventEnable = 0x340,
293             EventSet = 0x344,
294             EventClear = 0x348,
295             Counter = 0x504,
296             Prescaler = 0x508,
297             Compare0 = 0x540,
298             Compare1 = 0x544,
299             Compare2 = 0x548,
300             Compare3 = 0x54C
301         }
302     }
303 }
304