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.Linq;
9 using System.Collections.Generic;
10 using System.Text;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Core.Structure;
14 using Antmicro.Renode.Core.Structure.Registers;
15 using Antmicro.Renode.Logging;
16 using Antmicro.Renode.Peripherals.Bus;
17 using Antmicro.Renode.Peripherals.I2C;
18 using Antmicro.Renode.Utilities;
19 
20 namespace Antmicro.Renode.Peripherals.SPI
21 {
22     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)]
23     public class AmbiqApollo4_IOMaster : IPeripheralContainer<ISPIPeripheral, TypedNumberRegistrationPoint<int>>, IPeripheralContainer<II2CPeripheral, TypedNumberRegistrationPoint<int>>,
24         IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IPeripheral, IKnownSize
25     {
AmbiqApollo4_IOMaster(IMachine machine)26         public AmbiqApollo4_IOMaster(IMachine machine)
27         {
28             RegistersCollection = new DoubleWordRegisterCollection(this);
29 
30             IRQ = new GPIO();
31 
32             // The countChangeAction cannot be set in the FIFO constructor.
33             incomingFifo = new Fifo("incoming FIFO", this);
34             incomingFifo.CountChangeAction += IncomingFifoCountChangeAction;
35             outgoingFifo = new Fifo("outgoing FIFO", this);
36             outgoingFifo.CountChangeAction += OutgoingFifoCountChangeAction;
37 
38             spiPeripherals = new Dictionary<int, ISPIPeripheral>();
39             i2cPeripherals = new Dictionary<int, II2CPeripheral>();
40 
41             this.machine = machine;
42 
43             DefineRegisters();
44             Reset();
45         }
46 
Reset()47         public void Reset()
48         {
49             activeTransactionContinue = false;
50             activeSpiSlaveSelect = 0;
51             i2cSlaveAddress = 0;
52 
53             RegistersCollection.Reset();
54             activeTransactionStatus.Value = Status.Idle;
55             status = Status.Idle;
56 
57             IRQ.Unset();
58             incomingFifo.Reset();
59             outgoingFifo.Reset();
60         }
61 
ReadDoubleWord(long offset)62         public uint ReadDoubleWord(long offset)
63         {
64             return RegistersCollection.Read(offset);
65         }
66 
WriteDoubleWord(long offset, uint value)67         public void WriteDoubleWord(long offset, uint value)
68         {
69             RegistersCollection.Write(offset, value);
70         }
71 
Register(ISPIPeripheral peripheral, TypedNumberRegistrationPoint<int> registrationPoint)72         public void Register(ISPIPeripheral peripheral, TypedNumberRegistrationPoint<int> registrationPoint)
73         {
74             if(registrationPoint.Address < 0 || registrationPoint.Address >= MaxSpiPeripheralsConnected)
75             {
76                 throw new ConstructionException($"Invalid SPI peripheral ID: {registrationPoint.Address}! Only IDs from 0 to {MaxSpiPeripheralsConnected - 1} are valid.");
77             }
78             Register(spiPeripherals, peripheral, registrationPoint.WithType<ISPIPeripheral>());
79         }
80 
Register(II2CPeripheral peripheral, TypedNumberRegistrationPoint<int> registrationPoint)81         public void Register(II2CPeripheral peripheral, TypedNumberRegistrationPoint<int> registrationPoint)
82         {
83             Register(i2cPeripherals, peripheral, registrationPoint.WithType<II2CPeripheral>());
84         }
85 
Unregister(ISPIPeripheral peripheral)86         public void Unregister(ISPIPeripheral peripheral)
87         {
88             Unregister(spiPeripherals, peripheral);
89         }
90 
Unregister(II2CPeripheral peripheral)91         public void Unregister(II2CPeripheral peripheral)
92         {
93             Unregister(i2cPeripherals, peripheral);
94         }
95 
GetRegistrationPoints(ISPIPeripheral peripheral)96         public IEnumerable<TypedNumberRegistrationPoint<int>> GetRegistrationPoints(ISPIPeripheral peripheral)
97         {
98             return spiPeripherals.Keys.Select(x => new TypedNumberRegistrationPoint<int>(x, typeof(ISPIPeripheral)))
99                 .ToList();
100         }
101 
GetRegistrationPoints(II2CPeripheral peripheral)102         public IEnumerable<TypedNumberRegistrationPoint<int>> GetRegistrationPoints(II2CPeripheral peripheral)
103         {
104             return i2cPeripherals.Keys.Select(x => new TypedNumberRegistrationPoint<int>(x, typeof(II2CPeripheral)))
105                 .ToList();
106         }
107 
108         IEnumerable<IRegistered<ISPIPeripheral, TypedNumberRegistrationPoint<int>>> IPeripheralContainer<ISPIPeripheral, TypedNumberRegistrationPoint<int>>.Children =>
109             spiPeripherals.Select(x => Registered.Create(x.Value, new TypedNumberRegistrationPoint<int>(x.Key, typeof(ISPIPeripheral)))).ToList();
110 
111         IEnumerable<IRegistered<II2CPeripheral, TypedNumberRegistrationPoint<int>>> IPeripheralContainer<II2CPeripheral, TypedNumberRegistrationPoint<int>>.Children =>
112             i2cPeripherals.Select(x => Registered.Create(x.Value, new TypedNumberRegistrationPoint<int>(x.Key, typeof(II2CPeripheral)))).ToList();
113 
114         public GPIO IRQ { get; }
115 
116         public DoubleWordRegisterCollection RegistersCollection { get; }
117 
118         public long Size => 0x400;
119 
DefineRegisters()120         private void DefineRegisters()
121         {
122             Registers.OutgoingFifoAccessPort.DefineMany(this, 8, (register, index) =>
123                 {
124                     register.WithValueField(0, 32,
125                         valueProviderCallback: _ => outgoingFifo.DirectGet((uint)index),
126                         writeCallback: (_, newValue) => outgoingFifo.DirectSet((uint)index, (uint)newValue));
127                 }, stepInBytes: 4);
128 
129             Registers.IncomingFifoAccessPort.DefineMany(this, 8, (register, index) =>
130                 {
131                     register.WithValueField(0, 32, FieldMode.Read,
132                         valueProviderCallback: _ => incomingFifo.DirectGet((uint)index));
133                 }, stepInBytes: 4);
134 
135             Registers.FifoSizeAndRemainingSlotsOpenValues.Define(this)
136                 .WithValueField(0, 8, FieldMode.Read, name: "FIFO0SIZ", valueProviderCallback: _ => outgoingFifo.BytesCount)
137                 .WithValueField(8, 8, FieldMode.Read, name: "FIFO0REM", valueProviderCallback: _ => outgoingFifo.BytesLeft)
138                 .WithValueField(16, 8, FieldMode.Read, name: "FIFO1SIZ", valueProviderCallback: _ => incomingFifo.BytesCount)
139                 .WithValueField(24, 8, FieldMode.Read, name: "FIFO1REM", valueProviderCallback: _ => incomingFifo.BytesLeft)
140                 ;
141 
142             Registers.FifoThresholdConfiguration.Define(this)
143                 .WithValueField(0, 6, out fifoInterruptReadThreshold, name: "FIFORTHR")
144                 .WithReservedBits(6, 2)
145                 .WithValueField(8, 6, out fifoInterruptWriteThreshold, name: "FIFOWTHR")
146                 .WithReservedBits(14, 18)
147                 .WithChangeCallback((_, __) => UpdateFifoThresholdInterruptStatus())
148                 ;
149 
150             Registers.FifoPop.Define(this)
151                 .WithValueField(0, 32, FieldMode.Read, name: "FIFODOUT", valueProviderCallback: _ =>
152                 {
153                     // "Will advance the internal read pointer of the incoming FIFO (FIFO1) when read, if POPWR is not active.
154                     // If POPWR is active, a write to this register is needed to advance the internal FIFO pointer."
155                     if(!(writePopToAdvanceReadPointer.Value ? incomingFifo.TryPeek(out var value) : incomingFifo.TryPop(out value)))
156                     {
157                         this.Log(LogLevel.Warning, "Failed to read from incoming FIFO");
158                         InterruptStatusSet(IoMasterInterrupts.ReadFifoUnderflow, true);
159                     }
160                     return value;
161                 }, writeCallback: (_, __) =>
162                 {
163                     if(writePopToAdvanceReadPointer.Value)
164                     {
165                         if(!incomingFifo.TryAdvancePointer())
166                         {
167                             this.Log(LogLevel.Warning, "Failed to advance the internal read pointer of the incoming FIFO");
168                         }
169                     }
170                     else
171                     {
172                         this.Log(LogLevel.Warning, "Tried to write the FIFOPOP register but the POPWR isn't set");
173                     }
174                 })
175                 ;
176 
177             Registers.FifoPush.Define(this)
178                 .WithValueField(0, 32, FieldMode.Write, name: "FIFODIN", writeCallback: (_, newValue) =>
179                 {
180                     if(!outgoingFifo.TryPush((uint)newValue))
181                     {
182                         this.Log(LogLevel.Warning, "Failed to write to the outgoing FIFO (value: 0x{0})", newValue);
183                         InterruptStatusSet(IoMasterInterrupts.WriteFifoOverflow, true);
184                     }
185                 })
186                 ;
187 
188             Registers.FifoControl.Define(this, 0x00000002)
189                 .WithFlag(0, out writePopToAdvanceReadPointer, name: "POPWR")
190                 .WithFlag(1, name: "FIFORSTN", writeCallback: (_, newValue) =>
191                     {
192                         // FIFORSTN is an inversed reset flag; FIFOs are reset when this flag is 0.
193                         incomingFifo.ResetFlag = !newValue;
194                         outgoingFifo.ResetFlag = !newValue;
195                     },
196                     // The value should be the same for both FIFOs so it doesn't matter which is returned here.
197                     valueProviderCallback: _ => incomingFifo.ResetFlag)
198                 .WithReservedBits(2, 30)
199                 ;
200 
201             Registers.FifoPointers.Define(this)
202                 .WithValueField(0, 4, FieldMode.Read, name: "FIFOWPTR", valueProviderCallback: _ => outgoingFifo.Pointer)
203                 .WithReservedBits(4, 4)
204                 .WithValueField(8, 4, FieldMode.Read, name: "FIFORPTR", valueProviderCallback: _ => incomingFifo.Pointer)
205                 .WithReservedBits(12, 20)
206                 ;
207 
208             // Some software expects values written to
209             // IOCLKEN, FSEL, DIVEN, LOWPER, TOTPER to be retained
210             Registers.IOClockConfiguration.Define(this)
211                 .WithFlag(0, name: "IOCLKEN")
212                 .WithReservedBits(1, 7)
213                 .WithValueField(8, 3, name: "FSEL")
214                 .WithTaggedFlag("DIV3", 11)
215                 .WithFlag(12, name: "DIVEN")
216                 .WithReservedBits(13, 3)
217                 .WithValueField(16, 8, name: "LOWPER")
218                 .WithValueField(24, 8, name: "TOTPER")
219                 ;
220 
221             Registers.SubmoduleControl.Define(this)
222                 .WithFlag(0, out spiMasterEnabled, name: "SMOD0EN")
223                 .WithEnumField<DoubleWordRegister, SubmoduleTypes>(1, 3, FieldMode.Read, name: "SMOD0TYPE", valueProviderCallback: _ => SubmoduleTypes.SpiMaster)
224                 .WithFlag(4, out i2cMasterEnabled, name: "SMOD1EN")
225                 .WithEnumField<DoubleWordRegister, SubmoduleTypes>(5, 3, FieldMode.Read, name: "SMOD1TYPE", valueProviderCallback: _ => SubmoduleTypes.I2CMaster)
226                 .WithTaggedFlag("SMOD2EN", 8)
227                 // It should be I2SMaster_Slave (0x4), but I2S isn't currently supported so let's mark it as NotInstalled.
228                 .WithEnumField<DoubleWordRegister, SubmoduleTypes>(9, 3, FieldMode.Read, name: "SMOD2TYPE", valueProviderCallback: _ => SubmoduleTypes.NotInstalled)
229                 .WithReservedBits(12, 20)
230                 .WithWriteCallback((_, __) =>
231                 {
232                     if(spiMasterEnabled.Value && i2cMasterEnabled.Value)
233                     {
234                         this.Log(LogLevel.Warning, "Both SPI and I2C modules have been enabled");
235                         spiMasterEnabled.Value = false;
236                         i2cMasterEnabled.Value = false;
237                     }
238                 })
239                 ;
240 
241             Registers.CommandAndOffset.Define(this)
242                 .WithEnumField(0, 4, out transactionCommand, name: "CMD")
243                 .WithValueField(4, 3, out transactionOffsetCount, name: "OFFSETCNT")
244                 .WithFlag(7, out transactionContinue, name: "CONT")
245                 .WithValueField(8, 12, out transactionSize, name: "TSIZE")
246                 .WithValueField(20, 2, out spiSlaveSelect, name: "CMDSEL")
247                 .WithReservedBits(22, 2)
248                 .WithValueField(24, 8, out transactionOffsetLow, name: "OFFSETLO")
249                 .WithWriteCallback((_, __) =>
250                 {
251                     this.Log(LogLevel.Debug,
252                             "Transaction received for #{0}; command: {1}, size: {2}, offset: <count: {3}, low=0x{4:X2}, high=0x{5:X8}>, cont: {6}",
253                             PrettyPendingPeripheral, transactionCommand.Value, transactionSize.Value, transactionOffsetCount.Value,
254                             transactionOffsetLow.Value, transactionOffsetHigh.Value, transactionContinue.Value);
255 
256                     if(!spiMasterEnabled.Value && !i2cMasterEnabled.Value)
257                     {
258                         this.Log(LogLevel.Error, "Invalid operation; SPI/I2C Master inferfaces are disabled!");
259                         return;
260                     }
261 
262                     if(activeTransactionCommand.Value != Commands.None)
263                     {
264                         this.Log(LogLevel.Error, "Dropping the new transaction received for #{0}: {1}; {2} on {3} is still being processed.",
265                             PrettyPendingPeripheral, transactionCommand.Value, activeTransactionCommand.Value, PrettyActivePeripheral);
266                         return;
267                     }
268 
269                     if(!IsTransactionValid(transactionCommand.Value, (uint)transactionSize.Value, (uint)transactionOffsetCount.Value,
270                             (int)spiSlaveSelect.Value, out var errorMessage))
271                     {
272                         this.Log(LogLevel.Error, errorMessage);
273                         activeTransactionStatus.Value = Status.Error;
274                         status = Status.Idle;
275                         InterruptStatusSet(IoMasterInterrupts.IllegalCommand);
276                         return;
277                     }
278 
279                     // Preserve important values for the transaction being currently processed.
280                     // Only the command and size left values are accessible through registers.
281                     activeTransactionCommand.Value = transactionCommand.Value;
282                     activeTransactionContinue = transactionContinue.Value;
283                     activeSpiSlaveSelect = (int)spiSlaveSelect.Value;
284                     activeTransactionSizeLeft.Value = transactionSize.Value;
285 
286                     SendTransactionOffset((uint)transactionOffsetCount.Value);
287 
288                     if(activeTransactionSizeLeft.Value == 0)
289                     {
290                         // As this is 0-size TX transaction and we already send transaction offset,
291                         // there is nothing more to do.
292                         TryFinishTransaction();
293                         return;
294                     }
295 
296                     status = Status.Active;
297 
298                     while(activeTransactionSizeLeft.Value > 0)
299                     {
300                         if(activeTransactionCommand.Value == Commands.Read)
301                         {
302                             if(!incomingFifo.TryReceiveAndPush(ReceiveData))
303                             {
304                                 this.Log(LogLevel.Debug, "Cannot push more data to the incoming FIFO; {0}B remain to complete the read command.",
305                                     activeTransactionSizeLeft.Value);
306                                 break;
307                             }
308                         }
309                         else if(activeTransactionCommand.Value == Commands.Write)
310                         {
311                             if(!outgoingFifo.TryPop(out var value))
312                             {
313                                 this.Log(LogLevel.Debug, "No more data in the outgoing FIFO; {0}B remain to complete the write command.",
314                                     activeTransactionSizeLeft.Value);
315                                 break;
316                             }
317                             SendData(value);
318                         }  // No else because only Read and Write commands are handled after 'IsTransactionValid'.
319                     }
320 
321                     if(activeTransactionSizeLeft.Value != 0)
322                     {
323                         activeTransactionStatus.Value = Status.Wait;
324                     }
325                 })
326                 ;
327 
328             Registers.DcxControlAndCeUsageSelection.Define(this)
329                 .WithTag("DCXSEL", 0, 4)
330                 .WithTaggedFlag("DCXEN", 4)
331                 .WithReservedBits(5, 27)
332                 ;
333 
334             Registers.HighOrderBytesBfOffsetForIOTransaction.Define(this)
335                 .WithValueField(0, 32, out transactionOffsetHigh, name: "OFFSETHI")
336                 ;
337 
338             Registers.CommandStatus.Define(this)
339                 // This field is designed to hold the value written to CMD. The MSB is stated as unused.
340                 .WithEnumField(0, 5, out activeTransactionCommand, FieldMode.Read, name: "CCMD")
341                 .WithEnumField(5, 3, out activeTransactionStatus, FieldMode.Read, name: "CMDSTAT")
342                 .WithValueField(8, 12, out activeTransactionSizeLeft, FieldMode.Read, name: "CTSIZE")
343                 .WithReservedBits(20, 12)
344                 ;
345 
346             Registers.IOMasterInterruptsEnable.Define(this)
347                 .WithFlags(0, IoMasterInterruptsCount, out ioMasterInterruptsEnableFlags, name: "INTENi")
348                 .WithReservedBits(15, 17)
349                 .WithChangeCallback((_, __) => UpdateIRQ())
350                 ;
351 
352             Registers.IOMasterInterruptsStatus.Define(this)
353                 .WithFlags(0, IoMasterInterruptsCount, out ioMasterInterruptsStatusFlags, FieldMode.Read, name: "INTSTATi")
354                 .WithReservedBits(15, 17)
355                 ;
356 
357             Registers.IOMasterInterruptsClear.Define(this)
358                 .WithFlags(0, IoMasterInterruptsCount, FieldMode.Write, writeCallback: (interrupt, _, newValue) =>
359                 {
360                     if(newValue)
361                     {
362                         InterruptStatusSet((IoMasterInterrupts)interrupt, false);
363                     }
364                 }, name: "INTCLRi")
365                 // IgnoredBits not to trigger warnings when 0xFFFFFFFF is written to clear all INTs.
366                 .WithIgnoredBits(15, 17)
367                 ;
368 
369             Registers.IOMasterInterruptsSet.Define(this)
370                 .WithFlags(0, IoMasterInterruptsCount, FieldMode.Write, writeCallback: (interrupt, _, newValue) =>
371                 {
372                     if(newValue)
373                     {
374                         InterruptStatusSet((IoMasterInterrupts)interrupt, true);
375                     }
376                 }, name: "INTSETi")
377                 .WithReservedBits(15, 17)
378                 ;
379 
380             Registers.DmaTriggerEnable.Define(this)
381                 .WithTaggedFlag("DCMDCMPEN", 0)
382                 .WithTaggedFlag("DTHREN", 1)
383                 .WithReservedBits(2, 30)
384                 ;
385 
386             Registers.DmaTriggerStatus.Define(this)
387                 .WithTaggedFlag("DCMDCMP", 0)
388                 .WithTaggedFlag("DTHR", 1)
389                 .WithTaggedFlag("DTOTCMP", 2)
390                 .WithReservedBits(3, 29)
391                 ;
392 
393             Registers.DmaConfiguration.Define(this)
394                 .WithTaggedFlag("DMAEN", 0)
395                 .WithTaggedFlag("DMADIR", 1)
396                 .WithReservedBits(2, 6)
397                 .WithTaggedFlag("DMAPRI", 8)
398                 .WithTaggedFlag("DPWROFF", 9)
399                 .WithReservedBits(10, 22)
400                 ;
401 
402             Registers.DmaTotalTransferCount.Define(this)
403                 .WithTag("TOTCOUNT", 0, 12)
404                 .WithReservedBits(12, 20)
405                 ;
406 
407             Registers.DmaTargetAddress.Define(this)
408                 .WithTag("TARGADDR", 0, 29)
409                 .WithReservedBits(29, 3)
410                 ;
411 
412             Registers.DmaStatus.Define(this)
413                 .WithTaggedFlag("DMATIP", 0)
414                 .WithTaggedFlag("DMACPL", 1)
415                 .WithTaggedFlag("DMAERR", 2)
416                 .WithReservedBits(3, 29)
417                 ;
418 
419             Registers.CommandQueueConfiguration.Define(this)
420                 .WithTaggedFlag("CQEN", 0)
421                 .WithTaggedFlag("CQPRI", 1)
422                 .WithTag("MSPIFLGSEL", 2, 2)
423                 .WithReservedBits(4, 28)
424                 ;
425 
426             Registers.CommandQueueTargetReadAddress.Define(this)
427                 .WithReservedBits(0, 2)
428                 .WithTag("CQADDR", 2, 27)
429                 .WithReservedBits(29, 3)
430                 ;
431 
432             Registers.CommandQueueStatus.Define(this)
433                 .WithTaggedFlag("CQTIP", 0)
434                 .WithTaggedFlag("CQPAUSED", 1)
435                 .WithTaggedFlag("CQERR", 2)
436                 .WithReservedBits(3, 29)
437                 ;
438 
439             Registers.CommandQueueFlag.Define(this)
440                 .WithTag("CQFLAGS", 0, 16)
441                 .WithTag("CQIRQMASK", 16, 16)
442                 ;
443 
444             Registers.CommandQueueFlagSetClear.Define(this)
445                 .WithTag("CQFSET", 0, 8)
446                 .WithTag("CQFTGL", 8, 8)
447                 .WithTag("CQFCLR", 16, 8)
448                 .WithReservedBits(24, 8)
449                 ;
450 
451             Registers.CommandQueuePauseEnable.Define(this)
452                 .WithTag("CQPEN", 0, 16)
453                 .WithReservedBits(16, 16)
454                 ;
455 
456             Registers.CommandQueueCurrentIndexValue.Define(this)
457                 .WithTag("CQCURIDX", 0, 8)
458                 .WithReservedBits(8, 24)
459                 ;
460 
461             Registers.CommandQueueEndIndexValue.Define(this)
462                 .WithTag("CQENDIDX", 0, 8)
463                 .WithReservedBits(8, 24)
464                 ;
465 
466             Registers.IOModuleStatus.Define(this)
467                 .WithTaggedFlag("ERR", 0)
468                 .WithFlag(1, FieldMode.Read, name: "CMDACT", valueProviderCallback: _ => status == Status.Active)
469                 .WithFlag(2, FieldMode.Read, name: "IDLEST", valueProviderCallback: _ => status == Status.Idle)
470                 .WithReservedBits(3, 29)
471                 ;
472 
473             Registers.SpiModuleMasterConfiguration.Define(this, 0x00200000)
474                 .WithTaggedFlag("SPOL", 0)
475                 .WithTaggedFlag("SPHA", 1)
476                 .WithTaggedFlag("FULLDUP", 2)
477                 .WithReservedBits(3, 13)
478                 .WithTaggedFlag("WTFC", 16)
479                 .WithTaggedFlag("RDFC", 17)
480                 .WithTaggedFlag("MOSIINV", 18)
481                 .WithReservedBits(19, 1)
482                 .WithTaggedFlag("WTFCIRQ", 20)
483                 .WithTaggedFlag("WTFCPOL", 21)
484                 .WithTaggedFlag("RDFCPOL", 22)
485                 .WithTaggedFlag("SPILSB", 23)
486                 .WithTag("DINDLY", 24, 3)
487                 .WithTag("DOUTDLY", 27, 3)
488                 .WithTaggedFlag("MSPIRST", 30)
489                 .WithReservedBits(31, 1)
490                 ;
491 
492             // Some software expects values written to
493             // SDADLY, SCLENDLY and SDAENDLY to be retined
494             Registers.I2CMasterConfiguration.Define(this)
495                 .WithFlag(0, out i2cExtendedAdressingMode, name: "ADDRSZ")
496                 .WithTaggedFlag("I2CLSB", 1)
497                 .WithTaggedFlag("ARBEN", 2)
498                 .WithReservedBits(3, 1)
499                 .WithValueField(4, 2, name: "SDADLY")
500                 .WithFlag(6, name: "MI2CRST")
501                 .WithReservedBits(7, 1)
502                 .WithValueField(8, 4, name: "SCLENDLY")
503                 .WithValueField(12, 4, name: "SDAENDLY")
504                 .WithTag("SMPCNT", 16, 8)
505                 .WithTaggedFlag("STRDIS", 24)
506                 .WithReservedBits(25, 7)
507                 ;
508 
509             Registers.I2CDeviceConfiguration.Define(this)
510                 .WithValueField(0, 10, name: "DEVADDR",
511                     valueProviderCallback: _ => i2cSlaveAddress,
512                     writeCallback: (_, value) =>
513                     {
514                         if(!i2cExtendedAdressingMode.Value && value > 0x7F)
515                         {
516                             value &= 0x7F;
517                             this.Log(LogLevel.Warning, "Tried to set 10-bit address with extended mode disabled; truncated to 7-bit");
518                         }
519                         i2cSlaveAddress = (uint)value;
520                     })
521                 .WithReservedBits(10, 22)
522                 ;
523 
524             Registers.I2SControl.Define(this)
525                 .WithTaggedFlag("I2SEN", 0)
526                 .WithTaggedFlag("RXTXN", 1)
527                 .WithTaggedFlag("CLKMS", 2)
528                 .WithTaggedFlag("SE", 3)
529                 .WithTag("CHANSIZE", 4, 5)
530                 .WithTag("SAMPLESIZE", 9, 5)
531                 .WithTag("BOFFSET", 14, 5)
532                 .WithTag("CHANCNT", 19, 3)
533                 .WithTaggedFlag("LSBFIRST", 22)
534                 .WithTaggedFlag("CLKGAP", 23)
535                 .WithTag("CTRLSPARE", 24, 8)
536                 ;
537 
538             Registers.I2SClockControl.Define(this)
539                 .WithTaggedFlag("ASRCEN", 0)
540                 .WithTaggedFlag("ASRCCLKSEL", 1)
541                 .WithTag("I2SCLKSEL", 2, 2)
542                 .WithTag("ASEL", 4, 3)
543                 .WithReservedBits(7, 25)
544                 ;
545 
546             Registers.I2SFrameSyncControl.Define(this)
547                 .WithTag("FSLEN", 0, 5)
548                 .WithTaggedFlag("FSPOL", 5)
549                 .WithTaggedFlag("FSEDGE", 6)
550                 .WithReservedBits(7, 1)
551                 .WithTag("FSOFFSET", 8, 4)
552                 .WithReservedBits(12, 20)
553                 ;
554 
555             Registers.IOModuleDebug.Define(this)
556                 .WithTaggedFlag("DBGEN", 0)
557                 .WithTaggedFlag("IOCLKON", 1)
558                 .WithTaggedFlag("APBCLKON", 2)
559                 .WithTag("DBGDATA", 3, 29)
560                 ;
561         }
562 
IncomingFifoCountChangeAction(Fifo fifo, uint currentCount, uint previousCount)563         private void IncomingFifoCountChangeAction(Fifo fifo, uint currentCount, uint previousCount)
564         {
565             // Ignore change in count if it didn't decrease
566             if(currentCount >= previousCount)
567             {
568                 return;
569             }
570 
571             // Receive more data if there's a space to receive and Read command awaits.
572             if(!fifo.Full && activeTransactionCommand.Value == Commands.Read && activeTransactionSizeLeft.Value > 0)
573             {
574                 if(fifo.TryReceiveAndPush(ReceiveData))
575                 {
576                     this.Log(LogLevel.Noisy, "Unfinished read command found and incoming FIFO has a space; receiving...");
577                 }
578             }
579             UpdateFifoThresholdInterruptStatus();
580         }
581 
InterruptStatusSet(IoMasterInterrupts interrupt, bool value = true)582         private void InterruptStatusSet(IoMasterInterrupts interrupt, bool value = true)
583         {
584             ioMasterInterruptsStatusFlags[(int)interrupt].Value = value;
585             UpdateIRQ();
586         }
587 
IsTransactionValid(Commands command, uint size, uint offsetCount, int spiSlaveSelect, out string errorMessage)588         private bool IsTransactionValid(Commands command, uint size, uint offsetCount, int spiSlaveSelect, out string errorMessage)
589         {
590             errorMessage = null;
591             if(command != Commands.Read && command != Commands.Write)
592             {
593                 errorMessage = $"Unsupported transaction command: {command}";
594             }
595             else if(command == Commands.Read && size == 0)
596             {
597                 errorMessage = "Read transaction with size 0 is illegal.";
598             }
599             else if(command == Commands.Write && size != 0 && outgoingFifo.Empty)
600             {
601                 errorMessage = $"{size}-byte write requested but the outgoing FIFO is empty.";
602             }
603             else if(offsetCount > 5)
604             {
605                 errorMessage = $"Invalid transaction offset count: {offsetCount}";
606             }
607             else if(spiMasterEnabled.Value && !spiPeripherals.ContainsKey(spiSlaveSelect))
608             {
609                 errorMessage = $"Transaction cannot be completed. There's no SPI peripheral registered with ID: {spiSlaveSelect}";
610             }
611             else if(i2cMasterEnabled.Value && !i2cPeripherals.ContainsKey((int)i2cSlaveAddress))
612             {
613                 errorMessage = $"Transaction cannot be completed. There's no I2C peripheral registered with ID: {i2cSlaveAddress}";
614             }
615             return errorMessage == null;
616         }
617 
OutgoingFifoCountChangeAction(Fifo fifo, uint currentCount, uint previousCount)618         private void OutgoingFifoCountChangeAction(Fifo fifo, uint currentCount, uint previousCount)
619         {
620             // Ignore if we are currently sending data
621             if(currentCount <= previousCount)
622             {
623                 return;
624             }
625 
626             // Send more data if there's data to send and Write command awaits.
627             if(!fifo.Empty && activeTransactionCommand.Value == Commands.Write && activeTransactionSizeLeft.Value > 0)
628             {
629                 if(fifo.TryPop(out var value))
630                 {
631                     this.Log(LogLevel.Noisy, "Unfinished write command found and outgoing FIFO contains data; sending 0x{0:X}...", value);
632                     SendData(value);
633                 }
634             }
635             UpdateFifoThresholdInterruptStatus();
636         }
637 
ReceiveData()638         private uint ReceiveData()
639         {
640             if(activeTransactionSizeLeft.Value > 0)
641             {
642                 var bytesToReceive = Math.Min(4, (uint)activeTransactionSizeLeft.Value);
643                 uint result = 0;
644                 if(ActiveTransactionPeripheral is ISPIPeripheral spiPeripheral)
645                 {
646                     for(int i = 0; i < bytesToReceive; i++)
647                     {
648                         BitHelper.UpdateWithShifted(ref result, spiPeripheral.Transmit(0), (int)(i * 8), 8);
649                     }
650                 }
651                 else if(ActiveTransactionPeripheral is II2CPeripheral i2cPeripheral)
652                 {
653                     var data = i2cPeripheral.Read((int)bytesToReceive);
654                     foreach(var item in data.Select((value, index) => new { index, value }))
655                     {
656                         BitHelper.UpdateWithShifted(ref result, item.value, (int)(item.index * 8), 8);
657                     }
658                 }
659                 else
660                 {
661                     // This code should be unreachable
662                     throw new ArgumentException("Peripheral has to be selected before receiving data!");
663                 }
664                 activeTransactionSizeLeft.Value -= bytesToReceive;
665                 TryFinishTransaction();
666                 return result;
667             }
668             else
669             {
670                 throw new ArgumentException($"Data shouldn't be received if activeTransactionSizeLeft equals 0!");
671             }
672         }
673 
Send(uint data, uint size, bool forceMSBFirst = false)674         private void Send(uint data, uint size, bool forceMSBFirst = false)
675         {
676             if(ActiveTransactionPeripheral is ISPIPeripheral spiPeripheral)
677             {
678                 var dataBytes = BitHelper.GetBytesFromValue(data, (int)size, reverse: !forceMSBFirst);
679                 foreach(var dataByte in dataBytes)
680                 {
681                     spiPeripheral.Transmit(dataByte);
682                     this.Log(LogLevel.Noisy, "Byte sent to the SPI peripheral: 0x{0:X}", dataByte);
683                 }
684             }
685             else if(ActiveTransactionPeripheral is II2CPeripheral i2cPeripheral)
686             {
687                 var dataBytes = BitHelper.GetBytesFromValue(data, (int)size, reverse: !forceMSBFirst);
688                 i2cPeripheral.Write(dataBytes);
689                 this.Log(LogLevel.Noisy, "{0} byte(s) sent to the I2C peripheral: 0x{0:X08}", size, data);
690             }
691             else
692             {
693                 // This code should be unreachable
694                 throw new ArgumentException("Peripheral has to be selected before sending data!");
695             }
696         }
697 
SendData(uint value)698         private void SendData(uint value)
699         {
700             if(activeTransactionSizeLeft.Value > 0)
701             {
702                 var bytesToSend = Math.Min(4, (uint)activeTransactionSizeLeft.Value);
703                 Send(value, bytesToSend);
704                 activeTransactionSizeLeft.Value -= bytesToSend;
705                 TryFinishTransaction();
706             }
707             else
708             {
709                 throw new ArgumentException("Data shouldn't be sent if activeTransactionSizeLeft equals 0!");
710             }
711         }
712 
SendTransactionOffset(uint count)713         private void SendTransactionOffset(uint count)
714         {
715             // Depending on the offset count:
716             // * for high=0xDEADBEEF, low=0x12: count=1 sends only 0x12, count=2: 0xEF12...
717             // * transfer always begins with the MSB of the resulting value (0xEF for the count=2 example).
718             if(count > 0)
719             {
720                 if(count > 1)
721                 {
722                     Send((uint)transactionOffsetHigh.Value, count - 1, forceMSBFirst: true);
723                 }
724                 Send((uint)transactionOffsetLow.Value, 1);
725             }
726         }
727 
TryFinishTransaction()728         private bool TryFinishTransaction()
729         {
730             if(activeTransactionSizeLeft.Value == 0)
731             {
732                 this.Log(LogLevel.Noisy, "Command completed: {0}", activeTransactionCommand.Value);
733                 if(activeTransactionContinue)
734                 {
735                     this.Log(LogLevel.Noisy, "The transmission won't be finished; the CONT flag was sent with the command.");
736                 }
737                 else
738                 {
739                     if(ActiveTransactionPeripheral is ISPIPeripheral spiPeripheral)
740                     {
741                         spiPeripheral.FinishTransmission();
742                     }
743                     else if(ActiveTransactionPeripheral is II2CPeripheral i2cPeripheral)
744                     {
745                         i2cPeripheral.FinishTransmission();
746                     }
747                     else
748                     {
749                         // This code should be unreachable
750                         throw new ArgumentException("Trying to finish transaction for which peripherial wasn't chosen");
751                     }
752                 }
753                 activeTransactionCommand.Value = Commands.None;
754 
755                 // Not sure if these are valid for the continuous transmission.
756                 activeTransactionStatus.Value = Status.Idle;
757                 status = Status.Idle;
758                 InterruptStatusSet(IoMasterInterrupts.CommandComplete);
759 
760                 return true;
761             }
762             return false;
763         }
764 
UpdateFifoThresholdInterruptStatus()765         private void UpdateFifoThresholdInterruptStatus()
766         {
767             InterruptStatusSet(IoMasterInterrupts.FifoThreshold, value:
768                 outgoingFifo.BytesCount < fifoInterruptWriteThreshold.Value
769                 || incomingFifo.BytesCount > fifoInterruptReadThreshold.Value
770             );
771         }
772 
UpdateIRQ()773         private void UpdateIRQ()
774         {
775             var newIrqState = false;
776             for(var i = 0; i < IoMasterInterruptsCount; i++)
777             {
778                 if(ioMasterInterruptsEnableFlags[i].Value && ioMasterInterruptsStatusFlags[i].Value)
779                 {
780                     newIrqState = true;
781                     break;
782                 }
783             }
784 
785             if(newIrqState != IRQ.IsSet)
786             {
787                 this.Log(LogLevel.Debug, "{0} IRQ", newIrqState ? "Setting" : "Resetting");
788                 IRQ.Set(newIrqState);
789             }
790         }
791 
792         private void Register<T>(Dictionary<int, T> container, T peripheral, TypedNumberRegistrationPoint<int> registrationPoint) where T: IPeripheral
793         {
794             if(container.ContainsKey(registrationPoint.Address))
795             {
796                 throw new RegistrationException("The specified registration point is already in use.");
797             }
798             container.Add(registrationPoint.Address, peripheral);
799             machine.RegisterAsAChildOf(this, peripheral, registrationPoint);
800         }
801 
802         private void Unregister<T>(Dictionary<int, T> container, T peripheral) where T: IPeripheral
803         {
804             var toRemove = container.Where(x => x.Value.Equals(peripheral)).Select(x => x.Key).ToList(); //ToList required, as we remove from the source
805             if(toRemove.Count == 0)
806             {
807                 throw new RegistrationException("The specified peripheral was never registered.");
808             }
809             foreach(var key in toRemove)
810             {
811                 container.Remove(key);
812             }
813             machine.UnregisterAsAChildOf(this, peripheral);
814         }
815 
816         private string PrettyPendingPeripheral
817         {
818             get
819             {
820                 if(spiMasterEnabled.Value)
821                 {
822                     return $"SPI#{spiSlaveSelect.Value}";
823                 }
824                 else if(i2cMasterEnabled.Value)
825                 {
826                     return $"I2C#{i2cSlaveAddress}";
827                 }
828                 return String.Empty;
829             }
830         }
831 
832         private string PrettyActivePeripheral
833         {
834             get
835             {
836                 if(spiMasterEnabled.Value)
837                 {
838                     return $"SPI#{activeSpiSlaveSelect}";
839                 }
840                 else if(i2cMasterEnabled.Value)
841                 {
842                     return $"I2C#{i2cSlaveAddress}";
843                 }
844                 return String.Empty;
845             }
846         }
847 
848         private IPeripheral ActiveTransactionPeripheral
849         {
850             get
851             {
852                 if(activeTransactionCommand.Value == Commands.None)
853                 {
854                     return null;
855                 }
856                 else if(spiMasterEnabled.Value)
857                 {
858                     return spiPeripherals[activeSpiSlaveSelect];
859                 }
860                 else if(i2cMasterEnabled.Value)
861                 {
862                     return i2cPeripherals[(int)i2cSlaveAddress];
863                 }
864                 return null;
865             }
866         }
867 
868         private IEnumRegisterField<Commands> activeTransactionCommand;
869         private IValueRegisterField activeTransactionSizeLeft;
870         private IEnumRegisterField<Status> activeTransactionStatus;
871         private IValueRegisterField fifoInterruptReadThreshold;
872         private IValueRegisterField fifoInterruptWriteThreshold;
873         private IFlagRegisterField[] ioMasterInterruptsEnableFlags;
874         private IFlagRegisterField[] ioMasterInterruptsStatusFlags;
875         private IFlagRegisterField spiMasterEnabled;
876         private IFlagRegisterField i2cMasterEnabled;
877         private IEnumRegisterField<Commands> transactionCommand;
878         private IFlagRegisterField transactionContinue;
879         private IValueRegisterField transactionOffsetCount;
880         private IValueRegisterField transactionOffsetHigh;
881         private IValueRegisterField transactionOffsetLow;
882         private IValueRegisterField transactionSize;
883         private IValueRegisterField spiSlaveSelect;
884         private IFlagRegisterField writePopToAdvanceReadPointer;
885         private IFlagRegisterField i2cExtendedAdressingMode;
886 
887         private bool activeTransactionContinue;
888         private int activeSpiSlaveSelect;
889         private uint i2cSlaveAddress;
890         private Status status;
891 
892         /*
893             Both FIFOs occupy a single 64-byte memory:
894             * 0x00 -- 0x1F outgoingFifo: "FIFO 0 (written by MCU, read by interface)",
895             * 0x20 -- 0x3F incomingFifo: "FIFO 1 (written by interface, read by MCU)"
896             Queues aren't used because random access is also needed.
897         */
898         private readonly Fifo incomingFifo;
899         private readonly Fifo outgoingFifo;
900         private readonly Dictionary<int, ISPIPeripheral> spiPeripherals;
901         private readonly Dictionary<int, II2CPeripheral> i2cPeripherals;
902         private readonly IMachine machine;
903 
904         private const int IoMasterInterruptsCount = 15;
905         private const int MaxSpiPeripheralsConnected = 4;
906 
907         private class Fifo
908         {
Fifo(string name, IPeripheral owner)909             public Fifo(string name, IPeripheral owner)
910             {
911                 this.name = name;
912                 this.owner = owner;
913                 Reset();
914             }
915 
DirectGet(uint index)916             public uint DirectGet(uint index)
917             {
918                 if(CheckResetFlag("get"))
919                 {
920                     return memory[index];
921                 }
922                 return 0x0;
923             }
924 
DirectSet(uint index, uint value)925             public void DirectSet(uint index, uint value)
926             {
927                 if(CheckResetFlag("set"))
928                 {
929                     memory[index] = value;
930                 }
931             }
932 
Reset()933             public void Reset()
934             {
935                 SoftwareReset();
936                 resetFlag = false;
937             }
938 
ToString()939             public override string ToString()
940             {
941                 StringBuilder builder = new StringBuilder();
942                 builder.AppendFormat("{0}: ", name);
943                 builder.AppendFormat("Count: {0}; values: ", Count, headIndex, tailIndex);
944                 for(int index = headIndex; index < headIndex + Count; index++)
945                 {
946                     builder.AppendFormat("0x{0:X8} ", memory[index % DoubleWordCapacity]);
947                 }
948                 return builder.ToString();
949             }
950 
TryAdvancePointer()951             public bool TryAdvancePointer()
952             {
953                 if(CheckResetFlag("advance the pointer") && Check(!Empty, "Cannot advance the pointer; FIFO is empty."))
954                 {
955                     // Clear the current head. It won't be read with Peek/Pop without a prior
956                     // setting it with Push but it could be read with a random access.
957                     memory[headIndex] = 0x0;
958 
959                     headIndex = (headIndex + 1) % DoubleWordCapacity;
960                     Count--;
961                     return true;
962                 }
963                 return false;
964             }
965 
TryPeek(out uint value, string actionName = R)966             public bool TryPeek(out uint value, string actionName = "peek")
967             {
968                 if(CheckResetFlag(actionName) && Check(!Empty, $"Cannot {actionName}; FIFO is empty."))
969                 {
970                     value = memory[headIndex];
971                     return true;
972                 }
973                 value = 0x0;
974                 return false;
975             }
976 
TryPop(out uint value)977             public bool TryPop(out uint value)
978             {
979                 if(TryPeek(out value, actionName: "pop"))
980                 {
981                     TryAdvancePointer();
982                     return true;
983                 }
984                 return false;
985             }
986 
TryPush(uint value)987             public bool TryPush(uint value)
988             {
989                 return TryReceiveAndPush(() => value);
990             }
991 
992             // To prevent losing the received value, the receiveFunction will only be called if push is possible.
TryReceiveAndPush(Func<uint> receiveFunction)993             public bool TryReceiveAndPush(Func<uint> receiveFunction)
994             {
995                 if(CheckResetFlag("push") && Check(!Full, "Cannot push; FIFO is full."))
996                 {
997                     Push(receiveFunction());
998                     return true;
999                 }
1000                 return false;
1001             }
1002 
1003             public Action<Fifo, uint, uint> CountChangeAction { get; set; }
1004 
1005             public uint BytesCapacity => DoubleWordCapacity * 4;
1006             public uint BytesCount => Count * 4;
1007             public uint BytesLeft => (DoubleWordCapacity * 4) - BytesCount;
1008             public bool Empty => Count == 0;
1009             public bool Full => Count == DoubleWordCapacity;
1010             public uint Pointer => (uint)headIndex;
1011 
1012             public bool ResetFlag
1013             {
1014                 get => resetFlag;
1015                 set
1016                 {
1017                     if(!resetFlag && value)
1018                     {
1019                         SoftwareReset();
1020                     }
1021                     resetFlag = value;
1022                 }
1023             }
1024 
Check(bool condition, string errorMessage)1025             private bool Check(bool condition, string errorMessage)
1026             {
1027                 if(!condition)
1028                 {
1029                     owner.DebugLog("{0}: {1}", name, errorMessage);
1030                     return false;
1031                 }
1032                 return true;
1033             }
1034 
CheckResetFlag(string actionName)1035             private bool CheckResetFlag(string actionName)
1036             {
1037                 return Check(!resetFlag, $"Cannot {actionName}; the reset flag is set.");
1038             }
1039 
ClearMemory()1040             private void ClearMemory()
1041             {
1042                 for(int i = 0; i < DoubleWordCapacity; i++)
1043                 {
1044                     memory[i] = 0x0;
1045                 }
1046             }
1047 
Push(uint value)1048             private void Push(uint value)
1049             {
1050                 if(Full)
1051                 {
1052                     throw new ArgumentException($"Cannot push; the {name} is full!");
1053                 }
1054                 tailIndex = (tailIndex + 1) % DoubleWordCapacity;
1055                 Count++;
1056                 memory[tailIndex] = value;
1057             }
1058 
SoftwareReset()1059             private void SoftwareReset()
1060             {
1061                 ClearMemory();
1062                 Count = 0;
1063                 headIndex = 0;
1064                 tailIndex = -1;
1065             }
1066 
1067             private uint Count
1068             {
1069                 get => count;
1070                 set
1071                 {
1072                     if(value < 0 || value > DoubleWordCapacity)
1073                     {
1074                         throw new ArgumentException($"{name}: Invalid value for Count: {value} (Capacity: {DoubleWordCapacity})");
1075                     }
1076                     var previousCount = count;
1077                     count = value;
1078                     CountChangeAction?.Invoke(this, count, previousCount);
1079                 }
1080             }
1081 
1082             private uint count;
1083             private int headIndex;
1084             private bool resetFlag;
1085             private int tailIndex = -1;
1086 
1087             private readonly uint[] memory = new uint[DoubleWordCapacity];
1088             private readonly string name;
1089             private readonly IPeripheral owner;
1090 
1091             private const int DoubleWordCapacity = 8;
1092         }
1093 
1094         private enum Commands
1095         {
1096             None = 0x0,
1097             Write = 0x1,
1098             Read = 0x2,
1099             TestModeWrite = 0x3,
1100             TestModeRead = 0x4,
1101         }
1102 
1103         private enum Status
1104         {
1105             Error = 0x1,
1106             Active = 0x2,
1107             Idle = 0x4,
1108             Wait = 0x6,
1109         }
1110 
1111         private enum IoMasterInterrupts
1112         {
1113             CommandComplete,
1114             FifoThreshold,
1115             ReadFifoUnderflow,
1116             WriteFifoOverflow,
1117             I2CNak,
1118             IllegalFifoAccess,
1119             IllegalCommand,
1120             StartCommand,
1121             StopCommand,
1122             ArbitrationLoss,
1123             DmaComplete,
1124             DmaError,
1125             CommandQueuePaused,
1126             // No sure what "UPD" means here. This is the full description:
1127             // "CQ write operation performed a register write with the register address bit 0 set to 1.
1128             // The low address bits in the CQ address fields are unused and bit 0 can be used to trigger
1129             // an interrupt to indicate when this register write is performed by the CQ operation."
1130             CommandQueueUPD,
1131             CommandQueueError,
1132         }
1133 
1134         private enum Registers : long
1135         {
1136             OutgoingFifoAccessPort = 0x0,
1137             IncomingFifoAccessPort = 0x20,
1138             FifoSizeAndRemainingSlotsOpenValues = 0x100,
1139             FifoThresholdConfiguration = 0x104,
1140             FifoPop = 0x108,
1141             FifoPush = 0x10C,
1142             FifoControl = 0x110,
1143             FifoPointers = 0x114,
1144             IOClockConfiguration = 0x118,
1145             SubmoduleControl = 0x11C,
1146             CommandAndOffset = 0x120,
1147             DcxControlAndCeUsageSelection = 0x124,
1148             HighOrderBytesBfOffsetForIOTransaction = 0x128,
1149             CommandStatus = 0x12C,
1150             IOMasterInterruptsEnable = 0x200,
1151             IOMasterInterruptsStatus = 0x204,
1152             IOMasterInterruptsClear = 0x208,
1153             IOMasterInterruptsSet = 0x20C,
1154             DmaTriggerEnable = 0x210,
1155             DmaTriggerStatus = 0x214,
1156             DmaConfiguration = 0x218,
1157             DmaTotalTransferCount = 0x21C,
1158             DmaTargetAddress = 0x220,
1159             DmaStatus = 0x224,
1160             CommandQueueConfiguration = 0x228,
1161             CommandQueueTargetReadAddress = 0x22C,
1162             CommandQueueStatus = 0x230,
1163             CommandQueueFlag = 0x234,
1164             CommandQueueFlagSetClear = 0x238,
1165             CommandQueuePauseEnable = 0x23C,
1166             CommandQueueCurrentIndexValue = 0x240,
1167             CommandQueueEndIndexValue = 0x244,
1168             IOModuleStatus = 0x248,
1169             SpiModuleMasterConfiguration = 0x280,
1170             I2CMasterConfiguration = 0x2C0,
1171             I2CDeviceConfiguration = 0x2C4,
1172             I2SControl = 0x300,
1173             I2SClockControl = 0x304,
1174             I2SFrameSyncControl = 0x308,
1175             IOModuleDebug = 0x388,
1176         }
1177 
1178         private enum SubmoduleTypes : uint
1179         {
1180             SpiMaster = 0x0,
1181             I2CMaster = 0x1,
1182             SpiSlave = 0x2,
1183             I2CSlave = 0x3,
1184             I2SMaster_Slave = 0x4,
1185             NotInstalled = 0x7,
1186         }
1187     }
1188 }
1189