1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 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.Peripherals.Timers; 14 using Antmicro.Renode.Utilities; 15 using Antmicro.Renode.Utilities.RESD; 16 using Antmicro.Renode.Time; 17 18 namespace Antmicro.Renode.Peripherals.Analog 19 { 20 public class SAM4S_ADC : BasicDoubleWordPeripheral, IKnownSize 21 { SAM4S_ADC(Machine machine, long baseFrequency = 32768, decimal referenceVoltage = 5m)22 public SAM4S_ADC(Machine machine, long baseFrequency = 32768, decimal referenceVoltage = 5m) : base(machine) 23 { 24 internalTimer = new LimitTimer(machine.ClockSource, baseFrequency, this, "internalTimer", limit: 1, divider: 2, eventEnabled: true, workMode: WorkMode.OneShot); 25 internalTimer.LimitReached += ConversionFinished; 26 27 ReferenceVoltage = referenceVoltage; 28 29 DefineRegisters(); 30 } 31 Reset()32 public override void Reset() 33 { 34 base.Reset(); 35 36 foreach(var it in channelStream.Select((Stream, Index) => new { Stream, Index })) 37 { 38 it.Stream?.Dispose(); 39 channelStream[it.Index] = null; 40 } 41 } 42 FeedSamplesFromRESD(ReadFilePath filePath, int channelIndex, uint? resdChannelOverride = null, RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.Specified, long sampleOffsetTime = 0)43 public void FeedSamplesFromRESD(ReadFilePath filePath, int channelIndex, uint? resdChannelOverride = null, 44 RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.Specified, long sampleOffsetTime = 0) 45 { 46 AssertChannelIndex(channelIndex); 47 48 var resdChannel = resdChannelOverride ?? (uint)channelIndex; 49 channelStream[channelIndex] = this.CreateRESDStream<VoltageSample>(filePath, resdChannel, sampleOffsetType, sampleOffsetTime); 50 } 51 DefaultChannelVoltage(int channelIndex)52 public decimal DefaultChannelVoltage(int channelIndex) 53 { 54 AssertChannelIndex(channelIndex); 55 return defaultChannelValue[channelIndex]; 56 } 57 DefaultChannelVoltage(int channelIndex, decimal newVoltage)58 public void DefaultChannelVoltage(int channelIndex, decimal newVoltage) 59 { 60 AssertChannelIndex(channelIndex); 61 defaultChannelValue[channelIndex] = newVoltage; 62 } 63 GetChannelValue(int channelIndex)64 public ushort GetChannelValue(int channelIndex) 65 { 66 AssertChannelIndex(channelIndex); 67 68 var voltage = defaultChannelValue[channelIndex]; 69 if(temperatureSensorEnabled.Value && channelIndex == TemperatureChannelIndex) 70 { 71 voltage = BaseTemperatureVoltage + (Temperature - BaseTemperature) * TemperatureProportionalCoefficient; 72 } 73 else if(channelStream[channelIndex] != null) 74 { 75 var streamStatus = channelStream[channelIndex].TryGetCurrentSample(this, out var sample, out _); 76 if(streamStatus == RESDStreamStatus.OK) 77 { 78 voltage = sample.Voltage / 1000m; 79 } 80 else if(streamStatus == RESDStreamStatus.AfterStream) 81 { 82 channelStream[channelIndex].Dispose(); 83 channelStream[channelIndex] = null; 84 } 85 } 86 87 voltage = voltage.Clamp(0, ReferenceVoltage); 88 return (ushort)(voltage * MaximumChannelValue / ReferenceVoltage); 89 } 90 91 public long Size => 0x100; 92 93 public GPIO IRQ { get; } = new GPIO(); 94 95 public decimal ReferenceVoltage { get; set; } 96 97 public decimal Temperature { get; set; } 98 UpdateInterrupts()99 private void UpdateInterrupts() 100 { 101 var interrupt = Enumerable.Range(0, NumberOfChannels) 102 .Any(index => endOfConversionInterruptEnabled[index].Value && endOfConversionInterruptPending[index].Value); 103 interrupt |= dataReadyInterruptEnabled.Value && dataReadyInterruptPending.Value; 104 interrupt |= endOfCalibrationInterruptEnabled.Value && endOfCalibrationInterruptPending.Value; 105 106 this.Log(LogLevel.Debug, "IRQ set to {0}", interrupt); 107 IRQ.Set(interrupt); 108 } 109 StartConversion()110 private void StartConversion() 111 { 112 internalTimer.ResetValue(); 113 internalTimer.Enabled = true; 114 this.Log(LogLevel.Debug, "Started conversion"); 115 } 116 ConversionFinished()117 private void ConversionFinished() 118 { 119 foreach(var channelIndex in Enumerable.Range(0, NumberOfChannels).Where(index => channelStatus[index].Value)) 120 { 121 endOfConversionInterruptPending[channelIndex].Value = true; 122 channelValue[channelIndex].Value = (ulong)GetChannelValue(channelIndex); 123 lastDataConverted.Value = channelValue[channelIndex].Value; 124 this.Log(LogLevel.Debug, "Setting channel#{0} to {1:X03}", channelIndex, channelValue[channelIndex].Value); 125 } 126 127 this.Log(LogLevel.Debug, "Conversion finished"); 128 129 dataReadyInterruptPending.Value |= endOfConversionInterruptPending.Any(interrupt => interrupt.Value); 130 UpdateInterrupts(); 131 132 if(freerunMode.Value) 133 { 134 StartConversion(); 135 } 136 } 137 AssertChannelIndex(int channelIndex)138 private void AssertChannelIndex(int channelIndex) 139 { 140 if(channelIndex < 0 || channelIndex >= NumberOfChannels) 141 { 142 throw new RecoverableException($"'{nameof(channelIndex)}' should be between 0 and {NumberOfChannels - 1}"); 143 } 144 } 145 DefineRegisters()146 private void DefineRegisters() 147 { 148 Registers.Control.Define(this) 149 .WithFlag(0, FieldMode.Write, name: "SWRST", 150 writeCallback: (_, value) => { if(value) Reset(); }) 151 .WithFlag(1, FieldMode.Write, name: "START", 152 writeCallback: (_, value) => { if(value) StartConversion(); }) 153 .WithReservedBits(2, 1) 154 .WithFlag(3, FieldMode.Write, name: "AUTOCAL", 155 writeCallback: (_, value) => 156 { 157 if(!value) 158 { 159 return; 160 } 161 162 endOfCalibrationInterruptPending.Value = true; 163 UpdateInterrupts(); 164 }) 165 .WithReservedBits(4, 28) 166 ; 167 168 Registers.Mode.Define(this) 169 .WithTaggedFlag("TRGEN", 0) 170 .WithTag("TRGSEL", 1, 3) 171 .WithTaggedFlag("SLEEP", 5) 172 .WithTaggedFlag("FWUP", 6) 173 .WithFlag(7, out freerunMode, name: "FREERUN") 174 .WithValueField(8, 8, name: "PRESCAL", 175 valueProviderCallback: _ => (byte)((internalTimer.Divider / 2) - 1), 176 changeCallback: (_, value) => internalTimer.Divider = ((int)value + 1) * 2) 177 .WithTag("STARTUP", 16, 4) 178 .WithTag("SETTLING", 20, 2) 179 .WithTaggedFlag("ANACH", 23) 180 .WithTag("TRACKTIM", 24, 4) 181 .WithTag("TRANSFER", 28, 2) 182 .WithTaggedFlag("USEQ", 31) 183 ; 184 185 Registers.Sequence1.Define(this) 186 .WithTag("USCH1", 0, 4) 187 .WithTag("USCH2", 4, 4) 188 .WithTag("USCH3", 8, 4) 189 .WithTag("USCH4", 12, 4) 190 .WithTag("USCH5", 16, 4) 191 .WithTag("USCH6", 20, 4) 192 .WithTag("USCH7", 24, 4) 193 .WithTag("USCH8", 28, 4) 194 ; 195 196 Registers.Sequence2.Define(this) 197 .WithTag("USCH9", 0, 4) 198 .WithTag("USCH10", 4, 4) 199 .WithTag("USCH11", 8, 4) 200 .WithTag("USCH12", 12, 4) 201 .WithTag("USCH13", 16, 4) 202 .WithTag("USCH14", 20, 4) 203 .WithTag("USCH15", 24, 4) 204 .WithReservedBits(28, 4) 205 ; 206 207 Registers.ChannelEnable.Define(this) 208 .WithFlags(0, 16, FieldMode.Set, name: "ADC_CHER", 209 writeCallback: (index, _, value) => { if(value) channelStatus[index].Value = true; }) 210 .WithReservedBits(16, 16) 211 ; 212 213 Registers.ChannelDisable.Define(this) 214 .WithFlags(0, 16, FieldMode.WriteOneToClear, name: "ADC_CHDR", 215 writeCallback: (index, _, value) => { if(value) channelStatus[index].Value = false; }) 216 .WithReservedBits(16, 16) 217 ; 218 219 Registers.ChannelStatus.Define(this) 220 .WithFlags(0, 16, out channelStatus, FieldMode.Read, name: "ADC_CHSR") 221 .WithReservedBits(16, 16) 222 ; 223 224 Registers.LastConvertedData.Define(this) 225 .WithValueField(0, 12, out lastDataConverted, name: "LDATA") 226 .WithTag("CHNB", 12, 4) 227 .WithReservedBits(16, 16) 228 .WithReadCallback((_, __) => 229 { 230 dataReadyInterruptPending.Value = false; 231 UpdateInterrupts(); 232 }) 233 ; 234 235 Registers.InterruptEnable.Define(this) 236 .WithFlags(0, 16, FieldMode.Set, name: "EOC", 237 writeCallback: (index, _, value) => { if(value) endOfConversionInterruptEnabled[index].Value = true; }) 238 .WithReservedBits(16, 7) 239 .WithFlag(23, FieldMode.Set, name: "EOCAL", 240 writeCallback: (_, value) => { if(value) endOfCalibrationInterruptEnabled.Value = true; }) 241 .WithFlag(24, FieldMode.Set, name: "DRDY", 242 writeCallback: (_, value) => { if(value) dataReadyInterruptEnabled.Value = true; }) 243 .WithTaggedFlag("GOVRE", 25) 244 .WithTaggedFlag("COMPE", 26) 245 .WithTaggedFlag("ENDRX", 27) 246 .WithTaggedFlag("RXBFUF", 28) 247 .WithReservedBits(29, 3) 248 .WithChangeCallback((_, __) => UpdateInterrupts()) 249 ; 250 251 Registers.InterruptDisable.Define(this) 252 .WithFlags(0, 16, FieldMode.Set, name: "EOC", 253 writeCallback: (index, _, value) => { if(value) endOfConversionInterruptEnabled[index].Value = false; }) 254 .WithReservedBits(16, 7) 255 .WithFlag(23, FieldMode.Set, name: "EOCAL", 256 writeCallback: (_, value) => { if(value) endOfCalibrationInterruptEnabled.Value = false; }) 257 .WithFlag(24, FieldMode.Set, name: "DRDY", 258 writeCallback: (_, value) => { if(value) dataReadyInterruptEnabled.Value = false; }) 259 .WithTaggedFlag("GOVRE", 25) 260 .WithTaggedFlag("COMPE", 26) 261 .WithTaggedFlag("ENDRX", 27) 262 .WithTaggedFlag("RXBFUF", 28) 263 .WithReservedBits(29, 3) 264 .WithChangeCallback((_, __) => UpdateInterrupts()) 265 ; 266 267 Registers.InterruptMask.Define(this) 268 .WithFlags(0, 16, out endOfConversionInterruptEnabled, FieldMode.Read, name: "EOC") 269 .WithReservedBits(16, 7) 270 .WithFlag(23, out endOfCalibrationInterruptEnabled, FieldMode.Read, name: "EOCAL") 271 .WithFlag(24, out dataReadyInterruptEnabled, FieldMode.Read, name: "DRDY") 272 .WithTaggedFlag("GOVRE", 25) 273 .WithTaggedFlag("COMPE", 26) 274 .WithTaggedFlag("ENDRX", 27) 275 .WithTaggedFlag("RXBFUF", 28) 276 .WithReservedBits(29, 3) 277 ; 278 279 Registers.InterruptStatus.Define(this) 280 .WithFlags(0, 16, out endOfConversionInterruptPending, FieldMode.Read, name: "EOC") 281 .WithReservedBits(16, 7) 282 // NOTE: We are not doing the actual calibration, so we are just clearing the 283 // flag on the first access. 284 .WithFlag(23, out endOfCalibrationInterruptPending, FieldMode.ReadToClear, name: "EOCAL") 285 .WithFlag(24, out dataReadyInterruptPending, FieldMode.Read, name: "DRDY") 286 .WithTaggedFlag("GOVRE", 25) 287 .WithTaggedFlag("COMPE", 26) 288 .WithTaggedFlag("ENDRX", 27) 289 .WithTaggedFlag("RXBFUF", 28) 290 .WithReservedBits(29, 3) 291 ; 292 293 Registers.OverrunStatus.Define(this) 294 .WithTaggedFlag("OVRE0", 0) 295 .WithTaggedFlag("OVRE1", 1) 296 .WithTaggedFlag("OVRE2", 2) 297 .WithTaggedFlag("OVRE3", 3) 298 .WithTaggedFlag("OVRE4", 4) 299 .WithTaggedFlag("OVRE5", 5) 300 .WithTaggedFlag("OVRE6", 6) 301 .WithTaggedFlag("OVRE7", 7) 302 .WithTaggedFlag("OVRE8", 8) 303 .WithTaggedFlag("OVRE9", 9) 304 .WithTaggedFlag("OVRE10", 10) 305 .WithTaggedFlag("OVRE11", 11) 306 .WithTaggedFlag("OVRE12", 12) 307 .WithTaggedFlag("OVRE13", 13) 308 .WithTaggedFlag("OVRE14", 14) 309 .WithTaggedFlag("OVRE15", 15) 310 .WithReservedBits(16, 16) 311 ; 312 313 Registers.ExtendedMode.Define(this) 314 .WithTag("CMPMODE", 0, 2) 315 .WithReservedBits(2, 2) 316 .WithTag("CMPSEL", 4, 4) 317 .WithReservedBits(8, 1) 318 .WithTaggedFlag("CMPALL", 9) 319 .WithReservedBits(10, 14) 320 .WithTaggedFlag("TAG", 24) 321 .WithReservedBits(25, 7) 322 ; 323 324 Registers.CompareWindow.Define(this) 325 .WithTag("LOWTHRES", 0, 12) 326 .WithReservedBits(12, 4) 327 .WithTag("HIGHTHRES", 16, 12) 328 .WithReservedBits(28, 4) 329 ; 330 331 Registers.ChannelGain.Define(this) 332 .WithTaggedFlag("GAIN0", 0) 333 .WithTaggedFlag("GAIN1", 1) 334 .WithTaggedFlag("GAIN2", 2) 335 .WithTaggedFlag("GAIN3", 3) 336 .WithTaggedFlag("GAIN4", 4) 337 .WithTaggedFlag("GAIN5", 5) 338 .WithTaggedFlag("GAIN6", 6) 339 .WithTaggedFlag("GAIN7", 7) 340 .WithTaggedFlag("GAIN8", 8) 341 .WithTaggedFlag("GAIN9", 9) 342 .WithTaggedFlag("GAIN10", 10) 343 .WithTaggedFlag("GAIN11", 11) 344 .WithTaggedFlag("GAIN12", 12) 345 .WithTaggedFlag("GAIN13", 13) 346 .WithTaggedFlag("GAIN14", 14) 347 .WithTaggedFlag("GAIN15", 15) 348 ; 349 350 Registers.ChannelOffset.Define(this) 351 .WithTaggedFlag("OFF0", 0) 352 .WithTaggedFlag("OFF1", 1) 353 .WithTaggedFlag("OFF2", 2) 354 .WithTaggedFlag("OFF3", 3) 355 .WithTaggedFlag("OFF4", 4) 356 .WithTaggedFlag("OFF5", 5) 357 .WithTaggedFlag("OFF6", 6) 358 .WithTaggedFlag("OFF7", 7) 359 .WithTaggedFlag("OFF8", 8) 360 .WithTaggedFlag("OFF9", 9) 361 .WithTaggedFlag("OFF10", 10) 362 .WithTaggedFlag("OFF11", 11) 363 .WithTaggedFlag("OFF12", 12) 364 .WithTaggedFlag("OFF13", 13) 365 .WithTaggedFlag("OFF14", 14) 366 .WithTaggedFlag("OFF15", 15) 367 .WithTaggedFlag("DIFF0", 16) 368 .WithTaggedFlag("DIFF1", 17) 369 .WithTaggedFlag("DIFF2", 18) 370 .WithTaggedFlag("DIFF3", 19) 371 .WithTaggedFlag("DIFF4", 20) 372 .WithTaggedFlag("DIFF5", 21) 373 .WithTaggedFlag("DIFF6", 22) 374 .WithTaggedFlag("DIFF7", 23) 375 .WithTaggedFlag("DIFF8", 24) 376 .WithTaggedFlag("DIFF9", 25) 377 .WithTaggedFlag("DIFF10", 26) 378 .WithTaggedFlag("DIFF11", 27) 379 .WithTaggedFlag("DIFF12", 28) 380 .WithTaggedFlag("DIFF13", 29) 381 .WithTaggedFlag("DIFF14", 30) 382 .WithTaggedFlag("DIFF15", 31) 383 ; 384 385 Registers.ChannelData0.DefineMany(this, NumberOfChannels, (register, index) => 386 register 387 .WithValueField(0, 12, out channelValue[index], name: $"ADC_CDR{index}") 388 .WithReservedBits(12, 20) 389 .WithReadCallback((_, __) => 390 { 391 endOfConversionInterruptPending[index].Value = false; 392 UpdateInterrupts(); 393 })) 394 ; 395 396 Registers.AnalogControl.Define(this) 397 .WithReservedBits(0, 4) 398 .WithFlag(4, out temperatureSensorEnabled, name: "TSON") 399 .WithReservedBits(5, 3) 400 .WithTag("IBCTL", 8, 24) 401 ; 402 403 Registers.WriteProtectMode.Define(this) 404 .WithTaggedFlag("WPEN", 0) 405 .WithReservedBits(1, 7) 406 .WithTag("WPKEY", 8, 24) 407 ; 408 } 409 410 private IFlagRegisterField freerunMode; 411 412 private IFlagRegisterField[] channelStatus; 413 414 private IFlagRegisterField[] endOfConversionInterruptEnabled; 415 private IFlagRegisterField[] endOfConversionInterruptPending; 416 417 private IFlagRegisterField endOfCalibrationInterruptEnabled; 418 private IFlagRegisterField endOfCalibrationInterruptPending; 419 420 private IFlagRegisterField dataReadyInterruptEnabled; 421 private IFlagRegisterField dataReadyInterruptPending; 422 423 private IValueRegisterField lastDataConverted; 424 private IFlagRegisterField temperatureSensorEnabled; 425 426 private readonly IValueRegisterField[] channelValue = new IValueRegisterField[NumberOfChannels]; 427 private readonly RESDStream<VoltageSample>[] channelStream = new RESDStream<VoltageSample>[NumberOfChannels]; 428 429 private readonly decimal[] defaultChannelValue = new decimal[NumberOfChannels]; 430 private readonly LimitTimer internalTimer; 431 432 private const int NumberOfChannels = 16; 433 private const ushort MaximumChannelValue = 0xFFF; 434 435 private const decimal BaseTemperature = 27m; 436 private const decimal BaseTemperatureVoltage = 1.44m; 437 private const decimal TemperatureProportionalCoefficient = 4.7m / 1000m; 438 private const int TemperatureChannelIndex = 15; 439 440 private enum Registers : uint 441 { 442 Control = 0x00, 443 Mode = 0x04, 444 Sequence1 = 0x08, 445 Sequence2 = 0x0C, 446 ChannelEnable = 0x10, 447 ChannelDisable = 0x14, 448 ChannelStatus = 0x18, 449 LastConvertedData = 0x20, 450 InterruptEnable = 0x24, 451 InterruptDisable = 0x28, 452 InterruptMask = 0x2C, 453 InterruptStatus = 0x30, 454 OverrunStatus = 0x3C, 455 ExtendedMode = 0x40, 456 CompareWindow = 0x44, 457 ChannelGain = 0x48, 458 ChannelOffset = 0x4C, 459 // NOTE: This is offset of the first register; 460 // there are `NumberOfChannels` registers, one for each channel 461 ChannelData0 = 0x50, 462 AnalogControl = 0x94, 463 WriteProtectMode = 0xE4, 464 WriteProtectStatus = 0xE8, 465 // NOTE: Those registers are used by PDC block which isn't currently supported 466 // by this peripheral 467 ReceivePointer = 0x100, 468 ReceiveCounter = 0x104, 469 ReceiveNextPointer = 0x110, 470 ReceiveNextCounter = 0x114, 471 TransferControl = 0x120, 472 TransferStatus = 0x124 473 } 474 } 475 } 476