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 System.Linq;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Peripherals.CPU;
15 
16 namespace Antmicro.Renode.Peripherals.DMA
17 {
18     // Currently only memory to memory transfers are supported.
19     public class RenesasRZG_DMAC : IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize, INumberedGPIOOutput
20     {
RenesasRZG_DMAC(IMachine machine)21         public RenesasRZG_DMAC(IMachine machine)
22         {
23             sysbus = machine.GetSystemBus(this);
24             ErrorIRQ = new GPIO();
25             channels = new Channel[ChannelCount];
26             RegistersCollection = new DoubleWordRegisterCollection(this, DefineRegisters());
27             Connections = channels
28                 .Select((channel, i) => new { Key = i, Value = (IGPIO)channel.IRQ })
29                 .ToDictionary(x => x.Key, x => x.Value);
30         }
31 
Reset()32         public void Reset()
33         {
34             RegistersCollection.Reset();
35             ErrorIRQ.Unset();
36             foreach(var channel in channels)
37             {
38                 channel.Reset();
39             }
40         }
41 
ReadDoubleWord(long offset)42         public uint ReadDoubleWord(long offset)
43         {
44             return RegistersCollection.Read(offset);
45         }
46 
WriteDoubleWord(long offset, uint value)47         public void WriteDoubleWord(long offset, uint value)
48         {
49             RegistersCollection.Write(offset, value);
50         }
51 
52         public IReadOnlyDictionary<int, IGPIO> Connections { get; }
53         public DoubleWordRegisterCollection RegistersCollection { get; }
54         public long Size => 0x1000;
55         public GPIO ErrorIRQ { get; }
56 
DefineRegisters()57         private Dictionary<long, DoubleWordRegister> DefineRegisters()
58         {
59             var registerMap = new Dictionary<long, DoubleWordRegister>();
60             for(var block = 0; block < BlockCount; block++)
61             {
62                 var blockIdx = block;
63                 for(var channelIdx = 0; channelIdx < ChannelsPerBlock; channelIdx++)
64                 {
65                     var absoluteIdx = ChannelsPerBlock * block + channelIdx;
66                     var channel = new Channel(this, absoluteIdx);
67                     channels[absoluteIdx] = channel;
68 
69                     var registersOffset = 0x400 * block + 0x40 * channelIdx;
70                     foreach(var registerEntry in channel.DefineRegisters())
71                     {
72                         registerMap.Add(registersOffset + registerEntry.Key, registerEntry.Value);
73                     }
74                 }
75 
76                 var baseOffset = 0x400 * block + 0x300;
77                 registerMap.Add(baseOffset + (long)ControlRegisters.Control, new DoubleWordRegister(this)
78                     .WithTaggedFlag("PR (Transfer Priority)", 0)
79                     .WithTaggedFlag("LVINT (Interrupt Level)", 1)
80                     .WithReservedBits(2, 14)
81                     .WithTag("LDPR (Link Descriptor Protection)", 16, 3)
82                     .WithReservedBits(19, 1)
83                     .WithTag("LDCA (Link Descriptor Cache)", 20, 4)
84                     .WithTag("LWPR (Link WriteBack Protection)", 24, 3)
85                     .WithReservedBits(27, 1)
86                     .WithTag("LWCA (Link WriteBack Cache)", 28, 4));
87 
88                 registerMap.Add(baseOffset + (long)ControlRegisters.StatusEnabled, new DoubleWordRegister(this)
89                     .WithFlags(0, ChannelsPerBlock, FieldMode.Read, name: "EN",
90                         valueProviderCallback: (i, _) => channels[ChannelsPerBlock * blockIdx + i].Enabled)
91                     .WithReservedBits(ChannelsPerBlock, 32 - ChannelsPerBlock));
92 
93                 registerMap.Add(baseOffset + (long)ControlRegisters.StatusError, new DoubleWordRegister(this)
94                     .WithFlags(0, ChannelsPerBlock, FieldMode.Read, name: "ER",
95                         valueProviderCallback: (i, _) => channels[ChannelsPerBlock * blockIdx + i].Error)
96                     .WithReservedBits(ChannelsPerBlock, 32 - ChannelsPerBlock));
97 
98                 registerMap.Add(baseOffset + (long)ControlRegisters.StatusInterrupted, new DoubleWordRegister(this)
99                     .WithFlags(0, ChannelsPerBlock, FieldMode.Read, name: "END",
100                         valueProviderCallback: (i, _) => channels[ChannelsPerBlock * blockIdx + i].EndInterrupt)
101                     .WithReservedBits(ChannelsPerBlock, 32 - ChannelsPerBlock));
102 
103                 registerMap.Add(baseOffset + (long)ControlRegisters.StatusTerminalCount, new DoubleWordRegister(this)
104                     .WithFlags(0, ChannelsPerBlock, FieldMode.Read, name: "TC",
105                         valueProviderCallback: (i, _) => channels[ChannelsPerBlock * blockIdx + i].TerminalCount)
106                     .WithReservedBits(ChannelsPerBlock, 32 - ChannelsPerBlock));
107 
108                 registerMap.Add(baseOffset + (long)ControlRegisters.StatusSuspend, new DoubleWordRegister(this)
109                     .WithTaggedFlags("SUS", 0, ChannelsPerBlock)
110                     .WithReservedBits(ChannelsPerBlock, 32 - ChannelsPerBlock));
111             }
112             return registerMap;
113         }
114 
UpdateErrorInterrupt()115         private void UpdateErrorInterrupt()
116         {
117             var status = channels.Where(x => x.Error).Any();
118             this.DebugLog("ErrorIRQ: {0}", status ? "Set" : "Unset");
119             ErrorIRQ.Set(status);
120         }
121 
122         private readonly IBusController sysbus;
123         private readonly Channel[] channels;
124 
125         private const int BlockCount = 2;
126         private const int ChannelsPerBlock = 8;
127         private const int ChannelCount = BlockCount * ChannelsPerBlock;
128 
129         private enum ControlRegisters
130         {
131             Control             = 0x00, // DCTRL_n_m/n_mS
132             StatusEnabled       = 0x10, // DCTRL_n_m/n_mS
133             StatusError         = 0x14, // DSTAT_ER_n_m/n_mS
134             StatusInterrupted   = 0x18, // DSTAT_END_n_m/n_mS
135             StatusTerminalCount = 0x1C, // DSTAT_TC_n_m/n_mS
136             StatusSuspend       = 0x20, // DSTAT_SUS_n_m/n_mS
137         }
138 
139         private class Channel
140         {
Channel(RenesasRZG_DMAC parent, int channelId)141             public Channel(RenesasRZG_DMAC parent, int channelId)
142             {
143                 this.parent = parent;
144                 IRQ = new GPIO();
145                 logPrefix = $"DMA Channel {channelId}";
146                 dma = new DmaEngine(parent.sysbus);
147                 nextSourceAddresses = new IValueRegisterField[AddressRegisterBanks];
148                 nextDestinationAddresses = new IValueRegisterField[AddressRegisterBanks];
149                 nextTransactionBytes = new IValueRegisterField[AddressRegisterBanks];
150 
151                 Reset();
152             }
153 
Reset()154             public void Reset()
155             {
156                 IRQ.Unset();
157                 currentSourceAddress = 0;
158                 currentDestinationAddress = 0;
159                 currentTransactionByte = 0;
160                 sourceTransferType = TransferType.Byte;
161                 destinationTransferType = TransferType.Byte;
162                 requestingCpu = null;
163             }
164 
DefineRegisters()165             public Dictionary<long, DoubleWordRegister> DefineRegisters()
166             {
167                 var registers = new Dictionary<long, DoubleWordRegister>();
168 
169                 registers.Add((long)Registers.Next0SourceAddress, new DoubleWordRegister(parent)
170                     .WithValueField(0, 32, out nextSourceAddresses[0], name: "SA (Next Source Address 0)"));
171 
172                 registers.Add((long)Registers.Next0DestinationAddress, new DoubleWordRegister(parent)
173                     .WithValueField(0, 32, out nextDestinationAddresses[0], name: "DA (Next Destination Address 0)"));
174 
175                 registers.Add((long)Registers.Next0TransactionByte, new DoubleWordRegister(parent)
176                     .WithValueField(0, 32, out nextTransactionBytes[0], name: "TB (Next Transaction Byte 0)"));
177 
178                 registers.Add((long)Registers.Next1SourceAddress, new DoubleWordRegister(parent)
179                     .WithValueField(0, 32, out nextSourceAddresses[1], name: "SA (Next Source Address 1)"));
180 
181                 registers.Add((long)Registers.Next1DestinationAddress, new DoubleWordRegister(parent)
182                     .WithValueField(0, 32, out nextDestinationAddresses[1], name: "DA (Next Destination Address 1)"));
183 
184                 registers.Add((long)Registers.Next1TransactionByte, new DoubleWordRegister(parent)
185                     .WithValueField(0, 32, out nextTransactionBytes[1], name: "TB (Next Transaction Byte 1)"));
186 
187                 registers.Add((long)Registers.CurrentSourceAddress, new DoubleWordRegister(parent)
188                     .WithValueField(0, 32, FieldMode.Read, name: "CRSA (Current Source Address)",
189                         valueProviderCallback: _ => currentSourceAddress));
190 
191                 registers.Add((long)Registers.CurrentDestinationAddress, new DoubleWordRegister(parent)
192                     .WithValueField(0, 32, FieldMode.Read, name: "CRDA (Current Destination Address)",
193                         valueProviderCallback: _ => currentDestinationAddress));
194 
195                 registers.Add((long)Registers.CurrentTransactionByte, new DoubleWordRegister(parent)
196                     .WithValueField(0, 32, FieldMode.Read, name: "CRTB (Current Transaction Byte)",
197                         valueProviderCallback: _ => currentTransactionByte));
198 
199                 statusRegister = new DoubleWordRegister(parent)
200                     .WithFlag(0, out dmaEnabled, FieldMode.Read, name: "EN (Enable)")
201                     .WithTaggedFlag("RQST (Request)", 1)
202                     .WithTaggedFlag("TACT (Transaction Active)", 2)
203                     .WithTaggedFlag("SUS (Suspended)", 3)
204                     .WithFlag(4, out dmaError, FieldMode.Read, name: "ER (Error Bit)")
205                     .WithFlag(5, out endInterrupt, FieldMode.Read, name: "END (End Interrupted)")
206                     .WithFlag(6, out terminalCount, FieldMode.Read, name: "TC (Terminal Count)")
207                     .WithFlag(7, FieldMode.Read, name: "SR (Selected Register Set)",
208                         valueProviderCallback: _ => registerSetSelect.Value)
209                     .WithTaggedFlag("DL (Descriptor Load)", 8)
210                     .WithTaggedFlag("DW (Descriptor WriteBack)", 9)
211                     .WithTaggedFlag("DER (Descriptor Error)", 10)
212                     .WithEnumField<DoubleWordRegister, DMAMode>(11, 1, FieldMode.Read, name: "MODE (DMA Mode)",
213                         valueProviderCallback: _ => dmaMode.Value)
214                     .WithReservedBits(12, 4)
215                     .WithTaggedFlag("INTMSK (Temporary Interrupt Mask)", 16)
216                     .WithReservedBits(17, 15);
217 
218                 registers.Add((long)Registers.ChannelStatus, statusRegister);
219 
220                 registers.Add((long)Registers.ChannelControl, new DoubleWordRegister(parent)
221                     .WithFlag(0, FieldMode.Write, name: "SETEN (Set Enable)",
222                         writeCallback: (_, value) =>
223                         {
224                             if(value)
225                             {
226                                 LoadTransferParameters();
227                             }
228                         })
229                     .WithFlag(1, FieldMode.Write, name: "CLREN (Clear Enable)",
230                         writeCallback: (_, value) =>
231                         {
232                             if(value)
233                             {
234                                 dmaEnabled.Value = false;
235                             }
236                         })
237                     .WithFlag(2, FieldMode.Write, name: "STG (Software Trigger)",
238                         writeCallback: (_, value) =>
239                         {
240                             if(value)
241                             {
242                                 if(!PerformTransfer())
243                                 {
244                                     return;
245                                 }
246 
247                                 // Continue the register transfer
248                                 LoadTransferParameters(setRequestingCpu: false);
249                                 if(performFullTransfer.Value)
250                                 {
251                                     // Perform the transfer again, with new parameters, if full transfer was requested
252                                     PerformTransfer();
253                                 }
254                             }
255                         })
256                     .WithFlag(3, FieldMode.Write, name: "SWRST (Software Reset)",
257                         writeCallback: (_, value) =>
258                         {
259                             if(value)
260                             {
261                                 statusRegister.Reset();
262                             }
263                         })
264                     .WithTaggedFlag("CLRRQ (Clear Request bit)", 4)
265                     .WithFlag(5, FieldMode.Write, name: "CLREND (Clear End bit)",
266                         writeCallback: (_, value) =>
267                         {
268                             if(value)
269                             {
270                                 endInterrupt.Value = false;
271                             }
272                         })
273                     .WithFlag(6, FieldMode.Write, name: "CLRTC (Clear Terminal Count)",
274                         writeCallback: (_, value) =>
275                         {
276                             if(value)
277                             {
278                                 terminalCount.Value = false;
279                             }
280                         })
281                     .WithReservedBits(7, 1)
282                     .WithTaggedFlag("SETSUS (Set Suspend)", 8)
283                     .WithTaggedFlag("CLRSUS (Clear Suspend)", 9)
284                     .WithReservedBits(10, 6)
285                     .WithTaggedFlag("SETINTMSK (Set Temporary Interrupt Mask)", 16)
286                     .WithTaggedFlag("CLRINTMSK (Clear Temporary Interrupt Mask)", 17)
287                     .WithReservedBits(18, 14)
288                     .WithWriteCallback((_, __) => UpdateInterrupts()));
289 
290                 registers.Add((long)Registers.ChannelConfiguration, new DoubleWordRegister(parent)
291                     .WithTag("SEL (DMAC Channel Select)", 0, 3)
292                     .WithTaggedFlag("REQD (Request Direction)", 3)
293                     .WithTaggedFlag("LOEN (Low Enable)", 4)
294                     .WithTaggedFlag("HIEN (High Enable)", 5)
295                     .WithTaggedFlag("LVL (Level)", 6)
296                     .WithReservedBits(7, 1)
297                     .WithTag("AM (ACK Mode)", 8, 3)
298                     .WithReservedBits(11, 1)
299                     .WithValueField(12, 4, name: "SDS (Source Data Size)",
300                         changeCallback: (_, value) => sourceTransferType = ConvertToTransferType(value))
301                     .WithValueField(16, 4, name: "DDS (Destination Data Size)",
302                         changeCallback: (_, value) => destinationTransferType = ConvertToTransferType(value))
303                     .WithFlag(20, out sourceAddressCounting, name: "SAD (Source Address Counting Direction)")
304                     .WithFlag(21, out destinationAddressCounting, name: "DAD (Destination Address Counting Direction)")
305                     .WithFlag(22, out performFullTransfer, name: "TM (Transfer Mode)")
306                     .WithReservedBits(23, 1)
307                     .WithFlag(24, out endInterruptMask, name: "DEM (DMA Transfer End Interrupt Mask)")
308                     .WithReservedBits(25, 2)
309                     .WithTaggedFlag("SBE (Sweep Buffer Enable)", 27)
310                     .WithFlag(28, out registerSetSelect, name: "RSEL (Register Set Select)")
311                     .WithFlag(29, out registerSetSwitch, name: "RSW (Register Select Switch)")
312                     .WithFlag(30, out registerSetEnable, name: "REN (Register Set Enable)")
313                     .WithEnumField(31, 1, out dmaMode, name: "DMS (DMA Mode Select)"));
314 
315                 registers.Add((long)Registers.ChannelInterval, new DoubleWordRegister(parent)
316                     .WithTag("ITVL (Channel Transfer Interval)", 0, 16)
317                     .WithReservedBits(16, 16));
318 
319                 registers.Add((long)Registers.ChannelExtension, new DoubleWordRegister(parent)
320                     .WithTag("SPR (Source Protection)", 0, 3)
321                     .WithReservedBits(3, 1)
322                     .WithTag("SCA (Source Cache)", 4, 4)
323                     .WithTag("DPR (Destination Protection)", 8, 3)
324                     .WithReservedBits(11, 1)
325                     .WithTag("DCA (Destination Cache)", 12, 4)
326                     .WithReservedBits(16, 16));
327 
328                 registers.Add((long)Registers.NextLinkAddress, new DoubleWordRegister(parent)
329                     .WithTag("NXLA (Next Link Address)", 0, 32));
330 
331                 registers.Add((long)Registers.CurrentLinkAddress, new DoubleWordRegister(parent)
332                     .WithTag("CRLA (Current Link Address)", 0, 32));
333 
334                 return registers;
335             }
336 
PerformTransfer()337             public bool PerformTransfer()
338             {
339                 if(!dmaEnabled.Value)
340                 {
341                     parent.WarningLog("{0}: Attempted to perform trigger a transaction on a disabled DMA channel, ignoring", logPrefix);
342                     return false;
343                 }
344 
345                 Request request;
346                 if(performFullTransfer.Value)
347                 {
348                     request = new Request(
349                         new Place(currentSourceAddress),
350                         new Place(currentDestinationAddress),
351                         (int)currentTransactionByte,
352                         sourceTransferType, destinationTransferType,
353                         !sourceAddressCounting.Value,
354                         !destinationAddressCounting.Value
355                     );
356                     currentTransactionByte = 0;
357                 }
358                 else
359                 {
360                     var transferSize = (int)sourceTransferType;
361                     request = new Request(
362                         new Place(currentSourceAddress),
363                         new Place(currentDestinationAddress),
364                         transferSize,
365                         sourceTransferType, destinationTransferType,
366                         !sourceAddressCounting.Value,
367                         !destinationAddressCounting.Value
368                     );
369                     currentTransactionByte -= (ulong)transferSize;
370                 }
371 
372                 var response = dma.IssueCopy(request, requestingCpu);
373                 currentSourceAddress = response.ReadAddress.Value;
374                 currentDestinationAddress = response.WriteAddress.Value;
375 
376                 if(currentTransactionByte == 0)
377                 {
378                     if(registerSetSwitch.Value)
379                     {
380                         registerSetSelect.Value = !registerSetSelect.Value;
381                     }
382 
383                     if(registerSetEnable.Value && dmaMode.Value == DMAMode.Register)
384                     {
385                         registerSetEnable.Value = false;
386                         // Transfer hasn't completed it will be continued from the next register set
387                         return true;
388                     }
389 
390                     dmaEnabled.Value = false;
391                     terminalCount.Value = true;
392                     endInterrupt.Value = terminalCount.Value && !endInterruptMask.Value;
393                     endInterruptMask.Value = false;
394                     UpdateInterrupts();
395                 }
396 
397                 return false;
398             }
399 
400             public GPIO IRQ { get; }
401             public bool Enabled => dmaEnabled.Value;
402             public bool Error => dmaError.Value;
403             public bool EndInterrupt => endInterrupt.Value;
404             public bool TerminalCount => terminalCount.Value;
405 
ConvertToTransferType(ulong value)406             private TransferType ConvertToTransferType(ulong value)
407             {
408                 const ulong MaxTransferSize = (ulong)TransferType.QuadWord;
409                 ulong transferWordSize = value + 1;
410                 if(transferWordSize > MaxTransferSize)
411                 {
412                     parent.ErrorLog("{0}: Transfer size set to {1}, but the maximum supported transfer size is {2}. Clamping to {2}",
413                         logPrefix, transferWordSize, MaxTransferSize);
414                     transferWordSize = MaxTransferSize;
415                 }
416 
417                 return (TransferType)transferWordSize;
418             }
419 
LoadTransferParameters(bool setRequestingCpu = true)420             private void LoadTransferParameters(bool setRequestingCpu = true)
421             {
422                 if(setRequestingCpu)
423                 {
424                     if(!parent.sysbus.TryGetCurrentCPU(out requestingCpu))
425                     {
426                         parent.WarningLog("{0}: Could not obtain a CPU context when starting a transaction", logPrefix);
427                     }
428                 }
429 
430                 switch(dmaMode.Value)
431                 {
432                     case DMAMode.Register:
433                     {
434                         dmaEnabled.Value = true;
435                         var bank = registerSetSelect.Value ? 1 : 0;
436                         currentSourceAddress = nextSourceAddresses[bank].Value;
437                         currentDestinationAddress = nextDestinationAddresses[bank].Value;
438                         currentTransactionByte = nextTransactionBytes[bank].Value;
439                         break;
440                     }
441                     case DMAMode.Link:
442                         parent.ErrorLog("{0}: {1} DMA mode is currently not supported, triggering an error", logPrefix, nameof(DMAMode.Link));
443                         dmaEnabled.Value = false;
444                         dmaError.Value = true;
445                         UpdateInterrupts();
446                         return;
447                     default:
448                         throw new Exception("unreachable");
449                 }
450             }
451 
UpdateInterrupts()452             private void UpdateInterrupts()
453             {
454                 parent.DebugLog("{0}: IRQ: {1}", logPrefix, endInterrupt.Value ? "Set" : "Unset");
455                 IRQ.Set(endInterrupt.Value);
456                 parent.UpdateErrorInterrupt();
457             }
458 
459             private readonly RenesasRZG_DMAC parent;
460             private readonly DmaEngine dma;
461             private readonly string logPrefix;
462 
463             private readonly IValueRegisterField[] nextSourceAddresses;
464             private readonly IValueRegisterField[] nextDestinationAddresses;
465             private readonly IValueRegisterField[] nextTransactionBytes;
466 
467             private DoubleWordRegister statusRegister;
468             private ulong currentSourceAddress;
469             private ulong currentDestinationAddress;
470             private ulong currentTransactionByte;
471             private ICPU requestingCpu;
472             private TransferType sourceTransferType;
473             private TransferType destinationTransferType;
474             private IFlagRegisterField sourceAddressCounting;
475             private IFlagRegisterField destinationAddressCounting;
476             private IFlagRegisterField registerSetSelect;
477             private IFlagRegisterField registerSetSwitch;
478             private IFlagRegisterField registerSetEnable;
479             private IFlagRegisterField dmaEnabled;
480             private IFlagRegisterField performFullTransfer;
481             private IEnumRegisterField<DMAMode> dmaMode;
482 
483             private IFlagRegisterField terminalCount;
484             private IFlagRegisterField endInterrupt;
485             private IFlagRegisterField endInterruptMask;
486             private IFlagRegisterField dmaError;
487 
488             private const int AddressRegisterBanks = 2;
489 
490             private enum DMAMode
491             {
492                 Register = 0,
493                 Link     = 1,
494             }
495 
496             private enum Registers
497             {
498                 Next0SourceAddress          = 0x00, // N0SA_n/nS
499                 Next0DestinationAddress     = 0x04, // N0DA_n/nS
500                 Next0TransactionByte        = 0x08, // N0TB_n/nS
501                 Next1SourceAddress          = 0x0C, // N1SA_n/nS
502                 Next1DestinationAddress     = 0x10, // N1DA_n/nS
503                 Next1TransactionByte        = 0x14, // N1TB_n/nS
504                 CurrentSourceAddress        = 0x18, // CRSA_n/nS
505                 CurrentDestinationAddress   = 0x1C, // CRDA_n/nS
506                 CurrentTransactionByte      = 0x20, // CRTB_n/nS
507                 ChannelStatus               = 0x24, // CHSTAT_n/nS
508                 ChannelControl              = 0x28, // CHCTRL_n/nS
509                 ChannelConfiguration        = 0x2C, // CHCFG_n/nS
510                 ChannelInterval             = 0x30, // CHITVL_n/nS
511                 ChannelExtension            = 0x34, // CHEXT_n/nS
512                 NextLinkAddress             = 0x38, // NXLA_n/nS
513                 CurrentLinkAddress          = 0x3C, // CRLA_n/nS
514             }
515         }
516     }
517 }
518