1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // Copyright (c) 2020-2021 Microsoft 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 9 using Antmicro.Renode.Peripherals.Bus; 10 using Antmicro.Renode.Core; 11 using System.Collections.Generic; 12 using Antmicro.Renode.Backends.Display; 13 using Antmicro.Renode.Core.Structure.Registers; 14 using System; 15 using Antmicro.Migrant; 16 using Antmicro.Migrant.Hooks; 17 18 namespace Antmicro.Renode.Peripherals.DMA 19 { 20 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 21 public sealed class STM32DMA2D : IDoubleWordPeripheral, IKnownSize 22 { STM32DMA2D(IMachine machine)23 public STM32DMA2D(IMachine machine) : this() 24 { 25 sysbus = machine.GetSystemBus(this); 26 IRQ = new GPIO(); 27 Reset(); 28 } 29 Reset()30 public void Reset() 31 { 32 registers.Reset(); 33 } 34 ReadDoubleWord(long offset)35 public uint ReadDoubleWord(long offset) 36 { 37 return registers.Read(offset); 38 } 39 WriteDoubleWord(long offset, uint value)40 public void WriteDoubleWord(long offset, uint value) 41 { 42 registers.Write(offset, value); 43 } 44 45 public GPIO IRQ { get; private set; } 46 47 public long Size 48 { 49 get 50 { 51 return 0xC00; 52 } 53 } 54 55 private byte[] foregroundClut; 56 private byte[] backgroundClut; 57 STM32DMA2D()58 private STM32DMA2D() 59 { 60 var controlRegister = new DoubleWordRegister(this) 61 .WithFlag(0, out startFlag, name: "Start", writeCallback: (old, @new) => { if(@new) DoTransfer(); }) 62 .WithEnumField(16, 2, out dma2dMode, name: "Mode") 63 ; 64 65 var foregroundClutMemoryAddressRegister = new DoubleWordRegister(this).WithValueField(0, 32); 66 var backgroundClutMemoryAddressRegister = new DoubleWordRegister(this).WithValueField(0, 32); 67 68 var interruptStatusRegister = new DoubleWordRegister(this) 69 .WithTaggedFlag("TEIF", 0) 70 .WithFlag(1, out transferCompleteFlag, FieldMode.Read, name: "TCIF") 71 .WithTaggedFlag("TWIF", 2) 72 .WithTaggedFlag("CAEIF", 3) 73 .WithTaggedFlag("CTCIF", 4) 74 .WithTaggedFlag("CEIF", 5) 75 .WithReservedBits(6, 26) 76 ; 77 78 var interruptFlagClearRegister = new DoubleWordRegister(this) 79 .WithFlag(1, FieldMode.Read | FieldMode.WriteOneToClear, name: "CTCIF", writeCallback: (_, val) => { 80 if(val) { IRQ.Unset(); transferCompleteFlag.Value = false; }}) 81 ; 82 83 var numberOfLineRegister = new DoubleWordRegister(this) 84 .WithValueField(0, 16, out numberOfLineField, name: "NL") 85 .WithValueField(16, 14, out pixelsPerLineField, name: "PL") 86 .WithChangeCallback((_, __) => 87 { 88 HandleOutputBufferSizeChange(); 89 HandleBackgroundBufferSizeChange(); 90 HandleForegroundBufferSizeChange(); 91 }) 92 ; 93 94 outputMemoryAddressRegister = new DoubleWordRegister(this).WithValueField(0, 32); 95 backgroundMemoryAddressRegister = new DoubleWordRegister(this).WithValueField(0, 32); 96 foregroundMemoryAddressRegister = new DoubleWordRegister(this).WithValueField(0, 32); 97 98 var outputPfcControlRegister = new DoubleWordRegister(this) 99 .WithEnumField(0, 3, out outputColorModeField, name: "CM", 100 changeCallback: (_, __) => 101 { 102 HandlePixelFormatChange(); 103 HandleOutputBufferSizeChange(); 104 }) 105 ; 106 107 var foregroundPfcControlRegister = new DoubleWordRegister(this) 108 .WithEnumField(0, 4, out foregroundColorModeField, name: "CM", 109 changeCallback: (_, __) => 110 { 111 HandlePixelFormatChange(); 112 HandleForegroundBufferSizeChange(); 113 }) 114 .WithEnumField(4, 1, out foregroundClutColorModeField, name: "CCM", changeCallback: (_, __) => HandlePixelFormatChange()) 115 .WithValueField(8, 8, out var foregroundClutSizeField, name: "CS") //out of order to use the var in the next field 116 .WithFlag(5, name: "START", valueProviderCallback: _ => false, 117 writeCallback: (_, value) => 118 { 119 if(!value) 120 { 121 return; 122 } 123 124 foregroundClut = new byte[(foregroundClutSizeField.Value + 1) * (uint)foregroundClutColorModeField.Value.ToPixelFormat().GetColorDepth()]; 125 sysbus.ReadBytes(foregroundClutMemoryAddressRegister.Value, foregroundClut.Length, foregroundClut, 0, true); 126 }) 127 .WithEnumField(16, 2, out foregroundAlphaMode, name: "AM", changeCallback: (_, __) => HandlePixelFormatChange()) 128 .WithValueField(24, 8, out foregroundAlphaField, name: "ALPHA") 129 ; 130 131 var foregroundColorRegister = new DoubleWordRegister(this) 132 .WithValueField(0, 8, out foregroundColorBlueChannelField, name: "BLUE") 133 .WithValueField(8, 8, out foregroundColorGreenChannelField, name: "GREEN") 134 .WithValueField(16, 8, out foregroundColorRedChannelField, name: "RED") 135 .WithReservedBits(24, 8) 136 .WithChangeCallback((_, __) => HandlePixelFormatChange()) 137 ; 138 139 var backgroundPfcControlRegister = new DoubleWordRegister(this) 140 .WithEnumField(0, 4, out backgroundColorModeField, name: "CM", 141 changeCallback: (_, __) => 142 { 143 HandlePixelFormatChange(); 144 HandleBackgroundBufferSizeChange(); 145 }) 146 .WithEnumField(4, 1, out backgroundClutColorModeField, name: "CCM", changeCallback: (_, __) => HandlePixelFormatChange()) 147 .WithValueField(8, 8, out var backgroundClutSizeField, name: "CS") //out of order to use the var in the next field 148 .WithFlag(5, name: "START", valueProviderCallback: _ => false, 149 writeCallback: (_, value) => 150 { 151 if(!value) 152 { 153 return; 154 } 155 156 backgroundClut = new byte[(backgroundClutSizeField.Value + 1) * (uint)backgroundClutColorModeField.Value.ToPixelFormat().GetColorDepth()]; 157 sysbus.ReadBytes(backgroundClutMemoryAddressRegister.Value, backgroundClut.Length, backgroundClut, 0, true); 158 }) 159 .WithEnumField(16, 2, out backgroundAlphaMode, name: "AM", changeCallback: (_, __) => HandlePixelFormatChange()) 160 .WithValueField(24, 8, out backgroundAlphaField, name: "ALPHA") 161 ; 162 163 var backgroundColorRegister = new DoubleWordRegister(this) 164 .WithValueField(0, 8, out backgroundColorBlueChannelField, name: "BLUE") 165 .WithValueField(8, 8, out backgroundColorGreenChannelField, name: "GREEN") 166 .WithValueField(16, 8, out backgroundColorRedChannelField, name: "RED") 167 .WithReservedBits(24, 8) 168 .WithChangeCallback((_, __) => HandlePixelFormatChange()) 169 ; 170 171 outputColorRegister = new DoubleWordRegister(this).WithValueField(0, 32); 172 173 var outputOffsetRegister = new DoubleWordRegister(this) 174 .WithValueField(0, 14, out outputLineOffsetField, name: "LO") 175 ; 176 177 var foregroundOffsetRegister = new DoubleWordRegister(this) 178 .WithValueField(0, 14, out foregroundLineOffsetField, name: "LO") 179 ; 180 181 var backgroundOffsetRegister = new DoubleWordRegister(this) 182 .WithValueField(0, 14, out backgroundLineOffsetField, name: "LO") 183 ; 184 185 var regs = new Dictionary<long, DoubleWordRegister> 186 { 187 { (long)Register.ControlRegister, controlRegister }, 188 { (long)Register.InterruptStatusRegister, interruptStatusRegister }, 189 { (long)Register.InterruptFlagClearRegister, interruptFlagClearRegister }, 190 { (long)Register.ForegroundMemoryAddressRegister, foregroundMemoryAddressRegister }, 191 { (long)Register.ForegroundOffsetRegister, foregroundOffsetRegister }, 192 { (long)Register.BackgroundMemoryAddressRegister, backgroundMemoryAddressRegister }, 193 { (long)Register.BackgroundOffsetRegister, backgroundOffsetRegister }, 194 { (long)Register.ForegroundPfcControlRegister, foregroundPfcControlRegister }, 195 { (long)Register.ForegroundColorRegister, foregroundColorRegister }, 196 { (long)Register.BackgroundPfcControlRegister, backgroundPfcControlRegister }, 197 { (long)Register.BackgroundColorRegister, backgroundColorRegister }, 198 { (long)Register.OutputPfcControlRegister, outputPfcControlRegister }, 199 { (long)Register.OutputColorRegister, outputColorRegister }, 200 { (long)Register.OutputMemoryAddressRegister, outputMemoryAddressRegister }, 201 { (long)Register.OutputOffsetRegister, outputOffsetRegister }, 202 { (long)Register.NumberOfLineRegister, numberOfLineRegister }, 203 { (long)Register.ForegroundClutMemoryAddressRegister, foregroundClutMemoryAddressRegister }, 204 { (long)Register.BackgroundClutMemoryAddressRegister, backgroundClutMemoryAddressRegister } 205 }; 206 207 registers = new DoubleWordRegisterCollection(this, regs); 208 } 209 HandleOutputBufferSizeChange()210 private void HandleOutputBufferSizeChange() 211 { 212 var outputFormatColorDepth = outputColorModeField.Value.ToPixelFormat().GetColorDepth(); 213 outputBuffer = new byte[numberOfLineField.Value * pixelsPerLineField.Value * (uint)outputFormatColorDepth]; 214 outputLineBuffer = new byte[pixelsPerLineField.Value * (uint)outputFormatColorDepth]; 215 } 216 HandleBackgroundBufferSizeChange()217 private void HandleBackgroundBufferSizeChange() 218 { 219 var backgroundFormatColorDepth = backgroundColorModeField.Value.ToPixelFormat().GetColorDepth(); 220 backgroundBuffer = new byte[pixelsPerLineField.Value * numberOfLineField.Value * (uint)backgroundFormatColorDepth]; 221 backgroundLineBuffer = new byte[pixelsPerLineField.Value * (uint)backgroundFormatColorDepth]; 222 } 223 HandleForegroundBufferSizeChange()224 private void HandleForegroundBufferSizeChange() 225 { 226 var foregroundFormatColorDepth = foregroundColorModeField.Value.ToPixelFormat().GetColorDepth(); 227 foregroundBuffer = new byte[pixelsPerLineField.Value * numberOfLineField.Value * (uint)foregroundFormatColorDepth]; 228 foregroundLineBuffer = new byte[pixelsPerLineField.Value * (uint)foregroundFormatColorDepth]; 229 } 230 231 [PostDeserialization] HandlePixelFormatChange()232 private void HandlePixelFormatChange() 233 { 234 var outputFormat = outputColorModeField.Value.ToPixelFormat(); 235 var backgroundFormat = backgroundColorModeField.Value.ToPixelFormat(); 236 var backgroundFixedColor = new Pixel( 237 (byte)backgroundColorRedChannelField.Value, 238 (byte)backgroundColorGreenChannelField.Value, 239 (byte)backgroundColorBlueChannelField.Value, 240 (byte)0xFF); 241 242 var foregroundFormat = foregroundColorModeField.Value.ToPixelFormat(); 243 var foregroundFixedColor = new Pixel( 244 (byte)foregroundColorRedChannelField.Value, 245 (byte)foregroundColorGreenChannelField.Value, 246 (byte)foregroundColorBlueChannelField.Value, 247 (byte)0xFF); 248 249 bgConverter = PixelManipulationTools.GetConverter(backgroundFormat, Endianness, outputFormat, Endianness, backgroundClutColorModeField.Value.ToPixelFormat(), backgroundFixedColor); 250 fgConverter = PixelManipulationTools.GetConverter(foregroundFormat, Endianness, outputFormat, Endianness, foregroundClutColorModeField.Value.ToPixelFormat(), foregroundFixedColor); 251 blender = PixelManipulationTools.GetBlender(backgroundFormat, Endianness, foregroundFormat, Endianness, outputFormat, Endianness, foregroundClutColorModeField.Value.ToPixelFormat(), backgroundClutColorModeField.Value.ToPixelFormat(), backgroundFixedColor, foregroundFixedColor); 252 } 253 DoTransfer()254 private void DoTransfer() 255 { 256 var foregroundFormat = foregroundColorModeField.Value.ToPixelFormat(); 257 var outputFormat = outputColorModeField.Value.ToPixelFormat(); 258 259 switch(dma2dMode.Value) 260 { 261 case Mode.RegisterToMemory: 262 var colorBytes = BitConverter.GetBytes(outputColorRegister.Value); 263 var colorDepth = outputFormat.GetColorDepth(); 264 265 // fill area with the color defined in output color register 266 for(var i = 0; i < outputBuffer.Length; i++) 267 { 268 outputBuffer[i] = colorBytes[i % colorDepth]; 269 } 270 271 if(outputLineOffsetField.Value == 0) 272 { 273 // we can copy everything at once - it might be faster 274 sysbus.WriteBytes(outputBuffer, outputMemoryAddressRegister.Value); 275 } 276 else 277 { 278 // we have to copy per line 279 var lineWidth = (int)pixelsPerLineField.Value * outputFormat.GetColorDepth(); 280 var offset = lineWidth + ((int)outputLineOffsetField.Value * outputFormat.GetColorDepth()); 281 for(var line = 0; line < (int)numberOfLineField.Value; line++) 282 { 283 sysbus.WriteBytes(outputBuffer, (ulong)(outputMemoryAddressRegister.Value + line * offset), line * lineWidth, lineWidth); 284 } 285 } 286 break; 287 case Mode.MemoryToMemoryWithBlending: 288 var bgBlendingMode = backgroundAlphaMode.Value.ToPixelBlendingMode(); 289 var fgBlendingMode = foregroundAlphaMode.Value.ToPixelBlendingMode(); 290 var bgAlpha = (byte)backgroundAlphaField.Value; 291 var fgAlpha = (byte)foregroundAlphaField.Value; 292 293 if(outputLineOffsetField.Value == 0 && foregroundLineOffsetField.Value == 0 && backgroundLineOffsetField.Value == 0) 294 { 295 // we can optimize here and copy everything at once 296 DoCopy(foregroundMemoryAddressRegister.Value, outputMemoryAddressRegister.Value, foregroundBuffer, 297 converter: (localForegroundBuffer, line) => 298 { 299 sysbus.ReadBytes(backgroundMemoryAddressRegister.Value, backgroundBuffer.Length, backgroundBuffer, 0); 300 // per-pixel alpha blending 301 blender.Blend(backgroundBuffer, backgroundClut, localForegroundBuffer, foregroundClut, ref outputBuffer, new Pixel(0, 0, 0, 0xFF), bgAlpha, bgBlendingMode, fgAlpha, fgBlendingMode); 302 return outputBuffer; 303 }); 304 } 305 else 306 { 307 var backgroundFormat = backgroundColorModeField.Value.ToPixelFormat(); 308 DoCopy(foregroundMemoryAddressRegister.Value, outputMemoryAddressRegister.Value, 309 foregroundLineBuffer, 310 (int)foregroundLineOffsetField.Value * foregroundFormat.GetColorDepth(), 311 (int)outputLineOffsetField.Value * outputFormat.GetColorDepth(), 312 (int)numberOfLineField.Value, 313 (localForegroundBuffer, line) => 314 { 315 sysbus.ReadBytes((ulong)(backgroundMemoryAddressRegister.Value + line * (uint)(backgroundLineOffsetField.Value + pixelsPerLineField.Value) * backgroundFormat.GetColorDepth()), backgroundLineBuffer.Length, backgroundLineBuffer, 0); 316 blender.Blend(backgroundLineBuffer, backgroundClut, localForegroundBuffer, foregroundClut, ref outputLineBuffer, null, bgAlpha, bgBlendingMode, fgAlpha, fgBlendingMode); 317 return outputLineBuffer; 318 }); 319 } 320 break; 321 case Mode.MemoryToMemoryWithPfc: 322 fgAlpha = (byte)foregroundAlphaField.Value; 323 fgBlendingMode = foregroundAlphaMode.Value.ToPixelBlendingMode(); 324 325 if(outputLineOffsetField.Value == 0 && foregroundLineOffsetField.Value == 0 && backgroundLineOffsetField.Value == 0) 326 { 327 DoCopy(foregroundMemoryAddressRegister.Value, outputMemoryAddressRegister.Value, 328 foregroundBuffer, 329 converter: (localForegroundBuffer, line) => 330 { 331 fgConverter.Convert(localForegroundBuffer, foregroundClut, fgAlpha, fgBlendingMode, ref outputBuffer); 332 return outputBuffer; 333 }); 334 } 335 else 336 { 337 DoCopy(foregroundMemoryAddressRegister.Value, outputMemoryAddressRegister.Value, 338 foregroundLineBuffer, 339 (int)foregroundLineOffsetField.Value * foregroundFormat.GetColorDepth(), 340 (int)outputLineOffsetField.Value * outputFormat.GetColorDepth(), 341 (int)numberOfLineField.Value, 342 (localForegroundBuffer, line) => 343 { 344 fgConverter.Convert(localForegroundBuffer, foregroundClut, fgAlpha, fgBlendingMode, ref outputLineBuffer); 345 return outputLineBuffer; 346 }); 347 } 348 break; 349 case Mode.MemoryToMemory: 350 if(outputLineOffsetField.Value == 0 && foregroundLineOffsetField.Value == 0) 351 { 352 // we can optimize here and copy everything at once 353 DoCopy(foregroundMemoryAddressRegister.Value, outputMemoryAddressRegister.Value, foregroundBuffer); 354 } 355 else 356 { 357 // in this mode no graphical data transformation is performed 358 // color format is stored in foreground pfc control register 359 360 DoCopy(foregroundMemoryAddressRegister.Value, outputMemoryAddressRegister.Value, 361 foregroundLineBuffer, 362 (int)foregroundLineOffsetField.Value * foregroundFormat.GetColorDepth(), 363 (int)outputLineOffsetField.Value * foregroundFormat.GetColorDepth(), 364 (int)numberOfLineField.Value); 365 } 366 break; 367 } 368 369 startFlag.Value = false; 370 transferCompleteFlag.Value = true; 371 IRQ.Set(); 372 } 373 DoCopy(ulong sourceAddress, ulong destinationAddress, byte[] sourceBuffer, int sourceOffset = 0, int destinationOffset = 0, int count = 1, Func<byte[], int, byte[]> converter = null)374 private void DoCopy(ulong sourceAddress, ulong destinationAddress, byte[] sourceBuffer, int sourceOffset = 0, int destinationOffset = 0, int count = 1, Func<byte[], int, byte[]> converter = null) 375 { 376 var currentSource = sourceAddress; 377 var currentDestination = destinationAddress; 378 379 for(var line = 0; line < count; line++) 380 { 381 sysbus.ReadBytes(currentSource, sourceBuffer.Length, sourceBuffer, 0); 382 var destinationBuffer = converter == null ? sourceBuffer : converter(sourceBuffer, line); 383 sysbus.WriteBytes(destinationBuffer, currentDestination, 0, destinationBuffer.Length); 384 385 currentSource += (ulong)(sourceBuffer.Length + sourceOffset); 386 currentDestination += (ulong)(destinationBuffer.Length + destinationOffset); 387 } 388 } 389 390 private readonly IBusController sysbus; 391 private readonly IFlagRegisterField startFlag; 392 private readonly IFlagRegisterField transferCompleteFlag; 393 private readonly IEnumRegisterField<Mode> dma2dMode; 394 private readonly IValueRegisterField numberOfLineField; 395 private readonly IValueRegisterField pixelsPerLineField; 396 private readonly DoubleWordRegister outputMemoryAddressRegister; 397 private readonly DoubleWordRegister backgroundMemoryAddressRegister; 398 private readonly DoubleWordRegister foregroundMemoryAddressRegister; 399 private readonly IEnumRegisterField<Dma2DColorMode> outputColorModeField; 400 private readonly IEnumRegisterField<Dma2DColorMode> foregroundColorModeField; 401 private readonly IEnumRegisterField<Dma2DColorMode> backgroundColorModeField; 402 private readonly IEnumRegisterField<Dma2DAlphaMode> backgroundAlphaMode; 403 private readonly IEnumRegisterField<Dma2DAlphaMode> foregroundAlphaMode; 404 private readonly IValueRegisterField backgroundColorBlueChannelField; 405 private readonly IValueRegisterField backgroundColorGreenChannelField; 406 private readonly IValueRegisterField backgroundColorRedChannelField; 407 private readonly IValueRegisterField foregroundColorBlueChannelField; 408 private readonly IValueRegisterField foregroundColorGreenChannelField; 409 private readonly IValueRegisterField foregroundColorRedChannelField; 410 private readonly IValueRegisterField backgroundAlphaField; 411 private readonly IValueRegisterField foregroundAlphaField; 412 private readonly DoubleWordRegister outputColorRegister; 413 private readonly IValueRegisterField outputLineOffsetField; 414 private readonly IValueRegisterField foregroundLineOffsetField; 415 private readonly IValueRegisterField backgroundLineOffsetField; 416 private readonly IEnumRegisterField<Dma2DColorMode> foregroundClutColorModeField; 417 private readonly IEnumRegisterField<Dma2DColorMode> backgroundClutColorModeField; 418 private readonly DoubleWordRegisterCollection registers; 419 420 private byte[] outputBuffer; 421 private byte[] outputLineBuffer; 422 423 private byte[] foregroundBuffer; 424 private byte[] foregroundLineBuffer; 425 426 private byte[] backgroundBuffer; 427 private byte[] backgroundLineBuffer; 428 429 [Transient] 430 private IPixelBlender blender; 431 [Transient] 432 private IPixelConverter bgConverter; 433 [Transient] 434 private IPixelConverter fgConverter; 435 436 private const ELFSharp.ELF.Endianess Endianness = ELFSharp.ELF.Endianess.LittleEndian; 437 438 private enum Mode 439 { 440 MemoryToMemory, 441 MemoryToMemoryWithPfc, 442 MemoryToMemoryWithBlending, 443 RegisterToMemory 444 } 445 446 private enum Register : long 447 { 448 ControlRegister = 0x0, 449 InterruptStatusRegister = 0x4, 450 InterruptFlagClearRegister = 0x8, 451 ForegroundMemoryAddressRegister = 0xC, 452 ForegroundOffsetRegister = 0x10, 453 BackgroundMemoryAddressRegister = 0x14, 454 BackgroundOffsetRegister = 0x18, 455 ForegroundPfcControlRegister = 0x1C, 456 ForegroundColorRegister = 0x20, 457 BackgroundPfcControlRegister = 0x24, 458 BackgroundColorRegister = 0x28, 459 ForegroundClutMemoryAddressRegister = 0x2C, 460 BackgroundClutMemoryAddressRegister = 0x30, 461 OutputPfcControlRegister = 0x34, 462 OutputColorRegister = 0x38, 463 OutputMemoryAddressRegister = 0x3C, 464 OutputOffsetRegister = 0x40, 465 NumberOfLineRegister = 0x44 466 } 467 } 468 } 469