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 Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Exceptions;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Time;
14 using Antmicro.Renode.Peripherals.Sensor;
15 using Antmicro.Renode.Peripherals.Timers;
16 using Antmicro.Renode.Utilities;
17 using Antmicro.Renode.Utilities.RESD;
18 
19 namespace Antmicro.Renode.Peripherals.Analog
20 {
21     public class CAES_ADC : BasicDoubleWordPeripheral, IADC, IKnownSize
22     {
CAES_ADC(IMachine machine, uint frequency = 50000000)23         public CAES_ADC(IMachine machine, uint frequency = 50000000) : base(machine)
24         {
25             this.frequency = frequency;
26             DefineRegisters();
27             rawVoltage = Enumerable.Repeat(DefaultChannelVoltage, ADCChannelCount).ToArray();
28             resdStream = new RESDStream<VoltageSample>[NumberOfDataChannels];
29             samplingTimer = new LimitTimer(
30                 machine.ClockSource, frequency, this, "samplingClock",
31                 eventEnabled: true,
32                 direction: Direction.Ascending,
33                 enabled: false,
34                 autoUpdate: false,
35                 workMode: WorkMode.OneShot);
36             samplingTimer.LimitReached += OnConversionFinished;
37         }
38 
FeedSamplesFromRESD(ReadFilePath filePath, uint adcChannel, uint resdChannel = 0, RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.CurrentVirtualTime, long sampleOffsetTime = 0)39         public void FeedSamplesFromRESD(ReadFilePath filePath, uint adcChannel, uint resdChannel = 0,
40             RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.CurrentVirtualTime, long sampleOffsetTime = 0)
41         {
42             EnsureChannelIsValid(adcChannel);
43             try
44             {
45                 resdStream[adcChannel] = this.CreateRESDStream<VoltageSample>(filePath, resdChannel, sampleOffsetType, sampleOffsetTime);
46             }
47             catch(RESDException)
48             {
49                 for(var channelId = 0; channelId < NumberOfDataChannels; channelId++)
50                 {
51                     resdStream[channelId]?.Dispose();
52                 }
53                 throw new RecoverableException($"Could not load RESD channel {resdChannel} from {filePath}");
54             }
55         }
56 
Reset()57         public override void Reset()
58         {
59             base.Reset();
60             samplingTimer.Reset();
61             IRQ.Unset();
62         }
63 
SetADCValue(int adcChannel, uint value)64         public void SetADCValue(int adcChannel, uint value)
65         {
66             EnsureChannelIsValid((uint)adcChannel);
67             rawVoltage[adcChannel] = value;
68         }
69 
GetADCValue(int adcChannel)70         public uint GetADCValue(int adcChannel)
71         {
72             EnsureChannelIsValid((uint)adcChannel);
73             return rawVoltage[adcChannel];
74         }
75 
76         public GPIO IRQ { get; } = new GPIO();
77 
78         public long Size => 0x1000;
79 
80         public int ADCChannelCount => (int)NumberOfDataChannels;
81 
UpdateInterrupts()82         private void UpdateInterrupts()
83         {
84             bool value = adcInterruptEnabled.Value && conversionCompleteInterruptEnabled.Value && channelInterruptPending.Value != 0;
85             this.Log(LogLevel.Debug, "Setting IRQ to {0}", value);
86             IRQ.Set(value);
87         }
88 
GetChannelVoltage(uint dataChannelId)89         private uint GetChannelVoltage(uint dataChannelId)
90         {
91             if(resdStream[dataChannelId] == null || resdStream[dataChannelId].TryGetCurrentSample(this, (sample) => sample.Voltage, out var voltage, out _) != RESDStreamStatus.OK)
92             {
93                 voltage = rawVoltage[dataChannelId];
94             }
95             else
96             {
97                 rawVoltage[dataChannelId] = voltage;
98             }
99 
100             voltage = (uint)((float)voltage * GetGain(channelGain[DataToConfigChannel(dataChannelId)].Value));
101 
102             if(voltage > MaxVoltage)
103             {
104                 this.Log(LogLevel.Warning, "The maximum allowed input voltage is {0}μV. Provided value: {1}μV", MaxVoltage, voltage);
105                 return MaxVoltage;
106             }
107 
108             return voltage;
109         }
110 
EnsureChannelIsValid(uint channelIdx)111         private void EnsureChannelIsValid(uint channelIdx)
112         {
113             if(channelIdx >= NumberOfDataChannels)
114             {
115                 throw new RecoverableException($"Invalid argument value: {channelIdx}. This peripheral implements only channels in range 0-{NumberOfDataChannels - 1}");
116             }
117         }
118 
GetSingleEndedValue(uint voltage)119         private uint GetSingleEndedValue(uint voltage)
120         {
121             // The most significant bit needs to be flipped in single-ended
122             // conversion due to the fact that the positive voltage range is
123             // mapped to a signed integer format
124             return (uint)((ulong)voltage * MaxValue / MaxVoltage) ^ 0x800;
125         }
126 
GetDifferentialValue(uint voltagePositive, uint voltageNegative)127         private uint GetDifferentialValue(uint voltagePositive, uint voltageNegative)
128         {
129             return (uint)(((ulong)voltagePositive - voltageNegative + MaxVoltage) * MaxValue / (2 * MaxVoltage));
130         }
131 
StartConversion()132         private void StartConversion()
133         {
134             var enabledChannelsCount = 0;
135 
136             if(!adcEnabled.Value)
137             {
138                 this.Log(LogLevel.Warning, "Tried to start the conversion, but the ADC_EN bit is not set");
139                 return;
140             }
141 
142             this.Log(LogLevel.Debug, "Starting conversion");
143 
144             for(var i = 0; i < NumberOfConfigChannels; i++)
145             {
146                 if(channelEnable[i].Value)
147                 {
148                     enabledChannelsCount++;
149                 }
150             }
151 
152             // Values below are derived from the "Calculating Channel Conversion Time" section in the ADC chapter of the UT32M0R500 Functional Manual
153             samplingTimer.Frequency = GetClockFrequency(oscillatorDivider.Value);
154             samplingTimer.Limit = (ulong) enabledChannelsCount * (sequenceDelay.Value * SequenceDelayDuration + oversamplingRate.Value);
155             samplingTimer.Enabled = true;
156         }
157 
OnConversionFinished()158         private void OnConversionFinished()
159         {
160             this.Log(LogLevel.Debug, "Ending conversion");
161 
162             if(!adcInterruptEnabled.Value || !conversionCompleteInterruptEnabled.Value)
163             {
164                 return;
165             }
166 
167             for(var i = 0; i < NumberOfConfigChannels; i++)
168             {
169                 if(!channelEnable[i].Value)
170                 {
171                     continue;
172                 }
173 
174                 var dataChannelIdx = ConfigToDataChannel((uint)i);
175                 this.Log(LogLevel.Debug, "Channel {0} enabled ({1}), data channel index: {2}", i, i < DifferentialChannelsOffset ? "single-ended" : "differential", dataChannelIdx);
176                 channelInterruptPending.SetBit((byte)i, true);
177 
178                 if(i < DifferentialChannelsOffset)
179                 {
180                     channelData[dataChannelIdx].Value = GetSingleEndedValue(GetChannelVoltage(dataChannelIdx));
181                 }
182                 else
183                 {
184                     channelData[dataChannelIdx].Value = GetDifferentialValue(GetChannelVoltage(dataChannelIdx), GetChannelVoltage(dataChannelIdx + 1));
185                 }
186 
187                 conversionCompleteCombined.Value = true;
188             }
189 
190             UpdateInterrupts();
191         }
192 
GetGain(Gain gainSetting)193         private float GetGain(Gain gainSetting)
194         {
195             float gain = 1.0F;
196             if(gainAmplifierEnabled.Value)
197             {
198                 switch(gainSetting)
199                 {
200                     case Gain.DivideBy2:
201                         gain = 0.5F;
202                         break;
203                     case Gain.NoGain:
204                         gain = 1.0F;
205                         break;
206                     case Gain.MultiplyBy2:
207                         gain = 2.0F;
208                         break;
209                     case Gain.MultiplyBy4:
210                         gain = 4.0F;
211                         break;
212                     case Gain.MultiplyBy8:
213                         gain = 8.0F;
214                         break;
215                     case Gain.MultiplyBy16:
216                     case Gain.MultiplyBy16Alt1:
217                     case Gain.MultiplyBy16Alt2:
218                         gain = 16.0F;
219                         break;
220                     default:
221                         this.Log(LogLevel.Warning, "Unsupported value of the gain setting.");
222                         break;
223                 }
224             }
225             return gain;
226         }
227 
GetClockFrequency(OscillatorDivider oscillatorDividerSetting)228         private uint GetClockFrequency(OscillatorDivider oscillatorDividerSetting)
229         {
230             uint clockFrequency;
231             var oscillatorDivider = 0;
232 
233             switch(oscillatorDividerSetting)
234             {
235                 case OscillatorDivider.DivideBy2:
236                     oscillatorDivider = 2;
237                     break;
238                 case OscillatorDivider.DivideBy4:
239                     oscillatorDivider = 4;
240                     break;
241                 case OscillatorDivider.DivideBy8:
242                     oscillatorDivider = 8;
243                     break;
244                 case OscillatorDivider.DivideBy16:
245                     oscillatorDivider = 16;
246                     break;
247                 default:
248                     this.Log(LogLevel.Warning, "Unsupported value of the oscillator divider setting.");
249                     break;
250             }
251 
252             clockFrequency = oscillatorDivider == 0 ? 0 : frequency / (uint)oscillatorDivider;
253 
254             return clockFrequency;
255         }
256 
DataToConfigChannel(uint dataChannelIdx)257         private uint DataToConfigChannel(uint dataChannelIdx)
258         {
259             uint configChannelIdx = 0;
260 
261             // Even data channels can contain either differential or
262             // single-ended conversion. If both are enabled, differential
263             // conversion takes precedence.
264             //
265             // Temperature channel is unique in a way that it only has a
266             // single configuration register which applies to both single-ended
267             // and differential channels.
268             if(channelEnable[DifferentialChannelsOffset + (dataChannelIdx / 2)].Value || dataChannelIdx >= TemperatureDataChannelsOffset)
269             {
270                 configChannelIdx = (uint)(DifferentialChannelsOffset + (dataChannelIdx / 2));
271             }
272             else
273             {
274                 configChannelIdx = dataChannelIdx;
275             }
276 
277             return configChannelIdx;
278         }
279 
ConfigToDataChannel(uint configChannelIdx)280         private uint ConfigToDataChannel(uint configChannelIdx)
281         {
282             uint dataChannelIdx = 0;
283 
284             if(configChannelIdx < DifferentialChannelsOffset)
285             {
286                 dataChannelIdx = configChannelIdx;
287             }
288             else
289             {
290                 dataChannelIdx = (configChannelIdx - DifferentialChannelsOffset) * 2;
291             }
292 
293             return dataChannelIdx;
294         }
295 
DefineRegisters()296         private void DefineRegisters()
297         {
298             Registers.SPBConfiguration0.Define(this)
299                 .WithFlag(0, out adcEnabled, name: "ADC_EN")
300                 .WithFlag(1, out gainAmplifierEnabled, name: "EN_PGA")
301                 .WithTaggedFlag("EN_DSM", 2)
302                 .WithTaggedFlag("EN_AAF", 3)
303                 .WithTaggedFlag("DDF2_CLK_EN", 4)
304                 .WithTaggedFlag("EN_REFP", 5)
305                 .WithReservedBits(6,1)
306                 .WithTaggedFlag("EN_REFC", 7)
307                 .WithReservedBits(8,1)
308                 .WithTaggedFlag("EN_BIASGEN", 9)
309                 .WithTaggedFlag("EN_CLKGEN", 10)
310                 .WithReservedBits(11,2)
311                 .WithTaggedFlag("ADC_SINGLESWEEP", 13)
312                 .WithTag("ODB", 14, 2)
313                 .WithReservedBits(16,8)
314                 .WithFlag(24, out adcInterruptEnabled, name: "ADC_INTR_EN",
315                     writeCallback: (_, val) =>
316                     {
317                         UpdateInterrupts();
318                     })
319                 .WithReservedBits(25,2)
320                 .WithTaggedFlag("COI_OVER_IEN", 27)
321                 .WithTaggedFlag("SINC4_OVER_IEN", 28)
322                 .WithTaggedFlag("DSM_OVL_IEN", 29)
323                 .WithTaggedFlag("TRIG_UNDER_IEN", 30)
324                 .WithFlag(31, out conversionCompleteInterruptEnabled, name: "CONV_COMPL_IEN",
325                     writeCallback: (_, val) =>
326                     {
327                         UpdateInterrupts();
328                     })
329             ;
330 
331             Registers.SPBConfiguration1.Define(this)
332                 .WithFlag(0, name: "ADC_TRIGGER",
333                     writeCallback: (_, value) =>
334                     {
335                         if(value)
336                         {
337                             StartConversion();
338                         }
339                     })
340                 .WithReservedBits(1,6)
341                 .WithTaggedFlag("ADC_RST_CCONV", 7)
342                 .WithReservedBits(8,1)
343                 .WithTaggedFlag("ADC_READYFLAG", 9)
344                 .WithReservedBits(10,20)
345                 .WithFlag(31, name: "ADC_REGDEF",
346                     writeCallback: (_, value) =>
347                     {
348                         if(value)
349                         {
350                             // Reset all registers to default state
351                             RegistersCollection.Reset();
352                         }
353                     })
354             ;
355 
356             Registers.SingleEndedChannelConfiguration0.DefineMany(this, NumberOfSingleEndedChannels,
357                 (register, idx) =>
358                 {
359                     register
360                         .WithFlag(0, out channelEnable[SingleEndedChannelsOffset + idx], name: "EN")
361                         .WithReservedBits(1,2)
362                         .WithTaggedFlag("DDF2", 3)
363                         .WithEnumField(4, 3, out channelGain[SingleEndedChannelsOffset + idx], name: "GAIN")
364                         .WithReservedBits(7,25)
365                     ;
366                 }, resetValue: 0x10);
367 
368             Registers.DifferentialChannelConfiguration0.DefineMany(this, NumberOfDifferentialChannels,
369                 (register, idx) =>
370                 {
371                     register
372                         .WithFlag(0, out channelEnable[DifferentialChannelsOffset + idx], name: "EN")
373                         .WithReservedBits(1,2)
374                         .WithTaggedFlag("DDF2", 3)
375                         .WithEnumField(4, 3, out channelGain[DifferentialChannelsOffset + idx], name: "GAIN")
376                         .WithReservedBits(7,25)
377                     ;
378                 }, resetValue: 0x10);
379 
380             Registers.TemperatureChannelConfiguration.Define(this, resetValue: 0x10)
381                 .WithFlag(0, out channelEnable[TemperatureChannelsOffset], name: "EN")
382                 .WithReservedBits(1,2)
383                 .WithTaggedFlag("DDF2", 3)
384                 .WithEnumField(4, 3, out channelGain[TemperatureChannelsOffset], name: "GAIN")
385                 .WithReservedBits(7,25)
386             ;
387 
388             Registers.TimingControl.Define(this, resetValue: 0x64)
389                 .WithValueField(0, 8, out oversamplingRate, name: "ADC_DSMOSR")
390                 .WithEnumField(8, 2, out oscillatorDivider, name: "ADC_OSCDIV")
391                 .WithReservedBits(10,22)
392             ;
393 
394             Registers.SequenceControl.Define(this)
395                 .WithValueField(0, 6, out sequenceDelay, name: "ADC_SEQDLY")
396                 .WithReservedBits(6,26)
397             ;
398 
399             Registers.DSMDigitalStabilityControl.Define(this, resetValue: 0x81e)
400                 .WithTag("DSM_OVL_CNT", 0, 7)
401                 .WithReservedBits(7,1)
402                 .WithTag("DSM_OVL_RST", 8, 5)
403                 .WithReservedBits(13,3)
404                 .WithTaggedFlag("DSM_OVL_FLAG", 16)
405                 .WithReservedBits(17,15)
406             ;
407 
408             Registers.InterruptStatus.Define(this)
409                 // SET_CHNL_INTR_PEND, DIFF_CHNL_INTR_PEND and TEMP_CHNL_INTR_PEND combined
410                 .WithValueField(0, 25, out channelInterruptPending, FieldMode.ReadToClear, name: "CHNL_INTR_PEND")
411                 .WithReservedBits(25,2)
412                 .WithTaggedFlag("COI_OVER_COMB", 27)
413                 .WithTaggedFlag("SINC4_OVER_COMB", 28)
414                 .WithTaggedFlag("DSM_OVL_FLAG_COMB", 29)
415                 .WithTaggedFlag("TRIG_UNDER_COMB", 30)
416                 .WithFlag(31, out conversionCompleteCombined, FieldMode.ReadToClear, name: "CONV_COMPL_COMB")
417                 .WithReadCallback((_, __) => UpdateInterrupts())
418             ;
419 
420             Registers.DataOutputWord0.DefineMany(this, NumberOfDataOutputChannels, (register, idx) =>
421             {
422                 register
423                     .WithValueField(0, 12, out channelData[idx], FieldMode.Read, name: "DATA_OUT")
424                     .WithReservedBits(12,2)
425                     .WithTaggedFlag("DATA_ERROR", 14)
426                     .WithReservedBits(15,1)
427                     .WithTaggedFlag("CHNL_EN", 16)
428                     .WithReservedBits(17,2)
429                     .WithTaggedFlag("DD", 19)
430                     .WithTag("GAIN", 20, 3)
431                     .WithTaggedFlag("COI_OVER", 23)
432                     .WithTaggedFlag("SINC4_OVER", 24)
433                     .WithTaggedFlag("DSM_OVL_FLAG", 25)
434                     .WithTaggedFlag("TRIG_UNDER", 26)
435                     .WithReservedBits(27,1)
436                     .WithTag("DATA_OUT_LSB", 28, 4)
437                     .WithReadCallback((_, __) =>
438                     {
439                         channelInterruptPending.SetBit((byte)DataToConfigChannel((uint)idx), false);
440                         UpdateInterrupts();
441                     })
442                 ;
443             });
444         }
445 
446         private IFlagRegisterField adcEnabled;
447         private IFlagRegisterField adcInterruptEnabled;
448         private IFlagRegisterField conversionCompleteInterruptEnabled;
449         private IFlagRegisterField conversionCompleteCombined;
450         private IValueRegisterField channelInterruptPending;
451         private IFlagRegisterField gainAmplifierEnabled;
452         private IFlagRegisterField[] channelEnable = new IFlagRegisterField[NumberOfConfigChannels];
453         private IValueRegisterField[] channelData = new IValueRegisterField[NumberOfDataChannels];
454         private IEnumRegisterField<Gain>[] channelGain = new IEnumRegisterField<Gain>[NumberOfConfigChannels];
455         private IEnumRegisterField<OscillatorDivider> oscillatorDivider;
456 
457         private IValueRegisterField oversamplingRate;
458         private IValueRegisterField sequenceDelay;
459 
460         private uint[] rawVoltage;
461 
462         private const uint MaxValue = 0xFFF;
463         private const uint MaxVoltage = 1500000; // [μV]
464         private const uint SequenceDelayDuration = 25;
465         private const uint NumberOfDataChannels = 18;
466         private const uint NumberOfConfigChannels = 25;
467         private const uint NumberOfSingleEndedChannels = 16;
468         private const uint NumberOfDifferentialChannels = 8;
469         private const uint NumberOfDataOutputChannels = 18;
470         private const uint SingleEndedChannelsOffset = 0;
471         private const uint DifferentialChannelsOffset = 16;
472         private const uint TemperatureChannelsOffset = 24;
473         private const uint TemperatureDataChannelsOffset = 16;
474         private const uint DefaultChannelVoltage = 0;
475 
476         private readonly LimitTimer samplingTimer;
477         private readonly RESDStream<VoltageSample>[] resdStream;
478         private readonly uint frequency;
479 
480         private enum OscillatorDivider
481         {
482             DivideBy4,
483             DivideBy8,
484             DivideBy16,
485             DivideBy2
486         }
487 
488         private enum Gain
489         {
490             DivideBy2,
491             NoGain,
492             MultiplyBy2,
493             MultiplyBy4,
494             MultiplyBy8,
495             MultiplyBy16,
496             MultiplyBy16Alt1,
497             MultiplyBy16Alt2,
498         }
499 
500         private enum Registers : long
501         {
502             SPBConfiguration0 = 0x00,
503             SPBConfiguration1 = 0x04,
504             SingleEndedChannelConfiguration0 = 0x10, // 16 SingleEndedChannelConfiguration registers, 0x10–0x4C
505             DifferentialChannelConfiguration0 = 0x50, // 8 DifferentialChannelConfiguration registers, 0x50–0x6C
506             TemperatureChannelConfiguration = 0x70,
507             TimingControl = 0x74,
508             SequenceControl = 0x78,
509             DSMDigitalStabilityControl = 0x84,
510             InterruptStatus = 0x8C,
511             DataOutputWord0 = 0x90 // 18 DataOutputWord registers, 0x90–0xD4
512         }
513     }
514 }
515