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.Core;
11 using Antmicro.Renode.Logging;
12 using System.Linq;
13 using System.Collections.Generic;
14 
15 namespace Antmicro.Renode.Peripherals.DMA
16 {
17     public sealed class STM32LDMA : IDoubleWordPeripheral, IKnownSize, INumberedGPIOOutput
18     {
STM32LDMA(IMachine machine)19         public STM32LDMA(IMachine machine)
20         {
21             engine = new DmaEngine(machine.GetSystemBus(this));
22             channels = new Channel[8];
23             for(var i = 0; i < channels.Length; i++)
24             {
25                 channels[i] = new Channel(this, i);
26             }
27         }
28 
ReadDoubleWord(long offset)29         public uint ReadDoubleWord(long offset)
30         {
31             if(offset >= 0x08 && offset <= 0x8C)
32             {
33                 return channels[(offset - 0x08) / 0x14].Read(offset - 0x08 - ((offset - 0x08) / 0x14) * 0x14);
34             }
35             switch((Offset)offset)
36             {
37             case Offset.InterruptStatus:
38                 return HandleInterruptStatusRead();
39             }
40             this.LogUnhandledRead(offset);
41             return 0;
42         }
43 
44 
WriteDoubleWord(long offset, uint value)45         public void WriteDoubleWord(long offset, uint value)
46         {
47             if(offset >= 0x08 && offset <= 0x8C)
48             {
49                 var channelNo = (offset - 0x08) / 0x14;
50                 channels[channelNo].Write(offset - 0x08 - channelNo * 0x14, value);
51                 return;
52             }
53             switch((Offset)offset)
54             {
55             case Offset.InterruptClear:
56                 HandleClearInterrupt(value);
57                 break;
58             default:
59                 this.LogUnhandledWrite(offset, value);
60                 break;
61             }
62         }
63 
64         public IReadOnlyDictionary<int, IGPIO> Connections { get { var i = 0; return channels.ToDictionary(x => i++, y => (IGPIO)y.IRQ); } }
65 
Reset()66         public void Reset()
67         {
68             // TODO
69         }
70 
71         public long Size
72         {
73             get
74             {
75                 return 0x400;
76             }
77         }
78 
HandleInterruptStatusRead()79         private uint HandleInterruptStatusRead()
80         {
81             var returnValue = 0u;
82             for(var i = 0; i < channels.Length; i++)
83             {
84                 returnValue |= channels[i].IRQ.IsSet ? (3u << i * 4) : 0u;
85             }
86             return returnValue;
87         }
88 
HandleClearInterrupt(uint value)89         private void HandleClearInterrupt(uint value)
90         {
91             for(var i = 0; i < channels.Length; i++)
92             {
93                 var ourBit1 = 4 * i;
94                 var ourBit2 = ourBit1 + 1;
95                 if((value & (1 << ourBit1)) != 0 || (value & (1 << ourBit2)) != 0)
96                 {
97                     channels[i].ClearInterrupt();
98                 }
99             }
100         }
101 
102         private sealed class Channel
103         {
Channel(STM32LDMA parent, int channelNo)104             public Channel(STM32LDMA parent, int channelNo)
105             {
106                 this.parent = parent;
107                 memoryTransferType = TransferType.Byte;
108                 peripheralTransferType = TransferType.Byte;
109                 IRQ = new GPIO();
110                 this.channelNo = channelNo;
111             }
112 
113             public GPIO IRQ { get; private set; }
114 
Read(long offset)115             public uint Read(long offset)
116             {
117                 switch((Offset)offset)
118                 {
119                 case Offset.Configuration:
120                     return HandleConfigurationRead();
121                 case Offset.NumberOfData:
122                     return (uint)numberOfData;
123                 case Offset.PeripheralAddress:
124                     return peripheralAddress;
125                 case Offset.MemoryAddress:
126                     return memoryAddress;
127                 default:
128                     parent.Log(LogLevel.Warning, "Channel {0}: unhandled read from 0x{1:X}.", channelNo, offset);
129                     return 0;
130                 }
131             }
132 
Write(long offset, uint value)133             public void Write(long offset, uint value)
134             {
135                 switch((Offset)offset)
136                 {
137                 case Offset.Configuration:
138                     HandleConfigurationWrite(value);
139                     break;
140                 case Offset.NumberOfData:
141                     numberOfData = (int)value;
142                     break;
143                 case Offset.PeripheralAddress:
144                     peripheralAddress = value;
145                     break;
146                 case Offset.MemoryAddress:
147                     memoryAddress = value;
148                     break;
149                 default:
150                     parent.Log(LogLevel.Warning, "Channel {0}: unhandled write 0x{1:X} to 0x{2:X}.", channelNo, offset);
151                     break;
152                 }
153             }
154 
ClearInterrupt()155             public void ClearInterrupt()
156             {
157                 IRQ.Unset();
158             }
159 
Reset()160             public void Reset()
161             {
162                 IRQ.Unset();
163                 peripheralIncrement = false;
164                 peripheralAddress = 0u;
165                 memoryAddress = 0u;
166                 memoryIncrement = false;
167                 memoryTransferType = 0;
168                 peripheralTransferType = 0;
169                 completeInterruptEnabled = false;
170                 transferErrorInterruptEnabled = false;
171                 numberOfData = 0;
172                 priority = 0;
173                 direction = 0;
174             }
175 
HandleConfigurationRead()176             private uint HandleConfigurationRead()
177             {
178                 var returnValue = 0u;
179                 returnValue |= completeInterruptEnabled ? (1u << 1) : 0u;
180                 returnValue |= transferErrorInterruptEnabled ? (1u << 3) : 0u;
181                 returnValue |= ((uint)direction) << 4;
182                 returnValue |= peripheralIncrement ? (1u << 6) : 0u;
183                 returnValue |= memoryIncrement ? (1u << 7) : 0u;
184                 returnValue |= (uint)(priority << 12);
185                 return returnValue;
186             }
187 
HandleConfigurationWrite(uint value)188             private void HandleConfigurationWrite(uint value)
189             {
190                 completeInterruptEnabled = (value & (1 << 1)) != 0;
191                 transferErrorInterruptEnabled = (value & (1 << 3)) != 0;
192                 direction = (Direction)((value >> 4) & 1);
193                 peripheralIncrement = (value & (1 << 6)) != 0;
194                 memoryIncrement = (value & (1 << 7)) != 0;
195                 priority = (byte)((value >> 12) & 3);
196 
197                 if((value & ~0x30DB) != 0)
198                 {
199                     parent.Log(LogLevel.Warning, "Channel {0}: some unhandled bits were written to configuration register. Value is 0x{1:X}.", channelNo, value);
200                 }
201 
202                 if((value & 1) != 0)
203                 {
204                     DoTransfer();
205                 }
206             }
207 
DoTransfer()208             private void DoTransfer()
209             {
210                 uint sourceAddress, destinationAddress;
211                 bool incrementSourceAddress, incrementDestinationAddress;
212                 TransferType sourceTransferType, destinationTransferType;
213 
214                 if(direction == Direction.ReadFromMemory)
215                 {
216                     sourceAddress = memoryAddress;
217                     destinationAddress = peripheralAddress;
218                     incrementSourceAddress = memoryIncrement;
219                     incrementDestinationAddress = peripheralIncrement;
220                     sourceTransferType = memoryTransferType;
221                     destinationTransferType = peripheralTransferType;
222                 }
223                 else
224                 {
225                     sourceAddress = peripheralAddress;
226                     destinationAddress = memoryAddress;
227                     incrementSourceAddress = peripheralIncrement;
228                     incrementDestinationAddress = memoryIncrement;
229                     sourceTransferType = peripheralTransferType;
230                     destinationTransferType = memoryTransferType;
231                 }
232 
233                 var request = new Request(sourceAddress, destinationAddress, numberOfData, sourceTransferType, destinationTransferType,
234                                   incrementSourceAddress, incrementDestinationAddress);
235                 parent.engine.IssueCopy(request);
236                 IRQ.Set();
237             }
238 
239             private enum Offset
240             {
241                 Configuration = 0x0,
242                 NumberOfData = 0x4,
243                 PeripheralAddress = 0x8,
244                 MemoryAddress = 0xC
245             }
246 
247             private bool memoryIncrement;
248             private bool peripheralIncrement;
249             private uint peripheralAddress;
250             private uint memoryAddress;
251             private TransferType memoryTransferType;
252             private TransferType peripheralTransferType;
253             private bool completeInterruptEnabled;
254             private bool transferErrorInterruptEnabled;
255             private int numberOfData;
256             private byte priority;
257             private Direction direction;
258             private readonly STM32LDMA parent;
259             private readonly int channelNo;
260         }
261 
262         private enum Offset
263         {
264             InterruptStatus = 0x0,
265             InterruptClear = 0x4
266         }
267 
268         private enum Direction
269         {
270             ReadFromPeripheral = 0,
271             ReadFromMemory = 1
272         }
273 
274         private readonly DmaEngine engine;
275         private readonly Channel[] channels;
276     }
277 }
278 
279