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 System.Collections.Generic; 9 using System.Collections.ObjectModel; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals; 14 using Antmicro.Renode.Time; 15 using Antmicro.Renode.Utilities; 16 17 namespace Antmicro.Renode.Peripherals.Timers 18 { 19 public class SAM_TC : BasicDoubleWordPeripheral, INumberedGPIOOutput, IKnownSize 20 { SAM_TC(IMachine machine, long masterClockFrequency = 20000000)21 public SAM_TC(IMachine machine, long masterClockFrequency = 20000000) : base(machine) 22 { 23 var connections = new Dictionary<int, IGPIO>(); 24 channels = new Channel[NumberOfChannels]; 25 for(int i = 0; i < NumberOfChannels; i++) 26 { 27 channels[i] = new Channel(machine.ClockSource, masterClockFrequency, this, i); 28 connections[i] = channels[i].IRQ; 29 } 30 Connections = new ReadOnlyDictionary<int, IGPIO>(connections); 31 32 DefineRegisters(); 33 } 34 Reset()35 public override void Reset() 36 { 37 base.Reset(); 38 for(int i = 0; i < NumberOfChannels; i++) 39 { 40 channels[i].Reset(); 41 } 42 } 43 WriteDoubleWord(long offset, uint value)44 public override void WriteDoubleWord(long offset, uint value) 45 { 46 // channel mode changes with write to channel mode register so we need to set condition before write 47 var channel = offset / ChannelSize; 48 if(channel < NumberOfChannels && (offset % ChannelSize) == (long)Registers.ChannelMode0) 49 { 50 channels[channel].WaveformMode = BitHelper.IsBitSet(value, 15); 51 } 52 53 base.WriteDoubleWord(offset, value); 54 } 55 56 public IReadOnlyDictionary<int, IGPIO> Connections { get; } 57 58 public long Size => 0x100; 59 DefineRegisters()60 private void DefineRegisters() 61 { 62 Registers.ChannelControl0.DefineMany(this, NumberOfChannels, stepInBytes: ChannelSize, setup: (reg, id) => reg 63 .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { if(value) channels[id].Enable(); }, name: "CLKEN") 64 .WithFlag(1, FieldMode.Write, writeCallback: (_, value) => { if(value) channels[id].Disable(); }, name: "CLKDIS") 65 .WithFlag(2, FieldMode.Write, writeCallback: (_, value) => { if(value) channels[id].SoftwareTrigger(); }, name: "SWTRG") 66 .WithReservedBits(3, 29) 67 ); 68 69 Registers.ChannelMode0.DefineManyConditional(this, NumberOfChannels, id => !channels[id].WaveformMode, stepInBytes: ChannelSize, setup: (reg, id) => reg 70 .WithEnumField<DoubleWordRegister, ClockSelection>(0, 3, writeCallback: (_, value) => channels[id].ClockSelected = value, valueProviderCallback: _ => channels[id].ClockSelected, name: "TCCLKS") 71 .WithTaggedFlag("CLKI", 3) 72 .WithTag("BURST", 4, 2) 73 .WithTaggedFlag("LDBSTOP", 6) 74 .WithTaggedFlag("LDBDIS", 7) 75 .WithTag("ETRGEDG", 8, 2) 76 .WithTaggedFlag("ABETRG", 10) 77 .WithReservedBits(11, 3) 78 .WithTaggedFlag("CPCTRG", 14) 79 .WithFlag(15, valueProviderCallback: _ => channels[id].WaveformMode, name: "WAVE") 80 .WithTag("LDRA", 16, 2) 81 .WithTag("LDRB", 18, 2) 82 .WithReservedBits(20, 12) 83 ); 84 85 Registers.ChannelMode0.DefineManyConditional(this, NumberOfChannels, id => channels[id].WaveformMode, stepInBytes: ChannelSize, setup: (reg, id) => reg 86 .WithEnumField<DoubleWordRegister, ClockSelection>(0, 3, writeCallback: (_, value) => channels[id].ClockSelected = value, valueProviderCallback: _ => channels[id].ClockSelected, name: "TCCLKS") 87 .WithTaggedFlag("CLKI", 3) 88 .WithTag("BURST", 4, 2) 89 .WithFlag(6, writeCallback: (_, value) => channels[id].StopOnC = value, valueProviderCallback: _ => channels[id].StopOnC, name: "CPCSTOP") 90 .WithTaggedFlag("CPCDIS", 7) 91 .WithTag("EEVTEDG", 8, 2) 92 .WithTag("EEVT", 10, 2) 93 .WithTaggedFlag("ENETRG", 12) 94 .WithEnumField<DoubleWordRegister, WaveSelection>(13, 2, writeCallback: (_, value) => channels[id].WaveformSelected = value, valueProviderCallback: _ => channels[id].WaveformSelected, name: "WAVSEL") 95 .WithFlag(15, valueProviderCallback: _ => channels[id].WaveformMode, name: "WAVE") 96 .WithTag("ACPA", 16, 2) 97 .WithTag("ACPC", 18, 2) 98 .WithTag("AEEVT", 20, 2) 99 .WithTag("ASWTRG", 22, 2) 100 .WithTag("BCPB", 24, 2) 101 .WithTag("BCPC", 26, 2) 102 .WithTag("BEEVT", 28, 2) 103 .WithTag("BSWTRG", 30, 2) 104 ); 105 106 Registers.StepperMotorMode0.DefineMany(this, NumberOfChannels, stepInBytes: ChannelSize, setup: (reg, id) => reg 107 .WithTaggedFlag("GCEN", 0) 108 .WithTaggedFlag("DOWN", 1) 109 .WithReservedBits(2, 30) 110 ); 111 112 Registers.CounterValue0.DefineMany(this, NumberOfChannels, stepInBytes: ChannelSize, setup: (reg, id) => reg 113 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => 114 { 115 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 116 { 117 cpu.SyncTime(); 118 } 119 return channels[id].Value; 120 }, name: "CV") 121 ); 122 123 Registers.A0.DefineMany(this, NumberOfChannels, stepInBytes: ChannelSize, setup: (reg, id) => reg 124 .WithValueField(0, 32, writeCallback: (_, value) => channels[id].A = value, valueProviderCallback: _ => channels[id].A, name: "RA") 125 ); 126 127 Registers.B0.DefineMany(this, NumberOfChannels, stepInBytes: ChannelSize, setup: (reg, id) => reg 128 .WithValueField(0, 32, writeCallback: (_, value) => channels[id].B = value, valueProviderCallback: _ => channels[id].B, name: "RB") 129 ); 130 131 Registers.C0.DefineMany(this, NumberOfChannels, stepInBytes: ChannelSize, setup: (reg, id) => reg 132 .WithValueField(0, 32, writeCallback: (_, value) => channels[id].C = value, valueProviderCallback: _ => channels[id].C, name: "RC") 133 ); 134 135 Registers.Status0.DefineMany(this, NumberOfChannels, stepInBytes: ChannelSize, setup: (reg, id) => reg 136 .WithFlag(0, FieldMode.ReadToClear, valueProviderCallback: _ => channels[id].Overflow, name: "COVFS") 137 .WithTaggedFlag("LOVRS", 1) 138 .WithFlag(2, FieldMode.ReadToClear, valueProviderCallback: _ => channels[id].CompareAInterrupt, name: "CPAS") 139 .WithFlag(3, FieldMode.ReadToClear, valueProviderCallback: _ => channels[id].CompareBInterrupt, name: "CPBS") 140 .WithFlag(4, FieldMode.ReadToClear, valueProviderCallback: _ => channels[id].CompareCInterrupt, name: "CPCS") 141 .WithFlag(5, FieldMode.ReadToClear, valueProviderCallback: _ => channels[id].LoadingAInterrupt, name: "LDRAS") 142 .WithFlag(6, FieldMode.ReadToClear, valueProviderCallback: _ => channels[id].LoadingBInterrupt, name: "LDRBS") 143 .WithTaggedFlag("ETRGS", 7) 144 .WithReservedBits(8, 8) 145 .WithFlag(16, FieldMode.Read, valueProviderCallback: _ => channels[id].Enabled, name: "CLKSTA") 146 .WithTaggedFlag("MTIOA", 17) 147 .WithTaggedFlag("MTIOB", 18) 148 .WithReservedBits(19, 13) 149 .WithReadCallback((_, __) => channels[id].UpdateInterrupts()) 150 ); 151 152 Registers.InterruptEnable0.DefineMany(this, NumberOfChannels, stepInBytes: ChannelSize, setup: (reg, id) => reg 153 .WithFlag(0, FieldMode.Set, writeCallback: (_, value) => { if(value) channels[id].OverflowInterruptEnable = true; }, name: "COVFS") 154 .WithTaggedFlag("LOVRS", 1) 155 .WithFlag(2, FieldMode.Set, writeCallback: (_, value) => { if(value) channels[id].CompareAInterruptEnable = true; }, name: "CPAS") 156 .WithFlag(3, FieldMode.Set, writeCallback: (_, value) => { if(value) channels[id].CompareBInterruptEnable = true; }, name: "CPBS") 157 .WithFlag(4, FieldMode.Set, writeCallback: (_, value) => { if(value) channels[id].CompareCInterruptEnable = true; }, name: "CPCS") 158 .WithFlag(5, FieldMode.Set, writeCallback: (_, value) => { if(value) channels[id].LoadingAInterruptEnable = true; }, name: "LDRAS") 159 .WithFlag(6, FieldMode.Set, writeCallback: (_, value) => { if(value) channels[id].LoadingBInterruptEnable = true; }, name: "LDRBS") 160 .WithTaggedFlag("ETRGS", 7) 161 .WithReservedBits(8, 24) 162 .WithWriteCallback((_, __) => channels[id].UpdateInterrupts()) 163 ); 164 165 Registers.InterruptDisable0.DefineMany(this, NumberOfChannels, stepInBytes: ChannelSize, setup: (reg, id) => reg 166 .WithFlag(0, FieldMode.WriteOneToClear, writeCallback: (_, value) => { if(value) channels[id].OverflowInterruptEnable = false; }, name: "COVFS") 167 .WithTaggedFlag("LOVRS", 1) 168 .WithFlag(2, FieldMode.WriteOneToClear, writeCallback: (_, value) => { if(value) channels[id].CompareAInterruptEnable = false; }, name: "CPAS") 169 .WithFlag(3, FieldMode.WriteOneToClear, writeCallback: (_, value) => { if(value) channels[id].CompareBInterruptEnable = false; }, name: "CPBS") 170 .WithFlag(4, FieldMode.WriteOneToClear, writeCallback: (_, value) => { if(value) channels[id].CompareCInterruptEnable = false; }, name: "CPCS") 171 .WithFlag(5, FieldMode.WriteOneToClear, writeCallback: (_, value) => { if(value) channels[id].LoadingAInterruptEnable = false; }, name: "LDRAS") 172 .WithFlag(6, FieldMode.WriteOneToClear, writeCallback: (_, value) => { if(value) channels[id].LoadingBInterruptEnable = false; }, name: "LDRBS") 173 .WithTaggedFlag("ETRGS", 7) 174 .WithReservedBits(8, 24) 175 .WithWriteCallback((_, __) => channels[id].UpdateInterrupts()) 176 ); 177 178 Registers.InterruptMask0.DefineMany(this, NumberOfChannels, stepInBytes: ChannelSize, setup: (reg, id) => reg 179 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => channels[id].OverflowInterruptEnable, name: "COVFS") 180 .WithTaggedFlag("LOVRS", 1) 181 .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => channels[id].CompareAInterruptEnable, name: "CPAS") 182 .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => channels[id].CompareBInterruptEnable, name: "CPBS") 183 .WithFlag(4, FieldMode.Read, valueProviderCallback: _ => channels[id].CompareCInterruptEnable, name: "CPCS") 184 .WithFlag(5, FieldMode.Read, valueProviderCallback: _ => channels[id].LoadingAInterruptEnable, name: "LDRAS") 185 .WithFlag(6, FieldMode.Read, valueProviderCallback: _ => channels[id].LoadingBInterruptEnable, name: "LDRBS") 186 .WithTaggedFlag("ETRGS", 7) 187 .WithReservedBits(8, 24) 188 ); 189 190 Registers.BlockControl.Define(this) 191 .WithFlag(0, FieldMode.Set, writeCallback: (_, value) => 192 { 193 if(!value) 194 { 195 return; 196 } 197 for(int i = 0; i < NumberOfChannels; i++) 198 { 199 channels[i].SoftwareTrigger(); 200 } 201 }, name: "SYNC") 202 .WithReservedBits(1, 31) 203 ; 204 205 Registers.BlockMode.Define(this) 206 .WithTag("TC0XC0S", 0, 2) 207 .WithTag("TC1XC1S", 2, 2) 208 .WithTag("TC2XC2S", 4, 2) 209 .WithReservedBits(6, 2) 210 .WithTaggedFlag("QDEN", 8) 211 .WithTaggedFlag("POSEN", 9) 212 .WithTaggedFlag("SPEEDEN", 10) 213 .WithTaggedFlag("QDTRANS", 11) 214 .WithTaggedFlag("EDGPHA", 12) 215 .WithTaggedFlag("INVA", 13) 216 .WithTaggedFlag("INVB", 14) 217 .WithTaggedFlag("INVIDX", 15) 218 .WithTaggedFlag("SWAP", 16) 219 .WithTaggedFlag("IDXPHB", 17) 220 .WithReservedBits(18, 2) 221 .WithTag("MAXFILT", 20, 6) 222 .WithReservedBits(26, 6) 223 ; 224 225 Registers.QdecInterruptEnable.Define(this) 226 .WithTaggedFlag("IDX", 0) 227 .WithTaggedFlag("DIRCHG", 1) 228 .WithTaggedFlag("QERR", 2) 229 .WithReservedBits(3, 29) 230 ; 231 232 Registers.QdecInterruptDisable.Define(this) 233 .WithTaggedFlag("IDX", 0) 234 .WithTaggedFlag("DIRCHG", 1) 235 .WithTaggedFlag("QERR", 2) 236 .WithReservedBits(3, 29) 237 ; 238 239 Registers.QdecInterruptMask.Define(this) 240 .WithTaggedFlag("IDX", 0) 241 .WithTaggedFlag("DIRCHG", 1) 242 .WithTaggedFlag("QERR", 2) 243 .WithReservedBits(3, 29) 244 ; 245 246 Registers.QdecInterruptStatus.Define(this) 247 .WithTaggedFlag("IDX", 0) 248 .WithTaggedFlag("DIRCHG", 1) 249 .WithTaggedFlag("QERR", 2) 250 .WithReservedBits(3, 5) 251 .WithTaggedFlag("DIR", 8) 252 .WithReservedBits(9, 23) 253 ; 254 255 Registers.FaultMode.Define(this) 256 .WithTaggedFlag("ENCF0", 0) 257 .WithTaggedFlag("ENCF1", 1) 258 .WithReservedBits(2, 30) 259 ; 260 261 Registers.WriteProtectionMode.Define(this) 262 .WithTaggedFlag("WPEN", 0) 263 .WithReservedBits(1, 7) 264 .WithTag("WPKEY", 8, 24) 265 ; 266 } 267 268 private readonly Channel[] channels; 269 private const int NumberOfChannels = 3; 270 private const int ChannelSize = 0x40; 271 272 public enum Registers 273 { 274 ChannelControl0 = 0x00, // TC_CCR WO 0x00 + channel * 0x40 + 0x00 275 ChannelMode0 = 0x04, // TC_CMR RW 0x00 + channel * 0x40 + 0x04 276 StepperMotorMode0 = 0x08, // TC_SMMR RW 0x00 + channel * 0x40 + 0x08 277 // Reserved0 = 0x0C, 278 CounterValue0 = 0x10, // TC_CV RO 0x00 + channel * 0x40 + 0x10 279 A0 = 0x14, // TC_RA RW 0x00 + channel * 0x40 + 0x14 280 B0 = 0x18, // TC_RB RW 0x00 + channel * 0x40 + 0x18 281 C0 = 0x1C, // TC_RC RW 0x00 + channel * 0x40 + 0x1C 282 Status0 = 0x20, // TC_SR RO 0x00 + channel * 0x40 + 0x20 283 InterruptEnable0 = 0x24, // TC_IER WO 0x00 + channel * 0x40 + 0x24 284 InterruptDisable0 = 0x28, // TC_IDR WO 0x00 + channel * 0x40 + 0x28 285 InterruptMask0 = 0x2C, // TC_IMR RO 0x00 + channel * 0x40 + 0x2C 286 287 ChannelControl1 = 0x40, 288 ChannelMode1 = 0x44, 289 StepperMotorMode1 = 0x48, 290 // Reserved1 = 0x4C, 291 CounterValue1 = 0x50, 292 A1 = 0x54, 293 B1 = 0x58, 294 C1 = 0x5C, 295 Status1 = 0x60, 296 InterruptEnable1 = 0x64, 297 InterruptDisable1 = 0x68, 298 InterruptMask1 = 0x6C, 299 300 ChannelControl2 = 0x80, 301 ChannelMode2 = 0x84, 302 StepperMotorMode2 = 0x88, 303 // Reserved2 = 0x8C, 304 CounterValue2 = 0x90, 305 A2 = 0x94, 306 B2 = 0x98, 307 C2 = 0x9C, 308 Status2 = 0xA0, 309 InterruptEnable2 = 0xA4, 310 InterruptDisable2 = 0xA8, 311 InterruptMask2 = 0xAC, 312 313 BlockControl = 0xC0, // TC_BCR WO 314 BlockMode = 0xC4, // TC_BMR RW 315 QdecInterruptEnable = 0xC8, // TC_QIER WO 316 QdecInterruptDisable = 0xCC, // TC_QIDR WO 317 QdecInterruptMask = 0xD0, // TC_QIMR RO 318 QdecInterruptStatus = 0xD4, // TC_QISR RO 319 FaultMode = 0xD8, // TC_FMR RW 320 WriteProtectionMode = 0xE4, // TC_WPMR RW 321 } 322 323 private enum ClockSelection 324 { 325 MCK_2 = 0, 326 MCK_8 = 1, 327 MCK_32 = 2, 328 MCK_128 = 3, 329 SLCK = 4, 330 XC0 = 5, 331 XC1 = 6, 332 XC2 = 7, 333 } 334 335 private enum WaveSelection 336 { 337 Up = 0b00, 338 UpDown = 0b01, 339 UpRC = 0b10, 340 UpDownRC = 0b11, 341 } 342 343 private class Channel 344 { Channel(IClockSource clockSource, long masterClockFrequency, IPeripheral owner, int channel)345 public Channel(IClockSource clockSource, long masterClockFrequency, IPeripheral owner, int channel) 346 { 347 this.masterClockFrequency = masterClockFrequency; 348 this.channel = channel; 349 parent = owner; 350 IRQ = new GPIO(); 351 timer = new LimitTimer(clockSource, masterClockFrequency, owner, $"channel-{channel}", MaxValue, Direction.Ascending, eventEnabled: true, divider: 2); 352 cTimer = new LimitTimer(clockSource, masterClockFrequency, owner, $"channel-{channel} C capture", MaxValue, Direction.Ascending, workMode: WorkMode.OneShot, eventEnabled: true, divider: 2); 353 timer.LimitReached += LimitReached; 354 cTimer.LimitReached += delegate 355 { 356 if(StopOnC) 357 { 358 timer.Enabled = false; 359 enabled = false; 360 } 361 compareCInterrupt = true; 362 parent.NoisyLog("Channel #{0} compare C", channel); 363 UpdateInterrupts(); 364 }; 365 } 366 Reset()367 public void Reset() 368 { 369 valueA = 0x0; 370 valueB = 0x0; 371 valueC = 0x0; 372 enabled = false; 373 overflow = false; 374 overflowInterruptEnable = false; 375 compareAInterrupt = false; 376 compareAInterruptEnable = false; 377 compareBInterrupt = false; 378 compareBInterruptEnable = false; 379 compareCInterrupt = false; 380 compareCInterruptEnable = false; 381 loadingAInterrupt = false; 382 loadingAInterruptEnable = false; 383 loadingBInterrupt = false; 384 loadingBInterruptEnable = false; 385 waveformMode = false; 386 waveformSelected = WaveSelection.Up; 387 clockSelected = ClockSelection.MCK_2; 388 timer.Reset(); 389 cTimer.Reset(); 390 UpdateInterrupts(); 391 } 392 UpdateInterrupts()393 public void UpdateInterrupts() 394 { 395 var state = false; 396 state |= overflow && overflowInterruptEnable; 397 state |= compareAInterrupt && compareAInterruptEnable; 398 state |= compareBInterrupt && compareBInterruptEnable; 399 state |= compareCInterrupt && compareCInterruptEnable; 400 state |= loadingAInterrupt && loadingAInterruptEnable; 401 state |= loadingBInterrupt && loadingBInterruptEnable; 402 if(state) 403 { 404 parent.DebugLog("Channel #{0} IRQ blinked", channel); 405 // NOTE: We use Blink here due to specific software behaviour: 406 // The TC interrupt was set while the interrupts were masked, 407 // after the interrupts were enabled it was expected that 408 // the TC interrupts were handled as they came without clearing 409 // the pending status. 410 IRQ.Blink(); 411 } 412 } 413 Enable(bool start = false, bool debugLog = true)414 public void Enable(bool start = false, bool debugLog = true) 415 { 416 enabled = true; 417 UpdateTimer(start); 418 if(debugLog) 419 { 420 parent.DebugLog("Channel #{0} enabled", channel); 421 } 422 } 423 Disable()424 public void Disable() 425 { 426 enabled = false; 427 UpdateTimer(); 428 parent.DebugLog("Channel #{0} disabled", channel); 429 } 430 SoftwareTrigger()431 public void SoftwareTrigger() 432 { 433 parent.DebugLog("Channel #{0} software trigger", channel); 434 timer.Value = 0; 435 Enable(true, debugLog: false); 436 } 437 438 public IGPIO IRQ { get; } 439 440 public ulong Value => timer.Value; 441 442 public bool Enabled => timer.Enabled; 443 444 public bool Overflow => Misc.ReturnThenClear(ref overflow); 445 446 public bool OverflowInterruptEnable 447 { 448 get => overflowInterruptEnable; 449 set => overflowInterruptEnable = value; 450 } 451 452 public bool CompareAInterrupt => Misc.ReturnThenClear(ref compareAInterrupt); 453 454 public bool CompareAInterruptEnable 455 { 456 get => compareAInterruptEnable; 457 set => compareAInterruptEnable = value; 458 } 459 460 public bool CompareBInterrupt => Misc.ReturnThenClear(ref compareBInterrupt); 461 462 public bool CompareBInterruptEnable 463 { 464 get => compareBInterruptEnable; 465 set => compareBInterruptEnable = value; 466 } 467 468 public bool CompareCInterrupt => Misc.ReturnThenClear(ref compareCInterrupt); 469 470 public bool CompareCInterruptEnable 471 { 472 get => compareCInterruptEnable; 473 set => compareCInterruptEnable = value; 474 } 475 476 public bool LoadingAInterrupt => Misc.ReturnThenClear(ref loadingAInterrupt); 477 478 public bool LoadingAInterruptEnable 479 { 480 get => loadingAInterruptEnable; 481 set => loadingAInterruptEnable = value; 482 } 483 484 public bool LoadingBInterrupt => Misc.ReturnThenClear(ref loadingBInterrupt); 485 486 public bool LoadingBInterruptEnable 487 { 488 get => loadingBInterruptEnable; 489 set => loadingBInterruptEnable = value; 490 } 491 492 public ulong A 493 { 494 get => valueA; 495 set => valueA = value; 496 } 497 498 public ulong B 499 { 500 get => valueB; 501 set => valueB = value; 502 } 503 504 public ulong C 505 { 506 get => valueC; 507 set 508 { 509 valueC = value; 510 UpdateTimer(); 511 } 512 } 513 514 public bool WaveformMode 515 { 516 get => waveformMode; 517 set 518 { 519 waveformMode = value; 520 UpdateTimer(); 521 } 522 } 523 524 public WaveSelection WaveformSelected 525 { 526 get => waveformSelected; 527 set 528 { 529 waveformSelected = value; 530 UpdateTimer(); 531 } 532 } 533 534 public bool StopOnC { get; set; } 535 536 public ClockSelection ClockSelected 537 { 538 get => clockSelected; 539 set 540 { 541 if(clockSelected == value) 542 { 543 return; 544 } 545 546 clockSelected = value; 547 UpdateFrequency(); 548 } 549 } 550 UpdateFrequency()551 private void UpdateFrequency() 552 { 553 switch(clockSelected) 554 { 555 case ClockSelection.MCK_2: 556 timer.Frequency = masterClockFrequency; 557 timer.Divider = 2; 558 break; 559 case ClockSelection.MCK_8: 560 timer.Frequency = masterClockFrequency; 561 timer.Divider = 8; 562 break; 563 case ClockSelection.MCK_32: 564 timer.Frequency = masterClockFrequency; 565 timer.Divider = 32; 566 break; 567 case ClockSelection.MCK_128: 568 timer.Frequency = masterClockFrequency; 569 timer.Divider = 128; 570 break; 571 case ClockSelection.SLCK: 572 case ClockSelection.XC0: 573 case ClockSelection.XC1: 574 case ClockSelection.XC2: 575 parent.ErrorLog("Unimplemented"); 576 break; 577 default: 578 throw new Exception("unreachable"); 579 } 580 cTimer.Frequency = timer.Frequency; 581 cTimer.Divider = timer.Divider; 582 } 583 UpdateTimer(bool start = false)584 private void UpdateTimer(bool start = false) 585 { 586 if(!enabled) 587 { 588 return; 589 } 590 591 if(!waveformMode) 592 { 593 parent.ErrorLog("Unimplemented"); 594 } 595 else 596 { 597 switch(waveformSelected) 598 { 599 case WaveSelection.Up: 600 timer.Direction = Direction.Ascending; 601 timer.Limit = MaxValue; 602 break; 603 case WaveSelection.UpDown: 604 timer.Limit = MaxValue; 605 break; 606 case WaveSelection.UpRC: 607 timer.Direction = Direction.Ascending; 608 timer.Limit = valueC; 609 break; 610 case WaveSelection.UpDownRC: 611 timer.Limit = valueC; 612 break; 613 default: 614 throw new Exception("unreachable"); 615 } 616 } 617 618 timer.Enabled |= start; 619 UpdateCTimer(); 620 } 621 UpdateCTimer()622 private void UpdateCTimer() 623 { 624 if(!enabled || !waveformMode) 625 { 626 cTimer.Enabled = false; 627 return; 628 } 629 switch(waveformSelected) 630 { 631 case WaveSelection.Up: 632 case WaveSelection.UpDown: 633 var direction = timer.Direction; 634 var value = timer.Value; 635 var limit = timer.Limit; 636 637 if(direction == Direction.Ascending ? value > valueC : value < valueC) 638 { 639 cTimer.Enabled = false; 640 return; 641 } 642 643 if(direction == Direction.Ascending) 644 { 645 cTimer.Value = value; 646 cTimer.Limit = valueC; 647 } 648 else 649 { 650 cTimer.Value = limit - value; 651 cTimer.Limit = limit - valueC; 652 } 653 cTimer.Enabled = timer.Enabled; 654 break; 655 default: 656 cTimer.Enabled = false; 657 break; 658 } 659 } 660 ChangeDirection()661 private void ChangeDirection() 662 { 663 timer.Direction = timer.Direction == Direction.Ascending ? Direction.Descending : Direction.Ascending; 664 } 665 LimitReached()666 private void LimitReached() 667 { 668 var cReached = timer.Limit == valueC && timer.Direction == Direction.Ascending; 669 if(cReached && StopOnC) 670 { 671 timer.Enabled = false; 672 enabled = false; 673 } 674 if(!waveformMode) 675 { 676 parent.ErrorLog("Unimplemented"); 677 } 678 else 679 { 680 switch(waveformSelected) 681 { 682 case WaveSelection.Up: 683 overflow = true; 684 break; 685 case WaveSelection.UpRC: 686 parent.NoisyLog("Channel #{0} compare C", channel); 687 compareCInterrupt = true; 688 break; 689 case WaveSelection.UpDown: 690 if(timer.Value == MaxValue) 691 { 692 parent.NoisyLog("Channel #{0} overflow", channel); 693 overflow = true; 694 } 695 ChangeDirection(); 696 break; 697 case WaveSelection.UpDownRC: 698 if(cReached) 699 { 700 parent.NoisyLog("Channel #{0} compare C", channel); 701 compareCInterrupt = true; 702 } 703 ChangeDirection(); 704 break; 705 default: 706 throw new Exception("unreachable"); 707 } 708 } 709 UpdateCTimer(); 710 UpdateInterrupts(); 711 } 712 713 private ulong valueA; 714 private ulong valueB; 715 private ulong valueC; 716 private bool enabled; 717 private bool overflow; 718 private bool overflowInterruptEnable; 719 private bool compareAInterrupt; 720 private bool compareAInterruptEnable; 721 private bool compareBInterrupt; 722 private bool compareBInterruptEnable; 723 private bool compareCInterrupt; 724 private bool compareCInterruptEnable; 725 private bool loadingAInterrupt; 726 private bool loadingAInterruptEnable; 727 private bool loadingBInterrupt; 728 private bool loadingBInterruptEnable; 729 private bool waveformMode; 730 private WaveSelection waveformSelected; 731 732 private ClockSelection clockSelected; 733 private readonly long masterClockFrequency; 734 private readonly int channel; 735 private readonly IPeripheral parent; 736 private readonly LimitTimer timer; 737 private readonly LimitTimer cTimer; 738 739 private const ulong MaxValue = 0xFFFF; 740 } 741 } 742 } 743