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.IO; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Core.Structure; 14 using Antmicro.Renode.Core.Structure.Registers; 15 using Antmicro.Renode.Exceptions; 16 using Antmicro.Renode.Logging; 17 using Antmicro.Renode.Utilities; 18 using Antmicro.Renode.Peripherals.Bus; 19 using Antmicro.Renode.Peripherals.SPI; 20 using Antmicro.Renode.Peripherals.Timers; 21 using Antmicro.Renode.Time; 22 using Antmicro.Renode.Peripherals.CPU; 23 using Antmicro.Renode.Peripherals.UART; 24 25 namespace Antmicro.Renode.Peripherals.UART 26 { 27 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.DoubleWordToByte)] 28 public class EFR32xG2_USART_0 : UARTBase, IUARTWithBufferState, IDoubleWordPeripheral, IPeripheralContainer<ISPIPeripheral, NullRegistrationPoint> 29 { EFR32xG2_USART_0(Machine machine, uint clockFrequency)30 public EFR32xG2_USART_0(Machine machine, uint clockFrequency) : base(machine) 31 { 32 this.machine = machine; 33 uartClockFrequency = clockFrequency; 34 35 TransmitIRQ = new GPIO(); 36 ReceiveIRQ = new GPIO(); 37 RxDataAvailableRequest = new GPIO(); 38 RxDataAvailableSingleRequest = new GPIO(); 39 TxBufferLowRequest = new GPIO(); 40 TxBufferLowSingleRequest = new GPIO(); 41 TxEmptyRequest = new GPIO(); 42 RxDataAvailableRightRequest = new GPIO(); 43 RxDataAvailableRightSingleRequest = new GPIO(); 44 RxDataAvailableGpioSignal = new GPIO(); 45 TxBufferLowRightRequest = new GPIO(); 46 TxBufferLowRightSingleRequest = new GPIO(); 47 48 compareTimer = new LimitTimer(machine.ClockSource, 115214, this, "usart-compare-timer", 255, direction: Direction.Ascending, 49 enabled: false, workMode: WorkMode.OneShot, eventEnabled: true, autoUpdate: true); 50 compareTimer.LimitReached += CompareTimerHandleLimitReached; 51 52 registersCollection = BuildRegistersCollection(); 53 54 TxEmptyRequest.Set(true); 55 TxBufferLowRequest.Set(true); 56 } 57 Reset()58 public override void Reset() 59 { 60 base.Reset(); 61 isEnabled = false; 62 spiSlaveDevice = null; 63 TxEmptyRequest.Set(true); 64 TxBufferLowRequest.Set(true); 65 } 66 ReadDoubleWord(long offset)67 public uint ReadDoubleWord(long offset) 68 { 69 return ReadRegister(offset); 70 } 71 ReadRegister(long offset, bool internal_read = false)72 private uint ReadRegister(long offset, bool internal_read = false) 73 { 74 var result = 0U; 75 long internal_offset = offset; 76 77 // Set, Clear, Toggle registers should only be used for write operations. But just in case we convert here as well. 78 if (offset >= SetRegisterOffset && offset < ClearRegisterOffset) 79 { 80 // Set register 81 internal_offset = offset - SetRegisterOffset; 82 if(!internal_read) 83 { 84 this.Log(LogLevel.Noisy, "SET Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset); 85 } 86 } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset) 87 { 88 // Clear register 89 internal_offset = offset - ClearRegisterOffset; 90 if(!internal_read) 91 { 92 this.Log(LogLevel.Noisy, "CLEAR Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset); 93 } 94 } else if (offset >= ToggleRegisterOffset) 95 { 96 // Toggle register 97 internal_offset = offset - ToggleRegisterOffset; 98 if(!internal_read) 99 { 100 this.Log(LogLevel.Noisy, "TOGGLE Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset); 101 } 102 } 103 104 if(!registersCollection.TryRead(internal_offset, out result)) 105 { 106 if(!internal_read) 107 { 108 this.Log(LogLevel.Noisy, "Unhandled read at offset 0x{0:X} ({1}).", internal_offset, (Registers)internal_offset); 109 } 110 } 111 else 112 { 113 if(!internal_read) 114 { 115 this.Log(LogLevel.Noisy, "Read at offset 0x{0:X} ({1}), returned 0x{2:X}.", internal_offset, (Registers)internal_offset, result); 116 } 117 } 118 119 return result; 120 } 121 WriteDoubleWord(long offset, uint value)122 public void WriteDoubleWord(long offset, uint value) 123 { 124 WriteRegister(offset, value); 125 } 126 WriteRegister(long offset, uint value, bool internal_write = false)127 private void WriteRegister(long offset, uint value, bool internal_write = false) 128 { 129 machine.ClockSource.ExecuteInLock(delegate { 130 long internal_offset = offset; 131 uint internal_value = value; 132 133 if (offset >= SetRegisterOffset && offset < ClearRegisterOffset) 134 { 135 // Set register 136 internal_offset = offset - SetRegisterOffset; 137 uint old_value = ReadRegister(internal_offset, true); 138 internal_value = old_value | value; 139 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); 140 } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset) 141 { 142 // Clear register 143 internal_offset = offset - ClearRegisterOffset; 144 uint old_value = ReadRegister(internal_offset, true); 145 internal_value = old_value & ~value; 146 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); 147 } else if (offset >= ToggleRegisterOffset) 148 { 149 // Toggle register 150 internal_offset = offset - ToggleRegisterOffset; 151 uint old_value = ReadRegister(internal_offset, true); 152 internal_value = old_value ^ value; 153 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); 154 } 155 156 this.Log(LogLevel.Noisy, "Write at offset 0x{0:X} ({1}), value 0x{2:X}.", internal_offset, (Registers)internal_offset, internal_value); 157 158 if(!registersCollection.TryWrite(internal_offset, internal_value)) 159 { 160 this.Log(LogLevel.Noisy, "Unhandled write at offset 0x{0:X} ({1}), value 0x{2:X}.", internal_offset, (Registers)internal_offset, internal_value); 161 return; 162 } 163 }); 164 } 165 BuildRegistersCollection()166 private DoubleWordRegisterCollection BuildRegistersCollection() 167 { 168 var registerDictionary = new Dictionary<long, DoubleWordRegister> 169 { 170 {(long)Registers.InterruptFlag, new DoubleWordRegister(this) 171 // RENODE-80: without this workaround, the RX flow in sl_iostream_usart breaks. 172 // It is unclear if this is the result of a bug in this model or a race condition 173 // in the embedded code. 174 .WithFlag(0, out txCompleteInterrupt, valueProviderCallback: _ => (txCompleteInterrupt.Value && txCompleteInterruptEnable.Value), name: "TXCIF") 175 .WithFlag(1, out txBufferLevelInterrupt, name: "TXBLIF") 176 .WithFlag(2, out rxDataValidInterrupt, name: "RXDATAVIF") 177 .WithFlag(3, out rxBufferFullInterrupt, name: "RXFULLIF") 178 .WithFlag(4, out rxOverflowInterrupt, name: "RXOFIF") 179 .WithFlag(5, out rxUnderflowInterrupt, name: "RXUFIF") 180 .WithTaggedFlag("TXOFIF", 6) 181 .WithTaggedFlag("TXUFIF", 7) 182 .WithTaggedFlag("PERRIF", 8) 183 .WithTaggedFlag("FERRIF", 9) 184 .WithTaggedFlag("MPAFIF", 10) 185 .WithTaggedFlag("SSMIF", 11) 186 .WithTaggedFlag("CCFIF", 12) 187 .WithTaggedFlag("TXIDLEIF", 13) 188 .WithFlag(14, out timeCompareInterrupt[0], name: "TCMP0IF") 189 .WithFlag(15, out timeCompareInterrupt[1], name: "TCMP1IF") 190 .WithFlag(16, out timeCompareInterrupt[2], name: "TCMP2IF") 191 .WithReservedBits(17, 15) 192 .WithWriteCallback((_, __) => UpdateInterrupts()) 193 }, 194 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 195 .WithFlag(0, out txCompleteInterruptEnable, name: "TXCIEN") 196 .WithFlag(1, out txBufferLevelInterruptEnable, name: "TXBLIEN") 197 .WithFlag(2, out rxDataValidInterruptEnable, name: "RXDATAVIEN") 198 .WithFlag(3, out rxBufferFullInterruptEnable, name: "RXFULLIEN") 199 .WithFlag(4, out rxOverflowInterruptEnable, name: "RXOFIEN") 200 .WithFlag(5, out rxUnderflowInterruptEnable, name: "RXUFIEN") 201 .WithTaggedFlag("TXOFIEN", 6) 202 .WithTaggedFlag("TXUFIEN", 7) 203 .WithTaggedFlag("PERRIEN", 8) 204 .WithTaggedFlag("FERRIEN", 9) 205 .WithTaggedFlag("MPAFIEN", 10) 206 .WithTaggedFlag("SSMIEN", 11) 207 .WithTaggedFlag("CCFIEN", 12) 208 .WithTaggedFlag("TXIDLEIEN", 13) 209 .WithFlag(14, out timeCompareInterruptEnable[0], name: "TCMP0IEN") 210 .WithFlag(15, out timeCompareInterruptEnable[1], name: "TCMP1IEN") 211 .WithFlag(16, out timeCompareInterruptEnable[2], name: "TCMP2IEN") 212 .WithReservedBits(17, 15) 213 .WithWriteCallback((_, __) => UpdateInterrupts()) 214 }, 215 {(long)Registers.Enable, new DoubleWordRegister(this) 216 .WithFlag(0, changeCallback: (_, value) => { isEnabled = value; }, name: "EN") 217 .WithReservedBits(1, 31) 218 }, 219 {(long)Registers.Control, new DoubleWordRegister(this) 220 .WithEnumField(0, 1, out operationModeField, name: "SYNC") 221 .WithTaggedFlag("LOOPBK", 1) 222 .WithTaggedFlag("CCEN", 2) 223 .WithTaggedFlag("MPM", 3) 224 .WithTaggedFlag("MPAB", 4) 225 .WithEnumField(5, 2, out oversamplingField, name: "OVS") 226 .WithReservedBits(7, 1) 227 .WithTaggedFlag("CLKPOL", 8) 228 .WithTaggedFlag("CLKPHA", 9) 229 .WithTaggedFlag("MSBF", 10) 230 .WithTaggedFlag("CSMA", 11) 231 .WithTaggedFlag("TXBIL", 12) 232 .WithTaggedFlag("RXINV", 13) 233 .WithTaggedFlag("TXINV", 14) 234 .WithTaggedFlag("CSINV", 15) 235 .WithTaggedFlag("AUTOCS", 16) 236 .WithTaggedFlag("AUTOTRI", 17) 237 .WithTaggedFlag("SCMODE", 18) 238 .WithTaggedFlag("SCRETRANS", 19) 239 .WithTaggedFlag("SKIPPERRF", 20) 240 .WithTaggedFlag("BIT8DV", 21) 241 .WithTaggedFlag("ERRSDMA", 22) 242 .WithTaggedFlag("ERRSRX", 23) 243 .WithTaggedFlag("ERRSTX", 24) 244 .WithTaggedFlag("SSSEARLY", 25) 245 .WithReservedBits(26, 2) 246 .WithTaggedFlag("BYTESWAP", 28) 247 .WithTaggedFlag("AUTOTX", 29) 248 .WithTaggedFlag("MVDIS", 30) 249 .WithTaggedFlag("SMSDELAY", 31) 250 }, 251 {(long)Registers.FrameFormat, new DoubleWordRegister(this) 252 .WithTag("DATABITS", 0, 4) 253 .WithReservedBits(4, 4) 254 .WithEnumField(8, 2, out parityBitModeField, name: "PARITY") 255 .WithReservedBits(10, 2) 256 .WithEnumField(12, 2, out stopBitsModeField, name: "STOPBITS") 257 .WithReservedBits(14, 18) 258 }, 259 {(long)Registers.TriggerControl, new DoubleWordRegister(this) 260 .WithReservedBits(0, 4) 261 .WithTaggedFlag("RXTEN", 4) 262 .WithTaggedFlag("TXTEN", 5) 263 .WithTaggedFlag("AUTOTXTEN", 6) 264 .WithTaggedFlag("TXARX0EN", 7) 265 .WithTaggedFlag("TXARX1EN", 8) 266 .WithTaggedFlag("TXARX2EN", 9) 267 .WithTaggedFlag("RXATX0EN", 10) 268 .WithTaggedFlag("RXATX1EN", 11) 269 .WithTaggedFlag("RXATX2EN", 12) 270 .WithReservedBits(13, 3) 271 .WithTag("TSEL", 16, 4) 272 .WithReservedBits(20, 12) 273 }, 274 {(long)Registers.Command, new DoubleWordRegister(this) 275 .WithFlag(0, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) receiverEnableFlag.Value = true; }, name: "RXEN") 276 .WithFlag(1, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) receiverEnableFlag.Value = false; }, name: "RXDIS") 277 .WithFlag(2, FieldMode.Set, writeCallback: (_, newValue) => 278 { 279 if(newValue) 280 { 281 transmitterEnableFlag.Value = true; 282 txBufferLevelInterrupt.Value = true; 283 UpdateInterrupts(); 284 } 285 }, name: "TXEN") 286 .WithFlag(3, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) transmitterEnableFlag.Value = false; }, name: "TXDIS") 287 .WithTaggedFlag("MASTEREN", 4) 288 .WithTaggedFlag("MASTERDIS", 5) 289 .WithTaggedFlag("RXBLOCKEN", 6) 290 .WithTaggedFlag("RXBLOCKDIS", 7) 291 .WithTaggedFlag("TXTRIEN", 8) 292 .WithTaggedFlag("TXTRIDIS", 9) 293 .WithTaggedFlag("CLEARTX", 10) 294 .WithFlag(11, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) ClearBuffer(); }, name: "CLEARRX") 295 .WithReservedBits(12, 20) 296 }, 297 {(long)Registers.Status, new DoubleWordRegister(this, 0x00002040) 298 .WithFlag(0, out receiverEnableFlag, FieldMode.Read, name: "RXENS") 299 .WithFlag(1, out transmitterEnableFlag, FieldMode.Read, name: "TXENS") 300 .WithTaggedFlag("MASTER", 2) 301 .WithTaggedFlag("RXBLOCK", 3) 302 .WithTaggedFlag("TXTRI", 4) 303 .WithFlag(5, out transferCompleteFlag, FieldMode.Read, name: "TXC") 304 .WithTaggedFlag("TXBL", 6) 305 .WithFlag(7, out receiveDataValidFlag, FieldMode.Read, name: "RXDATAV") 306 .WithFlag(8, FieldMode.Read, valueProviderCallback: _ => Count == BufferSize, name: "RXFULL") 307 .WithTaggedFlag("TXBDRIGHT", 9) 308 .WithTaggedFlag("TXBSRIGHT", 10) 309 .WithTaggedFlag("RXDATAVRIGHT", 11) 310 .WithTaggedFlag("RXFULLRIGHT", 12) 311 .WithFlag(13, FieldMode.Read, valueProviderCallback: _ => true, name: "TXIDLE") 312 .WithTaggedFlag("TIMERRESTARTED", 14) 313 .WithReservedBits(15, 1) 314 .WithValueField(16, 2, FieldMode.Read, valueProviderCallback: _ => 0, name: "TXBUFCNT") 315 .WithReservedBits(18, 14) 316 }, 317 {(long)Registers.ClockControl, new DoubleWordRegister(this) 318 .WithReservedBits(0, 3) 319 .WithValueField(3, 20, out fractionalClockDividerField, name: "DIV") 320 .WithReservedBits(23, 8) 321 .WithTaggedFlag("AUTOBAUDEN", 31) 322 }, 323 {(long)Registers.RxBufferDataExtended, new DoubleWordRegister(this) 324 .WithTag("RXDATA", 0, 8) 325 .WithReservedBits(8, 5) 326 .WithTaggedFlag("PERR", 14) 327 .WithTaggedFlag("FERR", 15) 328 .WithReservedBits(16, 16) 329 }, 330 {(long)Registers.RxBufferData, new DoubleWordRegister(this) 331 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: (_) => ReadBuffer(), name: "RXDATA") 332 .WithReservedBits(8, 24) 333 }, 334 {(long)Registers.RxBufferDoubleDataExtended, new DoubleWordRegister(this) 335 .WithTag("RXDATA0", 0, 8) 336 .WithReservedBits(8, 5) 337 .WithTaggedFlag("PERR0", 14) 338 .WithTaggedFlag("FERR0", 15) 339 .WithTag("RXDATA1", 16, 8) 340 .WithReservedBits(24, 5) 341 .WithTaggedFlag("PERR1", 30) 342 .WithTaggedFlag("FERR1", 31) 343 }, 344 {(long)Registers.RxBufferDoubleData, new DoubleWordRegister(this) 345 .WithTag("RXDATA0", 0, 8) 346 .WithTag("RXDATA1", 8, 8) 347 .WithReservedBits(16, 16) 348 }, 349 {(long)Registers.RxBufferDoubleDataExtendedPeek, new DoubleWordRegister(this) 350 .WithTag("RXDATAP", 0, 8) 351 .WithReservedBits(8, 5) 352 .WithTaggedFlag("PERRP", 14) 353 .WithTaggedFlag("FERRP", 15) 354 .WithReservedBits(16, 16) 355 }, 356 {(long)Registers.TxBufferDataExtended, new DoubleWordRegister(this) 357 .WithTag("TXDATAX", 0, 8) 358 .WithReservedBits(8, 2) 359 .WithTaggedFlag("UBRXAT", 11) 360 .WithTaggedFlag("TXTRIAT", 12) 361 .WithTaggedFlag("TXBREAK", 13) 362 .WithTaggedFlag("TXDISAT", 14) 363 .WithTaggedFlag("RXENAT", 15) 364 .WithReservedBits(16, 16) 365 }, 366 {(long)Registers.TxBufferData, new DoubleWordRegister(this) 367 .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, v) => HandleTxBufferData((byte)v), name: "TXDATA") 368 .WithReservedBits(8, 24) 369 }, 370 {(long)Registers.TxBufferDoubleDataExtended, new DoubleWordRegister(this) 371 .WithTag("TXDATA0", 0, 8) 372 .WithReservedBits(8, 2) 373 .WithTaggedFlag("UBRXAT0", 11) 374 .WithTaggedFlag("TXTRIAT0", 12) 375 .WithTaggedFlag("TXBREAK0", 13) 376 .WithTaggedFlag("TXDISAT0", 14) 377 .WithTaggedFlag("RXENAT0", 15) 378 .WithTag("TXDATA1", 16, 8) 379 .WithReservedBits(24, 2) 380 .WithTaggedFlag("UBRXAT1", 27) 381 .WithTaggedFlag("TXTRIAT1", 28) 382 .WithTaggedFlag("TXBREAK1", 29) 383 .WithTaggedFlag("TXDISAT1", 30) 384 .WithTaggedFlag("RXENAT1", 31) 385 }, 386 {(long)Registers.TxBufferDoubleData, new DoubleWordRegister(this) 387 .WithTag("TXDATA0", 0, 8) 388 .WithTag("TXDATA1", 8, 8) 389 .WithReservedBits(16, 16) 390 }, 391 {(long)Registers.IrDAControl, new DoubleWordRegister(this) 392 .WithTaggedFlag("IREN", 0) 393 .WithTag("IRPW", 1, 2) 394 .WithTaggedFlag("IRFILT", 3) 395 .WithReservedBits(4, 3) 396 .WithTaggedFlag("IRPRSEN", 7) 397 .WithTag("IRPRSSEL", 8, 4) 398 .WithReservedBits(12, 20) 399 }, 400 {(long)Registers.I2SControl, new DoubleWordRegister(this) 401 .WithTaggedFlag("EN", 0) 402 .WithTaggedFlag("MONO", 1) 403 .WithTaggedFlag("JUSTIFY", 2) 404 .WithTaggedFlag("DMASPLIT", 3) 405 .WithTaggedFlag("DELAY", 4) 406 .WithReservedBits(5, 3) 407 .WithTag("FORMAT", 8, 3) 408 .WithReservedBits(11, 21) 409 }, 410 {(long)Registers.Timing, new DoubleWordRegister(this) 411 .WithReservedBits(0, 16) 412 .WithTag("TXDELAY", 16, 2) 413 .WithReservedBits(19, 1) 414 .WithTag("CSSETUP", 20, 3) 415 .WithReservedBits(23, 1) 416 .WithTag("ICS", 24, 3) 417 .WithReservedBits(27, 1) 418 .WithTag("CSHOLD", 28, 3) 419 .WithReservedBits(31, 1) 420 }, 421 {(long)Registers.ControlExtended, new DoubleWordRegister(this) 422 .WithTaggedFlag("DBHALT", 0) 423 .WithTaggedFlag("CTSINV", 1) 424 .WithTaggedFlag("CTSEN", 2) 425 .WithTaggedFlag("RTSINV", 3) 426 .WithReservedBits(4, 27) 427 .WithTaggedFlag("GPIODELAYXOREN", 31) 428 }, 429 {(long)Registers.TimeCompare0, new DoubleWordRegister(this) 430 .WithValueField(0, 8, out compareTimerCompare[0], name: "TCMPVAL") 431 .WithReservedBits(8, 8) 432 .WithEnumField<DoubleWordRegister, TimeCompareStartSource>(16, 3, out timeCompareStartSource[0], name: "TSTART") 433 .WithReservedBits(19, 1) 434 .WithEnumField<DoubleWordRegister, TimeCompareStopSource>(20, 3, out timeCompareStopSource[0], name: "TSTOP") 435 .WithReservedBits(23, 1) 436 .WithFlag(24, out timeCompareRestart[0], name: "RESTARTEN") 437 .WithReservedBits(25, 7) 438 }, 439 {(long)Registers.TimeCompare1, new DoubleWordRegister(this) 440 .WithValueField(0, 8, out compareTimerCompare[1], name: "TCMPVAL") 441 .WithReservedBits(8, 8) 442 .WithEnumField<DoubleWordRegister, TimeCompareStartSource>(16, 3, out timeCompareStartSource[1], name: "TSTART") 443 .WithReservedBits(19, 1) 444 .WithEnumField<DoubleWordRegister, TimeCompareStopSource>(20, 3, out timeCompareStopSource[1], name: "TSTOP") 445 .WithReservedBits(23, 1) 446 .WithFlag(24, out timeCompareRestart[1], name: "RESTARTEN") 447 .WithReservedBits(25, 7) 448 }, 449 {(long)Registers.TimeCompare2, new DoubleWordRegister(this) 450 .WithValueField(0, 8, out compareTimerCompare[2], name: "TCMPVAL") 451 .WithReservedBits(8, 8) 452 .WithEnumField<DoubleWordRegister, TimeCompareStartSource>(16, 3, out timeCompareStartSource[2], name: "TSTART") 453 .WithReservedBits(19, 1) 454 .WithEnumField<DoubleWordRegister, TimeCompareStopSource>(20, 3, out timeCompareStopSource[2], name: "TSTOP") 455 .WithReservedBits(23, 1) 456 .WithFlag(24, out timeCompareRestart[2], name: "RESTARTEN") 457 .WithReservedBits(25, 7) 458 }, 459 {(long)Registers.Test, new DoubleWordRegister(this) 460 .WithTaggedFlag("GPIODELAYSTABLE", 0) 461 .WithTaggedFlag("GPIODELAYXOR", 1) 462 .WithReservedBits(2, 30) 463 }, 464 }; 465 return new DoubleWordRegisterCollection(this, registerDictionary); 466 } 467 468 public long Size => 0x4000; 469 private readonly Machine machine; 470 private readonly DoubleWordRegisterCollection registersCollection; 471 private const uint SetRegisterOffset = 0x1000; 472 private const uint ClearRegisterOffset = 0x2000; 473 private const uint ToggleRegisterOffset = 0x3000; 474 private const uint NumberOfTimeCompareTimers = 3; 475 476 #region methods Register(ISPIPeripheral peripheral, NullRegistrationPoint registrationPoint)477 public void Register(ISPIPeripheral peripheral, NullRegistrationPoint registrationPoint) 478 { 479 if(spiSlaveDevice != null) 480 { 481 throw new RegistrationException("Cannot register more than one peripheral."); 482 } 483 Machine.RegisterAsAChildOf(this, peripheral, registrationPoint); 484 spiSlaveDevice = peripheral; 485 } 486 Unregister(ISPIPeripheral peripheral)487 public void Unregister(ISPIPeripheral peripheral) 488 { 489 if(peripheral != spiSlaveDevice) 490 { 491 throw new RegistrationException("Trying to unregister not registered device."); 492 } 493 494 Machine.UnregisterAsAChildOf(this, peripheral); 495 spiSlaveDevice = null; 496 } 497 GetRegistrationPoints(ISPIPeripheral peripheral)498 public IEnumerable<NullRegistrationPoint> GetRegistrationPoints(ISPIPeripheral peripheral) 499 { 500 if(peripheral != spiSlaveDevice) 501 { 502 throw new RegistrationException("Trying to obtain a registration point for a not registered device."); 503 } 504 505 return new[] { NullRegistrationPoint.Instance }; 506 } 507 WriteChar(byte value)508 public override void WriteChar(byte value) 509 { 510 if(BufferState == BufferState.Full) 511 { 512 rxOverflowInterrupt.Value = true; 513 UpdateInterrupts(); 514 this.Log(LogLevel.Warning, "RX buffer is full. Dropping incoming byte (0x{0:X})", value); 515 return; 516 } 517 TriggerCompareTimerStopEvent(TimeCompareStopSource.RxActive); 518 base.WriteChar(value); 519 } 520 521 IEnumerable<IRegistered<ISPIPeripheral, NullRegistrationPoint>> IPeripheralContainer<ISPIPeripheral, NullRegistrationPoint>.Children 522 { 523 get 524 { 525 return new[] { Registered.Create(spiSlaveDevice, NullRegistrationPoint.Instance) }; 526 } 527 } 528 529 public GPIO TransmitIRQ { get; } 530 public GPIO ReceiveIRQ { get; } 531 public GPIO RxDataAvailableRequest { get; } 532 public GPIO RxDataAvailableSingleRequest { get; } 533 public GPIO TxBufferLowRequest { get; } 534 public GPIO TxBufferLowSingleRequest { get; } 535 public GPIO TxEmptyRequest { get; } 536 public GPIO RxDataAvailableRightRequest { get; } 537 public GPIO RxDataAvailableRightSingleRequest { get; } 538 public GPIO RxDataAvailableGpioSignal { get; } 539 public GPIO TxBufferLowRightRequest { get; } 540 public GPIO TxBufferLowRightSingleRequest { get; } 541 542 public override Parity ParityBit { get { return parityBitModeField.Value; } } 543 544 public override Bits StopBits { get { return stopBitsModeField.Value; } } 545 546 public override uint BaudRate 547 { 548 get 549 { 550 var oversample = 1u; 551 switch(oversamplingField.Value) 552 { 553 case OversamplingMode.Times16: 554 oversample = 16; 555 break; 556 case OversamplingMode.Times8: 557 oversample = 8; 558 break; 559 case OversamplingMode.Times6: 560 oversample = 6; 561 break; 562 case OversamplingMode.Times4: 563 oversample = 4; 564 break; 565 } 566 return (uint)(uartClockFrequency / (oversample * (1 + ((double)(fractionalClockDividerField.Value << 3)) / 256))); 567 } 568 } 569 570 public BufferState BufferState 571 { 572 get 573 { 574 return bufferState; 575 } 576 577 private set 578 { 579 if(bufferState == value) 580 { 581 return; 582 } 583 bufferState = value; 584 BufferStateChanged?.Invoke(value); 585 switch(bufferState) 586 { 587 case BufferState.Empty: 588 RxDataAvailableRequest.Set(false); 589 RxDataAvailableSingleRequest.Set(false); 590 RxDataAvailableGpioSignal.Set(false); 591 break; 592 case BufferState.Ready: 593 RxDataAvailableRequest.Set(false); 594 RxDataAvailableSingleRequest.Set(true); 595 RxDataAvailableGpioSignal.Set(true); 596 break; 597 case BufferState.Full: 598 RxDataAvailableRequest.Set(true); 599 RxDataAvailableGpioSignal.Set(true); 600 rxBufferFullInterrupt.Value = true; 601 UpdateInterrupts(); 602 break; 603 default: 604 this.Log(LogLevel.Error, "Unreachable code. Invalid BufferState value."); 605 return; 606 } 607 } 608 } 609 CharWritten()610 protected override void CharWritten() 611 { 612 rxDataValidInterrupt.Value = true; 613 receiveDataValidFlag.Value = true; 614 UpdateInterrupts(); 615 BufferState = Count == BufferSize ? BufferState.Full : BufferState.Ready; 616 TriggerCompareTimerStopEvent(TimeCompareStopSource.RxInactive); 617 TriggerCompareTimerStartEvent(TimeCompareStartSource.RxEndOfFrame); 618 } 619 QueueEmptied()620 protected override void QueueEmptied() 621 { 622 rxDataValidInterrupt.Value = false; 623 receiveDataValidFlag.Value = false; 624 BufferState = BufferState.Empty; 625 UpdateInterrupts(); 626 } 627 HandleTxBufferData(byte data)628 private void HandleTxBufferData(byte data) 629 { 630 this.Log(LogLevel.Noisy, "Handle TX buffer data: {0}", data); 631 632 if(!transmitterEnableFlag.Value) 633 { 634 this.Log(LogLevel.Warning, "Trying to send data, but the transmitter is disabled: 0x{0:X}", data); 635 return; 636 } 637 638 TriggerCompareTimerStopEvent(TimeCompareStopSource.TxStart); 639 640 transferCompleteFlag.Value = false; 641 if(operationModeField.Value == OperationMode.Synchronous) 642 { 643 if(spiSlaveDevice != null) 644 { 645 var result = spiSlaveDevice.Transmit(data); 646 WriteChar(result); 647 } 648 else 649 { 650 this.Log(LogLevel.Warning, "Writing data in synchronous mode, but no device is currently connected."); 651 WriteChar(0x0); 652 } 653 } 654 else 655 { 656 TransmitCharacter(data); 657 txBufferLevelInterrupt.Value = true; 658 txCompleteInterrupt.Value = true; 659 UpdateInterrupts(); 660 } 661 transferCompleteFlag.Value = true; 662 TriggerCompareTimerStartEvent(TimeCompareStartSource.TxEndOfFrame); 663 TriggerCompareTimerStartEvent(TimeCompareStartSource.TxComplete); 664 } 665 ReadBuffer()666 private byte ReadBuffer() 667 { 668 byte character; 669 if (TryGetCharacter(out character)) 670 { 671 return character; 672 } 673 else 674 { 675 rxUnderflowInterrupt.Value = true; 676 UpdateInterrupts(); 677 return (byte)0; 678 } 679 } 680 TriggerCompareTimerStartEvent(TimeCompareStartSource source)681 protected void TriggerCompareTimerStartEvent(TimeCompareStartSource source) 682 { 683 for(uint i=0; i<NumberOfTimeCompareTimers; i++) 684 { 685 if (timeCompareStartSource[i].Value == source) 686 { 687 // From the design book: "The start source enables the comparator, resets the counter, 688 // and starts the counter. If the counter is already running, the start source will reset 689 // the counter and restart it." 690 RestartCompareTimer(i); 691 break; 692 } 693 } 694 } 695 TriggerCompareTimerStopEvent(TimeCompareStopSource source)696 protected void TriggerCompareTimerStopEvent(TimeCompareStopSource source) 697 { 698 if (compareTimer.Enabled 699 && startIndex < NumberOfTimeCompareTimers 700 && source == timeCompareStopSource[startIndex].Value) 701 { 702 compareTimer.Enabled = false; 703 startIndex = 0xFF; 704 } 705 } 706 CompareTimerHandleLimitReached()707 protected void CompareTimerHandleLimitReached() 708 { 709 uint timerIndex = startIndex; 710 compareTimer.Enabled = false; 711 timeCompareInterrupt[startIndex].Value = true; 712 startIndex = 0xFF; 713 UpdateInterrupts(); 714 715 if (timeCompareRestart[timerIndex].Value) 716 { 717 RestartCompareTimer(timerIndex); 718 } 719 } 720 RestartCompareTimer(uint timerIndex)721 protected void RestartCompareTimer(uint timerIndex) 722 { 723 startIndex = (byte)timerIndex; 724 725 // Start source will reset the counter and restart it 726 compareTimer.Frequency = BaudRate; 727 compareTimer.Limit = compareTimerCompare[timerIndex].Value; 728 compareTimer.Enabled = true; 729 } 730 UpdateInterrupts()731 private void UpdateInterrupts() 732 { 733 machine.ClockSource.ExecuteInLock(delegate { 734 var txIrq = ((txCompleteInterruptEnable.Value && txCompleteInterrupt.Value) 735 || (txBufferLevelInterruptEnable.Value && txBufferLevelInterrupt.Value)); 736 TransmitIRQ.Set(txIrq); 737 738 var rxIrq = ((rxDataValidInterruptEnable.Value && rxDataValidInterrupt.Value) 739 || (rxBufferFullInterruptEnable.Value && rxBufferFullInterrupt.Value) 740 || (rxOverflowInterruptEnable.Value && rxOverflowInterrupt.Value) 741 || (rxUnderflowInterruptEnable.Value && rxUnderflowInterrupt.Value) 742 || (timeCompareInterruptEnable[0].Value && timeCompareInterrupt[0].Value) 743 || (timeCompareInterruptEnable[1].Value && timeCompareInterrupt[1].Value) 744 || (timeCompareInterruptEnable[2].Value && timeCompareInterrupt[2].Value)); 745 ReceiveIRQ.Set(rxIrq); 746 }); 747 } 748 TrySyncTime()749 private bool TrySyncTime() 750 { 751 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 752 { 753 cpu.SyncTime(); 754 return true; 755 } 756 return false; 757 } 758 GetTime()759 private TimeInterval GetTime() => machine.LocalTimeSource.ElapsedVirtualTime; 760 protected override bool IsReceiveEnabled => receiverEnableFlag.Value; 761 #endregion 762 763 #region fields 764 private bool isEnabled = false; 765 private ISPIPeripheral spiSlaveDevice; 766 public event Action<BufferState> BufferStateChanged; 767 private IEnumRegisterField<OperationMode> operationModeField; 768 private IEnumRegisterField<OversamplingMode> oversamplingField; 769 private IEnumRegisterField<Parity> parityBitModeField; 770 private IEnumRegisterField<Bits> stopBitsModeField; 771 private IValueRegisterField fractionalClockDividerField; 772 private IFlagRegisterField transferCompleteFlag; 773 private IFlagRegisterField receiveDataValidFlag; 774 private IFlagRegisterField receiverEnableFlag; 775 private IFlagRegisterField transmitterEnableFlag; 776 private readonly uint uartClockFrequency; 777 private BufferState bufferState; 778 private const int BufferSize = 3; // with shift register 779 private IValueRegisterField[] compareTimerCompare = new IValueRegisterField[NumberOfTimeCompareTimers]; 780 private IEnumRegisterField<TimeCompareStartSource>[] timeCompareStartSource = new IEnumRegisterField<TimeCompareStartSource>[NumberOfTimeCompareTimers]; 781 private IEnumRegisterField<TimeCompareStopSource>[] timeCompareStopSource = new IEnumRegisterField<TimeCompareStopSource>[NumberOfTimeCompareTimers]; 782 private IFlagRegisterField[] timeCompareRestart = new IFlagRegisterField[NumberOfTimeCompareTimers]; 783 private LimitTimer compareTimer; 784 private byte startIndex = 0xFF; 785 // Interrupts 786 private IFlagRegisterField txCompleteInterrupt; 787 private IFlagRegisterField txBufferLevelInterrupt; 788 private IFlagRegisterField rxDataValidInterrupt; 789 private IFlagRegisterField rxBufferFullInterrupt; 790 private IFlagRegisterField rxOverflowInterrupt; 791 private IFlagRegisterField rxUnderflowInterrupt; 792 private IFlagRegisterField[] timeCompareInterrupt = new IFlagRegisterField[NumberOfTimeCompareTimers]; 793 private IFlagRegisterField txCompleteInterruptEnable; 794 private IFlagRegisterField txBufferLevelInterruptEnable; 795 private IFlagRegisterField rxDataValidInterruptEnable; 796 private IFlagRegisterField rxBufferFullInterruptEnable; 797 private IFlagRegisterField rxOverflowInterruptEnable; 798 private IFlagRegisterField rxUnderflowInterruptEnable; 799 private IFlagRegisterField[] timeCompareInterruptEnable = new IFlagRegisterField[NumberOfTimeCompareTimers]; 800 #endregion 801 802 #region enums 803 protected enum OperationMode 804 { 805 Asynchronous, 806 Synchronous 807 } 808 809 protected enum OversamplingMode 810 { 811 Times16, 812 Times8, 813 Times6, 814 Times4 815 } 816 817 protected enum TimeCompareStartSource 818 { 819 Disabled = 0, 820 TxEndOfFrame = 1, 821 TxComplete = 2, 822 RxActive = 3, 823 RxEndOfFrame = 4, 824 } 825 826 protected enum TimeCompareStopSource 827 { 828 CompareValueReached = 0, 829 TxStart = 1, 830 RxActive = 2, 831 RxInactive = 3, 832 } 833 834 private enum Registers 835 { 836 IpVersion = 0x0000, 837 Enable = 0x0004, 838 Control = 0x0008, 839 FrameFormat = 0x000C, 840 TriggerControl = 0x0010, 841 Command = 0x0014, 842 Status = 0x0018, 843 ClockControl = 0x001C, 844 RxBufferDataExtended = 0x0020, 845 RxBufferData = 0x0024, 846 RxBufferDoubleDataExtended = 0x0028, 847 RxBufferDoubleData = 0x002C, 848 RxBufferDataExtendedPeek = 0x0030, 849 RxBufferDoubleDataExtendedPeek = 0x0034, 850 TxBufferDataExtended = 0x0038, 851 TxBufferData = 0x003C, 852 TxBufferDoubleDataExtended = 0x0040, 853 TxBufferDoubleData = 0x0044, 854 InterruptFlag = 0x0048, 855 InterruptEnable = 0x004C, 856 IrDAControl = 0x0050, 857 I2SControl = 0x0054, 858 Timing = 0x0058, 859 ControlExtended = 0x005C, 860 TimeCompare0 = 0x0060, 861 TimeCompare1 = 0x0064, 862 TimeCompare2 = 0x0068, 863 Test = 0x006C, 864 // Set 865 IpVersion_Set = 0x1000, 866 Enable_Set = 0x1004, 867 Control_Set = 0x1008, 868 FrameFormat_Set = 0x100C, 869 TriggerControl_Set = 0x1010, 870 Command_Set = 0x1014, 871 Status_Set = 0x1018, 872 ClockControl_Set = 0x101C, 873 RxBufferDataExtended_Set = 0x1020, 874 RxBufferData_Set = 0x1024, 875 RxBufferDoubleDataExtended_Set = 0x1028, 876 RxBufferDoubleData_Set = 0x102C, 877 RxBufferDataExtendedPeek_Set = 0x1030, 878 RxBufferDoubleDataExtendedPeek_Set = 0x1034, 879 TxBufferDataExtended_Set = 0x1038, 880 TxBufferData_Set = 0x103C, 881 TxBufferDoubleDataExtended_Set = 0x1040, 882 TxBufferDoubleData_Set = 0x1044, 883 InterruptFlag_Set = 0x1048, 884 InterruptEnable_Set = 0x104C, 885 IrDAControl_Set = 0x1050, 886 I2SControl_Set = 0x1054, 887 Timing_Set = 0x1058, 888 ControlExtended_Set = 0x105C, 889 TimeCompare0_Set = 0x1060, 890 TimeCompare1_Set = 0x1064, 891 TimeCompare2_Set = 0x1068, 892 Test_Set = 0x106C, 893 // Clear 894 IpVersion_Clr = 0x2000, 895 Enable_Clr = 0x2004, 896 Control_Clr = 0x2008, 897 FrameFormat_Clr = 0x200C, 898 TriggerControl_Clr = 0x2010, 899 Command_Clr = 0x2014, 900 Status_Clr = 0x2018, 901 ClockControl_Clr = 0x201C, 902 RxBufferDataExtended_Clr = 0x2020, 903 RxBufferData_Clr = 0x2024, 904 RxBufferDoubleDataExtended_Clr = 0x2028, 905 RxBufferDoubleData_Clr = 0x202C, 906 RxBufferDataExtendedPeek_Clr = 0x2030, 907 RxBufferDoubleDataExtendedPeek_Clr = 0x2034, 908 TxBufferDataExtended_Clr = 0x2038, 909 TxBufferData_Clr = 0x203C, 910 TxBufferDoubleDataExtended_Clr = 0x2040, 911 TxBufferDoubleData_Clr = 0x2044, 912 InterruptFlag_Clr = 0x2048, 913 InterruptEnable_Clr = 0x204C, 914 IrDAControl_Clr = 0x2050, 915 I2SControl_Clr = 0x2054, 916 Timing_Clr = 0x2058, 917 ControlExtended_Clr = 0x205C, 918 TimeCompare0_Clr = 0x2060, 919 TimeCompare1_Clr = 0x2064, 920 TimeCompare2_Clr = 0x2068, 921 Test_Clr = 0x206C, 922 // Toggle 923 IpVersion_Tgl = 0x3000, 924 Enable_Tgl = 0x3004, 925 Control_Tgl = 0x3008, 926 FrameFormat_Tgl = 0x300C, 927 TriggerControl_Tgl = 0x3010, 928 Command_Tgl = 0x3014, 929 Status_Tgl = 0x3018, 930 ClockControl_Tgl = 0x301C, 931 RxBufferDataExtended_Tgl = 0x3020, 932 RxBufferData_Tgl = 0x3024, 933 RxBufferDoubleDataExtended_Tgl = 0x3028, 934 RxBufferDoubleData_Tgl = 0x302C, 935 RxBufferDataExtendedPeek_Tgl = 0x3030, 936 RxBufferDoubleDataExtendedPeek_Tgl = 0x3034, 937 TxBufferDataExtended_Tgl = 0x3038, 938 TxBufferData_Tgl = 0x303C, 939 TxBufferDoubleDataExtended_Tgl = 0x3040, 940 TxBufferDoubleData_Tgl = 0x3044, 941 InterruptFlag_Tgl = 0x3048, 942 InterruptEnable_Tgl = 0x304C, 943 IrDAControl_Tgl = 0x3050, 944 I2SControl_Tgl = 0x3054, 945 Timing_Tgl = 0x3058, 946 ControlExtended_Tgl = 0x305C, 947 TimeCompare0_Tgl = 0x3060, 948 TimeCompare1_Tgl = 0x3064, 949 TimeCompare2_Tgl = 0x3068, 950 Test_Tgl = 0x306C, 951 } 952 #endregion 953 } 954 }