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.Collections.Generic; 9 using System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.Bus; 14 using Antmicro.Renode.Exceptions; 15 using Antmicro.Renode.Time; 16 using Antmicro.Renode.Peripherals.Timers; 17 using Antmicro.Renode.Utilities; 18 using Antmicro.Renode.Utilities.RESD; 19 20 namespace Antmicro.Renode.Peripherals.Analog 21 { 22 public class RenesasDA14_GPADC : BasicDoubleWordPeripheral, IKnownSize 23 { RenesasDA14_GPADC(IMachine machine)24 public RenesasDA14_GPADC(IMachine machine) : base(machine) 25 { 26 DefineRegisters(); 27 resdStream = new RESDStream<VoltageSample>[NumberOfChannels]; 28 samplingTimer = new LimitTimer( 29 machine.ClockSource, 1000000, this, "samplingClock", 30 eventEnabled: true, 31 direction: Direction.Ascending, 32 enabled: false, 33 autoUpdate: false, 34 workMode: WorkMode.OneShot); 35 samplingTimer.LimitReached += OnConversionFinished; 36 } 37 FeedSamplesFromRESD(ReadFilePath filePath, uint adcChannel, uint resdChannel = 0, RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.Specified, long sampleOffsetTime = 0)38 public void FeedSamplesFromRESD(ReadFilePath filePath, uint adcChannel, uint resdChannel = 0, 39 RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.Specified, long sampleOffsetTime = 0) 40 { 41 EnsureChannelIsValid(adcChannel); 42 try 43 { 44 resdStream[adcChannel] = this.CreateRESDStream<VoltageSample>(filePath, resdChannel, sampleOffsetType, sampleOffsetTime); 45 } 46 catch (RESDException) 47 { 48 for(var channelId = 0; channelId < NumberOfChannels; channelId++) 49 { 50 if(resdStream[channelId] != null) 51 { 52 resdStream[channelId].Dispose(); 53 } 54 } 55 throw new RecoverableException($"Could not load RESD channel {resdChannel} from {filePath}"); 56 } 57 } 58 Reset()59 public override void Reset() 60 { 61 base.Reset(); 62 samplingTimer.Reset(); 63 selectNegative = 0; 64 interruptPending.Value = false; 65 UpdateInterrupts(); 66 } 67 68 public GPIO IRQ { get; } = new GPIO(); 69 public long Size => 0x24; 70 ParseResult(uint voltage)71 private uint ParseResult(uint voltage) 72 { 73 return (voltage << (6 - Math.Min(6, (int)conversionNumber.Value))) & ((1 << 16) - 1); 74 } 75 HandleConversion(uint channelId)76 private uint HandleConversion(uint channelId) 77 { 78 EnsureChannelIsValid(channelId); 79 if(resdStream[channelId] == null || resdStream[channelId].TryGetCurrentSample(this, (sample) => sample.Voltage / 1000, out var voltage, out _) != RESDStreamStatus.OK) 80 { 81 return ParseResult(DefaultChannelVoltage); 82 } 83 // The attenuator value scales the allowed voltage input. 84 // If the voltage is greater than the allowed level, then maxVoltage is returned. 85 var maxVoltage = (uint) (0.9 * (attenuator.Value + 1) * 1000); 86 if(voltage > maxVoltage) 87 { 88 this.Log(LogLevel.Warning, "The enabled attenuator allows input voltage up to {0}mV. Provided value: {1}mV", maxVoltage, voltage); 89 return ParseResult(maxVoltage); 90 } 91 return ParseResult(voltage); 92 } 93 EnsureChannelIsValid(uint channelIdx)94 private void EnsureChannelIsValid(uint channelIdx) 95 { 96 if(channelIdx >= NumberOfChannels) 97 { 98 throw new RecoverableException($"Invalid argument value: {channelIdx}. This peripheral implements only channels in range 0-{NumberOfChannels - 1}"); 99 } 100 } 101 StartConversion()102 private void StartConversion() 103 { 104 if(!enabled.Value) 105 { 106 this.Log(LogLevel.Warning, "Tried to start the conversion, but the GP_ADC_EN is not enabled"); 107 } 108 this.Log(LogLevel.Debug, "Starting conversion on channel: {0}", selectPositive.Value); 109 110 // Documentation chapter 21.2.3.1 111 // The total conversion time of an AD conversion depends on various settings. 112 // Conversion time is especially important for the continuous mode 113 samplingTimer.Limit = (ulong) Math.Pow(2, conversionNumber.Value) * (sampleTime.Value == 0 ? 2 : sampleTime.Value * 8) + (samplingTimer.Mode == WorkMode.Periodic ? converionInterval.Value : 0); 114 samplingTimer.Enabled = true; 115 } 116 OnConversionFinished()117 private void OnConversionFinished() 118 { 119 this.Log(LogLevel.Debug, "Ending conversion on channel: {0}", selectPositive.Value); 120 121 // Documentation chapter 21.2.4 122 // With bit GP_ADC_CTRL_REG[GP_ADC_MUTE] = 1, the input is connected to 0.5 × ADC reference. 123 // So, the ideal ADC result should be 511.5. Any deviation from this is the ADC offset 124 if(mute.Value) 125 { 126 result.Value = 0x200 << 6; 127 } 128 else 129 { 130 result.Value = HandleConversion((uint) selectPositive.Value) - (singleEnded.Value ? 0 : HandleConversion((uint) selectNegative)); 131 } 132 interruptPending.Value = true; 133 UpdateInterrupts(); 134 } 135 UpdateInterrupts()136 private void UpdateInterrupts() 137 { 138 bool value = interruptPending.Value && unmaskedInterrupt.Value; 139 IRQ.Set(value); 140 } 141 DefineRegisters()142 private void DefineRegisters() 143 { 144 GeneralPurposeRegisters.Control.Define(this) 145 .WithFlag(0, out enabled, name: "GP_ADC_EN", writeCallback: (_, val) => { if(!val) Reset(); }) 146 .WithFlag(1, valueProviderCallback: _ => samplingTimer.Enabled , name: "GP_ADC_START", changeCallback: (_, val) => 147 { 148 if(val) 149 { 150 StartConversion(); 151 } 152 else 153 { 154 this.Log(LogLevel.Warning, "It is not allowed to write GP_ADC_START bit while it is not (yet) zero."); 155 } 156 }) 157 .WithFlag(2, name: "GP_ADC_CONT", writeCallback: (_, value) => { if(value) samplingTimer.Mode = WorkMode.Periodic; }) 158 .WithFlag(3, out dmaEnabled, name: "GP_ADC_DMA_EN") 159 .WithFlag(4, out interruptPending, FieldMode.Read, name: "GP_ADC_INT") 160 .WithFlag(5, out unmaskedInterrupt, name: "GP_ADC_MINT", writeCallback: (_, __) => UpdateInterrupts()) 161 .WithFlag(6, out singleEnded, name: "GP_ADC_SE") 162 .WithFlag(7, out mute, name: "GP_ADC_MUTE") 163 .WithTaggedFlag("GP_ADC_SIGN", 8) 164 .WithTaggedFlag("GP_ADC_CHOP", 9) 165 .WithTaggedFlag("GP_ADC_LDO_HOLD", 10) 166 .WithReservedBits(11, 1) 167 .WithTaggedFlag("DIE_TEMP_EN", 12) 168 .WithReservedBits(13, 19); 169 170 GeneralPurposeRegisters.Control2.Define(this, resetValue: 0x00000200) 171 .WithValueField(0, 2, out attenuator, name: "GP_ADC_ATTN") 172 .WithTaggedFlag("GP_ADC_I20U", 2) 173 .WithReservedBits(3, 3) 174 .WithValueField(6, 3, out conversionNumber, name: "GP_ADC_CONV_NRS") 175 .WithValueField(9, 4, out sampleTime, name: "GP_ADC_SMPL_TIME") 176 .WithTag("GP_ADC_STORE_DEL", 13 ,3) 177 .WithReservedBits(16, 16); 178 179 GeneralPurposeRegisters.Control3.Define(this, resetValue: 0x00000040) 180 .WithTag("GP_ADC_EN_DEL", 0, 8) 181 .WithValueField(8, 8, out converionInterval, name: "GP_ADC_INTERVAL") 182 .WithReservedBits(16, 16); 183 184 GeneralPurposeRegisters.InputSelection.Define(this) 185 .WithValueField(0, 4, name: "GP_ADC_SEL_N", writeCallback: (_, val) => 186 { 187 if(val <= 3 || val >= 10) 188 { 189 selectNegative = val; 190 } 191 else 192 { 193 this.Log(LogLevel.Warning, "Incorrect GP_ADC_SEL_N value {0}", val); 194 } 195 }) 196 .WithValueField(4, 4, out selectPositive, name: "GP_ADC_SEL_P") 197 .WithReservedBits(8, 24); 198 199 GeneralPurposeRegisters.PositiveOffset.Define(this, resetValue: 0x00000200) 200 .WithTag("GP_ADC_OFFP", 0, 10) 201 .WithReservedBits(10, 22); 202 203 GeneralPurposeRegisters.NegativeOffset.Define(this) 204 .WithTag("GP_ADC_OFFN", 0, 10) 205 .WithReservedBits(10, 22); 206 207 GeneralPurposeRegisters.ClearInterrupt.Define(this) 208 .WithValueField(0, 16, valueProviderCallback: _ => 0, writeCallback: (_, __) => 209 { 210 interruptPending.Value = false; 211 UpdateInterrupts(); 212 }, name: "GP_ADC_CLR_INT") 213 .WithReservedBits(16, 16); 214 215 GeneralPurposeRegisters.ResultRegister.Define(this) 216 .WithValueField(0, 16, out result, FieldMode.Read, name: "GP_ADC_VAL") 217 .WithReservedBits(16, 16); 218 } 219 220 private IFlagRegisterField enabled; 221 private IFlagRegisterField dmaEnabled; 222 private IFlagRegisterField interruptPending; 223 private IFlagRegisterField unmaskedInterrupt; 224 private IFlagRegisterField singleEnded; 225 private IFlagRegisterField mute; 226 private IValueRegisterField attenuator; 227 private IValueRegisterField conversionNumber; 228 private IValueRegisterField sampleTime; 229 private IValueRegisterField converionInterval; 230 private IValueRegisterField selectPositive; 231 private IValueRegisterField result; 232 233 private const int NumberOfChannels = 14; 234 private const uint DefaultChannelVoltage = 100; 235 236 private readonly LimitTimer samplingTimer; 237 private readonly RESDStream<VoltageSample>[] resdStream; 238 private ulong selectNegative; 239 240 private enum GeneralPurposeRegisters : long 241 { 242 Control = 0x0, 243 Control2 = 0x4, 244 Control3 = 0x8, 245 InputSelection = 0xC, 246 PositiveOffset = 0x10, 247 NegativeOffset = 0x14, 248 ClearInterrupt = 0x1C, 249 ResultRegister = 0x20, 250 } 251 } 252 } 253