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 System.Linq; 9 using System.Collections.Generic; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Peripherals.I2C; 15 using Antmicro.Renode.Peripherals.SPI; 16 using Antmicro.Renode.Utilities; 17 18 namespace Antmicro.Renode.Peripherals.Sensors 19 { 20 public partial class ICM20948 : II2CPeripheral, ISPIPeripheral, IGPIOReceiver, IProvidesRegisterCollection<ByteRegisterCollection>, IPeripheralContainer<II2CPeripheral, NumberRegistrationPoint<int>> 21 { ICM20948(IMachine machine)22 public ICM20948(IMachine machine) 23 { 24 this.machine = machine; 25 IRQ = new GPIO(); 26 27 gyroAccelUserBank0Registers = new ByteRegisterCollection(this); 28 gyroAccelUserBank1Registers = new ByteRegisterCollection(this); 29 gyroAccelUserBank2Registers = new ByteRegisterCollection(this); 30 gyroAccelUserBank3Registers = new ByteRegisterCollection(this); 31 32 i2cContainer = new SimpleContainerHelper<II2CPeripheral>(machine, this); 33 34 userBankRegisters = new Dictionary<ulong, ByteRegisterCollection> 35 { 36 {0, gyroAccelUserBank0Registers}, 37 {1, gyroAccelUserBank1Registers}, 38 {2, gyroAccelUserBank2Registers}, 39 {3, gyroAccelUserBank3Registers}, 40 }; 41 42 DefineGyroAccelUserBank0Registers(); 43 DefineGyroAccelUserBank1Registers(); 44 DefineGyroAccelUserBank2Registers(); 45 DefineGyroAccelUserBank3Registers(); 46 } 47 Write(byte[] data)48 public void Write(byte[] data) 49 { 50 var offset = 0; 51 this.NoisyLog("Write {0}", data.ToLazyHexString()); 52 53 switch(state) 54 { 55 case State.Idle: 56 selectedRegister = data[offset++]; 57 this.DebugLog("Selected bank #{0} register 0x{1:X} ({2})", userBankSelected, selectedRegister, SelectedRegisterName); 58 59 state = State.ReceivedFirstByte; 60 61 if(data.Length == offset) 62 { 63 break; 64 } 65 goto case State.ReceivedFirstByte; 66 67 case State.ReceivedFirstByte: 68 case State.WritingWaitingForValue: 69 var startingRegister = selectedRegister; 70 71 foreach(var b in data.Skip(offset)) 72 { 73 RegistersCollection.Write(selectedRegister++, b); 74 } 75 76 this.DebugLog("Write at bank #{0} from 0x{1:X} to 0x{2:X}: data {3}", userBankSelected, startingRegister, selectedRegister, SelectedRegisterName, data.Skip(offset).ToLazyHexString()); 77 state = State.WritingWaitingForValue; 78 break; 79 80 case State.Reading: 81 // Reads are able to use address set during write transfer, the opposite isn't true 82 this.WarningLog("Trying to write without specifying address, ignoring write"); 83 break; 84 85 default: 86 this.ErrorLog("Attempted write in an unexpected state: {0}", state); 87 break; 88 } 89 } 90 Read(int count)91 public byte[] Read(int count) 92 { 93 state = State.Reading; // Read can be started regardless of state, last selectedRegister is used 94 95 var startingRegister = selectedRegister; 96 this.DebugLog("Staring read of {0} bytes at bank #{1} from 0x{2:X} ({3})", count, userBankSelected, selectedRegister, SelectedRegisterName); 97 98 var result = Misc.Iterate(() => RegistersCollection.Read(selectedRegister++)).Take(count).ToArray(); 99 100 this.NoisyLog("Read at bank #{0} from 0x{1:X} to 0x{2:X}, returned {3}", userBankSelected, startingRegister, selectedRegister, Misc.PrettyPrintCollectionHex(result)); 101 return result; 102 } 103 II2CPeripheral.FinishTransmission()104 void II2CPeripheral.FinishTransmission() 105 { 106 if(state != State.ReceivedFirstByte) // in case of reading we may (documentation permits this or repeated START) receive STOP before the read transfer 107 { 108 state = State.Idle; 109 } 110 } 111 Transmit(byte data)112 public byte Transmit(byte data) 113 { 114 byte result = 0; 115 116 switch(state) 117 { 118 case State.Idle: 119 selectedRegister = BitHelper.GetValue(data, 0, 7); 120 var isRead = BitHelper.IsBitSet(data, 7); 121 122 if(isRead) 123 { 124 this.NoisyLog("Preparing for read at bank #{0} from 0x{1:X} ({2})", userBankSelected, selectedRegister, SelectedRegisterName); 125 state = State.Reading; 126 break; 127 } 128 129 this.NoisyLog("Preparing for write at bank #{0} to 0x{1:X} ({2})", userBankSelected, selectedRegister, SelectedRegisterName); 130 state = State.Writing; 131 break; 132 133 case State.Reading: 134 result = RegistersCollection.Read(selectedRegister); 135 this.NoisyLog("Read at bank #{0} from 0x{1:X} ({2}), returned 0x{3:X}", userBankSelected, selectedRegister, SelectedRegisterName, result); 136 selectedRegister++; 137 break; 138 139 case State.Writing: 140 this.DebugLog("Write at bank #{0} to 0x{1:X} ({2}): value 0x{3:X}", userBankSelected, selectedRegister, SelectedRegisterName, data); 141 RegistersCollection.Write(selectedRegister++, data); 142 break; 143 144 default: 145 this.ErrorLog("Attempted transmission in an unexpected state: {0}", state); 146 break; 147 } 148 149 this.NoisyLog("Received byte 0x{0:X}, returning 0x{1:X}", data, result); 150 return result; 151 } 152 ISPIPeripheral.FinishTransmission()153 void ISPIPeripheral.FinishTransmission() 154 { 155 this.NoisyLog("Finishing transmission, going idle"); 156 state = State.Idle; 157 } 158 OnGPIO(int number, bool value)159 public void OnGPIO(int number, bool value) 160 { 161 if(number != 0) 162 { 163 this.WarningLog("This model supports only CS on pin 0, but got signal on pin {0}", number); 164 return; 165 } 166 167 var previousChipSelected = chipSelected; 168 chipSelected = !value; 169 170 if(previousChipSelected && !chipSelected) 171 { 172 ((ISPIPeripheral)this).FinishTransmission(); 173 } 174 } 175 Reset()176 public void Reset() 177 { 178 SoftwareReset(); 179 180 accelerometerFeederThread?.Stop(); 181 accelerometerResdStream?.Dispose(); 182 accelerometerResdStream = null; 183 accelerometerFeederThread = null; 184 185 gyroFeederThread?.Stop(); 186 gyroResdStream?.Dispose(); 187 gyroResdStream = null; 188 gyroFeederThread = null; 189 190 magFeederThread?.Stop(); 191 magFeederThread?.Dispose(); 192 magFeederThread = null; 193 } 194 SoftwareReset()195 public void SoftwareReset() 196 { 197 gyroAccelUserBank0Registers.Reset(); 198 gyroAccelUserBank1Registers.Reset(); 199 gyroAccelUserBank2Registers.Reset(); 200 gyroAccelUserBank3Registers.Reset(); 201 202 chipSelected = false; 203 selectedRegister = 0x0; 204 state = State.Idle; 205 } 206 207 public ByteRegisterCollection RegistersCollection => userBankRegisters.GetOrDefault(userBankSelected, userBankRegisters[0]); 208 209 public GPIO IRQ { get; } 210 Register(II2CPeripheral peripheral, NumberRegistrationPoint<int> registrationPoint)211 public virtual void Register(II2CPeripheral peripheral, NumberRegistrationPoint<int> registrationPoint) => i2cContainer.Register(peripheral, registrationPoint); 212 Unregister(II2CPeripheral peripheral)213 public virtual void Unregister(II2CPeripheral peripheral) => i2cContainer.Unregister(peripheral); 214 215 public IEnumerable<NumberRegistrationPoint<int>> GetRegistrationPoints(II2CPeripheral peripheral) => i2cContainer.GetRegistrationPoints(peripheral); 216 217 IEnumerable<IRegistered<II2CPeripheral, NumberRegistrationPoint<int>>> IPeripheralContainer<II2CPeripheral, NumberRegistrationPoint<int>>.Children => 218 i2cContainer.Children; 219 UpdateInterrupts()220 private void UpdateInterrupts() 221 { 222 var dmpInterrupt = digitalMotionProcessorInterruptEnabled.Value && digitalMotionProcessorInterruptStatus.Value; 223 var wakeOnMotionInterrupt = wakeOnMotionInterruptEnabled.Value && wakeOnMotionInterruptStatus.Value; 224 var pllReadyInterrupt = pllReadyInterruptEnabled.Value && pllReadyInterruptStatus.Value; 225 var i2cMasterInterrupt = i2cMasterInterruptEnabled.Value && i2cMasterInterruptStatus.Value; 226 var rawDataReadyInterrupt = rawDataReadyInterruptEnabled.Value && rawDataReadyInterruptStatus.Value; 227 var fifoOverflowInterrupt = fifoOverflowInterruptEnabled.Value && fifoOverflowInterruptStatus.Value; 228 var fifoWatermarkInterrupt = fifoWatermarkInterruptEnabled.Value && fifoWatermarkInterruptStatus.Value; 229 230 var irq = dmpInterrupt 231 | wakeOnMotionInterrupt 232 | pllReadyInterrupt 233 | i2cMasterInterrupt 234 | rawDataReadyInterrupt 235 | fifoOverflowInterrupt 236 | fifoWatermarkInterrupt 237 ; 238 239 this.Log(LogLevel.Debug, "IRQ {0}", irq ? "set" : "unset"); 240 this.Log(LogLevel.Noisy, "Interrupts: DMP {0}, Wake on Motion {1}, PLL RDY {2}, I2C Master {3}, Raw Data Ready {4}, FIFO Overflow {5}, FIFO Watermark {6}", 241 dmpInterrupt, wakeOnMotionInterrupt, pllReadyInterrupt, i2cMasterInterrupt, rawDataReadyInterrupt, fifoOverflowInterrupt, fifoWatermarkInterrupt); 242 IRQ.Set(irq); 243 } 244 RegisterIndexToString(int register, ulong bank)245 private string RegisterIndexToString(int register, ulong bank) 246 { 247 switch(bank) 248 { 249 case 0: 250 return ((GyroAccelUserBank0Registers)register).ToString(); 251 case 1: 252 return ((GyroAccelUserBank1Registers)register).ToString(); 253 case 2: 254 return ((GyroAccelUserBank2Registers)register).ToString(); 255 case 3: 256 return ((GyroAccelUserBank3Registers)register).ToString(); 257 default: 258 throw new Exception("Unreachable"); 259 } 260 } 261 DefineBankSelectRegister(ByteRegisterCollection registers)262 private void DefineBankSelectRegister(ByteRegisterCollection registers) 263 { 264 // this register is the same for all user banks 265 // so there is no difference which enum we use 266 GyroAccelUserBank0Registers.RegisterBankSelection.Define(registers) 267 .WithReservedBits(6, 2) 268 .WithValueField(4, 2, writeCallback: (_, value) => userBankSelected = value, name: "USER_BANK") 269 .WithReservedBits(0, 4); 270 } 271 ConvertMeasurement(decimal value, Func<decimal, decimal> converter)272 private ushort ConvertMeasurement(decimal value, Func<decimal, decimal> converter) 273 { 274 var converted = converter(value); 275 var rounded = Math.Round(converted); 276 var clamped = (short)rounded.Clamp(short.MinValue, short.MaxValue); 277 return (ushort)clamped; 278 } 279 280 private string SelectedRegisterName => RegisterIndexToString(selectedRegister, userBankSelected); 281 282 private readonly IMachine machine; 283 private readonly SimpleContainerHelper<II2CPeripheral> i2cContainer; 284 285 private bool chipSelected; 286 private byte selectedRegister; 287 private ulong userBankSelected; 288 private State state; 289 290 private readonly IReadOnlyDictionary<ulong, ByteRegisterCollection> userBankRegisters; 291 private readonly ByteRegisterCollection gyroAccelUserBank0Registers; 292 private readonly ByteRegisterCollection gyroAccelUserBank1Registers; 293 private readonly ByteRegisterCollection gyroAccelUserBank2Registers; 294 private readonly ByteRegisterCollection gyroAccelUserBank3Registers; 295 296 private const int NumberOfExternalSlaveSensorDataRegisters = 24; 297 private const decimal InternalSampleRateHz = 1125; 298 private const int MaxFifoBytes = 4096; 299 300 private enum State 301 { 302 Idle, 303 Reading, 304 Writing, 305 ReceivedFirstByte, 306 WaitingForAddress, 307 WritingWaitingForValue, 308 } 309 310 private enum FifoMode 311 { 312 Stream = 0, 313 Snapshot = 1 314 } 315 } 316 } 317