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.Core; 11 using Antmicro.Renode.Core.Structure; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Peripherals.Bus; 15 using Antmicro.Renode.Exceptions; 16 using Antmicro.Renode.Utilities; 17 18 namespace Antmicro.Renode.Peripherals.SPI 19 { 20 public class MAX32650_SPI : SimpleContainer<ISPIPeripheral>, IDoubleWordPeripheral, IWordPeripheral, IBytePeripheral, IKnownSize 21 { MAX32650_SPI(IMachine machine, int numberOfSlaves, bool hushTxFifoLevelWarnings = false)22 public MAX32650_SPI(IMachine machine, int numberOfSlaves, bool hushTxFifoLevelWarnings = false) : base(machine) 23 { 24 if(numberOfSlaves < 0 || numberOfSlaves > MaximumNumberOfSlaves) 25 { 26 throw new ConstructionException($"numberOfSlaves should be between 0 and {MaximumNumberOfSlaves - 1}"); 27 } 28 29 IRQ = new GPIO(); 30 NumberOfSlaves = numberOfSlaves; 31 32 registers = new DoubleWordRegisterCollection(this, BuildRegisterMap()); 33 rxQueue = new Queue<byte>(); 34 txQueue = new Queue<byte>(); 35 shouldDeassert = new bool[numberOfSlaves]; 36 37 this.hushTxFifoLevelWarnings = hushTxFifoLevelWarnings; 38 } 39 Reset()40 public override void Reset() 41 { 42 IRQ.Unset(); 43 registers.Reset(); 44 45 rxQueue.Clear(); 46 txQueue.Clear(); 47 48 charactersToTransmit = 0; 49 transactionInProgress = false; 50 51 for(var i = 0; i < NumberOfSlaves; ++i) 52 { 53 shouldDeassert[i] = false; 54 } 55 } 56 ReadByte(long address)57 public byte ReadByte(long address) 58 { 59 if(address >= (long)Registers.FIFOData + FIFODataWidth) 60 { 61 this.Log(LogLevel.Warning, "Tried to perform byte read from different register than FIFO; ignoring"); 62 return 0x00; 63 } 64 return RxDequeue(); 65 } 66 WriteByte(long address, byte value)67 public void WriteByte(long address, byte value) 68 { 69 if(address >= (long)Registers.FIFOData + FIFODataWidth) 70 { 71 this.Log(LogLevel.Warning, "Tried to perform byte write to different register than FIFO; ignoring"); 72 return; 73 } 74 TxEnqueue(value); 75 } 76 ReadWord(long address)77 public ushort ReadWord(long address) 78 { 79 if(address >= (long)Registers.FIFOData + FIFODataWidth) 80 { 81 this.Log(LogLevel.Warning, "Tried to perform word read from different register than FIFO; ignoring"); 82 return 0x00; 83 } 84 85 var value1 = RxDequeue(); 86 var value2 = (ushort)RxDequeue() << 8; 87 return (ushort)(value1 | value2); 88 } 89 WriteWord(long address, ushort value)90 public void WriteWord(long address, ushort value) 91 { 92 if(address >= (long)Registers.FIFOData + FIFODataWidth) 93 { 94 this.Log(LogLevel.Warning, "Tried to perform word write to different register than FIFO; ignoring"); 95 return; 96 } 97 TxEnqueue((byte)value); 98 TxEnqueue((byte)(value >> 8)); 99 } 100 ReadDoubleWord(long address)101 public uint ReadDoubleWord(long address) 102 { 103 return registers.Read(address); 104 } 105 WriteDoubleWord(long address, uint value)106 public void WriteDoubleWord(long address, uint value) 107 { 108 registers.Write(address, value); 109 } 110 111 public long Size => 0x1000; 112 113 public GPIO IRQ { get; } 114 115 public int NumberOfSlaves { get; } 116 UpdateInterrupts()117 private void UpdateInterrupts() 118 { 119 interruptRxLevelPending.Value = rxQueue.Count >= (int)rxFIFOThreshold.Value; 120 interruptTxLevelPending.Value = txQueue.Count >= (int)txFIFOThreshold.Value; 121 122 var pending = false; 123 pending |= interruptTxLevelEnabled.Value && interruptTxLevelPending.Value; 124 pending |= interruptTxEmptyEnabled.Value && interruptTxEmptyPending.Value; 125 pending |= interruptRxLevelEnabled.Value && interruptRxLevelPending.Value; 126 pending |= interruptRxFullEnabled.Value && interruptRxFullPending.Value; 127 pending |= interruptTransactionFinishedEnabled.Value && interruptTransactionFinishedPending.Value; 128 pending |= interruptRxOverrunEnabled.Value && interruptRxOverrunPending.Value; 129 pending |= interruptRxUnderrunEnabled.Value && interruptRxUnderrunPending.Value; 130 IRQ.Set(pending); 131 } 132 DeassertCS(Func<int, bool> predicate)133 private void DeassertCS(Func<int, bool> predicate) 134 { 135 foreach(var indexPeripheral in ActivePeripherals) 136 { 137 var index = indexPeripheral.Item1; 138 var peripheral = indexPeripheral.Item2; 139 140 if(predicate(index)) 141 { 142 peripheral.FinishTransmission(); 143 shouldDeassert[index] = false; 144 } 145 } 146 } 147 StartTransaction()148 private void StartTransaction() 149 { 150 // deassert CS of active peripherals that are not enabled in the slave select register anymore 151 DeassertCS(x => !BitHelper.IsBitSet((uint)slaveSelect.Value, (byte)x)); 152 153 foreach(var value in txQueue) 154 { 155 Transmit(value); 156 } 157 158 txQueue.Clear(); 159 160 transactionInProgress = true; 161 interruptTxEmptyPending.Value = true; 162 163 UpdateInterrupts(); 164 TryFinishTransaction(); 165 } 166 TryFinishTransaction()167 private void TryFinishTransaction() 168 { 169 if(charactersToTransmit > 0) 170 { 171 return; 172 } 173 174 transactionInProgress = false; 175 interruptTransactionFinishedPending.Value = true; 176 177 // deassert CS of active peripherals marked in the should deassert array 178 DeassertCS(x => shouldDeassert[x]); 179 UpdateInterrupts(); 180 } 181 Transmit(byte value)182 private void Transmit(byte value) 183 { 184 var numberOfPeripherals = ActivePeripherals.Count(); 185 foreach(var indexPeripheral in ActivePeripherals) 186 { 187 var peripheral = indexPeripheral.Item2; 188 var output = peripheral.Transmit(value); 189 // In case multiple SS lines are chosen, we are deliberately 190 // ignoring output from all of them. Therefore, this configuration 191 // can only be used to send data to multiple receivers at once. 192 if(numberOfPeripherals == 1) 193 { 194 RxEnqueue(output); 195 } 196 } 197 198 if(numberOfPeripherals == 0) 199 { 200 // If there is no target device we still need to populate the RX queue 201 // with dummy bytes 202 RxEnqueue(DummyResponseByte); 203 } 204 205 charactersToTransmit -= 1; 206 TryFinishTransaction(); 207 } 208 TryTransmit()209 private void TryTransmit() 210 { 211 if(!transactionInProgress || rxQueue.Count == FIFOLength || txQueue.Count == 0) 212 { 213 return; 214 } 215 216 var bytesToTransmit = Math.Min(FIFOLength - rxQueue.Count, txQueue.Count); 217 for(var i = 0; i < bytesToTransmit; ++i) 218 { 219 Transmit(txQueue.Dequeue()); 220 } 221 } 222 RxEnqueue(byte value)223 private void RxEnqueue(byte value) 224 { 225 if(!rxFIFOEnabled.Value) 226 { 227 return; 228 } 229 230 if(rxQueue.Count == FIFOLength) 231 { 232 interruptRxOverrunPending.Value = true; 233 UpdateInterrupts(); 234 return; 235 } 236 rxQueue.Enqueue(value); 237 if(rxQueue.Count == FIFOLength) 238 { 239 interruptRxFullPending.Value = true; 240 UpdateInterrupts(); 241 } 242 } 243 RxDequeue()244 private byte RxDequeue() 245 { 246 if(!rxFIFOEnabled.Value) 247 { 248 this.Log(LogLevel.Warning, "Tried to read from RX FIFO while it's disabled"); 249 return 0x00; 250 } 251 252 if(!rxQueue.TryDequeue(out var result)) 253 { 254 interruptRxUnderrunPending.Value |= true; 255 } 256 else 257 { 258 TryTransmit(); 259 } 260 261 TryFinishTransaction(); 262 UpdateInterrupts(); 263 264 return result; 265 } 266 TxEnqueue(byte value)267 private void TxEnqueue(byte value) 268 { 269 if(transactionInProgress && rxQueue.Count < FIFOLength) 270 { 271 // If we have active transaction and we have room to receive data, 272 // send/receive it immediately 273 Transmit(value); 274 } 275 else 276 { 277 // Otherwise, we either generate TX overrun interrupt if internal 278 // TX buffer is full, or enqueue new data to it. This data will be 279 // send either after START condition, or when there is room in RX 280 // buffer when transaction is active 281 if(txQueue.Count == FIFOLength) 282 { 283 interruptTxOverrunPending.Value = true; 284 } 285 else 286 { 287 txQueue.Enqueue(value); 288 } 289 } 290 UpdateInterrupts(); 291 } 292 BuildRegisterMap()293 private Dictionary<long, DoubleWordRegister> BuildRegisterMap() 294 { 295 var registerMap = new Dictionary<long, DoubleWordRegister>() 296 { 297 {(long)Registers.FIFOData, new DoubleWordRegister(this) 298 .WithValueFields(0, 8, FIFODataWidth, name: "DATA.data", 299 valueProviderCallback: (_, __) => RxDequeue(), 300 writeCallback: (_, __, value) => TxEnqueue((byte)value)) 301 }, 302 {(long)Registers.MasterSignalsControl, new DoubleWordRegister(this) 303 .WithFlag(0, out var spiEnabled, name: "CTRL0.spi_en", 304 // deassert all CS lines when disabling the controller 305 writeCallback: (_, value) => { if(!value) DeassertCS(x => true); }) 306 .WithFlag(1, name: "CTRL0.mm_en", 307 changeCallback: (_, value) => 308 { 309 if(!value && spiEnabled.Value) 310 { 311 this.Log(LogLevel.Warning, "CTRL0.mm_en has been unset, but only Master mode is supported"); 312 } 313 }) 314 .WithReservedBits(2, 2) 315 .WithTaggedFlag("CTRL0.ss_io", 4) 316 .WithFlag(5, FieldMode.WriteOneToClear, name: "CTRL0.start", 317 writeCallback: (_, value) => { if(value) StartTransaction(); }) 318 .WithReservedBits(6, 2) 319 .WithFlag(8, name: "CTRL0.ss_ctrl", 320 changeCallback: (_, value) => 321 { 322 foreach(var indexPeripheral in ActivePeripherals) 323 { 324 shouldDeassert[indexPeripheral.Item1] |= !value; 325 } 326 }) 327 .WithReservedBits(9, 7) 328 .WithValueField(16, 4, out slaveSelect, name: "CTRL0.ss_sel", 329 changeCallback: (_, value) => 330 { 331 for(var i = 0; i < NumberOfSlaves; ++i) 332 { 333 if(BitHelper.IsBitSet(value, (byte)i) && !TryGetByAddress(i, out var __)) 334 { 335 this.Log(LogLevel.Warning, "Tried to select SS{0}, but it's not connected to anything; ignoring", i); 336 BitHelper.SetBit(ref value, (byte)i, false); 337 } 338 } 339 slaveSelect.Value = value; 340 }) 341 .WithReservedBits(20, 12) 342 }, 343 {(long)Registers.TrasmitPacketSize, new DoubleWordRegister(this) 344 .WithValueField(0, 16, name: "CTRL1.tx_num_char", 345 writeCallback: (_, value) => charactersToTransmit = (uint)value) 346 .WithTag("CTRL1.rx_num_char", 16, 16) 347 }, 348 {(long)Registers.StaticConfiguration, new DoubleWordRegister(this) 349 .WithTaggedFlag("CTRL2.clk_pha", 0) 350 .WithTaggedFlag("CTRL2.clk_pol", 1) 351 .WithReservedBits(2, 6) 352 .WithValueField(8, 4, name: "CTRL2.num_bits", 353 writeCallback: (_, value) => 354 { 355 if(value >= 1 && value != 8) 356 { 357 this.Log(LogLevel.Warning, "Only 8-bit characters are supported, but tried to change to {0}-bit characters; ignored", value); 358 } 359 }) 360 .WithTag("CTRL2.bus_width", 12, 2) 361 .WithReservedBits(14, 1) 362 .WithTaggedFlag("CTRL2.three_wire", 15) 363 .WithTag("CTRL2.ss_pol", 16, 4) 364 .WithReservedBits(20, 12) 365 }, 366 {(long)Registers.InterruptStatusFlags, new DoubleWordRegister(this) 367 .WithFlag(0, out interruptTxLevelPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL.tx_level") 368 .WithFlag(1, out interruptTxEmptyPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL.tx_empty") 369 .WithFlag(2, out interruptRxLevelPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL.rx_level") 370 .WithFlag(3, out interruptRxFullPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL.rx_full") 371 .WithTaggedFlag("INT_FL.ssa", 4) 372 .WithTaggedFlag("INT_FL.ssd", 5) 373 .WithReservedBits(6, 2) 374 .WithTaggedFlag("INT_FL.fault", 8) 375 .WithTaggedFlag("INT_FL.abort", 9) 376 .WithReservedBits(10, 1) 377 .WithFlag(11, out interruptTransactionFinishedPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL.m_done") 378 .WithFlag(12, out interruptTxOverrunPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL.tx_ovr") 379 .WithTaggedFlag("INT_FL.tx_und", 13) 380 .WithFlag(14, out interruptRxOverrunPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL.rx_ovr") 381 .WithFlag(15, out interruptRxUnderrunPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_EN.rx_und") 382 .WithReservedBits(16, 16) 383 .WithChangeCallback((_, __) => UpdateInterrupts()) 384 }, 385 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 386 .WithFlag(0, out interruptTxLevelEnabled, name: "INT_EN.tx_level") 387 .WithFlag(1, out interruptTxEmptyEnabled, name: "INT_EN.tx_empty") 388 .WithFlag(2, out interruptRxLevelEnabled, name: "INT_EN.rx_level") 389 .WithFlag(3, out interruptRxFullEnabled, name: "INT_EN.rx_full") 390 .WithTaggedFlag("INT_EN.ssa", 4) 391 .WithTaggedFlag("INT_EN.ssd", 5) 392 .WithReservedBits(6, 2) 393 .WithTaggedFlag("INT_EN.fault", 8) 394 .WithTaggedFlag("INT_EN.abort", 9) 395 .WithReservedBits(10, 1) 396 .WithFlag(11, out interruptTransactionFinishedEnabled, name: "INT_EN.m_done") 397 .WithFlag(12, out interruptTxOverrunEnabled, name: "INT_EN.tx_ovr") 398 .WithTaggedFlag("INT_EN.tx_und", 13) 399 .WithFlag(14, out interruptRxOverrunEnabled, name: "INT_EN.rx_ovr") 400 .WithFlag(15, out interruptRxUnderrunEnabled, name: "INT_EN.rx_und") 401 .WithReservedBits(16, 16) 402 .WithChangeCallback((_, __) => UpdateInterrupts()) 403 }, 404 {(long)Registers.ActiveStatus, new DoubleWordRegister(this) 405 .WithTaggedFlag("STAT.busy", 0) 406 .WithReservedBits(1, 31) 407 } 408 }; 409 410 { 411 var constructedRegister = new DoubleWordRegister(this) 412 .WithValueField(0, 5, out txFIFOThreshold, name: "DMA.tx_fifo_level") 413 // NOTE: 5th bit covered in if statement 414 .WithFlag(6, out txFIFOEnabled, name: "DMA.tx_fifo_en") 415 .WithFlag(7, FieldMode.WriteOneToClear, name: "DMA.tx_fifo_clear", 416 writeCallback: (_, value) => { if(value) txQueue.Clear(); }) 417 .WithValueField(8, 6, FieldMode.Read, name: "DMA.tx_fifo_cnt", 418 valueProviderCallback: _ => (uint)txQueue.Count) 419 .WithReservedBits(14, 1) 420 .WithTaggedFlag("DMA.tx_dma_en", 15) 421 .WithValueField(16, 5, out rxFIFOThreshold, name: "DMA.rx_fifo_level") 422 .WithReservedBits(21, 1) 423 .WithFlag(22, out rxFIFOEnabled, name: "DMA.rx_fifo_en") 424 .WithFlag(23, FieldMode.WriteOneToClear, name: "DMA.rx_fifo_clear", 425 writeCallback: (_, value) => { if(value) rxQueue.Clear(); }) 426 .WithValueField(24, 6, FieldMode.Read, name: "DMA.rx_fifo_cnt", 427 valueProviderCallback: _ => (uint)rxQueue.Count) 428 .WithReservedBits(30, 1) 429 .WithTag("DMA.rx_dma_en", 31, 1) 430 .WithChangeCallback((_, __) => UpdateInterrupts()) 431 ; 432 // Depending on the peripheral constructor argument, treat writes to reserved field as error or don't. 433 if(hushTxFifoLevelWarnings) 434 { 435 constructedRegister.WithFlag(5, name: "RESERVED"); 436 } 437 else 438 { 439 constructedRegister.WithReservedBits(5, 1); 440 } 441 registerMap.Add((long)Registers.DMAControl, constructedRegister); 442 } 443 444 return registerMap; 445 } 446 447 private IEnumerable<Tuple<int, ISPIPeripheral>> ActivePeripherals 448 { 449 get 450 { 451 return Enumerable 452 .Range(0, NumberOfSlaves) 453 .Select(index => 454 { 455 if(!BitHelper.IsBitSet(slaveSelect.Value, (byte)index)) 456 { 457 return null; 458 } 459 if(!TryGetByAddress(index, out var peripheral)) 460 { 461 return null; 462 } 463 return Tuple.Create(index, peripheral); 464 }) 465 .Where(tuple => tuple != null); 466 } 467 } 468 469 private bool[] shouldDeassert; 470 private bool transactionInProgress; 471 private bool hushTxFifoLevelWarnings; 472 private uint charactersToTransmit; 473 474 private IValueRegisterField slaveSelect; 475 476 private IFlagRegisterField rxFIFOEnabled; 477 private IFlagRegisterField txFIFOEnabled; 478 479 private IValueRegisterField rxFIFOThreshold; 480 private IValueRegisterField txFIFOThreshold; 481 482 private IFlagRegisterField interruptTxLevelPending; 483 private IFlagRegisterField interruptTxEmptyPending; 484 private IFlagRegisterField interruptRxLevelPending; 485 private IFlagRegisterField interruptRxFullPending; 486 private IFlagRegisterField interruptTransactionFinishedPending; 487 private IFlagRegisterField interruptTxOverrunPending; 488 private IFlagRegisterField interruptRxOverrunPending; 489 private IFlagRegisterField interruptRxUnderrunPending; 490 491 private IFlagRegisterField interruptTxLevelEnabled; 492 private IFlagRegisterField interruptTxEmptyEnabled; 493 private IFlagRegisterField interruptRxLevelEnabled; 494 private IFlagRegisterField interruptRxFullEnabled; 495 private IFlagRegisterField interruptTransactionFinishedEnabled; 496 private IFlagRegisterField interruptTxOverrunEnabled; 497 private IFlagRegisterField interruptRxOverrunEnabled; 498 private IFlagRegisterField interruptRxUnderrunEnabled; 499 500 private const int FIFODataWidth = 0x04; 501 private const int FIFOLength = 32; 502 private const int MaximumNumberOfSlaves = 4; 503 504 private readonly Queue<byte> rxQueue; 505 private readonly Queue<byte> txQueue; 506 private readonly DoubleWordRegisterCollection registers; 507 508 private const byte DummyResponseByte = 0xFF; 509 510 private enum Registers : long 511 { 512 FIFOData = 0x00, 513 MasterSignalsControl = 0x04, 514 TrasmitPacketSize = 0x08, 515 StaticConfiguration = 0x0C, 516 SlaveSelectTiming = 0x10, 517 MasterClockConfiguration = 0x14, 518 DMAControl = 0x1C, 519 InterruptStatusFlags = 0x20, 520 InterruptEnable = 0x24, 521 WakeupStatusFlags = 0x28, 522 WakeupEnable = 0x2C, 523 ActiveStatus = 0x30, 524 } 525 } 526 } 527