1 //
2 // Copyright (c) 2010-2023 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using System;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Peripherals.Bus;
12 using System.Collections.Generic;
13 using Antmicro.Migrant;
14 
15 namespace Antmicro.Renode.Peripherals.UART
16 {
17     public class LEUART : IDoubleWordPeripheral, IUART
18     {
LEUART()19         public LEUART()
20         {
21             queueLock = new object();
22             IRQ = new GPIO();
23             Reset();
24         }
25 
26         public GPIO IRQ { get; private set; }
27 
ReadDoubleWord(long offset)28         public uint ReadDoubleWord(long offset)
29         {
30             switch((Register)offset)
31             {
32             case Register.Status:
33                 this.NoisyLog("Status register read, returned {0}.", currentStatus);
34                 return (uint)currentStatus;
35             case Register.InterruptFlag:
36                 this.NoisyLog("Interrupt flag register read, returned {0}.", interruptFlag);
37                 return (uint)interruptFlag;
38             case Register.InterruptEnable:
39                 return interruptEnable;
40             case Register.ReceiveBufferData:
41                 return HandleReadCharacter();
42             case Register.Freeze:
43                 return 0;
44             case Register.SyncBusy:
45                 return 0;
46             default:
47                 this.LogUnhandledRead(offset);
48                 break;
49             }
50             return 0;
51         }
52 
WriteDoubleWord(long offset, uint value)53         public void WriteDoubleWord(long offset, uint value)
54         {
55             switch((Register)offset)
56             {
57             case Register.Control:
58                 controlRegister = value;
59                 break;
60             case Register.TransmitBufferData:
61                 TransmitCharacter((byte)value);
62                 UpdateInterrupts();
63                 break;
64             case Register.InterruptClear:
65                 ClearInterrupt();
66                 break;
67             case Register.InterruptEnable:
68                 interruptEnable = value;
69                 UpdateInterrupts();
70                 break;
71             case Register.ClockControl:
72                 clockControl = value & 0x7FF8;
73                 break;
74             default:
75                 this.LogUnhandledWrite(offset, value);
76                 break;
77             }
78         }
79 
Reset()80         public void Reset()
81         {
82             currentStatus = Status.TransmitBufferEmpty;
83             // Assume that TX buffer is always empty
84             interruptFlag = InterruptFlag.TxCompleteInterrutpt | InterruptFlag.TxBufferLevelInterrupt;
85             waitingChars = new Queue<byte>();
86         }
87 
WriteChar(byte data)88         public void WriteChar(byte data)
89         {
90             this.NoisyLog("Char 0x{0:X} written.", data);
91             interruptFlag |= InterruptFlag.RxDataAvailable;
92             lock(queueLock)
93             {
94                 waitingChars.Enqueue(data);
95                 currentStatus |= Status.RxDataAvailable;
96             }
97             UpdateInterrupts();
98         }
99 
100         [field: Transient]
101         public event Action<byte> CharReceived;
102 
HandleReadCharacter()103         private uint HandleReadCharacter()
104         {
105             lock(queueLock)
106             {
107                 if(waitingChars.Count == 0)
108                 {
109                     return 0;
110                 }
111                 var waitingChar = waitingChars.Dequeue();
112                 if(waitingChars.Count == 0)
113                 {
114                     currentStatus &= ~Status.RxDataAvailable;
115                 }
116                 UpdateInterrupts();
117                 this.NoisyLog("ReceiveBufferData read, returned {0}", waitingChar);
118                 return waitingChar;
119             }
120         }
121 
TransmitCharacter(byte data)122         private void TransmitCharacter(byte data)
123         {
124             var charReceived = CharReceived;
125             if(charReceived != null)
126             {
127                 charReceived(data);
128             }
129         }
130 
ClearInterrupt()131         private void ClearInterrupt()
132         {
133             IRQ.Set(false);
134             interruptFlag &= ~InterruptFlag.RxDataAvailable;
135         }
136 
UpdateInterrupts()137         private void UpdateInterrupts()
138         {
139             IRQ.Set(
140                 ((interruptEnable & (uint)InterruptEnable.RxDataAvailable) != 0 && waitingChars.Count > 0) ||
141                 (interruptEnable & (uint)InterruptEnable.TxBufferLevelInterrupt) != 0 ||
142                 (interruptEnable & (uint)InterruptEnable.TxCompleteInterrutpt) != 0
143             );
144         }
145 
146         private uint controlRegister;
147         private uint clockControl;
148         private uint interruptEnable;
149 
150         private Status currentStatus;
151         private InterruptFlag interruptFlag;
152         private Queue<byte> waitingChars;
153         private readonly object queueLock;
154 
155         [Flags]
156         private enum InterruptFlag
157         {
158             RxDataAvailable = 1 << 2,
159             TxBufferLevelInterrupt = 1 << 1,
160             TxCompleteInterrutpt = 1 << 0
161         }
162 
163         [Flags]
164         private enum InterruptEnable
165         {
166             RxDataAvailable = 1 << 2,
167             TxBufferLevelInterrupt = 1 << 1,
168             TxCompleteInterrutpt = 1 << 0
169         }
170 
171         [Flags]
172         private enum Status
173         {
174             TransmitBufferEmpty = 1 << 4,
175             RxDataAvailable = 1 << 5
176         }
177 
178         [Flags]
179         private enum Control
180         {
181             ParityL =  1 << 2,
182             ParityH =  1 << 3,
183             StopBits = 1 << 4
184         }
185 
186         private enum Register
187         {
188             Control            = 0x000,
189             Status             = 0x008,
190             ReceiveBufferData  = 0x01C,
191             TransmitBufferData = 0x028,
192             InterruptFlag      = 0x02C,
193             InterruptClear     = 0x034,
194             InterruptEnable    = 0x038,
195             Freeze             = 0x040,
196             SyncBusy           = 0x044,
197             ClockControl       = 0x00C
198         }
199 
200         public Bits StopBits
201         {
202             get
203             {
204                 return (controlRegister & (uint)Control.StopBits) != 0 ? Bits.Two : Bits.One;
205             }
206         }
207 
208         public Parity ParityBit
209         {
210             get
211             {
212                 var bits = ((controlRegister & (uint)(Control.ParityH | Control.ParityL)) >> 2);
213                 switch(bits)
214                 {
215                 case 0:
216                     return Parity.None;
217                 case 2:
218                     return Parity.Even;
219                 case 3:
220                     return Parity.Odd;
221                 default:
222                     throw new ArgumentException("Wrong parity bits register value");
223                 }
224             }
225         }
226 
227         public uint BaudRate
228         {
229             get
230             {
231                 // divisor cannot be 0, so there is no need to check it
232                 return UARTClockFrequency / (1 + clockControl/256);
233             }
234         }
235 
236         private const uint UARTClockFrequency = 0;
237     }
238 }
239 
240