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 Antmicro.Renode.Core;
8 using Antmicro.Renode.Core.Structure.Registers;
9 using Antmicro.Renode.Logging;
10 using Antmicro.Renode.Peripherals;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Time;
13 using Antmicro.Renode.Utilities;
14 
15 namespace Antmicro.Renode.Peripherals.Timers
16 {
17     public class RenesasRA_AGT : IBytePeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, IWordPeripheral, IProvidesRegisterCollection<WordRegisterCollection>, IKnownSize
18     {
RenesasRA_AGT(IMachine machine, long lowSpeedOnChipOscillatorFrequency, long subClockOscillatorFrequency, long peripheralClockBFrequency)19         public RenesasRA_AGT(IMachine machine, long lowSpeedOnChipOscillatorFrequency, long subClockOscillatorFrequency, long peripheralClockBFrequency)
20         {
21             this.lowSpeedOnChipOscillatorFrequency = lowSpeedOnChipOscillatorFrequency;
22             this.subClockOscillatorFrequency = subClockOscillatorFrequency;
23             this.peripheralClockBFrequency = peripheralClockBFrequency;
24             this.machine = machine;
25             IRQ = new GPIO();
26             timer = new LimitTimer(machine.ClockSource, peripheralClockBFrequency, this, "agt", ushort.MaxValue, eventEnabled: true, autoUpdate: true);
27             timer.LimitReached += HandleLimitReached;
28 
29             channels = new CompareChannel[2];
30             for(var i = 0; i < channels.Length; ++i)
31             {
32                 channels[i] = new CompareChannel(machine, peripheralClockBFrequency, this, $"agt-channel{(char)((int)'A' + i)}");
33             }
34 
35             ByteRegisterCollection = new ByteRegisterCollection(this);
36             WordRegisterCollection = new WordRegisterCollection(this);
37             DefineRegisters();
38         }
39 
Reset()40         public void Reset()
41         {
42             timer.Reset();
43             foreach(var channel in channels)
44             {
45                 channel.Reset();
46             }
47             WordRegisterCollection.Reset();
48             WordRegisterCollection.Reset();
49         }
50 
ReadByte(long offset)51         public byte ReadByte(long offset)
52         {
53             return ByteRegisterCollection.Read(offset);
54         }
55 
WriteByte(long offset, byte value)56         public void WriteByte(long offset, byte value)
57         {
58             ByteRegisterCollection.Write(offset, value);
59         }
60 
ReadWord(long offset)61         public ushort ReadWord(long offset)
62         {
63             return WordRegisterCollection.Read(offset);
64         }
65 
WriteWord(long offset, ushort value)66         public void WriteWord(long offset, ushort value)
67         {
68             WordRegisterCollection.Write(offset, value);
69         }
70 
71         ByteRegisterCollection IProvidesRegisterCollection<ByteRegisterCollection>.RegistersCollection => ByteRegisterCollection;
72         WordRegisterCollection IProvidesRegisterCollection<WordRegisterCollection>.RegistersCollection => WordRegisterCollection;
73 
74         public long Size => 0x100;
75         public long PeripheralClockBFrequency
76         {
77             get => peripheralClockBFrequency;
78             set
79             {
80                 peripheralClockBFrequency = value;
81                 switch(countSource.Value)
82                 {
83                 case CountSource.PeripheralClockB_Div1:
84                 case CountSource.PeripheralClockB_Div8:
85                 case CountSource.PeripheralClockB_Div2:
86                     Frequency = peripheralClockBFrequency;
87                     break;
88                 default:
89                     break;
90                 }
91             }
92         }
93 
94         public GPIO IRQ { get; }
95         public GPIO CompareMatchA => channels[0].IRQ;
96         public GPIO CompareMatchB => channels[1].IRQ;
97 
DefineRegisters()98         private void DefineRegisters()
99         {
100             IProvidesRegisterCollection<WordRegisterCollection> thisWithWordRegisters = this;
101             IProvidesRegisterCollection<ByteRegisterCollection> thisWithByteRegisters = this;
102 
103             Registers.Counter.Define(thisWithWordRegisters, 0xFFFF)
104                 .WithValueField(0, 16, name: "Counter And Reload",
105                     valueProviderCallback: _ => Value,
106                     writeCallback: (_, value) => Limit = (ushort)value
107                 )
108             ;
109 
110             Registers.CompareMatchA.Define(thisWithWordRegisters, 0xFFFF)
111                 .WithValueField(0, 16, name: "Compare Match A",
112                     valueProviderCallback: _ => channels[0].CompareValue,
113                     writeCallback: (_, value) => channels[0].CompareValue = (ushort)value
114                 )
115             ;
116 
117             Registers.CompareMatchB.Define(thisWithWordRegisters, 0xFFFF)
118                 .WithValueField(0, 16, name: "Compare Match B",
119                     valueProviderCallback: _ => channels[1].CompareValue,
120                     writeCallback: (_, value) => channels[1].CompareValue = (ushort)value
121                 )
122             ;
123 
124             Registers.Control.Define(thisWithByteRegisters)
125                 .WithFlag(0, name: "Count Start (TSTART)",
126                     valueProviderCallback: _ => Enabled,
127                     writeCallback: (_, value) =>
128                     {
129                         if(value && !Enabled)
130                         {
131                             Value = Limit;
132                         }
133                         Enabled = value;
134                     }
135                 )
136                 .WithFlag(1, FieldMode.Read, name: "Count Status Flag (TCSTF)",
137                     valueProviderCallback: _ => Enabled
138                 )
139                 .WithFlag(2, FieldMode.Write, name: "Count Forces Stop (TSTOP)",
140                     writeCallback: (_, value) =>
141                     {
142                         if(value)
143                         {
144                             Enabled = false;
145                             Value = ushort.MaxValue;
146                         }
147                     }
148                 )
149                 .WithReservedBits(3, 1)
150                 .WithTaggedFlag("Active Edge Judgment Flag (TEDGF)", 4)
151                 .WithFlag(5, out underflowFlag, FieldMode.Read | FieldMode.WriteZeroToClear, name: "Underflow Flag (TUNDF)")
152                 .WithFlag(6, FieldMode.Read | FieldMode.WriteZeroToClear, name: "Compare Match A Flag (TCMAF)",
153                     valueProviderCallback: _ => channels[0].MatchFlag,
154                     writeCallback: (_, value) => channels[0].MatchFlag &= value
155                 )
156                 .WithFlag(7, FieldMode.Read | FieldMode.WriteZeroToClear, name: "Compare Match B Flag (TCMBF)",
157                     valueProviderCallback: _ => channels[1].MatchFlag,
158                     writeCallback: (_, value) => channels[1].MatchFlag &= value
159                 )
160             ;
161 
162             Registers.Mode1.Define(thisWithByteRegisters)
163                 .WithEnumField<ByteRegister, OperatingMode>(0, 3, out operatingMode, name: "Operating Mode (TMOD)",
164                     writeCallback: (_, __) =>
165                     {
166                         switch(operatingMode.Value)
167                         {
168                         case OperatingMode.Timer:
169                         case OperatingMode.PulseOutput:
170                             Limit = ushort.MaxValue;
171                             break;
172                         case OperatingMode.EventCounter:
173                         case OperatingMode.PuleWidthMeasurement:
174                         case OperatingMode.PulsePeriodMeasurement:
175                             this.Log(LogLevel.Error, "Unimplemented operating mode ({0}). Ignoring write.", operatingMode.Value);
176                             break;
177                         default:
178                             this.Log(LogLevel.Error, "Illegal operating mode (0x{0:X}). Ignoring write.", operatingMode.Value);
179                             break;
180                         }
181                     }
182                 )
183                 .WithTaggedFlag("Edge Polarity (TEDGPL)", 3)
184                 .WithEnumField<ByteRegister, CountSource>(4, 3, out countSource, name: "Count Source (TCK)",
185                     writeCallback: (previousValue, _) =>
186                     {
187                         switch(countSource.Value)
188                         {
189                         case CountSource.PeripheralClockB_Div1:
190                             Frequency = peripheralClockBFrequency;
191                             Divider = 1;
192                             break;
193                         case CountSource.PeripheralClockB_Div8:
194                             Frequency = peripheralClockBFrequency;
195                             Divider = 8;
196                             break;
197                         case CountSource.PeripheralClockB_Div2:
198                             Frequency = peripheralClockBFrequency;
199                             Divider = 2;
200                             break;
201                         case CountSource.LowSpeedOnChipOscillator:
202                             Frequency = lowSpeedOnChipOscillatorFrequency;
203                             Divider = 1 << (int)divider.Value;
204                             break;
205                         case CountSource.UnderflowEventFromAGT:
206                             this.Log(LogLevel.Error, "Unimplemented count source selected ({0}). Ignoring write.", countSource.Value);
207                             countSource.Value = previousValue;
208                             break;
209                         case CountSource.SubClockOscillator:
210                             Frequency = subClockOscillatorFrequency;
211                             Divider = 1 << (int)divider.Value;
212                             break;
213                         default:
214                             this.Log(LogLevel.Error, "Illegal count source selected (0x{0:X}). Ignoring write.", countSource.Value);
215                             countSource.Value = previousValue;
216                             break;
217                         }
218                     }
219                 )
220                 .WithReservedBits(7, 1)
221             ;
222 
223             Registers.Mode2.Define(thisWithByteRegisters)
224                 .WithValueField(0, 3, out divider, name: "Source Clock Frequency Division Ratio (CKS)",
225                     writeCallback: (_, __) =>
226                     {
227                         switch(countSource.Value)
228                         {
229                         case CountSource.LowSpeedOnChipOscillator:
230                         case CountSource.SubClockOscillator:
231                             Divider = 1 << (int)divider.Value;
232                             break;
233                         default:
234                             return;
235                         }
236                     }
237                 )
238                 .WithReservedBits(3, 4)
239                 .WithTaggedFlag("Low Power Mode (LPM)", 7)
240             ;
241 
242             Registers.IOControl.Define(thisWithByteRegisters)
243                 .WithTaggedFlag("I/O Polarity Switch (TEDGSEL)", 0)
244                 .WithReservedBits(1, 1)
245                 .WithTaggedFlag("AGTOn pin Output Enable (TOE)", 2)
246                 .WithReservedBits(3, 1)
247                 .WithTag("Input Filter (TIPF)", 4, 2)
248                 .WithTag("Count Control (TIOGT)", 6, 2)
249             ;
250 
251             Registers.EventPinSelect.Define(thisWithByteRegisters)
252                 .WithReservedBits(0, 2)
253                 .WithTaggedFlag("AGTEEn Polarity Selection (EEPS)", 2)
254                 .WithReservedBits(3, 5)
255             ;
256 
257             Registers.CompareMatchFunctionSelect.Define(thisWithByteRegisters)
258                 .WithFlag(0, name: "Compare Match A Register Enable (TCMEA)",
259                     valueProviderCallback: _ => channels[0].CompareMatchEnabled,
260                     writeCallback: (_, value) => channels[0].CompareMatchEnabled = value
261                 )
262                 .WithTaggedFlag("AGTOAn Pin Output Enable (TOEA)", 1)
263                 .WithTaggedFlag("AGTOAn Pin Polarity Select (TOPOLA)", 2)
264                 .WithReservedBits(3, 1)
265                 .WithFlag(4, name: "Compare Match B Register Enable (TCMEB)",
266                     valueProviderCallback: _ => channels[1].CompareMatchEnabled,
267                     writeCallback: (_, value) => channels[1].CompareMatchEnabled = value
268                 )
269                 .WithTaggedFlag("AGTOBn Pin Output Enable (TOEB)", 5)
270                 .WithTaggedFlag("AGTOBn Pin Polarity Select (TOPOLB)", 6)
271                 .WithReservedBits(7, 1)
272             ;
273 
274             Registers.PinSelect.Define(thisWithByteRegisters)
275                 .WithTag("AGTIOn Pin Select (SEL)", 0, 2)
276                 .WithReservedBits(2, 2)
277                 .WithTaggedFlag("AGTIOn Pin Input Enable (TIES)", 4)
278                 .WithReservedBits(5, 3)
279             ;
280         }
281 
HandleLimitReached()282         private void HandleLimitReached()
283         {
284             foreach(var channel in channels)
285             {
286                 channel.Restart();
287             }
288             underflowFlag.Value = true;
289             IRQ.Blink();
290             this.Log(LogLevel.Debug, "IRQ blinked");
291         }
292 
TrySyncTime()293         private bool TrySyncTime()
294         {
295             if(machine.SystemBus.TryGetCurrentCPU(out var cpu))
296             {
297                 cpu.SyncTime();
298                 return true;
299             }
300             return false;
301         }
302 
303         private ushort Limit
304         {
305             get => (ushort)timer.Limit;
306             set
307             {
308                 timer.Limit = value;
309                 foreach(var channel in channels)
310                 {
311                     channel.Limit = value;
312                 }
313             }
314         }
315 
316         private ushort Value
317         {
318             get
319             {
320                 TrySyncTime();
321                 return (ushort)timer.Value;
322             }
323             set
324             {
325                 timer.Value = value;
326                 foreach(var channel in channels)
327                 {
328                     channel.Value = value;
329                 }
330             }
331         }
332 
333         private bool Enabled
334         {
335             get => timer.Enabled;
336             set
337             {
338                 timer.Enabled = value;
339                 foreach(var channel in channels)
340                 {
341                     channel.Enabled = value;
342                 }
343             }
344         }
345 
346         private long Frequency
347         {
348             set
349             {
350                 timer.Frequency = value;
351                 foreach(var channel in channels)
352                 {
353                     channel.Frequency = value;
354                 }
355             }
356         }
357 
358         private int Divider
359         {
360             set
361             {
362                 timer.Divider = value;
363                 foreach(var channel in channels)
364                 {
365                     channel.Divider = value;
366                 }
367             }
368         }
369 
370         private ByteRegisterCollection ByteRegisterCollection { get; }
371         private WordRegisterCollection WordRegisterCollection { get; }
372 
373         private IFlagRegisterField underflowFlag;
374         private IEnumRegisterField<OperatingMode> operatingMode;
375         private IEnumRegisterField<CountSource> countSource;
376         private IValueRegisterField divider;
377 
378         private long peripheralClockBFrequency;
379 
380         private readonly long lowSpeedOnChipOscillatorFrequency;
381         private readonly long subClockOscillatorFrequency;
382 
383         private readonly IMachine machine;
384         private readonly LimitTimer timer;
385         private readonly CompareChannel[] channels;
386 
387         public enum Registers
388         {
389             Counter                     = 0x00,
390             CompareMatchA               = 0x02,
391             CompareMatchB               = 0x04,
392             Control                     = 0x08,
393             Mode1                       = 0x09,
394             Mode2                       = 0x0A,
395             IOControl                   = 0x0C,
396             EventPinSelect              = 0x0D,
397             CompareMatchFunctionSelect  = 0x0E,
398             PinSelect                   = 0x0F,
399         }
400 
401         private enum OperatingMode
402         {
403             Timer = 0b000,
404             PulseOutput = 0b001,
405             EventCounter = 0b010,
406             PuleWidthMeasurement = 0b011,
407             PulsePeriodMeasurement = 0b100,
408         }
409 
410         private enum CountSource
411         {
412             PeripheralClockB_Div1 = 0b000,
413             PeripheralClockB_Div8 = 0b001,
414             PeripheralClockB_Div2 = 0b011,
415             LowSpeedOnChipOscillator = 0b100,
416             UnderflowEventFromAGT = 0b101,
417             SubClockOscillator = 0b111,
418         }
419 
420         private class CompareChannel
421         {
CompareChannel(IMachine machine, long frequency, IPeripheral parent, string localName)422             public CompareChannel(IMachine machine, long frequency, IPeripheral parent, string localName)
423             {
424                 this.parent = parent;
425                 this.name = localName;
426                 IRQ = new GPIO();
427                 innerTimer = new LimitTimer(machine.ClockSource, frequency, parent, localName, ushort.MaxValue, workMode: WorkMode.OneShot, eventEnabled: true);
428                 innerTimer.LimitReached += () =>
429                 {
430                     MatchFlag = true;
431                     Running = false;
432                     IRQ.Blink();
433                     parent.Log(LogLevel.Debug, "{0}.IRQ blinked ", name);
434                 };
435             }
436 
Reset()437             public void Reset()
438             {
439                 innerTimer.Reset();
440                 compareMatchEnabled = false;
441                 enabled = false;
442                 running = false;
443                 compareValue = ushort.MaxValue;
444                 Limit = ushort.MaxValue;
445             }
446 
Restart()447             public void Restart()
448             {
449                 Running = Limit < CompareValue;
450                 if(!Running)
451                 {
452                     return;
453                 }
454                 innerTimer.Limit = (ulong)(Limit - CompareValue);
455                 innerTimer.ResetValue();
456             }
457 
458             public GPIO IRQ { get; }
459 
460             public ushort CompareValue
461             {
462                 get => compareValue;
463                 set
464                 {
465                     var counterValue = Value;
466                     compareValue = value;
467                     if(Limit < CompareValue)
468                     {
469                         Running = false;
470                         return;
471                     }
472                     innerTimer.Limit = (ulong)(Limit - CompareValue);
473                     if(CompareValue > counterValue)
474                     {
475                         Running = false;
476                         return;
477                     }
478                     Value = counterValue;
479                     Running = true;
480                 }
481             }
482 
483             public ushort Limit
484             {
485                 private get => limit;
486                 set
487                 {
488                     limit = value;
489                     if(Limit < CompareValue)
490                     {
491                         return;
492                     }
493                     innerTimer.Limit = (ulong)(Limit - CompareValue);
494                 }
495             }
496 
497             public ushort Value
498             {
499                 private get => (ushort)((ushort)innerTimer.Value + CompareValue);
500                 set
501                 {
502                     Running = value >= CompareValue;
503                     if(!Running)
504                     {
505                         return;
506                     }
507                     innerTimer.Value = (ulong)(value - CompareValue);
508                 }
509             }
510 
511             public bool CompareMatchEnabled
512             {
513                 get => compareMatchEnabled;
514                 set
515                 {
516                     compareMatchEnabled = value;
517                     Refresh();
518                 }
519             }
520 
521             public bool Enabled
522             {
523                 private get => enabled;
524                 set
525                 {
526                     enabled = value;
527                     Refresh();
528                 }
529             }
530 
531             public long Frequency
532             {
533                 set => innerTimer.Frequency = value;
534             }
535 
536             public int Divider
537             {
538                 set => innerTimer.Divider = value;
539             }
540 
541             public bool MatchFlag { get; set; }
542 
Refresh()543             private void Refresh()
544             {
545                 innerTimer.Enabled = Enabled && Running && CompareMatchEnabled;
546             }
547 
548             private bool Running
549             {
550                 get => running;
551                 set
552                 {
553                     running = value;
554                     Refresh();
555                 }
556             }
557 
558             private ushort compareValue;
559             private ushort limit;
560             private bool compareMatchEnabled;
561             private bool enabled;
562             private bool running;
563 
564             private readonly string name;
565 
566             private readonly IPeripheral parent;
567             private readonly LimitTimer innerTimer;
568         }
569     }
570 }
571