1 // 2 // Copyright (c) 2010-2024 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 System.Linq; 10 using Antmicro.Renode.Debugging; 11 using Antmicro.Renode.Peripherals.Bus; 12 using Antmicro.Renode.Peripherals.Memory; 13 14 namespace Antmicro.Renode.Peripherals.DMA 15 { 16 public sealed class DmaEngine 17 { DmaEngine(IBusController systemBus)18 public DmaEngine(IBusController systemBus) 19 { 20 sysbus = systemBus; 21 } 22 IssueCopy(Request request, CPU.ICPU context = null)23 public Response IssueCopy(Request request, CPU.ICPU context = null) 24 { 25 var response = new Response 26 { 27 ReadAddress = request.Source.Address, 28 WriteAddress = request.Destination.Address, 29 }; 30 31 var readLengthInBytes = (int)request.ReadTransferType; 32 var writeLengthInBytes = (int)request.WriteTransferType; 33 34 // some sanity checks 35 if((request.Size % readLengthInBytes) != 0 || (request.Size % writeLengthInBytes) != 0) 36 { 37 throw new ArgumentException("Request size is not aligned properly to given read or write transfer type (or both)."); 38 } 39 40 var buffer = new byte[request.Size]; 41 var sourceAddress = request.Source.Address ?? 0; 42 var whatIsAtSource = sysbus.WhatIsAt(sourceAddress, context); 43 var isSourceContinuousMemory = (whatIsAtSource == null || whatIsAtSource.Peripheral is MappedMemory) // Not a peripheral 44 && readLengthInBytes == request.SourceIncrementStep; // Consistent memory region 45 if(!request.Source.Address.HasValue) 46 { 47 // request array based copy 48 Array.Copy(request.Source.Array, request.Source.StartIndex.Value, buffer, 0, request.Size); 49 } 50 else if(isSourceContinuousMemory) 51 { 52 if(request.IncrementReadAddress) 53 { 54 // Transfer Units | 1 | 2 | 3 | 4 | 55 // Source | A | B | C | D | 56 // Copied | A | B | C | D | 57 sysbus.ReadBytes(sourceAddress, request.Size, buffer, 0, context: context); 58 response.ReadAddress += (ulong)request.Size; 59 } 60 else 61 { 62 // When reading from the memory with IncrementReadAddress unset, effectively, only the last unit will be used 63 // Transfer Units | 1 | 2 | 3 | 4 | 64 // Source | A | B | C | D | 65 // Copied | D | | | | 66 sysbus.ReadBytes(sourceAddress, readLengthInBytes, buffer, 0, context: context); 67 } 68 } 69 else if(whatIsAtSource != null) 70 { 71 // Read from peripherals 72 var transferred = 0; 73 var offset = 0UL; 74 while(transferred < request.Size) 75 { 76 var readAddress = sourceAddress + offset; 77 switch(request.ReadTransferType) 78 { 79 case TransferType.Byte: 80 buffer[transferred] = sysbus.ReadByte(readAddress, context); 81 break; 82 case TransferType.Word: 83 BitConverter.GetBytes(sysbus.ReadWord(readAddress, context)).CopyTo(buffer, transferred); 84 break; 85 case TransferType.DoubleWord: 86 BitConverter.GetBytes(sysbus.ReadDoubleWord(readAddress, context)).CopyTo(buffer, transferred); 87 break; 88 case TransferType.QuadWord: 89 BitConverter.GetBytes(sysbus.ReadQuadWord(readAddress, context)).CopyTo(buffer, transferred); 90 break; 91 default: 92 throw new ArgumentOutOfRangeException($"Requested read transfer size: {request.ReadTransferType} is not supported by DmaEngine"); 93 } 94 transferred += readLengthInBytes; 95 if(request.IncrementReadAddress) 96 { 97 offset += request.SourceIncrementStep; 98 response.ReadAddress += request.SourceIncrementStep; 99 } 100 } 101 } 102 103 var destinationAddress = request.Destination.Address ?? 0; 104 var whatIsAtDestination = sysbus.WhatIsAt(destinationAddress, context); 105 var isDestinationContinuousMemory = (whatIsAtDestination == null || whatIsAtDestination.Peripheral is MappedMemory) // Not a peripheral 106 && writeLengthInBytes == request.DestinationIncrementStep; // Consistent memory region 107 if(!request.Destination.Address.HasValue) 108 { 109 // request array based copy 110 Array.Copy(buffer, 0, request.Destination.Array, request.Destination.StartIndex.Value, request.Size); 111 } 112 else if(isDestinationContinuousMemory) 113 { 114 if(request.IncrementWriteAddress) 115 { 116 if(request.IncrementReadAddress || !isSourceContinuousMemory) 117 { 118 // Transfer Units | 1 | 2 | 3 | 4 | 119 // Source | A | B | C | D | 120 // Destination | A | B | C | D | 121 sysbus.WriteBytes(buffer, destinationAddress, context: context); 122 } 123 else 124 { 125 // When writing memory with IncrementReadAddress unset all destination units are written with the first source unit 126 // Transfer Units | 1 | 2 | 3 | 4 | 127 // Source | A | B | C | D | 128 // Destination | A | A | A | A | 129 var chunkStartOffset = 0UL; 130 var chunk = buffer.Take(writeLengthInBytes).ToArray(); 131 while(chunkStartOffset < (ulong)request.Size) 132 { 133 var writeAddress = destinationAddress + chunkStartOffset; 134 sysbus.WriteBytes(chunk, writeAddress, context: context); 135 chunkStartOffset += (ulong)writeLengthInBytes; 136 } 137 } 138 response.WriteAddress += (ulong)request.Size; 139 } 140 else 141 { 142 // When writing to memory with IncrementWriteAddress unset, effectively, only the last unit is written with the last unit of source 143 // Transfer Units | 1 | 2 | 3 | 4 | 144 // Source | A | B | C | D | 145 // Destination | D | | | | 146 var skipCount = (request.Size == writeLengthInBytes) ? 0 : request.Size - writeLengthInBytes; 147 DebugHelper.Assert((skipCount + request.Size) <= buffer.Length); 148 sysbus.WriteBytes(buffer.Skip(skipCount).ToArray(), destinationAddress, context: context); 149 } 150 } 151 else if(whatIsAtDestination != null) 152 { 153 // Write to peripheral 154 var transferred = 0; 155 var offset = 0UL; 156 while(transferred < request.Size) 157 { 158 switch(request.WriteTransferType) 159 { 160 case TransferType.Byte: 161 sysbus.WriteByte(destinationAddress + offset, buffer[transferred], context); 162 break; 163 case TransferType.Word: 164 sysbus.WriteWord(destinationAddress + offset, BitConverter.ToUInt16(buffer, transferred), context); 165 break; 166 case TransferType.DoubleWord: 167 sysbus.WriteDoubleWord(destinationAddress + offset, BitConverter.ToUInt32(buffer, transferred), context); 168 break; 169 case TransferType.QuadWord: 170 sysbus.WriteQuadWord(destinationAddress + offset, BitConverter.ToUInt64(buffer, transferred), context); 171 break; 172 default: 173 throw new ArgumentOutOfRangeException($"Requested write transfer size: {request.WriteTransferType} is not supported by DmaEngine"); 174 } 175 transferred += writeLengthInBytes; 176 if(request.IncrementWriteAddress) 177 { 178 offset += request.DestinationIncrementStep; 179 response.WriteAddress += request.DestinationIncrementStep; 180 } 181 } 182 } 183 184 return response; 185 } 186 187 private readonly IBusController sysbus; 188 } 189 } 190 191