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