1 // 2 // Copyright (c) 2010-2023 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.Collections.Generic; 8 using Antmicro.Renode.Peripherals.Bus; 9 using Antmicro.Renode.Core.Structure.Registers; 10 using Antmicro.Renode.Core.Structure; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Utilities; 14 15 namespace Antmicro.Renode.Peripherals.I2C 16 { 17 public class OpenCoresI2C : SimpleContainer<II2CPeripheral>, IBytePeripheral, IKnownSize 18 { OpenCoresI2C(IMachine machine)19 public OpenCoresI2C(IMachine machine) : base(machine) 20 { 21 dataToSlave = new Queue<byte>(); 22 dataFromSlave = new Queue<byte>(); 23 24 var commonRegistersMap = new Dictionary<long, ByteRegister>() 25 { 26 {(long)Registers.Control, new ByteRegister(this) 27 .WithFlag(7, out enabled) 28 .WithTag("Interrupt enable", 6, 1) 29 .WithReservedBits(0, 6) 30 } 31 }; 32 33 var readRegistersMap = new Dictionary<long, ByteRegister>(commonRegistersMap) 34 { 35 {(long)Registers.Receive, new ByteRegister(this) 36 .WithValueField(0, 8, out receiveBuffer, FieldMode.Read) 37 }, 38 39 {(long)Registers.Status, new ByteRegister(this) 40 .WithFlag(7, out receivedAckFromSlaveNegated, FieldMode.Read) 41 .WithFlag(6, FieldMode.Read, valueProviderCallback: _ => transactionInProgress, name: "Busy") 42 // We're using receivedAckFromSlaveNegated as we do not implement other ways of detecting arbitration loss 43 .WithFlag(5, FieldMode.Read, valueProviderCallback: _ => receivedAckFromSlaveNegated.Value, name: "Arbitration lost") 44 .WithReservedBits(2, 3) 45 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => false, name: "Transfer in progress") 46 .WithFlag(0, out interruptFlag, FieldMode.Read) 47 } 48 }; 49 50 var writeRegistersMap = new Dictionary<long, ByteRegister>(commonRegistersMap) 51 { 52 {(long)Registers.Transmit, new ByteRegister(this) 53 .WithValueField(0, 8, out transmitBuffer, FieldMode.Write) 54 }, 55 56 {(long)Registers.Command, new ByteRegister(this) 57 .WithFlag(7, out generateStartCondition, FieldMode.Write) 58 .WithFlag(6, out generateStopCondition, FieldMode.Write) 59 .WithFlag(5, out readFromSlave, FieldMode.Write) 60 .WithFlag(4, out writeToSlave, FieldMode.Write) 61 .WithTag("ACK", 3, 1) 62 .WithReservedBits(1, 2) 63 .WithFlag(0, FieldMode.Write, writeCallback: (_, __) => interruptFlag.Value = false) 64 .WithWriteCallback((_, __) => 65 { 66 if(!enabled.Value) 67 { 68 this.Log(LogLevel.Warning, "Writing to a disabled controller."); 69 return; 70 } 71 72 if(generateStartCondition.Value) 73 { 74 generateStartCondition.Value = false; 75 if(transactionInProgress) 76 { 77 // repeated start - finish previous transaction first 78 SendDataToSlave(); 79 } 80 else 81 { 82 transactionInProgress = true; 83 } 84 85 dataFromSlave.Clear(); 86 87 if(!TryResolveSelectedSlave(out selectedSlave)) 88 { 89 interruptFlag.Value = true; 90 transactionInProgress = false; 91 return; 92 } 93 } 94 else if(writeToSlave.Value) 95 { 96 writeToSlave.Value = false; 97 HandleWriteToSlaveCommand(); 98 } 99 else if(readFromSlave.Value) 100 { 101 readFromSlave.Value = false; 102 HandleReadFromSlaveCommand(); 103 } 104 105 if(generateStopCondition.Value) 106 { 107 generateStopCondition.Value = false; 108 if(!transactionInProgress) 109 { 110 this.Log(LogLevel.Warning, "Asked to generate STOP signal, but no START signal has been recently generated"); 111 return; 112 } 113 114 SendDataToSlave(); 115 selectedSlave = null; 116 transactionInProgress = false; 117 } 118 }) 119 } 120 }; 121 122 readRegisters = new ByteRegisterCollection(this, readRegistersMap); 123 writeRegisters = new ByteRegisterCollection(this, writeRegistersMap); 124 } 125 ReadByte(long offset)126 public byte ReadByte(long offset) 127 { 128 return readRegisters.Read(offset); 129 } 130 WriteByte(long offset, byte value)131 public void WriteByte(long offset, byte value) 132 { 133 writeRegisters.Write(offset, value); 134 } 135 Reset()136 public override void Reset() 137 { 138 readRegisters.Reset(); 139 writeRegisters.Reset(); 140 dataToSlave.Clear(); 141 dataFromSlave.Clear(); 142 selectedSlave = null; 143 transactionInProgress = false; 144 } 145 146 public long Size => 0x1000; 147 TryResolveSelectedSlave(out II2CPeripheral selectedSlave)148 private bool TryResolveSelectedSlave(out II2CPeripheral selectedSlave) 149 { 150 var slaveAddress = (byte)(transmitBuffer.Value >> 1); 151 if(!ChildCollection.TryGetValue(slaveAddress, out selectedSlave)) 152 { 153 this.Log(LogLevel.Warning, "Addressing unregistered slave: 0x{0:X}", slaveAddress); 154 receivedAckFromSlaveNegated.Value = true; 155 return false; 156 } 157 158 receivedAckFromSlaveNegated.Value = false; 159 return true; 160 } 161 HandleReadFromSlaveCommand()162 private void HandleReadFromSlaveCommand() 163 { 164 if(dataFromSlave.Count == 0) 165 { 166 foreach(var b in selectedSlave.Read()) 167 { 168 dataFromSlave.Enqueue(b); 169 } 170 171 if(dataFromSlave.Count == 0) 172 { 173 this.Log(LogLevel.Warning, "Trying to read from slave, but no data is available"); 174 receiveBuffer.Value = 0; 175 return; 176 } 177 } 178 179 receiveBuffer.Value = dataFromSlave.Dequeue(); 180 } 181 SendDataToSlave()182 private void SendDataToSlave() 183 { 184 if(dataToSlave.Count == 0 || selectedSlave == null) 185 { 186 this.Log(LogLevel.Warning, "Trying to send data to slave, but either no data is available or the slave is not selected"); 187 return; 188 } 189 190 selectedSlave.Write(dataToSlave.ToArray()); 191 dataToSlave.Clear(); 192 } 193 HandleWriteToSlaveCommand()194 private void HandleWriteToSlaveCommand() 195 { 196 if(selectedSlave == null) 197 { 198 this.Log(LogLevel.Warning, "Trying to write to not selected slave, ignoring"); 199 return; 200 } 201 202 dataToSlave.Enqueue((byte)transmitBuffer.Value); 203 interruptFlag.Value = true; 204 } 205 206 private bool transactionInProgress; 207 private II2CPeripheral selectedSlave; 208 209 private readonly Queue<byte> dataToSlave; 210 private readonly Queue<byte> dataFromSlave; 211 private readonly IValueRegisterField receiveBuffer; 212 private readonly IValueRegisterField transmitBuffer; 213 private readonly IFlagRegisterField readFromSlave; 214 private readonly IFlagRegisterField writeToSlave; 215 private readonly IFlagRegisterField interruptFlag; 216 private readonly IFlagRegisterField enabled; 217 private readonly IFlagRegisterField receivedAckFromSlaveNegated; 218 private readonly IFlagRegisterField generateStartCondition; 219 private readonly IFlagRegisterField generateStopCondition; 220 private readonly ByteRegisterCollection readRegisters; 221 private readonly ByteRegisterCollection writeRegisters; 222 223 private enum Registers 224 { 225 ClockPrescaleLow = 0x0, 226 ClockPrescaleHigh = 0x4, 227 Control = 0x8, 228 // no, it's not a bug - Transmit is write-only, Receive read-only 229 Transmit = 0xC, 230 Receive = 0xC, 231 // no, it's not a bug - Command is write-only, Status read-only 232 Command = 0x10, 233 Status = 0x10 234 } 235 } 236 } 237