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 8 using System; 9 using System.Collections.Generic; 10 using System.IO; 11 using System.Linq; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Core.Structure.Registers; 14 using Antmicro.Renode.Exceptions; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Renode.Peripherals.Sensors; 17 using Antmicro.Renode.Utilities; 18 19 namespace Antmicro.Renode.Peripherals.Analog 20 { 21 public class IMXRT_ADC : BasicDoubleWordPeripheral, IGPIOReceiver, IKnownSize 22 { IMXRT_ADC(IMachine machine)23 public IMXRT_ADC(IMachine machine) : base(machine) 24 { 25 samplesFifos = new SensorSamplesFifo<ScalarSample>[NumberOfChannels]; 26 for(var i = 0; i < samplesFifos.Length; i++) 27 { 28 samplesFifos[i] = new SensorSamplesFifo<ScalarSample>(); 29 } 30 31 conversionCompleteInterruptEnable = new IFlagRegisterField[NumberOfHardwareTriggers]; 32 inputChannel = new IValueRegisterField[NumberOfHardwareTriggers]; 33 cdata = new IValueRegisterField[NumberOfHardwareTriggers]; 34 35 IRQ = new GPIO(); 36 DefineRegisters(); 37 } 38 39 public GPIO IRQ { get; } 40 41 public long Size => 0x5C; 42 OnGPIO(int number, bool value)43 public void OnGPIO(int number, bool value) 44 { 45 if(number < 0 || number >= NumberOfHardwareTriggers) 46 { 47 this.Log(LogLevel.Warning, "Unsupported external hardware trigger #{0} (supported are 0-{1})", number, NumberOfHardwareTriggers - 1); 48 return; 49 } 50 51 if(value) 52 { 53 HandleTrigger(number, true); 54 } 55 } 56 Reset()57 public override void Reset() 58 { 59 base.Reset(); 60 UpdateInterrupts(); 61 } 62 FeedSample(byte sample, int channel)63 public void FeedSample(byte sample, int channel) 64 { 65 if(channel < 0 || channel >= NumberOfChannels) 66 { 67 throw new RecoverableException($"This model supports channels 0-{(NumberOfChannels- 1)} inclusive"); 68 } 69 70 samplesFifos[channel].FeedSample(new ScalarSample(sample)); 71 } 72 FeedSamplesFromFile(string path, int channel)73 public void FeedSamplesFromFile(string path, int channel) 74 { 75 if(channel < 0 || channel >= NumberOfChannels) 76 { 77 throw new RecoverableException($"This model supports channels 0-{(NumberOfChannels- 1)} inclusive"); 78 } 79 80 samplesFifos[channel].FeedSamplesFromFile(path); 81 } 82 TriggerConversion(uint channel)83 public void TriggerConversion(uint channel) 84 { 85 if(channel >= NumberOfChannels) 86 { 87 throw new RecoverableException($"This model supports channels 0-{(NumberOfChannels - 1)} inclusive"); 88 } 89 90 this.Log(LogLevel.Debug, "Starting conversion on channel #{0}", channel); 91 samplesFifos[channel].TryDequeueNewSample(); 92 cdata[channel].Value = (uint)samplesFifos[channel].Sample.Value; 93 94 conversionComplete.Value = true; 95 UpdateInterrupts(); 96 } 97 HandleTrigger(int triggerId, bool isHW)98 private void HandleTrigger(int triggerId, bool isHW) 99 { 100 this.Log(LogLevel.Debug, "Trigger #{0} fired", triggerId); 101 102 var channel = (uint)inputChannel[triggerId].Value; 103 if(channel == ConversionDisabledMask) 104 { 105 this.Log(LogLevel.Warning, "Trigger #{0} fired, but the conversion is disabled", triggerId); 106 return; 107 } 108 if(channel == AdcEtcMask) 109 { 110 this.Log(LogLevel.Warning, "External channel selection is not supported for trigger #{0}", triggerId); 111 return; 112 } 113 if(channel > 15) 114 { 115 this.Log(LogLevel.Warning, "Unsupported channel #{0} selected for trigger #{1}", channel, triggerId); 116 return; 117 } 118 119 if(isHW && !hardwareTriggerSelect.Value) 120 { 121 this.Log(LogLevel.Warning, "Trigger #{0} fired, but hardware trigger select is not set", triggerId); 122 return; 123 } 124 125 TriggerConversion(channel); 126 } 127 DefineRegisters()128 private void DefineRegisters() 129 { 130 Registers.HardwareTriggersControl0.DefineMany(this, NumberOfHardwareTriggers, (register, idx) => 131 { 132 var j = idx; 133 register 134 .WithValueField(0, 5, out inputChannel[j], name: "ADCH - Input Channel Select") 135 .WithReservedBits(5, 2) 136 .WithFlag(7, out conversionCompleteInterruptEnable[j], name: "AIEN - Conversion Complete Interrupt Enable/Disable Control") 137 .WithReservedBits(8, 24) 138 .WithWriteCallback((_, __) => 139 { 140 if(j == 0) 141 { 142 // only trigger 0 can be fired by SW 143 HandleTrigger(0, false); 144 } 145 }); 146 }); 147 148 // NOTE: the documentation says there is only one bit here, 149 // but shouldn't there be more? 150 Registers.HardwareTriggersStatus.Define(this) 151 .WithFlag(0, out conversionComplete, FieldMode.Read, name: "COCO0 - Conversion Complete Flag") 152 .WithReservedBits(1, 31); 153 154 Registers.HardwareTriggersDataResult0.DefineMany(this, NumberOfHardwareTriggers, (register, idx) => 155 { 156 register 157 .WithValueField(0, 12, out cdata[idx], FieldMode.Read, name: "CDATA - Data") 158 .WithReservedBits(12, 20) 159 .WithReadCallback((_, __) => 160 { 161 conversionComplete.Value = false; // Set this to clear COCOn and turn off IRQ after read 162 UpdateInterrupts(); 163 }); 164 }); 165 166 Registers.Configuration.Define(this) 167 .WithTag("ADICLK - Input Clock Select", 0, 2) 168 .WithTag("MODE - Conversion Mode Selection", 2, 2) 169 .WithTaggedFlag("ADLSMP - Long Sample Time Configuration", 4) 170 .WithTag("ADIV - Clock Divide Select", 5, 2) 171 .WithTaggedFlag("ADLPC - Low-Power Configuration", 7) 172 .WithTag("ADSTS - Total Sample Duration In Number Of Full Cycles", 8, 2) 173 .WithTaggedFlag("ADHSC - High Speed Configuration", 10) 174 .WithTag("REFSEL - Voltage Reference Selection", 11, 2) 175 .WithFlag(13, out hardwareTriggerSelect, name: "ADTRG - Conversion Trigger Select") 176 .WithTag("AVGS - Hardware Avarage select", 14, 2) 177 .WithTaggedFlag("OVWREN - Data Overwrite Enable", 16) 178 .WithReservedBits(17, 15); 179 180 Registers.GeneralControl.Define(this) 181 .WithTaggedFlag("ADACKEN - Asynchronous clock output enable", 0) 182 .WithTaggedFlag("DMAEN - DMA Enable", 1) 183 .WithTaggedFlag("ACREN - Compare Function Range Enable", 2) 184 .WithTaggedFlag("ACFGT - Compare Function Enable", 3) 185 .WithTaggedFlag("ACFE - Compare Function Enable", 4) 186 .WithTaggedFlag("AVGE - Hardware avarage enable", 5) 187 .WithTaggedFlag("ADCO - Continouous Conversion Enable", 6) 188 .WithFlag(7, name: "CAL - Calibration", valueProviderCallback: _ => false) // calibration ends right away 189 .WithReservedBits(8, 24); 190 191 Registers.GeneralStatus.Define(this) 192 .WithTaggedFlag("ADACT - Conversion Active", 0) 193 .WithTaggedFlag("CALF - Calibration Failed Flag", 1) 194 .WithTaggedFlag("AWKST - Asynchronous wakeup interrupt status", 2) 195 .WithReservedBits(3, 29); 196 197 Registers.CompareValue.Define(this) 198 .WithTag("CV1 - Compare Value 1", 0, 12) 199 .WithReservedBits(12, 4) 200 .WithTag("CV2 - Compare Value 2", 16, 12) 201 .WithReservedBits(28, 4); 202 203 Registers.OffsetCorrectionValue.Define(this) 204 .WithTag("OFS - Offset value", 0, 12) 205 .WithTaggedFlag("SIGN - Sign bit", 12) 206 .WithReservedBits(13, 19); 207 208 Registers.CalibrationValue.Define(this) 209 .WithTag("CAL_CODE - Calibration Result Value", 0, 4) 210 .WithReservedBits(4, 28); 211 } 212 UpdateInterrupts()213 private void UpdateInterrupts() 214 { 215 var flag = false; 216 217 flag |= conversionComplete.Value && conversionCompleteInterruptEnable.Any(x => x.Value); 218 219 this.Log(LogLevel.Info, "Setting IRQ to {0}", flag); 220 IRQ.Set(flag); 221 } 222 223 private IFlagRegisterField hardwareTriggerSelect; 224 private IFlagRegisterField conversionComplete; 225 226 private readonly IFlagRegisterField[] conversionCompleteInterruptEnable; 227 private readonly IValueRegisterField[] inputChannel; 228 private readonly IValueRegisterField[] cdata; 229 private readonly SensorSamplesFifo<ScalarSample>[] samplesFifos; 230 231 private const int NumberOfHardwareTriggers = 8; 232 private const int NumberOfChannels = 16; 233 234 private const int AdcEtcMask = 0b10000; 235 private const int ConversionDisabledMask = 0b11111; 236 237 private enum Registers 238 { 239 HardwareTriggersControl0 = 0x00, 240 HardwareTriggersControl1 = 0x04, 241 HardwareTriggersControl2 = 0x08, 242 HardwareTriggersControl3 = 0x0C, 243 HardwareTriggersControl4 = 0x10, 244 HardwareTriggersControl5 = 0x14, 245 HardwareTriggersControl6 = 0x18, 246 HardwareTriggersControl7 = 0x1C, 247 248 HardwareTriggersStatus = 0x20, 249 250 HardwareTriggersDataResult0 = 0x24, 251 HardwareTriggersDataResult1 = 0x28, 252 HardwareTriggersDataResult2 = 0x2C, 253 HardwareTriggersDataResult3 = 0x30, 254 HardwareTriggersDataResult4 = 0x34, 255 HardwareTriggersDataResult5 = 0x38, 256 HardwareTriggersDataResult6 = 0x3C, 257 HardwareTriggersDataResult7 = 0x40, 258 259 Configuration = 0x44, 260 GeneralControl = 0x48, 261 GeneralStatus = 0x4C, 262 CompareValue = 0x50, 263 OffsetCorrectionValue = 0x54, 264 CalibrationValue = 0x58, 265 } 266 } 267 } 268