1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Structure.Registers;
10 using Antmicro.Renode.Exceptions;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.I2C;
13 using Antmicro.Renode.Peripherals.Sensor;
14 using Antmicro.Renode.Utilities;
15 using Antmicro.Renode.Utilities.RESD;
16 
17 namespace Antmicro.Renode.Peripherals.Sensors
18 {
19     public abstract class AK0991x : II2CPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, IMagneticSensor, IUnderstandRESD
20     {
AK0991x(IMachine machine)21         public AK0991x(IMachine machine)
22         {
23             RegistersCollection = new ByteRegisterCollection(this);
24             DefineRegisters();
25         }
26 
FeedMagneticSamplesFromRESD(ReadFilePath filePath, uint channelId = 0, RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.Specified, long sampleOffsetTime = 0)27         public void FeedMagneticSamplesFromRESD(ReadFilePath filePath, uint channelId = 0,
28             RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.Specified, long sampleOffsetTime = 0)
29         {
30             magResdStream?.Dispose();
31             magResdStream = this.CreateRESDStream<MagneticSample>(filePath, channelId, sampleOffsetType, sampleOffsetTime);
32             this.Log(LogLevel.Noisy, "RESD stream set to {0}", filePath);
33         }
34 
Write(byte[] data)35         public void Write(byte[] data)
36         {
37             this.Log(LogLevel.Noisy, "Writing bytes: {0}", Misc.PrettyPrintCollectionHex(data));
38             foreach(var b in data)
39             {
40                 switch(state)
41                 {
42                     case State.Idle:
43                         selectedRegister = (Registers)b;
44                         this.Log(LogLevel.Noisy, "Selected register: 0x{0:X}", selectedRegister);
45                         state = State.ReceivedFirstByte;
46                         break;
47                     case State.ReceivedFirstByte:
48                     case State.WritingWaitingForValue:
49                         this.Log(LogLevel.Noisy, "Writing to register 0x{0:X} value 0x{1:X}", selectedRegister, b);
50                         RegistersCollection.Write((byte)selectedRegister, b);
51                         state = State.WritingWaitingForValue;
52                         selectedRegister = IICGetNextRegister();
53 
54                         break;
55                     case State.Reading:
56                         //this isn't documented, but reads are able to use address set during write transfer, opposite isn't true
57                         this.Log(LogLevel.Warning, "Trying to write without specifying address, byte is omitted");
58                         break;
59                 }
60             }
61         }
62 
Read(int count)63         public byte[] Read(int count)
64         {
65             state = State.Reading; //reading can be started regardless of state, last selectedRegister is used
66             var buf = new byte[count];
67             for(var i = 0; i < buf.Length; i++)
68             {
69                 buf[i] = RegistersCollection.Read((byte)selectedRegister);
70                 selectedRegister = IICGetNextRegister();
71             }
72             this.Log(LogLevel.Noisy, "Reading bytes: {0}", Misc.PrettyPrintCollectionHex(buf));
73 
74             return buf;
75         }
76 
FinishTransmission()77         public void FinishTransmission()
78         {
79             if(state != State.ReceivedFirstByte) //in case of reading we may (documentation permits this or repeated START) receive STOP before the read transfer
80             {
81                 state = State.Idle;
82             }
83         }
84 
Reset()85         public void Reset()
86         {
87             SoftwareReset();
88             magResdStream?.Dispose();
89             magResdStream = null;
90         }
91 
92         public ByteRegisterCollection RegistersCollection { get; }
93 
94         public int MagneticFluxDensityX
95         {
96             get => GetSampleFromRESDStream(ref magResdStream, Direction.X);
97             set => throw new RecoverableException($"Explicitly setting magnetic flux density is not supported by this model. " +
98                 $"Magnetic flux density should be provided from a RESD file or set via the '{nameof(DefaultMagneticFluxDensityX)}' property");
99         }
100         public int MagneticFluxDensityY
101         {
102             get => GetSampleFromRESDStream(ref magResdStream, Direction.Y);
103             set => throw new RecoverableException($"Explicitly setting magnetic flux density is not supported by this model. " +
104                 $"Magnetic flux density should be provided from a RESD file or set via the '{nameof(DefaultMagneticFluxDensityY)}' property");
105         }
106         public int MagneticFluxDensityZ
107         {
108             get => GetSampleFromRESDStream(ref magResdStream, Direction.Z);
109             set => throw new RecoverableException($"Explicitly setting magnetic flux density is not supported by this model. " +
110                 $"Magnetic flux density should be provided from a RESD file or set via the '{nameof(DefaultMagneticFluxDensityZ)}' property");
111         }
112 
113         public int DefaultMagneticFluxDensityX { get; set; }
114         public int DefaultMagneticFluxDensityY { get; set; }
115         public int DefaultMagneticFluxDensityZ { get; set; }
116 
117         public abstract byte CompanyID { get; }
118         public abstract byte DeviceID { get; }
119 
GetMagneticSampleValueDefault(Direction d)120         private int GetMagneticSampleValueDefault(Direction d)
121         {
122             switch(d)
123             {
124             case Direction.X:
125                 return DefaultMagneticFluxDensityX;
126             case Direction.Y:
127                 return DefaultMagneticFluxDensityY;
128             case Direction.Z:
129                 return DefaultMagneticFluxDensityZ;
130             default:
131                 throw new Exception("Unreachable");
132             }
133         }
134 
GetMagneticSampleValue(MagneticSample sample, Direction d)135         private int GetMagneticSampleValue(MagneticSample sample, Direction d)
136         {
137             switch(d)
138             {
139                 case Direction.X:
140                     return sample.MagneticFluxDensityX;
141                 case Direction.Y:
142                     return sample.MagneticFluxDensityY;
143                 case Direction.Z:
144                     return sample.MagneticFluxDensityZ;
145                 default:
146                     throw new Exception("Unreachable");
147             }
148         }
149 
GetSampleFromRESDStream(ref RESDStream<MagneticSample> stream, Direction d)150         private int GetSampleFromRESDStream(ref RESDStream<MagneticSample> stream, Direction d)
151         {
152             if(mode.Value == Mode.PowerDown)
153             {
154                 this.Log(LogLevel.Error, "Tried to read sample while in Power down mode, getting default value.");
155                 return GetMagneticSampleValueDefault(d);
156             }
157             if(stream == null)
158             {
159                 this.Log(LogLevel.Noisy, "RESD stream not found, getting default value");
160                 return GetMagneticSampleValueDefault(d);
161             }
162 
163             switch(magResdStream.TryGetCurrentSample(this, out var sample, out var _))
164             {
165                 case RESDStreamStatus.OK:
166                     this.Log(LogLevel.Noisy, "RESD stream status OK, setting sample: {0}", sample);
167                     return GetMagneticSampleValue(sample, d);
168                 case RESDStreamStatus.BeforeStream:
169                     this.Log(LogLevel.Noisy, "RESD before stream status, setting default value");
170                     return GetMagneticSampleValueDefault(d);
171                 case RESDStreamStatus.AfterStream:
172                     this.Log(LogLevel.Noisy, "RESD after stream status, setting default value");
173                     magResdStream.Dispose();
174                     magResdStream = null;
175                     return GetMagneticSampleValueDefault(d);
176                 default:
177                     throw new Exception("Unreachable");
178             }
179         }
180 
IICGetNextRegister()181         private Registers IICGetNextRegister()
182         {
183             if(selectedRegister == Registers.Reserved2)
184             {
185                 return Registers.Status1;
186             }
187             else if(selectedRegister == Registers.Status2)
188             {
189                 return Registers.CompanyID;
190             }
191             else if(selectedRegister == Registers.Control3)
192             {
193                 return Registers.Control1;
194             }
195             return selectedRegister + 1;
196         }
197 
SoftwareReset()198         private void SoftwareReset()
199         {
200             RegistersCollection.Reset();
201             selectedRegister = 0;
202         }
203 
DefineRegisters()204         private void DefineRegisters()
205         {
206             Registers.CompanyID.Define(this)
207                 .WithValueField(0, 8, FieldMode.Read, name: "WIA1",
208                         valueProviderCallback: _ => CompanyID);
209 
210             Registers.DeviceID.Define(this)
211                 .WithValueField(0, 8, FieldMode.Read, name: "WIA2",
212                         valueProviderCallback: _ => DeviceID);
213 
214             Registers.Status1.Define(this)
215                 .WithReservedBits(2, 6)
216                 .WithFlag(1, FieldMode.Read, name: "DOR", valueProviderCallback: _ => false)
217                 .WithFlag(0, FieldMode.Read, name: "DRDY", valueProviderCallback: _ => true);
218 
219             Registers.XAxisMeasurementDataLower.Define(this)
220                 .WithValueField(0, 8, FieldMode.Read, name: "HX_Low",
221                         valueProviderCallback: _ =>
222                             (byte)BitHelper.GetValue((uint)(MagneticFluxDensityX / SensorSensitivity), 0, 8));
223 
224             Registers.XAxisMeasurementDataUpper.Define(this)
225                 .WithValueField(0, 8, FieldMode.Read, name: "HX_High",
226                         valueProviderCallback: _ =>
227                             (byte)BitHelper.GetValue((uint)(MagneticFluxDensityX / SensorSensitivity), 8, 8));
228 
229             Registers.YAxisMeasurementDataLower.Define(this)
230                 .WithValueField(0, 8, FieldMode.Read, name: "HY_Low",
231                         valueProviderCallback: _ =>
232                             (byte)BitHelper.GetValue((uint)(MagneticFluxDensityY / SensorSensitivity), 0, 8));
233 
234             Registers.YAxisMeasurementDataUpper.Define(this)
235                 .WithValueField(0, 8, FieldMode.Read, name: "HY_High",
236                         valueProviderCallback: _ =>
237                             (byte)BitHelper.GetValue((uint)(MagneticFluxDensityY / SensorSensitivity), 8, 8));
238 
239             Registers.ZAxisMeasurementDataLower.Define(this)
240                 .WithValueField(0, 8, FieldMode.Read, name: "ZY_Low",
241                         valueProviderCallback: _ =>
242                             (byte)BitHelper.GetValue((uint)(MagneticFluxDensityZ / SensorSensitivity), 0, 8));
243 
244             Registers.ZAxisMeasurementDataUpper.Define(this)
245                 .WithValueField(0, 8, FieldMode.Read, name: "ZY_High",
246                         valueProviderCallback: _ =>
247                             (byte)BitHelper.GetValue((uint)(MagneticFluxDensityZ / SensorSensitivity), 8, 8));
248 
249             Registers.Dummy.Define(this)
250                 .WithValueField(0, 8, FieldMode.Read, name: "DUMMY");
251 
252             Registers.Status2.Define(this)
253                 .WithReservedBits(4, 4)
254                 .WithFlag(3, name: "HOFL", valueProviderCallback: _ => false)
255                 .WithReservedBits(0, 3);
256 
257             Registers.Control1.Define(this)
258                 .WithTag("CTRL1", 0, 8);
259 
260             Registers.Control2.Define(this)
261                 .WithReservedBits(5, 3)
262                 .WithEnumField<ByteRegister, Mode>(0, 5, out mode, name: "MODE");
263 
264             Registers.Control3.Define(this)
265                 .WithReservedBits(1, 7)
266                 .WithFlag(0, name: "SRST",
267                         valueProviderCallback: _ => false,
268                         writeCallback: (_, value) =>
269                         {
270                             if(value)
271                             {
272                                 SoftwareReset();
273                             }
274                         });
275 
276             Registers.Test1.Define(this)
277                 .WithReservedBits(0, 8);
278 
279             Registers.Test2.Define(this)
280                 .WithReservedBits(0, 8);
281         }
282 
283         private RESDStream<MagneticSample> magResdStream;
284 
285         private IEnumRegisterField<Mode> mode;
286         private Registers selectedRegister;
287         private State state;
288 
289         private const int SensorSensitivity = 150; // nT/LSB
290 
291         private enum State
292         {
293             Idle,
294             Reading,
295             Writing,
296             ReceivedFirstByte,
297             WaitingForAddress,
298             WritingWaitingForValue,
299         }
300 
301         private enum Registers : byte
302         {
303             CompanyID = 0x0,
304             DeviceID = 0x1,
305             Reserved1 = 0x2,
306             Reserved2 = 0x3,
307             Status1 = 0x10,
308             XAxisMeasurementDataLower = 0x11,
309             XAxisMeasurementDataUpper = 0x12,
310             YAxisMeasurementDataLower = 0x13,
311             YAxisMeasurementDataUpper = 0x14,
312             ZAxisMeasurementDataLower = 0x15,
313             ZAxisMeasurementDataUpper = 0x16,
314             Dummy = 0x17,
315             Status2 = 0x18,
316             Control1 = 0x30,
317             Control2 = 0x31,
318             Control3 = 0x32,
319             Test1 = 0x33,
320             Test2 = 0x34
321         }
322 
323         private enum Mode : byte
324         {
325             PowerDown = 0x0,
326             SingleMeasurement = 0x1,
327             ContinuousMeasurement1 = 0x2,
328             ContinuousMeasurement2 = 0x4,
329             ContinuousMeasurement3 = 0x6,
330             ContinuousMeasurement4 = 0x8,
331             SelfTest = 0x10,
332         }
333 
334         private enum Direction : byte
335         {
336             X = 0x0,
337             Y = 0x1,
338             Z = 0x2
339         }
340     }
341 }
342