1 //
2 // Copyright (c) 2010-2025 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals;
14 using Antmicro.Renode.Peripherals.Bus;
15 using Antmicro.Renode.Utilities;
16 
17 namespace Antmicro.Renode.Peripherals.DMA
18 {
19     public interface ISamPdcPeripheral : IProvidesRegisterCollection<DoubleWordRegisterCollection>, IBusPeripheral
20     {
21         TransferType DmaReadAccessWidth { get; }
22         TransferType DmaWriteAccessWidth { get; }
23     }
24 
25     public interface ISamPdcBytePeripheral : ISamPdcPeripheral
26     {
DmaByteRead()27         byte? DmaByteRead();
DmaByteWrite(byte data)28         void DmaByteWrite(byte data);
29     }
30 
31     public interface ISamPdcBlockBytePeripheral : ISamPdcPeripheral
32     {
DmaBlockByteRead(int count)33         byte[] DmaBlockByteRead(int count);
DmaBlockByteWrite(byte[] data)34         void DmaBlockByteWrite(byte[] data);
35     }
36 
37     public interface ISamPdcWordPeripheral : ISamPdcPeripheral
38     {
DmaWordRead()39         ushort? DmaWordRead();
DmaWordWrite(ushort data)40         void DmaWordWrite(ushort data);
41     }
42 
43     public interface ISamPdcDoubleWordPeripheral : ISamPdcPeripheral
44     {
DmaDoubleWordRead()45         uint? DmaDoubleWordRead();
DmaDoubleWordWrite(uint data)46         void DmaDoubleWordWrite(uint data);
47     }
48 
49     public interface ISamPdcQuadWordPeripheral : ISamPdcPeripheral
50     {
DmaQuadWordRead()51         ulong? DmaQuadWordRead();
DmaQuadWordWrite(ulong data)52         void DmaQuadWordWrite(ulong data);
53     }
54 
55     public class SAM_PDC
56     {
SAM_PDC(IMachine machine, ISamPdcPeripheral parent, long registersOffset, Action flagsChangedCallback, bool receiverEnabled = true, bool transmitterEnabled = true)57         public SAM_PDC(IMachine machine, ISamPdcPeripheral parent, long registersOffset, Action flagsChangedCallback, bool receiverEnabled = true, bool transmitterEnabled = true)
58         {
59             this.machine = machine;
60             this.parent = parent;
61             this.receiverEnabled = receiverEnabled;
62             this.transmitterEnabled = transmitterEnabled;
63             FlagsChanged = flagsChangedCallback;
64             engine = new DmaEngine(machine.GetSystemBus(parent));
65             receiverBuffer = new List<byte>();
66             DefineRegisters(registersOffset);
67             Reset();
68         }
69 
Reset()70         public void Reset()
71         {
72             RxBufferFull = true;
73             TxBufferEmpty = true;
74             EndOfRxBuffer = false;
75             EndOfTxBuffer = false;
76             receiverBuffer.Clear();
77             transmitterBuffer = null;
78             transmitterBufferOffset = 0;
79             InvokeFlagsChanged();
80         }
81 
DefineRegisters(long offset)82         public void DefineRegisters(long offset)
83         {
84             ((Registers)((long)Registers.ReceivePointer + offset)).Define(parent)
85                 .WithValueField(0, 32, out receivePointer, name: "RXPTR")
86             ;
87 
88             ((Registers)((long)Registers.ReceiveCounter + offset)).Define(parent)
89                 .WithValueField(0, 16, out receiveCounter, name: "RXCTR")
90                 .WithReservedBits(16, 16)
91                 .WithWriteCallback((_, __) =>
92                 {
93                     RxBufferFull = receiveCounter.Value == 0;
94                     EndOfRxBuffer = false;
95                     InvokeFlagsChanged();
96                 })
97             ;
98 
99             ((Registers)((long)Registers.TransmitPointer + offset)).Define(parent)
100                 .WithValueField(0, 32, out transmitPointer, name: "TXPTR")
101             ;
102 
103             ((Registers)((long)Registers.TransmitCounter + offset)).Define(parent)
104                 .WithValueField(0, 16, out transmitCounter, name: "TXCTR")
105                 .WithReservedBits(16, 16)
106                 .WithWriteCallback((_, __) =>
107                 {
108                     TxBufferEmpty = transmitCounter.Value == 0;
109                     EndOfTxBuffer = false;
110                     InvokeFlagsChanged();
111                 })
112             ;
113 
114             ((Registers)((long)Registers.ReceiveNextPointer + offset)).Define(parent)
115                 .WithValueField(0, 32, out receiveNextPointer, name: "RXNPTR")
116             ;
117 
118             ((Registers)((long)Registers.ReceiveNextCounter + offset)).Define(parent)
119                 .WithValueField(0, 16, out receiveNextCounter, name: "RXNCTR")
120                 .WithReservedBits(16, 16)
121                 .WithWriteCallback((_, __) =>
122                 {
123                     EndOfRxBuffer = false;
124                     InvokeFlagsChanged();
125                 })
126             ;
127 
128             ((Registers)((long)Registers.TransmitNextPointer + offset)).Define(parent)
129                 .WithValueField(0, 32, out transmitNextPointer, name: "TXNPTR")
130             ;
131 
132             ((Registers)((long)Registers.TransmitNextCounter + offset)).Define(parent)
133                 .WithValueField(0, 16, out transmitNextCounter, name: "TXNCTR")
134                 .WithReservedBits(16, 16)
135                 .WithWriteCallback((_, __) =>
136                 {
137                     EndOfTxBuffer = false;
138                     InvokeFlagsChanged();
139                 })
140             ;
141 
142             ((Registers)((long)Registers.TransferControl + offset)).Define(parent)
143                 .If(receiverEnabled)
144                     .Then(reg => reg
145                         .WithFlag(0, out receiverTransferEnabled, FieldMode.Set, name: "RXTEN")
146                         .WithFlag(1, FieldMode.WriteOneToClear, writeCallback: (_, value) => { if(value) receiverTransferEnabled.Value = false; }, name: "RXTDIS")
147                         .WithWriteCallback((_, __) => TriggerReceiver())
148                     )
149                     .Else(reg => reg
150                         .WithReservedBits(0, 2)
151                     )
152                 .WithReservedBits(2, 6)
153                 .If(transmitterEnabled)
154                     .Then(reg => reg
155                         .WithFlag(8, out transmitterTransferEnabled, FieldMode.Set, name: "TXTEN")
156                         .WithFlag(9, FieldMode.WriteOneToClear, writeCallback: (_, value) => { if(value) transmitterTransferEnabled.Value = false; }, name: "TXTDIS")
157                         .WithWriteCallback((_, __) =>
158                         {
159                             TriggerTransmitter();
160                             InvokeFlagsChanged();
161                         })
162                     )
163                     .Else(reg => reg
164                         .WithReservedBits(8, 2)
165                     )
166                 .WithReservedBits(10, 22)
167                 .WithWriteCallback((_, __) => InvokeFlagsChanged())
168             ;
169 
170             ((Registers)((long)Registers.TransferStatus + offset)).Define(parent)
171                 .If(receiverEnabled)
172                     .Then(reg => reg
173                         .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => receiverTransferEnabled.Value, name: "RXTEN")
174                     )
175                     .Else(reg => reg
176                         .WithReservedBits(0, 1)
177                     )
178                 .WithReservedBits(1, 7)
179                 .If(transmitterEnabled)
180                     .Then(reg => reg
181                         .WithFlag(8, FieldMode.Read, valueProviderCallback: _ => transmitterTransferEnabled.Value, name: "TXTEN")
182                     )
183                     .Else(reg => reg
184                         .WithReservedBits(8, 1)
185                     )
186                 .WithReservedBits(9, 23)
187             ;
188         }
189 
TriggerReceiver()190         public void TriggerReceiver()
191         {
192             if(!receiverEnabled)
193             {
194                 return;
195             }
196 
197             if(receiveCounter.Value == 0)
198             {
199                 parent.NoisyLog("Receiver triggered, but buffer is not available");
200                 receiverTransferEnabled.Value = false;
201                 EndOfRxBuffer = true;
202                 InvokeFlagsChanged();
203                 return;
204             }
205 
206             if(!receiverTransferEnabled.Value)
207             {
208                 return;
209             }
210             parent.NoisyLog("Receiver triggered");
211 
212             TriggerReceiverInner();
213 
214             if(receiveCounter.Value == 0)
215             {
216                 EndOfRxBuffer = true;
217                 if(receiveNextCounter.Value == 0)
218                 {
219                     receiverTransferEnabled.Value = false;
220                     RxBufferFull = true;
221                     InvokeFlagsChanged();
222                     return;
223                 }
224 
225                 receivePointer.Value = receiveNextPointer.Value;
226                 receiveNextPointer.Value = 0x0;
227                 receiveCounter.Value = receiveNextCounter.Value;
228                 receiveNextCounter.Value = 0;
229                 TriggerReceiverInner();
230             }
231             InvokeFlagsChanged();
232         }
233 
234         public bool RxBufferFull
235         {
236             get => rxBufferFull;
237             private set
238             {
239                 flagsChanged |= rxBufferFull != value;
240                 rxBufferFull = value;
241             }
242         }
243 
244         public bool TxBufferEmpty
245         {
246             get => txBufferEmpty;
247             private set
248             {
249                 flagsChanged |= txBufferEmpty != value;
250                 txBufferEmpty = value;
251             }
252         }
253 
254         public bool EndOfRxBuffer
255         {
256             get => endOfRxBuffer;
257             private set
258             {
259                 flagsChanged |= endOfRxBuffer != value;
260                 endOfRxBuffer = value;
261             }
262         }
263 
264         public bool EndOfTxBuffer
265         {
266             get => endOfTxBuffer;
267             private set
268             {
269                 flagsChanged |= endOfTxBuffer != value;
270                 endOfTxBuffer = value;
271             }
272         }
273 
FinalizeReceiverTransfer()274         private void FinalizeReceiverTransfer()
275         {
276             if(receiverBuffer.Count == 0)
277             {
278                 return;
279             }
280             var transferType = parent.DmaReadAccessWidth;
281 
282             var request = new Request(
283                 new Place(receiverBuffer.ToArray(), 0),
284                 new Place(receivePointer.Value),
285                 receiverBuffer.Count,
286                 transferType,
287                 transferType
288             );
289 
290             parent.DebugLog("Executing receiver transfer to 0x{0:X} of {1} bytes", receivePointer.Value, receiverBuffer.Count);
291             engine.IssueCopy(request);
292             receiverBuffer.Clear();
293         }
294 
TriggerTransmitter()295         private void TriggerTransmitter()
296         {
297             if(!transmitterEnabled)
298             {
299                 return;
300             }
301 
302             if(transmitCounter.Value == 0)
303             {
304                 parent.NoisyLog("Transmitter triggered, but buffer is not available");
305                 transmitterTransferEnabled.Value = false;
306                 EndOfTxBuffer = true;
307                 return;
308             }
309 
310             if(!transmitterTransferEnabled.Value)
311             {
312                 return;
313             }
314             parent.NoisyLog("Transmitter triggered");
315 
316             TriggerTransmitterInner();
317 
318             if(transmitCounter.Value == 0)
319             {
320                 EndOfTxBuffer = true;
321                 if(transmitNextCounter.Value == 0)
322                 {
323                     transmitterTransferEnabled.Value = false;
324                     TxBufferEmpty = true;
325                     InvokeFlagsChanged();
326                     return;
327                 }
328 
329                 transmitPointer.Value = transmitNextPointer.Value;
330                 transmitNextPointer.Value = 0x0;
331                 transmitCounter.Value = transmitNextCounter.Value;
332                 transmitNextCounter.Value = 0;
333                 TriggerTransmitterInner();
334             }
335         }
336 
StartTransmitterTransfer()337         private void StartTransmitterTransfer()
338         {
339             var transferType = parent.DmaWriteAccessWidth;
340             transmitterBuffer = new byte[(int)transmitCounter.Value * (int)transferType];
341             transmitterBufferOffset = 0;
342             var request = new Request(
343                 new Place(transmitPointer.Value),
344                 new Place(transmitterBuffer, 0),
345                 (int)transmitCounter.Value,
346                 transferType,
347                 transferType
348             );
349 
350             parent.DebugLog("Executing transmitter transfer from 0x{0:X} of {1} bytes", transmitPointer.Value, transmitterBuffer.Length);
351             engine.IssueCopy(request);
352         }
353 
TriggerReceiverInner()354         private void TriggerReceiverInner()
355         {
356             // Block accesses are preferred, but are optional
357             if(TryBlockRead((int)receiveCounter.Value))
358             {
359                 receiveCounter.Value = 0;
360             }
361 
362             for(; receiveCounter.Value > 0; receiveCounter.Value -= 1)
363             {
364                 if(!TryRead())
365                 {
366                     break;
367                 }
368             }
369 
370             if(receiveCounter.Value == 0)
371             {
372                 FinalizeReceiverTransfer();
373             }
374         }
375 
TriggerTransmitterInner()376         private void TriggerTransmitterInner()
377         {
378             if(transmitterBuffer == null)
379             {
380                 StartTransmitterTransfer();
381             }
382 
383             // Block accesses are preferred, but are optional
384             if(TryBlockWrite((int)transmitCounter.Value))
385             {
386                 transmitCounter.Value = 0;
387                 return;
388             }
389 
390             for(; transmitCounter.Value > 0; transmitCounter.Value -= 1)
391             {
392                 Write();
393             }
394         }
395 
TryBlockWrite(int count)396         private bool TryBlockWrite(int count)
397         {
398             var buffer = transmitterBuffer.Skip(transmitterBufferOffset);
399             var transferType = parent.DmaWriteAccessWidth;
400 
401             switch(transferType)
402             {
403             case TransferType.Byte:
404                 if(parent is ISamPdcBlockBytePeripheral bytePeripheral)
405                 {
406                     bytePeripheral.DmaBlockByteWrite(buffer.Take(count).ToArray());
407                     break;
408                 }
409                 return false;
410             case TransferType.Word:
411             case TransferType.DoubleWord:
412             case TransferType.QuadWord:
413                 // Not implemented
414                 return false;
415             default:
416                 throw new Exception("Unreachable");
417             }
418 
419             transmitterBufferOffset += (int)transferType * count;
420             if(transmitterBufferOffset == transmitterBuffer.Length)
421             {
422                 transmitterBuffer = null;
423             }
424             return true;
425         }
426 
Write()427         private void Write()
428         {
429             var transferType = parent.DmaWriteAccessWidth;
430             switch(transferType)
431             {
432             case TransferType.Byte:
433                 if(parent is ISamPdcBytePeripheral bytePeripheral)
434                 {
435                     bytePeripheral.DmaByteWrite(transmitterBuffer[transmitterBufferOffset]);
436                     break;
437                 }
438                 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcBytePeripheral, but Byte transfer was selected");
439                 return;
440             case TransferType.Word:
441                 if(parent is ISamPdcWordPeripheral wordPeripheral)
442                 {
443                     wordPeripheral.DmaWordWrite(BitConverter.ToUInt16(transmitterBuffer, transmitterBufferOffset));
444                     break;
445                 }
446                 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcWordPeripheral, but Word transfer was selected");
447                 return;
448             case TransferType.DoubleWord:
449                 if(parent is ISamPdcDoubleWordPeripheral doubleWordPeripheral)
450                 {
451                     doubleWordPeripheral.DmaDoubleWordWrite(BitConverter.ToUInt32(transmitterBuffer, transmitterBufferOffset));
452                     break;
453                 }
454                 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcDoubleWordPeripheral, but DoubleWord transfer was selected");
455                 return;
456             case TransferType.QuadWord:
457                 if(parent is ISamPdcQuadWordPeripheral quadWordPeripheral)
458                 {
459                     quadWordPeripheral.DmaQuadWordWrite(BitConverter.ToUInt64(transmitterBuffer, transmitterBufferOffset));
460                     break;
461                 }
462                 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcQuadWordPeripheral, but QuadWord transfer was selected");
463                 return;
464             default:
465                 throw new Exception("Unreachable");
466             }
467             transmitterBufferOffset += (int)transferType;
468             if(transmitterBufferOffset == transmitterBuffer.Length)
469             {
470                 transmitterBuffer = null;
471             }
472         }
473 
TryBlockRead(int count)474         private bool TryBlockRead(int count)
475         {
476             byte[] data = null;
477 
478             switch(parent.DmaReadAccessWidth)
479             {
480             case TransferType.Byte:
481                 if(parent is ISamPdcBlockBytePeripheral bytePeripheral)
482                 {
483                     data = bytePeripheral.DmaBlockByteRead(count);
484                     break;
485                 }
486                 return false;
487             case TransferType.Word:
488             case TransferType.DoubleWord:
489             case TransferType.QuadWord:
490                 // Not implemented
491                 return false;
492             default:
493                 throw new Exception("Unreachable");
494             }
495 
496             if(data == null)
497             {
498                 return false;
499             }
500 
501             var paddedData = data.Concat(Enumerable.Repeat((byte)0x0, count)).Take(count);
502             receiverBuffer.AddRange(paddedData);
503             return true;
504         }
505 
TryRead()506         private bool TryRead()
507         {
508             byte[] data = null;
509 
510             switch(parent.DmaReadAccessWidth)
511             {
512             case TransferType.Byte:
513                 if(parent is ISamPdcBytePeripheral bytePeripheral)
514                 {
515                     data = bytePeripheral.DmaByteRead()?.AsRawBytes();
516                     break;
517                 }
518                 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcBytePeripheral, but Byte transfer was selected");
519                 return false;
520             case TransferType.Word:
521                 if(parent is ISamPdcWordPeripheral wordPeripheral)
522                 {
523                     data = wordPeripheral.DmaWordRead()?.AsRawBytes();
524                     break;
525                 }
526                 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcWordPeripheral, but Word transfer was selected");
527                 return false;
528             case TransferType.DoubleWord:
529                 if(parent is ISamPdcDoubleWordPeripheral doubleWordPeripheral)
530                 {
531                     data = doubleWordPeripheral.DmaDoubleWordRead()?.AsRawBytes();
532                     break;
533                 }
534                 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcDoubleWordPeripheral, but DoubleWord transfer was selected");
535                 return false;
536             case TransferType.QuadWord:
537                 if(parent is ISamPdcQuadWordPeripheral quadWordPeripheral)
538                 {
539                     data = quadWordPeripheral.DmaQuadWordRead()?.AsRawBytes();
540                     break;
541                 }
542                 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcQuadWordPeripheral, but QuadWord transfer was selected");
543                 return false;
544             default:
545                 throw new Exception("Unreachable");
546             }
547 
548             if(data == null)
549             {
550                 return false;
551             }
552 
553             receiverBuffer.AddRange(data);
554             return true;
555         }
556 
InvokeFlagsChanged()557         private void InvokeFlagsChanged()
558         {
559             if(flagsChanged)
560             {
561                 FlagsChanged?.Invoke();
562                 flagsChanged = false;
563             }
564         }
565 
566         private Action FlagsChanged { get; }
567 
568         private bool flagsChanged;
569         private bool rxBufferFull;
570         private bool txBufferEmpty;
571         private bool endOfRxBuffer;
572         private bool endOfTxBuffer;
573         private IValueRegisterField receivePointer;
574         private IValueRegisterField receiveCounter;
575         private IValueRegisterField transmitPointer;
576         private IValueRegisterField transmitCounter;
577         private IValueRegisterField receiveNextPointer;
578         private IValueRegisterField receiveNextCounter;
579         private IValueRegisterField transmitNextPointer;
580         private IValueRegisterField transmitNextCounter;
581         private IFlagRegisterField receiverTransferEnabled;
582         private IFlagRegisterField transmitterTransferEnabled;
583 
584         private byte[] transmitterBuffer;
585         private int transmitterBufferOffset;
586 
587         private readonly IMachine machine;
588         private readonly ISamPdcPeripheral parent;
589         private readonly DmaEngine engine;
590         private readonly List<byte> receiverBuffer;
591         private readonly bool receiverEnabled;
592         private readonly bool transmitterEnabled;
593 
594         public enum Registers
595         {
596             ReceivePointer      = 0x00,
597             ReceiveCounter      = 0x04,
598             TransmitPointer     = 0x08,
599             TransmitCounter     = 0x0C,
600             ReceiveNextPointer  = 0x10,
601             ReceiveNextCounter  = 0x14,
602             TransmitNextPointer = 0x18,
603             TransmitNextCounter = 0x1C,
604             TransferControl     = 0x20,
605             TransferStatus      = 0x24,
606         }
607     }
608 }