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