1 //
2 // Copyright (c) 2010-2021 Antmicro
3 //
4 //  This file is licensed under the MIT License.
5 //  Full license text is available in 'licenses/MIT.txt'.
6 //
7 
8 // Uncomment the following line and rebuild in order to see packet dumps in the log
9 // #define DEBUG_PACKETS
10 
11 using System;
12 using Antmicro.Renode.Utilities;
13 using Antmicro.Renode.Utilities.Collections;
14 using Antmicro.Renode.Logging;
15 using Antmicro.Renode.Core.Structure.Registers;
16 
17 namespace Antmicro.Renode.Peripherals.I2C
18 {
19     public abstract class I2CPeripheralBase<T> : II2CPeripheral, IProvidesRegisterCollection<ByteRegisterCollection> where T : IConvertible
20     {
I2CPeripheralBase(int addressLength)21         public I2CPeripheralBase(int addressLength)
22         {
23             this.addressLength = addressLength;
24 
25             cache = new SimpleCache();
26             RegistersCollection = new ByteRegisterCollection(this);
27             DefineRegisters();
28 
29             Reset();
30         }
31 
Write(byte[] data)32         public void Write(byte[] data)
33         {
34 #if DEBUG_PACKETS
35             this.Log(LogLevel.Noisy, "Written {0} bytes: {1}", data.Length, Misc.PrettyPrintCollectionHex(data));
36 #else
37             this.Log(LogLevel.Noisy, "Written {0} bytes", data.Length);
38 #endif
39             foreach(var b in data)
40             {
41                 WriteByte(b);
42             }
43         }
44 
Read(int count)45         public byte[] Read(int count)
46         {
47             var result = RegistersCollection.Read(address);
48             this.NoisyLog("Reading register {0} (0x{1:X}) from device: 0x{2:X}", cache.Get(address, x => Enum.GetName(typeof(T), x)), address, result);
49 
50             return new byte [] { result };
51         }
52 
FinishTransmission()53         public void FinishTransmission()
54         {
55             this.NoisyLog("Finishing transmission, going to the idle state");
56             ResetState();
57         }
58 
Reset()59         public virtual void Reset()
60         {
61             ResetState();
62         }
63 
64         public ByteRegisterCollection RegistersCollection { get; }
65 
DefineRegisters()66         protected abstract void DefineRegisters();
67 
ResetState()68         private void ResetState()
69         {
70             // clearing of the address variable itself
71             // is deferred to the moment when
72             // it will be overwritten in WriteByte;
73             // this way ReadByte method can access it
74             addressBitsLeft = addressLength;
75             state = State.CollectingAddress;
76         }
77 
WriteByte(byte b)78         private void WriteByte(byte b)
79         {
80             switch(state)
81             {
82                 case State.CollectingAddress:
83                 {
84                     if(addressBitsLeft == addressLength)
85                     {
86                         // if this is the first write after
87                         // resetting state, clear the address
88                         // to make sure there are no stale bits
89                         // (address can be shorter than 8-bits)
90                         address = 0;
91                     }
92 
93                     // address length is configurable to any number of bits,
94                     // so we need to handle all possible cases - including
95                     // values non-divisable by 8
96                     var position = Math.Max(0, addressBitsLeft - 8);
97                     var width = Math.Min(8, addressBitsLeft);
98 
99                     address = BitHelper.SetBitsFrom(address, b, position, width);
100                     addressBitsLeft -= width;
101 
102                     if(addressBitsLeft == 0)
103                     {
104                         this.Log(LogLevel.Noisy, "Setting register address to {0} (0x{1:X})", cache.Get(address, x => Enum.GetName(typeof(T), x)), address);
105                         state = State.Processing;
106                     }
107                     break;
108                 }
109 
110                 case State.Processing:
111                     this.Log(LogLevel.Noisy, "Writing value 0x{0:X} to register {1} (0x{2:X})", b, cache.Get(address, x => Enum.GetName(typeof(T), x)), address);
112                     RegistersCollection.Write(address, b);
113                     break;
114 
115                 default:
116                     throw new ArgumentException($"Unexpected state: {state}");
117             }
118         }
119 
120         private int addressBitsLeft;
121 
122         private uint address;
123         private State state;
124 
125         private readonly int addressLength;
126         private readonly SimpleCache cache;
127 
128         private enum State
129         {
130             CollectingAddress,
131             Processing
132         }
133     }
134 }
135