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