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.Diagnostics;
12 using System.IO;
13 using Antmicro.Renode.Core;
14 using Antmicro.Renode.Core.Structure;
15 using Antmicro.Renode.Core.Structure.Registers;
16 using Antmicro.Renode.Exceptions;
17 using Antmicro.Renode.Logging;
18 using Antmicro.Renode.Utilities;
19 using Antmicro.Renode.Peripherals.Bus;
20 using Antmicro.Renode.Peripherals.SPI;
21 using Antmicro.Renode.Peripherals.Timers;
22 using Antmicro.Renode.Time;
23 using Antmicro.Renode.Peripherals.CPU;
24 using Antmicro.Renode.Peripherals.UART;
25 
26 namespace Antmicro.Renode.Peripherals.UART
27 {
28     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.DoubleWordToByte)]
29     public class EFR32xG2_EUSART_2 : UARTBase, IUARTWithBufferState, IDoubleWordPeripheral, IPeripheralContainer<ISPIPeripheral, NullRegistrationPoint>
30     {
EFR32xG2_EUSART_2(Machine machine, uint clockFrequency = 19000000)31         public EFR32xG2_EUSART_2(Machine machine, uint clockFrequency = 19000000) : base(machine)
32         {
33             this.machine = machine;
34             uartClockFrequency = clockFrequency;
35 
36             TransmitIRQ = new GPIO();
37             ReceiveIRQ = new GPIO();
38             RxFifoLevel = new GPIO();
39             TxFifoLevel = new GPIO();
40             RxFifoLevelGpioSignal = new GPIO();
41 
42             registersCollection = BuildRegistersCollection();
43             TxFifoLevel.Set(true);
44         }
45 
Reset()46         public override void Reset()
47         {
48             base.Reset();
49             isEnabled = false;
50             spiSlaveDevice = null;
51             TxFifoLevel.Set(true);
52         }
53 
ReadDoubleWord(long offset)54         public uint ReadDoubleWord(long offset)
55         {
56             var value = ReadRegister(offset);
57             return value;
58         }
59 
ReadRegister(long offset, bool internal_read = false)60         private uint ReadRegister(long offset, bool internal_read = false)
61         {
62             var result = 0U;
63             long internal_offset = offset;
64 
65             // Set, Clear, Toggle registers should only be used for write operations. But just in case we convert here as well.
66             if (offset >= SetRegisterOffset && offset < ClearRegisterOffset)
67             {
68                 // Set register
69                 internal_offset = offset - SetRegisterOffset;
70                 if(!internal_read)
71                 {
72                     this.Log(LogLevel.Noisy, "SET Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset);
73                 }
74             } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset)
75             {
76                 // Clear register
77                 internal_offset = offset - ClearRegisterOffset;
78                 if(!internal_read)
79                 {
80                     this.Log(LogLevel.Noisy, "CLEAR Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset);
81                 }
82             } else if (offset >= ToggleRegisterOffset)
83             {
84                 // Toggle register
85                 internal_offset = offset - ToggleRegisterOffset;
86                 if(!internal_read)
87                 {
88                     this.Log(LogLevel.Noisy, "TOGGLE Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset);
89                 }
90             }
91 
92             if(!registersCollection.TryRead(internal_offset, out result))
93             {
94                 if(!internal_read)
95                 {
96                     this.Log(LogLevel.Noisy, "Unhandled read at offset 0x{0:X} ({1}).", internal_offset, (Registers)internal_offset);
97                 }
98             }
99             else
100             {
101                 if(!internal_read)
102                 {
103                     this.Log(LogLevel.Noisy, "Read at offset 0x{0:X} ({1}), returned 0x{2:X}.", internal_offset, (Registers)internal_offset, result);
104                 }
105             }
106 
107             return result;
108         }
109 
WriteDoubleWord(long offset, uint value)110         public void WriteDoubleWord(long offset, uint value)
111         {
112             WriteRegister(offset, value);
113         }
114 
WriteRegister(long offset, uint value, bool internal_write = false)115         private void WriteRegister(long offset, uint value, bool internal_write = false)
116         {
117             machine.ClockSource.ExecuteInLock(delegate {
118                 long internal_offset = offset;
119                 uint internal_value = value;
120 
121                 if (offset >= SetRegisterOffset && offset < ClearRegisterOffset)
122                 {
123                     // Set register
124                     internal_offset = offset - SetRegisterOffset;
125                     uint old_value = ReadRegister(internal_offset, true);
126                     internal_value = old_value | value;
127                     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);
128                 } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset)
129                 {
130                     // Clear register
131                     internal_offset = offset - ClearRegisterOffset;
132                     uint old_value = ReadRegister(internal_offset, true);
133                     internal_value = old_value & ~value;
134                     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);
135                 } else if (offset >= ToggleRegisterOffset)
136                 {
137                     // Toggle register
138                     internal_offset = offset - ToggleRegisterOffset;
139                     uint old_value = ReadRegister(internal_offset, true);
140                     internal_value = old_value ^ value;
141                     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);
142                 }
143 
144                 this.Log(LogLevel.Noisy, "Write at offset 0x{0:X} ({1}), value 0x{2:X}.", internal_offset, (Registers)internal_offset, internal_value);
145 
146                 if(!registersCollection.TryWrite(internal_offset, internal_value))
147                 {
148                     this.Log(LogLevel.Noisy, "Unhandled write at offset 0x{0:X} ({1}), value 0x{2:X}.", internal_offset, (Registers)internal_offset, internal_value);
149                     return;
150                 }
151             });
152         }
153 
BuildRegistersCollection()154         private DoubleWordRegisterCollection BuildRegistersCollection()
155         {
156             var registerDictionary = new Dictionary<long, DoubleWordRegister>
157             {
158                     {(long)Registers.InterruptFlag, new DoubleWordRegister(this)
159                     .WithFlag(0, out txCompleteInterrupt, name: "TXCIF")
160                     .WithFlag(1, out txBufferLevelInterrupt, name: "TXFLIF")
161                     .WithFlag(2, out rxDataValidInterrupt, name: "RXFLIF")
162                     .WithFlag(3, out rxBufferFullInterrupt, name: "RXFULLIF")
163                     .WithFlag(4, out rxOverflowInterrupt, name: "RXOFIF")
164                     .WithFlag(5, out rxUnderflowInterrupt, name: "RXUFIF")
165                     .WithTaggedFlag("TXOFIF", 6)
166                     .WithTaggedFlag("TXUFIF", 7)
167                     .WithTaggedFlag("PERRIF", 8)
168                     .WithTaggedFlag("FERRIF", 9)
169                     .WithTaggedFlag("MPAFIF", 10)
170                     .WithTaggedFlag("LOADERRIF", 11)
171                     .WithTaggedFlag("CCFIF", 12)
172                     .WithTaggedFlag("TXIDLEIF", 13)
173                     .WithReservedBits(14, 2)
174                     .WithTaggedFlag("CSWUIF", 16)
175                     .WithReservedBits(17, 1)
176                     .WithTaggedFlag("STARTFIF", 18)
177                     .WithTaggedFlag("SIGFIF", 19)
178                     .WithReservedBits(20, 4)
179                     .WithTaggedFlag("AUTOBAUDDONEIF", 24)
180                     .WithTaggedFlag("RXTOIF", 25)
181                     .WithReservedBits(26, 6)
182                     .WithWriteCallback((_, __) => UpdateInterrupts())
183                 },
184                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this)
185                     .WithFlag(0, out txCompleteInterruptEnable, name: "TXCIEN")
186                     .WithFlag(1, out txBufferLevelInterruptEnable, name: "TXFLIEN")
187                     .WithFlag(2, out rxDataValidInterruptEnable, name: "RXFLIEN")
188                     .WithFlag(3, out rxBufferFullInterruptEnable, name: "RXFULLIEN")
189                     .WithFlag(4, out rxOverflowInterruptEnable, name: "RXOFIEN")
190                     .WithFlag(5, out rxUnderflowInterruptEnable, name: "RXUFIEN")
191                     .WithTaggedFlag("TXOFIEN", 6)
192                     .WithTaggedFlag("TXUFIEN", 7)
193                     .WithTaggedFlag("PERRIEN", 8)
194                     .WithTaggedFlag("FERRIEN", 9)
195                     .WithTaggedFlag("MPAFIEN", 10)
196                     .WithTaggedFlag("LOADERRIEN", 11)
197                     .WithTaggedFlag("CCFIEN", 12)
198                     .WithTaggedFlag("TXIDLEIEN", 13)
199                     .WithReservedBits(14, 2)
200                     .WithTaggedFlag("CSWUIEN", 16)
201                     .WithReservedBits(17, 1)
202                     .WithTaggedFlag("STARTFIEN", 18)
203                     .WithTaggedFlag("SIGFIEN", 19)
204                     .WithReservedBits(20, 4)
205                     .WithTaggedFlag("AUTOBAUDDONEIEN", 24)
206                     .WithTaggedFlag("RXTOIEN", 25)
207                     .WithReservedBits(26, 6)
208                     .WithWriteCallback((_, __) => UpdateInterrupts())
209                 },
210                 {(long)Registers.Enable, new DoubleWordRegister(this)
211                     .WithFlag(0, changeCallback: (_, value) => { isEnabled = value; }, name: "EN")
212                     .WithTaggedFlag("DISABLING",1)
213                     .WithReservedBits(2, 30)
214                 },
215                 {(long)Registers.Cfg_0, new DoubleWordRegister(this)
216                     .WithEnumField(0, 1, out operationModeField, name: "SYNC")
217                     .WithTaggedFlag("LOOPBK", 1)
218                     .WithTaggedFlag("CCEN", 2)
219                     .WithTaggedFlag("MPM", 3)
220                     .WithTaggedFlag("MPAB", 4)
221                     .WithEnumField(5, 3, out oversamplingField, name: "OVS")
222                     .WithReservedBits(8, 2)
223                     .WithTaggedFlag("MSBF", 10)
224                     .WithReservedBits(11, 2)
225                     .WithTaggedFlag("RXINV", 13)
226                     .WithTaggedFlag("TXINV", 14)
227                     .WithReservedBits(15, 2)
228                     .WithTaggedFlag("AUTOTRI", 17)
229                     .WithReservedBits(18, 2)
230                     .WithTaggedFlag("SKIPPERRF", 20)
231                     .WithReservedBits(21, 1)
232                     .WithTaggedFlag("ERRSDMA", 22)
233                     .WithTaggedFlag("ERRSRX", 23)
234                     .WithTaggedFlag("ERRSTX", 24)
235                     .WithReservedBits(25, 5)
236                     .WithTaggedFlag("MVDIS", 30)
237                     .WithTaggedFlag("AUTOBAUDEN", 31)
238                 },
239                 {(long)Registers.Cfg_1, new DoubleWordRegister(this)
240                     .WithTaggedFlag("DBGHALT", 0)
241                     .WithTaggedFlag("CTSINV", 1)
242                     .WithTaggedFlag("CTSEN", 2)
243                     .WithTaggedFlag("RTSINV", 3)
244                     .WithTag("RXTIMEOUT",4,3)
245                     .WithReservedBits(7,2)
246                     .WithTaggedFlag("TXDMAWU", 9)
247                     .WithTaggedFlag("RXDMAWU", 10)
248                     .WithTaggedFlag("SFUBRX", 11)
249                     .WithReservedBits(12,3)
250                     .WithTaggedFlag("RXPRSEN", 15)
251                     .WithTag("TXFIW",16,4)
252                     .WithReservedBits(20,2)
253                     .WithTag("RTSRXFW",22,4)
254                     .WithReservedBits(26,1)
255                     .WithValueField(27, 4, out rxWatermark, name: "RXFIW")
256                     .WithReservedBits(31,1)
257 
258                 },
259                 {(long)Registers.Cfg_2, new DoubleWordRegister(this)
260                     .WithTaggedFlag("MASTER", 0)
261                     .WithTaggedFlag("CLKPOL", 1)
262                     .WithTaggedFlag("CLKPHA", 2)
263                     .WithTaggedFlag("CSINV", 3)
264                     .WithTaggedFlag("AUTOTX", 4)
265                     .WithTaggedFlag("AUTOCS", 5)
266                     .WithTaggedFlag("CLKPRSEN", 6)
267                     .WithTaggedFlag("FORCELOAD", 7)
268                     .WithReservedBits(8,16)
269                     .WithTag("SDIV",24,8)
270                 },
271                 {(long)Registers.FrameCfg, new DoubleWordRegister(this)
272                     .WithTag("DATABITS", 0, 4)
273                     .WithReservedBits(4, 4)
274                     .WithEnumField(8, 2, out parityBitModeField, name: "PARITY")
275                     .WithReservedBits(10, 2)
276                     .WithEnumField(12, 2, out stopBitsModeField, name: "STOPBITS")
277                     .WithReservedBits(14, 18)
278                 },
279                 {(long)Registers.DtxDataCfg, new DoubleWordRegister(this)
280                     .WithTag("DTXDAT", 0, 16)
281                     .WithReservedBits(16, 16)
282                 },
283                 {(long)Registers.IrHfCfg, new DoubleWordRegister(this)
284                     .WithTaggedFlag("IRHFEN", 0)
285                     .WithTag("IRHFPW", 1, 2)
286                     .WithTaggedFlag("IRHFFILT", 3)
287                     .WithReservedBits(4, 28)
288                 },
289                  {(long)Registers.IrLfCfg, new DoubleWordRegister(this)
290                     .WithTaggedFlag("IRLFEN", 0)
291                     .WithReservedBits(1, 31)
292                 },
293                  {(long)Registers.Timing, new DoubleWordRegister(this)
294                     .WithTag("TXDELAY", 0, 2)
295                     .WithReservedBits(2, 2)
296                     .WithTag("CSSETUP", 4, 3)
297                     .WithReservedBits(7, 1)
298                     .WithTag("CSHOLD", 8, 3)
299                     .WithReservedBits(11, 1)
300                     .WithTag("ICS", 12, 3)
301                     .WithReservedBits(15, 1)
302                     .WithTag("SETUPWINDOW", 16, 4)
303                     .WithReservedBits(20, 12)
304                 },
305                 {(long)Registers.StartFrameCfg, new DoubleWordRegister(this)
306                     .WithTag("STARTFRAME", 0, 8)
307                     .WithReservedBits(8, 24)
308                 },
309                 {(long)Registers.SigFrameCfg, new DoubleWordRegister(this)
310                     .WithTag("SIGFRAME", 0, 32)
311                 },
312                 {(long)Registers.ClkDiv, new DoubleWordRegister(this)
313                     .WithReservedBits(0, 3)
314                     .WithValueField(3, 20, out fractionalClockDividerField, name: "DIV")
315                     .WithReservedBits(23, 9)
316                 },
317                 {(long)Registers.TriggerControl, new DoubleWordRegister(this)
318                     .WithTaggedFlag("RXTEN", 0)
319                     .WithTaggedFlag("TXTEN", 1)
320                     .WithTaggedFlag("AUTOTXTEN", 2)
321                     .WithReservedBits(3, 29)
322                 },
323                 {(long)Registers.Command, new DoubleWordRegister(this)
324                     .WithFlag(0, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) receiverEnableFlag.Value = true; }, name: "RXEN")
325                     .WithFlag(1, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) receiverEnableFlag.Value = false; }, name: "RXDIS")
326                     .WithFlag(2, FieldMode.Set, writeCallback: (_, newValue) =>
327                     {
328                         if(newValue)
329                         {
330                             transmitterEnableFlag.Value = true;
331                             txBufferLevelInterrupt.Value = true;
332                             UpdateInterrupts();
333                         }
334                     }, name: "TXEN")
335                     .WithFlag(3, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) transmitterEnableFlag.Value = false; }, name: "TXDIS")
336                     .WithTaggedFlag("RXBLOCKEN", 4)
337                     .WithTaggedFlag("RXBLOCKDIS", 5)
338                     .WithTaggedFlag("TXTRIEN", 6)
339                     .WithTaggedFlag("TXTRIDIS", 7)
340                     .WithTaggedFlag("CLEARTX", 8)
341                     .WithReservedBits(9, 23)
342                 },
343                 {(long)Registers.RxData, new DoubleWordRegister(this)
344                     .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: (_) => ReadBuffer(), name: "RXDATA")
345                     .WithReservedBits(8, 24)
346                 },
347                  {(long)Registers.RxDataPeek, new DoubleWordRegister(this)
348                     .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: (_) => PeekBuffer(), name: "RXDATAP")
349                     .WithReservedBits(16, 16)
350                 },
351                 {(long)Registers.TxData, new DoubleWordRegister(this)
352                     .WithValueField(0, 16, FieldMode.Write, writeCallback: (_, v) => HandleTxBufferData((byte)v), name: "TXDATA")
353                     .WithReservedBits(16, 16)
354                 },
355                 {(long)Registers.Status, new DoubleWordRegister(this, 0x00003040)
356                     .WithFlag(0, out receiverEnableFlag, FieldMode.Read, name: "RXENS")
357                     .WithFlag(1, out transmitterEnableFlag, FieldMode.Read, name: "TXENS")
358                     .WithReservedBits(2,1)
359                     .WithTaggedFlag("RXBLOCK", 3)
360                     .WithTaggedFlag("TXTRI", 4)
361                     .WithFlag(5, out transferCompleteFlag, FieldMode.Read, name: "TXC")
362                     .WithTaggedFlag("TXFL", 6)
363                     .WithFlag(7, out receiveDataValidFlag, FieldMode.Read, name: "RXFL")
364                     .WithFlag(8, FieldMode.Read, valueProviderCallback: _ => Count == BufferSize, name: "RXFULL")
365                     .WithReservedBits(9,3)
366                     .WithFlag(12, FieldMode.Read, valueProviderCallback: _ => true, name: "RXIDLE")
367                     .WithFlag(13, FieldMode.Read, valueProviderCallback: _ => true, name: "TXIDLE")
368                     .WithReservedBits(14,2)
369                     .WithValueField(16, 5, FieldMode.Read, valueProviderCallback: _ => 0, name: "TXFCNT")
370                     .WithReservedBits(21,3)
371                     .WithTaggedFlag("AUTOBAUDDONE", 24)
372                     .WithTaggedFlag("CLEARTXBUSY", 25)
373                     .WithReservedBits(26, 6)
374                 },
375                 {(long)Registers.SyncBusy, new DoubleWordRegister(this)
376                     .WithTaggedFlag("DIV", 0)
377                     .WithTaggedFlag("RXTEN", 1)
378                     .WithTaggedFlag("TXTEN", 2)
379                     .WithTaggedFlag("RXEN", 3)
380                     .WithTaggedFlag("RXDIS", 4)
381                     .WithTaggedFlag("TXEN", 5)
382                     .WithTaggedFlag("TXDIS", 6)
383                     .WithTaggedFlag("RXBLOCKEN", 7)
384                     .WithTaggedFlag("RXBLOCKDIS", 8)
385                     .WithTaggedFlag("TXTRIEN", 9)
386                     .WithTaggedFlag("TXTRIDIS", 10)
387                     .WithTaggedFlag("AUTOTXTEN", 11)
388                     .WithReservedBits(12, 20)
389                 },
390                 {(long)Registers.DaliCfg, new DoubleWordRegister(this)
391                     .WithTaggedFlag("DALIEN", 0)
392                     .WithTag("DALITXDATABITS", 1, 5)
393                     .WithReservedBits(6, 2)
394                     .WithTag("DALIRXDATABITS", 8, 5)
395                     .WithReservedBits(13, 2)
396                     .WithTaggedFlag("DALIRXENDT", 15)
397                     .WithReservedBits(16, 16)
398                 },
399                 {(long)Registers.Test, new DoubleWordRegister(this)
400                     .WithTag("DBGPRSSEL", 0, 2)
401                     .WithReservedBits(2, 30)
402                 },
403             };
404             return new DoubleWordRegisterCollection(this, registerDictionary);
405         }
406 
407         public long Size => 0x4000;
408         private readonly Machine machine;
409         private readonly DoubleWordRegisterCollection registersCollection;
410         private const uint SetRegisterOffset = 0x1000;
411         private const uint ClearRegisterOffset = 0x2000;
412         private const uint ToggleRegisterOffset = 0x3000;
413 #region methods
Register(ISPIPeripheral peripheral, NullRegistrationPoint registrationPoint)414         public void Register(ISPIPeripheral peripheral, NullRegistrationPoint registrationPoint)
415         {
416             if(spiSlaveDevice != null)
417             {
418                 throw new RegistrationException("Cannot register more than one peripheral.");
419             }
420             Machine.RegisterAsAChildOf(this, peripheral, registrationPoint);
421             spiSlaveDevice = peripheral;
422         }
423 
Unregister(ISPIPeripheral peripheral)424         public void Unregister(ISPIPeripheral peripheral)
425         {
426             if(peripheral != spiSlaveDevice)
427             {
428                 throw new RegistrationException("Trying to unregister not registered device.");
429             }
430 
431             Machine.UnregisterAsAChildOf(this, peripheral);
432             spiSlaveDevice = null;
433         }
434 
GetRegistrationPoints(ISPIPeripheral peripheral)435         public IEnumerable<NullRegistrationPoint> GetRegistrationPoints(ISPIPeripheral peripheral)
436         {
437             if(peripheral != spiSlaveDevice)
438             {
439                 throw new RegistrationException("Trying to obtain a registration point for a not registered device.");
440             }
441 
442             return new[] { NullRegistrationPoint.Instance };
443         }
444 
WriteChar(byte value)445         public override void WriteChar(byte value)
446         {
447             if(BufferState == BufferState.Full)
448             {
449                 rxOverflowInterrupt.Value = true;
450                 UpdateInterrupts();
451                 this.Log(LogLevel.Warning, "RX buffer is full. Dropping incoming byte (0x{0:X})", value);
452                 return;
453             }
454             base.WriteChar(value);
455             this.Log(LogLevel.Noisy," Character (0x{0:X}) has been written!",value);
456         }
457 
458         IEnumerable<IRegistered<ISPIPeripheral, NullRegistrationPoint>> IPeripheralContainer<ISPIPeripheral, NullRegistrationPoint>.Children
459         {
460             get
461             {
462                 return new[] { Registered.Create(spiSlaveDevice, NullRegistrationPoint.Instance) };
463             }
464         }
465 
466         public GPIO TransmitIRQ { get; }
467         public GPIO ReceiveIRQ { get; }
468         public GPIO RxFifoLevel { get; }
469         public GPIO TxFifoLevel { get; }
470         public GPIO RxFifoLevelGpioSignal { get; }
471 
472         public override Parity ParityBit { get { return parityBitModeField.Value; } }
473 
474         public override Bits StopBits { get { return stopBitsModeField.Value; } }
475 
476         public override uint BaudRate
477         {
478             get
479             {
480                 var oversample = 1u;
481                 switch(oversamplingField.Value)
482                 {
483                     case OversamplingMode.Times16:
484                         oversample = 16;
485                         break;
486                     case OversamplingMode.Times8:
487                         oversample = 8;
488                         break;
489                     case OversamplingMode.Times6:
490                         oversample = 6;
491                         break;
492                     case OversamplingMode.Times4:
493                         oversample = 4;
494                         break;
495                     case OversamplingMode.Disabled:
496                         oversample = 1;
497                         break;
498                 }
499                 return (uint)(uartClockFrequency / (oversample * (1 + ((double)(fractionalClockDividerField.Value << 3)) / 256)));
500             }
501         }
502 
503         public BufferState BufferState
504         {
505             get
506             {
507                 return bufferState;
508             }
509 
510             private set
511             {
512                 bufferState = value;
513                 BufferStateChanged?.Invoke(value);
514                 this.Log(LogLevel.Noisy, "Buffer state: {0}", bufferState);
515                 switch(bufferState)
516                 {
517                     case BufferState.Empty:
518                         RxFifoLevel.Set(false);
519                         RxFifoLevelGpioSignal.Set(false);
520                         break;
521                     case BufferState.Ready:
522                         if(Count >= (int)rxWatermark.Value + 1)
523                         {
524                             RxFifoLevel.Set(true);
525                             RxFifoLevelGpioSignal.Set(true);
526                             rxDataValidInterrupt.Value = true;
527                         }
528                         else
529                         {
530                             RxFifoLevel.Set(false);
531                             RxFifoLevelGpioSignal.Set(false);
532                         }
533                         UpdateInterrupts();
534                         break;
535                     case BufferState.Full:
536                         RxFifoLevel.Set(true);
537                         RxFifoLevelGpioSignal.Set(true);
538                         rxBufferFullInterrupt.Value = true;
539                         UpdateInterrupts();
540                         break;
541                     default:
542                         this.Log(LogLevel.Error, "Unreachable code. Invalid BufferState value.");
543                         return;
544                 }
545             }
546         }
547 
CharWritten()548         protected override void CharWritten()
549         {
550             if(Count >= (int) rxWatermark.Value + 1)
551             {
552                 rxDataValidInterrupt.Value = true;
553                 receiveDataValidFlag.Value = true;
554                 UpdateInterrupts();
555             }
556             BufferState = Count == BufferSize ? BufferState.Full : BufferState.Ready;
557         }
558 
QueueEmptied()559         protected override void QueueEmptied()
560         {
561 
562             rxDataValidInterrupt.Value = false;
563             receiveDataValidFlag.Value = false;
564             BufferState = BufferState.Empty;
565             UpdateInterrupts();
566         }
567 
HandleTxBufferData(byte data)568         private void HandleTxBufferData(byte data)
569         {
570             this.Log(LogLevel.Noisy , "Handle TX buffer data: {0}", (char)data);
571 
572             if(!transmitterEnableFlag.Value)
573             {
574                 this.Log(LogLevel.Warning, "Trying to send data, but the transmitter is disabled: 0x{0:X}", data);
575                 return;
576             }
577 
578             transferCompleteFlag.Value = false;
579             if(operationModeField.Value == OperationMode.Synchronous)
580             {
581                 if(spiSlaveDevice != null)
582                 {
583                     var result = spiSlaveDevice.Transmit(data);
584                     WriteChar(result);
585                 }
586                 else
587                 {
588                     this.Log(LogLevel.Warning, "Writing data in synchronous mode, but no device is currently connected.");
589                     WriteChar(0x0);
590                 }
591             }
592             else
593             {
594                 TransmitCharacter(data);
595                 txBufferLevelInterrupt.Value = true;
596                 txCompleteInterrupt.Value = true;
597                 UpdateInterrupts();
598             }
599             transferCompleteFlag.Value = true;
600         }
601 
ReadBuffer()602         private byte ReadBuffer()
603         {
604             byte character;
605             return  TryGetCharacter(out character) ? character : (byte)0;
606         }
PeekBuffer()607         private byte PeekBuffer()
608         {
609             byte character;
610             return  TryGetCharacter(out character,true) ? character : (byte)0;
611         }
612 
UpdateInterrupts()613         private void UpdateInterrupts()
614         {
615             machine.ClockSource.ExecuteInLock(delegate {
616                 var txIrq = ((txCompleteInterruptEnable.Value && txCompleteInterrupt.Value)
617                              || (txBufferLevelInterruptEnable.Value && txBufferLevelInterrupt.Value));
618                 TransmitIRQ.Set(txIrq);
619 
620                 var rxIrq = ((rxDataValidInterruptEnable.Value && rxDataValidInterrupt.Value)
621                              || (rxBufferFullInterruptEnable.Value && rxBufferFullInterrupt.Value)
622                              || (rxOverflowInterruptEnable.Value && rxOverflowInterrupt.Value)
623                              || (rxUnderflowInterruptEnable.Value && rxUnderflowInterrupt.Value));
624                 ReceiveIRQ.Set(rxIrq);
625             });
626         }
627 
TrySyncTime()628         private bool TrySyncTime()
629         {
630             if(machine.SystemBus.TryGetCurrentCPU(out var cpu))
631             {
632                 cpu.SyncTime();
633                 return true;
634             }
635             return false;
636         }
637 
GetTime()638         private TimeInterval GetTime() => machine.LocalTimeSource.ElapsedVirtualTime;
639         protected override bool IsReceiveEnabled => receiverEnableFlag.Value;
640 #endregion
641 
642 #region fields
643         private bool isEnabled = false;
644         private ISPIPeripheral spiSlaveDevice;
645         public event Action<BufferState> BufferStateChanged;
646         private IEnumRegisterField<OperationMode> operationModeField;
647         private IEnumRegisterField<OversamplingMode> oversamplingField;
648         private IEnumRegisterField<Parity> parityBitModeField;
649         private IEnumRegisterField<Bits> stopBitsModeField;
650         private IValueRegisterField fractionalClockDividerField;
651         private IFlagRegisterField transferCompleteFlag;
652         private IFlagRegisterField receiveDataValidFlag;
653         private IFlagRegisterField receiverEnableFlag;
654         private IValueRegisterField rxWatermark;
655         private IFlagRegisterField transmitterEnableFlag;
656         private readonly uint uartClockFrequency;
657         private BufferState bufferState;
658         private const int BufferSize = 17; // with shift register
659         // Interrupts
660         private IFlagRegisterField txCompleteInterrupt;
661         private IFlagRegisterField txBufferLevelInterrupt;
662         private IFlagRegisterField rxDataValidInterrupt;
663         private IFlagRegisterField rxBufferFullInterrupt;
664         private IFlagRegisterField rxOverflowInterrupt;
665         private IFlagRegisterField rxUnderflowInterrupt;
666         private IFlagRegisterField txCompleteInterruptEnable;
667         private IFlagRegisterField txBufferLevelInterruptEnable;
668         private IFlagRegisterField rxDataValidInterruptEnable;
669         private IFlagRegisterField rxBufferFullInterruptEnable;
670         private IFlagRegisterField rxOverflowInterruptEnable;
671         private IFlagRegisterField rxUnderflowInterruptEnable;
672 #endregion
673 
674 #region enums
675         private enum OperationMode
676         {
677             Asynchronous,
678             Synchronous
679         }
680 
681         private enum OversamplingMode
682         {
683             Times16,
684             Times8,
685             Times6,
686             Times4,
687             Disabled
688         }
689 
690 
691       private enum Registers : long // : IRegisterDescription
692         {
693             IpVersion                                       = 0x0000,
694             Enable                                          = 0x0004,
695             Cfg_0                                           = 0x0008,
696             Cfg_1                                           = 0x000C,
697             Cfg_2                                           = 0x0010,
698             FrameCfg                                        = 0x001F,
699             DtxDataCfg                                      = 0x0018,
700             IrHfCfg                                         = 0x001C,
701             IrLfCfg                                         = 0x0020,
702             Timing                                          = 0x0024,
703             StartFrameCfg                                   = 0x0028,
704             SigFrameCfg                                     = 0x002C,
705             ClkDiv                                          = 0x0030,
706             TriggerControl                                  = 0x0034,
707             Command                                         = 0x0038,
708             RxData                                          = 0x003C,
709             RxDataPeek                                      = 0x0040,
710             TxData                                          = 0x0044,
711             Status                                          = 0x0048,
712             InterruptFlag                                   = 0x004C,
713             InterruptEnable                                 = 0x0050,
714             SyncBusy                                        = 0x0054,
715             DaliCfg                                         = 0x0058,
716             Test                                            = 0x0100,
717             //set
718             IpVersion_Set                                   = 0x1000,
719             Enable_Set                                      = 0x1004,
720             Cfg_0_Set                                       = 0x1008,
721             Cfg_1_Set                                       = 0x100C,
722             Cfg_2_Set                                       = 0x1010,
723             FrameCfg_Set                                    = 0x101F,
724             DtxDataCfg_Set                                  = 0x1018,
725             IrHfCfg_Set                                     = 0x101C,
726             IrLfCfg_Set                                     = 0x1020,
727             Timing_Set                                      = 0x1024,
728             StartFrameCfg_Set                               = 0x1028,
729             SigFrameCfg_Set                                 = 0x102C,
730             ClkDiv_Set                                      = 0x1030,
731             TriggerControl_Set                              = 0x1034,
732             Command_Set                                     = 0x1038,
733             RxData_Set                                      = 0x103C,
734             RxDataPeek_Set                                  = 0x1040,
735             TxData_Set                                      = 0x1044,
736             Status_Set                                      = 0x1048,
737             InterruptFlag_Set                               = 0x104C,
738             InterruptEnable_Set                             = 0x1050,
739             SyncBusy_Set                                    = 0x1054,
740             DaliCfg_Set                                     = 0x1058,
741             Test_Set                                        = 0x1100,
742             //clr
743             IpVersion_Clr                                   = 0x2000,
744             Enable_Clr                                      = 0x2004,
745             Cfg_0_Clr                                       = 0x2008,
746             Cfg_1_Clr                                       = 0x200C,
747             Cfg_2_Clr                                       = 0x2010,
748             FrameCfg_Clr                                    = 0x201F,
749             DtxDataCfg_Clr                                  = 0x2018,
750             IrHfCfg_Clr                                     = 0x201C,
751             IrLfCfg_Clr                                     = 0x2020,
752             Timing_Clr                                      = 0x2024,
753             StartFrameCfg_Clr                               = 0x2028,
754             SigFrameCfg_Clr                                 = 0x202C,
755             ClkDiv_Clr                                      = 0x2030,
756             TriggerControl_Clr                              = 0x2034,
757             Command_Clr                                     = 0x2038,
758             RxData_Clr                                      = 0x203C,
759             RxDataPeek_Clr                                  = 0x2040,
760             TxData_Clr                                      = 0x2044,
761             Status_Clr                                      = 0x2048,
762             InterruptFlag_Clr                               = 0x204C,
763             InterruptEnable_Clr                             = 0x2050,
764             SyncBusy_Clr                                    = 0x2054,
765             DaliCfg_Clr                                     = 0x2058,
766             Test_Clr                                        = 0x2100,
767             //Toggle
768             IpVersion_Tgl                                   = 0x3000,
769             Enable_Tgl                                      = 0x3004,
770             Cfg_0_Tgl                                       = 0x3008,
771             Cfg_1_Tgl                                       = 0x300C,
772             Cfg_2_Tgl                                       = 0x3010,
773             FrameCfg_Tgl                                    = 0x301F,
774             DtxDataCfg_Tgl                                  = 0x3018,
775             IrHfCfg_Tgl                                     = 0x301C,
776             IrLfCfg_Tgl                                     = 0x3020,
777             Timing_Tgl                                      = 0x3024,
778             StartFrameCfg_Tgl                               = 0x3028,
779             SigFrameCfg_Tgl                                 = 0x302C,
780             ClkDiv_Tgl                                      = 0x3030,
781             TriggerControl_Tgl                              = 0x3034,
782             Command_Tgl                                     = 0x3038,
783             RxData_Tgl                                      = 0x303C,
784             RxDataPeek_Tgl                                  = 0x3040,
785             TxData_Tgl                                      = 0x3044,
786             Status_Tgl                                      = 0x3048,
787             InterruptFlag_Tgl                               = 0x304C,
788             InterruptEnable_Tgl                             = 0x3050,
789             SyncBusy_Tgl                                    = 0x3054,
790             DaliCfg_Tgl                                     = 0x3058,
791             Test_Tgl                                        = 0x3100,
792         }
793 #endregion
794     }
795 }