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.Collections.Generic; 11 using System.IO; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Core.Structure.Registers; 14 using Antmicro.Renode.Exceptions; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Renode.Peripherals.Bus; 17 using Antmicro.Renode.Peripherals.Timers; 18 using Antmicro.Renode.Time; 19 using Antmicro.Renode.Peripherals.CPU; 20 21 namespace Antmicro.Renode.Peripherals.Miscellaneous.SiLabs 22 { 23 // Allows for the viewing of register contents when debugging 24 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 25 public class EFR32xG2_SYSRTC_1 : IDoubleWordPeripheral, IKnownSize 26 { EFR32xG2_SYSRTC_1(Machine machine, uint frequency)27 public EFR32xG2_SYSRTC_1(Machine machine, uint frequency) 28 { 29 this.machine = machine; 30 this.timerFrequency = frequency; 31 32 timer = new LimitTimer(machine.ClockSource, timerFrequency, this, "sysrtctimer", 0xFFFFFFFFUL, direction: Direction.Ascending, 33 enabled: false, workMode: WorkMode.OneShot, eventEnabled: true, autoUpdate: true); 34 timer.LimitReached += TimerLimitReached; 35 36 IRQ = new GPIO(); 37 registersCollection = BuildRegistersCollection(); 38 } 39 Reset()40 public void Reset() 41 { 42 timerIsRunning = false; 43 timer.Enabled = false; 44 } 45 ReadDoubleWord(long offset)46 public uint ReadDoubleWord(long offset) 47 { 48 return ReadRegister(offset); 49 } 50 ReadByte(long offset)51 public byte ReadByte(long offset) 52 { 53 int byteOffset = (int)(offset & 0x3); 54 uint registerValue = ReadRegister(offset, true); 55 byte result = (byte)((registerValue >> byteOffset*8) & 0xFF); 56 return result; 57 } 58 ReadRegister(long offset, bool internal_read = false)59 private uint ReadRegister(long offset, bool internal_read = false) 60 { 61 var result = 0U; 62 long internal_offset = offset; 63 64 // Set, Clear, Toggle registers should only be used for write operations. But just in case we convert here as well. 65 if (offset >= SetRegisterOffset && offset < ClearRegisterOffset) 66 { 67 // Set register 68 internal_offset = offset - SetRegisterOffset; 69 if(!internal_read) 70 { 71 this.Log(LogLevel.Noisy, "SET Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset); 72 } 73 } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset) 74 { 75 // Clear register 76 internal_offset = offset - ClearRegisterOffset; 77 if(!internal_read) 78 { 79 this.Log(LogLevel.Noisy, "CLEAR Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset); 80 } 81 } else if (offset >= ToggleRegisterOffset) 82 { 83 // Toggle register 84 internal_offset = offset - ToggleRegisterOffset; 85 if(!internal_read) 86 { 87 this.Log(LogLevel.Noisy, "TOGGLE Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset); 88 } 89 } 90 91 if(!registersCollection.TryRead(internal_offset, out result)) 92 { 93 if(!internal_read) 94 { 95 this.Log(LogLevel.Noisy, "Unhandled read at offset 0x{0:X} ({1}).", internal_offset, (Registers)internal_offset); 96 } 97 } 98 else 99 { 100 if(!internal_read) 101 { 102 this.Log(LogLevel.Noisy, "Read at offset 0x{0:X} ({1}), returned 0x{2:X}.", internal_offset, (Registers)internal_offset, result); 103 } 104 } 105 106 return result; 107 } 108 WriteDoubleWord(long offset, uint value)109 public void WriteDoubleWord(long offset, uint value) 110 { 111 WriteRegister(offset, value); 112 } 113 WriteRegister(long offset, uint value, bool internal_write = false)114 private void WriteRegister(long offset, uint value, bool internal_write = false) 115 { 116 machine.ClockSource.ExecuteInLock(delegate { 117 long internal_offset = offset; 118 uint internal_value = value; 119 120 if (offset >= SetRegisterOffset && offset < ClearRegisterOffset) 121 { 122 // Set register 123 internal_offset = offset - SetRegisterOffset; 124 uint old_value = ReadRegister(internal_offset, true); 125 internal_value = old_value | value; 126 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); 127 } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset) 128 { 129 // Clear register 130 internal_offset = offset - ClearRegisterOffset; 131 uint old_value = ReadRegister(internal_offset, true); 132 internal_value = old_value & ~value; 133 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); 134 } else if (offset >= ToggleRegisterOffset) 135 { 136 // Toggle register 137 internal_offset = offset - ToggleRegisterOffset; 138 uint old_value = ReadRegister(internal_offset, true); 139 internal_value = old_value ^ value; 140 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); 141 } 142 143 this.Log(LogLevel.Noisy, "Write at offset 0x{0:X} ({1}), value 0x{2:X}.", internal_offset, (Registers)internal_offset, internal_value); 144 145 if(!registersCollection.TryWrite(internal_offset, internal_value)) 146 { 147 this.Log(LogLevel.Noisy, "Unhandled write at offset 0x{0:X} ({1}), value 0x{2:X}.", internal_offset, (Registers)internal_offset, internal_value); 148 return; 149 } 150 }); 151 } 152 BuildRegistersCollection()153 private DoubleWordRegisterCollection BuildRegistersCollection() 154 { 155 var registerDictionary = new Dictionary<long, DoubleWordRegister> 156 { 157 {(long)Registers.Enable, new DoubleWordRegister(this) 158 .WithFlag(0, out enable, name: "EN") 159 .WithTaggedFlag("DISABLING", 1) 160 .WithReservedBits(2, 30) 161 }, 162 {(long)Registers.SoftwareReset, new DoubleWordRegister(this) 163 .WithFlag(0, out swrst_swrst_bit, FieldMode.Write, 164 writeCallback: (_, __) => Swrst_Swrst_Write(_, __), 165 name: "Swrst") 166 .WithFlag(1, out swrst_resetting_bit, FieldMode.Read, 167 valueProviderCallback: (_) => { 168 return swrst_resetting_bit.Value; 169 }, 170 name: "Resetting") 171 .WithReservedBits(2, 30) 172 }, 173 {(long)Registers.Config, new DoubleWordRegister(this) 174 .WithEnumField<DoubleWordRegister, CFG_DEBUGRUN>(0, 1, out cfg_debugrun_bit, 175 valueProviderCallback: (_) => { 176 return cfg_debugrun_bit.Value; 177 }, 178 writeCallback: (_, __) => { 179 WriteWSTATIC(); 180 }, 181 name: "Debugrun") 182 .WithReservedBits(1, 31) 183 }, 184 {(long)Registers.Status, new DoubleWordRegister(this) 185 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => timerIsRunning, name: "RUNNING") 186 .WithEnumField<DoubleWordRegister, STATUS_LOCKSTATUS>(1, 1, out status_lockstatus_bit, FieldMode.Read, 187 valueProviderCallback: (_) => { 188 return status_lockstatus_bit.Value; 189 }, 190 name: "Lockstatus") 191 .WithTaggedFlag("FAILDETLOCKSTATUS", 2) 192 .WithReservedBits(3, 29) 193 }, 194 {(long)Registers.Lock, new DoubleWordRegister(this) 195 .WithValueField(0, 16, out lock_lockkey_field, FieldMode.Write, 196 writeCallback: (_, __) => Lock_Lockkey_Write(_, __), 197 name: "Lockkey") 198 .WithReservedBits(16, 16) 199 }, 200 {(long)Registers.Command, new DoubleWordRegister(this) 201 .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { if (value) { StartCommand(); } }, name: "START") 202 .WithFlag(1, FieldMode.Write, writeCallback: (_, value) => { if (value) { StopCommand(); } }, name: "STOP") 203 .WithReservedBits(2, 30) 204 }, 205 {(long)Registers.Counter, new DoubleWordRegister(this) 206 .WithValueField(0, 32, valueProviderCallback: _ => TimerCounter, writeCallback: (_, value) => TimerCounter = (uint)value, name: "CNT") 207 }, 208 {(long)Registers.Group0InterruptFlags, new DoubleWordRegister(this) 209 .WithFlag(0, out overflowInterrupt, name: "OVFIF") 210 .WithFlag(1, out compare0Interrupt, name: "COMP0IF") 211 .WithFlag(2, out compare1Interrupt, name: "COMP1IF") 212 .WithFlag(3, out capture0Interrupt, name: "CAP0IF") 213 .WithReservedBits(4, 28) 214 .WithChangeCallback((_, __) => UpdateInterrupts()) 215 }, 216 {(long)Registers.Group0InterruptEnable, new DoubleWordRegister(this) 217 .WithFlag(0, out overflowInterruptEnable, name: "OVFIEN") 218 .WithFlag(1, out compare0InterruptEnable, name: "COMP0IEN") 219 .WithFlag(2, out compare1InterruptEnable, name: "COMP1IEN") 220 .WithFlag(3, out capture0InterruptEnable, name: "CAP0IEN") 221 .WithReservedBits(4, 28) 222 .WithChangeCallback((_, __) => UpdateInterrupts()) 223 }, 224 {(long)Registers.Group0Control, new DoubleWordRegister(this) 225 .WithFlag(0, out compare0Enable, name: "CMP0EN") 226 .WithFlag(1, out compare1Enable, name: "CMP1EN") 227 .WithFlag(2, out capture0Enable, name: "CAP0EN") 228 .WithEnumField<DoubleWordRegister, CTRL_GROUP0_CMP0CMOA>(3, 3, out ctrl_group0_cmp0cmoa_field, 229 valueProviderCallback: (_) => { 230 return ctrl_group0_cmp0cmoa_field.Value; 231 }, 232 name: "Cmp0cmoa") 233 .WithEnumField<DoubleWordRegister, CTRL_GROUP0_CMP1CMOA>(6, 3, out ctrl_group0_cmp1cmoa_field, 234 valueProviderCallback: (_) => { 235 return ctrl_group0_cmp1cmoa_field.Value; 236 }, 237 name: "Cmp1cmoa") 238 .WithEnumField<DoubleWordRegister, CTRL_GROUP0_CAP0EDGE>(9, 2, out ctrl_group0_cap0edge_field, 239 valueProviderCallback: (_) => { 240 return ctrl_group0_cap0edge_field.Value; 241 }, 242 name: "Cap0edge") 243 .WithReservedBits(11, 21) 244 .WithChangeCallback((_, __) => RestartTimer()) 245 }, 246 {(long)Registers.Group0Compare0, new DoubleWordRegister(this) 247 .WithValueField(0, 32, out compare0Value, name: "CMP0VALUE") 248 .WithChangeCallback((_, __) => RestartTimer()) 249 }, 250 {(long)Registers.Group0Compare1, new DoubleWordRegister(this) 251 .WithValueField(0, 32, out compare1Value, name: "CMP1VALUE") 252 .WithChangeCallback((_, __) => RestartTimer()) 253 }, 254 {(long)Registers.Group0Capture0, new DoubleWordRegister(this) 255 .WithValueField(0, 32, out capture0Value, name: "CAP0VALUE") 256 .WithChangeCallback((_, __) => RestartTimer()) 257 }, 258 }; 259 return new DoubleWordRegisterCollection(this, registerDictionary); 260 } 261 262 public long Size => 0x4000; 263 public GPIO IRQ { get; } 264 private readonly Machine machine; 265 private readonly DoubleWordRegisterCollection registersCollection; 266 private LimitTimer timer; 267 private uint timerFrequency; 268 public bool timerIsRunning = false; 269 private const uint SetRegisterOffset = 0x1000; 270 private const uint ClearRegisterOffset = 0x2000; 271 private const uint ToggleRegisterOffset = 0x3000; 272 public event Action CompareMatchGroup0Channel0; 273 public event Action CompareMatchGroup0Channel1; 274 public bool Group0_Capture_Enabled 275 { 276 get 277 { 278 return capture0Enable.Value; 279 } 280 } 281 282 #region register fields 283 private IFlagRegisterField enable; 284 private IFlagRegisterField swrst_swrst_bit; 285 private IFlagRegisterField swrst_resetting_bit; 286 private IEnumRegisterField<CFG_DEBUGRUN> cfg_debugrun_bit; 287 private IValueRegisterField lock_lockkey_field; 288 private IEnumRegisterField<STATUS_LOCKSTATUS> status_lockstatus_bit; 289 private IFlagRegisterField compare0Enable; 290 private IEnumRegisterField<CTRL_GROUP0_CMP0CMOA> ctrl_group0_cmp0cmoa_field; 291 private IFlagRegisterField compare1Enable; 292 private IEnumRegisterField<CTRL_GROUP0_CMP1CMOA> ctrl_group0_cmp1cmoa_field; 293 private IFlagRegisterField capture0Enable; 294 protected IEnumRegisterField<CTRL_GROUP0_CAP0EDGE> ctrl_group0_cap0edge_field; 295 private IValueRegisterField compare0Value; 296 private IValueRegisterField compare1Value; 297 private IValueRegisterField capture0Value; 298 // Interrupts 299 private IFlagRegisterField overflowInterrupt; 300 private IFlagRegisterField compare0Interrupt; 301 private IFlagRegisterField compare1Interrupt; 302 private IFlagRegisterField capture0Interrupt; 303 private IFlagRegisterField overflowInterruptEnable; 304 private IFlagRegisterField compare0InterruptEnable; 305 private IFlagRegisterField compare1InterruptEnable; 306 private IFlagRegisterField capture0InterruptEnable; 307 #endregion 308 309 #region methods GetTime()310 private TimeInterval GetTime() => machine.LocalTimeSource.ElapsedVirtualTime; 311 public uint TimerCounter 312 { 313 get 314 { 315 if (timerIsRunning) 316 { 317 if (timer.Enabled) 318 { 319 TrySyncTime(); 320 return (uint)timer.Value; 321 } 322 else 323 { 324 return (uint)timer.Limit; 325 } 326 } 327 return 0; 328 } 329 330 set 331 { 332 timer.Value = value; 333 RestartTimer(); 334 } 335 } 336 StartCommand()337 private void StartCommand() 338 { 339 timerIsRunning = true; 340 RestartTimer(true); 341 } 342 StopCommand()343 private void StopCommand() 344 { 345 timerIsRunning = false; 346 timer.Enabled = false; 347 } 348 TimerLimitReached()349 private void TimerLimitReached() 350 { 351 bool restartFromZero = false; 352 353 if (timer.Limit == 0xFFFFFFFF) 354 { 355 overflowInterrupt.Value = true; 356 restartFromZero = true; 357 } 358 if (timer.Limit == compare0Value.Value + 1) 359 { 360 compare0Interrupt.Value = true; 361 //Invoke event (simulated hardware signal) 362 CompareMatchGroup0Channel0?.Invoke(); 363 364 } 365 if (timer.Limit == compare1Value.Value + 1) 366 { 367 compare1Interrupt.Value = true; 368 // Invoke event (simulated hardware signal) 369 CompareMatchGroup0Channel1?.Invoke(); 370 } 371 372 // RENODE-65: add support for capture functionality 373 UpdateInterrupts(); 374 RestartTimer(restartFromZero); 375 } 376 RestartTimer(bool restartFromZero = false)377 private void RestartTimer(bool restartFromZero = false) 378 { 379 if (!timerIsRunning) 380 { 381 return; 382 } 383 384 uint currentValue = restartFromZero ? 0 : TimerCounter; 385 386 timer.Enabled = false; 387 uint limit = 0xFFFFFFFF; 388 389 // Compare interrupt fires "on the next cycle", therefore we just set 390 // the timer to the +1 value and fire the interrupt right away when 391 // we hit the limit. 392 393 if (compare0Enable.Value 394 && currentValue < (compare0Value.Value + 1) 395 && (compare0Value.Value + 1) < limit) 396 { 397 limit = (uint)compare0Value.Value + 1; 398 } 399 400 if (compare1Enable.Value 401 && currentValue < (compare1Value.Value + 1) 402 && (compare1Value.Value + 1) < limit) 403 { 404 limit = (uint)compare1Value.Value + 1; 405 } 406 407 // RENODE-65: add support for capture functionality 408 409 timer.Limit = limit; 410 timer.Enabled = true; 411 timer.Value = currentValue; 412 } 413 UpdateInterrupts()414 private void UpdateInterrupts() 415 { 416 machine.ClockSource.ExecuteInLock(delegate { 417 var irq = ((overflowInterruptEnable.Value && overflowInterrupt.Value) 418 || (compare0InterruptEnable.Value && compare0Interrupt.Value) 419 || (compare1InterruptEnable.Value && compare1Interrupt.Value) 420 || (capture0InterruptEnable.Value && capture0Interrupt.Value)); 421 IRQ.Set(irq); 422 }); 423 } 424 TrySyncTime()425 private bool TrySyncTime() 426 { 427 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 428 { 429 cpu.SyncTime(); 430 return true; 431 } 432 return false; 433 } 434 WriteWSTATIC()435 private void WriteWSTATIC() 436 { 437 if(enable.Value) 438 { 439 this.Log(LogLevel.Error, "Trying to write to a WSTATIC register while peripheral is enabled EN = {0}", enable); 440 } 441 } 442 Swrst_Swrst_Write(bool a, bool b)443 protected void Swrst_Swrst_Write(bool a, bool b) 444 { 445 if (b == true) 446 { 447 swrst_resetting_bit.Value = true; 448 Reset(); 449 ResetRegisters(); 450 swrst_resetting_bit.Value = false; 451 } 452 } 453 ResetRegisters()454 protected void ResetRegisters() 455 { 456 foreach (Registers reg in System.Enum.GetValues(typeof(Registers))) 457 { 458 if (reg != (Registers)0x2008 && (uint)reg >= ClearRegisterOffset && (uint)reg < ToggleRegisterOffset) 459 { 460 WriteDoubleWord((long)reg, 0xFFFFFFFF); 461 } 462 Lock_Lockkey_Write(0, 0x4776); 463 } 464 } 465 Lock_Lockkey_Write(ulong a, ulong b)466 protected void Lock_Lockkey_Write(ulong a, ulong b) 467 { 468 if (b == 0x4776) 469 { 470 status_lockstatus_bit.Value = STATUS_LOCKSTATUS.UNLOCKED; 471 } 472 else 473 { 474 status_lockstatus_bit.Value = STATUS_LOCKSTATUS.LOCKED; 475 } 476 } 477 CaptureGroup0()478 public void CaptureGroup0() 479 { 480 this.Log(LogLevel.Debug, "Capturing SYSRTC Group 0"); 481 WriteDoubleWord((long)Registers.Group0Capture0, TimerCounter); 482 capture0Interrupt.Value = true; 483 } 484 485 #endregion 486 487 #region enums 488 private enum Registers 489 { 490 IpVersion = 0x0000, 491 Enable = 0x0004, 492 SoftwareReset = 0x0008, 493 Config = 0x000C, 494 Command = 0x0010, 495 Status = 0x0014, 496 Counter = 0x0018, 497 SyncBusy = 0x001C, 498 Lock = 0x0020, 499 FailureDetection = 0x0030, 500 FailureDetectionLock = 0x0034, 501 Group0InterruptFlags = 0x0040, 502 Group0InterruptEnable = 0x0044, 503 Group0Control = 0x0048, 504 Group0Compare0 = 0x004C, 505 Group0Compare1 = 0x0050, 506 Group0Capture0 = 0x0054, 507 Group0SyncBusy = 0x0058, 508 // Set registers 509 IpVersion_Set = 0x1000, 510 Enable_Set = 0x1004, 511 SoftwareReset_Set = 0x1008, 512 Config_Set = 0x100C, 513 Command_Set = 0x1010, 514 Status_Set = 0x1014, 515 Counter_Set = 0x1018, 516 SyncBusy_Set = 0x101C, 517 Lock_Set = 0x1020, 518 FailureDetection_Set = 0x1030, 519 FailureDetectionLock_Set = 0x1034, 520 Group0InterruptFlags_Set = 0x1040, 521 Group0InterruptEnable_Set = 0x1044, 522 Group0Control_Set = 0x1048, 523 Group0Compare0_Set = 0x104C, 524 Group0Compare1_Set = 0x1050, 525 Group0Capture0_Set = 0x1054, 526 Group0SyncBusy_Set = 0x1058, 527 // Clear registers 528 IpVersion_Clr = 0x2000, 529 Enable_Clr = 0x2004, 530 SoftwareReset_Clr = 0x2008, 531 Config_Clr = 0x200C, 532 Command_Clr = 0x2010, 533 Status_Clr = 0x2014, 534 Counter_Clr = 0x2018, 535 SyncBusy_Clr = 0x201C, 536 Lock_Clr = 0x2020, 537 FailureDetection_Clr = 0x2030, 538 FailureDetectionLock_Clr = 0x2034, 539 Group0InterruptFlags_Clr = 0x2040, 540 Group0InterruptEnable_Clr = 0x2044, 541 Group0Control_Clr = 0x2048, 542 Group0Compare0_Clr = 0x204C, 543 Group0Compare1_Clr = 0x2050, 544 Group0Capture0_Clr = 0x2054, 545 Group0SyncBusy_Clr = 0x2058, 546 // Toggle registers 547 IpVersion_Tgl = 0x3000, 548 Enable_Tgl = 0x3004, 549 SoftwareReset_Tgl = 0x3008, 550 Config_Tgl = 0x300C, 551 Command_Tgl = 0x3010, 552 Status_Tgl = 0x3014, 553 Counter_Tgl = 0x3018, 554 SyncBusy_Tgl = 0x301C, 555 Lock_Tgl = 0x3020, 556 FailureDetection_Tgl = 0x3030, 557 FailureDetectionLock_Tgl = 0x3034, 558 Group0InterruptFlags_Tgl = 0x3040, 559 Group0InterruptEnable_Tgl = 0x3044, 560 Group0Control_Tgl = 0x3048, 561 Group0Compare0_Tgl = 0x304C, 562 Group0Compare1_Tgl = 0x3050, 563 Group0Capture0_Tgl = 0x3054, 564 Group0SyncBusy_Tgl = 0x3058, 565 } 566 protected enum CFG_DEBUGRUN 567 { 568 DISABLE = 0, // SYSRTC is frozen in debug mode 569 ENABLE = 1, // SYSRTC is running in debug mode 570 } 571 protected enum STATUS_LOCKSTATUS 572 { 573 UNLOCKED = 0, // SYSRTC registers are unlocked 574 LOCKED = 1, // SYSRTC registers are locked 575 } 576 protected enum CTRL_GROUP0_CMP0CMOA 577 { 578 CLEAR = 0, // Cleared on the next cycle 579 SET = 1, // Set on the next cycle 580 PULSE = 2, // Set on the next cycle, cleared on the cycle after 581 TOGGLE = 3, // Inverted on the next cycle 582 CMPIF = 4, // Export this channel's CMP IF 583 } 584 protected enum CTRL_GROUP0_CMP1CMOA 585 { 586 CLEAR = 0, // Cleared on the next cycle 587 SET = 1, // Set on the next cycle 588 PULSE = 2, // Set on the next cycle, cleared on the cycle after 589 TOGGLE = 3, // Inverted on the next cycle 590 CMPIF = 4, // Export this channel's CMP IF 591 } 592 protected enum CTRL_GROUP0_CAP0EDGE 593 { 594 RISING = 0, // Rising edges detected 595 FALLING = 1, // Falling edges detected 596 BOTH = 2, // Both edges detected 597 } 598 #endregion 599 } 600 }