1 // 2 // Copyright (c) 2010-2023 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.Collections.ObjectModel; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.Bus; 14 15 namespace Antmicro.Renode.Peripherals.DMA 16 { 17 public class STM32G0DMA : IDoubleWordPeripheral, IKnownSize, IGPIOReceiver, INumberedGPIOOutput 18 { STM32G0DMA(IMachine machine, int numberOfChannels)19 public STM32G0DMA(IMachine machine, int numberOfChannels) 20 { 21 this.machine = machine; 22 engine = new DmaEngine(machine.GetSystemBus(this)); 23 this.numberOfChannels = numberOfChannels; 24 channels = new Channel[numberOfChannels]; 25 var innerConnections = new Dictionary<int, IGPIO>(); 26 for(var i = 0; i < numberOfChannels; ++i) 27 { 28 channels[i] = new Channel(this, i); 29 innerConnections[i] = new GPIO(); 30 } 31 32 Connections = new ReadOnlyDictionary<int, IGPIO>(innerConnections); 33 34 var interruptStatus = new DoubleWordRegister(this); 35 var interruptFlagClear = new DoubleWordRegister(this) 36 .WithWriteCallback((_, __) => Update()); 37 38 for(var i = 0; i < numberOfChannels; ++i) 39 { 40 var j = i; 41 interruptStatus.DefineFlagField(j * 4 + 0, FieldMode.Read, 42 valueProviderCallback: _ => channels[j].GlobalInterrupt, 43 name: $"Global interrupt flag for channel {j} (GIF{j})"); 44 interruptStatus.DefineFlagField(j * 4 + 1, FieldMode.Read, 45 valueProviderCallback: _ => channels[j].TransferComplete, 46 name: $"Transfer complete flag for channel {j} (TCIF{j})"); 47 interruptStatus.DefineFlagField(j * 4 + 2, FieldMode.Read, 48 valueProviderCallback: _ => channels[j].HalfTransfer, 49 name: $"Half transfer flag for channel {j} (HTIF{j})"); 50 interruptStatus.Tag($"Transfer error flag for channel {j} (TEIF{j})", j * 4 + 3, 1); 51 52 interruptFlagClear.DefineFlagField(j * 4 + 0, FieldMode.Write, 53 writeCallback: (_, val) => 54 { 55 if(!val) 56 { 57 return; 58 } 59 channels[j].GlobalInterrupt = false; 60 channels[j].TransferComplete = false; 61 channels[j].HalfTransfer = false; 62 }, 63 name: $"Global interrupt flag clear for channel {j} (CGIF{j})"); 64 interruptFlagClear.DefineFlagField(j * 4 + 1, FieldMode.Write, 65 writeCallback: (_, val) => 66 { 67 if(!val) 68 { 69 return; 70 } 71 channels[j].TransferComplete = false; 72 channels[j].GlobalInterrupt = false; 73 }, 74 name: $"Transfer complete flag clear for channel {j} (CTEIF{j})"); 75 interruptFlagClear.DefineFlagField(j * 4 + 2, FieldMode.Write, 76 writeCallback: (_, val) => 77 { 78 if(!val) 79 { 80 return; 81 } 82 channels[j].HalfTransfer = false; 83 channels[j].GlobalInterrupt = false; 84 }, 85 name: $"Half transfer flag clear for channel {j} (CHTIF{j})"); 86 interruptFlagClear.Tag($"Transfer error flag clear for channel {j} (CTEIF{j})", j * 4 + 3, 1); 87 } 88 89 var channelSelection = new DoubleWordRegister(this) 90 .WithTag("DMA channel 1 selection (C1S)", 0, 4) 91 .WithTag("DMA channel 2 selection (C2S)", 4, 4) 92 .WithTag("DMA channel 3 selection (C3S)", 8, 4) 93 .WithTag("DMA channel 4 selection (C4S)", 12, 4) 94 .WithTag("DMA channel 5 selection (C5S)", 16, 4) 95 .WithTag("DMA channel 6 selection (C6S)", 20, 4) 96 .WithTag("DMA channel 7 selection (C7S)", 24, 4) 97 .WithReservedBits(28, 4); 98 99 var registerMap = new Dictionary<long, DoubleWordRegister> 100 { 101 {(long)Registers.InterruptStatus, interruptStatus }, 102 {(long)Registers.InterruptFlagClear, interruptFlagClear }, 103 {(long)Registers.ChannelSelection, channelSelection }, 104 }; 105 106 registers = new DoubleWordRegisterCollection(this, registerMap); 107 } 108 Reset()109 public void Reset() 110 { 111 registers.Reset(); 112 } 113 ReadDoubleWord(long offset)114 public uint ReadDoubleWord(long offset) 115 { 116 if(registers.TryRead(offset, out var result)) 117 { 118 return result; 119 } 120 if(TryGetChannelNumberBasedOnOffset(offset, out var channelNo)) 121 { 122 return channels[channelNo].ReadDoubleWord(offset); 123 } 124 this.Log(LogLevel.Error, "Could not read from offset 0x{0:X} nor read from channel {1}, the channel has to be in range 0-{2}", offset, channelNo, numberOfChannels); 125 return 0; 126 } 127 WriteDoubleWord(long offset, uint value)128 public void WriteDoubleWord(long offset, uint value) 129 { 130 if(registers.TryWrite(offset, value)) 131 { 132 return; 133 } 134 if(TryGetChannelNumberBasedOnOffset(offset, out var channelNo)) 135 { 136 channels[channelNo].WriteDoubleWord(offset, value); 137 return; 138 } 139 this.Log(LogLevel.Error, "Could not write to offset 0x{0:X} nor write to channel {1}, the channel has to be in range 0-{2}", offset, channelNo, numberOfChannels); 140 } 141 OnGPIO(int number, bool value)142 public void OnGPIO(int number, bool value) 143 { 144 if(number == 0 || number > channels.Length) 145 { 146 this.Log(LogLevel.Error, "Channel number {0} is out of range, must be in [1; {1}]", number, channels.Length); 147 return; 148 } 149 150 if(!value) 151 { 152 return; 153 } 154 155 this.Log(LogLevel.Debug, "DMA peripheral request on channel {0}", number); 156 if(!channels[number - 1].TryTriggerTransfer()) 157 { 158 this.Log(LogLevel.Warning, "DMA peripheral request on channel {0} ignored - channel is disabled " 159 + "or has data count set to 0", number); 160 } 161 } 162 163 public IReadOnlyDictionary<int, IGPIO> Connections { get; } 164 165 public long Size => 0x100; 166 TryGetChannelNumberBasedOnOffset(long offset, out int channel)167 private bool TryGetChannelNumberBasedOnOffset(long offset, out int channel) 168 { 169 var shifted = offset - (long)Registers.Channel1Configuration; 170 channel = (int)(shifted / ShiftBetweenChannels); 171 if(channel < 0 || channel > numberOfChannels) 172 { 173 return false; 174 } 175 return true; 176 } 177 Update()178 private void Update() 179 { 180 for(var i = 0; i < numberOfChannels; ++i) 181 { 182 Connections[i].Set((channels[i].TransferComplete && channels[i].TransferCompleteInterruptEnable) 183 || (channels[i].HalfTransfer && channels[i].HalfTransferInterruptEnable)); 184 } 185 } 186 187 private readonly IMachine machine; 188 private readonly DmaEngine engine; 189 private readonly DoubleWordRegisterCollection registers; 190 private readonly Channel[] channels; 191 private readonly int numberOfChannels; 192 193 private const int ShiftBetweenChannels = (int)((long)Registers.Channel2Configuration - (long)Registers.Channel1Configuration); 194 195 private class Channel 196 { Channel(STM32G0DMA parent, int number)197 public Channel(STM32G0DMA parent, int number) 198 { 199 this.parent = parent; 200 channelNumber = number; 201 202 var registersMap = new Dictionary<long, DoubleWordRegister>(); 203 registersMap.Add((long)ChannelRegisters.ChannelConfiguration + (number * ShiftBetweenChannels), new DoubleWordRegister(parent) 204 .WithFlag(0, out channelEnable, 205 writeCallback: (_, val) => 206 { 207 if(!val) 208 { 209 return; 210 } 211 if(memoryToMemory.Value || transferDirection.Value == TransferDirection.MemoryToPeripheral) 212 { 213 DoTransfer(); 214 } 215 }, 216 valueProviderCallback: _ => false, name: "Channel enable (EN)") 217 .WithFlag(1, out transferCompleteInterruptEnable, name: "Transfer complete interrupt enable (TCIE)") 218 .WithFlag(2, out halfTransferInterruptEnable, name: "Half transfer interrupt enable (HTIE)") 219 .WithTag("Transfer error interrupt enable (TEIE)", 3, 1) 220 .WithEnumField(4, 1, out transferDirection, name: "Data transfer direction (DIR)") 221 .WithFlag(5, out circularMode, name: "Circular mode (CIRC)") 222 .WithFlag(6, out peripheralIncrementMode, name: "Peripheral increment mode (PINC)") 223 .WithFlag(7, out memoryIncrementMode, name: "Memory increment mode (MINC)") 224 .WithEnumField(8, 2, out peripheralTransferType, name: "Peripheral size (PSIZE)") 225 .WithEnumField(10, 2, out memoryTransferType, name: "Memory size (MSIZE)") 226 .WithTag("Priority level (PL)", 12, 2) 227 .WithFlag(14, out memoryToMemory, name: "Memory-to-memory mode (MEM2MEM)") 228 .WithReservedBits(15, 17) 229 .WithWriteCallback( 230 (_, __) => parent.Update())); 231 232 // The ChannelDataCount register stores the currently remaining number of data units to copy. 233 // It is decremented on each transfer. The originalDataCount field stores the original value 234 // written by software in order to implement the half transfer interrupt. 235 registersMap.Add((long)ChannelRegisters.ChannelDataCount + (number * ShiftBetweenChannels), new DoubleWordRegister(parent) 236 .WithValueField(0, 16, out dataCount, name: "Number of data to transfer (NDT)", 237 writeCallback: (_, val) => originalDataCount = val) 238 .WithReservedBits(16, 16)); 239 240 // The ChannelPeripheralAddress and ChannelMemoryAddress registers retain the value written 241 // by software. This value is read by the model during circular mode wraparound. 242 // The incremented address is stored in the currentAddress fields. 243 registersMap.Add((long)ChannelRegisters.ChannelPeripheralAddress + (number * ShiftBetweenChannels), new DoubleWordRegister(parent) 244 .WithValueField(0, 32, out peripheralAddress, 245 writeCallback: (_, val) => currentPeripheralAddress = val, name: "Peripheral address (PA)")); 246 247 registersMap.Add((long)ChannelRegisters.ChannelMemoryAddress + (number * ShiftBetweenChannels), new DoubleWordRegister(parent) 248 .WithValueField(0, 32, out memoryAddress, 249 writeCallback: (_, val) => currentMemoryAddress = val, name: "Memory address (MA)")); 250 251 registers = new DoubleWordRegisterCollection(parent, registersMap); 252 } 253 ReadDoubleWord(long offset)254 public uint ReadDoubleWord(long offset) 255 { 256 return registers.Read(offset); 257 } 258 WriteDoubleWord(long offset, uint value)259 public void WriteDoubleWord(long offset, uint value) 260 { 261 registers.Write(offset, value); 262 } 263 Reset()264 public void Reset() 265 { 266 registers.Reset(); 267 TransferComplete = false; 268 HalfTransfer = false; 269 } 270 TryTriggerTransfer()271 public bool TryTriggerTransfer() 272 { 273 if(!Enabled || dataCount.Value == 0) 274 { 275 return false; 276 } 277 278 DoTransfer(); 279 parent.Update(); 280 return true; 281 } 282 283 public bool GlobalInterrupt 284 { 285 get 286 { 287 return HalfTransfer || TransferComplete; 288 } 289 set 290 { 291 if(value) 292 { 293 return; 294 } 295 HalfTransfer = false; 296 TransferComplete = false; 297 } 298 } 299 300 public bool Enabled => channelEnable.Value; 301 302 public bool HalfTransfer { get; set; } 303 304 public bool TransferComplete { get; set; } 305 306 public bool HalfTransferInterruptEnable => halfTransferInterruptEnable.Value; 307 308 public bool TransferCompleteInterruptEnable => transferCompleteInterruptEnable.Value; 309 DoTransfer()310 private void DoTransfer() 311 { 312 // This value is still valid in memory-to-memory mode, "peripheral" means 313 // "the address specified by the peripheralAddress field" and not necessarily 314 // a peripheral. 315 if(transferDirection.Value == TransferDirection.PeripheralToMemory) 316 { 317 var toCopy = (uint)dataCount.Value; 318 // In peripheral-to-memory mode, only copy one data unit. Otherwise, do the whole block. 319 if(!memoryToMemory.Value) 320 { 321 toCopy = Math.Max((uint)SizeToType(memoryTransferType.Value), 322 (uint)SizeToType(peripheralTransferType.Value)); 323 dataCount.Value -= 1; 324 } 325 else 326 { 327 dataCount.Value = 0; 328 } 329 var response = IssueCopy(currentPeripheralAddress, currentMemoryAddress, toCopy, 330 peripheralIncrementMode.Value, memoryIncrementMode.Value, peripheralTransferType.Value, 331 memoryTransferType.Value); 332 currentPeripheralAddress = response.ReadAddress.Value; 333 currentMemoryAddress = response.WriteAddress.Value; 334 HalfTransfer = dataCount.Value <= originalDataCount / 2; 335 TransferComplete = dataCount.Value == 0; 336 } 337 else // 1-bit field, so we handle both possible values 338 { 339 IssueCopy(memoryAddress.Value, peripheralAddress.Value, (uint)dataCount.Value, 340 memoryIncrementMode.Value, peripheralIncrementMode.Value, memoryTransferType.Value, 341 peripheralTransferType.Value); 342 dataCount.Value = 0; 343 HalfTransfer = true; 344 TransferComplete = true; 345 } 346 347 // Loop around if circular mode is enabled 348 if(circularMode.Value && !memoryToMemory.Value && dataCount.Value == 0) 349 { 350 dataCount.Value = originalDataCount; 351 currentPeripheralAddress = peripheralAddress.Value; 352 currentMemoryAddress = memoryAddress.Value; 353 } 354 // No parent.Update - this is called by the register write and TryTriggerTransfer 355 // to avoid calling it twice in the former case 356 } 357 IssueCopy(ulong sourceAddress, ulong destinationAddress, uint size, bool incrementReadAddress, bool incrementWriteAddress, TransferSize sourceTransferType, TransferSize destinationTransferType)358 private Response IssueCopy(ulong sourceAddress, ulong destinationAddress, uint size, 359 bool incrementReadAddress, bool incrementWriteAddress, TransferSize sourceTransferType, 360 TransferSize destinationTransferType) 361 { 362 var request = new Request( 363 sourceAddress, 364 destinationAddress, 365 (int)size, 366 SizeToType(sourceTransferType), 367 SizeToType(destinationTransferType), 368 incrementReadAddress, 369 incrementWriteAddress 370 ); 371 return parent.engine.IssueCopy(request); 372 } 373 SizeToType(TransferSize size)374 private TransferType SizeToType(TransferSize size) 375 { 376 switch(size) 377 { 378 case TransferSize.Bits32: 379 return TransferType.DoubleWord; 380 case TransferSize.Bits16: 381 return TransferType.Word; 382 case TransferSize.Bits8: 383 default: 384 return TransferType.Byte; 385 } 386 } 387 388 389 private IEnumRegisterField<TransferDirection> transferDirection; 390 private IFlagRegisterField circularMode; 391 private IFlagRegisterField peripheralIncrementMode; 392 private IFlagRegisterField memoryIncrementMode; 393 private IFlagRegisterField memoryToMemory; 394 private IValueRegisterField dataCount; 395 private IValueRegisterField memoryAddress; 396 private IValueRegisterField peripheralAddress; 397 private IFlagRegisterField channelEnable; 398 private IFlagRegisterField transferCompleteInterruptEnable; 399 private IFlagRegisterField halfTransferInterruptEnable; 400 private IEnumRegisterField<TransferSize> memoryTransferType; 401 private IEnumRegisterField<TransferSize> peripheralTransferType; 402 private ulong currentPeripheralAddress; 403 private ulong currentMemoryAddress; 404 private ulong originalDataCount; 405 406 private readonly DoubleWordRegisterCollection registers; 407 private readonly STM32G0DMA parent; 408 private readonly int channelNumber; 409 410 private enum TransferSize 411 { 412 Bits8 = 0, 413 Bits16 = 1, 414 Bits32 = 2, 415 Reserved = 3 416 } 417 418 private enum TransferDirection 419 { 420 PeripheralToMemory = 0, 421 MemoryToPeripheral = 1, 422 } 423 424 private enum ChannelRegisters 425 { 426 ChannelConfiguration = 0x8, 427 ChannelDataCount = 0xC, 428 ChannelPeripheralAddress = 0x10, 429 ChannelMemoryAddress = 0x14, 430 } 431 } 432 433 private enum Registers : long 434 { 435 InterruptStatus = 0x0, 436 InterruptFlagClear = 0x4, 437 438 Channel1Configuration = 0x8, 439 Channel1DataCount = 0xC, 440 Channel1PeripheralAddress = 0x10, 441 Channel1MemoryAddress = 0x14, 442 443 Channel2Configuration = 0x1C, 444 Channel2DataCount = 0x20, 445 Channel2PeripheralAddress = 0x24, 446 Channel2MemoryAddress = 0x28, 447 448 Channel3Configuration = 0x30, 449 Channel3DataCount = 0x34, 450 Channel3PeripheralAddress = 0x38, 451 Channel3MemoryAddress = 0x3c, 452 453 Channel4Configuration = 0x44, 454 Channel4DataCount = 0x48, 455 Channel4PeripheralAddress = 0x4c, 456 Channel4MemoryAddress = 0x50, 457 458 Channel5Configuration = 0x58, 459 Channel5DataCount = 0x5c, 460 Channel5PeripheralAddress = 0x60, 461 Channel5MemoryAddress = 0x64, 462 463 Channel6Configuration = 0x6C, 464 Channel6DataCount = 0x70, 465 Channel6PeripheralAddress = 0x74, 466 Channel6MemoryAddress = 0x78, 467 468 Channel7Configuration = 0x80, 469 Channel7DataCount = 0x84, 470 Channel7PeripheralAddress = 0x88, 471 Channel7MemoryAddress = 0x8c, 472 // 0x90 to 0xA4 - Reserved 473 ChannelSelection = 0xA8 474 } 475 } 476 } 477