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.Core;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Peripherals.Bus;
12 using System.Collections.Generic;
13 using System.Collections.ObjectModel;
14 
15 namespace Antmicro.Renode.Peripherals.DMA
16 {
17     public sealed class TegraDma : IDoubleWordPeripheral, IKnownSize, INumberedGPIOOutput
18     {
TegraDma(IMachine machine)19         public TegraDma(IMachine machine)
20         {
21             dmaEngine = new DmaEngine(machine.GetSystemBus(this));
22             channels = new Channel[ChannelNo];
23 
24             var innerConnections = new Dictionary<int, IGPIO>();
25             for(var i = 0; i < channels.Length; i++)
26             {
27                 channels[i] = new Channel(this, i);
28                 innerConnections[i] = channels[i].IRQ;
29             }
30 
31             Connections = new ReadOnlyDictionary<int, IGPIO>(innerConnections);
32             Reset();
33         }
34 
35         public long Size
36         {
37             get
38             {
39                 return 0x2000;
40             }
41         }
42 
ReadDoubleWord(long offset)43         public uint ReadDoubleWord(long offset)
44         {
45             if(offset >= 0x1000 && offset < 0x1000 + 32*ChannelNo)
46             {
47                 return channels[(offset - 0x1000)/32].Read(offset % 32);
48             }
49             switch((Register)offset)
50             {
51             case Register.Counter:
52                 return counterRegister;
53             case Register.Status:
54                 // currently we're never busy
55                 return activeChannelInterrupts;
56             default:
57                 this.LogUnhandledRead(offset);
58                 return 0;
59             }
60         }
61 
WriteDoubleWord(long offset, uint value)62         public void WriteDoubleWord(long offset, uint value)
63         {
64             if(offset >= 0x1000 && offset < 0x1000 + 32*ChannelNo)
65             {
66                 channels[(offset - 0x1000)/32].Write(offset % 32, value);
67                 return;
68             }
69             switch((Register)offset)
70             {
71             case Register.Command:
72                 enabled = ((1 << 31) & value) != 0;
73                 break;
74             case Register.Counter:
75                 // throttle down is ignored by us, we only keep the value for the reads
76                 counterRegister = value;
77                 break;
78             case Register.IrqMaskSet:
79                 irqMask |= value;
80                 break;
81             case Register.IrqMaskClear:
82                 irqMask &= ~value;
83                 break;
84             default:
85                 this.LogUnhandledWrite(offset, value);
86                 break;
87             }
88         }
89 
Reset()90         public void Reset()
91         {
92             enabled = false;
93             counterRegister = 0;
94             irqMask = 0;
95             activeChannelInterrupts = 0;
96             for(var i = 0; i < channels.Length; i++)
97             {
98                 channels[i].Reset();
99             }
100         }
101 
102         private enum Register
103         {
104             Command = 0x0,
105             Status = 0x4,
106             Counter = 0x10,
107             IrqMaskSet = 0x20,
108             IrqMaskClear = 0x24
109         }
110 
111         private sealed class Channel
112         {
Channel(TegraDma parent, int number)113             public Channel(TegraDma parent, int number)
114             {
115                 this.parent = parent;
116                 channelNumber = number;
117                 IRQ = new GPIO();
118             }
119 
Reset()120             public void Reset()
121             {
122                 apbStartingAddress = 0;
123                 ahbStartingAddress =0;
124                 interruptEnabled = false;
125                 apbIsSource = false;
126                 apbTransferType = TransferType.Byte;
127                 ahbTransferType= TransferType.Byte;
128             }
129 
130             public GPIO IRQ { get; private set; }
131 
Read(long offset)132             public uint Read(long offset)
133             {
134                 switch((ChannelRegister)offset)
135                 {
136                 case ChannelRegister.Status:
137                     if((parent.activeChannelInterrupts & (1 << channelNumber)) != 0)
138                     {
139                         return 1 << 30;
140                     }
141                     return 0;
142                 default:
143                     parent.Log(LogLevel.Warning, "Unhandled read from 0x{1:X} (channel {0}).", offset, channelNumber);
144                     return 0;
145                 }
146             }
147 
Write(long offset, uint value)148             public void Write(long offset, uint value)
149             {
150                 switch((ChannelRegister)offset)
151                 {
152                 case ChannelRegister.Control:
153                     HandleControlWrite(value);
154                     break;
155                 case ChannelRegister.Status:
156                     if((value & (1 << 30)) == 0)
157                     {
158                         break;
159                     }
160                     parent.activeChannelInterrupts &= (ushort)~(1 << channelNumber);
161                     IRQ.Unset();
162                     break;
163                 case ChannelRegister.AhbStartingAddress:
164                     ahbStartingAddress = value;
165                     break;
166                 case ChannelRegister.ApbStartingAddress:
167                     apbStartingAddress = 0x70000000;
168                     apbStartingAddress |= value & 0xFFFF;
169                     break;
170                 case ChannelRegister.ApbAddressSequencer:
171                     // TODO: currently we ignore the send interrupt to COP option
172                     apbTransferType = GetTransferType(value);
173                     if((value & ~0xF0010000) != 0)
174                     {
175                         parent.Log(LogLevel.Warning, "Channel {0}: unsupported APB sequencer flags, value written is 0x{1:X}.", channelNumber, value);
176                     }
177                     break;
178                 case ChannelRegister.AhbAddressSequencer:
179                     // TODO: currently we ignore the send interrupt to COP option
180                     ahbTransferType = GetTransferType(value);
181                     if((value & ~0xF4000000) != 0)
182                     {
183                         parent.Log(LogLevel.Warning, "Channel {0}: unsupported AHB sequencer flags, value written is 0x{1:X}.", channelNumber, value);
184                     }
185                     break;
186                 default:
187                     parent.Log(LogLevel.Warning, "Unhandler write to offset 0x{1:X}, value 0x{2:X} (channel {0}).", channelNumber, offset, value);
188                     break;
189                 }
190             }
191 
GetTransferType(uint value)192             private TransferType GetTransferType(uint value)
193             {
194                 var busWidth = value >> 28;
195                 switch(busWidth)
196                 {
197                 case 0:
198                     return TransferType.Byte;
199                 case 1:
200                     return TransferType.Word;
201                 default:
202                     return TransferType.DoubleWord;
203                 }
204             }
205 
HandleControlWrite(uint value)206             private void HandleControlWrite(uint value)
207             {
208                 interruptEnabled = ((1 << 30) & value) != 0;
209                 var start = ((1 << 31) & value) != 0;
210                 apbIsSource = ((1 << 28) & value) == 0;
211                 if(((1 << 27) & value) == 0)
212                 {
213                     parent.Log(LogLevel.Warning, "Channel {0}: unsupported multi block transfer.", channelNumber);
214                 }
215                 // check whether we have some not supported flags
216                 // note that we also ignore bit 21, i.e. flow control enable
217                 // D020 = enabled bits 21, 27, 28, 30 and 31
218                 if((value & (~0xD820FFFF)) != 0)
219                 {
220                     parent.Log(LogLevel.Warning, "Channel {0}: unsupported flags in channel control register (value written was 0x{1:X}.", channelNumber, value);
221                 }
222                 if(start && parent.enabled)
223                 {
224                     var sourceAddress = apbIsSource ? apbStartingAddress : ahbStartingAddress;
225                     var destinationAddress = apbIsSource ? ahbStartingAddress : apbStartingAddress;
226                     var sourceTransferType = apbIsSource ? apbTransferType : ahbTransferType;
227                     var destinationTransferType = apbIsSource ? ahbTransferType : apbTransferType;
228                     var size = ((((int)value & 0xFFFF) >> 2) + 1) << 2;
229                     parent.DebugLog("Channel {0}: issuing copy: 0x{1:X} ({2}) ---> 0x{3:X} ({4}), size {5}B.", channelNumber, sourceAddress, sourceTransferType,
230                                     destinationAddress, destinationTransferType, size);
231                     parent.dmaEngine.IssueCopy(new Request(sourceAddress, destinationAddress, size, sourceTransferType, destinationTransferType));
232                     parent.activeChannelInterrupts |= (ushort)(1 << channelNumber);
233                     if(interruptEnabled)
234                     {
235                         IRQ.Set();
236                     }
237                 }
238             }
239 
240             private enum ChannelRegister
241             {
242                 Control = 0x0,
243                 Status = 0x4,
244                 AhbStartingAddress = 0x10,
245                 AhbAddressSequencer = 0x14,
246                 ApbStartingAddress = 0x18,
247                 ApbAddressSequencer = 0x1c,
248             }
249 
250             private uint apbStartingAddress;
251             private uint ahbStartingAddress;
252             private readonly TegraDma parent;
253             private bool interruptEnabled;
254             private readonly int channelNumber;
255             private bool apbIsSource;
256             private TransferType apbTransferType;
257             private TransferType ahbTransferType;
258         }
259 
260         private bool enabled;
261         private uint counterRegister;
262         private uint irqMask;
263         private readonly Channel[] channels;
264         private readonly DmaEngine dmaEngine;
265         private ushort activeChannelInterrupts;
266 
267         public IReadOnlyDictionary<int, IGPIO> Connections { get; private set; }
268 
269         private const int ChannelNo = 16;
270     }
271 }
272 
273