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