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 System.Collections.Generic; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Utilities; 13 using Antmicro.Renode.Peripherals.Bus; 14 15 namespace Antmicro.Renode.Peripherals.UART 16 { 17 public class NRF52840_UART : UARTBase, IDoubleWordPeripheral, IKnownSize 18 { NRF52840_UART(IMachine machine, bool easyDMA = false)19 public NRF52840_UART(IMachine machine, bool easyDMA = false) : base(machine) 20 { 21 sysbus = machine.GetSystemBus(this); 22 this.easyDMA = easyDMA; 23 IRQ = new GPIO(); 24 interruptManager = new InterruptManager<Interrupts>(this); 25 registers = new DoubleWordRegisterCollection(this, DefineRegisters()); 26 } 27 ReadDoubleWord(long offset)28 public uint ReadDoubleWord(long offset) 29 { 30 lock(interruptManager) 31 { 32 return registers.Read(offset); 33 } 34 } 35 WriteDoubleWord(long offset, uint value)36 public void WriteDoubleWord(long offset, uint value) 37 { 38 lock(interruptManager) 39 { 40 registers.Write(offset, value); 41 } 42 } 43 Reset()44 public override void Reset() 45 { 46 base.Reset(); 47 lock(interruptManager) 48 { 49 interruptManager.Reset(); 50 registers.Reset(); 51 } 52 53 currentRxPointer = 0; 54 rxStarted = false; 55 } 56 57 public override Bits StopBits => stopBit.Value ? Bits.Two : Bits.One; 58 59 public override Parity ParityBit 60 { 61 get 62 { 63 switch(parity.Value) 64 { 65 case ParityConfig.Excluded: 66 return Parity.None; 67 case ParityConfig.Included: 68 return Parity.Even; 69 default: 70 this.Log(LogLevel.Error, "Wrong parity bits register value"); 71 return Parity.None; 72 } 73 } 74 } 75 76 public override uint BaudRate => GetBaudRate((uint)baudrate.Value); 77 public long Size => 0x1000; 78 79 [IrqProvider] 80 public GPIO IRQ { get; private set; } 81 CharWritten()82 protected override void CharWritten() 83 { 84 if(enabled.Value == EnableState.Disabled || !rxStarted) 85 { 86 this.Log(LogLevel.Warning, "Received a character, but the receiver is disabled."); 87 // The character should not be received. This is safe because QueueEmptied is not used 88 this.TryGetCharacter(out var _); 89 return; 90 } 91 92 lock(interruptManager) 93 { 94 if(interruptManager.IsSet(Interrupts.EndReceive)) 95 { 96 // The receiver stopped, but there might still be characters in the buffer. 97 // This occurs when we paste text to terminal - UART is assumed to be slower 98 // than ISR. That's why we silently wait for the StartRx event. 99 return; 100 } 101 102 if(easyDMA) 103 { 104 // do DMA transfer 105 if(!TryGetCharacter(out var character)) 106 { 107 this.Log(LogLevel.Warning, "Trying to do a DMA transfer from an empty Rx FIFO."); 108 } 109 this.Log(LogLevel.Noisy, "Transfering 0x{0:X} to 0x{1:X}", character, currentRxPointer); 110 sysbus.WriteByte(currentRxPointer, character); 111 rxAmount.Value++; 112 currentRxPointer++; 113 if(rxAmount.Value == rxMaximumCount.Value) 114 { 115 interruptManager.SetInterrupt(Interrupts.EndReceive); 116 } 117 } 118 interruptManager.SetInterrupt(Interrupts.ReceiveReady); 119 } 120 } 121 QueueEmptied()122 protected override void QueueEmptied() 123 { 124 // Intentionally left blank. Implementing this callback might break 125 // the logic of CharWritten when the receiver is disabled. 126 } 127 DefineRegisters()128 private Dictionary<long, DoubleWordRegister> DefineRegisters() 129 { 130 var dict = new Dictionary<long, DoubleWordRegister> 131 { 132 {(long)Registers.StartRx, new DoubleWordRegister(this) 133 .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { if(value) { StartRx(); }}, name: "TASKS_STARTRX") 134 .WithReservedBits(1, 31) 135 }, 136 {(long)Registers.StopRx, new DoubleWordRegister(this) 137 .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { if(value) { StopRx(); }}, name: "TASKS_STOPRX") 138 .WithReservedBits(1, 31) 139 }, 140 {(long)Registers.StartTx, new DoubleWordRegister(this) 141 .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { if(value) { StartTx(); }}, name: "TASKS_STARTTX") 142 .WithReservedBits(1, 31) 143 }, 144 {(long)Registers.StopTx, new DoubleWordRegister(this) 145 .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { if(value) { StopTx(); }}, name: "TASKS_STOPTX") 146 .WithReservedBits(1, 31) 147 }, 148 {(long)Registers.RxDReady, GetEventRegister(Interrupts.ReceiveReady, "EVENTS_RXDRDY") 149 }, 150 {(long)Registers.TxDReady, GetEventRegister(Interrupts.TransmitReady, "EVENTS_TXDRDY") 151 }, 152 {(long)Registers.ErrorDetected, GetEventRegister(Interrupts.Error, "EVENTS_ERROR") 153 // we don't use this interrupt - just want to hush the register 154 }, 155 {(long)Registers.InterruptEnableSet, interruptManager.GetRegister<DoubleWordRegister>( 156 writeCallback: (interrupt, _, newValue) => 157 { 158 if(newValue) 159 { 160 this.Machine.LocalTimeSource.ExecuteInNearestSyncedState(ts => interruptManager.EnableInterrupt(interrupt)); 161 } 162 }, 163 valueProviderCallback: (interrupt, _) => interruptManager.IsEnabled(interrupt) 164 ) 165 }, 166 {(long)Registers.InterruptEnableClear, interruptManager.GetRegister<DoubleWordRegister>( 167 writeCallback: (interrupt, _, newValue) => 168 { 169 if(newValue) 170 { 171 interruptManager.DisableInterrupt(interrupt); 172 } 173 }, 174 valueProviderCallback: (interrupt, _) => interruptManager.IsEnabled(interrupt) 175 ) 176 }, 177 {(long)Registers.Enable, new DoubleWordRegister(this) 178 .WithEnumField(0, 8, out enabled, name: "ENABLE") 179 .WithReservedBits(9, 23) 180 }, 181 {(long)Registers.PinSelectRTS, new DoubleWordRegister(this, 0xFFFFFFFF) 182 .WithTag("PIN", 0, 5) 183 .WithTaggedFlag("PORT", 5) 184 .WithReservedBits(6, 25) 185 .WithTaggedFlag("CONNECT", 31) 186 }, 187 {(long)Registers.PinSelectTXD, new DoubleWordRegister(this, 0xFFFFFFFF) 188 .WithTag("PIN", 0, 5) 189 .WithTaggedFlag("PORT", 5) 190 .WithReservedBits(6, 25) 191 .WithTaggedFlag("CONNECT", 31) 192 }, 193 {(long)Registers.PinSelectCTS, new DoubleWordRegister(this, 0xFFFFFFFF) 194 .WithTag("PIN", 0, 5) 195 .WithTaggedFlag("PORT", 5) 196 .WithReservedBits(6, 25) 197 .WithTaggedFlag("CONNECT", 31) 198 }, 199 {(long)Registers.PinSelectRXD, new DoubleWordRegister(this, 0xFFFFFFFF) 200 .WithTag("PIN", 0, 5) 201 .WithTaggedFlag("PORT", 5) 202 .WithReservedBits(6, 25) 203 .WithTaggedFlag("CONNECT", 31) 204 }, 205 {(long)Registers.BaudRate, new DoubleWordRegister(this, 0x04000000) 206 .WithValueField(0, 32, out baudrate, name: "BAUDRATE") 207 }, 208 {(long)Registers.Config, new DoubleWordRegister(this) 209 .WithTaggedFlag("HWFC", 0) 210 .WithEnumField(1, 3, out parity, name: "CONFIG_PARITY") 211 .WithFlag(4, out stopBit, name: "CONFIG_STOP") 212 .WithReservedBits(5, 27) 213 } 214 }; 215 if(!easyDMA) 216 { 217 // these are registers only for non-eDMA version of the UART 218 dict.Add((long)Registers.RxD, new DoubleWordRegister(this) 219 .WithValueField(0, 8, FieldMode.Read, name: "RXD", valueProviderCallback: _ => 220 { 221 if(!TryGetCharacter(out var character)) 222 { 223 this.Log(LogLevel.Warning, "Trying to read from an empty Rx FIFO."); 224 } 225 226 if(Count > 0) 227 { 228 interruptManager.SetInterrupt(Interrupts.ReceiveReady); 229 } 230 231 return character; 232 }) 233 .WithReservedBits(8, 24) 234 ); 235 dict.Add((long)Registers.TxD, new DoubleWordRegister(this) 236 .WithValueField(0, 8, FieldMode.Write, name: "TXD", writeCallback: (_, value) => 237 { 238 if(enabled.Value == EnableState.Disabled) 239 { 240 this.Log(LogLevel.Warning, "Trying to transmit a character, but the peripheral is disabled."); 241 return; 242 } 243 TransmitCharacter((byte)value); 244 interruptManager.SetInterrupt(Interrupts.TransmitReady); 245 }) 246 .WithReservedBits(8, 24) 247 ); 248 } 249 else 250 { 251 // these are registers only for eDMA version of the UART 252 dict.Add((long)Registers.EndRx, GetEventRegister(Interrupts.EndReceive, "EVENTS_ENDRX")); 253 254 dict.Add((long)Registers.EndTx, GetEventRegister(Interrupts.EndTransmit, "EVENTS_ENDRX")); 255 256 dict.Add((long)Registers.RxTimeout, GetEventRegister(Interrupts.ReceiveTimeout, "EVENTS_RXTO")); 257 258 dict.Add((long)Registers.RxStarted, GetEventRegister(Interrupts.ReceiveStarted, "EVENTS_RXSTARTED")); 259 260 dict.Add((long)Registers.TxStarted, GetEventRegister(Interrupts.TransmitStarted, "EVENTS_TXSTARTED")); 261 262 dict.Add((long)Registers.TxStopped, GetEventRegister(Interrupts.TransmitStopped, "EVENTS_TXSTOPPED")); 263 264 dict.Add((long)Registers.InterruptEnable, interruptManager.GetInterruptEnableRegister<DoubleWordRegister>()); 265 266 dict.Add((long)Registers.RxDPointer, new DoubleWordRegister(this) 267 .WithValueField(0, 32, out rxPointer, name: "PTR") 268 ); 269 270 dict.Add((long)Registers.RxDMaximumCount, new DoubleWordRegister(this) 271 .WithValueField(0, 16, out rxMaximumCount, name: "MAXCNT") 272 .WithReservedBits(16, 16) 273 ); 274 275 dict.Add((long)Registers.RxDAmount, new DoubleWordRegister(this) 276 .WithValueField(0, 16, out rxAmount, FieldMode.Read, name: "AMOUNT") 277 .WithReservedBits(16, 16) 278 ); 279 280 dict.Add((long)Registers.TxDPointer, new DoubleWordRegister(this) 281 .WithValueField(0, 32, out txPointer, name: "PTR") 282 ); 283 284 dict.Add((long)Registers.TxDMaximumCount, new DoubleWordRegister(this) 285 .WithValueField(0, 16, out txMaximumCount, name: "MAXCNT") 286 .WithReservedBits(16, 16) 287 ); 288 289 dict.Add((long)Registers.TxDAmount, new DoubleWordRegister(this) 290 .WithValueField(0, 16, out txAmount, FieldMode.Read, name: "AMOUNT") 291 .WithReservedBits(16, 16) 292 ); 293 } 294 return dict; 295 } 296 GetEventRegister(Interrupts interrupt, string name)297 private DoubleWordRegister GetEventRegister(Interrupts interrupt, string name) 298 { 299 return new DoubleWordRegister(this) 300 .WithFlag(0, 301 valueProviderCallback: _ => interruptManager.IsSet(interrupt), 302 writeCallback: (_, value) => interruptManager.SetInterrupt(interrupt, value), 303 name: name) 304 .WithReservedBits(1, 31); 305 } 306 StartRx()307 private void StartRx() 308 { 309 interruptManager.SetInterrupt(Interrupts.ReceiveStarted); 310 if(easyDMA) 311 { 312 rxAmount.Value = 0; 313 currentRxPointer = (uint)rxPointer.Value; 314 } 315 rxStarted = true; 316 if(Count > 0) 317 { 318 // With the new round of reception we might still have some characters in the 319 // buffer. 320 CharWritten(); 321 } 322 } 323 StopRx()324 private void StopRx() 325 { 326 if(easyDMA && rxAmount.Value < rxMaximumCount.Value) 327 { 328 // we have not generater ENDRX yet, but it's guaranteed to appear before RXTO 329 interruptManager.SetInterrupt(Interrupts.EndReceive); 330 } 331 interruptManager.SetInterrupt(Interrupts.ReceiveTimeout); 332 rxStarted = false; 333 } 334 StartTx()335 private void StartTx() 336 { 337 if(easyDMA) 338 { 339 // we set these interrupts regardless of the transfer length 340 interruptManager.SetInterrupt(Interrupts.TransmitStarted); 341 interruptManager.SetInterrupt(Interrupts.TransmitStopped); 342 interruptManager.SetInterrupt(Interrupts.EndTransmit); 343 344 if(txMaximumCount.Value == 0) 345 { 346 // fake transfer to generate TXSTOPPED (according to the driver, but not the docs) 347 return; 348 } 349 // Should we preallocate? MAXCNT can reach 0xFFFF, which is quite a lot. We could split, but 350 // it's complicated 351 var bytesRead = sysbus.ReadBytes(txPointer.Value, (int)txMaximumCount.Value); 352 foreach(var character in bytesRead) 353 { 354 TransmitCharacter(character); 355 } 356 txAmount.Value = txMaximumCount.Value; 357 } 358 interruptManager.SetInterrupt(Interrupts.TransmitReady); 359 } 360 StopTx()361 private void StopTx() 362 { 363 // the remark from StopRx applies here as well, but we assume that StartTx is always finished at once 364 interruptManager.SetInterrupt(Interrupts.TransmitStopped); 365 } 366 GetBaudRate(uint value)367 private uint GetBaudRate(uint value) 368 { 369 switch((Baudrate)value) 370 { 371 case Baudrate.Baud1200: return 1200; 372 case Baudrate.Baud2400: return 2400; 373 case Baudrate.Baud4800: return 4800; 374 case Baudrate.Baud9600: return 9600; 375 case Baudrate.Baud14400: return 14400; 376 case Baudrate.Baud19200: return 19200; 377 case Baudrate.Baud28800: return 28800; 378 case Baudrate.Baud31250: return 31250; 379 case Baudrate.Baud56000: return 56000; 380 case Baudrate.Baud57600: return 57600; 381 case Baudrate.Baud115200: return 115200; 382 case Baudrate.Baud230400: return 230400; 383 case Baudrate.Baud250000: return 250000; 384 case Baudrate.Baud460800: return 460800; 385 case Baudrate.Baud921600: return 921600; 386 case Baudrate.Baud1M: return 1000000; 387 default: return 0; 388 } 389 } 390 391 private readonly IBusController sysbus; 392 private readonly DoubleWordRegisterCollection registers; 393 private readonly InterruptManager<Interrupts> interruptManager; 394 private readonly bool easyDMA; 395 396 private uint currentRxPointer; 397 private bool rxStarted; 398 399 private IValueRegisterField rxPointer; 400 private IValueRegisterField rxMaximumCount; 401 private IValueRegisterField rxAmount; 402 private IValueRegisterField txPointer; 403 private IValueRegisterField txMaximumCount; 404 private IValueRegisterField txAmount; 405 406 private IValueRegisterField baudrate; 407 private IEnumRegisterField<ParityConfig> parity; 408 private IEnumRegisterField<EnableState> enabled; 409 private IFlagRegisterField stopBit; 410 411 private enum Interrupts 412 { 413 ClearToSend = 0, 414 NotClearToSend = 1, 415 ReceiveReady = 2, 416 EndReceive = 4, 417 TransmitReady = 7, 418 EndTransmit = 8, 419 Error = 9, 420 ReceiveTimeout = 17, 421 ReceiveStarted = 19, 422 TransmitStarted = 20, 423 TransmitStopped = 22, 424 } 425 426 private enum Registers : long 427 { 428 StartRx = 0x000, 429 StopRx = 0x004, 430 StartTx = 0x008, 431 StopTx = 0x00C, 432 Suspend = 0x01C, // no easyDMA 433 FlushRx = 0x02C, // easyDMA 434 ClearToSend = 0x100, 435 NotClearToSend = 0x104, 436 RxDReady = 0x108, 437 EndRx = 0x110, // easyDMA 438 TxDReady = 0x11C, 439 EndTx = 0x120, // easyDMA 440 ErrorDetected = 0x124, 441 RxTimeout = 0x144, 442 RxStarted = 0x14C, // easyDMA 443 TxStarted = 0x150, // easyDMA 444 TxStopped = 0x158, // easyDMA 445 Shortcuts = 0x200, 446 InterruptEnable = 0x300, // easyDMA 447 InterruptEnableSet = 0x304, 448 InterruptEnableClear = 0x308, 449 ErrorSource = 0x480, 450 Enable = 0x500, 451 PinSelectRTS = 0x508, 452 PinSelectTXD = 0x50C, 453 PinSelectCTS = 0x510, 454 PinSelectRXD = 0x514, 455 RxD = 0x518, // no easyDMA 456 TxD = 0x51C, // no easyDMA 457 BaudRate = 0x524, 458 RxDPointer = 0x534, // easyDMA 459 RxDMaximumCount = 0x538, // easyDMA 460 RxDAmount = 0x53C, // easyDMA 461 TxDPointer = 0x544, // easyDMA 462 TxDMaximumCount = 0x548, // easyDMA 463 TxDAmount = 0x54C, // easyDMA 464 Config = 0x56C 465 } 466 467 // The Baudrate values come from documentation 468 private enum Baudrate : long 469 { 470 Baud1200 = 0x0004F000, 471 Baud2400 = 0x0009D000, 472 Baud4800 = 0x0013B000, 473 Baud9600 = 0x00275000, 474 Baud14400 = 0x003B0000, 475 Baud19200 = 0x004EA000, 476 Baud28800 = 0x0075F000, 477 Baud31250 = 0x00800000, 478 Baud38400 = 0x009D5000, 479 Baud56000 = 0x00E50000, 480 Baud57600 = 0x00EBF000, 481 Baud76800 = 0x013A9000, 482 Baud115200 = 0x01D7E000, 483 Baud230400 = 0x03AFB000, 484 Baud250000 = 0x04000000, 485 Baud460800 = 0x075F7000, 486 Baud921600 = 0x0EBED000, 487 Baud1M = 0x10000000 488 } 489 490 private enum ParityConfig 491 { 492 Excluded = 0x0, 493 Included = 0x7 494 } 495 496 private enum EnableState 497 { 498 Disabled = 0x0, 499 EnabledUART = 0x4, 500 EnabledUARTE = 0x8, 501 } 502 } 503 } 504