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.Peripherals.Bus; 10 using Antmicro.Renode.Logging; 11 using Antmicro.Renode.Core; 12 using System.Collections.Generic; 13 using System.Linq; 14 15 namespace Antmicro.Renode.Peripherals.DMA 16 { 17 public sealed class STM32DMA : IDoubleWordPeripheral, IKnownSize, IGPIOReceiver, INumberedGPIOOutput 18 { STM32DMA(IMachine machine)19 public STM32DMA(IMachine machine) 20 { 21 streamFinished = new bool[NumberOfStreams]; 22 streams = new Stream[NumberOfStreams]; 23 for(var i = 0; i < streams.Length; i++) 24 { 25 streams[i] = new Stream(this, i); 26 } 27 this.machine = machine; 28 engine = new DmaEngine(machine.GetSystemBus(this)); 29 Reset(); 30 } 31 32 public IReadOnlyDictionary<int, IGPIO> Connections 33 { 34 get 35 { 36 var i = 0; 37 return streams.ToDictionary(x => i++, y => (IGPIO)y.IRQ); 38 } 39 } 40 41 public long Size 42 { 43 get 44 { 45 return 0x400; 46 } 47 } 48 ReadDoubleWord(long offset)49 public uint ReadDoubleWord(long offset) 50 { 51 switch((Registers)offset) 52 { 53 case Registers.LowInterruptStatus: 54 case Registers.HighInterruptStatus: 55 return HandleInterruptRead((int)(offset/4)); 56 default: 57 if(offset >= StreamOffsetStart && offset <= StreamOffsetEnd) 58 { 59 offset -= StreamOffsetStart; 60 return streams[offset / StreamSize].Read(offset % StreamSize); 61 } 62 this.LogUnhandledRead(offset); 63 return 0; 64 } 65 } 66 WriteDoubleWord(long offset, uint value)67 public void WriteDoubleWord(long offset, uint value) 68 { 69 switch((Registers)offset) 70 { 71 case Registers.LowInterruptClear: 72 case Registers.HighInterruptClear: 73 HandleInterruptClear((int)((offset - 8)/4), value); 74 break; 75 default: 76 if(offset >= StreamOffsetStart && offset <= StreamOffsetEnd) 77 { 78 offset -= StreamOffsetStart; 79 streams[offset / StreamSize].Write(offset % StreamSize, value); 80 } 81 else 82 { 83 this.LogUnhandledWrite(offset, value); 84 } 85 break; 86 } 87 } 88 Reset()89 public void Reset() 90 { 91 streamFinished.Initialize(); 92 foreach(var stream in streams) 93 { 94 stream.Reset(); 95 } 96 } 97 OnGPIO(int number, bool value)98 public void OnGPIO(int number, bool value) 99 { 100 if(number < 0 || number >= streams.Length) 101 { 102 this.Log(LogLevel.Error, "Attempted to start non-existing DMA stream number: {0}. Maximum value is {1}", number, streams.Length); 103 return; 104 } 105 106 if(value) 107 { 108 this.Log(LogLevel.Debug, "DMA peripheral request on stream {0} {1}", number, value); 109 if(streams[number].Enabled) 110 { 111 streams[number].DoPeripheralTransfer(); 112 } 113 else 114 { 115 this.Log(LogLevel.Warning, "DMA peripheral request on stream {0} ignored", number); 116 } 117 } 118 } 119 HandleInterruptRead(int offset)120 private uint HandleInterruptRead(int offset) 121 { 122 lock(streamFinished) 123 { 124 var returnValue = 0u; 125 for(var i = 4 * offset; i < 4 * (offset + 1); i++) 126 { 127 if(streamFinished[i]) 128 { 129 returnValue |= 1u << BitNumberForStream(i - 4 * offset); 130 } 131 } 132 return returnValue; 133 } 134 } 135 HandleInterruptClear(int offset, uint value)136 private void HandleInterruptClear(int offset, uint value) 137 { 138 lock(streamFinished) 139 { 140 for(var i = 4 * offset; i < 4 * (offset + 1); i++) 141 { 142 var bitNo = BitNumberForStream(i - 4 * offset); 143 if((value & (1 << bitNo)) != 0) 144 { 145 streamFinished[i] = false; 146 streams[i].IRQ.Unset(); 147 } 148 } 149 } 150 } 151 BitNumberForStream(int streamNo)152 private static int BitNumberForStream(int streamNo) 153 { 154 switch(streamNo) 155 { 156 case 0: 157 return 5; 158 case 1: 159 return 11; 160 case 2: 161 return 21; 162 case 3: 163 return 27; 164 default: 165 throw new InvalidOperationException("Should not reach here."); 166 } 167 } 168 169 private readonly bool[] streamFinished; 170 private readonly Stream[] streams; 171 private readonly DmaEngine engine; 172 private readonly IMachine machine; 173 174 private const int NumberOfStreams = 8; 175 private const int StreamOffsetStart = 0x10; 176 private const int StreamOffsetEnd = 0xCC; 177 private const int StreamSize = 0x18; 178 179 private enum Registers 180 { 181 LowInterruptStatus = 0x0, // DMA_LISR 182 HighInterruptStatus = 0x4, // DMA_HISR 183 LowInterruptClear = 0x8, //DMA_LIFCR 184 HighInterruptClear = 0xC // DMA_HIFCR 185 } 186 187 private class Stream 188 { Stream(STM32DMA parent, int streamNo)189 public Stream(STM32DMA parent, int streamNo) 190 { 191 this.parent = parent; 192 this.streamNo = streamNo; 193 IRQ = new GPIO(); 194 } 195 Read(long offset)196 public uint Read(long offset) 197 { 198 switch((Registers)offset) 199 { 200 case Registers.Configuration: 201 return HandleConfigurationRead(); 202 case Registers.NumberOfData: 203 return (uint)numberOfData; 204 case Registers.PeripheralAddress: 205 return peripheralAddress; 206 case Registers.Memory0Address: 207 return memory0Address; 208 case Registers.Memory1Address: 209 return memory1Address; 210 default: 211 parent.Log(LogLevel.Warning, "Unexpected read access from not implemented register (offset 0x{0:X}).", offset); 212 return 0; 213 } 214 } 215 Write(long offset, uint value)216 public void Write(long offset, uint value) 217 { 218 switch((Registers)offset) 219 { 220 case Registers.Configuration: 221 HandleConfigurationWrite(value); 222 break; 223 case Registers.NumberOfData: 224 numberOfData = (int)value; 225 break; 226 case Registers.PeripheralAddress: 227 peripheralAddress = value; 228 break; 229 case Registers.Memory0Address: 230 memory0Address = value; 231 break; 232 case Registers.Memory1Address: 233 memory1Address = value; 234 break; 235 default: 236 parent.Log(LogLevel.Warning, "Unexpected write access to not implemented register (offset 0x{0:X}, value 0x{1:X}).", offset, value); 237 break; 238 } 239 } 240 241 public GPIO IRQ { get; private set; } 242 Reset()243 public void Reset() 244 { 245 memory0Address = 0u; 246 memory1Address = 0u; 247 numberOfData = 0; 248 transferredSize = 0; 249 memoryTransferType = TransferType.Byte; 250 peripheralTransferType = TransferType.Byte; 251 memoryIncrementAddress = false; 252 peripheralIncrementAddress = false; 253 direction = Direction.PeripheralToMemory; 254 interruptOnComplete = false; 255 Enabled = false; 256 } 257 CreateRequest(int? size = null, int? destinationOffset = null)258 private Request CreateRequest(int? size = null, int? destinationOffset = null) 259 { 260 var sourceAddress = 0u; 261 var destinationAddress = 0u; 262 switch(direction) 263 { 264 case Direction.PeripheralToMemory: 265 case Direction.MemoryToMemory: 266 sourceAddress = peripheralAddress; 267 destinationAddress = memory0Address; 268 break; 269 case Direction.MemoryToPeripheral: 270 sourceAddress = memory0Address; 271 destinationAddress = peripheralAddress; 272 break; 273 } 274 275 var sourceTransferType = direction == Direction.PeripheralToMemory ? peripheralTransferType : memoryTransferType; 276 var destinationTransferType = direction == Direction.MemoryToPeripheral ? peripheralTransferType : memoryTransferType; 277 var incrementSourceAddress = direction == Direction.PeripheralToMemory ? peripheralIncrementAddress : memoryIncrementAddress; 278 var incrementDestinationAddress = direction == Direction.MemoryToPeripheral ? peripheralIncrementAddress : memoryIncrementAddress; 279 return new Request(sourceAddress, (uint)(destinationAddress + (destinationOffset ?? 0)), size ?? numberOfData, sourceTransferType, destinationTransferType, 280 incrementSourceAddress, incrementDestinationAddress); 281 } 282 DoTransfer()283 public void DoTransfer() 284 { 285 var request = CreateRequest(numberOfData * (int)memoryTransferType); 286 if(request.Size > 0) 287 { 288 lock(parent.streamFinished) 289 { 290 parent.engine.IssueCopy(request); 291 parent.streamFinished[streamNo] = true; 292 if(interruptOnComplete) 293 { 294 parent.machine.LocalTimeSource.ExecuteInNearestSyncedState(_ => IRQ.Set()); 295 } 296 } 297 } 298 } 299 DoPeripheralTransfer()300 public void DoPeripheralTransfer() 301 { 302 var request = CreateRequest((int)memoryTransferType, transferredSize); 303 transferredSize += (int)memoryTransferType; 304 if(request.Size > 0) 305 { 306 lock(parent.streamFinished) 307 { 308 parent.engine.IssueCopy(request); 309 if(transferredSize == numberOfData * (int)memoryTransferType) 310 { 311 transferredSize = 0; 312 parent.streamFinished[streamNo] = true; 313 Enabled = false; 314 if(interruptOnComplete) 315 { 316 parent.machine.LocalTimeSource.ExecuteInNearestSyncedState(_ => IRQ.Set()); 317 } 318 } 319 } 320 } 321 } 322 323 public bool Enabled { get; private set; } 324 HandleConfigurationRead()325 private uint HandleConfigurationRead() 326 { 327 var returnValue = 0u; 328 returnValue |= (uint)(channel << 25); 329 returnValue |= (uint)(priority << 16); 330 331 returnValue |= FromTransferType(memoryTransferType) << 13; 332 returnValue |= FromTransferType(peripheralTransferType) << 11; 333 returnValue |= memoryIncrementAddress ? (1u << 10) : 0u; 334 returnValue |= peripheralIncrementAddress ? (1u << 9) : 0u; 335 returnValue |= ((uint)direction) << 6; 336 returnValue |= interruptOnComplete ? (1u << 4) : 0u; 337 // regarding enable bit - our transfer is always finished 338 return returnValue; 339 } 340 HandleConfigurationWrite(uint value)341 private void HandleConfigurationWrite(uint value) 342 { 343 // we ignore channel selection and priority 344 channel = (byte)((value >> 25) & 7); 345 priority = (byte)((value >> 16) & 3); 346 347 memoryTransferType = ToTransferType(value >> 13); 348 peripheralTransferType = ToTransferType(value >> 11); 349 memoryIncrementAddress = (value & (1 << 10)) != 0; 350 peripheralIncrementAddress = (value & (1 << 9)) != 0; 351 direction = (Direction)((value >> 6) & 3); 352 interruptOnComplete = (value & (1 << 4)) != 0; 353 // we ignore transfer error interrupt enable as we never post errors 354 if((value & ~0xE037ED5) != 0) 355 { 356 parent.Log(LogLevel.Warning, "Channel {0}: unsupported bits written to configuration register. Value is 0x{1:X}.", streamNo, value); 357 } 358 359 if((value & 1) != 0) 360 { 361 if(direction != Direction.PeripheralToMemory) 362 { 363 DoTransfer(); 364 } 365 else 366 { 367 Enabled = true; 368 } 369 } 370 } 371 ToTransferType(uint dataSize)372 private TransferType ToTransferType(uint dataSize) 373 { 374 dataSize &= 3; 375 switch(dataSize) 376 { 377 case 0: 378 return TransferType.Byte; 379 case 1: 380 return TransferType.Word; 381 case 2: 382 return TransferType.DoubleWord; 383 default: 384 parent.Log(LogLevel.Warning, "Stream {0}: Non existitng possible value written as data size.", streamNo); 385 return TransferType.Byte; 386 } 387 } 388 FromTransferType(TransferType transferType)389 private static uint FromTransferType(TransferType transferType) 390 { 391 switch(transferType) 392 { 393 case TransferType.Byte: 394 return 0; 395 case TransferType.Word: 396 return 1; 397 case TransferType.DoubleWord: 398 return 2; 399 } 400 throw new InvalidOperationException("Should not reach here."); 401 } 402 403 private uint memory0Address; 404 private uint memory1Address; 405 private uint peripheralAddress; 406 private int numberOfData; 407 private int transferredSize; 408 private TransferType memoryTransferType; 409 private TransferType peripheralTransferType; 410 private bool memoryIncrementAddress; 411 private bool peripheralIncrementAddress; 412 private Direction direction; 413 private bool interruptOnComplete; 414 private byte channel; 415 private byte priority; 416 417 private readonly STM32DMA parent; 418 private readonly int streamNo; 419 420 private enum Registers 421 { 422 Configuration = 0x0, // DMA_SxCR 423 NumberOfData = 0x4, // DMA_SxNDTR 424 PeripheralAddress = 0x8, // DMA_SxPAR 425 Memory0Address = 0xC, // DMA_SxM0AR 426 Memory1Address = 0x10, // DMA_SxM1AR 427 FIFOControl = 0x14, // DMA_SxFCR 428 } 429 430 private enum Direction : byte 431 { 432 PeripheralToMemory = 0, 433 MemoryToPeripheral = 1, 434 MemoryToMemory = 2 435 } 436 } 437 } 438 } 439 440