1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // Copyright (c) 2022-2025 Silicon Labs 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 9 using System; 10 using System.Collections.Generic; 11 using System.Diagnostics; 12 using System.IO; 13 using Antmicro.Renode.Core; 14 using Antmicro.Renode.Core.Structure; 15 using Antmicro.Renode.Core.Structure.Registers; 16 using Antmicro.Renode.Exceptions; 17 using Antmicro.Renode.Logging; 18 using Antmicro.Renode.Utilities; 19 using Antmicro.Renode.Peripherals.Bus; 20 using Antmicro.Renode.Peripherals.SPI; 21 using Antmicro.Renode.Peripherals.Timers; 22 using Antmicro.Renode.Time; 23 using Antmicro.Renode.Peripherals.CPU; 24 using Antmicro.Renode.Peripherals.UART; 25 26 namespace Antmicro.Renode.Peripherals.UART 27 { 28 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.DoubleWordToByte)] 29 public class EFR32xG2_EUSART_2 : UARTBase, IUARTWithBufferState, IDoubleWordPeripheral, IPeripheralContainer<ISPIPeripheral, NullRegistrationPoint> 30 { EFR32xG2_EUSART_2(Machine machine, uint clockFrequency = 19000000)31 public EFR32xG2_EUSART_2(Machine machine, uint clockFrequency = 19000000) : base(machine) 32 { 33 this.machine = machine; 34 uartClockFrequency = clockFrequency; 35 36 TransmitIRQ = new GPIO(); 37 ReceiveIRQ = new GPIO(); 38 RxFifoLevel = new GPIO(); 39 TxFifoLevel = new GPIO(); 40 RxFifoLevelGpioSignal = new GPIO(); 41 42 registersCollection = BuildRegistersCollection(); 43 TxFifoLevel.Set(true); 44 } 45 Reset()46 public override void Reset() 47 { 48 base.Reset(); 49 isEnabled = false; 50 spiSlaveDevice = null; 51 TxFifoLevel.Set(true); 52 } 53 ReadDoubleWord(long offset)54 public uint ReadDoubleWord(long offset) 55 { 56 var value = ReadRegister(offset); 57 return value; 58 } 59 ReadRegister(long offset, bool internal_read = false)60 private uint ReadRegister(long offset, bool internal_read = false) 61 { 62 var result = 0U; 63 long internal_offset = offset; 64 65 // Set, Clear, Toggle registers should only be used for write operations. But just in case we convert here as well. 66 if (offset >= SetRegisterOffset && offset < ClearRegisterOffset) 67 { 68 // Set register 69 internal_offset = offset - SetRegisterOffset; 70 if(!internal_read) 71 { 72 this.Log(LogLevel.Noisy, "SET Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset); 73 } 74 } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset) 75 { 76 // Clear register 77 internal_offset = offset - ClearRegisterOffset; 78 if(!internal_read) 79 { 80 this.Log(LogLevel.Noisy, "CLEAR Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset); 81 } 82 } else if (offset >= ToggleRegisterOffset) 83 { 84 // Toggle register 85 internal_offset = offset - ToggleRegisterOffset; 86 if(!internal_read) 87 { 88 this.Log(LogLevel.Noisy, "TOGGLE Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset); 89 } 90 } 91 92 if(!registersCollection.TryRead(internal_offset, out result)) 93 { 94 if(!internal_read) 95 { 96 this.Log(LogLevel.Noisy, "Unhandled read at offset 0x{0:X} ({1}).", internal_offset, (Registers)internal_offset); 97 } 98 } 99 else 100 { 101 if(!internal_read) 102 { 103 this.Log(LogLevel.Noisy, "Read at offset 0x{0:X} ({1}), returned 0x{2:X}.", internal_offset, (Registers)internal_offset, result); 104 } 105 } 106 107 return result; 108 } 109 WriteDoubleWord(long offset, uint value)110 public void WriteDoubleWord(long offset, uint value) 111 { 112 WriteRegister(offset, value); 113 } 114 WriteRegister(long offset, uint value, bool internal_write = false)115 private void WriteRegister(long offset, uint value, bool internal_write = false) 116 { 117 machine.ClockSource.ExecuteInLock(delegate { 118 long internal_offset = offset; 119 uint internal_value = value; 120 121 if (offset >= SetRegisterOffset && offset < ClearRegisterOffset) 122 { 123 // Set register 124 internal_offset = offset - SetRegisterOffset; 125 uint old_value = ReadRegister(internal_offset, true); 126 internal_value = old_value | value; 127 this.Log(LogLevel.Noisy, "SET Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}, SET_value=0x{3:X}, old_value=0x{4:X}, new_value=0x{5:X}", (Registers)internal_offset, offset, internal_offset, value, old_value, internal_value); 128 } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset) 129 { 130 // Clear register 131 internal_offset = offset - ClearRegisterOffset; 132 uint old_value = ReadRegister(internal_offset, true); 133 internal_value = old_value & ~value; 134 this.Log(LogLevel.Noisy, "CLEAR Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}, CLEAR_value=0x{3:X}, old_value=0x{4:X}, new_value=0x{5:X}", (Registers)internal_offset, offset, internal_offset, value, old_value, internal_value); 135 } else if (offset >= ToggleRegisterOffset) 136 { 137 // Toggle register 138 internal_offset = offset - ToggleRegisterOffset; 139 uint old_value = ReadRegister(internal_offset, true); 140 internal_value = old_value ^ value; 141 this.Log(LogLevel.Noisy, "TOGGLE Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}, TOGGLE_value=0x{3:X}, old_value=0x{4:X}, new_value=0x{5:X}", (Registers)internal_offset, offset, internal_offset, value, old_value, internal_value); 142 } 143 144 this.Log(LogLevel.Noisy, "Write at offset 0x{0:X} ({1}), value 0x{2:X}.", internal_offset, (Registers)internal_offset, internal_value); 145 146 if(!registersCollection.TryWrite(internal_offset, internal_value)) 147 { 148 this.Log(LogLevel.Noisy, "Unhandled write at offset 0x{0:X} ({1}), value 0x{2:X}.", internal_offset, (Registers)internal_offset, internal_value); 149 return; 150 } 151 }); 152 } 153 BuildRegistersCollection()154 private DoubleWordRegisterCollection BuildRegistersCollection() 155 { 156 var registerDictionary = new Dictionary<long, DoubleWordRegister> 157 { 158 {(long)Registers.InterruptFlag, new DoubleWordRegister(this) 159 .WithFlag(0, out txCompleteInterrupt, name: "TXCIF") 160 .WithFlag(1, out txBufferLevelInterrupt, name: "TXFLIF") 161 .WithFlag(2, out rxDataValidInterrupt, name: "RXFLIF") 162 .WithFlag(3, out rxBufferFullInterrupt, name: "RXFULLIF") 163 .WithFlag(4, out rxOverflowInterrupt, name: "RXOFIF") 164 .WithFlag(5, out rxUnderflowInterrupt, name: "RXUFIF") 165 .WithTaggedFlag("TXOFIF", 6) 166 .WithTaggedFlag("TXUFIF", 7) 167 .WithTaggedFlag("PERRIF", 8) 168 .WithTaggedFlag("FERRIF", 9) 169 .WithTaggedFlag("MPAFIF", 10) 170 .WithTaggedFlag("LOADERRIF", 11) 171 .WithTaggedFlag("CCFIF", 12) 172 .WithTaggedFlag("TXIDLEIF", 13) 173 .WithReservedBits(14, 2) 174 .WithTaggedFlag("CSWUIF", 16) 175 .WithReservedBits(17, 1) 176 .WithTaggedFlag("STARTFIF", 18) 177 .WithTaggedFlag("SIGFIF", 19) 178 .WithReservedBits(20, 4) 179 .WithTaggedFlag("AUTOBAUDDONEIF", 24) 180 .WithTaggedFlag("RXTOIF", 25) 181 .WithReservedBits(26, 6) 182 .WithWriteCallback((_, __) => UpdateInterrupts()) 183 }, 184 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 185 .WithFlag(0, out txCompleteInterruptEnable, name: "TXCIEN") 186 .WithFlag(1, out txBufferLevelInterruptEnable, name: "TXFLIEN") 187 .WithFlag(2, out rxDataValidInterruptEnable, name: "RXFLIEN") 188 .WithFlag(3, out rxBufferFullInterruptEnable, name: "RXFULLIEN") 189 .WithFlag(4, out rxOverflowInterruptEnable, name: "RXOFIEN") 190 .WithFlag(5, out rxUnderflowInterruptEnable, name: "RXUFIEN") 191 .WithTaggedFlag("TXOFIEN", 6) 192 .WithTaggedFlag("TXUFIEN", 7) 193 .WithTaggedFlag("PERRIEN", 8) 194 .WithTaggedFlag("FERRIEN", 9) 195 .WithTaggedFlag("MPAFIEN", 10) 196 .WithTaggedFlag("LOADERRIEN", 11) 197 .WithTaggedFlag("CCFIEN", 12) 198 .WithTaggedFlag("TXIDLEIEN", 13) 199 .WithReservedBits(14, 2) 200 .WithTaggedFlag("CSWUIEN", 16) 201 .WithReservedBits(17, 1) 202 .WithTaggedFlag("STARTFIEN", 18) 203 .WithTaggedFlag("SIGFIEN", 19) 204 .WithReservedBits(20, 4) 205 .WithTaggedFlag("AUTOBAUDDONEIEN", 24) 206 .WithTaggedFlag("RXTOIEN", 25) 207 .WithReservedBits(26, 6) 208 .WithWriteCallback((_, __) => UpdateInterrupts()) 209 }, 210 {(long)Registers.Enable, new DoubleWordRegister(this) 211 .WithFlag(0, changeCallback: (_, value) => { isEnabled = value; }, name: "EN") 212 .WithTaggedFlag("DISABLING",1) 213 .WithReservedBits(2, 30) 214 }, 215 {(long)Registers.Cfg_0, new DoubleWordRegister(this) 216 .WithEnumField(0, 1, out operationModeField, name: "SYNC") 217 .WithTaggedFlag("LOOPBK", 1) 218 .WithTaggedFlag("CCEN", 2) 219 .WithTaggedFlag("MPM", 3) 220 .WithTaggedFlag("MPAB", 4) 221 .WithEnumField(5, 3, out oversamplingField, name: "OVS") 222 .WithReservedBits(8, 2) 223 .WithTaggedFlag("MSBF", 10) 224 .WithReservedBits(11, 2) 225 .WithTaggedFlag("RXINV", 13) 226 .WithTaggedFlag("TXINV", 14) 227 .WithReservedBits(15, 2) 228 .WithTaggedFlag("AUTOTRI", 17) 229 .WithReservedBits(18, 2) 230 .WithTaggedFlag("SKIPPERRF", 20) 231 .WithReservedBits(21, 1) 232 .WithTaggedFlag("ERRSDMA", 22) 233 .WithTaggedFlag("ERRSRX", 23) 234 .WithTaggedFlag("ERRSTX", 24) 235 .WithReservedBits(25, 5) 236 .WithTaggedFlag("MVDIS", 30) 237 .WithTaggedFlag("AUTOBAUDEN", 31) 238 }, 239 {(long)Registers.Cfg_1, new DoubleWordRegister(this) 240 .WithTaggedFlag("DBGHALT", 0) 241 .WithTaggedFlag("CTSINV", 1) 242 .WithTaggedFlag("CTSEN", 2) 243 .WithTaggedFlag("RTSINV", 3) 244 .WithTag("RXTIMEOUT",4,3) 245 .WithReservedBits(7,2) 246 .WithTaggedFlag("TXDMAWU", 9) 247 .WithTaggedFlag("RXDMAWU", 10) 248 .WithTaggedFlag("SFUBRX", 11) 249 .WithReservedBits(12,3) 250 .WithTaggedFlag("RXPRSEN", 15) 251 .WithTag("TXFIW",16,4) 252 .WithReservedBits(20,2) 253 .WithTag("RTSRXFW",22,4) 254 .WithReservedBits(26,1) 255 .WithValueField(27, 4, out rxWatermark, name: "RXFIW") 256 .WithReservedBits(31,1) 257 258 }, 259 {(long)Registers.Cfg_2, new DoubleWordRegister(this) 260 .WithTaggedFlag("MASTER", 0) 261 .WithTaggedFlag("CLKPOL", 1) 262 .WithTaggedFlag("CLKPHA", 2) 263 .WithTaggedFlag("CSINV", 3) 264 .WithTaggedFlag("AUTOTX", 4) 265 .WithTaggedFlag("AUTOCS", 5) 266 .WithTaggedFlag("CLKPRSEN", 6) 267 .WithTaggedFlag("FORCELOAD", 7) 268 .WithReservedBits(8,16) 269 .WithTag("SDIV",24,8) 270 }, 271 {(long)Registers.FrameCfg, new DoubleWordRegister(this) 272 .WithTag("DATABITS", 0, 4) 273 .WithReservedBits(4, 4) 274 .WithEnumField(8, 2, out parityBitModeField, name: "PARITY") 275 .WithReservedBits(10, 2) 276 .WithEnumField(12, 2, out stopBitsModeField, name: "STOPBITS") 277 .WithReservedBits(14, 18) 278 }, 279 {(long)Registers.DtxDataCfg, new DoubleWordRegister(this) 280 .WithTag("DTXDAT", 0, 16) 281 .WithReservedBits(16, 16) 282 }, 283 {(long)Registers.IrHfCfg, new DoubleWordRegister(this) 284 .WithTaggedFlag("IRHFEN", 0) 285 .WithTag("IRHFPW", 1, 2) 286 .WithTaggedFlag("IRHFFILT", 3) 287 .WithReservedBits(4, 28) 288 }, 289 {(long)Registers.IrLfCfg, new DoubleWordRegister(this) 290 .WithTaggedFlag("IRLFEN", 0) 291 .WithReservedBits(1, 31) 292 }, 293 {(long)Registers.Timing, new DoubleWordRegister(this) 294 .WithTag("TXDELAY", 0, 2) 295 .WithReservedBits(2, 2) 296 .WithTag("CSSETUP", 4, 3) 297 .WithReservedBits(7, 1) 298 .WithTag("CSHOLD", 8, 3) 299 .WithReservedBits(11, 1) 300 .WithTag("ICS", 12, 3) 301 .WithReservedBits(15, 1) 302 .WithTag("SETUPWINDOW", 16, 4) 303 .WithReservedBits(20, 12) 304 }, 305 {(long)Registers.StartFrameCfg, new DoubleWordRegister(this) 306 .WithTag("STARTFRAME", 0, 8) 307 .WithReservedBits(8, 24) 308 }, 309 {(long)Registers.SigFrameCfg, new DoubleWordRegister(this) 310 .WithTag("SIGFRAME", 0, 32) 311 }, 312 {(long)Registers.ClkDiv, new DoubleWordRegister(this) 313 .WithReservedBits(0, 3) 314 .WithValueField(3, 20, out fractionalClockDividerField, name: "DIV") 315 .WithReservedBits(23, 9) 316 }, 317 {(long)Registers.TriggerControl, new DoubleWordRegister(this) 318 .WithTaggedFlag("RXTEN", 0) 319 .WithTaggedFlag("TXTEN", 1) 320 .WithTaggedFlag("AUTOTXTEN", 2) 321 .WithReservedBits(3, 29) 322 }, 323 {(long)Registers.Command, new DoubleWordRegister(this) 324 .WithFlag(0, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) receiverEnableFlag.Value = true; }, name: "RXEN") 325 .WithFlag(1, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) receiverEnableFlag.Value = false; }, name: "RXDIS") 326 .WithFlag(2, FieldMode.Set, writeCallback: (_, newValue) => 327 { 328 if(newValue) 329 { 330 transmitterEnableFlag.Value = true; 331 txBufferLevelInterrupt.Value = true; 332 UpdateInterrupts(); 333 } 334 }, name: "TXEN") 335 .WithFlag(3, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) transmitterEnableFlag.Value = false; }, name: "TXDIS") 336 .WithTaggedFlag("RXBLOCKEN", 4) 337 .WithTaggedFlag("RXBLOCKDIS", 5) 338 .WithTaggedFlag("TXTRIEN", 6) 339 .WithTaggedFlag("TXTRIDIS", 7) 340 .WithTaggedFlag("CLEARTX", 8) 341 .WithReservedBits(9, 23) 342 }, 343 {(long)Registers.RxData, new DoubleWordRegister(this) 344 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: (_) => ReadBuffer(), name: "RXDATA") 345 .WithReservedBits(8, 24) 346 }, 347 {(long)Registers.RxDataPeek, new DoubleWordRegister(this) 348 .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: (_) => PeekBuffer(), name: "RXDATAP") 349 .WithReservedBits(16, 16) 350 }, 351 {(long)Registers.TxData, new DoubleWordRegister(this) 352 .WithValueField(0, 16, FieldMode.Write, writeCallback: (_, v) => HandleTxBufferData((byte)v), name: "TXDATA") 353 .WithReservedBits(16, 16) 354 }, 355 {(long)Registers.Status, new DoubleWordRegister(this, 0x00003040) 356 .WithFlag(0, out receiverEnableFlag, FieldMode.Read, name: "RXENS") 357 .WithFlag(1, out transmitterEnableFlag, FieldMode.Read, name: "TXENS") 358 .WithReservedBits(2,1) 359 .WithTaggedFlag("RXBLOCK", 3) 360 .WithTaggedFlag("TXTRI", 4) 361 .WithFlag(5, out transferCompleteFlag, FieldMode.Read, name: "TXC") 362 .WithTaggedFlag("TXFL", 6) 363 .WithFlag(7, out receiveDataValidFlag, FieldMode.Read, name: "RXFL") 364 .WithFlag(8, FieldMode.Read, valueProviderCallback: _ => Count == BufferSize, name: "RXFULL") 365 .WithReservedBits(9,3) 366 .WithFlag(12, FieldMode.Read, valueProviderCallback: _ => true, name: "RXIDLE") 367 .WithFlag(13, FieldMode.Read, valueProviderCallback: _ => true, name: "TXIDLE") 368 .WithReservedBits(14,2) 369 .WithValueField(16, 5, FieldMode.Read, valueProviderCallback: _ => 0, name: "TXFCNT") 370 .WithReservedBits(21,3) 371 .WithTaggedFlag("AUTOBAUDDONE", 24) 372 .WithTaggedFlag("CLEARTXBUSY", 25) 373 .WithReservedBits(26, 6) 374 }, 375 {(long)Registers.SyncBusy, new DoubleWordRegister(this) 376 .WithTaggedFlag("DIV", 0) 377 .WithTaggedFlag("RXTEN", 1) 378 .WithTaggedFlag("TXTEN", 2) 379 .WithTaggedFlag("RXEN", 3) 380 .WithTaggedFlag("RXDIS", 4) 381 .WithTaggedFlag("TXEN", 5) 382 .WithTaggedFlag("TXDIS", 6) 383 .WithTaggedFlag("RXBLOCKEN", 7) 384 .WithTaggedFlag("RXBLOCKDIS", 8) 385 .WithTaggedFlag("TXTRIEN", 9) 386 .WithTaggedFlag("TXTRIDIS", 10) 387 .WithTaggedFlag("AUTOTXTEN", 11) 388 .WithReservedBits(12, 20) 389 }, 390 {(long)Registers.DaliCfg, new DoubleWordRegister(this) 391 .WithTaggedFlag("DALIEN", 0) 392 .WithTag("DALITXDATABITS", 1, 5) 393 .WithReservedBits(6, 2) 394 .WithTag("DALIRXDATABITS", 8, 5) 395 .WithReservedBits(13, 2) 396 .WithTaggedFlag("DALIRXENDT", 15) 397 .WithReservedBits(16, 16) 398 }, 399 {(long)Registers.Test, new DoubleWordRegister(this) 400 .WithTag("DBGPRSSEL", 0, 2) 401 .WithReservedBits(2, 30) 402 }, 403 }; 404 return new DoubleWordRegisterCollection(this, registerDictionary); 405 } 406 407 public long Size => 0x4000; 408 private readonly Machine machine; 409 private readonly DoubleWordRegisterCollection registersCollection; 410 private const uint SetRegisterOffset = 0x1000; 411 private const uint ClearRegisterOffset = 0x2000; 412 private const uint ToggleRegisterOffset = 0x3000; 413 #region methods Register(ISPIPeripheral peripheral, NullRegistrationPoint registrationPoint)414 public void Register(ISPIPeripheral peripheral, NullRegistrationPoint registrationPoint) 415 { 416 if(spiSlaveDevice != null) 417 { 418 throw new RegistrationException("Cannot register more than one peripheral."); 419 } 420 Machine.RegisterAsAChildOf(this, peripheral, registrationPoint); 421 spiSlaveDevice = peripheral; 422 } 423 Unregister(ISPIPeripheral peripheral)424 public void Unregister(ISPIPeripheral peripheral) 425 { 426 if(peripheral != spiSlaveDevice) 427 { 428 throw new RegistrationException("Trying to unregister not registered device."); 429 } 430 431 Machine.UnregisterAsAChildOf(this, peripheral); 432 spiSlaveDevice = null; 433 } 434 GetRegistrationPoints(ISPIPeripheral peripheral)435 public IEnumerable<NullRegistrationPoint> GetRegistrationPoints(ISPIPeripheral peripheral) 436 { 437 if(peripheral != spiSlaveDevice) 438 { 439 throw new RegistrationException("Trying to obtain a registration point for a not registered device."); 440 } 441 442 return new[] { NullRegistrationPoint.Instance }; 443 } 444 WriteChar(byte value)445 public override void WriteChar(byte value) 446 { 447 if(BufferState == BufferState.Full) 448 { 449 rxOverflowInterrupt.Value = true; 450 UpdateInterrupts(); 451 this.Log(LogLevel.Warning, "RX buffer is full. Dropping incoming byte (0x{0:X})", value); 452 return; 453 } 454 base.WriteChar(value); 455 this.Log(LogLevel.Noisy," Character (0x{0:X}) has been written!",value); 456 } 457 458 IEnumerable<IRegistered<ISPIPeripheral, NullRegistrationPoint>> IPeripheralContainer<ISPIPeripheral, NullRegistrationPoint>.Children 459 { 460 get 461 { 462 return new[] { Registered.Create(spiSlaveDevice, NullRegistrationPoint.Instance) }; 463 } 464 } 465 466 public GPIO TransmitIRQ { get; } 467 public GPIO ReceiveIRQ { get; } 468 public GPIO RxFifoLevel { get; } 469 public GPIO TxFifoLevel { get; } 470 public GPIO RxFifoLevelGpioSignal { get; } 471 472 public override Parity ParityBit { get { return parityBitModeField.Value; } } 473 474 public override Bits StopBits { get { return stopBitsModeField.Value; } } 475 476 public override uint BaudRate 477 { 478 get 479 { 480 var oversample = 1u; 481 switch(oversamplingField.Value) 482 { 483 case OversamplingMode.Times16: 484 oversample = 16; 485 break; 486 case OversamplingMode.Times8: 487 oversample = 8; 488 break; 489 case OversamplingMode.Times6: 490 oversample = 6; 491 break; 492 case OversamplingMode.Times4: 493 oversample = 4; 494 break; 495 case OversamplingMode.Disabled: 496 oversample = 1; 497 break; 498 } 499 return (uint)(uartClockFrequency / (oversample * (1 + ((double)(fractionalClockDividerField.Value << 3)) / 256))); 500 } 501 } 502 503 public BufferState BufferState 504 { 505 get 506 { 507 return bufferState; 508 } 509 510 private set 511 { 512 bufferState = value; 513 BufferStateChanged?.Invoke(value); 514 this.Log(LogLevel.Noisy, "Buffer state: {0}", bufferState); 515 switch(bufferState) 516 { 517 case BufferState.Empty: 518 RxFifoLevel.Set(false); 519 RxFifoLevelGpioSignal.Set(false); 520 break; 521 case BufferState.Ready: 522 if(Count >= (int)rxWatermark.Value + 1) 523 { 524 RxFifoLevel.Set(true); 525 RxFifoLevelGpioSignal.Set(true); 526 rxDataValidInterrupt.Value = true; 527 } 528 else 529 { 530 RxFifoLevel.Set(false); 531 RxFifoLevelGpioSignal.Set(false); 532 } 533 UpdateInterrupts(); 534 break; 535 case BufferState.Full: 536 RxFifoLevel.Set(true); 537 RxFifoLevelGpioSignal.Set(true); 538 rxBufferFullInterrupt.Value = true; 539 UpdateInterrupts(); 540 break; 541 default: 542 this.Log(LogLevel.Error, "Unreachable code. Invalid BufferState value."); 543 return; 544 } 545 } 546 } 547 CharWritten()548 protected override void CharWritten() 549 { 550 if(Count >= (int) rxWatermark.Value + 1) 551 { 552 rxDataValidInterrupt.Value = true; 553 receiveDataValidFlag.Value = true; 554 UpdateInterrupts(); 555 } 556 BufferState = Count == BufferSize ? BufferState.Full : BufferState.Ready; 557 } 558 QueueEmptied()559 protected override void QueueEmptied() 560 { 561 562 rxDataValidInterrupt.Value = false; 563 receiveDataValidFlag.Value = false; 564 BufferState = BufferState.Empty; 565 UpdateInterrupts(); 566 } 567 HandleTxBufferData(byte data)568 private void HandleTxBufferData(byte data) 569 { 570 this.Log(LogLevel.Noisy , "Handle TX buffer data: {0}", (char)data); 571 572 if(!transmitterEnableFlag.Value) 573 { 574 this.Log(LogLevel.Warning, "Trying to send data, but the transmitter is disabled: 0x{0:X}", data); 575 return; 576 } 577 578 transferCompleteFlag.Value = false; 579 if(operationModeField.Value == OperationMode.Synchronous) 580 { 581 if(spiSlaveDevice != null) 582 { 583 var result = spiSlaveDevice.Transmit(data); 584 WriteChar(result); 585 } 586 else 587 { 588 this.Log(LogLevel.Warning, "Writing data in synchronous mode, but no device is currently connected."); 589 WriteChar(0x0); 590 } 591 } 592 else 593 { 594 TransmitCharacter(data); 595 txBufferLevelInterrupt.Value = true; 596 txCompleteInterrupt.Value = true; 597 UpdateInterrupts(); 598 } 599 transferCompleteFlag.Value = true; 600 } 601 ReadBuffer()602 private byte ReadBuffer() 603 { 604 byte character; 605 return TryGetCharacter(out character) ? character : (byte)0; 606 } PeekBuffer()607 private byte PeekBuffer() 608 { 609 byte character; 610 return TryGetCharacter(out character,true) ? character : (byte)0; 611 } 612 UpdateInterrupts()613 private void UpdateInterrupts() 614 { 615 machine.ClockSource.ExecuteInLock(delegate { 616 var txIrq = ((txCompleteInterruptEnable.Value && txCompleteInterrupt.Value) 617 || (txBufferLevelInterruptEnable.Value && txBufferLevelInterrupt.Value)); 618 TransmitIRQ.Set(txIrq); 619 620 var rxIrq = ((rxDataValidInterruptEnable.Value && rxDataValidInterrupt.Value) 621 || (rxBufferFullInterruptEnable.Value && rxBufferFullInterrupt.Value) 622 || (rxOverflowInterruptEnable.Value && rxOverflowInterrupt.Value) 623 || (rxUnderflowInterruptEnable.Value && rxUnderflowInterrupt.Value)); 624 ReceiveIRQ.Set(rxIrq); 625 }); 626 } 627 TrySyncTime()628 private bool TrySyncTime() 629 { 630 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 631 { 632 cpu.SyncTime(); 633 return true; 634 } 635 return false; 636 } 637 GetTime()638 private TimeInterval GetTime() => machine.LocalTimeSource.ElapsedVirtualTime; 639 protected override bool IsReceiveEnabled => receiverEnableFlag.Value; 640 #endregion 641 642 #region fields 643 private bool isEnabled = false; 644 private ISPIPeripheral spiSlaveDevice; 645 public event Action<BufferState> BufferStateChanged; 646 private IEnumRegisterField<OperationMode> operationModeField; 647 private IEnumRegisterField<OversamplingMode> oversamplingField; 648 private IEnumRegisterField<Parity> parityBitModeField; 649 private IEnumRegisterField<Bits> stopBitsModeField; 650 private IValueRegisterField fractionalClockDividerField; 651 private IFlagRegisterField transferCompleteFlag; 652 private IFlagRegisterField receiveDataValidFlag; 653 private IFlagRegisterField receiverEnableFlag; 654 private IValueRegisterField rxWatermark; 655 private IFlagRegisterField transmitterEnableFlag; 656 private readonly uint uartClockFrequency; 657 private BufferState bufferState; 658 private const int BufferSize = 17; // with shift register 659 // Interrupts 660 private IFlagRegisterField txCompleteInterrupt; 661 private IFlagRegisterField txBufferLevelInterrupt; 662 private IFlagRegisterField rxDataValidInterrupt; 663 private IFlagRegisterField rxBufferFullInterrupt; 664 private IFlagRegisterField rxOverflowInterrupt; 665 private IFlagRegisterField rxUnderflowInterrupt; 666 private IFlagRegisterField txCompleteInterruptEnable; 667 private IFlagRegisterField txBufferLevelInterruptEnable; 668 private IFlagRegisterField rxDataValidInterruptEnable; 669 private IFlagRegisterField rxBufferFullInterruptEnable; 670 private IFlagRegisterField rxOverflowInterruptEnable; 671 private IFlagRegisterField rxUnderflowInterruptEnable; 672 #endregion 673 674 #region enums 675 private enum OperationMode 676 { 677 Asynchronous, 678 Synchronous 679 } 680 681 private enum OversamplingMode 682 { 683 Times16, 684 Times8, 685 Times6, 686 Times4, 687 Disabled 688 } 689 690 691 private enum Registers : long // : IRegisterDescription 692 { 693 IpVersion = 0x0000, 694 Enable = 0x0004, 695 Cfg_0 = 0x0008, 696 Cfg_1 = 0x000C, 697 Cfg_2 = 0x0010, 698 FrameCfg = 0x001F, 699 DtxDataCfg = 0x0018, 700 IrHfCfg = 0x001C, 701 IrLfCfg = 0x0020, 702 Timing = 0x0024, 703 StartFrameCfg = 0x0028, 704 SigFrameCfg = 0x002C, 705 ClkDiv = 0x0030, 706 TriggerControl = 0x0034, 707 Command = 0x0038, 708 RxData = 0x003C, 709 RxDataPeek = 0x0040, 710 TxData = 0x0044, 711 Status = 0x0048, 712 InterruptFlag = 0x004C, 713 InterruptEnable = 0x0050, 714 SyncBusy = 0x0054, 715 DaliCfg = 0x0058, 716 Test = 0x0100, 717 //set 718 IpVersion_Set = 0x1000, 719 Enable_Set = 0x1004, 720 Cfg_0_Set = 0x1008, 721 Cfg_1_Set = 0x100C, 722 Cfg_2_Set = 0x1010, 723 FrameCfg_Set = 0x101F, 724 DtxDataCfg_Set = 0x1018, 725 IrHfCfg_Set = 0x101C, 726 IrLfCfg_Set = 0x1020, 727 Timing_Set = 0x1024, 728 StartFrameCfg_Set = 0x1028, 729 SigFrameCfg_Set = 0x102C, 730 ClkDiv_Set = 0x1030, 731 TriggerControl_Set = 0x1034, 732 Command_Set = 0x1038, 733 RxData_Set = 0x103C, 734 RxDataPeek_Set = 0x1040, 735 TxData_Set = 0x1044, 736 Status_Set = 0x1048, 737 InterruptFlag_Set = 0x104C, 738 InterruptEnable_Set = 0x1050, 739 SyncBusy_Set = 0x1054, 740 DaliCfg_Set = 0x1058, 741 Test_Set = 0x1100, 742 //clr 743 IpVersion_Clr = 0x2000, 744 Enable_Clr = 0x2004, 745 Cfg_0_Clr = 0x2008, 746 Cfg_1_Clr = 0x200C, 747 Cfg_2_Clr = 0x2010, 748 FrameCfg_Clr = 0x201F, 749 DtxDataCfg_Clr = 0x2018, 750 IrHfCfg_Clr = 0x201C, 751 IrLfCfg_Clr = 0x2020, 752 Timing_Clr = 0x2024, 753 StartFrameCfg_Clr = 0x2028, 754 SigFrameCfg_Clr = 0x202C, 755 ClkDiv_Clr = 0x2030, 756 TriggerControl_Clr = 0x2034, 757 Command_Clr = 0x2038, 758 RxData_Clr = 0x203C, 759 RxDataPeek_Clr = 0x2040, 760 TxData_Clr = 0x2044, 761 Status_Clr = 0x2048, 762 InterruptFlag_Clr = 0x204C, 763 InterruptEnable_Clr = 0x2050, 764 SyncBusy_Clr = 0x2054, 765 DaliCfg_Clr = 0x2058, 766 Test_Clr = 0x2100, 767 //Toggle 768 IpVersion_Tgl = 0x3000, 769 Enable_Tgl = 0x3004, 770 Cfg_0_Tgl = 0x3008, 771 Cfg_1_Tgl = 0x300C, 772 Cfg_2_Tgl = 0x3010, 773 FrameCfg_Tgl = 0x301F, 774 DtxDataCfg_Tgl = 0x3018, 775 IrHfCfg_Tgl = 0x301C, 776 IrLfCfg_Tgl = 0x3020, 777 Timing_Tgl = 0x3024, 778 StartFrameCfg_Tgl = 0x3028, 779 SigFrameCfg_Tgl = 0x302C, 780 ClkDiv_Tgl = 0x3030, 781 TriggerControl_Tgl = 0x3034, 782 Command_Tgl = 0x3038, 783 RxData_Tgl = 0x303C, 784 RxDataPeek_Tgl = 0x3040, 785 TxData_Tgl = 0x3044, 786 Status_Tgl = 0x3048, 787 InterruptFlag_Tgl = 0x304C, 788 InterruptEnable_Tgl = 0x3050, 789 SyncBusy_Tgl = 0x3054, 790 DaliCfg_Tgl = 0x3058, 791 Test_Tgl = 0x3100, 792 } 793 #endregion 794 } 795 }