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.Linq;
9 using System.Collections.Generic;
10 using System.Collections.ObjectModel;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 using Antmicro.Renode.Core.Structure.Registers;
14 using Antmicro.Renode.Core;
15 using Antmicro.Renode.Time;
16 using Antmicro.Renode.Utilities;
17 using Antmicro.Renode.Utilities.Collections;
18 
19 using TimeDirection = Antmicro.Renode.Time.Direction;
20 
21 namespace Antmicro.Renode.Peripherals.Timers
22 {
23     public class RenesasRA_GPT : IDoubleWordPeripheral, INumberedGPIOOutput, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize
24     {
RenesasRA_GPT(IMachine machine, int numberOf32BitChannels, int numberOf16BitChannels, long commonRegistersOffset, long peripheralClockDFrequency)25         public RenesasRA_GPT(IMachine machine, int numberOf32BitChannels, int numberOf16BitChannels, long commonRegistersOffset,
26             long peripheralClockDFrequency)
27         {
28             this.peripheralClockDFrequency = peripheralClockDFrequency;
29             this.machine = machine;
30 
31             this.numberOf32BitChannels = numberOf32BitChannels;
32             this.numberOf16BitChannels = numberOf16BitChannels;
33             this.channels = new GPTChannel[TotalChannels];
34 
35             Size = commonRegistersOffset + 0x100;
36             RegistersCollection = new DoubleWordRegisterCollection(this, BuildRegisterMap(commonRegistersOffset));
37 
38             Connections = new ReadOnlyDictionary<int, IGPIO>(
39                 channels
40                     .SelectMany(channel => channel.IRQ)
41                     .Select((x, i) => new { Key = i, Value = (IGPIO)x })
42                     .ToDictionary(x => x.Key, x => x.Value)
43             );
44 
45             Reset();
46         }
47 
Reset()48         public void Reset()
49         {
50             RegistersCollection.Reset();
51         }
52 
ReadDoubleWord(long offset)53         public uint ReadDoubleWord(long offset)
54         {
55             return RegistersCollection.Read(offset);
56         }
57 
WriteDoubleWord(long offset, uint value)58         public void WriteDoubleWord(long offset, uint value)
59         {
60             RegistersCollection.Write(offset, value);
61         }
62 
63         public long Size { get; }
64         public DoubleWordRegisterCollection RegistersCollection { get; }
65 
66         // IRQs are bundled in 9 signals per channel in the following order:
67         // 0: CCMPA - ompare match / input capture A
68         // 1: CCMPB - compare match / input capture B
69         // 2: CMPC - compare match C
70         // 3: CMPD - compare match D
71         // 4: CMPE - compare match E
72         // 5: CMPF - compare match F
73         // 6: OVF - overflow
74         // 7: UDF - underflow
75         // 8: PC - count stop
76         public IReadOnlyDictionary<int, IGPIO> Connections { get; }
77 
BuildRegisterMap(long commonRegistersOffset)78         private Dictionary<long, DoubleWordRegister> BuildRegisterMap(long commonRegistersOffset)
79         {
80             var registerMap = new Dictionary<long, DoubleWordRegister>
81             {
82                 {commonRegistersOffset + (long)CommonRegisters.OutputPhaseSwitchingControl, new DoubleWordRegister(this)
83                     .WithFlag(0, name: "UF (Input Phase Soft Setting)")
84                     .WithFlag(1, name: "VF (Input Phase Soft Setting)")
85                     .WithFlag(2, name: "WF (Input Phase Soft Setting)")
86                     .WithReservedBits(3, 1)
87                     .WithFlag(4, name: "U (Input U-Phase Monitor)")
88                     .WithFlag(5, name: "V (Input V-Phase Monitor)")
89                     .WithFlag(6, name: "W (Input W-Phase Monitor)")
90                     .WithReservedBits(7, 1)
91                     .WithFlag(8, name: "EN (Output Phase Enable)")
92                     .WithReservedBits(9, 7)
93                     .WithFlag(16, name: "FB (External Feedback Signal Enable)")
94                     .WithFlag(17, name: "P (Positive-Phase Output (P) Control)")
95                     .WithFlag(18, name: "N (Negative-Phase Output (N) Control)")
96                     .WithFlag(19, name: "INV (Output Phase Invert Control)")
97                     .WithFlag(20, name: "RV (Output Phase Rotation Direction Reversal Control)")
98                     .WithFlag(21, name: "ALIGN (Input Phase Alignment)")
99                     .WithReservedBits(22, 2)
100                     .WithValueField(24, 2, name: "GRP (Output Disabled Source Selection)")
101                     .WithFlag(26, name: "GODF (Group Output Disable Function)")
102                     .WithReservedBits(27, 2)
103                     .WithFlag(29, name: "NFEN (External Input Noise Filter Enable)")
104                     .WithValueField(30, 2, name: "NFCS (External Input Noise Filter Clock Selection)")
105                 },
106             };
107 
108             for(long i = 0; i < numberOf32BitChannels; ++i)
109             {
110                 var channel = new GPTChannel(this, i, 32);
111                 channels[i] = channel;
112                 registerMap = registerMap
113                     .Concat(BuildChannelRegisterMap(0x100 * i, channel))
114                     .ToDictionary(x => x.Key, x => x.Value);
115             }
116 
117             for(long i = 0; i < numberOf16BitChannels; ++i)
118             {
119                 // 16-bit channels are numbered after 32-bit channels,
120                 // i.e. if 32-bit channels are 0 to 7 then 16-bit are 8 to 13.
121                 var channelNumber = numberOf32BitChannels + i;
122                 var channel = new GPTChannel(this, channelNumber, 16);
123                 channels[channelNumber] = channel;
124                 registerMap = registerMap
125                     .Concat(BuildChannelRegisterMap(0x100 * channelNumber, channel))
126                     .ToDictionary(x => x.Key, x => x.Value);
127             }
128 
129             return registerMap;
130         }
131 
BuildChannelRegisterMap(long channelOffset, GPTChannel channel)132         private Dictionary<long, DoubleWordRegister> BuildChannelRegisterMap(long channelOffset, GPTChannel channel)
133         {
134             var registerMap = new Dictionary<long, DoubleWordRegister>
135             {
136                 {channelOffset + (long)ChannelRegisters.WriteProtection, new DoubleWordRegister(this)
137                     .WithFlag(0, name: "WP (Register Write Disable)")
138                     .WithFlag(1, name: "STRWP (CSTRT Bit Write Disable)")
139                     .WithFlag(2, name: "STPWP (CSTOP Bit Write Disable)")
140                     .WithFlag(3, name: "CLRWP (CCLR Bit Write Disable)")
141                     .WithFlag(4, name: "CMNWP (Common Register Write Disable)")
142                     .WithReservedBits(5, 3)
143                     .WithValueField(8, 8, name: "PRKEY (GTWP Key Code)")
144                     .WithReservedBits(16, 16)
145                 },
146                 {channelOffset + (long)ChannelRegisters.SoftwareStart, new DoubleWordRegister(this)
147                     .WithFlags(0, TotalChannels, name: "CSTRT",
148                         valueProviderCallback: (i, _) => channels[i].Enable,
149                         writeCallback: (i, _, value) =>
150                         {
151                             if(value)
152                             {
153                                 channels[i].Enable = true;
154                             }
155                         }
156                     )
157                     .WithReservedBits(TotalChannels, 32 - TotalChannels)
158                 },
159                 {channelOffset + (long)ChannelRegisters.SoftwareStop, new DoubleWordRegister(this)
160                     .WithFlags(0, TotalChannels, name: "CSTOP",
161                         valueProviderCallback: (i, _) => !channels[i].Enable,
162                         writeCallback: (i, _, value) =>
163                         {
164                             if(value)
165                             {
166                                 channels[i].Enable = false;
167                             }
168                         }
169                     )
170                     .WithReservedBits(TotalChannels, 32 - TotalChannels)
171                 },
172                 {channelOffset + (long)ChannelRegisters.SoftwareClear, new DoubleWordRegister(this)
173                     .WithFlags(0, 14, name: "CCLR")
174                     .WithReservedBits(14, 18)
175                 },
176                 {channelOffset + (long)ChannelRegisters.StartSourceSelect, DefineSourceSelectRegister("Start", 'S')},
177                 {channelOffset + (long)ChannelRegisters.StopSourceSelect, DefineSourceSelectRegister("Stop", 'P')},
178                 {channelOffset + (long)ChannelRegisters.ClearSourceSelect, DefineSourceSelectRegister("Clear", 'C')},
179                 {channelOffset + (long)ChannelRegisters.UpCountSourceSelect, DefineSourceSelectRegister("Count Up", 'U', false)},
180                 {channelOffset + (long)ChannelRegisters.DownCountSourceSelect, DefineSourceSelectRegister("Count Down", 'D', false)},
181                 {channelOffset + (long)ChannelRegisters.InputCaptureSourceSelectA, DefineSourceSelectRegister("Input Capture", 'A', false, "GTCCRA")},
182                 {channelOffset + (long)ChannelRegisters.InputCaptureSourceSelectB, DefineSourceSelectRegister("Input Capture", 'B', false, "GTCCRB")},
183                 {channelOffset + (long)ChannelRegisters.TimerControl, new DoubleWordRegister(this)
184                     .WithFlag(0, name: "CST (Count Start)")
185                     .WithReservedBits(1, 15)
186                     .WithEnumField<DoubleWordRegister, Mode>(16, 3, name: "MD (Mode Select)",
187                         valueProviderCallback: _ => channel.Mode,
188                         writeCallback: (_, value) => channel.Mode = value
189                     )
190                     .WithReservedBits(19, 4)
191                     .WithValueField(23, 4, name: "TPCS (Timer Prescaler Select)")
192                     .WithReservedBits(27, 5)
193                 },
194                 {channelOffset + (long)ChannelRegisters.CountDirectionAndDutySetting, new DoubleWordRegister(this)
195                     .WithEnumField<DoubleWordRegister, Direction>(0, 1, name: "UD (Count Direction Setting)",
196                         valueProviderCallback: _ => channel.Direction,
197                         writeCallback: (_, value) => channel.Direction = value
198                     )
199                     .WithFlag(1, name: "UDF (Forcible Count Direction Setting)")
200                     .WithReservedBits(2, 14)
201                     .WithValueField(16, 2, name: "OADTY (GTIOCnA Output Duty Setting)")
202                     .WithFlag(18, name: "OADTYF (Forcible GTIOCnA Output Duty Setting)")
203                     .WithFlag(19, name: "OADTYR (GTIOCnA Output Value Selecting)")
204                     .WithReservedBits(20, 4)
205                     .WithValueField(24, 2, name: "OBDTY (GTIOCnB Output Duty Setting)")
206                     .WithFlag(26, name: "OBDTYF (Forcible GTIOCnB Output Duty Setting)")
207                     .WithFlag(27, name: "OBDTYR (GTIOCnB Output Value Selecting)")
208                     .WithReservedBits(28, 4)
209                 },
210                 {channelOffset + (long)ChannelRegisters.IOControl, new DoubleWordRegister(this)
211                     .WithValueField(0, 5, name: "GTIOA (GTIOCnA Pin Function Select)")
212                     .WithReservedBits(5, 1)
213                     .WithFlag(6, name: "OADFLT (GTIOCnA Pin Output Value Setting at the Count Stop)")
214                     .WithFlag(7, name: "OAHLD (GTIOCnA Pin Output Value Setting at the Start/Stop Count)")
215                     .WithFlag(8, name: "OAE (GTIOCnA Pin Output Enable)")
216                     .WithFlag(9, name: "OADF (GTIOCnA Pin Disaable Value Setting)")
217                     .WithReservedBits(11, 2)
218                     .WithFlag(13, name: "NFAEN (Noise Filter A Enable)")
219                     .WithValueField(14, 2, name: "NFCSA (Noise Filter A Sampling Clock Select)")
220                     .WithValueField(16, 5, name: "GTIOB (GTIOCnB Pin Function Select)")
221                     .WithReservedBits(21, 1)
222                     .WithFlag(22, name: "OBDFLT (GTIOCnB Pin Output Value Setting at the Count Stop)")
223                     .WithFlag(23, name: "OBHLD (GTIOCnB Pin Output Value Setting at the Start/Stop Count)")
224                     .WithFlag(24, name: "OBE (GTIOCnB Pin Output Enable)")
225                     .WithFlag(25, name: "OBDF (GTIOCnB Pin Disaable Value Setting)")
226                     .WithReservedBits(27, 2)
227                     .WithFlag(29, name: "NFBEN (Noise Filter B Enable)")
228                     .WithValueField(30, 2, name: "NFCSB (Noise Filter B Sampling Clock Select)")
229                 },
230                 {channelOffset + (long)ChannelRegisters.InterruptOutputSetting, new DoubleWordRegister(this)
231                     .WithReservedBits(0, 16)
232                     .WithFlag(16, name: "ADTRAUEN (GTADTRA Register Compare Match (Up-Counting) A/D Conversion Start)")
233                     .WithFlag(17, name: "ADTRADEN (GTADTRA Register Compare Match (Down-Counting) A/D Conversion Start)")
234                     .WithFlag(18, name: "ADTRBUEN (GTADTRB Register Compare Match (Up-Counting) A/D Conversion Start)")
235                     .WithFlag(19, name: "ADTRBDEN (GTADTRB Register Compare Match (Down-Counting) A/D Conversion Start)")
236                     .WithReservedBits(20, 4)
237                     .WithValueField(24, 2, name: "GRP (Output Disable Source Select)")
238                     .WithReservedBits(26, 3)
239                     .WithFlag(29, name: "GRPABH (Same Time Output Level High Disable Request Enable)")
240                     .WithFlag(30, name: "GRPABL (Same Time Output Level Low Disable Request Enable)")
241                     .WithReservedBits(31, 1)
242                 },
243                 // Status register defined later
244                 {channelOffset + (long)ChannelRegisters.BufferEnable, new DoubleWordRegister(this)
245                     .WithFlag(0, name: "BD0 (GTCCR Buffer Operation Disable)")
246                     .WithFlag(1, name: "BD1 (GTPR Buffer Operation Disable)")
247                     .WithFlag(2, name: "BD2 (GTAADTRA/GTADTRB Registers Buffer Operation Disable)")
248                     .WithReservedBits(3, 13)
249                     .WithValueField(16, 2, name: "CCRA (GTCCRA Buffer Operation)")
250                     .WithValueField(18, 2, name: "CCRB (GTCCRB Buffer Operation)")
251                     .WithValueField(20, 2, name: "PR (GTPR Buffer Operation)")
252                     .WithFlag(22, name: "CCRSWT (GTCCRA and GTCCRB Forcible Buffer Operation)")
253                     .WithReservedBits(23, 1)
254                     .WithValueField(24, 2, name: "ADTTA (GTADTRA Register Buffer Transfer Timing Select)")
255                     .WithFlag(26, name: "ADTDA (GTADTRA Register Double Buffer Operation)")
256                     .WithReservedBits(27, 1)
257                     .WithValueField(28, 2, name: "ADTTB (GTADTRB Register Buffer Transfer Timing Select)")
258                     .WithFlag(30, name: "ADTDB (GTADTRB Register Double Buffer Operation)")
259                     .WithReservedBits(31, 1)
260                 },
261                 {channelOffset + (long)ChannelRegisters.Counter, new DoubleWordRegister(this)
262                     .WithValueField(0, 32, name: "GTCNT",
263                         valueProviderCallback: _ => channel.Value,
264                         writeCallback: (_, value) => channel.Value = value
265                     )
266                 },
267                 // Compare Capture register A..F defined later
268                 {channelOffset + (long)ChannelRegisters.CycleSetting, new DoubleWordRegister(this)
269                     .WithValueField(0, 32, name: "GTPR",
270                         valueProviderCallback: _ => channel.Cycle,
271                         writeCallback: (_, value) => channel.Cycle = value
272                     )
273                 },
274                 {channelOffset + (long)ChannelRegisters.CycleSettingBuffer, new DoubleWordRegister(this)
275                     .WithValueField(0, 32, name: "GTPBR")
276                 },
277                 {channelOffset + (long)ChannelRegisters.ADConversionStartA, new DoubleWordRegister(this)
278                     .WithValueField(0, 32, name: "GTADTRA")
279                 },
280                 {channelOffset + (long)ChannelRegisters.ADConversionStartB, new DoubleWordRegister(this)
281                     .WithValueField(0, 32, name: "GTADTRB")
282                 },
283                 {channelOffset + (long)ChannelRegisters.ADConversionStartBufferA, new DoubleWordRegister(this)
284                     .WithValueField(0, 32, name: "GTADTBRA")
285                 },
286                 {channelOffset + (long)ChannelRegisters.ADConversionStartBufferB, new DoubleWordRegister(this)
287                     .WithValueField(0, 32, name: "GTADTBRB")
288                 },
289                 {channelOffset + (long)ChannelRegisters.ADConversionStartDoubleBufferA, new DoubleWordRegister(this)
290                     .WithValueField(0, 32, name: "GTADTDBRA")
291                 },
292                 {channelOffset + (long)ChannelRegisters.ADConversionStartDoubleBufferB, new DoubleWordRegister(this)
293                     .WithValueField(0, 32, name: "GTADTDBRB")
294                 },
295                 {channelOffset + (long)ChannelRegisters.DeadTimeControl, new DoubleWordRegister(this)
296                     .WithFlag(0, name: "TDE (Negative-Phase Waveform Setting)")
297                     .WithReservedBits(1, 31)
298                 },
299                 {channelOffset + (long)ChannelRegisters.DeadTimeValue, new DoubleWordRegister(this)
300                     .WithValueField(0, 32, name: "GTDVU")
301                 },
302                 {channelOffset + (long)ChannelRegisters.ADConversionStartSignalMonitoring, new DoubleWordRegister(this)
303                     .WithValueField(0, 2, name: "ADSMS0 (A/D Conversion Start Request Signal Monitor 0 Selection)")
304                     .WithReservedBits(2, 6)
305                     .WithFlag(8, name: "ADSMEN0 (A/D Conversion Start Request Signal Monitor 0 Output Enabling)")
306                     .WithReservedBits(9, 7)
307                     .WithValueField(16, 2, name: "ADSMS1 (A/D Conversion Start Request Signal Monitor 1 Selection)")
308                     .WithReservedBits(18, 6)
309                     .WithFlag(24, name: "ADSMEN1 (A/D Conversion Start Request Signal Monitor 1 Output Enabling)")
310                     .WithReservedBits(25, 7)
311                 },
312                 {channelOffset + (long)ChannelRegisters.InterChannelSetting, new DoubleWordRegister(this)
313                     .WithValueField(0, 3, name: "ICLFA (GTIOCnA Output Logical Operation Function Select)")
314                     .WithReservedBits(3, 1)
315                     .WithValueField(4, 6, name: "ICLFSELC (Inter Channel Signaal C Select)")
316                     .WithReservedBits(10, 6)
317                     .WithValueField(16, 3, name: "ICLFB (GTIOCnB Output Logical Operation Function Select)")
318                     .WithReservedBits(19, 1)
319                     .WithValueField(20, 6, name: "ICLFSELD (Inter Channel Signal D Select)")
320                     .WithReservedBits(26, 6)
321                 },
322                 {channelOffset + (long)ChannelRegisters.PeriodCount, new DoubleWordRegister(this)
323                     .WithFlag(0, name: "PCEN (Period Count Function Enable)")
324                     .WithReservedBits(1, 7)
325                     .WithFlag(8, name: "ASTP (Automatic Stop Function Enable)")
326                     .WithReservedBits(9, 7)
327                     .WithValueField(16, 12, name: "PCNT (Period Counter)")
328                     .WithReservedBits(28, 4)
329                 },
330                 {channelOffset + (long)ChannelRegisters.OperationEnableBitChannelSelect, new DoubleWordRegister(this)
331                     .WithFlags(0, 14, name: "SECSEL")
332                     .WithReservedBits(14, 18)
333                 },
334                 {channelOffset + (long)ChannelRegisters.OperationEnableBit, new DoubleWordRegister(this)
335                     .WithFlag(0, name: "SBDCE (GTCCR Register Buffer Operation Simultaneous Enable)")
336                     .WithFlag(1, name: "SBDPE (GTPR Register Buffer Operation Simultaneous Enable)")
337                     .WithFlag(2, name: "SBDAE (GTADTR Register Buffer Operation Simultaneous Enable)")
338                     .WithReservedBits(3, 5)
339                     .WithFlag(8, name: "SBDCD (GTCCR Register Buffer Operation Simultaneous Disable)")
340                     .WithFlag(9, name: "SBDPD (GTPR Register Buffer Operation Simultaneous Disable)")
341                     .WithFlag(10, name: "SBDAD (GTADTR Register Buffer Operation Simultaneous Disable)")
342                     .WithReservedBits(17, 7)
343                     .WithReservedBits(25, 7)
344                 },
345             };
346 
347             var statusRegister = new DoubleWordRegister(this);
348             foreach(var f in RangeWithLetters(0, 6))
349             {
350                 statusRegister
351                     .WithFlag(f.offset, name: $"TCF{f.c} (Input Capture/Compare Match Flag {f.c})");
352             }
353             statusRegister
354                 .WithFlag(6, FieldMode.Read | FieldMode.WriteZeroToClear, name: "TCFPO (Overflow Flag)",
355                     valueProviderCallback: _ => channel.Overflow,
356                     writeCallback: (_, __) => channel.Overflow = false
357                 )
358                 .WithFlag(7, name: "TCFPU (Underflow Flag)")
359                 .WithReservedBits(8, 7)
360                 .WithFlag(15, name: "TUCF (Count Direction Flag)")
361                 .WithFlag(16, name: "ADTRAUF (GTADTRA Register Compare Match (Up-Counting) A/D Conversion Start)")
362                 .WithFlag(17, name: "ADTRADF (GTADTRA Register Compare Match (Down-Counting) A/D Conversion Start)")
363                 .WithFlag(18, name: "ADTRBUF (GTADTRB Register Compare Match (Up-Counting) A/D Conversion Start)")
364                 .WithFlag(19, name: "ADTRBDF (GTADTRB Register Compare Match (Down-Counting) A/D Conversion Start)")
365                 .WithReservedBits(20, 4)
366                 .WithFlag(24, name: "ODF (Output Disable Flag)")
367                 .WithReservedBits(25, 4)
368                 .WithFlag(29, name: "OABHF (Same Time Output Level High Flag)")
369                 .WithFlag(30, name: "OABLF (Same Time Output Level Low Flag)")
370                 .WithFlag(31, name: "PCF (Period Count Function Finish Flag)");
371             registerMap.Add(channelOffset + (long)ChannelRegisters.Status, statusRegister);
372 
373             foreach(var r in RangeWithLetters((int)channelOffset + (int)ChannelRegisters.CompareCaptureA, 6, 4))
374             {
375                 var compareCaptureRegister = new DoubleWordRegister(this)
376                     .WithValueField(0, 32, name: $"GTCCR{r.c}");
377                 registerMap.Add(r.offset, compareCaptureRegister);
378             }
379 
380             return registerMap;
381         }
382 
DefineSourceSelectRegister(string name, char marker, bool hasClear = true, string sourceName = R)383         private DoubleWordRegister DefineSourceSelectRegister(string name, char marker, bool hasClear = true, string sourceName = "Counter")
384         {
385             var sourceSelectRegister = new DoubleWordRegister(this);
386             foreach(var f in RangeWithLetters(0, 4, 2))
387             {
388                 sourceSelectRegister
389                     .WithFlag(f.offset,     name: $"{marker}SGTRG{f.c}R (GTETRG{f.c} Pin Rising Input Source {sourceName} {name} Enable)")
390                     .WithFlag(f.offset + 1, name: $"{marker}SGTRG{f.c}F (GTETRG{f.c} Pin Falling Input Source {sourceName} {name} Enable)");
391             }
392             foreach(var f in RangeWithLetters(8, 2, 4))
393             {
394                 var other = f.c == 'A' ? 'B' : 'A';
395                 sourceSelectRegister
396                     .WithFlag(f.offset,     name: $"{marker}SC{f.c}RBL (GTIOCn{f.c} Pin Rising Input during GTIOCn{other} Value Low Source {sourceName} {name} Enable)")
397                     .WithFlag(f.offset + 1, name: $"{marker}SC{f.c}RBH (GTIOCn{f.c} Pin Rising Input during GTIOCn{other} Value High Source {sourceName} {name} Enable)")
398                     .WithFlag(f.offset + 2, name: $"{marker}SC{f.c}FBL (GTIOCn{f.c} Pin Falling Input during GTIOCn{other} Value Low Source {sourceName} {name} Enable)")
399                     .WithFlag(f.offset + 3, name: $"{marker}SC{f.c}FBH (GTIOCn{f.c} Pin Falling Input during GTIOCn{other} Value High Source {sourceName} {name} Enable)");
400             }
401             foreach(var f in RangeWithLetters(16, 8))
402             {
403                 sourceSelectRegister
404                     .WithFlag(f.offset, name: $"{marker}SELC{f.c} (ELC_GPT{f.c} Event Source {sourceName} {name} Enable)");
405             }
406             if(hasClear)
407             {
408                 sourceSelectRegister
409                     .WithReservedBits(24, 7)
410                     .WithFlag(31, name: $"C{name.ToUpper()} (Software Source {sourceName} {name} Enable)");
411             }
412             else
413             {
414                 sourceSelectRegister
415                     .WithReservedBits(24, 8);
416             }
417             return sourceSelectRegister;
418         }
419 
420         private class OffsetWithLetter
421         {
422             public int index;
423             public int offset;
424             public char c;
425 
OffsetWithLetter(int index, int offset, char c)426             public OffsetWithLetter(int index, int offset, char c)
427             {
428                 this.index = index;
429                 this.offset = offset;
430                 this.c = c;
431             }
432         }
433 
RangeWithLetters(int start, int count, int step = 1)434         private IEnumerable<OffsetWithLetter> RangeWithLetters(int start, int count, int step = 1)
435         {
436             for(int i = 0; i < count; ++i)
437             {
438                 int offset = start + i * step;
439                 var c = (char)('A' + i);
440                 yield return new OffsetWithLetter(i, offset, c);
441             }
442         }
443 
444         private int TotalChannels => numberOf32BitChannels + numberOf16BitChannels;
445 
446         private readonly IMachine machine;
447         private readonly int numberOf32BitChannels;
448         private readonly int numberOf16BitChannels;
449         private readonly long peripheralClockDFrequency;
450         private readonly GPTChannel[] channels;
451 
452         private class GPTChannel
453         {
GPTChannel(RenesasRA_GPT parent, long index, int width)454             public GPTChannel(RenesasRA_GPT parent, long index, int width)
455             {
456                 this.parent = parent;
457                 this.index = index;
458                 this.width = width;
459                 IRQ = new GPIO[InterruptCount];
460                 for(int i = 0; i < InterruptCount; ++i)
461                 {
462                     IRQ[i] = new GPIO();
463                 }
464 
465                 timer = new LimitTimer(parent.machine.ClockSource, parent.peripheralClockDFrequency, parent, $"Timer{index}",
466                     MaxLimit, direction: TimeDirection.Descending, workMode: WorkMode.Periodic, eventEnabled: true
467                 );
468                 timer.LimitReached += OnMainTimerLimitReached;
469 
470                 Reset();
471             }
472 
Reset()473             public void Reset()
474             {
475                 Overflow = false;
476                 timer.Reset();
477                 Cycle = MaxLimit;
478                 Direction = Direction.DownCounting;
479                 Mode = Mode.SawWave;
480             }
481 
482             public Mode Mode
483             {
484                 get => mode;
485                 set
486                 {
487                     if(value != Mode.SawWave)
488                     {
489                         this.parent.Log(LogLevel.Warning, "GPT{0}: Modes other than Saw Wave (default) are not supported yet. Ignoring", index);
490                         return;
491                     }
492                     mode = value;
493                 }
494             }
495 
496             public Direction Direction
497             {
498                 get => direction;
499                 set
500                 {
501                     direction = value;
502                     timer.Direction = direction == Direction.UpCounting ? TimeDirection.Ascending : TimeDirection.Descending;
503                 }
504             }
505 
506             public bool Enable
507             {
508                 get => timer.Enabled;
509                 set
510                 {
511                     timer.Enabled = value;
512                 }
513             }
514 
515             public ulong Cycle
516             {
517                 get => timer.Limit;
518                 set
519                 {
520                     if(value > MaxLimit)
521                     {
522                         this.parent.Log(LogLevel.Warning, "GPT{0}: Cycle {1} is higher than maximum limit of {2}. Truncating to {3} bits", index, value, MaxLimit, width);
523                     }
524                     timer.Limit = value & MaxLimit;
525                 }
526             }
527 
528             public ulong Value
529             {
530                 get => timer.Value;
531                 set
532                 {
533                     if(Enable)
534                     {
535                         this.parent.Log(LogLevel.Warning, "GPT{0}: Setting GTCNT while counting is still on-going. Ignoring", index);
536                         return;
537                     }
538                     if(value > MaxLimit)
539                     {
540                         this.parent.Log(LogLevel.Warning, "GPT{0}: Value {1} is higher than maximum limit of {2}. Truncating to {3} bits", index, value, MaxLimit, width);
541                     }
542                     var newValue = value & MaxLimit;
543                     if(newValue >= Cycle)
544                     {
545                         Overflow = true;
546                         IRQ[OvfInterruptIndex].Blink();
547                         timer.Reset();
548                     }
549                     else
550                     {
551                         Overflow = false;
552                         timer.Value = newValue;
553                     }
554                 }
555             }
556 
557             public GPIO[] IRQ { get; }
558 
559             public bool Overflow { get; set; }
560             public bool Underflow { get; set; }
561 
562             public const long InterruptCount = 9;
563 
OnMainTimerLimitReached()564             private void OnMainTimerLimitReached()
565             {
566                 if(direction == Direction.UpCounting)
567                 {
568                     Overflow = true;
569                     IRQ[OvfInterruptIndex].Blink();
570                 }
571                 else
572                 {
573                     Underflow = true;
574                     IRQ[UdfInterruptIndex].Blink();
575                 }
576             }
577 
578             private ulong MaxLimit => (1ul << width) - 1ul;
579 
580             private Mode mode;
581             private Direction direction;
582 
583             private readonly LimitTimer timer;
584             private readonly RenesasRA_GPT parent;
585             private readonly long index;
586             private readonly int width;
587 
588             private const long OvfInterruptIndex = 6;
589             private const long UdfInterruptIndex = 7;
590         }
591 
592         public enum Mode
593         {
594             // single buffer or double buffer possible
595             SawWave        = 0b000,
596             // fixed buffer operation
597             SawWaveOneShot = 0b001,
598             // single buffer or double buffer possible
599             // 32-bit transfer at trough
600             TriangleWave1  = 0b100,
601             // single buffer or double buffer possible
602             // 32-bit transfer at crest and trough
603             TriangleWave2  = 0b101,
604             // fixed buffer operation
605             // 64-bit transfer at trough
606             TriangleWave3  = 0b110,
607         }
608 
609         public enum Direction
610         {
611             DownCounting = 0,
612             UpCounting   = 1,
613         }
614 
615         public enum ChannelRegisters
616         {
617             WriteProtection                   = 0x00,
618             SoftwareStart                     = 0x04,
619             SoftwareStop                      = 0x08,
620             SoftwareClear                     = 0x0C,
621             StartSourceSelect                 = 0x10,
622             StopSourceSelect                  = 0x14,
623             ClearSourceSelect                 = 0x18,
624             UpCountSourceSelect               = 0x1C,
625             DownCountSourceSelect             = 0x20,
626             InputCaptureSourceSelectA         = 0x24,
627             InputCaptureSourceSelectB         = 0x28,
628             TimerControl                      = 0x2C,
629             CountDirectionAndDutySetting      = 0x30,
630             IOControl                         = 0x34,
631             InterruptOutputSetting            = 0x38,
632             Status                            = 0x3C,
633             BufferEnable                      = 0x40,
634             Counter                           = 0x48,
635             CompareCaptureA                   = 0x4C,
636             // CompareCaptureB..F             = 0x50..0x60,
637             CycleSetting                      = 0x64,
638             CycleSettingBuffer                = 0x68,
639             ADConversionStartA                = 0x70,
640             ADConversionStartB                = 0x7C,
641             ADConversionStartBufferA          = 0x74,
642             ADConversionStartBufferB          = 0x80,
643             ADConversionStartDoubleBufferA    = 0x78,
644             ADConversionStartDoubleBufferB    = 0x84,
645             DeadTimeControl                   = 0x88,
646             DeadTimeValue                     = 0x8C,
647             ADConversionStartSignalMonitoring = 0xA4,
648             InterChannelSetting               = 0xB8,
649             PeriodCount                       = 0xBC,
650             OperationEnableBitChannelSelect   = 0xD0,
651             OperationEnableBit                = 0xD4,
652         }
653 
654         public enum CommonRegisters
655         {
656             OutputPhaseSwitchingControl       = 0x00,
657         }
658     }
659 }
660