1 //
2 // Copyright (c) 2010-2022 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 Antmicro.Renode.Core;
9 using Antmicro.Renode.Logging;
10 using Antmicro.Renode.Peripherals.Bus;
11 using System.Collections.Generic;
12 using Antmicro.Renode.Core.Structure;
13 using System;
14 using Antmicro.Renode.Exceptions;
15 
16 namespace Antmicro.Renode.Peripherals.UART
17 {
18     public class EFM32_UART : UARTBase, IDoubleWordPeripheral
19     {
EFM32_UART(IMachine machine)20         public EFM32_UART(IMachine machine) : base(machine)
21         {
22             TransmitIRQ = new GPIO();
23             ReceiveIRQ = new GPIO();
24         }
25 
26         public GPIO TransmitIRQ{get; private set;}
27         public GPIO ReceiveIRQ{get; private set;}
28 
29         private uint frameFormatRegister;
30         private uint clockControlRegister;
31         private bool txInterruptEnabled = false;
WriteDoubleWord(long address, uint value)32         public void WriteDoubleWord(long address, uint value)
33         {
34             switch((Registers)address)
35             {
36             case Registers.FrameFormat:
37                 frameFormatRegister = value;
38                 break;
39             case Registers.Command:
40                 break;
41             case Registers.TxBufferData:
42                 TransmitCharacter((byte)value);
43                 // HACK:
44                 if(txInterruptEnabled)
45                     TransmitIRQ.Blink();
46                 break;
47             case Registers.InterruptEnable:
48                 txInterruptEnabled = (value & 0x3) > 0;
49                 break;
50             case Registers.ClockControl:
51                 clockControlRegister = value & 0x1FFFE0;
52                 break;
53             default:
54                 this.LogUnhandledWrite(address, value);
55                 break;
56             }
57         }
58 
ReadDoubleWord(long offset)59         public uint ReadDoubleWord(long offset)
60         {
61             this.NoisyLog("Read {0}", (Registers)offset);
62             switch((Registers)offset)
63             {
64             case Registers.Status:
65                 uint res = 0x40;
66                 if(Count > 0)
67                 {
68                     res |= 0x80;
69                 }
70                 this.NoisyLog("Returned {0:X}.", res);
71                 return res;
72             case Registers.RxBufferData:
73             case Registers.RxBufferDataExtended:
74                 this.NoisyLog("Read data");
75                 byte character;
76                 if(!TryGetCharacter(out character))
77                 {
78                     this.NoisyLog("Failed");
79                     return 0;
80                 }
81                 this.NoisyLog("Succeeded");
82                 return character;
83             case Registers.TxBufferData:
84                 return 0;
85             case Registers.InterruptFlag:
86                 return 4;
87             default:
88                 this.LogUnhandledRead(offset);
89                 return 0x00;
90             }
91         }
92 
CharWritten()93         protected override void CharWritten()
94         {
95             this.NoisyLog("Char written. Count is {0}.", Count);
96             ReceiveIRQ.Set();
97         }
98 
QueueEmptied()99         protected override void QueueEmptied()
100         {
101             this.NoisyLog("Queue empty.");
102             ReceiveIRQ.Unset();
103         }
104 
105         [Flags]
106         private enum FrameFormat
107         {
108             StopBitsH = 1 << 13,
109             StopBitsL = 1 << 12,
110             ParityH   = 1 << 9,
111             ParityL   = 1 << 8
112         }
113 
114         private enum Registers
115         {
116             FrameFormat          = 0x004, // USARTn_FRAME
117             Command              = 0x00C, // USARTn_CMD
118             Status               = 0x010, // USARTn_STATUS
119             RxBufferDataExtended = 0x018, // USARTn_RXDATAX
120             RxBufferData         = 0x01C, // USARTn_RXDATA
121             TxBufferData         = 0x034, // USARTn_TXDATA
122             InterruptFlag        = 0x040, // USARTn_IF
123             InterruptEnable      = 0x04C, // USARTn_IEN
124             ClockControl         = 0x014  // USARTn_CLKDIV
125         }
126 
127         public override Parity ParityBit
128         {
129             get
130             {
131                 var parity = (frameFormatRegister & (uint)(FrameFormat.ParityH | FrameFormat.ParityL)) >> 8;
132                 switch (parity)
133                 {
134                 case 0:
135                     return Parity.None;
136                 case 2:
137                     return Parity.Even;
138                 case 3:
139                     return Parity.Odd;
140                 default:
141                     throw new ArgumentException("Wrong parity bits register value");
142                 }
143             }
144         }
145 
146         public override Bits StopBits
147         {
148             get
149             {
150                 var bits = (frameFormatRegister & (uint)(FrameFormat.StopBitsH | FrameFormat.StopBitsL)) >> 12;
151                 switch (bits)
152                 {
153                 case 0:
154                     return Bits.Half;
155                 case 1:
156                     return Bits.One;
157                 case 2:
158                     return Bits.OneAndAHalf;
159                 default:
160                     return Bits.Two;
161                 }
162             }
163         }
164 
165         public override uint BaudRate
166         {
167             get
168             {
169                 // divisor cannot be 0, so there is no need to check it
170                 return UARTClockFrequency / (2 * (1 + clockControlRegister/256));
171             }
172         }
173 
174         private const uint UARTClockFrequency = 0;
175     }
176 }
177 
178