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 
8 using System;
9 using System.Linq;
10 using System.Collections.Generic;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Utilities;
15 using Antmicro.Renode.Core.Structure.Registers;
16 using Antmicro.Renode.Peripherals.Bus;
17 using Antmicro.Renode.Peripherals.DMA;
18 using Antmicro.Renode.Peripherals.Sensors;
19 
20 namespace Antmicro.Renode.Peripherals.Analog
21 {
22     // Superset of all ADC features found on many STM MPU series.
23     //
24     // Available features:
25     //     watchdogCount ------ Specifies the number of analog watchdogs inside the peripheral between 1 and 3.
26     //    *hasCalibration ----- Specifies whether the calibration factor and voltage regulator are available to the software.
27     //                          ADCs without this feature will still have the ADCAL flag available to trigger the calibration procedure,
28     //                          but not the CALFACT register nor the ADVREGEN field.
29     //     channelCount ------- Specifies the amount of available channels.
30     //                          Includes both internal sources (like the temperature sensor) as well as external.
31     //    *hasPrescaler ------- Specifies whether the ADC contains a prescaler for the external clock input.
32     //                          Technically either this property could be made an enum,
33     //                          or there could be added a separate property which describes whether the internal clock can be used.
34     //                          ex.
35     //                            - the STM32F0xx can either use PCLK or the ADC asynchronous clock and has no precaler
36     //                            - the STM32WBA only uses the ADC asynchronous clock but has a precaler
37     //                          but for now, this feature describes both (i.e. true means has prescaler *and* no internal clock).
38     //    *hasVbatPin --------- Specifies whether this ADC provides a pin for monitoring of an external power supply.
39     //    *hasChannelSequence - Specifies whether this ADC provides a fully configurable sequencer.
40     //                          If not, the ADC can convert a single channel or a sequence of channels,
41     //                          but only scanning sequentially either forwards or backwards.
42     //    *hasPowerRegister --- Specifies whether this ADC has a separate register for power managment.
43     //                          If false, that means the model exposes features like auto-off in one of the configuration registers.
44     //    *hasChannelSelect --- Specifies whether this ADC has channel selection register.
45     //                          If false, third watchdog threshold configuration register will live under this register's offset.
46     //
47     // * - Feature is either partially implemented, or not at all.
48     public abstract class STM32_ADC_Common : IKnownSize, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IDoubleWordPeripheral
49     {
STM32_ADC_Common(IMachine machine, double referenceVoltage, uint externalEventFrequency, int dmaChannel = 0, IDMA dmaPeripheral = null, int? watchdogCount = null, bool? hasCalibration = null, int? channelCount = null, bool? hasPrescaler = null, bool? hasVbatPin = null, bool? hasChannelSequence = null, bool? hasPowerRegister = null, bool? hasChannelSelect = null)50         public STM32_ADC_Common(IMachine machine, double referenceVoltage, uint externalEventFrequency, int dmaChannel = 0, IDMA dmaPeripheral = null,
51             int? watchdogCount = null, bool? hasCalibration = null, int? channelCount = null, bool? hasPrescaler = null,
52             bool? hasVbatPin = null, bool? hasChannelSequence = null, bool? hasPowerRegister = null, bool? hasChannelSelect = null)
53         {
54             if(!watchdogCount.HasValue || !hasCalibration.HasValue || !channelCount.HasValue || !hasPrescaler.HasValue ||
55                 !hasVbatPin.HasValue || !hasChannelSequence.HasValue || !hasPowerRegister.HasValue || !hasChannelSelect.HasValue)
56             {
57                 throw new ConstructionException("Missing configuration options");
58             }
59 
60             if(dmaPeripheral == null)
61             {
62                 if(dmaChannel != 0)
63                 {
64                     throw new ConstructionException($"Unspecified DMA peripheral to use with channel number {dmaChannel}");
65                 }
66             }
67             else
68             {
69                 if(dmaChannel <= 0 || dmaChannel > dmaPeripheral.NumberOfChannels)
70                 {
71                     throw new ConstructionException($"Invalid 'dmaChannel' argument value: '{dmaChannel}'. Available channels: 1-{dma.NumberOfChannels}");
72                 }
73             }
74 
75             this.machine = machine;
76 
77             bool calibration = hasCalibration.Value;
78             bool prescaler = hasPrescaler.Value;
79             bool vbatPin = hasVbatPin.Value;
80             bool channelSequence = hasChannelSequence.Value;
81             bool powerRegister = hasPowerRegister.Value;
82             ChannelCount = channelCount.Value;
83             WatchdogCount = watchdogCount.Value;
84             this.hasChannelSelect = hasChannelSelect.Value;
85 
86             if(WatchdogCount < 1 || WatchdogCount > 3)
87             {
88                 throw new ConstructionException("Invalid watchdog count");
89             }
90 
91             analogWatchdogFlags = new IFlagRegisterField[WatchdogCount];
92             analogWatchdogHighValues = new IValueRegisterField[WatchdogCount];
93             analogWatchdogLowValues = new IValueRegisterField[WatchdogCount];
94             if(WatchdogCount >= 2)
95             {
96                 analogWatchdog2SelectedChannels = new IFlagRegisterField[ChannelCount];
97             }
98             if(WatchdogCount == 3)
99             {
100                 analogWatchdog3SelectedChannels = new IFlagRegisterField[ChannelCount];
101             }
102 
103             registers = new DoubleWordRegisterCollection(this, BuildRegistersMap(calibration, prescaler, vbatPin, channelSequence, powerRegister));
104 
105             IRQ = new GPIO();
106             this.dmaChannel = dmaChannel;
107             this.dma = dmaPeripheral;
108             this.referenceVoltage = referenceVoltage;
109             this.externalEventFrequency = externalEventFrequency;
110 
111             samplingThread = machine.ObtainManagedThread(StartSampling, externalEventFrequency);
112             channelSelected = new bool[ChannelCount];
113             sampleProvider = new SensorSamplesFifo<ScalarSample>[ChannelCount];
114             for(var channel = 0; channel < ChannelCount; channel++)
115             {
116                 sampleProvider[channel] = new SensorSamplesFifo<ScalarSample>();
117             }
118             Reset();
119         }
120 
FeedVoltageSampleToChannel(int channel, string path)121         public void FeedVoltageSampleToChannel(int channel, string path)
122         {
123             ValidateChannel(channel);
124             sampleProvider[channel].FeedSamplesFromFile(path);
125         }
126 
FeedVoltageSampleToChannel(int channel, decimal valueInmV, uint repeat)127         public void FeedVoltageSampleToChannel(int channel, decimal valueInmV, uint repeat)
128         {
129             ValidateChannel(channel);
130             var sample = new ScalarSample(valueInmV);
131             for(var i = 0; i < repeat; i++)
132             {
133                 sampleProvider[channel].FeedSample(sample);
134             }
135         }
136 
SetDefaultValue(decimal valueInmV, int? channel = null)137         public void SetDefaultValue(decimal valueInmV, int? channel = null)
138         {
139             if(channel != null)
140             {
141                 ValidateChannel(channel.Value);
142                 sampleProvider[channel.Value].DefaultSample.Value = valueInmV;
143                 return;
144             }
145             for(var i = 0; i < ChannelCount; i++)
146             {
147                 sampleProvider[i].DefaultSample.Value = valueInmV;
148             }
149         }
150 
Reset()151         public void Reset()
152         {
153             RegistersCollection.Reset();
154             for(var i = 0; i < ChannelCount; i++)
155             {
156                 channelSelected[i] = false;
157             }
158             currentChannel = 0;
159             awaitingConversion = false;
160             enabled = false;
161             externalTrigger = false;
162             sequenceInProgress = false;
163             sequenceCounter = 0;
164             samplingThread.Stop();
165         }
166 
ReadDoubleWord(long offset)167         public uint ReadDoubleWord(long offset)
168         {
169             return RegistersCollection.Read(offset);
170         }
171 
WriteDoubleWord(long offset, uint value)172         public void WriteDoubleWord(long offset, uint value)
173         {
174             RegistersCollection.Write(offset, value);
175         }
176 
177         public DoubleWordRegisterCollection RegistersCollection { get => registers; }
178         public long Size => 0x400;
179         public GPIO IRQ { get; }
180 
ValidateChannel(int channel)181         private void ValidateChannel(int channel)
182         {
183             if(channel >= ChannelCount || channel < 0)
184             {
185                 throw new RecoverableException($"Invalid argument value: {channel}. This peripheral implements only channels in range 0-{ChannelCount-1}");
186             }
187         }
188 
UpdateInterrupts()189         private void UpdateInterrupts()
190         {
191             var adcReady = adcReadyFlag.Value && adcReadyInterruptEnable.Value;
192             var analogWatchdog = analogWatchdogsInterruptEnable.Zip(analogWatchdogFlags, (enable, flag) =>
193             {
194                 return enable.Value && flag.Value;
195             }).Any(flag => flag);
196             var endOfSampling = endOfSamplingFlag.Value && endOfSamplingInterruptEnable.Value;
197             var endOfConversion = endOfConversionFlag.Value && endOfConversionInterruptEnable.Value;
198             var endOfSequence = endOfSequenceFlag.Value && endOfSequenceInterruptEnable.Value;
199 
200             IRQ.Set(adcReady || analogWatchdog || endOfSampling || endOfConversion || endOfSequence);
201         }
202 
StartSampling()203         private void StartSampling()
204         {
205             if(sequenceInProgress)
206             {
207                 if(waitFlag.Value)
208                 {
209                     awaitingConversion = true;
210                     return;
211                 }
212                 this.Log(LogLevel.Warning, "Issued a start event before the last sequence finished");
213                 return;
214             }
215             if(hasChannelSelect)
216             {
217                 currentChannel = (scanDirection.Value == ScanDirection.Ascending) ? 0 : ChannelCount - 1;
218             }
219             else
220             {
221                 sequenceCounter = (scanDirection.Value == ScanDirection.Ascending) ? 0 : (int)regularSequenceLength.Value;
222                 currentChannel = (int)regularSequence[sequenceCounter].Value;
223             }
224             sequenceInProgress = true;
225             startFlag.Value = true;
226             SampleNextChannel();
227         }
228 
SendDmaRequest()229         private void SendDmaRequest()
230         {
231             if(dma != null)
232             {
233                 dma.RequestTransfer(dmaChannel);
234             }
235             else
236             {
237                 this.Log(LogLevel.Warning, "Received DMA transfer request, but no DMA is configured for this peripheral.");
238             }
239         }
240 
WatchdogEnabled(int watchdogNumber)241         private bool WatchdogEnabled(int watchdogNumber)
242         {
243             switch(watchdogNumber)
244             {
245             case 0:
246                 var enabledOnAll = !analogWatchdogSingleChannel.Value;
247                 var enabledOnCurrent = enabledOnAll || (int)analogWatchdogChannel.Value == currentChannel;
248                 return analogWatchdogEnable.Value && enabledOnCurrent;
249             case 1:
250                 return analogWatchdog2SelectedChannels[currentChannel].Value;
251             case 2:
252                 return analogWatchdog3SelectedChannels[currentChannel].Value;
253             }
254             throw new Exception("Unreachable, the watchdog count is checked in the constructor");
255         }
256 
SampleNextChannel()257         private void SampleNextChannel()
258         {
259             // Exit when peripheral is not enabled
260             if(!enabled)
261             {
262                 currentChannel = 0;
263                 sequenceCounter = 0;
264                 sequenceInProgress = false;
265                 return;
266             }
267 
268             Func<bool> iterationFinished = null;
269             if(hasChannelSelect)
270             {
271                 iterationFinished = () => currentChannel >= ChannelCount;
272             }
273             else
274             {
275                 iterationFinished = () => sequenceCounter > (int)regularSequenceLength.Value;
276             }
277 
278             while(!iterationFinished() && currentChannel >= 0)
279             {
280                 if(hasChannelSelect && !channelSelected[currentChannel])
281                 {
282                     SwitchToNextChannel();
283                     continue;
284                 }
285                 else
286                 {
287                     data.Value = GetSampleFromChannel(currentChannel);
288                     if(dmaEnabled.Value)
289                     {
290                         SendDmaRequest();
291                     }
292                     endOfSamplingFlag.Value = true;
293 
294                     for(int i = 0; i < WatchdogCount; i++)
295                     {
296                         if(WatchdogEnabled(i))
297                         {
298                             if(data.Value > analogWatchdogHighValues[i].Value || data.Value < analogWatchdogLowValues[i].Value)
299                             {
300                                 analogWatchdogFlags[i].Value = true;
301                                 this.Log(LogLevel.Debug, "Analog watchdog {0} flag raised for value {1} on channel {2}", i, data.Value, currentChannel);
302                             }
303                         }
304                     }
305                     endOfConversionFlag.Value = true;
306                     UpdateInterrupts();
307                     this.Log(LogLevel.Debug, "Sampled channel {0}", currentChannel);
308                     SwitchToNextChannel();
309                     return;
310                 }
311             }
312             this.Log(LogLevel.Debug, "No more channels enabled");
313             endOfSequenceFlag.Value = true;
314             sequenceInProgress = false;
315             sequenceCounter  = 0;
316             UpdateInterrupts();
317             startFlag.Value = false;
318 
319             if(awaitingConversion)
320             {
321                 awaitingConversion = false;
322                 StartSampling();
323             }
324         }
325 
SwitchToNextChannel()326         private void SwitchToNextChannel()
327         {
328             if(hasChannelSelect)
329             {
330                 currentChannel = (scanDirection.Value == ScanDirection.Ascending) ? currentChannel + 1 : currentChannel - 1;
331             }
332             else
333             {
334                 sequenceCounter = (scanDirection.Value == ScanDirection.Ascending) ? sequenceCounter + 1 : sequenceCounter - 1;
335                 // NOTE: Sequence finishes when `sequenceCounter` is either greater than `regularSequenceLength` or less than `0`.
336                 // In both of those cases, we assume that at this point `currentChannel` will contain invalid value.
337                 if(sequenceCounter >= 0 && sequenceCounter <= (int)regularSequenceLength.Value)
338                 {
339                     currentChannel = (int)regularSequence[sequenceCounter].Value;
340                 }
341             }
342         }
343 
GetSampleFromChannel(int channelNumber)344         private uint GetSampleFromChannel(int channelNumber)
345         {
346             var sample = sampleProvider[channelNumber].Sample;
347             sampleProvider[channelNumber].TryDequeueNewSample();
348             return MilivoltsToSample((double)sample.Value);
349         }
350 
MilivoltsToSample(double sampleInMilivolts)351         private uint MilivoltsToSample(double sampleInMilivolts)
352         {
353             ushort resolutionInBits;
354             switch(resolution.Value)
355             {
356                 case Resolution.Bits6:
357                     resolutionInBits = 6;
358                     break;
359                 case Resolution.Bits8:
360                     resolutionInBits = 8;
361                     break;
362                 case Resolution.Bits10:
363                     resolutionInBits = 10;
364                     break;
365                 case Resolution.Bits12:
366                     resolutionInBits = 12;
367                     break;
368                 default:
369                     throw new Exception("This should never have happend!");
370             }
371 
372             uint referencedValue = (uint)Math.Round((sampleInMilivolts / (referenceVoltage * 1000)) * ((1 << resolutionInBits) - 1));
373             if(align.Value == Align.Left)
374             {
375                 referencedValue = referencedValue << (16 - resolutionInBits);
376             }
377             return referencedValue;
378         }
379 
BuildRegistersMap(bool hasCalibration, bool hasPrescaler, bool hasVbatPin, bool hasChannelSequence, bool hasPowerRegister)380         private Dictionary<long, DoubleWordRegister> BuildRegistersMap(bool hasCalibration, bool hasPrescaler, bool hasVbatPin, bool hasChannelSequence, bool hasPowerRegister)
381         {
382             var isrRegister = new DoubleWordRegister(this)
383                 .WithFlag(0, out adcReadyFlag, FieldMode.Read | FieldMode.WriteOneToClear, name: "ADRDY")
384                 .WithFlag(1, out endOfSamplingFlag, FieldMode.Read | FieldMode.WriteOneToClear, name: "EOSMP")
385                 .WithFlag(2, out endOfConversionFlag, FieldMode.Read | FieldMode.WriteOneToClear,  writeCallback: (_, val) =>
386                     {
387                         if(val)
388                         {
389                             // Clearing the End Of Conversion flag triggers next conversion
390                             // This function call must be delayed to avoid deadlock on registers access
391                             machine.LocalTimeSource.ExecuteInNearestSyncedState((___) => SampleNextChannel());
392                         }
393                     }, name: "EOC")
394                 .WithFlag(3, out endOfSequenceFlag, FieldMode.Read | FieldMode.WriteOneToClear, name: "EOSEQ")
395                 .WithFlag(4, out adcOverrunFlag, FieldMode.Read | FieldMode.WriteOneToClear, name: "OVR")
396                 .WithReservedBits(5, 2)
397                 .WithFlags(7, WatchdogCount, out analogWatchdogFlags, FieldMode.Read | FieldMode.WriteOneToClear, name: "AWD")
398                 .WithReservedBits(7 + WatchdogCount, 4 - WatchdogCount)
399                 .WithReservedBits(13, 19);
400 
401             var interruptEnableRegister = new DoubleWordRegister(this)
402                 .WithFlag(0, out adcReadyInterruptEnable, name: "ADRDYIE")
403                 .WithFlag(1, out endOfSamplingInterruptEnable, name: "EOSMPIE")
404                 .WithFlag(2, out endOfConversionInterruptEnable, name: "EOCIE")
405                 .WithFlag(3, out endOfSequenceInterruptEnable, name: "EOSEQIE")
406                 .WithFlag(4, out adcOverrunInterruptEnable, name: "OVRIE")
407                 .WithReservedBits(5, 2)
408                 .WithFlags(7, WatchdogCount, out analogWatchdogsInterruptEnable, name: "AWDIE")
409                 .WithReservedBits(7 + WatchdogCount, 4 - WatchdogCount)
410                 .WithReservedBits(13, 19)
411                 .WithWriteCallback((_, __) => UpdateInterrupts());
412 
413             if(hasCalibration)
414             {
415                 isrRegister
416                     .WithTaggedFlag("EOCAL", 11)
417                     .WithTaggedFlag("LDORDY", 12);
418                 interruptEnableRegister
419                     .WithTaggedFlag("EOCALIE", 11)
420                     .WithTaggedFlag("LDORDYIE", 12);
421             }
422             else
423             {
424                 isrRegister
425                     .WithReservedBits(11, 2);
426                 interruptEnableRegister
427                     .WithReservedBits(11, 2);
428             }
429 
430             var configurationRegister1 = new DoubleWordRegister(this)
431                 .WithFlag(0, out dmaEnabled, name: "DMAEN")
432                 .WithTaggedFlag("DMACFG", 1)
433                 // When fully configurable channel sequencer is available, the SCANDIR and RES fields are swapped
434                 .WithEnumField<DoubleWordRegister, ScanDirection>(hasChannelSequence ? 4 : 2, 1, out scanDirection, name: "SCANDIR")
435                 .WithEnumField<DoubleWordRegister, Resolution>(hasChannelSequence ? 2 : 3, 2, out resolution, name: "RES")
436                 .WithEnumField<DoubleWordRegister, Align>(5, 1, out align, name: "ALIGN")
437                 .WithTag("EXTSEL", 6, 2)
438                 .WithReservedBits(9, 1)
439                 .WithValueField(10, 2, writeCallback: (_, val) =>
440                     {
441                         // On hardware it is possible to configure on which edge should the trigger fire
442                         // This Peripheral mocks external trigger using `externalEventFrequency`, so we only distinguish between manual/external trigger
443                         externalTrigger = (val > 0);
444                     }, name: "EXTEN")
445                 .WithTaggedFlag("OVRMOD", 12)
446                 .WithTaggedFlag("CONT", 13)
447                 .WithFlag(14, out waitFlag, name: "WAIT")
448                 .WithTaggedFlag("DISCEN", 16)
449                 .WithReservedBits(17, 4)
450                 .WithFlag(22, out analogWatchdogSingleChannel, name: "AWDSGL")
451                 .WithFlag(23, out analogWatchdogEnable, name: "AWDEN")
452                 .WithReservedBits(24, 2)
453                 .WithValueField(26, 5, out analogWatchdogChannel, name: "AWDCH")
454                 .WithReservedBits(31, 1);
455 
456             if(!hasPowerRegister)
457             {
458                 configurationRegister1
459                     .WithTaggedFlag("AUTOFF", 15);
460             }
461             else
462             {
463                 configurationRegister1
464                     .WithReservedBits(15, 1);
465             }
466 
467             var regularSequence1 = new DoubleWordRegister(this);
468 
469             if(hasChannelSequence)
470             {
471                 if(hasChannelSelect)
472                 {
473                     configurationRegister1
474                         .WithFlag(21, name: "CHSELRMOD"); // no actual logic, but software expects to read the value back
475                 }
476                 else
477                 {
478                     regularSequence1
479                         .WithValueField(0, 4, out regularSequenceLength)
480                         .WithReservedBits(28, 4);
481 
482                     for(var i = 0; i < 4; ++i)
483                     {
484                         var j = i;
485                         regularSequence1
486                             .WithReservedBits(4 + 6 * i, 2)
487                             .WithValueField(6 + 6 * i, 4, out regularSequence[j]);
488                     }
489                 }
490             }
491             else
492             {
493                 configurationRegister1
494                     .WithReservedBits(21, 1);
495 
496                 regularSequence1
497                     .WithReservedBits(0, 32);
498             }
499 
500             var configurationRegister2 = new DoubleWordRegister(this)
501                 .WithReservedBits(0, 30)
502                 .WithTag("CKMODE", 30, 2);
503 
504             var commonConfigurationRegister = new DoubleWordRegister(this)
505                 .WithReservedBits(0, 18)
506                 .WithTaggedFlag("VREFEN", 22)
507                 .WithTaggedFlag("TSEN", 23)
508                 .WithReservedBits(25, 7);
509 
510             if(hasPrescaler)
511             {
512                 commonConfigurationRegister
513                     .WithValueField(18, 4, name: "PRESC");
514             }
515             else
516             {
517                 commonConfigurationRegister
518                     .WithReservedBits(18, 4);
519             }
520 
521             if(hasVbatPin)
522             {
523                 commonConfigurationRegister
524                     .WithTaggedFlag("VBATEN", 24);
525             }
526             else
527             {
528                 commonConfigurationRegister
529                     .WithReservedBits(24, 1);
530             }
531 
532             var registers = new Dictionary<long, DoubleWordRegister>
533             {
534                 {(long)Registers.InterruptAndStatus, isrRegister},
535                 {(long)Registers.InterruptEnable, interruptEnableRegister},
536                 {(long)Registers.Control, new DoubleWordRegister(this)
537                     .WithFlag(0, valueProviderCallback: _ => enabled, writeCallback: (_, val) =>
538                         {
539                             if(val)
540                             {
541                                 enabled = true;
542                                 adcReadyFlag.Value = true;
543                                 UpdateInterrupts();
544                             }
545                         }, name: "ADEN")
546                     // Reading one from below field would mean that command is in progress. This is never the case in this model
547                     .WithFlag(1, valueProviderCallback: _ => false, writeCallback: (_, val) => { if(val) enabled = false; }, name: "ADDIS")
548                     // Reading one from this field means that conversion is in progress
549                     .WithFlag(2, out startFlag, writeCallback: (_, val) =>
550                         {
551                             if(val)
552                             {
553                                 if(externalTrigger)
554                                 {
555                                     samplingThread.Start();
556                                 }
557                                 else
558                                 {
559                                     StartSampling();
560                                 }
561                             }
562                         },name: "ADSTART")
563                     .WithReservedBits(3, 1)
564                     // Reading one from below field would mean that command is in progress. This is never the case in this model
565                     .WithFlag(4, valueProviderCallback: _ => false,  writeCallback: (_, val) =>
566                         {
567                             if(val)
568                             {
569                                 samplingThread.Stop();
570                                 sequenceInProgress = false;
571                             }
572                         }, name: "ADSTP")
573                     .WithReservedBits(5, 23)
574                     .WithFlag(28, name: "ADVREGEN") // no logic implemented, but software expects to read this flag back
575                     .WithReservedBits(29, 2)
576                     .WithTaggedFlag("ADCAL", 31)
577                 },
578                 {(long)Registers.Configuration1, configurationRegister1},
579                 {(long)Registers.Configuration2, configurationRegister2},
580                 {(long)Registers.SamplingTime, new DoubleWordRegister(this)
581                     .WithTag("SMP", 0, 3)
582                     .WithReservedBits(3, 29)
583                 },
584                 {(long)Registers.RegularSequence1, regularSequence1},
585                 {(long)Registers.DataRegister, new DoubleWordRegister(this)
586                     .WithValueField(0, 16, out data, FieldMode.Read, readCallback: (_, __) =>
587                         {
588                             endOfConversionFlag.Value = false;
589                             // This function call must be delayed to avoid deadlock on registers access
590                             if(sequenceInProgress)
591                             {
592                                 machine.LocalTimeSource.ExecuteInNearestSyncedState((___) => SampleNextChannel());
593                             }
594                         }, name: "DATA")
595                     .WithReservedBits(16, 16)
596                 },
597                 {(long)Registers.CommonConfiguration, commonConfigurationRegister},
598             };
599 
600             // Optional registers
601             if(hasChannelSelect)
602             {
603                 registers.Add((long)Registers.ChannelSelection, new DoubleWordRegister(this)
604                     .WithFlags(0, ChannelCount,
605                            valueProviderCallback: (id, __) => channelSelected[id],
606                            writeCallback: (id, _, val) => { this.Log(LogLevel.Debug, "Channel {0} enable set as {1}", id, val); channelSelected[id] = val; })
607                     .WithReservedBits(ChannelCount, 32 - ChannelCount)
608                 );
609             }
610 
611             if(WatchdogCount >= 1)
612             {
613                 registers.Add((long)Registers.Watchdog1Threshold, new DoubleWordRegister(this)
614                     .WithValueField(0, 12, out analogWatchdogLowValues[0], name: "LT")
615                     .WithReservedBits(12, 4)
616                     .WithValueField(16, 12, out analogWatchdogHighValues[0], name: "HT")
617                     .WithReservedBits(28, 4));
618             }
619             if(WatchdogCount >= 2)
620             {
621                 registers.Add((long)Registers.Watchdog2Threshold, new DoubleWordRegister(this)
622                     .WithValueField(0, 12, out analogWatchdogLowValues[1], name: "LT")
623                     .WithReservedBits(12, 4)
624                     .WithValueField(16, 12, out analogWatchdogHighValues[1], name: "HT")
625                     .WithReservedBits(28, 4));
626                 registers.Add((long)Registers.Watchdog2Configuration, new DoubleWordRegister(this)
627                     .WithFlags(0, 14, out analogWatchdog2SelectedChannels, name: "AWD2CH")
628                     .WithReservedBits(14, 18));
629             }
630             if(WatchdogCount == 3)
631             {
632                 // NOTE: If given implementation doesn't have channel selection, the third Watchdog Threshold will be under ChannelSelection offset
633                 registers.Add(hasChannelSelect ? (long)Registers.Watchdog3Threshold : (long)Registers.ChannelSelection, new DoubleWordRegister(this)
634                     .WithValueField(0, 12, out analogWatchdogLowValues[2], name: "LT")
635                     .WithReservedBits(12, 4)
636                     .WithValueField(16, 12, out analogWatchdogHighValues[2], name: "HT")
637                     .WithReservedBits(28, 4));
638                 registers.Add((long)Registers.Watchdog3Configuration, new DoubleWordRegister(this)
639                     .WithFlags(0, 14, out analogWatchdog3SelectedChannels, name: "AWD3CH")
640                     .WithReservedBits(14, 18));
641             }
642 
643             if(hasCalibration)
644             {
645                 registers.Add((long)Registers.CalibrationFactor, new DoubleWordRegister(this)
646                     .WithValueField(0, 7, name: "CALFACT")
647                     .WithReservedBits(7, 25));
648             }
649 
650             if(hasPowerRegister)
651             {
652                 registers.Add((long)Registers.Power, new DoubleWordRegister(this)
653                     .WithTaggedFlag("AUTOFF", 0)
654                     .WithTaggedFlag("DPD", 1) // Deep-power-down mode
655                     .WithReservedBits(2, 30));
656             }
657 
658             return registers;
659         }
660 
661         private int currentChannel;
662         private int sequenceCounter;
663         private bool enabled;
664         private bool externalTrigger;
665         private bool sequenceInProgress;
666         private bool awaitingConversion;
667         private bool[] channelSelected;
668 
669         private IEnumRegisterField<Align> align;
670         private IEnumRegisterField<Resolution> resolution;
671         private IEnumRegisterField<ScanDirection> scanDirection;
672 
673         private IFlagRegisterField dmaEnabled;
674         private IFlagRegisterField analogWatchdogEnable;
675         private IFlagRegisterField startFlag;
676         private IFlagRegisterField waitFlag;
677 
678         private IFlagRegisterField adcOverrunFlag;
679         private IFlagRegisterField adcReadyFlag;
680         private IFlagRegisterField[] analogWatchdogFlags;
681         private IFlagRegisterField endOfConversionFlag;
682         private IFlagRegisterField endOfSamplingFlag;
683         private IFlagRegisterField endOfSequenceFlag;
684         private IFlagRegisterField adcOverrunInterruptEnable;
685         private IFlagRegisterField adcReadyInterruptEnable;
686         private IFlagRegisterField[] analogWatchdogsInterruptEnable;
687         private IFlagRegisterField endOfConversionInterruptEnable;
688         private IFlagRegisterField endOfSamplingInterruptEnable;
689         private IFlagRegisterField endOfSequenceInterruptEnable;
690         private IFlagRegisterField analogWatchdogSingleChannel;
691 
692         private IValueRegisterField data;
693         // Watchdog 1 either watches all channels or a single channel
694         private IValueRegisterField analogWatchdogChannel;
695         // While watchdogs 2 and 3 use bitfields for selecting channels to watch
696         private IFlagRegisterField[] analogWatchdog2SelectedChannels;
697         private IFlagRegisterField[] analogWatchdog3SelectedChannels;
698         private IValueRegisterField[] analogWatchdogHighValues;
699         private IValueRegisterField[] analogWatchdogLowValues;
700 
701         private IValueRegisterField regularSequenceLength;
702         private IValueRegisterField[] regularSequence = new IValueRegisterField[MaximumSequenceLength];
703 
704         private readonly IDMA dma;
705         private readonly int dmaChannel;
706         private readonly bool hasChannelSelect;
707         private readonly uint externalEventFrequency;
708         private readonly double referenceVoltage;
709         private readonly IManagedThread samplingThread;
710         private readonly SensorSamplesFifo<ScalarSample>[] sampleProvider;
711         private readonly DoubleWordRegisterCollection registers;
712         private readonly IMachine machine;
713 
714         private readonly int ChannelCount;
715         private readonly int WatchdogCount;
716 
717         private const int MaximumSequenceLength = 16;
718 
719         private enum Resolution
720         {
721             Bits12 = 0b00,
722             Bits10 = 0b01,
723             Bits8  = 0b10,
724             Bits6  = 0b11,
725         }
726 
727         private enum ScanDirection
728         {
729             Ascending  = 0b0,
730             Descending = 0b1,
731         }
732 
733         private enum Align
734         {
735             Right = 0x0,
736             Left  = 0x1,
737         }
738 
739         private enum Registers
740         {
741             InterruptAndStatus     = 0x00, // ADC_ISR
742             InterruptEnable        = 0x04, // ADC_IER
743             Control                = 0x08, // ADC_CR
744             Configuration1         = 0x0C, // ADC_CFGR1
745             Configuration2         = 0x10, // ADC_CFGR2
746             SamplingTime           = 0x14, // ADC_SMPR
747             // Gap intended
748             Watchdog1Threshold     = 0x20, // ADC_AWD1TR
749             Watchdog2Threshold     = 0x24, // ADC_AWD2TR
750             ChannelSelection       = 0x28, // ADC_CHSELR
751             Watchdog3Threshold     = 0x2C, // ADC_AWD3TR
752             RegularSequence1       = 0x30, // ADC_SQR1
753             // Gap intended
754             DataRegister           = 0x40, // ADC_DR
755             // Gap intended
756             Power                  = 0x44, // ADC_PWRR
757             // Gap intended
758             Watchdog2Configuration = 0xA0, // ADC_AWD2CR
759             Watchdog3Configuration = 0xA4, // ADC_AWD3CR
760             // Gap intended
761             CalibrationFactor      = 0xC4, // ADC_CALFACT
762             // Gap intended
763             CommonConfiguration    = 0x308, // ADC_CCR
764         }
765     }
766 }
767