1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // Copyright (c) 2021-2023 Precursor Project 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System.Collections.Generic; 9 using System; 10 using Antmicro.Renode.Peripherals.Bus; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Core.Structure; 13 using Antmicro.Renode.Core; 14 using Antmicro.Renode.Logging; 15 using Antmicro.Renode.Time; 16 using System.Threading; 17 18 // This model is a reimplementation of the OpenCoresI2C module. 19 namespace Antmicro.Renode.Peripherals.I2C 20 { 21 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 22 public class BetrustedEcI2C : SimpleContainer<II2CPeripheral>, IDoubleWordPeripheral, IKnownSize 23 { BetrustedEcI2C(IMachine machine)24 public BetrustedEcI2C(IMachine machine) : base(machine) 25 { 26 dataToSlave = new Queue<byte>(); 27 dataFromSlave = new Queue<byte>(); 28 IRQ = new GPIO(); 29 30 irqTimeoutCallback = new ClockEntry(IrqTimeout, 1000, this.FinishTransaction, machine, "Irq Scheduler"); 31 32 var registersMap = new Dictionary<long, DoubleWordRegister>() 33 { 34 {(long)Registers.Prescale, new DoubleWordRegister(this) 35 .WithReservedBits(16, 16) 36 .WithValueField(0, 16, FieldMode.Read | FieldMode.Write) 37 }, 38 39 {(long)Registers.Control, new DoubleWordRegister(this) 40 .WithReservedBits(8, 24) 41 .WithFlag(7, out enabled) 42 .WithTag("Interrupt enable", 6, 1) 43 .WithReservedBits(0, 6) 44 }, 45 46 {(long)Registers.Txr, new DoubleWordRegister(this) 47 .WithReservedBits(8, 24) 48 .WithValueField(0, 8, out transmitBuffer, FieldMode.Write) 49 }, 50 51 {(long)Registers.Rxr, new DoubleWordRegister(this) 52 .WithReservedBits(8, 24) 53 .WithValueField(0, 8, out receiveBuffer, FieldMode.Read) 54 }, 55 56 {(long)Registers.Status, new DoubleWordRegister(this) 57 .WithReservedBits(8, 24) 58 .WithFlag(7, out receivedAckFromSlaveNegated, FieldMode.Read) 59 .WithFlag(6, FieldMode.Read, valueProviderCallback: _ => false, name: "Busy") 60 .WithFlag(5, FieldMode.Read, valueProviderCallback: _ => false, name: "Arbitration lost") 61 .WithReservedBits(2, 3) 62 .WithFlag(1, name: "Transfer in progress", valueProviderCallback: _ => 63 { 64 fakeTip = !fakeTip; 65 return fakeTip; 66 }) 67 .WithFlag(0, out i2cIrqStatus, FieldMode.Read) 68 }, 69 70 {(long)Registers.Command, new DoubleWordRegister(this) 71 .WithReservedBits(8, 24) 72 .WithFlag(7, out generateStartCondition, FieldMode.Write) 73 .WithFlag(6, out generateStopCondition, FieldMode.Write) 74 .WithFlag(5, out readFromSlave, FieldMode.Write) 75 .WithFlag(4, out writeToSlave, FieldMode.Write) 76 .WithFlag(3, FieldMode.Read, name: "ACK", valueProviderCallback: _ => true) 77 .WithReservedBits(1, 2) 78 .WithFlag(0, FieldMode.Write, writeCallback: (_, __) => i2cIrqStatus.Value = false) 79 .WithWriteCallback((_, __) => 80 { 81 if(!enabled.Value) 82 { 83 return; 84 } 85 86 if(generateStartCondition.Value) 87 { 88 generateStartCondition.Value = false; 89 if(transactionInProgress) 90 { 91 // repeated start - finish previous transaction first 92 SendDataToSlave(); 93 } 94 else 95 { 96 transactionInProgress = true; 97 } 98 99 dataFromSlave.Clear(); 100 101 selectedSlave?.FinishTransmission(); 102 if(!TryResolveSelectedSlave(out selectedSlave)) 103 { 104 return; 105 } 106 } 107 108 if(writeToSlave.Value) 109 { 110 writeToSlave.Value = false; 111 HandleWriteToSlaveCommand(); 112 } 113 114 if(readFromSlave.Value) 115 { 116 readFromSlave.Value = false; 117 HandleReadFromSlaveCommand(); 118 } 119 120 if (transactionInProgress && 0 == Interlocked.CompareExchange(ref irqTimeoutCallbackQueued, 1, 0)) { 121 // this.Log(LogLevel.Error, "I2C: Adding clock entry for {0}",this.irqTimeoutCallback); 122 try { 123 machine.ClockSource.AddClockEntry(irqTimeoutCallback); 124 } 125 catch (ArgumentException ex) 126 { 127 this.Log(LogLevel.Error, "Unable to add clock entry for timeout (queued: {0}): {1}", ex, irqTimeoutCallbackQueued); 128 // this.Log(LogLevel.Error, "I2C: Done adding clock entry for {0}",this.irqTimeoutCallback); 129 } 130 } 131 132 if(generateStopCondition.Value) 133 { 134 selectedSlave?.FinishTransmission(); 135 generateStopCondition.Value = false; 136 if(!transactionInProgress) 137 { 138 return; 139 } 140 141 SendDataToSlave(); 142 transactionInProgress = false; 143 } 144 145 shouldSendTxRxIrq = true; 146 }) 147 }, 148 {(long)Registers.EventStatus, new DoubleWordRegister(this) 149 .WithReservedBits(4, 28) 150 .WithFlag(3, FieldMode.Read, name: "USBCC_INT_STATUS", valueProviderCallback: _ => false /*usbccIrqStatus*/) 151 .WithFlag(2, FieldMode.Read, name: "GYRO_INT_STATUS", valueProviderCallback: _ => false /*gyroIrqStatus*/) 152 .WithFlag(1, FieldMode.Read, name: "GG_INT_STATUS", valueProviderCallback: _ => false /*ggIrqStatus*/) 153 .WithFlag(0, FieldMode.Read, name: "I2C_INT_STATUS", valueProviderCallback: _ => i2cIrqStatus.Value) 154 }, 155 156 {(long)Registers.EventPending, new DoubleWordRegister(this) 157 .WithReservedBits(4, 28) 158 .WithFlag(3, out usbccIrqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "USBCC_INT_PENDING", changeCallback: (_, __) => UpdateInterrupts()) 159 .WithFlag(2, out gyroIrqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "GYRO_INT_PENDING", changeCallback: (_, __) => UpdateInterrupts()) 160 .WithFlag(1, out ggIrqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "GG_INT_PENDING", changeCallback: (_, __) => UpdateInterrupts()) 161 .WithFlag(0, out i2cIrqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "I2C_INT_PENDING", changeCallback: (_, __) => UpdateInterrupts()) 162 }, 163 164 {(long)Registers.EventEnable, new DoubleWordRegister(this) 165 .WithReservedBits(4, 28) 166 .WithFlag(3, out usbccIrqEnabled, name: "USBCC_INT_ENABLE", changeCallback: (_, __) => UpdateInterrupts()) 167 .WithFlag(2, out gyroIrqEnabled, name: "GYRO_INT_ENABLE", changeCallback: (_, __) => UpdateInterrupts()) 168 .WithFlag(1, out ggIrqEnabled, name: "GG_INT_ENABLE", changeCallback: (_, __) => UpdateInterrupts()) 169 .WithFlag(0, out i2cIrqEnabled, name: "I2C_INT_ENABLE", changeCallback: (_, __) => UpdateInterrupts()) 170 } 171 }; 172 173 registers = new DoubleWordRegisterCollection(this, registersMap); 174 UpdateInterrupts(); 175 } 176 FinishTransaction()177 private void FinishTransaction() 178 { 179 // this.Log(LogLevel.Error, "I2C: Removing clock entry for {0}",this.irqTimeoutCallback); 180 machine.ClockSource.TryRemoveClockEntry(FinishTransaction); 181 irqTimeoutCallbackQueued = 0; 182 if(shouldSendTxRxIrq) 183 { 184 shouldSendTxRxIrq = false; 185 // txRxDoneIrqStatus = true; 186 UpdateInterrupts(); 187 // txRxDoneIrqStatus = false; 188 } 189 } 190 Reset()191 public override void Reset() 192 { 193 registers.Reset(); 194 dataToSlave.Clear(); 195 dataFromSlave.Clear(); 196 UpdateInterrupts(); 197 } 198 ReadDoubleWord(long offset)199 public uint ReadDoubleWord(long offset) 200 { 201 return registers.Read(offset); 202 } 203 WriteDoubleWord(long offset, uint value)204 public void WriteDoubleWord(long offset, uint value) 205 { 206 registers.Write(offset, value); 207 } 208 UpdateInterrupts()209 private void UpdateInterrupts() 210 { 211 if(i2cIrqStatus.Value) 212 { 213 i2cIrqPending.Value = true; 214 } 215 /* 216 if(ggIrqStatus) 217 { 218 ggIrqPending.Value = true; 219 } 220 if(gyroIrqStatus) 221 { 222 gyroIrqPending.Value = true; 223 } 224 if(usbccIrqStatus) 225 { 226 usbccIrqPending.Value = true; 227 } 228 */ 229 // this.Log(LogLevel.Noisy, " Setting status: {0}, enabled: {1}, pending: {2}", txRxDoneIrqStatus, txRxDoneIrqEnabled.Value, txRxDoneIrqPending.Value); 230 IRQ.Set((i2cIrqPending.Value && i2cIrqEnabled.Value) 231 || (ggIrqPending.Value && ggIrqEnabled.Value) 232 || (gyroIrqPending.Value && gyroIrqEnabled.Value) 233 || (usbccIrqPending.Value && usbccIrqEnabled.Value)); 234 } 235 236 public GPIO IRQ { get; } 237 238 public long Size => 0x800; 239 TryResolveSelectedSlave(out II2CPeripheral selectedSlave)240 private bool TryResolveSelectedSlave(out II2CPeripheral selectedSlave) 241 { 242 var slaveAddress = (byte)(transmitBuffer.Value >> 1); 243 if(!ChildCollection.TryGetValue(slaveAddress, out selectedSlave)) 244 { 245 this.Log(LogLevel.Warning, "Addressing unregistered slave: 0x{0:X}", slaveAddress); 246 receivedAckFromSlaveNegated.Value = true; 247 return false; 248 } 249 250 receivedAckFromSlaveNegated.Value = false; 251 return true; 252 } 253 HandleReadFromSlaveCommand()254 private void HandleReadFromSlaveCommand() 255 { 256 if (dataFromSlave == null) 257 { 258 this.Log(LogLevel.Error, "dataFromSlave is NULL!"); 259 return; 260 } 261 if(selectedSlave == null) 262 { 263 this.Log(LogLevel.Error, "selectedSlave is NULL!"); 264 return; 265 } 266 267 foreach(var b in selectedSlave.Read()) 268 { 269 dataFromSlave.Enqueue(b); 270 } 271 272 if(dataFromSlave.Count == 0) 273 { 274 this.Log(LogLevel.Warning, "Trying to read from slave, but no data is available"); 275 receiveBuffer.Value = 0; 276 return; 277 } 278 279 if (receiveBuffer == null) 280 { 281 this.Log(LogLevel.Error, "receiveBuffer is NULL!"); 282 dataFromSlave.Dequeue(); 283 } 284 receiveBuffer.Value = dataFromSlave.Dequeue(); 285 UpdateInterrupts(); 286 } 287 SendDataToSlave()288 private void SendDataToSlave() 289 { 290 if(dataToSlave.Count == 0 || selectedSlave == null) 291 { 292 this.Log(LogLevel.Warning, "Trying to send data to slave, but either no data is available or the slave is not selected"); 293 return; 294 } 295 296 selectedSlave.Write(dataToSlave.ToArray()); 297 dataToSlave.Clear(); 298 } 299 HandleWriteToSlaveCommand()300 private void HandleWriteToSlaveCommand() 301 { 302 if(!transactionInProgress) 303 { 304 this.Log(LogLevel.Warning, "Writing to slave without generating START signal"); 305 return; 306 } 307 308 dataToSlave.Enqueue((byte)transmitBuffer.Value); 309 i2cIrqStatus.Value = true; 310 UpdateInterrupts(); 311 } 312 313 private bool transactionInProgress; 314 // Just toggle TIP here to fake it. 315 private bool fakeTip; 316 private II2CPeripheral selectedSlave; 317 318 private readonly Queue<byte> dataToSlave; 319 private readonly Queue<byte> dataFromSlave; 320 private readonly IValueRegisterField receiveBuffer; 321 private readonly IValueRegisterField transmitBuffer; 322 private readonly IFlagRegisterField readFromSlave; 323 private readonly IFlagRegisterField writeToSlave; 324 325 private IFlagRegisterField i2cIrqEnabled; 326 private IFlagRegisterField ggIrqEnabled; 327 private IFlagRegisterField gyroIrqEnabled; 328 private IFlagRegisterField usbccIrqEnabled; 329 private IFlagRegisterField i2cIrqStatus; 330 // private bool ggIrqStatus; 331 // private bool gyroIrqStatus; 332 // private bool usbccIrqStatus; 333 private IFlagRegisterField i2cIrqPending; 334 private IFlagRegisterField ggIrqPending; 335 private IFlagRegisterField gyroIrqPending; 336 private IFlagRegisterField usbccIrqPending; 337 338 private bool shouldSendTxRxIrq; 339 // private bool txRxDoneIrqStatus; 340 private long irqTimeoutCallbackQueued; 341 private readonly IFlagRegisterField enabled; 342 private readonly IFlagRegisterField receivedAckFromSlaveNegated; 343 private readonly IFlagRegisterField generateStartCondition; 344 private readonly IFlagRegisterField generateStopCondition; 345 private readonly DoubleWordRegisterCollection registers; 346 private ClockEntry irqTimeoutCallback; 347 348 private const ulong IrqTimeout = 5; 349 350 private enum Registers 351 { 352 Prescale = 0x0, 353 Control = 0x4, 354 Txr = 0x8, 355 Rxr = 0xC, 356 Command = 0x10, 357 Status = 0x14, 358 BitbangMode = 0x18, 359 Bb = 0x1c, 360 BbR = 0x20, 361 EventStatus = 0x24, 362 EventPending = 0x28, 363 EventEnable = 0x2c 364 } 365 } 366 } 367