1 //
2 // Copyright (c) 2010-2024 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.Collections.ObjectModel;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Core.Structure.Registers;
14 using Antmicro.Renode.Peripherals.CPU;
15 using Antmicro.Renode.Exceptions;
16 using Antmicro.Renode.Utilities;
17 
18 namespace Antmicro.Renode.Peripherals.DMA
19 {
20     public partial class PL330_DMA : BasicDoubleWordPeripheral, IKnownSize, IDMA, INumberedGPIOOutput, IGPIOReceiver
21     {
22         // This model doesn't take into account differences in AXI bus width,
23         // which could have impact on unaligned transfers in real HW
24         // this is a know limitation at this moment
PL330_DMA(IMachine machine, uint numberOfSupportedEventsAndInterrupts = MaximumSupportedEventsOrInterrupts, uint numberOfSupportedPeripheralRequestInterfaces = MaximumSupportedPeripheralRequestInterfaces, byte revision = 0x3)25         public PL330_DMA(IMachine machine, uint numberOfSupportedEventsAndInterrupts = MaximumSupportedEventsOrInterrupts, uint numberOfSupportedPeripheralRequestInterfaces = MaximumSupportedPeripheralRequestInterfaces, byte revision = 0x3) : base(machine)
26         {
27             this.Revision = revision;
28             if(numberOfSupportedEventsAndInterrupts > MaximumSupportedEventsOrInterrupts)
29             {
30                 throw new ConstructionException($"No more than {MaximumSupportedEventsOrInterrupts} Events or Interrupts are supported by this peripheral");
31             }
32             this.NumberOfSupportedEventsAndInterrupts = (int)numberOfSupportedEventsAndInterrupts;
33             if(numberOfSupportedPeripheralRequestInterfaces > MaximumSupportedPeripheralRequestInterfaces)
34             {
35                 throw new ConstructionException($"No more than {MaximumSupportedPeripheralRequestInterfaces} Peripheral Request Interfaces are supported by this peripheral");
36             }
37             this.NumberOfSupportedPeripheralRequestInterfaces = (int)numberOfSupportedPeripheralRequestInterfaces;
38 
39             channels = new Channel[NumberOfChannels];
40             for(int i = 0; i < channels.Length; ++i)
41             {
42                 channels[i] = new Channel(this, i);
43             }
44 
45             RegisterInstructions();
46             DefineRegisters();
47 
48             eventActive = new bool[NumberOfSupportedEventsAndInterrupts];
49 
50             var gpios = new Dictionary<int, IGPIO>();
51             for(var i = 0; i < NumberOfSupportedEventsAndInterrupts; ++i)
52             {
53                 gpios.Add(i, new GPIO());
54             }
55             Connections = new ReadOnlyDictionary<int, IGPIO>(gpios);
56 
57             Reset();
58         }
59 
Reset()60         public override void Reset()
61         {
62             base.Reset();
63             debugStatus = false;
64 
65             for(int i = 0; i < channels.Length; ++i)
66             {
67                 channels[i].Reset();
68             }
69 
70             foreach(var connection in Connections.Values)
71             {
72                 connection.Unset();
73             }
74             for(int i = 0; i < NumberOfSupportedEventsAndInterrupts; ++i)
75             {
76                 eventActive[i] = false;
77             }
78             AbortIRQ.Unset();
79         }
80 
OnGPIO(int number, bool value)81         public void OnGPIO(int number, bool value)
82         {
83             if(!IsPeripheralInterfaceValid((uint)number))
84             {
85                 return;
86             }
87             lock(executeLock)
88             {
89                 if(!value)
90                 {
91                     // To simulate requestLast, hold IRQ line high during the entire transmission sequence,
92                     // and bring it down, when all data is sent
93                     // If this feature is not used at all in DMA microcode (program) this doesn't matter
94                     foreach(var channel in channels.Where(c => c?.Peripheral == number))
95                     {
96                         channel.RequestLast = true;
97                     }
98                     return;
99                 }
100 
101                 bool anyChangedState = false;
102                 foreach(var channel in channels.Where(c => c.Status == Channel.ChannelStatus.WaitingForPeripheral).Where(c => c.WaitingEventOrPeripheralNumber == number))
103                 {
104                     // A peripheral has signaled that it's ready for DMA operations to start
105                     channel.Status = Channel.ChannelStatus.Executing;
106                     anyChangedState = true;
107                 }
108                 if(anyChangedState)
109                 {
110                     // Rerun ExecuteLoop if any channel woke up from sleep
111                     ExecuteLoop();
112                 }
113             }
114         }
115 
RequestTransfer(int channel)116         public void RequestTransfer(int channel)
117         {
118             throw new RecoverableException("This DMA requires an in-memory program to transfer data");
119         }
120 
121         // This method should be called from Monitor to decode instruction at given address.
122         // It is intended as a helper to investigate in-memory program.
123         // Uses QuadWord accesses, so the target must support them.
TryDecodeInstructionAtAddress(ulong address, bool fullDecode = false)124         public string TryDecodeInstructionAtAddress(ulong address, bool fullDecode = false)
125         {
126             ulong bytes = machine.GetSystemBus(this).ReadQuadWord(address);
127             if(!decoderRoot.TryParseOpcode((byte)bytes, out var instruction))
128             {
129                 throw new RecoverableException("Unrecognized instruction");
130             }
131             if(fullDecode)
132             {
133                 instruction.ParseAll(bytes);
134             }
135             return instruction.ToString();
136         }
137 
138         public long Size => 0x1000;
139         public int NumberOfChannels => 8;
140 
141         public IReadOnlyDictionary<int, IGPIO> Connections { get; }
142         public GPIO AbortIRQ { get; } = new GPIO();
143 
144         public byte Revision { get; }
145         public int NumberOfSupportedEventsAndInterrupts { get; }
146         public int NumberOfSupportedPeripheralRequestInterfaces { get; }
147 
148         // The values below are only used in Configuration and DmaConfiguration registers - they have no impact on the model's operation
149         public int InstructionCacheLineLength { get; set; } = 16;
150         public int InstructionCacheLinesNumber { get; set; } = 32;
151 
152         public ulong WriteIssuingCapability { get; set; } = 8;
153         public ulong ReadIssuingCapability { get; set; } = 8;
154 
155         public ulong ReadQueueDepth { get; set; } = 16;
156         public ulong WriteQueueDepth { get; set; } = 16;
157 
158         public ulong DataBufferDepth { get; set; } = 1024;
159         public ulong AXIBusWidth { get; set; } = 32;
160 
DefineRegisters()161         private void DefineRegisters()
162         {
163             Registers.DmaInterruptEnable.Define(this)
164                 .WithFlags(0, NumberOfSupportedEventsAndInterrupts, out interruptEnabled,
165                     writeCallback: (idx, _, val) =>
166                         {
167                             if(!val)
168                             {
169                                 Connections[idx].Unset();
170                             }
171                         },
172                     name: "DMA Interrupt Enable")
173                 .WithReservedBits(NumberOfSupportedEventsAndInterrupts, 32 - NumberOfSupportedEventsAndInterrupts);
174 
175             Registers.DmaEventInterruptRawStatus.Define(this)
176                 .WithFlags(0, NumberOfSupportedEventsAndInterrupts, FieldMode.Read,
177                     valueProviderCallback: (idx, _) => eventActive[idx] || Connections[idx].IsSet,
178                     name: "DMA Event or Interrupt Status")
179                 .WithReservedBits(NumberOfSupportedEventsAndInterrupts, 32 - NumberOfSupportedEventsAndInterrupts);
180 
181             Registers.DmaInterruptStatus.Define(this)
182                 .WithFlags(0, NumberOfSupportedEventsAndInterrupts, FieldMode.Read,
183                     valueProviderCallback: (idx, _) => Connections[idx].IsSet,
184                     name: "DMA Interrupt Status")
185                 .WithReservedBits(NumberOfSupportedEventsAndInterrupts, 32 - NumberOfSupportedEventsAndInterrupts);
186 
187             Registers.DmaInterruptClear.Define(this)
188                 .WithFlags(0, NumberOfSupportedEventsAndInterrupts, FieldMode.Write,
189                     writeCallback: (idx, _, val) =>
190                         {
191                             if(val)
192                             {
193                                 Connections[idx].Unset();
194                             }
195                         },
196                     name: "DMA Interrupt Clear")
197                 .WithReservedBits(NumberOfSupportedEventsAndInterrupts, 32 - NumberOfSupportedEventsAndInterrupts);
198 
199             // This register is RO. To bring channel out of fault, issue KILL to Debug Registers
200             Registers.FaultStatusDmaChannel.Define(this)
201                 .WithFlags(0, NumberOfChannels, FieldMode.Read,
202                     valueProviderCallback: (idx, _) => channels[idx].Status == Channel.ChannelStatus.Faulting,
203                     name: "Faulting channels");
204 
205             Registers.DebugStatus.Define(this)
206                 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => debugStatus, name: "Debug status (dbgstatus)")
207                 .WithReservedBits(1, 31);
208 
209             Registers.DebugCommand.Define(this)
210                 .WithValueField(0, 2, FieldMode.Write,
211                     writeCallback: (_, val) =>
212                         {
213                             if(val == 0b00)
214                             {
215                                 ExecuteDebugStart();
216                             }
217                             else
218                             {
219                                 this.Log(LogLevel.Error, "Undefined DMA Debug Command: {0}", val);
220                             }
221                         },
222                     name: "Debug Command")
223                 .WithReservedBits(2, 30);
224 
225             Registers.DebugInstruction0.Define(this)
226                 .WithEnumField(0, 1, out debugThreadType, name: "Debug thread select")
227                 .WithReservedBits(1, 7)
228                 .WithValueField(8, 3, out debugChannelNumber, name: "Debug channel select")
229                 .WithReservedBits(11, 5)
230                 .WithValueField(16, 8, out debugInstructionByte0, name: "Instruction byte 0")
231                 .WithValueField(24, 8, out debugInstructionByte1, name: "Instruction byte 1");
232 
233             Registers.DebugInstruction1.Define(this)
234                 .WithValueField(0, 32, out debugInstructionByte2_3_4_5, name: "Instruction byte 2,3,4,5");
235 
236             Registers.Configuration0.Define(this)
237                 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => NumberOfSupportedPeripheralRequestInterfaces > 0, name: "Supports Peripheral Request Interface")
238                 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => false, name: "Boot from PC")
239                 .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => false, name: "Boot security state for Manager Thread")
240                 .WithReservedBits(3, 1)
241                 .WithValueField(4, 3, FieldMode.Read, valueProviderCallback: _ => (ulong)NumberOfChannels - 1, name: "Number of DMA channels")
242                 .WithReservedBits(7, 5)
243                 .WithValueField(12, 5, FieldMode.Read, valueProviderCallback: _ => (ulong)NumberOfSupportedPeripheralRequestInterfaces - 1, name: "Number of Peripheral Request Interfaces")
244                 .WithValueField(17, 5, FieldMode.Read, valueProviderCallback: _ => (ulong)NumberOfSupportedEventsAndInterrupts - 1, name: "Number of Events/Interrupts")
245                 .WithReservedBits(22, 10);
246 
247             Registers.Configuration1.Define(this)
248                 .WithValueField(0, 3, FieldMode.Read, valueProviderCallback: _ => (ulong)Math.Floor(Math.Log(InstructionCacheLineLength, 2)), name: "Length of icache line")
249                 .WithReservedBits(3, 1)
250                 .WithValueField(4, 4, FieldMode.Read, valueProviderCallback: _ => (ulong)InstructionCacheLinesNumber - 1, name: "Number of icache lines")
251                 .WithReservedBits(8, 24);
252 
253             // Following Configuration registers are tagged only, and currently have no functionality implemented
254             // this is the model's limitation at this moment
255             Registers.Configuration2.Define(this)
256                 .WithTag("Boot PC Address", 0, 32);
257 
258             Registers.Configuration3.Define(this)
259                 .WithTag("Event/Interrupt is Non-secure", 0, NumberOfSupportedEventsAndInterrupts)
260                 .WithReservedBits(NumberOfSupportedEventsAndInterrupts, 32 - NumberOfSupportedEventsAndInterrupts);
261 
262             Registers.Configuration4.Define(this)
263                 .WithTag("Peripheral Request Interface is Non-secure", 0, NumberOfSupportedEventsAndInterrupts)
264                 .WithReservedBits(NumberOfSupportedEventsAndInterrupts, 32 - NumberOfSupportedEventsAndInterrupts);
265 
266             // These are configurable, but have currently no effect on DMA operation
267             Registers.DmaConfiguration.Define(this)
268                 .WithValueField(0, 3, valueProviderCallback: _ => (ulong)Math.Floor(Math.Log(AXIBusWidth, 2)) - 3, name: "AXI bus width") // formula is: width_bytes = log2 (width / 8) ==> log2 width - 3
269                 .WithReservedBits(3, 1)
270                 .WithValueField(4, 3, valueProviderCallback: _ => WriteIssuingCapability - 1, name: "Write issuing capability (number of outstanding write transactions)")
271                 .WithReservedBits(7, 1)
272                 .WithValueField(8, 4, valueProviderCallback: _ => WriteQueueDepth - 1, name: "Write queue depth")
273                 .WithValueField(12, 3, valueProviderCallback: _ => ReadIssuingCapability - 1, name: "Read issuing capability (number of outstanding read transactions)")
274                 .WithReservedBits(15, 1)
275                 .WithValueField(16, 4, valueProviderCallback: _ => ReadQueueDepth - 1, name: "Read queue depth")
276                 .WithValueField(20, 10, valueProviderCallback: _ => DataBufferDepth - 1, name: "Data buffer lines")
277                 .WithReservedBits(30, 2);
278 
279             Registers.PeripheralIdentification0.Define(this)
280                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => 0x30, name: "Part number 0")
281                 .WithReservedBits(8, 24);
282 
283             Registers.PeripheralIdentification1.Define(this)
284                 .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: _ => 0x3, name: "Part number 1")
285                 .WithValueField(4, 4, FieldMode.Read, valueProviderCallback: _ => 0x1, name: "Designer 0")
286                 .WithReservedBits(8, 24);
287 
288             Registers.PeripheralIdentification2.Define(this)
289                 .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: _ => 0x4, name: "Designer 1")
290                 .WithValueField(4, 4, FieldMode.Read, valueProviderCallback: _ => Revision, name: "Revision")
291                 .WithReservedBits(8, 24);
292 
293             Registers.PeripheralIdentification3.Define(this)
294                 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => false, name: "Integration test logic")
295                 .WithReservedBits(1, 31);
296 
297             Registers.ComponentIdentification0.Define(this)
298                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => 0x0D, name: "Component ID 0")
299                 .WithReservedBits(8, 24);
300 
301             Registers.ComponentIdentification1.Define(this)
302                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => 0xF0, name: "Component ID 1")
303                 .WithReservedBits(8, 24);
304 
305             Registers.ComponentIdentification2.Define(this)
306                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => 0x05, name: "Component ID 2")
307                 .WithReservedBits(8, 24);
308 
309             Registers.ComponentIdentification3.Define(this)
310                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => 0xB1, name: "Component ID 3")
311                 .WithReservedBits(8, 24);
312         }
313 
IsEventOrInterruptValid(uint eventNumber)314         private bool IsEventOrInterruptValid(uint eventNumber)
315         {
316             if(eventNumber >= NumberOfSupportedEventsAndInterrupts)
317             {
318                 this.Log(LogLevel.Error, "Trying to signal event: {0}, greater than number of supported events {1}", eventNumber, NumberOfSupportedEventsAndInterrupts);
319                 return false;
320             }
321             return true;
322         }
323 
IsPeripheralInterfaceValid(uint peripheral)324         private bool IsPeripheralInterfaceValid(uint peripheral)
325         {
326             if(peripheral >= NumberOfSupportedPeripheralRequestInterfaces)
327             {
328                 this.Log(LogLevel.Error, "Peripheral Request Interface {0} is not supported. {1} Request Interfaces are enabled.",
329                     peripheral, NumberOfSupportedPeripheralRequestInterfaces);
330                 return false;
331             }
332             return true;
333         }
334 
SignalEventOrInterrupt(uint eventNumber)335         private bool SignalEventOrInterrupt(uint eventNumber)
336         {
337             if(!IsEventOrInterruptValid(eventNumber))
338             {
339                 return false;
340             }
341 
342             // Depending on the value of DmaInterruptEnable register, we either signal an IRQ on a line
343             // or raise an internal event to wake up a thread that might be waiting for this event
344             // either now, or in the future.
345             if(interruptEnabled[eventNumber].Value)
346             {
347                 // Signal an IRQ - use DmaInterruptClear to clear it.
348                 Connections[(int)eventNumber].Set();
349             }
350             else
351             {
352                 // Raise an event
353                 eventActive[eventNumber] = true;
354                 bool anyResumed = false;
355                 foreach(var channel in channels.Where(c => c.Status == Channel.ChannelStatus.WaitingForEvent).Where(c => c.WaitingEventOrPeripheralNumber == eventNumber))
356                 {
357                     anyResumed = true;
358                     // The channel will execute in next iteration of `ExecuteLoop`
359                     channel.Status = Channel.ChannelStatus.Executing;
360                 }
361 
362                 // Only clear the event if there already existed a channel waiting for this event - see section 2.7.1 of the Reference Manual
363                 if(anyResumed)
364                 {
365                     eventActive[eventNumber] = false;
366                 }
367             }
368             return true;
369         }
370 
UpdateAbortInterrupt()371         private void UpdateAbortInterrupt()
372         {
373             if(channels.Any(c => c.Status == Channel.ChannelStatus.Faulting))
374             {
375                 AbortIRQ.Set();
376             }
377             else
378             {
379                 AbortIRQ.Unset();
380             }
381         }
382 
ExecuteDebugStart()383         private void ExecuteDebugStart()
384         {
385             debugStatus = true;
386             ulong debugInstructionBytes = debugInstructionByte2_3_4_5.Value << 16 | debugInstructionByte1.Value << 8 | debugInstructionByte0.Value;
387 
388             string binaryInstructionString = Convert.ToString((long)debugInstructionBytes, 2);
389             this.Log(LogLevel.Debug, "Inserted debug instruction: {0}", binaryInstructionString);
390 
391             if(!decoderRoot.TryParseOpcode((byte)debugInstructionBytes, out var debugInstruction))
392             {
393                 this.Log(LogLevel.Error, "Debug instruction \"{0}\" is not supported or invalid. DMA will not execute.", binaryInstructionString);
394                 return;
395             }
396 
397             debugInstruction.ParseAll(debugInstructionBytes);
398             ExecuteDebugInstruction(debugInstruction, (int)debugChannelNumber.Value, debugThreadType.Value);
399             debugStatus = false;
400         }
401 
ExecuteDebugInstruction(Instruction firstInstruction, int channelIndex, DMAThreadType threadType)402         private void ExecuteDebugInstruction(Instruction firstInstruction, int channelIndex, DMAThreadType threadType)
403         {
404             if(!(firstInstruction is DMAGO
405                 || firstInstruction is DMAKILL
406                 || firstInstruction is DMASEV))
407             {
408                 this.Log(LogLevel.Error, "Debug instruction \"{0}\" is not DMAGO, DMAKILL, DMASEV. It cannot be the first instruction.", firstInstruction.ToString());
409                 return;
410             }
411 
412             LogInstructionExecuted(firstInstruction, threadType, channelIndex);
413             // This is an instruction provided by the debug registers - it can't advance PC
414             firstInstruction.Execute(threadType, threadType != DMAThreadType.Manager ? (int?)channelIndex : null, suppressAdvance: true);
415             debugStatus = false;
416             ExecuteLoop();
417         }
418 
ExecuteLoop()419         private void ExecuteLoop()
420         {
421             // lock uses Monitor calls, and is re-entrant, so there will be no problems when executing on the same thread
422             lock(executeLock)
423             {
424                 // TODO: in case of infinite loop, this will hang the emulation.
425                 // It's not ideal - separate thread will be good, but what about time flow?
426                 // Still, it's enough in the beginning, for simple use cases
427                 do
428                 {
429                     foreach(var channelThread in channels.Where(c => c.Status == Channel.ChannelStatus.Executing))
430                     {
431                         this.Log(LogLevel.Debug, "Executing channel thread: {0}", channelThread.Id);
432 
433                         while(channelThread.Status == Channel.ChannelStatus.Executing)
434                         {
435                             var address = channelThread.PC;
436                             var insn = sysbus.ReadByte(address, context: GetCurrentCPUOrNull());
437                             if(!decoderRoot.TryParseOpcode(insn, out var instruction))
438                             {
439                                 this.Log(LogLevel.Error, "Invalid instruction with opcode 0x{0:X} at address: 0x{1:X}. Aborting thread {2}.", insn, address, channelThread.Id);
440                                 channelThread.SignalChannelAbort(Channel.ChannelFaultReason.UndefinedInstruction);
441                                 continue;
442                             }
443 
444                             while(!instruction.IsFinished)
445                             {
446                                 instruction.Parse(sysbus.ReadByte(address, context: GetCurrentCPUOrNull()));
447                                 address += sizeof(byte);
448                             }
449 
450                             LogInstructionExecuted(instruction, DMAThreadType.Channel, channelThread.Id, channelThread.PC);
451                             instruction.Execute(DMAThreadType.Channel, channelThread.Id);
452                         }
453                     }
454 
455                 // A channel might have become unpaused as a result of an event generated by another channel
456                 // As long as there are any channels in executing state, we have to retry
457                 } while(channels.Any(c => c.Status == Channel.ChannelStatus.Executing));
458             }
459         }
460 
LogInstructionExecuted(Instruction insn, DMAThreadType threadType, int threadId, ulong? address = null)461         private void LogInstructionExecuted(Instruction insn, DMAThreadType threadType, int threadId, ulong? address = null)
462         {
463             this.Log(LogLevel.Noisy, "[{0}] Executing: {1} {2}", threadType == DMAThreadType.Manager ? "M" : threadId.ToString(),
464                 insn.ToString(), address != null ? $"@ 0x{address:X}" : "" );
465         }
466 
GetCurrentCPUOrNull()467         private ICPU GetCurrentCPUOrNull()
468         {
469             if(!machine.SystemBus.TryGetCurrentCPU(out var cpu))
470             {
471                 return null;
472             }
473             return cpu;
474         }
475 
476         private IEnumRegisterField<DMAThreadType> debugThreadType;
477         private IValueRegisterField debugChannelNumber;
478         private IValueRegisterField debugInstructionByte0;
479         private IValueRegisterField debugInstructionByte1;
480         private IValueRegisterField debugInstructionByte2_3_4_5;
481         private IFlagRegisterField[] interruptEnabled;
482         // It's volatile, so this status is visible if several CPU threads will try to drive the DMA
483         private volatile bool debugStatus;
484         private readonly bool[] eventActive;
485         // Driving DMA from several cores at once has little sense
486         // but it's still possible that another core will drive a client peripheral requesting transfers
487         private readonly object executeLock = new object();
488 
489         private readonly Channel[] channels;
490 
491         private const int MaximumSupportedEventsOrInterrupts = 32;
492         private const int MaximumSupportedPeripheralRequestInterfaces = 32;
493 
494         private class Channel
495         {
Channel(PL330_DMA parent, int id)496             public Channel(PL330_DMA parent, int id)
497             {
498                 this.Parent = parent;
499                 this.Id = id;
500 
501                 Reset();
502                 DefineRegisters();
503             }
504 
Reset()505             public void Reset()
506             {
507                 ChannelControlRawValue = 0x00800200;
508                 PC = 0;
509                 SourceAddress = 0;
510                 DestinationAddress = 0;
511 
512                 status = ChannelStatus.Stopped;
513                 faultReason = ChannelFaultReason.NoFault;
514                 RequestType = ChannelRequestType.Single;
515                 RequestLast = false;
516                 WaitingEventOrPeripheralNumber = 0;
517                 Peripheral = null;
518 
519                 LoopCounter[0] = 0;
520                 LoopCounter[1] = 0;
521                 localMFIFO.Clear();
522             }
523 
DefineRegisters()524             private void DefineRegisters()
525             {
526                 (Registers.Channel0FaultType + Id * 4).Define(Parent)
527                     .WithEnumField<DoubleWordRegister, ChannelFaultReason>(0, 32, FieldMode.Read, valueProviderCallback: _ => faultReason, name: $"Channel {Id} Fault Reason");
528 
529                 (Registers.Channel0Status + Id * 8).Define(Parent)
530                     .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: _ => (ulong)Status, name: $"Channel {Id} Status")
531                     .WithValueField(4, 5, FieldMode.Read, valueProviderCallback: _ => WaitingEventOrPeripheralNumber, name: $"Channel {Id} Wakeup Number")
532                     .WithReservedBits(9, 5)
533                     .WithFlag(14, FieldMode.Read, valueProviderCallback: _ => RequestType == ChannelRequestType.Burst, name: "DMAWFP is burst set") // dmawfp_b_ns
534                     .WithTaggedFlag("DMAWFP is periph bit set", 15) // It the transfer type is driven by the peripheral
535                     .WithReservedBits(16, 5)
536                     .WithTaggedFlag("Channel Non Secure", 21)
537                     .WithReservedBits(22, 10);
538 
539                 (Registers.Channel0ProgramCounter + Id * 8).Define(Parent)
540                     .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => PC, name: $"Channel {Id} Program Counter");
541 
542                 (Registers.Channel0SourceAddress + Id * 0x20).Define(Parent)
543                     .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => SourceAddress, name: $"Channel {Id} Source Address");
544 
545                 (Registers.Channel0DestinationAddress + Id * 0x20).Define(Parent)
546                     .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => DestinationAddress, name: $"Channel {Id} Destination Address");
547 
548                 (Registers.Channel0Control + Id * 0x20).Define(Parent)
549                     .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => ChannelControlRawValue, name: $"Channel {Id} Control");
550 
551                 (Registers.Channel0LoopCounter0 + Id * 0x20).Define(Parent)
552                     .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => LoopCounter[0], name: $"Channel {Id} Loop Counter 0");
553 
554                 (Registers.Channel0LoopCounter1 + Id * 0x20).Define(Parent)
555                     .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => LoopCounter[1], name: $"Channel {Id} Loop Counter 1");
556             }
557 
558             public ulong PC { get; set; }
559 
560             public uint SourceAddress { get; set; }
561             public uint DestinationAddress { get; set; }
562             public uint ChannelControlRawValue
563             {
564                 get => channelControlRawValue;
565                 set
566                 {
567                     channelControlRawValue = value;
568 
569                     SourceIncrementingAddress = BitHelper.GetValue(value, 0, 1) == 1;
570                     SourceReadSize = 1 << (int)BitHelper.GetValue(value, 1, 3);
571                     SourceBurstLength = (int)BitHelper.GetValue(value, 4, 4) + 1;
572 
573                     DestinationIncrementingAddress = BitHelper.GetValue(value, 14, 1) == 1;
574                     DestinationWriteSize = 1 << (int)BitHelper.GetValue(value, 15, 3);
575                     DestinationBurstLength = (int)BitHelper.GetValue(value, 18, 4) + 1;
576 
577                     EndianSwapSize = 1 << (int)BitHelper.GetValue(value, 28, 3);
578                 }
579             }
580 
SignalChannelAbort(Channel.ChannelFaultReason reason)581             public void SignalChannelAbort(Channel.ChannelFaultReason reason)
582             {
583                 Parent.Log(LogLevel.Error, "Channel {0} is aborting because of {1}", Id, reason.ToString());
584                 // Changing status will automatically set Abort IRQ
585                 this.Status = Channel.ChannelStatus.Faulting;
586                 this.faultReason = reason;
587             }
588 
589             public ChannelStatus Status
590             {
591                 get => status;
592                 set
593                 {
594                     status = value;
595                     if(status != Channel.ChannelStatus.Faulting)
596                     {
597                         // If we are not Faulting then clear Fault Reason
598                         // since it could be set previously, when the channel aborted
599                         this.faultReason = ChannelFaultReason.NoFault;
600                     }
601                     Parent.UpdateAbortInterrupt();
602                 }
603             }
604 
605             // RequestType is part of Peripheral Request Interface (is set by `DMAWFP`)
606             public ChannelRequestType RequestType { get; set; }
607             // Whether it's a last request - this is set in peripheral transfers only, in infinite loop transfers
608             public bool RequestLast { get; set; }
609 
610             // Sizes are specified in bytes
611             public int SourceReadSize { get; private set; }
612             public int DestinationWriteSize { get; private set; }
613 
614             public int SourceBurstLength { get; private set; }
615             public int DestinationBurstLength { get; private set; }
616 
617             public bool SourceIncrementingAddress { get; private set; }
618             public bool DestinationIncrementingAddress { get; private set; }
619 
620             // In bytes
621             public int EndianSwapSize { get; private set; }
622 
623             // What event is the channel waiting for (after calling DMAWFE)
624             // We don't care about clearing this, since it only has meaning when the channel is in WaitingForEvent state
625             public uint WaitingEventOrPeripheralNumber { get; set; }
626             // Peripheral bound to the channel
627             public int? Peripheral { get; set; }
628 
629             public readonly int Id;
630             public readonly byte[] LoopCounter = new byte[2];
631             public readonly Queue<byte> localMFIFO = new Queue<byte>();
632 
633             private ChannelStatus status;
634             private ChannelFaultReason faultReason;
635             private uint channelControlRawValue;
636             private readonly PL330_DMA Parent;
637 
638             public enum ChannelStatus
639             {
640                 // The documentation enumerates more states, but let's reduce this number for now
641                 // for the implementation to be more manageable.
642                 // Also, some states have no meaning for us, as they are related to the operation of the bus
643                 Stopped = 0b0000,
644                 Executing = 0b0001,
645                 WaitingForEvent = 0b0100,
646                 WaitingForPeripheral = 0b0111,
647                 Faulting = 0b1111,
648             }
649 
650             [Flags]
651             public enum ChannelFaultReason
652             {
653                 NoFault = 0,
654                 UndefinedInstruction = 1 << 0,           // undef_instr
655                 InvalidOperand = 1 << 1,                 // operand_invalid
656                 EventInvalidSecurityState = 1 << 5,      // ch_evnt_err
657                 PeripheralInvalidSecurityState = 1 << 6, // ch_periph_err
658                 ChannelControlRegisterManipulationInvalidSecurityState = 1 << 7, // ch_rdwr_err
659                 OutOfSpaceInMFIFO = 1 << 12,          // mfifo_err
660                 NotEnoughStoredDataInMFIFO = 1 << 13, // st_data_unavailable
661                 InstructionFetchError = 1 << 16,      // instr_fetch_err
662                 DataWriteError = 1 << 17,             // data_write_err
663                 DataReadError = 1 << 18,              // data_read_err
664                 OriginatesFromDebugInstruction = 1 << 30, // dbg_instr
665                 LockupError = 1 << 31,                // lockup_err
666             }
667 
668             public enum ChannelRequestType
669             {
670                 Single = 0,
671                 Burst = 1
672             }
673         }
674 
675         private enum DMAThreadType
676         {
677             Manager = 0,
678             Channel = 1
679         }
680 
681         private enum Registers : long
682         {
683             DmaManagerStatus = 0x0,
684             DmaProgramCounter = 0x4,
685             DmaInterruptEnable = 0x20,
686             DmaEventInterruptRawStatus = 0x24,
687             DmaInterruptStatus = 0x28,
688             DmaInterruptClear = 0x2C,
689             FaultStatusDmaManager = 0x30,
690             FaultStatusDmaChannel = 0x34,
691             FaultTypeDmaManager = 0x38,
692 
693             Channel0FaultType = 0x40,
694             Channel1FaultType = 0x44,
695             Channel2FaultType = 0x48,
696             Channel3FaultType = 0x4C,
697             Channel4FaultType = 0x50,
698             Channel5FaultType = 0x54,
699             Channel6FaultType = 0x58,
700             Channel7FaultType = 0x5C,
701 
702             Channel0Status = 0x100,
703             Channel1Status = 0x108,
704             Channel2Status = 0x110,
705             Channel3Status = 0x118,
706             Channel4Status = 0x120,
707             Channel5Status = 0x128,
708             Channel6Status = 0x130,
709             Channel7Status = 0x138,
710 
711             Channel0ProgramCounter = 0x104,
712             Channel1ProgramCounter = 0x10C,
713             Channel2ProgramCounter = 0x114,
714             Channel3ProgramCounter = 0x11C,
715             Channel4ProgramCounter = 0x124,
716             Channel5ProgramCounter = 0x12C,
717             Channel6ProgramCounter = 0x134,
718             Channel7ProgramCounter = 0x13C,
719 
720             Channel0SourceAddress = 0x400,
721             Channel1SourceAddress = 0x420,
722             Channel2SourceAddress = 0x440,
723             Channel3SourceAddress = 0x460,
724             Channel4SourceAddress = 0x480,
725             Channel5SourceAddress = 0x4A0,
726             Channel6SourceAddress = 0x4C0,
727             Channel7SourceAddress = 0x4E0,
728 
729             Channel0DestinationAddress = 0x404,
730             Channel1DestinationAddress = 0x424,
731             Channel2DestinationAddress = 0x444,
732             Channel3DestinationAddress = 0x464,
733             Channel4DestinationAddress = 0x484,
734             Channel5DestinationAddress = 0x4A4,
735             Channel6DestinationAddress = 0x4C4,
736             Channel7DestinationAddress = 0x4E4,
737 
738             Channel0Control = 0x408,
739             Channel1Control = 0x428,
740             Channel2Control = 0x448,
741             Channel3Control = 0x468,
742             Channel4Control = 0x488,
743             Channel5Control = 0x4A8,
744             Channel6Control = 0x4C8,
745             Channel7Control = 0x4E8,
746 
747             Channel0LoopCounter0 = 0x40C,
748             Channel1LoopCounter0 = 0x42C,
749             Channel2LoopCounter0 = 0x44C,
750             Channel3LoopCounter0 = 0x46C,
751             Channel4LoopCounter0 = 0x48C,
752             Channel5LoopCounter0 = 0x4AC,
753             Channel6LoopCounter0 = 0x4CC,
754             Channel7LoopCounter0 = 0x4EC,
755 
756             Channel0LoopCounter1 = 0x410,
757             Channel1LoopCounter1 = 0x430,
758             Channel2LoopCounter1 = 0x450,
759             Channel3LoopCounter1 = 0x470,
760             Channel4LoopCounter1 = 0x490,
761             Channel5LoopCounter1 = 0x4B0,
762             Channel6LoopCounter1 = 0x4D0,
763             Channel7LoopCounter1 = 0x4F0,
764 
765             DebugStatus = 0xD00,
766             DebugCommand = 0xD04,
767             DebugInstruction0 = 0xD08,
768             DebugInstruction1 = 0xD0C,
769 
770             Configuration0 = 0xE00,
771             Configuration1 = 0xE04,
772             Configuration2 = 0xE08,
773             Configuration3 = 0xE0C,
774             Configuration4 = 0xE10,
775             DmaConfiguration = 0xE14,
776             // The watchdog is used to detect lock-ups - when there are not enough resources (MFIFO space) to complete a transfer.
777             // It can be a source of abort IRQ. For now, let's leave it unimplemented (2.8.3 Watchdog abort)
778             Watchdog = 0xE80,
779 
780             PeripheralIdentification0 = 0xFE0,
781             PeripheralIdentification1 = 0xFE4,
782             PeripheralIdentification2 = 0xFE8,
783             PeripheralIdentification3 = 0xFEC,
784             ComponentIdentification0 = 0xFF0,
785             ComponentIdentification1 = 0xFF4,
786             ComponentIdentification2 = 0xFF8,
787             ComponentIdentification3 = 0xFFC,
788         }
789     }
790 }
791