1 // 2 // Copyright (c) 2010-2025 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.Linq; 10 using Antmicro.Renode.Exceptions; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Peripherals.Bus; 13 using Antmicro.Renode.Peripherals.I2C; 14 using Antmicro.Renode.Utilities; 15 16 namespace Antmicro.Renode.Peripherals.Memory 17 { 18 public class GenericI2cEeprom: II2CPeripheral 19 { GenericI2cEeprom(IMemory memory, int addressBitSize = DefaultAddressBitSize, bool writable = DefaultWritable, int pageSize = DefaultPageSize)20 public GenericI2cEeprom(IMemory memory, int addressBitSize = DefaultAddressBitSize, bool writable = DefaultWritable, int pageSize = DefaultPageSize) 21 { 22 if(addressBitSize != 8 && addressBitSize != 16) 23 { 24 throw new ConstructionException($"'{nameof(addressBitSize)}' has to be one of the legal values: 8, 16"); 25 } 26 if(pageSize <= 0 || !Misc.IsPowerOfTwo((ulong)pageSize)) 27 { 28 throw new ConstructionException($"'{nameof(pageSize)}' has to be a positive power of 2"); 29 } 30 Memory = memory; 31 addressSize = addressBitSize / 8; 32 this.writable = writable; 33 this.pageSize = pageSize; 34 } 35 GenericI2cEeprom(ulong size, int addressBitSize = DefaultAddressBitSize, bool writable = DefaultWritable, int pageSize = DefaultPageSize)36 public GenericI2cEeprom(ulong size, int addressBitSize = DefaultAddressBitSize, bool writable = DefaultWritable, int pageSize = DefaultPageSize) 37 : this(new ArrayMemory(size, Byte.MaxValue), addressBitSize, writable, pageSize) 38 { 39 } 40 Write(byte[] data)41 public void Write(byte[] data) 42 { 43 var index = 0; 44 if(addressBytes < addressSize) 45 { 46 var bytesLeft = addressSize - addressBytes; 47 var bytesWritten = Math.Min(data.Length, bytesLeft); 48 49 var addressValue = BitHelper.ToUInt32(data, 0, bytesWritten, reverse: false); 50 currentAddress = (int)BitHelper.ReplaceBits((uint)currentAddress, addressValue, width: bytesWritten * 8, destinationPosition: (bytesLeft - bytesWritten) * 8); 51 52 addressBytes += bytesWritten; 53 index = bytesWritten; 54 if(addressBytes == addressSize) 55 { 56 this.NoisyLog("Set address: 0x{0:X4}", currentAddress); 57 } 58 } 59 60 var count = data.Length - index; 61 if(count == 0) 62 { 63 return; 64 } 65 66 if(!writable) 67 { 68 this.WarningLog("This memory is configured as read only, but write was attempted"); 69 return; 70 } 71 72 var pageOffset = currentAddress % pageSize; 73 var pageStart = currentAddress - pageOffset; 74 if(count > pageSize) 75 { 76 count = pageSize; 77 index = data.Length - count; 78 } 79 80 var bytesToWrite = Math.Min(count, pageSize - pageOffset); 81 this.DebugLog("Write {0} bytes to address 0x{1:X4}: {2}", bytesToWrite, currentAddress, data.Skip(index).Take(bytesToWrite).ToLazyHexString()); 82 Memory.WriteBytes(currentAddress, data, index, bytesToWrite); 83 index += bytesToWrite; 84 count -= bytesToWrite; 85 currentAddress = pageStart; 86 87 // rollover to the start of the current page 88 if(count > 0) 89 { 90 this.DebugLog("Write {0} bytes to address 0x{1:X4}: {2}", count, currentAddress, data.Skip(index).Take(count).ToLazyHexString()); 91 Memory.WriteBytes(pageStart, data, index, count); 92 currentAddress += count; 93 } 94 } 95 Read(int count = 1)96 public byte[] Read(int count = 1) 97 { 98 var startingAddress = currentAddress; 99 var address = Enumerable.Empty<byte>(); 100 var data = Enumerable.Empty<byte>(); 101 102 if(addressBytes != addressSize && count > 0) 103 { 104 address = BitHelper.GetBytesFromValue((ulong)currentAddress, typeSize: addressSize, reverse: true) 105 .Skip(addressBytes) 106 ; 107 var bytesAdded = Math.Min(addressSize - addressBytes, count); 108 count -= bytesAdded; 109 addressBytes += bytesAdded; 110 } 111 var dataCount = count; 112 113 // reads rollover to the start of the memory 114 if(count >= Memory.Size) 115 { 116 // read is spanning the whole memory 117 data = ReadRollover(0, (int)Memory.Size, currentAddress, count); 118 currentAddress = (currentAddress + count) % (int)Memory.Size; 119 count = 0; 120 } 121 else 122 { 123 var bytesToRead = (int)Math.Min(count, Memory.Size - currentAddress); 124 data = Memory.ReadBytes(currentAddress, bytesToRead); 125 currentAddress += bytesToRead; 126 count -= bytesToRead; 127 128 // rollover 129 if(count > 0) 130 { 131 data = data.Concat(Memory.ReadBytes(0, count)); 132 currentAddress = count; 133 count = 0; 134 } 135 } 136 137 this.NoisyLog("Read {0} bytes from address 0x{1:X4}: {2}", dataCount, startingAddress, data.ToLazyHexString()); 138 return address.Concat(data).ToArray(); 139 } 140 FinishTransmission()141 public void FinishTransmission() 142 { 143 this.NoisyLog("Transmission finished"); 144 addressBytes = 0; 145 } 146 Reset()147 public void Reset() 148 { 149 addressBytes = 0; 150 currentAddress = 0; 151 } 152 153 public IMemory Memory { get; } 154 ReadRollover(int start, int size, int offset, int count)155 private IEnumerable<byte> ReadRollover(int start, int size, int offset, int count) 156 { 157 var data = Memory.ReadBytes(start, size); 158 return Enumerable.Repeat<IEnumerable<byte>>(data, (count + size - 1) / size) 159 .SelectMany(x => x) 160 .Skip(offset) 161 .Take(count) 162 ; 163 } 164 165 private int addressBytes; 166 private int currentAddress; 167 private readonly int addressSize; 168 private readonly bool writable; 169 private readonly int pageSize; 170 171 private const int DefaultAddressBitSize = 8; 172 private const bool DefaultWritable = true; 173 private const int DefaultPageSize = 64; 174 } 175 } 176