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