1 // 2 // Copyright (c) 2010-2025 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.Linq; 8 using System.Collections.Generic; 9 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Extensions; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Core.Structure; 14 using Antmicro.Renode.Logging; 15 using Antmicro.Renode.Peripherals.Bus; 16 17 namespace Antmicro.Renode.Peripherals.I2C 18 { 19 public class SAMD21_I2C : SimpleContainer<II2CPeripheral>, 20 IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, 21 IWordPeripheral, IProvidesRegisterCollection<WordRegisterCollection>, 22 IBytePeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, 23 IKnownSize 24 { SAMD21_I2C(IMachine machine)25 public SAMD21_I2C(IMachine machine) : base(machine) 26 { 27 // NOTE: This class is manually managing access translation for respective 28 // register collections. As such, this comes with some caveats; please refer 29 // to the implementation of Read/Write methods for more information. 30 doubleWordRegistersCollection = new DoubleWordRegisterCollection(this); 31 wordRegistersCollection = new WordRegisterCollection(this); 32 byteRegistersCollection = new ByteRegisterCollection(this); 33 34 IRQ = new GPIO(); 35 36 DefineRegisters(); 37 Reset(); 38 } 39 ReadDoubleWord(long offset)40 public uint ReadDoubleWord(long offset) 41 { 42 return doubleWordRegistersCollection.Read(offset); 43 } 44 WriteDoubleWord(long offset, uint value)45 public void WriteDoubleWord(long offset, uint value) 46 { 47 doubleWordRegistersCollection.Write(offset, value); 48 } 49 ReadWord(long offset)50 public ushort ReadWord(long offset) 51 { 52 if(!wordRegistersCollection.HasRegisterAtOffset(offset)) 53 { 54 // NOTE: Fallback to DoubleWord registers if we don't have 55 // an explicit Word register for given offset 56 return this.ReadWordUsingDoubleWord(offset); 57 } 58 return wordRegistersCollection.Read(offset); 59 } 60 WriteWord(long offset, ushort value)61 public void WriteWord(long offset, ushort value) 62 { 63 if(!wordRegistersCollection.HasRegisterAtOffset(offset)) 64 { 65 // NOTE: Fallback to DoubleWord registers if we don't have 66 // an explicit Word register for given offset 67 // WARN: THIS SHOULD BE USED CAREFULLY IF THE valueProviderCallback 68 // FOR GIVEN REGISTER HAS SIDE-EFFECTS! 69 this.WriteWordUsingDoubleWord(offset, value); 70 return; 71 } 72 wordRegistersCollection.Write(offset, value); 73 } 74 ReadByte(long offset)75 public byte ReadByte(long offset) 76 { 77 if(!byteRegistersCollection.HasRegisterAtOffset(offset)) 78 { 79 // NOTE: Fallback to Word registers if we don't have 80 // an explicit Byte register for given offset 81 return this.ReadByteUsingWord(offset); 82 } 83 return byteRegistersCollection.Read(offset); 84 } 85 WriteByte(long offset, byte value)86 public void WriteByte(long offset, byte value) 87 { 88 if(!byteRegistersCollection.HasRegisterAtOffset(offset)) 89 { 90 // NOTE: Fallback to Word registers if we don't have 91 // an explicit Byte register for given offset 92 // WARN: THIS SHOULD BE USED CAREFULLY IF THE valueProviderCallback 93 // FOR GIVEN REGISTER HAS SIDE-EFFECTS! 94 this.WriteByteUsingWord(offset, value); 95 return; 96 } 97 byteRegistersCollection.Write(offset, value); 98 } 99 Reset()100 public override void Reset() 101 { 102 doubleWordRegistersCollection.Reset(); 103 wordRegistersCollection.Reset(); 104 byteRegistersCollection.Reset(); 105 } 106 107 public GPIO IRQ { get; } 108 109 DoubleWordRegisterCollection IProvidesRegisterCollection<DoubleWordRegisterCollection>.RegistersCollection => doubleWordRegistersCollection; 110 WordRegisterCollection IProvidesRegisterCollection<WordRegisterCollection>.RegistersCollection => wordRegistersCollection; 111 ByteRegisterCollection IProvidesRegisterCollection<ByteRegisterCollection>.RegistersCollection => byteRegistersCollection; 112 113 public long Size => 0x100; 114 TryFlush()115 private void TryFlush() 116 { 117 if(activePeripheral == null || txQueue.Count == 0) 118 { 119 return; 120 } 121 122 activePeripheral.Write(txQueue.ToArray()); 123 txQueue.Clear(); 124 } 125 StartTransaction(int address, bool reading)126 private void StartTransaction(int address, bool reading) 127 { 128 if(!TryGetByAddress(address, out var peripheral)) 129 { 130 // NOTE: Peripheral with given address is not connected 131 hostOnBusInterruptPending.Value = true; 132 acknowledgeMissing.Value = true; 133 UpdateInterrupts(); 134 return; 135 } 136 137 activePeripheral = peripheral; 138 state = reading ? State.Reading : State.Writing; 139 RestartTransaction(); 140 } 141 RestartTransaction()142 private void RestartTransaction() 143 { 144 if(state != State.Reading && state != State.Writing) 145 { 146 this.Log(LogLevel.Warning, "Tried to RESTART without an ongoing transaction"); 147 return; 148 } 149 150 151 txQueue.Clear(); 152 hostOnBusInterruptPending.Value |= state != State.Reading; 153 clientOnBusInterruptPending.Value |= state == State.Reading; 154 acknowledgeMissing.Value = false; 155 UpdateInterrupts(); 156 } 157 DefineRegisters()158 private void DefineRegisters() 159 { 160 var thisProvidesDoubleWordRegisterCollection = this as IProvidesRegisterCollection<DoubleWordRegisterCollection>; 161 var thisProvidesWordRegisterCollection = this as IProvidesRegisterCollection<WordRegisterCollection>; 162 var thisProvidesByteRegisterCollection = this as IProvidesRegisterCollection<ByteRegisterCollection>; 163 164 Registers.ControlA.Define(thisProvidesDoubleWordRegisterCollection) 165 .WithFlag(0, FieldMode.Write, name: "SWRST", 166 writeCallback: (_, value) => { if (value) { Reset(); } }) 167 .WithFlag(1, out enabled, name: "ENABLE") 168 .WithEnumField(2, 3, out mode, name: "MODE", 169 changeCallback: (previousValue, value) => 170 { 171 switch(value) 172 { 173 case Mode.I2CHost: 174 break; 175 176 default: 177 this.Log(LogLevel.Warning, "{0} mode is currently not supported, reverting to {1}", value, previousValue); 178 mode.Value = previousValue; 179 break; 180 } 181 182 }) 183 .WithReservedBits(5, 2) 184 .WithTaggedFlag("RUNSTDBY", 7) 185 .WithReservedBits(8, 8) 186 .WithTaggedFlag("PINOUT", 16) 187 .WithReservedBits(17, 3) 188 .WithTag("SDAHOLD", 20, 2) 189 .WithTaggedFlag("MEXTTOEN", 22) 190 .WithTaggedFlag("SEXTTOEN", 23) 191 .WithEnumField(24, 2, out speedMode, name: "SPEED", 192 changeCallback: (previousValue, value) => 193 { 194 switch(value) 195 { 196 case SpeedMode.Standard: 197 case SpeedMode.Fast: 198 case SpeedMode.HighSpeed: 199 break; 200 default: 201 this.WarningLog("Attempted write with reserved value to SPEED (0x{0:X}), ignoring", value); 202 speedMode.Value = previousValue; 203 break; 204 } 205 } 206 ) 207 .WithReservedBits(26, 1) 208 .WithTaggedFlag("SCLSM", 27) 209 .WithTag("INACTOUT", 28, 2) 210 .WithTaggedFlag("LOWTOUTEN", 30) 211 .WithReservedBits(31, 1) 212 ; 213 214 Registers.ControlB.Define(thisProvidesDoubleWordRegisterCollection) 215 .WithReservedBits(0, 8) 216 .WithFlag(8, out smartMode, name: "SMEN") 217 .WithTaggedFlag("GCMD / QCEN", 9) 218 .WithTaggedFlag("AACKEN", 10) 219 .WithReservedBits(11, 3) 220 .WithTag("AMODE", 14, 2) 221 .WithValueField(16, 2, name: "CMD", 222 valueProviderCallback: _ => 0, 223 writeCallback: (_, value) => 224 { 225 switch(value) 226 { 227 case 0: // NOTE: No-op 228 break; 229 case 1: 230 RestartTransaction(); 231 break; 232 case 2: 233 // NOTE: "Execute acknowledge, then read byte" 234 // As we execute actual byte read on reading the `DATA` register, 235 // we can omit that here. 236 break; 237 case 3: 238 state = State.Idle; 239 if(shouldFinishTransmission.Value && !smartMode.Value) 240 { 241 activePeripheral?.FinishTransmission(); 242 } 243 break; 244 } 245 }) 246 .WithFlag(18, out shouldFinishTransmission, name: "ACKACT") 247 .WithReservedBits(19, 5) 248 .WithReservedBits(24, 8) 249 ; 250 251 Registers.BaudRate.Define(thisProvidesDoubleWordRegisterCollection) 252 .WithTag("BAUD", 0, 8) 253 .WithTag("BAUDLOW", 8, 8) 254 .WithTag("HSBAUD", 16, 8) 255 .WithTag("HSBAUDLOW", 24, 8) 256 ; 257 258 Registers.InterruptEnableClear.Define(thisProvidesByteRegisterCollection) 259 .WithFlag(0, name: "AMATCH / MB", 260 valueProviderCallback: _ => hostOnBusInterruptEnabled, 261 writeCallback: (_, value) => { if(value) hostOnBusInterruptEnabled = false; }) 262 .WithFlag(1, name: "PREC / SB", 263 valueProviderCallback: _ => clientOnBusInterruptEnabled, 264 writeCallback: (_, value) => { if(value) clientOnBusInterruptEnabled = false; }) 265 .WithReservedBits(2, 5) 266 .WithFlag(7, name: "ERROR", 267 valueProviderCallback: _ => errorInterruptEnabled, 268 writeCallback: (_, value) => { if(value) errorInterruptEnabled = false; }) 269 .WithChangeCallback((_, __) => UpdateInterrupts()) 270 ; 271 272 Registers.InterruptEnableSet.Define(thisProvidesByteRegisterCollection) 273 .WithFlag(0, name: "AMATCH / MB", 274 valueProviderCallback: _ => hostOnBusInterruptEnabled, 275 writeCallback: (_, value) => { if(value) hostOnBusInterruptEnabled = true; }) 276 .WithFlag(1, name: "PREC / SB", 277 valueProviderCallback: _ => clientOnBusInterruptEnabled, 278 writeCallback: (_, value) => { if(value) clientOnBusInterruptEnabled = true; }) 279 .WithReservedBits(2, 5) 280 .WithFlag(7, name: "ERROR", 281 valueProviderCallback: _ => errorInterruptEnabled, 282 writeCallback: (_, value) => { if(value) errorInterruptEnabled = true; }) 283 .WithChangeCallback((_, __) => UpdateInterrupts()) 284 ; 285 286 Registers.InterruptFlagStatusAndClear.Define(thisProvidesByteRegisterCollection) 287 .WithFlag(0, out hostOnBusInterruptPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "AMATCH / MB") 288 .WithFlag(1, out clientOnBusInterruptPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "PREC / SB") 289 .WithReservedBits(2, 5) 290 .WithFlag(7, out errorInterruptPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "ERROR") 291 .WithChangeCallback((_, __) => UpdateInterrupts()) 292 ; 293 294 Registers.Status.Define(thisProvidesWordRegisterCollection) 295 .WithTaggedFlag("BUSERR", 0) 296 .WithTaggedFlag("COLL / ARBLOST", 1) 297 .WithFlag(2, out acknowledgeMissing, name: "RXNACK") 298 .WithTaggedFlag("DIR", 3) 299 .WithTag("SR / BUSSTATE", 4, 2) 300 .WithTaggedFlag("LOWTOUT", 6) 301 .WithTaggedFlag("CLKHOLD", 7) 302 .WithTaggedFlag("MEXTTOUT", 8) 303 .WithTaggedFlag("SEXTTOUT", 9) 304 .WithTaggedFlag("HS / LENERR", 10) 305 .WithReservedBits(11, 5) 306 ; 307 308 Registers.SynchronizationBusy.Define(thisProvidesDoubleWordRegisterCollection) 309 .WithTaggedFlag("SWRST", 0) 310 .WithTaggedFlag("ENABLE", 1) 311 .WithTaggedFlag("SYSOP", 2) 312 .WithReservedBits(3, 29) 313 ; 314 315 Registers.AddressRegister.Define(thisProvidesDoubleWordRegisterCollection) 316 .WithValueField(0, 11, name: "ADDR", 317 writeCallback: (_, value) => 318 { 319 TryFlush(); 320 var reading = (value & 0x1) > 0; 321 var address = (int)(value >> 1); 322 StartTransaction(address, reading); 323 }) 324 .WithReservedBits(11, 2) 325 .WithTaggedFlag("LENEN", 13) 326 .WithTaggedFlag("HS", 14) 327 .WithTaggedFlag("TENBITEN", 15) 328 .WithTag("LEN", 16, 8) 329 .WithReservedBits(24, 8) 330 ; 331 332 Registers.DataRegister0.Define(thisProvidesByteRegisterCollection) 333 .WithValueField(0, 8, name: "DATA", 334 valueProviderCallback: _ => 335 { 336 if(state == State.Reading) 337 { 338 clientOnBusInterruptPending.Value = true; 339 UpdateInterrupts(); 340 } 341 342 var returnValue = activePeripheral?.Read(1)?.FirstOrDefault() ?? (byte)0x00; 343 if(smartMode.Value && shouldFinishTransmission.Value) 344 { 345 activePeripheral?.FinishTransmission(); 346 } 347 348 return returnValue; 349 }, 350 writeCallback: (_, value) => 351 { 352 if(state != State.Writing) 353 { 354 this.Log(LogLevel.Warning, "Tried to write the DATA register while not in writing state"); 355 return; 356 } 357 358 txQueue.Enqueue((byte)value); 359 hostOnBusInterruptPending.Value = true; 360 UpdateInterrupts(); 361 }) 362 ; 363 364 Registers.DataRegister1.Define(thisProvidesByteRegisterCollection) 365 .WithReservedBits(0, 8) 366 ; 367 368 Registers.DebugControl.Define(thisProvidesByteRegisterCollection) 369 .WithTaggedFlag("DBGSTOP", 0) 370 .WithReservedBits(1, 7) 371 ; 372 } 373 UpdateInterrupts()374 private void UpdateInterrupts() 375 { 376 var interrupt = false; 377 378 interrupt |= hostOnBusInterruptEnabled && hostOnBusInterruptPending.Value; 379 interrupt |= clientOnBusInterruptEnabled && clientOnBusInterruptPending.Value; 380 interrupt |= errorInterruptEnabled && errorInterruptPending.Value; 381 382 this.Log(LogLevel.Debug, "IRQ set to: {0}", interrupt); 383 IRQ.Set(interrupt); 384 } 385 386 private readonly DoubleWordRegisterCollection doubleWordRegistersCollection; 387 private readonly WordRegisterCollection wordRegistersCollection; 388 private readonly ByteRegisterCollection byteRegistersCollection; 389 private readonly Queue<byte> txQueue = new Queue<byte>(); 390 391 private State state; 392 private II2CPeripheral activePeripheral; 393 394 private IFlagRegisterField enabled; 395 private IEnumRegisterField<Mode> mode; 396 private IFlagRegisterField smartMode; 397 private IEnumRegisterField<SpeedMode> speedMode; 398 private IFlagRegisterField acknowledgeMissing; 399 private IFlagRegisterField shouldFinishTransmission; 400 401 private IFlagRegisterField hostOnBusInterruptPending; 402 private IFlagRegisterField clientOnBusInterruptPending; 403 private IFlagRegisterField errorInterruptPending; 404 405 private bool hostOnBusInterruptEnabled; 406 private bool clientOnBusInterruptEnabled; 407 private bool errorInterruptEnabled; 408 409 private enum State 410 { 411 Unknown, 412 Idle, 413 Reading, 414 Writing, 415 } 416 417 private enum Mode : ulong 418 { 419 USARTExternalClock, 420 USARTInternalClock, 421 SPIClient, 422 SPIHost, 423 I2CClient, 424 I2CHost, 425 Undefined0, 426 Undefined1, 427 } 428 429 private enum SpeedMode : ulong 430 { 431 Standard, 432 Fast, 433 HighSpeed, 434 } 435 436 private enum Registers : long 437 { 438 ControlA = 0x0, // CTRLA 439 ControlB = 0x4, // CTRLB 440 // Reserved 0x08 - 0x0B, 441 BaudRate = 0xC, // BAUD 442 // Reserved 0x10 - 0x13, 443 InterruptEnableClear = 0x14, // INTENCLR 444 // Reserved 0x15, 445 InterruptEnableSet = 0x16, // INTENSET 446 // Reserved 0x17, 447 InterruptFlagStatusAndClear = 0x18, // INTFLAG 448 // Reserved 0x19, 449 Status = 0x1A, // STATUS 450 SynchronizationBusy = 0x1C, // SYNCBUSY 451 // Reserved 0x20 - 0x23, 452 AddressRegister = 0x24, // ADDR 453 DataRegister0 = 0x28, // DATA [ 7:0] 454 DataRegister1 = 0x29, // DATA [15:8] 455 DebugControl = 0x30 // DBGCTRL 456 } 457 } 458 } 459