1 //
2 // Copyright (c) 2010-2024 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.Logging;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Peripherals.Timers;
13 
14 namespace Antmicro.Renode.Peripherals.Miscellaneous
15 {
16     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)]
17     public class STM32L0_RCC : BasicDoubleWordPeripheral, IKnownSize
18     {
STM32L0_RCC(IMachine machine, IPeripheral rtc = null, ITimer lptimer = null, IHasDivisibleFrequency systick = null, long apbFrequency = DefaultApbFrequency, long lsiFrequency = DefaultLsiFrequency, long lseFrequency = DefaultLseFrequency, long hseFrequency = DefaultHseFrequency)19         public STM32L0_RCC(IMachine machine, IPeripheral rtc = null, ITimer lptimer = null, IHasDivisibleFrequency systick = null,
20             long apbFrequency = DefaultApbFrequency, long lsiFrequency = DefaultLsiFrequency, long lseFrequency = DefaultLseFrequency,
21             long hseFrequency = DefaultHseFrequency) : base(machine)
22         {
23             if(systick == null)
24             {
25                 this.Log(LogLevel.Warning, "Systick not passed in the RCC constructor. Changes to the system clock will be ignored");
26             }
27             this.systick = systick;
28             if(rtc == null)
29             {
30                 this.Log(LogLevel.Warning, "RTC not passed in the RCC constructor. Changes to the real-time clock will be ignored");
31             }
32             this.rtc = rtc;
33             if(lptimer == null)
34             {
35                 this.Log(LogLevel.Warning, "Lptimer not passed in the RCC constructor. Changes to the low-power timer clock will be ignored");
36             }
37             this.lptimer = lptimer;
38 
39             this.apbFrequency = apbFrequency;
40             this.lsiFrequency = lsiFrequency;
41             this.lseFrequency = lseFrequency;
42             this.hseFrequency = hseFrequency;
43 
44             DefineRegisters();
45             Reset();
46         }
47 
Reset()48         public override void Reset()
49         {
50             // Set PLL divisor to a default value of 1.
51             // Despite it not being a valid value, this is what it resets to
52             // and we don't want the error about invalid PLLDIV to show up after reset.
53             pllDivisor = 1;
54             pllMultiplier = 3;
55             msiMultiplier = 32;
56             if(systick != null)
57             {
58                 systick.Divider = 1;
59             }
60             base.Reset();
61             UpdateClocks();
62         }
63 
64         public long Size => 0x400;
65 
66         // Update frequencies and divisors of all clocks connected to the RCC.
67         // Make sure it's called after any configuration register is touched
UpdateClocks()68         private void UpdateClocks()
69         {
70             UpdateSystemClock();
71             UpdateLpTimerClock();
72         }
73 
UpdateSystemClock()74         private void UpdateSystemClock()
75         {
76             if(systick == null)
77             {
78                 return;
79             }
80 
81             var old = systick.Frequency;
82             switch(systemClockSwitch.Value)
83             {
84                 case SystemClockSourceSelection.Msi:
85                     systick.Frequency = MsiFrequency;
86                     break;
87                 case SystemClockSourceSelection.Hsi16:
88                     systick.Frequency = Hsi16Frequency;
89                     break;
90                 case SystemClockSourceSelection.Hse:
91                     systick.Frequency = hseFrequency;
92                     break;
93                 case SystemClockSourceSelection.Pll:
94                     if(!pllOn.Value)
95                     {
96                         this.Log(LogLevel.Error, "Systick source set to PLL when PLL is disabled.");
97                     }
98                     systick.Frequency = PllFrequency;
99                     break;
100                 default:
101                     throw new Exception("unreachable code");
102             }
103             if(old != systick.Frequency)
104             {
105                 this.Log(
106                     LogLevel.Debug,
107                     "systick clock frequency changed to {0}. Current effective frequency: {1}",
108                     systick.Frequency,
109                     systick.Frequency / systick.Divider
110                 );
111             }
112         }
113 
UpdateLpTimerClock()114         private void UpdateLpTimerClock()
115         {
116             if(lptimer == null)
117             {
118                 return;
119             }
120 
121             var old = lptimer.Frequency;
122             switch(lpTimer1Selection.Value)
123             {
124                 case LpTimerClockSourceSelection.Apb:
125                     lptimer.Frequency = apbFrequency;
126                     break;
127                 case LpTimerClockSourceSelection.Lsi:
128                     lptimer.Frequency = lsiFrequency;
129                     break;
130                 case LpTimerClockSourceSelection.Hsi16:
131                     lptimer.Frequency = Hsi16Frequency;
132                     break;
133                 case LpTimerClockSourceSelection.Lse:
134                     lptimer.Frequency = lseFrequency;
135                     break;
136                 default:
137                     throw new Exception("unreachable code");
138             }
139             if(old != lptimer.Frequency)
140             {
141                 this.Log(LogLevel.Debug, "LpTimer clock frequency changed to {0}", lptimer.Frequency);
142             }
143         }
144 
DefineRegisters()145         private void DefineRegisters()
146         {
147             // Keep in mind that most of these registers do not affect other
148             // peripherals or their clocks.
149             Registers.ClockControl.Define(this, 0x00000300)
150                 .WithFlag(0, out var hsi16on, name: "HSI16ON")
151                 .WithFlag(1, name: "HSI16KERON")
152                 .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => hsi16on.Value, name: "HSI16RDYF")
153                 .WithFlag(3, out hsi16diven, name: "HSI16DIVEN")
154                 .WithFlag(4, FieldMode.Read, valueProviderCallback: _ => hsi16diven.Value, name: "HSI16DIVF")
155                 .WithTaggedFlag("HSI16OUTEN", 5)
156                 .WithReservedBits(6, 2)
157                 .WithFlag(8, out var msion, name: "MSION")
158                 .WithFlag(9, FieldMode.Read, valueProviderCallback: _ => msion.Value, name: "MSIRDY")
159                 .WithReservedBits(10, 6)
160                 .WithFlag(16, out var hseon, name: "HSEON")
161                 .WithFlag(17, FieldMode.Read, valueProviderCallback: _ => hseon.Value, name: "HSERDY")
162                 .WithFlag(18, name: "HSEBYP")
163                 .WithTag("CSSHSEON", 19, 1)
164                 .WithValueField(20, 2, name: "RTCPRE")
165                 .WithReservedBits(22, 2)
166                 .WithFlag(24, out pllOn, name: "PLLON",
167                     writeCallback: (previous, value) =>
168                     {
169                         if(!previous && value && pllDivisor == 1)
170                         {
171                             this.Log(LogLevel.Error, "PLL enabled without setting a valid PLLDIV");
172                         }
173                     })
174                 .WithFlag(25, FieldMode.Read, valueProviderCallback: _ => pllOn.Value, name: "PLLRDY")
175                 .WithReservedBits(26, 6)
176                 .WithWriteCallback((_, __) => UpdateClocks())
177                 ;
178 
179             Registers.InternalClockSourcesCalibration.Define(this, 0x0000B000)
180                 .WithValueField(0, 8, name: "HSI16CAL")
181                 .WithValueField(8, 5, name: "HSI16TRIM")
182                 .WithValueField(13, 3, name: "MSIRANGE",
183                     writeCallback: (_, value) =>
184                     {
185                         if(value >= 0b111)
186                         {
187                             this.Log(LogLevel.Error, "Invalid MSI range: {0}", value);
188                             return;
189                         }
190 
191                         msiMultiplier = (long)Math.Pow(2, value);
192                     })
193                 .WithValueField(16, 8, name: "MSICAL")
194                 .WithValueField(24, 8, name: "MSITRIM")
195                 .WithWriteCallback((_, __) => UpdateClocks())
196                 ;
197 
198             Registers.ClockRecoveryRc.Define(this)
199                 .WithFlag(0, out var hsi48on, name: "HSI48ON")
200                 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => hsi48on.Value, name: "HSI48RDY")
201                 .WithTaggedFlag("HSI48DIV6EN", 2)
202                 .WithReservedBits(3, 5)
203                 .WithValueField(8, 8, name: "HSI48CAL")
204                 .WithReservedBits(16, 16)
205                 .WithWriteCallback((_, __) => UpdateClocks())
206                 ;
207 
208             Registers.ClockConfigurationCfgr.Define(this)
209                 .WithEnumField<DoubleWordRegister, SystemClockSourceSelection>(0, 2, out systemClockSwitch, name: "SW")
210                 .WithValueField(2, 2, FieldMode.Read, name: "SWS", valueProviderCallback: _ => (ulong)systemClockSwitch.Value)
211                 .WithValueField(4, 4, name: "HPRE",
212                     writeCallback: (previous, value) =>
213                     {
214                         if(systick == null || previous == value)
215                         {
216                             return;
217                         }
218 
219                         // SYSCLK is not divided unless HPRE is set to 0b1000 or higher,
220                         // in which case it's divided by consecutive powers of 2.
221                         if((0b1000 & value) == 0)
222                         {
223                             systick.Divider = 1;
224                         }
225                         else
226                         {
227                             var power = (0b111 & value) + 1;
228                             systick.Divider = (int)Math.Pow(2, power);
229                         }
230                         this.Log(
231                             LogLevel.Debug,
232                             "systick clock divisor changed to {0}. Current effective frequency: {1}",
233                             systick.Divider,
234                             systick.Frequency / systick.Divider
235                         );
236                     })
237                 .WithValueField(8, 3, name: "PPRE1")
238                 .WithValueField(11, 3, name: "PPRE2")
239                 .WithReservedBits(14, 1)
240                 .WithFlag(15, name: "STOPWUCK")
241                 .WithEnumField<DoubleWordRegister, PllSourceSelection>(16, 1, out pllSource, name: "PLLSRC",
242                     writeCallback: (previous, value) =>
243                     {
244                         if(pllOn.Value && previous != value)
245                         {
246                             this.Log(LogLevel.Error, "PLLDIV modified while PLL is enabled");
247                         }
248                     })
249                 .WithReservedBits(17, 1)
250                 .WithValueField(18, 4, name: "PLLMUL",
251                     writeCallback: (previous, value) =>
252                     {
253                         if(pllOn.Value && previous != value)
254                         {
255                             this.Log(LogLevel.Error, "PLLMUL modified while PLL is enabled");
256                         }
257 
258                         switch(value)
259                         {
260                             case 0b0000:
261                                 pllMultiplier = 3;
262                                 break;
263                             case 0b0001:
264                                 pllMultiplier = 4;
265                                 break;
266                             case 0b0010:
267                                 pllMultiplier = 6;
268                                 break;
269                             case 0b0011:
270                                 pllMultiplier = 8;
271                                 break;
272                             case 0b0100:
273                                 pllMultiplier = 12;
274                                 break;
275                             case 0b0101:
276                                 pllMultiplier = 16;
277                                 break;
278                             case 0b0110:
279                                 pllMultiplier = 24;
280                                 break;
281                             case 0b0111:
282                                 pllMultiplier = 32;
283                                 break;
284                             case 0b1000:
285                                 pllMultiplier = 48;
286                                 break;
287                             default:
288                                 // We don't need a special check here, compared to PLLDIV,
289                                 // as here the reset value is valid and the only invalid values
290                                 // are ones that'd be deliberately set by the software
291                                 this.Log(LogLevel.Error, "Invalid PLLMUL: {0}", value);
292                                 break;
293                         }
294                     })
295                 .WithValueField(22, 2, name: "PLLDIV",
296                     writeCallback: (previous, value) =>
297                     {
298                         if(pllOn.Value && previous != value)
299                         {
300                             this.Log(LogLevel.Error, "PLLDIV modified while PLL is enabled");
301                         }
302 
303                         switch(value)
304                         {
305                             case 0b00:
306                                 // Only emit the error if the PLL divisor has been set to a valid value previously.
307                                 // If it hasn't, we'll still emit an error whenever PLL is enabled,
308                                 // and if it has, we'll inform that the software changed it to an invalid value
309                                 // (but only when the value actually changes)
310                                 if(pllDivisor != 1 && previous != value)
311                                 {
312                                     this.Log(LogLevel.Error, "Invalid PLLDIV: 0");
313                                 }
314                                 break;
315                             case 0b01:
316                                 pllDivisor = 2;
317                                 break;
318                             case 0b10:
319                                 pllDivisor = 3;
320                                 break;
321                             case 0b11:
322                                 pllDivisor = 4;
323                                 break;
324                             default:
325                                 throw new Exception("unreachable code");
326                         }
327                     })
328                 .WithValueField(24, 4, name: "MCOSEL")
329                 .WithValueField(28, 3, name: "MCOPRE")
330                 .WithWriteCallback((_, __) => UpdateClocks())
331                 ;
332 
333             Registers.ClockInterruptEnable.Define(this)
334                 .WithFlag(0, out var lsirdyie, name: "LSIRDYIE")
335                 .WithFlag(1, out var lserdyie, name: "LSERDYIE")
336                 .WithFlag(2, out var hsi16rdyie, name: "HSI16RDYIE")
337                 .WithFlag(3, out var hserdyie, name: "HSERDYIE")
338                 .WithFlag(4, out var pllrdyie, name: "PLLRDYIE")
339                 .WithFlag(5, out var msirdyie, name: "MSIRDYIE")
340                 .WithFlag(6, out var hsi48rdyie, name: "HSI48RDYIE")
341                 .WithFlag(7, name: "CSSLSE")
342                 .WithReservedBits(8, 24)
343                 ;
344 
345             Registers.ClockInterruptFlag.Define(this)
346                 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => lsirdyie.Value, name: "LSIRDYF")
347                 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => lserdyie.Value, name: "LSERDYF")
348                 .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => hsi16rdyie.Value, name: "HSI16RDYF")
349                 .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => hserdyie.Value, name: "HSERDYF")
350                 .WithFlag(4, FieldMode.Read, valueProviderCallback: _ => pllrdyie.Value, name: "PLLRDYF")
351                 .WithFlag(5, FieldMode.Read, valueProviderCallback: _ => msirdyie.Value, name: "MSIRDYF")
352                 .WithFlag(6, FieldMode.Read, valueProviderCallback: _ => hsi48rdyie.Value, name: "HSI48RDYF")
353                 .WithTaggedFlag("CSSLSEF", 7)
354                 .WithTaggedFlag("CSSHSEF", 8)
355                 .WithReservedBits(9, 23)
356                 ;
357 
358             Registers.ClockInterruptClear.Define(this)
359                 .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { if(value) { lsirdyie.Value = false; } }, name: "LSIRDYC")
360                 .WithFlag(1, FieldMode.Write, writeCallback: (_, value) => { if(value) { lserdyie.Value = false; } }, name: "LSERDYC")
361                 .WithFlag(2, FieldMode.Write, writeCallback: (_, value) => { if(value) { hsi16rdyie.Value = false;  } }, name: "HSI16RDYC")
362                 .WithFlag(3, FieldMode.Write, writeCallback: (_, value) => { if(value) { hserdyie.Value = false;  } }, name: "HSERDYC")
363                 .WithFlag(4, FieldMode.Write, writeCallback: (_, value) => { if(value) { pllrdyie.Value = false; } }, name: "PLLRDYC")
364                 .WithFlag(5, FieldMode.Write, writeCallback: (_, value) => { if(value) { msirdyie.Value = false; } }, name: "MSIRDYC")
365                 .WithFlag(6, FieldMode.Write, writeCallback: (_, value) => { if(value) { hsi48rdyie.Value = false; } }, name: "HSI48RDYC")
366                 .WithTaggedFlag("CSSLSEC", 7)
367                 .WithTaggedFlag("CSSHSEC", 8)
368                 .WithReservedBits(9, 23)
369                 ;
370 
371             Registers.IoPortReset.Define(this)
372                 .WithTaggedFlag("IOPARST", 0)
373                 .WithTaggedFlag("IOPBRST", 1)
374                 .WithTaggedFlag("IOPCRST", 2)
375                 .WithTaggedFlag("IOPDRST", 3)
376                 .WithTaggedFlag("IOPERST", 4)
377                 .WithReservedBits(5, 2)
378                 .WithTaggedFlag("IOPHRST", 7)
379                 ;
380 
381             Registers.AhbPeripheralReset.Define(this)
382                 .WithTaggedFlag("DMARST", 0)
383                 .WithReservedBits(1, 7)
384                 .WithTaggedFlag("MIFRST", 8)
385                 .WithReservedBits(9, 3)
386                 .WithTaggedFlag("CRCRST", 12)
387                 .WithReservedBits(13, 3)
388                 .WithTaggedFlag("TSCRST", 16)
389                 .WithReservedBits(17, 3)
390                 .WithTaggedFlag("RNGRST", 20)
391                 .WithReservedBits(21, 3)
392                 .WithTaggedFlag("CRYPTRST", 24)
393                 .WithReservedBits(25, 7)
394                 ;
395 
396             Registers.Apb2PeripheralReset.Define(this)
397                 .WithTaggedFlag("SYSCFGRST", 0)
398                 .WithReservedBits(1, 1)
399                 .WithTaggedFlag("TIM21RST", 2)
400                 .WithReservedBits(3, 2)
401                 .WithTaggedFlag("TIM22RST", 5)
402                 .WithReservedBits(6, 3)
403                 .WithTaggedFlag("ADCRST", 9)
404                 .WithReservedBits(10, 2)
405                 .WithTaggedFlag("SPI1RST", 12)
406                 .WithReservedBits(13, 1)
407                 .WithTaggedFlag("USART1RST", 14)
408                 .WithReservedBits(15, 7)
409                 .WithTaggedFlag("DBGRST", 22)
410                 .WithReservedBits(23, 9)
411                 ;
412 
413             Registers.Apb1PeripheralReset.Define(this)
414                 .WithTaggedFlag("TIM2RST", 0)
415                 .WithTaggedFlag("TIM3RST", 1)
416                 .WithReservedBits(2, 2)
417                 .WithTaggedFlag("TIM6RST", 4)
418                 .WithTaggedFlag("TIM7RST", 5)
419                 .WithReservedBits(6, 3)
420                 .WithTaggedFlag("LCDRST", 9)
421                 .WithReservedBits(10, 1)
422                 .WithTaggedFlag("WWDGRST", 11)
423                 .WithReservedBits(12, 2)
424                 .WithTaggedFlag("SPI2RST", 14)
425                 .WithReservedBits(15, 2)
426                 .WithTaggedFlag("USART2RST", 17)
427                 .WithTaggedFlag("LPUART1RST", 18)
428                 .WithTaggedFlag("USART4RST", 19)
429                 .WithTaggedFlag("USART5RST", 20)
430                 .WithTaggedFlag("I2C1RST", 21)
431                 .WithTaggedFlag("I2C2RST", 22)
432                 .WithTaggedFlag("USBRST", 23)
433                 .WithReservedBits(24, 3)
434                 .WithTaggedFlag("CRSRST", 27)
435                 .WithTaggedFlag("PWRRST", 28)
436                 .WithTaggedFlag("DACRST", 29)
437                 .WithTaggedFlag("I2C3RST", 30)
438                 .WithFlag(31, writeCallback: (_, value) =>
439                 {
440                     if(value)
441                     {
442                         (lptimer as IPeripheral)?.Reset();
443                     }
444                 }, name: "LPTIM1RST")
445                 ;
446 
447             // Most clock enable bits are flags instead of tags to reduce the number of warnings in the log
448             Registers.IoPortClockEnable.Define(this)
449                 .WithFlag(0, name: "IOPAEN")
450                 .WithFlag(1, name: "IOPBEN")
451                 .WithFlag(2, name: "IOPCEN")
452                 .WithFlag(3, name: "IOPDEN")
453                 .WithFlag(4, name: "IOPEEN")
454                 .WithReservedBits(5, 2)
455                 .WithFlag(7, name: "IOPHEN")
456                 ;
457 
458             Registers.AhbPeripheralClockEnable.Define(this, 0x100)
459                 .WithFlag(0, name: "DMAEN")
460                 .WithReservedBits(1, 7)
461                 .WithFlag(8, name: "MIFEN")
462                 .WithReservedBits(9, 3)
463                 .WithFlag(12, name: "CRCEN")
464                 .WithReservedBits(13, 3)
465                 .WithFlag(16, name: "TSCEN")
466                 .WithReservedBits(17, 3)
467                 .WithFlag(20, name: "RNGEN")
468                 .WithReservedBits(21, 3)
469                 .WithFlag(24, name: "CRYPEN")
470                 .WithReservedBits(25, 7)
471                 ;
472 
473             Registers.Apb2PeripheralClockEnable.Define(this)
474                 .WithFlag(0, name: "SYSCFEN")
475                 .WithReservedBits(1, 1)
476                 .WithFlag(2, name: "TIM21EN")
477                 .WithReservedBits(3, 2)
478                 .WithFlag(5, name: "TIM22EN")
479                 .WithReservedBits(6, 1)
480                 .WithFlag(7, name: "FWEN")
481                 .WithReservedBits(8, 1)
482                 .WithFlag(9, name: "ADCEN")
483                 .WithReservedBits(10, 2)
484                 .WithFlag(12, name: "SPI1EN")
485                 .WithReservedBits(13, 1)
486                 .WithFlag(14, name: "USART1EN")
487                 .WithReservedBits(15, 7)
488                 .WithFlag(22, name: "DBGEN")
489                 .WithReservedBits(23, 9)
490                 ;
491 
492             Registers.Apb1PeripheralClockEnable.Define(this)
493                 .WithFlag(0, name: "TIM2EN")
494                 .WithFlag(1, name: "TIM3EN")
495                 .WithReservedBits(2, 2)
496                 .WithFlag(4, name: "TIM6EN")
497                 .WithFlag(5, name: "TIM7EN")
498                 .WithReservedBits(6, 3)
499                 .WithFlag(9, name: "LCDEN")
500                 .WithReservedBits(10, 1)
501                 .WithFlag(11, name: "WWDGEN")
502                 .WithReservedBits(12, 2)
503                 .WithFlag(14, name: "SPI2EN")
504                 .WithReservedBits(15, 2)
505                 .WithFlag(17, name: "USART2EN")
506                 .WithFlag(18, name: "LPUART1EN")
507                 .WithFlag(19, name: "USART4EN")
508                 .WithFlag(20, name: "USART5EN")
509                 .WithFlag(21, name: "I2C1EN")
510                 .WithFlag(22, name: "I2C2EN")
511                 .WithFlag(23, name: "USBEN")
512                 .WithReservedBits(24, 3)
513                 .WithFlag(27, name: "CRSEN")
514                 .WithFlag(28, name: "PWREN")
515                 .WithFlag(29, name: "DACEN")
516                 .WithFlag(30, name: "I2C3EN")
517                 .WithFlag(31, name: "LPTIM1EN")
518                 ;
519 
520             Registers.IoPortClockEnableInSleepMode.Define(this)
521                 .WithFlag(0, name: "IOPASMEN")
522                 .WithFlag(1, name: "IOPBSMEN")
523                 .WithFlag(2, name: "IOPCSMEN")
524                 .WithFlag(3, name: "IOPDSMEN")
525                 .WithFlag(4, name: "IOPESMEN")
526                 .WithReservedBits(5, 2)
527                 .WithFlag(7, name: "IOPHSMEN")
528                 ;
529 
530             Registers.AhbPeripheralClockEnableInSleepMode.Define(this, 0x100)
531                 .WithFlag(0, name: "DMASMEN")
532                 .WithReservedBits(1, 7)
533                 .WithFlag(8, name: "MIFSMEN")
534                 .WithReservedBits(9, 3)
535                 .WithFlag(12, name: "CRCSMEN")
536                 .WithReservedBits(13, 3)
537                 .WithFlag(16, name: "TSCSMEN")
538                 .WithReservedBits(17, 3)
539                 .WithFlag(20, name: "RNGSMEN")
540                 .WithReservedBits(21, 3)
541                 .WithFlag(24, name: "CRYPSMEN")
542                 .WithReservedBits(25, 7)
543                 ;
544 
545             Registers.Apb2PeripheralClockEnableInSleepMode.Define(this)
546                 .WithFlag(0, name: "SYSCFSMEN")
547                 .WithReservedBits(1, 1)
548                 .WithFlag(2, name: "TIM21SMEN")
549                 .WithReservedBits(3, 2)
550                 .WithFlag(5, name: "TIM22SMEN")
551                 .WithReservedBits(6, 1)
552                 .WithFlag(7, name: "FWSMEN")
553                 .WithReservedBits(8, 1)
554                 .WithFlag(9, name: "ADCSMEN")
555                 .WithReservedBits(10, 2)
556                 .WithFlag(12, name: "SPI1SMEN")
557                 .WithReservedBits(13, 1)
558                 .WithFlag(14, name: "USART1SMEN")
559                 .WithReservedBits(15, 7)
560                 .WithFlag(22, name: "DBGSMEN")
561                 .WithReservedBits(23, 9)
562                 ;
563 
564             Registers.Apb1PeripheralClockEnableInSleepMode.Define(this)
565                 .WithFlag(0, name: "TIM2SMEN")
566                 .WithFlag(1, name: "TIM3SMEN")
567                 .WithReservedBits(2, 2)
568                 .WithFlag(4, name: "TIM6SMEN")
569                 .WithFlag(5, name: "TIM7SMEN")
570                 .WithReservedBits(6, 3)
571                 .WithFlag(9, name: "LCDSMEN")
572                 .WithReservedBits(10, 1)
573                 .WithFlag(11, name: "WWDGSMEN")
574                 .WithReservedBits(12, 2)
575                 .WithFlag(14, name: "SPI2SMEN")
576                 .WithReservedBits(15, 2)
577                 .WithFlag(17, name: "USART2SMEN")
578                 .WithFlag(18, name: "LPUART1SMEN")
579                 .WithFlag(19, name: "USART4SMEN")
580                 .WithFlag(20, name: "USART5SMEN")
581                 .WithFlag(21, name: "I2C1SMEN")
582                 .WithFlag(22, name: "I2C2SMEN")
583                 .WithFlag(23, name: "USBSMEN")
584                 .WithReservedBits(24, 3)
585                 .WithFlag(27, name: "CRSSMEN")
586                 .WithFlag(28, name: "PWRSMEN")
587                 .WithFlag(29, name: "DACSMEN")
588                 .WithFlag(30, name: "I2C3SMEN")
589                 .WithFlag(31, name: "LPTIM1SMEN")
590                 ;
591 
592             Registers.ClockConfigurationCcipr.Define(this)
593                 .WithValueField(0, 2, name: "USART1SEL")
594                 .WithValueField(2, 2, name: "USART2SEL")
595                 .WithReservedBits(4, 6)
596                 .WithValueField(10, 2, name: "LPUART1SEL")
597                 .WithValueField(12, 2, name: "I2C1SEL")
598                 .WithReservedBits(14, 2)
599                 .WithValueField(16, 2, name: "I2C3SEL")
600                 .WithEnumField<DoubleWordRegister, LpTimerClockSourceSelection>(18, 2, out lpTimer1Selection, name: "LPTIM1SEL")
601                 .WithReservedBits(20, 6)
602                 .WithTaggedFlag("HSI48SEL", 26)
603                 .WithReservedBits(27, 5)
604                 .WithWriteCallback((_, __) => UpdateClocks())
605                 ;
606 
607             Registers.ControlStatus.Define(this, 0x0C000000)
608                 .WithFlag(0, out var lsion, name: "LSION")
609                 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => lsion.Value, name: "LSIRDY")
610                 .WithReservedBits(2, 6)
611                 .WithFlag(8, out var lseon, name: "LSEON")
612                 .WithFlag(9, FieldMode.Read, valueProviderCallback: _ => lseon.Value, name: "LSERDY")
613                 .WithTaggedFlag("LSEBYP", 10)
614                 .WithValueField(11, 2, name: "LSEDRV")
615                 .WithTaggedFlag("CSSLSEON", 13)
616                 .WithTaggedFlag("CSSLSED", 14)
617                 .WithValueField(16, 2, name: "RTCSEL")
618                 .WithFlag(18, writeCallback: (_, value) =>
619                     {
620                         if(rtc == null)
621                         {
622                             return;
623                         }
624                         sysbus.SetPeripheralEnabled(rtc, value);
625                     },
626                     valueProviderCallback: _ => rtc == null ? false : sysbus.IsPeripheralEnabled(rtc), name: "RTCEN")
627                 .WithFlag(19, writeCallback: (_, value) =>
628                     {
629                         if(rtc == null)
630                         {
631                             return;
632                         }
633                         if(value)
634                         {
635                             rtc.Reset();
636                             sysbus.DisablePeripheral(rtc);
637                         }
638                     }, name: "RTCRST")
639                 .WithReservedBits(20, 3)
640                 .WithTaggedFlag("RMVF", 23)
641                 .WithTaggedFlag("FWRSTF", 24)
642                 .WithTaggedFlag("OBLRSTF", 25)
643                 .WithTaggedFlag("PINRSTF", 26)
644                 .WithTaggedFlag("PORRSTF", 27)
645                 .WithTaggedFlag("SFTRSTF", 28)
646                 .WithTaggedFlag("IWDGRSTF", 29)
647                 .WithTaggedFlag("WWDGRSTF", 30)
648                 .WithTaggedFlag("LPWRRSTF", 31)
649                 ;
650         }
651 
652         private long Hsi16Frequency { get => 16000000 / (hsi16diven.Value ? 4 : 1); }
653         private long PllFrequency
654         {
655             get =>
656                 (pllSource.Value == PllSourceSelection.Hse ? hseFrequency : Hsi16Frequency)
657                 * pllMultiplier
658                 / pllDivisor;
659         }
660         private long MsiFrequency { get => BaseMsiFrequency * msiMultiplier; }
661 
662         private IFlagRegisterField hsi16diven;
663         private IEnumRegisterField<PllSourceSelection> pllSource;
664         private IEnumRegisterField<SystemClockSourceSelection> systemClockSwitch;
665         private IEnumRegisterField<LpTimerClockSourceSelection> lpTimer1Selection;
666         private IFlagRegisterField pllOn;
667 
668         private long pllMultiplier;
669         private long pllDivisor;
670         private long msiMultiplier;
671 
672         private readonly long apbFrequency;
673         private readonly long lsiFrequency;
674         private readonly long lseFrequency;
675         private readonly long hseFrequency;
676 
677         private readonly IHasDivisibleFrequency systick;
678         private readonly IPeripheral rtc;
679         private readonly ITimer lptimer;
680 
681         private const long DefaultApbFrequency = 32000000;
682         private const long DefaultLsiFrequency = 37000;
683         private const long DefaultLseFrequency = 32768;
684         private const long DefaultHseFrequency = 16000000;
685         private const long DefaultMsiFrequency = 2097000;
686         private const long BaseMsiFrequency = 65536;
687 
688         // There can't be one common ClockSourceSelection enum because different peripherals
689         // have different sets of possible values:
690         // I2C has APB, system clock, HSI16, reserved;
691         // UARTs have APB, system clock, HSI16, LSE.
692         // The system clock can be HSI16, HSE, PLL, MSI (default)
693         private enum LpTimerClockSourceSelection
694         {
695             Apb,
696             Lsi,
697             Hsi16,
698             Lse,
699         }
700 
701         private enum SystemClockSourceSelection
702         {
703             Msi,
704             Hsi16,
705             Hse,
706             Pll,
707         }
708 
709         private enum PllSourceSelection
710         {
711             Hsi16,
712             Hse,
713         }
714 
715         private enum Registers
716         {
717             ClockControl = 0x0,
718             InternalClockSourcesCalibration = 0x4,
719             ClockRecoveryRc = 0x8,
720             ClockConfigurationCfgr = 0xC,
721             ClockInterruptEnable = 0x10,
722             ClockInterruptFlag = 0x14,
723             ClockInterruptClear = 0x18,
724             IoPortReset = 0x1C,
725             AhbPeripheralReset = 0x20,
726             Apb2PeripheralReset = 0x24,
727             Apb1PeripheralReset = 0x28,
728             IoPortClockEnable = 0x2C,
729             AhbPeripheralClockEnable = 0x30,
730             Apb2PeripheralClockEnable = 0x34,
731             Apb1PeripheralClockEnable = 0x38,
732             IoPortClockEnableInSleepMode = 0x3C,
733             AhbPeripheralClockEnableInSleepMode = 0x40,
734             Apb2PeripheralClockEnableInSleepMode = 0x44,
735             Apb1PeripheralClockEnableInSleepMode = 0x48,
736             ClockConfigurationCcipr = 0x4C,
737             ControlStatus = 0x50,
738         }
739     }
740 }
741