1 //
2 // Copyright (c) 2010-2024 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 Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Utilities;
13 using Antmicro.Renode.Logging;
14 
15 namespace Antmicro.Renode.Peripherals.DMA
16 {
17     public abstract class RenesasDA_DMABase : IProvidesRegisterCollection<DoubleWordRegisterCollection>, IDoubleWordPeripheral, IGPIOReceiver
18     {
RenesasDA_DMABase(IMachine machine, int channelCount, int peripheralSelectCount)19         public RenesasDA_DMABase(IMachine machine, int channelCount, int peripheralSelectCount)
20         {
21             this.machine = machine;
22             interruptsManager = new InterruptManager<Interrupt>(this, IRQ);
23             this.channelCount = channelCount;
24             channels = new Channel[channelCount];
25             for(int i = 0; i < channelCount; i++)
26             {
27                 channels[i] = new Channel(this, i);
28             }
29             RegistersCollection = new DoubleWordRegisterCollection(this, BuildRegisterMap());
30             peripheralSelect = new IValueRegisterField[peripheralSelectCount];
31         }
32 
Reset()33         public void Reset()
34         {
35             RegistersCollection.Reset();
36             interruptsManager.Reset();
37             foreach(var channel in channels)
38             {
39                 channel.Reset();
40             }
41         }
42 
ReadDoubleWord(long offset)43         public uint ReadDoubleWord(long offset)
44         {
45             return RegistersCollection.Read(offset);
46         }
47 
WriteDoubleWord(long offset, uint mask)48         public void WriteDoubleWord(long offset, uint mask)
49         {
50             RegistersCollection.Write(offset, mask);
51         }
52 
53         /*
54          GPIO number is used to identify a peripheral that requested DMA transfer and its direction (rx/tx).
55         */
OnGPIO(int number, bool value)56         public void OnGPIO(int number, bool value)
57         {
58             var peripheralSource = number / 2;
59             var oddChannel = number % 2;
60 
61             // See Table 528: DMA_REQ_MUX_REG.
62             if(peripheralSource < 0 || peripheralSource >= 16)
63             {
64                 this.ErrorLog("DMA request from unknown source: {0}. Allowed values are in the range 0-15", peripheralSource);
65                 return;
66             }
67 
68             if(!value)
69             {
70                 return;
71             }
72             // Documentation 35.21
73             // DMA_REQ_MUX_REG - Select which combination of peripherals are mapped on the DMA channels.
74             var index = Array.FindIndex(peripheralSelect, val => (int)val.Value == peripheralSource);
75             if(index != -1)
76             {
77                 var channelID = index * 2 + oddChannel;
78                 if(!channels[channelID].dmaEnabled.Value)
79                 {
80                     this.Log(LogLevel.Warning, "Channel {0} isn't enabled. Ignoring request", channelID);
81                     return;
82                 }
83                 channels[channelID].DoTransfer();
84             }
85             else
86             {
87                 this.Log(LogLevel.Warning, "No DMA channel is programmed to handle request 0x{0:X}. Ignoring request", number);
88             }
89         }
90 
91         public DoubleWordRegisterCollection RegistersCollection { get; }
92 
93         public GPIO IRQ { get; } = new GPIO();
94 
95         protected readonly Channel[] channels;
96         protected readonly InterruptManager<Interrupt> interruptsManager;
97         protected readonly IValueRegisterField[] peripheralSelect;
98 
BuildRegisterMap()99         private Dictionary<long, DoubleWordRegister> BuildRegisterMap()
100         {
101             var registerMap = new Dictionary<long, DoubleWordRegister>();
102             for(long i = 0; i < channelCount; i++)
103             {
104                 long channelID = i;
105                 long offset = 0x20 * i;
106                 registerMap.Add(offset + (long)Registers.SourceAddress_0, new DoubleWordRegister(this)
107                     .WithValueField(0, 32,
108                         valueProviderCallback: (_) => channels[channelID].SourceAddress,
109                         writeCallback: (_, val) => { channels[channelID].SourceAddress = val; },
110                         name: $"DMA{i}_A_START")
111                 );
112                 registerMap.Add(offset + (long)Registers.DestinationAddress_0, new DoubleWordRegister(this)
113                     .WithValueField(0, 32,
114                         valueProviderCallback: (_) => channels[channelID].DestinationAddress,
115                         writeCallback: (_, val) => {channels[channelID].DestinationAddress = val; },
116                         name: $"DMA{i}_B_START")
117                 );
118                 registerMap.Add(offset + (long)Registers.InterruptLength_0, new DoubleWordRegister(this)
119                     .WithValueField(0, 16, out channels[i].interruptLength, name: $"DMA{i}_INT")
120                     .WithReservedBits(16, 16)
121                 );
122                 registerMap.Add(offset + (long)Registers.TransferLength_0, new DoubleWordRegister(this)
123                     .WithValueField(0, 16, out channels[i].transferLength, name: $"DMA{i}_LEN")
124                     .WithReservedBits(16, 16)
125                 );
126                 registerMap.Add(offset + (long)Registers.Control_0, new DoubleWordRegister(this, 0x00018000)
127                     .WithFlag(0, out channels[i].dmaEnabled, name: $"DMA{i}_ON", writeCallback: (_, value) =>
128                         {
129                             if(value && !channels[channelID].peripheralTriggered.Value)
130                             {
131                                 channels[channelID].itemsAlreadyTransferred.Value = 0;
132                                 channels[channelID].DoTransfer();
133                             }
134                         })
135                     .WithValueField(1, 2, name: "BW", writeCallback: (_, value) =>
136                       {
137                           if(value == 3)
138                           {
139                               this.Log(LogLevel.Warning, "The bus width value cannot be set to {0}", value);
140                               return;
141                           }
142                           channels[channelID].transferType = (TransferType)Math.Pow(2, value);
143                       })
144                     .WithFlag(3, out channels[i].peripheralTriggered, name: "DREQ_MODE")
145                     .WithFlag(4, out channels[i].incrementDestinationAddress, name: "BINC")
146                     .WithFlag(5, out channels[i].incrementSourceAddress, name: "AINC")
147                     .WithFlag(6, out channels[i].circularMode, name: "CIRCULAR")
148                     .WithTag("DMA_PRIO", 7, 3)
149                     .WithTaggedFlag("DMA_IDLE", 10)
150                     .WithFlag(11, out channels[i].dmaInit, name: "DMA_INIT")
151                     .WithTaggedFlag("REQ_SENSE", 12)
152                     .WithEnumField(13, 2, out channels[i].burstMode, name: "BURST_MODE")
153                     .WithTaggedFlag("BUS_ERROR_DETECT", 15)
154                     .WithTaggedFlag("DMA_EXCLUSIVE_ACCESS", 16)
155                     .WithReservedBits(17, 15)
156                 );
157                 registerMap.Add(offset + (long)Registers.IndexPointer_0, new DoubleWordRegister(this)
158                     .WithValueField(0, 16, out channels[i].itemsAlreadyTransferred, FieldMode.Read, name: $"DMA{i}_IDX")
159                     .WithReservedBits(16, 16)
160                 );
161             }
162 
163             return registerMap;
164         }
165 
166         private readonly IMachine machine;
167         private readonly int channelCount;
168 
169         protected class Channel
170         {
Channel(RenesasDA_DMABase parent, int channelNumber)171             public Channel(RenesasDA_DMABase parent, int channelNumber)
172             {
173                 this.parent = parent;
174                 this.channelNumber = channelNumber;
175                 engine = new DmaEngine(parent.machine.GetSystemBus(parent));
176             }
177 
Reset()178             public void Reset()
179             {
180                 transferCompleted = false;
181             }
182 
DoTransfer()183             public void DoTransfer()
184             {
185                 // Table 497: DMA0_IDX_REG
186                 // When the transfer is completed and circular mode is not set,
187                 // itemsAlreadyTransferred is automatically reset to 0 upon starting a new transfer.
188                 if(transferCompleted && !circularMode.Value)
189                 {
190                     transferCompleted = false;
191                     itemsAlreadyTransferred.Value = 0;
192                 }
193 
194                 var bytesToTransfer = (int)(transferLength.Value + 1) * (int)transferType;
195                 if((ulong)bytesToTransfer == itemsAlreadyTransferred.Value)
196                 {
197                     parent.Log(LogLevel.Noisy, "All requested data are already transfered. Skipping this transfer.");
198                     return;
199                 }
200 
201                 // In normal mode, we are instantly copying requested data size,
202                 // in peripheral trigger mode, peripheral should trigger DMA on each sample
203                 // unless burst mode is selected which requests a few samples at once.
204                 var transactionLength = peripheralTriggered.Value ? (int)transferType * GetBurstCount(burstMode.Value) : bytesToTransfer;
205                 Request getDescriptorData = new Request(sourceAddress, destinationAddress,
206                     transactionLength, transferType, transferType, incrementSourceAddress.Value && !dmaInit.Value,
207                     incrementDestinationAddress.Value);
208 
209                 parent.Log(LogLevel.Debug, "[Channel {0}] Starting transfer from 0x{1:X} to 0x{2:X}. Copy length: 0x{3:X}; transferType = {4}, srcAddrIncr = {5} dstAddrIncr = {6}",
210                            channelNumber,
211                            sourceAddress,
212                            destinationAddress,
213                            transactionLength,
214                            transferType,
215                            (incrementSourceAddress.Value && !dmaInit.Value),
216                            incrementDestinationAddress.Value);
217 
218                 var response = engine.IssueCopy(getDescriptorData);
219                 itemsAlreadyTransferred.Value += (ulong)transactionLength;
220                 sourceAddress = (ulong)response.ReadAddress;
221                 destinationAddress = (ulong)response.WriteAddress;
222                 if((interruptLength.Value * (ulong)transferType) < itemsAlreadyTransferred.Value)
223                 {
224                     parent.interruptsManager.SetInterrupt((Interrupt)channelNumber);
225                 }
226 
227                 if((ulong)bytesToTransfer == itemsAlreadyTransferred.Value)
228                 {
229                     // Keep internal information about transfer state
230                     // to know if transfer continues or starts from begin.
231                     // Used for tracking peripheral triggered requests.
232                     transferCompleted = true;
233                 }
234 
235                 if(peripheralTriggered.Value && transferCompleted)
236                 {
237                     if(circularMode.Value)
238                     {
239                         CircularModeReset();
240                         return;
241                     }
242                     this.parent.Log(LogLevel.Noisy, "Disabling DMA channel because all items were transferred and circular mode is off");
243                     dmaEnabled.Value = false;
244                 }
245             }
246 
247             public ulong SourceAddress
248             {
249                 get
250                 {
251                     return sourceAddress;
252                 }
253                 set
254                 {
255                     setSourceAddress = value;
256                     sourceAddress = value;
257                 }
258             }
259 
260             public ulong DestinationAddress
261             {
262                 get
263                 {
264                     return destinationAddress;
265                 }
266                 set
267                 {
268                     setDestinationAddress = value;
269                     destinationAddress = value;
270                 }
271             }
272 
273             public ulong sourceAddress;
274             public ulong destinationAddress;
275 
276             public IFlagRegisterField dmaEnabled;
277             public IValueRegisterField transferLength;
278             public IValueRegisterField interruptLength;
279             public IFlagRegisterField incrementSourceAddress;
280             public IFlagRegisterField incrementDestinationAddress;
281             public IValueRegisterField itemsAlreadyTransferred;
282             public IFlagRegisterField peripheralTriggered;
283             public IFlagRegisterField circularMode;
284             public IFlagRegisterField dmaInit;
285             public IEnumRegisterField<BurstMode> burstMode;
286             public TransferType transferType;
287 
CircularModeReset()288             private void CircularModeReset()
289             {
290                 sourceAddress = setSourceAddress;
291                 destinationAddress = setDestinationAddress;
292                 itemsAlreadyTransferred.Value = 0;
293             }
294 
GetBurstCount(BurstMode mode)295             private int GetBurstCount(BurstMode mode)
296             {
297                 switch(mode)
298                 {
299                     case BurstMode.Disabled: return 1;
300                     case BurstMode.Four: return 4;
301                     case BurstMode.Eight: return 8;
302                     default:
303                         parent.WarningLog("Invalid selection of burst mode: {0}", mode);
304                         return 0;
305                 }
306             }
307 
308             // For use in circular mode
309             private ulong setSourceAddress;
310             private ulong setDestinationAddress;
311 
312             private bool transferCompleted;
313 
314             private readonly int channelNumber;
315             private readonly RenesasDA_DMABase parent;
316             private readonly DmaEngine engine;
317 
318             public enum BurstMode
319             {
320                 Disabled = 0,
321                 Four = 1,
322                 Eight = 2,
323                 Reserved = 3
324             }
325         }
326 
327         protected enum Interrupt
328         {
329             Channel0,
330             Channel1,
331             Channel2,
332             Channel3,
333             Channel4,
334             Channel5,
335             Channel6,
336             Channel7,
337             Channel8,
338             Channel9,
339             Channel10,
340             Channel11,
341             Channel12,
342             Channel13,
343             Channel14,
344             Channel15
345         }
346 
347         private enum Registers
348         {
349             SourceAddress_0 = 0x0,
350             DestinationAddress_0 = 0x4,
351             InterruptLength_0 = 0x8,
352             TransferLength_0 = 0xC,
353             Control_0 = 0x10,
354             IndexPointer_0 = 0x14,
355         }
356     }
357 }
358 
359