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.IO;
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Core.Structure;
14 using Antmicro.Renode.Core.Structure.Registers;
15 using Antmicro.Renode.Exceptions;
16 using Antmicro.Renode.Logging;
17 using Antmicro.Renode.Utilities;
18 using Antmicro.Renode.Peripherals.Bus;
19 using Antmicro.Renode.Peripherals.SPI;
20 using Antmicro.Renode.Peripherals.Timers;
21 using Antmicro.Renode.Time;
22 using Antmicro.Renode.Peripherals.CPU;
23 using Antmicro.Renode.Peripherals.UART;
24 
25 namespace Antmicro.Renode.Peripherals.UART
26 {
27     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.DoubleWordToByte)]
28     public class EFR32xG2_USART_0 : UARTBase, IUARTWithBufferState, IDoubleWordPeripheral, IPeripheralContainer<ISPIPeripheral, NullRegistrationPoint>
29     {
EFR32xG2_USART_0(Machine machine, uint clockFrequency)30         public EFR32xG2_USART_0(Machine machine, uint clockFrequency) : base(machine)
31         {
32             this.machine = machine;
33             uartClockFrequency = clockFrequency;
34 
35             TransmitIRQ = new GPIO();
36             ReceiveIRQ = new GPIO();
37             RxDataAvailableRequest = new GPIO();
38             RxDataAvailableSingleRequest = new GPIO();
39             TxBufferLowRequest = new GPIO();
40             TxBufferLowSingleRequest = new GPIO();
41             TxEmptyRequest = new GPIO();
42             RxDataAvailableRightRequest = new GPIO();
43             RxDataAvailableRightSingleRequest = new GPIO();
44             RxDataAvailableGpioSignal = new GPIO();
45             TxBufferLowRightRequest = new GPIO();
46             TxBufferLowRightSingleRequest = new GPIO();
47 
48             compareTimer = new LimitTimer(machine.ClockSource, 115214, this, "usart-compare-timer", 255, direction: Direction.Ascending,
49                                           enabled: false, workMode: WorkMode.OneShot, eventEnabled: true, autoUpdate: true);
50             compareTimer.LimitReached += CompareTimerHandleLimitReached;
51 
52             registersCollection = BuildRegistersCollection();
53 
54             TxEmptyRequest.Set(true);
55             TxBufferLowRequest.Set(true);
56         }
57 
Reset()58         public override void Reset()
59         {
60             base.Reset();
61             isEnabled = false;
62             spiSlaveDevice = null;
63             TxEmptyRequest.Set(true);
64             TxBufferLowRequest.Set(true);
65         }
66 
ReadDoubleWord(long offset)67         public uint ReadDoubleWord(long offset)
68         {
69             return ReadRegister(offset);
70         }
71 
ReadRegister(long offset, bool internal_read = false)72         private uint ReadRegister(long offset, bool internal_read = false)
73         {
74             var result = 0U;
75             long internal_offset = offset;
76 
77             // Set, Clear, Toggle registers should only be used for write operations. But just in case we convert here as well.
78             if (offset >= SetRegisterOffset && offset < ClearRegisterOffset)
79             {
80                 // Set register
81                 internal_offset = offset - SetRegisterOffset;
82                 if(!internal_read)
83                 {
84                     this.Log(LogLevel.Noisy, "SET Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset);
85                 }
86             } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset)
87             {
88                 // Clear register
89                 internal_offset = offset - ClearRegisterOffset;
90                 if(!internal_read)
91                 {
92                     this.Log(LogLevel.Noisy, "CLEAR Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset);
93                 }
94             } else if (offset >= ToggleRegisterOffset)
95             {
96                 // Toggle register
97                 internal_offset = offset - ToggleRegisterOffset;
98                 if(!internal_read)
99                 {
100                     this.Log(LogLevel.Noisy, "TOGGLE Operation on {0}, offset=0x{1:X}, internal_offset=0x{2:X}", (Registers)internal_offset, offset, internal_offset);
101                 }
102             }
103 
104             if(!registersCollection.TryRead(internal_offset, out result))
105             {
106                 if(!internal_read)
107                 {
108                     this.Log(LogLevel.Noisy, "Unhandled read at offset 0x{0:X} ({1}).", internal_offset, (Registers)internal_offset);
109                 }
110             }
111             else
112             {
113                 if(!internal_read)
114                 {
115                     this.Log(LogLevel.Noisy, "Read at offset 0x{0:X} ({1}), returned 0x{2:X}.", internal_offset, (Registers)internal_offset, result);
116                 }
117             }
118 
119             return result;
120         }
121 
WriteDoubleWord(long offset, uint value)122         public void WriteDoubleWord(long offset, uint value)
123         {
124             WriteRegister(offset, value);
125         }
126 
WriteRegister(long offset, uint value, bool internal_write = false)127         private void WriteRegister(long offset, uint value, bool internal_write = false)
128         {
129             machine.ClockSource.ExecuteInLock(delegate {
130                 long internal_offset = offset;
131                 uint internal_value = value;
132 
133                 if (offset >= SetRegisterOffset && offset < ClearRegisterOffset)
134                 {
135                     // Set register
136                     internal_offset = offset - SetRegisterOffset;
137                     uint old_value = ReadRegister(internal_offset, true);
138                     internal_value = old_value | value;
139                     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);
140                 } else if (offset >= ClearRegisterOffset && offset < ToggleRegisterOffset)
141                 {
142                     // Clear register
143                     internal_offset = offset - ClearRegisterOffset;
144                     uint old_value = ReadRegister(internal_offset, true);
145                     internal_value = old_value & ~value;
146                     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);
147                 } else if (offset >= ToggleRegisterOffset)
148                 {
149                     // Toggle register
150                     internal_offset = offset - ToggleRegisterOffset;
151                     uint old_value = ReadRegister(internal_offset, true);
152                     internal_value = old_value ^ value;
153                     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);
154                 }
155 
156                 this.Log(LogLevel.Noisy, "Write at offset 0x{0:X} ({1}), value 0x{2:X}.", internal_offset, (Registers)internal_offset, internal_value);
157 
158                 if(!registersCollection.TryWrite(internal_offset, internal_value))
159                 {
160                     this.Log(LogLevel.Noisy, "Unhandled write at offset 0x{0:X} ({1}), value 0x{2:X}.", internal_offset, (Registers)internal_offset, internal_value);
161                     return;
162                 }
163             });
164         }
165 
BuildRegistersCollection()166         private DoubleWordRegisterCollection BuildRegistersCollection()
167         {
168             var registerDictionary = new Dictionary<long, DoubleWordRegister>
169             {
170                 {(long)Registers.InterruptFlag, new DoubleWordRegister(this)
171                     // RENODE-80: without this workaround, the RX flow in sl_iostream_usart breaks.
172                     // It is unclear if this is the result of a bug in this model or a race condition
173                     // in the embedded code.
174                     .WithFlag(0, out txCompleteInterrupt, valueProviderCallback: _ => (txCompleteInterrupt.Value && txCompleteInterruptEnable.Value), name: "TXCIF")
175                     .WithFlag(1, out txBufferLevelInterrupt, name: "TXBLIF")
176                     .WithFlag(2, out rxDataValidInterrupt, name: "RXDATAVIF")
177                     .WithFlag(3, out rxBufferFullInterrupt, name: "RXFULLIF")
178                     .WithFlag(4, out rxOverflowInterrupt, name: "RXOFIF")
179                     .WithFlag(5, out rxUnderflowInterrupt, name: "RXUFIF")
180                     .WithTaggedFlag("TXOFIF", 6)
181                     .WithTaggedFlag("TXUFIF", 7)
182                     .WithTaggedFlag("PERRIF", 8)
183                     .WithTaggedFlag("FERRIF", 9)
184                     .WithTaggedFlag("MPAFIF", 10)
185                     .WithTaggedFlag("SSMIF", 11)
186                     .WithTaggedFlag("CCFIF", 12)
187                     .WithTaggedFlag("TXIDLEIF", 13)
188                     .WithFlag(14, out timeCompareInterrupt[0], name: "TCMP0IF")
189                     .WithFlag(15, out timeCompareInterrupt[1], name: "TCMP1IF")
190                     .WithFlag(16, out timeCompareInterrupt[2], name: "TCMP2IF")
191                     .WithReservedBits(17, 15)
192                     .WithWriteCallback((_, __) => UpdateInterrupts())
193                 },
194                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this)
195                     .WithFlag(0, out txCompleteInterruptEnable, name: "TXCIEN")
196                     .WithFlag(1, out txBufferLevelInterruptEnable, name: "TXBLIEN")
197                     .WithFlag(2, out rxDataValidInterruptEnable, name: "RXDATAVIEN")
198                     .WithFlag(3, out rxBufferFullInterruptEnable, name: "RXFULLIEN")
199                     .WithFlag(4, out rxOverflowInterruptEnable, name: "RXOFIEN")
200                     .WithFlag(5, out rxUnderflowInterruptEnable, name: "RXUFIEN")
201                     .WithTaggedFlag("TXOFIEN", 6)
202                     .WithTaggedFlag("TXUFIEN", 7)
203                     .WithTaggedFlag("PERRIEN", 8)
204                     .WithTaggedFlag("FERRIEN", 9)
205                     .WithTaggedFlag("MPAFIEN", 10)
206                     .WithTaggedFlag("SSMIEN", 11)
207                     .WithTaggedFlag("CCFIEN", 12)
208                     .WithTaggedFlag("TXIDLEIEN", 13)
209                     .WithFlag(14, out timeCompareInterruptEnable[0], name: "TCMP0IEN")
210                     .WithFlag(15, out timeCompareInterruptEnable[1], name: "TCMP1IEN")
211                     .WithFlag(16, out timeCompareInterruptEnable[2], name: "TCMP2IEN")
212                     .WithReservedBits(17, 15)
213                     .WithWriteCallback((_, __) => UpdateInterrupts())
214                 },
215                 {(long)Registers.Enable, new DoubleWordRegister(this)
216                     .WithFlag(0, changeCallback: (_, value) => { isEnabled = value; }, name: "EN")
217                     .WithReservedBits(1, 31)
218                 },
219                 {(long)Registers.Control, new DoubleWordRegister(this)
220                     .WithEnumField(0, 1, out operationModeField, name: "SYNC")
221                     .WithTaggedFlag("LOOPBK", 1)
222                     .WithTaggedFlag("CCEN", 2)
223                     .WithTaggedFlag("MPM", 3)
224                     .WithTaggedFlag("MPAB", 4)
225                     .WithEnumField(5, 2, out oversamplingField, name: "OVS")
226                     .WithReservedBits(7, 1)
227                     .WithTaggedFlag("CLKPOL", 8)
228                     .WithTaggedFlag("CLKPHA", 9)
229                     .WithTaggedFlag("MSBF", 10)
230                     .WithTaggedFlag("CSMA", 11)
231                     .WithTaggedFlag("TXBIL", 12)
232                     .WithTaggedFlag("RXINV", 13)
233                     .WithTaggedFlag("TXINV", 14)
234                     .WithTaggedFlag("CSINV", 15)
235                     .WithTaggedFlag("AUTOCS", 16)
236                     .WithTaggedFlag("AUTOTRI", 17)
237                     .WithTaggedFlag("SCMODE", 18)
238                     .WithTaggedFlag("SCRETRANS", 19)
239                     .WithTaggedFlag("SKIPPERRF", 20)
240                     .WithTaggedFlag("BIT8DV", 21)
241                     .WithTaggedFlag("ERRSDMA", 22)
242                     .WithTaggedFlag("ERRSRX", 23)
243                     .WithTaggedFlag("ERRSTX", 24)
244                     .WithTaggedFlag("SSSEARLY", 25)
245                     .WithReservedBits(26, 2)
246                     .WithTaggedFlag("BYTESWAP", 28)
247                     .WithTaggedFlag("AUTOTX", 29)
248                     .WithTaggedFlag("MVDIS", 30)
249                     .WithTaggedFlag("SMSDELAY", 31)
250                 },
251                 {(long)Registers.FrameFormat, new DoubleWordRegister(this)
252                     .WithTag("DATABITS", 0, 4)
253                     .WithReservedBits(4, 4)
254                     .WithEnumField(8, 2, out parityBitModeField, name: "PARITY")
255                     .WithReservedBits(10, 2)
256                     .WithEnumField(12, 2, out stopBitsModeField, name: "STOPBITS")
257                     .WithReservedBits(14, 18)
258                 },
259                 {(long)Registers.TriggerControl, new DoubleWordRegister(this)
260                     .WithReservedBits(0, 4)
261                     .WithTaggedFlag("RXTEN", 4)
262                     .WithTaggedFlag("TXTEN", 5)
263                     .WithTaggedFlag("AUTOTXTEN", 6)
264                     .WithTaggedFlag("TXARX0EN", 7)
265                     .WithTaggedFlag("TXARX1EN", 8)
266                     .WithTaggedFlag("TXARX2EN", 9)
267                     .WithTaggedFlag("RXATX0EN", 10)
268                     .WithTaggedFlag("RXATX1EN", 11)
269                     .WithTaggedFlag("RXATX2EN", 12)
270                     .WithReservedBits(13, 3)
271                     .WithTag("TSEL", 16, 4)
272                     .WithReservedBits(20, 12)
273                 },
274                 {(long)Registers.Command, new DoubleWordRegister(this)
275                     .WithFlag(0, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) receiverEnableFlag.Value = true; }, name: "RXEN")
276                     .WithFlag(1, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) receiverEnableFlag.Value = false; }, name: "RXDIS")
277                     .WithFlag(2, FieldMode.Set, writeCallback: (_, newValue) =>
278                     {
279                         if(newValue)
280                         {
281                             transmitterEnableFlag.Value = true;
282                             txBufferLevelInterrupt.Value = true;
283                             UpdateInterrupts();
284                         }
285                     }, name: "TXEN")
286                     .WithFlag(3, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) transmitterEnableFlag.Value = false; }, name: "TXDIS")
287                     .WithTaggedFlag("MASTEREN", 4)
288                     .WithTaggedFlag("MASTERDIS", 5)
289                     .WithTaggedFlag("RXBLOCKEN", 6)
290                     .WithTaggedFlag("RXBLOCKDIS", 7)
291                     .WithTaggedFlag("TXTRIEN", 8)
292                     .WithTaggedFlag("TXTRIDIS", 9)
293                     .WithTaggedFlag("CLEARTX", 10)
294                     .WithFlag(11, FieldMode.Set, writeCallback: (_, newValue) => { if(newValue) ClearBuffer(); }, name: "CLEARRX")
295                     .WithReservedBits(12, 20)
296                 },
297                 {(long)Registers.Status, new DoubleWordRegister(this, 0x00002040)
298                     .WithFlag(0, out receiverEnableFlag, FieldMode.Read, name: "RXENS")
299                     .WithFlag(1, out transmitterEnableFlag, FieldMode.Read, name: "TXENS")
300                     .WithTaggedFlag("MASTER", 2)
301                     .WithTaggedFlag("RXBLOCK", 3)
302                     .WithTaggedFlag("TXTRI", 4)
303                     .WithFlag(5, out transferCompleteFlag, FieldMode.Read, name: "TXC")
304                     .WithTaggedFlag("TXBL", 6)
305                     .WithFlag(7, out receiveDataValidFlag, FieldMode.Read, name: "RXDATAV")
306                     .WithFlag(8, FieldMode.Read, valueProviderCallback: _ => Count == BufferSize, name: "RXFULL")
307                     .WithTaggedFlag("TXBDRIGHT", 9)
308                     .WithTaggedFlag("TXBSRIGHT", 10)
309                     .WithTaggedFlag("RXDATAVRIGHT", 11)
310                     .WithTaggedFlag("RXFULLRIGHT", 12)
311                     .WithFlag(13, FieldMode.Read, valueProviderCallback: _ => true, name: "TXIDLE")
312                     .WithTaggedFlag("TIMERRESTARTED", 14)
313                     .WithReservedBits(15, 1)
314                     .WithValueField(16, 2, FieldMode.Read, valueProviderCallback: _ => 0, name: "TXBUFCNT")
315                     .WithReservedBits(18, 14)
316                 },
317                 {(long)Registers.ClockControl, new DoubleWordRegister(this)
318                     .WithReservedBits(0, 3)
319                     .WithValueField(3, 20, out fractionalClockDividerField, name: "DIV")
320                     .WithReservedBits(23, 8)
321                     .WithTaggedFlag("AUTOBAUDEN", 31)
322                 },
323                 {(long)Registers.RxBufferDataExtended, new DoubleWordRegister(this)
324                     .WithTag("RXDATA", 0, 8)
325                     .WithReservedBits(8, 5)
326                     .WithTaggedFlag("PERR", 14)
327                     .WithTaggedFlag("FERR", 15)
328                     .WithReservedBits(16, 16)
329                 },
330                 {(long)Registers.RxBufferData, new DoubleWordRegister(this)
331                     .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: (_) => ReadBuffer(), name: "RXDATA")
332                     .WithReservedBits(8, 24)
333                 },
334                 {(long)Registers.RxBufferDoubleDataExtended, new DoubleWordRegister(this)
335                     .WithTag("RXDATA0", 0, 8)
336                     .WithReservedBits(8, 5)
337                     .WithTaggedFlag("PERR0", 14)
338                     .WithTaggedFlag("FERR0", 15)
339                     .WithTag("RXDATA1", 16, 8)
340                     .WithReservedBits(24, 5)
341                     .WithTaggedFlag("PERR1", 30)
342                     .WithTaggedFlag("FERR1", 31)
343                 },
344                 {(long)Registers.RxBufferDoubleData, new DoubleWordRegister(this)
345                     .WithTag("RXDATA0", 0, 8)
346                     .WithTag("RXDATA1", 8, 8)
347                     .WithReservedBits(16, 16)
348                 },
349                 {(long)Registers.RxBufferDoubleDataExtendedPeek, new DoubleWordRegister(this)
350                     .WithTag("RXDATAP", 0, 8)
351                     .WithReservedBits(8, 5)
352                     .WithTaggedFlag("PERRP", 14)
353                     .WithTaggedFlag("FERRP", 15)
354                     .WithReservedBits(16, 16)
355                 },
356                 {(long)Registers.TxBufferDataExtended, new DoubleWordRegister(this)
357                     .WithTag("TXDATAX", 0, 8)
358                     .WithReservedBits(8, 2)
359                     .WithTaggedFlag("UBRXAT", 11)
360                     .WithTaggedFlag("TXTRIAT", 12)
361                     .WithTaggedFlag("TXBREAK", 13)
362                     .WithTaggedFlag("TXDISAT", 14)
363                     .WithTaggedFlag("RXENAT", 15)
364                     .WithReservedBits(16, 16)
365                 },
366                 {(long)Registers.TxBufferData, new DoubleWordRegister(this)
367                     .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, v) => HandleTxBufferData((byte)v), name: "TXDATA")
368                     .WithReservedBits(8, 24)
369                 },
370                 {(long)Registers.TxBufferDoubleDataExtended, new DoubleWordRegister(this)
371                     .WithTag("TXDATA0", 0, 8)
372                     .WithReservedBits(8, 2)
373                     .WithTaggedFlag("UBRXAT0", 11)
374                     .WithTaggedFlag("TXTRIAT0", 12)
375                     .WithTaggedFlag("TXBREAK0", 13)
376                     .WithTaggedFlag("TXDISAT0", 14)
377                     .WithTaggedFlag("RXENAT0", 15)
378                     .WithTag("TXDATA1", 16, 8)
379                     .WithReservedBits(24, 2)
380                     .WithTaggedFlag("UBRXAT1", 27)
381                     .WithTaggedFlag("TXTRIAT1", 28)
382                     .WithTaggedFlag("TXBREAK1", 29)
383                     .WithTaggedFlag("TXDISAT1", 30)
384                     .WithTaggedFlag("RXENAT1", 31)
385                 },
386                 {(long)Registers.TxBufferDoubleData, new DoubleWordRegister(this)
387                     .WithTag("TXDATA0", 0, 8)
388                     .WithTag("TXDATA1", 8, 8)
389                     .WithReservedBits(16, 16)
390                 },
391                 {(long)Registers.IrDAControl, new DoubleWordRegister(this)
392                     .WithTaggedFlag("IREN", 0)
393                     .WithTag("IRPW", 1, 2)
394                     .WithTaggedFlag("IRFILT", 3)
395                     .WithReservedBits(4, 3)
396                     .WithTaggedFlag("IRPRSEN", 7)
397                     .WithTag("IRPRSSEL", 8, 4)
398                     .WithReservedBits(12, 20)
399                 },
400                 {(long)Registers.I2SControl, new DoubleWordRegister(this)
401                     .WithTaggedFlag("EN", 0)
402                     .WithTaggedFlag("MONO", 1)
403                     .WithTaggedFlag("JUSTIFY", 2)
404                     .WithTaggedFlag("DMASPLIT", 3)
405                     .WithTaggedFlag("DELAY", 4)
406                     .WithReservedBits(5, 3)
407                     .WithTag("FORMAT", 8, 3)
408                     .WithReservedBits(11, 21)
409                 },
410                 {(long)Registers.Timing, new DoubleWordRegister(this)
411                     .WithReservedBits(0, 16)
412                     .WithTag("TXDELAY", 16, 2)
413                     .WithReservedBits(19, 1)
414                     .WithTag("CSSETUP", 20, 3)
415                     .WithReservedBits(23, 1)
416                     .WithTag("ICS", 24, 3)
417                     .WithReservedBits(27, 1)
418                     .WithTag("CSHOLD", 28, 3)
419                     .WithReservedBits(31, 1)
420                 },
421                 {(long)Registers.ControlExtended, new DoubleWordRegister(this)
422                     .WithTaggedFlag("DBHALT", 0)
423                     .WithTaggedFlag("CTSINV", 1)
424                     .WithTaggedFlag("CTSEN", 2)
425                     .WithTaggedFlag("RTSINV", 3)
426                     .WithReservedBits(4, 27)
427                     .WithTaggedFlag("GPIODELAYXOREN", 31)
428                 },
429                 {(long)Registers.TimeCompare0, new DoubleWordRegister(this)
430                     .WithValueField(0, 8, out compareTimerCompare[0], name: "TCMPVAL")
431                     .WithReservedBits(8, 8)
432                     .WithEnumField<DoubleWordRegister, TimeCompareStartSource>(16, 3, out timeCompareStartSource[0], name: "TSTART")
433                     .WithReservedBits(19, 1)
434                     .WithEnumField<DoubleWordRegister, TimeCompareStopSource>(20, 3, out timeCompareStopSource[0], name: "TSTOP")
435                     .WithReservedBits(23, 1)
436                     .WithFlag(24, out timeCompareRestart[0], name: "RESTARTEN")
437                     .WithReservedBits(25, 7)
438                 },
439                 {(long)Registers.TimeCompare1, new DoubleWordRegister(this)
440                     .WithValueField(0, 8, out compareTimerCompare[1], name: "TCMPVAL")
441                     .WithReservedBits(8, 8)
442                     .WithEnumField<DoubleWordRegister, TimeCompareStartSource>(16, 3, out timeCompareStartSource[1], name: "TSTART")
443                     .WithReservedBits(19, 1)
444                     .WithEnumField<DoubleWordRegister, TimeCompareStopSource>(20, 3, out timeCompareStopSource[1], name: "TSTOP")
445                     .WithReservedBits(23, 1)
446                     .WithFlag(24, out timeCompareRestart[1], name: "RESTARTEN")
447                     .WithReservedBits(25, 7)
448                 },
449                 {(long)Registers.TimeCompare2, new DoubleWordRegister(this)
450                     .WithValueField(0, 8, out compareTimerCompare[2], name: "TCMPVAL")
451                     .WithReservedBits(8, 8)
452                     .WithEnumField<DoubleWordRegister, TimeCompareStartSource>(16, 3, out timeCompareStartSource[2], name: "TSTART")
453                     .WithReservedBits(19, 1)
454                     .WithEnumField<DoubleWordRegister, TimeCompareStopSource>(20, 3, out timeCompareStopSource[2], name: "TSTOP")
455                     .WithReservedBits(23, 1)
456                     .WithFlag(24, out timeCompareRestart[2], name: "RESTARTEN")
457                     .WithReservedBits(25, 7)
458                 },
459                 {(long)Registers.Test, new DoubleWordRegister(this)
460                     .WithTaggedFlag("GPIODELAYSTABLE", 0)
461                     .WithTaggedFlag("GPIODELAYXOR", 1)
462                     .WithReservedBits(2, 30)
463                 },
464             };
465             return new DoubleWordRegisterCollection(this, registerDictionary);
466         }
467 
468         public long Size => 0x4000;
469         private readonly Machine machine;
470         private readonly DoubleWordRegisterCollection registersCollection;
471         private const uint SetRegisterOffset = 0x1000;
472         private const uint ClearRegisterOffset = 0x2000;
473         private const uint ToggleRegisterOffset = 0x3000;
474         private const uint NumberOfTimeCompareTimers = 3;
475 
476 #region methods
Register(ISPIPeripheral peripheral, NullRegistrationPoint registrationPoint)477         public void Register(ISPIPeripheral peripheral, NullRegistrationPoint registrationPoint)
478         {
479             if(spiSlaveDevice != null)
480             {
481                 throw new RegistrationException("Cannot register more than one peripheral.");
482             }
483             Machine.RegisterAsAChildOf(this, peripheral, registrationPoint);
484             spiSlaveDevice = peripheral;
485         }
486 
Unregister(ISPIPeripheral peripheral)487         public void Unregister(ISPIPeripheral peripheral)
488         {
489             if(peripheral != spiSlaveDevice)
490             {
491                 throw new RegistrationException("Trying to unregister not registered device.");
492             }
493 
494             Machine.UnregisterAsAChildOf(this, peripheral);
495             spiSlaveDevice = null;
496         }
497 
GetRegistrationPoints(ISPIPeripheral peripheral)498         public IEnumerable<NullRegistrationPoint> GetRegistrationPoints(ISPIPeripheral peripheral)
499         {
500             if(peripheral != spiSlaveDevice)
501             {
502                 throw new RegistrationException("Trying to obtain a registration point for a not registered device.");
503             }
504 
505             return new[] { NullRegistrationPoint.Instance };
506         }
507 
WriteChar(byte value)508         public override void WriteChar(byte value)
509         {
510             if(BufferState == BufferState.Full)
511             {
512                 rxOverflowInterrupt.Value = true;
513                 UpdateInterrupts();
514                 this.Log(LogLevel.Warning, "RX buffer is full. Dropping incoming byte (0x{0:X})", value);
515                 return;
516             }
517             TriggerCompareTimerStopEvent(TimeCompareStopSource.RxActive);
518             base.WriteChar(value);
519         }
520 
521         IEnumerable<IRegistered<ISPIPeripheral, NullRegistrationPoint>> IPeripheralContainer<ISPIPeripheral, NullRegistrationPoint>.Children
522         {
523             get
524             {
525                 return new[] { Registered.Create(spiSlaveDevice, NullRegistrationPoint.Instance) };
526             }
527         }
528 
529         public GPIO TransmitIRQ { get; }
530         public GPIO ReceiveIRQ { get; }
531         public GPIO RxDataAvailableRequest { get; }
532         public GPIO RxDataAvailableSingleRequest { get; }
533         public GPIO TxBufferLowRequest { get; }
534         public GPIO TxBufferLowSingleRequest { get; }
535         public GPIO TxEmptyRequest { get; }
536         public GPIO RxDataAvailableRightRequest { get; }
537         public GPIO RxDataAvailableRightSingleRequest { get; }
538         public GPIO RxDataAvailableGpioSignal  { get; }
539         public GPIO TxBufferLowRightRequest { get; }
540         public GPIO TxBufferLowRightSingleRequest { get; }
541 
542         public override Parity ParityBit { get { return parityBitModeField.Value; } }
543 
544         public override Bits StopBits { get { return stopBitsModeField.Value; } }
545 
546         public override uint BaudRate
547         {
548             get
549             {
550                 var oversample = 1u;
551                 switch(oversamplingField.Value)
552                 {
553                     case OversamplingMode.Times16:
554                         oversample = 16;
555                         break;
556                     case OversamplingMode.Times8:
557                         oversample = 8;
558                         break;
559                     case OversamplingMode.Times6:
560                         oversample = 6;
561                         break;
562                     case OversamplingMode.Times4:
563                         oversample = 4;
564                         break;
565                 }
566                 return (uint)(uartClockFrequency / (oversample * (1 + ((double)(fractionalClockDividerField.Value << 3)) / 256)));
567             }
568         }
569 
570         public BufferState BufferState
571         {
572             get
573             {
574                 return bufferState;
575             }
576 
577             private set
578             {
579                 if(bufferState == value)
580                 {
581                     return;
582                 }
583                 bufferState = value;
584                 BufferStateChanged?.Invoke(value);
585                 switch(bufferState)
586                 {
587                     case BufferState.Empty:
588                         RxDataAvailableRequest.Set(false);
589                         RxDataAvailableSingleRequest.Set(false);
590                         RxDataAvailableGpioSignal.Set(false);
591                         break;
592                     case BufferState.Ready:
593                         RxDataAvailableRequest.Set(false);
594                         RxDataAvailableSingleRequest.Set(true);
595                         RxDataAvailableGpioSignal.Set(true);
596                         break;
597                     case BufferState.Full:
598                         RxDataAvailableRequest.Set(true);
599                         RxDataAvailableGpioSignal.Set(true);
600                         rxBufferFullInterrupt.Value = true;
601                         UpdateInterrupts();
602                         break;
603                     default:
604                         this.Log(LogLevel.Error, "Unreachable code. Invalid BufferState value.");
605                         return;
606                 }
607             }
608         }
609 
CharWritten()610         protected override void CharWritten()
611         {
612             rxDataValidInterrupt.Value = true;
613             receiveDataValidFlag.Value = true;
614             UpdateInterrupts();
615             BufferState = Count == BufferSize ? BufferState.Full : BufferState.Ready;
616             TriggerCompareTimerStopEvent(TimeCompareStopSource.RxInactive);
617             TriggerCompareTimerStartEvent(TimeCompareStartSource.RxEndOfFrame);
618         }
619 
QueueEmptied()620         protected override void QueueEmptied()
621         {
622             rxDataValidInterrupt.Value = false;
623             receiveDataValidFlag.Value = false;
624             BufferState = BufferState.Empty;
625             UpdateInterrupts();
626         }
627 
HandleTxBufferData(byte data)628         private void HandleTxBufferData(byte data)
629         {
630             this.Log(LogLevel.Noisy, "Handle TX buffer data: {0}", data);
631 
632             if(!transmitterEnableFlag.Value)
633             {
634                 this.Log(LogLevel.Warning, "Trying to send data, but the transmitter is disabled: 0x{0:X}", data);
635                 return;
636             }
637 
638             TriggerCompareTimerStopEvent(TimeCompareStopSource.TxStart);
639 
640             transferCompleteFlag.Value = false;
641             if(operationModeField.Value == OperationMode.Synchronous)
642             {
643                 if(spiSlaveDevice != null)
644                 {
645                     var result = spiSlaveDevice.Transmit(data);
646                     WriteChar(result);
647                 }
648                 else
649                 {
650                     this.Log(LogLevel.Warning, "Writing data in synchronous mode, but no device is currently connected.");
651                     WriteChar(0x0);
652                 }
653             }
654             else
655             {
656                 TransmitCharacter(data);
657                 txBufferLevelInterrupt.Value = true;
658                 txCompleteInterrupt.Value = true;
659                 UpdateInterrupts();
660             }
661             transferCompleteFlag.Value = true;
662             TriggerCompareTimerStartEvent(TimeCompareStartSource.TxEndOfFrame);
663             TriggerCompareTimerStartEvent(TimeCompareStartSource.TxComplete);
664         }
665 
ReadBuffer()666         private byte ReadBuffer()
667         {
668             byte character;
669             if (TryGetCharacter(out character))
670             {
671                 return character;
672             }
673             else
674             {
675                 rxUnderflowInterrupt.Value = true;
676                 UpdateInterrupts();
677                 return (byte)0;
678             }
679         }
680 
TriggerCompareTimerStartEvent(TimeCompareStartSource source)681         protected void TriggerCompareTimerStartEvent(TimeCompareStartSource source)
682         {
683             for(uint i=0; i<NumberOfTimeCompareTimers; i++)
684             {
685                 if (timeCompareStartSource[i].Value == source)
686                 {
687                     // From the design book: "The start source enables the comparator, resets the counter,
688                     // and starts the counter. If the counter is already running, the start source will reset
689                     // the counter and restart it."
690                     RestartCompareTimer(i);
691                     break;
692                 }
693             }
694         }
695 
TriggerCompareTimerStopEvent(TimeCompareStopSource source)696         protected void TriggerCompareTimerStopEvent(TimeCompareStopSource source)
697         {
698             if (compareTimer.Enabled
699                 && startIndex < NumberOfTimeCompareTimers
700                 && source == timeCompareStopSource[startIndex].Value)
701             {
702                 compareTimer.Enabled = false;
703                 startIndex = 0xFF;
704             }
705         }
706 
CompareTimerHandleLimitReached()707         protected void CompareTimerHandleLimitReached()
708         {
709             uint timerIndex = startIndex;
710             compareTimer.Enabled = false;
711             timeCompareInterrupt[startIndex].Value = true;
712             startIndex = 0xFF;
713             UpdateInterrupts();
714 
715             if (timeCompareRestart[timerIndex].Value)
716             {
717                 RestartCompareTimer(timerIndex);
718             }
719         }
720 
RestartCompareTimer(uint timerIndex)721         protected void RestartCompareTimer(uint timerIndex)
722         {
723             startIndex = (byte)timerIndex;
724 
725             // Start source will reset the counter and restart it
726             compareTimer.Frequency = BaudRate;
727             compareTimer.Limit = compareTimerCompare[timerIndex].Value;
728             compareTimer.Enabled = true;
729         }
730 
UpdateInterrupts()731         private void UpdateInterrupts()
732         {
733             machine.ClockSource.ExecuteInLock(delegate {
734                 var txIrq = ((txCompleteInterruptEnable.Value && txCompleteInterrupt.Value)
735                              || (txBufferLevelInterruptEnable.Value && txBufferLevelInterrupt.Value));
736                 TransmitIRQ.Set(txIrq);
737 
738                 var rxIrq = ((rxDataValidInterruptEnable.Value && rxDataValidInterrupt.Value)
739                              || (rxBufferFullInterruptEnable.Value && rxBufferFullInterrupt.Value)
740                              || (rxOverflowInterruptEnable.Value && rxOverflowInterrupt.Value)
741                              || (rxUnderflowInterruptEnable.Value && rxUnderflowInterrupt.Value)
742                              || (timeCompareInterruptEnable[0].Value && timeCompareInterrupt[0].Value)
743                              || (timeCompareInterruptEnable[1].Value && timeCompareInterrupt[1].Value)
744                              || (timeCompareInterruptEnable[2].Value && timeCompareInterrupt[2].Value));
745                 ReceiveIRQ.Set(rxIrq);
746             });
747         }
748 
TrySyncTime()749         private bool TrySyncTime()
750         {
751             if(machine.SystemBus.TryGetCurrentCPU(out var cpu))
752             {
753                 cpu.SyncTime();
754                 return true;
755             }
756             return false;
757         }
758 
GetTime()759         private TimeInterval GetTime() => machine.LocalTimeSource.ElapsedVirtualTime;
760         protected override bool IsReceiveEnabled => receiverEnableFlag.Value;
761 #endregion
762 
763 #region fields
764         private bool isEnabled = false;
765         private ISPIPeripheral spiSlaveDevice;
766         public event Action<BufferState> BufferStateChanged;
767         private IEnumRegisterField<OperationMode> operationModeField;
768         private IEnumRegisterField<OversamplingMode> oversamplingField;
769         private IEnumRegisterField<Parity> parityBitModeField;
770         private IEnumRegisterField<Bits> stopBitsModeField;
771         private IValueRegisterField fractionalClockDividerField;
772         private IFlagRegisterField transferCompleteFlag;
773         private IFlagRegisterField receiveDataValidFlag;
774         private IFlagRegisterField receiverEnableFlag;
775         private IFlagRegisterField transmitterEnableFlag;
776         private readonly uint uartClockFrequency;
777         private BufferState bufferState;
778         private const int BufferSize = 3; // with shift register
779         private IValueRegisterField[] compareTimerCompare = new IValueRegisterField[NumberOfTimeCompareTimers];
780         private IEnumRegisterField<TimeCompareStartSource>[] timeCompareStartSource = new IEnumRegisterField<TimeCompareStartSource>[NumberOfTimeCompareTimers];
781         private IEnumRegisterField<TimeCompareStopSource>[] timeCompareStopSource = new IEnumRegisterField<TimeCompareStopSource>[NumberOfTimeCompareTimers];
782         private IFlagRegisterField[] timeCompareRestart = new IFlagRegisterField[NumberOfTimeCompareTimers];
783         private LimitTimer compareTimer;
784         private byte startIndex = 0xFF;
785         // Interrupts
786         private IFlagRegisterField txCompleteInterrupt;
787         private IFlagRegisterField txBufferLevelInterrupt;
788         private IFlagRegisterField rxDataValidInterrupt;
789         private IFlagRegisterField rxBufferFullInterrupt;
790         private IFlagRegisterField rxOverflowInterrupt;
791         private IFlagRegisterField rxUnderflowInterrupt;
792         private IFlagRegisterField[] timeCompareInterrupt = new IFlagRegisterField[NumberOfTimeCompareTimers];
793         private IFlagRegisterField txCompleteInterruptEnable;
794         private IFlagRegisterField txBufferLevelInterruptEnable;
795         private IFlagRegisterField rxDataValidInterruptEnable;
796         private IFlagRegisterField rxBufferFullInterruptEnable;
797         private IFlagRegisterField rxOverflowInterruptEnable;
798         private IFlagRegisterField rxUnderflowInterruptEnable;
799         private IFlagRegisterField[] timeCompareInterruptEnable = new IFlagRegisterField[NumberOfTimeCompareTimers];
800 #endregion
801 
802 #region enums
803         protected enum OperationMode
804         {
805             Asynchronous,
806             Synchronous
807         }
808 
809         protected enum OversamplingMode
810         {
811             Times16,
812             Times8,
813             Times6,
814             Times4
815         }
816 
817         protected enum TimeCompareStartSource
818         {
819             Disabled            = 0,
820             TxEndOfFrame        = 1,
821             TxComplete          = 2,
822             RxActive            = 3,
823             RxEndOfFrame        = 4,
824         }
825 
826         protected enum TimeCompareStopSource
827         {
828             CompareValueReached = 0,
829             TxStart             = 1,
830             RxActive            = 2,
831             RxInactive          = 3,
832         }
833 
834         private enum Registers
835         {
836             IpVersion                                       = 0x0000,
837             Enable                                          = 0x0004,
838             Control                                         = 0x0008,
839             FrameFormat                                     = 0x000C,
840             TriggerControl                                  = 0x0010,
841             Command                                         = 0x0014,
842             Status                                          = 0x0018,
843             ClockControl                                    = 0x001C,
844             RxBufferDataExtended                            = 0x0020,
845             RxBufferData                                    = 0x0024,
846             RxBufferDoubleDataExtended                      = 0x0028,
847             RxBufferDoubleData                              = 0x002C,
848             RxBufferDataExtendedPeek                        = 0x0030,
849             RxBufferDoubleDataExtendedPeek                  = 0x0034,
850             TxBufferDataExtended                            = 0x0038,
851             TxBufferData                                    = 0x003C,
852             TxBufferDoubleDataExtended                      = 0x0040,
853             TxBufferDoubleData                              = 0x0044,
854             InterruptFlag                                   = 0x0048,
855             InterruptEnable                                 = 0x004C,
856             IrDAControl                                     = 0x0050,
857             I2SControl                                      = 0x0054,
858             Timing                                          = 0x0058,
859             ControlExtended                                 = 0x005C,
860             TimeCompare0                                    = 0x0060,
861             TimeCompare1                                    = 0x0064,
862             TimeCompare2                                    = 0x0068,
863             Test                                            = 0x006C,
864             // Set
865             IpVersion_Set                                   = 0x1000,
866             Enable_Set                                      = 0x1004,
867             Control_Set                                     = 0x1008,
868             FrameFormat_Set                                 = 0x100C,
869             TriggerControl_Set                              = 0x1010,
870             Command_Set                                     = 0x1014,
871             Status_Set                                      = 0x1018,
872             ClockControl_Set                                = 0x101C,
873             RxBufferDataExtended_Set                        = 0x1020,
874             RxBufferData_Set                                = 0x1024,
875             RxBufferDoubleDataExtended_Set                  = 0x1028,
876             RxBufferDoubleData_Set                          = 0x102C,
877             RxBufferDataExtendedPeek_Set                    = 0x1030,
878             RxBufferDoubleDataExtendedPeek_Set              = 0x1034,
879             TxBufferDataExtended_Set                        = 0x1038,
880             TxBufferData_Set                                = 0x103C,
881             TxBufferDoubleDataExtended_Set                  = 0x1040,
882             TxBufferDoubleData_Set                          = 0x1044,
883             InterruptFlag_Set                               = 0x1048,
884             InterruptEnable_Set                             = 0x104C,
885             IrDAControl_Set                                 = 0x1050,
886             I2SControl_Set                                  = 0x1054,
887             Timing_Set                                      = 0x1058,
888             ControlExtended_Set                             = 0x105C,
889             TimeCompare0_Set                                = 0x1060,
890             TimeCompare1_Set                                = 0x1064,
891             TimeCompare2_Set                                = 0x1068,
892             Test_Set                                        = 0x106C,
893             // Clear
894             IpVersion_Clr                                   = 0x2000,
895             Enable_Clr                                      = 0x2004,
896             Control_Clr                                     = 0x2008,
897             FrameFormat_Clr                                 = 0x200C,
898             TriggerControl_Clr                              = 0x2010,
899             Command_Clr                                     = 0x2014,
900             Status_Clr                                      = 0x2018,
901             ClockControl_Clr                                = 0x201C,
902             RxBufferDataExtended_Clr                        = 0x2020,
903             RxBufferData_Clr                                = 0x2024,
904             RxBufferDoubleDataExtended_Clr                  = 0x2028,
905             RxBufferDoubleData_Clr                          = 0x202C,
906             RxBufferDataExtendedPeek_Clr                    = 0x2030,
907             RxBufferDoubleDataExtendedPeek_Clr              = 0x2034,
908             TxBufferDataExtended_Clr                        = 0x2038,
909             TxBufferData_Clr                                = 0x203C,
910             TxBufferDoubleDataExtended_Clr                  = 0x2040,
911             TxBufferDoubleData_Clr                          = 0x2044,
912             InterruptFlag_Clr                               = 0x2048,
913             InterruptEnable_Clr                             = 0x204C,
914             IrDAControl_Clr                                 = 0x2050,
915             I2SControl_Clr                                  = 0x2054,
916             Timing_Clr                                      = 0x2058,
917             ControlExtended_Clr                             = 0x205C,
918             TimeCompare0_Clr                                = 0x2060,
919             TimeCompare1_Clr                                = 0x2064,
920             TimeCompare2_Clr                                = 0x2068,
921             Test_Clr                                        = 0x206C,
922             // Toggle
923             IpVersion_Tgl                                   = 0x3000,
924             Enable_Tgl                                      = 0x3004,
925             Control_Tgl                                     = 0x3008,
926             FrameFormat_Tgl                                 = 0x300C,
927             TriggerControl_Tgl                              = 0x3010,
928             Command_Tgl                                     = 0x3014,
929             Status_Tgl                                      = 0x3018,
930             ClockControl_Tgl                                = 0x301C,
931             RxBufferDataExtended_Tgl                        = 0x3020,
932             RxBufferData_Tgl                                = 0x3024,
933             RxBufferDoubleDataExtended_Tgl                  = 0x3028,
934             RxBufferDoubleData_Tgl                          = 0x302C,
935             RxBufferDataExtendedPeek_Tgl                    = 0x3030,
936             RxBufferDoubleDataExtendedPeek_Tgl              = 0x3034,
937             TxBufferDataExtended_Tgl                        = 0x3038,
938             TxBufferData_Tgl                                = 0x303C,
939             TxBufferDoubleDataExtended_Tgl                  = 0x3040,
940             TxBufferDoubleData_Tgl                          = 0x3044,
941             InterruptFlag_Tgl                               = 0x3048,
942             InterruptEnable_Tgl                             = 0x304C,
943             IrDAControl_Tgl                                 = 0x3050,
944             I2SControl_Tgl                                  = 0x3054,
945             Timing_Tgl                                      = 0x3058,
946             ControlExtended_Tgl                             = 0x305C,
947             TimeCompare0_Tgl                                = 0x3060,
948             TimeCompare1_Tgl                                = 0x3064,
949             TimeCompare2_Tgl                                = 0x3068,
950             Test_Tgl                                        = 0x306C,
951         }
952 #endregion
953     }
954 }