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