1 //
2 // Copyright (c) 2010-2023 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using System;
9 using Antmicro.Renode.Peripherals.Bus;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Core;
12 using System.Collections.Generic;
13 using System.Linq;
14 
15 namespace Antmicro.Renode.Peripherals.DMA
16 {
17     public sealed class STM32DMA : IDoubleWordPeripheral, IKnownSize, IGPIOReceiver, INumberedGPIOOutput
18     {
STM32DMA(IMachine machine)19         public STM32DMA(IMachine machine)
20         {
21             streamFinished = new bool[NumberOfStreams];
22             streams = new Stream[NumberOfStreams];
23             for(var i = 0; i < streams.Length; i++)
24             {
25                 streams[i] = new Stream(this, i);
26             }
27             this.machine = machine;
28             engine = new DmaEngine(machine.GetSystemBus(this));
29             Reset();
30         }
31 
32         public IReadOnlyDictionary<int, IGPIO> Connections
33         {
34            get
35            {
36               var i = 0;
37               return streams.ToDictionary(x => i++, y => (IGPIO)y.IRQ);
38            }
39         }
40 
41         public long Size
42         {
43             get
44             {
45                 return 0x400;
46             }
47         }
48 
ReadDoubleWord(long offset)49         public uint ReadDoubleWord(long offset)
50         {
51             switch((Registers)offset)
52             {
53             case Registers.LowInterruptStatus:
54             case Registers.HighInterruptStatus:
55                 return HandleInterruptRead((int)(offset/4));
56             default:
57                 if(offset >= StreamOffsetStart && offset <= StreamOffsetEnd)
58                 {
59                     offset -= StreamOffsetStart;
60                     return streams[offset / StreamSize].Read(offset % StreamSize);
61                 }
62                 this.LogUnhandledRead(offset);
63                 return 0;
64             }
65         }
66 
WriteDoubleWord(long offset, uint value)67         public void WriteDoubleWord(long offset, uint value)
68         {
69             switch((Registers)offset)
70             {
71             case Registers.LowInterruptClear:
72             case Registers.HighInterruptClear:
73                 HandleInterruptClear((int)((offset - 8)/4), value);
74                 break;
75             default:
76                 if(offset >= StreamOffsetStart && offset <= StreamOffsetEnd)
77                 {
78                     offset -= StreamOffsetStart;
79                     streams[offset / StreamSize].Write(offset % StreamSize, value);
80                 }
81                 else
82                 {
83                     this.LogUnhandledWrite(offset, value);
84                 }
85                 break;
86             }
87         }
88 
Reset()89         public void Reset()
90         {
91             streamFinished.Initialize();
92             foreach(var stream in streams)
93             {
94                 stream.Reset();
95             }
96         }
97 
OnGPIO(int number, bool value)98         public void OnGPIO(int number, bool value)
99         {
100             if(number < 0 || number >= streams.Length)
101             {
102                 this.Log(LogLevel.Error, "Attempted to start non-existing DMA stream number: {0}. Maximum value is {1}", number, streams.Length);
103                 return;
104             }
105 
106             if(value)
107             {
108                 this.Log(LogLevel.Debug, "DMA peripheral request on stream {0} {1}", number, value);
109                 if(streams[number].Enabled)
110                 {
111                     streams[number].DoPeripheralTransfer();
112                 }
113                 else
114                 {
115                     this.Log(LogLevel.Warning, "DMA peripheral request on stream {0} ignored", number);
116                 }
117             }
118         }
119 
HandleInterruptRead(int offset)120         private uint HandleInterruptRead(int offset)
121         {
122             lock(streamFinished)
123             {
124                 var returnValue = 0u;
125                 for(var i = 4 * offset; i < 4 * (offset + 1); i++)
126                 {
127                     if(streamFinished[i])
128                     {
129                         returnValue |= 1u << BitNumberForStream(i - 4 * offset);
130                     }
131                 }
132                 return returnValue;
133             }
134         }
135 
HandleInterruptClear(int offset, uint value)136         private void HandleInterruptClear(int offset, uint value)
137         {
138             lock(streamFinished)
139             {
140                 for(var i = 4 * offset; i < 4 * (offset + 1); i++)
141                 {
142                     var bitNo = BitNumberForStream(i - 4 * offset);
143                     if((value & (1 << bitNo)) != 0)
144                     {
145                         streamFinished[i] = false;
146                         streams[i].IRQ.Unset();
147                     }
148                 }
149             }
150         }
151 
BitNumberForStream(int streamNo)152         private static int BitNumberForStream(int streamNo)
153         {
154             switch(streamNo)
155             {
156             case 0:
157                 return 5;
158             case 1:
159                 return 11;
160             case 2:
161                 return 21;
162             case 3:
163                 return 27;
164             default:
165                 throw new InvalidOperationException("Should not reach here.");
166             }
167         }
168 
169         private readonly bool[] streamFinished;
170         private readonly Stream[] streams;
171         private readonly DmaEngine engine;
172         private readonly IMachine machine;
173 
174         private const int NumberOfStreams = 8;
175         private const int StreamOffsetStart = 0x10;
176         private const int StreamOffsetEnd = 0xCC;
177         private const int StreamSize = 0x18;
178 
179         private enum Registers
180         {
181             LowInterruptStatus = 0x0, // DMA_LISR
182             HighInterruptStatus = 0x4, // DMA_HISR
183             LowInterruptClear = 0x8, //DMA_LIFCR
184             HighInterruptClear = 0xC // DMA_HIFCR
185         }
186 
187         private class Stream
188         {
Stream(STM32DMA parent, int streamNo)189             public Stream(STM32DMA parent, int streamNo)
190             {
191                 this.parent = parent;
192                 this.streamNo = streamNo;
193                 IRQ = new GPIO();
194             }
195 
Read(long offset)196             public uint Read(long offset)
197             {
198                 switch((Registers)offset)
199                 {
200                 case Registers.Configuration:
201                     return HandleConfigurationRead();
202                 case Registers.NumberOfData:
203                     return (uint)numberOfData;
204                 case Registers.PeripheralAddress:
205                     return peripheralAddress;
206                 case Registers.Memory0Address:
207                     return memory0Address;
208                 case Registers.Memory1Address:
209                     return memory1Address;
210                 default:
211                     parent.Log(LogLevel.Warning, "Unexpected read access from not implemented register (offset 0x{0:X}).", offset);
212                     return 0;
213                 }
214             }
215 
Write(long offset, uint value)216             public void Write(long offset, uint value)
217             {
218                 switch((Registers)offset)
219                 {
220                 case Registers.Configuration:
221                     HandleConfigurationWrite(value);
222                     break;
223                 case Registers.NumberOfData:
224                     numberOfData = (int)value;
225                     break;
226                 case Registers.PeripheralAddress:
227                     peripheralAddress = value;
228                     break;
229                 case Registers.Memory0Address:
230                     memory0Address = value;
231                     break;
232                 case Registers.Memory1Address:
233                     memory1Address = value;
234                     break;
235                 default:
236                     parent.Log(LogLevel.Warning, "Unexpected write access to not implemented register (offset 0x{0:X}, value 0x{1:X}).", offset, value);
237                     break;
238                 }
239             }
240 
241             public GPIO IRQ { get; private set; }
242 
Reset()243             public void Reset()
244             {
245                 memory0Address = 0u;
246                 memory1Address = 0u;
247                 numberOfData = 0;
248                 transferredSize = 0;
249                 memoryTransferType = TransferType.Byte;
250                 peripheralTransferType = TransferType.Byte;
251                 memoryIncrementAddress = false;
252                 peripheralIncrementAddress = false;
253                 direction = Direction.PeripheralToMemory;
254                 interruptOnComplete = false;
255                 Enabled = false;
256             }
257 
CreateRequest(int? size = null, int? destinationOffset = null)258             private Request CreateRequest(int? size = null, int? destinationOffset = null)
259             {
260                 var sourceAddress = 0u;
261                 var destinationAddress = 0u;
262                 switch(direction)
263                 {
264                 case Direction.PeripheralToMemory:
265                 case Direction.MemoryToMemory:
266                     sourceAddress = peripheralAddress;
267                     destinationAddress = memory0Address;
268                     break;
269                 case Direction.MemoryToPeripheral:
270                     sourceAddress = memory0Address;
271                     destinationAddress = peripheralAddress;
272                     break;
273                 }
274 
275                 var sourceTransferType = direction == Direction.PeripheralToMemory ? peripheralTransferType : memoryTransferType;
276                 var destinationTransferType = direction == Direction.MemoryToPeripheral ? peripheralTransferType : memoryTransferType;
277                 var incrementSourceAddress = direction == Direction.PeripheralToMemory ? peripheralIncrementAddress : memoryIncrementAddress;
278                 var incrementDestinationAddress = direction == Direction.MemoryToPeripheral ? peripheralIncrementAddress : memoryIncrementAddress;
279                 return new Request(sourceAddress, (uint)(destinationAddress + (destinationOffset ?? 0)), size ?? numberOfData, sourceTransferType, destinationTransferType,
280                         incrementSourceAddress, incrementDestinationAddress);
281             }
282 
DoTransfer()283             public void DoTransfer()
284             {
285                 var request = CreateRequest(numberOfData * (int)memoryTransferType);
286                 if(request.Size > 0)
287                 {
288                     lock(parent.streamFinished)
289                     {
290                         parent.engine.IssueCopy(request);
291                         parent.streamFinished[streamNo] = true;
292                         if(interruptOnComplete)
293                         {
294                             parent.machine.LocalTimeSource.ExecuteInNearestSyncedState(_ => IRQ.Set());
295                         }
296                     }
297                 }
298             }
299 
DoPeripheralTransfer()300             public void DoPeripheralTransfer()
301             {
302                 var request = CreateRequest((int)memoryTransferType, transferredSize);
303                 transferredSize += (int)memoryTransferType;
304                 if(request.Size > 0)
305                 {
306                     lock(parent.streamFinished)
307                     {
308                         parent.engine.IssueCopy(request);
309                         if(transferredSize == numberOfData * (int)memoryTransferType)
310                         {
311                             transferredSize = 0;
312                             parent.streamFinished[streamNo] = true;
313                             Enabled = false;
314                             if(interruptOnComplete)
315                             {
316                                 parent.machine.LocalTimeSource.ExecuteInNearestSyncedState(_ => IRQ.Set());
317                             }
318                         }
319                     }
320                 }
321             }
322 
323             public bool Enabled { get; private set; }
324 
HandleConfigurationRead()325             private uint HandleConfigurationRead()
326             {
327                 var returnValue = 0u;
328                 returnValue |= (uint)(channel << 25);
329                 returnValue |= (uint)(priority << 16);
330 
331                 returnValue |= FromTransferType(memoryTransferType) << 13;
332                 returnValue |= FromTransferType(peripheralTransferType) << 11;
333                 returnValue |= memoryIncrementAddress ? (1u << 10) : 0u;
334                 returnValue |= peripheralIncrementAddress ? (1u << 9) : 0u;
335                 returnValue |= ((uint)direction) << 6;
336                 returnValue |= interruptOnComplete ? (1u << 4) : 0u;
337                 // regarding enable bit - our transfer is always finished
338                 return returnValue;
339             }
340 
HandleConfigurationWrite(uint value)341             private void HandleConfigurationWrite(uint value)
342             {
343                 // we ignore channel selection and priority
344                 channel = (byte)((value >> 25) & 7);
345                 priority = (byte)((value >> 16) & 3);
346 
347                 memoryTransferType = ToTransferType(value >> 13);
348                 peripheralTransferType = ToTransferType(value >> 11);
349                 memoryIncrementAddress = (value & (1 << 10)) != 0;
350                 peripheralIncrementAddress = (value & (1 << 9)) != 0;
351                 direction = (Direction)((value >> 6) & 3);
352                 interruptOnComplete = (value & (1 << 4)) != 0;
353                 // we ignore transfer error interrupt enable as we never post errors
354                 if((value & ~0xE037ED5) != 0)
355                 {
356                     parent.Log(LogLevel.Warning, "Channel {0}: unsupported bits written to configuration register. Value is 0x{1:X}.", streamNo, value);
357                 }
358 
359                 if((value & 1) != 0)
360                 {
361                     if(direction != Direction.PeripheralToMemory)
362                     {
363                         DoTransfer();
364                     }
365                     else
366                     {
367                         Enabled = true;
368                     }
369                 }
370             }
371 
ToTransferType(uint dataSize)372             private TransferType ToTransferType(uint dataSize)
373             {
374                 dataSize &= 3;
375                 switch(dataSize)
376                 {
377                 case 0:
378                     return TransferType.Byte;
379                 case 1:
380                     return TransferType.Word;
381                 case 2:
382                     return TransferType.DoubleWord;
383                 default:
384                     parent.Log(LogLevel.Warning, "Stream {0}: Non existitng possible value written as data size.", streamNo);
385                     return TransferType.Byte;
386                 }
387             }
388 
FromTransferType(TransferType transferType)389             private static uint FromTransferType(TransferType transferType)
390             {
391                 switch(transferType)
392                 {
393                 case TransferType.Byte:
394                     return 0;
395                 case TransferType.Word:
396                     return 1;
397                 case TransferType.DoubleWord:
398                     return 2;
399                 }
400                 throw new InvalidOperationException("Should not reach here.");
401             }
402 
403             private uint memory0Address;
404             private uint memory1Address;
405             private uint peripheralAddress;
406             private int numberOfData;
407             private int transferredSize;
408             private TransferType memoryTransferType;
409             private TransferType peripheralTransferType;
410             private bool memoryIncrementAddress;
411             private bool peripheralIncrementAddress;
412             private Direction direction;
413             private bool interruptOnComplete;
414             private byte channel;
415             private byte priority;
416 
417             private readonly STM32DMA parent;
418             private readonly int streamNo;
419 
420             private enum Registers
421             {
422                 Configuration = 0x0, // DMA_SxCR
423                 NumberOfData = 0x4, // DMA_SxNDTR
424                 PeripheralAddress = 0x8, // DMA_SxPAR
425                 Memory0Address = 0xC, // DMA_SxM0AR
426                 Memory1Address = 0x10, // DMA_SxM1AR
427                 FIFOControl = 0x14, // DMA_SxFCR
428             }
429 
430             private enum Direction : byte
431             {
432                 PeripheralToMemory = 0,
433                 MemoryToPeripheral = 1,
434                 MemoryToMemory = 2
435             }
436         }
437     }
438 }
439 
440