1 //
2 // Copyright (c) 2010-2023 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.Collections.Generic;
8 using System.Collections.ObjectModel;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 
14 namespace Antmicro.Renode.Peripherals.DMA
15 {
16     [AllowedTranslations(AllowedTranslation.QuadWordToDoubleWord)]
17     public class MPFS_PDMA : IKnownSize, IDoubleWordPeripheral, INumberedGPIOOutput
18     {
MPFS_PDMA(IMachine machine)19         public MPFS_PDMA(IMachine machine)
20         {
21             this.machine = machine;
22             dmaEngine = new DmaEngine(this.machine.GetSystemBus(this));
23             channels = new Channel[ChannelCount];
24 
25             var irqCounter = 0;
26             var innerConnections = new Dictionary<int, IGPIO>();
27             for(var i = 0; i < ChannelCount; ++i)
28             {
29                 channels[i] = new Channel(this, i);
30                 innerConnections[irqCounter++] = channels[i].DoneInterrupt;
31                 innerConnections[irqCounter++] = channels[i].ErrorInterrupt;
32             }
33             Connections = new ReadOnlyDictionary<int, IGPIO>(innerConnections);
34             Reset();
35         }
36 
Reset()37         public void Reset()
38         {
39             for(var i = 0; i < ChannelCount; ++i)
40             {
41                 channels[i].Reset();
42             }
43         }
44 
ReadDoubleWord(long offset)45         public uint ReadDoubleWord(long offset)
46         {
47             var channelNumber = offset / ShiftBetweenChannels;
48             if(channelNumber >= ChannelCount)
49             {
50                 this.Log(LogLevel.Error, "Trying to read from nonexistent channel");
51                 return 0;
52             }
53             return channels[channelNumber].ReadDoubleWord(offset);
54         }
55 
WriteDoubleWord(long offset, uint value)56         public void WriteDoubleWord(long offset, uint value)
57         {
58             var channelNumber = offset / ShiftBetweenChannels;
59             if(channelNumber >= ChannelCount)
60             {
61                 this.Log(LogLevel.Error, "Trying to write to nonexistent channel");
62                 return;
63             }
64             channels[channelNumber].WriteDoubleWord(offset, value);
65         }
66 
67         public IReadOnlyDictionary<int, IGPIO> Connections { get; }
68         public long Size => 0x4000;
69 
70         private readonly IMachine machine;
71         private readonly DmaEngine dmaEngine;
72         private readonly Channel[] channels;
73 
74         private const int ChannelCount = 4;
75         private const int ShiftBetweenChannels = 0x1000;
76 
77         private class Channel : IDoubleWordPeripheral
78         {
Channel(MPFS_PDMA parent, int number)79             public Channel(MPFS_PDMA parent, int number)
80             {
81                 this.parent = parent;
82                 channelNumber = number;
83                 DoneInterrupt = new GPIO();
84                 ErrorInterrupt = new GPIO();
85 
86                 var registersMap = new Dictionary<long, DoubleWordRegister>();
87                 registersMap.Add(
88                     (long)ChannelRegisters.Control + ShiftBetweenChannels * channelNumber,
89                     new DoubleWordRegister(this)
90                         .WithFlag(0,
91                             writeCallback: (_, val) =>
92                             {
93                                 if(!val && !isRun)
94                                 {
95                                     isClaimed = false;
96                                 }
97                                 if(val && !isRun)
98                                 {
99                                     if(!isClaimed)
100                                     {
101                                         nextBytesLow.Value = 0;
102                                         nextBytesHigh.Value = 0;
103                                         nextSourceLow.Value = 0;
104                                         nextSourceHigh.Value = 0;
105                                         nextDestinationLow.Value = 0;
106                                         nextDestinationHigh.Value = 0;
107                                     }
108                                     isClaimed = true;
109                                 }
110                             },
111                             valueProviderCallback: _ =>
112                             {
113                                 return isClaimed;
114                             }, name: "claim")
115                         .WithFlag(1,
116                             writeCallback: (_, val) =>
117                             {
118                                 if(val)
119                                 {
120                                     isRun = true;
121                                     do
122                                     {
123                                         InitTransfer();
124                                     } while(repeat.Value);
125                                 }
126                             },
127                             valueProviderCallback: _ =>
128                             {
129                                 return isRun;
130                             }, name: "run")
131                         .WithReservedBits(2, 12)
132                         .WithFlag(14, out doneInterruptEnabled,
133                             writeCallback: (_, val) =>
134                             {
135                                 if(!val)
136                                 {
137                                     DoneInterrupt.Unset();
138                                 }
139                             }, name: "doneIE")
140                         .WithTag("errorIE", 15, 1)
141                         .WithReservedBits(16, 14)
142                         .WithFlag(30, out isDone, name: "done")
143                         .WithTag("error", 31, 1)
144                 );
145 
146                 // NEXT registers
147                 nextConfigRegister = new DoubleWordRegister(this)
148                     .WithReservedBits(0, 1)
149                     .WithFlag(2, out repeat, name: "repeat")
150                     .WithTag("order", 3, 1)
151                     .WithReservedBits(4, 20)
152                     .WithValueField(24, 4, out wsize, name: "wsize")
153                     .WithValueField(28, 4, out rsize, name: "rsize");
154                 registersMap.Add(
155                     (long)ChannelRegisters.NextConfig + ShiftBetweenChannels * channelNumber,
156                     nextConfigRegister
157                 );
158                 registersMap.Add(
159                     (long)ChannelRegisters.NextBytesLow + ShiftBetweenChannels * channelNumber,
160                     new DoubleWordRegister(this)
161                         .WithValueField(0, 32, out nextBytesLow)
162                 );
163                 registersMap.Add(
164                     (long)ChannelRegisters.NextBytesHigh + ShiftBetweenChannels * channelNumber,
165                     new DoubleWordRegister(this)
166                         .WithValueField(0, 32, out nextBytesHigh)
167                 );
168                 registersMap.Add(
169                     (long)ChannelRegisters.NextDestinationLow + ShiftBetweenChannels * channelNumber,
170                     new DoubleWordRegister(this)
171                         .WithValueField(0, 32, out nextDestinationLow)
172                 );
173                 registersMap.Add(
174                     (long)ChannelRegisters.NextDestinationHigh + ShiftBetweenChannels * channelNumber,
175                     new DoubleWordRegister(this)
176                         .WithValueField(0, 32, out nextDestinationHigh)
177                 );
178                 registersMap.Add(
179                     (long)ChannelRegisters.NextSourceLow + ShiftBetweenChannels * channelNumber,
180                     new DoubleWordRegister(this)
181                         .WithValueField(0, 32, out nextSourceLow)
182                 );
183                 registersMap.Add(
184                     (long)ChannelRegisters.NextSourceHigh + ShiftBetweenChannels * channelNumber,
185                     new DoubleWordRegister(this)
186                         .WithValueField(0, 32, out nextSourceHigh)
187                 );
188 
189                 // EXEC registers
190                 execConfig = new DoubleWordRegister(this).WithValueField(0, 32, FieldMode.Read);
191                 registersMap.Add(
192                     (long)ChannelRegisters.ExecConfig + ShiftBetweenChannels * channelNumber,
193                     execConfig
194                 );
195                 execBytesLow = new DoubleWordRegister(this).WithValueField(0, 32, FieldMode.Read);
196                 registersMap.Add(
197                     (long)ChannelRegisters.ExecBytesLow + ShiftBetweenChannels * channelNumber,
198                     execBytesLow
199                 );
200                 execBytesHigh = new DoubleWordRegister(this).WithValueField(0, 32, FieldMode.Read);
201                 registersMap.Add(
202                     (long)ChannelRegisters.ExecBytesHigh + ShiftBetweenChannels * channelNumber,
203                     execBytesHigh
204                 );
205                 execDestinationLow = new DoubleWordRegister(this).WithValueField(0, 32, FieldMode.Read);
206                 registersMap.Add(
207                     (long)ChannelRegisters.ExecDestinationLow + ShiftBetweenChannels * channelNumber,
208                     execDestinationLow
209                 );
210                 execDestinationHigh = new DoubleWordRegister(this).WithValueField(0, 32, FieldMode.Read);
211                 registersMap.Add(
212                     (long)ChannelRegisters.ExecDestinationHigh + ShiftBetweenChannels * channelNumber,
213                     execDestinationHigh
214                 );
215                 execSourceLow = new DoubleWordRegister(this).WithValueField(0, 32, FieldMode.Read);
216                 registersMap.Add(
217                     (long)ChannelRegisters.ExecSourceLow + ShiftBetweenChannels * channelNumber,
218                     execSourceLow
219                 );
220                 execSourceHigh = new DoubleWordRegister(this).WithValueField(0, 32, FieldMode.Read);
221                 registersMap.Add(
222                     (long)ChannelRegisters.ExecSourceHigh + ShiftBetweenChannels * channelNumber,
223                     execSourceHigh
224                 );
225                 registers = new DoubleWordRegisterCollection(this, registersMap);
226             }
227 
ReadDoubleWord(long offset)228             public uint ReadDoubleWord(long offset)
229             {
230                 return registers.Read(offset);
231             }
232 
WriteDoubleWord(long offset, uint value)233             public void WriteDoubleWord(long offset, uint value)
234             {
235                 registers.Write(offset, value);
236             }
237 
Reset()238             public void Reset()
239             {
240                 registers.Reset();
241                 DoneInterrupt.Unset();
242                 ErrorInterrupt.Unset();
243                 isClaimed = false;
244                 isRun = false;
245             }
246 
247             public GPIO DoneInterrupt { get; private set; }
248             public GPIO ErrorInterrupt { get; private set; }
249 
InitTransfer()250             private void InitTransfer()
251             {
252                 execConfig.Write(0, nextConfigRegister.Value);
253                 execBytesLow.Write(0, (uint)nextBytesLow.Value);
254                 execBytesHigh.Write(0, (uint)nextBytesHigh.Value);
255                 execSourceLow.Write(0, (uint)nextSourceLow.Value);
256                 execSourceHigh.Write(0, (uint)nextSourceHigh.Value);
257                 execDestinationLow.Write(0, (uint)nextDestinationLow.Value);
258                 execDestinationHigh.Write(0, (uint)nextDestinationHigh.Value);
259 
260                 ulong sourceAddress = nextSourceHigh.Value;
261                 sourceAddress = sourceAddress << 32;
262                 sourceAddress = sourceAddress + nextSourceLow.Value;
263 
264                 ulong destinationAddress = nextDestinationHigh.Value;
265                 destinationAddress = destinationAddress << 32;
266                 destinationAddress = destinationAddress + nextDestinationLow.Value;
267 
268                 ulong size = nextBytesHigh.Value;
269                 size = size << 32;
270                 size = size + nextBytesLow.Value;
271 
272                 parent.machine.LocalTimeSource.ExecuteInNearestSyncedState(_ =>
273                 {
274                     IssueCopy(sourceAddress, destinationAddress, size);
275                     FinishTransfer(sourceAddress, destinationAddress, size);
276                 });
277             }
278 
IssueCopy(ulong sourceAddress, ulong destinationAddress, ulong size)279             private void IssueCopy(ulong sourceAddress, ulong destinationAddress, ulong size)
280             {
281                 var dataLeft = size;
282                 while(dataLeft > 0)
283                 {
284                     var partSize = int.MaxValue;
285                     if(dataLeft <= int.MaxValue)
286                     {
287                         partSize = (int)dataLeft;
288                     }
289 
290                     var request = new Request(
291                         sourceAddress,
292                         destinationAddress,
293                         partSize,
294                         GetTransactionSize(TransactionDirection.Read, rsize.Value),
295                         GetTransactionSize(TransactionDirection.Write, wsize.Value)
296                     );
297                     parent.dmaEngine.IssueCopy(request);
298                     sourceAddress += (ulong)partSize;
299                     destinationAddress += (ulong)partSize;
300                     dataLeft -= (ulong)partSize;
301                 }
302             }
303 
FinishTransfer(ulong sourceAddress, ulong destinationAddress, ulong size)304             private void FinishTransfer(ulong sourceAddress, ulong destinationAddress, ulong size)
305             {
306                 execBytesLow.Write(0, 0);
307                 execBytesHigh.Write(0, 0);
308                 var execSource = sourceAddress + size;
309                 execSourceLow.Write(0, (uint)execSource);
310                 execSourceHigh.Write(0, (uint)(execSource >> 32));
311                 var execDestination = destinationAddress + size;
312                 execDestinationLow.Write(0, (uint)execDestination);
313                 execDestinationHigh.Write(0, (uint)(execDestination >> 32));
314 
315                 isClaimed = false;
316                 isDone.Value = true;
317                 isRun = false;
318                 if(doneInterruptEnabled.Value)
319                 {
320                     DoneInterrupt.Set();
321                 }
322             }
323 
GetTransactionSize(TransactionDirection direction, ulong val)324             private TransferType GetTransactionSize(TransactionDirection direction, ulong val)
325             {
326                 TransferType type;
327                 switch(val)
328                 {
329                     case 0:
330                         type = TransferType.Byte;
331                         break;
332                     case 1:
333                         type = TransferType.Word;
334                         break;
335                     case 2:
336                         type = TransferType.DoubleWord;
337                         break;
338                     default:
339                         type = TransferType.DoubleWord;
340                         this.Log(LogLevel.Warning, "{0} transaction size has been truncated to 4 bytes.", direction);
341                         break;
342                 }
343                 return type;
344             }
345 
346             private DoubleWordRegister nextConfigRegister;
347             private DoubleWordRegister execConfig;
348             private DoubleWordRegister execBytesLow;
349             private DoubleWordRegister execBytesHigh;
350             private DoubleWordRegister execDestinationLow;
351             private DoubleWordRegister execDestinationHigh;
352             private DoubleWordRegister execSourceLow;
353             private DoubleWordRegister execSourceHigh;
354             private bool isClaimed;
355             private bool isRun;
356             private IFlagRegisterField isDone;
357             private IValueRegisterField wsize;
358             private IValueRegisterField rsize;
359             private IFlagRegisterField repeat;
360             private IValueRegisterField nextBytesLow;
361             private IValueRegisterField nextBytesHigh;
362             private IValueRegisterField nextDestinationLow;
363             private IValueRegisterField nextDestinationHigh;
364             private IValueRegisterField nextSourceLow;
365             private IValueRegisterField nextSourceHigh;
366 
367             private readonly IFlagRegisterField doneInterruptEnabled;
368             private readonly DoubleWordRegisterCollection registers;
369             private readonly MPFS_PDMA parent;
370             private readonly int channelNumber;
371 
372             private enum TransactionDirection
373             {
374                 Read,
375                 Write
376             }
377 
378             private enum ChannelRegisters
379             {
380                 Control = 0x000,
381                 NextConfig = 0x004,
382                 NextBytesLow = 0x008,
383                 NextBytesHigh = 0x00C,
384                 NextDestinationLow = 0x010,
385                 NextDestinationHigh = 0x014,
386                 NextSourceLow = 0x018,
387                 NextSourceHigh = 0x01C,
388                 ExecConfig = 0x104,
389                 ExecBytesLow = 0x108,
390                 ExecBytesHigh = 0x10C,
391                 ExecDestinationLow = 0x110,
392                 ExecDestinationHigh = 0x114,
393                 ExecSourceLow = 0x118,
394                 ExecSourceHigh = 0x11C,
395             }
396         }
397     }
398 }