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;
8 using System.Collections.Generic;
9 using System.Collections.ObjectModel;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.Bus;
14 
15 namespace Antmicro.Renode.Peripherals.DMA
16 {
17     public class STM32G0DMA : IDoubleWordPeripheral, IKnownSize, IGPIOReceiver, INumberedGPIOOutput
18     {
STM32G0DMA(IMachine machine, int numberOfChannels)19         public STM32G0DMA(IMachine machine, int numberOfChannels)
20         {
21             this.machine = machine;
22             engine = new DmaEngine(machine.GetSystemBus(this));
23             this.numberOfChannels = numberOfChannels;
24             channels = new Channel[numberOfChannels];
25             var innerConnections = new Dictionary<int, IGPIO>();
26             for(var i = 0; i < numberOfChannels; ++i)
27             {
28                 channels[i] = new Channel(this, i);
29                 innerConnections[i] = new GPIO();
30             }
31 
32             Connections = new ReadOnlyDictionary<int, IGPIO>(innerConnections);
33 
34             var interruptStatus = new DoubleWordRegister(this);
35             var interruptFlagClear = new DoubleWordRegister(this)
36                     .WithWriteCallback((_, __) => Update());
37 
38             for(var i = 0; i < numberOfChannels; ++i)
39             {
40                 var j = i;
41                 interruptStatus.DefineFlagField(j * 4 + 0, FieldMode.Read,
42                     valueProviderCallback: _ => channels[j].GlobalInterrupt,
43                     name: $"Global interrupt flag for channel {j} (GIF{j})");
44                 interruptStatus.DefineFlagField(j * 4 + 1, FieldMode.Read,
45                     valueProviderCallback: _ => channels[j].TransferComplete,
46                     name: $"Transfer complete flag for channel {j} (TCIF{j})");
47                 interruptStatus.DefineFlagField(j * 4 + 2, FieldMode.Read,
48                     valueProviderCallback: _ => channels[j].HalfTransfer,
49                     name: $"Half transfer flag for channel {j} (HTIF{j})");
50                 interruptStatus.Tag($"Transfer error flag for channel {j} (TEIF{j})", j * 4 + 3, 1);
51 
52                 interruptFlagClear.DefineFlagField(j * 4 + 0, FieldMode.Write,
53                     writeCallback: (_, val) =>
54                     {
55                         if(!val)
56                         {
57                             return;
58                         }
59                         channels[j].GlobalInterrupt = false;
60                         channels[j].TransferComplete = false;
61                         channels[j].HalfTransfer = false;
62                     },
63                     name: $"Global interrupt flag clear for channel {j} (CGIF{j})");
64                 interruptFlagClear.DefineFlagField(j * 4 + 1, FieldMode.Write,
65                     writeCallback: (_, val) =>
66                     {
67                         if(!val)
68                         {
69                             return;
70                         }
71                         channels[j].TransferComplete = false;
72                         channels[j].GlobalInterrupt = false;
73                     },
74                     name: $"Transfer complete flag clear for channel {j} (CTEIF{j})");
75                 interruptFlagClear.DefineFlagField(j * 4 + 2, FieldMode.Write,
76                     writeCallback: (_, val) =>
77                     {
78                         if(!val)
79                         {
80                             return;
81                         }
82                         channels[j].HalfTransfer = false;
83                         channels[j].GlobalInterrupt = false;
84                     },
85                     name: $"Half transfer flag clear for channel {j} (CHTIF{j})");
86                 interruptFlagClear.Tag($"Transfer error flag clear for channel {j} (CTEIF{j})", j * 4 + 3, 1);
87             }
88 
89             var channelSelection = new DoubleWordRegister(this)
90                 .WithTag("DMA channel 1 selection (C1S)", 0, 4)
91                 .WithTag("DMA channel 2 selection (C2S)", 4, 4)
92                 .WithTag("DMA channel 3 selection (C3S)", 8, 4)
93                 .WithTag("DMA channel 4 selection (C4S)", 12, 4)
94                 .WithTag("DMA channel 5 selection (C5S)", 16, 4)
95                 .WithTag("DMA channel 6 selection (C6S)", 20, 4)
96                 .WithTag("DMA channel 7 selection (C7S)", 24, 4)
97                 .WithReservedBits(28, 4);
98 
99             var registerMap = new Dictionary<long, DoubleWordRegister>
100             {
101                 {(long)Registers.InterruptStatus, interruptStatus },
102                 {(long)Registers.InterruptFlagClear, interruptFlagClear },
103                 {(long)Registers.ChannelSelection, channelSelection },
104             };
105 
106             registers = new DoubleWordRegisterCollection(this, registerMap);
107         }
108 
Reset()109         public void Reset()
110         {
111             registers.Reset();
112         }
113 
ReadDoubleWord(long offset)114         public uint ReadDoubleWord(long offset)
115         {
116             if(registers.TryRead(offset, out var result))
117             {
118                 return result;
119             }
120             if(TryGetChannelNumberBasedOnOffset(offset, out var channelNo))
121             {
122                 return channels[channelNo].ReadDoubleWord(offset);
123             }
124             this.Log(LogLevel.Error, "Could not read from offset 0x{0:X} nor read from channel {1}, the channel has to be in range 0-{2}", offset, channelNo, numberOfChannels);
125             return 0;
126         }
127 
WriteDoubleWord(long offset, uint value)128         public void WriteDoubleWord(long offset, uint value)
129         {
130             if(registers.TryWrite(offset, value))
131             {
132                 return;
133             }
134             if(TryGetChannelNumberBasedOnOffset(offset, out var channelNo))
135             {
136                 channels[channelNo].WriteDoubleWord(offset, value);
137                 return;
138             }
139             this.Log(LogLevel.Error, "Could not write to offset 0x{0:X} nor write to channel {1}, the channel has to be in range 0-{2}", offset, channelNo, numberOfChannels);
140         }
141 
OnGPIO(int number, bool value)142         public void OnGPIO(int number, bool value)
143         {
144             if(number == 0 || number > channels.Length)
145             {
146                 this.Log(LogLevel.Error, "Channel number {0} is out of range, must be in [1; {1}]", number, channels.Length);
147                 return;
148             }
149 
150             if(!value)
151             {
152                 return;
153             }
154 
155             this.Log(LogLevel.Debug, "DMA peripheral request on channel {0}", number);
156             if(!channels[number - 1].TryTriggerTransfer())
157             {
158                 this.Log(LogLevel.Warning, "DMA peripheral request on channel {0} ignored - channel is disabled "
159                     + "or has data count set to 0", number);
160             }
161         }
162 
163         public IReadOnlyDictionary<int, IGPIO> Connections { get; }
164 
165         public long Size => 0x100;
166 
TryGetChannelNumberBasedOnOffset(long offset, out int channel)167         private bool TryGetChannelNumberBasedOnOffset(long offset, out int channel)
168         {
169             var shifted = offset - (long)Registers.Channel1Configuration;
170             channel = (int)(shifted / ShiftBetweenChannels);
171             if(channel < 0 || channel > numberOfChannels)
172             {
173                 return false;
174             }
175             return true;
176         }
177 
Update()178         private void Update()
179         {
180             for(var i = 0; i < numberOfChannels; ++i)
181             {
182                 Connections[i].Set((channels[i].TransferComplete && channels[i].TransferCompleteInterruptEnable)
183                         || (channels[i].HalfTransfer && channels[i].HalfTransferInterruptEnable));
184             }
185         }
186 
187         private readonly IMachine machine;
188         private readonly DmaEngine engine;
189         private readonly DoubleWordRegisterCollection registers;
190         private readonly Channel[] channels;
191         private readonly int numberOfChannels;
192 
193         private const int ShiftBetweenChannels = (int)((long)Registers.Channel2Configuration - (long)Registers.Channel1Configuration);
194 
195         private class Channel
196         {
Channel(STM32G0DMA parent, int number)197             public Channel(STM32G0DMA parent, int number)
198             {
199                 this.parent = parent;
200                 channelNumber = number;
201 
202                 var registersMap = new Dictionary<long, DoubleWordRegister>();
203                 registersMap.Add((long)ChannelRegisters.ChannelConfiguration + (number * ShiftBetweenChannels), new DoubleWordRegister(parent)
204                     .WithFlag(0, out channelEnable,
205                         writeCallback: (_, val) =>
206                         {
207                             if(!val)
208                             {
209                                 return;
210                             }
211                             if(memoryToMemory.Value || transferDirection.Value == TransferDirection.MemoryToPeripheral)
212                             {
213                                 DoTransfer();
214                             }
215                         },
216                         valueProviderCallback: _ => false, name: "Channel enable (EN)")
217                     .WithFlag(1, out transferCompleteInterruptEnable, name: "Transfer complete interrupt enable (TCIE)")
218                     .WithFlag(2, out halfTransferInterruptEnable, name: "Half transfer interrupt enable (HTIE)")
219                     .WithTag("Transfer error interrupt enable (TEIE)", 3, 1)
220                     .WithEnumField(4, 1, out transferDirection, name: "Data transfer direction (DIR)")
221                     .WithFlag(5, out circularMode, name: "Circular mode (CIRC)")
222                     .WithFlag(6, out peripheralIncrementMode, name: "Peripheral increment mode (PINC)")
223                     .WithFlag(7, out memoryIncrementMode, name: "Memory increment mode (MINC)")
224                     .WithEnumField(8, 2, out peripheralTransferType, name: "Peripheral size (PSIZE)")
225                     .WithEnumField(10, 2, out memoryTransferType, name: "Memory size (MSIZE)")
226                     .WithTag("Priority level (PL)", 12, 2)
227                     .WithFlag(14, out memoryToMemory, name: "Memory-to-memory mode (MEM2MEM)")
228                     .WithReservedBits(15, 17)
229                     .WithWriteCallback(
230                             (_, __) => parent.Update()));
231 
232                 // The ChannelDataCount register stores the currently remaining number of data units to copy.
233                 // It is decremented on each transfer. The originalDataCount field stores the original value
234                 // written by software in order to implement the half transfer interrupt.
235                 registersMap.Add((long)ChannelRegisters.ChannelDataCount + (number * ShiftBetweenChannels), new DoubleWordRegister(parent)
236                     .WithValueField(0, 16, out dataCount, name: "Number of data to transfer (NDT)",
237                         writeCallback: (_, val) => originalDataCount = val)
238                     .WithReservedBits(16, 16));
239 
240                 // The ChannelPeripheralAddress and ChannelMemoryAddress registers retain the value written
241                 // by software. This value is read by the model during circular mode wraparound.
242                 // The incremented address is stored in the currentAddress fields.
243                 registersMap.Add((long)ChannelRegisters.ChannelPeripheralAddress + (number * ShiftBetweenChannels), new DoubleWordRegister(parent)
244                     .WithValueField(0, 32, out peripheralAddress,
245                         writeCallback: (_, val) => currentPeripheralAddress = val, name: "Peripheral address (PA)"));
246 
247                 registersMap.Add((long)ChannelRegisters.ChannelMemoryAddress + (number * ShiftBetweenChannels), new DoubleWordRegister(parent)
248                     .WithValueField(0, 32, out memoryAddress,
249                         writeCallback: (_, val) => currentMemoryAddress = val, name: "Memory address (MA)"));
250 
251                 registers = new DoubleWordRegisterCollection(parent, registersMap);
252             }
253 
ReadDoubleWord(long offset)254             public uint ReadDoubleWord(long offset)
255             {
256                 return registers.Read(offset);
257             }
258 
WriteDoubleWord(long offset, uint value)259             public void WriteDoubleWord(long offset, uint value)
260             {
261                 registers.Write(offset, value);
262             }
263 
Reset()264             public void Reset()
265             {
266                 registers.Reset();
267                 TransferComplete = false;
268                 HalfTransfer = false;
269             }
270 
TryTriggerTransfer()271             public bool TryTriggerTransfer()
272             {
273                 if(!Enabled || dataCount.Value == 0)
274                 {
275                     return false;
276                 }
277 
278                 DoTransfer();
279                 parent.Update();
280                 return true;
281             }
282 
283             public bool GlobalInterrupt
284             {
285                 get
286                 {
287                     return HalfTransfer || TransferComplete;
288                 }
289                 set
290                 {
291                     if(value)
292                     {
293                         return;
294                     }
295                     HalfTransfer = false;
296                     TransferComplete = false;
297                 }
298             }
299 
300             public bool Enabled => channelEnable.Value;
301 
302             public bool HalfTransfer { get; set; }
303 
304             public bool TransferComplete { get; set; }
305 
306             public bool HalfTransferInterruptEnable => halfTransferInterruptEnable.Value;
307 
308             public bool TransferCompleteInterruptEnable => transferCompleteInterruptEnable.Value;
309 
DoTransfer()310             private void DoTransfer()
311             {
312                 // This value is still valid in memory-to-memory mode, "peripheral" means
313                 // "the address specified by the peripheralAddress field" and not necessarily
314                 // a peripheral.
315                 if(transferDirection.Value == TransferDirection.PeripheralToMemory)
316                 {
317                     var toCopy = (uint)dataCount.Value;
318                     // In peripheral-to-memory mode, only copy one data unit. Otherwise, do the whole block.
319                     if(!memoryToMemory.Value)
320                     {
321                         toCopy = Math.Max((uint)SizeToType(memoryTransferType.Value),
322                             (uint)SizeToType(peripheralTransferType.Value));
323                         dataCount.Value -= 1;
324                     }
325                     else
326                     {
327                         dataCount.Value = 0;
328                     }
329                     var response = IssueCopy(currentPeripheralAddress, currentMemoryAddress, toCopy,
330                         peripheralIncrementMode.Value, memoryIncrementMode.Value, peripheralTransferType.Value,
331                         memoryTransferType.Value);
332                     currentPeripheralAddress = response.ReadAddress.Value;
333                     currentMemoryAddress = response.WriteAddress.Value;
334                     HalfTransfer = dataCount.Value <= originalDataCount / 2;
335                     TransferComplete = dataCount.Value == 0;
336                 }
337                 else // 1-bit field, so we handle both possible values
338                 {
339                     IssueCopy(memoryAddress.Value, peripheralAddress.Value, (uint)dataCount.Value,
340                         memoryIncrementMode.Value, peripheralIncrementMode.Value, memoryTransferType.Value,
341                         peripheralTransferType.Value);
342                     dataCount.Value = 0;
343                     HalfTransfer = true;
344                     TransferComplete = true;
345                 }
346 
347                 // Loop around if circular mode is enabled
348                 if(circularMode.Value && !memoryToMemory.Value && dataCount.Value == 0)
349                 {
350                     dataCount.Value = originalDataCount;
351                     currentPeripheralAddress = peripheralAddress.Value;
352                     currentMemoryAddress = memoryAddress.Value;
353                 }
354                 // No parent.Update - this is called by the register write and TryTriggerTransfer
355                 // to avoid calling it twice in the former case
356             }
357 
IssueCopy(ulong sourceAddress, ulong destinationAddress, uint size, bool incrementReadAddress, bool incrementWriteAddress, TransferSize sourceTransferType, TransferSize destinationTransferType)358             private Response IssueCopy(ulong sourceAddress, ulong destinationAddress, uint size,
359                 bool incrementReadAddress, bool incrementWriteAddress, TransferSize sourceTransferType,
360                 TransferSize destinationTransferType)
361             {
362                 var request = new Request(
363                     sourceAddress,
364                     destinationAddress,
365                     (int)size,
366                     SizeToType(sourceTransferType),
367                     SizeToType(destinationTransferType),
368                     incrementReadAddress,
369                     incrementWriteAddress
370                 );
371                 return parent.engine.IssueCopy(request);
372             }
373 
SizeToType(TransferSize size)374             private TransferType SizeToType(TransferSize size)
375             {
376                 switch(size)
377                 {
378                     case TransferSize.Bits32:
379                         return TransferType.DoubleWord;
380                     case TransferSize.Bits16:
381                         return TransferType.Word;
382                     case TransferSize.Bits8:
383                     default:
384                         return TransferType.Byte;
385                 }
386             }
387 
388 
389             private IEnumRegisterField<TransferDirection> transferDirection;
390             private IFlagRegisterField circularMode;
391             private IFlagRegisterField peripheralIncrementMode;
392             private IFlagRegisterField memoryIncrementMode;
393             private IFlagRegisterField memoryToMemory;
394             private IValueRegisterField dataCount;
395             private IValueRegisterField memoryAddress;
396             private IValueRegisterField peripheralAddress;
397             private IFlagRegisterField channelEnable;
398             private IFlagRegisterField transferCompleteInterruptEnable;
399             private IFlagRegisterField halfTransferInterruptEnable;
400             private IEnumRegisterField<TransferSize> memoryTransferType;
401             private IEnumRegisterField<TransferSize> peripheralTransferType;
402             private ulong currentPeripheralAddress;
403             private ulong currentMemoryAddress;
404             private ulong originalDataCount;
405 
406             private readonly DoubleWordRegisterCollection registers;
407             private readonly STM32G0DMA parent;
408             private readonly int channelNumber;
409 
410             private enum TransferSize
411             {
412                 Bits8 = 0,
413                 Bits16 = 1,
414                 Bits32 = 2,
415                 Reserved = 3
416             }
417 
418             private enum TransferDirection
419             {
420                 PeripheralToMemory = 0,
421                 MemoryToPeripheral = 1,
422             }
423 
424             private enum ChannelRegisters
425             {
426                 ChannelConfiguration = 0x8,
427                 ChannelDataCount = 0xC,
428                 ChannelPeripheralAddress = 0x10,
429                 ChannelMemoryAddress = 0x14,
430             }
431         }
432 
433         private enum Registers : long
434         {
435             InterruptStatus = 0x0,
436             InterruptFlagClear = 0x4,
437 
438             Channel1Configuration = 0x8,
439             Channel1DataCount = 0xC,
440             Channel1PeripheralAddress = 0x10,
441             Channel1MemoryAddress = 0x14,
442 
443             Channel2Configuration = 0x1C,
444             Channel2DataCount = 0x20,
445             Channel2PeripheralAddress = 0x24,
446             Channel2MemoryAddress = 0x28,
447 
448             Channel3Configuration = 0x30,
449             Channel3DataCount = 0x34,
450             Channel3PeripheralAddress = 0x38,
451             Channel3MemoryAddress = 0x3c,
452 
453             Channel4Configuration = 0x44,
454             Channel4DataCount = 0x48,
455             Channel4PeripheralAddress = 0x4c,
456             Channel4MemoryAddress = 0x50,
457 
458             Channel5Configuration = 0x58,
459             Channel5DataCount = 0x5c,
460             Channel5PeripheralAddress = 0x60,
461             Channel5MemoryAddress = 0x64,
462 
463             Channel6Configuration = 0x6C,
464             Channel6DataCount = 0x70,
465             Channel6PeripheralAddress = 0x74,
466             Channel6MemoryAddress = 0x78,
467 
468             Channel7Configuration = 0x80,
469             Channel7DataCount = 0x84,
470             Channel7PeripheralAddress = 0x88,
471             Channel7MemoryAddress = 0x8c,
472             // 0x90 to 0xA4 - Reserved
473             ChannelSelection = 0xA8
474         }
475     }
476 }
477