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 using System; 8 using System.Collections.Generic; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Peripherals.Bus; 13 14 namespace Antmicro.Renode.Peripherals.Analog 15 { 16 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)] 17 public class AmbiqApollo4_ADC : BasicDoubleWordPeripheral, IKnownSize 18 { AmbiqApollo4_ADC(IMachine machine)19 public AmbiqApollo4_ADC(IMachine machine) : base(machine) 20 { 21 // This is just a mock. Getters simply read the properties. 22 channelDataGettersArray = new Func<uint>[] 23 { 24 () => Channel0Data, () => Channel1Data, () => Channel2Data, () => Channel3Data, () => Channel4Data, () => Channel5Data, 25 () => Channel6Data, () => Channel7Data, () => Channel8Data, () => Channel9Data, () => Channel10Data, () => Channel11Data, 26 }; 27 28 fifo = new Queue<FifoEntry>(); 29 interruptStatuses = new bool[InterruptsCount]; 30 IRQ = new GPIO(); 31 32 slots = new Slot[SlotsCount]; 33 for(int slotNumber = 0; slotNumber < SlotsCount; slotNumber++) 34 { 35 slots[slotNumber] = new Slot(slotNumber); 36 } 37 38 DefineRegisters(); 39 Reset(); 40 } 41 Reset()42 public override void Reset() 43 { 44 base.Reset(); 45 46 fifo.Clear(); 47 for(int interruptNumber = 0; interruptNumber < InterruptsCount; interruptNumber++) 48 { 49 interruptStatuses[interruptNumber] = false; 50 } 51 } 52 ScanAllSlots()53 public void ScanAllSlots() 54 { 55 this.Log(LogLevel.Noisy, "Scanning all enabled slots..."); 56 foreach(var slot in slots) 57 { 58 if(slot.IsEnabled) 59 { 60 var channelNumber = (int)slot.channelSelect.Value; 61 if(TryGetDataFromChannel(channelNumber, out var data)) 62 { 63 PushToFifo(data, (uint)slot.Number); 64 } 65 } 66 } 67 } 68 WriteDoubleWord(long offset, uint value)69 public override void WriteDoubleWord(long offset, uint value) 70 { 71 switch((Registers)offset) 72 { 73 case Registers.Configuration: 74 case Registers.Slot0Configuration: 75 case Registers.Slot1Configuration: 76 case Registers.Slot2Configuration: 77 case Registers.Slot3Configuration: 78 case Registers.Slot4Configuration: 79 case Registers.Slot5Configuration: 80 case Registers.Slot6Configuration: 81 case Registers.Slot7Configuration: 82 // Only the configuration changes which stop ADC are allowed if it's enabled. 83 var writeStopsTheModule = (Registers)offset == Registers.Configuration && (value & 1) == 0; 84 if(!moduleEnabled.Value || writeStopsTheModule) 85 { 86 break; 87 } 88 this.Log(LogLevel.Warning, "{0}: Ignoring the write with value: 0x{1:X}; the module has to be disabled first.", (Registers)offset, value); 89 return; 90 } 91 base.WriteDoubleWord(offset, value); 92 } 93 94 public uint Channel0Data { get; set; } 95 public uint Channel1Data { get; set; } 96 public uint Channel2Data { get; set; } 97 public uint Channel3Data { get; set; } 98 public uint Channel4Data { get; set; } 99 public uint Channel5Data { get; set; } 100 public uint Channel6Data { get; set; } 101 public uint Channel7Data { get; set; } 102 public uint Channel8Data { get; set; } 103 public uint Channel9Data { get; set; } 104 public uint Channel10Data { get; set; } 105 public uint Channel11Data { get; set; } 106 107 public GPIO IRQ { get; } 108 109 public long Size => 0x294; 110 DefineRegisters()111 private void DefineRegisters() 112 { 113 Registers.Configuration.Define(this) 114 .WithFlag(0, out moduleEnabled, name: "ADCEN", writeCallback: (oldValue, newValue) => { if(oldValue && !newValue) fifo.Clear(); }) 115 .WithReservedBits(1, 1) 116 .WithTaggedFlag("RPTEN", 2) 117 .WithTaggedFlag("LPMODE", 3) 118 .WithTaggedFlag("CKMODE", 4) 119 .WithReservedBits(5, 7) 120 .WithFlag(12, out fifoPushEnabled, name: "DFIFORDEN") 121 .WithReservedBits(13, 3) 122 .WithTag("TRIGSEL", 16, 3) 123 .WithTaggedFlag("TRIGPOL", 19) 124 .WithTaggedFlag("RPTTRIGSEL", 20) 125 .WithReservedBits(21, 3) 126 .WithTag("CLKSEL", 24, 2) 127 .WithReservedBits(26, 6) 128 ; 129 130 Registers.PowerStatus.Define(this) 131 .WithTaggedFlag("PWDSTAT", 0) 132 .WithReservedBits(1, 31) 133 ; 134 135 Registers.SoftwareTrigger.Define(this) 136 .WithValueField(0, 8, FieldMode.Write, name: "SWT", writeCallback: (_, newValue) => 137 { 138 // Writing the magic value initiates a scan regardless of the Trigger Select field in the Configuration register. 139 if(newValue == SoftwareTriggerMagicValue) 140 { 141 ScanAllSlots(); 142 } 143 }) 144 .WithReservedBits(8, 24) 145 ; 146 147 Registers.Slot0Configuration.Define32Many(this, SlotsCount, (register, index) => 148 { 149 register 150 .WithFlag(0, out slots[index].enableFlag, name: $"SLEN{index}") 151 .WithTaggedFlag($"WCEN{index}", 1) 152 .WithReservedBits(2, 6) 153 .WithEnumField(8, 4, out slots[index].channelSelect, name: $"CHSEL{index}", writeCallback: (oldValue, newValue) => 154 { 155 if((int)newValue >= ChannelsCount) 156 { 157 this.Log(LogLevel.Error, "Slot{0}: Invalid channel select: {1}; the previous value will be kept: {2}", 158 index, newValue, oldValue); 159 slots[index].channelSelect.Value = oldValue; 160 } 161 }) 162 .WithReservedBits(12, 4) 163 .WithTag($"PRMODE{index}", 16, 2) 164 .WithTag($"TRKCYC{index}", 18, 6) 165 .WithTag($"ADSEL{index}", 24, 3) 166 .WithReservedBits(27, 5) 167 ; 168 }); 169 170 Registers.WindowComparatorUpperLimits.Define(this) 171 .WithTag("ULIM", 0, 20) 172 .WithReservedBits(20, 12) 173 ; 174 175 Registers.WindowComparatorLowerLimits.Define(this) 176 .WithTag("LLIM", 0, 20) 177 .WithReservedBits(20, 12) 178 ; 179 180 Registers.ScaleWindowComparatorLimits.Define(this) 181 .WithTaggedFlag("SCWLIMEN", 0) 182 .WithReservedBits(1, 31) 183 ; 184 185 Registers.Fifo.Define(this) 186 .WithValueField(0, 20, name: "DATA", valueProviderCallback: _ => fifo.Count > 0 ? fifo.Peek().Data : 0x0) 187 .WithValueField(20, 8, name: "COUNT", valueProviderCallback: _ => (uint)fifo.Count) 188 .WithValueField(28, 3, name: "SLOTNUM", valueProviderCallback: _ => fifo.Count > 0 ? fifo.Peek().SlotNumber : 0x0) 189 .WithTaggedFlag("RSVD", 31) 190 // Writing FIFO register with any value causes the Pop to occur. 191 .WithWriteCallback((__, ___) => { if(fifo.Count > 0) _ = fifo.Dequeue(); }) 192 ; 193 194 Registers.FifoPopRead.Define(this) 195 .WithValueField(0, 20, FieldMode.Read, name: "DATA", 196 valueProviderCallback: _ => (fifoPushEnabled.Value && fifo.Count > 0) ? fifo.Peek().Data : 0x0) 197 .WithValueField(20, 8, FieldMode.Read, name: "COUNT", valueProviderCallback: _ => (uint)fifo.Count) 198 .WithValueField(28, 3, FieldMode.Read, name: "SLOTNUMPR", 199 valueProviderCallback: _ => (fifoPushEnabled.Value && fifo.Count > 0) ? fifo.Peek().SlotNumber : 0x0) 200 .WithTaggedFlag("RSVDPR", 31) 201 // Reading FIFOPR register causes the Pop to occur if it's enabled in the Configuration register. 202 .WithReadCallback((__, ___) => { if(fifoPushEnabled.Value && fifo.Count > 0) _ = fifo.Dequeue(); }) 203 ; 204 205 Registers.InternalTimerConfiguration.Define(this) 206 .WithTag("TIMERMAX", 0, 10) 207 .WithReservedBits(10, 6) 208 .WithTag("CLKDIV", 16, 3) 209 .WithReservedBits(19, 12) 210 .WithTaggedFlag("TIMEREN", 31) 211 ; 212 213 Registers.ZeroCrossingComparatorConfiguration.Define(this) 214 .WithTaggedFlag("ZXEN", 0) 215 .WithReservedBits(1, 3) 216 .WithTaggedFlag("ZXCHANSEL", 4) 217 .WithReservedBits(5, 27) 218 ; 219 220 Registers.ZeroCrossingComparatorLimits.Define(this) 221 .WithTag("LZXC", 0, 12) 222 .WithReservedBits(12, 4) 223 .WithTag("UZXC", 16, 12) 224 .WithReservedBits(28, 4) 225 ; 226 227 Registers.PGAGainConfiguration.Define(this) 228 .WithTaggedFlag("PGACTRLEN", 0) 229 .WithReservedBits(1, 3) 230 .WithTaggedFlag("UPDATEMODE", 4) 231 .WithReservedBits(5, 27) 232 ; 233 234 Registers.PGAGainCodes.Define(this) 235 .WithTag("LGA", 0, 7) 236 .WithReservedBits(7, 1) 237 .WithTag("HGADELTA", 8, 7) 238 .WithReservedBits(15, 1) 239 .WithTag("LGB", 16, 7) 240 .WithReservedBits(23, 1) 241 .WithTag("HGBDELTA", 24, 7) 242 .WithReservedBits(31, 1) 243 ; 244 245 Registers.SaturationComparatorConfiguration.Define(this) 246 .WithTaggedFlag("SATEN", 0) 247 .WithReservedBits(1, 3) 248 .WithTaggedFlag("SATCHANSEL", 4) 249 .WithReservedBits(5, 27) 250 ; 251 252 Registers.SaturationComparatorLimits.Define(this) 253 .WithTag("LSATC", 0, 12) 254 .WithReservedBits(12, 4) 255 .WithTag("USATC", 16, 12) 256 .WithReservedBits(28, 4) 257 ; 258 259 Registers.SaturationComparatorEventCounterLimits.Define(this, 0x00010001) 260 .WithTag("SATCAMAX", 0, 12) 261 .WithReservedBits(12, 4) 262 .WithTag("SATCBMAX", 16, 12) 263 .WithReservedBits(28, 4) 264 ; 265 266 Registers.SaturationComparatorEventCounterClear.Define(this) 267 .WithTaggedFlag("SATCACLR", 0) 268 .WithTaggedFlag("SATCBCLR", 1) 269 .WithReservedBits(2, 30) 270 ; 271 272 Registers.InterruptEnable.Define(this) 273 .WithFlags(0, 12, out interruptEnableFlags, name: "INTENx") 274 .WithReservedBits(12, 20) 275 .WithChangeCallback((_, __) => UpdateIRQ()) 276 ; 277 278 Registers.InterruptStatus.Define(this) 279 .WithFlags(0, 12, FieldMode.Read, name: "INTSTATx", valueProviderCallback: (interrupt, _) => interruptStatuses[interrupt]) 280 .WithReservedBits(12, 20) 281 ; 282 283 Registers.InterruptClear.Define(this) 284 .WithFlags(0, 12, FieldMode.Write, name: "INTCLRx", 285 writeCallback: (interrupt, _, newValue) => { if(newValue) SetInterruptStatus((Interrupts)interrupt, false); }) 286 .WithReservedBits(12, 20) 287 ; 288 289 Registers.InterruptSet.Define(this) 290 .WithFlags(0, 12, FieldMode.Write, name: "INTSETx", 291 writeCallback: (interrupt, _, newValue) => { if(newValue) SetInterruptStatus((Interrupts)interrupt, true); }) 292 .WithReservedBits(12, 20) 293 ; 294 295 Registers.DMATriggerEnable.Define(this) 296 .WithTaggedFlag("DFIFO75", 0) 297 .WithTaggedFlag("DFIFOFULL", 1) 298 .WithReservedBits(2, 30) 299 ; 300 301 Registers.DMATriggerStatus.Define(this) 302 .WithTaggedFlag("D75STAT", 0) 303 .WithTaggedFlag("DFULLSTAT", 1) 304 .WithReservedBits(2, 30) 305 ; 306 307 Registers.DMAConfiguration.Define(this) 308 .WithTaggedFlag("DMAEN", 0) 309 .WithReservedBits(1, 1) 310 .WithTaggedFlag("DMADIR", 2) 311 .WithReservedBits(3, 5) 312 .WithTaggedFlag("DMAPRI", 8) 313 .WithTaggedFlag("DMADYNPRI", 9) 314 .WithReservedBits(10, 7) 315 .WithTaggedFlag("DMAMSK", 17) 316 .WithTaggedFlag("DPWROFF", 18) 317 .WithReservedBits(19, 13) 318 ; 319 320 Registers.DMATotalTransferCount.Define(this) 321 .WithReservedBits(0, 2) 322 .WithTag("TOTCOUNT", 2, 16) 323 .WithReservedBits(18, 14) 324 ; 325 326 Registers.DMATargetAddress.Define(this, 0x10000000) 327 .WithTag("LTARGADDR", 0, 28) 328 .WithTag("UTARGADDR", 28, 4) 329 ; 330 331 Registers.DMAStatus.Define(this) 332 .WithTaggedFlag("DMATIP", 0) 333 .WithTaggedFlag("DMACPL", 1) 334 .WithTaggedFlag("DMAERR", 2) 335 .WithReservedBits(3, 29) 336 ; 337 } 338 PushToFifo(uint data, uint slotNumber)339 private void PushToFifo(uint data, uint slotNumber) 340 { 341 this.Log(LogLevel.Noisy, "Data pushed to Fifo for slot#{0}: 0x{1:X}", slotNumber, data); 342 fifo.Enqueue(new FifoEntry(data, slotNumber)); 343 SetInterruptStatus(Interrupts.ConversionComplete, true); 344 } 345 SetInterruptStatus(Interrupts interrupt, bool value)346 private void SetInterruptStatus(Interrupts interrupt, bool value) 347 { 348 if(interruptStatuses[(int)interrupt] != value) 349 { 350 this.NoisyLog("{0} interrupt status {1}", interrupt, value ? "set" : "reset"); 351 interruptStatuses[(int)interrupt] = value; 352 UpdateIRQ(); 353 } 354 } 355 TryGetDataFromChannel(int channelNumber, out uint data)356 private bool TryGetDataFromChannel(int channelNumber, out uint data) 357 { 358 data = 0x0; 359 if(channelNumber >= ChannelsCount) 360 { 361 this.Log(LogLevel.Warning, "Invalid channel number: {0}", channelNumber); 362 return false; 363 } 364 365 var channelDataGet = channelDataGettersArray[channelNumber]; 366 data = channelDataGet(); 367 return true; 368 } 369 UpdateIRQ()370 private void UpdateIRQ() 371 { 372 var newStatus = false; 373 for(int i = 0; i < InterruptsCount; i++) 374 { 375 if(interruptStatuses[i] && interruptEnableFlags[i].Value) 376 { 377 newStatus = true; 378 break; 379 } 380 } 381 382 if(newStatus != IRQ.IsSet) 383 { 384 this.NoisyLog("IRQ {0}", newStatus ? "set" : "reset"); 385 IRQ.Set(newStatus); 386 } 387 } 388 389 private IFlagRegisterField fifoPushEnabled; 390 private IFlagRegisterField[] interruptEnableFlags; 391 private IFlagRegisterField moduleEnabled; 392 393 private readonly Func<uint>[] channelDataGettersArray; 394 private readonly Queue<FifoEntry> fifo; 395 private readonly bool[] interruptStatuses; 396 private readonly Slot[] slots; 397 398 private const int ChannelsCount = 12; 399 private const int InterruptsCount = 12; 400 private const int SlotsCount = 8; 401 private const int SoftwareTriggerMagicValue = 0x37; 402 403 private struct FifoEntry 404 { FifoEntryAntmicro.Renode.Peripherals.Analog.AmbiqApollo4_ADC.FifoEntry405 public FifoEntry(uint data, uint slotNumber) 406 { 407 Data = data; 408 SlotNumber = slotNumber; 409 } 410 411 public uint Data; 412 public uint SlotNumber; 413 } 414 415 private class Slot 416 { Slot(int number)417 public Slot(int number) 418 { 419 Number = number; 420 } 421 422 public bool IsEnabled => enableFlag.Value; 423 public int Number { get; } 424 425 public IEnumRegisterField<Channels> channelSelect; 426 public IFlagRegisterField enableFlag; 427 } 428 429 private enum Channels 430 { 431 SingleEndedExternalGPIOPad16 = 0x0, 432 SingleEndedExternalGPIOPad29 = 0x1, 433 SingleEndedExternalGPIOPad11 = 0x2, 434 SingleEndedExternalGPIOPad31 = 0x3, 435 SingleEndedExternalGPIOPad32 = 0x4, 436 SingleEndedExternalGPIOPad33 = 0x5, 437 SingleEndedExternalGPIOPad34 = 0x6, 438 SingleEndedExternalGPIOPad35 = 0x7, 439 InternalTemperatureSensor = 0x8, 440 InternalVoltageDivideByThreeConnection = 0x9, 441 AnalogTestmux = 0xA, 442 InputVSS = 0xB, 443 } 444 445 private enum Interrupts 446 { 447 // For values based on multiple scans, this is set only when the average value is pushed to FIFO. 448 ConversionComplete = 0, 449 ScanComplete = 1, 450 Fifo75PercentFull = 2, 451 Fifo100PercentFull = 3, 452 WindowComparatorVoltageExcursion = 4, 453 WindowComparatorVoltageIncursion = 5, 454 DmaTransferComplete = 6, 455 DmaErrorCondition = 7, 456 ZeroCrossingChannelA = 8, 457 ZeroCrossingChannelB = 9, 458 SaturationChannelA = 10, 459 SaturationChannelB = 11, 460 } 461 462 private enum Registers : long 463 { 464 Configuration = 0x0, 465 PowerStatus = 0x4, 466 SoftwareTrigger = 0x8, 467 Slot0Configuration = 0xC, 468 Slot1Configuration = 0x10, 469 Slot2Configuration = 0x14, 470 Slot3Configuration = 0x18, 471 Slot4Configuration = 0x1C, 472 Slot5Configuration = 0x20, 473 Slot6Configuration = 0x24, 474 Slot7Configuration = 0x28, 475 WindowComparatorUpperLimits = 0x2C, 476 WindowComparatorLowerLimits = 0x30, 477 ScaleWindowComparatorLimits = 0x34, 478 Fifo = 0x38, 479 FifoPopRead = 0x3C, 480 InternalTimerConfiguration = 0x40, 481 ZeroCrossingComparatorConfiguration = 0x60, 482 ZeroCrossingComparatorLimits = 0x64, 483 PGAGainConfiguration = 0x68, 484 PGAGainCodes = 0x6C, 485 SaturationComparatorConfiguration = 0xA4, 486 SaturationComparatorLimits = 0xA8, 487 SaturationComparatorEventCounterLimits = 0xAC, 488 SaturationComparatorEventCounterClear = 0xB0, 489 InterruptEnable = 0x200, 490 InterruptStatus = 0x204, 491 InterruptClear = 0x208, 492 InterruptSet = 0x20C, 493 DMATriggerEnable = 0x240, 494 DMATriggerStatus = 0x244, 495 DMAConfiguration = 0x280, 496 DMATotalTransferCount = 0x288, 497 DMATargetAddress = 0x28C, 498 DMAStatus = 0x290, 499 } 500 } 501 } 502