1 // 2 // Copyright (c) 2010-2020 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 Antmicro.Renode.Core; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Exceptions; 14 using Antmicro.Renode.Logging; 15 using Antmicro.Renode.Utilities; 16 17 namespace Antmicro.Renode.Peripherals.Analog 18 { 19 public class EOSS3_ADC : BasicDoubleWordPeripheral, IKnownSize 20 { EOSS3_ADC(IMachine machine)21 public EOSS3_ADC(IMachine machine) : base(machine) 22 { 23 channels = new Channel[] { new Channel(this, 0), new Channel(this, 1) }; 24 DefineRegisters(); 25 } 26 FeedSample(uint value, uint channelNo, int repeat = 1)27 public void FeedSample(uint value, uint channelNo, int repeat = 1) 28 { 29 if(channelNo > 1) 30 { 31 throw new RecoverableException("Only channels 0/1 are supported"); 32 } 33 34 var sample = new Sample { Value = value }; 35 channels[channelNo].FeedSample(sample, repeat); 36 } 37 FeedSample(string path, uint channelNo, int repeat = 1)38 public void FeedSample(string path, uint channelNo, int repeat = 1) 39 { 40 if(channelNo > 1) 41 { 42 throw new RecoverableException("Only channels 0/1 are supported"); 43 } 44 45 var parsedSamples = ParseSamplesFile(path); 46 channels[channelNo].FeedSample(parsedSamples, repeat); 47 } 48 Reset()49 public override void Reset() 50 { 51 base.Reset(); 52 state = State.Idle; 53 54 foreach(var c in channels) 55 { 56 c.Reset(); 57 } 58 } 59 60 public long Size => 0x10; 61 ParseSamplesFile(string path)62 private IEnumerable<Sample> ParseSamplesFile(string path) 63 { 64 var localQueue = new Queue<Sample>(); 65 var lineNumber = 0; 66 67 try 68 { 69 using(var reader = File.OpenText(path)) 70 { 71 var line = ""; 72 while((line = reader.ReadLine()) != null) 73 { 74 ++lineNumber; 75 if(!uint.TryParse(line.Trim(), out var sample)) 76 { 77 throw new RecoverableException($"Wrong data file format at line {lineNumber}. Expected an unsigned integer number, but got '{line}'"); 78 } 79 80 localQueue.Enqueue(new Sample { Value = sample }); 81 } 82 } 83 } 84 catch(Exception e) 85 { 86 if(e is RecoverableException) 87 { 88 throw; 89 } 90 91 // this is to nicely handle IO errors in monitor 92 throw new RecoverableException(e.Message); 93 } 94 95 return localQueue; 96 } 97 DefineRegisters()98 private void DefineRegisters() 99 { 100 Registers.Out.Define(this) 101 .WithValueField(0, 12, FieldMode.Read, name: "out", valueProviderCallback: _ => channels[selectedChannel1.Value ? 1 : 0].GetSample()) 102 .WithReservedBits(12, 20) 103 ; 104 105 Registers.Status.Define(this) 106 .WithFlag(0, FieldMode.Read, name: "eoc", valueProviderCallback: _ => 107 { 108 switch(state) 109 { 110 case State.Idle: 111 return true; 112 113 case State.ConversionStarted: 114 channels[selectedChannel1.Value ? 1 : 0].PrepareSample(); 115 state = State.SampleReady; 116 return false; 117 118 case State.SampleReady: 119 state = State.Idle; 120 return true; 121 122 default: 123 throw new ArgumentException($"Unexpected state: {state}"); 124 } 125 }) 126 .WithReservedBits(1, 31) 127 ; 128 129 Registers.Control.Define(this) 130 .WithFlag(0, name: "soc", writeCallback: (_, val) => StartConversion(val)) 131 .WithFlag(1, out selectedChannel1, name: "sel") 132 .WithFlag(2, name: "meas_en") 133 .WithReservedBits(3, 28) 134 ; 135 } 136 StartConversion(bool flag)137 private void StartConversion(bool flag) 138 { 139 state = flag 140 ? State.ConversionStarted 141 : State.Idle; 142 } 143 144 private State state; 145 private IFlagRegisterField selectedChannel1; 146 147 private readonly Channel[] channels; 148 149 private class Channel 150 { Channel(EOSS3_ADC parent, int id)151 public Channel(EOSS3_ADC parent, int id) 152 { 153 this.id = id; 154 this.parent = parent; 155 samples = new Queue<Sample>(); 156 } 157 PrepareSample()158 public void PrepareSample() 159 { 160 lock(samples) 161 { 162 if(samples.Count == 0 && BufferedSamples != null) 163 { 164 samples.EnqueueRange(BufferedSamples); 165 } 166 167 if(!samples.TryDequeue(out currentSample)) 168 { 169 currentSample = new Sample(); 170 parent.Log(LogLevel.Warning, "No more samples available in channel {0}", id); 171 } 172 } 173 } 174 GetSample()175 public uint GetSample() 176 { 177 return currentSample.Value; 178 } 179 FeedSample(Sample sample, int repeat = 1)180 public void FeedSample(Sample sample, int repeat = 1) 181 { 182 lock(samples) 183 { 184 if(repeat == -1) 185 { 186 BufferedSamples = new Sample[] { sample }; 187 } 188 else 189 { 190 BufferedSamples = null; 191 for(var i = 0; i < repeat; i++) 192 { 193 samples.Enqueue(sample); 194 } 195 } 196 } 197 } 198 FeedSample(IEnumerable<Sample> samplesCollection, int repeat = 1)199 public void FeedSample(IEnumerable<Sample> samplesCollection, int repeat = 1) 200 { 201 lock(samples) 202 { 203 if(repeat == -1) 204 { 205 BufferedSamples = samplesCollection; 206 } 207 else 208 { 209 BufferedSamples = null; 210 for(var i = 0; i < repeat; i++) 211 { 212 samples.EnqueueRange(samplesCollection); 213 } 214 } 215 } 216 } 217 Reset()218 public void Reset() 219 { 220 lock(samples) 221 { 222 BufferedSamples = null; 223 samples.Clear(); 224 currentSample = new Sample(); 225 } 226 } 227 228 public IEnumerable<Sample> BufferedSamples { get; private set; } 229 230 private Sample currentSample; 231 232 private readonly Queue<Sample> samples; 233 private readonly int id; 234 private readonly EOSS3_ADC parent; 235 } 236 237 private struct Sample 238 { 239 public uint Value; 240 } 241 242 private enum State 243 { 244 Idle, 245 ConversionStarted, 246 SampleReady 247 } 248 249 private enum Registers 250 { 251 Out = 0x0, 252 Status = 0x4, 253 Control = 0x8 254 } 255 } 256 } 257