1 //
2 // Copyright (c) 2010-2025 Antmicro
3 // Copyright (c) 2022-2025 Silicon Labs
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 
9 using System;
10 using System.Collections.Generic;
11 using System.Collections.ObjectModel;
12 using System.Linq;
13 using System.Runtime.InteropServices;
14 using Antmicro.Renode.Core;
15 using Antmicro.Renode.Core.Structure.Registers;
16 using Antmicro.Renode.Exceptions;
17 using Antmicro.Renode.Logging;
18 using Antmicro.Renode.Peripherals.Bus;
19 using Antmicro.Renode.Peripherals.Timers;
20 using Antmicro.Renode.Time;
21 using Antmicro.Renode.Utilities.Packets;
22 
23 namespace Antmicro.Renode.Peripherals.DMA
24 {
25     public class EFR32xG22_LDMA : IBusPeripheral, IGPIOReceiver, IKnownSize
26     {
EFR32xG22_LDMA(Machine machine)27         public EFR32xG22_LDMA(Machine machine)
28         {
29             this.machine = machine;
30             engine = new DmaEngine(machine.GetSystemBus(this));
31             signals = new HashSet<int>();
32             IRQ = new GPIO();
33             channels = new Channel[NumberOfChannels];
34             for(var i = 0; i < NumberOfChannels; ++i)
35             {
36                 channels[i] = new Channel(this, i);
37             }
38             ldmaRegistersCollection = BuildLdmaRegisters();
39             ldmaXbarRegistersCollection = BuildLdmaXbarRegisters();
40         }
41 
Reset()42         public void Reset()
43         {
44             signals.Clear();
45             foreach(var channel in channels)
46             {
47                 channel.Reset();
48             }
49             UpdateInterrupts();
50         }
51 
OnGPIO(int number, bool value)52         public void OnGPIO(int number, bool value)
53         {
54             var signal = (SignalSelect)(number & 0xf);
55             var source = (SourceSelect)((number >> 4) & 0x3f);
56             bool single = ((number >> 12) & 1) != 0;
57 
58             if(!value)
59             {
60                 signals.Remove(number);
61                 return;
62             }
63             signals.Add(number);
64 
65             for(var i = 0; i < NumberOfChannels; ++i)
66             {
67                 if(single && channels[i].IgnoreSingleRequests)
68                 {
69                     continue;
70                 }
71                 if(channels[i].Signal == signal && channels[i].Source == source)
72                 {
73                     channels[i].StartFromSignal();
74                 }
75             }
76         }
77 
78         public GPIO IRQ { get; }
79 
80         public long Size => 0x400;
81         private readonly DoubleWordRegisterCollection ldmaRegistersCollection;
82         private readonly DoubleWordRegisterCollection ldmaXbarRegistersCollection;
83         private readonly Machine machine;
84 
85         private uint Read<T>(DoubleWordRegisterCollection registersCollection, string regionName, long offset, bool internal_read = false)
86         where T : struct, IComparable, IFormattable
87         {
88             var result = 0U;
89             long internal_offset = offset;
90 
91             // Set, Clear, Toggle registers should only be used for write operations. But just in case we convert here as well.
92             if (offset >= SetRegisterOffset && offset < ClearRegisterOffset)
93             {
94                 // Set register
95                 internal_offset = offset - SetRegisterOffset;
96                 if(!internal_read)
97                 {
98                     this.Log(LogLevel.Noisy, "SET Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", Enum.Format(typeof(T), internal_offset, "G"), offset, internal_offset);
99                 }
100             } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset)
101             {
102                 // Clear register
103                 internal_offset = offset - ClearRegisterOffset;
104                 if(!internal_read)
105                 {
106                     this.Log(LogLevel.Noisy, "CLEAR Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", Enum.Format(typeof(T), internal_offset, "G"), offset, internal_offset);
107                 }
108             } else if (offset >= ToggleRegisterOffset)
109             {
110                 // Toggle register
111                 internal_offset = offset - ToggleRegisterOffset;
112                 if(!internal_read)
113                 {
114                     this.Log(LogLevel.Noisy, "TOGGLE Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", Enum.Format(typeof(T), internal_offset, "G"), offset, internal_offset);
115                 }
116             }
117 
118             if(!registersCollection.TryRead(internal_offset, out result))
119             {
120                 if(!internal_read)
121                 {
122                     this.Log(LogLevel.Noisy, "Unhandled read from {0} at offset 0x{1:X} ({2}).", regionName, internal_offset, Enum.Format(typeof(T), internal_offset, "G"));
123                 }
124             }
125             else
126             {
127                 if(!internal_read)
128                 {
129                     this.Log(LogLevel.Noisy, "{0}: Read from {1} at offset 0x{2:X} ({3}), returned 0x{4:X}",
130                              this.GetTime(), regionName, internal_offset, Enum.Format(typeof(T), internal_offset, "G"), result);
131                 }
132             }
133 
134             return result;
135         }
136 
137         private void Write<T>(DoubleWordRegisterCollection registersCollection, string regionName, long offset, uint value)
138         where T : struct, IComparable, IFormattable
139         {
machine.ClockSource.ExecuteInLockAntmicro.Renode.Peripherals.DMA.EFR32xG22_LDMA.IFormattable140             machine.ClockSource.ExecuteInLock(delegate {
141                 long internal_offset = offset;
142                 uint internal_value = value;
143 
144                 if (offset >= SetRegisterOffset && offset < ClearRegisterOffset)
145                 {
146                     // Set register
147                     internal_offset = offset - SetRegisterOffset;
148                     uint old_value = Read<T>(registersCollection, regionName, internal_offset, true);
149                     internal_value = old_value | value;
150                     this.Log(LogLevel.Noisy, "SET Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}, SET_value=0x{3:X}, old_value=0x{4:X}, new_value=0x{5:X}", Enum.Format(typeof(T), internal_offset, "G"), offset, internal_offset, value, old_value, internal_value);
151                 } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset)
152                 {
153                     // Clear register
154                     internal_offset = offset - ClearRegisterOffset;
155                     uint old_value = Read<T>(registersCollection, regionName, internal_offset, true);
156                     internal_value = old_value & ~value;
157                     this.Log(LogLevel.Noisy, "CLEAR Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}, CLEAR_value=0x{3:X}, old_value=0x{4:X}, new_value=0x{5:X}", Enum.Format(typeof(T), internal_offset, "G"), offset, internal_offset, value, old_value, internal_value);
158                 } else if (offset >= ToggleRegisterOffset)
159                 {
160                     // Toggle register
161                     internal_offset = offset - ToggleRegisterOffset;
162                     uint old_value = Read<T>(registersCollection, regionName, internal_offset, true);
163                     internal_value = old_value ^ value;
164                     this.Log(LogLevel.Noisy, "TOGGLE Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}, TOGGLE_value=0x{3:X}, old_value=0x{4:X}, new_value=0x{5:X}", Enum.Format(typeof(T), internal_offset, "G"), offset, internal_offset, value, old_value, internal_value);
165                 }
166 
167                 this.Log(LogLevel.Noisy, "{0}: Write to {1} at offset 0x{2:X} ({3}), value 0x{4:X}",
168                         this.GetTime(), regionName, internal_offset, Enum.Format(typeof(T), internal_offset, "G"), internal_value);
169 
170                 if(!registersCollection.TryWrite(internal_offset, internal_value))
171                 {
172                     this.Log(LogLevel.Debug, "Unhandled write to {0} at offset 0x{1:X} ({2}), value 0x{3:X}.", regionName, internal_offset, Enum.Format(typeof(T), internal_offset, "G"), internal_value);
173                     return;
174                 }
175             });
176         }
177 
178         [ConnectionRegionAttribute("ldma")]
WriteDoubleWordToLdma(long offset, uint value)179         public void WriteDoubleWordToLdma(long offset, uint value)
180         {
181             Write<LdmaRegisters>(ldmaRegistersCollection, "Ldma", offset, value);
182         }
183 
184         [ConnectionRegionAttribute("ldma")]
ReadDoubleWordFromLdma(long offset)185         public uint ReadDoubleWordFromLdma(long offset)
186         {
187             return Read<LdmaRegisters>(ldmaRegistersCollection, "Ldma", offset);
188         }
189 
190         [ConnectionRegionAttribute("ldmaxbar")]
WriteDoubleWordToLdmaXbar(long offset, uint value)191         public void WriteDoubleWordToLdmaXbar(long offset, uint value)
192         {
193             Write<LdmaXbarRegisters>(ldmaXbarRegistersCollection, "LdmaXbar", offset, value);
194         }
195 
196         [ConnectionRegionAttribute("ldmaxbar")]
ReadDoubleWordFromLdmaXbar(long offset)197         public uint ReadDoubleWordFromLdmaXbar(long offset)
198         {
199             return Read<LdmaXbarRegisters>(ldmaXbarRegistersCollection, "LdmaXbar", offset);
200         }
201 
BuildLdmaRegisters()202         private DoubleWordRegisterCollection BuildLdmaRegisters()
203         {
204             DoubleWordRegisterCollection c =  new DoubleWordRegisterCollection(this, new Dictionary<long, DoubleWordRegister>
205             {
206                 {(long)LdmaRegisters.CTRL, new DoubleWordRegister(this)
207                     .WithReservedBits(0, 24)
208                     .WithTag("NUMFIXED", 24, 5)
209                     .WithReservedBits(29, 2)
210                     .WithTaggedFlag("CORERST", 31)
211                 },
212                 {(long)LdmaRegisters.STATUS, new DoubleWordRegister(this)
213                     .WithTaggedFlag("ANYBUSY", 0)
214                     .WithTaggedFlag("ANYREQ", 1)
215                     .WithReservedBits(2, 1)
216                     .WithTag("CHGRANT", 3, 5)
217                     .WithTag("CHERROR", 8, 5)
218                     .WithReservedBits(13, 3)
219                     .WithTag("FIFOLEVEL", 16, 5)
220                     .WithReservedBits(21, 3)
221                     .WithTag("CHNUM", 24, 5)
222                     .WithReservedBits(29, 3)
223                 },
224                 {(long)LdmaRegisters.CHEN, new DoubleWordRegister(this)
225                     .WithFlags(0, 8, writeCallback: (i, _, value) => { if (value) channels[i].Enabled = true; }, name: "CHEN")
226                     .WithReservedBits(8, 24)
227                 },
228                 {(long)LdmaRegisters.CHDIS, new DoubleWordRegister(this)
229                     .WithFlags(0, 8, writeCallback: (i, _, value) => { if (value) channels[i].Enabled = false; }, name: "CHDIS")
230                     .WithReservedBits(8, 24)
231                 },
232                 {(long)LdmaRegisters.CHBUSY, new DoubleWordRegister(this)
233                     .WithFlags(0, 8, FieldMode.Read, valueProviderCallback: (i, _) => channels[i].Busy, name: "CHBUSY")
234                     .WithReservedBits(8, 24)
235                 },
236                 {(long)LdmaRegisters.CHSTATUS, new DoubleWordRegister(this)
237                     .WithFlags(0, 8, FieldMode.Read, valueProviderCallback: (i, _) => channels[i].Enabled, name: "CHSTATUS")
238                     .WithReservedBits(8, 24)
239                 },
240                 {(long)LdmaRegisters.CHDONE, new DoubleWordRegister(this)
241                     .WithFlags(0, 8, writeCallback: (i, _, value) => channels[i].Done = value, valueProviderCallback: (i, _) => channels[i].Done, name: "CHDONE")
242                     .WithReservedBits(8, 24)
243                 },
244                 {(long)LdmaRegisters.DBGHALT, new DoubleWordRegister(this)
245                     .WithTag("DBGHALT", 0, 8)
246                     .WithReservedBits(8, 24)
247                 },
248                 {(long)LdmaRegisters.SWREQ, new DoubleWordRegister(this)
249                     .WithFlags(0, 8, FieldMode.Set, writeCallback: (i, _, value) => { if(value) channels[i].StartTransfer(); }, name: "SWREQ")
250                     .WithReservedBits(8, 24)
251                 },
252                 {(long)LdmaRegisters.REQDIS, new DoubleWordRegister(this)
253                     .WithFlags(0, 8, writeCallback: (i, _, value) => channels[i].RequestDisable = value, valueProviderCallback: (i, _) => channels[i].RequestDisable, name: "REQDIS")
254                     .WithReservedBits(8, 24)
255                 },
256                 {(long)LdmaRegisters.REQPEND, new DoubleWordRegister(this)
257                     .WithTag("REQPEND", 0, 8)
258                     .WithReservedBits(8, 24)
259                 },
260                 {(long)LdmaRegisters.LINKLOAD, new DoubleWordRegister(this)
261                     .WithFlags(0, 8, FieldMode.Set, writeCallback: (i, _, value) => { if(value) channels[i].LinkLoad(); }, name: "LINKLOAD")
262                     .WithReservedBits(8, 24)
263                 },
264                 {(long)LdmaRegisters.REQCLEAR, new DoubleWordRegister(this)
265                     .WithTag("REQCLEAR", 0, 8)
266                     .WithReservedBits(8, 24)
267                 },
268                 {(long)LdmaRegisters.IF, new DoubleWordRegister(this)
269                     .WithFlags(0, 8, writeCallback: (i, _, value) => channels[i].DoneInterrupt = value, valueProviderCallback: (i, _) => channels[i].DoneInterrupt, name: "IF")
270                     .WithReservedBits(8, 23)
271                     .WithTaggedFlag("ERROR", 31)
272                     .WithWriteCallback((_, __) => UpdateInterrupts())
273                 },
274                 {(long)LdmaRegisters.IEN, new DoubleWordRegister(this)
275                     .WithFlags(0, 8, writeCallback: (i, _, value) => channels[i].DoneInterruptEnable = value, valueProviderCallback: (i, _) => channels[i].DoneInterruptEnable, name: "IEN")
276                     .WithReservedBits(8, 23)
277                     .WithTaggedFlag("ERROR", 31)
278                     .WithWriteCallback((_, __) => UpdateInterrupts())
279                 },
280             });
281 
282             var channelDelta = (uint)((long)LdmaRegisters.CH1_CFG - (long)LdmaRegisters.CH0_CFG);
283             BindRegisters(LdmaRegisters.CH0_CFG, c, NumberOfChannels, i => channels[i].ConfigurationRegister, channelDelta);
284             BindRegisters(LdmaRegisters.CH0_LOOP, c, NumberOfChannels, i => channels[i].LoopCounterRegister, channelDelta);
285             BindRegisters(LdmaRegisters.CH0_CTRL, c, NumberOfChannels, i => channels[i].DescriptorControlWordRegister, channelDelta);
286             BindRegisters(LdmaRegisters.CH0_SRC, c, NumberOfChannels, i => channels[i].DescriptorSourceDataAddressRegister, channelDelta);
287             BindRegisters(LdmaRegisters.CH0_DST, c, NumberOfChannels, i => channels[i].DescriptorDestinationDataAddressRegister, channelDelta);
288             BindRegisters(LdmaRegisters.CH0_LINK, c, NumberOfChannels, i => channels[i].DescriptorLinkStructureAddressRegister, channelDelta);
289 
290             return c;
291         }
292 
BuildLdmaXbarRegisters()293         private DoubleWordRegisterCollection BuildLdmaXbarRegisters()
294         {
295             DoubleWordRegisterCollection c =  new DoubleWordRegisterCollection(this, new Dictionary<long, DoubleWordRegister>());
296 
297             var channelDelta = (uint)((long)LdmaXbarRegisters.XBAR_CH1_REQSEL - (long)LdmaXbarRegisters.XBAR_CH0_REQSEL);
298             BindRegisters(LdmaXbarRegisters.XBAR_CH0_REQSEL, c, NumberOfChannels, i => channels[i].PeripheralRequestSelectRegister, channelDelta);
299 
300             return c;
301         }
302 
BindRegisters(IConvertible o, DoubleWordRegisterCollection c, uint count, Func<int, DoubleWordRegister> setup, uint stepInBytes = 4)303         private void BindRegisters(IConvertible o, DoubleWordRegisterCollection c, uint count, Func<int, DoubleWordRegister> setup, uint stepInBytes = 4)
304         {
305             if(!o.GetType().IsEnum)
306             {
307                 throw new ArgumentException("This method should be called on enumerated type");
308             }
309 
310             var baseAddress = Convert.ToInt64(o);
311             for(var i = 0; i < count; i++)
312             {
313                 var register = setup(i);
314                 c.AddRegister(baseAddress + i * stepInBytes, register);
315             }
316         }
317 
UpdateInterrupts()318         private void UpdateInterrupts()
319         {
320             this.Log(LogLevel.Debug, "Interrupt set for channels: {0}", String.Join(", ",
321                 channels
322                     .Where(channel => channel.IRQ)
323                     .Select(channel => channel.Index)
324                 ));
325             IRQ.Set(channels.Any(channel => channel.IRQ));
326         }
327 
GetTime()328         private TimeInterval GetTime() => machine.LocalTimeSource.ElapsedVirtualTime;
329 
330         private readonly DmaEngine engine;
331         private readonly HashSet<int> signals;
332         private readonly Channel[] channels;
333         private const uint SetRegisterOffset = 0x1000;
334         private const uint ClearRegisterOffset = 0x2000;
335         private const uint ToggleRegisterOffset = 0x3000;
336         private const int NumberOfChannels = 8;
337 
338         private enum SignalSelect
339         {
340             // if SOURCESEL is LDMAXBAR
341             LDMAXBAR_DMA_PRSREQ0                = 0x0,
342             LDMAXBAR_DMA_PRSREQ1                = 0x1,
343             // if SOURCESEL is TIMER0
344             TIMER0_DMA_CC0                      = 0x0,
345             TIMER0_DMA_CC1                      = 0x1,
346             TIMER0_DMA_CC2                      = 0x2,
347             TIMER0_DMA_UFOF                     = 0x3,
348             // if SOURCESEL is TIMER1
349             TIMER1_DMA_CC0                      = 0x0,
350             TIMER1_DMA_CC1                      = 0x1,
351             TIMER1_DMA_CC2                      = 0x2,
352             TIMER1_DMA_UFOF                     = 0x3,
353             // if SOURCESEL is USART0
354             USART0_DMA_RXDATAV                  = 0x0,
355             USART0_DMA_RXDATAVRIGHT             = 0x1,
356             USART0_DMA_TXBL                     = 0x2,
357             USART0_DMA_TXBLRIGHT                = 0x3,
358             USART0_DMA_TXEMPTY                  = 0x4,
359             // if SOURCESEL is USART1
360             USART1_DMA_RXDATAV                  = 0x0,
361             USART1_DMA_RXDATAVRIGHT             = 0x1,
362             USART1_DMA_TXBL                     = 0x2,
363             USART1_DMA_TXBLRIGHT                = 0x3,
364             USART1_DMA_TXEMPTY                  = 0x4,
365             // if SOURCESEL is I2C0
366             I2C0_DMA_RXDATAV                    = 0x0,
367             I2C0_DMA_TXBL                       = 0x1,
368             // if SOURCESEL is I2C1
369             I2C1_DMA_RXDATAV                    = 0x0,
370             I2C1_DMA_TXBL                       = 0x1,
371             // if SOURCESEL is IADC0
372             IADC0_DMA_IADC_SCAN                 = 0x0,
373             IADC0_DMA_IADC_SINGLE               = 0x1,
374             // if SOURCESEL is MSC
375             MSC_DMA_WDATA                       = 0x0,
376             // if SOURCESEL is TIMER2
377             TIMER2_DMA_CC0                      = 0x0,
378             TIMER2_DMA_CC1                      = 0x1,
379             TIMER2_DMA_CC2                      = 0x2,
380             TIMER2_DMA_UFOF                     = 0x3,
381             // if SOURCESEL is TIMER3
382             TIMER3_DMA_CC0                      = 0x0,
383             TIMER3_DMA_CC1                      = 0x1,
384             TIMER3_DMA_CC2                      = 0x2,
385             TIMER3_DMA_UFOF                     = 0x3,
386             // if SOURCESEL is PDM
387             PDM_DMA_RXDATAV                     = 0x0,
388             // if SOURCESEL is EUART0
389             EUART0_DMA_RXFL                     = 0x0,
390             EUART0_DMA_TXFL                     = 0x1,
391             // if SOURCESEL is TIMER4
392             TIMER4_DMA_CC0                      = 0x0,
393             TIMER4_DMA_CC1                      = 0x1,
394             TIMER4_DMA_CC2                      = 0x2,
395             TIMER4_DMA_UFOF                     = 0x3,
396         }
397 
398         private enum SourceSelect
399         {
400             None     = 0x0,
401             LDMAXBAR = 0x1,
402             TIMER0   = 0x2,
403             TIMER1   = 0x3,
404             USART0   = 0x4,
405             USART1   = 0x5,
406             I2C0     = 0x6,
407             I2C1     = 0x7,
408             IADC0    = 0xB,
409             MSC      = 0xC,
410             TIMER2   = 0xD,
411             TIMER3   = 0xE,
412             PDM      = 0xF,
413             EUART0   = 0x10,
414             TIMER4   = 0x11,
415         }
416 
417         private enum LdmaRegisters : long
418         {
419             IPVERSION              = 0x0000,
420             EN                     = 0x0004,
421             CTRL                   = 0x0008,
422             STATUS                 = 0x000C,
423             SYNCSWSET              = 0x0010,
424             SYNCSWCLR              = 0x0014,
425             SYNCHWEN               = 0x0018,
426             SYNCHWSEL              = 0x001C,
427             SYNCSTATUS             = 0x0020,
428             CHEN                   = 0x0024,
429             CHDIS                  = 0x0028,
430             CHSTATUS               = 0x002C,
431             CHBUSY                 = 0x0030,
432             CHDONE                 = 0x0034,
433             DBGHALT                = 0x0038,
434             SWREQ                  = 0x003C,
435             REQDIS                 = 0x0040,
436             REQPEND                = 0x0044,
437             LINKLOAD               = 0x0048,
438             REQCLEAR               = 0x004C,
439             IF                     = 0x0050,
440             IEN                    = 0x0054,
441             CH0_CFG                = 0x005C,
442             CH0_LOOP               = 0x0060,
443             CH0_CTRL               = 0x0064,
444             CH0_SRC                = 0x0068,
445             CH0_DST                = 0x006C,
446             CH0_LINK               = 0x0070,
447             CH1_CFG                = 0x008C,
448             CH1_LOOP               = 0x0090,
449             CH1_CTRL               = 0x0094,
450             CH1_SRC                = 0x0098,
451             CH1_DST                = 0x009C,
452             CH1_LINK               = 0x00A0,
453             CH2_CFG                = 0x00BC,
454             CH2_LOOP               = 0x00C0,
455             CH2_CTRL               = 0x00C4,
456             CH2_SRC                = 0x00C8,
457             CH2_DST                = 0x00CC,
458             CH2_LINK               = 0x00D0,
459             CH3_CFG                = 0x00EC,
460             CH3_LOOP               = 0x00F0,
461             CH3_CTRL               = 0x00F4,
462             CH3_SRC                = 0x00F8,
463             CH3_DST                = 0x00FC,
464             CH3_LINK               = 0x0100,
465             CH4_CFG                = 0x011C,
466             CH4_LOOP               = 0x0120,
467             CH4_CTRL               = 0x0124,
468             CH4_SRC                = 0x0128,
469             CH4_DST                = 0x012C,
470             CH4_LINK               = 0x0130,
471             CH5_CFG                = 0x014C,
472             CH5_LOOP               = 0x0150,
473             CH5_CTRL               = 0x0154,
474             CH5_SRC                = 0x0158,
475             CH5_DST                = 0x015C,
476             CH5_LINK               = 0x0160,
477             CH6_CFG                = 0x017C,
478             CH6_LOOP               = 0x0180,
479             CH6_CTRL               = 0x0184,
480             CH6_SRC                = 0x0188,
481             CH6_DST                = 0x018C,
482             CH6_LINK               = 0x0190,
483             CH7_CFG                = 0x01AC,
484             CH7_LOOP               = 0x01B0,
485             CH7_CTRL               = 0x01B4,
486             CH7_SRC                = 0x01B8,
487             CH7_DST                = 0x01BC,
488             CH7_LINK               = 0x01C0,
489             // Set registers
490             IPVERSION_Set          = 0x1000,
491             EN_Set                 = 0x1004,
492             CTRL_Set               = 0x1008,
493             STATUS_Set             = 0x100C,
494             SYNCSWSET_Set          = 0x1010,
495             SYNCSWCLR_Set          = 0x1014,
496             SYNCHWEN_Set           = 0x1018,
497             SYNCHWSEL_Set          = 0x101C,
498             SYNCSTATUS_Set         = 0x1020,
499             CHEN_Set               = 0x1024,
500             CHDIS_Set              = 0x1028,
501             CHSTATUS_Set           = 0x102C,
502             CHBUSY_Set             = 0x1030,
503             CHDONE_Set             = 0x1034,
504             DBGHALT_Set            = 0x1038,
505             SWREQ_Set              = 0x103C,
506             REQDIS_Set             = 0x1040,
507             REQPEND_Set            = 0x1044,
508             LINKLOAD_Set           = 0x1048,
509             REQCLEAR_Set           = 0x104C,
510             IF_Set                 = 0x1050,
511             IEN_Set                = 0x1054,
512             CH0_CFG_Set            = 0x105C,
513             CH0_LOOP_Set           = 0x1060,
514             CH0_CTRL_Set           = 0x1064,
515             CH0_SRC_Set            = 0x1068,
516             CH0_DST_Set            = 0x106C,
517             CH0_LINK_Set           = 0x1070,
518             CH1_CFG_Set            = 0x108C,
519             CH1_LOOP_Set           = 0x1090,
520             CH1_CTRL_Set           = 0x1094,
521             CH1_SRC_Set            = 0x1098,
522             CH1_DST_Set            = 0x109C,
523             CH1_LINK_Set           = 0x10A0,
524             CH2_CFG_Set            = 0x10BC,
525             CH2_LOOP_Set           = 0x10C0,
526             CH2_CTRL_Set           = 0x10C4,
527             CH2_SRC_Set            = 0x10C8,
528             CH2_DST_Set            = 0x10CC,
529             CH2_LINK_Set           = 0x10D0,
530             CH3_CFG_Set            = 0x10EC,
531             CH3_LOOP_Set           = 0x10F0,
532             CH3_CTRL_Set           = 0x10F4,
533             CH3_SRC_Set            = 0x10F8,
534             CH3_DST_Set            = 0x10FC,
535             CH3_LINK_Set           = 0x1100,
536             CH4_CFG_Set            = 0x111C,
537             CH4_LOOP_Set           = 0x1120,
538             CH4_CTRL_Set           = 0x1124,
539             CH4_SRC_Set            = 0x1128,
540             CH4_DST_Set            = 0x112C,
541             CH4_LINK_Set           = 0x1130,
542             CH5_CFG_Set            = 0x114C,
543             CH5_LOOP_Set           = 0x1150,
544             CH5_CTRL_Set           = 0x1154,
545             CH5_SRC_Set            = 0x1158,
546             CH5_DST_Set            = 0x115C,
547             CH5_LINK_Set           = 0x1160,
548             CH6_CFG_Set            = 0x117C,
549             CH6_LOOP_Set           = 0x1180,
550             CH6_CTRL_Set           = 0x1184,
551             CH6_SRC_Set            = 0x1188,
552             CH6_DST_Set            = 0x118C,
553             CH6_LINK_Set           = 0x1190,
554             CH7_CFG_Set            = 0x11AC,
555             CH7_LOOP_Set           = 0x11B0,
556             CH7_CTRL_Set           = 0x11B4,
557             CH7_SRC_Set            = 0x11B8,
558             CH7_DST_Set            = 0x11BC,
559             CH7_LINK_Set           = 0x11C0,
560             // Clear registers
561             IPVERSION_Clr          = 0x2000,
562             EN_Clr                 = 0x2004,
563             CTRL_Clr               = 0x2008,
564             STATUS_Clr             = 0x200C,
565             SYNCSWSET_Clr          = 0x2010,
566             SYNCSWCLR_Clr          = 0x2014,
567             SYNCHWEN_Clr           = 0x2018,
568             SYNCHWSEL_Clr          = 0x201C,
569             SYNCSTATUS_Clr         = 0x2020,
570             CHEN_Clr               = 0x2024,
571             CHDIS_Clr              = 0x2028,
572             CHSTATUS_Clr           = 0x202C,
573             CHBUSY_Clr             = 0x2030,
574             CHDONE_Clr             = 0x2034,
575             DBGHALT_Clr            = 0x2038,
576             SWREQ_Clr              = 0x203C,
577             REQDIS_Clr             = 0x2040,
578             REQPEND_Clr            = 0x2044,
579             LINKLOAD_Clr           = 0x2048,
580             REQCLEAR_Clr           = 0x204C,
581             IF_Clr                 = 0x2050,
582             IEN_Clr                = 0x2054,
583             CH0_CFG_Clr            = 0x205C,
584             CH0_LOOP_Clr           = 0x2060,
585             CH0_CTRL_Clr           = 0x2064,
586             CH0_SRC_Clr            = 0x2068,
587             CH0_DST_Clr            = 0x206C,
588             CH0_LINK_Clr           = 0x2070,
589             CH1_CFG_Clr            = 0x208C,
590             CH1_LOOP_Clr           = 0x2090,
591             CH1_CTRL_Clr           = 0x2094,
592             CH1_SRC_Clr            = 0x2098,
593             CH1_DST_Clr            = 0x209C,
594             CH1_LINK_Clr           = 0x20A0,
595             CH2_CFG_Clr            = 0x20BC,
596             CH2_LOOP_Clr           = 0x20C0,
597             CH2_CTRL_Clr           = 0x20C4,
598             CH2_SRC_Clr            = 0x20C8,
599             CH2_DST_Clr            = 0x20CC,
600             CH2_LINK_Clr           = 0x20D0,
601             CH3_CFG_Clr            = 0x20EC,
602             CH3_LOOP_Clr           = 0x20F0,
603             CH3_CTRL_Clr           = 0x20F4,
604             CH3_SRC_Clr            = 0x20F8,
605             CH3_DST_Clr            = 0x20FC,
606             CH3_LINK_Clr           = 0x2100,
607             CH4_CFG_Clr            = 0x211C,
608             CH4_LOOP_Clr           = 0x2120,
609             CH4_CTRL_Clr           = 0x2124,
610             CH4_SRC_Clr            = 0x2128,
611             CH4_DST_Clr            = 0x212C,
612             CH4_LINK_Clr           = 0x2130,
613             CH5_CFG_Clr            = 0x214C,
614             CH5_LOOP_Clr           = 0x2150,
615             CH5_CTRL_Clr           = 0x2154,
616             CH5_SRC_Clr            = 0x2158,
617             CH5_DST_Clr            = 0x215C,
618             CH5_LINK_Clr           = 0x2160,
619             CH6_CFG_Clr            = 0x217C,
620             CH6_LOOP_Clr           = 0x2180,
621             CH6_CTRL_Clr           = 0x2184,
622             CH6_SRC_Clr            = 0x2188,
623             CH6_DST_Clr            = 0x218C,
624             CH6_LINK_Clr           = 0x2190,
625             CH7_CFG_Clr            = 0x21AC,
626             CH7_LOOP_Clr           = 0x21B0,
627             CH7_CTRL_Clr           = 0x21B4,
628             CH7_SRC_Clr            = 0x21B8,
629             CH7_DST_Clr            = 0x21BC,
630             CH7_LINK_Clr           = 0x21C0,
631             // Toggle registers
632             IPVERSION_Tgl          = 0x3000,
633             EN_Tgl                 = 0x3004,
634             CTRL_Tgl               = 0x3008,
635             STATUS_Tgl             = 0x300C,
636             SYNCSWSET_Tgl          = 0x3010,
637             SYNCSWCLR_Tgl          = 0x3014,
638             SYNCHWEN_Tgl           = 0x3018,
639             SYNCHWSEL_Tgl          = 0x301C,
640             SYNCSTATUS_Tgl         = 0x3020,
641             CHEN_Tgl               = 0x3024,
642             CHDIS_Tgl              = 0x3028,
643             CHSTATUS_Tgl           = 0x302C,
644             CHBUSY_Tgl             = 0x3030,
645             CHDONE_Tgl             = 0x3034,
646             DBGHALT_Tgl            = 0x3038,
647             SWREQ_Tgl              = 0x303C,
648             REQDIS_Tgl             = 0x3040,
649             REQPEND_Tgl            = 0x3044,
650             LINKLOAD_Tgl           = 0x3048,
651             REQCLEAR_Tgl           = 0x304C,
652             IF_Tgl                 = 0x3050,
653             IEN_Tgl                = 0x3054,
654             CH0_CFG_Tgl            = 0x305C,
655             CH0_LOOP_Tgl           = 0x3060,
656             CH0_CTRL_Tgl           = 0x3064,
657             CH0_SRC_Tgl            = 0x3068,
658             CH0_DST_Tgl            = 0x306C,
659             CH0_LINK_Tgl           = 0x3070,
660             CH1_CFG_Tgl            = 0x308C,
661             CH1_LOOP_Tgl           = 0x3090,
662             CH1_CTRL_Tgl           = 0x3094,
663             CH1_SRC_Tgl            = 0x3098,
664             CH1_DST_Tgl            = 0x309C,
665             CH1_LINK_Tgl           = 0x30A0,
666             CH2_CFG_Tgl            = 0x30BC,
667             CH2_LOOP_Tgl           = 0x30C0,
668             CH2_CTRL_Tgl           = 0x30C4,
669             CH2_SRC_Tgl            = 0x30C8,
670             CH2_DST_Tgl            = 0x30CC,
671             CH2_LINK_Tgl           = 0x30D0,
672             CH3_CFG_Tgl            = 0x30EC,
673             CH3_LOOP_Tgl           = 0x30F0,
674             CH3_CTRL_Tgl           = 0x30F4,
675             CH3_SRC_Tgl            = 0x30F8,
676             CH3_DST_Tgl            = 0x30FC,
677             CH3_LINK_Tgl           = 0x3100,
678             CH4_CFG_Tgl            = 0x311C,
679             CH4_LOOP_Tgl           = 0x3120,
680             CH4_CTRL_Tgl           = 0x3124,
681             CH4_SRC_Tgl            = 0x3128,
682             CH4_DST_Tgl            = 0x312C,
683             CH4_LINK_Tgl           = 0x3130,
684             CH5_CFG_Tgl            = 0x314C,
685             CH5_LOOP_Tgl           = 0x3150,
686             CH5_CTRL_Tgl           = 0x3154,
687             CH5_SRC_Tgl            = 0x3158,
688             CH5_DST_Tgl            = 0x315C,
689             CH5_LINK_Tgl           = 0x3160,
690             CH6_CFG_Tgl            = 0x317C,
691             CH6_LOOP_Tgl           = 0x3180,
692             CH6_CTRL_Tgl           = 0x3184,
693             CH6_SRC_Tgl            = 0x3188,
694             CH6_DST_Tgl            = 0x318C,
695             CH6_LINK_Tgl           = 0x3190,
696             CH7_CFG_Tgl            = 0x31AC,
697             CH7_LOOP_Tgl           = 0x31B0,
698             CH7_CTRL_Tgl           = 0x31B4,
699             CH7_SRC_Tgl            = 0x31B8,
700             CH7_DST_Tgl            = 0x31BC,
701             CH7_LINK_Tgl           = 0x31C0,
702         }
703 
704         private enum LdmaXbarRegisters : long
705         {
706             XBAR_CH0_REQSEL        = 0x0000,
707             XBAR_CH1_REQSEL        = 0x0004,
708             XBAR_CH2_REQSEL        = 0x0008,
709             XBAR_CH3_REQSEL        = 0x000C,
710             XBAR_CH4_REQSEL        = 0x0010,
711             XBAR_CH5_REQSEL        = 0x0014,
712             XBAR_CH6_REQSEL        = 0x0018,
713             XBAR_CH7_REQSEL        = 0x001C,
714             // Set registers
715             XBAR_CH0_REQSEL_Set    = 0x1000,
716             XBAR_CH1_REQSEL_Set    = 0x1004,
717             XBAR_CH2_REQSEL_Set    = 0x1008,
718             XBAR_CH3_REQSEL_Set    = 0x100C,
719             XBAR_CH4_REQSEL_Set    = 0x1010,
720             XBAR_CH5_REQSEL_Set    = 0x1014,
721             XBAR_CH6_REQSEL_Set    = 0x1018,
722             XBAR_CH7_REQSEL_Set    = 0x101C,
723             // Clear registers
724             XBAR_CH0_REQSEL_Clr    = 0x2000,
725             XBAR_CH1_REQSEL_Clr    = 0x2004,
726             XBAR_CH2_REQSEL_Clr    = 0x2008,
727             XBAR_CH3_REQSEL_Clr    = 0x200C,
728             XBAR_CH4_REQSEL_Clr    = 0x2010,
729             XBAR_CH5_REQSEL_Clr    = 0x2014,
730             XBAR_CH6_REQSEL_Clr    = 0x2018,
731             XBAR_CH7_REQSEL_Clr    = 0x201C,
732             // Toggle registers
733             XBAR_CH0_REQSEL_Tgl    = 0x3000,
734             XBAR_CH1_REQSEL_Tgl    = 0x3004,
735             XBAR_CH2_REQSEL_Tgl    = 0x3008,
736             XBAR_CH3_REQSEL_Tgl    = 0x300C,
737             XBAR_CH4_REQSEL_Tgl    = 0x3010,
738             XBAR_CH5_REQSEL_Tgl    = 0x3014,
739             XBAR_CH6_REQSEL_Tgl    = 0x3018,
740             XBAR_CH7_REQSEL_Tgl    = 0x301C,
741         }
742 
743 
744         private class Channel
745         {
Channel(EFR32xG22_LDMA parent, int index)746             public Channel(EFR32xG22_LDMA parent, int index)
747             {
748                 this.parent = parent;
749                 Index = index;
750                 descriptor = default(Descriptor);
751 
752                 PeripheralRequestSelectRegister = new DoubleWordRegister(parent)
753                     .WithEnumField<DoubleWordRegister, SignalSelect>(0, 4, out signalSelect, name: "SIGSEL")
754                     .WithReservedBits(4, 12)
755                     .WithEnumField<DoubleWordRegister, SourceSelect>(16, 6, out sourceSelect, name: "SOURCESEL")
756                     .WithReservedBits(22, 10)
757                 ;
758                 ConfigurationRegister = new DoubleWordRegister(parent)
759                     .WithReservedBits(0, 16)
760                     .WithEnumField<DoubleWordRegister, ArbitrationSlotNumberMode>(16, 2, out arbitrationSlotNumberSelect, name: "ARBSLOTS")
761                     .WithReservedBits(18, 2)
762                     .WithEnumField<DoubleWordRegister, Sign>(20, 1, out sourceAddressIncrementSign, name: "SRCINCSIGN")
763                     .WithEnumField<DoubleWordRegister, Sign>(21, 1, out destinationAddressIncrementSign, name: "DSTINCSIGN")
764                     .WithReservedBits(22, 10)
765                 ;
766                 LoopCounterRegister = new DoubleWordRegister(parent)
767                     .WithValueField(0, 8, out loopCounter, name: "LOOPCNT")
768                     .WithReservedBits(8, 24)
769                 ;
770                 DescriptorControlWordRegister = new DoubleWordRegister(parent)
771                     .WithEnumField<DoubleWordRegister, StructureType>(0, 2, FieldMode.Read,
772                         valueProviderCallback: _ => descriptor.structureType,
773                         name: "STRUCTTYPE")
774                     .WithReservedBits(2, 1)
775                     .WithFlag(3, FieldMode.Set,
776                         writeCallback: (_, value) => descriptor.structureTransferRequest = value,
777                         name: "STRUCTREQ")
778                     .WithValueField(4, 11,
779                         writeCallback: (_, value) => descriptor.transferCount = (ushort)value,
780                         valueProviderCallback: _ => descriptor.transferCount,
781                         name: "XFERCNT")
782                     .WithFlag(15,
783                         writeCallback: (_, value) => descriptor.byteSwap = value,
784                         valueProviderCallback: _ => descriptor.byteSwap,
785                         name: "BYTESWAP")
786                     .WithEnumField<DoubleWordRegister, BlockSizeMode>(16, 4,
787                         writeCallback: (_, value) => descriptor.blockSize = value,
788                         valueProviderCallback: _ => descriptor.blockSize,
789                         name: "BLOCKSIZE")
790                     .WithFlag(20,
791                         writeCallback: (_, value) => descriptor.operationDoneInterruptFlagSetEnable = value,
792                         valueProviderCallback: _ => descriptor.operationDoneInterruptFlagSetEnable,
793                         name: "DONEIEN")
794                     .WithEnumField<DoubleWordRegister, RequestTransferMode>(21, 1,
795                         writeCallback: (_, value) => descriptor.requestTransferModeSelect = value,
796                         valueProviderCallback: _ => descriptor.requestTransferModeSelect,
797                         name: "REQMODE")
798                     .WithFlag(22,
799                         writeCallback: (_, value) => descriptor.decrementLoopCount = value,
800                         valueProviderCallback: _ => descriptor.decrementLoopCount,
801                         name: "DECLOOPCNT")
802                     .WithFlag(23,
803                         writeCallback: (_, value) => descriptor.ignoreSingleRequests = value,
804                         valueProviderCallback: _ => descriptor.ignoreSingleRequests,
805                         name: "IGNORESREQ")
806                     .WithEnumField<DoubleWordRegister, IncrementMode>(24, 2,
807                         writeCallback: (_, value) => descriptor.sourceIncrement = value,
808                         valueProviderCallback: _ => descriptor.sourceIncrement,
809                         name: "SRCINC")
810                     .WithEnumField<DoubleWordRegister, SizeMode>(26, 2,
811                         writeCallback: (_, value) => descriptor.size = value,
812                         valueProviderCallback: _ => descriptor.size,
813                         name: "SIZE")
814                     .WithEnumField<DoubleWordRegister, IncrementMode>(28, 2,
815                         writeCallback: (_, value) => descriptor.destinationIncrement = value,
816                         valueProviderCallback: _ => descriptor.destinationIncrement,
817                         name: "DSTINC")
818                     .WithEnumField<DoubleWordRegister, AddressingMode>(30, 1, FieldMode.Read,
819                         valueProviderCallback: _ => descriptor.sourceAddressingMode,
820                         name: "SRCMODE")
821                     .WithEnumField<DoubleWordRegister, AddressingMode>(31, 1, FieldMode.Read,
822                         valueProviderCallback: _ => descriptor.destinationAddressingMode,
823                         name: "DSTMODE")
824                     .WithChangeCallback((_, __) => { if(descriptor.structureTransferRequest) LinkLoad(); })
825                 ;
826                 DescriptorSourceDataAddressRegister = new DoubleWordRegister(parent)
827                     .WithValueField(0, 32,
828                         writeCallback: (_, value) => descriptor.sourceAddress = (uint)value,
829                         valueProviderCallback: _ => descriptor.sourceAddress,
830                         name: "SRCADDR")
831                 ;
832                 DescriptorDestinationDataAddressRegister = new DoubleWordRegister(parent)
833                     .WithValueField(0, 32,
834                         writeCallback: (_, value) => descriptor.destinationAddress = (uint)value,
835                         valueProviderCallback: _ => descriptor.destinationAddress,
836                         name: "DSTADDR")
837                 ;
838                 DescriptorLinkStructureAddressRegister = new DoubleWordRegister(parent)
839                     .WithEnumField<DoubleWordRegister, AddressingMode>(0, 1, FieldMode.Read,
840                         valueProviderCallback: _ => descriptor.linkMode,
841                         name: "LINKMODE")
842                     .WithFlag(1,
843                         writeCallback: (_, value) => descriptor.link = value,
844                         valueProviderCallback: _ => descriptor.link,
845                         name: "LINK")
846                     .WithValueField(2, 30,
847                         writeCallback: (_, value) => descriptor.linkAddress = (uint)value,
848                         valueProviderCallback: _ => descriptor.linkAddress,
849                         name: "LINKADDR")
850                 ;
851 
852                 pullTimer = new LimitTimer(parent.machine.ClockSource, 1000000, null, $"pullTimer-{Index}", 15, Direction.Ascending, false, WorkMode.Periodic, true, true);
853                 pullTimer.LimitReached += delegate
854                 {
855                     if(!RequestDisable)
856                     {
857                         StartTransferInner();
858                     }
859                     if(!SignalIsOn || !ShouldPullSignal)
860                     {
861                         pullTimer.Enabled = false;
862                     }
863                 };
864             }
865 
StartFromSignal()866             public void StartFromSignal()
867             {
868                 if(!RequestDisable)
869                 {
870                     StartTransfer();
871                 }
872             }
873 
LinkLoad()874             public void LinkLoad()
875             {
876                 LoadDescriptor();
877                 if(!RequestDisable && (descriptor.structureTransferRequest || SignalIsOn))
878                 {
879                     StartTransfer();
880                 }
881             }
882 
StartTransfer()883             public void StartTransfer()
884             {
885                 if(ShouldPullSignal)
886                 {
887                     pullTimer.Enabled = true;
888                 }
889                 else
890                 {
891                     StartTransferInner();
892                 }
893             }
894 
Reset()895             public void Reset()
896             {
897                 descriptor = default(Descriptor);
898                 pullTimer.Reset();
899                 DoneInterrupt = false;
900                 DoneInterruptEnable = false;
901                 descriptorAddress = null;
902                 requestDisable = false;
903                 enabled = false;
904                 done = false;
905             }
906 
907             public int Index { get; }
908 
909             public SignalSelect Signal => signalSelect.Value;
910             public SourceSelect Source => sourceSelect.Value;
911             public bool IgnoreSingleRequests => descriptor.ignoreSingleRequests;
912             public bool DoneInterrupt { get; set; }
913             public bool DoneInterruptEnable { get; set; }
914             public bool IRQ => DoneInterrupt && DoneInterruptEnable;
915 
916             public DoubleWordRegister PeripheralRequestSelectRegister { get; }
917             public DoubleWordRegister ConfigurationRegister { get; }
918             public DoubleWordRegister LoopCounterRegister { get; }
919             public DoubleWordRegister DescriptorControlWordRegister { get; }
920             public DoubleWordRegister DescriptorSourceDataAddressRegister { get; }
921             public DoubleWordRegister DescriptorDestinationDataAddressRegister { get; }
922             public DoubleWordRegister DescriptorLinkStructureAddressRegister { get; }
923 
924             public bool Enabled
925             {
926                 get
927                 {
928                     return enabled;
929                 }
930                 set
931                 {
932                     if(enabled == value)
933                     {
934                         return;
935                     }
936                     enabled = value;
937                     if(enabled)
938                     {
939                         Done = false;
940                         StartTransfer();
941                     }
942                 }
943             }
944 
945             public bool Done
946             {
947                 get
948                 {
949                     return done;
950                 }
951 
952                 set
953                 {
954                     if (!done)
955                     {
956                         DoneInterrupt |= value && descriptor.operationDoneInterruptFlagSetEnable;
957                     }
958                     done = value;
959                 }
960             }
961 
962             public bool Busy
963             {
964                 get
965                 {
966                     return isInProgress;
967                 }
968             }
969 
970             public bool RequestDisable
971             {
972                 get
973                 {
974                     return requestDisable;
975                 }
976 
977                 set
978                 {
979                     bool oldValue = requestDisable;
980                     requestDisable = value;
981 
982                     if(oldValue && !value)
983                     {
984                         if(SignalIsOn)
985                         {
986                             StartTransfer();
987                         }
988                     }
989                 }
990             }
991 
StartTransferInner()992             private void StartTransferInner()
993             {
994                 if(isInProgress || Done)
995                 {
996                     return;
997                 }
998 
999                 isInProgress = true;
1000                 var loaded = false;
1001                 do
1002                 {
1003                     loaded = false;
1004                     Transfer();
1005                     if(Done && descriptor.link)
1006                     {
1007                         loaded = true;
1008                         LoadDescriptor();
1009                         Done = false;
1010                     }
1011                 }
1012                 while((descriptor.structureTransferRequest && loaded) || (!Done && SignalIsOn));
1013 
1014                 isInProgress = false;
1015                 if (Done)
1016                 {
1017                     pullTimer.Enabled = false;
1018                 }
1019             }
1020 
LoadDescriptor()1021             private void LoadDescriptor()
1022             {
1023                 var address = LinkStructureAddress;
1024                 if(descriptorAddress.HasValue && descriptor.linkMode == AddressingMode.Relative)
1025                 {
1026                     address += descriptorAddress.Value;
1027                 }
1028                 var data = parent.machine.SystemBus.ReadBytes(address, DescriptorSize);
1029                 descriptorAddress = address;
1030                 descriptor = Packet.Decode<Descriptor>(data);
1031 #if DEBUG
1032                 parent.Log(LogLevel.Noisy, "Channel #{0} data {1}", Index, BitConverter.ToString(data));
1033                 parent.Log(LogLevel.Debug, "Channel #{0} Loaded {1}", Index, descriptor.PrettyString);
1034 #endif
1035             }
1036 
Transfer()1037             private void Transfer()
1038             {
1039                 switch(descriptor.structureType)
1040                 {
1041                     case StructureType.Transfer:
1042                         var request = new Request(
1043                             source: new Place(descriptor.sourceAddress),
1044                             destination: new Place(descriptor.destinationAddress),
1045                             size: Bytes,
1046                             readTransferType: SizeAsTransferType,
1047                             writeTransferType: SizeAsTransferType,
1048                             sourceIncrementStep: SourceIncrement,
1049                             destinationIncrementStep: DestinationIncrement
1050                         );
1051                         parent.Log(LogLevel.Debug, "Channel #{0} Performing Transfer", Index);
1052                         parent.engine.IssueCopy(request);
1053                         if(descriptor.requestTransferModeSelect == RequestTransferMode.Block)
1054                         {
1055                             var blockSizeMultiplier = Math.Min(TransferCount, BlockSizeMultiplier);
1056                             parent.Log(LogLevel.Debug, "Channel #{0} TransferCount={1} BlockSizeMultiplier={2}", Index, TransferCount, BlockSizeMultiplier);
1057                             if(blockSizeMultiplier == TransferCount)
1058                             {
1059                                 Done = true;
1060                                 descriptor.transferCount = 0;
1061                             }
1062                             else
1063                             {
1064                                 descriptor.transferCount -= blockSizeMultiplier;
1065                             }
1066                             descriptor.sourceAddress += SourceIncrement * blockSizeMultiplier;
1067                             descriptor.destinationAddress += DestinationIncrement * blockSizeMultiplier;
1068                         }
1069                         else
1070                         {
1071                             Done = true;
1072                         }
1073                         break;
1074                     case StructureType.Synchronize:
1075                         parent.Log(LogLevel.Warning, "Channel #{0} Synchronize is not implemented.", Index);
1076                         break;
1077                     case StructureType.Write:
1078                         parent.Log(LogLevel.Warning, "Channel #{0} Write is not implemented.", Index);
1079                         break;
1080                     default:
1081                         parent.Log(LogLevel.Error, "Channel #{0} Invalid structure type value. No action was performed.", Index);
1082                         return;
1083                 }
1084                 parent.UpdateInterrupts();
1085             }
1086 
1087             private bool ShouldPullSignal
1088             {
1089                 get
1090                 {
1091                     // if this returns true for the selected source and signal
1092                     // then the signal will be periodically pulled instead of waiting
1093                     // for an rising edge
1094                     switch(Source)
1095                     {
1096                         case SourceSelect.None:
1097                             return false;
1098                         case SourceSelect.LDMAXBAR:
1099                             switch(Signal)
1100                             {
1101                                 case SignalSelect.LDMAXBAR_DMA_PRSREQ0:
1102                                 case SignalSelect.LDMAXBAR_DMA_PRSREQ1:
1103                                     return false;
1104                                 default:
1105                                     goto default;
1106                             }
1107                         case SourceSelect.IADC0:
1108                             switch(Signal)
1109                             {
1110                                 case SignalSelect.IADC0_DMA_IADC_SCAN:
1111                                 case SignalSelect.IADC0_DMA_IADC_SINGLE:
1112                                     return false;
1113                                 default:
1114                                     goto default;
1115                             }
1116                         case SourceSelect.USART0:
1117                         case SourceSelect.USART1:
1118                             switch(Signal)
1119                             {
1120                                 case SignalSelect.USART0_DMA_RXDATAV:
1121                                     return false;
1122                                 case SignalSelect.USART0_DMA_TXBL:
1123                                 case SignalSelect.USART0_DMA_TXEMPTY:
1124                                     return true;
1125                                 default:
1126                                     goto default;
1127                             }
1128                         case SourceSelect.EUART0:
1129                             switch(Signal)
1130                             {
1131                                 case SignalSelect.EUART0_DMA_RXFL:
1132                                     return false;
1133                                 case SignalSelect.EUART0_DMA_TXFL:
1134                                     return true;
1135                                 default:
1136                                     goto default;
1137                             }
1138                         case SourceSelect.I2C0:
1139                         case SourceSelect.I2C1:
1140                             switch(Signal)
1141                             {
1142                                 case SignalSelect.I2C0_DMA_RXDATAV:
1143                                     return false;
1144                                 case SignalSelect.I2C0_DMA_TXBL:
1145                                     return true;
1146                                 default:
1147                                     goto default;
1148                             }
1149                         case SourceSelect.TIMER0:
1150                         case SourceSelect.TIMER1:
1151                         case SourceSelect.TIMER2:
1152                         case SourceSelect.TIMER3:
1153                         case SourceSelect.TIMER4:
1154                             switch(Signal)
1155                             {
1156                                 case SignalSelect.TIMER0_DMA_CC0:
1157                                 case SignalSelect.TIMER0_DMA_CC1:
1158                                 case SignalSelect.TIMER0_DMA_CC2:
1159                                 case SignalSelect.TIMER0_DMA_UFOF:
1160                                     return false;
1161                                 default:
1162                                     goto default;
1163                             }
1164                         case SourceSelect.MSC:
1165                             switch(Signal)
1166                             {
1167                                 case SignalSelect.MSC_DMA_WDATA:
1168                                     return false;
1169                                 default:
1170                                     goto default;
1171                             }
1172                         default:
1173                             parent.Log(LogLevel.Error, "Channel #{0} Invalid Source (0x{1:X}) and Signal (0x{2:X}) pair.", Index, Source, Signal);
1174                             return false;
1175                     }
1176                 }
1177             }
1178 
1179             private uint BlockSizeMultiplier
1180             {
1181                 get
1182                 {
1183                     switch(descriptor.blockSize)
1184                     {
1185                         case BlockSizeMode.Unit1:
1186                         case BlockSizeMode.Unit2:
1187                             return 1u << (byte)descriptor.blockSize;
1188                         case BlockSizeMode.Unit3:
1189                             return 3;
1190                         case BlockSizeMode.Unit4:
1191                             return 4;
1192                         case BlockSizeMode.Unit6:
1193                             return 6;
1194                         case BlockSizeMode.Unit8:
1195                             return 8;
1196                         case BlockSizeMode.Unit16:
1197                             return 16;
1198                         case BlockSizeMode.Unit32:
1199                         case BlockSizeMode.Unit64:
1200                         case BlockSizeMode.Unit128:
1201                         case BlockSizeMode.Unit256:
1202                         case BlockSizeMode.Unit512:
1203                         case BlockSizeMode.Unit1024:
1204                             return 1u << ((byte)descriptor.blockSize - 4);
1205                         case BlockSizeMode.All:
1206                             return TransferCount;
1207                         default:
1208                             parent.Log(LogLevel.Warning, "Channel #{0} Invalid Block Size Mode value.", Index);
1209                             return 0;
1210                     }
1211                 }
1212             }
1213 
1214             private bool SignalIsOn
1215             {
1216                 get
1217                 {
1218                     var number = ((int)Source << 4) | (int)Signal;
1219                     return parent.signals.Contains(number) || (!IgnoreSingleRequests && parent.signals.Contains(number | 1 << 12));
1220                 }
1221             }
1222 
1223             private uint TransferCount => (uint)descriptor.transferCount + 1;
1224             private ulong LinkStructureAddress => (ulong)descriptor.linkAddress << 2;
1225 
1226             private uint SourceIncrement => descriptor.sourceIncrement == IncrementMode.None ? 0u : ((1u << (byte)descriptor.size) << (byte)descriptor.sourceIncrement);
1227             private uint DestinationIncrement => descriptor.destinationIncrement == IncrementMode.None ? 0u : ((1u << (byte)descriptor.size) << (byte)descriptor.destinationIncrement);
1228             private TransferType SizeAsTransferType => (TransferType)(1 << (byte)descriptor.size);
1229             private int Bytes => (int)(descriptor.requestTransferModeSelect == RequestTransferMode.All ? TransferCount : Math.Min(TransferCount, BlockSizeMultiplier)) << (byte)descriptor.size;
1230 
1231             private Descriptor descriptor;
1232             private ulong? descriptorAddress;
1233             private bool requestDisable;
1234             private bool enabled;
1235             private bool done;
1236 
1237             // Accesses to sysubs may cause changes in signals, but we should ignore those during active transaction
1238             private bool isInProgress;
1239 
1240             private IEnumRegisterField<SignalSelect> signalSelect;
1241             private IEnumRegisterField<SourceSelect> sourceSelect;
1242             private IEnumRegisterField<ArbitrationSlotNumberMode> arbitrationSlotNumberSelect;
1243             private IEnumRegisterField<Sign> sourceAddressIncrementSign;
1244             private IEnumRegisterField<Sign> destinationAddressIncrementSign;
1245             private IValueRegisterField loopCounter;
1246 
1247             private readonly EFR32xG22_LDMA parent;
1248             private readonly LimitTimer pullTimer;
1249 
1250             protected readonly int DescriptorSize = Packet.CalculateLength<Descriptor>();
1251 
1252             private enum ArbitrationSlotNumberMode
1253             {
1254                 One   = 0,
1255                 Two   = 1,
1256                 Four  = 2,
1257                 Eight = 3,
1258             }
1259 
1260             private enum Sign
1261             {
1262                 Positive = 0,
1263                 Negative = 1,
1264             }
1265 
1266             protected enum StructureType : uint
1267             {
1268                 Transfer    = 0,
1269                 Synchronize = 1,
1270                 Write       = 2,
1271             }
1272 
1273             protected enum BlockSizeMode : uint
1274             {
1275                 Unit1    = 0,
1276                 Unit2    = 1,
1277                 Unit3    = 2,
1278                 Unit4    = 3,
1279                 Unit6    = 4,
1280                 Unit8    = 5,
1281                 Unit16   = 7,
1282                 Unit32   = 9,
1283                 Unit64   = 10,
1284                 Unit128  = 11,
1285                 Unit256  = 12,
1286                 Unit512  = 13,
1287                 Unit1024 = 14,
1288                 All      = 15,
1289             }
1290 
1291             protected enum RequestTransferMode : uint
1292             {
1293                 Block = 0,
1294                 All   = 1,
1295             }
1296 
1297             protected enum IncrementMode : uint
1298             {
1299                 One  = 0,
1300                 Two  = 1,
1301                 Four = 2,
1302                 None = 3,
1303             }
1304 
1305             protected enum SizeMode : uint
1306             {
1307                 Byte     = 0,
1308                 HalfWord = 1,
1309                 Word     = 2,
1310             }
1311 
1312             protected enum AddressingMode : uint
1313             {
1314                 Absolute = 0,
1315                 Relative = 1,
1316             }
1317 
1318             [LeastSignificantByteFirst]
1319             private struct Descriptor
1320             {
1321                 public string PrettyString => $@"Descriptor {{
1322     structureType: {structureType},
1323     structureTransferRequest: {structureTransferRequest},
1324     transferCount: {transferCount + 1},
1325     byteSwap: {byteSwap},
1326     blockSize: {blockSize},
1327     operationDoneInterruptFlagSetEnable: {operationDoneInterruptFlagSetEnable},
1328     requestTransferModeSelect: {requestTransferModeSelect},
1329     decrementLoopCount: {decrementLoopCount},
1330     ignoreSingleRequests: {ignoreSingleRequests},
1331     sourceIncrement: {sourceIncrement},
1332     size: {size},
1333     destinationIncrement: {destinationIncrement},
1334     sourceAddressingMode: {sourceAddressingMode},
1335     destinationAddressingMode: {destinationAddressingMode},
1336     sourceAddress: 0x{sourceAddress:X},
1337     destinationAddress: 0x{destinationAddress:X},
1338     linkMode: {linkMode},
1339     link: {link},
1340     linkAddress: 0x{(linkAddress << 2):X}
1341 }}";
1342 
1343 // Some of this fields are read only via sysbus, but can be loaded from memory
1344 #pragma warning disable 649
1345                 [PacketField, Offset(bytes: 0 << 2, bits: 0), Width(2)]
1346                 public StructureType structureType;
1347                 [PacketField, Offset(bytes: 0 << 2, bits: 3), Width(1)]
1348                 public bool structureTransferRequest;
1349                 [PacketField, Offset(bytes: 0 << 2, bits: 4), Width(11)]
1350                 public uint transferCount;
1351                 [PacketField, Offset(bytes: 0 << 2, bits: 15), Width(1)]
1352                 public bool byteSwap;
1353                 [PacketField, Offset(bytes: 0 << 2, bits: 16), Width(4)]
1354                 public BlockSizeMode blockSize;
1355                 [PacketField, Offset(bytes: 0 << 2, bits: 20), Width(1)]
1356                 public bool operationDoneInterruptFlagSetEnable;
1357                 [PacketField, Offset(bytes: 0 << 2, bits: 21), Width(1)]
1358                 public RequestTransferMode requestTransferModeSelect;
1359                 [PacketField, Offset(bytes: 0 << 2, bits: 22), Width(1)]
1360                 public bool decrementLoopCount;
1361                 [PacketField, Offset(bytes: 0 << 2, bits: 23), Width(1)]
1362                 public bool ignoreSingleRequests;
1363                 [PacketField, Offset(bytes: 0 << 2, bits: 24), Width(2)]
1364                 public IncrementMode sourceIncrement;
1365                 [PacketField, Offset(bytes: 0 << 2, bits: 26), Width(2)]
1366                 public SizeMode size;
1367                 [PacketField, Offset(bytes: 0 << 2, bits: 28), Width(2)]
1368                 public IncrementMode destinationIncrement;
1369                 [PacketField, Offset(bytes: 0 << 2, bits: 30), Width(1)]
1370                 public AddressingMode sourceAddressingMode;
1371                 [PacketField, Offset(bytes: 0 << 2, bits: 31), Width(1)]
1372                 public AddressingMode destinationAddressingMode;
1373                 [PacketField, Offset(bytes: 1 << 2, bits: 0), Width(32)]
1374                 public uint sourceAddress;
1375                 [PacketField, Offset(bytes: 2 << 2, bits: 0), Width(32)]
1376                 public uint destinationAddress;
1377                 [PacketField, Offset(bytes: 3 << 2, bits: 0), Width(1)]
1378                 public AddressingMode linkMode;
1379                 [PacketField, Offset(bytes: 3 << 2, bits: 1), Width(1)]
1380                 public bool link;
1381                 [PacketField, Offset(bytes: 3 << 2, bits: 2), Width(30)]
1382                 public uint linkAddress;
1383 #pragma warning restore 649
1384             }
1385         }
1386     }
1387 }