1 // 2 // Copyright (c) 2010-2023 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.Peripherals.Sensor; 11 using Antmicro.Renode.Peripherals.I2C; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Core; 14 using Antmicro.Renode.Core.Structure.Registers; 15 using Antmicro.Renode.Utilities; 16 17 namespace Antmicro.Renode.Peripherals.Sensors 18 { 19 public class SI7210 : II2CPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, ITemperatureSensor 20 { SI7210(IMachine machine, byte offset = 0, byte gain = 0)21 public SI7210(IMachine machine, byte offset = 0, byte gain = 0) 22 { 23 RegistersCollection = new ByteRegisterCollection(this); 24 DefineRegisters(); 25 26 Reset(); 27 28 temperatureOffset = offset; 29 temperatureGain = gain; 30 } 31 Write(byte[] data)32 public void Write(byte[] data) 33 { 34 if(data.Length == 0) 35 { 36 this.Log(LogLevel.Warning, "Unexpected write with no data"); 37 return; 38 } 39 40 registerAddress = (Registers)data[0]; 41 42 if(data.Length > 1) 43 { 44 foreach(var value in data.Skip(1)) 45 { 46 RegistersCollection.Write((byte)registerAddress, value); 47 } 48 } 49 } 50 Read(int count)51 public byte[] Read(int count) 52 { 53 if(!registerAddress.HasValue) 54 { 55 this.Log(LogLevel.Warning, "Trying to read without setting address"); 56 return new byte[] { 0 }; 57 } 58 59 var result = new byte[count]; 60 for(var i = 0; i < count; ++i) 61 { 62 result[i] = RegistersCollection.Read((byte)((int)registerAddress + i)); 63 } 64 65 if(autoIncrement.Value) 66 { 67 registerAddress += 1; 68 } 69 return result; 70 } 71 FinishTransmission()72 public void FinishTransmission() 73 { 74 registerAddress = null; 75 } 76 Reset()77 public void Reset() 78 { 79 RegistersCollection.Reset(); 80 registerAddress = null; 81 currentTemperature = ZeroCelsiusApproximation; 82 } 83 84 public ByteRegisterCollection RegistersCollection { get; } 85 86 public decimal Temperature 87 { 88 get => temperature; 89 set 90 { 91 if(IsTemperatureOutOfRange(value)) 92 { 93 return; 94 } 95 temperature = value; 96 97 var measurement = (ushort)(short)(temperature * TemperatureGainApproximation) << 3; 98 currentTemperature = ZeroCelsiusApproximation + (ulong)measurement; 99 } 100 } 101 DefineRegisters()102 private void DefineRegisters() 103 { 104 Registers.ChipID.Define(this) 105 .WithValueField(0, 8, FieldMode.Read, name: "ChipID", 106 valueProviderCallback: _ => (uint)ChipID) 107 ; 108 109 Registers.MeasurementHigh.Define(this) 110 .WithValueField(0, 7, name: "DSPSIGM", 111 valueProviderCallback: _ => currentTemperature >> 8) 112 .WithTaggedFlag("fresh", 7) 113 ; 114 115 Registers.MeasurementLow.Define(this) 116 .WithValueField(0, 8, name: "DSPSIGL", 117 valueProviderCallback: _ => currentTemperature) 118 ; 119 120 Registers.EnableTemperatureReadout.Define(this) 121 .WithValueField(0, 3, out enableTemperatureReadout, name: "DSPSIGSEL") 122 .WithReservedBits(3, 5) 123 ; 124 125 Registers.PowerControl.Define(this) 126 .WithTaggedFlag("sleep", 0) 127 .WithTaggedFlag("stop", 1) 128 .WithTaggedFlag("oneburst", 2) 129 .WithTaggedFlag("usestore", 3) 130 .WithReservedBits(4, 3) 131 .WithTaggedFlag("meas", 7) 132 ; 133 134 Registers.AutoIncrement.Define(this) 135 .WithFlag(0, out autoIncrement, name: "ARAUTOINC") 136 .WithReservedBits(1, 7) 137 ; 138 139 Registers.OTPAddress.Define(this) 140 .WithValueField(0, 8, name: "OTP_Addr", 141 writeCallback: (_, value) => otpData = HandleOTP_ReadRequest((byte)value)) 142 ; 143 144 Registers.OTPData.Define(this) 145 .WithValueField(0, 8, name: "OTP_Data", 146 valueProviderCallback: _ => 147 { 148 return (uint)otpData; 149 }, 150 writeCallback: (_, value) => otpData = (byte)value) 151 ; 152 153 Registers.OTPControl.Define(this) 154 .WithTag("otp_busy", 0, 1) 155 .WithFlag(1, out otpReadEnable, name: "OTP_Control.otp_read_en") 156 .WithTag("reserved", 2, 6) 157 ; 158 } 159 HandleOTP_ReadRequest(byte offset)160 private byte HandleOTP_ReadRequest(byte offset) 161 { 162 if(!otpReadEnable.Value) 163 { 164 return 0; 165 } 166 switch((OTPRegisters)offset) 167 { 168 case OTPRegisters.PartBase: 169 // Base part number dropping the “Si72”, for example 01 for Si7201 170 return PartNumber; 171 case OTPRegisters.TempOffset: 172 // Temp sensor offset adjustment 173 return temperatureOffset; 174 case OTPRegisters.TempGain: 175 // Temp sensor gain adjustment 176 return temperatureGain; 177 default: 178 this.Log(LogLevel.Noisy, "Tried to read OTP_DATA offset: 0x{0:X}, returning 0", offset); 179 return 0; 180 } 181 } 182 IsTemperatureOutOfRange(decimal temperature)183 private bool IsTemperatureOutOfRange(decimal temperature) 184 { 185 if (temperature < MinTemperature || temperature > MaxTemperature) 186 { 187 this.Log(LogLevel.Warning, "Temperature {0} is out of range, use value from the range <{1:F2};{2:F2}>", temperature, MinTemperature, MaxTemperature); 188 return true; 189 } 190 return false; 191 } 192 193 private Registers? registerAddress; 194 195 private decimal temperature; 196 197 private byte otpData; 198 private IFlagRegisterField otpReadEnable; 199 private IFlagRegisterField autoIncrement; 200 private IValueRegisterField enableTemperatureReadout; 201 private byte temperatureOffset; 202 private byte temperatureGain; 203 204 private ulong currentTemperature; 205 206 private const decimal MinTemperature = -65.0m; 207 private const decimal MaxTemperature = 150.0m; 208 209 private const ushort ChipID = 0x14; 210 private const byte PartNumber = 0x10; 211 212 private const ulong ZeroCelsiusApproximation = 0x3920; 213 private const decimal TemperatureGainApproximation = 6.667m; 214 215 private enum Registers : byte 216 { 217 ChipID = 0xC0, 218 MeasurementHigh = 0xC1, 219 MeasurementLow = 0xC2, 220 EnableTemperatureReadout = 0xC3, 221 PowerControl = 0xC4, 222 AutoIncrement = 0xC5, 223 OTPAddress = 0xE1, 224 OTPData = 0xE2, 225 OTPControl = 0xE3, 226 TestFieldGenerator = 0xE4, 227 } 228 229 private enum OTPRegisters : byte 230 { 231 PartBase = 0x14, 232 TempOffset = 0x1D, 233 TempGain = 0x1E, 234 } 235 } 236 } 237