1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // Copyright (c) 2018-2022 Microchip 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 // Basic generic graphical controller without any 2D/3D acceleration. 9 // Example baremetal drivers located at https://github.com/AntonKrug/mustein_gpu_driver 10 // 11 // v0.2 2018/11/29 anton.krug@microchip.com SanFrancisco Summit variant 12 // v0.3 2019/02/13 anton.krug@microchip.com More color modes, embedded memory and added versality 13 // 14 15 using Antmicro.Renode.Backends.Display; 16 using Antmicro.Renode.Core; 17 using Antmicro.Renode.Core.Structure.Registers; 18 using Antmicro.Renode.Logging; 19 using Antmicro.Renode.Peripherals.Bus; 20 using Antmicro.Renode.Peripherals.Memory; 21 using System; 22 using System.Collections.Generic; 23 24 namespace Antmicro.Renode.Peripherals.Video 25 { 26 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 27 public class MusteinGenericGPU : AutoRepaintingVideo, IDoubleWordPeripheral, IKnownSize 28 { MusteinGenericGPU(IMachine machine, MappedMemory buffer, bool registers64bitAligned = false, int controlBit = 23, uint frameBufferSize = 0x800000)29 public MusteinGenericGPU(IMachine machine, MappedMemory buffer, bool registers64bitAligned = false, int controlBit = 23, uint frameBufferSize = 0x800000) : base(machine) 30 { 31 this.machine = machine; 32 this.frameBufferSize = frameBufferSize; 33 this.controlBit = controlBit; 34 this.controlOffset = 1U << controlBit; 35 this.is64bitAligned = registers64bitAligned; 36 this.accessAligment = (registers64bitAligned) ? 8 : 4; 37 this.sync = new object(); 38 this.underlyingBuffer = buffer; 39 40 // Allows to switch ordering for the 8bit mode lookup table depending what is closer to the native host colorspace 41 var lookupTableRgbx = false; 42 #if !PLATFORM_WINDOWS 43 lookupTableRgbx = true; 44 #endif 45 46 // Populating lookup table for the 8bit color mode to 24bit conversion, because the 332 format is unbalanced so 47 // much and the Red/Green have 50% more bits than Blue the value 0xFF has a yellow tint. Balanced white (gray) 48 // color is actually value 0xF6. The white color is fairly gray because the discarted least significant bits 49 // still acumulate to a 1/4 of the total brightness/value (we are cutting away too many bits). 50 colorTable = new uint[256]; 51 for(var index = 0u; index < colorTable.Length; index++) 52 { 53 var red = index & 0x7; 54 var green = (index & 0x38) >> 3; 55 var blue = (index & 0xc0) >> 6; 56 var value = 0u; 57 if(lookupTableRgbx) 58 { 59 value = red << 21 | green << 13 | blue << 6; // Converting RGB332 to RGB888 which will be used for RGBX8888 60 } 61 else 62 { 63 value = red << 5 | green << 13 | blue << 22; // Converting RGB332 to BGR888 which will be used for BGRX8888 64 } 65 colorTable[index] = value; 66 } 67 68 colorModeToPixelFormatTable = new Dictionary<ColorMode, PixelFormat>() 69 { 70 { ColorMode.LowColor, (lookupTableRgbx) ? PixelFormat.RGBX8888 : PixelFormat.BGRX8888 }, 71 { ColorMode.HighColor, PixelFormat.RGB565 }, 72 { ColorMode.TrueColor, PixelFormat.RGBX8888 } 73 }; 74 75 Reconfigure(DefaultWidth, DefaultHeight, DefaultColor); 76 77 copyPatterns = new Dictionary<Tuple<ColorMode, bool, PixelPacking>, Action>() 78 { 79 { Tuple.Create(ColorMode.LowColor, false, PixelPacking.SinglePixelPerWrite), () => ConvertAndSkip(1, 3) }, 80 { Tuple.Create(ColorMode.HighColor, false, PixelPacking.SinglePixelPerWrite), () => CopyAndSkip(2, 2) }, 81 { Tuple.Create(ColorMode.TrueColor, false, PixelPacking.SinglePixelPerWrite), CopyFully }, 82 83 { Tuple.Create(ColorMode.LowColor, false, PixelPacking.FullyPacked32bit), ConvertFully }, 84 { Tuple.Create(ColorMode.HighColor, false, PixelPacking.FullyPacked32bit), CopyFully }, 85 { Tuple.Create(ColorMode.TrueColor, false, PixelPacking.FullyPacked32bit), CopyFully }, 86 87 // In a 32bit peripheral aligment mode using fully packed 64bit is ilegal and will act as fully packed 32bit 88 { Tuple.Create(ColorMode.LowColor, false, PixelPacking.FullyPacked64bit), ConvertFully }, 89 { Tuple.Create(ColorMode.HighColor, false, PixelPacking.FullyPacked64bit), CopyFully }, 90 { Tuple.Create(ColorMode.TrueColor, false, PixelPacking.FullyPacked64bit), CopyFully }, 91 92 { Tuple.Create(ColorMode.LowColor, true, PixelPacking.SinglePixelPerWrite), () => ConvertAndSkip(1, 7) }, 93 { Tuple.Create(ColorMode.HighColor, true, PixelPacking.SinglePixelPerWrite), () => CopyAndSkip(2, 6) }, 94 { Tuple.Create(ColorMode.TrueColor, true, PixelPacking.SinglePixelPerWrite), () => CopyAndSkip(4, 4) }, 95 96 { Tuple.Create(ColorMode.LowColor, true, PixelPacking.FullyPacked32bit), () => ConvertAndSkip(4, 4) }, 97 { Tuple.Create(ColorMode.HighColor, true, PixelPacking.FullyPacked32bit), () => CopyAndSkip(2, 4) }, 98 { Tuple.Create(ColorMode.TrueColor, true, PixelPacking.FullyPacked32bit), () => CopyAndSkip(4, 4) }, 99 100 { Tuple.Create(ColorMode.LowColor, true, PixelPacking.FullyPacked64bit), ConvertFully }, 101 { Tuple.Create(ColorMode.HighColor, true, PixelPacking.FullyPacked64bit), CopyFully }, 102 { Tuple.Create(ColorMode.TrueColor, true, PixelPacking.FullyPacked64bit), CopyFully }, 103 }; 104 105 // Populate the control registers addresses 106 GenerateRegisterCollection(); 107 } 108 WriteDoubleWord(long address, uint value)109 public void WriteDoubleWord(long address, uint value) 110 { 111 registers.Write(address, value); 112 } 113 ReadDoubleWord(long offset)114 public uint ReadDoubleWord(long offset) 115 { 116 return registers.Read(offset); 117 } 118 Reset()119 public override void Reset() 120 { 121 registers.Reset(); 122 colorMode = default(ColorMode); 123 pixelPacking = default(PixelPacking); 124 } 125 ChangePacking(PixelPacking packing)126 public void ChangePacking(PixelPacking packing) 127 { 128 this.Log(LogLevel.Noisy, "The display is using {0} packing format", packing); 129 pixelPacking = packing; 130 } 131 132 public long Size => controlOffset * 2; // Peripheral is split into 2 equal partitions (buffer and control registers) 133 Repaint()134 protected override void Repaint() 135 { 136 if(copyPatterns.TryGetValue(Tuple.Create(colorMode, is64bitAligned, pixelPacking), out Action command)) 137 { 138 lock(sync) 139 { 140 command.Invoke(); 141 } 142 } 143 else 144 { 145 this.Log(LogLevel.Error, "Unsuported colorMode ({0}, {1}, {2}), aligment and pixel packing is used", colorMode, is64bitAligned, pixelPacking); 146 } 147 } 148 Reconfigure(uint? setWidth = null, uint? setHeight = null, ColorMode? setColor = null)149 private void Reconfigure(uint? setWidth = null, uint? setHeight = null, ColorMode? setColor = null) 150 { 151 var finalFormat = Format; 152 if(setColor != null) 153 { 154 if(colorModeToPixelFormatTable.TryGetValue((ColorMode)setColor, out finalFormat)) 155 { 156 colorMode = (ColorMode)setColor; 157 } 158 else 159 { 160 this.Log(LogLevel.Error, "Setting wrong color value {0}, keeping original value {1}", setColor, finalFormat); 161 } 162 } 163 164 lock(sync) 165 { 166 base.Reconfigure((int?)setWidth, (int?)setHeight, finalFormat); 167 } 168 169 this.Log(LogLevel.Noisy, "The display is reconfigured to {0}x{1} with {2} color format (setColor ={3})", Width, Height, Format.ToString(), setColor); 170 171 if(((setWidth != null) || (setHeight != null)) && ((Width * Height * accessAligment) > frameBufferSize)) 172 { 173 this.Log(LogLevel.Warning, "This resolution with some (or all) pixel packing modes will not fit in the frameBuffer, if needed increase the frameBufferSize."); 174 } 175 } 176 GenerateRegisterCollection()177 private void GenerateRegisterCollection() 178 { 179 var registerDictionary = new Dictionary<long, DoubleWordRegister> 180 { 181 { controlOffset + (long)Registers.Width * accessAligment, new DoubleWordRegister(this, DefaultWidth) 182 .WithValueField(0, 16, name: "Width", writeCallback: (_, x) => Reconfigure(setWidth: (uint)x), valueProviderCallback: _ => (uint)Width) 183 }, 184 185 { controlOffset + (long)Registers.Height * accessAligment, new DoubleWordRegister(this, DefaultHeight) 186 .WithValueField(0, 16, name: "Height", writeCallback: (_, y) => Reconfigure(setHeight: (uint)y), valueProviderCallback: _ => (uint)Height) 187 }, 188 189 { controlOffset + (long)Registers.Format * accessAligment, new DoubleWordRegister(this, (uint)DefaultColor | ((uint)DefaultPacking << 4)) 190 .WithEnumField<DoubleWordRegister, ColorMode>(0, 4, name: "Color", writeCallback: (_, c) => Reconfigure(setColor: c), valueProviderCallback: _ => colorMode) 191 .WithEnumField<DoubleWordRegister, PixelPacking>(4, 4, name: "Packing", writeCallback: (_, c) => ChangePacking(c), valueProviderCallback: _ => pixelPacking) 192 } 193 }; 194 195 if(is64bitAligned) 196 { 197 // Dormant registers for the high 32bit accesses of the 64bit registers, 198 // just so there will not be logged any unimplemented accesses 199 foreach(long registerIndex in Enum.GetValues(typeof(Registers))) 200 { 201 registerDictionary.Add( 202 controlOffset + registerIndex * accessAligment + 4, new DoubleWordRegister(this, 0x0) 203 .WithTag("Dormant upper 32bit of 64bit registers", 0, 32) 204 ); 205 } 206 } 207 registers = new DoubleWordRegisterCollection(this, registerDictionary); 208 } 209 210 // We can convert from the 332 format the whole lot of bytes without skipping bytes in the input buffer ConvertFully()211 private void ConvertFully() 212 { 213 var colorValues = underlyingBuffer.ReadBytes(0, buffer.Length * 4); 214 for(var i = 0; i < buffer.Length; ++i) 215 { 216 buffer[i] = 0; 217 buffer[i] = colorValues[i * 4 + 1]; 218 buffer[i] = (byte)(colorValues[i * 4 + 2] >> 8); 219 buffer[i] = (byte)(colorValues[i * 4 + 3] >> 16); 220 } 221 } 222 223 // When there are parts to skip and the the rest needs to be converted from 332 format ConvertAndSkip(int bytesToCopy, int bytesToSkip)224 private void ConvertAndSkip(int bytesToCopy, int bytesToSkip) 225 { 226 var indexDest = 0; 227 var indexSrc = 0; 228 while(indexDest < buffer.Length) 229 { 230 for(var indexPack = 0u; indexPack < bytesToCopy; indexPack++, indexSrc++) 231 { 232 HandleByte(indexSrc, ref indexDest); 233 } 234 indexSrc += bytesToSkip; 235 } 236 } 237 HandleByte(int indexSrc, ref int indexDest)238 private void HandleByte(int indexSrc, ref int indexDest) 239 { 240 // Each byte gets transfered via the lookup table (to transfer the 8bit 332 format to a format the backend can display 241 var colorValue = colorTable[underlyingBuffer.ReadDoubleWord(indexSrc)]; 242 buffer[indexDest++] = 0; 243 buffer[indexDest++] = (byte)(colorValue); 244 buffer[indexDest++] = (byte)(colorValue >> 8); 245 buffer[indexDest++] = (byte)(colorValue >> 16); 246 } 247 248 // We can copy the whole lot as it is. No conversion between the bytes CopyFully()249 private void CopyFully() 250 { 251 var bytes = underlyingBuffer.ReadBytes(0, buffer.Length); 252 for(var index = 0; index < buffer.Length; index++) 253 { 254 buffer[index] = bytes[index]; 255 } 256 } 257 258 // When there are parts to skip but no conversion needed CopyAndSkip(int bytesToCopy, int bytesToSkip)259 private void CopyAndSkip(int bytesToCopy, int bytesToSkip) 260 { 261 var indexDest = 0; 262 var indexSrc = 0; 263 while(indexDest < buffer.Length) 264 { 265 for(var indexPack = 0; indexPack < bytesToCopy; indexPack++, indexDest++, indexSrc++) 266 { 267 buffer[indexDest] = underlyingBuffer.ReadByte(indexSrc); 268 } 269 indexSrc += bytesToSkip; 270 } 271 } 272 273 private DoubleWordRegisterCollection registers; 274 private object sync; 275 private ColorMode colorMode; 276 private PixelPacking pixelPacking; 277 private readonly MappedMemory underlyingBuffer; 278 279 private readonly IMachine machine; 280 private readonly int accessAligment; 281 private readonly bool is64bitAligned; 282 private readonly int controlBit; 283 private readonly uint controlOffset; 284 private readonly uint frameBufferSize; 285 private readonly uint[] colorTable; 286 private readonly Dictionary<Tuple<ColorMode, bool, PixelPacking>, Action> copyPatterns; 287 private readonly Dictionary<ColorMode, PixelFormat> colorModeToPixelFormatTable; 288 289 private const int Alignment = 0x1000; 290 private const uint DefaultWidth = 128; 291 private const uint DefaultHeight = 128; 292 private const ColorMode DefaultColor = ColorMode.TrueColor; 293 private const PixelPacking DefaultPacking = PixelPacking.SinglePixelPerWrite; 294 295 public enum PixelPacking : uint 296 { 297 SinglePixelPerWrite = 0, 298 FullyPacked32bit = 1, 299 FullyPacked64bit = 2 300 } 301 302 private enum Registers : long 303 { 304 Width = 0x0, 305 Height = 0x1, 306 Format = 0x2 307 } 308 309 private enum ColorMode : uint 310 { 311 LowColor = 0, // 8-bit per pixel, 3-bits Red, 3-bits Green and 2-bits Blue 312 HighColor = 1, // 16-bit per pixel, 5-bits Red, 6-bits Green and 5-bits Blue 313 TrueColor = 2 // 32-bit per pixel, 8-bits Red, 8-bits Green and 8-bits Blue 314 } 315 } 316 } 317