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