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 Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Peripherals.I2C;
14 using Antmicro.Renode.Peripherals.Sensor;
15 using Antmicro.Renode.Utilities;
16 
17 namespace Antmicro.Renode.Peripherals.Sensors
18 {
19     public class ZMOD4xxx : II2CPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, ISensor
20     {
ZMOD4xxx(Model model)21         public ZMOD4xxx(Model model)
22         {
23             this.model = model;
24             /* Below configurations are not a property of the sensor, but a fields that can vary between units.
25                Those are just one of the possible configurations that are proved to work */
26             this.configuration = new byte[]{0x80,0x80,0x80,0x80,0x80,0x80};
27             switch(model)
28             {
29                 case Model.ZMOD4410:
30                     productId = zmod4410_productId;
31                     this.productionData = new byte[]{0x2D, 0xCF, 0x46, 0x29, 0x04, 0xB4};
32                     this.initConfigurationRField = new byte[] { 0x21, 0x48, 0x3B, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33                                                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
34                                                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
35                     this.rField = new byte[] { 0x15, 0x48, 0xBF, 0x92, 0x8D, 0x59, 0xF2, 0x73, 0x42, 0xB5, 0x98, 0x1E, 0x8C, 0x09,
36                                                0x71, 0xDB, 0x51, 0x40, 0x64, 0x58, 0x4E, 0xBE, 0x14, 0xDF, 0xB7, 0xA2, 0x86, 0x9D,
37                                                0x4B, 0xB4, 0x02, 0x8D };
38                     break;
39                 case Model.ZMOD4510:
40                     productId = zmod4510_productId;
41                     this.productionData = new byte[ProductionDataLengthInBytes];
42                     this.initConfigurationRField = new byte[] { 0x2A, 0xFC, 0xF3, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43                                                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
44                                                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
45                     this.rField = new byte[] { 0x15, 0x6A, 0xBF, 0xC0, 0x1B, 0x48, 0x63, 0xA0, 0x84, 0xE8, 0xBF, 0x5C, 0x88, 0x18,
46                                                0x7E, 0xF4, 0x3F, 0x64, 0xCC, 0x47, 0xBC, 0x8A, 0x3C, 0x59, 0x33, 0xB8, 0x75, 0x88,
47                                                0x2C, 0x67, 0xC7, 0x5E };
48                     break;
49                 default:
50                     throw new ConstructionException($"This model ({model}) is not supported");
51             }
52 
53             RegistersCollection = new ByteRegisterCollection(this);
54 
55             IRQ = new GPIO();
56 
57             DefineRegisters();
58             Reset();
59         }
60 
Write(byte[] data)61         public void Write(byte[] data)
62         {
63             if(data.Length == 0)
64             {
65                 this.DebugLog("Empty write. Ignoring");
66                 return;
67             }
68             currentRegister = data[0];
69             this.DebugLog("Address set to 0x{0:X} [{1}]", currentRegister, (Registers)currentRegister);
70             foreach(var b in data.Skip(1))
71             {
72                 this.DebugLog("Writing 0x{0:x} to addr 0x{1:x}", b, currentRegister);
73                 // Using TryWrite to avoid logs on unhandled writes
74                 RegistersCollection.TryWrite(currentRegister, b);
75                 currentRegister += 1;
76             }
77         }
78 
Read(int count)79         public byte[] Read(int count)
80         {
81             var response = new byte[count];
82             for(var index = 0; index < count; index++)
83             {
84                 // Using TryRead to avoid logs on unhandled reads
85                 RegistersCollection.TryRead(currentRegister, out response[index]);
86                 this.DebugLog("Read 0x{0:x} from addr 0x{1:x}", response[index], currentRegister);
87                 currentRegister++;
88             }
89             return response;
90         }
91 
FinishTransmission()92         public void FinishTransmission()
93         {
94             this.DebugLog("Finished transmission");
95         }
96 
Reset()97         public void Reset()
98         {
99             RegistersCollection.Reset();
100             sensorInMeasureMode = false;
101             currentRegister = 0;
102         }
103 
104         public String RValue
105         {
106             get
107             {
108                 return Misc.Stringify(rField);
109             }
110             set
111             {
112                 if(!TryParseHexStringWithAssertedLength(value, ResultLengthInBytes, out rField, out var err))
113                 {
114                     throw new RecoverableException(err);
115                 }
116             }
117         }
118 
119         public String InitConfigurationRValue
120         {
121             get
122             {
123                 return Misc.Stringify(initConfigurationRField);
124             }
125             set
126             {
127                 if(!TryParseHexStringWithAssertedLength(value, ResultLengthInBytes, out initConfigurationRField, out var err))
128                 {
129                     throw new RecoverableException(err);
130                 }
131             }
132         }
133 
134         public String Configuration
135         {
136             get
137             {
138                 return Misc.Stringify(configuration);
139             }
140             set
141             {
142                 if(!TryParseHexStringWithAssertedLength(value, ConfigurationLengthInBytes, out configuration, out var err))
143                 {
144                     throw new RecoverableException(err);
145                 }
146             }
147         }
148 
149         public String ProductionData
150         {
151             get
152             {
153                 return Misc.Stringify(productionData);
154             }
155             set
156             {
157                 if(!TryParseHexStringWithAssertedLength(value, ProductionDataLengthInBytes, out productionData, out var err))
158                 {
159                     throw new RecoverableException(err);
160                 }
161             }
162         }
163 
164         public GPIO IRQ { get; }
165 
166         public ByteRegisterCollection RegistersCollection { get; }
167 
TryParseHexStringWithAssertedLength(string hexstring, int expectedLenghtInBytes, out byte[] byteArray, out string err)168         private bool TryParseHexStringWithAssertedLength(string hexstring, int expectedLenghtInBytes, out byte[] byteArray, out string err)
169         {
170             byteArray = new byte[expectedLenghtInBytes];
171             err = "";
172 
173             if(hexstring.Length != (expectedLenghtInBytes * 2))
174             {
175                 err = $"Wrong hexsting length. Expected {expectedLenghtInBytes} bytes";
176                 return false;
177             }
178 
179             if(!Misc.TryParseHexString(hexstring, out byteArray, elementSize: 1))
180             {
181                 err = "Unable to parse as a hexstring";
182                 return false;
183             }
184             return true;
185         }
186 
EmulateMeasurementFinished()187         private void EmulateMeasurementFinished()
188         {
189             // Normally the sensor sets this line high when starting the measurements, and then sets is low when finished.
190             // We emulate the measurement as instantaneous
191             IRQ.Blink();
192         }
193 
DefineRegisters()194         private void DefineRegisters()
195         {
196             Registers.ProductID0.Define(this)
197                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: (_) => productId[0], name: "PID 0");
198 
199             Registers.ProductID1.Define(this)
200                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: (_) => productId[1], name: "PID 1");
201 
202             Registers.Configuration.DefineMany(this, ConfigurationLengthInBytes, (register, index) =>
203             {
204                 register
205                     .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: (_) => configuration[index], name: $"Configuration{index}");
206             });
207 
208             Registers.ProductionData.DefineMany(this, ProductionDataLengthInBytes, (register, index) =>
209             {
210                 register
211                     .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: (_) => productionData[index], name: $"ProductionData{index}");
212             });
213 
214             Registers.Tracking.DefineMany(this, TrackingLengthInBytes, (register, index) =>
215             {
216                 register
217                     .WithTag($"Tracking{index}", 0, 8);
218             });
219 
220             Registers.H.DefineMany(this, HLengthInBytes, (register, index) =>
221             {
222                 register
223                     .WithTag($"H{index}", 0, 8);
224             });
225 
226             Registers.D.DefineMany(this, DLengthInBytes, (register, index) =>
227             {
228                 register
229                     .WithTag($"D{index}", 0, 8);
230             });
231 
232             Registers.M.DefineMany(this, MLengthInBytes, (register, index) =>
233             {
234                 register
235                     .WithTag($"M{index}", 0, 8)
236                     .WithWriteCallback((_, val) =>
237                     {
238                         if(index == 0)
239                         {
240                             sensorInMeasureMode = (val != M0InitValue);
241                             this.DebugLog("Sensor in measure state = {0}", sensorInMeasureMode);
242                         }
243                     });
244             });
245 
246             Registers.S.DefineMany(this, SLengthInBytes, (register, index) =>
247             {
248                 register
249                     .WithTag($"S{index}", 0, 8);
250             });
251 
252             Registers.Command.Define(this)
253                 .WithReservedBits(0, 7)
254                 .WithFlag(7, changeCallback: (_, val) => {
255                     if(val)
256                     {
257                         this.DebugLog("Measurement trigerred");
258                         EmulateMeasurementFinished();
259                     }
260                     else
261                     {
262                         this.DebugLog("Measurement stopped");
263                     }
264                 }, name: "Start");
265 
266             Registers.Status0.Define(this)
267                 .WithTag("Last executed sequencer step", 0, 5)
268                 .WithTaggedFlag("Alarm", 5)
269                 .WithTaggedFlag("Sleep Timer Enabled", 6)
270                 .WithTaggedFlag("Sequencer Running", 7);
271 
272             Registers.Result.DefineMany(this, ResultLengthInBytes, (register, index) =>
273             {
274                 register
275                     .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: (_) => sensorInMeasureMode ? rField[index] : initConfigurationRField[index], name: $"R{index}");
276             });
277 
278             Registers.Error.Define(this)
279                 .WithReservedBits(0, 6)
280                 .WithTaggedFlag("Access Conflict", 6)
281                 .WithTaggedFlag("POR Event", 7);
282         }
283 
284         private readonly Model model;
285 
286         private const int ResultLengthInBytes = 32;
287         private const int ConfigurationLengthInBytes = 6;
288         private const int ProductionDataLengthInBytes = 6;
289         private const int TrackingLengthInBytes = 6;
290         private const int HLengthInBytes = 10;
291         private const int DLengthInBytes = 6;
292         private const int MLengthInBytes = 1;
293         private const int SLengthInBytes = 30;
294 
295         // There's no documentation for that, but this is what gets written in the init phase
296         private const int M0InitValue = 0xC3;
297 
298         private readonly byte[] zmod4510_productId = new byte[] { 0x63, 0x20 };
299         private readonly byte[] zmod4410_productId = new byte[] { 0x23, 0x10 };
300         private readonly byte[] productId;
301         private byte[] configuration;
302         private byte[] productionData;
303         private byte[] initConfigurationRField;
304         private byte[] rField;
305 
306         private byte currentRegister;
307         private bool sensorInMeasureMode;
308 
309         public enum Model
310         {
311             // This is also the address on the I2C bus
312             ZMOD4510 = 0x33,
313             ZMOD4410 = 0x32,
314         }
315 
316         private enum Registers : byte
317         {
318             ProductID0 = 0x0,
319             ProductID1 = 0x1,
320             Configuration = 0x20,
321             ProductionData = 0x26,
322             Tracking = 0x3A,
323             H = 0x40,
324             D = 0x50,
325             M = 0x60,
326             S = 0x68,
327             Command = 0x93,
328             Status0 = 0x94,
329             Result = 0x97,
330             Error = 0xB7,
331         }
332     }
333 }
334