1 // 2 // Copyright (c) 2010-2024 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 Antmicro.Renode.Core; 9 using Antmicro.Renode.Core.Structure.Registers; 10 using Antmicro.Renode.Logging; 11 using Antmicro.Renode.Peripherals.Bus; 12 using Antmicro.Renode.Utilities; 13 using Antmicro.Renode.Utilities.Collections; 14 15 namespace Antmicro.Renode.Peripherals.UART 16 { 17 public class LPC_USART : UARTBase, IDoubleWordPeripheral, IKnownSize, IProvidesRegisterCollection<DoubleWordRegisterCollection> 18 { LPC_USART(IMachine machine, ulong clockFrequency = DefaultClockFrequency)19 public LPC_USART(IMachine machine, ulong clockFrequency = DefaultClockFrequency) : base(machine) 20 { 21 this.clockFrequency = clockFrequency; 22 RegistersCollection = new DoubleWordRegisterCollection(this); 23 IRQ = new GPIO(); 24 DefineRegisters(); 25 Reset(); 26 } 27 Reset()28 public override void Reset() 29 { 30 stopBits = false; 31 parityMode = ParityMode.NoParity; 32 RegistersCollection.Reset(); 33 base.Reset(); 34 IRQ.Unset(); 35 } 36 ReadDoubleWord(long offset)37 public uint ReadDoubleWord(long offset) 38 { 39 return RegistersCollection.Read(offset); 40 } 41 WriteDoubleWord(long offset, uint value)42 public void WriteDoubleWord(long offset, uint value) 43 { 44 RegistersCollection.Write(offset, value); 45 } 46 47 public DoubleWordRegisterCollection RegistersCollection { get; } 48 49 public GPIO IRQ { get; } 50 51 public long Size => 0x1000; 52 53 public override uint BaudRate => (uint)((clockFrequency / (oversampleSelection.Value + 1)) / (baudDivider.Value + 1)); 54 55 public override Bits StopBits => stopBits ? Bits.Two : Bits.One; 56 57 public override Parity ParityBit 58 { 59 get 60 { 61 switch(parityMode) 62 { 63 case ParityMode.Odd: 64 return Parity.Odd; 65 case ParityMode.Even: 66 return Parity.Even; 67 case ParityMode.NoParity: 68 case ParityMode.Reserved: 69 return Parity.None; 70 } 71 throw new Exception("Unreachable"); 72 } 73 } 74 CharWritten()75 protected override void CharWritten() 76 { 77 UpdateInterrupts(); 78 } 79 QueueEmptied()80 protected override void QueueEmptied() 81 { 82 UpdateInterrupts(); 83 } 84 85 private bool TxLevelInterruptStatus 86 { 87 get => txFifoLevelInterruptEnable.Value && GetTxFifoTriggerLevelStatus(); 88 } 89 90 private bool RxLevelInterruptStatus 91 { 92 get => rxFifoLevelInterruptEnable.Value && GetRxFifoTriggerLevelStatus(); 93 } 94 DefineRegisters()95 private void DefineRegisters() 96 { 97 Registers.Configuration.Define(this) 98 .WithTaggedFlag("ENABLE", 0) 99 .WithReservedBits(1, 1) 100 .WithTag("DATALEN", 2, 2) 101 .WithEnumField<DoubleWordRegister, ParityMode>(4, 2, name: "PARITYSEL", 102 valueProviderCallback: _ => parityMode, 103 writeCallback: (_, val) => parityMode = val 104 ) 105 .WithFlag(6, name: "STOPLEN", 106 valueProviderCallback: _ => stopBits, 107 writeCallback: (_, val) => stopBits = val 108 ) 109 .WithTaggedFlag("MODE32K", 7) 110 .WithTaggedFlag("LINMODE", 8) 111 .WithTaggedFlag("CTSEN", 9) 112 .WithReservedBits(10, 1) 113 .WithTaggedFlag("SYNCEN", 11) 114 .WithTaggedFlag("CLKPOL", 12) 115 .WithReservedBits(13, 1) 116 .WithTaggedFlag("SYNCMST", 14) 117 .WithTaggedFlag("LOOP", 15) 118 .WithReservedBits(16, 2) 119 .WithTaggedFlag("OETA", 18) 120 .WithTaggedFlag("AUTOADDR", 19) 121 .WithTaggedFlag("OESEL", 20) 122 .WithTaggedFlag("OEPOL", 21) 123 .WithTaggedFlag("RXPOL", 22) 124 .WithTaggedFlag("TXPOL", 23) 125 .WithReservedBits(24, 8); 126 127 Registers.Control.Define(this) 128 .WithReservedBits(0, 1) 129 .WithTaggedFlag("TXBRKEN", 1) 130 .WithTaggedFlag("ADDRDET", 2) 131 .WithReservedBits(3, 3) 132 .WithFlag(6, out transmitDisable, name: "TXDIS") 133 .WithReservedBits(7, 1) 134 .WithTaggedFlag("CC", 8) 135 .WithTaggedFlag("CLRCCONRX", 9) 136 .WithReservedBits(10, 6) 137 .WithTaggedFlag("AUTOBAUD", 16) 138 .WithReservedBits(17, 15); 139 140 Registers.Status.Define(this) 141 .WithReservedBits(0, 1) 142 .WithTaggedFlag("RXIDLE", 1) 143 .WithReservedBits(2, 1) 144 .WithFlag(3, FieldMode.Read, name: "TXIDLE", valueProviderCallback: _ => true) 145 .WithFlag(4, FieldMode.Read, name: "CTS - Clear To Send", valueProviderCallback: _ => true) 146 .WithTaggedFlag("DELTACTS", 5) 147 .WithTaggedFlag("TXDISSTAT", 6) 148 .WithReservedBits(7, 2) 149 .WithTaggedFlag("RXBRK", 10) 150 .WithTaggedFlag("DELTARXBRK", 11) 151 .WithTaggedFlag("START", 12) 152 .WithTaggedFlag("FRAMERRINT", 13) 153 .WithTaggedFlag("PARITYERRINT", 14) 154 .WithTaggedFlag("RXNOSEINT", 15) 155 .WithTaggedFlag("ABERR", 16) 156 .WithReservedBits(17, 15); 157 158 Registers.InterruptEnableReadSet.Define(this) 159 .WithReservedBits(0, 3) 160 .WithTaggedFlag("TXIDLEEN", 3) 161 .WithReservedBits(4, 1) 162 .WithTaggedFlag("DELTACTSEN", 5) 163 .WithTaggedFlag("TXDISEN", 6) 164 .WithReservedBits(7, 4) 165 .WithTaggedFlag("DELTARXBRKEN", 11) 166 .WithTaggedFlag("STARTEN", 12) 167 .WithTaggedFlag("FRAMERREN", 13) 168 .WithTaggedFlag("PARITYERREN", 14) 169 .WithTaggedFlag("RXNOISEEN", 15) 170 .WithTaggedFlag("ABERREN", 16) 171 .WithReservedBits(17, 15); 172 173 Registers.InterruptEnableClear.Define(this) 174 .WithReservedBits(0, 3) 175 .WithTaggedFlag("TXIDLECLR", 3) 176 .WithReservedBits(4, 1) 177 .WithTaggedFlag("DELTACTSCLR", 5) 178 .WithTaggedFlag("TXDISCLR", 6) 179 .WithReservedBits(7, 4) 180 .WithTaggedFlag("DELTARXBRKCLR", 11) 181 .WithTaggedFlag("STARTCLR", 12) 182 .WithTaggedFlag("FRAMERRCLR", 13) 183 .WithTaggedFlag("PARITYERRCLR", 14) 184 .WithTaggedFlag("RXNOISECLR", 15) 185 .WithTaggedFlag("ABERRCLR", 16) 186 .WithReservedBits(17, 15); 187 188 Registers.BaudRateGenerator.Define(this) 189 .WithValueField(0, 16, out baudDivider, name: "BRGVAL") 190 .WithReservedBits(16, 16); 191 192 Registers.InterruptStatus.Define(this) 193 .WithReservedBits(0, 3) 194 .WithTaggedFlag("TXIDLE", 3) 195 .WithReservedBits(4, 1) 196 .WithTaggedFlag("DELTACTS", 5) 197 .WithTaggedFlag("TXDISINT", 6) 198 .WithReservedBits(7, 4) 199 .WithTaggedFlag("DELTARXBRK", 11) 200 .WithTaggedFlag("START", 12) 201 .WithTaggedFlag("FRAMERRINT", 13) 202 .WithTaggedFlag("PARITYERRINT", 14) 203 .WithTaggedFlag("RXNOISEINT", 15) 204 .WithTaggedFlag("ABERRINT", 16) 205 .WithReservedBits(17, 15); 206 207 Registers.OversampleSelection.Define(this) 208 .WithValueField(0, 8, out oversampleSelection, name: "OSRVAL") 209 .WithReservedBits(8, 24); 210 211 Registers.AutomaticAddressMatching.Define(this) 212 .WithTag("ADDRESS", 0, 8) 213 .WithReservedBits(8, 24); 214 215 Registers.FifoConfiguration.Define(this) 216 .WithFlag(0, name: "ENABLETX") 217 .WithFlag(1, name: "ENABLERX") 218 .WithReservedBits(2, 2) 219 .WithTag("SIZE", 4, 2) 220 .WithReservedBits(6, 6) 221 .WithTaggedFlag("DMATX", 12) 222 .WithTaggedFlag("DMARX", 13) 223 .WithTaggedFlag("WAKETX", 14) 224 .WithTaggedFlag("WAKERX", 15) 225 .WithTaggedFlag("EMPTYTX", 16) 226 .WithFlag(17, FieldMode.WriteOneToClear, name: "EMPTYRX", writeCallback: (_, val) => HandleClearRxQueue(val)) 227 .WithTaggedFlag("POPDBG", 18) 228 .WithReservedBits(19, 13); 229 230 Registers.FifoStatus.Define(this) 231 .WithTaggedFlag("TXERR", 0) 232 .WithTaggedFlag("RXERR", 1) 233 .WithReservedBits(2, 1) 234 .WithTaggedFlag("PERINT", 3) 235 .WithFlag(4, FieldMode.Read, name: "TXEMPTY", valueProviderCallback: _ => true) 236 .WithFlag(5, FieldMode.Read, name: "TXNOTFULL", valueProviderCallback: _ => true) 237 .WithFlag(6, FieldMode.Read, name: "RXNOTEMPTY", valueProviderCallback: _ => Count > 0) 238 .WithFlag(7, FieldMode.Read, name: "RXFULL", valueProviderCallback: _ => Count == (int)FifoCount.Full) 239 .WithValueField(8, 5, FieldMode.Read, name: "TXLVL", valueProviderCallback: _ => 0) 240 .WithReservedBits(13, 3) 241 .WithValueField(16, 5, FieldMode.Read, name: "RXLVL", valueProviderCallback: _ => (ulong)Count) 242 .WithReservedBits(21, 11); 243 244 Registers.FifoTriggerSettings.Define(this) 245 .WithFlag(0, out txFifoLevelTriggerEnable, name: "TXLVLENA") 246 .WithFlag(1, out rxFifoLevelTriggerEnable, name: "RXLVLENA") 247 .WithReservedBits(2, 6) 248 .WithValueField(8, 4, out txFifoLevelTriggerPoint, name: "TXLVL") 249 .WithReservedBits(12, 4) 250 .WithValueField(16, 4, out rxFifoLevelTriggerPoint, name: "RXLVL") 251 .WithReservedBits(20, 12) 252 .WithWriteCallback((_, __) => UpdateInterrupts()); 253 254 Registers.FifoInterruptEnable.Define(this) 255 .WithTaggedFlag("TXERR", 0) 256 .WithTaggedFlag("RXERR", 1) 257 .WithFlag(2, out txFifoLevelInterruptEnable, FieldMode.Read | FieldMode.Set, name: "TXLVL") 258 .WithFlag(3, out rxFifoLevelInterruptEnable, FieldMode.Read | FieldMode.Set, name: "RXLVL") 259 .WithReservedBits(4, 28) 260 .WithWriteCallback((_, __) => UpdateInterrupts()); 261 262 Registers.FifoInterruptClear.Define(this) 263 .WithTaggedFlag("TXERR", 0) 264 .WithTaggedFlag("RXERR", 1) 265 .WithFlag(2, name: "TXLVL", 266 writeCallback: (_, val) => 267 { 268 if(!val) 269 { 270 return; 271 } 272 txFifoLevelInterruptEnable.Value = false; 273 }, 274 valueProviderCallback: _ => txFifoLevelInterruptEnable.Value) 275 .WithFlag(3, name: "RXLVL", 276 writeCallback: (_, val) => 277 { 278 if(!val) 279 { 280 return; 281 } 282 rxFifoLevelInterruptEnable.Value = false; 283 }, 284 valueProviderCallback: _ => rxFifoLevelInterruptEnable.Value) 285 .WithReservedBits(4, 28) 286 .WithWriteCallback((_, __) => UpdateInterrupts()); 287 288 Registers.FifoInterruptStatus.Define(this) 289 .WithTaggedFlag("TXERR", 0) 290 .WithTaggedFlag("RXERR", 1) 291 .WithFlag(2, FieldMode.Read, name: "TXLVL", valueProviderCallback: _ => TxLevelInterruptStatus) 292 .WithFlag(3, FieldMode.Read, name: "RXLVL", valueProviderCallback: _ => RxLevelInterruptStatus) 293 .WithReservedBits(4, 28); 294 295 Registers.FifoWriteData.Define(this) 296 .WithValueField(0, 8, FieldMode.Write, name: "TXDATA", 297 writeCallback: (_, val) => 298 { 299 if(transmitDisable.Value) 300 { 301 return; 302 } 303 TransmitCharacter((byte)val); 304 }) 305 .WithReservedBits(8, 24) 306 .WithWriteCallback((_, __) => UpdateInterrupts()); 307 308 Registers.FifoReadData.Define(this) 309 .WithValueField(0, 8, FieldMode.Read, name: "RXDATA", 310 valueProviderCallback: _ => 311 { 312 if(!TryGetCharacter(out var character)) 313 { 314 return 0; 315 } 316 return character; 317 }) 318 .WithReservedBits(8, 24) 319 .WithReadCallback((_, __) => UpdateInterrupts()); 320 321 Registers.FifoDataReadNoFifoPop.Define(this) 322 .WithTag("RXDATA", 0, 9) 323 .WithReservedBits(9, 4) 324 .WithTaggedFlag("FRAMERR", 13) 325 .WithTaggedFlag("PARITYERR", 14) 326 .WithTaggedFlag("RXNOISE", 15) 327 .WithReservedBits(16, 16); 328 329 Registers.FifoSize.Define(this) 330 .WithTag("FIFOSIZE", 0, 5) 331 .WithReservedBits(5, 27); 332 333 // Normally we can select which peripheral to use on Flexcomm. 334 // Since we use this peripheral instead Flexcomm we hardcode USART 335 // as the only possible option. 336 Registers.PeripheralSelectAndId.Define(this) 337 .WithValueField(0, 3, out peripheralSelectId, name: "PERIPHERAL_SELECT") 338 .WithFlag(3, out peripheralSelectLock, FieldMode.Read | FieldMode.Set, name: "LOCK") 339 .WithFlag(4, FieldMode.Read, name: "USART_PRESENT", valueProviderCallback: _ => true) 340 .WithTaggedFlag("SPI_PRESENT", 5) 341 .WithTaggedFlag("I2C_PRESENT", 6) 342 .WithTaggedFlag("I2S_PRESENT", 7) 343 .WithReservedBits(8, 4) 344 .WithTag("ID", 12, 20) 345 .WithWriteCallback((oldVal, __) => HandleFlexcommPeripheralSelect(oldVal)); 346 347 Registers.PeripheralIdentification.Define(this) 348 .WithTag("APRETURE", 0, 8) 349 .WithTag("MINOR_REV", 8, 4) 350 .WithTag("MAJOR_REV", 12, 4) 351 .WithTag("ID", 16, 16); 352 } 353 GetTxFifoTriggerLevelStatus()354 private bool GetTxFifoTriggerLevelStatus() 355 { 356 switch(txFifoLevelTriggerPoint.Value) 357 { 358 case 0b0000: 359 return Count == (int)FifoCount.Empty; 360 case 0b0001: 361 return Count == (int)FifoCount.OneEntry; 362 case 0b1111: 363 return Count <= (int)FifoCount.NoLongerFull; 364 default: 365 this.Log(LogLevel.Warning, "Encountered unexpected TX FIFO trigger level point."); 366 return false; 367 } 368 } 369 GetRxFifoTriggerLevelStatus()370 private bool GetRxFifoTriggerLevelStatus() 371 { 372 switch(rxFifoLevelTriggerPoint.Value) 373 { 374 case 0b0000: 375 return Count >= (int)FifoCount.OneEntry; 376 case 0b0001: 377 return Count >= (int)FifoCount.TwoEntries; 378 case 0b1111: 379 return Count == (int)FifoCount.Full; 380 default: 381 this.Log(LogLevel.Warning, "Encountered unexpected RX FIFO trigger level point."); 382 return false; 383 } 384 } 385 UpdateInterrupts()386 private void UpdateInterrupts() 387 { 388 var status = TxLevelInterruptStatus || RxLevelInterruptStatus; 389 this.Log(LogLevel.Noisy, "IRQ set to {0}.", status); 390 IRQ.Set(status); 391 } 392 HandleClearRxQueue(bool value)393 private void HandleClearRxQueue(bool value) 394 { 395 if(value) 396 { 397 ClearBuffer(); 398 UpdateInterrupts(); 399 } 400 } 401 HandleFlexcommPeripheralSelect(ulong oldValue)402 private void HandleFlexcommPeripheralSelect(ulong oldValue) 403 { 404 var oldIsLocked = (oldValue & 0x8) != 0; 405 var oldPeripheralSelectId = oldValue & 0x7; 406 407 if(oldIsLocked) 408 { 409 this.Log(LogLevel.Warning, "Tried to change flexcomm PSELID register after it has been locked"); 410 peripheralSelectId.Value = oldPeripheralSelectId; 411 peripheralSelectLock.Value = oldIsLocked; 412 return; 413 } 414 415 if(peripheralSelectId.Value != FlexcommPeripheralNotSelected && peripheralSelectId.Value != FlexcommPeripheralUsart) 416 { 417 this.Log( 418 LogLevel.Warning, 419 "Tried to select flexcomm peripheral with ID {0}, but only USART is available", 420 peripheralSelectId.Value 421 ); 422 peripheralSelectId.Value = FlexcommPeripheralNotSelected; 423 } 424 } 425 426 private bool stopBits; 427 private ParityMode parityMode; 428 429 private readonly ulong clockFrequency; 430 431 private const ulong DefaultClockFrequency = 10000000; 432 private const ulong FlexcommPeripheralNotSelected = 0x0; 433 private const ulong FlexcommPeripheralUsart = 0x1; 434 435 private IFlagRegisterField transmitDisable; 436 private IFlagRegisterField txFifoLevelInterruptEnable; 437 private IFlagRegisterField rxFifoLevelInterruptEnable; 438 private IFlagRegisterField txFifoLevelTriggerEnable; 439 private IFlagRegisterField rxFifoLevelTriggerEnable; 440 private IFlagRegisterField peripheralSelectLock; 441 private IValueRegisterField peripheralSelectId; 442 private IValueRegisterField txFifoLevelTriggerPoint; 443 private IValueRegisterField rxFifoLevelTriggerPoint; 444 private IValueRegisterField baudDivider; 445 private IValueRegisterField oversampleSelection; 446 447 private enum ParityMode 448 { 449 NoParity = 0b00, 450 Reserved = 0b01, 451 Even = 0b10, 452 Odd = 0b11, 453 } 454 455 private enum FifoCount : int 456 { 457 Full = 16, 458 NoLongerFull = 15, 459 TwoEntries = 2, 460 OneEntry = 1, 461 Empty = 0 462 } 463 464 private enum Registers 465 { 466 Configuration = 0x0, 467 Control = 0x4, 468 Status = 0x8, 469 InterruptEnableReadSet = 0xC, 470 InterruptEnableClear = 0x10, 471 BaudRateGenerator = 0x20, 472 InterruptStatus = 0x24, 473 OversampleSelection = 0x28, 474 AutomaticAddressMatching = 0x2C, 475 FifoConfiguration = 0xE00, 476 FifoStatus = 0xE04, 477 FifoTriggerSettings = 0xE08, 478 FifoInterruptEnable = 0xE10, 479 FifoInterruptClear = 0xE14, 480 FifoInterruptStatus = 0xE18, 481 FifoWriteData = 0xE20, 482 FifoReadData = 0xE30, 483 FifoDataReadNoFifoPop = 0xE40, 484 FifoSize = 0xE48, 485 PeripheralSelectAndId = 0xFF8, 486 PeripheralIdentification = 0xFFC 487 } 488 } 489 } 490