1 // 2 // Copyright (c) 2010-2024 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 System.Linq; 9 using Antmicro.Renode.Peripherals.Bus; 10 using Antmicro.Renode.Peripherals.Helpers; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Core.Structure; 13 using Antmicro.Renode.Core.Structure.Registers; 14 using Antmicro.Renode.Logging; 15 using Antmicro.Renode.Utilities; 16 17 namespace Antmicro.Renode.Peripherals.SPI 18 { 19 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)] 20 public class Cadence_SPI : SimpleContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize 21 { Cadence_SPI(IMachine machine, int txFifoCapacity = DefaultTxFifoCapacity, int rxFifoCapacity = DefaultRxFifoCapacity)22 public Cadence_SPI(IMachine machine, int txFifoCapacity = DefaultTxFifoCapacity, int rxFifoCapacity = DefaultRxFifoCapacity) : base(machine) 23 { 24 this.txFifoCapacity = txFifoCapacity; 25 this.rxFifoCapacity = rxFifoCapacity; 26 27 txFifo = new Queue<byte>(this.txFifoCapacity); 28 rxFifo = new Queue<byte>(this.rxFifoCapacity); 29 registers = new DoubleWordRegisterCollection(this, BuildRegisterMap()); 30 31 txFifoFull = new CadenceInterruptFlag(() => txFifo.Count >= this.txFifoCapacity); 32 txFifoNotFull = new CadenceInterruptFlag(() => txFifo.Count < (int)txFifoThreshold.Value); 33 txFifoUnderflow = new CadenceInterruptFlag(() => false); 34 rxFifoOverflow = new CadenceInterruptFlag(() => false); 35 rxFifoFull = new CadenceInterruptFlag(() => rxFifo.Count >= this.rxFifoCapacity); 36 rxFifoNotEmpty = new CadenceInterruptFlag(() => rxFifo.Count >= (int)rxFifoThreshold.Value); 37 modeFail = new CadenceInterruptFlag(() => false); // Not handled 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 Reset()50 public override void Reset() 51 { 52 registers.Reset(); 53 txFifo.Clear(); 54 rxFifo.Clear(); 55 selectedPeripheral = null; 56 57 foreach(var flag in GetInterruptFlags()) 58 { 59 flag.Reset(); 60 } 61 UpdateInterrupts(); 62 } 63 64 public long Size => 0x100; 65 66 [DefaultInterrupt] 67 public GPIO IRQ { get; } = new GPIO(); 68 69 public GPIO TxFifoFullIRQ { get; } = new GPIO(); 70 public GPIO TxFifoFillLevelThresholdIRQ { get; } = new GPIO(); 71 72 public GPIO RxFifoFullIRQ { get; } = new GPIO(); 73 public GPIO RxFifoFillLevelThresholdIRQ { get; } = new GPIO(); 74 TransmitData()75 private void TransmitData() 76 { 77 if(spiMode.Value == SPIMode.PeripheralMode) 78 { 79 this.Log(LogLevel.Warning, "Trying to transfer data from a SPI in peripheral mode. Only controller mode is supported."); 80 return; 81 } 82 if(!spiEnable.Value) 83 { 84 this.Log(LogLevel.Warning, "Trying to transfer data from a disabled SPI."); 85 return; 86 } 87 if(txFifo.Count == 0) 88 { 89 txFifoUnderflow.SetSticky(true); 90 this.Log(LogLevel.Debug, "Trying to transfer data from an empty Tx FIFO."); 91 return; 92 } 93 94 selectedPeripheral = GetPeripheral(); 95 if(selectedPeripheral != null) 96 { 97 while(txFifo.Count > 0) 98 { 99 EnqueueRx(selectedPeripheral.Transmit(txFifo.Dequeue())); 100 } 101 if(!manualChipSelect.Value) 102 { 103 DeselectPeripheral(); 104 } 105 } 106 else 107 { 108 for(var i = 0; i < txFifo.Count; i++) 109 { 110 EnqueueRx(default(byte)); 111 } 112 txFifo.Clear(); 113 this.Log(LogLevel.Warning, "There is no peripheral with selected address, received data contains dummy bytes."); 114 } 115 } 116 TryTransmitDataAutomatically()117 private void TryTransmitDataAutomatically() 118 { 119 if(!manualTransmission.Value && txFifo.Count > 0) 120 { 121 TransmitData(); 122 } 123 } 124 DeselectPeripheral()125 private void DeselectPeripheral() 126 { 127 if(selectedPeripheral != null) 128 { 129 selectedPeripheral.FinishTransmission(); 130 selectedPeripheral = null; 131 } 132 } 133 EnqueueTx(byte data)134 private void EnqueueTx(byte data) 135 { 136 if(!spiEnable.Value) 137 { 138 this.Log(LogLevel.Debug, "Writing to a disabled SPI peripheral FIFO."); 139 } 140 if(txFifoFull.Status) 141 { 142 this.Log(LogLevel.Warning, "Trying to write to a full Tx FIFO, data not queued."); 143 // According to the Zynq US+ Technical Reference Manual, the rxFifoOverflow interrupt should be asserted also for Tx FIFO overflow. 144 rxFifoOverflow.SetSticky(true); 145 return; 146 } 147 txFifo.Enqueue(data); 148 } 149 EnqueueRx(byte data)150 private void EnqueueRx(byte data) 151 { 152 if(rxFifoFull.Status) 153 { 154 rxFifoOverflow.SetSticky(true); 155 this.Log(LogLevel.Warning, "Can't write to a full Rx FIFO, data not queued."); 156 return; 157 } 158 rxFifo.Enqueue(data); 159 } 160 DequeueRx()161 private byte DequeueRx() 162 { 163 if(!spiEnable.Value) 164 { 165 this.Log(LogLevel.Debug, "Reading from a disabled SPI peripheral FIFO."); 166 } 167 if(rxFifo.Count == 0) 168 { 169 this.Log(LogLevel.Warning, "Trying to read from an empty Rx FIFO, dummy data returned."); 170 return default(byte); 171 } 172 return rxFifo.Dequeue(); 173 } 174 GetPeripheral()175 private ISPIPeripheral GetPeripheral() 176 { 177 if(TryGetPeripheralAddress(out var address)) 178 { 179 if(TryGetByAddress(address, out var peripheral)) 180 { 181 return peripheral; 182 } 183 this.Log(LogLevel.Warning, "Can't find SPI peripheral at address 0x{0:X}.", address); 184 } 185 return null; 186 } 187 TryGetPeripheralAddress(out int address)188 private bool TryGetPeripheralAddress(out int address) 189 { 190 if(chipSelectAddr.Value != ChipSelectNoPeripheral) 191 { 192 var addressReg = (int)chipSelectAddr.Value & ChipSelectAddrMask; 193 if(chipSelectMode.Value != ChipSelectMode.ThreeChipsNoDecoder) 194 { 195 address = addressReg; 196 return true; 197 } 198 199 // Address is set to 0 for register = 0bxxx0, 1 for register = 0bxx01 and so on 200 for(var index = 0; index < ChipSelectNoDecoderCount; index++) 201 { 202 if((addressReg & 0b1) == 0) 203 { 204 address = index; 205 return true; 206 } 207 addressReg >>= 1; 208 } 209 // The 0b0111 value of register is reserved 210 this.Log(LogLevel.Warning, "The 0x{0:X} value of the chipSelectAddr register is reserved.", chipSelectAddr.Value); 211 } 212 address = -1; 213 return false; 214 } 215 UpdateSticky()216 private void UpdateSticky() 217 { 218 foreach(var flag in GetInterruptFlags()) 219 { 220 flag.UpdateStickyStatus(); 221 } 222 } 223 UpdateInterrupts()224 private void UpdateInterrupts() 225 { 226 IRQ.Set(GetInterruptFlags().Any(x => x.InterruptStatus)); 227 RxFifoFullIRQ.Set(rxFifoFull.InterruptStatus); 228 TxFifoFullIRQ.Set(txFifoFull.InterruptStatus); 229 RxFifoFillLevelThresholdIRQ.Set(rxFifoNotEmpty.InterruptStatus); 230 TxFifoFillLevelThresholdIRQ.Set(txFifoNotFull.InterruptStatus); 231 } 232 BuildRegisterMap()233 private Dictionary<long, DoubleWordRegister> BuildRegisterMap() 234 { 235 var txFifoThresholdBits = BitHelper.GetMostSignificantSetBitIndex((ulong)txFifoCapacity); 236 var rxFifoThresholdBits = BitHelper.GetMostSignificantSetBitIndex((ulong)rxFifoCapacity); 237 return new Dictionary<long, DoubleWordRegister> 238 { 239 {(long)Registers.Config, new DoubleWordRegister(this, 0x00020000) 240 .WithReservedBits(18, 14) 241 .WithFlag(17, out modeFailEnable, name: "modeFailEnable") 242 .WithFlag(16, FieldMode.Write, name: "manualStartTransmission", 243 writeCallback: (_, val) => { if(val) TransmitData(); }) 244 .WithFlag(15, out manualTransmission, name: "manualTransmission") 245 .WithFlag(14, out manualChipSelect, name: "manualChipSelect") 246 .WithValueField(10, 4, out chipSelectAddr, name: "chipSelectAddress") 247 .WithEnumField(9, 1, out chipSelectMode) 248 .WithFlag(8, out referenceClockSelect, name: "referenceClockSelect") 249 .WithReservedBits(6, 2) 250 .WithValueField(3, 3, out baudRateDivider, name: "baudRateDivider") 251 .WithFlag(2, out clockPhase, name: "clockPhase") 252 .WithFlag(1, out clockPolarity, name: "clockPolarity") 253 .WithEnumField(0, 1, out spiMode, name: "spiMode", 254 changeCallback: (_, val) => { 255 if(val == SPIMode.PeripheralMode) 256 { 257 this.Log(LogLevel.Warning, "SPI mode changed to peripheral, while only controller mode is supported."); 258 } 259 }) 260 .WithWriteCallback((_, val) => 261 { 262 if(chipSelectAddr.Value == ChipSelectNoPeripheral || selectedPeripheral != GetPeripheral()) 263 { 264 DeselectPeripheral(); 265 } 266 }) 267 }, 268 {(long)Registers.InterruptStatus, new DoubleWordRegister(this) 269 .WithReservedBits(7, 25) 270 .WithFlag(6, 271 valueProviderCallback: (_) => txFifoUnderflow.StickyStatus, 272 writeCallback: (_, val) => txFifoUnderflow.ClearSticky(val), 273 name: "txFifoUnderflowInterruptStatus" 274 ) 275 // A few interrupts aren't sticky so they are cleared at every read 276 .WithFlag(5, 277 valueProviderCallback: (_) => rxFifoFull.StickyStatus, 278 writeCallback: (_, val) => rxFifoFull.ClearSticky(val), 279 readCallback: (_, __) => rxFifoFull.ClearSticky(true), 280 name: "rxFifoFullInterruptStatus" 281 ) 282 .WithFlag(4, 283 valueProviderCallback: (_) => rxFifoNotEmpty.StickyStatus, 284 writeCallback: (_, val) => rxFifoNotEmpty.ClearSticky(val), 285 readCallback: (_, __) => rxFifoNotEmpty.ClearSticky(true), 286 name: "rxFifoNotEmptyInterruptStatus" 287 ) 288 .WithFlag(3, 289 valueProviderCallback: (_) => txFifoFull.StickyStatus, 290 writeCallback: (_, val) => txFifoFull.ClearSticky(val), 291 readCallback: (_, __) => txFifoFull.ClearSticky(true), 292 name: "txFifoFullInterruptStatus" 293 ) 294 .WithFlag(2, 295 valueProviderCallback: (_) => txFifoNotFull.StickyStatus, 296 writeCallback: (_, val) => txFifoNotFull.ClearSticky(val), 297 readCallback: (_, __) => txFifoNotFull.ClearSticky(true), 298 name: "txFifoNotFullInterruptStatus" 299 ) 300 .WithFlag(1, 301 valueProviderCallback: (_) => modeFail.StickyStatus, 302 writeCallback: (_, val) => modeFail.ClearSticky(val), 303 name: "modeFailInterruptStatus" 304 ) 305 .WithFlag(0, 306 valueProviderCallback: (_) => rxFifoOverflow.StickyStatus, 307 writeCallback: (_, val) => rxFifoOverflow.ClearSticky(val), 308 name: "rxFifoOverflowInterruptStatus" 309 ) 310 .WithWriteCallback((_, __) => 311 { 312 UpdateSticky(); 313 UpdateInterrupts(); 314 }) 315 }, 316 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 317 .WithReservedBits(7, 25) 318 .WithFlag(6, FieldMode.Write, 319 writeCallback: (_, val) => txFifoUnderflow.InterruptEnable(val), 320 name: "txFifoUnderflowInterruptEnable" 321 ) 322 .WithFlag(5, FieldMode.Write, 323 writeCallback: (_, val) => rxFifoFull.InterruptEnable(val), 324 name: "rxFifoFullInterruptEnable" 325 ) 326 .WithFlag(4, FieldMode.Write, 327 writeCallback: (_, val) => rxFifoNotEmpty.InterruptEnable(val), 328 name: "rxFifoNotEmptyInterruptEnable" 329 ) 330 .WithFlag(3, FieldMode.Write, 331 writeCallback: (_, val) => txFifoFull.InterruptEnable(val), 332 name: "txFifoFullInterruptEnable" 333 ) 334 .WithFlag(2, FieldMode.Write, 335 writeCallback: (_, val) => txFifoNotFull.InterruptEnable(val), 336 name: "txFifoNotFullInterruptEnable" 337 ) 338 .WithFlag(1, FieldMode.Write, 339 writeCallback: (_, val) => modeFail.InterruptEnable(val), 340 name: "modeFailInterruptEnable" 341 ) 342 .WithFlag(0, FieldMode.Write, 343 writeCallback: (_, val) => rxFifoOverflow.InterruptEnable(val), 344 name: "rxFifoOverflowInterruptEnable" 345 ) 346 .WithWriteCallback((_, __) => UpdateInterrupts()) 347 }, 348 {(long)Registers.InterruptDisable, new DoubleWordRegister(this) 349 .WithReservedBits(7, 25) 350 .WithFlag(6, FieldMode.Write, 351 writeCallback: (_, val) => txFifoUnderflow.InterruptDisable(val), 352 name: "txFifoUnderflowInterruptDisable" 353 ) 354 .WithFlag(5, FieldMode.Write, 355 writeCallback: (_, val) => rxFifoFull.InterruptDisable(val), 356 name: "rxFifoFullInterruptDisable" 357 ) 358 .WithFlag(4, FieldMode.Write, 359 writeCallback: (_, val) => rxFifoNotEmpty.InterruptDisable(val), 360 name: "rxFifoNotEmptyInterruptDisable" 361 ) 362 .WithFlag(3, FieldMode.Write, 363 writeCallback: (_, val) => txFifoFull.InterruptDisable(val), 364 name: "txFifoFullInterruptDisable" 365 ) 366 .WithFlag(2, FieldMode.Write, 367 writeCallback: (_, val) => txFifoNotFull.InterruptDisable(val), 368 name: "txFifoNotFullInterruptDisable" 369 ) 370 .WithFlag(1, FieldMode.Write, 371 writeCallback: (_, val) => modeFail.InterruptDisable(val), 372 name: "modeFailInterruptDisable" 373 ) 374 .WithFlag(0, FieldMode.Write, 375 writeCallback: (_, val) => rxFifoOverflow.InterruptDisable(val), 376 name: "rxFifoOverflowInterruptDisable" 377 ) 378 .WithWriteCallback((_, __) => UpdateInterrupts()) 379 }, 380 {(long)Registers.InterruptMask, new DoubleWordRegister(this) 381 .WithReservedBits(7, 25) 382 .WithFlag(6, FieldMode.Read, 383 valueProviderCallback: (_) => txFifoUnderflow.InterruptMask, 384 name: "txFifoUnderflowInterruptMask" 385 ) 386 .WithFlag(5, FieldMode.Read, 387 valueProviderCallback: (_) => rxFifoFull.InterruptMask, 388 name: "rxFifoFullInterruptMask" 389 ) 390 .WithFlag(4, FieldMode.Read, 391 valueProviderCallback: (_) => rxFifoNotEmpty.InterruptMask, 392 name: "rxFifoNotEmptyInterruptMask" 393 ) 394 .WithFlag(3, FieldMode.Read, 395 valueProviderCallback: (_) => txFifoFull.InterruptMask, 396 name: "txFifoFullInterruptMask" 397 ) 398 .WithFlag(2, FieldMode.Read, 399 valueProviderCallback: (_) => txFifoNotFull.InterruptMask, 400 name: "txFifoNotFullInterruptMask" 401 ) 402 .WithFlag(1, FieldMode.Read, 403 valueProviderCallback: (_) => modeFail.InterruptMask, 404 name: "modeFailInterruptMask" 405 ) 406 .WithFlag(0, FieldMode.Read, 407 valueProviderCallback: (_) => rxFifoOverflow.InterruptMask, 408 name: "rxFifoOverflowInterruptMask" 409 ) 410 }, 411 {(long)Registers.Enable, new DoubleWordRegister(this) 412 .WithReservedBits(1, 31) 413 .WithFlag(0, out spiEnable, name: "spiEnable", 414 writeCallback: (_, val) => 415 { 416 if(val) 417 { 418 TryTransmitDataAutomatically(); 419 } 420 else 421 { 422 DeselectPeripheral(); 423 } 424 }) 425 }, 426 {(long)Registers.TxData, new DoubleWordRegister(this) 427 .WithReservedBits(8, 24) 428 .WithValueField(0, 8, FieldMode.Write, name: "txData", 429 writeCallback: (_, data) => 430 { 431 EnqueueTx((byte)data); 432 TryTransmitDataAutomatically(); 433 }) 434 .WithWriteCallback((_, __) => 435 { 436 UpdateSticky(); 437 UpdateInterrupts(); 438 }) 439 }, 440 {(long)Registers.RxData, new DoubleWordRegister(this) 441 .WithReservedBits(8, 24) 442 .WithValueField(0, 8, FieldMode.Read, name: "rxData", 443 valueProviderCallback: (_) => DequeueRx() 444 ) 445 .WithReadCallback((_, __) => 446 { 447 UpdateSticky(); 448 UpdateInterrupts(); 449 }) 450 }, 451 {(long)Registers.TxFifoThreshold, new DoubleWordRegister(this, InitialFifoThreshold) 452 .WithReservedBits(txFifoThresholdBits, 32 - txFifoThresholdBits) 453 .WithValueField(0, txFifoThresholdBits, out txFifoThreshold) 454 .WithWriteCallback((_, __) => 455 { 456 UpdateSticky(); 457 UpdateInterrupts(); 458 }) 459 }, 460 {(long)Registers.RxFifoThreshold, new DoubleWordRegister(this, InitialFifoThreshold) 461 .WithReservedBits(rxFifoThresholdBits, 32 - rxFifoThresholdBits) 462 .WithValueField(0, rxFifoThresholdBits, out rxFifoThreshold) 463 .WithWriteCallback((_, __) => 464 { 465 UpdateSticky(); 466 UpdateInterrupts(); 467 }) 468 }, 469 {(long)Registers.ModuleID, new DoubleWordRegister(this) 470 .WithReservedBits(25, 7) 471 .WithValueField(0, 25, FieldMode.Read, name: "moduleID", 472 valueProviderCallback: (_) => ModuleID 473 ) 474 } 475 }; 476 } 477 GetInterruptFlags()478 private IEnumerable<CadenceInterruptFlag> GetInterruptFlags() 479 { 480 yield return txFifoFull; 481 yield return txFifoNotFull; 482 yield return txFifoUnderflow; 483 yield return rxFifoOverflow; 484 yield return rxFifoFull; 485 yield return rxFifoNotEmpty; 486 } 487 488 private ISPIPeripheral selectedPeripheral; 489 490 private IFlagRegisterField modeFailEnable; 491 private IFlagRegisterField manualTransmission; 492 private IFlagRegisterField manualChipSelect; 493 private IValueRegisterField chipSelectAddr; 494 private IEnumRegisterField<ChipSelectMode> chipSelectMode; 495 private IEnumRegisterField<SPIMode> spiMode; 496 private IFlagRegisterField spiEnable; 497 private IValueRegisterField txFifoThreshold; 498 private IValueRegisterField rxFifoThreshold; 499 500 // These fields are related to a physical layer, which Renode doesn't simulate for the SPI bus 501 private IFlagRegisterField referenceClockSelect; 502 private IValueRegisterField baudRateDivider; 503 private IFlagRegisterField clockPhase; 504 private IFlagRegisterField clockPolarity; 505 506 private readonly CadenceInterruptFlag txFifoFull; 507 private readonly CadenceInterruptFlag txFifoNotFull; 508 private readonly CadenceInterruptFlag txFifoUnderflow; 509 private readonly CadenceInterruptFlag rxFifoOverflow; 510 private readonly CadenceInterruptFlag rxFifoFull; 511 private readonly CadenceInterruptFlag rxFifoNotEmpty; 512 private readonly CadenceInterruptFlag modeFail; 513 514 private readonly int txFifoCapacity; 515 private readonly int rxFifoCapacity; 516 private readonly Queue<byte> txFifo; 517 private readonly Queue<byte> rxFifo; 518 private readonly DoubleWordRegisterCollection registers; 519 520 private const int DefaultTxFifoCapacity = 128; 521 private const int DefaultRxFifoCapacity = 128; 522 private const int InitialFifoThreshold = 0x1; 523 private const int ChipSelectNoPeripheral = 0b1111; 524 private const int ChipSelectAddrMask = 0b0111; 525 private const int ChipSelectNoDecoderCount = 3; 526 private const int ModuleID = 0x90108; 527 528 private enum ChipSelectMode 529 { 530 ThreeChipsNoDecoder = 0x0, 531 EightChipsWithDecoder = 0x1 532 } 533 534 private enum SPIMode 535 { 536 PeripheralMode = 0x0, 537 ControllerMode = 0x1 538 } 539 540 private enum Registers : long 541 { 542 Config = 0x00, 543 InterruptStatus = 0x04, 544 InterruptEnable = 0x08, 545 InterruptDisable = 0x0c, 546 InterruptMask = 0x10, 547 Enable = 0x14, 548 Delay = 0x18, 549 TxData = 0x1c, 550 RxData = 0x20, 551 SlaveIdleCount = 0x24, 552 TxFifoThreshold = 0x28, 553 RxFifoThreshold = 0x2c, 554 ModuleID = 0xfc 555 } 556 } 557 } 558