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.Collections.Generic; 9 using System.Linq; 10 using Antmicro.Renode.Peripherals.Bus; 11 using Antmicro.Renode.Peripherals.Helpers; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Core.Structure; 14 using Antmicro.Renode.Core.Structure.Registers; 15 using Antmicro.Renode.Utilities; 16 using Antmicro.Renode.Logging; 17 using Antmicro.Renode.Peripherals.SPI.Cadence_xSPICommands; 18 19 namespace Antmicro.Renode.Peripherals.SPI 20 { 21 public class Cadence_xSPI : SimpleContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize 22 { Cadence_xSPI(IMachine machine)23 public Cadence_xSPI(IMachine machine) : base(machine) 24 { 25 registers = new DoubleWordRegisterCollection(this, BuildRegisterMap()); 26 auxiliaryRegisters = new DoubleWordRegisterCollection(this); 27 28 controllerIdle = new CadenceInterruptFlag(() => currentCommand == null || currentCommand.Completed || currentCommand.Failed); 29 commandCompleted = new CadenceInterruptFlag(() => (currentCommand as STIGCommand)?.Completed ?? false); 30 commandIgnored = new CadenceInterruptFlag(); 31 dmaTriggered = new CadenceInterruptFlag(() => (currentCommand as IDMACommand)?.DMATriggered ?? false); 32 dmaError = new CadenceInterruptFlag(() => (currentCommand as IDMACommand)?.DMAError ?? false); 33 autoCommandCompleted = new CadenceInterruptFlag(() => (currentCommand as PIOCommand)?.Completed ?? false, initialMask: true); 34 } 35 WriteDoubleWord(long offset, uint value)36 public void WriteDoubleWord(long offset, uint value) 37 { 38 registers.Write(offset, value); 39 } 40 ReadDoubleWord(long offset)41 public uint ReadDoubleWord(long offset) 42 { 43 return registers.Read(offset); 44 } 45 46 [ConnectionRegion("auxiliary")] WriteDoubleWordToAuxiliary(long offset, uint value)47 public void WriteDoubleWordToAuxiliary(long offset, uint value) 48 { 49 auxiliaryRegisters.Write(offset, value); 50 } 51 52 [ConnectionRegion("auxiliary")] ReadDoubleWordFromAuxiliary(long offset)53 public uint ReadDoubleWordFromAuxiliary(long offset) 54 { 55 return auxiliaryRegisters.Read(offset); 56 } 57 58 // There is no information in the Linux driver about handling offset for a DMA access 59 // The comment above applies to all Write*ToDMA and Read*FromDMA methods 60 [ConnectionRegion("dma")] WriteByteUsingDMA(long offset, byte value)61 public void WriteByteUsingDMA(long offset, byte value) 62 { 63 WriteUsingDMA(new byte[] { value }); 64 } 65 66 [ConnectionRegion("dma")] WriteWordUsingDMA(long offset, ushort value)67 public void WriteWordUsingDMA(long offset, ushort value) 68 { 69 WriteUsingDMA(BitHelper.GetBytesFromValue(value, 2)); 70 } 71 72 [ConnectionRegion("dma")] WriteDoubleWordUsingDMA(long offset, uint value)73 public void WriteDoubleWordUsingDMA(long offset, uint value) 74 { 75 WriteUsingDMA(BitHelper.GetBytesFromValue(value, 4)); 76 } 77 78 [ConnectionRegion("dma")] ReadByteUsingDMA(long offset)79 public byte ReadByteUsingDMA(long offset) 80 { 81 return ReadUsingDMA(1).First(); 82 } 83 84 [ConnectionRegion("dma")] ReadWordUsingDMA(long offset)85 public ushort ReadWordUsingDMA(long offset) 86 { 87 return BitHelper.ToUInt16(ReadUsingDMA(2).ToArray(), 0, false); 88 } 89 90 [ConnectionRegion("dma")] ReadDoubleWordUsingDMA(long offset)91 public uint ReadDoubleWordUsingDMA(long offset) 92 { 93 return BitHelper.ToUInt32(ReadUsingDMA(4).ToArray(), 0, 4, false); 94 } 95 Reset()96 public override void Reset() 97 { 98 registers.Reset(); 99 Array.Clear(commandPayload, 0, commandPayload.Length); 100 auxiliaryRegisters.Reset(); 101 currentCommand = null; 102 foreach(var flag in GetAllInterruptFlags()) 103 { 104 flag.Reset(); 105 } 106 UpdateInterrupts(); 107 } 108 109 public ControllerMode Mode => controllerMode.Value; 110 111 public long Size => 0x1040; 112 113 public GPIO IRQ { get; } = new GPIO(); 114 TryGetPeripheral(int address, out ISPIPeripheral peripheral)115 internal bool TryGetPeripheral(int address, out ISPIPeripheral peripheral) 116 { 117 return TryGetByAddress(address, out peripheral); 118 } 119 TriggerCommand()120 private void TriggerCommand() 121 { 122 var previousCommand = currentCommand; 123 currentCommand = Command.CreateCommand(this, new CommandPayload(commandPayload)); 124 this.Log(LogLevel.Debug, "New command: {0}", currentCommand); 125 126 if(currentCommand == null) 127 { 128 commandIgnored.SetSticky(true); 129 } 130 else if(previousCommand != null && previousCommand.ChipSelect != currentCommand.ChipSelect && !previousCommand.TransmissionFinished) 131 { 132 this.Log(LogLevel.Error, "Triggering command with chip select different than previous one, when the previous transaction isn't finished."); 133 previousCommand.FinishTransmission(); 134 } 135 136 currentCommand?.Transmit(); 137 UpdateSticky(); 138 UpdateInterrupts(); 139 } 140 WriteUsingDMA(IReadOnlyList<byte> data)141 private void WriteUsingDMA(IReadOnlyList<byte> data) 142 { 143 if(TryGetDMACommand(TransmissionDirection.Write, out var command)) 144 { 145 command.WriteData(data); 146 UpdateSticky(); 147 UpdateInterrupts(); 148 } 149 } 150 ReadUsingDMA(int length)151 private IEnumerable<byte> ReadUsingDMA(int length) 152 { 153 if(TryGetDMACommand(TransmissionDirection.Read, out var command)) 154 { 155 var data = command.ReadData(length); 156 UpdateSticky(); 157 UpdateInterrupts(); 158 return data; 159 } 160 return new byte[length]; 161 } 162 TryGetDMACommand(TransmissionDirection accessDirection, out IDMACommand command)163 private bool TryGetDMACommand(TransmissionDirection accessDirection, out IDMACommand command) 164 { 165 command = currentCommand as IDMACommand; 166 if(command == null) 167 { 168 this.Log(LogLevel.Warning, "Trying to access data using DMA, when the latest command isn't a DMA transaction command."); 169 return false; 170 } 171 if(command.DMADirection != accessDirection) 172 { 173 this.Log(LogLevel.Warning, "Trying to access data using DMA with the wrong direction ({0}), expected {1}.", accessDirection, command.DMADirection); 174 return false; 175 } 176 return true; 177 } 178 BuildRegisterMap()179 private Dictionary<long, DoubleWordRegister> BuildRegisterMap() 180 { 181 return new Dictionary<long, DoubleWordRegister> 182 { 183 {(long)Registers.Command0, new DoubleWordRegister(this) 184 .WithValueField(0, 32, name: "command0Payload", 185 valueProviderCallback: _ => commandPayload[0], 186 writeCallback: (_, val) => commandPayload[0] = (uint)val 187 ) 188 .WithWriteCallback((_, __) => 189 { 190 if(!IsModeSupported(controllerMode.Value)) 191 { 192 this.Log(LogLevel.Warning, "Command trigger ignored, the mode {0} isn't supported.", controllerMode.Value); 193 return; 194 } 195 // Based on the Linux driver all command registers are written in sequence 196 // The command0 register are written as the last one, what triggers command execution 197 TriggerCommand(); 198 } 199 ) 200 }, 201 {(long)Registers.Command1, new DoubleWordRegister(this) 202 .WithValueField(0, 32, name: "command1Payload", 203 valueProviderCallback: _ => commandPayload[1], 204 writeCallback: (_, val) => commandPayload[1] = (uint)val 205 ) 206 }, 207 {(long)Registers.Command2, new DoubleWordRegister(this) 208 .WithValueField(0, 32, name: "command2Payload", 209 valueProviderCallback: _ => commandPayload[2], 210 writeCallback: (_, val) => commandPayload[2] = (uint)val 211 ) 212 }, 213 {(long)Registers.Command3, new DoubleWordRegister(this) 214 .WithValueField(0, 32, name: "command3Payload", 215 valueProviderCallback: _ => commandPayload[3], 216 writeCallback: (_, val) => commandPayload[3] = (uint)val 217 ) 218 }, 219 {(long)Registers.Command4, new DoubleWordRegister(this) 220 .WithValueField(0, 32, name: "command4Payload", 221 valueProviderCallback: _ => commandPayload[4], 222 writeCallback: (_, val) => commandPayload[4] = (uint)val 223 ) 224 }, 225 {(long)Registers.Command5, new DoubleWordRegister(this) 226 .WithValueField(0, 32, name: "command5Payload", 227 valueProviderCallback: _ => commandPayload[5], 228 writeCallback: (_, val) => commandPayload[5] = (uint)val 229 ) 230 }, 231 {(long)Registers.CommandStatus, new DoubleWordRegister(this) 232 .WithReservedBits(16, 16) 233 .WithFlag(15, FieldMode.Read, name: "commandCompleted", 234 valueProviderCallback: _ => currentCommand?.Completed ?? false 235 ) 236 .WithFlag(14, FieldMode.Read, name: "commandFailed", 237 valueProviderCallback: _ => currentCommand?.Failed ?? false 238 ) 239 .WithReservedBits(4, 10) 240 .WithTaggedFlag("commandDQSError", 3) 241 .WithFlag(2, FieldMode.Read, name: "commandCRCError", 242 valueProviderCallback: _ => currentCommand?.CRCError ?? false 243 ) 244 .WithFlag(1, FieldMode.Read, name: "commandBusError", 245 valueProviderCallback: _ => currentCommand?.BusError ?? false 246 ) 247 .WithFlag(0, FieldMode.Read, name: "commandInvalidCommandError", 248 valueProviderCallback: _ => currentCommand?.InvalidCommandError ?? false 249 ) 250 }, 251 {(long)Registers.ControllerStatus, new DoubleWordRegister(this) 252 .WithReservedBits(17, 15) 253 .WithFlag(16, FieldMode.Read, name: "initializationCompleted", 254 valueProviderCallback: _ => true 255 ) 256 .WithReservedBits(10, 6) 257 .WithTaggedFlag("initializationLegacy", 9) 258 .WithFlag(8, FieldMode.Read, name: "initializationFail", 259 valueProviderCallback: _ => false 260 ) 261 .WithFlag(7, FieldMode.Read, name: "controllerBusy", 262 valueProviderCallback: _ => !controllerIdle.Status 263 ) 264 .WithReservedBits(0, 7) 265 }, 266 {(long)Registers.AutoCommandStatus, new DoubleWordRegister(this) 267 .WithReservedBits(1, 31) 268 .WithFlag(0, FieldMode.Read, name: "autoCommandControllerBusy", 269 valueProviderCallback: _ => !controllerIdle.Status 270 ) 271 }, 272 {(long)Registers.InterruptStatus, new DoubleWordRegister(this) 273 .WithReservedBits(24, 8) 274 .WithFlag(23, name: "commandCompletedInterruptStatus", 275 valueProviderCallback: _ => commandCompleted.StickyStatus, 276 writeCallback: (_, val) => commandCompleted.ClearSticky(val) 277 ) 278 .WithFlag(22, name: "dmaErrorInterruptStatus", 279 valueProviderCallback: _ => dmaError.StickyStatus, 280 writeCallback: (_, val) => dmaError.ClearSticky(val) 281 ) 282 .WithFlag(21, name: "dmaTriggeredInterruptStatus", 283 valueProviderCallback: _ => dmaTriggered.StickyStatus, 284 writeCallback: (_, val) => dmaTriggered.ClearSticky(val) 285 ) 286 .WithFlag(20, name: "commandIgnoredInterruptStatus", 287 valueProviderCallback: _ => commandIgnored.StickyStatus, 288 writeCallback: (_, val) => commandIgnored.ClearSticky(val) 289 ) 290 .WithReservedBits(19, 1) 291 .WithTaggedFlag("DDMA_TERR_InterruptStatus", 18) 292 .WithTaggedFlag("DMA_TREE_InterruptStatus", 17) 293 .WithFlag(16, name: "controllerIdleInterruptStatus", 294 valueProviderCallback: _ => controllerIdle.StickyStatus, 295 writeCallback: (_, val) => controllerIdle.ClearSticky(val) 296 ) 297 .WithReservedBits(0, 16) 298 .WithWriteCallback((_, __) => UpdateInterrupts()) 299 }, 300 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 301 .WithFlag(31, out interruptsEnabled, name: "enableInterrupts") 302 .WithReservedBits(24, 7) 303 .WithFlag(23, name: "commandCompletedInterruptEnable", 304 valueProviderCallback: _ => commandCompleted.InterruptMask, 305 writeCallback: (_, val) => commandCompleted.InterruptEnable(val) 306 ) 307 .WithFlag(22, name: "dmaErrorInterruptEnable", 308 valueProviderCallback: _ => dmaError.InterruptMask, 309 writeCallback: (_, val) => dmaError.InterruptEnable(val) 310 ) 311 .WithFlag(21, name: "dmaTriggeredInterruptEnable", 312 valueProviderCallback: _ => dmaTriggered.InterruptMask, 313 writeCallback: (_, val) => dmaTriggered.InterruptEnable(val) 314 ) 315 .WithFlag(20, name: "commandIgnoredInterruptEnable", 316 valueProviderCallback: _ => commandIgnored.InterruptMask, 317 writeCallback: (_, val) => commandIgnored.InterruptEnable(val) 318 ) 319 .WithReservedBits(19, 1) 320 .WithTaggedFlag("DDMA_TERR_InterruptEnable", 18) 321 .WithTaggedFlag("DMA_TREE_InterruptEnable", 17) 322 .WithFlag(16, name: "controllerIdleInterruptEnable", 323 valueProviderCallback: _ => controllerIdle.InterruptMask, 324 writeCallback: (_, val) => controllerIdle.InterruptEnable(val) 325 ) 326 .WithReservedBits(0, 16) 327 .WithWriteCallback((_, __) => UpdateInterrupts()) 328 }, 329 {(long)Registers.AutoCommandCompleteInterruptStatus, new DoubleWordRegister(this) 330 .WithReservedBits(1, 31) 331 .WithFlag(0, name: "autoCommandCompletedInterruptStatus", 332 valueProviderCallback: _ => autoCommandCompleted.StickyStatus, 333 writeCallback: (_, val) => autoCommandCompleted.ClearSticky(val) 334 ) 335 .WithWriteCallback((_, __) => UpdateInterrupts()) 336 }, 337 {(long)Registers.ControllerConfig, new DoubleWordRegister(this) 338 .WithReservedBits(7, 25) 339 .WithEnumField(5, 2, out controllerMode, name: "controllerMode", 340 changeCallback: (_, val) => 341 { 342 if(!IsModeSupported(val)) 343 { 344 this.Log(LogLevel.Warning, "Setting the controller mode to one which isn't supported ({0}).", val); 345 } 346 } 347 ) 348 .WithReservedBits(0, 5) 349 }, 350 {(long)Registers.DMASize, new DoubleWordRegister(this) 351 .WithValueField(0, 32, FieldMode.Read, name: "DMASize", 352 valueProviderCallback: _ => (currentCommand as IDMACommand)?.DMADataCount ?? 0 353 ) 354 }, 355 {(long)Registers.DMAStatus, new DoubleWordRegister(this) 356 .WithReservedBits(9, 23) 357 .WithEnumField<DoubleWordRegister, TransmissionDirection>(8, 1, FieldMode.Read, name: "DMADirection", 358 valueProviderCallback: _ => (currentCommand as IDMACommand)?.DMADirection ?? default(TransmissionDirection) 359 ) 360 .WithReservedBits(0, 8) 361 }, 362 {(long)Registers.ControllerVersion, new DoubleWordRegister(this) 363 .WithValueField(0, 8, FieldMode.Read, name: "hardwareRevision", 364 valueProviderCallback: _ => HardwareRevision 365 ) 366 .WithReservedBits(8, 8) 367 .WithValueField(16, 16, FieldMode.Read, name: "hardwareMagicNumber", 368 valueProviderCallback: _ => HardwareMagicNumber 369 ) 370 }, 371 {(long)Registers.ControllerFeatures, new DoubleWordRegister(this) 372 .WithReservedBits(26, 6) 373 .WithTag("banksCount", 24, 2) 374 .WithReservedBits(22, 2) 375 .WithTaggedFlag("dmaDataWidth", 21) 376 .WithReservedBits(4, 17) 377 .WithTag("threadsCount", 0, 4) 378 } 379 }; 380 } 381 IsModeSupported(ControllerMode mode)382 private bool IsModeSupported(ControllerMode mode) 383 { 384 return mode == ControllerMode.SoftwareTriggeredInstructionGenerator || mode == ControllerMode.AutoCommand; 385 } 386 UpdateSticky()387 private void UpdateSticky() 388 { 389 foreach(var flag in GetAllInterruptFlags()) 390 { 391 flag.UpdateStickyStatus(); 392 } 393 } 394 UpdateInterrupts()395 private void UpdateInterrupts() 396 { 397 IRQ.Set(interruptsEnabled.Value && GetAllInterruptFlags().Any(x => x.InterruptStatus)); 398 } 399 GetControllerInterruptFlags()400 private IEnumerable<CadenceInterruptFlag> GetControllerInterruptFlags() 401 { 402 yield return controllerIdle; 403 yield return commandCompleted; 404 yield return commandIgnored; 405 yield return dmaTriggered; 406 yield return dmaError; 407 } 408 GetAutoCommandInterruptFlags()409 private IEnumerable<CadenceInterruptFlag> GetAutoCommandInterruptFlags() 410 { 411 yield return autoCommandCompleted; 412 } 413 GetAllInterruptFlags()414 private IEnumerable<CadenceInterruptFlag> GetAllInterruptFlags() 415 { 416 return GetControllerInterruptFlags().Concat(GetAutoCommandInterruptFlags()); 417 } 418 419 private Command currentCommand; 420 421 private IFlagRegisterField interruptsEnabled; 422 private IEnumRegisterField<ControllerMode> controllerMode; 423 424 // Command registers have different fields at same offset depending on the command type 425 // The commandPayload array contains all command registers values 426 // It's passed to the Command class constructor and decoded 427 private readonly uint[] commandPayload = new uint[6]; 428 429 private readonly CadenceInterruptFlag controllerIdle; 430 private readonly CadenceInterruptFlag commandCompleted; 431 private readonly CadenceInterruptFlag commandIgnored; 432 private readonly CadenceInterruptFlag dmaTriggered; 433 private readonly CadenceInterruptFlag dmaError; 434 private readonly CadenceInterruptFlag autoCommandCompleted; 435 436 private readonly DoubleWordRegisterCollection registers; 437 private readonly DoubleWordRegisterCollection auxiliaryRegisters; 438 439 private const uint HardwareMagicNumber = 0x6522; 440 private const uint HardwareRevision = 0x0; 441 442 public enum ControllerMode 443 { 444 Direct = 0x0, 445 SoftwareTriggeredInstructionGenerator = 0x1, 446 // Based on the Linux driver there is no mode for the 0x2 value 447 AutoCommand = 0x3 448 } 449 450 public enum TransmissionDirection 451 { 452 Read = 0x0, 453 Write = 0x1 454 } 455 456 private enum Registers : long 457 { 458 Command0 = 0x0000, 459 Command1 = 0x0004, 460 Command2 = 0x0008, 461 Command3 = 0x000c, 462 Command4 = 0x0010, 463 Command5 = 0x0014, 464 CommandStatusPointer = 0x0040, 465 CommandStatus = 0x0044, 466 ControllerStatus = 0x0100, 467 AutoCommandStatus = 0x0104, 468 InterruptStatus = 0x0110, 469 InterruptEnable = 0x0114, 470 AutoCommandCompleteInterruptStatus = 0x0120, 471 AutoCommandErrorInterruptStatus = 0x0130, 472 AutoCommandErrorInterruptEnable = 0x0134, 473 ControllerConfig = 0x0230, 474 DMASize = 0x0240, 475 DMAStatus = 0x0244, 476 ControllerVersion = 0x0f00, 477 ControllerFeatures = 0x0f04, 478 DLLControl = 0x1034, 479 } 480 481 private enum AuxiliaryRegisters : long 482 { 483 DQTiming = 0x0000, 484 DQSTiming = 0x0004, 485 GateLoopbackControl = 0x0008, 486 DLLSlaveControl = 0x0010, 487 } 488 } 489 } 490