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