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.Core.Structure.Registers; 13 using Antmicro.Renode.Peripherals.Bus; 14 using Antmicro.Renode.Peripherals.SPI; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Renode.Exceptions; 17 using Antmicro.Renode.Utilities; 18 19 namespace Antmicro.Renode.Peripherals.I2C 20 { 21 public class MAX32650_I2C : SimpleContainer<II2CPeripheral>, IDoubleWordPeripheral, IKnownSize 22 { MAX32650_I2C(IMachine machine)23 public MAX32650_I2C(IMachine machine) : base(machine) 24 { 25 IRQ = new GPIO(); 26 27 registers = new DoubleWordRegisterCollection(this, BuildRegisterMap()); 28 txQueue = new Queue<byte>(); 29 rxQueue = new Queue<byte>(); 30 } 31 Reset()32 public override void Reset() 33 { 34 state = States.Idle; 35 destinationAddress = 0; 36 bytesToReceive = 0; 37 enabled = false; 38 39 registers.Reset(); 40 txQueue.Clear(); 41 rxQueue.Clear(); 42 IRQ.Unset(); 43 } 44 ReadDoubleWord(long offset)45 public uint ReadDoubleWord(long offset) 46 { 47 return registers.Read(offset); 48 } 49 WriteDoubleWord(long offset, uint value)50 public void WriteDoubleWord(long offset, uint value) 51 { 52 registers.Write(offset, value); 53 } 54 55 public long Size => 0x1000; 56 57 public GPIO IRQ { get; } 58 HandleTransaction()59 private void HandleTransaction() 60 { 61 if(!enabled) 62 { 63 // We are ignoring calls to HandleTransaction when I2C peripheral is disabled 64 return; 65 } 66 67 switch(state) 68 { 69 case States.Ready: 70 // We are being called as a result of START condition 71 while(state == States.Ready || state == States.WaitForAddress) 72 { 73 if(txQueue.TryDequeue(out var data)) 74 { 75 HandleWriteByte(data); 76 } 77 else 78 { 79 state = States.Idle; 80 break; 81 } 82 } 83 break; 84 85 case States.Writing: 86 // We are being called as a result of RESTART/STOP condition 87 CurrentSlave.Write(txQueue.ToArray()); 88 txQueue.Clear(); 89 interruptDonePending.Value = true; 90 break; 91 92 default: 93 // We don't have any writes to do; ignore 94 break; 95 } 96 97 UpdateInterrupts(); 98 } 99 HandleWriteByte(byte data)100 private void HandleWriteByte(byte data) 101 { 102 if(!enabled) 103 { 104 this.Log(LogLevel.Warning, "Trying to write byte to FIFO, but peripheral is disabled"); 105 return; 106 } 107 108 switch(state) 109 { 110 case States.Idle: 111 case States.Writing: 112 txQueue.Enqueue(data); 113 break; 114 115 case States.Ready: 116 if(slaveExtendedAddress.Value) 117 { 118 destinationAddress = (uint)data & 0x3; 119 state = States.WaitForAddress; 120 } 121 else 122 { 123 state = ((data & 0x1) != 0) ? States.Reading : States.Writing; 124 destinationAddress = (uint)(data >> 1) & 0x7F; 125 126 if(CurrentSlave == null) 127 { 128 this.Log(LogLevel.Warning, "Trying to access a peripheral at an address 0x{0:X02}, but no such peripheral is connected", destinationAddress); 129 interruptTimeoutPending.Value = true; 130 state = States.Idle; 131 txQueue.Clear(); 132 } 133 } 134 135 TryReadingToBuffer(); 136 break; 137 138 case States.WaitForAddress: 139 state = ((destinationAddress & 0x1) != 0) ? States.Reading : States.Writing; 140 destinationAddress = (destinationAddress & 0x6) << 7; 141 destinationAddress |= data; 142 143 if(CurrentSlave == null) 144 { 145 this.Log(LogLevel.Warning, "Trying to access a peripheral at an address 0x{0:X03}, but no such peripheral is connected", destinationAddress); 146 interruptTimeoutPending.Value = true; 147 state = States.Idle; 148 txQueue.Clear(); 149 } 150 151 TryReadingToBuffer(); 152 break; 153 154 case States.ReadingFromBuffer: 155 this.Log(LogLevel.Warning, "Writing data to FIFO while in reading state, ignoring incoming data."); 156 break; 157 158 default: 159 throw new Exception($"Unhandled state in HandleWriteByte: {state}"); 160 } 161 162 UpdateInterrupts(); 163 } 164 HandleReadByte()165 private byte HandleReadByte() 166 { 167 if(!enabled) 168 { 169 this.Log(LogLevel.Error, "Trying to read byte from FIFO, but peripheral is disabled"); 170 return DefaultReturnValue; 171 } 172 173 byte result = DefaultReturnValue; 174 if(rxQueue.TryDequeue(out var data)) 175 { 176 result = data; 177 } 178 else 179 { 180 interruptDonePending.Value = true; 181 } 182 183 UpdateInterrupts(); 184 return result; 185 } 186 TryReadingToBuffer()187 private void TryReadingToBuffer() 188 { 189 if(state != States.Reading) 190 { 191 return; 192 } 193 194 if(rxQueue.Count > 0) 195 { 196 this.Log(LogLevel.Warning, "Receiving new data with non-empty RX queue"); 197 } 198 199 var receivedBytes = CurrentSlave.Read(bytesToReceive); 200 if(receivedBytes.Length != bytesToReceive) 201 { 202 this.Log(LogLevel.Warning, "Requested {0} bytes, but received {1}", bytesToReceive, rxQueue.Count); 203 } 204 205 foreach(var value in receivedBytes) 206 { 207 rxQueue.Enqueue(value); 208 } 209 210 state = States.ReadingFromBuffer; 211 } 212 UpdateInterrupts()213 private void UpdateInterrupts() 214 { 215 interruptRxThresholdPending.Value = rxQueue.Count >= (int)rxThreshold.Value; 216 interruptTxThresholdPending.Value = txQueue.Count <= (int)txThreshold.Value; 217 218 var pending = false; 219 pending |= interruptTimeoutEnabled.Value && interruptTimeoutPending.Value; 220 pending |= interruptRxThresholdEnabled.Value && interruptRxThresholdPending.Value; 221 pending |= interruptTxThresholdEnabled.Value && interruptTxThresholdPending.Value; 222 pending |= interruptStopEnabled.Value && interruptStopPending.Value; 223 pending |= interruptDoneEnabled.Value && interruptDonePending.Value; 224 IRQ.Set(pending); 225 } 226 BuildRegisterMap()227 private Dictionary<long, DoubleWordRegister> BuildRegisterMap() 228 { 229 var registerMap = new Dictionary<long, DoubleWordRegister>() 230 { 231 {(long)Registers.Status, new DoubleWordRegister(this) 232 .WithFlag(0, FieldMode.Read, name: "STAT.busy", 233 valueProviderCallback: _ => state != States.Idle) 234 .WithFlag(1, FieldMode.Read, name: "STAT.rxe", 235 valueProviderCallback: _ => rxQueue.Count == 0) 236 .WithFlag(2, FieldMode.Read, name: "STAT.rxf", 237 valueProviderCallback: _ => false) 238 .WithFlag(3, FieldMode.Read, name: "STAT.txe", 239 valueProviderCallback: _ => true) 240 .WithFlag(4, FieldMode.Read, name: "STAT.txf", 241 valueProviderCallback: _ => false) 242 .WithFlag(5, FieldMode.Read, name: "STAT.chmd", 243 valueProviderCallback: _ => state != States.Idle) 244 .WithReservedBits(6, 26) 245 }, 246 {(long)Registers.Control0, new DoubleWordRegister(this) 247 .WithFlag(0, name: "CTRL0.i2cen", 248 changeCallback: (_, value) => 249 { 250 state = States.Idle; 251 enabled = value; 252 }) 253 .WithFlag(1, name: "CTRL0.mst", 254 changeCallback: (_, value) => 255 { 256 if(!value) 257 { 258 this.Log(LogLevel.Warning, "CTRL0.mst has been set to false, but only Master mode is supported; ignoring"); 259 } 260 state = States.Idle; 261 }) 262 .WithTaggedFlag("CTRL0.gcen", 2) 263 .WithTaggedFlag("CTRL0.irxm", 3) 264 .WithTaggedFlag("CTRL0.ack", 4) 265 .WithReservedBits(5, 1) 266 .WithFlag(6, out var sclOutput, name: "CTRL0.sclo") 267 .WithFlag(7, out var sdaOutput, name: "CTRL0.sdao") 268 .WithFlag(8, FieldMode.Read, name: "CTRL0.scl", 269 valueProviderCallback: _ => sclOutput.Value) 270 .WithFlag(9, FieldMode.Read, name: "CTRL0.sda", 271 valueProviderCallback: _ => sdaOutput.Value) 272 .WithFlag(10, name: "CTRL0.swoe") 273 .WithTaggedFlag("CTRL0.read", 11) 274 .WithTaggedFlag("CTRL0.scl_strd", 12) 275 .WithTaggedFlag("CTRL0.scl_ppm", 13) 276 .WithReservedBits(14, 1) 277 .WithTaggedFlag("CTRL0.hsmode", 15) 278 .WithReservedBits(16, 16) 279 }, 280 {(long)Registers.InterruptFlags0, new DoubleWordRegister(this) 281 .WithFlag(0, out interruptDonePending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL0.donei") 282 .WithTaggedFlag("INT_FL0.irxmi", 1) 283 .WithTaggedFlag("INT_FL0.gci", 2) 284 .WithTaggedFlag("INT_FL0.ami", 3) 285 .WithFlag(4, out interruptRxThresholdPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL0.rxthi") 286 .WithFlag(5, out interruptTxThresholdPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL0.txthi") 287 .WithFlag(6, out interruptStopPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL0.stopi") 288 .WithTaggedFlag("INT_FL0.adracki", 7) 289 .WithTaggedFlag("INT_FL0.arberi", 8) 290 .WithFlag(9, out interruptTimeoutPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL0.toeri") 291 .WithTaggedFlag("INT_FL0.adreri", 10) 292 .WithTaggedFlag("INT_FL0.dateri", 11) 293 .WithTaggedFlag("INT_FL0.dnreri", 12) 294 .WithTaggedFlag("INT_FL0.strteri", 13) 295 .WithTaggedFlag("INT_FL0.stoperi", 14) 296 // We are using flag instead of tagged flag to hush down unnecessary logs 297 .WithFlag(15, FieldMode.WriteOneToClear, name: "INT_FL0.txloi") 298 .WithReservedBits(16, 16) 299 .WithChangeCallback((_, __) => UpdateInterrupts()) 300 }, 301 {(long)Registers.InterruptEnable0, new DoubleWordRegister(this) 302 .WithFlag(0, out interruptDoneEnabled, name: "INT_EN0.donei") 303 .WithTaggedFlag("INT_EN0.irxmi", 1) 304 .WithTaggedFlag("INT_EN0.gci", 2) 305 .WithTaggedFlag("INT_EN0.ami", 3) 306 .WithFlag(4, out interruptRxThresholdEnabled, name: "INT_EN0.rxthi") 307 .WithFlag(5, out interruptTxThresholdEnabled, name: "INT_EN0.txthi") 308 .WithFlag(6, out interruptStopEnabled, name: "INT_EN0.stopi") 309 .WithTaggedFlag("INT_EN0.adracki", 7) 310 .WithTaggedFlag("INT_EN0.arberi", 8) 311 .WithFlag(9, out interruptTimeoutEnabled, name: "INT_EN0.toeri") 312 .WithTaggedFlag("INT_EN0.adreri", 10) 313 .WithTaggedFlag("INT_EN0.dateri", 11) 314 .WithTaggedFlag("INT_EN0.dnreri", 12) 315 .WithTaggedFlag("INT_EN0.strteri", 13) 316 .WithTaggedFlag("INT_EN0.stoperi", 14) 317 .WithTaggedFlag("INT_EN0.txloi", 15) 318 .WithReservedBits(16, 16) 319 .WithChangeCallback((_, __) => UpdateInterrupts()) 320 }, 321 {(long)Registers.ReceiveControl0, new DoubleWordRegister(this) 322 .WithTaggedFlag("RX_CTRL0.dnr", 0) 323 .WithReservedBits(1, 6) 324 .WithFlag(7, FieldMode.Read | FieldMode.WriteOneToClear, name: "RX_CTRL0.rxfsh", 325 writeCallback: (_, value) => { if(value) rxQueue.Clear(); }) 326 .WithValueField(8, 4, out rxThreshold, name: "RX_CTRL0.rxth") 327 .WithReservedBits(12, 20) 328 }, 329 {(long)Registers.ReceiveControl1, new DoubleWordRegister(this) 330 .WithValueField(0, 8, name: "RX_CTRL1.rxcnt", 331 valueProviderCallback: _ => (uint)bytesToReceive, 332 writeCallback: (_, value) => bytesToReceive = (value == 0) ? 256 : (int)value) 333 .WithValueField(8, 4, FieldMode.Read, name: "RX_CTRL1.rxfifo", 334 valueProviderCallback: _ => (uint)rxQueue.Count) 335 .WithReservedBits(12, 20) 336 }, 337 {(long)Registers.TransmitControl0, new DoubleWordRegister(this) 338 .WithFlag(0, name: "TX_CTRL0.txpreld") 339 .WithReservedBits(1, 6) 340 .WithFlag(7, FieldMode.Read | FieldMode.WriteOneToClear, name: "TX_CTRL0.txfsh", 341 writeCallback: (_, value) => { if(value) txQueue.Clear(); }) 342 .WithValueField(8, 4, out txThreshold, name: "TX_CTRL0.txth") 343 .WithReservedBits(12, 20) 344 }, 345 {(long)Registers.FIFO, new DoubleWordRegister(this) 346 .WithValueField(0, 8, name: "FIFO.data", 347 valueProviderCallback: _ => HandleReadByte(), 348 writeCallback: (_, value) => HandleWriteByte((byte)value)) 349 .WithReservedBits(8, 24) 350 }, 351 {(long)Registers.MasterMode, new DoubleWordRegister(this) 352 .WithFlag(0, FieldMode.Read | FieldMode.Set, name: "MSTR_MODE.start", 353 valueProviderCallback: _ => state != States.Idle, 354 writeCallback: (_, value) => 355 { 356 if(value && state == States.Idle) 357 { 358 state = States.Ready; 359 HandleTransaction(); 360 } 361 }) 362 .WithFlag(1, FieldMode.Read | FieldMode.WriteOneToClear, name: "MSTR_MODE.restart", 363 writeCallback: (_, value) => 364 { 365 if(value) 366 { 367 HandleTransaction(); 368 state = States.Ready; 369 } 370 }, 371 valueProviderCallback: _ => false) 372 .WithFlag(2, FieldMode.Read | FieldMode.WriteOneToClear, name: "MSTR_MODE.stop", 373 writeCallback: (_, value) => 374 { 375 if(!value) 376 { 377 return; 378 } 379 380 HandleTransaction(); 381 interruptStopPending.Value = true; 382 interruptDonePending.Value = true; 383 state = States.Idle; 384 CurrentSlave?.FinishTransmission(); 385 UpdateInterrupts(); 386 }) 387 .WithReservedBits(3, 4) 388 .WithFlag(7, out slaveExtendedAddress, name: "MSTR_MODE.sea") 389 .WithReservedBits(8, 24) 390 } 391 }; 392 return registerMap; 393 } 394 395 private II2CPeripheral CurrentSlave => 396 TryGetByAddress((int)destinationAddress, out var peripheral) ? peripheral : null; 397 398 private bool enabled; 399 private States state; 400 private uint destinationAddress; 401 private int bytesToReceive; 402 403 private IFlagRegisterField slaveExtendedAddress; 404 405 private IValueRegisterField rxThreshold; 406 private IValueRegisterField txThreshold; 407 408 private IFlagRegisterField interruptTimeoutPending; 409 private IFlagRegisterField interruptRxThresholdPending; 410 private IFlagRegisterField interruptTxThresholdPending; 411 private IFlagRegisterField interruptStopPending; 412 private IFlagRegisterField interruptDonePending; 413 414 private IFlagRegisterField interruptTimeoutEnabled; 415 private IFlagRegisterField interruptRxThresholdEnabled; 416 private IFlagRegisterField interruptTxThresholdEnabled; 417 private IFlagRegisterField interruptStopEnabled; 418 private IFlagRegisterField interruptDoneEnabled; 419 420 private readonly Queue<byte> txQueue; 421 private readonly Queue<byte> rxQueue; 422 423 private readonly DoubleWordRegisterCollection registers; 424 425 private const byte DefaultReturnValue = 0x00; 426 427 private enum States 428 { 429 Idle, 430 Ready, 431 WaitForAddress, 432 Reading, 433 ReadingFromBuffer, 434 Writing, 435 } 436 437 private enum Registers 438 { 439 Control0 = 0x00, 440 Status = 0x04, 441 InterruptFlags0 = 0x08, 442 InterruptEnable0 = 0x0C, 443 InterruptFlags1 = 0x10, 444 InterruptEnable1 = 0x14, 445 FIFOLength = 0x18, 446 ReceiveControl0 = 0x1C, 447 ReceiveControl1 = 0x20, 448 TransmitControl0 = 0x24, 449 TransmitControl1 = 0x28, 450 FIFO = 0x2C, 451 MasterMode = 0x30, 452 ClockLowTime = 0x34, 453 ClockHighTime = 0x38, 454 Timeout = 0x40, 455 SlaveAddress = 0x44, 456 DMAEnable = 0x48, 457 } 458 } 459 } 460