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.Collections.Generic; 9 using System.Linq; 10 using Antmicro.Renode.Peripherals.Bus; 11 using Antmicro.Renode.Peripherals.Helpers; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Core.Structure; 14 using Antmicro.Renode.Core.Structure.Registers; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Renode.Utilities; 17 18 namespace Antmicro.Renode.Peripherals.I2C 19 { 20 [AllowedTranslations(AllowedTranslation.WordToDoubleWord)] 21 public class Cadence_I2C : SimpleContainer<II2CPeripheral>, IDoubleWordPeripheral, IKnownSize 22 { Cadence_I2C(IMachine machine)23 public Cadence_I2C(IMachine machine) : base(machine) 24 { 25 IRQ = new GPIO(); 26 rxFifo = new Queue<byte>(); 27 txFifo = new Queue<byte>(FifoCapacity); 28 txTransfer = new Queue<byte>(); 29 registers = new DoubleWordRegisterCollection(this, BuildRegisterMap()); 30 31 txFifoOverflow = new CadenceInterruptFlag(); 32 rxFifoOverflow = new CadenceInterruptFlag(); 33 rxFifoUnderflow = new CadenceInterruptFlag(); 34 targetReady = new CadenceInterruptFlag(); 35 transferNotAcknowledged = new CadenceInterruptFlag(); 36 transferNewData = new CadenceInterruptFlag(); 37 transferCompleted = new CadenceInterruptFlag(); 38 } 39 WriteDoubleWord(long offset, uint value)40 public void WriteDoubleWord(long offset, uint value) 41 { 42 registers.Write(offset, value); 43 } 44 ReadDoubleWord(long offset)45 public uint ReadDoubleWord(long offset) 46 { 47 return registers.Read(offset); 48 } 49 50 public long Size => 0x40; 51 52 public GPIO IRQ { get; } 53 Reset()54 public override void Reset() 55 { 56 registers.Reset(); 57 targetDevice = null; 58 transferState = TransferState.Idle; 59 60 ClearFifos(); 61 txTransfer.Clear(); 62 63 foreach(var flag in GetInterruptFlags()) 64 { 65 flag.Reset(); 66 } 67 UpdateInterrupts(); 68 } 69 ClearFifos()70 private void ClearFifos() 71 { 72 rxFifo.Clear(); 73 txFifo.Clear(); 74 // transferSize relates to the FIFOs counts, so it's also reseted 75 transferSize.Value = 0; 76 } 77 UpdateInterrupts()78 private void UpdateInterrupts() 79 { 80 var newState = GetInterruptFlags().Any(x => x.InterruptStatus); 81 if(IRQ.IsSet != newState) 82 { 83 this.Log(LogLevel.Debug, "Setting IRQ to {0}", newState); 84 IRQ.Set(newState); 85 } 86 } 87 EnqueueTx(byte data)88 private void EnqueueTx(byte data) 89 { 90 if(transferState != TransferState.Transmitting) 91 { 92 // Before transmission start data are collected in the txFifo 93 if(txFifo.Count == FifoCapacity) 94 { 95 this.Log(LogLevel.Warning, "Trying to write to a full Tx FIFO."); 96 txFifoOverflow.SetSticky(true); 97 return; 98 } 99 txFifo.Enqueue(data); 100 transferSize.Value = (uint)txFifo.Count; 101 } 102 else 103 { 104 // When transmission is ongoing data are collected in the txTransfer for send to a peripheral at once 105 txTransfer.Enqueue(data); 106 transferSize.Value = 0; 107 transferCompleted.SetSticky(true); 108 } 109 } 110 DequeueRx()111 private byte DequeueRx() 112 { 113 if(!rxFifo.TryDequeue(out var data)) 114 { 115 this.Log(LogLevel.Warning, "Trying to read from an empty Rx FIFO"); 116 rxFifoUnderflow.SetSticky(true); 117 return default(byte); 118 } 119 120 // RX interrupts are triggered at each FIFO reading to mimic a reception byte by byte 121 SetRxSticky(); 122 transferSize.Value = (uint)rxFifo.Count; 123 return data; 124 } 125 126 // TransferTriggered argument is set only by the transferAddress write callback which triggers transfer TryChangeState(bool transferTriggered)127 private bool TryChangeState(bool transferTriggered) 128 { 129 var oldState = transferState; 130 ChangeState(transferTriggered); 131 if(oldState != transferState) 132 { 133 OnStateChange(oldState, transferState); 134 return true; 135 } 136 return false; 137 } 138 ChangeState(bool transferTriggered)139 private void ChangeState(bool transferTriggered) 140 { 141 if(transferTriggered) 142 { 143 switch(transferDirection.Value) 144 { 145 case TransferDirection.Transmit: 146 transferState = TransferState.Transmitting; 147 break; 148 case TransferDirection.Receive: 149 transferState = TransferState.Receiving; 150 break; 151 default: 152 throw new Exception($"Unsupported TransferSize enum member {transferDirection.Value}"); 153 } 154 } 155 else if(targetDevice == null || !transferHold.Value) 156 { 157 // There is second call of the TryChangeState in the transferAddress write callback to immediately return to the idle state in case of target non-appearence 158 // Also transferHold flag clear causes return to the idle state 159 transferState = TransferState.Idle; 160 } 161 } 162 OnStateChange(TransferState oldState, TransferState state)163 private void OnStateChange(TransferState oldState, TransferState state) 164 { 165 if(oldState == TransferState.Idle) 166 { 167 if(targetDevice == null) 168 { 169 this.Log(LogLevel.Warning, "Can't find an I2C peripheral at address 0x{0:X}.", TransferAddress); 170 transferNotAcknowledged.SetSticky(true); 171 } 172 else if(targetMonitorEnabled.Value) 173 { 174 targetReady.SetSticky(true); 175 } 176 } 177 178 if(state == TransferState.Transmitting) 179 { 180 if(targetDevice != null) 181 { 182 txTransfer.EnqueueRange(txFifo.DequeueAll()); 183 transferCompleted.SetSticky(true); 184 } 185 else 186 { 187 // Even in the case of a target non-appearance one byte is read from the Tx FIFO 188 // It's assumed that hardware sends at least one byte to receive NACK 189 txFifo.TryDequeue(out var result); 190 } 191 transferSize.Value = (uint)txFifo.Count; 192 } 193 194 if(oldState == TransferState.Transmitting) 195 { 196 if(targetDevice != null) 197 { 198 // Flushing at once all data collected during transmission to the target device 199 targetDevice.Write(txTransfer.DequeueAll()); 200 } 201 } 202 203 if(state == TransferState.Receiving) 204 { 205 if(rxFifo.Count > 0) 206 { 207 this.Log(LogLevel.Warning, "Starting new read operation when Rx FIFO isn't empty. TransferSize value may be corrupted."); 208 // Receiving data without a FIFO clear isn't recommended and isn't covered in detail by the datasheet. 209 // This peripheral reads all data at once from an I2C peripheral, so we need to drop the data in that case. 210 // Keeping some data mimics the FIFO full of data from the previous transaction. 211 var readData = rxFifo.DequeueRange(FifoCapacity); 212 rxFifo.Clear(); 213 rxFifo.EnqueueRange(readData); 214 } 215 216 if(targetDevice != null) 217 { 218 // All data are read from the target device at once 219 var size = (int)transferSize.Value; 220 var data = targetDevice.Read(size); 221 if(data.Length < size) 222 { 223 this.Log(LogLevel.Warning, "The I2C peripheral returns less bytes ({0}) than expected ({1}).", data.Length, size); 224 } 225 else if(data.Length > size) 226 { 227 this.Log(LogLevel.Warning, "The I2C peripheral returns more bytes ({0}) than expected ({1}).", data.Length, size); 228 } 229 230 if(rxFifo.Count + size <= MaxTransferSize) 231 { 232 // When data from the previous transaction and transfer data fit into FIFO, we may just enqueue it. 233 rxFifo.EnqueueRange(data, size); 234 // In case of returning too little data from the I2C peripheral, some dummy data are enqueued. 235 for(var i = data.Length; i < size; i++) 236 { 237 rxFifo.Enqueue(default(byte)); 238 } 239 } 240 else 241 { 242 this.Log(LogLevel.Error, "Due to not empty Rx FIFO some data read from the I2C target can't be queued."); 243 rxFifo.EnqueueRange(data, size - rxFifo.Count); 244 } 245 } 246 247 SetRxSticky(); 248 } 249 250 if(state == TransferState.Idle) 251 { 252 if(targetDevice != null) 253 { 254 targetDevice.FinishTransmission(); 255 } 256 } 257 } 258 SetRxSticky()259 private void SetRxSticky() 260 { 261 if(rxFifo.Count == 0) 262 { 263 return; 264 } 265 266 transferNewData.SetSticky(true); 267 // The I2C interface implementation in Renode operates on byte collections - it sends all the data to the device in one go and receives one response. 268 // As a result it can receive more data that could fit in the controller's FIFO (`rxFifo.Count > FifoCapacity`). We need to handle it correctly in the model. 269 if(rxFifo.Count <= FifoCapacity) 270 { 271 transferCompleted.SetSticky(true); 272 } 273 } 274 BuildRegisterMap()275 private Dictionary<long, DoubleWordRegister> BuildRegisterMap() 276 { 277 return new Dictionary<long, DoubleWordRegister> 278 { 279 {(long)Registers.Control, new DoubleWordRegister(this) 280 .WithReservedBits(16, 16) 281 .WithTag("divisorA", 14, 2) 282 .WithTag("divisorB", 8, 6) 283 .WithReservedBits(7, 1) 284 .WithFlag(6, FieldMode.Read | FieldMode.WriteOneToClear, name: "clearFifo", 285 writeCallback: (_, val) => { if(val) ClearFifos(); } 286 ) 287 .WithFlag(5, out targetMonitorEnabled, name: "targetMonitorEnabled") 288 .WithFlag(4, out transferHold, name: "transferHold", 289 writeCallback: (_, __) => TryChangeState(transferTriggered: false)) 290 .WithTaggedFlag("transferAcknowledge", 3) 291 .WithEnumField(2, 1, out addressingMode, name: "addressingMode") 292 .WithTaggedFlag("interfaceMode", 1) 293 .WithEnumField(0, 1, out transferDirection, name: "transferDirection", 294 changeCallback: (prevVal, val) => 295 { 296 if(transferState != TransferState.Idle && transferSize.Value > 0) 297 { 298 this.Log(LogLevel.Warning, "Changing transfer direction when transfer isn't completed."); 299 } 300 } 301 ) 302 .WithWriteCallback((_, __) => UpdateInterrupts()) 303 }, 304 {(long)Registers.Status, new DoubleWordRegister(this) 305 .WithReservedBits(9, 2) 306 .WithFlag(8, FieldMode.Read, name: "busActive", 307 valueProviderCallback: (_) => transferState != TransferState.Idle 308 ) 309 .WithFlag(7, FieldMode.Read, name: "rxFifoOverflow", 310 // It's always false, there is no way to overflow the Rx FIFO 311 valueProviderCallback: (_) => false 312 ) 313 .WithFlag(6, FieldMode.Read, name: "txFifoNotEmpty", 314 valueProviderCallback: (_) => txFifo.Count > 0 315 ) 316 .WithFlag(5, FieldMode.Read, name: "rxFifoNotEmpty", 317 valueProviderCallback: (_) => rxFifo.Count > 0 318 ) 319 .WithReservedBits(4, 1) 320 .WithTaggedFlag("targetModeTransferDirection", 3) 321 .WithReservedBits(0, 3) 322 }, 323 {(long)Registers.TransferAddress, new DoubleWordRegister(this) 324 .WithReservedBits(10, 22) 325 .WithValueField(0, 10, out transferAddressReg, name: "transferAddress", 326 writeCallback: (prevVal, val) => 327 { 328 if(transferState != TransferState.Idle && prevVal != val) 329 { 330 this.Log(LogLevel.Error, "Changing the transfer address during transmission."); 331 } 332 333 TryGetByAddress(TransferAddress, out targetDevice); 334 if(TryChangeState(transferTriggered: true)) 335 { 336 //Last byte reception/transmission or lack of target may cause immediate change to the Idle state 337 TryChangeState(transferTriggered: false); 338 } 339 } 340 ) 341 .WithWriteCallback((_, __) => UpdateInterrupts()) 342 }, 343 {(long)Registers.TransferData, new DoubleWordRegister(this) 344 .WithReservedBits(8, 24) 345 .WithValueField(0, 8, name: "transferData", 346 writeCallback: (_, val) => EnqueueTx((byte)val), 347 valueProviderCallback: (_) => DequeueRx() 348 ) 349 .WithWriteCallback((_, __) => UpdateInterrupts()) 350 }, 351 {(long)Registers.InterruptStatus, new DoubleWordRegister(this) 352 .WithReservedBits(10, 22) 353 .WithTaggedFlag("arbitrationLostStatus", 9) 354 .WithReservedBits(8, 1) 355 .WithFlag(7, 356 valueProviderCallback: (_) => rxFifoUnderflow.StickyStatus, 357 writeCallback: (_, val) => rxFifoUnderflow.ClearSticky(val), 358 name: "rxFifoUnderflowStatus" 359 ) 360 .WithFlag(6, 361 valueProviderCallback: (_) => txFifoOverflow.StickyStatus, 362 writeCallback: (_, val) => txFifoOverflow.ClearSticky(val), 363 name: "txFifoOverflowStatus" 364 ) 365 .WithFlag(5, 366 valueProviderCallback: (_) => rxFifoOverflow.StickyStatus, 367 writeCallback: (_, val) => rxFifoOverflow.ClearSticky(val), 368 name: "rxFifoOverflowStatus" 369 ) 370 .WithFlag(4, 371 valueProviderCallback: (_) => targetReady.StickyStatus, 372 writeCallback: (_, val) => targetReady.ClearSticky(val), 373 name: "targetReadyStatus" 374 ) 375 .WithTaggedFlag("timeoutStatus", 3) 376 .WithFlag(2, 377 valueProviderCallback: (_) => transferNotAcknowledged.StickyStatus, 378 writeCallback: (_, val) => transferNotAcknowledged.ClearSticky(val), 379 name: "transferNotAcknowledgedStatus" 380 ) 381 .WithFlag(1, 382 valueProviderCallback: (_) => transferNewData.StickyStatus, 383 writeCallback: (_, val) => transferNewData.ClearSticky(val), 384 name: "transferNewDataStatus" 385 ) 386 .WithFlag(0, 387 valueProviderCallback: (_) => transferCompleted.StickyStatus, 388 writeCallback: (_, val) => transferCompleted.ClearSticky(val), 389 name: "transferCompletedStatus" 390 ) 391 .WithWriteCallback((_, __) => UpdateInterrupts()) 392 }, 393 {(long)Registers.TransferSize, new DoubleWordRegister(this) 394 .WithReservedBits(8, 24) 395 .WithValueField(0, 8, out transferSize, 396 changeCallback: (_, __) => 397 { 398 if(transferDirection.Value == TransferDirection.Transmit) 399 { 400 this.Log(LogLevel.Warning, "Changing transfer size while the transfer direction is set to transmit."); 401 } 402 }) 403 }, 404 {(long)Registers.InterruptMask, new DoubleWordRegister(this) 405 .WithReservedBits(10, 22) 406 .WithTaggedFlag("arbitrationLostMask", 9) 407 .WithReservedBits(8, 1) 408 .WithFlag(7, FieldMode.Read, 409 valueProviderCallback: (_) => !rxFifoUnderflow.InterruptMask, 410 name: "rxFifoUnderflowMask" 411 ) 412 .WithFlag(6, FieldMode.Read, 413 valueProviderCallback: (_) => !txFifoOverflow.InterruptMask, 414 name: "txFifoOverflowMask" 415 ) 416 .WithFlag(5, FieldMode.Read, 417 valueProviderCallback: (_) => !rxFifoOverflow.InterruptMask, 418 name: "rxFifoOverflowMask" 419 ) 420 .WithFlag(4, FieldMode.Read, 421 valueProviderCallback: (_) => !targetReady.InterruptMask, 422 name: "targetReadyMask" 423 ) 424 .WithTaggedFlag("timeoutMask", 3) 425 .WithFlag(2, FieldMode.Read, 426 valueProviderCallback: (_) => !transferNotAcknowledged.InterruptMask, 427 name: "transferNotAcknowledgedMask" 428 ) 429 .WithFlag(1, FieldMode.Read, 430 valueProviderCallback: (_) => !transferNewData.InterruptMask, 431 name: "transferNewDataMask" 432 ) 433 .WithFlag(0, FieldMode.Read, 434 valueProviderCallback: (_) => !transferCompleted.InterruptMask, 435 name: "transferCompletedMask" 436 ) 437 }, 438 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 439 .WithReservedBits(10, 22) 440 .WithTaggedFlag("arbitrationLostInterruptEnable", 9) 441 .WithReservedBits(8, 1) 442 .WithFlag(7, FieldMode.Write, 443 writeCallback: (_, val) => rxFifoUnderflow.InterruptEnable(val), 444 name: "rxFifoUnderflowInterruptEnable" 445 ) 446 .WithFlag(6, FieldMode.Write, 447 writeCallback: (_, val) => txFifoOverflow.InterruptEnable(val), 448 name: "txFifoOverflowInterruptEnable" 449 ) 450 .WithFlag(5, FieldMode.Write, 451 writeCallback: (_, val) => rxFifoOverflow.InterruptEnable(val), 452 name: "rxFifoOverflowInterruptEnable" 453 ) 454 .WithFlag(4, FieldMode.Write, 455 writeCallback: (_, val) => targetReady.InterruptEnable(val), 456 name: "targetReadyInterruptEnable" 457 ) 458 .WithTaggedFlag("timeoutInterruptEnable", 3) 459 .WithFlag(2, FieldMode.Write, 460 writeCallback: (_, val) => transferNotAcknowledged.InterruptEnable(val), 461 name: "transferNotAcknowledgedInterruptEnable" 462 ) 463 .WithFlag(1, FieldMode.Write, 464 writeCallback: (_, val) => transferNewData.InterruptEnable(val), 465 name: "transferNewDataInterruptEnable" 466 ) 467 .WithFlag(0, FieldMode.Write, 468 writeCallback: (_, val) => transferCompleted.InterruptEnable(val), 469 name: "transferCompletedInterruptEnable" 470 ) 471 .WithWriteCallback((_, __) => UpdateInterrupts()) 472 }, 473 {(long)Registers.InterruptDisable, new DoubleWordRegister(this) 474 .WithReservedBits(10, 22) 475 .WithTaggedFlag("arbitrationLostInterruptDisable", 9) 476 .WithReservedBits(8, 1) 477 .WithFlag(7, FieldMode.Write, 478 writeCallback: (_, val) => rxFifoUnderflow.InterruptDisable(val), 479 name: "rxFifoUnderflowInterruptDisable" 480 ) 481 .WithFlag(6, FieldMode.Write, 482 writeCallback: (_, val) => txFifoOverflow.InterruptDisable(val), 483 name: "txFifoOverflowInterruptDisable" 484 ) 485 .WithFlag(5, FieldMode.Write, 486 writeCallback: (_, val) => rxFifoOverflow.InterruptDisable(val), 487 name: "rxFifoOverflowInterruptDisable" 488 ) 489 .WithFlag(4, FieldMode.Write, 490 writeCallback: (_, val) => targetReady.InterruptDisable(val), 491 name: "targetReadyInterruptDisable" 492 ) 493 .WithTaggedFlag("timeoutInterruptDisable", 3) 494 .WithFlag(2, FieldMode.Write, 495 writeCallback: (_, val) => transferNotAcknowledged.InterruptDisable(val), 496 name: "transferNotAcknowledgedInterruptDisable" 497 ) 498 .WithFlag(1, FieldMode.Write, 499 writeCallback: (_, val) => transferNewData.InterruptDisable(val), 500 name: "transferNewDataInterruptDisable" 501 ) 502 .WithFlag(0, FieldMode.Write, 503 writeCallback: (_, val) => transferCompleted.InterruptDisable(val), 504 name: "transferCompletedInterruptDisable" 505 ) 506 .WithWriteCallback((_, __) => UpdateInterrupts()) 507 }, 508 }; 509 } 510 GetInterruptFlags()511 private IEnumerable<CadenceInterruptFlag> GetInterruptFlags() 512 { 513 yield return txFifoOverflow; 514 yield return rxFifoOverflow; 515 yield return rxFifoUnderflow; 516 yield return targetReady; 517 yield return transferNotAcknowledged; 518 yield return transferNewData; 519 yield return transferCompleted; 520 } 521 522 private int TransferAddress 523 { 524 get 525 { 526 if(addressingMode.Value == AddressingMode.Extended) 527 { 528 return (int)transferAddressReg.Value & ExtendedAddressMask; 529 } 530 return (int)transferAddressReg.Value & NormalAddressMask; 531 } 532 } 533 534 private II2CPeripheral targetDevice; 535 private TransferState transferState; 536 537 private IFlagRegisterField targetMonitorEnabled; 538 private IFlagRegisterField transferHold; 539 private IEnumRegisterField<AddressingMode> addressingMode; 540 private IEnumRegisterField<TransferDirection> transferDirection; 541 private IValueRegisterField transferAddressReg; 542 private IValueRegisterField transferSize; 543 544 private readonly CadenceInterruptFlag txFifoOverflow; 545 private readonly CadenceInterruptFlag rxFifoOverflow; 546 private readonly CadenceInterruptFlag rxFifoUnderflow; 547 private readonly CadenceInterruptFlag targetReady; 548 private readonly CadenceInterruptFlag transferNotAcknowledged; 549 private readonly CadenceInterruptFlag transferNewData; 550 private readonly CadenceInterruptFlag transferCompleted; 551 552 private readonly Queue<byte> txFifo; 553 private readonly Queue<byte> txTransfer; 554 private readonly Queue<byte> rxFifo; 555 private readonly DoubleWordRegisterCollection registers; 556 557 private const int FifoCapacity = 16; 558 private const int MaxTransferSize = 255; 559 private const int ExtendedAddressMask = 0x3FF; 560 private const int NormalAddressMask = 0x7F; 561 562 private enum TransferState 563 { 564 Idle, 565 Transmitting, 566 Receiving 567 } 568 569 private enum AddressingMode 570 { 571 Extended = 0x0, 572 Normal = 0x1 573 } 574 575 private enum TransferDirection 576 { 577 Transmit = 0x0, 578 Receive = 0x1 579 } 580 581 private enum Registers : long 582 { 583 Control = 0x00, 584 Status = 0x04, 585 TransferAddress = 0x08, 586 TransferData = 0x0c, 587 InterruptStatus = 0x10, 588 TransferSize = 0x14, 589 TargetMonitor = 0x18, 590 Timeout = 0x1c, 591 InterruptMask = 0x20, 592 InterruptEnable = 0x24, 593 InterruptDisable = 0x28, 594 GlitchFilter = 0x2c 595 } 596 } 597 } 598