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