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; 8 using System.Linq; 9 using System.Collections.Generic; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Utilities; 14 using Antmicro.Renode.Core.Structure.Registers; 15 16 namespace Antmicro.Renode.Peripherals.I2C 17 { 18 // This only implements I2C master operation 19 public abstract class EFR32_GenericI2CController : SimpleContainer<II2CPeripheral> 20 { EFR32_GenericI2CController(IMachine machine)21 public EFR32_GenericI2CController(IMachine machine) : base(machine) 22 { 23 IRQ = new GPIO(); 24 txBuffer = new Queue<byte>(); 25 rxBuffer = new Queue<byte>(); 26 interruptsManager = new InterruptManager<Interrupt>(this); 27 } 28 Reset()29 public override void Reset() 30 { 31 currentAddress = 0; 32 isWrite = false; 33 waitingForAddressByte = false; 34 txBuffer.Clear(); 35 rxBuffer.Clear(); 36 interruptsManager.Reset(); 37 } 38 39 [IrqProvider] 40 public GPIO IRQ { get; } 41 GenerateControlRegister()42 protected DoubleWordRegister GenerateControlRegister() => new DoubleWordRegister(this) 43 .WithFlag(0, out enableFlag, name: "EN") 44 .WithTaggedFlag("SLAVE", 1) 45 .WithFlag(2, out autoack, name: "AUTOACK") 46 .WithTaggedFlag("AUTOSE", 3) 47 .WithTaggedFlag("AUTOSN", 4) 48 .WithTaggedFlag("ARBDIS", 5) 49 .WithTaggedFlag("GCAMEN", 6) 50 .WithTaggedFlag("TXBIL", 7) 51 .WithTag("CLHR", 8, 2) 52 .WithReservedBits(10, 2) 53 .WithTag("BITO", 12, 2) 54 .WithReservedBits(14, 1) 55 .WithTaggedFlag("GIBITO", 15) 56 .WithTag("CLTO", 16, 3); 57 GenerateCommandRegister()58 protected DoubleWordRegister GenerateCommandRegister() => new DoubleWordRegister(this) 59 .WithValueField(0, 8, FieldMode.Write, name: "COMMAND", writeCallback: (_, v) => HandleCommand((Command)v)); 60 GenerateStateRegister()61 protected DoubleWordRegister GenerateStateRegister() => new DoubleWordRegister(this) 62 .WithTaggedFlag("BUSY", 0) 63 .WithTaggedFlag("MASTER", 1) 64 .WithTaggedFlag("TRANSMITTER", 2) 65 .WithTaggedFlag("NACKED", 3) 66 .WithTaggedFlag("BUSHOLD", 4) 67 .WithTag("STATE", 5, 3) 68 .WithReservedBits(8, 24); 69 GenerateStatusRegister()70 protected DoubleWordRegister GenerateStatusRegister() => new DoubleWordRegister(this) 71 .WithTaggedFlag("PSTART", 0) 72 .WithTaggedFlag("PSTOP", 1) 73 .WithTaggedFlag("PACK", 2) 74 .WithTaggedFlag("PNACK", 3) 75 .WithTaggedFlag("PCONT", 4) 76 .WithTaggedFlag("PABORT", 5) 77 .WithTaggedFlag("TXC", 6) 78 .WithFlag(7, mode: FieldMode.Read, valueProviderCallback: (_) => !txBuffer.Any(), name: "TXBL") 79 .WithFlag(8, mode: FieldMode.Read, valueProviderCallback: (_) => rxBuffer.Any(), name: "RXDATAV") 80 .WithFlag(9, mode: FieldMode.Read, valueProviderCallback: (_) => rxBuffer.Count >= maxRxBufferBytes, name: "RXFULL"); 81 GenerateClockDivisionRegister()82 protected DoubleWordRegister GenerateClockDivisionRegister() => new DoubleWordRegister(this) 83 .WithTag("DIV", 0, 9) 84 .WithReservedBits(9, 23); 85 GenerateSlaveAddressRegister()86 protected DoubleWordRegister GenerateSlaveAddressRegister() => new DoubleWordRegister(this) 87 .WithReservedBits(0, 1) 88 .WithTag("ADDR", 1, 7) 89 .WithReservedBits(8, 24); 90 GenerateSlaveAddressMaskRegister()91 protected DoubleWordRegister GenerateSlaveAddressMaskRegister() => new DoubleWordRegister(this) 92 .WithReservedBits(0, 1) 93 .WithTag("SADDRMASK", 1, 7) 94 .WithReservedBits(8, 24); 95 GenerateReceiveBufferDataRegister()96 protected DoubleWordRegister GenerateReceiveBufferDataRegister() => new DoubleWordRegister(this) 97 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => ReadRxByte(), name: "RXDATA"); 98 GenerateReceiveBufferDoubleDataRegister()99 protected DoubleWordRegister GenerateReceiveBufferDoubleDataRegister() => new DoubleWordRegister(this) 100 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => ReadRxByte(), name: "RXDATA0") 101 .WithValueField(8, 8, FieldMode.Read, valueProviderCallback: _ => ReadRxByte(), name: "RXDATA1") 102 .WithReservedBits(16, 16); 103 GenerateReceiveBufferDataPeekRegister()104 protected DoubleWordRegister GenerateReceiveBufferDataPeekRegister() => new DoubleWordRegister(this) 105 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => PeekRxByte(0), name: "RXDATAP"); 106 GenerateReceiveBufferDoubleDataPeekRegister()107 protected DoubleWordRegister GenerateReceiveBufferDoubleDataPeekRegister() => new DoubleWordRegister(this) 108 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => PeekRxByte(0), name: "RXDATAP0") 109 .WithValueField(8, 8, FieldMode.Read, valueProviderCallback: _ => PeekRxByte(1), name: "RXDATAP1") 110 .WithReservedBits(16, 16); 111 GenerateTransmitBufferDataRegister()112 protected DoubleWordRegister GenerateTransmitBufferDataRegister() => new DoubleWordRegister(this) 113 .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, v) => LoadTxData((byte)v), name: "TXDATA"); 114 GenerateTransmitBufferDoubleDataRegister()115 protected DoubleWordRegister GenerateTransmitBufferDoubleDataRegister() => new DoubleWordRegister(this) 116 .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, v) => LoadTxData((byte)v), name: "TXDATA0") 117 .WithValueField(8, 8, FieldMode.Write, writeCallback: (_, v) => LoadTxData((byte)v), name: "TXDATA1") 118 .WithReservedBits(16, 16); 119 GenerateInterruptFlagClearRegister()120 protected DoubleWordRegister GenerateInterruptFlagClearRegister() => interruptsManager.GetInterruptClearRegister<DoubleWordRegister>(); 121 GenerateInterruptFlagSetRegister()122 protected DoubleWordRegister GenerateInterruptFlagSetRegister() => interruptsManager.GetInterruptSetRegister<DoubleWordRegister>(); 123 GenerateInterruptEnableRegister()124 protected DoubleWordRegister GenerateInterruptEnableRegister() => interruptsManager.GetInterruptEnableRegister<DoubleWordRegister>(); 125 GenerateInterruptFlagRegister()126 protected DoubleWordRegister GenerateInterruptFlagRegister() => interruptsManager.GetRegister<DoubleWordRegister>( 127 valueProviderCallback: (irq, __) => interruptsManager.IsSet(irq) && interruptsManager.IsEnabled(interrupt: irq), 128 writeCallback: (irq, prevValue, newValue) => 129 { 130 if (newValue) 131 { 132 interruptsManager.ClearInterrupt(irq); 133 } 134 } 135 ); 136 ReadRxByte()137 private byte ReadRxByte() 138 { 139 if(rxBuffer.TryDequeue(out var result)) 140 { 141 if(autoack.Value) 142 { 143 // When autoack is enabled, refill the Rx buffer to simulate an ACK being sent and 144 // a new byte being received 145 LoadRxData(1); 146 } 147 else 148 { 149 // RXDATAV is automatically cleared when reading the receive buffer 150 // Check if any bytes remain and update the flag appropriately 151 interruptsManager.SetInterrupt(Interrupt.ReceiveDataValid, rxBuffer.Any()); 152 } 153 return result; 154 } 155 156 // The buffer does not contain any data, the behavior is undefined. 157 // Raise the RXUF IRQ to signal the condition 158 interruptsManager.SetInterrupt(Interrupt.ReceiveBufferUnderflow); 159 return 0x0; 160 } 161 PeekRxByte(int index)162 private byte PeekRxByte(int index) 163 { 164 if(index < rxBuffer.Count) 165 { 166 return rxBuffer.ElementAt(index); 167 } 168 169 // The buffer does not contain any data, the behavior is undefined. 170 // RXUF IRQ is _not_ raised in this case. 171 return 0x0; 172 } 173 HandleCommand(Command command)174 private void HandleCommand(Command command) 175 { 176 foreach(var c in Enum.GetValues(typeof(Command)).Cast<Command>().Where(x => command.HasFlag(x))) 177 { 178 switch(c) 179 { 180 case Command.SendStartCondition: 181 OnStartCommand(); 182 break; 183 184 case Command.SendStopCondition: 185 OnStopCommand(); 186 break; 187 188 case Command.SendAck: 189 OnAckCommand(); 190 break; 191 192 case Command.SendNotAck: 193 this.Log(LogLevel.Noisy, "Send NACK"); 194 interruptsManager.SetInterrupt(Interrupt.MasterStopCondition); 195 break; 196 197 case Command.ClearTransmitBufferAndShiftRegister: 198 this.Log(LogLevel.Noisy, "Cleared TX buffer"); 199 txBuffer.Clear(); 200 break; 201 202 default: 203 this.Log(LogLevel.Warning, "Received unsupported command: {0}", c); 204 break; 205 } 206 } 207 } 208 OnStartCommand()209 private void OnStartCommand() 210 { 211 // Note: This does not detect repeated starts after a read request 212 // We have to specifically handle repeated starts after a write as the data 213 // is written to the device only after a stop command is sent. 214 if(isWrite) 215 { 216 this.Log(LogLevel.Noisy, "Repeated start command"); 217 interruptsManager.SetInterrupt(Interrupt.RepeatedStartCondition); 218 OnStopCommand(); 219 } 220 221 interruptsManager.SetInterrupt(Interrupt.StartCondition); 222 switch(txBuffer.Count) 223 { 224 case 0: 225 // the first byte contains device address and R/W flag; we have to wait for it 226 waitingForAddressByte = true; 227 interruptsManager.SetInterrupt(Interrupt.BusHold); 228 // TODO: here we should also set I2Cn_STATE to 0x57 according to p.442 229 break; 230 case 1: 231 // there is a byte address waiting already in the buffer 232 HandleAddressByte(); 233 break; 234 default: 235 // there is a byte address waiting already in the buffer, along with some data 236 HandleAddressByte(); 237 // Clear TXBL, as some data is already present in the buffer 238 interruptsManager.ClearInterrupt(interrupt: Interrupt.TransmitBufferLevel); 239 HandleDataByte(); 240 break; 241 } 242 } 243 OnStopCommand()244 private void OnStopCommand() 245 { 246 interruptsManager.SetInterrupt(Interrupt.MasterStopCondition); 247 if(!isWrite) 248 { 249 return; 250 } 251 252 WriteToSlave(targetPeripheral, txBuffer); 253 txBuffer.Clear(); 254 interruptsManager.SetInterrupt(Interrupt.TransmitBufferLevel); 255 interruptsManager.SetInterrupt(Interrupt.TransferCompleted); 256 isWrite = false; 257 } 258 OnAckCommand()259 private void OnAckCommand() 260 { 261 // Sending an ACK implies a read operation is in progress 262 if(isWrite) 263 { 264 return; 265 } 266 267 this.Log(LogLevel.Noisy, "ACK command"); 268 // Fetch another byte from the target 269 LoadRxData(1); 270 } 271 LoadTxData(byte value)272 private void LoadTxData(byte value) 273 { 274 // TXBL is cleared when new data is written to the transmit buffer 275 interruptsManager.ClearInterrupt(Interrupt.TransmitBufferLevel); 276 277 txBuffer.Enqueue(value); 278 if(waitingForAddressByte) 279 { 280 HandleAddressByte(); 281 } 282 else 283 { 284 HandleDataByte(); 285 } 286 } 287 HandleAddressByte()288 private void HandleAddressByte() 289 { 290 waitingForAddressByte = false; 291 292 currentAddress = txBuffer.Dequeue(); 293 isWrite = (currentAddress & 0x1) == 0; 294 currentAddress >>= 1; 295 296 // Try getting the peripheral that the transmission is targeting 297 // If a peripheral at that address is not present, raise a NACK 298 if(!TryGetByAddress(currentAddress, out targetPeripheral)) 299 { 300 interruptsManager.SetInterrupt(Interrupt.NotAcknowledgeReceived); 301 this.Log(LogLevel.Warning, "Trying to address non-existent I2C peripheral with address 0x{0:x}", currentAddress); 302 return; 303 } 304 305 // Device exists, ACK the address byte 306 interruptsManager.SetInterrupt(Interrupt.AcknowledgeReceived); 307 308 // Immediately read data from the device if the transfer is a read 309 if(!isWrite) 310 { 311 // If autoack is enabled, load two bytes instead to fill the Rx buffer 312 LoadRxData(autoack.Value ? 2 : 1); 313 } 314 } 315 HandleDataByte()316 private void HandleDataByte() 317 { 318 if(!isWrite) 319 { 320 return; 321 } 322 323 // The byte was already written to the Tx buffer; only need to update ACK state now 324 // If there is a device connected at the current address, then simulate an ACK. 325 // Otherwise, raise a NACK 326 if(targetPeripheral == null) 327 { 328 interruptsManager.SetInterrupt(Interrupt.NotAcknowledgeReceived); 329 } 330 else 331 { 332 interruptsManager.SetInterrupt(Interrupt.AcknowledgeReceived); 333 } 334 } 335 LoadRxData(int count)336 private void LoadRxData(int count) 337 { 338 var spaceLeft = Math.Max(0, maxRxBufferBytes - rxBuffer.Count); 339 if(spaceLeft == 0) 340 { 341 this.Log(LogLevel.Warning, "Requesting read but no space left in RX buffer"); 342 return; 343 } 344 345 if(count > spaceLeft) 346 { 347 this.Log(LogLevel.Warning, "Requesting read of {0} bytes, but have space only for {1} bytes", count, spaceLeft); 348 } 349 350 var bytesToRead = Math.Min(spaceLeft, count); 351 ReadFromSlave(targetPeripheral, rxBuffer, bytesToRead); 352 interruptsManager.SetInterrupt(Interrupt.ReceiveDataValid, rxBuffer.Any()); 353 } 354 ReadFromSlave(II2CPeripheral slave, Queue<byte> buffer, int count)355 private void ReadFromSlave(II2CPeripheral slave, Queue<byte> buffer, int count) 356 { 357 if(slave == null) 358 { 359 this.Log(LogLevel.Warning, "Trying to read from nonexisting slave with address \"{0}\"", currentAddress); 360 return; 361 } 362 363 var rxArray = slave.Read(count); 364 buffer.EnqueueRange(rxArray, count); 365 366 this.Log(LogLevel.Noisy, "Devices returned {0} bytes of data.", rxArray.Length); 367 } 368 WriteToSlave(II2CPeripheral slave, IEnumerable<byte> data)369 private void WriteToSlave(II2CPeripheral slave, IEnumerable<byte> data) 370 { 371 if(slave == null) 372 { 373 this.Log(LogLevel.Warning, "Trying to write to nonexisting slave with address \"{0}\"", currentAddress); 374 return; 375 } 376 377 slave.Write(data.ToArray()); 378 } 379 380 private int currentAddress; 381 private bool isWrite; 382 private bool waitingForAddressByte; 383 private IFlagRegisterField enableFlag; 384 private IFlagRegisterField autoack; 385 private II2CPeripheral targetPeripheral; 386 private readonly Queue<byte> txBuffer; 387 private readonly Queue<byte> rxBuffer; 388 private readonly InterruptManager<Interrupt> interruptsManager; 389 private const int maxRxBufferBytes = 2; 390 391 private enum Registers 392 { 393 Control = 0x00, 394 Command = 0x04, 395 State = 0x08, 396 Status = 0x0C, 397 ClockDivision = 0x10, 398 SlaveAddress = 0x14, 399 SlaveAddressMask = 0x18, 400 ReceiveBufferData = 0x1C, 401 ReceiveBufferDoubleData = 0x20, 402 ReceiveBufferDataPeek = 0x24, 403 ReceiveBufferDoubleDataPeek = 0x28, 404 TransmitBufferData = 0x2C, 405 TransmitBufferDoubleData = 0x30, 406 InterruptFlag = 0x34, 407 InterruptFlagSet = 0x38, 408 InterruptFlagClear = 0x3C, 409 InterruptEnable = 0x40, 410 IORoutingPinEnable = 0x44, 411 IORoutingLocation = 0x48 412 } 413 414 [Flags] 415 private enum Command 416 { 417 SendStartCondition = 0x01, 418 SendStopCondition = 0x02, 419 SendAck = 0x04, 420 SendNotAck = 0x08, 421 ContinueTransmission = 0x10, 422 AbortTransmission = 0x20, 423 ClearTransmitBufferAndShiftRegister = 0x40, 424 ClearPendingCommands = 0x80 425 } 426 427 private enum Interrupt 428 { 429 StartCondition = 0x00, 430 RepeatedStartCondition = 0x01, 431 Address = 0x02, 432 TransferCompleted = 0x03, 433 [NotSettable] 434 TransmitBufferLevel = 0x04, 435 [NotSettable] 436 ReceiveDataValid = 0x05, 437 AcknowledgeReceived = 0x06, 438 NotAcknowledgeReceived = 0x07, 439 MasterStopCondition = 0x08, 440 ArbitrationLost = 0x09, 441 BusError = 0x0A, 442 BusHold = 0x0B, 443 TransmitBufferOverflow = 0x0C, 444 ReceiveBufferUnderflow = 0x0D, 445 BusIdleTimeout = 0x0E, 446 ClockLowTimeout = 0x0F, 447 SlaveStopCondition = 0x10, 448 ReceiveBufferFull = 0x11, 449 ClockLowError = 0x12 450 } 451 } 452 } 453