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.Core; 11 using Antmicro.Renode.Logging; 12 using System.Linq; 13 using System.Collections.Generic; 14 15 namespace Antmicro.Renode.Peripherals.DMA 16 { 17 public sealed class STM32LDMA : IDoubleWordPeripheral, IKnownSize, INumberedGPIOOutput 18 { STM32LDMA(IMachine machine)19 public STM32LDMA(IMachine machine) 20 { 21 engine = new DmaEngine(machine.GetSystemBus(this)); 22 channels = new Channel[8]; 23 for(var i = 0; i < channels.Length; i++) 24 { 25 channels[i] = new Channel(this, i); 26 } 27 } 28 ReadDoubleWord(long offset)29 public uint ReadDoubleWord(long offset) 30 { 31 if(offset >= 0x08 && offset <= 0x8C) 32 { 33 return channels[(offset - 0x08) / 0x14].Read(offset - 0x08 - ((offset - 0x08) / 0x14) * 0x14); 34 } 35 switch((Offset)offset) 36 { 37 case Offset.InterruptStatus: 38 return HandleInterruptStatusRead(); 39 } 40 this.LogUnhandledRead(offset); 41 return 0; 42 } 43 44 WriteDoubleWord(long offset, uint value)45 public void WriteDoubleWord(long offset, uint value) 46 { 47 if(offset >= 0x08 && offset <= 0x8C) 48 { 49 var channelNo = (offset - 0x08) / 0x14; 50 channels[channelNo].Write(offset - 0x08 - channelNo * 0x14, value); 51 return; 52 } 53 switch((Offset)offset) 54 { 55 case Offset.InterruptClear: 56 HandleClearInterrupt(value); 57 break; 58 default: 59 this.LogUnhandledWrite(offset, value); 60 break; 61 } 62 } 63 64 public IReadOnlyDictionary<int, IGPIO> Connections { get { var i = 0; return channels.ToDictionary(x => i++, y => (IGPIO)y.IRQ); } } 65 Reset()66 public void Reset() 67 { 68 // TODO 69 } 70 71 public long Size 72 { 73 get 74 { 75 return 0x400; 76 } 77 } 78 HandleInterruptStatusRead()79 private uint HandleInterruptStatusRead() 80 { 81 var returnValue = 0u; 82 for(var i = 0; i < channels.Length; i++) 83 { 84 returnValue |= channels[i].IRQ.IsSet ? (3u << i * 4) : 0u; 85 } 86 return returnValue; 87 } 88 HandleClearInterrupt(uint value)89 private void HandleClearInterrupt(uint value) 90 { 91 for(var i = 0; i < channels.Length; i++) 92 { 93 var ourBit1 = 4 * i; 94 var ourBit2 = ourBit1 + 1; 95 if((value & (1 << ourBit1)) != 0 || (value & (1 << ourBit2)) != 0) 96 { 97 channels[i].ClearInterrupt(); 98 } 99 } 100 } 101 102 private sealed class Channel 103 { Channel(STM32LDMA parent, int channelNo)104 public Channel(STM32LDMA parent, int channelNo) 105 { 106 this.parent = parent; 107 memoryTransferType = TransferType.Byte; 108 peripheralTransferType = TransferType.Byte; 109 IRQ = new GPIO(); 110 this.channelNo = channelNo; 111 } 112 113 public GPIO IRQ { get; private set; } 114 Read(long offset)115 public uint Read(long offset) 116 { 117 switch((Offset)offset) 118 { 119 case Offset.Configuration: 120 return HandleConfigurationRead(); 121 case Offset.NumberOfData: 122 return (uint)numberOfData; 123 case Offset.PeripheralAddress: 124 return peripheralAddress; 125 case Offset.MemoryAddress: 126 return memoryAddress; 127 default: 128 parent.Log(LogLevel.Warning, "Channel {0}: unhandled read from 0x{1:X}.", channelNo, offset); 129 return 0; 130 } 131 } 132 Write(long offset, uint value)133 public void Write(long offset, uint value) 134 { 135 switch((Offset)offset) 136 { 137 case Offset.Configuration: 138 HandleConfigurationWrite(value); 139 break; 140 case Offset.NumberOfData: 141 numberOfData = (int)value; 142 break; 143 case Offset.PeripheralAddress: 144 peripheralAddress = value; 145 break; 146 case Offset.MemoryAddress: 147 memoryAddress = value; 148 break; 149 default: 150 parent.Log(LogLevel.Warning, "Channel {0}: unhandled write 0x{1:X} to 0x{2:X}.", channelNo, offset); 151 break; 152 } 153 } 154 ClearInterrupt()155 public void ClearInterrupt() 156 { 157 IRQ.Unset(); 158 } 159 Reset()160 public void Reset() 161 { 162 IRQ.Unset(); 163 peripheralIncrement = false; 164 peripheralAddress = 0u; 165 memoryAddress = 0u; 166 memoryIncrement = false; 167 memoryTransferType = 0; 168 peripheralTransferType = 0; 169 completeInterruptEnabled = false; 170 transferErrorInterruptEnabled = false; 171 numberOfData = 0; 172 priority = 0; 173 direction = 0; 174 } 175 HandleConfigurationRead()176 private uint HandleConfigurationRead() 177 { 178 var returnValue = 0u; 179 returnValue |= completeInterruptEnabled ? (1u << 1) : 0u; 180 returnValue |= transferErrorInterruptEnabled ? (1u << 3) : 0u; 181 returnValue |= ((uint)direction) << 4; 182 returnValue |= peripheralIncrement ? (1u << 6) : 0u; 183 returnValue |= memoryIncrement ? (1u << 7) : 0u; 184 returnValue |= (uint)(priority << 12); 185 return returnValue; 186 } 187 HandleConfigurationWrite(uint value)188 private void HandleConfigurationWrite(uint value) 189 { 190 completeInterruptEnabled = (value & (1 << 1)) != 0; 191 transferErrorInterruptEnabled = (value & (1 << 3)) != 0; 192 direction = (Direction)((value >> 4) & 1); 193 peripheralIncrement = (value & (1 << 6)) != 0; 194 memoryIncrement = (value & (1 << 7)) != 0; 195 priority = (byte)((value >> 12) & 3); 196 197 if((value & ~0x30DB) != 0) 198 { 199 parent.Log(LogLevel.Warning, "Channel {0}: some unhandled bits were written to configuration register. Value is 0x{1:X}.", channelNo, value); 200 } 201 202 if((value & 1) != 0) 203 { 204 DoTransfer(); 205 } 206 } 207 DoTransfer()208 private void DoTransfer() 209 { 210 uint sourceAddress, destinationAddress; 211 bool incrementSourceAddress, incrementDestinationAddress; 212 TransferType sourceTransferType, destinationTransferType; 213 214 if(direction == Direction.ReadFromMemory) 215 { 216 sourceAddress = memoryAddress; 217 destinationAddress = peripheralAddress; 218 incrementSourceAddress = memoryIncrement; 219 incrementDestinationAddress = peripheralIncrement; 220 sourceTransferType = memoryTransferType; 221 destinationTransferType = peripheralTransferType; 222 } 223 else 224 { 225 sourceAddress = peripheralAddress; 226 destinationAddress = memoryAddress; 227 incrementSourceAddress = peripheralIncrement; 228 incrementDestinationAddress = memoryIncrement; 229 sourceTransferType = peripheralTransferType; 230 destinationTransferType = memoryTransferType; 231 } 232 233 var request = new Request(sourceAddress, destinationAddress, numberOfData, sourceTransferType, destinationTransferType, 234 incrementSourceAddress, incrementDestinationAddress); 235 parent.engine.IssueCopy(request); 236 IRQ.Set(); 237 } 238 239 private enum Offset 240 { 241 Configuration = 0x0, 242 NumberOfData = 0x4, 243 PeripheralAddress = 0x8, 244 MemoryAddress = 0xC 245 } 246 247 private bool memoryIncrement; 248 private bool peripheralIncrement; 249 private uint peripheralAddress; 250 private uint memoryAddress; 251 private TransferType memoryTransferType; 252 private TransferType peripheralTransferType; 253 private bool completeInterruptEnabled; 254 private bool transferErrorInterruptEnabled; 255 private int numberOfData; 256 private byte priority; 257 private Direction direction; 258 private readonly STM32LDMA parent; 259 private readonly int channelNo; 260 } 261 262 private enum Offset 263 { 264 InterruptStatus = 0x0, 265 InterruptClear = 0x4 266 } 267 268 private enum Direction 269 { 270 ReadFromPeripheral = 0, 271 ReadFromMemory = 1 272 } 273 274 private readonly DmaEngine engine; 275 private readonly Channel[] channels; 276 } 277 } 278 279