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 Antmicro.Renode.Core;
8 using Antmicro.Renode.Core.Structure.Registers;
9 using Antmicro.Renode.Logging;
10 using System;
11 using System.Linq;
12 
13 namespace Antmicro.Renode.Peripherals.Timers
14 {
15     public class EFR32_RTCC : BasicDoubleWordPeripheral, IKnownSize
16     {
EFR32_RTCC(IMachine machine, long frequency)17         public EFR32_RTCC(IMachine machine, long frequency) : base(machine)
18         {
19             IRQ = new GPIO();
20 
21             captureCompareInterruptPending = new IFlagRegisterField[NumberOfCaptureCompareChannels];
22             captureCompareInterruptEnabled = new IFlagRegisterField[NumberOfCaptureCompareChannels];
23 
24             innerTimer = new EFR32_RTCCCounter(machine, frequency, this, "rtcc", CounterWidth, PreCounterWidth, NumberOfCaptureCompareChannels);
25             innerTimer.LimitReached += delegate
26             {
27                 overflowInterrupt.Value = true;
28                 UpdateInterrupts();
29             };
30             innerTimer.CounterTicked += delegate
31             {
32                 counterTick.Value = true;
33                 UpdateInterrupts();
34             };
35 
36             for(var idx = 0; idx < NumberOfCaptureCompareChannels; ++idx)
37             {
38                 var i = idx;
39                 innerTimer.Channels[i].CompareReached += delegate
40                 {
41                     captureCompareInterruptPending[i].Value = true;
42                     UpdateInterrupts();
43                 };
44             }
45 
46             DefineRegisters();
47         }
48 
Reset()49         public override void Reset()
50         {
51             base.Reset();
52             innerTimer.Reset();
53             UpdateInterrupts();
54         }
55 
56         public long Size => 0x400;
57 
58         public GPIO IRQ { get; }
59 
DefineRegisters()60         private void DefineRegisters()
61         {
62             Register.Control.Define(this)
63                 .WithFlag(0,
64                     writeCallback: (_, value) => innerTimer.Enabled = value,
65                     valueProviderCallback: _ => innerTimer.Enabled,
66                     name: "ENABLE")
67                 .WithReservedBits(1, 1)
68                 .WithTaggedFlag("DEBUGRUN", 2)
69                 .WithReservedBits(3, 1)
70                 .WithTaggedFlag("PRECCV0TOP", 4)
71                 .WithTaggedFlag("CCV1TOP", 5)
72                 .WithReservedBits(6, 2)
73                 .WithValueField(8, 4,
74                     writeCallback: (_, value) => innerTimer.Prescaler = (int)Math.Pow(2, value),
75                     valueProviderCallback: _ => (uint)Math.Log(innerTimer.Prescaler, 2),
76                     name: "CNTPRESC")
77                 .WithTaggedFlag("CNTTICK", 12)
78                 .WithReservedBits(13, 2)
79                 .WithTaggedFlag("OSCFDETEN", 15)
80                 .WithTag("CNTMODE", 16, 1)
81                 .WithTaggedFlag("LYEARCORRDIS", 17)
82                 .WithReservedBits(18, 14)
83             ;
84 
85             Register.PreCounter.Define(this)
86                 .WithValueField(0, 15,
87                     writeCallback: (_, value) => innerTimer.PreCounter = value,
88                     valueProviderCallback: _ => innerTimer.PreCounter,
89                     name: "PRECNT")
90                 .WithReservedBits(15, 17)
91             ;
92 
93             Register.CounterValue.Define(this)
94                 .WithValueField(0, 32,
95                     writeCallback: (_, value) => innerTimer.Counter = value,
96                     valueProviderCallback: _ => innerTimer.Counter,
97                     name: "CNT")
98             ;
99 
100             Register.CombinedPreCouterValue.Define(this)
101                 .WithValueField(0, 15, FieldMode.Read,
102                     valueProviderCallback: _ => innerTimer.PreCounter,
103                     name: "PRECNT")
104                 .WithValueField(15, 17, FieldMode.Read,
105                     valueProviderCallback: _ => innerTimer.Counter,
106                     name: "CNTLSB")
107             ;
108 
109             Register.Time.Define(this)
110                 .WithTag("SECU", 0, 4)
111                 .WithTag("SECT", 4, 3)
112                 .WithReservedBits(7, 1)
113                 .WithTag("MINU", 8, 4)
114                 .WithTag("MINT", 12, 3)
115                 .WithReservedBits(15, 1)
116                 .WithTag("HOURU", 16, 4)
117                 .WithTag("HOURT", 20, 2)
118                 .WithReservedBits(22, 10)
119             ;
120 
121             Register.Date.Define(this)
122                 .WithTag("DAYOMU", 0, 4)
123                 .WithTag("DAYOMT", 4, 2)
124                 .WithReservedBits(6, 2)
125                 .WithTag("MONTHU", 8, 4)
126                 .WithTag("MONTHT", 12, 1)
127                 .WithReservedBits(13, 3)
128                 .WithTag("YEARU", 16, 4)
129                 .WithTag("YEART", 20, 4)
130                 .WithTag("DAYOW", 24, 3)
131                 .WithReservedBits(27, 5)
132             ;
133 
134             Register.InterruptFlags.Define(this)
135                 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => overflowInterrupt.Value, name: "OF")
136                 .WithFlags(1, 3, FieldMode.Read, valueProviderCallback: (i, _) => captureCompareInterruptPending[i].Value, name: "CC")
137                 .WithTaggedFlag("OSCFAIL", 4)
138                 .WithFlag(5, FieldMode.Read, valueProviderCallback: _ => counterTick.Value, name: "CNTTICK")
139                 .WithTaggedFlag("MINTICK", 6)
140                 .WithTaggedFlag("HOURTICK", 7)
141                 .WithTaggedFlag("DAYTICK", 8)
142                 .WithTaggedFlag("DAYOWOF", 9)
143                 .WithTaggedFlag("MONTHTICK", 10)
144                 .WithReservedBits(11, 21)
145             ;
146 
147             Register.InterruptFlagSet.Define(this)
148                 .WithFlag(0, FieldMode.Set, writeCallback: (_, value) => overflowInterrupt.Value |= value, name: "OF")
149                 .WithFlags(1, 3, FieldMode.Set, writeCallback: (i, _, value) => captureCompareInterruptPending[i].Value |= value, name: "CC")
150                 .WithTaggedFlag("OSCFAIL", 4)
151                 .WithFlag(5, FieldMode.Set, writeCallback: (_, value) => counterTick.Value |= value, name: "CNTTICK")
152                 .WithTaggedFlag("MINTICK", 6)
153                 .WithTaggedFlag("HOURTICK", 7)
154                 .WithTaggedFlag("DAYTICK", 8)
155                 .WithTaggedFlag("DAYOWOF", 9)
156                 .WithTaggedFlag("MONTHTICK", 10)
157                 .WithReservedBits(11, 21)
158                 .WithWriteCallback((_, __) => UpdateInterrupts())
159             ;
160 
161             Register.InterruptFlagClear.Define(this)
162                 .WithFlag(0, out overflowInterrupt, FieldMode.ReadToClear | FieldMode.WriteOneToClear, name: "OF")
163                 .WithFlags(1, 3, out captureCompareInterruptPending, FieldMode.ReadToClear | FieldMode.WriteOneToClear, name: "CC")
164                 .WithTaggedFlag("OSCFAIL", 4)
165                 .WithFlag(5, out counterTick, FieldMode.ReadToClear | FieldMode.WriteOneToClear, name: "CNTTICK")
166                 .WithTaggedFlag("MINTICK", 6)
167                 .WithTaggedFlag("HOURTICK", 7)
168                 .WithTaggedFlag("DAYTICK", 8)
169                 .WithTaggedFlag("DAYOWOF", 9)
170                 .WithTaggedFlag("MONTHTICK", 10)
171                 .WithReservedBits(11, 21)
172                 .WithWriteCallback((_, __) => UpdateInterrupts())
173                 .WithReadCallback((_, __) => UpdateInterrupts())
174             ;
175 
176             Register.InterruptEnable.Define(this)
177                 .WithFlag(0, out overflowInterruptEnabled, name: "OF")
178                 .WithFlags(1, 3, out captureCompareInterruptEnabled, name: "CC")
179                 .WithTaggedFlag("OSCFAIL", 4)
180                 .WithFlag(5, out counterTickEnabled, name: "CNTTICK")
181                 .WithTaggedFlag("MINTICK", 6)
182                 .WithTaggedFlag("HOURTICK", 7)
183                 .WithTaggedFlag("DAYTICK", 8)
184                 .WithTaggedFlag("DAYOWOF", 9)
185                 .WithTaggedFlag("MONTHTICK", 10)
186                 .WithReservedBits(11, 21)
187                 .WithChangeCallback((_, __) => UpdateInterrupts())
188             ;
189 
190             Register.Status.Define(this)
191                 .WithReservedBits(0, 32)
192             ;
193 
194             Register.Command.Define(this)
195                 .WithTaggedFlag("CLRSTATUS", 0)
196                 .WithReservedBits(1, 31)
197             ;
198 
199             Register.SyncBusy.Define(this)
200                 .WithReservedBits(0, 5)
201                 .WithTaggedFlag("CMD", 5)
202                 .WithReservedBits(6, 26)
203             ;
204 
205             Register.PowerDown.Define(this)
206                 .WithTaggedFlag("RAM", 0)
207                 .WithReservedBits(1, 31)
208             ;
209 
210             Register.ConfLock.Define(this)
211                 .WithTag("LOCKKEY", 0, 16)
212                 .WithReservedBits(16, 16)
213             ;
214 
215             Register.WakeUpEnable.Define(this)
216                 .WithTaggedFlag("EM4WU", 0)
217                 .WithReservedBits(1, 31)
218             ;
219 
220             Register.ChanelControlC0.DefineMany(this, NumberOfCaptureCompareChannels, (register, idx) =>
221                 register
222                     .WithEnumField<DoubleWordRegister, EFR32_RTCCCounter.CCChannelMode>(0, 2,
223                         writeCallback: (_, value) => innerTimer.Channels[idx].Mode = value,
224                         valueProviderCallback: _ => innerTimer.Channels[idx].Mode,
225                         name: "MODE")
226                     .WithTag("CMOA", 2, 2)
227                     .WithEnumField<DoubleWordRegister, Edge>(4, 2, name: "ICEDGE")
228                     .WithTag("PRSSEL", 6, 4)
229                     .WithReservedBits(10, 1)
230                     .WithEnumField<DoubleWordRegister, EFR32_RTCCCounter.CCChannelComparisonBase>(11, 1,
231                         writeCallback: (_, value) => innerTimer.Channels[idx].ComparisonBase = value,
232                         valueProviderCallback: _ => innerTimer.Channels[idx].ComparisonBase,
233                         name: "COMPBASE")
234                     .WithTag("COMPMASK", 12, 5)
235                     .WithTaggedFlag("DAYCC", 17)
236                     .WithReservedBits(18, 14)
237                 , stepInBytes: StepBetweenCaptureCompareRegisters)
238             ;
239 
240             Register.CaptureValueC0.DefineMany(this, NumberOfCaptureCompareChannels, (register, idx) =>
241                 register
242                     .WithValueField(0, 32,
243                         writeCallback: (_, value) => innerTimer.Channels[idx].CompareValue = value,
244                         valueProviderCallback: _ => innerTimer.Channels[idx].CompareValue,
245                         name: "CCV")
246                 , stepInBytes: StepBetweenCaptureCompareRegisters)
247             ;
248 
249             Register.CaptureTimeC0.DefineMany(this, NumberOfCaptureCompareChannels, (register, idx) =>
250                 register
251                     .WithTag("SECU", 0, 4)
252                     .WithTag("SECT", 4, 3)
253                     .WithReservedBits(7, 1)
254                     .WithTag("MINU", 8, 4)
255                     .WithTag("MINT", 12, 3)
256                     .WithReservedBits(15, 1)
257                     .WithTag("HOURU", 16, 4)
258                     .WithTag("HOURT", 20, 2)
259                     .WithReservedBits(22, 10)
260                 , stepInBytes: StepBetweenCaptureCompareRegisters)
261             ;
262 
263             Register.CaptureDateC0.DefineMany(this, NumberOfCaptureCompareChannels, (register, idx) =>
264                 register
265                     .WithTag("DAYU", 0, 4)
266                     .WithTag("DAYT", 4, 2)
267                     .WithReservedBits(6, 2)
268                     .WithTag("MONTHU", 8, 4)
269                     .WithTag("MONTHT", 12, 1)
270                     .WithReservedBits(13, 19)
271                 , stepInBytes: StepBetweenCaptureCompareRegisters)
272             ;
273 
274             Register.Retention0.DefineMany(this, NumberOfRetentionRegisters, (reg, idx) =>
275                 reg.WithValueField(0, 32, name: "REG")) //these registers store user-written values, no additional logic
276             ;
277         }
278 
UpdateInterrupts()279         private void UpdateInterrupts()
280         {
281             // Executed in lock as a precaution against the gpio/BaseClockSource deadlock until a proper fix is ready
282             machine.ClockSource.ExecuteInLock(delegate
283             {
284                 var value = false;
285                 value |= overflowInterrupt.Value && overflowInterruptEnabled.Value;
286                 value |= captureCompareInterruptPending.Zip(captureCompareInterruptEnabled, (a, b) => a.Value && b.Value).Any(a => a);
287                 value |= counterTick.Value && counterTickEnabled.Value;
288                 IRQ.Set(value);
289             });
290         }
291 
292         private readonly EFR32_RTCCCounter innerTimer;
293 
294         private IFlagRegisterField overflowInterrupt;
295         private IFlagRegisterField overflowInterruptEnabled;
296         private IFlagRegisterField[] captureCompareInterruptPending;
297         private IFlagRegisterField[] captureCompareInterruptEnabled;
298         private IFlagRegisterField counterTick;
299         private IFlagRegisterField counterTickEnabled;
300 
301         private const int NumberOfRetentionRegisters = 32;
302         private const int NumberOfCaptureCompareChannels = 3;
303         private const int StepBetweenCaptureCompareRegisters = Register.ChanelControlC1 - Register.ChanelControlC0;
304         private const int CounterWidth = 32;
305         private const int PreCounterWidth = 15;
306 
307         private enum Edge
308         {
309             Rising = 0x0,
310             Falling = 0x1,
311             Both = 0x2,
312             None = 0x3,
313         }
314 
315         private enum Register
316         {
317             Control = 0x0,
318             PreCounter = 0x4,
319             CounterValue = 0x8,
320             CombinedPreCouterValue = 0xC,
321             Time = 0x10,
322             Date = 0x14,
323             InterruptFlags = 0x18,
324             InterruptFlagSet = 0x1C,
325             InterruptFlagClear = 0x20,
326             InterruptEnable = 0x24,
327             Status = 0x28,
328             Command = 0x2C,
329             SyncBusy = 0x30,
330             PowerDown = 0x34,
331             ConfLock = 0x38,
332             WakeUpEnable = 0x3C,
333             ChanelControlC0 = 0x40,
334             CaptureValueC0 = 0x44,
335             CaptureTimeC0 = 0x48,
336             CaptureDateC0 = 0x4C,
337             ChanelControlC1 = 0x50,
338             CaptureValueC1 = 0x54,
339             CaptureTimeC1 = 0x58,
340             CaptureDateC1 = 0x5C,
341             ChanelControlC2 = 0x60,
342             CaptureValueC2 = 0x64,
343             CaptureTimeC2 = 0x68,
344             CaptureDateC2 = 0x6C,
345             Retention0 = 0x104,
346             Retention1 = 0x108,
347             // ...
348             Retention31 = 0x180,
349         }
350     }
351 }
352