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 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.Utilities; 15 16 namespace Antmicro.Renode.Peripherals.SPI 17 { 18 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 19 public class OpenTitan_SpiHost: SimpleContainer<ISPIPeripheral>, IWordPeripheral, IBytePeripheral, IDoubleWordPeripheral, IKnownSize 20 { OpenTitan_SpiHost(IMachine machine, int numberOfCSLines)21 public OpenTitan_SpiHost(IMachine machine, int numberOfCSLines) : base(machine) 22 { 23 this.numberOfCSLines = numberOfCSLines; 24 25 DefineRegisters(); 26 Error = new GPIO(); 27 SpiEvent = new GPIO(); 28 FatalAlert = new GPIO(); 29 30 txFifo = new Queue<byte>(); 31 rxFifo = new Queue<byte>(); 32 cmdFifo = new Queue<CommandDefinition>(); 33 34 Reset(); 35 } 36 Reset()37 public override void Reset() 38 { 39 txFifo.Clear(); 40 rxFifo.Clear(); 41 cmdFifo.Clear(); 42 selectedSlave = null; 43 readyFlag.Value = true; 44 } 45 ReadDoubleWord(long addr)46 public uint ReadDoubleWord(long addr) 47 { 48 return RegistersCollection.Read(addr); 49 } 50 WriteDoubleWord(long addr, uint val)51 public void WriteDoubleWord(long addr, uint val) 52 { 53 if(addr == (long)Registers.Txdata) 54 { 55 EnqueueTx(val, sizeof(uint)); 56 return; 57 } 58 RegistersCollection.Write(addr, val); 59 } 60 61 // Below methods are meeded just to make sure we don't enqueue too much tx bytes on write. ReadWord(long offset)62 public ushort ReadWord(long offset) 63 { 64 return (ushort)RegistersCollection.Read(offset); 65 } 66 WriteWord(long addr, ushort val)67 public void WriteWord(long addr, ushort val) 68 { 69 if(addr == (long)Registers.Txdata) 70 { 71 EnqueueTx(val, sizeof(ushort)); 72 return; 73 } 74 RegistersCollection.Write(addr, val); 75 } 76 WriteByte(long addr, byte val)77 public void WriteByte(long addr, byte val) 78 { 79 if(addr == (long)Registers.Txdata) 80 { 81 EnqueueTx(val, sizeof(byte)); 82 return; 83 } 84 RegistersCollection.Write(addr, val); 85 } 86 ReadByte(long offset)87 public byte ReadByte(long offset) 88 { 89 return (byte)RegistersCollection.Read(offset); 90 } 91 92 public long Size => 0x1000; 93 94 // Common Interrupt Offsets 95 public GPIO Error { get; } 96 public GPIO SpiEvent { get; } 97 98 // Alerts 99 public GPIO FatalAlert { get; } 100 101 public DoubleWordRegisterCollection RegistersCollection { get; private set; } 102 DefineRegisters()103 private void DefineRegisters() 104 { 105 var registerDictionary = new Dictionary<long, DoubleWordRegister> 106 {{(long)Registers.InterruptState, new DoubleWordRegister(this) 107 .WithFlag(0, out errorInterruptTriggered, FieldMode.Read | FieldMode.WriteOneToClear, name: "error") 108 .WithFlag(1, out spiEventInterruptTriggered, FieldMode.Read | FieldMode.WriteOneToClear, name: "spi_event") 109 .WithReservedBits(2, 30) 110 .WithWriteCallback((_, __) => UpdateInterrupts()) 111 }, 112 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 113 .WithFlag(0, out errorInterruptEnabled, name: "error") 114 .WithFlag(1, out spiEventInterruptEnabled, name: "spi_event") 115 .WithReservedBits(2, 30) 116 .WithWriteCallback((_, __) => UpdateInterrupts()) 117 }, 118 {(long)Registers.InterruptTest, new DoubleWordRegister(this) 119 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) errorInterruptTriggered.Value = true; }, name: "error") 120 .WithFlag(1, FieldMode.Write, writeCallback: (_, val) => { if(val) spiEventInterruptTriggered.Value = true; }, name: "spi_event") 121 .WithReservedBits(2, 30) 122 .WithWriteCallback((_, __) => UpdateInterrupts()) 123 }, 124 {(long)Registers.AlertTest, new DoubleWordRegister(this) 125 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => {if(val) FatalAlert.Blink();}, name: "fatal_fault") 126 .WithReservedBits(1, 31) 127 }, 128 {(long)Registers.Control, new DoubleWordRegister(this, 0x7f) 129 .WithValueField(0, 8, out rxWatermarkInDoublewords, name: "RX_WATERMARK") 130 .WithValueField(8, 8, out txWatermarkInDoublewords, name: "TX_WATERMARK") 131 .WithReservedBits(16, 13) 132 .WithFlag(29, out outputEnabled, name: "OUTPUT_EN") 133 .WithFlag(30, writeCallback: (_, val) => { if(val) Reset(); }, name: "SW_RST") 134 .WithFlag(31, out enabled, name: "SPIEN") 135 .WithChangeCallback((_, __) => 136 { 137 this.DebugLog("Control set to 'enabled': {0}, 'outputEnabled': {1}, rxWatermark = {2}[doublewords], txWatermark = {3}[doublewords]", enabled.Value, outputEnabled.Value, rxWatermarkInDoublewords.Value, txWatermarkInDoublewords.Value); 138 if(enabled.Value && outputEnabled.Value) 139 { 140 ExecuteCommands(); 141 } 142 }) 143 }, 144 {(long)Registers.Status, new DoubleWordRegister(this) 145 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => txCountInDoubleWords, name: "TXQD") 146 .WithValueField(8, 8, FieldMode.Read, valueProviderCallback: _ => rxCountInDoubleWords, name: "RXQD") 147 .WithValueField(16, 4, FieldMode.Read, valueProviderCallback: _ => cmdCountInDoubleWords, name: "CMDQD") 148 .WithFlag(20, out rxWatermarkEventTriggered, FieldMode.Read, name: "RXWM") 149 .WithReservedBits(21, 1) 150 .WithTaggedFlag("BYTEORDER", 22) 151 .WithTaggedFlag("RXSTALL", 23) 152 .WithFlag(24, FieldMode.Read, valueProviderCallback: _ => rxFifo.Count == 0, name: "RXEMPTY") 153 .WithFlag(25, out rxFullEventTriggered, FieldMode.Read, name: "RXFULL") 154 .WithFlag(26, out txWatermarkEventTriggered, FieldMode.Read, name: "TXWM") 155 .WithTaggedFlag("TXSTALL", 27) 156 .WithFlag(28, out txEmptyEventTriggered, FieldMode.Read, name: "TXEMPTY") 157 .WithFlag(29, FieldMode.Read, valueProviderCallback: _ => txFifo.Count == SpiHostTxDepth, name: "TXFULL") 158 .WithFlag(30, out active, FieldMode.Read, name: "ACTIVE") 159 .WithFlag(31, out readyFlag, FieldMode.Read, name: "READY") 160 }, 161 {(long)Registers.Configopts, new DoubleWordRegister(this) 162 .WithTag("CLKDIV_0", 0, 16) 163 .WithTag("CSNIDLE_0", 16, 4) 164 .WithTag("CSNTRAIL_0", 20, 4) 165 .WithTag("CSNLEAD_0", 24, 4) 166 .WithTaggedFlag("FULLCYC_0", 29) 167 .WithTaggedFlag("CPHA_0", 30) 168 .WithTaggedFlag("CPOL_0", 31) 169 }, 170 {(long)Registers.ChipSelectID, new DoubleWordRegister(this) 171 .WithValueField(0, 32, writeCallback: (_, val) => 172 { 173 if((long)val >= numberOfCSLines) 174 { 175 this.Log(LogLevel.Warning, "Tried to set CS id out of range: {0}", val); 176 csIdInvalidErrorTriggered.Value = true; 177 UpdateEvents(); 178 return; 179 } 180 181 if(!TryGetByAddress((int)val, out selectedSlave)) 182 { 183 this.Log(LogLevel.Warning, "No device connected with the ID {0}", val); 184 return; 185 } 186 187 this.DebugLog("The device address set to {0}", val); 188 } 189 , name: "CSID") 190 }, 191 {(long)Registers.Command, new DoubleWordRegister(this) 192 .WithValueField(0, 9, out var commandLength, FieldMode.Write, name: "LEN") 193 .WithFlag(9, out var keepChipSelect, name: "CSAAT") 194 .WithEnumField<DoubleWordRegister, CommandSpeed>(10, 2, out var commandSpeed, FieldMode.Write, name: "SPEED") 195 .WithEnumField<DoubleWordRegister, CommandDirection>(12, 2, out var commandDirection, FieldMode.Write, name: "DIRECTION") 196 .WithReservedBits(14, 18) 197 .WithWriteCallback((_, __) => EnqueueCommand((uint)commandLength.Value, commandDirection.Value, commandSpeed.Value, keepChipSelect.Value)) 198 }, 199 {(long)Registers.Rxdata, new DoubleWordRegister(this) 200 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => RxDequeueAsUInt(), name: "SPIReceiveData") 201 }, 202 {(long)Registers.Txdata, new DoubleWordRegister(this) 203 // This is already implemented in the WriteDoubleWord/WriteWord/WriteByte 204 .WithTag("SPITransmitData", 0, 32) 205 }, 206 {(long)Registers.ErrorEnable, new DoubleWordRegister(this, 0x1f) 207 .WithTaggedFlag("CMDBUSY", 0) 208 .WithFlag(1, out txOverflowErrorEnabled, name: "OVERFLOW") 209 .WithFlag(2, out rxUnderflowErrorEnabled, name: "UNDERFLOW") 210 .WithFlag(3, out commandInvalidErrorEnabled, name: "CMDINVAL") 211 .WithFlag(4, out csIdInvalidErrorEnabled, name: "CSIDINVAL") 212 .WithReservedBits(5, 27) 213 .WithWriteCallback((_,__) => UpdateErrors()) 214 }, 215 {(long)Registers.ErrorStatus, new DoubleWordRegister(this) 216 .WithFlag(0, FieldMode.Read | FieldMode.WriteOneToClear, name: "CMDBUSY") 217 .WithFlag(1, out txOverflowErrorTriggered, FieldMode.Read | FieldMode.WriteOneToClear, name: "OVERFLOW") 218 .WithFlag(2, out rxUnderflowErrorTriggred, FieldMode.Read | FieldMode.WriteOneToClear, name: "UNDERFLOW") 219 .WithFlag(3, out commandInvalidErrorTriggered, FieldMode.Read | FieldMode.WriteOneToClear, name: "CMDINVAL") 220 .WithFlag(4, out csIdInvalidErrorTriggered, FieldMode.Read | FieldMode.WriteOneToClear, name: "CSIDINVAL") 221 .WithTaggedFlag("ACCESSINVAL", 5) 222 .WithReservedBits(6, 26) 223 .WithWriteCallback((_,__) => UpdateErrors()) 224 }, 225 {(long)Registers.EventEnable, new DoubleWordRegister(this) 226 .WithFlag(0, out rxFullEventEnabled, name: "RXFULL") 227 .WithFlag(1, out txEmptyEventEnabled, name: "TXEMPTY") 228 .WithFlag(2, out rxWatermarkEventEnabled, name: "RXWM") 229 .WithFlag(3, out txWatermarkEventEnabled, name: "TXWM") 230 .WithFlag(4, out readyEventEnabled, name: "READY") 231 .WithFlag(5, out idleEventEnabled, name: "IDLE") 232 .WithReservedBits(6, 26) 233 .WithWriteCallback((_,__) => UpdateEvents()) 234 }}; 235 RegistersCollection = new DoubleWordRegisterCollection(this, registerDictionary); 236 } 237 UpdateInterrupts()238 private void UpdateInterrupts() 239 { 240 Error.Set(errorInterruptEnabled.Value & errorInterruptTriggered.Value); 241 SpiEvent.Set(spiEventInterruptEnabled.Value & spiEventInterruptTriggered.Value); 242 } 243 UpdateEvents()244 private void UpdateEvents() 245 { 246 spiEventInterruptTriggered.Value = 247 (rxFullEventTriggered.Value && rxFullEventEnabled.Value) || 248 (txEmptyEventTriggered.Value && txEmptyEventEnabled.Value) || 249 (txWatermarkEventTriggered.Value && txWatermarkEventEnabled.Value) || 250 (rxWatermarkEventTriggered.Value && rxWatermarkEventEnabled.Value) || 251 (readyFlag.Value && readyEventEnabled.Value) || 252 (!active.Value && idleEventEnabled.Value); 253 254 UpdateInterrupts(); 255 } 256 UpdateErrors()257 private void UpdateErrors() 258 { 259 // There should be also an CMDBUSY error, but there is no need as our peripheral will never be busy 260 errorInterruptTriggered.Value = 261 (txOverflowErrorTriggered.Value && txOverflowErrorEnabled.Value) || 262 (rxUnderflowErrorTriggred.Value && rxUnderflowErrorEnabled.Value) || 263 (commandInvalidErrorTriggered.Value && commandInvalidErrorEnabled.Value) || 264 (csIdInvalidErrorTriggered.Value && csIdInvalidErrorEnabled.Value); 265 266 UpdateInterrupts(); 267 } 268 ExecuteCommands()269 private void ExecuteCommands() 270 { 271 this.NoisyLog("Executing queued commands"); 272 while(cmdFifo.TryDequeue(out var x)) 273 { 274 HandleCommand(x); 275 } 276 readyFlag.Value = true; 277 active.Value = false; 278 UpdateEvents(); 279 } 280 EnqueueCommand(uint length, CommandDirection direction, CommandSpeed speed, bool keepChipSelect)281 private void EnqueueCommand(uint length, CommandDirection direction, CommandSpeed speed, bool keepChipSelect) 282 { 283 if((direction == CommandDirection.TxRx && speed != CommandSpeed.Standard) || 284 speed == CommandSpeed.Reserved) 285 { 286 commandInvalidErrorTriggered.Value = true; 287 UpdateErrors(); 288 return; 289 } 290 291 cmdFifo.Enqueue(new CommandDefinition(length, direction, keepChipSelect)); 292 if(outputEnabled.Value && enabled.Value) 293 { 294 ExecuteCommands(); 295 } 296 } 297 EnqueueTx(uint val, int accessSize)298 private void EnqueueTx(uint val, int accessSize) 299 { 300 var asBytes = Misc.AsBytes(new uint[] { val }); 301 for(int i = 0; i < accessSize; i++) 302 { 303 if(txFifo.Count < SpiHostTxDepth) 304 { 305 txFifo.Enqueue(asBytes[i]); 306 } 307 else 308 { 309 txOverflowErrorTriggered.Value = true; 310 break; 311 } 312 } 313 this.Log(LogLevel.Noisy, "Enqueued {0} Tx bytes, current fifo depth in words: {1}", accessSize, txCountInWords); 314 315 txWatermarkEventTriggered.Value = (txCountInDoubleWords < txWatermarkInDoublewords.Value); 316 UpdateEvents(); 317 } 318 EnqueueRx(byte val)319 private void EnqueueRx(byte val) 320 { 321 if(rxFifo.Count < SpiHostRxDepth) 322 { 323 rxFifo.Enqueue(val); 324 } 325 else 326 { 327 rxFullEventTriggered.Value = true; 328 } 329 330 rxWatermarkEventTriggered.Value = (rxCountInDoubleWords >= rxWatermarkInDoublewords.Value); 331 332 UpdateEvents(); 333 } 334 RxDequeueAsUInt()335 private uint RxDequeueAsUInt() 336 { 337 uint output = 0; 338 var count = Math.Min(sizeof(uint), rxFifo.Count); 339 for(int i = 0; i < count; i++) 340 { 341 output |= (uint)(rxFifo.Dequeue() << (i * 8)); 342 } 343 return output; 344 } 345 HandleCommand(CommandDefinition command)346 private void HandleCommand(CommandDefinition command) 347 { 348 if(command.Direction == CommandDirection.Dummy) 349 { 350 return; 351 } 352 353 // number of bytes to transfer is equal to `COMMAND.LEN + 1` 354 for(var i = 0; i <= command.Length; i++) 355 { 356 var byteToTransfer = (byte)0; 357 if(command.Direction == CommandDirection.TxOnly || command.Direction == CommandDirection.TxRx) 358 { 359 if(!Misc.TryDequeue(txFifo, out byteToTransfer)) 360 { 361 this.Log(LogLevel.Warning, "Tx Fifo empty, transmitting 0 instead"); 362 byteToTransfer = 0; 363 } 364 } 365 366 byte response; 367 if(selectedSlave != null) 368 { 369 this.NoisyLog("Transferring byte {0:x}", byteToTransfer); 370 response = selectedSlave.Transmit(byteToTransfer); 371 this.NoisyLog("Received byte {0:x}", response); 372 } 373 else 374 { 375 response = 0xff; 376 this.NoisyLog("No target device is available, returning dummy byte {0:x}", response); 377 } 378 379 if(command.Direction == CommandDirection.RxOnly || command.Direction == CommandDirection.TxRx) 380 { 381 EnqueueRx(response); 382 } 383 } 384 385 if(!command.KeepChipSelect) 386 { 387 this.Log(LogLevel.Debug, "Finished the transmission. Current fifo depth in words: {0}", rxCountInWords); 388 selectedSlave?.FinishTransmission(); 389 } 390 } 391 392 private IFlagRegisterField errorInterruptEnabled; 393 private IFlagRegisterField spiEventInterruptEnabled; 394 private IFlagRegisterField errorInterruptTriggered; 395 private IFlagRegisterField spiEventInterruptTriggered; 396 397 private IFlagRegisterField readyFlag; 398 private IFlagRegisterField active; 399 private IFlagRegisterField enabled; 400 private IFlagRegisterField outputEnabled; 401 402 private IValueRegisterField txWatermarkInDoublewords; 403 private IValueRegisterField rxWatermarkInDoublewords; 404 405 private ISPIPeripheral selectedSlave; 406 407 private IFlagRegisterField txEmptyEventTriggered; 408 private IFlagRegisterField rxFullEventTriggered; 409 private IFlagRegisterField txWatermarkEventTriggered; 410 private IFlagRegisterField rxWatermarkEventTriggered; 411 private IFlagRegisterField txOverflowErrorTriggered; 412 private IFlagRegisterField rxUnderflowErrorTriggred; 413 private IFlagRegisterField commandInvalidErrorTriggered; 414 private IFlagRegisterField csIdInvalidErrorTriggered; 415 private IFlagRegisterField rxFullEventEnabled; 416 private IFlagRegisterField txEmptyEventEnabled; 417 private IFlagRegisterField txWatermarkEventEnabled; 418 private IFlagRegisterField rxWatermarkEventEnabled; 419 private IFlagRegisterField readyEventEnabled; 420 private IFlagRegisterField idleEventEnabled; 421 private IFlagRegisterField txOverflowErrorEnabled; 422 private IFlagRegisterField rxUnderflowErrorEnabled; 423 private IFlagRegisterField commandInvalidErrorEnabled; 424 private IFlagRegisterField csIdInvalidErrorEnabled; 425 426 private readonly Queue<CommandDefinition> cmdFifo; 427 private readonly Queue<byte> txFifo; 428 private readonly Queue<byte> rxFifo; 429 private readonly int numberOfCSLines; 430 431 // The size of the Tx FIFO (in bytes) 432 private const uint SpiHostTxDepth = 288; 433 434 // The size of the Rx FIFO (in bytes) 435 private const uint SpiHostRxDepth = 256; 436 437 // The size of the Cmd FIFO (one segment descriptor per entry) 438 private const uint SpiHostCmdDepth = 4; 439 440 private uint txCountInWords => (uint)txFifo.Count / 2; 441 private uint rxCountInWords => (uint)rxFifo.Count / 2; 442 private uint cmdCountInWords => (uint)cmdFifo.Count / 2; 443 444 private uint txCountInDoubleWords => (uint)txFifo.Count / 4; 445 private uint rxCountInDoubleWords => (uint)rxFifo.Count / 4; 446 private uint cmdCountInDoubleWords => (uint)cmdFifo.Count / 4; 447 448 private enum CommandDirection 449 { 450 Dummy = 0, 451 RxOnly = 1, 452 TxOnly = 2, 453 TxRx = 3, 454 } 455 456 private enum CommandSpeed 457 { 458 Standard = 0, 459 Dual = 1, 460 Quad = 2, 461 Reserved = 3, 462 } 463 464 public enum Registers 465 { 466 InterruptState = 0x0, 467 InterruptEnable = 0x4, 468 InterruptTest = 0x8, 469 AlertTest = 0xc, 470 Control = 0x10, 471 Status = 0x14, 472 Configopts = 0x18, 473 ChipSelectID = 0x1c, 474 Command = 0x20, 475 Rxdata = 0x24, 476 Txdata = 0x28, 477 ErrorEnable = 0x2c, 478 ErrorStatus = 0x30, 479 EventEnable = 0x34, 480 } 481 482 private struct CommandDefinition 483 { CommandDefinitionAntmicro.Renode.Peripherals.SPI.OpenTitan_SpiHost.CommandDefinition484 public CommandDefinition(uint length, CommandDirection direction, bool keepChipSelect) 485 { 486 this.Length = length; 487 this.Direction = direction; 488 this.KeepChipSelect = keepChipSelect; 489 } 490 491 public uint Length { get; } 492 public CommandDirection Direction { get; } 493 public bool KeepChipSelect { get; } 494 } 495 } // End class OpenTitan_SpiHost 496 } // End of namespace 497