1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // Copyright (c) 2022-2025 Silicon Labs 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 9 using System; 10 using System.Diagnostics; 11 using System.IO; 12 using System.Collections.Generic; 13 using Antmicro.Renode.Core; 14 using Antmicro.Renode.Core.Structure.Registers; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Renode.Exceptions; 17 using Antmicro.Renode.Time; 18 using Antmicro.Renode.Peripherals.Timers; 19 using Antmicro.Renode.Peripherals.CPU; 20 using Antmicro.Renode.Peripherals.Bus; 21 22 namespace Antmicro.Renode.Peripherals.Miscellaneous.SiLabs 23 { 24 // Allows for the viewing of register contents when debugging 25 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 26 public class EFR32xG2_HFXO_2 : IHFXO_EFR32xG2, IDoubleWordPeripheral 27 { EFR32xG2_HFXO_2(Machine machine, uint startupDelayTicks)28 public EFR32xG2_HFXO_2(Machine machine, uint startupDelayTicks) 29 { 30 this.machine = machine; 31 this.delayTicks = startupDelayTicks; 32 33 timer = new LimitTimer(machine.ClockSource, 32768, this, "hfxodelay", 0xFFFFFFFFUL, direction: Direction.Ascending, 34 enabled: false, workMode: WorkMode.OneShot, eventEnabled: true, autoUpdate: true); 35 timer.LimitReached += OnStartUpTimerExpired; 36 37 IRQ = new GPIO(); 38 39 registersCollection = BuildRegistersCollection(); 40 } 41 Reset()42 public void Reset() 43 { 44 timer.Enabled = false; 45 } 46 ReadDoubleWord(long offset)47 public uint ReadDoubleWord(long offset) 48 { 49 return ReadRegister(offset); 50 } 51 ReadRegister(long offset, bool internal_read = false)52 private uint ReadRegister(long offset, bool internal_read = false) 53 { 54 var result = 0U; 55 long internal_offset = offset; 56 57 // Set, Clear, Toggle registers should only be used for write operations. But just in case we convert here as well. 58 if (offset >= SetRegisterOffset && offset < ClearRegisterOffset) 59 { 60 // Set register 61 internal_offset = offset - SetRegisterOffset; 62 if(!internal_read) 63 { 64 this.Log(LogLevel.Noisy, "SET Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset); 65 } 66 } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset) 67 { 68 // Clear register 69 internal_offset = offset - ClearRegisterOffset; 70 if(!internal_read) 71 { 72 this.Log(LogLevel.Noisy, "CLEAR Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset); 73 } 74 } else if (offset >= ToggleRegisterOffset) 75 { 76 // Toggle register 77 internal_offset = offset - ToggleRegisterOffset; 78 if(!internal_read) 79 { 80 this.Log(LogLevel.Noisy, "TOGGLE Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset); 81 } 82 } 83 84 if(!registersCollection.TryRead(internal_offset, out result)) 85 { 86 if(!internal_read) 87 { 88 this.Log(LogLevel.Noisy, "Unhandled read at offset 0x{0:X} ({1}).", internal_offset, (Registers)internal_offset); 89 } 90 } 91 else 92 { 93 if(!internal_read) 94 { 95 this.Log(LogLevel.Noisy, "Read at offset 0x{0:X} ({1}), returned 0x{2:X}.", internal_offset, (Registers)internal_offset, result); 96 } 97 } 98 99 return result; 100 } 101 WriteDoubleWord(long offset, uint value)102 public void WriteDoubleWord(long offset, uint value) 103 { 104 WriteRegister(offset, value); 105 } 106 WriteRegister(long offset, uint value, bool internal_write = false)107 private void WriteRegister(long offset, uint value, bool internal_write = false) 108 { 109 machine.ClockSource.ExecuteInLock(delegate { 110 long internal_offset = offset; 111 uint internal_value = value; 112 113 if (offset >= SetRegisterOffset && offset < ClearRegisterOffset) 114 { 115 // Set register 116 internal_offset = offset - SetRegisterOffset; 117 uint old_value = ReadRegister(internal_offset, true); 118 internal_value = old_value | value; 119 this.Log(LogLevel.Noisy, "SET Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}, SET_value=0x{3:X}, old_value=0x{4:X}, new_value=0x{5:X}", (Registers)internal_offset, offset, internal_offset, value, old_value, internal_value); 120 } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset) 121 { 122 // Clear register 123 internal_offset = offset - ClearRegisterOffset; 124 uint old_value = ReadRegister(internal_offset, true); 125 internal_value = old_value & ~value; 126 this.Log(LogLevel.Noisy, "CLEAR Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}, CLEAR_value=0x{3:X}, old_value=0x{4:X}, new_value=0x{5:X}", (Registers)internal_offset, offset, internal_offset, value, old_value, internal_value); 127 } else if (offset >= ToggleRegisterOffset) 128 { 129 // Toggle register 130 internal_offset = offset - ToggleRegisterOffset; 131 uint old_value = ReadRegister(internal_offset, true); 132 internal_value = old_value ^ value; 133 this.Log(LogLevel.Noisy, "TOGGLE Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}, TOGGLE_value=0x{3:X}, old_value=0x{4:X}, new_value=0x{5:X}", (Registers)internal_offset, offset, internal_offset, value, old_value, internal_value); 134 } 135 136 this.Log(LogLevel.Noisy, "Write at offset 0x{0:X} ({1}), value 0x{2:X}.", internal_offset, (Registers)internal_offset, internal_value); 137 138 if(!registersCollection.TryWrite(internal_offset, internal_value)) 139 { 140 this.Log(LogLevel.Noisy, "Unhandled write at offset 0x{0:X} ({1}), value 0x{2:X}.", internal_offset, (Registers)internal_offset, internal_value); 141 return; 142 } 143 }); 144 } 145 BuildRegistersCollection()146 private DoubleWordRegisterCollection BuildRegistersCollection() 147 { 148 var registerDictionary = new Dictionary<long, DoubleWordRegister> 149 { 150 {(long)Registers.Control, new DoubleWordRegister(this, 0x00000002) 151 .WithFlag(0, out forceEnable, writeCallback: (oldValue, newValue) => 152 { 153 if (!oldValue && newValue) 154 { 155 enabled.Value = true; 156 wakeUpSource = WakeUpSource.Force; 157 StartDelayTimer(); 158 } 159 else if (!newValue && disableOnDemand.Value) 160 { 161 enabled.Value = false; 162 } 163 }, name: "FORCEEN") 164 .WithFlag(1, out disableOnDemand, writeCallback: (_, value) => 165 { 166 if (value && !forceEnable.Value) 167 { 168 enabled.Value = false; 169 } 170 if (!value) 171 { 172 fsmLock.Value = true; 173 } 174 }, name: "DISONDEMAND") 175 .WithTaggedFlag("KEEPWARM", 2) 176 .WithReservedBits(3, 1) 177 .WithTaggedFlag("FORCEXI2GNDANA", 4) 178 .WithTaggedFlag("FORCEXO2GNDANA", 5) 179 .WithReservedBits(6, 26) 180 }, 181 {(long)Registers.Command, new DoubleWordRegister(this) 182 .WithFlag(0, out ready, FieldMode.Write, writeCallback: (_, value) => 183 { 184 coreBiasReady.Value = true; 185 }, name: "COREBIASOPT") 186 .WithFlag(1, out ready, FieldMode.Write, writeCallback: (_, value) => 187 { 188 if (coreBiasReady.Value) 189 { 190 if (forceEnable.Value && disableOnDemand.Value) 191 { 192 fsmLock.Value = false; 193 } 194 } 195 }, name: "MANUALOVERRIDE") 196 .WithReservedBits(2, 30) 197 }, 198 {(long)Registers.Status, new DoubleWordRegister(this) 199 .WithFlag(0, out ready, FieldMode.Read, name: "RDY") 200 .WithFlag(1, out coreBiasReady, FieldMode.Read, name: "COREBIASOPTRDY") 201 .WithReservedBits(2, 14) 202 .WithFlag(16, out enabled, FieldMode.Read, name: "ENS") 203 .WithTaggedFlag("HWREQ", 17) 204 .WithReservedBits(18, 1) 205 .WithTaggedFlag("ISWARM", 19) 206 .WithReservedBits(20, 10) 207 .WithFlag(30, out fsmLock, FieldMode.Read, name: "FSMLOCK") 208 .WithFlag(31, out locked, FieldMode.Read, name: "LOCK") 209 }, 210 {(long)Registers.InterruptFlags, new DoubleWordRegister(this) 211 .WithFlag(0, out readyInterrupt, name: "RDY") 212 .WithTaggedFlag("COREBIASOPTRDY", 1) 213 .WithReservedBits(2, 27) 214 .WithTaggedFlag("DNSERR", 29) 215 .WithReservedBits(30, 1) 216 .WithTaggedFlag("COREBIASOPTERR", 31) 217 .WithChangeCallback((_, __) => UpdateInterrupts()) 218 }, 219 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 220 .WithFlag(0, out readyInterruptEnable, name: "RDY") 221 .WithTaggedFlag("COREBIASOPTRDY", 1) 222 .WithReservedBits(2, 27) 223 .WithTaggedFlag("DNSERR", 29) 224 .WithReservedBits(30, 1) 225 .WithTaggedFlag("COREBIASOPTERR", 31) 226 .WithChangeCallback((_, __) => UpdateInterrupts()) 227 }, 228 {(long)Registers.Lock, new DoubleWordRegister(this) 229 .WithValueField(0, 16, writeCallback: (_, value) => 230 { 231 locked.Value = (value != UnlockCode); 232 }, name: "LOCKKEY") 233 }, 234 }; 235 return new DoubleWordRegisterCollection(this, registerDictionary); 236 } 237 238 #region methods GetTime()239 private TimeInterval GetTime() => machine.LocalTimeSource.ElapsedVirtualTime; StartDelayTimer()240 private void StartDelayTimer() 241 { 242 // Function which starts the start-up delay timer 243 timer.Enabled = false; 244 timer.Limit = delayTicks; 245 timer.Enabled = true; 246 } 247 OnRequest(HFXO_REQUESTER req)248 public void OnRequest(HFXO_REQUESTER req) 249 { 250 this.Log(LogLevel.Error, "OnRequest not implemented"); 251 } 252 OnEm2Wakeup()253 public void OnEm2Wakeup() 254 { 255 HfxoEnabled?.Invoke(); 256 this.Log(LogLevel.Error, "OnEm2Wakeup not implemented"); 257 } 258 OnClksel()259 public void OnClksel() 260 { 261 this.Log(LogLevel.Error, "OnClksel not implemented"); 262 } 263 OnStartUpTimerExpired()264 private void OnStartUpTimerExpired() 265 { 266 this.Log(LogLevel.Debug, "Start-up delay timer expired at: {0}", machine.ElapsedVirtualTime); 267 this.Log(LogLevel.Debug, "Wakeup Requester = {0}", wakeUpSource); 268 269 if (wakeUpSource == WakeUpSource.Force) 270 { 271 ready.Value = true; 272 coreBiasReady.Value = true; 273 readyInterrupt.Value = true; 274 wakeUpSource = WakeUpSource.None; 275 } 276 else 277 { 278 this.Log(LogLevel.Error, "Wake up source {0} not implemented", wakeUpSource); 279 } 280 281 timer.Enabled = false; 282 UpdateInterrupts(); 283 } 284 UpdateInterrupts()285 private void UpdateInterrupts() 286 { 287 machine.ClockSource.ExecuteInLock(delegate { 288 var irq = (readyInterruptEnable.Value && readyInterrupt.Value); 289 IRQ.Set(irq); 290 }); 291 } 292 #endregion 293 private readonly Machine machine; 294 public GPIO IRQ { get; } 295 private readonly DoubleWordRegisterCollection registersCollection; 296 public event Action HfxoEnabled; 297 private WakeUpSource wakeUpSource = WakeUpSource.None; 298 private LimitTimer timer; 299 private uint delayTicks; 300 private const uint UnlockCode = 0x580E; 301 private const uint SetRegisterOffset = 0x1000; 302 private const uint ClearRegisterOffset = 0x2000; 303 private const uint ToggleRegisterOffset = 0x3000; 304 #region register fields 305 private IFlagRegisterField ready; 306 private IFlagRegisterField coreBiasReady; 307 private IFlagRegisterField enabled; 308 private IFlagRegisterField locked; 309 private IFlagRegisterField fsmLock; 310 private IFlagRegisterField forceEnable; 311 private IFlagRegisterField disableOnDemand; 312 // Interrupts 313 private IFlagRegisterField readyInterrupt; 314 private IFlagRegisterField readyInterruptEnable; 315 #endregion 316 317 #region enums 318 private enum Registers 319 { 320 IpVersion = 0x0000, 321 CrystalConfig = 0x0010, 322 CrystalControl = 0x0018, 323 Config = 0x0020, 324 Control = 0x0028, 325 Command = 0x0050, 326 Status = 0x0058, 327 InterruptFlags = 0x0070, 328 InterruptEnable = 0x0074, 329 Lock = 0x0080, 330 // Set registers 331 IpVersion_Set = 0x1000, 332 CrystalConfig_Set = 0x1010, 333 CrystalControl_Set = 0x1018, 334 Config_Set = 0x1020, 335 Control_Set = 0x1028, 336 Command_Set = 0x1050, 337 Status_Set = 0x1058, 338 InterruptFlags_Set = 0x1070, 339 InterruptEnable_Set = 0x1074, 340 Lock_Set = 0x1080, 341 // Clear registers 342 IpVersion_Clr = 0x2000, 343 CrystalConfig_Clr = 0x2010, 344 CrystalControl_Clr = 0x2018, 345 Config_Clr = 0x2020, 346 Control_Clr = 0x2028, 347 Command_Clr = 0x2050, 348 Status_Clr = 0x2058, 349 InterruptFlags_Clr = 0x2070, 350 InterruptEnable_Clr = 0x2074, 351 Lock_Clr = 0x2080, 352 // Toggle registers 353 IpVersion_Tgl = 0x3000, 354 CrystalConfig_Tgl = 0x3010, 355 CrystalControl_Tgl = 0x3018, 356 Config_Tgl = 0x3020, 357 Control_Tgl = 0x3028, 358 Command_Tgl = 0x3050, 359 Status_Tgl = 0x3058, 360 InterruptFlags_Tgl = 0x3070, 361 InterruptEnable_Tgl = 0x3074, 362 Lock_Tgl = 0x3080, 363 } 364 365 private enum WakeUpSource 366 { 367 None = 0, 368 Prs = 1, 369 Force = 2, 370 } 371 #endregion 372 } 373 } 374