1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Time;
15 using Antmicro.Renode.Logging;
16 using Antmicro.Renode.Exceptions;
17 
18 namespace Antmicro.Renode.Peripherals.Timers
19 {
20     // This class does not implement advanced-control timers interrupts
21     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)]
22     public class STM32_Timer : LimitTimer, IDoubleWordPeripheral, IKnownSize, INumberedGPIOOutput, IPeripheralRegister<IGPIOReceiver, NumberRegistrationPoint<int>>, IPeripheralRegister<IGPIOReceiver, NullRegistrationPoint>
23     {
STM32_Timer(IMachine machine, long frequency, uint initialLimit)24         public STM32_Timer(IMachine machine, long frequency, uint initialLimit) : base(machine.ClockSource, frequency, limit: initialLimit, direction: Direction.Ascending, enabled: false, autoUpdate: false)
25         {
26             this.machine = machine;
27             sysbus = machine.GetSystemBus(this);
28             IRQ = new GPIO();
29             connections = Enumerable.Range(0, NumberOfCCChannels).ToDictionary(i => i, _ => (IGPIO)new GPIO());
30             this.initialLimit = initialLimit;
31             // If initialLimit is 0, throw an error - this is an invalid state for us, since we would not be able to infer the counter's width
32             if(initialLimit == 0)
33             {
34                 throw new ConstructionException($"{nameof(initialLimit)} has to be greater than zero");
35             }
36             // We need to ensure that the counter is at least as wide as the position of MSB in initialLimit
37             // but since we count from 0 (log_2 (1) = 0 ) - add 1
38             this.timerCounterLengthInBits = (int)Math.Floor(Math.Log(initialLimit, 2)) + 1;
39             if(this.timerCounterLengthInBits > 32)
40             {
41                 throw new ConstructionException($"Timer's width cannot be more than 32 bits - requested {this.timerCounterLengthInBits} bits (inferred from {nameof(initialLimit)})");
42             }
43 
44             LimitReached += delegate
45             {
46                 if(updateDisable.Value)
47                 {
48                     return;
49                 }
50                 Limit = autoReloadValue;
51 
52                 for(var i = 0; i < NumberOfCCChannels; ++i)
53                 {
54                     UpdateCaptureCompareTimer(i);
55                     if(!ccTimers[i].Enabled)
56                     {
57                         continue;
58                     }
59 
60                     switch(outputCompareModes[i].Value)
61                     {
62                         case OutputCompareMode.PwmMode1:
63                             Connections[i].Set();
64                             break;
65                         case OutputCompareMode.PwmMode2:
66                             Connections[i].Unset();
67                             break;
68                     }
69                 }
70 
71                 if(updateInterruptEnable.Value && repetitionsLeft == 0)
72                 {
73                     // 2 of central-aligned modes should raise IRQ only on overflow/underflow, hence it happens 2 times less often
74                     var centerAlignedUnbalancedMode = (centerAlignedMode.Value == CenterAlignedMode.CenterAligned1) || (centerAlignedMode.Value == CenterAlignedMode.CenterAligned2);
75                     this.Log(LogLevel.Noisy, "IRQ pending");
76                     updateInterruptFlag = true;
77                     repetitionsLeft = 1u + (uint)repetitionCounter.Value * (centerAlignedUnbalancedMode ? 2u : 1u);
78                     UpdateInterrupts();
79                 }
80 
81                 if(repetitionsLeft > 0)
82                 {
83                     repetitionsLeft--;
84                 }
85             };
86 
87             for(var i = 0; i < NumberOfCCChannels; ++i)
88             {
89                 var j = i;
90                 ccTimers[j] = new LimitTimer(machine.ClockSource, frequency, this, String.Format("cctimer{0}", j + 1), limit: initialLimit, eventEnabled: true, direction: Direction.Ascending, enabled: false, autoUpdate: false, workMode: WorkMode.OneShot);
91                 ccTimers[j].LimitReached += delegate
92                 {
93                     switch(outputCompareModes[j].Value)
94                     {
95                         case OutputCompareMode.SetActiveOnMatch:
96                             Connections[j].Blink(); // high pulse
97                             break;
98                         case OutputCompareMode.SetInactiveOnMatch:
99                             Connections[j].Unset();
100                             Connections[j].Set(); // low pulse
101                             break;
102                         case OutputCompareMode.ToggleOnMatch:
103                             Connections[j].Toggle();
104                             break;
105                         case OutputCompareMode.PwmMode1:
106                             Connections[j].Unset();
107                             break;
108                         case OutputCompareMode.PwmMode2:
109                             Connections[j].Set();
110                             break;
111                     }
112 
113                     if(ccInterruptEnable[j])
114                     {
115                         ccInterruptFlag[j] = true;
116                         this.Log(LogLevel.Noisy, "cctimer{0}: Compare IRQ pending", j + 1);
117                         UpdateInterrupts();
118                     }
119                 };
120             }
121 
122             var registersMap = new Dictionary<long, DoubleWordRegister>
123             {
124                 {(long)Registers.Control1, new DoubleWordRegister(this)
125                     .WithFlag(0, writeCallback: (_, val) =>
126                     {
127                         enableRequested = val;
128                         Enabled = enableRequested && autoReloadValue > 0;
129                     }, valueProviderCallback: _ => enableRequested, name: "Counter enable (CEN)")
130                     .WithFlag(1, out updateDisable, name: "Update disable (UDIS)")
131                     .WithFlag(2, out updateRequestSource, name: "Update request source (URS)")
132                     .WithFlag(3, writeCallback: (_, val) => Mode = val ? WorkMode.OneShot : WorkMode.Periodic, valueProviderCallback: _ => Mode == WorkMode.OneShot, name: "One-pulse mode (OPM)")
133                     .WithFlag(4, writeCallback: (_, val) => Direction = val ? Direction.Descending : Direction.Ascending, valueProviderCallback: _ => Direction == Direction.Descending, name: "Direction (DIR)")
134                     .WithEnumField(5, 2, out centerAlignedMode, name: "Center-aligned mode selection (CMS)")
135                     .WithFlag(7, out autoReloadPreloadEnable, name: "Auto-reload preload enable (APRE)")
136                     .WithTag("Clock Division (CKD)", 8, 2)
137                     .WithReservedBits(10, 22)
138                     .WithWriteCallback((_, __) => { UpdateCaptureCompareTimers(); UpdateInterrupts(); })
139                 },
140 
141                 {(long)Registers.Control2, new DoubleWordRegister(this)
142                     .WithTaggedFlag("CCPC", 0)
143                     .WithReservedBits(1, 1)
144                     .WithTaggedFlag("CCUS", 2)
145                     .WithTaggedFlag("CCDS", 3)
146                     .WithTag("MMS", 4, 2)
147                     .WithTaggedFlag("TI1S", 7)
148                     .WithTaggedFlag("OIS1", 8)
149                     .WithTaggedFlag("OIS1N", 9)
150                     .WithTaggedFlag("OIS2", 10)
151                     .WithTaggedFlag("OIS2N", 11)
152                     .WithTaggedFlag("OIS3", 12)
153                     .WithTaggedFlag("OIS3N", 13)
154                     .WithTaggedFlag("OIS4", 14)
155                     .WithReservedBits(15, 17)
156                 },
157 
158                 {(long)Registers.SlaveModeControl, new DoubleWordRegister(this)
159                     .WithTag("SMS", 0, 3)
160                     .WithTaggedFlag("OCCS", 3)
161                     .WithTag("TS", 4, 2)
162                     .WithTaggedFlag("MSM", 7)
163                     .WithTag("ETF", 8, 3)
164                     .WithTag("ETPS", 12, 2)
165                     .WithTaggedFlag("ECE", 14)
166                     .WithTaggedFlag("ETP", 15)
167                     .WithReservedBits(16, 16)
168                 },
169 
170                 {(long)Registers.DmaOrInterruptEnable, new DoubleWordRegister(this)
171                     .WithFlag(0, out updateInterruptEnable, name: "Update interrupt enable (UIE)")
172                     .WithFlag(1, valueProviderCallback: _ => ccInterruptEnable[0], writeCallback: (_, val) => WriteCaptureCompareInterruptEnable(0, val), name: "Capture/Compare 1 interrupt enable (CC1IE)")
173                     .WithFlag(2, valueProviderCallback: _ => ccInterruptEnable[1], writeCallback: (_, val) => WriteCaptureCompareInterruptEnable(1, val), name: "Capture/Compare 2 interrupt enable (CC2IE)")
174                     .WithFlag(3, valueProviderCallback: _ => ccInterruptEnable[2], writeCallback: (_, val) => WriteCaptureCompareInterruptEnable(2, val), name: "Capture/Compare 3 interrupt enable (CC3IE)")
175                     .WithFlag(4, valueProviderCallback: _ => ccInterruptEnable[3], writeCallback: (_, val) => WriteCaptureCompareInterruptEnable(3, val), name: "Capture/Compare 4 interrupt enable (CC4IE)")
176                     .WithReservedBits(5, 1)
177                     .WithTag("Trigger interrupt enable (TIE)", 6, 1)
178                     .WithReservedBits(7, 1)
179                     .WithTag("Update DMA request enable (UDE)", 8, 1)
180                     .WithTag("Capture/Compare 1 DMA request enable (CC1DE)", 9, 1)
181                     .WithTag("Capture/Compare 2 DMA request enable (CC2DE)", 10, 1)
182                     .WithTag("Capture/Compare 3 DMA request enable (CC3DE)", 11, 1)
183                     .WithTag("Capture/Compare 4 DMA request enable (CC4DE)", 12, 1)
184                     .WithReservedBits(13, 1)
185                     .WithTag("Trigger DMA request enable (TDE)", 14, 1)
186                     .WithReservedBits(15, 17)
187                     .WithWriteCallback((_, __) => UpdateInterrupts())
188                 },
189 
190                 {(long)Registers.Status, new DoubleWordRegister(this)
191                     .WithFlag(0, FieldMode.Read | FieldMode.WriteZeroToClear,
192                         writeCallback: (_, val) =>
193                         {
194                             if(!val)
195                             {
196                                 updateInterruptFlag = false;
197                                 this.Log(LogLevel.Noisy, "IRQ claimed");
198                             }
199                         },
200                         valueProviderCallback: (_) =>
201                         {
202                             return updateInterruptFlag;
203                         },
204                         name: "Update interrupt flag (UIF)")
205                     .WithFlag(1, FieldMode.Read | FieldMode.WriteZeroToClear, writeCallback: (_, val) => ClaimCaptureCompareInterrupt(0, val), valueProviderCallback: _ => ccInterruptFlag[0], name: "Capture/Compare 1 interrupt flag (CC1IF)")
206                     .WithFlag(2, FieldMode.Read | FieldMode.WriteZeroToClear, writeCallback: (_, val) => ClaimCaptureCompareInterrupt(1, val), valueProviderCallback: _ => ccInterruptFlag[1], name: "Capture/Compare 2 interrupt flag (CC2IF)")
207                     .WithFlag(3, FieldMode.Read | FieldMode.WriteZeroToClear, writeCallback: (_, val) => ClaimCaptureCompareInterrupt(2, val), valueProviderCallback: _ => ccInterruptFlag[2], name: "Capture/Compare 3 interrupt flag (CC3IF)")
208                     .WithFlag(4, FieldMode.Read | FieldMode.WriteZeroToClear, writeCallback: (_, val) => ClaimCaptureCompareInterrupt(3, val), valueProviderCallback: _ => ccInterruptFlag[3], name: "Capture/Compare 4 interrupt flag (CC4IF)")
209                     // Reserved fields were changed to flags to prevent from very frequent logging
210                     .WithFlag(5, name: "Reserved1")
211                     // These write callbacks are here only to prevent from very frequent logging.
212                     .WithValueField(6, 1, FieldMode.WriteZeroToClear, writeCallback: (_, __) => {}, name: "Trigger interrupt flag (TIE)")
213                     .WithFlag(7, name: "Reserved2")
214                     .WithFlag(8, name: "Reserved3")
215                     .WithValueField(9, 1, FieldMode.WriteZeroToClear, writeCallback: (_, __) => {}, name: "Capture/Compare 1 overcapture flag (CC1OF)")
216                     .WithValueField(10, 1, FieldMode.WriteZeroToClear, writeCallback: (_, __) => {}, name: "Capture/Compare 2 overcapture flag (CC2OF)")
217                     .WithValueField(11, 1, FieldMode.WriteZeroToClear, writeCallback: (_, __) => {}, name: "Capture/Compare 3 overcapture flag (CC3OF)")
218                     .WithValueField(12, 1, FieldMode.WriteZeroToClear, writeCallback: (_, __) => {}, name: "Capture/Compare 4 overcapture flag (CC4OF)")
219                     .WithValueField(13, 19, name: "Reserved4")
220                     .WithWriteCallback((_, __) => UpdateInterrupts())
221                 },
222 
223                 {(long)Registers.EventGeneration, new DoubleWordRegister(this)
224                     .WithFlag(0, FieldMode.WriteOneToClear, writeCallback: (_, val) =>
225                     {
226                         if(updateDisable.Value)
227                         {
228                             return;
229                         }
230                         if(Direction == Direction.Ascending)
231                         {
232                             Value = 0;
233                         }
234                         else if(Direction == Direction.Descending)
235                         {
236                             Value = autoReloadValue;
237                         }
238 
239                         repetitionsLeft = (uint)repetitionCounter.Value;
240 
241                         if(!updateRequestSource.Value && updateInterruptEnable.Value)
242                         {
243                             this.Log(LogLevel.Noisy, "IRQ pending");
244                             updateInterruptFlag = true;
245                         }
246                         for(var i = 0; i < NumberOfCCChannels; ++i)
247                         {
248                             if(ccTimers[i].Enabled)
249                             {
250                                 ccTimers[i].Value = Value;
251                             }
252                         }
253                     }, name: "Update generation (UG)")
254                     .WithTag("Capture/compare 1 generation (CC1G)", 1, 1)
255                     .WithTag("Capture/compare 2 generation (CC2G)", 2, 1)
256                     .WithTag("Capture/compare 3 generation (CC3G)", 3, 1)
257                     .WithTag("Capture/compare 4 generation (CC4G)", 4, 1)
258                     .WithTaggedFlag("Capture/compare update generation (COMG)", 5)
259                     .WithTag("Trigger generation (TG)", 6, 1)
260                     .WithReservedBits(7, 25)
261                     .WithWriteCallback((_, __) => UpdateInterrupts())
262                 },
263 
264                 {(long)Registers.CaptureOrCompareMode1, new DoubleWordRegister(this)
265                     // Fields of this register vary between 'Output compare'/'Input capture' mode
266                     // Only fields for output compare mode are defined
267                     .WithEnumField<DoubleWordRegister, CaptureCompareSelection>(0, 2, writeCallback: (_, val) => WriteCaptureCompareSelection(0, val), name: "CC1S")
268                     // Input mode:
269                         // "IC1PSC", 2, 2
270                         // "IC1F", 4, 4
271                     .WithTaggedFlag("OC1FE", 2)
272                     .WithTaggedFlag("OC1PE", 3)
273                     .WithEnumField(4, 3, out outputCompareModes[0], writeCallback: (_, val) => WriteOutputCompareMode(0, val), name: "OC1M")
274                     .WithTaggedFlag("OC1CE", 7)
275                     .WithEnumField<DoubleWordRegister, CaptureCompareSelection>(8, 2, writeCallback: (_, val) => WriteCaptureCompareSelection(1, val), name: "CC2S")
276                     .WithTaggedFlag("OC2FE", 10)
277                     .WithTaggedFlag("OC2PE", 11)
278                     .WithEnumField(12, 3, out outputCompareModes[1], writeCallback: (_, val) => WriteOutputCompareMode(1, val), name: "OC2M")
279                     .WithTaggedFlag("OC2CE", 15)
280                     // Input mode:
281                         // "IC2PSC", 10, 2
282                         // "IC2F", 12, 4
283                     .WithReservedBits(16, 16)
284                 },
285 
286                 {(long)Registers.CaptureOrCompareMode2, new DoubleWordRegister(this)
287                     // Fields of this register vary between 'Output compare'/'Input capture' mode
288                     // Only fields for output compare mode are defined
289                     .WithEnumField<DoubleWordRegister, CaptureCompareSelection>(0, 2, writeCallback: (_, val) => WriteCaptureCompareSelection(2, val), name: "CC3S")
290                     .WithTaggedFlag("OC3FE", 2)
291                     .WithTaggedFlag("OC3PE", 3)
292                     .WithEnumField(4, 3, out outputCompareModes[2], writeCallback: (_, val) => WriteOutputCompareMode(2, val), name: "OC3M")
293                     .WithTaggedFlag("OC3CE", 7)
294                     // Input mode:
295                         // "IC3PSC", 2, 2
296                         // "IC3F", 4, 4
297                     .WithEnumField<DoubleWordRegister, CaptureCompareSelection>(8, 2, writeCallback: (_, val) => WriteCaptureCompareSelection(3, val), name: "CC4S")
298                     .WithTaggedFlag("OC4FE", 10)
299                     .WithTaggedFlag("OC4PE", 11)
300                     .WithEnumField(12, 3, out outputCompareModes[3], writeCallback: (_, val) => WriteOutputCompareMode(3, val), name: "OC4M")
301                     .WithTaggedFlag("OC4CE", 15)
302                     // Input mode:
303                         // "IC4PSC", 10, 2
304                         // "IC4F", 12, 4
305                     .WithReservedBits(16, 16)
306                 },
307 
308                 {(long)Registers.CaptureOrCompareEnable, new DoubleWordRegister(this)
309                     .WithFlag(0, valueProviderCallback: _ => ccOutputEnable[0], writeCallback: (_, val) => WriteCaptureCompareOutputEnable(0, val), name: "Capture/Compare 1 enable (CC1E)")
310                     .WithTaggedFlag("CC1P", 1)
311                     .WithTaggedFlag("CC1NE", 2)
312                     .WithTaggedFlag("CC1NP", 3)
313                     .WithFlag(4, valueProviderCallback: _ => ccOutputEnable[1], writeCallback: (_, val) => WriteCaptureCompareOutputEnable(1, val), name: "Capture/Compare 2 enable (CC2E)")
314                     .WithTaggedFlag("CC2P", 5)
315                     .WithTaggedFlag("CC2NE", 6)
316                     .WithTaggedFlag("CC2NP", 7)
317                     .WithFlag(8, valueProviderCallback: _ => ccOutputEnable[2], writeCallback: (_, val) => WriteCaptureCompareOutputEnable(2, val), name: "Capture/Compare 3 enable (CC3E)")
318                     .WithTaggedFlag("CC3P", 9)
319                     .WithTaggedFlag("CC3NE", 10)
320                     .WithTaggedFlag("CC3NP", 11)
321                     .WithFlag(12, valueProviderCallback: _ => ccOutputEnable[3], writeCallback: (_, val) => WriteCaptureCompareOutputEnable(3, val), name: "Capture/Compare 4 enable (CC4E)")
322                     .WithTaggedFlag("CC4P", 13)
323                     .WithReservedBits(14, 18)
324                 },
325 
326                 {(long)Registers.Counter, new DoubleWordRegister(this)
327                     .WithValueField(0, timerCounterLengthInBits,
328                         writeCallback: (_, val) => Value = val,
329                         valueProviderCallback: _ =>
330                         {
331                             if(sysbus.TryGetCurrentCPU(out var cpu))
332                             {
333                                 cpu.SyncTime();
334                             }
335                             return (uint)Value;
336                         }, name: "Counter value (CNT)")
337                     .WithReservedBits(timerCounterLengthInBits, 32 - timerCounterLengthInBits)
338                     .WithWriteCallback((_, val) =>
339                     {
340                         for(var i = 0; i < NumberOfCCChannels; ++i)
341                         {
342                             if(val < ccTimers[i].Limit)
343                             {
344                                 ccTimers[i].Value = val;
345                             }
346                         }
347                         UpdateInterrupts();
348                     })
349                 },
350 
351                 {(long)Registers.Prescaler, new DoubleWordRegister(this)
352                     .WithValueField(0, 16, writeCallback: (_, val) => Divider = (int)val + 1, valueProviderCallback: _ => (uint)Divider - 1, name: "Prescaler value (PSC)")
353                     .WithReservedBits(16, 16)
354                     .WithWriteCallback((_, __) =>
355                     {
356                         for(var i = 0; i < NumberOfCCChannels; ++i)
357                         {
358                             ccTimers[i].Divider = Divider;
359                         }
360                         UpdateInterrupts();
361                     })
362                 },
363 
364                 {(long)Registers.AutoReload, new DoubleWordRegister(this)
365                     .WithValueField(0, timerCounterLengthInBits, writeCallback: (_, val) =>
366                     {
367                         autoReloadValue = (uint)val;
368                         Enabled = enableRequested && autoReloadValue > 0;
369                         if(!autoReloadPreloadEnable.Value)
370                         {
371                             Limit = autoReloadValue;
372                         }
373                     }, valueProviderCallback: _ => autoReloadValue, name: "Auto-reload value (ARR)")
374                     .WithReservedBits(timerCounterLengthInBits, 32 - timerCounterLengthInBits)
375                     .WithWriteCallback((_, __) => UpdateInterrupts())
376                 },
377                 {(long)Registers.RepetitionCounter, new DoubleWordRegister(this)
378                     .WithValueField(0, 8, out repetitionCounter, name: "Repetition counter (TIM1_RCR)")
379                     .WithReservedBits(8, 24)
380                 },
381                 {(long)Registers.BreakAndDeadTime, new DoubleWordRegister(this)
382                     .WithTag("Dead Time Generator (DTG)", 0, 8)
383                     .WithTag("LOCK", 8, 2)
384                     .WithTaggedFlag("Off-state selection idle mode (OSSI)", 10)
385                     .WithTaggedFlag("Off-state selection run mode (OSSR)", 11)
386                     .WithTaggedFlag("Break enable (BKE)", 12)
387                     .WithTaggedFlag("Break polarity (BKP)", 13)
388                     .WithTaggedFlag("Automatic output enable (AOE)", 14)
389                     .WithTaggedFlag("Main Output Enable (MOE)", 15)
390                     .WithReservedBits(16, 16)
391                 },
392             };
393 
394             for(var i = 0; i < NumberOfCCChannels; ++i)
395             {
396                 var j = i;
397                 registersMap.Add((long)Registers.CaptureOrCompare1 + (j * 0x4), new DoubleWordRegister(this)
398                     .WithValueField(0, timerCounterLengthInBits, valueProviderCallback: _ => (uint)ccTimers[j].Limit, writeCallback: (_, val) =>
399                     {
400                         if(val == 0)
401                         {
402                             ccTimers[j].Enabled = false;
403                         }
404                         ccTimers[j].Limit = val;
405                     }, name: String.Format("Capture/compare value {0} (CCR{0})", j + 1))
406                     .WithReservedBits(timerCounterLengthInBits, 32 - timerCounterLengthInBits)
407                     .WithWriteCallback((_, __) => { UpdateCaptureCompareTimer(j); UpdateInterrupts(); })
408                 );
409             }
410 
411             registers = new DoubleWordRegisterCollection(this, registersMap);
412             Reset();
413 
414             EventEnabled = true;
415         }
416 
ReadDoubleWord(long offset)417         public uint ReadDoubleWord(long offset)
418         {
419             return registers.Read(offset);
420         }
421 
WriteDoubleWord(long offset, uint value)422         public void WriteDoubleWord(long offset, uint value)
423         {
424             registers.Write(offset, value);
425         }
426 
Reset()427         public override void Reset()
428         {
429             base.Reset();
430             registers.Reset();
431             autoReloadValue = initialLimit;
432             enableRequested = false;
433             Limit = initialLimit;
434             repetitionsLeft = 0;
435             updateInterruptFlag = false;
436             for(var i = 0; i < NumberOfCCChannels; ++i)
437             {
438                 ccTimers[i].Reset();
439                 ccInterruptFlag[i] = false;
440                 ccInterruptEnable[i] = false;
441                 ccOutputEnable[i] = false;
442                 Connections[i].Unset();
443             }
444             UpdateInterrupts();
445         }
446 
447         public GPIO IRQ { get; private set; }
448         public IReadOnlyDictionary<int, IGPIO> Connections => connections;
449 
450         public long Size => 0x400;
451 
Register(IGPIOReceiver peripheral, NumberRegistrationPoint<int> registrationPoint)452         public void Register(IGPIOReceiver peripheral, NumberRegistrationPoint<int> registrationPoint)
453         {
454             machine.RegisterAsAChildOf(this, peripheral, registrationPoint);
455         }
456 
Register(IGPIOReceiver peripheral, NullRegistrationPoint registrationPoint)457         public void Register(IGPIOReceiver peripheral, NullRegistrationPoint registrationPoint)
458         {
459             machine.RegisterAsAChildOf(this, peripheral, registrationPoint);
460         }
461 
Unregister(IGPIOReceiver peripheral)462         public void Unregister(IGPIOReceiver peripheral)
463         {
464             machine.UnregisterAsAChildOf(this, peripheral);
465         }
466 
UpdateCaptureCompareTimer(int i)467         private void UpdateCaptureCompareTimer(int i)
468         {
469             ccTimers[i].Enabled = Enabled && IsInterruptOrOutputEnabled(i) && Value < ccTimers[i].Limit;
470             if(ccTimers[i].Enabled)
471             {
472                 ccTimers[i].Value = Value;
473             }
474             ccTimers[i].Direction = Direction;
475         }
476 
UpdateCaptureCompareTimers()477         private void UpdateCaptureCompareTimers()
478         {
479             for(var i = 0; i < NumberOfCCChannels; ++i)
480             {
481                 UpdateCaptureCompareTimer(i);
482             }
483         }
484 
WriteCaptureCompareOutputEnable(int i, bool value)485         private void WriteCaptureCompareOutputEnable(int i, bool value)
486         {
487             ccOutputEnable[i] = value;
488             UpdateCaptureCompareTimer(i);
489             if(!value)
490             {
491                 Connections[i].Unset();
492             }
493             this.Log(LogLevel.Noisy, "cctimer{0}: Output Enable set to {1}", i + 1, value);
494         }
495 
WriteCaptureCompareInterruptEnable(int i, bool value)496         private void WriteCaptureCompareInterruptEnable(int i, bool value)
497         {
498             ccInterruptEnable[i] = value;
499             UpdateCaptureCompareTimer(i);
500             this.Log(LogLevel.Noisy, "cctimer{0}: Interrupt Enable set to {1}", i + 1, value);
501         }
502 
IsInterruptOrOutputEnabled(int i)503         private bool IsInterruptOrOutputEnabled(int i)
504         {
505             return ccInterruptEnable[i] || ccOutputEnable[i];
506         }
507 
WriteCaptureCompareSelection(int i, CaptureCompareSelection value)508         private void WriteCaptureCompareSelection(int i, CaptureCompareSelection value)
509         {
510             if(value != CaptureCompareSelection.Output)
511             {
512                 this.Log(LogLevel.Warning, "Channel {0}: input capture mode is not supported", i + 1);
513             }
514         }
515 
WriteOutputCompareMode(int i, OutputCompareMode value)516         private void WriteOutputCompareMode(int i, OutputCompareMode value)
517         {
518             this.Log(LogLevel.Noisy, "cctimer{0}: output compare mode set to {1}", i + 1, value);
519             switch(value)
520             {
521                 case OutputCompareMode.ForceInactive:
522                 case OutputCompareMode.SetActiveOnMatch:
523                     Connections[i].Unset();
524                     break;
525                 case OutputCompareMode.SetInactiveOnMatch:
526                 case OutputCompareMode.ForceActive:
527                     Connections[i].Set();
528                     break;
529             }
530         }
531 
ClaimCaptureCompareInterrupt(int i, bool value)532         private void ClaimCaptureCompareInterrupt(int i, bool value)
533         {
534             if(!value)
535             {
536                 ccInterruptFlag[i] = false;
537                 this.Log(LogLevel.Noisy, "cctimer{0}: Compare IRQ claimed", i + 1);
538             }
539         }
540 
UpdateInterrupts()541         private void UpdateInterrupts()
542         {
543             var value = false;
544             value |= updateInterruptFlag & updateInterruptEnable.Value;
545             for(var i  = 0; i < NumberOfCCChannels; ++i)
546             {
547                 value |= ccInterruptFlag[i] & ccInterruptEnable[i];
548             }
549 
550             IRQ.Set(value);
551         }
552 
553         private readonly uint initialLimit;
554         private readonly int timerCounterLengthInBits;
555         private uint autoReloadValue;
556         private uint repetitionsLeft;
557         private bool updateInterruptFlag;
558         private bool enableRequested;
559         private bool[] ccInterruptFlag = new bool[NumberOfCCChannels];
560         private bool[] ccInterruptEnable = new bool[NumberOfCCChannels];
561         private bool[] ccOutputEnable = new bool[NumberOfCCChannels];
562         private readonly IFlagRegisterField updateDisable;
563         private readonly IFlagRegisterField updateRequestSource;
564         private readonly IFlagRegisterField updateInterruptEnable;
565         private readonly IFlagRegisterField autoReloadPreloadEnable;
566         private readonly IEnumRegisterField<CenterAlignedMode> centerAlignedMode;
567         private readonly IValueRegisterField repetitionCounter;
568         private readonly DoubleWordRegisterCollection registers;
569         private readonly IEnumRegisterField<OutputCompareMode>[] outputCompareModes = new IEnumRegisterField<OutputCompareMode>[NumberOfCCChannels];
570         private readonly LimitTimer[] ccTimers = new LimitTimer[NumberOfCCChannels];
571         private readonly IMachine machine;
572         private readonly IBusController sysbus;
573         private readonly Dictionary<int, IGPIO> connections;
574 
575         private const int NumberOfCCChannels = 4;
576 
577         private enum CenterAlignedMode
578         {
579             EdgeAligned    = 0,   // Direction depending on direction bit (TIMx_CR1::BIT)
580             CenterAligned1 = 1,   // Up and down alternatively, compare interrupt flag set only when counting down
581             CenterAligned2 = 2,   // Up and down alternatively, compare interrupt flag set only when counting up
582             CenterAligned3 = 3,   // Up and down alternatively, compare interrupt flag set on both up/down counting
583         }
584 
585         private enum OutputCompareMode
586         {
587             Frozen             = 0, // Comparison between CNT and CCR has no effect on the outputs
588             SetActiveOnMatch   = 1, // Output is high when CNT = CCR
589             SetInactiveOnMatch = 2, // Output is low when CNT = CCR
590             ToggleOnMatch      = 3, // Output is toggled when CNT = CCR
591             ForceInactive      = 4, // Output is forced low
592             ForceActive        = 5, // Output is forced high
593             PwmMode1           = 6, // Ascending:  output is high when CNT < CCR
594                                     // Descending: output is high when CNT ≤ CCR
595             PwmMode2           = 7, // Ascending:  output is high when CNT ≥ CCR
596                                     // Descending: output is high when CNT > CCR
597         }
598 
599         private enum CaptureCompareSelection
600         {
601             Output   = 0, // Channel is configured as an output
602             InputTi1 = 1, // Channel is configured as an input, mapped on TI1
603             InputTi2 = 2, // Channel is configured as an input, mapped on TI2
604             InputTrc = 3, // Channel is configured as an input, mapped on TRC
605         }
606 
607         private enum Registers : long
608         {
609             Control1 = 0x0,
610             Control2 = 0x04,
611             SlaveModeControl = 0x08,
612             DmaOrInterruptEnable = 0x0C,
613             Status = 0x10,
614             EventGeneration = 0x14,
615             CaptureOrCompareMode1 = 0x18,
616             CaptureOrCompareMode2 = 0x1C,
617             CaptureOrCompareEnable = 0x20,
618             Counter = 0x24,
619             Prescaler = 0x28,
620             AutoReload = 0x2C,
621             // gap intended
622             RepetitionCounter = 0x30,
623             // gap intended
624             CaptureOrCompare1 = 0x34,
625             CaptureOrCompare2 = 0x38,
626             CaptureOrCompare3 = 0x3C,
627             CaptureOrCompare4 = 0x40,
628             BreakAndDeadTime = 0x44,
629             // gap intended
630             DmaControl = 0x48,
631             DmaAddressForFullTransfer = 0x4C,
632             Option = 0x50
633         }
634     }
635 }
636 
637