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