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