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