1 //
2 // Copyright (c) 2010-2025 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.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.Peripherals.Timers;
14 using Antmicro.Renode.Utilities;
15 using Antmicro.Renode.Utilities.RESD;
16 using Antmicro.Renode.Time;
17 
18 namespace Antmicro.Renode.Peripherals.Analog
19 {
20     public class SAM4S_ADC : BasicDoubleWordPeripheral, IKnownSize
21     {
SAM4S_ADC(Machine machine, long baseFrequency = 32768, decimal referenceVoltage = 5m)22         public SAM4S_ADC(Machine machine, long baseFrequency = 32768, decimal referenceVoltage = 5m) : base(machine)
23         {
24             internalTimer = new LimitTimer(machine.ClockSource, baseFrequency, this, "internalTimer", limit: 1, divider: 2, eventEnabled: true, workMode: WorkMode.OneShot);
25             internalTimer.LimitReached += ConversionFinished;
26 
27             ReferenceVoltage = referenceVoltage;
28 
29             DefineRegisters();
30         }
31 
Reset()32         public override void Reset()
33         {
34             base.Reset();
35 
36             foreach(var it in channelStream.Select((Stream, Index) => new { Stream, Index }))
37             {
38                 it.Stream?.Dispose();
39                 channelStream[it.Index] = null;
40             }
41         }
42 
FeedSamplesFromRESD(ReadFilePath filePath, int channelIndex, uint? resdChannelOverride = null, RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.Specified, long sampleOffsetTime = 0)43         public void FeedSamplesFromRESD(ReadFilePath filePath, int channelIndex, uint? resdChannelOverride = null,
44             RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.Specified, long sampleOffsetTime = 0)
45         {
46             AssertChannelIndex(channelIndex);
47 
48             var resdChannel = resdChannelOverride ?? (uint)channelIndex;
49             channelStream[channelIndex] = this.CreateRESDStream<VoltageSample>(filePath, resdChannel, sampleOffsetType, sampleOffsetTime);
50         }
51 
DefaultChannelVoltage(int channelIndex)52         public decimal DefaultChannelVoltage(int channelIndex)
53         {
54             AssertChannelIndex(channelIndex);
55             return defaultChannelValue[channelIndex];
56         }
57 
DefaultChannelVoltage(int channelIndex, decimal newVoltage)58         public void DefaultChannelVoltage(int channelIndex, decimal newVoltage)
59         {
60             AssertChannelIndex(channelIndex);
61             defaultChannelValue[channelIndex] = newVoltage;
62         }
63 
GetChannelValue(int channelIndex)64         public ushort GetChannelValue(int channelIndex)
65         {
66             AssertChannelIndex(channelIndex);
67 
68             var voltage = defaultChannelValue[channelIndex];
69             if(temperatureSensorEnabled.Value && channelIndex == TemperatureChannelIndex)
70             {
71                  voltage = BaseTemperatureVoltage + (Temperature - BaseTemperature) * TemperatureProportionalCoefficient;
72             }
73             else if(channelStream[channelIndex] != null)
74             {
75                 var streamStatus = channelStream[channelIndex].TryGetCurrentSample(this, out var sample, out _);
76                 if(streamStatus == RESDStreamStatus.OK)
77                 {
78                     voltage = sample.Voltage / 1000m;
79                 }
80                 else if(streamStatus == RESDStreamStatus.AfterStream)
81                 {
82                     channelStream[channelIndex].Dispose();
83                     channelStream[channelIndex] = null;
84                 }
85             }
86 
87             voltage = voltage.Clamp(0, ReferenceVoltage);
88             return (ushort)(voltage * MaximumChannelValue / ReferenceVoltage);
89         }
90 
91         public long Size => 0x100;
92 
93         public GPIO IRQ { get; } = new GPIO();
94 
95         public decimal ReferenceVoltage { get; set; }
96 
97         public decimal Temperature { get; set; }
98 
UpdateInterrupts()99         private void UpdateInterrupts()
100         {
101             var interrupt = Enumerable.Range(0, NumberOfChannels)
102                 .Any(index => endOfConversionInterruptEnabled[index].Value && endOfConversionInterruptPending[index].Value);
103             interrupt |= dataReadyInterruptEnabled.Value && dataReadyInterruptPending.Value;
104             interrupt |= endOfCalibrationInterruptEnabled.Value && endOfCalibrationInterruptPending.Value;
105 
106             this.Log(LogLevel.Debug, "IRQ set to {0}", interrupt);
107             IRQ.Set(interrupt);
108         }
109 
StartConversion()110         private void StartConversion()
111         {
112             internalTimer.ResetValue();
113             internalTimer.Enabled = true;
114             this.Log(LogLevel.Debug, "Started conversion");
115         }
116 
ConversionFinished()117         private void ConversionFinished()
118         {
119             foreach(var channelIndex in Enumerable.Range(0, NumberOfChannels).Where(index => channelStatus[index].Value))
120             {
121                 endOfConversionInterruptPending[channelIndex].Value = true;
122                 channelValue[channelIndex].Value = (ulong)GetChannelValue(channelIndex);
123                 lastDataConverted.Value = channelValue[channelIndex].Value;
124                 this.Log(LogLevel.Debug, "Setting channel#{0} to {1:X03}", channelIndex, channelValue[channelIndex].Value);
125             }
126 
127             this.Log(LogLevel.Debug, "Conversion finished");
128 
129             dataReadyInterruptPending.Value |= endOfConversionInterruptPending.Any(interrupt => interrupt.Value);
130             UpdateInterrupts();
131 
132             if(freerunMode.Value)
133             {
134                 StartConversion();
135             }
136         }
137 
AssertChannelIndex(int channelIndex)138         private void AssertChannelIndex(int channelIndex)
139         {
140             if(channelIndex < 0 || channelIndex >= NumberOfChannels)
141             {
142                 throw new RecoverableException($"'{nameof(channelIndex)}' should be between 0 and {NumberOfChannels - 1}");
143             }
144         }
145 
DefineRegisters()146         private void DefineRegisters()
147         {
148             Registers.Control.Define(this)
149                 .WithFlag(0, FieldMode.Write, name: "SWRST",
150                     writeCallback: (_, value) => { if(value) Reset(); })
151                 .WithFlag(1, FieldMode.Write, name: "START",
152                     writeCallback: (_, value) => { if(value) StartConversion(); })
153                 .WithReservedBits(2, 1)
154                 .WithFlag(3, FieldMode.Write, name: "AUTOCAL",
155                     writeCallback: (_, value) =>
156                     {
157                         if(!value)
158                         {
159                             return;
160                         }
161 
162                         endOfCalibrationInterruptPending.Value = true;
163                         UpdateInterrupts();
164                     })
165                 .WithReservedBits(4, 28)
166             ;
167 
168             Registers.Mode.Define(this)
169                 .WithTaggedFlag("TRGEN", 0)
170                 .WithTag("TRGSEL", 1, 3)
171                 .WithTaggedFlag("SLEEP", 5)
172                 .WithTaggedFlag("FWUP", 6)
173                 .WithFlag(7, out freerunMode, name: "FREERUN")
174                 .WithValueField(8, 8, name: "PRESCAL",
175                     valueProviderCallback: _ => (byte)((internalTimer.Divider / 2) - 1),
176                     changeCallback: (_, value) => internalTimer.Divider = ((int)value + 1) * 2)
177                 .WithTag("STARTUP", 16, 4)
178                 .WithTag("SETTLING", 20, 2)
179                 .WithTaggedFlag("ANACH", 23)
180                 .WithTag("TRACKTIM", 24, 4)
181                 .WithTag("TRANSFER", 28, 2)
182                 .WithTaggedFlag("USEQ", 31)
183             ;
184 
185             Registers.Sequence1.Define(this)
186                 .WithTag("USCH1", 0, 4)
187                 .WithTag("USCH2", 4, 4)
188                 .WithTag("USCH3", 8, 4)
189                 .WithTag("USCH4", 12, 4)
190                 .WithTag("USCH5", 16, 4)
191                 .WithTag("USCH6", 20, 4)
192                 .WithTag("USCH7", 24, 4)
193                 .WithTag("USCH8", 28, 4)
194             ;
195 
196             Registers.Sequence2.Define(this)
197                 .WithTag("USCH9", 0, 4)
198                 .WithTag("USCH10", 4, 4)
199                 .WithTag("USCH11", 8, 4)
200                 .WithTag("USCH12", 12, 4)
201                 .WithTag("USCH13", 16, 4)
202                 .WithTag("USCH14", 20, 4)
203                 .WithTag("USCH15", 24, 4)
204                 .WithReservedBits(28, 4)
205             ;
206 
207             Registers.ChannelEnable.Define(this)
208                 .WithFlags(0, 16, FieldMode.Set, name: "ADC_CHER",
209                     writeCallback: (index, _, value) => { if(value) channelStatus[index].Value = true;  })
210                 .WithReservedBits(16, 16)
211             ;
212 
213             Registers.ChannelDisable.Define(this)
214                 .WithFlags(0, 16, FieldMode.WriteOneToClear, name: "ADC_CHDR",
215                     writeCallback: (index, _, value) => { if(value) channelStatus[index].Value = false;  })
216                 .WithReservedBits(16, 16)
217             ;
218 
219             Registers.ChannelStatus.Define(this)
220                 .WithFlags(0, 16, out channelStatus, FieldMode.Read, name: "ADC_CHSR")
221                 .WithReservedBits(16, 16)
222             ;
223 
224             Registers.LastConvertedData.Define(this)
225                 .WithValueField(0, 12, out lastDataConverted, name: "LDATA")
226                 .WithTag("CHNB", 12, 4)
227                 .WithReservedBits(16, 16)
228                 .WithReadCallback((_, __) =>
229                 {
230                     dataReadyInterruptPending.Value = false;
231                     UpdateInterrupts();
232                 })
233             ;
234 
235             Registers.InterruptEnable.Define(this)
236                 .WithFlags(0, 16, FieldMode.Set, name: "EOC",
237                     writeCallback: (index, _, value) => { if(value) endOfConversionInterruptEnabled[index].Value = true; })
238                 .WithReservedBits(16, 7)
239                 .WithFlag(23, FieldMode.Set, name: "EOCAL",
240                     writeCallback: (_, value) => { if(value) endOfCalibrationInterruptEnabled.Value = true; })
241                 .WithFlag(24, FieldMode.Set, name: "DRDY",
242                     writeCallback: (_, value) => { if(value) dataReadyInterruptEnabled.Value = true; })
243                 .WithTaggedFlag("GOVRE", 25)
244                 .WithTaggedFlag("COMPE", 26)
245                 .WithTaggedFlag("ENDRX", 27)
246                 .WithTaggedFlag("RXBFUF", 28)
247                 .WithReservedBits(29, 3)
248                 .WithChangeCallback((_, __) => UpdateInterrupts())
249             ;
250 
251             Registers.InterruptDisable.Define(this)
252                 .WithFlags(0, 16, FieldMode.Set, name: "EOC",
253                     writeCallback: (index, _, value) => { if(value) endOfConversionInterruptEnabled[index].Value = false; })
254                 .WithReservedBits(16, 7)
255                 .WithFlag(23, FieldMode.Set, name: "EOCAL",
256                     writeCallback: (_, value) => { if(value) endOfCalibrationInterruptEnabled.Value = false; })
257                 .WithFlag(24, FieldMode.Set, name: "DRDY",
258                     writeCallback: (_, value) => { if(value) dataReadyInterruptEnabled.Value = false; })
259                 .WithTaggedFlag("GOVRE", 25)
260                 .WithTaggedFlag("COMPE", 26)
261                 .WithTaggedFlag("ENDRX", 27)
262                 .WithTaggedFlag("RXBFUF", 28)
263                 .WithReservedBits(29, 3)
264                 .WithChangeCallback((_, __) => UpdateInterrupts())
265             ;
266 
267             Registers.InterruptMask.Define(this)
268                 .WithFlags(0, 16, out endOfConversionInterruptEnabled, FieldMode.Read, name: "EOC")
269                 .WithReservedBits(16, 7)
270                 .WithFlag(23, out endOfCalibrationInterruptEnabled, FieldMode.Read, name: "EOCAL")
271                 .WithFlag(24, out dataReadyInterruptEnabled, FieldMode.Read, name: "DRDY")
272                 .WithTaggedFlag("GOVRE", 25)
273                 .WithTaggedFlag("COMPE", 26)
274                 .WithTaggedFlag("ENDRX", 27)
275                 .WithTaggedFlag("RXBFUF", 28)
276                 .WithReservedBits(29, 3)
277             ;
278 
279             Registers.InterruptStatus.Define(this)
280                 .WithFlags(0, 16, out endOfConversionInterruptPending, FieldMode.Read, name: "EOC")
281                 .WithReservedBits(16, 7)
282                 // NOTE: We are not doing the actual calibration, so we are just clearing the
283                 //       flag on the first access.
284                 .WithFlag(23, out endOfCalibrationInterruptPending, FieldMode.ReadToClear, name: "EOCAL")
285                 .WithFlag(24, out dataReadyInterruptPending, FieldMode.Read, name: "DRDY")
286                 .WithTaggedFlag("GOVRE", 25)
287                 .WithTaggedFlag("COMPE", 26)
288                 .WithTaggedFlag("ENDRX", 27)
289                 .WithTaggedFlag("RXBFUF", 28)
290                 .WithReservedBits(29, 3)
291             ;
292 
293             Registers.OverrunStatus.Define(this)
294                 .WithTaggedFlag("OVRE0", 0)
295                 .WithTaggedFlag("OVRE1", 1)
296                 .WithTaggedFlag("OVRE2", 2)
297                 .WithTaggedFlag("OVRE3", 3)
298                 .WithTaggedFlag("OVRE4", 4)
299                 .WithTaggedFlag("OVRE5", 5)
300                 .WithTaggedFlag("OVRE6", 6)
301                 .WithTaggedFlag("OVRE7", 7)
302                 .WithTaggedFlag("OVRE8", 8)
303                 .WithTaggedFlag("OVRE9", 9)
304                 .WithTaggedFlag("OVRE10", 10)
305                 .WithTaggedFlag("OVRE11", 11)
306                 .WithTaggedFlag("OVRE12", 12)
307                 .WithTaggedFlag("OVRE13", 13)
308                 .WithTaggedFlag("OVRE14", 14)
309                 .WithTaggedFlag("OVRE15", 15)
310                 .WithReservedBits(16, 16)
311             ;
312 
313             Registers.ExtendedMode.Define(this)
314                 .WithTag("CMPMODE", 0, 2)
315                 .WithReservedBits(2, 2)
316                 .WithTag("CMPSEL", 4, 4)
317                 .WithReservedBits(8, 1)
318                 .WithTaggedFlag("CMPALL", 9)
319                 .WithReservedBits(10, 14)
320                 .WithTaggedFlag("TAG", 24)
321                 .WithReservedBits(25, 7)
322             ;
323 
324             Registers.CompareWindow.Define(this)
325                 .WithTag("LOWTHRES", 0, 12)
326                 .WithReservedBits(12, 4)
327                 .WithTag("HIGHTHRES", 16, 12)
328                 .WithReservedBits(28, 4)
329             ;
330 
331             Registers.ChannelGain.Define(this)
332                 .WithTaggedFlag("GAIN0", 0)
333                 .WithTaggedFlag("GAIN1", 1)
334                 .WithTaggedFlag("GAIN2", 2)
335                 .WithTaggedFlag("GAIN3", 3)
336                 .WithTaggedFlag("GAIN4", 4)
337                 .WithTaggedFlag("GAIN5", 5)
338                 .WithTaggedFlag("GAIN6", 6)
339                 .WithTaggedFlag("GAIN7", 7)
340                 .WithTaggedFlag("GAIN8", 8)
341                 .WithTaggedFlag("GAIN9", 9)
342                 .WithTaggedFlag("GAIN10", 10)
343                 .WithTaggedFlag("GAIN11", 11)
344                 .WithTaggedFlag("GAIN12", 12)
345                 .WithTaggedFlag("GAIN13", 13)
346                 .WithTaggedFlag("GAIN14", 14)
347                 .WithTaggedFlag("GAIN15", 15)
348             ;
349 
350             Registers.ChannelOffset.Define(this)
351                 .WithTaggedFlag("OFF0", 0)
352                 .WithTaggedFlag("OFF1", 1)
353                 .WithTaggedFlag("OFF2", 2)
354                 .WithTaggedFlag("OFF3", 3)
355                 .WithTaggedFlag("OFF4", 4)
356                 .WithTaggedFlag("OFF5", 5)
357                 .WithTaggedFlag("OFF6", 6)
358                 .WithTaggedFlag("OFF7", 7)
359                 .WithTaggedFlag("OFF8", 8)
360                 .WithTaggedFlag("OFF9", 9)
361                 .WithTaggedFlag("OFF10", 10)
362                 .WithTaggedFlag("OFF11", 11)
363                 .WithTaggedFlag("OFF12", 12)
364                 .WithTaggedFlag("OFF13", 13)
365                 .WithTaggedFlag("OFF14", 14)
366                 .WithTaggedFlag("OFF15", 15)
367                 .WithTaggedFlag("DIFF0", 16)
368                 .WithTaggedFlag("DIFF1", 17)
369                 .WithTaggedFlag("DIFF2", 18)
370                 .WithTaggedFlag("DIFF3", 19)
371                 .WithTaggedFlag("DIFF4", 20)
372                 .WithTaggedFlag("DIFF5", 21)
373                 .WithTaggedFlag("DIFF6", 22)
374                 .WithTaggedFlag("DIFF7", 23)
375                 .WithTaggedFlag("DIFF8", 24)
376                 .WithTaggedFlag("DIFF9", 25)
377                 .WithTaggedFlag("DIFF10", 26)
378                 .WithTaggedFlag("DIFF11", 27)
379                 .WithTaggedFlag("DIFF12", 28)
380                 .WithTaggedFlag("DIFF13", 29)
381                 .WithTaggedFlag("DIFF14", 30)
382                 .WithTaggedFlag("DIFF15", 31)
383             ;
384 
385             Registers.ChannelData0.DefineMany(this, NumberOfChannels, (register, index) =>
386                 register
387                     .WithValueField(0, 12, out channelValue[index], name: $"ADC_CDR{index}")
388                     .WithReservedBits(12, 20)
389                     .WithReadCallback((_, __) =>
390                     {
391                         endOfConversionInterruptPending[index].Value = false;
392                         UpdateInterrupts();
393                     }))
394             ;
395 
396             Registers.AnalogControl.Define(this)
397                 .WithReservedBits(0, 4)
398                 .WithFlag(4, out temperatureSensorEnabled, name: "TSON")
399                 .WithReservedBits(5, 3)
400                 .WithTag("IBCTL", 8, 24)
401             ;
402 
403             Registers.WriteProtectMode.Define(this)
404                 .WithTaggedFlag("WPEN", 0)
405                 .WithReservedBits(1, 7)
406                 .WithTag("WPKEY", 8, 24)
407             ;
408         }
409 
410         private IFlagRegisterField freerunMode;
411 
412         private IFlagRegisterField[] channelStatus;
413 
414         private IFlagRegisterField[] endOfConversionInterruptEnabled;
415         private IFlagRegisterField[] endOfConversionInterruptPending;
416 
417         private IFlagRegisterField endOfCalibrationInterruptEnabled;
418         private IFlagRegisterField endOfCalibrationInterruptPending;
419 
420         private IFlagRegisterField dataReadyInterruptEnabled;
421         private IFlagRegisterField dataReadyInterruptPending;
422 
423         private IValueRegisterField lastDataConverted;
424         private IFlagRegisterField temperatureSensorEnabled;
425 
426         private readonly IValueRegisterField[] channelValue = new IValueRegisterField[NumberOfChannels];
427         private readonly RESDStream<VoltageSample>[] channelStream = new RESDStream<VoltageSample>[NumberOfChannels];
428 
429         private readonly decimal[] defaultChannelValue = new decimal[NumberOfChannels];
430         private readonly LimitTimer internalTimer;
431 
432         private const int NumberOfChannels = 16;
433         private const ushort MaximumChannelValue = 0xFFF;
434 
435         private const decimal BaseTemperature = 27m;
436         private const decimal BaseTemperatureVoltage = 1.44m;
437         private const decimal TemperatureProportionalCoefficient = 4.7m / 1000m;
438         private const int TemperatureChannelIndex = 15;
439 
440         private enum Registers : uint
441         {
442             Control = 0x00,
443             Mode = 0x04,
444             Sequence1 = 0x08,
445             Sequence2 = 0x0C,
446             ChannelEnable = 0x10,
447             ChannelDisable = 0x14,
448             ChannelStatus = 0x18,
449             LastConvertedData = 0x20,
450             InterruptEnable = 0x24,
451             InterruptDisable = 0x28,
452             InterruptMask = 0x2C,
453             InterruptStatus = 0x30,
454             OverrunStatus = 0x3C,
455             ExtendedMode = 0x40,
456             CompareWindow = 0x44,
457             ChannelGain = 0x48,
458             ChannelOffset = 0x4C,
459             // NOTE: This is offset of the first register;
460             //       there are `NumberOfChannels` registers, one for each channel
461             ChannelData0 = 0x50,
462             AnalogControl = 0x94,
463             WriteProtectMode = 0xE4,
464             WriteProtectStatus = 0xE8,
465             // NOTE: Those registers are used by PDC block which isn't currently supported
466             //       by this peripheral
467             ReceivePointer = 0x100,
468             ReceiveCounter = 0x104,
469             ReceiveNextPointer = 0x110,
470             ReceiveNextCounter = 0x114,
471             TransferControl = 0x120,
472             TransferStatus = 0x124
473         }
474     }
475 }
476