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.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 using Antmicro.Renode.Time;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.Peripherals.Timers
17 {
18     public class STM32F4_RTC : IDoubleWordPeripheral, IKnownSize
19     {
STM32F4_RTC(IMachine machine, long wakeupTimerFrequency = DefaultWakeupTimerFrequency)20         public STM32F4_RTC(IMachine machine, long wakeupTimerFrequency = DefaultWakeupTimerFrequency)
21         {
22             mainTimer = new TimerConfig(this);
23             alarmA = new AlarmConfig(this, mainTimer);
24             alarmB = new AlarmConfig(this, mainTimer);
25 
26             AlarmIRQ = new GPIO();
27             WakeupIRQ = new GPIO();
28 
29             // The ticker reaches its limit at (wakeupTimerFrequency / (PREDIV_A + 1) / (PREDIV_S + 1)) Hz
30             // The prediv values are usually chosen so that its frequency is 1 Hz but this is not required
31             ticker = new LimitTimer(machine.ClockSource, wakeupTimerFrequency, this, nameof(ticker), DefaultSynchronuousPrescaler + 1, direction: Direction.Descending, eventEnabled: true, divider: DefaultAsynchronuousPrescaler + 1);
32             ticker.LimitReached += UpdateState;
33 
34             // The fastTicker reaches its limit once for every increment of the ticker. It is used to
35             // implement subsecond alarm interrupts.
36             fastTicker = new LimitTimer(machine.ClockSource, wakeupTimerFrequency, this, nameof(fastTicker), 1, direction: Direction.Ascending, eventEnabled: true, divider: DefaultAsynchronuousPrescaler + 1);
37             fastTicker.LimitReached += UpdateAlarms;
38 
39             wakeupTimer = new LimitTimer(machine.ClockSource, wakeupTimerFrequency, this, nameof(wakeupTimer), direction: Direction.Ascending);
40             wakeupTimer.LimitReached += delegate
41             {
42                 wakeupTimerFlag.Value = true; // reset by software
43                 UpdateInterrupts();
44             };
45             ResetInnerTimers();
46 
47             IFlagRegisterField syncFlag = null;
48 
49             var registerMap = new Dictionary<long, DoubleWordRegister>
50             {
51                 {(long)Registers.TimeRegister, new DoubleWordRegister(this)
52                     .WithValueField(0, 4, name: "SU",
53                         writeCallback: (_, value) => UpdateMainTimer(Registers.TimeRegister, DateTimeSelect.Second, Rank.Units, value),
54                         valueProviderCallback: _ => mainTimer.Read(DateTimeSelect.Second, Rank.Units))
55                     .WithValueField(4, 3, name: "ST",
56                         writeCallback: (_, value) => UpdateMainTimer(Registers.TimeRegister, DateTimeSelect.Second, Rank.Tens, value),
57                         valueProviderCallback: _ => mainTimer.Read(DateTimeSelect.Second, Rank.Tens))
58                     .WithReservedBits(7, 1)
59                     .WithValueField(8, 4, name: "MU",
60                         writeCallback: (_, value) => UpdateMainTimer(Registers.TimeRegister, DateTimeSelect.Minute, Rank.Units, value),
61                         valueProviderCallback: _ => mainTimer.Read(DateTimeSelect.Minute, Rank.Units))
62                     .WithValueField(12, 3, name: "MT",
63                         writeCallback: (_, value) => UpdateMainTimer(Registers.TimeRegister, DateTimeSelect.Minute, Rank.Tens, value),
64                         valueProviderCallback: _ => mainTimer.Read(DateTimeSelect.Minute, Rank.Tens))
65                     .WithReservedBits(15, 1)
66                     .WithValueField(16, 4, name: "HU",
67                         writeCallback: (_, value) => UpdateMainTimer(Registers.TimeRegister, DateTimeSelect.Hour, Rank.Units, value),
68                         valueProviderCallback: _ => mainTimer.Read(DateTimeSelect.Hour, Rank.Units))
69                     .WithValueField(20, 2, name: "HT",
70                         writeCallback: (_, value) => UpdateMainTimer(Registers.TimeRegister, DateTimeSelect.Hour, Rank.Tens, value),
71                         valueProviderCallback: _ => mainTimer.Read(DateTimeSelect.Hour, Rank.Tens))
72                     .WithFlag(22, name: "PM",
73                         writeCallback: (_, value) =>
74                         {
75                             if(CheckIfInInitMode(Registers.TimeRegister) && CheckIfUnlocked(Registers.TimeRegister))
76                             {
77                                 mainTimer.PM = value;
78                             }
79                         },
80                         valueProviderCallback: _ => mainTimer.PM)
81                     .WithReservedBits(23, 9)
82                 },
83                 {(long)Registers.DateRegister, new DoubleWordRegister(this, 0x2101)
84                     .WithValueField(0, 4, name: "DU",
85                         writeCallback: (_, value) => UpdateMainTimer(Registers.DateRegister, DateTimeSelect.Day, Rank.Units, value),
86                         valueProviderCallback: _ => mainTimer.Read(DateTimeSelect.Day, Rank.Units))
87                     .WithValueField(4, 2, name: "DT",
88                         writeCallback: (_, value) => UpdateMainTimer(Registers.DateRegister, DateTimeSelect.Day, Rank.Tens, value),
89                         valueProviderCallback: _ => mainTimer.Read(DateTimeSelect.Day, Rank.Tens))
90                     .WithReservedBits(6, 2)
91                     .WithValueField(8, 4, name: "MU",
92                         writeCallback: (_, value) => UpdateMainTimer(Registers.DateRegister, DateTimeSelect.Month, Rank.Units, value),
93                         valueProviderCallback: _ => mainTimer.Read(DateTimeSelect.Month, Rank.Units))
94                     .WithValueField(12, 1, name: "MT",
95                         writeCallback: (_, value) => UpdateMainTimer(Registers.DateRegister, DateTimeSelect.Month, Rank.Tens, value),
96                         valueProviderCallback: _ => mainTimer.Read(DateTimeSelect.Month, Rank.Tens))
97                     .WithValueField(13, 3, name: "WDU",
98                         writeCallback: (_, value) =>
99                         {
100                             if(CheckIfInInitMode(Registers.DateRegister) && CheckIfUnlocked(Registers.DateRegister))
101                             {
102                                 if(value == 0)
103                                 {
104                                     this.Log(LogLevel.Warning, "Writting value 0 to WeekDay register is forbidden");
105                                     return;
106                                 }
107                                 mainTimer.WeekDay = (DayOfTheWeek)value;
108                             }
109                         },
110                         valueProviderCallback: _ => (uint)mainTimer.WeekDay)
111                     .WithValueField(16, 4, name: "YU",
112                         writeCallback: (_, value) => UpdateMainTimer(Registers.DateRegister, DateTimeSelect.Year, Rank.Units, value),
113                         valueProviderCallback: _ => mainTimer.Read(DateTimeSelect.Year, Rank.Units))
114                     .WithValueField(20, 4, name: "YT",
115                         writeCallback: (_, value) => UpdateMainTimer(Registers.DateRegister, DateTimeSelect.Year, Rank.Tens, value),
116                         valueProviderCallback: _ => mainTimer.Read(DateTimeSelect.Year, Rank.Tens))
117                     .WithReservedBits(24, 8)
118                 },
119                 {(long)Registers.ControlRegister, new DoubleWordRegister(this)
120                     .WithValueField(0, 3, out wakeupClockSelection, name: "WUCKSEL",
121                         writeCallback: (_, value) =>
122                         {
123                             if(!CheckIfUnlocked(Registers.ControlRegister))
124                             {
125                                 return;
126                             }
127                             if((value & 0b100) == 0)
128                             {
129                                 // 0xx: RTC / 2^(4 - xx) clock is selected
130                                 // 000: RTC / 2^4 = RTC / 16
131                                 // 011: RTC / 2^1 = RTC / 2
132                                 wakeupTimer.Divider = (int)Math.Pow(2, 4 - value);
133                             }
134                             else
135                             {
136                                 // 1xx: ck_spre (usually 1 Hz) clock is selected
137                                 // ck_spre = RTC / {(PREDIV_S + 1) * (PREDIV_A + 1)}, see RM p.548
138                                 wakeupTimer.Divider = (int)((predivS.Value + 1) * (predivA.Value + 1));
139                             }
140                         })
141                     .WithTag("TSEDGE", 3, 1)
142                     .WithTag("REFCKON", 4, 1)
143                     .WithFlag(5, name: "BYPSHAD",
144                         valueProviderCallback: _ => true, // Always report that shadow registers are bypassed
145                         writeCallback: (_, value) =>
146                         {
147                             if(!value)
148                             {
149                                 this.Log(LogLevel.Warning, "Shadow registers are not supported");
150                             }
151                         })
152                     .WithFlag(6, name: "FMT",
153                         writeCallback: (_, value) =>
154                         {
155                             if(CheckIfUnlocked(Registers.ControlRegister))
156                             {
157                                 AMPMFormat = value;
158 
159                                 mainTimer.ConfigureAMPM();
160                                 alarmA.ConfigureAMPM();
161                                 alarmB.ConfigureAMPM();
162                             }
163                         },
164                         valueProviderCallback: _ => AMPMFormat)
165                     .WithTag("DCE", 7, 1)
166                     .WithFlag(8, name: "ALRAE",
167                         writeCallback: (_, value) =>
168                         {
169                             if(CheckIfUnlocked(Registers.ControlRegister))
170                             {
171                                 alarmA.Enable = value;
172                             }
173                         },
174                         valueProviderCallback: _ => alarmA.Enable)
175                     .WithFlag(9, name: "ALRBE",
176                         writeCallback: (_, value) =>
177                         {
178                             if(CheckIfUnlocked(Registers.ControlRegister))
179                             {
180                                 alarmB.Enable = value;
181                             }
182                         },
183                         valueProviderCallback: _ => alarmB.Enable)
184                     .WithFlag(10, name: "WUTE",
185                         writeCallback: (_, value) =>
186                         {
187                             if(!CheckIfUnlocked(Registers.ControlRegister))
188                             {
189                                 return;
190                             }
191                             wakeupTimer.Enabled = value;
192                             wakeupTimer.Value = 0;
193                         },
194                         valueProviderCallback: _ => wakeupTimer.Enabled)
195                     .WithTag("TSE", 11, 1) // Timestamp not supported
196                     .WithFlag(12, name: "ALRAIE",
197                         writeCallback: (_, value) =>
198                         {
199                             if(CheckIfUnlocked(Registers.ControlRegister))
200                             {
201                                 alarmA.InterruptEnable = value;
202                             }
203                         },
204                         valueProviderCallback: _ => alarmA.InterruptEnable)
205                     .WithFlag(13, name: "ALRBIE",
206                         writeCallback: (_, value) =>
207                         {
208                             if(CheckIfUnlocked(Registers.ControlRegister))
209                             {
210                                 alarmB.InterruptEnable = value;
211                             }
212                         },
213                         valueProviderCallback: _ => alarmB.InterruptEnable)
214                     .WithFlag(14, name: "WUTIE",
215                         writeCallback: (_, value) =>
216                         {
217                             if(!CheckIfUnlocked(Registers.ControlRegister))
218                             {
219                                 return;
220                             }
221                             wakeupTimer.EventEnabled = value;
222                         },
223                         valueProviderCallback: _ => wakeupTimer.EventEnabled)
224                     .WithTag("TSIE", 15, 1) // Timestamp not supported
225                     .WithTag("ADD1H", 16, 1)
226                     .WithTag("SUB1H", 17, 1)
227                     .WithTag("BKP", 18, 1)
228                     .WithTag("COSEL", 19, 1)
229                     .WithTag("POL", 20, 1)
230                     .WithTag("OSEL", 21, 2)
231                     .WithTag("COE", 23, 1)
232                     .WithReservedBits(24, 8)
233                 },
234                 {(long)Registers.ISR, new DoubleWordRegister(this, 0x7)
235                     .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => !alarmA.Enable, name: "ALRAWF")
236                     .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => !alarmB.Enable, name: "ALRBWF")
237                     .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => !wakeupTimer.Enabled, name: "WUTWF")
238                     .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => false, name: "SHPF") // Shift operations not supported
239                     .WithFlag(4, FieldMode.Read, valueProviderCallback: _ => mainTimer.TimeState.Year != 2000, name: "INITS")
240                     .WithFlag(5, out syncFlag, FieldMode.Read | FieldMode.WriteZeroToClear, name: "RSF",
241                               readCallback: (_, curr) =>
242                               {
243                                   // this strange logic is required by the Zephyr driver;
244                                   // it wants to read 0 before reading 1, otherwise it times-out
245                                   if(!curr)
246                                   {
247                                       syncFlag.Value = true;
248                                   }
249                               })
250                     .WithFlag(6, FieldMode.Read, valueProviderCallback: _ => initMode, name: "INITF")
251                     .WithFlag(7, FieldMode.Write, name: "INIT",
252                         changeCallback: (_, value) =>
253                         {
254                             if(CheckIfUnlocked(Registers.ISR))
255                             {
256                                 ticker.Enabled = !value;
257                                 fastTicker.Enabled = !value;
258                                 initMode = value;
259                             }
260                         })
261                     .WithFlag(8, FieldMode.WriteZeroToClear | FieldMode.Read, name: "ALRAF",
262                         changeCallback: (_, __) =>
263                         {
264                             alarmA.Flag = false;
265                         },
266                         valueProviderCallback: _ => alarmA.Flag)
267                     .WithFlag(9, FieldMode.WriteZeroToClear | FieldMode.Read, name: "ALRBF",
268                         changeCallback: (_, __) =>
269                         {
270                             alarmB.Flag = false;
271                         },
272                         valueProviderCallback: _ => alarmB.Flag)
273                     .WithFlag(10, out wakeupTimerFlag, FieldMode.WriteZeroToClear | FieldMode.Read, name: "WUTF",
274                         changeCallback: (_, __) =>
275                         {
276                             UpdateInterrupts();
277                         })
278                     // We make the following bits flags instead of tags to reduce warnings in the log
279                     // because they are interrupt flag clear bits
280                     .WithFlag(11, FieldMode.Read | FieldMode.WriteZeroToClear, name: "TSF")
281                     .WithFlag(12, FieldMode.Read | FieldMode.WriteZeroToClear, name: "TSOVF")
282                     .WithFlag(13, FieldMode.Read | FieldMode.WriteZeroToClear, name: "TAMP1F")
283                     .WithFlag(14, FieldMode.Read | FieldMode.WriteZeroToClear, name: "TAMP2F")
284                     .WithFlag(15, FieldMode.Read | FieldMode.WriteZeroToClear, name: "TAMP3F")
285                     .WithFlag(16, FieldMode.Read, valueProviderCallback: _ => false, name: "RECALPF") // Recalibration not supported
286                     .WithIgnoredBits(17, 15) // We don't use reserved bits because the HAL sometimes writes 0s here and sometimes 1s
287                 },
288                 {(long)Registers.PrescalerRegister, new DoubleWordRegister(this, DefaultAsynchronuousPrescaler << 16 | DefaultSynchronuousPrescaler)
289                     .WithValueField(0, 15, out predivS, writeCallback: (_, value) => ticker.Limit = value + 1, name: "PREDIV_S")
290                     .WithReservedBits(15, 1)
291                     .WithValueField(16, 7, out predivA, writeCallback: (_, value) =>
292                     {
293                         ticker.Divider = (int)value + 1;
294                         fastTicker.Divider = (int)value + 1;
295                     }, name: "PREDIV_A")
296                     .WithReservedBits(23, 9)
297                 },
298                 {(long)Registers.WakeupTimerRegister, new DoubleWordRegister(this, 0xFFFF)
299                     .WithValueField(0, 16, out wakeupAutoReload, name: "WUT",
300                         writeCallback: (_, value) =>
301                         {
302                             if(!CheckIfUnlocked(Registers.WakeupTimerRegister))
303                             {
304                                 return;
305                             }
306                             // WUCKSEL value: '11x' = 2^16 is added to the WUT counter value (see reference manual p.565)
307                             if((wakeupClockSelection.Value & 0b110) == 0b110)
308                             {
309                                 value += 0x10000;
310                             }
311 
312                             // The wakeup timer flag needs to be set every (WUT + 1) cycles of the wakeup timer.
313                             wakeupTimer.Limit = value + 1;
314                         },
315                         valueProviderCallback: _ => WakeupTimerRegisterErrata ? wakeupAutoReload.Value : (uint)wakeupTimer.Limit)
316                     .WithReservedBits(16, 16)
317                 },
318                 {(long)Registers.CalibrationRegister, new DoubleWordRegister(this)
319                     .WithTag("DC", 0, 5)
320                     .WithReservedBits(5, 2)
321                     .WithTag("DCS", 7, 1)
322                     .WithReservedBits(8, 24)
323                 },
324                 {(long)Registers.AlarmARegister, new DoubleWordRegister(this)
325                     .WithValueField(0, 4, name: "SU",
326                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.Update(DateTimeSelect.Second, Rank.Units, (uint)value)),
327                         valueProviderCallback: _ => alarmA.Read(DateTimeSelect.Second, Rank.Units))
328                     .WithValueField(4, 3, name: "ST",
329                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.Update(DateTimeSelect.Second, Rank.Tens, (uint)value)),
330                         valueProviderCallback: _ => alarmA.Read(DateTimeSelect.Second, Rank.Tens))
331                     .WithFlag(7, name: "MSK1",
332                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.SecondsMask = value),
333                         valueProviderCallback: _ => alarmA.SecondsMask)
334                     .WithValueField(8, 4, name: "MU",
335                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.Update(DateTimeSelect.Minute, Rank.Units, (uint)value)),
336                         valueProviderCallback: _ => alarmA.Read(DateTimeSelect.Minute, Rank.Units))
337                     .WithValueField(12, 3, name: "MT",
338                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.Update(DateTimeSelect.Minute, Rank.Tens, (uint)value)),
339                         valueProviderCallback: _ => alarmA.Read(DateTimeSelect.Minute, Rank.Tens))
340                     .WithFlag(15, name: "MSK2",
341                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.MinutesMask = value),
342                         valueProviderCallback: _ => alarmA.MinutesMask)
343                     .WithValueField(16, 4, name: "HU",
344                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.Update(DateTimeSelect.Hour, Rank.Units, (uint)value)),
345                         valueProviderCallback: _ => alarmA.Read(DateTimeSelect.Hour, Rank.Units))
346                     .WithValueField(20, 2, name: "HT",
347                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.Update(DateTimeSelect.Hour, Rank.Tens, (uint)value)),
348                         valueProviderCallback: _ => alarmA.Read(DateTimeSelect.Hour, Rank.Tens))
349                     .WithFlag(22, name: "PM",
350                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.PM = value),
351                         valueProviderCallback: _ => alarmA.PM)
352                     .WithFlag(23, name: "MSK3",
353                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.HoursMask = value),
354                         valueProviderCallback: _ => alarmA.HoursMask)
355                     .WithValueField(24, 4, name: "DU",
356                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.Update(DateTimeSelect.Day, Rank.Units, (uint)value)),
357                         valueProviderCallback: _ => alarmA.Read(DateTimeSelect.Day, Rank.Units))
358                     .WithValueField(28, 2, name: "DT",
359                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.Update(DateTimeSelect.Day, Rank.Tens, (uint)value)),
360                         valueProviderCallback: _ => alarmA.Read(DateTimeSelect.Day, Rank.Tens))
361                     .WithTag("WDSEL", 30, 1) // Weekday instead of date units usupported
362                     .WithFlag(31, name: "MSK4",
363                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.DaysMask = value),
364                         valueProviderCallback: _ => alarmA.DaysMask)
365                 },
366                 {(long)Registers.AlarmBRegister, new DoubleWordRegister(this)
367                     .WithValueField(0, 4, name: "SU",
368                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.Update(DateTimeSelect.Second, Rank.Units, (uint)value)),
369                         valueProviderCallback: _ => alarmB.Read(DateTimeSelect.Second, Rank.Units))
370                     .WithValueField(4, 3, name: "ST",
371                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.Update(DateTimeSelect.Second, Rank.Tens, (uint)value)),
372                         valueProviderCallback: _ => alarmB.Read(DateTimeSelect.Second, Rank.Tens))
373                     .WithFlag(7, name: "MSK1",
374                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.SecondsMask = value),
375                         valueProviderCallback: _ => alarmB.SecondsMask)
376                     .WithValueField(8, 4, name: "MU",
377                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.Update(DateTimeSelect.Minute, Rank.Units, (uint)value)),
378                         valueProviderCallback: _ => alarmB.Read(DateTimeSelect.Minute, Rank.Units))
379                     .WithValueField(12, 3, name: "MT",
380                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.Update(DateTimeSelect.Minute, Rank.Tens, (uint)value)),
381                         valueProviderCallback: _ => alarmB.Read(DateTimeSelect.Minute, Rank.Tens))
382                     .WithFlag(15, name: "MSK2",
383                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.MinutesMask = value),
384                         valueProviderCallback: _ => alarmB.MinutesMask)
385                     .WithValueField(16, 4, name: "HU",
386                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.Update(DateTimeSelect.Hour, Rank.Units, (uint)value)),
387                         valueProviderCallback: _ => alarmB.Read(DateTimeSelect.Hour, Rank.Units))
388                     .WithValueField(20, 2, name: "HT",
389                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.Update(DateTimeSelect.Hour, Rank.Tens, (uint)value)),
390                         valueProviderCallback: _ => alarmB.Read(DateTimeSelect.Hour, Rank.Tens))
391                     .WithFlag(22, name: "PM",
392                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.PM = value),
393                         valueProviderCallback: _ => alarmB.PM)
394                     .WithFlag(23, name: "MSK3",
395                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.HoursMask = value),
396                         valueProviderCallback: _ => alarmB.HoursMask)
397                     .WithValueField(24, 4, name: "DU",
398                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.Update(DateTimeSelect.Day, Rank.Units, (uint)value)),
399                         valueProviderCallback: _ => alarmB.Read(DateTimeSelect.Day, Rank.Units))
400                     .WithValueField(28, 2, name: "DT",
401                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.Update(DateTimeSelect.Day, Rank.Tens, (uint)value)),
402                         valueProviderCallback: _ => alarmB.Read(DateTimeSelect.Day, Rank.Tens))
403                     .WithTag("WDSEL", 30, 1) // Weekday instead of date units usupported
404                     .WithFlag(31, name: "MSK4",
405                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.DaysMask = value),
406                         valueProviderCallback: _ => alarmB.DaysMask)
407                 },
408                 {(long)Registers.WriteProtectionRegister, new DoubleWordRegister(this)
409                     .WithValueField(0, 8, name: "KEY",
410                         writeCallback: (_, value) =>
411                         {
412                             if(value == UnlockKey1 && !firstStageUnlocked)
413                             {
414                                 firstStageUnlocked = true;
415                             }
416                             else if(value == UnlockKey2 && firstStageUnlocked)
417                             {
418                                 registersUnlocked = true;
419                                 firstStageUnlocked = false;
420                             }
421                             else
422                             {
423                                 firstStageUnlocked = false;
424                                 registersUnlocked = false;
425                             }
426                         },
427                         valueProviderCallback: _ => 0)
428                     .WithReservedBits(8, 24)
429                 },
430                 {(long)Registers.SubSecondRegister, new DoubleWordRegister(this)
431                     .WithValueField(0, 16, FieldMode.Read, name: "SS", valueProviderCallback: _ => (uint)ticker.Value)
432                     .WithReservedBits(16, 16)
433                 },
434                 {(long)Registers.ShiftControlRegister, new DoubleWordRegister(this)
435                     .WithTag("SUBFS", 0, 15)
436                     .WithReservedBits(15, 16)
437                     .WithTag("ADD1S", 31, 1)
438                 },
439                 {(long)Registers.TimestampTimeRegister, new DoubleWordRegister(this)
440                     .WithTag("Second", 0, 7)
441                     .WithReservedBits(7, 1)
442                     .WithTag("Minute", 8, 7)
443                     .WithReservedBits(15, 1)
444                     .WithTag("Hour", 16, 6)
445                     .WithTag("PM", 22, 1)
446                     .WithReservedBits(23, 9)
447                 },
448                 {(long)Registers.TimestampDateRegister, new DoubleWordRegister(this)
449                     .WithTag("Day", 0, 6)
450                     .WithReservedBits(6, 2)
451                     .WithTag("Month", 8, 5)
452                     .WithTag("WDU", 13, 3)
453                     .WithReservedBits(16, 16)
454                 },
455                 {(long)Registers.TimestampSubSecondRegister, new DoubleWordRegister(this)
456                     .WithTag("SS", 0, 16)
457                     .WithReservedBits(16, 16)
458                 },
459                 {(long)Registers.ClockCalibrationRegister, new DoubleWordRegister(this)
460                     .WithTag("CALM", 0, 9)
461                     .WithReservedBits(9, 4)
462                     .WithTag("CALW16", 13, 1)
463                     .WithTag("CALW8", 14, 1)
464                     .WithTag("CALP", 15, 1)
465                     .WithReservedBits(16, 16)
466                 },
467                 {(long)Registers.TamperAndAlternateFunctionConfigurationRegister, new DoubleWordRegister(this)
468                     .WithTag("TAMP1E", 0, 1)
469                     .WithTag("TAMP1TRG", 1, 1)
470                     .WithTag("TAMPIE", 2, 1)
471                     .WithTag("TAMP2E", 3, 1)
472                     .WithTag("TAMP2TRG", 4, 1)
473                     .WithReservedBits(5, 2)
474                     .WithTag("TAMPTS", 7, 1)
475                     .WithTag("TAMPFREQ", 8, 3)
476                     .WithTag("TAMPFLT", 11, 2)
477                     .WithTag("TAMPPRCH", 13, 2)
478                     .WithTag("TAMPPUDIS", 15, 1)
479                     .WithTag("TAMP1INSEL", 16, 1)
480                     .WithTag("TSINSEL", 17, 1)
481                     .WithTag("ALARMOUTTYPE", 18, 1)
482                     .WithReservedBits(19, 13)
483                 },
484                 {(long)Registers.AlarmASubSecondRegister, new DoubleWordRegister(this)
485                     .WithValueField(0, 15, name: "SS",
486                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.Subsecond = (int)value),
487                         valueProviderCallback: _ => (uint)alarmA.Subsecond)
488                     .WithReservedBits(15, 9)
489                     .WithValueField(24, 4, name: "MASKSS",
490                         writeCallback: (_, value) => UpdateAlarmA(alarm => alarm.SubsecondsMask = (uint)value),
491                         valueProviderCallback: _ => alarmA.SubsecondsMask)
492                     .WithReservedBits(28, 4)
493                 },
494                 {(long)Registers.AlarmBSubSecondRegister, new DoubleWordRegister(this)
495                     .WithValueField(0, 15, name: "SS",
496                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.Subsecond = (int)value),
497                         valueProviderCallback: _ => (uint)alarmB.Subsecond)
498                     .WithReservedBits(15, 9)
499                     .WithValueField(24, 4, name: "MASKSS",
500                         writeCallback: (_, value) => UpdateAlarmB(alarm => alarm.SubsecondsMask = (uint)value),
501                         valueProviderCallback: _ => alarmB.SubsecondsMask)
502                     .WithReservedBits(28, 4)
503                 },
504                 {(long)Registers.OptionRegister, new DoubleWordRegister(this)
505                     .WithTaggedFlag("RTC_ALARM_TYPE", 0)
506                     .WithTaggedFlag("RTC_OUT_RMP", 1)
507                     .WithReservedBits(2, 30)
508                 },
509             };
510             // These registers have no logic, they serve as scratchpad
511             for(var reg = (long)Registers.BackupStart; reg <= (long)Registers.BackupEnd; reg += 4)
512             {
513                 registerMap.Add(reg, new DoubleWordRegister(this, softResettable: false).WithValueField(0, 32));
514             }
515             registers = new DoubleWordRegisterCollection(this, registerMap);
516         }
517 
ReadDoubleWord(long offset)518         public uint ReadDoubleWord(long offset)
519         {
520             return registers.Read(offset);
521         }
522 
WriteDoubleWord(long offset, uint value)523         public void WriteDoubleWord(long offset, uint value)
524         {
525             registers.Write(offset, value);
526         }
527 
Reset()528         public void Reset()
529         {
530             registers.Reset();
531             AlarmIRQ.Unset();
532             WakeupIRQ.Unset();
533             ResetInnerTimers();
534             ResetInnerStatus();
535         }
536 
537         public long Size => 0x400;
538         public GPIO AlarmIRQ { get; }
539         public GPIO WakeupIRQ { get; }
540 
UpdateTimeState(DateTime timeState, DateTimeSelect select, int value)541         private static DateTime UpdateTimeState(DateTime timeState, DateTimeSelect select, int value)
542         {
543             switch(select)
544             {
545                 case DateTimeSelect.Second:
546                     return timeState.With(second: value);
547                 case DateTimeSelect.Minute:
548                     return timeState.With(minute: value);
549                 case DateTimeSelect.Hour:
550                     return timeState.With(hour: value);
551                 case DateTimeSelect.Day:
552                     return timeState.With(day: value);
553                 case DateTimeSelect.Month:
554                     return timeState.With(month: value);
555                 case DateTimeSelect.Year:
556                     return timeState.With(year: value);
557                 default:
558                     throw new ArgumentException($"Unexpected select: {select}");
559             }
560         }
561 
ResetInnerTimers()562         private void ResetInnerTimers()
563         {
564             mainTimer.Reset();
565             ticker.Reset();
566             fastTicker.Reset();
567             alarmA.Reset();
568             alarmB.Reset();
569             wakeupTimer.Reset();
570         }
571 
ResetInnerStatus()572         private void ResetInnerStatus()
573         {
574             firstStageUnlocked = false;
575             registersUnlocked = false;
576             initMode = false;
577             AMPMFormat = false;
578         }
579 
UpdateState()580         private void UpdateState()
581         {
582             var previousDayOfWeek = mainTimer.TimeState.DayOfWeek;
583 
584             mainTimer.TimeState = mainTimer.TimeState.AddSeconds(1);
585 
586             if(previousDayOfWeek != mainTimer.TimeState.DayOfWeek)
587             {
588                 // we allow for the WeekDay to be de-synchornized from
589                 // the actual day of week calculated from the TimeState
590                 // (as this is how the HW works)
591                 mainTimer.WeekDay = (DayOfTheWeek)(((int)mainTimer.WeekDay) % 7) + 1;
592             }
593         }
594 
UpdateAlarms()595         private void UpdateAlarms()
596         {
597             alarmA.UpdateInterruptFlag();
598             alarmB.UpdateInterruptFlag();
599         }
600 
UpdateInterrupts()601         private void UpdateInterrupts()
602         {
603             var state = false;
604 
605             state |= alarmA.Flag && alarmA.InterruptEnable;
606             state |= alarmB.Flag && alarmB.InterruptEnable;
607 
608             AlarmIRQ.Set(state);
609             WakeupIRQ.Set(wakeupTimerFlag.Value);
610         }
611 
CheckIfInInitMode(Registers reg)612         private bool CheckIfInInitMode(Registers reg)
613         {
614             if(initMode)
615             {
616                 return true;
617             }
618 
619             this.Log(LogLevel.Warning, "Writing to {0} allowed only in init mode", reg);
620             return false;
621         }
622 
CheckIfUnlocked(Registers reg)623         private bool CheckIfUnlocked(Registers reg)
624         {
625             if(registersUnlocked)
626             {
627                 return true;
628             }
629 
630             this.Log(LogLevel.Warning, "Writing to {0} is allowed only when the register is unlocked", reg);
631             return false;
632         }
633 
CheckIfDisabled(AlarmConfig timer)634         private bool CheckIfDisabled(AlarmConfig timer)
635         {
636             var enabled = timer.Enable;
637             if(!enabled)
638             {
639                 return true;
640             }
641 
642             this.Log(LogLevel.Warning, "Configuring {0} is allowed only when it is disabled", timer);
643             return false;
644         }
645 
UpdateMainTimer(Registers reg, DateTimeSelect what, Rank rank, ulong value)646         private void UpdateMainTimer(Registers reg, DateTimeSelect what, Rank rank, ulong value)
647         {
648             if(!CheckIfInInitMode(reg) && CheckIfUnlocked(reg))
649             {
650                 return;
651             }
652 
653             mainTimer.Update(what, rank, (uint)value);
654         }
655 
UpdateAlarm(AlarmConfig alarm, Registers register, Action<AlarmConfig> action)656         private void UpdateAlarm(AlarmConfig alarm, Registers register, Action<AlarmConfig> action)
657         {
658             if(!CheckIfDisabled(alarm) || !CheckIfUnlocked(register))
659             {
660                 return;
661             }
662 
663             action(alarm);
664         }
665 
UpdateAlarmA(Action<AlarmConfig> action)666         private void UpdateAlarmA(Action<AlarmConfig> action)
667         {
668             UpdateAlarm(alarmA, Registers.AlarmARegister, action);
669         }
670 
UpdateAlarmB(Action<AlarmConfig> action)671         private void UpdateAlarmB(Action<AlarmConfig> action)
672         {
673             UpdateAlarm(alarmB, Registers.AlarmBRegister, action);
674         }
675 
676         public bool WakeupTimerRegisterErrata { get; set; }
677 
678         private readonly TimerConfig mainTimer;
679         private readonly AlarmConfig alarmA;
680         private readonly AlarmConfig alarmB;
681         // timestamp timer is currenlty not implementedw
682 
683         private readonly DoubleWordRegisterCollection registers;
684         private readonly IValueRegisterField wakeupClockSelection;
685         private readonly IValueRegisterField predivS;
686         private readonly IValueRegisterField predivA;
687         private readonly IFlagRegisterField wakeupTimerFlag;
688         private readonly IValueRegisterField wakeupAutoReload;
689         private readonly LimitTimer ticker;
690         private readonly LimitTimer fastTicker;
691         private readonly LimitTimer wakeupTimer;
692 
693         private bool firstStageUnlocked;
694         private bool registersUnlocked;
695         private bool initMode;
696         private bool AMPMFormat;
697 
698         private const uint UnlockKey1 = 0xCA;
699         private const uint UnlockKey2 = 0x53;
700         private const long DefaultWakeupTimerFrequency = 32768;
701         private const int DefaultSynchronuousPrescaler = 0xFF;
702         private const int DefaultAsynchronuousPrescaler = 0x7F;
703 
704         private class TimerConfig
705         {
TimerConfig(STM32F4_RTC parent)706             public TimerConfig(STM32F4_RTC parent)
707             {
708                 this.parent = parent;
709             }
710 
711             public DateTime TimeState
712             {
713                 get => timeState;
714 
715                 set
716                 {
717                     timeState = value;
718                     ConfigureAMPM();
719                 }
720             }
721 
722             public bool PM
723             {
724                 get => timeState.Hour > 11 && parent.AMPMFormat;
725 
726                 set
727                 {
728                     pm = value;
729                     ConfigureAMPM();
730                 }
731             }
732 
733             // DateTime calculates the week day automatically based on the set date,
734             // but the device allows for setting an arbitrary value
735             public DayOfTheWeek WeekDay { get; set; }
736 
Reset()737             public void Reset()
738             {
739                 timeState = new DateTime(2020, 1, 1);
740 
741                 WeekDay = DayOfTheWeek.Monday;
742                 pm = false;
743             }
744 
Read(DateTimeSelect select, Rank rank)745             public uint Read(DateTimeSelect select, Rank rank)
746             {
747                 var currentValue = GetTimeSelect(select);
748                 return (uint)currentValue.ReadRank(rank);
749             }
750 
ConfigureAMPM()751             public void ConfigureAMPM()
752             {
753                 if(!parent.AMPMFormat)
754                 {
755                     return;
756                 }
757 
758                 if(pm)
759                 {
760                     if(TimeState.Hour < 12)
761                     {
762                         var newHour = TimeState.Hour + 12;
763                         timeState = UpdateTimeState(timeState, DateTimeSelect.Hour, newHour);
764                     }
765                 }
766                 else
767                 {
768                     if(TimeState.Hour >= 12)
769                     {
770                         var newHour = TimeState.Hour - 12;
771                         timeState = UpdateTimeState(timeState, DateTimeSelect.Hour, newHour);
772                     }
773                 }
774             }
775 
Update(DateTimeSelect what, Rank rank, uint value)776             public void Update(DateTimeSelect what, Rank rank, uint value)
777             {
778                 var currentValue = GetTimeSelect(what);
779                 var val = currentValue.WithUpdatedRank((int)value, rank);
780                 TimeState = UpdateTimeState(TimeState, what, val);
781             }
782 
GetTimeSelect(DateTimeSelect what)783             private int GetTimeSelect(DateTimeSelect what)
784             {
785                 switch(what)
786                 {
787                     case DateTimeSelect.Second:
788                         return timeState.Second;
789                     case DateTimeSelect.Minute:
790                         return timeState.Minute;
791                     case DateTimeSelect.Hour:
792                         return PM
793                             ? timeState.Hour - 12
794                             : timeState.Hour;
795                     case DateTimeSelect.Day:
796                         return timeState.Day;
797                     case DateTimeSelect.Month:
798                         return timeState.Month;
799                     case DateTimeSelect.Year:
800                         return timeState.Year;
801                     default:
802                         throw new ArgumentException($"Unexpected date time select: {what}");
803                 }
804             }
805 
806             private DateTime timeState;
807             private bool pm;
808 
809             private readonly STM32F4_RTC parent;
810         }
811 
812         private class AlarmConfig
813         {
AlarmConfig(STM32F4_RTC parent, TimerConfig masterTimer)814             public AlarmConfig(STM32F4_RTC parent, TimerConfig masterTimer)
815             {
816                 this.parent = parent;
817                 this.masterTimer = masterTimer;
818 
819                 Reset();
820             }
821 
822             public int Day
823             {
824                 get => day;
825 
826                 set
827                 {
828                     day = value;
829                     UpdateInterruptFlag();
830                 }
831             }
832 
833             public int Hour
834             {
835                 get => hour;
836 
837                 set
838                 {
839                     hour = value;
840                     UpdateInterruptFlag();
841                 }
842             }
843 
844             public int Minute
845             {
846                 get => minute;
847 
848                 set
849                 {
850                     minute = value;
851                     UpdateInterruptFlag();
852                 }
853             }
854 
855             public int Second
856             {
857                 get => second;
858 
859                 set
860                 {
861                     second = value;
862                     UpdateInterruptFlag();
863                 }
864             }
865 
866             public int Subsecond
867             {
868                 get => subsecond;
869 
870                 set
871                 {
872                     subsecond = value;
873                     UpdateInterruptFlag();
874                 }
875             }
876 
877             public bool PM
878             {
879                 get => Hour > 11 && parent.AMPMFormat;
880 
881                 set
882                 {
883                     pm = value;
884                     ConfigureAMPM();
885                 }
886             }
887 
888             public bool Flag
889             {
890                 get => flag;
891 
892                 set
893                 {
894                     if(value)
895                     {
896                         throw new ArgumentException("This field can only be explicitly cleared");
897                     }
898 
899                     flag = false;
900                     parent.UpdateInterrupts();
901                 }
902             }
903 
904             public bool Enable
905             {
906                 get => enable;
907 
908                 set
909                 {
910                     enable = value;
911                     UpdateInterruptFlag();
912                 }
913             }
914 
915             public bool InterruptEnable
916             {
917                 get => interruptEnable;
918 
919                 set
920                 {
921                     interruptEnable = value;
922                     UpdateInterruptFlag();
923                 }
924             }
925 
926 
927             // This is the number of compared least significant bits. 0 - no subsecond bits
928             // are compared, 1..14 - that many LSBs are compared, 15 - all bits are compared
929             public uint SubsecondsMask
930             {
931                 get => subsecondsMask;
932 
933                 set
934                 {
935                     subsecondsMask = value;
936                     UpdateInterruptFlag();
937                 }
938             }
939 
940             public bool SecondsMask
941             {
942                 get => secondsMask;
943 
944                 set
945                 {
946                     secondsMask = value;
947                     UpdateInterruptFlag();
948                 }
949             }
950 
951             public bool MinutesMask
952             {
953                 get => minutesMask;
954 
955                 set
956                 {
957                     minutesMask = value;
958                     UpdateInterruptFlag();
959                 }
960             }
961 
962             public bool HoursMask
963             {
964                 get => hoursMask;
965 
966                 set
967                 {
968                     hoursMask = value;
969                     UpdateInterruptFlag();
970                 }
971             }
972 
973             public bool DaysMask
974             {
975                 get => daysMask;
976 
977                 set
978                 {
979                     daysMask = value;
980                     UpdateInterruptFlag();
981                 }
982             }
983 
Reset()984             public void Reset()
985             {
986                 day = 0;
987 
988                 hour = 0;
989                 minute = 0;
990                 second = 0;
991                 subsecond = 0;
992 
993                 pm = false;
994                 enable = false;
995                 flag = false;
996                 interruptEnable = false;
997                 secondsMask = false;
998                 minutesMask = false;
999                 hoursMask = false;
1000                 daysMask = false;
1001                 subsecondsMask = 0;
1002             }
1003 
Read(DateTimeSelect select, Rank rank)1004             public uint Read(DateTimeSelect select, Rank rank)
1005             {
1006                 var currentValue = GetTimeSelect(select);
1007                 return (uint)currentValue.ReadRank(rank);
1008             }
1009 
ConfigureAMPM()1010             public void ConfigureAMPM()
1011             {
1012                 if(!parent.AMPMFormat)
1013                 {
1014                     return;
1015                 }
1016 
1017                 if(pm)
1018                 {
1019                     if(Hour < 12)
1020                     {
1021                         Hour += 12;
1022                     }
1023                 }
1024                 else
1025                 {
1026                     if(Hour >= 12)
1027                     {
1028                         Hour -= 12;
1029                     }
1030                 }
1031             }
1032 
UpdateInterruptFlag()1033             public void UpdateInterruptFlag()
1034             {
1035                 // the initial value of `state` will be false
1036                 // for a disabled timer
1037                 var state = Enable;
1038 
1039                 // Subseconds mask equal to 0 means the alarm is activated when the second unit is incremented
1040                 // (or at most once every 1 second)
1041                 if(SubsecondsMask == 0)
1042                 {
1043                     state &= (parent.ticker.Value == parent.ticker.Limit);
1044                 }
1045                 else
1046                 {
1047                     var ssComparedBitMask = (uint)BitHelper.Bits(0, (int)SubsecondsMask);
1048                     var maskedAlarmSubsecond = (ulong)(Subsecond & ssComparedBitMask);
1049                     var maskedCurrentSubsecond = parent.ticker.Value & ssComparedBitMask;
1050                     state &= (maskedAlarmSubsecond == maskedCurrentSubsecond);
1051                 }
1052 
1053                 if(!SecondsMask)
1054                 {
1055                     state &= (Second == masterTimer.TimeState.Second);
1056                 }
1057                 if(!MinutesMask)
1058                 {
1059                     state &= (Minute == masterTimer.TimeState.Minute);
1060                 }
1061                 if(!HoursMask)
1062                 {
1063                     state &= (Hour == masterTimer.TimeState.Hour);
1064                 }
1065                 if(!DaysMask)
1066                 {
1067                     // day of week not supported ATM
1068                     state &= (Day == masterTimer.TimeState.Day);
1069                 }
1070 
1071                 flag = state;
1072                 parent.UpdateInterrupts();
1073             }
1074 
Update(DateTimeSelect what, Rank rank, uint value)1075             public void Update(DateTimeSelect what, Rank rank, uint value)
1076             {
1077                 var currentValue = GetTimeSelect(what);
1078                 var val = currentValue.WithUpdatedRank((int)value, rank);
1079 
1080                 switch(what)
1081                 {
1082                     case DateTimeSelect.Second:
1083                         Second = val;
1084                         break;
1085                     case DateTimeSelect.Minute:
1086                         Minute = val;
1087                         break;
1088                     case DateTimeSelect.Hour:
1089                         Hour = val;
1090                         break;
1091                     case DateTimeSelect.Day:
1092                         Day = val;
1093                         break;
1094                     default:
1095                         throw new ArgumentException($"Unexpected date time select: {what}");
1096                 }
1097             }
1098 
GetTimeSelect(DateTimeSelect what)1099             private int GetTimeSelect(DateTimeSelect what)
1100             {
1101                 switch(what)
1102                 {
1103                     case DateTimeSelect.Second:
1104                         return Second;
1105                     case DateTimeSelect.Minute:
1106                         return Minute;
1107                     case DateTimeSelect.Hour:
1108                         return PM
1109                             ? Hour - 12
1110                             : Hour;
1111                     case DateTimeSelect.Day:
1112                         return Day;
1113                     default:
1114                         throw new ArgumentException($"Unexpected date time select: {what}");
1115                 }
1116             }
1117 
1118             // private DateTime timeState;
1119             private bool pm;
1120             private bool flag;
1121             private bool enable;
1122             private bool interruptEnable;
1123             private uint subsecondsMask;
1124             private bool secondsMask;
1125             private bool minutesMask;
1126             private bool hoursMask;
1127             private bool daysMask;
1128 
1129             private int day;
1130             private int subsecond;
1131             private int second;
1132             private int minute;
1133             private int hour;
1134 
1135             private readonly STM32F4_RTC parent;
1136             private readonly TimerConfig masterTimer;
1137         }
1138 
1139         private enum DayOfTheWeek
1140         {
1141             // 0 value is forbidden
1142             Monday = 1,
1143             Tuesday = 2,
1144             Wednesday = 3,
1145             Thursday = 4,
1146             Friday = 5,
1147             Saturday = 6,
1148             Sunday = 7
1149         }
1150 
1151         private enum DateTimeSelect
1152         {
1153             Second,
1154             Minute,
1155             Hour,
1156             Day,
1157             Month,
1158             Year
1159         }
1160 
1161         private enum Registers
1162         {
1163             TimeRegister = 0x0,
1164             DateRegister = 0x4,
1165             ControlRegister = 0x8,
1166             ISR = 0xc,
1167             PrescalerRegister = 0x10,
1168             WakeupTimerRegister = 0x14,
1169             CalibrationRegister = 0x18,
1170             AlarmARegister = 0x1c,
1171             AlarmBRegister = 0x20,
1172             WriteProtectionRegister = 0x24,
1173             SubSecondRegister = 0x28,
1174             ShiftControlRegister = 0x2c,
1175             TimestampTimeRegister = 0x30,
1176             TimestampDateRegister = 0x34,
1177             TimestampSubSecondRegister = 0x38,
1178             ClockCalibrationRegister = 0x3c,
1179             TamperAndAlternateFunctionConfigurationRegister = 0x40,
1180             AlarmASubSecondRegister = 0x44,
1181             AlarmBSubSecondRegister = 0x48,
1182             OptionRegister = 0x4c,
1183             BackupStart = 0x50,
1184             BackupEnd = 0x9c
1185         }
1186     }
1187 }
1188