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.Linq; 9 using System.Collections.Generic; 10 using System.Text; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Exceptions; 13 using Antmicro.Renode.Core.Structure; 14 using Antmicro.Renode.Core.Structure.Registers; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Renode.Peripherals.Bus; 17 using Antmicro.Renode.Peripherals.I2C; 18 using Antmicro.Renode.Utilities; 19 20 namespace Antmicro.Renode.Peripherals.SPI 21 { 22 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)] 23 public class AmbiqApollo4_IOMaster : IPeripheralContainer<ISPIPeripheral, TypedNumberRegistrationPoint<int>>, IPeripheralContainer<II2CPeripheral, TypedNumberRegistrationPoint<int>>, 24 IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IPeripheral, IKnownSize 25 { AmbiqApollo4_IOMaster(IMachine machine)26 public AmbiqApollo4_IOMaster(IMachine machine) 27 { 28 RegistersCollection = new DoubleWordRegisterCollection(this); 29 30 IRQ = new GPIO(); 31 32 // The countChangeAction cannot be set in the FIFO constructor. 33 incomingFifo = new Fifo("incoming FIFO", this); 34 incomingFifo.CountChangeAction += IncomingFifoCountChangeAction; 35 outgoingFifo = new Fifo("outgoing FIFO", this); 36 outgoingFifo.CountChangeAction += OutgoingFifoCountChangeAction; 37 38 spiPeripherals = new Dictionary<int, ISPIPeripheral>(); 39 i2cPeripherals = new Dictionary<int, II2CPeripheral>(); 40 41 this.machine = machine; 42 43 DefineRegisters(); 44 Reset(); 45 } 46 Reset()47 public void Reset() 48 { 49 activeTransactionContinue = false; 50 activeSpiSlaveSelect = 0; 51 i2cSlaveAddress = 0; 52 53 RegistersCollection.Reset(); 54 activeTransactionStatus.Value = Status.Idle; 55 status = Status.Idle; 56 57 IRQ.Unset(); 58 incomingFifo.Reset(); 59 outgoingFifo.Reset(); 60 } 61 ReadDoubleWord(long offset)62 public uint ReadDoubleWord(long offset) 63 { 64 return RegistersCollection.Read(offset); 65 } 66 WriteDoubleWord(long offset, uint value)67 public void WriteDoubleWord(long offset, uint value) 68 { 69 RegistersCollection.Write(offset, value); 70 } 71 Register(ISPIPeripheral peripheral, TypedNumberRegistrationPoint<int> registrationPoint)72 public void Register(ISPIPeripheral peripheral, TypedNumberRegistrationPoint<int> registrationPoint) 73 { 74 if(registrationPoint.Address < 0 || registrationPoint.Address >= MaxSpiPeripheralsConnected) 75 { 76 throw new ConstructionException($"Invalid SPI peripheral ID: {registrationPoint.Address}! Only IDs from 0 to {MaxSpiPeripheralsConnected - 1} are valid."); 77 } 78 Register(spiPeripherals, peripheral, registrationPoint.WithType<ISPIPeripheral>()); 79 } 80 Register(II2CPeripheral peripheral, TypedNumberRegistrationPoint<int> registrationPoint)81 public void Register(II2CPeripheral peripheral, TypedNumberRegistrationPoint<int> registrationPoint) 82 { 83 Register(i2cPeripherals, peripheral, registrationPoint.WithType<II2CPeripheral>()); 84 } 85 Unregister(ISPIPeripheral peripheral)86 public void Unregister(ISPIPeripheral peripheral) 87 { 88 Unregister(spiPeripherals, peripheral); 89 } 90 Unregister(II2CPeripheral peripheral)91 public void Unregister(II2CPeripheral peripheral) 92 { 93 Unregister(i2cPeripherals, peripheral); 94 } 95 GetRegistrationPoints(ISPIPeripheral peripheral)96 public IEnumerable<TypedNumberRegistrationPoint<int>> GetRegistrationPoints(ISPIPeripheral peripheral) 97 { 98 return spiPeripherals.Keys.Select(x => new TypedNumberRegistrationPoint<int>(x, typeof(ISPIPeripheral))) 99 .ToList(); 100 } 101 GetRegistrationPoints(II2CPeripheral peripheral)102 public IEnumerable<TypedNumberRegistrationPoint<int>> GetRegistrationPoints(II2CPeripheral peripheral) 103 { 104 return i2cPeripherals.Keys.Select(x => new TypedNumberRegistrationPoint<int>(x, typeof(II2CPeripheral))) 105 .ToList(); 106 } 107 108 IEnumerable<IRegistered<ISPIPeripheral, TypedNumberRegistrationPoint<int>>> IPeripheralContainer<ISPIPeripheral, TypedNumberRegistrationPoint<int>>.Children => 109 spiPeripherals.Select(x => Registered.Create(x.Value, new TypedNumberRegistrationPoint<int>(x.Key, typeof(ISPIPeripheral)))).ToList(); 110 111 IEnumerable<IRegistered<II2CPeripheral, TypedNumberRegistrationPoint<int>>> IPeripheralContainer<II2CPeripheral, TypedNumberRegistrationPoint<int>>.Children => 112 i2cPeripherals.Select(x => Registered.Create(x.Value, new TypedNumberRegistrationPoint<int>(x.Key, typeof(II2CPeripheral)))).ToList(); 113 114 public GPIO IRQ { get; } 115 116 public DoubleWordRegisterCollection RegistersCollection { get; } 117 118 public long Size => 0x400; 119 DefineRegisters()120 private void DefineRegisters() 121 { 122 Registers.OutgoingFifoAccessPort.DefineMany(this, 8, (register, index) => 123 { 124 register.WithValueField(0, 32, 125 valueProviderCallback: _ => outgoingFifo.DirectGet((uint)index), 126 writeCallback: (_, newValue) => outgoingFifo.DirectSet((uint)index, (uint)newValue)); 127 }, stepInBytes: 4); 128 129 Registers.IncomingFifoAccessPort.DefineMany(this, 8, (register, index) => 130 { 131 register.WithValueField(0, 32, FieldMode.Read, 132 valueProviderCallback: _ => incomingFifo.DirectGet((uint)index)); 133 }, stepInBytes: 4); 134 135 Registers.FifoSizeAndRemainingSlotsOpenValues.Define(this) 136 .WithValueField(0, 8, FieldMode.Read, name: "FIFO0SIZ", valueProviderCallback: _ => outgoingFifo.BytesCount) 137 .WithValueField(8, 8, FieldMode.Read, name: "FIFO0REM", valueProviderCallback: _ => outgoingFifo.BytesLeft) 138 .WithValueField(16, 8, FieldMode.Read, name: "FIFO1SIZ", valueProviderCallback: _ => incomingFifo.BytesCount) 139 .WithValueField(24, 8, FieldMode.Read, name: "FIFO1REM", valueProviderCallback: _ => incomingFifo.BytesLeft) 140 ; 141 142 Registers.FifoThresholdConfiguration.Define(this) 143 .WithValueField(0, 6, out fifoInterruptReadThreshold, name: "FIFORTHR") 144 .WithReservedBits(6, 2) 145 .WithValueField(8, 6, out fifoInterruptWriteThreshold, name: "FIFOWTHR") 146 .WithReservedBits(14, 18) 147 .WithChangeCallback((_, __) => UpdateFifoThresholdInterruptStatus()) 148 ; 149 150 Registers.FifoPop.Define(this) 151 .WithValueField(0, 32, FieldMode.Read, name: "FIFODOUT", valueProviderCallback: _ => 152 { 153 // "Will advance the internal read pointer of the incoming FIFO (FIFO1) when read, if POPWR is not active. 154 // If POPWR is active, a write to this register is needed to advance the internal FIFO pointer." 155 if(!(writePopToAdvanceReadPointer.Value ? incomingFifo.TryPeek(out var value) : incomingFifo.TryPop(out value))) 156 { 157 this.Log(LogLevel.Warning, "Failed to read from incoming FIFO"); 158 InterruptStatusSet(IoMasterInterrupts.ReadFifoUnderflow, true); 159 } 160 return value; 161 }, writeCallback: (_, __) => 162 { 163 if(writePopToAdvanceReadPointer.Value) 164 { 165 if(!incomingFifo.TryAdvancePointer()) 166 { 167 this.Log(LogLevel.Warning, "Failed to advance the internal read pointer of the incoming FIFO"); 168 } 169 } 170 else 171 { 172 this.Log(LogLevel.Warning, "Tried to write the FIFOPOP register but the POPWR isn't set"); 173 } 174 }) 175 ; 176 177 Registers.FifoPush.Define(this) 178 .WithValueField(0, 32, FieldMode.Write, name: "FIFODIN", writeCallback: (_, newValue) => 179 { 180 if(!outgoingFifo.TryPush((uint)newValue)) 181 { 182 this.Log(LogLevel.Warning, "Failed to write to the outgoing FIFO (value: 0x{0})", newValue); 183 InterruptStatusSet(IoMasterInterrupts.WriteFifoOverflow, true); 184 } 185 }) 186 ; 187 188 Registers.FifoControl.Define(this, 0x00000002) 189 .WithFlag(0, out writePopToAdvanceReadPointer, name: "POPWR") 190 .WithFlag(1, name: "FIFORSTN", writeCallback: (_, newValue) => 191 { 192 // FIFORSTN is an inversed reset flag; FIFOs are reset when this flag is 0. 193 incomingFifo.ResetFlag = !newValue; 194 outgoingFifo.ResetFlag = !newValue; 195 }, 196 // The value should be the same for both FIFOs so it doesn't matter which is returned here. 197 valueProviderCallback: _ => incomingFifo.ResetFlag) 198 .WithReservedBits(2, 30) 199 ; 200 201 Registers.FifoPointers.Define(this) 202 .WithValueField(0, 4, FieldMode.Read, name: "FIFOWPTR", valueProviderCallback: _ => outgoingFifo.Pointer) 203 .WithReservedBits(4, 4) 204 .WithValueField(8, 4, FieldMode.Read, name: "FIFORPTR", valueProviderCallback: _ => incomingFifo.Pointer) 205 .WithReservedBits(12, 20) 206 ; 207 208 // Some software expects values written to 209 // IOCLKEN, FSEL, DIVEN, LOWPER, TOTPER to be retained 210 Registers.IOClockConfiguration.Define(this) 211 .WithFlag(0, name: "IOCLKEN") 212 .WithReservedBits(1, 7) 213 .WithValueField(8, 3, name: "FSEL") 214 .WithTaggedFlag("DIV3", 11) 215 .WithFlag(12, name: "DIVEN") 216 .WithReservedBits(13, 3) 217 .WithValueField(16, 8, name: "LOWPER") 218 .WithValueField(24, 8, name: "TOTPER") 219 ; 220 221 Registers.SubmoduleControl.Define(this) 222 .WithFlag(0, out spiMasterEnabled, name: "SMOD0EN") 223 .WithEnumField<DoubleWordRegister, SubmoduleTypes>(1, 3, FieldMode.Read, name: "SMOD0TYPE", valueProviderCallback: _ => SubmoduleTypes.SpiMaster) 224 .WithFlag(4, out i2cMasterEnabled, name: "SMOD1EN") 225 .WithEnumField<DoubleWordRegister, SubmoduleTypes>(5, 3, FieldMode.Read, name: "SMOD1TYPE", valueProviderCallback: _ => SubmoduleTypes.I2CMaster) 226 .WithTaggedFlag("SMOD2EN", 8) 227 // It should be I2SMaster_Slave (0x4), but I2S isn't currently supported so let's mark it as NotInstalled. 228 .WithEnumField<DoubleWordRegister, SubmoduleTypes>(9, 3, FieldMode.Read, name: "SMOD2TYPE", valueProviderCallback: _ => SubmoduleTypes.NotInstalled) 229 .WithReservedBits(12, 20) 230 .WithWriteCallback((_, __) => 231 { 232 if(spiMasterEnabled.Value && i2cMasterEnabled.Value) 233 { 234 this.Log(LogLevel.Warning, "Both SPI and I2C modules have been enabled"); 235 spiMasterEnabled.Value = false; 236 i2cMasterEnabled.Value = false; 237 } 238 }) 239 ; 240 241 Registers.CommandAndOffset.Define(this) 242 .WithEnumField(0, 4, out transactionCommand, name: "CMD") 243 .WithValueField(4, 3, out transactionOffsetCount, name: "OFFSETCNT") 244 .WithFlag(7, out transactionContinue, name: "CONT") 245 .WithValueField(8, 12, out transactionSize, name: "TSIZE") 246 .WithValueField(20, 2, out spiSlaveSelect, name: "CMDSEL") 247 .WithReservedBits(22, 2) 248 .WithValueField(24, 8, out transactionOffsetLow, name: "OFFSETLO") 249 .WithWriteCallback((_, __) => 250 { 251 this.Log(LogLevel.Debug, 252 "Transaction received for #{0}; command: {1}, size: {2}, offset: <count: {3}, low=0x{4:X2}, high=0x{5:X8}>, cont: {6}", 253 PrettyPendingPeripheral, transactionCommand.Value, transactionSize.Value, transactionOffsetCount.Value, 254 transactionOffsetLow.Value, transactionOffsetHigh.Value, transactionContinue.Value); 255 256 if(!spiMasterEnabled.Value && !i2cMasterEnabled.Value) 257 { 258 this.Log(LogLevel.Error, "Invalid operation; SPI/I2C Master inferfaces are disabled!"); 259 return; 260 } 261 262 if(activeTransactionCommand.Value != Commands.None) 263 { 264 this.Log(LogLevel.Error, "Dropping the new transaction received for #{0}: {1}; {2} on {3} is still being processed.", 265 PrettyPendingPeripheral, transactionCommand.Value, activeTransactionCommand.Value, PrettyActivePeripheral); 266 return; 267 } 268 269 if(!IsTransactionValid(transactionCommand.Value, (uint)transactionSize.Value, (uint)transactionOffsetCount.Value, 270 (int)spiSlaveSelect.Value, out var errorMessage)) 271 { 272 this.Log(LogLevel.Error, errorMessage); 273 activeTransactionStatus.Value = Status.Error; 274 status = Status.Idle; 275 InterruptStatusSet(IoMasterInterrupts.IllegalCommand); 276 return; 277 } 278 279 // Preserve important values for the transaction being currently processed. 280 // Only the command and size left values are accessible through registers. 281 activeTransactionCommand.Value = transactionCommand.Value; 282 activeTransactionContinue = transactionContinue.Value; 283 activeSpiSlaveSelect = (int)spiSlaveSelect.Value; 284 activeTransactionSizeLeft.Value = transactionSize.Value; 285 286 SendTransactionOffset((uint)transactionOffsetCount.Value); 287 288 if(activeTransactionSizeLeft.Value == 0) 289 { 290 // As this is 0-size TX transaction and we already send transaction offset, 291 // there is nothing more to do. 292 TryFinishTransaction(); 293 return; 294 } 295 296 status = Status.Active; 297 298 while(activeTransactionSizeLeft.Value > 0) 299 { 300 if(activeTransactionCommand.Value == Commands.Read) 301 { 302 if(!incomingFifo.TryReceiveAndPush(ReceiveData)) 303 { 304 this.Log(LogLevel.Debug, "Cannot push more data to the incoming FIFO; {0}B remain to complete the read command.", 305 activeTransactionSizeLeft.Value); 306 break; 307 } 308 } 309 else if(activeTransactionCommand.Value == Commands.Write) 310 { 311 if(!outgoingFifo.TryPop(out var value)) 312 { 313 this.Log(LogLevel.Debug, "No more data in the outgoing FIFO; {0}B remain to complete the write command.", 314 activeTransactionSizeLeft.Value); 315 break; 316 } 317 SendData(value); 318 } // No else because only Read and Write commands are handled after 'IsTransactionValid'. 319 } 320 321 if(activeTransactionSizeLeft.Value != 0) 322 { 323 activeTransactionStatus.Value = Status.Wait; 324 } 325 }) 326 ; 327 328 Registers.DcxControlAndCeUsageSelection.Define(this) 329 .WithTag("DCXSEL", 0, 4) 330 .WithTaggedFlag("DCXEN", 4) 331 .WithReservedBits(5, 27) 332 ; 333 334 Registers.HighOrderBytesBfOffsetForIOTransaction.Define(this) 335 .WithValueField(0, 32, out transactionOffsetHigh, name: "OFFSETHI") 336 ; 337 338 Registers.CommandStatus.Define(this) 339 // This field is designed to hold the value written to CMD. The MSB is stated as unused. 340 .WithEnumField(0, 5, out activeTransactionCommand, FieldMode.Read, name: "CCMD") 341 .WithEnumField(5, 3, out activeTransactionStatus, FieldMode.Read, name: "CMDSTAT") 342 .WithValueField(8, 12, out activeTransactionSizeLeft, FieldMode.Read, name: "CTSIZE") 343 .WithReservedBits(20, 12) 344 ; 345 346 Registers.IOMasterInterruptsEnable.Define(this) 347 .WithFlags(0, IoMasterInterruptsCount, out ioMasterInterruptsEnableFlags, name: "INTENi") 348 .WithReservedBits(15, 17) 349 .WithChangeCallback((_, __) => UpdateIRQ()) 350 ; 351 352 Registers.IOMasterInterruptsStatus.Define(this) 353 .WithFlags(0, IoMasterInterruptsCount, out ioMasterInterruptsStatusFlags, FieldMode.Read, name: "INTSTATi") 354 .WithReservedBits(15, 17) 355 ; 356 357 Registers.IOMasterInterruptsClear.Define(this) 358 .WithFlags(0, IoMasterInterruptsCount, FieldMode.Write, writeCallback: (interrupt, _, newValue) => 359 { 360 if(newValue) 361 { 362 InterruptStatusSet((IoMasterInterrupts)interrupt, false); 363 } 364 }, name: "INTCLRi") 365 // IgnoredBits not to trigger warnings when 0xFFFFFFFF is written to clear all INTs. 366 .WithIgnoredBits(15, 17) 367 ; 368 369 Registers.IOMasterInterruptsSet.Define(this) 370 .WithFlags(0, IoMasterInterruptsCount, FieldMode.Write, writeCallback: (interrupt, _, newValue) => 371 { 372 if(newValue) 373 { 374 InterruptStatusSet((IoMasterInterrupts)interrupt, true); 375 } 376 }, name: "INTSETi") 377 .WithReservedBits(15, 17) 378 ; 379 380 Registers.DmaTriggerEnable.Define(this) 381 .WithTaggedFlag("DCMDCMPEN", 0) 382 .WithTaggedFlag("DTHREN", 1) 383 .WithReservedBits(2, 30) 384 ; 385 386 Registers.DmaTriggerStatus.Define(this) 387 .WithTaggedFlag("DCMDCMP", 0) 388 .WithTaggedFlag("DTHR", 1) 389 .WithTaggedFlag("DTOTCMP", 2) 390 .WithReservedBits(3, 29) 391 ; 392 393 Registers.DmaConfiguration.Define(this) 394 .WithTaggedFlag("DMAEN", 0) 395 .WithTaggedFlag("DMADIR", 1) 396 .WithReservedBits(2, 6) 397 .WithTaggedFlag("DMAPRI", 8) 398 .WithTaggedFlag("DPWROFF", 9) 399 .WithReservedBits(10, 22) 400 ; 401 402 Registers.DmaTotalTransferCount.Define(this) 403 .WithTag("TOTCOUNT", 0, 12) 404 .WithReservedBits(12, 20) 405 ; 406 407 Registers.DmaTargetAddress.Define(this) 408 .WithTag("TARGADDR", 0, 29) 409 .WithReservedBits(29, 3) 410 ; 411 412 Registers.DmaStatus.Define(this) 413 .WithTaggedFlag("DMATIP", 0) 414 .WithTaggedFlag("DMACPL", 1) 415 .WithTaggedFlag("DMAERR", 2) 416 .WithReservedBits(3, 29) 417 ; 418 419 Registers.CommandQueueConfiguration.Define(this) 420 .WithTaggedFlag("CQEN", 0) 421 .WithTaggedFlag("CQPRI", 1) 422 .WithTag("MSPIFLGSEL", 2, 2) 423 .WithReservedBits(4, 28) 424 ; 425 426 Registers.CommandQueueTargetReadAddress.Define(this) 427 .WithReservedBits(0, 2) 428 .WithTag("CQADDR", 2, 27) 429 .WithReservedBits(29, 3) 430 ; 431 432 Registers.CommandQueueStatus.Define(this) 433 .WithTaggedFlag("CQTIP", 0) 434 .WithTaggedFlag("CQPAUSED", 1) 435 .WithTaggedFlag("CQERR", 2) 436 .WithReservedBits(3, 29) 437 ; 438 439 Registers.CommandQueueFlag.Define(this) 440 .WithTag("CQFLAGS", 0, 16) 441 .WithTag("CQIRQMASK", 16, 16) 442 ; 443 444 Registers.CommandQueueFlagSetClear.Define(this) 445 .WithTag("CQFSET", 0, 8) 446 .WithTag("CQFTGL", 8, 8) 447 .WithTag("CQFCLR", 16, 8) 448 .WithReservedBits(24, 8) 449 ; 450 451 Registers.CommandQueuePauseEnable.Define(this) 452 .WithTag("CQPEN", 0, 16) 453 .WithReservedBits(16, 16) 454 ; 455 456 Registers.CommandQueueCurrentIndexValue.Define(this) 457 .WithTag("CQCURIDX", 0, 8) 458 .WithReservedBits(8, 24) 459 ; 460 461 Registers.CommandQueueEndIndexValue.Define(this) 462 .WithTag("CQENDIDX", 0, 8) 463 .WithReservedBits(8, 24) 464 ; 465 466 Registers.IOModuleStatus.Define(this) 467 .WithTaggedFlag("ERR", 0) 468 .WithFlag(1, FieldMode.Read, name: "CMDACT", valueProviderCallback: _ => status == Status.Active) 469 .WithFlag(2, FieldMode.Read, name: "IDLEST", valueProviderCallback: _ => status == Status.Idle) 470 .WithReservedBits(3, 29) 471 ; 472 473 Registers.SpiModuleMasterConfiguration.Define(this, 0x00200000) 474 .WithTaggedFlag("SPOL", 0) 475 .WithTaggedFlag("SPHA", 1) 476 .WithTaggedFlag("FULLDUP", 2) 477 .WithReservedBits(3, 13) 478 .WithTaggedFlag("WTFC", 16) 479 .WithTaggedFlag("RDFC", 17) 480 .WithTaggedFlag("MOSIINV", 18) 481 .WithReservedBits(19, 1) 482 .WithTaggedFlag("WTFCIRQ", 20) 483 .WithTaggedFlag("WTFCPOL", 21) 484 .WithTaggedFlag("RDFCPOL", 22) 485 .WithTaggedFlag("SPILSB", 23) 486 .WithTag("DINDLY", 24, 3) 487 .WithTag("DOUTDLY", 27, 3) 488 .WithTaggedFlag("MSPIRST", 30) 489 .WithReservedBits(31, 1) 490 ; 491 492 // Some software expects values written to 493 // SDADLY, SCLENDLY and SDAENDLY to be retined 494 Registers.I2CMasterConfiguration.Define(this) 495 .WithFlag(0, out i2cExtendedAdressingMode, name: "ADDRSZ") 496 .WithTaggedFlag("I2CLSB", 1) 497 .WithTaggedFlag("ARBEN", 2) 498 .WithReservedBits(3, 1) 499 .WithValueField(4, 2, name: "SDADLY") 500 .WithFlag(6, name: "MI2CRST") 501 .WithReservedBits(7, 1) 502 .WithValueField(8, 4, name: "SCLENDLY") 503 .WithValueField(12, 4, name: "SDAENDLY") 504 .WithTag("SMPCNT", 16, 8) 505 .WithTaggedFlag("STRDIS", 24) 506 .WithReservedBits(25, 7) 507 ; 508 509 Registers.I2CDeviceConfiguration.Define(this) 510 .WithValueField(0, 10, name: "DEVADDR", 511 valueProviderCallback: _ => i2cSlaveAddress, 512 writeCallback: (_, value) => 513 { 514 if(!i2cExtendedAdressingMode.Value && value > 0x7F) 515 { 516 value &= 0x7F; 517 this.Log(LogLevel.Warning, "Tried to set 10-bit address with extended mode disabled; truncated to 7-bit"); 518 } 519 i2cSlaveAddress = (uint)value; 520 }) 521 .WithReservedBits(10, 22) 522 ; 523 524 Registers.I2SControl.Define(this) 525 .WithTaggedFlag("I2SEN", 0) 526 .WithTaggedFlag("RXTXN", 1) 527 .WithTaggedFlag("CLKMS", 2) 528 .WithTaggedFlag("SE", 3) 529 .WithTag("CHANSIZE", 4, 5) 530 .WithTag("SAMPLESIZE", 9, 5) 531 .WithTag("BOFFSET", 14, 5) 532 .WithTag("CHANCNT", 19, 3) 533 .WithTaggedFlag("LSBFIRST", 22) 534 .WithTaggedFlag("CLKGAP", 23) 535 .WithTag("CTRLSPARE", 24, 8) 536 ; 537 538 Registers.I2SClockControl.Define(this) 539 .WithTaggedFlag("ASRCEN", 0) 540 .WithTaggedFlag("ASRCCLKSEL", 1) 541 .WithTag("I2SCLKSEL", 2, 2) 542 .WithTag("ASEL", 4, 3) 543 .WithReservedBits(7, 25) 544 ; 545 546 Registers.I2SFrameSyncControl.Define(this) 547 .WithTag("FSLEN", 0, 5) 548 .WithTaggedFlag("FSPOL", 5) 549 .WithTaggedFlag("FSEDGE", 6) 550 .WithReservedBits(7, 1) 551 .WithTag("FSOFFSET", 8, 4) 552 .WithReservedBits(12, 20) 553 ; 554 555 Registers.IOModuleDebug.Define(this) 556 .WithTaggedFlag("DBGEN", 0) 557 .WithTaggedFlag("IOCLKON", 1) 558 .WithTaggedFlag("APBCLKON", 2) 559 .WithTag("DBGDATA", 3, 29) 560 ; 561 } 562 IncomingFifoCountChangeAction(Fifo fifo, uint currentCount, uint previousCount)563 private void IncomingFifoCountChangeAction(Fifo fifo, uint currentCount, uint previousCount) 564 { 565 // Ignore change in count if it didn't decrease 566 if(currentCount >= previousCount) 567 { 568 return; 569 } 570 571 // Receive more data if there's a space to receive and Read command awaits. 572 if(!fifo.Full && activeTransactionCommand.Value == Commands.Read && activeTransactionSizeLeft.Value > 0) 573 { 574 if(fifo.TryReceiveAndPush(ReceiveData)) 575 { 576 this.Log(LogLevel.Noisy, "Unfinished read command found and incoming FIFO has a space; receiving..."); 577 } 578 } 579 UpdateFifoThresholdInterruptStatus(); 580 } 581 InterruptStatusSet(IoMasterInterrupts interrupt, bool value = true)582 private void InterruptStatusSet(IoMasterInterrupts interrupt, bool value = true) 583 { 584 ioMasterInterruptsStatusFlags[(int)interrupt].Value = value; 585 UpdateIRQ(); 586 } 587 IsTransactionValid(Commands command, uint size, uint offsetCount, int spiSlaveSelect, out string errorMessage)588 private bool IsTransactionValid(Commands command, uint size, uint offsetCount, int spiSlaveSelect, out string errorMessage) 589 { 590 errorMessage = null; 591 if(command != Commands.Read && command != Commands.Write) 592 { 593 errorMessage = $"Unsupported transaction command: {command}"; 594 } 595 else if(command == Commands.Read && size == 0) 596 { 597 errorMessage = "Read transaction with size 0 is illegal."; 598 } 599 else if(command == Commands.Write && size != 0 && outgoingFifo.Empty) 600 { 601 errorMessage = $"{size}-byte write requested but the outgoing FIFO is empty."; 602 } 603 else if(offsetCount > 5) 604 { 605 errorMessage = $"Invalid transaction offset count: {offsetCount}"; 606 } 607 else if(spiMasterEnabled.Value && !spiPeripherals.ContainsKey(spiSlaveSelect)) 608 { 609 errorMessage = $"Transaction cannot be completed. There's no SPI peripheral registered with ID: {spiSlaveSelect}"; 610 } 611 else if(i2cMasterEnabled.Value && !i2cPeripherals.ContainsKey((int)i2cSlaveAddress)) 612 { 613 errorMessage = $"Transaction cannot be completed. There's no I2C peripheral registered with ID: {i2cSlaveAddress}"; 614 } 615 return errorMessage == null; 616 } 617 OutgoingFifoCountChangeAction(Fifo fifo, uint currentCount, uint previousCount)618 private void OutgoingFifoCountChangeAction(Fifo fifo, uint currentCount, uint previousCount) 619 { 620 // Ignore if we are currently sending data 621 if(currentCount <= previousCount) 622 { 623 return; 624 } 625 626 // Send more data if there's data to send and Write command awaits. 627 if(!fifo.Empty && activeTransactionCommand.Value == Commands.Write && activeTransactionSizeLeft.Value > 0) 628 { 629 if(fifo.TryPop(out var value)) 630 { 631 this.Log(LogLevel.Noisy, "Unfinished write command found and outgoing FIFO contains data; sending 0x{0:X}...", value); 632 SendData(value); 633 } 634 } 635 UpdateFifoThresholdInterruptStatus(); 636 } 637 ReceiveData()638 private uint ReceiveData() 639 { 640 if(activeTransactionSizeLeft.Value > 0) 641 { 642 var bytesToReceive = Math.Min(4, (uint)activeTransactionSizeLeft.Value); 643 uint result = 0; 644 if(ActiveTransactionPeripheral is ISPIPeripheral spiPeripheral) 645 { 646 for(int i = 0; i < bytesToReceive; i++) 647 { 648 BitHelper.UpdateWithShifted(ref result, spiPeripheral.Transmit(0), (int)(i * 8), 8); 649 } 650 } 651 else if(ActiveTransactionPeripheral is II2CPeripheral i2cPeripheral) 652 { 653 var data = i2cPeripheral.Read((int)bytesToReceive); 654 foreach(var item in data.Select((value, index) => new { index, value })) 655 { 656 BitHelper.UpdateWithShifted(ref result, item.value, (int)(item.index * 8), 8); 657 } 658 } 659 else 660 { 661 // This code should be unreachable 662 throw new ArgumentException("Peripheral has to be selected before receiving data!"); 663 } 664 activeTransactionSizeLeft.Value -= bytesToReceive; 665 TryFinishTransaction(); 666 return result; 667 } 668 else 669 { 670 throw new ArgumentException($"Data shouldn't be received if activeTransactionSizeLeft equals 0!"); 671 } 672 } 673 Send(uint data, uint size, bool forceMSBFirst = false)674 private void Send(uint data, uint size, bool forceMSBFirst = false) 675 { 676 if(ActiveTransactionPeripheral is ISPIPeripheral spiPeripheral) 677 { 678 var dataBytes = BitHelper.GetBytesFromValue(data, (int)size, reverse: !forceMSBFirst); 679 foreach(var dataByte in dataBytes) 680 { 681 spiPeripheral.Transmit(dataByte); 682 this.Log(LogLevel.Noisy, "Byte sent to the SPI peripheral: 0x{0:X}", dataByte); 683 } 684 } 685 else if(ActiveTransactionPeripheral is II2CPeripheral i2cPeripheral) 686 { 687 var dataBytes = BitHelper.GetBytesFromValue(data, (int)size, reverse: !forceMSBFirst); 688 i2cPeripheral.Write(dataBytes); 689 this.Log(LogLevel.Noisy, "{0} byte(s) sent to the I2C peripheral: 0x{0:X08}", size, data); 690 } 691 else 692 { 693 // This code should be unreachable 694 throw new ArgumentException("Peripheral has to be selected before sending data!"); 695 } 696 } 697 SendData(uint value)698 private void SendData(uint value) 699 { 700 if(activeTransactionSizeLeft.Value > 0) 701 { 702 var bytesToSend = Math.Min(4, (uint)activeTransactionSizeLeft.Value); 703 Send(value, bytesToSend); 704 activeTransactionSizeLeft.Value -= bytesToSend; 705 TryFinishTransaction(); 706 } 707 else 708 { 709 throw new ArgumentException("Data shouldn't be sent if activeTransactionSizeLeft equals 0!"); 710 } 711 } 712 SendTransactionOffset(uint count)713 private void SendTransactionOffset(uint count) 714 { 715 // Depending on the offset count: 716 // * for high=0xDEADBEEF, low=0x12: count=1 sends only 0x12, count=2: 0xEF12... 717 // * transfer always begins with the MSB of the resulting value (0xEF for the count=2 example). 718 if(count > 0) 719 { 720 if(count > 1) 721 { 722 Send((uint)transactionOffsetHigh.Value, count - 1, forceMSBFirst: true); 723 } 724 Send((uint)transactionOffsetLow.Value, 1); 725 } 726 } 727 TryFinishTransaction()728 private bool TryFinishTransaction() 729 { 730 if(activeTransactionSizeLeft.Value == 0) 731 { 732 this.Log(LogLevel.Noisy, "Command completed: {0}", activeTransactionCommand.Value); 733 if(activeTransactionContinue) 734 { 735 this.Log(LogLevel.Noisy, "The transmission won't be finished; the CONT flag was sent with the command."); 736 } 737 else 738 { 739 if(ActiveTransactionPeripheral is ISPIPeripheral spiPeripheral) 740 { 741 spiPeripheral.FinishTransmission(); 742 } 743 else if(ActiveTransactionPeripheral is II2CPeripheral i2cPeripheral) 744 { 745 i2cPeripheral.FinishTransmission(); 746 } 747 else 748 { 749 // This code should be unreachable 750 throw new ArgumentException("Trying to finish transaction for which peripherial wasn't chosen"); 751 } 752 } 753 activeTransactionCommand.Value = Commands.None; 754 755 // Not sure if these are valid for the continuous transmission. 756 activeTransactionStatus.Value = Status.Idle; 757 status = Status.Idle; 758 InterruptStatusSet(IoMasterInterrupts.CommandComplete); 759 760 return true; 761 } 762 return false; 763 } 764 UpdateFifoThresholdInterruptStatus()765 private void UpdateFifoThresholdInterruptStatus() 766 { 767 InterruptStatusSet(IoMasterInterrupts.FifoThreshold, value: 768 outgoingFifo.BytesCount < fifoInterruptWriteThreshold.Value 769 || incomingFifo.BytesCount > fifoInterruptReadThreshold.Value 770 ); 771 } 772 UpdateIRQ()773 private void UpdateIRQ() 774 { 775 var newIrqState = false; 776 for(var i = 0; i < IoMasterInterruptsCount; i++) 777 { 778 if(ioMasterInterruptsEnableFlags[i].Value && ioMasterInterruptsStatusFlags[i].Value) 779 { 780 newIrqState = true; 781 break; 782 } 783 } 784 785 if(newIrqState != IRQ.IsSet) 786 { 787 this.Log(LogLevel.Debug, "{0} IRQ", newIrqState ? "Setting" : "Resetting"); 788 IRQ.Set(newIrqState); 789 } 790 } 791 792 private void Register<T>(Dictionary<int, T> container, T peripheral, TypedNumberRegistrationPoint<int> registrationPoint) where T: IPeripheral 793 { 794 if(container.ContainsKey(registrationPoint.Address)) 795 { 796 throw new RegistrationException("The specified registration point is already in use."); 797 } 798 container.Add(registrationPoint.Address, peripheral); 799 machine.RegisterAsAChildOf(this, peripheral, registrationPoint); 800 } 801 802 private void Unregister<T>(Dictionary<int, T> container, T peripheral) where T: IPeripheral 803 { 804 var toRemove = container.Where(x => x.Value.Equals(peripheral)).Select(x => x.Key).ToList(); //ToList required, as we remove from the source 805 if(toRemove.Count == 0) 806 { 807 throw new RegistrationException("The specified peripheral was never registered."); 808 } 809 foreach(var key in toRemove) 810 { 811 container.Remove(key); 812 } 813 machine.UnregisterAsAChildOf(this, peripheral); 814 } 815 816 private string PrettyPendingPeripheral 817 { 818 get 819 { 820 if(spiMasterEnabled.Value) 821 { 822 return $"SPI#{spiSlaveSelect.Value}"; 823 } 824 else if(i2cMasterEnabled.Value) 825 { 826 return $"I2C#{i2cSlaveAddress}"; 827 } 828 return String.Empty; 829 } 830 } 831 832 private string PrettyActivePeripheral 833 { 834 get 835 { 836 if(spiMasterEnabled.Value) 837 { 838 return $"SPI#{activeSpiSlaveSelect}"; 839 } 840 else if(i2cMasterEnabled.Value) 841 { 842 return $"I2C#{i2cSlaveAddress}"; 843 } 844 return String.Empty; 845 } 846 } 847 848 private IPeripheral ActiveTransactionPeripheral 849 { 850 get 851 { 852 if(activeTransactionCommand.Value == Commands.None) 853 { 854 return null; 855 } 856 else if(spiMasterEnabled.Value) 857 { 858 return spiPeripherals[activeSpiSlaveSelect]; 859 } 860 else if(i2cMasterEnabled.Value) 861 { 862 return i2cPeripherals[(int)i2cSlaveAddress]; 863 } 864 return null; 865 } 866 } 867 868 private IEnumRegisterField<Commands> activeTransactionCommand; 869 private IValueRegisterField activeTransactionSizeLeft; 870 private IEnumRegisterField<Status> activeTransactionStatus; 871 private IValueRegisterField fifoInterruptReadThreshold; 872 private IValueRegisterField fifoInterruptWriteThreshold; 873 private IFlagRegisterField[] ioMasterInterruptsEnableFlags; 874 private IFlagRegisterField[] ioMasterInterruptsStatusFlags; 875 private IFlagRegisterField spiMasterEnabled; 876 private IFlagRegisterField i2cMasterEnabled; 877 private IEnumRegisterField<Commands> transactionCommand; 878 private IFlagRegisterField transactionContinue; 879 private IValueRegisterField transactionOffsetCount; 880 private IValueRegisterField transactionOffsetHigh; 881 private IValueRegisterField transactionOffsetLow; 882 private IValueRegisterField transactionSize; 883 private IValueRegisterField spiSlaveSelect; 884 private IFlagRegisterField writePopToAdvanceReadPointer; 885 private IFlagRegisterField i2cExtendedAdressingMode; 886 887 private bool activeTransactionContinue; 888 private int activeSpiSlaveSelect; 889 private uint i2cSlaveAddress; 890 private Status status; 891 892 /* 893 Both FIFOs occupy a single 64-byte memory: 894 * 0x00 -- 0x1F outgoingFifo: "FIFO 0 (written by MCU, read by interface)", 895 * 0x20 -- 0x3F incomingFifo: "FIFO 1 (written by interface, read by MCU)" 896 Queues aren't used because random access is also needed. 897 */ 898 private readonly Fifo incomingFifo; 899 private readonly Fifo outgoingFifo; 900 private readonly Dictionary<int, ISPIPeripheral> spiPeripherals; 901 private readonly Dictionary<int, II2CPeripheral> i2cPeripherals; 902 private readonly IMachine machine; 903 904 private const int IoMasterInterruptsCount = 15; 905 private const int MaxSpiPeripheralsConnected = 4; 906 907 private class Fifo 908 { Fifo(string name, IPeripheral owner)909 public Fifo(string name, IPeripheral owner) 910 { 911 this.name = name; 912 this.owner = owner; 913 Reset(); 914 } 915 DirectGet(uint index)916 public uint DirectGet(uint index) 917 { 918 if(CheckResetFlag("get")) 919 { 920 return memory[index]; 921 } 922 return 0x0; 923 } 924 DirectSet(uint index, uint value)925 public void DirectSet(uint index, uint value) 926 { 927 if(CheckResetFlag("set")) 928 { 929 memory[index] = value; 930 } 931 } 932 Reset()933 public void Reset() 934 { 935 SoftwareReset(); 936 resetFlag = false; 937 } 938 ToString()939 public override string ToString() 940 { 941 StringBuilder builder = new StringBuilder(); 942 builder.AppendFormat("{0}: ", name); 943 builder.AppendFormat("Count: {0}; values: ", Count, headIndex, tailIndex); 944 for(int index = headIndex; index < headIndex + Count; index++) 945 { 946 builder.AppendFormat("0x{0:X8} ", memory[index % DoubleWordCapacity]); 947 } 948 return builder.ToString(); 949 } 950 TryAdvancePointer()951 public bool TryAdvancePointer() 952 { 953 if(CheckResetFlag("advance the pointer") && Check(!Empty, "Cannot advance the pointer; FIFO is empty.")) 954 { 955 // Clear the current head. It won't be read with Peek/Pop without a prior 956 // setting it with Push but it could be read with a random access. 957 memory[headIndex] = 0x0; 958 959 headIndex = (headIndex + 1) % DoubleWordCapacity; 960 Count--; 961 return true; 962 } 963 return false; 964 } 965 TryPeek(out uint value, string actionName = R)966 public bool TryPeek(out uint value, string actionName = "peek") 967 { 968 if(CheckResetFlag(actionName) && Check(!Empty, $"Cannot {actionName}; FIFO is empty.")) 969 { 970 value = memory[headIndex]; 971 return true; 972 } 973 value = 0x0; 974 return false; 975 } 976 TryPop(out uint value)977 public bool TryPop(out uint value) 978 { 979 if(TryPeek(out value, actionName: "pop")) 980 { 981 TryAdvancePointer(); 982 return true; 983 } 984 return false; 985 } 986 TryPush(uint value)987 public bool TryPush(uint value) 988 { 989 return TryReceiveAndPush(() => value); 990 } 991 992 // To prevent losing the received value, the receiveFunction will only be called if push is possible. TryReceiveAndPush(Func<uint> receiveFunction)993 public bool TryReceiveAndPush(Func<uint> receiveFunction) 994 { 995 if(CheckResetFlag("push") && Check(!Full, "Cannot push; FIFO is full.")) 996 { 997 Push(receiveFunction()); 998 return true; 999 } 1000 return false; 1001 } 1002 1003 public Action<Fifo, uint, uint> CountChangeAction { get; set; } 1004 1005 public uint BytesCapacity => DoubleWordCapacity * 4; 1006 public uint BytesCount => Count * 4; 1007 public uint BytesLeft => (DoubleWordCapacity * 4) - BytesCount; 1008 public bool Empty => Count == 0; 1009 public bool Full => Count == DoubleWordCapacity; 1010 public uint Pointer => (uint)headIndex; 1011 1012 public bool ResetFlag 1013 { 1014 get => resetFlag; 1015 set 1016 { 1017 if(!resetFlag && value) 1018 { 1019 SoftwareReset(); 1020 } 1021 resetFlag = value; 1022 } 1023 } 1024 Check(bool condition, string errorMessage)1025 private bool Check(bool condition, string errorMessage) 1026 { 1027 if(!condition) 1028 { 1029 owner.DebugLog("{0}: {1}", name, errorMessage); 1030 return false; 1031 } 1032 return true; 1033 } 1034 CheckResetFlag(string actionName)1035 private bool CheckResetFlag(string actionName) 1036 { 1037 return Check(!resetFlag, $"Cannot {actionName}; the reset flag is set."); 1038 } 1039 ClearMemory()1040 private void ClearMemory() 1041 { 1042 for(int i = 0; i < DoubleWordCapacity; i++) 1043 { 1044 memory[i] = 0x0; 1045 } 1046 } 1047 Push(uint value)1048 private void Push(uint value) 1049 { 1050 if(Full) 1051 { 1052 throw new ArgumentException($"Cannot push; the {name} is full!"); 1053 } 1054 tailIndex = (tailIndex + 1) % DoubleWordCapacity; 1055 Count++; 1056 memory[tailIndex] = value; 1057 } 1058 SoftwareReset()1059 private void SoftwareReset() 1060 { 1061 ClearMemory(); 1062 Count = 0; 1063 headIndex = 0; 1064 tailIndex = -1; 1065 } 1066 1067 private uint Count 1068 { 1069 get => count; 1070 set 1071 { 1072 if(value < 0 || value > DoubleWordCapacity) 1073 { 1074 throw new ArgumentException($"{name}: Invalid value for Count: {value} (Capacity: {DoubleWordCapacity})"); 1075 } 1076 var previousCount = count; 1077 count = value; 1078 CountChangeAction?.Invoke(this, count, previousCount); 1079 } 1080 } 1081 1082 private uint count; 1083 private int headIndex; 1084 private bool resetFlag; 1085 private int tailIndex = -1; 1086 1087 private readonly uint[] memory = new uint[DoubleWordCapacity]; 1088 private readonly string name; 1089 private readonly IPeripheral owner; 1090 1091 private const int DoubleWordCapacity = 8; 1092 } 1093 1094 private enum Commands 1095 { 1096 None = 0x0, 1097 Write = 0x1, 1098 Read = 0x2, 1099 TestModeWrite = 0x3, 1100 TestModeRead = 0x4, 1101 } 1102 1103 private enum Status 1104 { 1105 Error = 0x1, 1106 Active = 0x2, 1107 Idle = 0x4, 1108 Wait = 0x6, 1109 } 1110 1111 private enum IoMasterInterrupts 1112 { 1113 CommandComplete, 1114 FifoThreshold, 1115 ReadFifoUnderflow, 1116 WriteFifoOverflow, 1117 I2CNak, 1118 IllegalFifoAccess, 1119 IllegalCommand, 1120 StartCommand, 1121 StopCommand, 1122 ArbitrationLoss, 1123 DmaComplete, 1124 DmaError, 1125 CommandQueuePaused, 1126 // No sure what "UPD" means here. This is the full description: 1127 // "CQ write operation performed a register write with the register address bit 0 set to 1. 1128 // The low address bits in the CQ address fields are unused and bit 0 can be used to trigger 1129 // an interrupt to indicate when this register write is performed by the CQ operation." 1130 CommandQueueUPD, 1131 CommandQueueError, 1132 } 1133 1134 private enum Registers : long 1135 { 1136 OutgoingFifoAccessPort = 0x0, 1137 IncomingFifoAccessPort = 0x20, 1138 FifoSizeAndRemainingSlotsOpenValues = 0x100, 1139 FifoThresholdConfiguration = 0x104, 1140 FifoPop = 0x108, 1141 FifoPush = 0x10C, 1142 FifoControl = 0x110, 1143 FifoPointers = 0x114, 1144 IOClockConfiguration = 0x118, 1145 SubmoduleControl = 0x11C, 1146 CommandAndOffset = 0x120, 1147 DcxControlAndCeUsageSelection = 0x124, 1148 HighOrderBytesBfOffsetForIOTransaction = 0x128, 1149 CommandStatus = 0x12C, 1150 IOMasterInterruptsEnable = 0x200, 1151 IOMasterInterruptsStatus = 0x204, 1152 IOMasterInterruptsClear = 0x208, 1153 IOMasterInterruptsSet = 0x20C, 1154 DmaTriggerEnable = 0x210, 1155 DmaTriggerStatus = 0x214, 1156 DmaConfiguration = 0x218, 1157 DmaTotalTransferCount = 0x21C, 1158 DmaTargetAddress = 0x220, 1159 DmaStatus = 0x224, 1160 CommandQueueConfiguration = 0x228, 1161 CommandQueueTargetReadAddress = 0x22C, 1162 CommandQueueStatus = 0x230, 1163 CommandQueueFlag = 0x234, 1164 CommandQueueFlagSetClear = 0x238, 1165 CommandQueuePauseEnable = 0x23C, 1166 CommandQueueCurrentIndexValue = 0x240, 1167 CommandQueueEndIndexValue = 0x244, 1168 IOModuleStatus = 0x248, 1169 SpiModuleMasterConfiguration = 0x280, 1170 I2CMasterConfiguration = 0x2C0, 1171 I2CDeviceConfiguration = 0x2C4, 1172 I2SControl = 0x300, 1173 I2SClockControl = 0x304, 1174 I2SFrameSyncControl = 0x308, 1175 IOModuleDebug = 0x388, 1176 } 1177 1178 private enum SubmoduleTypes : uint 1179 { 1180 SpiMaster = 0x0, 1181 I2CMaster = 0x1, 1182 SpiSlave = 0x2, 1183 I2CSlave = 0x3, 1184 I2SMaster_Slave = 0x4, 1185 NotInstalled = 0x7, 1186 } 1187 } 1188 } 1189