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.Collections.Generic; 8 using Antmicro.Renode.Core; 9 using Antmicro.Renode.Core.Structure; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Exceptions; 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 public class MPFS_SPI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize, ISPIPeripheral 19 { MPFS_SPI(IMachine machine)20 public MPFS_SPI(IMachine machine) : base(machine) 21 { 22 locker = new object(); 23 receiveBuffer = new Queue<byte>(); 24 transmitBuffer = new Queue<byte>(); 25 IRQ = new GPIO(); 26 27 controlRegister = new DoubleWordRegister(this, 0x80000102) 28 .WithFlag(0, out coreEnabled, 29 changeCallback: (_, val) => 30 { 31 if(!val) 32 { 33 frameCounterLimit.Value = 0; 34 } 35 }, name: "ENABLE") 36 .WithFlag(1, out master, name: "MASTER") 37 .WithTag("MODE", 2, 2) 38 .WithFlag(4, out enableIrqOnReceive, name: "INTRXDATA") 39 .WithFlag(5, out enableIrqOnTransmit, name: "INTTXDATA") 40 .WithFlag(6, out enableIrqOnOverflow, name: "INTRCVOVFLOW") 41 .WithFlag(7, out enableIrqOnUnderrun, name: "INTTXTURUN") 42 .WithValueField(8, 16, out frameCounterLimit, 43 writeCallback: (_, val) => 44 { 45 framesReceived = 0; 46 framesTransmitted = 0; 47 }, name: "FRAMECNT") 48 .WithTag("SPO", 24, 1) 49 .WithTag("SPH", 25, 1) 50 .WithTag("SPS", 26, 1) 51 .WithTag("FRAMEURUN", 27, 1) 52 .WithTag("CLKMODE", 28, 1) 53 .WithFlag(29, 54 changeCallback: (_, val) => 55 { 56 if(val) 57 { 58 if(frameSize.Value <= 8) 59 { 60 fifoSize = 32; 61 } 62 else if(frameSize.Value >= 9 && frameSize.Value <= 16) 63 { 64 fifoSize = 16; 65 } 66 else if(frameSize.Value >= 17) 67 { 68 fifoSize = 8; 69 } 70 } 71 else 72 { 73 fifoSize = 4; 74 } 75 }, name: "BIGFIFO") 76 .WithTag("OENOFF", 30, 1) 77 .WithFlag(31, 78 writeCallback: (_, val) => 79 { 80 if(val) 81 { 82 Reset(); 83 } 84 }, name: "RESET"); 85 86 var registersMap = new Dictionary<long, DoubleWordRegister> 87 { 88 {(long)Registers.Control, controlRegister}, 89 90 {(long)Registers.FrameSize, new DoubleWordRegister(this, 0x4) 91 .WithValueField(0, 6, out frameSize, name: "FRAMESIZE") 92 }, 93 94 {(long)Registers.Status, new DoubleWordRegister(this, 0x2440) 95 .WithFlag(0, out dataSent, FieldMode.Read, name: "TXDATASENT") 96 .WithFlag(1, out dataReceived, FieldMode.Read, name: "RXDATARCVD") 97 .WithFlag(2, FieldMode.Read, valueProviderCallback: (_) => receiveOverflow.Value, name: "RXOVERFLOW") 98 .WithFlag(3, FieldMode.Read, valueProviderCallback: (_) => transmitUnderrun.Value, name: "TXUNDERRUN") 99 .WithFlag(4, FieldMode.Read, valueProviderCallback: (_) => receiveBuffer.Count == fifoSize, name: "RXFIFOFULL") 100 .WithFlag(5, FieldMode.Read, valueProviderCallback: (_) => (receiveBuffer.Count + 1) == fifoSize, name: "RXFIFOFULLNEXT") 101 .WithFlag(6, FieldMode.Read, valueProviderCallback: (_) => receiveBuffer.Count == 0, name: "RXFIFOEMPTY") 102 .WithFlag(7, FieldMode.Read, valueProviderCallback: (_) => receiveBuffer.Count == 1, name: "RXFIFOEMPTYNEXT") 103 .WithFlag(8, FieldMode.Read, valueProviderCallback: (_) => transmitBuffer.Count == fifoSize, name: "TXFIFOFULL") 104 .WithFlag(9, FieldMode.Read, valueProviderCallback: (_) => (transmitBuffer.Count + 1) == fifoSize, name: "TXFIFOFULLNEXT") 105 .WithFlag(10, FieldMode.Read, valueProviderCallback: (_) => transmitBuffer.Count == 0, name: "TXFIFOEMPTY") 106 .WithFlag(11, FieldMode.Read, valueProviderCallback: (_) => transmitBuffer.Count == 1, name: "TXFIFOEMPTYNEXT") 107 .WithTag("FRAMESTART", 12, 1) 108 .WithTag("SSEL", 13, 1) 109 .WithTag("ACTIVE", 14, 1) 110 }, 111 112 {(long)Registers.InterruptClear, new DoubleWordRegister(this) 113 .WithFlag(0, FieldMode.WriteOneToClear, 114 writeCallback: (_, val) => 115 { 116 if(val) 117 { 118 transmitDone.Value = false; 119 } 120 }, name: "TXDONE") 121 .WithFlag(1, FieldMode.WriteOneToClear, 122 writeCallback: (_, val) => 123 { 124 if(val) 125 { 126 receiveDone.Value = false; 127 } 128 }, name: "RXDONE") 129 .WithFlag(2, FieldMode.WriteOneToClear, 130 writeCallback: (_, val) => 131 { 132 if(val) 133 { 134 receiveOverflow.Value = false; 135 } 136 }, name: "RXOVERFLOW") 137 .WithFlag(3, FieldMode.WriteOneToClear, 138 writeCallback: (_, val) => 139 { 140 if(val) 141 { 142 transmitUnderrun.Value = false; 143 } 144 }, name: "TXUNDERRUN") 145 .WithFlag(4, FieldMode.WriteOneToClear, 146 writeCallback: (_, val) => 147 { 148 if(val) 149 { 150 fullCommandReceived.Value = false; 151 } 152 }, name: "CMDINT") 153 .WithFlag(5, FieldMode.WriteOneToClear, 154 writeCallback: (_, val) => 155 { 156 if(val) 157 { 158 slaveSelectGoneInactve.Value = false; 159 } 160 }, name: "SSEND") 161 }, 162 163 {(long)Registers.ReceiveData, new DoubleWordRegister(this) 164 .WithValueField(0, 32, FieldMode.Read, 165 valueProviderCallback: _ => 166 { 167 dataReceived.Value = false; 168 return receiveBuffer.Count > 0 ? receiveBuffer.Dequeue() : (byte)0x00; 169 }, name: "RXDATA") 170 }, 171 172 {(long)Registers.TransmitData, new DoubleWordRegister(this) 173 .WithValueField(0, 32, FieldMode.Write, 174 writeCallback: (_, val) => 175 { 176 if(coreEnabled.Value) 177 { 178 dataSent.Value = false; 179 if(master.Value == true) 180 { 181 this.Log(LogLevel.Noisy, $"Writing 0x{val:X} to slave."); 182 //TODO: should probably enqueue data if frame counter is enabled and the frameTransmitted reached the threshold. 183 // We do not handle TXFIFO as its handling is not clear from the documentation. 184 TryReceive(RegisteredPeripheral.Transmit((byte)val)); 185 if(framesReceived + framesTransmitted == (int)frameCounterLimit.Value) 186 { 187 dataSent.Value = true; 188 transmitDone.Value = true; 189 if(slaveSelect.Value > 0) 190 { 191 RegisteredPeripheral.FinishTransmission(); 192 } 193 } 194 } 195 else 196 { 197 this.Log(LogLevel.Noisy, $"Writing 0x{val:X} to transmit buffer in slave mode."); 198 transmitBuffer.Enqueue((byte)val); 199 } 200 } 201 }, name: "TXDATA") 202 }, 203 204 {(long)Registers.ClockRate, new DoubleWordRegister(this) 205 .WithValueField(0, 8, name: "CLKRATE") 206 }, 207 208 {(long)Registers.SlaveSelect, new DoubleWordRegister(this) 209 .WithValueField(0, 8, out slaveSelect, 210 writeCallback: (_, val) => 211 { 212 if(val > 0) 213 { 214 slaveSelect.Value = val; 215 framesReceived = 0; 216 framesTransmitted = 0; 217 } 218 else if(val == 0 && slaveSelect.Value > 0) 219 { 220 slaveSelect.Value = 0; 221 RegisteredPeripheral.FinishTransmission(); 222 } 223 }, name: "SSEL") 224 }, 225 226 {(long)Registers.InterruptMasked, new DoubleWordRegister(this) 227 .WithValueField(0, 6, valueProviderCallback: _ => CalculateMaskedInterruptValue()) 228 }, 229 230 {(long)Registers.InterruptRaw, new DoubleWordRegister(this) 231 .WithFlag(0, out transmitDone, name: "TXDONE") 232 .WithFlag(1, out receiveDone, name: "RXDONE") 233 .WithFlag(2, out receiveOverflow, name: "RXOVERFLOW") 234 .WithFlag(3, out transmitUnderrun, name: "TXUNDERRUN") 235 .WithFlag(4, out fullCommandReceived, name: "CMDINT") 236 .WithFlag(5, out slaveSelectGoneInactve, name: "SSEND") 237 }, 238 239 {(long)Registers.ControlBitsForEnhancedModes, new DoubleWordRegister(this) 240 .WithTag("AUTOSTATUS", 0, 1) 241 .WithTag("AUTOPOLL", 1, 1) 242 .WithFlag(2, out disableFrameCount, name: "DISFRMCNT") 243 // bit 3 reserved 244 .WithFlag(4, out enableIrqOnCmd, name: "INTEN_CMD") 245 .WithFlag(5, out enableIrqOnSsend, name: "INTEN_SSEND") 246 }, 247 248 {(long)Registers.CommandRegister, new DoubleWordRegister(this) 249 .WithFlag(0, FieldMode.Read, 250 writeCallback: (_, val) => 251 { 252 if(val) 253 { 254 while(transmitBuffer.Count < fifoSize) 255 { 256 transmitBuffer.Enqueue(0); 257 } 258 } 259 }, name: "AUTOFILL") 260 .WithTag("AUTOEMPTY", 1, 1) 261 .WithFlag(2, FieldMode.Read, 262 writeCallback: (_, val) => 263 { 264 if(val) 265 { 266 receiveBuffer.Clear(); 267 } 268 }, name: "RXFIFORST") 269 .WithFlag(3, FieldMode.Read, 270 writeCallback: (_, val) => 271 { 272 if(val) 273 { 274 transmitBuffer.Clear(); 275 } 276 }, name: "TXFIFORST") 277 .WithFlag(4, FieldMode.Read, 278 writeCallback: (_, val) => 279 { 280 if(val) 281 { 282 frameCounterLimit.Value = 0; 283 framesTransmitted = 0; 284 framesReceived = 0; 285 } 286 }, name: "CLRFRAMECNT") 287 .WithTag("AUTOSTALL", 5, 1) 288 .WithTag("TXNOW", 6, 1) 289 }, 290 291 {(long)Registers.CommandSize, new DoubleWordRegister(this) 292 .WithValueField(0, 6, out commandSize, name: "CMDSIZE") 293 }, 294 295 {(long)Registers.SlaveHardwareStatus, new DoubleWordRegister(this) 296 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: (_) => CalculateSlaveHardwareStatus(), name: "HWSTATUS") 297 }, 298 299 {(long)Registers.Status8, new DoubleWordRegister(this) 300 .WithTag("FIRSTFRAME", 0, 1) 301 .WithFlag(1, FieldMode.Read, valueProviderCallback: (_) => dataSent.Value && dataReceived.Value, name: "DONE") 302 .WithFlag(2, FieldMode.Read, valueProviderCallback: (_) => receiveBuffer.Count == 0, name: "RXEMPTY") 303 .WithFlag(3, FieldMode.Read, valueProviderCallback: (_) => transmitBuffer.Count == fifoSize, name: "TXFULL") 304 .WithFlag(4, FieldMode.Read, valueProviderCallback: (_) => receiveOverflow.Value, name: "RXOVFLOW") 305 .WithFlag(5, FieldMode.Read, valueProviderCallback: (_) => transmitUnderrun.Value, name: "TXUNDERRUN") 306 .WithFlag(6, FieldMode.Read, valueProviderCallback: (_) => slaveSelect.Value > 0, name: "SSEL") 307 .WithTag("ACTIVE", 7, 1) 308 }, 309 310 {(long)Registers.AliasedControlRegister0, new DoubleWordRegister(this) 311 .WithValueField(0, 8, 312 writeCallback: (_, newVal) => controlRegister.Write(0, (uint)BitHelper.SetMaskedValue(controlRegister.Value, newVal, 0, 8)), 313 valueProviderCallback: (_) => BitHelper.GetMaskedValue(controlRegister.Value, 0, 8), name: "CTRL0") 314 }, 315 316 {(long)Registers.AliasedControlRegister1, new DoubleWordRegister(this) 317 .WithValueField(0, 8, 318 writeCallback: (_, newVal) => controlRegister.Write(0, (uint)BitHelper.SetMaskedValue(controlRegister.Value, newVal, 8, 8)), 319 valueProviderCallback: (_) => BitHelper.GetMaskedValue(controlRegister.Value, 8, 8), name: "CTRL1") 320 }, 321 322 {(long)Registers.AliasedControlRegister2, new DoubleWordRegister(this) 323 .WithValueField(0, 8, 324 writeCallback: (_, newVal) => controlRegister.Write(0, (uint)BitHelper.SetMaskedValue(controlRegister.Value, newVal, 16, 8)), 325 valueProviderCallback: (_) => BitHelper.GetMaskedValue(controlRegister.Value, 16, 8), name: "CTRL2") 326 }, 327 328 {(long)Registers.AliasedControlRegister3, new DoubleWordRegister(this) 329 .WithValueField(0, 8, 330 writeCallback: (_, newVal) => controlRegister.Write(0, (uint)BitHelper.SetMaskedValue(controlRegister.Value, newVal, 24, 8)), 331 valueProviderCallback: (_) => BitHelper.GetMaskedValue(controlRegister.Value, 24, 8), name: "CTRL3") 332 } 333 }; 334 335 registers = new DoubleWordRegisterCollection(this, registersMap); 336 } 337 Reset()338 public override void Reset() 339 { 340 fifoSize = 0; 341 framesReceived = 0; 342 framesTransmitted = 0; 343 344 receiveBuffer.Clear(); 345 transmitBuffer.Clear(); 346 347 registers.Reset(); 348 RefreshInterrupt(); 349 } 350 ReadDoubleWord(long offset)351 public uint ReadDoubleWord(long offset) 352 { 353 var result = 0u; 354 lock(locker) 355 { 356 result = registers.Read(offset); 357 } 358 RefreshInterrupt(); 359 return result; 360 } 361 WriteDoubleWord(long offset, uint value)362 public void WriteDoubleWord(long offset, uint value) 363 { 364 lock(locker) 365 { 366 registers.Write(offset, value); 367 } 368 RefreshInterrupt(); 369 } 370 Transmit(byte data)371 public byte Transmit(byte data) 372 { 373 var returnValue = (byte)0x00; 374 lock(locker) 375 { 376 if(master.Value) 377 { 378 this.Log(LogLevel.Warning, "Cannot receive data when in master mode."); 379 return returnValue; 380 } 381 if(!coreEnabled.Value) 382 { 383 this.Log(LogLevel.Warning, "Cannot receive due to inactive core."); 384 return returnValue; 385 } 386 TryReceive(data); 387 if(framesReceived == (int)commandSize.Value) 388 { 389 fullCommandReceived.Value = true; 390 returnValue = CalculateSlaveHardwareStatus(); 391 } 392 if(transmitBuffer.Count > 0) 393 { 394 framesTransmitted++; 395 returnValue = transmitBuffer.Dequeue(); 396 } 397 else 398 { 399 transmitUnderrun.Value = true; 400 } 401 } 402 RefreshInterrupt(); 403 return returnValue; 404 } 405 FinishTransmission()406 public void FinishTransmission() 407 { 408 framesReceived = 0; 409 framesTransmitted = 0; 410 slaveSelectGoneInactve.Value = true; 411 RefreshInterrupt(); 412 } 413 414 public long Size => 0x1000; 415 416 public GPIO IRQ { get; } 417 TryReceive(byte data)418 private void TryReceive(byte data) 419 { 420 if(receiveBuffer.Count < (fifoSize * (int)frameSize.Value)) 421 { 422 framesReceived++; 423 receiveBuffer.Enqueue(data); 424 // Docs are not clear if this should be done in slave or master mode. 425 if(!disableFrameCount.Value && receiveBuffer.Count % (int)frameSize.Value == 0) 426 { 427 if(framesTransmitted + framesReceived == (int)frameCounterLimit.Value) 428 { 429 dataReceived.Value = true; 430 receiveDone.Value = true; 431 } 432 } 433 } 434 else 435 { 436 receiveOverflow.Value = true; 437 } 438 } 439 CalculateMaskedInterruptValue()440 private uint CalculateMaskedInterruptValue() 441 { 442 var result = new bool[6] 443 { 444 enableIrqOnReceive.Value && receiveDone.Value, 445 enableIrqOnTransmit.Value && transmitDone.Value, 446 enableIrqOnOverflow.Value && receiveOverflow.Value, 447 enableIrqOnUnderrun.Value && transmitUnderrun.Value, 448 fullCommandReceived.Value && enableIrqOnCmd.Value, 449 slaveSelectGoneInactve.Value && enableIrqOnSsend.Value 450 }; 451 return BitHelper.GetValueFromBitsArray(result); 452 } 453 CalculateSlaveHardwareStatus()454 private byte CalculateSlaveHardwareStatus() 455 { 456 var result = new bool[2] 457 { 458 receiveBuffer.Count > 0, 459 transmitBuffer.Count == 0 460 }; 461 return (byte)BitHelper.GetValueFromBitsArray(result); 462 } 463 RefreshInterrupt()464 private void RefreshInterrupt() 465 { 466 IRQ.Set(CalculateMaskedInterruptValue() != 0); 467 } 468 469 private readonly DoubleWordRegisterCollection registers; 470 private readonly DoubleWordRegister controlRegister; 471 private readonly Queue<byte> receiveBuffer; 472 private readonly Queue<byte> transmitBuffer; 473 private IValueRegisterField frameCounterLimit; 474 private IValueRegisterField slaveSelect; 475 private IFlagRegisterField coreEnabled; 476 private IFlagRegisterField master; 477 private IFlagRegisterField enableIrqOnReceive; 478 private IFlagRegisterField enableIrqOnTransmit; 479 private IFlagRegisterField enableIrqOnOverflow; 480 private IFlagRegisterField enableIrqOnUnderrun; 481 private IValueRegisterField frameSize; 482 private IFlagRegisterField dataSent; 483 private IFlagRegisterField dataReceived; 484 private IFlagRegisterField receiveOverflow; 485 private IFlagRegisterField transmitUnderrun; 486 private IFlagRegisterField fullCommandReceived; 487 private IFlagRegisterField slaveSelectGoneInactve; 488 private IFlagRegisterField transmitDone; 489 private IFlagRegisterField receiveDone; 490 private IFlagRegisterField disableFrameCount; 491 private IFlagRegisterField enableIrqOnCmd; 492 private IValueRegisterField commandSize; 493 private IFlagRegisterField enableIrqOnSsend; 494 private int fifoSize; 495 private int framesReceived; 496 private int framesTransmitted; 497 private object locker; 498 499 private enum Registers : long 500 { 501 Control = 0x00, 502 FrameSize = 0x04, 503 Status = 0x08, 504 InterruptClear = 0x0c, 505 ReceiveData = 0x10, 506 TransmitData = 0x14, 507 ClockRate = 0x18, 508 SlaveSelect = 0x1c, 509 InterruptMasked = 0x20, 510 InterruptRaw = 0x24, 511 ControlBitsForEnhancedModes = 0x28, 512 CommandRegister = 0x2c, 513 PacketSize = 0x30, 514 CommandSize = 0x34, 515 SlaveHardwareStatus = 0x38, 516 Status8 = 0x3c, 517 AliasedControlRegister0 = 0x40, 518 AliasedControlRegister1 = 0x44, 519 AliasedControlRegister2 = 0x48, 520 AliasedControlRegister3 = 0x4c 521 } 522 } 523 } 524