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 
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Extensions;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Time;
15 using Antmicro.Renode.Utilities;
16 
17 namespace Antmicro.Renode.Peripherals.Timers
18 {
19     [AllowedTranslations(AllowedTranslation.WordToByte)]
20     public class SAMD21_Timer : IBytePeripheral, IDoubleWordPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, IKnownSize
21     {
SAMD21_Timer(IMachine machine, long baseFrequency)22         public SAMD21_Timer(IMachine machine, long baseFrequency)
23         {
24             RegistersCollection = new ByteRegisterCollection(this);
25 
26             mainTimer = new LimitTimer(machine.ClockSource, baseFrequency, this, "clock", eventEnabled: true, direction: Direction.Ascending);
27             captureTimer0 = new LimitTimer(machine.ClockSource, baseFrequency, this, "capture0", eventEnabled: true, workMode: WorkMode.OneShot, direction: Direction.Ascending);
28             captureTimer1 = new LimitTimer(machine.ClockSource, baseFrequency, this, "capture1", eventEnabled: true, workMode: WorkMode.OneShot, direction: Direction.Ascending);
29 
30             mainTimer.LimitReached += () =>
31             {
32                 interruptOverflowPending.Value = true;
33 
34                 UpdateInterrupts();
35                 StartChannels();
36             };
37 
38             captureTimer0.LimitReached += () =>
39             {
40                 interruptChannel0Pending.Value = true;
41                 UpdateInterrupts();
42             };
43 
44             captureTimer1.LimitReached +=  () =>
45             {
46                 interruptChannel1Pending.Value = true;
47                 UpdateInterrupts();
48             };
49 
50             DefineRegisters();
51             Reset();
52         }
53 
WriteDoubleWord(long offset, uint value)54         public void WriteDoubleWord(long offset, uint value)
55         {
56             if(offset < (long)Registers.Counter0)
57             {
58                 this.WriteDoubleWordUsingByte(offset, value);
59                 return;
60             }
61 
62             long width = CounterWidth;
63             if(offset == (long)Registers.Counter0)
64             {
65                 // NOTE: Counter
66                 mainTimer.Value = value;
67                 return;
68             }
69 
70             if(offset == (long)Registers.ChannelCompareCaptureValue0_0)
71             {
72                 // NOTE: Channel Compare Capture 0 value
73                 compare0Value = value;
74                 // NOTE: If correct mode is selected, we should update TOP value of the main timer
75                 mainTimer.Limit = CounterTopValue;
76                 StartChannels();
77                 return;
78             }
79 
80             if(offset == (long)Registers.ChannelCompareCaptureValue0_0 + width)
81             {
82                 // NOTE: Channel Compare Capture 1 value
83                 compare1Value = value;
84                 StartChannels();
85                 return;
86             }
87 
88             this.Log(LogLevel.Warning, "Unhandled write on offset 0x{0:X}", offset);
89         }
90 
ReadDoubleWord(long offset)91         public uint ReadDoubleWord(long offset)
92         {
93             if(offset < (long)Registers.Counter0)
94             {
95                 return this.ReadDoubleWordUsingByte(offset);
96             }
97 
98             long width = CounterWidth;
99             if(offset == (long)Registers.Counter0)
100             {
101                 // NOTE: Counter
102                 return (uint)mainTimer.Value;
103             }
104 
105             if(offset == (long)Registers.ChannelCompareCaptureValue0_0)
106             {
107                 // NOTE: Channel Compare Capture 0 value
108                 return (uint)compare0Value;
109             }
110 
111             if(offset == (long)Registers.ChannelCompareCaptureValue0_0 + width)
112             {
113                 // NOTE: Channel Compare Capture 1 value
114                 return (uint)compare1Value;
115             }
116 
117             this.Log(LogLevel.Warning, "Unhandled read on offset 0x{0:X}", offset);
118             return 0;
119         }
120 
WriteByte(long offset, byte value)121         public void WriteByte(long offset, byte value)
122         {
123             RegistersCollection.Write(offset, value);
124         }
125 
ReadByte(long offset)126         public byte ReadByte(long offset)
127         {
128             return RegistersCollection.Read(offset);
129         }
130 
Reset()131         public void Reset()
132         {
133             RegistersCollection.Reset();
134 
135             interruptOverflowEnabled = false;
136             interruptChannel0Enabled = false;
137             interruptChannel1Enabled = false;
138 
139             UpdateInterrupts();
140 
141             Enabled = false;
142             Divider = 1;
143 
144             compare0Value = 0;
145             compare1Value = 0;
146         }
147 
148         public ByteRegisterCollection RegistersCollection { get; }
149 
150         public GPIO IRQ { get; } = new GPIO();
151 
152         public long Size => 0x20;
153 
154         public bool Enabled
155         {
156             get => mainTimer.Enabled;
157             set
158             {
159                 mainTimer.Enabled = value;
160                 captureTimer0.Enabled = value;
161                 captureTimer1.Enabled = value;
162             }
163         }
164 
165         public int Divider
166         {
167             get => mainTimer.Divider;
168             set
169             {
170                 mainTimer.Divider = value;
171                 captureTimer0.Divider = value;
172                 captureTimer1.Divider = value;
173             }
174         }
175 
UpdateInterrupts()176         private void UpdateInterrupts()
177         {
178             var interrupt = false;
179             interrupt |= interruptOverflowEnabled && interruptOverflowPending.Value;
180             interrupt |= interruptChannel0Enabled && interruptChannel0Pending.Value;
181             interrupt |= interruptChannel1Enabled && interruptChannel1Pending.Value;
182 
183             this.Log(LogLevel.Debug, "Changed IRQ to {0}", IRQ.IsSet);
184             IRQ.Set(interrupt);
185         }
186 
ReconfigureCounter()187         private void ReconfigureCounter()
188         {
189             mainTimer.Limit = CounterTopValue;
190             mainTimer.Value = mainTimer.Direction == Direction.Ascending ? 0 : CounterTopValue;
191         }
192 
StartChannels()193         private void StartChannels()
194         {
195             captureTimer0.Value = mainTimer.Value;
196             captureTimer1.Value = mainTimer.Value;
197 
198             if(mainTimer.Direction == Direction.Ascending)
199             {
200                 captureTimer0.Limit = compare0Value;
201                 captureTimer1.Limit = compare1Value;
202             }
203             else
204             {
205                 var topValue = CounterTopValue;
206 
207                 captureTimer0.Limit = topValue - compare0Value;
208                 captureTimer1.Limit = topValue - compare1Value;
209             }
210 
211             captureTimer0.Enabled = captureTimer0.Limit > 0 && captureTimer0.Value <= captureTimer0.Limit;
212             captureTimer1.Enabled = captureTimer1.Limit > 0 && captureTimer1.Value <= captureTimer1.Limit;
213         }
214 
DefineRegisters()215         private void DefineRegisters()
216         {
217             Registers.ControlA0.Define(this)
218                 .WithFlag(0, FieldMode.WriteOneToClear, name: "SWRST",
219                     writeCallback: (_, value) => { if(value) Reset(); })
220                 .WithFlag(1, name: "ENABLE",
221                     valueProviderCallback: _ => Enabled,
222                     writeCallback: (_, value) => Enabled = value)
223                 .WithEnumField<ByteRegister, CounterMode>(2, 2, out counterMode, name: "MODE",
224                     changeCallback: (_, __) =>
225                     {
226                         if(counterMode.Value == CounterMode.Reserved)
227                         {
228                             this.Log(LogLevel.Warning, "MODE field has been set to {0} (Reserved); this will be treated as default (0)", (uint)counterMode.Value);
229                         }
230 
231                         ReconfigureCounter();
232                     })
233                 .WithReservedBits(4, 1)
234                 .WithEnumField(5, 2, out waveformGenerationOperation, name: "WAVEGEN",
235                     changeCallback: (_, __) => ReconfigureCounter())
236                 .WithReservedBits(7, 1)
237             ;
238 
239             Registers.ControlA1.Define(this)
240                 .WithEnumField<ByteRegister, Prescaler>(0, 3, name: "PRESCALER",
241                     writeCallback: (_, value) => Divider = GetPrescalerValue(value))
242                 .WithTaggedFlag("RUNSTDBY", 3)
243                 .WithTag("PRESCSYNC", 4, 2)
244                 .WithReservedBits(6, 2)
245             ;
246 
247             Registers.ControlBSet.Define(this)
248                 .WithFlag(0, name: "DIR",
249                     valueProviderCallback: _ => mainTimer.Direction == Direction.Descending,
250                     writeCallback: (_, value) => { if(value) mainTimer.Direction = Direction.Descending; })
251                 .WithFlag(2, name: "ONESHOT",
252                     valueProviderCallback: _ => mainTimer.Mode == WorkMode.OneShot,
253                     writeCallback: (_, value) => { if(value) mainTimer.Mode = WorkMode.OneShot; })
254                 .WithReservedBits(3, 3)
255                 .WithEnumField<ByteRegister, Command>(6, 2, name: "CMD",
256                     writeCallback: (_, value) =>
257                     {
258                         switch(value)
259                         {
260                             case Command.Retrigger:
261                                 Enabled = true;
262                                 ReconfigureCounter();
263                                 StartChannels();
264                                 break;
265                             case Command.Stop:
266                                 Enabled = false;
267                                 break;
268                             default:
269                                 break;
270                         }
271                     })
272             ;
273 
274             Registers.ControlBClear.Define(this)
275                 .WithFlag(0, name: "DIR",
276                     valueProviderCallback: _ => mainTimer.Direction == Direction.Descending,
277                     writeCallback: (_, value) => { if(value) mainTimer.Direction = Direction.Ascending; })
278                 .WithReservedBits(1, 1)
279                 .WithFlag(2, name: "ONESHOT",
280                     valueProviderCallback: _ => mainTimer.Mode == WorkMode.OneShot,
281                     writeCallback: (_, value) => { if(value) mainTimer.Mode = WorkMode.Periodic; })
282                 .WithReservedBits(3, 3)
283                 .WithTag("CMD", 6, 2)
284             ;
285 
286             Registers.Status.Define(this)
287                 .WithReservedBits(0, 3)
288                 .WithFlag(3, FieldMode.Read, name: "STOP",
289                     valueProviderCallback: _ => !Enabled)
290                 .WithReservedBits(4, 3)
291                 .WithTaggedFlag("SYNCBUSY", 7)
292             ;
293 
294             Registers.InterruptEnableClear.Define(this)
295                 .WithFlag(0, name: "OVF",
296                     valueProviderCallback: _ => interruptOverflowEnabled,
297                     writeCallback: (_, value) => { if(value) interruptOverflowEnabled = false; })
298                 .WithTaggedFlag("ERR", 1)
299                 .WithReservedBits(2, 1)
300                 .WithTaggedFlag("SYNCRDY", 3)
301                 .WithFlag(4, name: "MC0",
302                     valueProviderCallback: _ => interruptChannel0Enabled,
303                     writeCallback: (_, value) => { if(value) interruptChannel0Enabled = false; })
304                 .WithFlag(5, name: "MC1",
305                     valueProviderCallback: _ => interruptChannel1Enabled,
306                     writeCallback: (_, value) => { if(value) interruptChannel1Enabled = false; })
307                 .WithReservedBits(6, 2)
308                 .WithWriteCallback((_, __) => UpdateInterrupts())
309             ;
310 
311             Registers.InterruptEnableSet.Define(this)
312                 .WithFlag(0, name: "OVF",
313                     valueProviderCallback: _ => interruptOverflowEnabled,
314                     writeCallback: (_, value) => { if(value) interruptOverflowEnabled = true; })
315                 .WithTaggedFlag("ERR", 1)
316                 .WithReservedBits(2, 1)
317                 .WithTaggedFlag("SYNCRDY", 3)
318                 .WithFlag(4, name: "MC0",
319                     valueProviderCallback: _ => interruptChannel0Enabled,
320                     writeCallback: (_, value) => { if(value) interruptChannel0Enabled = true; })
321                 .WithFlag(5, name: "MC1",
322                     valueProviderCallback: _ => interruptChannel1Enabled,
323                     writeCallback: (_, value) => { if(value) interruptChannel1Enabled = true; })
324                 .WithReservedBits(6, 2)
325                 .WithWriteCallback((_, __) => UpdateInterrupts())
326             ;
327 
328             Registers.InterruptFlags.Define(this)
329                 .WithFlag(0, out interruptOverflowPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "OVF")
330                 .WithTaggedFlag("ERR", 1)
331                 .WithReservedBits(2, 1)
332                 .WithTaggedFlag("SYNCRDY", 3)
333                 .WithFlag(4, out interruptChannel0Pending, FieldMode.Read | FieldMode.WriteOneToClear, name: "MC0")
334                 .WithFlag(5, out interruptChannel1Pending, FieldMode.Read | FieldMode.WriteOneToClear, name: "MC1")
335                 .WithReservedBits(6, 2)
336                 .WithWriteCallback((_, __) => UpdateInterrupts())
337             ;
338         }
339 
GetPrescalerValue(Prescaler prescaler)340         private int GetPrescalerValue(Prescaler prescaler)
341         {
342             switch(prescaler)
343             {
344                 case Prescaler.Div1:
345                     return 1;
346                 case Prescaler.Div2:
347                     return 2;
348                 case Prescaler.Div4:
349                     return 4;
350                 case Prescaler.Div8:
351                     return 8;
352                 case Prescaler.Div16:
353                     return 16;
354                 case Prescaler.Div64:
355                     return 64;
356                 case Prescaler.Div256:
357                     return 256;
358                 case Prescaler.Div1024:
359                     return 1024;
360                 default:
361                     throw new Exception("unreachable");
362             }
363         }
364 
365         private uint CounterWidth
366         {
367             get
368             {
369                 switch(counterMode.Value)
370                 {
371                     case CounterMode.Count8:
372                         return 1;
373                     case CounterMode.Count32:
374                         return 4;
375                     case CounterMode.Count16:
376                     default:
377                         return 2;
378                 }
379             }
380         }
381 
382         private uint CounterTopValue
383         {
384             get
385             {
386                 if(compare0Value > 0 &&
387                     (waveformGenerationOperation.Value == WaveformGenerationOperation.MatchFrequency ||
388                      waveformGenerationOperation.Value == WaveformGenerationOperation.MatchPWM))
389                 {
390                     return (uint)compare0Value;
391                 }
392 
393                 switch(counterMode.Value)
394                 {
395                     case CounterMode.Count8:
396                         return 0xFF;
397                     case CounterMode.Count32:
398                         return 0xFFFFFFFF;
399                     case CounterMode.Count16:
400                     default:
401                         return 0xFFFF;
402                 }
403             }
404         }
405 
406         private readonly LimitTimer mainTimer;
407         private readonly LimitTimer captureTimer0;
408         private readonly LimitTimer captureTimer1;
409 
410         private bool interruptOverflowEnabled;
411         private bool interruptChannel0Enabled;
412         private bool interruptChannel1Enabled;
413 
414         private ulong compare0Value;
415         private ulong compare1Value;
416 
417         private IEnumRegisterField<CounterMode> counterMode;
418         private IEnumRegisterField<WaveformGenerationOperation> waveformGenerationOperation;
419 
420         private IFlagRegisterField interruptOverflowPending;
421         private IFlagRegisterField interruptChannel0Pending;
422         private IFlagRegisterField interruptChannel1Pending;
423 
424         private enum Command
425         {
426             Nothing,
427             Retrigger,
428             Stop,
429             Reserved,
430         }
431 
432         private enum WaveformGenerationOperation
433         {
434             NormalFrequency,
435             MatchFrequency,
436             NormalPWM,
437             MatchPWM,
438         }
439 
440         private enum CounterMode
441         {
442             Count16,
443             Count8,
444             Count32,
445             Reserved,
446         }
447 
448         private enum Prescaler
449         {
450             Div1,
451             Div2,
452             Div4,
453             Div8,
454             Div16,
455             Div64,
456             Div256,
457             Div1024
458         }
459 
460         private enum Registers
461         {
462             ControlA0 = 0x00,
463             ControlA1 = 0x01,
464             ReadRequest0 = 0x02,
465             ReadRequest1 = 0x03,
466             ControlBClear = 0x04,
467             ControlBSet = 0x05,
468             ControlC = 0x06,
469 
470             Reserved0 = 0x07,
471 
472             DebugControl = 0x08,
473 
474             Reserved1 = 0x09,
475 
476             EventControl = 0x0A,
477             InterruptEnableClear = 0x0C,
478             InterruptEnableSet = 0x0D,
479             InterruptFlags = 0x0E,
480             Status = 0x0F,
481 
482             // NOTE: This is 8-bit, 16-bit or 32-bit register
483             Counter0 = 0x10,
484             Counter1 = 0x11,
485             Counter2 = 0x12,
486             Counter3 = 0x13,
487 
488             // NOTE: This is 8-bit, 16-bit or 32-bit register
489             ChannelCompareCaptureValue0_0 = 0x18,
490             ChannelCompareCaptureValue0_1 = 0x19,
491             ChannelCompareCaptureValue0_2 = 0x1A,
492             ChannelCompareCaptureValue0_3 = 0x1B,
493 
494             // NOTE: This is 8-bit, 16-bit or 32-bit register
495             ChannelCompareCaptureValue1_0 = 0x1C,
496             ChannelCompareCaptureValue1_1 = 0x1D,
497             ChannelCompareCaptureValue1_2 = 0x1E,
498             ChannelCompareCaptureValue1_3 = 0x1F,
499         }
500     }
501 }
502