1 // 2 // Copyright (c) 2010-2022 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 Antmicro.Renode.Core; 9 using Antmicro.Renode.Core.Structure.Registers; 10 using Antmicro.Renode.Logging; 11 12 namespace Antmicro.Renode.Peripherals.Analog 13 { 14 public class MAX32650_ADC : BasicDoubleWordPeripheral, IKnownSize 15 { MAX32650_ADC(IMachine machine)16 public MAX32650_ADC(IMachine machine) : base(machine) 17 { 18 IRQ = new GPIO(); 19 DefineRegisters(); 20 } 21 Reset()22 public override void Reset() 23 { 24 base.Reset(); 25 IRQ.Unset(); 26 } 27 28 public GPIO IRQ { get; } 29 30 public long Size => 0x1000; 31 32 public uint AIn0 33 { 34 get => aIn0; 35 set => aIn0 = ValidateAIn(0, value); 36 } 37 38 public uint AIn1 39 { 40 get => aIn1; 41 set => aIn1 = ValidateAIn(1, value); 42 } 43 44 public uint AIn2 45 { 46 get => aIn2; 47 set => aIn2 = ValidateAIn(2, value); 48 } 49 50 public uint AIn3 51 { 52 get => aIn3; 53 set => aIn3 = ValidateAIn(3, value); 54 } 55 GetValueFromActiveChannel()56 private uint GetValueFromActiveChannel() 57 { 58 switch(currentChannel.Value) 59 { 60 case Channels.AIn0: 61 return AIn0; 62 case Channels.AIn1: 63 return AIn1; 64 case Channels.AIn2: 65 return AIn2; 66 case Channels.AIn3: 67 return AIn3; 68 case Channels.AIn0Div5: 69 return AIn0 / 5; 70 case Channels.AIn1Div5: 71 return AIn1 / 5; 72 default: 73 this.Log(LogLevel.Warning, "{0} is not supported; ignoring", currentChannel.Value); 74 return 0x00; 75 } 76 } 77 ValidateAIn(int idx, uint value)78 private uint ValidateAIn(int idx, uint value) 79 { 80 if(value > ADCDataMask) 81 { 82 this.Log(LogLevel.Warning, "Tried to set AIn{0} to 0x{1:X}, which is bigger than 10-bits; value has been truncated to 0x{2:X03}", idx, value, value & ADCDataMask); 83 value &= ADCDataMask; 84 } 85 return value; 86 } 87 UpdateInterrupts()88 private void UpdateInterrupts() 89 { 90 var pending = false; 91 92 pending |= interruptDoneEnabled.Value && interruptDonePending.Value; 93 pending |= interruptReferenceReadyEnabled.Value && interruptReferenceReadyPending.Value; 94 95 interruptAnyPending.Value = pending; 96 IRQ.Set(pending); 97 } 98 DefineRegisters()99 private void DefineRegisters() 100 { 101 Registers.Control.Define(this) 102 .WithFlag(0, FieldMode.WriteOneToClear, name: "CTRL.start", 103 writeCallback: (_, value) => 104 { 105 if(value) 106 { 107 interruptDonePending.Value = true; 108 UpdateInterrupts(); 109 } 110 }) 111 .WithTaggedFlag("CTRL.pwr", 1) 112 .WithReservedBits(2, 1) 113 .WithFlag(3, name: "CTRL.refbuf_pwr", 114 writeCallback: (_, value) => 115 { 116 if(value) 117 { 118 this.machine.LocalTimeSource.ExecuteInNearestSyncedState(__ => 119 { 120 interruptReferenceReadyPending.Value = true; 121 UpdateInterrupts(); 122 }); 123 } 124 }) 125 .WithFlag(4, name: "CTRL.ref_sel") 126 .WithReservedBits(5, 3) 127 .WithTaggedFlag("CTRL.ref_scale", 8) 128 .WithTaggedFlag("CTRL.input_scale", 9) 129 .WithReservedBits(10, 1) 130 .WithTaggedFlag("CTRL.clk_en", 11) 131 .WithEnumField<DoubleWordRegister, Channels>(12, 4, out currentChannel, name: "CTRL.ch_sel") 132 .WithReservedBits(16, 1) 133 .WithEnumField<DoubleWordRegister, Alignment>(17, 1, out dataAlignment, name: "CTRL.data_align") 134 .WithReservedBits(19, 13) 135 ; 136 137 Registers.Status.Define(this) 138 .WithFlag(0, FieldMode.Read, name: "STATUS.active", 139 valueProviderCallback: _ => false) 140 .WithReservedBits(1, 1) 141 .WithTaggedFlag("STATUS.pwr_up_active", 2) 142 .WithTaggedFlag("STATUS.overflow", 3) 143 .WithReservedBits(4, 28) 144 ; 145 146 Registers.OutputData.Define(this) 147 .WithValueField(0, 16, name: "DATA.data", 148 valueProviderCallback: _ => 149 { 150 var data = GetValueFromActiveChannel(); 151 // Data can be justified either to LSB or MSB of the register. 152 // When alignment is set to LSB, we don't have to do anything; 153 // when it's set to MSB, we have to shift it in such way that 154 // MSB of 10-bit data is at 15th position of the register. 155 if(dataAlignment.Value == Alignment.MSB) 156 { 157 data <<= 6; 158 } 159 return data; 160 }) 161 .WithReservedBits(16, 16) 162 ; 163 164 Registers.InterruptControl.Define(this) 165 .WithFlag(0, out interruptDoneEnabled, name: "INTR.done_ie") 166 .WithFlag(1, out interruptReferenceReadyEnabled, name: "INTR.ref_ready_ie") 167 .WithTaggedFlag("INTR.hi_limit_ie", 2) 168 .WithTaggedFlag("INTR.lo_limit_ie", 3) 169 .WithTaggedFlag("INTR.overflow_ie", 4) 170 .WithReservedBits(5, 11) 171 .WithFlag(16, out interruptDonePending, FieldMode.WriteOneToClear | FieldMode.Read, name: "INTR.done_if") 172 .WithFlag(17, out interruptReferenceReadyPending, FieldMode.WriteOneToClear | FieldMode.Read, name: "INTR.ref_ready_if") 173 .WithTaggedFlag("INTR.hi_limit_if", 18) 174 .WithTaggedFlag("INTR.lo_limit_if", 19) 175 .WithTaggedFlag("INTR.overflow_if", 20) 176 .WithReservedBits(21, 1) 177 .WithFlag(22, out interruptAnyPending, FieldMode.Read, name: "INTR.pending") 178 .WithReservedBits(23, 9) 179 .WithChangeCallback((_, __) => UpdateInterrupts()) 180 ; 181 182 for(var i = 0; i < LimitRegistersCount; ++i) 183 { 184 (Registers.Limit0 + (long)(i * 0x04)).Define(this) 185 .WithTag($"LIMIT{i}.ch_lo_limit", 0, 10) 186 .WithReservedBits(10, 2) 187 .WithTag($"LIMIT{i}.ch_hi_limit", 12, 10) 188 .WithReservedBits(22, 2) 189 .WithTag($"LIMIT{i}.ch_sel", 24, 4) 190 .WithTaggedFlag($"LIMIT{i}.ch_lo_limit_en", 28) 191 .WithTaggedFlag($"LIMIT{i}.ch_hi_limit_en", 29) 192 .WithReservedBits(30, 2) 193 ; 194 } 195 } 196 197 private const int LimitRegistersCount = 4; 198 private const uint ADCDataMask = 0x3FF; 199 200 private IEnumRegisterField<Alignment> dataAlignment; 201 private IEnumRegisterField<Channels> currentChannel; 202 203 private IFlagRegisterField interruptDoneEnabled; 204 private IFlagRegisterField interruptReferenceReadyEnabled; 205 206 private IFlagRegisterField interruptDonePending; 207 private IFlagRegisterField interruptReferenceReadyPending; 208 private IFlagRegisterField interruptAnyPending; 209 210 private uint aIn0; 211 private uint aIn1; 212 private uint aIn2; 213 private uint aIn3; 214 215 private enum Alignment 216 { 217 LSB = 0, 218 MSB, 219 } 220 221 private enum Channels 222 { 223 AIn0 = 0, 224 AIn1, 225 AIn2, 226 AIn3, 227 AIn0Div5, 228 AIn1Div5, 229 VDDBDiv5, 230 VDDA, 231 VCORE, 232 VRTCDiv2, 233 Reserved, 234 VDDIODiv4, 235 VDDIOHDiv4, 236 } 237 238 private enum Registers : long 239 { 240 Control = 0x00, 241 Status = 0x04, 242 OutputData = 0x08, 243 InterruptControl = 0x0C, 244 Limit0 = 0x10, 245 Limit1 = 0x14, 246 Limit2 = 0x18, 247 Limit3 = 0x1C, 248 } 249 } 250 } 251