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.Core; 9 using Antmicro.Renode.Core.Structure; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Peripherals.Bus; 13 using Antmicro.Renode.Utilities; 14 15 namespace Antmicro.Renode.Peripherals.I2C 16 { 17 public sealed class STM32F7_I2C : SimpleContainer<II2CPeripheral>, II2CPeripheral, IDoubleWordPeripheral, IKnownSize 18 { STM32F7_I2C(IMachine machine)19 public STM32F7_I2C(IMachine machine) : base(machine) 20 { 21 EventInterrupt = new GPIO(); 22 ErrorInterrupt = new GPIO(); 23 registers = CreateRegisters(); 24 Reset(); 25 } 26 ReadDoubleWord(long offset)27 public uint ReadDoubleWord(long offset) 28 { 29 return registers.Read(offset); 30 } 31 WriteDoubleWord(long offset, uint value)32 public void WriteDoubleWord(long offset, uint value) 33 { 34 registers.Write(offset, value); 35 } 36 Reset()37 public override void Reset() 38 { 39 registers.Reset(); 40 txData = new Queue<byte>(); 41 rxData = new Queue<byte>(); 42 currentSlaveAddress = 0; 43 transferOutgoing = false; 44 EventInterrupt.Unset(); 45 ErrorInterrupt.Unset(); 46 masterMode = false; 47 } 48 Write(byte[] data)49 public void Write(byte[] data) 50 { 51 // RM0444 Rev 5, p.991/1390 52 // "0: Write transfer, slave enters receiver mode." 53 transferOutgoing = false; 54 55 rxData.EnqueueRange(data); 56 } 57 Read(int count = 1)58 public byte[] Read(int count = 1) 59 { 60 if(!addressMatched.Value) 61 { 62 // Note 1: 63 // RM0444 Rev 5, p.991/1390 64 // "1: Read transfer, slave enters transmitter mode." 65 // Note 2: 66 // this is a workaround for the protocol not supporting start/stop bits 67 transferOutgoing = (count > 0); 68 bytesToTransfer.Value = (uint)count; 69 addressMatched.Value = true; 70 Update(); 71 } 72 73 if(txData.Count >= (int)bytesToTransfer.Value) 74 { 75 // STOP condition 76 stopDetection.Value = true; 77 transmitInterruptStatus = false; 78 addressMatched.Value = false; 79 Update(); 80 } 81 else 82 { 83 // TODO: return partial results 84 return new byte[0]; 85 } 86 87 var result = new byte[count]; 88 for(var i = 0; i < count; i++) 89 { 90 if(!txData.TryDequeue(out result[i])) 91 { 92 return new byte[0]; 93 } 94 } 95 return result; 96 } 97 FinishTransmission()98 public void FinishTransmission() 99 { 100 } 101 102 public long Size { get { return 0x400; } } 103 104 public GPIO EventInterrupt { get; private set; } 105 106 public GPIO ErrorInterrupt { get; private set; } 107 108 public bool RxNotEmpty => rxData.Count > 0; 109 110 public bool OwnAddress1Enabled => ownAddress1Enabled.Value; 111 CreateRegisters()112 private DoubleWordRegisterCollection CreateRegisters() 113 { 114 var map = new Dictionary<long, DoubleWordRegister> { { 115 (long)Registers.Control1, new DoubleWordRegister(this) 116 .WithFlag(0, writeCallback: PeripheralEnabledWrite, name: "PE") 117 .WithFlag(1, out transferInterruptEnabled, name: "TXIE") 118 .WithFlag(2, out receiveInterruptEnabled, name: "RXIE") 119 .WithFlag(3, out addressMatchedInterruptEnabled, name: "ADDRIE") 120 .WithFlag(4, out nackReceivedInterruptEnabled, name: "NACKIE") 121 .WithFlag(5, out stopDetectionInterruptEnabled, name: "STOPIE") 122 .WithFlag(6, out transferCompleteInterruptEnabled, name: "TCIE") 123 .WithTag("ERRIE", 7, 1) 124 .WithTag("DNF", 8, 4) 125 .WithTag("ANFOFF", 12, 1) 126 .WithReservedBits(13, 1) 127 .WithTag("TXDMAEN", 14, 1) 128 .WithTag("RXDMAEN", 15, 1) 129 .WithTag("SBC", 16, 1) 130 .WithFlag(17, out noStretch, name: "NOSTRETCH") 131 .WithTag("WUPEN", 18, 1) 132 .WithTag("GCEN", 19, 1) 133 .WithTag("SMBHEN", 20, 1) 134 .WithTag("SMBDEN", 21, 1) 135 .WithTag("ALERTEN", 22, 1) 136 .WithTag("PECEN", 23, 1) 137 .WithReservedBits(24, 8) 138 .WithChangeCallback((_,__) => Update()) 139 }, { 140 (long)Registers.Control2, 141 new DoubleWordRegister(this) 142 .WithValueField(0, 10, out slaveAddress, name: "SADD") //Changing this from a normal field to a callback requires a change in StartWrite 143 .WithFlag(10, out isReadTransfer, name: "RD_WRN") 144 .WithFlag(11, out use10BitAddressing, name: "ADD10") 145 .WithTag("HEAD10R", 12, 1) 146 .WithFlag(13, out start, name: "START") 147 .WithFlag(14, out stop, name: "STOP") 148 .WithTag("NACK", 15, 1) 149 .WithValueField(16, 8, out bytesToTransfer, name: "NBYTES") 150 .WithFlag(24, out reload, name: "RELOAD") 151 .WithFlag(25, out autoEnd, name: "AUTOEND") 152 .WithTag("PECBYTE", 26, 1) 153 .WithReservedBits(27, 5) 154 .WithWriteCallback((oldVal, newVal) => 155 { 156 uint oldStart = (oldVal >> 13) & 1; 157 uint oldBytesToTransfer = (oldVal >> 16) & 0xFF; 158 159 if(start.Value && stop.Value) 160 { 161 this.Log(LogLevel.Warning, "Setting START and STOP at the same time, ignoring the transfer"); 162 } 163 else if(start.Value) 164 { 165 StartTransfer(); 166 } 167 else if(stop.Value) 168 { 169 StopTransfer(); 170 } 171 172 if(!start.Value) 173 { 174 if(bytesToTransfer.Value > 0 && masterMode && transferCompleteReload.Value && currentSlave != null) 175 { 176 ExtendTransfer(); 177 } 178 } 179 180 if(oldStart == 1) 181 { 182 if(oldBytesToTransfer != bytesToTransfer.Value) 183 { 184 this.Log(LogLevel.Error, "Changing NBYTES when START is set is not permitted"); 185 } 186 } 187 188 start.Value = false; 189 stop.Value = false; 190 }) 191 .WithChangeCallback((_,__) => Update()) 192 }, { 193 (long)Registers.OwnAddress1, new DoubleWordRegister(this) 194 .WithValueField(0, 10, out ownAddress1, name: "OA1") 195 .WithFlag(10, out ownAddress1Mode, name: "OA1MODE") 196 .WithReservedBits(11, 4) 197 .WithFlag(15, out ownAddress1Enabled, name: "OA1EN") 198 .WithReservedBits(16, 16) 199 .WithWriteCallback((_, val) => 200 this.Log(LogLevel.Info, "Slave address 1: 0x{0:X}, mode: {1}, status: {2}", ownAddress1.Value, ownAddress1Mode.Value ? "10-bit" : "7-bit", ownAddress1Enabled.Value ? "enabled" : "disabled") 201 ) 202 }, { 203 (long)Registers.OwnAddress2, new DoubleWordRegister(this) 204 .WithReservedBits(0, 1) 205 .WithValueField(1, 7, out ownAddress2, name: "OA2") 206 .WithValueField(8, 3, out ownAddress2Mask, name: "OA2MSK") 207 .WithReservedBits(11, 4) 208 .WithFlag(15, out ownAddress2Enabled, name: "OA2EN") 209 .WithReservedBits(16, 16) 210 .WithWriteCallback((_, val) => 211 this.Log(LogLevel.Info, "Slave address 2: 0x{0:X}, mask: 0x{1:X}, status: {2}", ownAddress2.Value, ownAddress2Mask.Value, ownAddress2Enabled.Value ? "enabled" : "disabled") 212 ) 213 }, { 214 (long)Registers.Timing, new DoubleWordRegister(this) 215 .WithTag("SCLL", 0, 8) 216 .WithTag("SCLH", 8, 8) 217 .WithTag("SDADEL", 16, 4) 218 .WithTag("SCLDEL", 20, 4) 219 .WithReservedBits(24, 4) 220 .WithTag("PRESC", 28, 4) 221 }, { 222 (long)Registers.InterruptAndStatus, new DoubleWordRegister(this, 1) 223 .WithFlag(0, 224 valueProviderCallback: _ => txData.Count == 0, 225 writeCallback: (_, value)=> 226 { 227 if(value) 228 { 229 txData.Clear(); 230 } 231 }, name: "TXE") 232 .WithFlag(1, 233 valueProviderCallback: _ => transmitInterruptStatus, 234 writeCallback: (_, val) => 235 { 236 if(!noStretch.Value) 237 { 238 return; 239 } 240 transmitInterruptStatus = val && transferInterruptEnabled.Value; 241 } , name: "TXIS") 242 .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => RxNotEmpty, name: "RXNE") 243 .WithFlag(3, out addressMatched, FieldMode.Read, name: "ADDR") 244 .WithTag("NACKF", 4, 1) 245 .WithFlag(5, out stopDetection, FieldMode.Read, name: "STOPF") 246 .WithFlag(6, out transferComplete, FieldMode.Read, name: "TC") 247 .WithFlag(7, out transferCompleteReload, FieldMode.Read, name: "TCR") 248 .WithTag("BERR", 8, 1) 249 .WithTag("ARLO", 9, 1) 250 .WithTag("OVR", 10, 1) 251 .WithTag("PECERR", 11, 1) 252 .WithTag("TIMEOUT", 12, 1) 253 .WithTag("ALERT", 13, 1) 254 .WithReservedBits(14, 1) 255 .WithTag("BUSY", 15, 1) 256 .WithFlag(16, FieldMode.Read, valueProviderCallback: _ => transferOutgoing, name: "DIR") 257 .WithTag("ADDCODE", 17, 7) 258 .WithReservedBits(24, 8) 259 .WithChangeCallback((_,__) => Update()) 260 }, { 261 (long)Registers.InterruptClear, new DoubleWordRegister(this, 0) 262 .WithReservedBits(0, 3) 263 .WithFlag(3, FieldMode.WriteOneToClear, 264 writeCallback: (_, value) => 265 { 266 if(value) 267 { 268 transmitInterruptStatus = transferOutgoing & (txData.Count == 0); 269 addressMatched.Value = false; 270 } 271 }, name: "ADDRCF") 272 .WithTag("NACKCF", 4, 1) 273 .WithFlag(5, FieldMode.WriteOneToClear, 274 writeCallback: (_, value) => 275 { 276 if(value) 277 { 278 stopDetection.Value = false; 279 } 280 }, name: "STOPCF") 281 .WithReservedBits(6, 2) 282 .WithTag("BERRCF", 8, 1) 283 .WithTag("ARLOCF", 9, 1) 284 .WithTag("OVRCF", 10, 1) 285 .WithTag("PECCF", 11, 1) 286 .WithTag("TIMOUTCF", 12, 1) 287 .WithTag("ALERTCF", 13, 1) 288 .WithReservedBits(14, 18) 289 .WithChangeCallback((_,__) => Update()) 290 }, { 291 (long)Registers.ReceiveData, new DoubleWordRegister(this, 0) 292 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: preVal => ReceiveDataRead((uint)preVal), name: "RXDATA") 293 .WithReservedBits(9, 23) 294 }, { 295 (long)Registers.TransmitData, new DoubleWordRegister(this, 0) 296 .WithValueField(0, 8, writeCallback: (prevVal, val) => HandleTransmitDataWrite((uint)prevVal, (uint)val), name: "TXDATA") 297 .WithReservedBits(9, 23) 298 } 299 }; 300 301 return new DoubleWordRegisterCollection(this, map); 302 } 303 PeripheralEnabledWrite(bool oldValue, bool newValue)304 private void PeripheralEnabledWrite(bool oldValue, bool newValue) 305 { 306 if(newValue) 307 { 308 return; 309 } 310 stopDetection.Value = false; 311 transferComplete.Value = false; 312 transferCompleteReload.Value = false; 313 transmitInterruptStatus = false; 314 } 315 ExtendTransfer()316 private void ExtendTransfer() 317 { 318 //in case of reads we can fetch data from peripheral immediately, but in case of writes we have to wait until something is written to TXDATA 319 if(isReadTransfer.Value) 320 { 321 var data = currentSlave.Read((int)bytesToTransfer.Value); 322 foreach(var item in data) 323 { 324 rxData.Enqueue(item); 325 } 326 } 327 transferCompleteReload.Value = false; 328 Update(); 329 } 330 StartTransfer()331 private void StartTransfer() 332 { 333 masterMode = true; 334 transferComplete.Value = false; 335 336 currentSlave = null; 337 338 rxData.Clear(); 339 //This is kinda volatile. If we change slaveAddress setting to a callback action, it might not be set at this moment. 340 currentSlaveAddress = (int)(use10BitAddressing.Value ? slaveAddress.Value : ((slaveAddress.Value >> 1) & 0x7F)); 341 if(!TryGetByAddress(currentSlaveAddress, out currentSlave)) 342 { 343 this.Log(LogLevel.Warning, "Unknown slave at address {0}.", currentSlaveAddress); 344 return; 345 } 346 347 if(isReadTransfer.Value) 348 { 349 transmitInterruptStatus = false; 350 var data = currentSlave.Read((int)bytesToTransfer.Value); 351 foreach(var item in data) 352 { 353 rxData.Enqueue(item); 354 } 355 } 356 else 357 { 358 transmitInterruptStatus = true; 359 } 360 Update(); 361 } 362 StopTransfer()363 private void StopTransfer() 364 { 365 masterMode = false; 366 stopDetection.Value = true; 367 currentSlave?.FinishTransmission(); 368 Update(); 369 } 370 ReceiveDataRead(uint oldValue)371 private uint ReceiveDataRead(uint oldValue) 372 { 373 if(rxData.Count > 0) 374 { 375 var value = rxData.Dequeue(); 376 if(rxData.Count == 0) 377 { 378 SetTransferCompleteFlags(); //TC/TCR is set when NBYTES data have been transfered 379 } 380 return value; 381 } 382 this.Log(LogLevel.Warning, "Receive buffer underflow!"); 383 return 0; 384 } 385 HandleTransmitDataWrite(uint oldValue, uint newValue)386 private void HandleTransmitDataWrite(uint oldValue, uint newValue) 387 { 388 if(masterMode) 389 { 390 MasterTransmitDataWrite(oldValue, newValue); 391 } 392 else 393 { 394 SlaveTransmitDataWrite(oldValue, newValue); 395 } 396 } 397 MasterTransmitDataWrite(uint oldValue, uint newValue)398 private void MasterTransmitDataWrite(uint oldValue, uint newValue) 399 { 400 if(currentSlave == null) 401 { 402 this.Log(LogLevel.Warning, "Trying to send byte {0} to an unknown slave with address {1}.", newValue, currentSlaveAddress); 403 return; 404 } 405 txData.Enqueue((byte)newValue); 406 if(txData.Count == (int)bytesToTransfer.Value) 407 { 408 currentSlave.Write(txData.ToArray()); 409 txData.Clear(); 410 SetTransferCompleteFlags(); 411 } 412 } 413 SlaveTransmitDataWrite(uint oldValue, uint newValue)414 private void SlaveTransmitDataWrite(uint oldValue, uint newValue) 415 { 416 txData.Enqueue((byte)newValue); 417 } 418 SetTransferCompleteFlags()419 private void SetTransferCompleteFlags() 420 { 421 if(!autoEnd.Value && !reload.Value) 422 { 423 transferComplete.Value = true; 424 } 425 if(autoEnd.Value) 426 { 427 currentSlave.FinishTransmission(); 428 stopDetection.Value = true; 429 masterMode = false; 430 } 431 if(reload.Value) 432 { 433 transferCompleteReload.Value = true; 434 } 435 else 436 { 437 transmitInterruptStatus = false; //this is a guess based on a driver 438 } 439 Update(); 440 } 441 Update()442 private void Update() 443 { 444 var value = (transferCompleteInterruptEnabled.Value && (transferCompleteReload.Value || transferComplete.Value)) 445 || (transferInterruptEnabled.Value && transmitInterruptStatus) 446 || (receiveInterruptEnabled.Value && isReadTransfer.Value && rxData.Count > 0) //RXNE is calculated dynamically 447 || (stopDetectionInterruptEnabled.Value && stopDetection.Value) 448 || (nackReceivedInterruptEnabled.Value && false) //TODO: implement NACKF 449 || (addressMatchedInterruptEnabled.Value && addressMatched.Value); 450 EventInterrupt.Set(value); 451 } 452 453 private IValueRegisterField bytesToTransfer; 454 private IValueRegisterField slaveAddress; 455 private IValueRegisterField ownAddress1; 456 private IValueRegisterField ownAddress2; 457 private IValueRegisterField ownAddress2Mask; 458 private IFlagRegisterField transferInterruptEnabled; 459 private IFlagRegisterField receiveInterruptEnabled; 460 private IFlagRegisterField addressMatchedInterruptEnabled; 461 private IFlagRegisterField nackReceivedInterruptEnabled; 462 private IFlagRegisterField stopDetectionInterruptEnabled; 463 private IFlagRegisterField transferCompleteInterruptEnabled; 464 private IFlagRegisterField isReadTransfer; 465 private IFlagRegisterField use10BitAddressing; 466 private IFlagRegisterField reload; 467 private IFlagRegisterField autoEnd; 468 private IFlagRegisterField noStretch; 469 private IFlagRegisterField ownAddress1Mode; 470 private IFlagRegisterField ownAddress1Enabled; 471 private IFlagRegisterField ownAddress2Enabled; 472 private IFlagRegisterField transferComplete; 473 private IFlagRegisterField transferCompleteReload; 474 private IFlagRegisterField stopDetection; 475 private IFlagRegisterField addressMatched; 476 private IFlagRegisterField start; 477 private IFlagRegisterField stop; 478 479 private DoubleWordRegisterCollection registers; 480 481 private II2CPeripheral currentSlave; 482 private Queue<byte> rxData; 483 private Queue<byte> txData; 484 private int currentSlaveAddress; 485 private bool transferOutgoing; 486 private bool transmitInterruptStatus; 487 private bool masterMode; 488 489 private enum Registers 490 { 491 Control1 = 0x00, 492 Control2 = 0x04, 493 OwnAddress1 = 0x08, 494 OwnAddress2 = 0x0C, 495 Timing = 0x10, 496 Timeout = 0x14, 497 InterruptAndStatus = 0x18, 498 InterruptClear = 0x1C, 499 PacketErrorChecking = 0x20, 500 ReceiveData = 0x24, 501 TransmitData = 0x28 502 } 503 } 504 } 505