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