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.Exceptions; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Time; 14 using Antmicro.Renode.Peripherals.Sensor; 15 using Antmicro.Renode.Peripherals.Timers; 16 using Antmicro.Renode.Utilities; 17 using Antmicro.Renode.Utilities.RESD; 18 19 namespace Antmicro.Renode.Peripherals.Analog 20 { 21 public class CAES_ADC : BasicDoubleWordPeripheral, IADC, IKnownSize 22 { CAES_ADC(IMachine machine, uint frequency = 50000000)23 public CAES_ADC(IMachine machine, uint frequency = 50000000) : base(machine) 24 { 25 this.frequency = frequency; 26 DefineRegisters(); 27 rawVoltage = Enumerable.Repeat(DefaultChannelVoltage, ADCChannelCount).ToArray(); 28 resdStream = new RESDStream<VoltageSample>[NumberOfDataChannels]; 29 samplingTimer = new LimitTimer( 30 machine.ClockSource, frequency, this, "samplingClock", 31 eventEnabled: true, 32 direction: Direction.Ascending, 33 enabled: false, 34 autoUpdate: false, 35 workMode: WorkMode.OneShot); 36 samplingTimer.LimitReached += OnConversionFinished; 37 } 38 FeedSamplesFromRESD(ReadFilePath filePath, uint adcChannel, uint resdChannel = 0, RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.CurrentVirtualTime, long sampleOffsetTime = 0)39 public void FeedSamplesFromRESD(ReadFilePath filePath, uint adcChannel, uint resdChannel = 0, 40 RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.CurrentVirtualTime, long sampleOffsetTime = 0) 41 { 42 EnsureChannelIsValid(adcChannel); 43 try 44 { 45 resdStream[adcChannel] = this.CreateRESDStream<VoltageSample>(filePath, resdChannel, sampleOffsetType, sampleOffsetTime); 46 } 47 catch(RESDException) 48 { 49 for(var channelId = 0; channelId < NumberOfDataChannels; channelId++) 50 { 51 resdStream[channelId]?.Dispose(); 52 } 53 throw new RecoverableException($"Could not load RESD channel {resdChannel} from {filePath}"); 54 } 55 } 56 Reset()57 public override void Reset() 58 { 59 base.Reset(); 60 samplingTimer.Reset(); 61 IRQ.Unset(); 62 } 63 SetADCValue(int adcChannel, uint value)64 public void SetADCValue(int adcChannel, uint value) 65 { 66 EnsureChannelIsValid((uint)adcChannel); 67 rawVoltage[adcChannel] = value; 68 } 69 GetADCValue(int adcChannel)70 public uint GetADCValue(int adcChannel) 71 { 72 EnsureChannelIsValid((uint)adcChannel); 73 return rawVoltage[adcChannel]; 74 } 75 76 public GPIO IRQ { get; } = new GPIO(); 77 78 public long Size => 0x1000; 79 80 public int ADCChannelCount => (int)NumberOfDataChannels; 81 UpdateInterrupts()82 private void UpdateInterrupts() 83 { 84 bool value = adcInterruptEnabled.Value && conversionCompleteInterruptEnabled.Value && channelInterruptPending.Value != 0; 85 this.Log(LogLevel.Debug, "Setting IRQ to {0}", value); 86 IRQ.Set(value); 87 } 88 GetChannelVoltage(uint dataChannelId)89 private uint GetChannelVoltage(uint dataChannelId) 90 { 91 if(resdStream[dataChannelId] == null || resdStream[dataChannelId].TryGetCurrentSample(this, (sample) => sample.Voltage, out var voltage, out _) != RESDStreamStatus.OK) 92 { 93 voltage = rawVoltage[dataChannelId]; 94 } 95 else 96 { 97 rawVoltage[dataChannelId] = voltage; 98 } 99 100 voltage = (uint)((float)voltage * GetGain(channelGain[DataToConfigChannel(dataChannelId)].Value)); 101 102 if(voltage > MaxVoltage) 103 { 104 this.Log(LogLevel.Warning, "The maximum allowed input voltage is {0}μV. Provided value: {1}μV", MaxVoltage, voltage); 105 return MaxVoltage; 106 } 107 108 return voltage; 109 } 110 EnsureChannelIsValid(uint channelIdx)111 private void EnsureChannelIsValid(uint channelIdx) 112 { 113 if(channelIdx >= NumberOfDataChannels) 114 { 115 throw new RecoverableException($"Invalid argument value: {channelIdx}. This peripheral implements only channels in range 0-{NumberOfDataChannels - 1}"); 116 } 117 } 118 GetSingleEndedValue(uint voltage)119 private uint GetSingleEndedValue(uint voltage) 120 { 121 // The most significant bit needs to be flipped in single-ended 122 // conversion due to the fact that the positive voltage range is 123 // mapped to a signed integer format 124 return (uint)((ulong)voltage * MaxValue / MaxVoltage) ^ 0x800; 125 } 126 GetDifferentialValue(uint voltagePositive, uint voltageNegative)127 private uint GetDifferentialValue(uint voltagePositive, uint voltageNegative) 128 { 129 return (uint)(((ulong)voltagePositive - voltageNegative + MaxVoltage) * MaxValue / (2 * MaxVoltage)); 130 } 131 StartConversion()132 private void StartConversion() 133 { 134 var enabledChannelsCount = 0; 135 136 if(!adcEnabled.Value) 137 { 138 this.Log(LogLevel.Warning, "Tried to start the conversion, but the ADC_EN bit is not set"); 139 return; 140 } 141 142 this.Log(LogLevel.Debug, "Starting conversion"); 143 144 for(var i = 0; i < NumberOfConfigChannels; i++) 145 { 146 if(channelEnable[i].Value) 147 { 148 enabledChannelsCount++; 149 } 150 } 151 152 // Values below are derived from the "Calculating Channel Conversion Time" section in the ADC chapter of the UT32M0R500 Functional Manual 153 samplingTimer.Frequency = GetClockFrequency(oscillatorDivider.Value); 154 samplingTimer.Limit = (ulong) enabledChannelsCount * (sequenceDelay.Value * SequenceDelayDuration + oversamplingRate.Value); 155 samplingTimer.Enabled = true; 156 } 157 OnConversionFinished()158 private void OnConversionFinished() 159 { 160 this.Log(LogLevel.Debug, "Ending conversion"); 161 162 if(!adcInterruptEnabled.Value || !conversionCompleteInterruptEnabled.Value) 163 { 164 return; 165 } 166 167 for(var i = 0; i < NumberOfConfigChannels; i++) 168 { 169 if(!channelEnable[i].Value) 170 { 171 continue; 172 } 173 174 var dataChannelIdx = ConfigToDataChannel((uint)i); 175 this.Log(LogLevel.Debug, "Channel {0} enabled ({1}), data channel index: {2}", i, i < DifferentialChannelsOffset ? "single-ended" : "differential", dataChannelIdx); 176 channelInterruptPending.SetBit((byte)i, true); 177 178 if(i < DifferentialChannelsOffset) 179 { 180 channelData[dataChannelIdx].Value = GetSingleEndedValue(GetChannelVoltage(dataChannelIdx)); 181 } 182 else 183 { 184 channelData[dataChannelIdx].Value = GetDifferentialValue(GetChannelVoltage(dataChannelIdx), GetChannelVoltage(dataChannelIdx + 1)); 185 } 186 187 conversionCompleteCombined.Value = true; 188 } 189 190 UpdateInterrupts(); 191 } 192 GetGain(Gain gainSetting)193 private float GetGain(Gain gainSetting) 194 { 195 float gain = 1.0F; 196 if(gainAmplifierEnabled.Value) 197 { 198 switch(gainSetting) 199 { 200 case Gain.DivideBy2: 201 gain = 0.5F; 202 break; 203 case Gain.NoGain: 204 gain = 1.0F; 205 break; 206 case Gain.MultiplyBy2: 207 gain = 2.0F; 208 break; 209 case Gain.MultiplyBy4: 210 gain = 4.0F; 211 break; 212 case Gain.MultiplyBy8: 213 gain = 8.0F; 214 break; 215 case Gain.MultiplyBy16: 216 case Gain.MultiplyBy16Alt1: 217 case Gain.MultiplyBy16Alt2: 218 gain = 16.0F; 219 break; 220 default: 221 this.Log(LogLevel.Warning, "Unsupported value of the gain setting."); 222 break; 223 } 224 } 225 return gain; 226 } 227 GetClockFrequency(OscillatorDivider oscillatorDividerSetting)228 private uint GetClockFrequency(OscillatorDivider oscillatorDividerSetting) 229 { 230 uint clockFrequency; 231 var oscillatorDivider = 0; 232 233 switch(oscillatorDividerSetting) 234 { 235 case OscillatorDivider.DivideBy2: 236 oscillatorDivider = 2; 237 break; 238 case OscillatorDivider.DivideBy4: 239 oscillatorDivider = 4; 240 break; 241 case OscillatorDivider.DivideBy8: 242 oscillatorDivider = 8; 243 break; 244 case OscillatorDivider.DivideBy16: 245 oscillatorDivider = 16; 246 break; 247 default: 248 this.Log(LogLevel.Warning, "Unsupported value of the oscillator divider setting."); 249 break; 250 } 251 252 clockFrequency = oscillatorDivider == 0 ? 0 : frequency / (uint)oscillatorDivider; 253 254 return clockFrequency; 255 } 256 DataToConfigChannel(uint dataChannelIdx)257 private uint DataToConfigChannel(uint dataChannelIdx) 258 { 259 uint configChannelIdx = 0; 260 261 // Even data channels can contain either differential or 262 // single-ended conversion. If both are enabled, differential 263 // conversion takes precedence. 264 // 265 // Temperature channel is unique in a way that it only has a 266 // single configuration register which applies to both single-ended 267 // and differential channels. 268 if(channelEnable[DifferentialChannelsOffset + (dataChannelIdx / 2)].Value || dataChannelIdx >= TemperatureDataChannelsOffset) 269 { 270 configChannelIdx = (uint)(DifferentialChannelsOffset + (dataChannelIdx / 2)); 271 } 272 else 273 { 274 configChannelIdx = dataChannelIdx; 275 } 276 277 return configChannelIdx; 278 } 279 ConfigToDataChannel(uint configChannelIdx)280 private uint ConfigToDataChannel(uint configChannelIdx) 281 { 282 uint dataChannelIdx = 0; 283 284 if(configChannelIdx < DifferentialChannelsOffset) 285 { 286 dataChannelIdx = configChannelIdx; 287 } 288 else 289 { 290 dataChannelIdx = (configChannelIdx - DifferentialChannelsOffset) * 2; 291 } 292 293 return dataChannelIdx; 294 } 295 DefineRegisters()296 private void DefineRegisters() 297 { 298 Registers.SPBConfiguration0.Define(this) 299 .WithFlag(0, out adcEnabled, name: "ADC_EN") 300 .WithFlag(1, out gainAmplifierEnabled, name: "EN_PGA") 301 .WithTaggedFlag("EN_DSM", 2) 302 .WithTaggedFlag("EN_AAF", 3) 303 .WithTaggedFlag("DDF2_CLK_EN", 4) 304 .WithTaggedFlag("EN_REFP", 5) 305 .WithReservedBits(6,1) 306 .WithTaggedFlag("EN_REFC", 7) 307 .WithReservedBits(8,1) 308 .WithTaggedFlag("EN_BIASGEN", 9) 309 .WithTaggedFlag("EN_CLKGEN", 10) 310 .WithReservedBits(11,2) 311 .WithTaggedFlag("ADC_SINGLESWEEP", 13) 312 .WithTag("ODB", 14, 2) 313 .WithReservedBits(16,8) 314 .WithFlag(24, out adcInterruptEnabled, name: "ADC_INTR_EN", 315 writeCallback: (_, val) => 316 { 317 UpdateInterrupts(); 318 }) 319 .WithReservedBits(25,2) 320 .WithTaggedFlag("COI_OVER_IEN", 27) 321 .WithTaggedFlag("SINC4_OVER_IEN", 28) 322 .WithTaggedFlag("DSM_OVL_IEN", 29) 323 .WithTaggedFlag("TRIG_UNDER_IEN", 30) 324 .WithFlag(31, out conversionCompleteInterruptEnabled, name: "CONV_COMPL_IEN", 325 writeCallback: (_, val) => 326 { 327 UpdateInterrupts(); 328 }) 329 ; 330 331 Registers.SPBConfiguration1.Define(this) 332 .WithFlag(0, name: "ADC_TRIGGER", 333 writeCallback: (_, value) => 334 { 335 if(value) 336 { 337 StartConversion(); 338 } 339 }) 340 .WithReservedBits(1,6) 341 .WithTaggedFlag("ADC_RST_CCONV", 7) 342 .WithReservedBits(8,1) 343 .WithTaggedFlag("ADC_READYFLAG", 9) 344 .WithReservedBits(10,20) 345 .WithFlag(31, name: "ADC_REGDEF", 346 writeCallback: (_, value) => 347 { 348 if(value) 349 { 350 // Reset all registers to default state 351 RegistersCollection.Reset(); 352 } 353 }) 354 ; 355 356 Registers.SingleEndedChannelConfiguration0.DefineMany(this, NumberOfSingleEndedChannels, 357 (register, idx) => 358 { 359 register 360 .WithFlag(0, out channelEnable[SingleEndedChannelsOffset + idx], name: "EN") 361 .WithReservedBits(1,2) 362 .WithTaggedFlag("DDF2", 3) 363 .WithEnumField(4, 3, out channelGain[SingleEndedChannelsOffset + idx], name: "GAIN") 364 .WithReservedBits(7,25) 365 ; 366 }, resetValue: 0x10); 367 368 Registers.DifferentialChannelConfiguration0.DefineMany(this, NumberOfDifferentialChannels, 369 (register, idx) => 370 { 371 register 372 .WithFlag(0, out channelEnable[DifferentialChannelsOffset + idx], name: "EN") 373 .WithReservedBits(1,2) 374 .WithTaggedFlag("DDF2", 3) 375 .WithEnumField(4, 3, out channelGain[DifferentialChannelsOffset + idx], name: "GAIN") 376 .WithReservedBits(7,25) 377 ; 378 }, resetValue: 0x10); 379 380 Registers.TemperatureChannelConfiguration.Define(this, resetValue: 0x10) 381 .WithFlag(0, out channelEnable[TemperatureChannelsOffset], name: "EN") 382 .WithReservedBits(1,2) 383 .WithTaggedFlag("DDF2", 3) 384 .WithEnumField(4, 3, out channelGain[TemperatureChannelsOffset], name: "GAIN") 385 .WithReservedBits(7,25) 386 ; 387 388 Registers.TimingControl.Define(this, resetValue: 0x64) 389 .WithValueField(0, 8, out oversamplingRate, name: "ADC_DSMOSR") 390 .WithEnumField(8, 2, out oscillatorDivider, name: "ADC_OSCDIV") 391 .WithReservedBits(10,22) 392 ; 393 394 Registers.SequenceControl.Define(this) 395 .WithValueField(0, 6, out sequenceDelay, name: "ADC_SEQDLY") 396 .WithReservedBits(6,26) 397 ; 398 399 Registers.DSMDigitalStabilityControl.Define(this, resetValue: 0x81e) 400 .WithTag("DSM_OVL_CNT", 0, 7) 401 .WithReservedBits(7,1) 402 .WithTag("DSM_OVL_RST", 8, 5) 403 .WithReservedBits(13,3) 404 .WithTaggedFlag("DSM_OVL_FLAG", 16) 405 .WithReservedBits(17,15) 406 ; 407 408 Registers.InterruptStatus.Define(this) 409 // SET_CHNL_INTR_PEND, DIFF_CHNL_INTR_PEND and TEMP_CHNL_INTR_PEND combined 410 .WithValueField(0, 25, out channelInterruptPending, FieldMode.ReadToClear, name: "CHNL_INTR_PEND") 411 .WithReservedBits(25,2) 412 .WithTaggedFlag("COI_OVER_COMB", 27) 413 .WithTaggedFlag("SINC4_OVER_COMB", 28) 414 .WithTaggedFlag("DSM_OVL_FLAG_COMB", 29) 415 .WithTaggedFlag("TRIG_UNDER_COMB", 30) 416 .WithFlag(31, out conversionCompleteCombined, FieldMode.ReadToClear, name: "CONV_COMPL_COMB") 417 .WithReadCallback((_, __) => UpdateInterrupts()) 418 ; 419 420 Registers.DataOutputWord0.DefineMany(this, NumberOfDataOutputChannels, (register, idx) => 421 { 422 register 423 .WithValueField(0, 12, out channelData[idx], FieldMode.Read, name: "DATA_OUT") 424 .WithReservedBits(12,2) 425 .WithTaggedFlag("DATA_ERROR", 14) 426 .WithReservedBits(15,1) 427 .WithTaggedFlag("CHNL_EN", 16) 428 .WithReservedBits(17,2) 429 .WithTaggedFlag("DD", 19) 430 .WithTag("GAIN", 20, 3) 431 .WithTaggedFlag("COI_OVER", 23) 432 .WithTaggedFlag("SINC4_OVER", 24) 433 .WithTaggedFlag("DSM_OVL_FLAG", 25) 434 .WithTaggedFlag("TRIG_UNDER", 26) 435 .WithReservedBits(27,1) 436 .WithTag("DATA_OUT_LSB", 28, 4) 437 .WithReadCallback((_, __) => 438 { 439 channelInterruptPending.SetBit((byte)DataToConfigChannel((uint)idx), false); 440 UpdateInterrupts(); 441 }) 442 ; 443 }); 444 } 445 446 private IFlagRegisterField adcEnabled; 447 private IFlagRegisterField adcInterruptEnabled; 448 private IFlagRegisterField conversionCompleteInterruptEnabled; 449 private IFlagRegisterField conversionCompleteCombined; 450 private IValueRegisterField channelInterruptPending; 451 private IFlagRegisterField gainAmplifierEnabled; 452 private IFlagRegisterField[] channelEnable = new IFlagRegisterField[NumberOfConfigChannels]; 453 private IValueRegisterField[] channelData = new IValueRegisterField[NumberOfDataChannels]; 454 private IEnumRegisterField<Gain>[] channelGain = new IEnumRegisterField<Gain>[NumberOfConfigChannels]; 455 private IEnumRegisterField<OscillatorDivider> oscillatorDivider; 456 457 private IValueRegisterField oversamplingRate; 458 private IValueRegisterField sequenceDelay; 459 460 private uint[] rawVoltage; 461 462 private const uint MaxValue = 0xFFF; 463 private const uint MaxVoltage = 1500000; // [μV] 464 private const uint SequenceDelayDuration = 25; 465 private const uint NumberOfDataChannels = 18; 466 private const uint NumberOfConfigChannels = 25; 467 private const uint NumberOfSingleEndedChannels = 16; 468 private const uint NumberOfDifferentialChannels = 8; 469 private const uint NumberOfDataOutputChannels = 18; 470 private const uint SingleEndedChannelsOffset = 0; 471 private const uint DifferentialChannelsOffset = 16; 472 private const uint TemperatureChannelsOffset = 24; 473 private const uint TemperatureDataChannelsOffset = 16; 474 private const uint DefaultChannelVoltage = 0; 475 476 private readonly LimitTimer samplingTimer; 477 private readonly RESDStream<VoltageSample>[] resdStream; 478 private readonly uint frequency; 479 480 private enum OscillatorDivider 481 { 482 DivideBy4, 483 DivideBy8, 484 DivideBy16, 485 DivideBy2 486 } 487 488 private enum Gain 489 { 490 DivideBy2, 491 NoGain, 492 MultiplyBy2, 493 MultiplyBy4, 494 MultiplyBy8, 495 MultiplyBy16, 496 MultiplyBy16Alt1, 497 MultiplyBy16Alt2, 498 } 499 500 private enum Registers : long 501 { 502 SPBConfiguration0 = 0x00, 503 SPBConfiguration1 = 0x04, 504 SingleEndedChannelConfiguration0 = 0x10, // 16 SingleEndedChannelConfiguration registers, 0x10–0x4C 505 DifferentialChannelConfiguration0 = 0x50, // 8 DifferentialChannelConfiguration registers, 0x50–0x6C 506 TemperatureChannelConfiguration = 0x70, 507 TimingControl = 0x74, 508 SequenceControl = 0x78, 509 DSMDigitalStabilityControl = 0x84, 510 InterruptStatus = 0x8C, 511 DataOutputWord0 = 0x90 // 18 DataOutputWord registers, 0x90–0xD4 512 } 513 } 514 } 515