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.Text;
9 using System.Linq;
10 using System.Collections.Generic;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Utilities;
13 
14 namespace Antmicro.Renode.Peripherals.DMA
15 {
16     public partial class PL330_DMA
17     {
RegisterInstructions()18         private void RegisterInstructions()
19         {
20             decoderRoot.AddOpcode(0b01010100, 8, () => new DMAADH(this, isDestinationAddressRegister: false));
21             decoderRoot.AddOpcode(0b01010110, 8, () => new DMAADH(this, isDestinationAddressRegister: true));
22 
23             // DMAADNH should not be present in product revision r0p0
24             if(Revision > 0x0)
25             {
26                 decoderRoot.AddOpcode(0b01011100, 8, () => new DMAADNH(this, isDestinationAddressRegister: false));
27                 decoderRoot.AddOpcode(0b01011110, 8, () => new DMAADNH(this, isDestinationAddressRegister: true));
28             }
29 
30             decoderRoot.AddOpcode(0b00000000, 8, () => new DMAEND(this));
31 
32             decoderRoot.AddOpcode(0b10100000, 8, () => new DMAGO(this, nonSecure: false));
33             decoderRoot.AddOpcode(0b10100010, 8, () => new DMAGO(this, nonSecure: true));
34 
35             decoderRoot.AddOpcode(0b00000001, 8, () => new DMAKILL(this));
36             decoderRoot.AddOpcode(0b00110100, 8, () => new DMASEV(this));
37             decoderRoot.AddOpcode(0b00011000, 8, () => new DMANOP(this));
38             decoderRoot.AddOpcode(0b00110110, 8, () => new DMAWFE(this));
39             decoderRoot.AddOpcode(0b00010011, 8, () => new DMAWMB(this));
40             decoderRoot.AddOpcode(0b00010010, 8, () => new DMARMB(this));
41 
42             decoderRoot.AddOpcode(0b00000100, 8, () => new DMALD(this, isConditional: false));
43             decoderRoot.AddOpcode(0b00000101, 8, () => new DMALD(this, isConditional: true, transactionType: Channel.ChannelRequestType.Single));
44             decoderRoot.AddOpcode(0b00000111, 8, () => new DMALD(this, isConditional: true, transactionType: Channel.ChannelRequestType.Burst));
45 
46             decoderRoot.AddOpcode(0b00001000, 8, () => new DMAST(this, isConditional: false));
47             decoderRoot.AddOpcode(0b00001001, 8, () => new DMAST(this, isConditional: true, transactionType: Channel.ChannelRequestType.Single));
48             decoderRoot.AddOpcode(0b00001011, 8, () => new DMAST(this, isConditional: true, transactionType: Channel.ChannelRequestType.Burst));
49             decoderRoot.AddOpcode(0b00001100, 8, () => new DMASTZ(this));
50 
51             decoderRoot.AddOpcode(0b10111100, 8, () => new DMAMOV(this));
52 
53             decoderRoot.AddOpcode(0b00100000, 8, () => new DMALP(this, loopCounterIndex: 0));
54             decoderRoot.AddOpcode(0b00100010, 8, () => new DMALP(this, loopCounterIndex: 1));
55 
56             decoderRoot.AddOpcode(0b00111000, 8, () => new DMALPEND(this, isConditional: false));
57             decoderRoot.AddOpcode(0b00111100, 8, () => new DMALPEND(this, isConditional: false, loopCounterIndex: 1));
58             decoderRoot.AddOpcode(0b00101100, 8, () => new DMALPEND(this, isConditional: false, loopCounterIndex: 1, isForever: true));
59             decoderRoot.AddOpcode(0b00111001, 8, () => new DMALPEND(this, isConditional: true, transactionType: Channel.ChannelRequestType.Single));
60             decoderRoot.AddOpcode(0b00111101, 8, () => new DMALPEND(this, isConditional: true, transactionType: Channel.ChannelRequestType.Single, loopCounterIndex: 1));
61             decoderRoot.AddOpcode(0b00101101, 8, () => new DMALPEND(this, isConditional: true, transactionType: Channel.ChannelRequestType.Single, loopCounterIndex: 1, isForever: true));
62             decoderRoot.AddOpcode(0b00111011, 8, () => new DMALPEND(this, isConditional: true, transactionType: Channel.ChannelRequestType.Burst));
63             decoderRoot.AddOpcode(0b00111111, 8, () => new DMALPEND(this, isConditional: true, transactionType: Channel.ChannelRequestType.Burst, loopCounterIndex: 1));
64             decoderRoot.AddOpcode(0b00101111, 8, () => new DMALPEND(this, isConditional: true, transactionType: Channel.ChannelRequestType.Burst, loopCounterIndex: 1 , isForever: true));
65 
66             decoderRoot.AddOpcode(0b00110101, 8, () => new DMAFLUSHP(this));
67             decoderRoot.AddOpcode(0b00110001, 8, () => new DMAWFP(this, isPeripheralDriven: true));
68             decoderRoot.AddOpcode(0b00110000, 8, () => new DMAWFP(this, isPeripheralDriven: false, transactionType: Channel.ChannelRequestType.Single));
69             decoderRoot.AddOpcode(0b00110010, 8, () => new DMAWFP(this, isPeripheralDriven: false, transactionType: Channel.ChannelRequestType.Burst));
70 
71             decoderRoot.AddOpcode(0b00100101, 8, () => new DMALDP(this, transactionType: Channel.ChannelRequestType.Single));
72             decoderRoot.AddOpcode(0b00100111, 8, () => new DMALDP(this, transactionType: Channel.ChannelRequestType.Burst));
73 
74             decoderRoot.AddOpcode(0b00101001, 8, () => new DMASTP(this, transactionType: Channel.ChannelRequestType.Single));
75             decoderRoot.AddOpcode(0b00101011, 8, () => new DMASTP(this, transactionType: Channel.ChannelRequestType.Burst));
76         }
77 
78         private SimpleInstructionDecoder<Instruction> decoderRoot = new SimpleInstructionDecoder<Instruction>();
79 
80         private abstract class Instruction
81         {
ParseAll(ulong value)82             public void ParseAll(ulong value)
83             {
84                 if(Length > sizeof(ulong))
85                 {
86                     // Quite unlikely, since the instructions have at most 48 bits total
87                     throw new ArgumentException($"Expected instruction length: {Length} is greater than the size of provided value");
88                 }
89 
90                 while(!IsFinished)
91                 {
92                     Parse((byte)value);
93                     value >>= 8;
94                 }
95             }
96 
Parse(byte value)97             public void Parse(byte value)
98             {
99                 if(IsFinished)
100                 {
101                     return;
102                 }
103 
104                 currentByteCount++;
105                 instructionBytes.Add(value);
106 
107                 if(IsFinished)
108                 {
109                     ParseCompleteAction();
110                 }
111             }
112 
Execute(DMAThreadType threadType, int? channelIndex = null, bool suppressAdvance = false)113             public void Execute(DMAThreadType threadType, int? channelIndex = null, bool suppressAdvance = false)
114             {
115                 if(threadType == DMAThreadType.Manager && channelIndex != null)
116                 {
117                     throw new InvalidOperationException("Thread is a manager, but channel given");
118                 }
119                 if(threadType == DMAThreadType.Channel && channelIndex == null)
120                 {
121                     throw new InvalidOperationException("Thread is a channel, but no channel given");
122                 }
123 
124                 ulong offset = ExecuteIfCorrectThread(threadType, channelIndex);
125 
126                 // Automatically advance PC by offset (usually instruction length) if offset > 0
127                 // Don't do it by length automatically, since there exist instructions that change PC explicitly
128                 if(!suppressAdvance && offset > 0)
129                 {
130                     if(threadType == DMAThreadType.Channel)
131                     {
132                         Parent.channels[channelIndex.Value].PC += offset;
133                     }
134                     else
135                     {
136                         Parent.Log(LogLevel.Error, "DMA Manager thread behavior is unimplemented.");
137                     }
138                 }
139             }
140 
ToString()141             public override string ToString()
142             {
143                 StringBuilder bits = new StringBuilder("");
144                 foreach(var b in instructionBytes.Reverse())
145                 {
146                     bits.Append(Convert.ToString(b, 2).PadLeft(8, '0'));
147                 }
148                 return $"{Name}" + (bits.Length > 0 ? $" [{bits}]" : "");
149             }
150 
151             public string Name { get; }
152             public bool IsFinished
153             {
154                 get => currentByteCount == Length;
155             }
156 
Instruction(PL330_DMA parent, uint length = 1, bool usableByChannel = true, bool usableByManager = false)157             protected Instruction(PL330_DMA parent, uint length = 1, bool usableByChannel = true, bool usableByManager = false)
158             {
159                 this.usableByManager = usableByManager;
160                 this.usableByChannel = usableByChannel;
161                 this.Length = length;
162                 this.Parent = parent;
163 
164                 Name = GetType().Name;
165             }
166 
ParseCompleteAction()167             protected virtual void ParseCompleteAction() {}
168 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)169             protected virtual ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
170             {
171                 Parent.Log(LogLevel.Error, "Instruction \"{0}\" is not implemented. Skipping it!", Name);
172                 return Length;
173             }
174 
ExecuteIfCorrectThread(DMAThreadType threadType, int? channelIndex)175             private ulong ExecuteIfCorrectThread(DMAThreadType threadType, int? channelIndex)
176             {
177                 if(threadType == DMAThreadType.Manager && !usableByManager)
178                 {
179                     Parent.Log(LogLevel.Error, "Thread is a manager, but instruction {0} not usable by manager", this.ToString());
180                     // TODO: We should abort manager thread here, but its logic is unimplemented now
181                     return Length;
182                 }
183                 if(threadType == DMAThreadType.Channel && !usableByChannel)
184                 {
185                     Parent.Log(LogLevel.Error, "Thread is a channel, but instruction {0} not usable by channel", this.ToString());
186                     // The docs are not clear about what happens here, but UndefinedInstruction seems logical
187                     Parent.channels[channelIndex.Value].SignalChannelAbort(Channel.ChannelFaultReason.UndefinedInstruction);
188                     return Length;
189                 }
190                 return ExecuteInner(threadType, channelIndex);
191             }
192 
193             protected readonly uint Length;
194 
195             protected int currentByteCount;
196             protected IList<byte> instructionBytes = new List<byte>();
197 
198             protected readonly PL330_DMA Parent;
199             private readonly bool usableByManager;
200             private readonly bool usableByChannel;
201         }
202 
203         private abstract class DMAADH_base : Instruction
204         {
DMAADH_base(PL330_DMA parent, bool isDestinationAddressRegister, bool negative)205             public DMAADH_base(PL330_DMA parent, bool isDestinationAddressRegister, bool negative) : base(parent, length: 3)
206             {
207                 this.negative = negative;
208                 this.isDestinationAddressRegister = isDestinationAddressRegister;
209             }
210 
ParseCompleteAction()211             protected override void ParseCompleteAction()
212             {
213                 immediate = (ushort)((instructionBytes[1] << 8) | instructionBytes[0]);
214             }
215 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)216             protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
217             {
218                 unchecked
219                 {
220                     if(isDestinationAddressRegister)
221                     {
222                         if(!negative)
223                         {
224                             Parent.channels[channelIndex.Value].DestinationAddress += immediate;
225                         }
226                         else
227                         {
228                             Parent.channels[channelIndex.Value].DestinationAddress -= immediate;
229                         }
230                     }
231                     else
232                     {
233                         if(!negative)
234                         {
235                             Parent.channels[channelIndex.Value].SourceAddress += immediate;
236                         }
237                         else
238                         {
239                             Parent.channels[channelIndex.Value].SourceAddress -= immediate;
240                         }
241                     }
242                 }
243                 return Length;
244             }
245 
246             private ushort immediate;
247 
248             private readonly bool negative;
249             private readonly bool isDestinationAddressRegister;
250         }
251 
252         private class DMAADH : DMAADH_base
253         {
DMAADH(PL330_DMA parent, bool isDestinationAddressRegister)254             public DMAADH(PL330_DMA parent, bool isDestinationAddressRegister) : base(parent, isDestinationAddressRegister, negative: false) {}
255         }
256 
257         private class DMAADNH : DMAADH_base
258         {
DMAADNH(PL330_DMA parent, bool isDestinationAddressRegister)259             public DMAADNH(PL330_DMA parent, bool isDestinationAddressRegister) : base(parent, isDestinationAddressRegister, negative: true) {}
260         }
261 
262         private class DMAEND : Instruction
263         {
DMAEND(PL330_DMA parent)264             public DMAEND(PL330_DMA parent) : base(parent, usableByManager: true) {}
265 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)266             protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
267             {
268                 if(threadType == DMAThreadType.Manager)
269                 {
270                     Parent.Log(LogLevel.Error, "DMAEND is currently not supported for manager");
271                 }
272                 else
273                 {
274                     var selectedChannel = Parent.channels[channelIndex.Value];
275                     selectedChannel.Status = Channel.ChannelStatus.Stopped;
276                     selectedChannel.localMFIFO.Clear();
277                     selectedChannel.Peripheral = null;
278                 }
279                 return Length;
280             }
281         }
282 
283         private class DMAGO : Instruction
284         {
DMAGO(PL330_DMA parent, bool nonSecure)285             public DMAGO(PL330_DMA parent, bool nonSecure) : base(parent, length: 6, usableByChannel: false, usableByManager: true)
286             {
287                 this.nonSecure = nonSecure;
288             }
289 
ParseCompleteAction()290             protected override void ParseCompleteAction()
291             {
292                 channelNumber = instructionBytes[1] & 0b111;
293 
294                 foreach(var b in instructionBytes.Reverse().Take(4))
295                 {
296                     programCounter <<= 8;
297                     programCounter |= b;
298                 }
299             }
300 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)301             protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
302             {
303                 var channel = Parent.channels[channelNumber];
304 
305                 if(channel.Status != Channel.ChannelStatus.Stopped)
306                 {
307                     Parent.Log(LogLevel.Debug, "This channel is in state: {0}, DMAGO treated as NOP.", channel.Status.ToString());
308                     return Length;
309                 }
310                 if(nonSecure)
311                 {
312                     Parent.Log(LogLevel.Warning, "Non-secure bit is ignored, value: {0}", nonSecure);
313                 }
314 
315                 channel.PC = programCounter;
316                 channel.Status = Channel.ChannelStatus.Executing;
317 
318                 return 0;
319             }
320 
321             private int channelNumber;
322             // The immediate value, used to set nth's channel's PC
323             private uint programCounter;
324             // Used to force channel into Non-Secure mode. We don't support secure/non-secure mode, so it's ignored
325             private readonly bool nonSecure;
326         }
327 
328         private class DMAKILL : Instruction
329         {
DMAKILL(PL330_DMA parent)330             public DMAKILL(PL330_DMA parent) : base(parent, usableByManager: true) {}
331 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)332             protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
333             {
334                 if(threadType == DMAThreadType.Manager)
335                 {
336                     Parent.Log(LogLevel.Error, "KILL on Manager thread is currently unsupported");
337                 }
338                 else
339                 {
340                     var selectedChannel = Parent.channels[channelIndex.Value];
341                     selectedChannel.localMFIFO.Clear();
342                     selectedChannel.Status = Channel.ChannelStatus.Stopped;
343                     selectedChannel.Peripheral = null;
344                 }
345                 return Length;
346             }
347         }
348 
349         private abstract class DMA_LD_ST_base : Instruction
350         {
DMA_LD_ST_base(PL330_DMA parent, bool IsConditional, Channel.ChannelRequestType TransactionType, uint length = 1)351             public DMA_LD_ST_base(PL330_DMA parent, bool IsConditional, Channel.ChannelRequestType TransactionType, uint length = 1)
352                 : base(parent, length)
353             {
354                 this.isConditional = IsConditional;
355                 this.transactionType = TransactionType;
356             }
357 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)358             protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
359             {
360                 if(!isConditional)
361                 {
362                     DoTransfer(channelIndex.Value, false);
363                     return Length;
364                 }
365 
366                 var requestType = Parent.channels[channelIndex.Value].RequestType;
367                 if(requestType == transactionType)
368                 {
369                     DoTransfer(channelIndex.Value, requestType == Channel.ChannelRequestType.Single);
370                     return Length;
371                 }
372 
373                 // Treat as NOP
374                 return Length;
375             }
376 
DoEndianSwap(Channel selectedChannel, int dataLengthToSwap)377             protected void DoEndianSwap(Channel selectedChannel, int dataLengthToSwap)
378             {
379                 byte[] bytesToSwap = selectedChannel.localMFIFO.DequeueRange(dataLengthToSwap);
380                 byte[] bytesRemaining = selectedChannel.localMFIFO.DequeueAll();
381 
382                 if(bytesToSwap.Length % selectedChannel.EndianSwapSize != 0)
383                 {
384                     // Not sure what happens here - the docs recommend to avoid this state
385                     // but there is no mention if DMA should abort now
386                     Parent.Log(LogLevel.Error, "Number of bytes requested for transfer: {0} is not a multiple of EndianSwapSize: {1}", bytesToSwap.Length, selectedChannel.EndianSwapSize);
387                 }
388 
389                 for(int i = 0; i < bytesToSwap.Length; i += selectedChannel.EndianSwapSize)
390                 {
391                     Array.Reverse(bytesToSwap, i, Math.Min(selectedChannel.EndianSwapSize, bytesToSwap.Length - i));
392                 }
393                 // Restore contents of the thread's buffer
394                 selectedChannel.localMFIFO.EnqueueRange(bytesToSwap);
395                 selectedChannel.localMFIFO.EnqueueRange(bytesRemaining);
396             }
397 
DoTransfer(int channelIndex, bool ignoreBurst)398             protected abstract void DoTransfer(int channelIndex, bool ignoreBurst);
399 
400             private readonly bool isConditional;
401             private readonly Channel.ChannelRequestType transactionType;
402         }
403 
404         private class DMALD : DMA_LD_ST_base
405         {
DMALD(PL330_DMA parent, bool isConditional, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)406             public DMALD(PL330_DMA parent, bool isConditional, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)
407                 : base(parent, isConditional, transactionType) {}
408 
DoTransfer(int channelIndex, bool ignoreBurst)409             protected override void DoTransfer(int channelIndex, bool ignoreBurst)
410             {
411                 var selectedChannel = Parent.channels[channelIndex];
412                 var readLength = selectedChannel.SourceReadSize;
413 
414                 for(var burst = 0; burst < (ignoreBurst ? 1 : selectedChannel.SourceBurstLength); ++burst)
415                 {
416                     byte[] byteArray = Parent.machine.GetSystemBus(Parent).ReadBytes(selectedChannel.SourceAddress, readLength, context: Parent.GetCurrentCPUOrNull());
417                     selectedChannel.localMFIFO.EnqueueRange(byteArray);
418 
419                     if(selectedChannel.SourceIncrementingAddress)
420                     {
421                         selectedChannel.SourceAddress += (uint)readLength;
422                     }
423                 }
424             }
425         }
426 
427         private class DMAST : DMA_LD_ST_base
428         {
DMAST(PL330_DMA parent, bool isConditional, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)429             public DMAST(PL330_DMA parent, bool isConditional, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)
430                 : base(parent, isConditional, transactionType) {}
431 
DoTransfer(int channelIndex, bool ignoreBurst)432             protected override void DoTransfer(int channelIndex, bool ignoreBurst)
433             {
434                 var selectedChannel = Parent.channels[channelIndex];
435                 var writeLength = selectedChannel.DestinationWriteSize;
436                 var burstLength = ignoreBurst ? 1 : selectedChannel.DestinationBurstLength;
437 
438                 // If requested, swap endianness of data in buffer, just before the transmission
439                 if(selectedChannel.EndianSwapSize > 1)
440                 {
441                     DoEndianSwap(selectedChannel, writeLength * burstLength);
442                 }
443 
444                 for(var burst = 0; burst < burstLength; ++burst)
445                 {
446                     byte[] byteArray = selectedChannel.localMFIFO.DequeueRange(writeLength);
447                     if(byteArray.Length != writeLength)
448                     {
449                         Parent.Log(LogLevel.Error, "Underflow in channel queue, {0} bytes remaining in FIFO, but requested to write {1}. Aborting thread.", byteArray.Length, writeLength);
450                         selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.NotEnoughStoredDataInMFIFO);
451                         return;
452                     }
453                     Parent.machine.GetSystemBus(Parent).WriteBytes(byteArray, selectedChannel.DestinationAddress, context: Parent.GetCurrentCPUOrNull());
454 
455                     if(selectedChannel.DestinationIncrementingAddress)
456                     {
457                         selectedChannel.DestinationAddress += (uint)writeLength;
458                     }
459                 }
460             }
461         }
462 
463         private class DMASTZ : DMA_LD_ST_base
464         {
DMASTZ(PL330_DMA parent)465             public DMASTZ(PL330_DMA parent) : base(parent, IsConditional: false, TransactionType: Channel.ChannelRequestType.Single) {}
466 
DoTransfer(int channelIndex, bool _)467             protected override void DoTransfer(int channelIndex, bool _)
468             {
469                 var selectedChannel = Parent.channels[channelIndex];
470                 var writeLength = selectedChannel.DestinationWriteSize;
471 
472                 for(var burst = 0; burst < selectedChannel.DestinationBurstLength; ++burst)
473                 {
474                     Parent.sysbus.WriteBytes(Enumerable.Repeat((byte)0, writeLength).ToArray(), selectedChannel.DestinationAddress, context: Parent.GetCurrentCPUOrNull());
475 
476                     if(selectedChannel.DestinationIncrementingAddress)
477                     {
478                         selectedChannel.DestinationAddress += (uint)writeLength;
479                     }
480                 }
481             }
482         }
483 
484 
485         private abstract class DMANOP_base : Instruction
486         {
DMANOP_base(PL330_DMA parent, uint length, bool usableByChannel = true, bool usableByManager = false)487             public DMANOP_base(PL330_DMA parent, uint length, bool usableByChannel = true, bool usableByManager = false)
488                 : base(parent, length, usableByChannel, usableByManager)
489             {}
490 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)491             protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
492             {
493                 // NOP - intentionally no action
494                 return Length;
495             }
496         }
497 
498         private class DMANOP : DMANOP_base
499         {
DMANOP(PL330_DMA parent)500             public DMANOP(PL330_DMA parent) : base(parent, length: 1, usableByManager: true) {}
501         }
502 
503         private class DMAWMB : DMANOP_base
504         {
505             // Write memory barrier - treat as NOP
506             // for us each load/store instruction has immediate result
507             // so there should be no need for a barrier operation
DMAWMB(PL330_DMA parent)508             public DMAWMB(PL330_DMA parent) : base(parent, length: 1) {}
509         }
510 
511         private class DMARMB : DMANOP_base
512         {
513             // See: DMAWMB for explanation
DMARMB(PL330_DMA parent)514             public DMARMB(PL330_DMA parent) : base(parent, length: 1) {}
515         }
516 
517 
518         private class DMASEV : Instruction
519         {
DMASEV(PL330_DMA parent)520             public DMASEV(PL330_DMA parent) : base(parent, length: 2, usableByManager: true) {}
521 
ParseCompleteAction()522             protected override void ParseCompleteAction()
523             {
524                 eventNumber = (uint)instructionBytes[1] >> 3;
525             }
526 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)527             protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
528             {
529                 if(threadType == DMAThreadType.Manager)
530                 {
531                     Parent.Log(LogLevel.Error, "SEV on Manager thread is currently unsupported");
532                 }
533                 else
534                 {
535                     if(!Parent.SignalEventOrInterrupt(eventNumber))
536                     {
537                         Parent.channels[channelIndex.Value].SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand);
538                     }
539                 }
540                 return Length;
541             }
542 
543             private uint eventNumber;
544         }
545 
546 
547         private class DMAWFE : Instruction
548         {
DMAWFE(PL330_DMA parent)549             public DMAWFE(PL330_DMA parent) : base(parent, length: 2, usableByManager: true) {}
550 
ParseCompleteAction()551             protected override void ParseCompleteAction()
552             {
553                 eventNumber = (uint)instructionBytes[1] >> 3;
554                 invalid = ((instructionBytes[1] >> 1) & 0x1) == 1;
555             }
556 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)557             protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
558             {
559                 if(threadType == DMAThreadType.Manager)
560                 {
561                     Parent.Log(LogLevel.Error, "WFE on Manager thread is currently unsupported");
562                 }
563                 else
564                 {
565                     var selectedChannel = Parent.channels[channelIndex.Value];
566 
567                     if(!Parent.IsEventOrInterruptValid(eventNumber))
568                     {
569                         selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand);
570                         return Length;
571                     }
572 
573                     if(Parent.eventActive[eventNumber])
574                     {
575                         // If the event was pending before, let's deactivate it and continue execution normally
576                         Parent.eventActive[eventNumber] = false;
577                     }
578                     else
579                     {
580                         // If the event is not active, then let's wait
581                         selectedChannel.WaitingEventOrPeripheralNumber = eventNumber;
582                         selectedChannel.Status = Channel.ChannelStatus.WaitingForEvent;
583                         Parent.Log(LogLevel.Noisy, "DMAWFE: Channel {0} is waiting for event: {1}", selectedChannel.Id, eventNumber);
584                     }
585                 }
586                 return Length;
587             }
588 
589             private uint eventNumber;
590             // Invalid bit is used to force DMAC to invalidate its icache - we don't have any cache implemented
591             private bool invalid;
592         }
593 
594         private class DMAMOV : Instruction
595         {
DMAMOV(PL330_DMA parent)596             public DMAMOV(PL330_DMA parent) : base(parent, length: 6) {}
597 
ParseCompleteAction()598             protected override void ParseCompleteAction()
599             {
600                 registerNumber = instructionBytes[1] & 0b111;
601                 foreach(var b in instructionBytes.Reverse().Take(4))
602                 {
603                     immediate <<= 8;
604                     immediate |= b;
605                 }
606             }
607 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)608             protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
609             {
610                 var selectedChannel = Parent.channels[channelIndex.Value];
611                 switch(registerNumber)
612                 {
613                     case 0b000: // SAR
614                         selectedChannel.SourceAddress = immediate;
615                         break;
616                     case 0b001: // CCR
617                         selectedChannel.ChannelControlRawValue = immediate;
618                         break;
619                     case 0b010: // DAR
620                         selectedChannel.DestinationAddress = immediate;
621                         break;
622                     default:
623                         Parent.Log(LogLevel.Error, "Invalid destination bits: {0}", registerNumber);
624                         selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand);
625                         break;
626                 }
627                 return Length;
628             }
629 
630             private uint immediate;
631             private int registerNumber;
632         }
633 
634         private class DMALP : Instruction
635         {
DMALP(PL330_DMA parent, int loopCounterIndex)636             public DMALP(PL330_DMA parent, int loopCounterIndex) : base(parent, length: 2)
637             {
638                 this.loopCounterIndex = loopCounterIndex;
639             }
640 
ParseCompleteAction()641             protected override void ParseCompleteAction()
642             {
643                 loopIterations = instructionBytes[1];
644             }
645 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)646             protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
647             {
648                 // There is no need to save loop start address here, it's done by assembler at code generation time in DMALPEND
649                 var selectedChannel = Parent.channels[channelIndex.Value];
650                 selectedChannel.LoopCounter[loopCounterIndex] = loopIterations;
651                 return Length;
652             }
653 
654             private byte loopIterations;
655 
656             private readonly int loopCounterIndex;
657         }
658 
659         private class DMALPEND : Instruction
660         {
DMALPEND(PL330_DMA parent, bool isConditional = false, bool isForever = false, int loopCounterIndex = 0, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)661             public DMALPEND(PL330_DMA parent, bool isConditional = false, bool isForever = false, int loopCounterIndex = 0, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single) : base(parent, length: 2)
662             {
663                 this.isConditional = isConditional;
664                 this.isForever = isForever;
665                 this.loopCounterIndex = loopCounterIndex;
666                 this.transactionType = transactionType;
667             }
668 
ParseCompleteAction()669             protected override void ParseCompleteAction()
670             {
671                 backwardsJump = instructionBytes[1];
672             }
673 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)674             protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
675             {
676                 if(!isConditional)
677                 {
678                     return DoJump(channelIndex.Value);
679                 }
680 
681                 var requestType = Parent.channels[channelIndex.Value].RequestType;
682                 if(requestType == transactionType)
683                 {
684                     return DoJump(channelIndex.Value);
685                 }
686 
687                 // Treat as NOP
688                 return Length;
689             }
690 
DoJump(int channelIndex)691             private ulong DoJump(int channelIndex)
692             {
693                 var channel = Parent.channels[channelIndex];
694 
695                 bool shouldExitLoop = (!isForever && (channel.LoopCounter[loopCounterIndex] == 0))
696                                         || (isForever && channel.RequestLast);
697                 if(shouldExitLoop)
698                 {
699                     return Length;
700                 }
701 
702                 channel.PC -= backwardsJump;
703 
704                 if(!isForever)
705                 {
706                     --channel.LoopCounter[loopCounterIndex];
707                 }
708                 return 0;
709             }
710 
711             private byte backwardsJump;
712 
713             private readonly bool isConditional;
714             private readonly bool isForever;
715             private readonly int loopCounterIndex;
716             private readonly Channel.ChannelRequestType transactionType;
717         }
718 
719         private class DMAFLUSHP : Instruction
720         {
721             // We don't model FLUSHP requests to the peripheral
722             // but let's use it to bind a peripheral to a channel
723             // this information will be cleared on END or KILL
DMAFLUSHP(PL330_DMA parent)724             public DMAFLUSHP(PL330_DMA parent) : base(parent, length: 2) {}
725 
ParseCompleteAction()726             protected override void ParseCompleteAction()
727             {
728                 peripheral = (byte)(instructionBytes[1] >> 3);
729             }
730 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)731             protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
732             {
733                 var selectedChannel = Parent.channels[channelIndex.Value];
734 
735                 if(!Parent.IsPeripheralInterfaceValid(peripheral))
736                 {
737                     selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand);
738                     return Length;
739                 }
740                 selectedChannel.Peripheral = peripheral;
741 
742                 return Length;
743             }
744 
745             private byte peripheral;
746         }
747 
748         private class DMAWFP : Instruction
749         {
DMAWFP(PL330_DMA parent, bool isPeripheralDriven = false, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)750             public DMAWFP(PL330_DMA parent, bool isPeripheralDriven = false, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single) : base(parent, length: 2)
751             {
752                 this.isPeripheralDriven = isPeripheralDriven;
753                 this.transactionType = transactionType;
754             }
755 
ParseCompleteAction()756             protected override void ParseCompleteAction()
757             {
758                 peripheral = (byte)(instructionBytes[1] >> 3);
759             }
760 
ExecuteInner(DMAThreadType threadType, int? channelIndex = null)761             protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null)
762             {
763                 var selectedChannel = Parent.channels[channelIndex.Value];
764 
765                 if(!Parent.IsPeripheralInterfaceValid(peripheral))
766                 {
767                     selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand);
768                     return Length;
769                 }
770                 selectedChannel.Peripheral = peripheral;
771 
772                 if(isPeripheralDriven)
773                 {
774                     // This feature is not yet implemented in this model
775                     // treat it as invalid operand case, so a driver can kill the DMA thread and potentially recover
776                     Parent.Log(LogLevel.Error, "DMAWFP: Channel {0}, waiting for peripheral: {1}, cannot be peripheral driven (periph bit set) - this is not yet supported. Aborting thread.", selectedChannel.Id, peripheral);
777                     selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand);
778                     return Length;
779                 }
780 
781                 // Wait for peripheral here
782                 selectedChannel.WaitingEventOrPeripheralNumber = peripheral;
783                 selectedChannel.Status = Channel.ChannelStatus.WaitingForPeripheral;
784 
785                 // Since we don't support `periph` we operate under the simplified assumption
786                 // that we will be woken up by the correct transfer type from the peripheral
787                 // this might not always be correct
788                 selectedChannel.RequestType = transactionType;
789                 selectedChannel.RequestLast = false;
790 
791                 Parent.Log(LogLevel.Noisy, "DMAWFP: Channel {0} is waiting for peripheral: {1}", selectedChannel.Id, peripheral);
792                 return Length;
793             }
794 
795             private byte peripheral;
796 
797             private readonly bool isPeripheralDriven;
798             private readonly Channel.ChannelRequestType transactionType;
799         }
800 
801         private class DMALDP : DMA_LD_ST_base
802         {
DMALDP(PL330_DMA parent, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)803             public DMALDP(PL330_DMA parent, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)
804                 : base(parent, true, transactionType, length: 2)
805             {
806                 this.transactionType = transactionType;
807             }
808 
ParseCompleteAction()809             protected override void ParseCompleteAction()
810             {
811                 peripheral = (byte)(instructionBytes[1] >> 3);
812             }
813 
DoTransfer(int channelIndex, bool ignoreBurst)814             protected override void DoTransfer(int channelIndex, bool ignoreBurst)
815             {
816                 var selectedChannel = Parent.channels[channelIndex];
817                 if(!Parent.IsPeripheralInterfaceValid(peripheral))
818                 {
819                     selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand);
820                     return;
821                 }
822                 selectedChannel.Peripheral = peripheral;
823 
824                 var dmaEngine = new DmaEngine(Parent.machine.GetSystemBus(Parent));
825                 var readLengthInBytes = selectedChannel.SourceBurstLength * selectedChannel.SourceReadSize;
826 
827                 var bufferPlace = new Place(new byte[readLengthInBytes], 0);
828 
829                 var request = new Request(
830                     selectedChannel.SourceAddress,
831                     bufferPlace,
832                     readLengthInBytes,
833                     (TransferType)selectedChannel.SourceReadSize,
834                     (TransferType)selectedChannel.DestinationWriteSize,
835                     selectedChannel.SourceIncrementingAddress,
836                     selectedChannel.DestinationIncrementingAddress
837                 );
838                 var response = dmaEngine.IssueCopy(request, Parent.GetCurrentCPUOrNull());
839 
840                 // Update address, if it was incrementing
841                 selectedChannel.SourceAddress = (uint)response.ReadAddress.Value;
842                 selectedChannel.localMFIFO.EnqueueRange(bufferPlace.Array);
843             }
844 
845             private byte peripheral;
846 
847             private readonly Channel.ChannelRequestType transactionType;
848         }
849 
850         private class DMASTP : DMA_LD_ST_base
851         {
DMASTP(PL330_DMA parent, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)852             public DMASTP(PL330_DMA parent, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)
853                 : base(parent, true, transactionType, length: 2)
854             {
855                 this.transactionType = transactionType;
856             }
857 
ParseCompleteAction()858             protected override void ParseCompleteAction()
859             {
860                 peripheral = (byte)(instructionBytes[1] >> 3);
861             }
862 
DoTransfer(int channelIndex, bool ignoreBurst)863             protected override void DoTransfer(int channelIndex, bool ignoreBurst)
864             {
865                 var selectedChannel = Parent.channels[channelIndex];
866                 if(!Parent.IsPeripheralInterfaceValid(peripheral))
867                 {
868                     selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand);
869                     return;
870                 }
871                 selectedChannel.Peripheral = peripheral;
872 
873                 var dmaEngine = new DmaEngine(Parent.machine.GetSystemBus(Parent));
874                 var writeLengthInBytes = selectedChannel.DestinationBurstLength * selectedChannel.DestinationWriteSize;
875 
876                 // If requested, swap endianness of data in buffer, just before the transmission
877                 if(selectedChannel.EndianSwapSize > 1)
878                 {
879                     DoEndianSwap(selectedChannel, writeLengthInBytes);
880                 }
881 
882                 var bufferPlace = new Place(selectedChannel.localMFIFO.DequeueRange(writeLengthInBytes), 0);
883 
884                 var request = new Request(
885                     bufferPlace,
886                     selectedChannel.DestinationAddress,
887                     writeLengthInBytes,
888                     (TransferType)selectedChannel.SourceReadSize,
889                     (TransferType)selectedChannel.DestinationWriteSize,
890                     selectedChannel.SourceIncrementingAddress,
891                     selectedChannel.DestinationIncrementingAddress
892                 );
893                 var response = dmaEngine.IssueCopy(request, Parent.GetCurrentCPUOrNull());
894 
895                 // Update address, if it was incrementing
896                 selectedChannel.DestinationAddress = (uint)response.WriteAddress.Value;
897             }
898 
899             private byte peripheral;
900 
901             private readonly Channel.ChannelRequestType transactionType;
902         }
903 
904     }
905 }
906