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