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; 8 using System.Collections.Generic; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.Bus; 14 using Antmicro.Renode.Peripherals.DMA; 15 using Antmicro.Renode.Utilities; 16 17 namespace Antmicro.Renode.Peripherals.SPI 18 { 19 public class SAM_SPI : SimpleContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize, IProvidesRegisterCollection<DoubleWordRegisterCollection>, ISamPdcBytePeripheral, ISamPdcWordPeripheral, ISamPdcDoubleWordPeripheral 20 { SAM_SPI(IMachine machine)21 public SAM_SPI(IMachine machine) : base(machine) 22 { 23 IRQ = new GPIO(); 24 irqManager = new InterruptManager<Interrupts>(this, IRQ, nameof(IRQ)); 25 transmitBuffer = new Queue<byte>(); 26 RegistersCollection = new DoubleWordRegisterCollection(this); 27 pdc = new SAM_PDC(machine, this, (long)Registers.PdcReceivePointer, HandlePDCInterrupts); 28 DefineRegisters(); 29 Reset(); 30 } 31 Reset()32 public override void Reset() 33 { 34 RegistersCollection.Reset(); 35 SWReset(); 36 irqManager.Reset(); 37 irqManager.SetInterrupt(Interrupts.TransmissionRegistersEmpty); 38 pdc.Reset(); 39 } 40 ReadDoubleWord(long offset)41 public uint ReadDoubleWord(long offset) 42 { 43 return RegistersCollection.Read(offset); 44 } 45 WriteDoubleWord(long offset, uint value)46 public void WriteDoubleWord(long offset, uint value) 47 { 48 if(writeProtection && writeProtected.Contains(offset)) 49 { 50 writeProtectionViolationSource.Value = (ulong)offset; 51 writeProtectionViolationStatus.Value = true; 52 this.WarningLog("Writing to Write Protected register at offset: 0x{0:X}", offset); 53 } 54 else 55 { 56 RegistersCollection.Write(offset, value); 57 } 58 } 59 DmaByteRead()60 public byte? DmaByteRead() => (byte?)DmaWordRead(); 61 62 public void DmaByteWrite(byte data) => DmaDoubleWordWrite(data); 63 DmaWordRead()64 public ushort? DmaWordRead() 65 { 66 if(!irqManager.IsSet(Interrupts.ReceiveDataRegisterFull)) 67 { 68 return null; 69 } 70 var rval = RegistersCollection.Read((long)Registers.ReceiveData); 71 return (ushort)(rval & 0xFFFF); 72 } 73 DmaWordWrite(ushort data)74 public void DmaWordWrite(ushort data) => DmaDoubleWordWrite(data); 75 DmaDoubleWordRead()76 public uint? DmaDoubleWordRead() => null; 77 DmaDoubleWordWrite(uint data)78 public void DmaDoubleWordWrite(uint data) 79 { 80 RegistersCollection.Write((long)Registers.TransmitData, data); 81 pdc.TriggerReceiver(); 82 } 83 84 public DoubleWordRegisterCollection RegistersCollection { get; } 85 public GPIO IRQ { get; } 86 public long Size => 0x128; 87 88 public TransferType DmaWriteAccessWidth { get; private set; } 89 public TransferType DmaReadAccessWidth { get; private set; } 90 DefineRegisters()91 private void DefineRegisters() 92 { 93 Registers.Control.Define(this) 94 .WithFlag(0, FieldMode.Write, 95 writeCallback: (_, val) => 96 { 97 if(!val) 98 { 99 return; 100 } 101 isEnabled.Value = true; 102 irqManager.SetInterrupt(Interrupts.TransmitDataRegisterEmpty); 103 if(!waitDataReadBeforeTransfer.Value || !irqManager.IsSet(Interrupts.ReceiveDataRegisterFull)) 104 { 105 TryTransfer(); 106 } 107 }, 108 name: "SPIEN") 109 .WithFlag(1, FieldMode.Write, 110 writeCallback: (_, val) => 111 { 112 if(val) 113 { 114 isEnabled.Value = false; 115 irqManager.ClearInterrupt(Interrupts.TransmitDataRegisterEmpty); 116 } 117 }, 118 name: "SPIDIS") 119 .WithReservedBits(2, 5) 120 .WithFlag(7, FieldMode.Write, 121 writeCallback: (_, val) => 122 { 123 if (val) 124 { 125 SWReset(); 126 } 127 }, 128 name: "SWRST") 129 .WithReservedBits(8, 16) 130 .WithTaggedFlag("LASTXFER", 24) 131 .WithReservedBits(25, 7) 132 ; 133 134 // Protected by WPEN 135 Registers.Mode.Define(this) 136 .WithEnumField(0, 1, out mode, name: "MSTR") 137 .WithEnumField(1, 1, out peripheralSelectMode, name: "PS") 138 .WithFlag(2, out useDecoder, name: "PCSDEC") 139 .WithReservedBits(3, 1) 140 .WithTaggedFlag("MODFDIS", 4) 141 .WithFlag(5, out waitDataReadBeforeTransfer, name: "WDRBT") 142 .WithReservedBits(6, 1) 143 .WithFlag(7, out localLoopback, name: "LLB") 144 .WithReservedBits(8, 8) 145 .WithValueField(16, 4, 146 writeCallback: (_, val) => ChipSelect(val), 147 name: "PCS") 148 .WithReservedBits(20, 4) 149 .WithTag("DLYBCS", 24, 8) 150 .WithWriteCallback((_, __) => SetDmaAccessWidth()) 151 ; 152 153 Registers.ReceiveData.Define(this) 154 .WithValueField(0, 16, out receiveBuffer, FieldMode.Read, name: "RD") 155 .WithValueField(16, 4, FieldMode.Read, 156 valueProviderCallback: _ => mode.Value == SPIMode.Master ? rawChipSelect : 0, 157 name: "PCS") 158 .WithReservedBits(20, 12) 159 .WithReadCallback((_, __) => 160 { 161 irqManager.ClearInterrupt(Interrupts.ReceiveDataRegisterFull); 162 if(isEnabled.Value && mode.Value == SPIMode.Master && waitDataReadBeforeTransfer.Value) 163 { 164 TryTransfer(); 165 } 166 }) 167 ; 168 169 Registers.TransmitData.Define(this) 170 .WithValueField(0, 16, out transmitData, FieldMode.Write, name: "TD") 171 .WithValueField(16, 4, out var chipSelect, FieldMode.Write, name: "PCS") 172 .WithReservedBits(20, 4) 173 .WithTaggedFlag("LASTXFER", 24) 174 .WithReservedBits(25, 7) 175 .WithWriteCallback((_, __) => 176 { 177 transmitBuffer.Clear(); 178 if(peripheralSelectMode.Value == PeripheralSelectMode.Variable) 179 { 180 ChipSelect(chipSelect.Value); 181 } 182 EnqueueTx(transmitData.Value); 183 irqManager.ClearInterrupt(Interrupts.TransmissionRegistersEmpty); 184 if(!isEnabled.Value || mode.Value != SPIMode.Master) 185 { 186 return; 187 } 188 if(!(waitDataReadBeforeTransfer.Value && irqManager.IsSet(Interrupts.ReceiveDataRegisterFull))) 189 { 190 TryTransfer(); 191 } 192 }) 193 ; 194 195 RegistersCollection.AddRegister((long)Registers.Status, 196 irqManager.GetRawInterruptFlagRegister<DoubleWordRegister>() 197 .WithReservedBits(11, 5) 198 .WithFlag(16, out isEnabled, FieldMode.Read, name: "SPIENS") 199 .WithReservedBits(17, 15) 200 .WithReadCallback((_, __) => 201 { 202 // These interrupts are cleared on read 203 irqManager.ClearInterrupt(Interrupts.ModeFaultError); 204 irqManager.ClearInterrupt(Interrupts.OverrunError); 205 irqManager.ClearInterrupt(Interrupts.NSSRising); 206 irqManager.ClearInterrupt(Interrupts.UnderrunError); 207 }) 208 ); 209 210 RegistersCollection.AddRegister((long)Registers.InterruptEnable, 211 irqManager.GetInterruptEnableSetRegister<DoubleWordRegister>() 212 .WithReservedBits(11, 21) 213 ); 214 215 RegistersCollection.AddRegister((long)Registers.InterruptDisable, 216 irqManager.GetInterruptEnableClearRegister<DoubleWordRegister>() 217 .WithReservedBits(11, 21) 218 ); 219 220 RegistersCollection.AddRegister((long)Registers.InterruptMask, 221 irqManager.GetInterruptEnableRegister<DoubleWordRegister>() 222 .WithReservedBits(11, 21) 223 ); 224 225 // Protected by WPEN 226 Registers.ChipSelect0.DefineMany(this, NumberOfChipSelectRegisters, setup: (register, registerIndex) => 227 { 228 register 229 .WithTaggedFlag("CPOL", 0) 230 .WithTaggedFlag("NCPHA", 1) 231 .WithTaggedFlag("CSNAAT", 2) 232 .WithTaggedFlag("CSAAT", 3) 233 .WithValueField(4, 4, 234 writeCallback: (_, val) => 235 { 236 txLengths[registerIndex] = val; 237 SetDmaAccessWidth(); 238 }, 239 valueProviderCallback: _ => 240 { 241 return txLengths[registerIndex]; 242 }, 243 name: "BITS") 244 .WithTag("SCBR", 8, 8) 245 .WithTag("DLYBS", 16, 8) 246 .WithTag("DLYBCT", 24, 8); 247 }, 248 stepInBytes: 4 249 ); 250 251 Registers.WriteProtectionMode.Define(this) 252 .WithFlag(0, out var enableProtection, name: "WPEN") 253 .WithReservedBits(1, 7) 254 .WithValueField(8, 24, out var passkey, name: "WPKEY") 255 .WithWriteCallback((_, val) => 256 { 257 if(passkey.Value != WriteProtectionPasswd) 258 { 259 this.WarningLog("Wrong Write Protection Key! WPVS bit remains unchanged."); 260 return; 261 } 262 writeProtection = enableProtection.Value; 263 }) 264 ; 265 266 Registers.WriteProtectionStatus.Define(this) 267 .WithFlag(0, out writeProtectionViolationStatus, FieldMode.ReadToClear, name: "WPVS") 268 .WithReservedBits(1, 7) 269 .WithValueField(8, 8, out writeProtectionViolationSource, FieldMode.Read, name: "WPVSRC") 270 .WithReservedBits(16, 16) 271 ; 272 } 273 HandlePDCInterrupts()274 private void HandlePDCInterrupts() 275 { 276 if(pdc == null) 277 { 278 return; 279 } 280 irqManager.SetInterrupt(Interrupts.EndOfReceiveBuffer, pdc.EndOfRxBuffer); 281 irqManager.SetInterrupt(Interrupts.EndOfTransmitBuffer, pdc.EndOfTxBuffer); 282 irqManager.SetInterrupt(Interrupts.TransmitBufferEmpty, pdc.TxBufferEmpty); 283 irqManager.SetInterrupt(Interrupts.ReceiveBufferFull, pdc.RxBufferFull); 284 } 285 EnqueueTx(ulong val)286 private void EnqueueTx(ulong val) 287 { 288 var byte0 = val & 0xFF; 289 transmitBuffer.Enqueue((byte)byte0); 290 if(SelectedSlaveByteMask > 0) 291 { 292 var byte1 = ((val & 0xFF00) >> 8) & SelectedSlaveByteMask; 293 transmitBuffer.Enqueue((byte)byte1); 294 } 295 } 296 TryTransfer()297 private void TryTransfer() 298 { 299 if(transmitBuffer.Count == 0 || !(TryGetByAddress(selectedSlaveAddr, out var slavePeripheral) || localLoopback.Value)) 300 { 301 return; 302 } 303 if(irqManager.IsSet(Interrupts.ReceiveDataRegisterFull)) 304 { 305 irqManager.SetInterrupt(Interrupts.OverrunError); 306 } 307 DmaReadAccessWidth = (TransferType)transmitBuffer.Count; 308 309 var transmit = localLoopback.Value ? (Func<byte, byte>)(b => b) : slavePeripheral.Transmit; 310 311 var transmitted = (ulong)0; 312 receiveBuffer.Value = 0; 313 314 for(var i = 0; i < transmitBuffer.Count; ++i) 315 { 316 var b = transmitBuffer.Dequeue(); 317 transmitted |= (ulong)b << (8 * i); 318 receiveBuffer.Value |= (ulong)transmit(b) << (8 * i); 319 } 320 321 if(localLoopback.Value) 322 { 323 this.NoisyLog("Transmitted 0x{0:X} in loopback", transmitted); 324 } 325 else 326 { 327 slavePeripheral.NoisyLog("Received 0x{0:X}, transmitted 0x{1:X}", transmitted, receiveBuffer.Value); 328 } 329 330 irqManager.SetInterrupt(Interrupts.ReceiveDataRegisterFull); 331 irqManager.SetInterrupt(Interrupts.TransmissionRegistersEmpty); 332 } 333 ChipSelect(ulong val)334 private void ChipSelect(ulong val) 335 { 336 rawChipSelect = val; 337 if(!useDecoder.Value) 338 { 339 // first zero from the least significant side describes the selected slave 340 selectedSlaveAddr = Misc.Logarithm2((int)BitHelper.GetLeastSignificantZero(val)); 341 } 342 else 343 { 344 selectedSlaveAddr = (int)val; 345 } 346 } 347 SetDmaAccessWidth()348 private void SetDmaAccessWidth() 349 { 350 if(peripheralSelectMode.Value == PeripheralSelectMode.Variable) 351 { 352 DmaWriteAccessWidth = TransferType.DoubleWord; 353 } 354 else 355 { 356 DmaWriteAccessWidth = txLengths[SelectedSlaveRegisterNumber] > 0 ? TransferType.Word : TransferType.Byte; 357 } 358 } 359 SWReset()360 private void SWReset() 361 { 362 // Software reset does not affect the PDC 363 isEnabled.Value = false; 364 useDecoder.Value = false; 365 waitDataReadBeforeTransfer.Value = false; 366 writeProtection = false; 367 localLoopback.Value = false; 368 selectedSlaveAddr = 0; 369 rawChipSelect = 0; 370 transmitBuffer.Clear(); 371 irqManager.SetInterrupt(Interrupts.TransmissionRegistersEmpty); 372 mode.Value = SPIMode.Slave; 373 peripheralSelectMode.Value = PeripheralSelectMode.Fixed; 374 DmaWriteAccessWidth = TransferType.Byte; 375 DmaReadAccessWidth = TransferType.Byte; 376 Array.Clear(txLengths, 0, txLengths.Length); 377 } 378 379 private int SelectedSlaveRegisterNumber => useDecoder.Value ? (selectedSlaveAddr / 4) : selectedSlaveAddr; 380 381 private ulong SelectedSlaveByteMask => (ulong)((1 << (int)txLengths[SelectedSlaveRegisterNumber]) - 1); 382 383 private bool writeProtection; 384 private int selectedSlaveAddr; 385 private ulong rawChipSelect; 386 private ulong[] txLengths = {0, 0, 0, 0}; 387 private Queue<byte> transmitBuffer; 388 private IValueRegisterField receiveBuffer; 389 private IValueRegisterField transmitData; 390 private IValueRegisterField writeProtectionViolationSource; 391 private IFlagRegisterField writeProtectionViolationStatus; 392 private IFlagRegisterField isEnabled; 393 private IFlagRegisterField useDecoder; 394 private IFlagRegisterField waitDataReadBeforeTransfer; 395 private IFlagRegisterField localLoopback; 396 private IEnumRegisterField<SPIMode> mode; 397 private IEnumRegisterField<PeripheralSelectMode> peripheralSelectMode; 398 private readonly InterruptManager<Interrupts> irqManager; 399 private readonly SAM_PDC pdc; 400 private readonly List<long> writeProtected = new List<long> 401 { 402 (long)Registers.Mode, 403 (long)Registers.ChipSelect0, 404 (long)Registers.ChipSelect1, 405 (long)Registers.ChipSelect2, 406 (long)Registers.ChipSelect3, 407 }; 408 private const int NumberOfChipSelectRegisters = 4; 409 private const int WriteProtectionPasswd = 0x535049; // ASCII: "SPI" 410 411 public enum Registers 412 { 413 Control = 0x00, 414 Mode = 0x04, 415 ReceiveData = 0x08, 416 TransmitData = 0x0C, 417 Status = 0x10, 418 InterruptEnable = 0x14, 419 InterruptDisable = 0x18, 420 InterruptMask = 0x1C, 421 ChipSelect0 = 0x30, 422 ChipSelect1 = 0x34, 423 ChipSelect2 = 0x38, 424 ChipSelect3 = 0x3C, 425 WriteProtectionMode = 0xE4, 426 WriteProtectionStatus = 0xE8, 427 // PDC 428 PdcReceivePointer = 0x100, 429 PdcReceiveCounter = 0x104, 430 PdcTransmitPointer = 0x108, 431 PdcTransmitCounter = 0x10C, 432 PdcReceiveNextPointer = 0x110, 433 PdcReceiveNextCounter = 0x114, 434 PdcTransmitNextPointer = 0x118, 435 PdcTransmitNextCounter = 0x11C, 436 PdcTransferControl = 0x120, 437 PdcTransferStatus = 0x124, 438 } 439 440 private enum SPIMode : ulong 441 { 442 Slave = 0x0, 443 Master = 0x1, 444 } 445 446 private enum PeripheralSelectMode : ulong 447 { 448 Fixed = 0x0, 449 Variable = 0x1, 450 } 451 452 private enum Interrupts 453 { 454 ReceiveDataRegisterFull = 0, 455 TransmitDataRegisterEmpty = 1, 456 ModeFaultError = 2, 457 OverrunError = 3, 458 EndOfReceiveBuffer = 4, 459 EndOfTransmitBuffer = 5, 460 ReceiveBufferFull = 6, 461 TransmitBufferEmpty = 7, 462 NSSRising = 8, 463 TransmissionRegistersEmpty = 9, 464 UnderrunError = 10, 465 } 466 } 467 } 468 469