1 //
2 // Copyright (c) 2010-2021 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 Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Structure.Registers;
10 using Antmicro.Renode.Exceptions;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Miscellaneous;
13 
14 namespace Antmicro.Renode.Peripherals.Timers
15 {
16     public class NRF52840_Timer : BasicDoubleWordPeripheral, IKnownSize, INRFEventProvider
17     {
NRF52840_Timer(IMachine machine, int numberOfEvents)18         public NRF52840_Timer(IMachine machine, int numberOfEvents) : base(machine)
19         {
20             IRQ = new GPIO();
21 
22             if(numberOfEvents > MaxNumberOfEvents)
23             {
24                 throw new ConstructionException($"Cannot create {nameof(NRF52840_Timer)} with {numberOfEvents} events (must be less than {MaxNumberOfEvents})");
25             }
26             this.numberOfEvents = numberOfEvents;
27 
28             this.eventCompareEnabled = new IFlagRegisterField[numberOfEvents];
29             innerTimers = new ComparingTimer[numberOfEvents];
30             for(var i = 0u; i < innerTimers.Length; i++)
31             {
32                 var j = i;
33                 innerTimers[j] = new ComparingTimer(machine.ClockSource, InitialFrequency, this, $"compare{j}", eventEnabled: true);
34                 innerTimers[j].CompareReached += () =>
35                 {
36                     this.Log(LogLevel.Noisy, "Compare Reached on CC{0} is {1}", j, innerTimers[j].Compare);
37                     eventCompareEnabled[j].Value = true;
38                     EventTriggered?.Invoke((uint)Register.Compare0EventPending + 0x4u * j);
39                     UpdateInterrupts();
40                 };
41             }
42 
43             DefineRegisters();
44         }
45 
Reset()46         public override void Reset()
47         {
48             base.Reset();
49 
50             foreach(var timer in innerTimers)
51             {
52                 timer.Reset();
53             }
54 
55             IRQ.Unset();
56         }
57 
58         public GPIO IRQ { get; }
59 
60         public long Size => 0x1000;
61 
62         public event Action<uint> EventTriggered;
63 
DefineRegisters()64         private void DefineRegisters()
65         {
66             Register.Start.Define(this)
67                 .WithFlag(0, FieldMode.Write, name: "TASKS_START", writeCallback: (_, value) =>
68                 {
69                     if(value)
70                     {
71                         timerRunning = true;
72                         if(mode.Value == Mode.Timer)
73                         {
74                             foreach(var timer in innerTimers)
75                             {
76                                 timer.Enabled = true;
77                             }
78                         }
79                     }
80                 })
81                 .WithReservedBits(1, 31)
82             ;
83 
84             Register.Stop.Define(this)
85                 .WithFlag(0, FieldMode.Write, name: "TASKS_STOP", writeCallback: (_, value) =>
86                 {
87                     if(value)
88                     {
89                         timerRunning = false;
90                         foreach(var timer in innerTimers)
91                         {
92                             timer.Enabled = false;
93                         }
94                     }
95                 })
96                 .WithReservedBits(1, 31)
97             ;
98 
99             Register.Count.Define(this)
100                 .WithFlag(0, FieldMode.Write, name: "TASK_COUNT", writeCallback: (_, value) =>
101                 {
102                     if(value)
103                     {
104                         if(!timerRunning)
105                         {
106                             this.Log(LogLevel.Warning, "Triggered TASK_COUNT before issuing TASK_START, ignoring...");
107                             return;
108                         }
109                         if(mode.Value == Mode.Timer)
110                         {
111                             this.Log(LogLevel.Warning, "Triggered TASK_COUNT in TIMER mode, ignoring...");
112                             return;
113                         }
114                         var i = 0;
115                         foreach(var timer in innerTimers)
116                         {
117                             timer.Value++;
118                             if(timer.Compare == timer.Value)
119                             {
120                                 eventCompareEnabled[i].Value = true;
121                                 UpdateInterrupts();
122                             }
123                             i++;
124                         }
125                     }
126                 })
127                 .WithReservedBits(1, 31)
128             ;
129 
130             Register.Clear.Define(this)
131                 .WithFlag(0, FieldMode.Write, name: "TASK_CLEAR", writeCallback: (_, value) =>
132                 {
133                     if(value)
134                     {
135                         foreach(var timer in innerTimers)
136                         {
137                             timer.Value = 0;
138                         }
139                     }
140                 })
141                 .WithReservedBits(1, 31)
142             ;
143 
144             Register.Capture0.DefineMany(this, (uint)numberOfEvents, setup: (register, idx) =>
145             {
146                 register
147                     .WithFlag(0, FieldMode.Write, name: "TASKS_CAPTURE", writeCallback: (_,__) =>
148                     {
149                         SetCompare(idx, innerTimers[idx].Value);
150                     })
151                     .WithReservedBits(1, 31);
152             });
153 
154             Register.Compare0EventPending.DefineMany(this, (uint)numberOfEvents, setup: (register, idx) =>
155             {
156                 register
157                     .WithFlag(0, out eventCompareEnabled[idx], name: $"EVENTS_COMPARE[{idx}]", writeCallback: (_,__) =>
158                     {
159                         UpdateInterrupts();
160                     })
161                     .WithReservedBits(1, 31);
162             });
163 
164             Register.InterruptEnableSet.Define(this)
165                 .WithReservedBits(0, 16)
166                 .WithFlags(16, numberOfEvents, out eventCompareInterruptEnabled, FieldMode.Set | FieldMode.Read, name: "COMPARE")
167                 .WithReservedBits(22 - MaxNumberOfEvents + numberOfEvents, 10 + MaxNumberOfEvents - numberOfEvents)
168                 .WithChangeCallback((_, __) =>
169                 {
170                     UpdateInterrupts();
171                 })
172             ;
173 
174             Register.InterruptEnableClear.Define(this)
175                 .WithReservedBits(0, 16)
176                 .WithFlags(16, numberOfEvents, name: "COMPARE", writeCallback: (i, _, value) => { if(value) eventCompareInterruptEnabled[i].Value = false; })
177                 .WithReservedBits(22 - MaxNumberOfEvents + numberOfEvents, 10 + MaxNumberOfEvents - numberOfEvents)
178                 .WithChangeCallback((_, __) =>
179                 {
180                     UpdateInterrupts();
181                 })
182             ;
183 
184             Register.Mode.Define(this)
185                 .WithEnumField(0, 2, out mode, name: "MODE", changeCallback: (_, value) =>
186                 {
187                     if(value != Mode.Timer && innerTimers[0].Enabled)
188                     {
189                         this.Log(LogLevel.Error, "Switching timer to COUNTER mode while the timer is running");
190                     }
191                 })
192             ;
193 
194             Register.Prescaler.Define(this)
195                 .WithValueField(0, 4, out prescaler, name: "PRESCALER", writeCallback: (_, value) =>
196                 {
197                     foreach(var timer in innerTimers)
198                     {
199                         timer.Divider = (uint)(1 << (int)value);
200                     }
201                 })
202                 .WithReservedBits(12, 20)
203             ;
204 
205             Register.Compare0.DefineMany(this, (uint)numberOfEvents, setup: (register, idx) =>
206             {
207                 register
208                     .WithValueField(0, 32, name: "CAPTURE_COMPARE", writeCallback: (_, value) =>
209                     {
210                         SetCompare(idx, value);
211                     },
212                     valueProviderCallback: _ =>
213                     {
214                         return (uint)innerTimers[idx].Compare;
215                     });
216             });
217         }
218 
SetCompare(int idx, ulong value)219         private void SetCompare(int idx, ulong value)
220         {
221             eventCompareEnabled[idx].Value = false;
222             UpdateInterrupts();
223             innerTimers[idx].Compare = value;
224         }
225 
UpdateInterrupts()226         private void UpdateInterrupts()
227         {
228             var flag = false;
229 
230             for(var i = 0; i < numberOfEvents; i++)
231             {
232                 flag |= eventCompareInterruptEnabled[i].Value && eventCompareEnabled[i].Value;
233             }
234 
235             IRQ.Set(flag);
236         }
237 
238         private IFlagRegisterField[] eventCompareEnabled;
239         private IFlagRegisterField[] eventCompareInterruptEnabled;
240         private IValueRegisterField prescaler;
241         private IEnumRegisterField<Mode> mode;
242         private bool timerRunning;
243 
244         private readonly ComparingTimer[] innerTimers;
245 
246         private readonly int numberOfEvents;
247         private const int InitialFrequency = 16000000;
248         private const int MaxNumberOfEvents = 6;
249 
250         private enum Mode
251         {
252             Timer,
253             Counter,
254             LowPowerCounter
255         }
256 
257         private enum Register : long
258         {
259             Start = 0x000,
260             Stop = 0x004,
261             Count = 0x008,
262             Clear = 0x00C,
263             Shutdown = 0x010,
264 
265             Capture0 = 0x040,
266             Capture1 = 0x044,
267             Capture2 = 0x048,
268             Capture3 = 0x04C,
269             Capture4 = 0x050,
270             Capture5 = 0x054,
271 
272             Compare0EventPending = 0x140,
273             Compare1EventPending = 0x144,
274             Compare2EventPending = 0x148,
275             Compare3EventPending = 0x14C,
276             Compare4EventPending = 0x150,
277             Compare5EventPending = 0x154,
278 
279             Shortcuts = 0x200,
280 
281             InterruptEnableSet = 0x304,
282             InterruptEnableClear = 0x308,
283 
284             Mode = 0x504,
285             BitMode = 0x508,
286             Prescaler = 0x510,
287 
288             Compare0 = 0x540,
289             Compare1 = 0x544,
290             Compare2 = 0x548,
291             Compare3 = 0x54C,
292             Compare4 = 0x550,
293             Compare5 = 0x554
294         }
295     }
296 }
297