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 System;
8 using System.Collections.Generic;
9 using System.Collections.ObjectModel;
10 using System.Linq;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Core.Structure.Registers;
14 using Antmicro.Renode.Exceptions;
15 using Antmicro.Renode.Time;
16 
17 namespace Antmicro.Renode.Peripherals.Timers
18 {
19     public class Cadence_TTC : IDoubleWordPeripheral, INumberedGPIOOutput, IKnownSize
20     {
Cadence_TTC(IMachine machine, long frequency = DefaultFrequency)21         public Cadence_TTC(IMachine machine, long frequency = DefaultFrequency)
22         {
23             var irqs = new Dictionary<int, IGPIO>(TimerUnitsCount);
24             var registersMap = new Dictionary<long, DoubleWordRegister>();
25 
26             for(var index = 0; index < timerUnits.Length; index++)
27             {
28                 var timer = new TimerUnit(machine.ClockSource, this, frequency, $"Timer{index + 1}");
29                 foreach(var register in BuildTimerUnitRegisters(timer))
30                 {
31                     registersMap[register.Key + index * RegisterSize] = register.Value;
32                 }
33 
34                 timerUnits[index] = timer;
35                 irqs[index] = timer.irq;
36             }
37 
38             Connections = new ReadOnlyDictionary<int, IGPIO>(irqs);
39             registers = new DoubleWordRegisterCollection(this, registersMap);
40         }
41 
WriteDoubleWord(long offset, uint value)42         public void WriteDoubleWord(long offset, uint value)
43         {
44             registers.Write(offset, value);
45         }
46 
ReadDoubleWord(long offset)47         public uint ReadDoubleWord(long offset)
48         {
49             return registers.Read(offset);
50         }
51 
Reset()52         public void Reset()
53         {
54             registers.Reset();
55             // Registers values depend only on a timer object (not on registers reset)
56             foreach(var timer in timerUnits)
57             {
58                 timer.Reset();
59             }
60         }
61 
SetCounterValue(int timerIndex, uint value)62         public void SetCounterValue(int timerIndex, uint value)
63         {
64             if(timerIndex < 0 || timerIndex >= TimerUnitsCount)
65             {
66                 throw new RecoverableException($"Invalid timer index: TTC contains {TimerUnitsCount} timers.");
67             }
68             timerUnits[timerIndex].Value = value;
69         }
70 
71         public long Size => 0x100;
72 
73         public IReadOnlyDictionary<int, IGPIO> Connections { get; }
74 
75         public long Frequency
76         {
77             get => timerUnits[0].Frequency;
78             set
79             {
80                 foreach(var timer in timerUnits)
81                 {
82                     timer.Frequency = value;
83                 }
84             }
85         }
86 
BuildTimerUnitRegisters(TimerUnit timer)87         private Dictionary<long, DoubleWordRegister> BuildTimerUnitRegisters(TimerUnit timer)
88         {
89             return new Dictionary<long, DoubleWordRegister>
90             {
91                 {(long)Registers.ClockControl1, new DoubleWordRegister(this)
92                     .WithReservedBits(7, 25)
93                     .WithTaggedFlag("ExternalClockEdge", 6)
94                     .WithTaggedFlag("ClockSource", 5)
95                     .WithValueField(1, 4, name: "PrescalerValue",
96                         writeCallback: (_, val) => timer.Prescaler = (int)val,
97                         valueProviderCallback: (_) => (uint)timer.Prescaler
98                     )
99                     .WithFlag(0, name: "PrescalerEnable",
100                         writeCallback: (_, val) => timer.PrescalerEnabled = val,
101                         valueProviderCallback: (_) => timer.PrescalerEnabled
102                     )
103                 },
104                 {(long)Registers.CounterControl1, new DoubleWordRegister(this)
105                     .WithReservedBits(7, 25)
106                     .WithTaggedFlag("WaveformPolarity", 6)
107                     .WithTaggedFlag("WaveformOutputDisable", 5)
108                     .WithFlag(4, name: "Reset",
109                         writeCallback: (_, val) => { if(val) timer.ResetValue(); },
110                         valueProviderCallback: (_) => false
111                     )
112                     .WithFlag(3, name: "MatchEnable",
113                         writeCallback: (_, val) => timer.MatchEnabled = val,
114                         valueProviderCallback: (_) => timer.MatchEnabled
115                     )
116                     .WithFlag(2, name: "CounterDecrement",
117                         writeCallback: (_, val) => timer.Direction = (val ? Direction.Descending : Direction.Ascending),
118                         valueProviderCallback: (_) => timer.Direction == Direction.Descending
119                     )
120                     .WithEnumField<DoubleWordRegister, CounterMode>(1, 1, name: "CounterMode",
121                         writeCallback: (_, val) => timer.Mode = val,
122                         valueProviderCallback: (_) => timer.Mode
123                     )
124                     .WithFlag(0, name: "Disable",
125                         writeCallback: (_, val) => timer.Enabled = !val,
126                         valueProviderCallback: (_) => !timer.Enabled
127                     )
128                 },
129                 {(long)Registers.CounterValue1, new DoubleWordRegister(this)
130                     .WithValueField(0, 32, FieldMode.Read, name: "CounterValue",
131                         valueProviderCallback: (_) => (uint)timer.Value
132                     )
133                 },
134                 {(long)Registers.CounterInterval1, new DoubleWordRegister(this)
135                     .WithValueField(0, 32, name: "IntervalCounter",
136                         writeCallback: (_, val) => timer.Interval = (uint)val,
137                         valueProviderCallback: (_) => timer.Interval
138                     )
139                 },
140                 {(long)Registers.Match1Counter1, new DoubleWordRegister(this)
141                     .WithValueField(0, 32, name: "Match1Value",
142                         writeCallback: (_, val) => timer.Match[0].MatchValue = (uint)val,
143                         valueProviderCallback: (_) => timer.Match[0].MatchValue
144                     )
145                 },
146                 {(long)Registers.Match2Counter1, new DoubleWordRegister(this)
147                     .WithValueField(0, 32, name: "Match1Value",
148                         writeCallback: (_, val) => timer.Match[1].MatchValue = (uint)val,
149                         valueProviderCallback: (_) => timer.Match[1].MatchValue
150                     )
151                 },
152                 {(long)Registers.Match3Counter1, new DoubleWordRegister(this)
153                     .WithValueField(0, 32, name: "Match1Value",
154                         writeCallback: (_, val) => timer.Match[2].MatchValue = (uint)val,
155                         valueProviderCallback: (_) => timer.Match[2].MatchValue
156                     )
157                 },
158                 {(long)Registers.InterruptStatus1, new DoubleWordRegister(this)
159                     .WithReservedBits(6, 26)
160                     .WithTaggedFlag("EventTimerOverflowInterrupt", 5)
161                     .WithFlag(4, FieldMode.ReadToClear, name: "CounterInterrupt",
162                         readCallback: (_, __) => timer.OverflowInterruptFlag = false,
163                         valueProviderCallback: (_) => timer.OverflowInterruptFlag
164                     )
165                     .WithFlag(3, FieldMode.ReadToClear, name: "Match3Interrupt",
166                         readCallback: (_, __) => timer.Match[2].Interrupt = false,
167                         valueProviderCallback: (_) => timer.Match[2].Interrupt
168                     )
169                     .WithFlag(2, FieldMode.ReadToClear, name: "Match2Interrupt",
170                         readCallback: (_, __) => timer.Match[1].Interrupt = false,
171                         valueProviderCallback: (_) => timer.Match[1].Interrupt
172                     )
173                     .WithFlag(1, FieldMode.ReadToClear, name: "Match1Interrupt",
174                         readCallback: (_, __) => timer.Match[0].Interrupt = false,
175                         valueProviderCallback: (_) => timer.Match[0].Interrupt
176                     )
177                     .WithFlag(0, FieldMode.ReadToClear, name: "IntervalInterrupt",
178                         readCallback: (_, __) => timer.IntervalInterruptFlag = false,
179                         valueProviderCallback: (_) => timer.IntervalInterruptFlag
180                     )
181                     .WithReadCallback((_, __) => timer.UpdateInterrupts())
182                 },
183                 {(long)Registers.InterruptEnable1, new DoubleWordRegister(this)
184                     .WithReservedBits(6, 26)
185                     .WithTaggedFlag("EventTimerOverflowInterruptEnable", 5)
186                     .WithFlag(4, name: "CounterInterruptEnable",
187                         writeCallback: (_, val) => timer.OverflowInterruptEnabled = val,
188                         valueProviderCallback: (_) => timer.OverflowInterruptEnabled
189                     )
190                     .WithFlag(3, name: "Match3InterruptEnable",
191                         writeCallback: (_, val) => timer.Match[2].InterruptEnable = val,
192                         valueProviderCallback: (_) => timer.Match[2].InterruptEnable
193                     )
194                     .WithFlag(2, name: "Match2InterruptEnable",
195                         writeCallback: (_, val) => timer.Match[1].InterruptEnable = val,
196                         valueProviderCallback: (_) => timer.Match[1].InterruptEnable
197                     )
198                     .WithFlag(1, name: "Match1InterruptEnable",
199                         writeCallback: (_, val) => timer.Match[0].InterruptEnable = val,
200                         valueProviderCallback: (_) => timer.Match[0].InterruptEnable
201                     )
202                     .WithFlag(0, name: "IntervalInterruptEnable",
203                         writeCallback: (_, val) => timer.IntervalInterruptEnabled = val,
204                         valueProviderCallback: (_) => timer.IntervalInterruptEnabled
205                     )
206                     .WithWriteCallback((_, __) => timer.UpdateInterrupts())
207                 }
208             };
209         }
210 
211         private readonly TimerUnit[] timerUnits = new TimerUnit[TimerUnitsCount];
212         private readonly DoubleWordRegisterCollection registers;
213 
214         private const long DefaultFrequency = 33330000;
215         private const int RegisterSize = 4;
216         private const int TimerUnitsCount = 3;
217         private const int MatchTimerUnitsCount = 3;
218 
219         private class TimerUnit : ITimer
220         {
TimerUnit(IClockSource clockSource, IPeripheral parent, long frequency, string localName)221             public TimerUnit(IClockSource clockSource, IPeripheral parent, long frequency, string localName)
222             {
223                 timer = new LimitTimer(clockSource, frequency, parent, localName, limit: OverflowLimit, direction: Direction.Ascending, eventEnabled: true);
224                 timer.LimitReached += OnLimitReached;
225 
226                 Match = new MatchTimerUnit[MatchTimerUnitsCount];
227                 for(var i = 0; i < MatchTimerUnitsCount; i++)
228                 {
229                     Match[i] = new MatchTimerUnit(clockSource, parent, this, frequency, $"{localName}-match{i}");
230                 }
231             }
232 
Reset()233             public void Reset()
234             {
235                 timer.Reset();
236                 MatchEnabled = false;
237                 Mode = CounterMode.Overflow;
238                 Interval = 0;
239                 PrescalerEnabled = false;
240                 Prescaler = 0;
241                 Array.ForEach(Match, m => m.Reset());
242 
243                 OverflowInterruptEnabled = false;
244                 IntervalInterruptEnabled = false;
245                 ResetFlags();
246             }
247 
ResetFlags()248             public void ResetFlags()
249             {
250                 OverflowInterruptFlag = false;
251                 IntervalInterruptFlag = false;
252                 UpdateInterrupts();
253             }
254 
ResetValue()255             public void ResetValue()
256             {
257                 timer.ResetValue();
258                 Array.ForEach(Match, m => m.Update());
259             }
260 
UpdateInterrupts()261             public void UpdateInterrupts()
262             {
263                 irq.Set((OverflowInterruptFlag && OverflowInterruptEnabled)
264                     || (IntervalInterruptFlag && IntervalInterruptEnabled)
265                     || Match.Any(m => m.IRQ));
266             }
267 
268             public bool Enabled
269             {
270                 get => timer.Enabled;
271                 set
272                 {
273                     timer.Enabled = value;
274                     Array.ForEach(Match, m => m.Update());
275                 }
276             }
277 
278             public bool MatchEnabled
279             {
280                 get => matchEnabled;
281                 set
282                 {
283                     matchEnabled = value;
284                     Array.ForEach(Match, m => m.Enabled = value);
285                 }
286             }
287 
288             public CounterMode Mode
289             {
290                 get => mode;
291                 set
292                 {
293                     mode = value;
294                     UpdateLimit();
295                 }
296             }
297 
298             public uint Interval
299             {
300                 get => interval;
301                 set
302                 {
303                     interval = value;
304                     UpdateLimit();
305                 }
306             }
307 
308             public bool PrescalerEnabled
309             {
310                 get => prescalerEnabled;
311                 set
312                 {
313                     prescalerEnabled = value;
314                     UpdateDivider();
315                 }
316             }
317 
318             public int Prescaler
319             {
320                 get => prescaler;
321                 set
322                 {
323                     prescaler = value;
324                     UpdateDivider();
325                 }
326             }
327 
328             public Direction Direction
329             {
330                 get => timer.Direction;
331                 set
332                 {
333                     timer.Direction = value;
334                     Array.ForEach(Match, m => m.Update());
335                 }
336             }
337 
338             public ulong Value
339             {
340                 get => timer.Value;
341                 set
342                 {
343                     timer.Value = value;
344                     Array.ForEach(Match, m => m.Update());
345                 }
346             }
347 
348             public long Frequency
349             {
350                 get => timer.Frequency;
351                 set
352                 {
353                     timer.Frequency = value;
354                     Array.ForEach(Match, m => m.Frequency = value);
355                 }
356             }
357 
358             public bool OverflowInterruptFlag { get; set; }
359             public bool OverflowInterruptEnabled { get; set; }
360             public bool IntervalInterruptFlag { get; set; }
361             public bool IntervalInterruptEnabled { get; set; }
362             public MatchTimerUnit[] Match { get; }
363 
364             public readonly IGPIO irq = new GPIO();
365 
OnLimitReached()366             private void OnLimitReached()
367             {
368                 if(Mode == CounterMode.Interval)
369                 {
370                     IntervalInterruptFlag = true;
371                 }
372                 else
373                 {
374                     OverflowInterruptFlag = true;
375                 }
376                 Array.ForEach(Match, m => m.Update());
377                 UpdateInterrupts();
378             }
379 
UpdateDivider()380             private void UpdateDivider()
381             {
382                 if(PrescalerEnabled)
383                 {
384                     timer.Divider = 1 << (Prescaler + 1);
385                 }
386                 else
387                 {
388                     timer.Divider = 1;
389                 }
390                 Array.ForEach(Match, m => m.Divider = timer.Divider);
391             }
392 
UpdateLimit()393             private void UpdateLimit()
394             {
395                 if(Mode == CounterMode.Interval)
396                 {
397                     timer.Limit = Interval;
398                 }
399                 else
400                 {
401                     timer.Limit = OverflowLimit;
402                 }
403                 Array.ForEach(Match, m => m.Limit = timer.Limit);
404             }
405 
406             private CounterMode mode;
407             private uint interval;
408             private bool prescalerEnabled;
409             private bool matchEnabled;
410             private int prescaler;
411 
412             private readonly LimitTimer timer;
413 
414             private const uint OverflowLimit = UInt32.MaxValue;
415 
416             public class MatchTimerUnit
417             {
MatchTimerUnit(IClockSource clockSource, IPeripheral parent, TimerUnit owner, long frequency, string localName)418                 public MatchTimerUnit(IClockSource clockSource, IPeripheral parent, TimerUnit owner, long frequency, string localName)
419                 {
420                     this.owner = owner;
421                     timer = new LimitTimer(clockSource, frequency, parent, localName, limit: OverflowLimit, direction: Direction.Ascending, workMode: WorkMode.OneShot);
422                     timer.LimitReached += owner.UpdateInterrupts;
423                     limit = OverflowLimit;
424                 }
425 
Reset()426                 public void Reset()
427                 {
428                     timer.Reset();
429                     matchValue = 0;
430                     // `limit` and `matchEnabled` are reset by `owner`
431                 }
432 
Update()433                 public void Update()
434                 {
435                     if(!enabled || matchValue > limit || !owner.Enabled || (IsAscending ? matchValue < owner.Value : owner.Value < matchValue))
436                     {
437                         timer.Enabled = false;
438                         return;
439                     }
440                     TimerMatchValue = matchValue;
441                     TimerValue = owner.Value;
442                     timer.Enabled = true;
443                 }
444 
445                 public uint MatchValue
446                 {
447                     get => matchValue;
448                     set
449                     {
450                         matchValue = value;
451                         Update();
452                     }
453                 }
454 
455                 public bool Enabled
456                 {
457                     get => enabled;
458                     set
459                     {
460                         enabled = value;
461                         Update();
462                     }
463                 }
464 
465                 public bool Interrupt
466                 {
467                     get => timer.RawInterrupt;
468                     set
469                     {
470                         if(!value)
471                         {
472                             timer.ClearInterrupt();
473                         }
474                     }
475                 }
476 
477                 public bool IRQ => timer.Interrupt;
478 
479                 public bool InterruptEnable
480                 {
481                     get => timer.EventEnabled;
482                     set => timer.EventEnabled = value;
483                 }
484 
485                 public int Divider
486                 {
487                     set => timer.Divider = value;
488                 }
489 
490                 public ulong Limit
491                 {
492                     set
493                     {
494                         limit = value;
495                         Update();
496                     }
497                 }
498 
499                 public long Frequency
500                 {
501                     set => timer.Frequency = value;
502                 }
503 
TranslateValueForInternalTimer(ulong value)504                 private ulong TranslateValueForInternalTimer(ulong value)
505                 {
506                     // For descending this class flips direction, thus counting in ascending
507                     // direction, changing sign and using values congruent modulo `limit`
508 
509                     // NOTE: ComparingTimer doesn't support descending direction so this
510                     // translation and usage of LimitTimer is a workaround
511                     return IsAscending ? value : limit - value;
512                 }
513 
514                 private ulong TimerMatchValue
515                 {
516                     get => TranslateValueForInternalTimer(timer.Limit);
517                     set => timer.Limit = TranslateValueForInternalTimer(value);
518                 }
519 
520                 private ulong TimerValue
521                 {
522                     get => TranslateValueForInternalTimer(timer.Value);
523                     set => timer.Value = TranslateValueForInternalTimer(value);
524                 }
525 
526                 private bool IsAscending => owner.Direction == Direction.Ascending;
527 
528                 private bool enabled;
529                 private ulong limit;
530                 private uint matchValue;
531 
532                 private readonly TimerUnit owner;
533                 private readonly LimitTimer timer;
534             }
535         }
536 
537         private enum CounterMode
538         {
539             Overflow = 0x0,
540             Interval = 0x1,
541         }
542 
543         private enum Registers : long
544         {
545             ClockControl1 = 0x00,
546             ClockControl2 = 0x04,
547             ClockControl3 = 0x08,
548             CounterControl1 = 0x0C,
549             CounterControl2 = 0x10,
550             CounterControl3 = 0x14,
551             CounterValue1 = 0x18,
552             CounterValue2 = 0x1C,
553             CounterValue3 = 0x20,
554             CounterInterval1 = 0x24,
555             CounterInterval2 = 0x28,
556             CounterInterval3 = 0x2C,
557             Match1Counter1 = 0x30,
558             Match1Counter2 = 0x34,
559             Match1Counter3 = 0x38,
560             Match2Counter1 = 0x3C,
561             Match2Counter2 = 0x40,
562             Match2Counter3 = 0x44,
563             Match3Counter1 = 0x48,
564             Match3Counter2 = 0x4C,
565             Match3Counter3 = 0x50,
566             InterruptStatus1 = 0x54,
567             InterruptStatus2 = 0x58,
568             InterruptStatus3 = 0x5C,
569             InterruptEnable1 = 0x60,
570             InterruptEnable2 = 0x64,
571             InterruptEnable3 = 0x68,
572             EventControlTimer1 = 0x6C,
573             EventControlTimer2 = 0x70,
574             EventControlTimer3 = 0x74,
575             EventRegister1 = 0x78,
576             EventRegister2 = 0x7C,
577             EventRegister3 = 0x80
578         }
579     }
580 }
581