1 //
2 // Copyright (c) 2010-2023 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.Collections.Generic;
10 using System.IO;
11 using System.Linq;
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Core.Structure.Registers;
14 using Antmicro.Renode.Exceptions;
15 using Antmicro.Renode.Logging;
16 using Antmicro.Renode.Utilities;
17 using Antmicro.Renode.Time;
18 using Antmicro.Renode.Peripherals.Bus;
19 using Antmicro.Renode.Peripherals.Timers;
20 
21 namespace Antmicro.Renode.Peripherals.Analog
22 {
23    // STM32 ADC has many features and only a partial subset are implemented here
24    //
25    // Supported:
26    // * Software triggered conversion
27    // * Single conversion
28    // * Scan mode with regular group
29    // * Continuous conversion
30    // * Modes of use
31    //   - Polling (read EOC status flag)
32    //   - Interrupt (enable ADC interrupt for EOC)
33    //   - DMA (enable DMA and configure stream for peripheral to memory transfer)
34    //
35    // Not Implemented:
36    // * Analog watchdog
37    // * Overrun detection
38    // * External triggers
39    // * Injected channels
40    // * Sampling time (time is fixed)
41    // * Discontinuous mode
42    // * Multi-ADC (i.e. Dual/Triple) mode
43    public class STM32_ADC : BasicDoubleWordPeripheral, IKnownSize
44    {
STM32_ADC(IMachine machine)45       public STM32_ADC(IMachine machine) : base(machine)
46       {
47          channels = Enumerable.Range(0, NumberOfChannels).Select(x => new ADCChannel(this, x)).ToArray();
48 
49          DefineRegisters();
50 
51          // Sampling time fixed
52          samplingTimer = new LimitTimer(
53                machine.ClockSource, 1000000, this, "samplingClock",
54                limit: 100,
55                eventEnabled: true,
56                direction: Direction.Ascending,
57                enabled: false,
58                autoUpdate: false,
59                workMode: WorkMode.OneShot);
60          samplingTimer.LimitReached += OnConversionFinished;
61       }
62 
FeedSample(uint value, uint channelIdx, int repeat = 1)63       public void FeedSample(uint value, uint channelIdx, int repeat = 1)
64       {
65          if(IsValidChannel(channelIdx))
66          {
67             channels[channelIdx].FeedSample(value, repeat);
68          }
69       }
70 
FeedSample(string path, uint channelIdx, int repeat = 1)71       public void FeedSample(string path, uint channelIdx, int repeat = 1)
72       {
73          if(IsValidChannel(channelIdx))
74          {
75             var parsedSamples = ADCChannel.ParseSamplesFile(path);
76             channels[channelIdx].FeedSample(parsedSamples, repeat);
77          }
78       }
79 
IsValidChannel(uint channelIdx)80       private bool IsValidChannel(uint channelIdx)
81       {
82          if(channelIdx >= NumberOfChannels)
83          {
84             throw new RecoverableException("Only channels 0/1 are supported");
85          }
86          return true;
87       }
88 
Reset()89       public override void Reset()
90       {
91          base.Reset();
92          foreach(var c in channels)
93          {
94             c.Reset();
95          }
96       }
97 
98       public long Size => 0x50;
99 
100       public GPIO IRQ { get; } = new GPIO();
101       public GPIO DMARequest { get; } = new GPIO();
102 
DefineRegisters()103       private void DefineRegisters()
104       {
105          Registers.Status.Define(this)
106             .WithTaggedFlag("Analog watchdog flag", 0)
107             .WithFlag(1, out endOfConversion, name: "Regular channel end of conversion")
108             .WithTaggedFlag("Injected channel end of conversion", 2)
109             .WithTaggedFlag("Injected channel start flag", 3)
110             .WithTaggedFlag("Regular channel start flag", 4)
111             .WithTaggedFlag("Overrun", 5)
112             .WithReservedBits(6, 26);
113 
114          Registers.Control1.Define(this)
115             .WithTag("Analog watchdog channel select bits", 0, 5)
116             .WithFlag(5, out eocInterruptEnable, name: "Interrupt enable for EOC")
117             .WithTaggedFlag("Analog watchdog interrupt enable", 6)
118             .WithTaggedFlag("Interrupt enable for injected channels", 7)
119             .WithFlag(8, out scanMode, name: "Scan mode")
120             .WithTaggedFlag("Enable the watchdog on a single channel in scan mode", 9)
121             .WithTaggedFlag("Automatic injected group conversion", 10)
122             .WithTaggedFlag("Discontinuous mode on regular channels", 11)
123             .WithTaggedFlag("Discontinuous mode on injected channels", 12)
124             .WithTag("Discontinuous mode channel count", 13, 3)
125             .WithReservedBits(16, 6)
126             .WithTaggedFlag("Analog watchdog enable on injected channels", 22)
127             .WithTaggedFlag("Analog watchdog enable on regular channels", 23)
128             .WithTag("Resolution", 24, 2)
129             .WithTaggedFlag("Overrun interrupt enable", 26)
130             .WithReservedBits(27, 5);
131 
132          Registers.Control2.Define(this, name: "Control2")
133             .WithFlag(0, out adcOn,
134                   name: "A/D Converter ON/OFF",
135                   changeCallback: (_, val) => { if(val) { EnableADC(); }})
136             .WithFlag(1, out continuousConversion, name: "Continous conversion")
137             .WithReservedBits(2, 6)
138             .WithFlag(8, out dmaEnabled, name: "Direct memory access mode")
139             .WithFlag(9, out dmaIssueRequest, name: "DMA disable selection")
140             .WithFlag(10, out endOfConversionSelect, name: "End of conversion select")
141             .WithTaggedFlag("Data Alignment", 11)
142             .WithReservedBits(12, 4)
143             .WithTag("External event select for injected group", 16, 4)
144             .WithTag("External trigger enable for injected channels", 20, 2)
145             .WithTaggedFlag("Start conversion of injected channels", 22)
146             .WithReservedBits(23, 1)
147             .WithTag("External event select for regular group", 24, 4)
148             .WithTag("External trigger enable for regular channels", 28, 2)
149             .WithFlag(30,
150                   name: "Start Conversion Of Regular Channels",
151                   writeCallback: (_, value) => { if(value) StartConversion(); },
152                   valueProviderCallback: _ => false)
153             .WithReservedBits(31, 1);
154 
155          Registers.SampleTime1.Define(this)
156             .WithTag("Channel 10 sampling time", 0, 3)
157             .WithTag("Channel 11 sampling time", 3, 3)
158             .WithTag("Channel 12 sampling time", 6, 3)
159             .WithTag("Channel 13 sampling time", 9, 3)
160             .WithTag("Channel 14 sampling time", 12, 3)
161             .WithTag("Channel 15 sampling time", 15, 3)
162             .WithTag("Channel 16 sampling time", 18, 3)
163             .WithTag("Channel 17 sampling time", 21, 3)
164             .WithTag("Channel 18 sampling time", 24, 3)
165             .WithReservedBits(27, 5);
166 
167          Registers.SampleTime2.Define(this)
168             .WithTag("Channel 0 sampling time", 0, 3)
169             .WithTag("Channel 1 sampling time", 3, 3)
170             .WithTag("Channel 2 sampling time", 6, 3)
171             .WithTag("Channel 3 sampling time", 9, 3)
172             .WithTag("Channel 4 sampling time", 12, 3)
173             .WithTag("Channel 5 sampling time", 15, 3)
174             .WithTag("Channel 6 sampling time", 18, 3)
175             .WithTag("Channel 7 sampling time", 21, 3)
176             .WithTag("Channel 8 sampling time", 24, 3)
177             .WithTag("Channel 9 sampling time", 27, 3)
178             .WithReservedBits(30, 2);
179 
180          Registers.InjectedChannelDataOffset1.Define(this)
181             .WithTag("Data offset for injected channel 1", 0, 12)
182             .WithReservedBits(12, 20);
183          Registers.InjectedChannelDataOffset2.Define(this)
184             .WithTag("Data offset for injected channel 2", 0, 12)
185             .WithReservedBits(12, 20);
186          Registers.InjectedChannelDataOffset3.Define(this)
187             .WithTag("Data offset for injected channel 3", 0, 12)
188             .WithReservedBits(12, 20);
189          Registers.InjectedChannelDataOffset4.Define(this)
190             .WithTag("Data offset for injected channel 4", 0, 12)
191             .WithReservedBits(12, 20);
192 
193          Registers.RegularSequence1.Define(this)
194             .WithValueField(0, 5, out regularSequence[12], name: "13th conversion in regular sequence")
195             .WithValueField(5, 5, out regularSequence[13], name: "14th conversion in regular sequence")
196             .WithValueField(10, 5, out regularSequence[14], name: "15th conversion in regular sequence")
197             .WithValueField(15, 5, out regularSequence[15], name: "16th conversion in regular sequence")
198             .WithValueField(20, 4, writeCallback: (_, val) => { regularSequenceLen = (uint)val + 1; }, name: "Regular channel sequence length");
199 
200          Registers.RegularSequence2.Define(this)
201             .WithValueField(0, 5, out regularSequence[6], name: "7th conversion in regular sequence")
202             .WithValueField(5, 5, out regularSequence[7], name: "8th conversion in regular sequence")
203             .WithValueField(10, 5, out regularSequence[8], name: "9th conversion in regular sequence")
204             .WithValueField(15, 5, out regularSequence[9], name: "10th conversion in regular sequence")
205             .WithValueField(20, 5, out regularSequence[10], name: "11th conversion in regular sequence")
206             .WithValueField(25, 5, out regularSequence[11], name: "12th conversion in regular sequence");
207 
208          Registers.RegularSequence3.Define(this)
209             .WithValueField(0, 5, out regularSequence[0], name: "1st conversion in regular sequence")
210             .WithValueField(5, 5, out regularSequence[1], name: "2nd conversion in regular sequence")
211             .WithValueField(10, 5, out regularSequence[2], name: "3rd conversion in regular sequence")
212             .WithValueField(15, 5, out regularSequence[3], name: "4th conversion in regular sequence")
213             .WithValueField(20, 5, out regularSequence[4], name: "5th conversion in regular sequence")
214             .WithValueField(25, 5, out regularSequence[5], name: "6th conversion in regular sequence");
215 
216          // Data register
217          Registers.RegularData.Define(this)
218             .WithValueField(0, 32,
219                   valueProviderCallback: _ =>
220                   {
221                       this.Log(LogLevel.Debug, "Reading ADC data {0}", adcData);
222                       // Reading ADC_DR should clear EOC
223                       endOfConversion.Value = false;
224                       IRQ.Set(false);
225                       return adcData;
226                   });
227       }
228 
EnableADC()229       private void EnableADC()
230       {
231           currentChannel = channels[regularSequence[currentChannelIdx].Value];
232       }
233 
StartConversion()234       private void StartConversion()
235       {
236          if(adcOn.Value)
237          {
238              this.Log(LogLevel.Debug, "Starting conversion time={0}",
239                    machine.ElapsedVirtualTime.TimeElapsed);
240 
241              // Enable timer, which will simulate conversion being performed.
242              samplingTimer.Enabled = true;
243          }
244          else
245          {
246              this.Log(LogLevel.Warning, "Trying to start conversion while ADC off");
247          }
248       }
249 
OnConversionFinished()250       private void OnConversionFinished()
251       {
252          this.Log(LogLevel.Debug, "OnConversionFinished: time={0} channel={1}",
253                machine.ElapsedVirtualTime.TimeElapsed,
254                currentChannelIdx);
255 
256          // Set data register and trigger DMA request
257          currentChannel.PrepareSample();
258          adcData = currentChannel.GetSample();
259          if(dmaEnabled.Value && dmaIssueRequest.Value)
260          {
261             // Issue DMA peripheral request, which when mapped to DMA
262             // controller will trigger a peripheral to memory transfer
263             DMARequest.Set();
264             DMARequest.Unset();
265          }
266 
267          var scanModeActive = scanMode.Value && currentChannelIdx < regularSequenceLen - 1;
268          var scanModeFinished = scanMode.Value && currentChannelIdx == regularSequenceLen - 1;
269 
270          // Signal EOC if EOCS set with scan mode enabled and finished or we finished scanning regular group
271          endOfConversion.Value = scanModeActive ? (endOfConversionSelect.Value || scanModeFinished) : true;
272 
273          // Iterate to next channel
274          currentChannelIdx = (currentChannelIdx + 1) % regularSequenceLen;
275          currentChannel = channels[regularSequence[currentChannelIdx].Value];
276 
277          // Auto trigger next conversion if we're scanning or CONT bit set
278          samplingTimer.Enabled = scanModeActive || continuousConversion.Value;
279 
280          // Trigger EOC interrupt
281          if(endOfConversion.Value && eocInterruptEnable.Value)
282          {
283             this.Log(LogLevel.Debug, "OnConversionFinished: Set IRQ");
284             IRQ.Set(true);
285          }
286       }
287 
288       // Control 1/2 fields
289       private IFlagRegisterField scanMode;
290       private IFlagRegisterField endOfConversion;
291       private IFlagRegisterField adcOn;
292       private IFlagRegisterField endOfConversionSelect;
293       private IFlagRegisterField eocInterruptEnable;
294       private IFlagRegisterField continuousConversion;
295 
296       private IFlagRegisterField dmaEnabled;
297       private IFlagRegisterField dmaIssueRequest;
298 
299       // Sampling timer. Provides time-based event for driving conversion of
300       // regular channel sequence.
301       private readonly LimitTimer samplingTimer;
302 
303       // Data sample to be returned from data register when read.
304       private uint adcData;
305 
306       // Regular sequence settings, i.e. the channels and order of channels
307       // for performing conversion
308       private uint regularSequenceLen;
309       private readonly IValueRegisterField[] regularSequence = new IValueRegisterField[19];
310 
311       // Channel objects, for managing input test data
312       private uint currentChannelIdx;
313       private ADCChannel currentChannel;
314       private readonly ADCChannel[] channels;
315 
316       public const int NumberOfChannels = 19;
317 
318       private enum Registers
319       {
320          Status = 0x0,
321          Control1 = 0x4,
322          Control2 = 0x8,
323          SampleTime1 = 0x0C,
324          SampleTime2 = 0x10,
325          InjectedChannelDataOffset1 = 0x14,
326          InjectedChannelDataOffset2 = 0x18,
327          InjectedChannelDataOffset3 = 0x1C,
328          InjectedChannelDataOffset4 = 0x20,
329          WatchdogHigherThreshold = 0x24,
330          WatchdogLowerThreshold = 0x28,
331          RegularSequence1 = 0x2C,
332          RegularSequence2 = 0x30,
333          RegularSequence3 = 0x34,
334          InjectedSequence = 0x38,
335          InjectedData1 = 0x3C,
336          InjectedData2 = 0x40,
337          InjectedData3 = 0x44,
338          InjectedData4 = 0x48,
339          RegularData = 0x4C
340       }
341    }
342 }
343