1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Threading;
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 using Antmicro.Migrant.Hooks;
15 using Antmicro.Renode.Core.Structure.Registers;
16 using Antmicro.Renode.Time;
17 
18 namespace Antmicro.Renode.Peripherals.UART
19 {
20     [AllowedTranslations(AllowedTranslation.WordToDoubleWord | AllowedTranslation.ByteToDoubleWord)]
21     public class STM32_UART : BasicDoubleWordPeripheral, IUART
22     {
STM32_UART(IMachine machine, uint frequency = 8000000)23         public STM32_UART(IMachine machine, uint frequency = 8000000) : base(machine)
24         {
25             this.frequency = frequency;
26             DefineRegisters();
27         }
28 
WriteChar(byte value)29         public void WriteChar(byte value)
30         {
31             if(!usartEnabled.Value && !receiverEnabled.Value)
32             {
33                 this.Log(LogLevel.Warning, "Received a character, but the receiver is not enabled, dropping.");
34                 return;
35             }
36             receiveFifo.Enqueue(value);
37             readFifoNotEmpty.Value = true;
38 
39             if(BaudRate == 0)
40             {
41                 this.Log(LogLevel.Warning, "Unknown baud rate, couldn't trigger the idle line interrupt");
42             }
43             else
44             {
45                 // Setup a timeout of 1 UART frame (8 bits) for Idle line detection
46                 idleLineDetectedCancellationTokenSrc?.Cancel();
47 
48                 var idleLineIn = (8 * 1000000) / BaudRate;
49                 idleLineDetectedCancellationTokenSrc = new CancellationTokenSource();
50                 machine.ScheduleAction(TimeInterval.FromMicroseconds(idleLineIn), _ => ReportIdleLineDetected(idleLineDetectedCancellationTokenSrc.Token), name: $"{nameof(STM32_UART)} Idle line detected");
51             }
52 
53             Update();
54         }
55 
Reset()56         public override void Reset()
57         {
58             base.Reset();
59             idleLineDetectedCancellationTokenSrc?.Cancel();
60             receiveFifo.Clear();
61             IRQ.Set(false);
62         }
63 
64         public uint BaudRate
65         {
66             get
67             {
68                 //OversamplingMode.By8 means we ignore the oldest bit of dividerFraction.Value
69                 var fraction = oversamplingMode.Value == OversamplingMode.By16 ? dividerFraction.Value : dividerFraction.Value & 0b111;
70 
71                 var divisor = 8 * (2 - (int)oversamplingMode.Value) * (dividerMantissa.Value + fraction / 16.0);
72                 return divisor == 0 ? 0 : (uint)(frequency / divisor);
73             }
74         }
75 
76         public Bits StopBits
77         {
78             get
79             {
80                 switch(stopBits.Value)
81                 {
82                 case StopBitsValues.Half:
83                     return Bits.Half;
84                 case StopBitsValues.One:
85                     return Bits.One;
86                 case StopBitsValues.OneAndAHalf:
87                     return Bits.OneAndAHalf;
88                 case StopBitsValues.Two:
89                     return Bits.Two;
90                 default:
91                     throw new ArgumentException("Invalid stop bits value");
92                 }
93             }
94         }
95 
96         public Parity ParityBit => parityControlEnabled.Value ?
97                                     (paritySelection.Value == ParitySelection.Even ?
98                                         Parity.Even :
99                                         Parity.Odd) :
100                                     Parity.None;
101 
102         public GPIO IRQ { get; } = new GPIO();
103 
104         [field: Transient]
105         public event Action<byte> CharReceived;
106 
DefineRegisters()107         private void DefineRegisters()
108         {
109             Register.Status.Define(this, 0xC0, name: "USART_SR")
110                 .WithTaggedFlag("PE", 0)
111                 .WithTaggedFlag("FE", 1)
112                 .WithTaggedFlag("NF", 2)
113                 .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => false, name: "ORE") // we assume no receive overruns
114                 .WithFlag(4, out idleLineDetected, FieldMode.Read, name: "IDLE")
115                 .WithFlag(5, out readFifoNotEmpty, FieldMode.Read | FieldMode.WriteZeroToClear, name: "RXNE") // as these two flags are WZTC, we cannot just calculate their results
116                 .WithFlag(6, out transmissionComplete, FieldMode.Read | FieldMode.WriteZeroToClear, name: "TC")
117                 .WithFlag(7, FieldMode.Read, valueProviderCallback: _ => true, name: "TXE") // we always assume "transmit data register empty"
118                 .WithTaggedFlag("LBD", 8)
119                 .WithTaggedFlag("CTS", 9)
120                 .WithReservedBits(10, 22)
121                 .WithWriteCallback((_, __) => Update())
122             ;
123             Register.Data.Define(this, name: "USART_DR")
124                 .WithValueField(0, 9, valueProviderCallback: _ =>
125                     {
126                         uint value = 0;
127 
128                         // "Cleared by a USART_SR register followed by a read to the USART_DR register."
129                         // We can assume that USART_SR has already been read on the ISR.
130                         idleLineDetected.Value = false;
131 
132                         if(receiveFifo.Count > 0)
133                         {
134                             value = receiveFifo.Dequeue();
135                         }
136                         readFifoNotEmpty.Value = receiveFifo.Count > 0;
137                         Update();
138                         return value;
139                     }, writeCallback: (_, value) =>
140                     {
141                         if(!usartEnabled.Value && !transmitterEnabled.Value)
142                         {
143                             this.Log(LogLevel.Warning, "Trying to transmit a character, but the transmitter is not enabled. dropping.");
144                             return;
145                         }
146                         CharReceived?.Invoke((byte)value);
147                         transmissionComplete.Value = true;
148                         Update();
149                     }, name: "DR"
150                 )
151             ;
152             Register.BaudRate.Define(this, name: "USART_BRR")
153                 .WithValueField(0, 4, out dividerFraction, name: "DIV_Fraction")
154                 .WithValueField(4, 12, out dividerMantissa, name: "DIV_Mantissa")
155             ;
156             Register.Control1.Define(this, name: "USART_CR1")
157                 .WithTaggedFlag("SBK", 0)
158                 .WithTaggedFlag("RWU", 1)
159                 .WithFlag(2, out receiverEnabled, name: "RE")
160                 .WithFlag(3, out transmitterEnabled, name: "TE")
161                 .WithFlag(4, out idleLineDetectedInterruptEnabled, name: "IDLEIE")
162                 .WithFlag(5, out receiverNotEmptyInterruptEnabled, name: "RXNEIE")
163                 .WithFlag(6, out transmissionCompleteInterruptEnabled, name: "TCIE")
164                 .WithFlag(7, out transmitDataRegisterEmptyInterruptEnabled, name: "TXEIE")
165                 .WithTaggedFlag("PEIE", 8)
166                 .WithEnumField(9, 1, out paritySelection, name: "PS")
167                 .WithFlag(10, out parityControlEnabled, name: "PCE")
168                 .WithTaggedFlag("WAKE", 11)
169                 .WithTaggedFlag("M", 12)
170                 .WithFlag(13, out usartEnabled, name: "UE")
171                 .WithReservedBits(14, 1)
172                 .WithEnumField(15, 1, out oversamplingMode, name: "OVER8")
173                 .WithReservedBits(16, 16)
174                 .WithWriteCallback((_, __) =>
175                 {
176                     if(!receiverEnabled.Value || !usartEnabled.Value)
177                     {
178                         idleLineDetectedCancellationTokenSrc?.Cancel();
179                     }
180                     Update();
181                 })
182             ;
183             Register.Control2.Define(this, name: "USART_CR2")
184                 .WithTag("ADD", 0, 4)
185                 .WithReservedBits(5, 1)
186                 .WithTaggedFlag("LBDIE", 6)
187                 .WithReservedBits(7, 1)
188                 .WithTaggedFlag("LBCL", 8)
189                 .WithTaggedFlag("CPHA", 9)
190                 .WithTaggedFlag("CPOL", 10)
191                 .WithTaggedFlag("CLKEN", 11)
192                 .WithEnumField(12, 2, out stopBits, name: "STOP")
193                 .WithTaggedFlag("LINEN", 14)
194                 .WithReservedBits(15, 17)
195             ;
196         }
197 
ReportIdleLineDetected(CancellationToken ct)198         private void ReportIdleLineDetected(CancellationToken ct)
199         {
200             if(!ct.IsCancellationRequested)
201             {
202                 idleLineDetected.Value = true;
203                 Update();
204             }
205         }
206 
Update()207         private void Update()
208         {
209             IRQ.Set(
210                 (idleLineDetectedInterruptEnabled.Value && idleLineDetected.Value) ||
211                 (receiverNotEmptyInterruptEnabled.Value && readFifoNotEmpty.Value) ||
212                 (transmitDataRegisterEmptyInterruptEnabled.Value) || // TXE is assumed to be true
213                 (transmissionCompleteInterruptEnabled.Value && transmissionComplete.Value)
214             );
215         }
216 
217         private readonly uint frequency;
218 
219         private CancellationTokenSource idleLineDetectedCancellationTokenSrc;
220 
221         private IEnumRegisterField<OversamplingMode> oversamplingMode;
222         private IEnumRegisterField<StopBitsValues> stopBits;
223         private IFlagRegisterField usartEnabled;
224         private IFlagRegisterField parityControlEnabled;
225         private IEnumRegisterField<ParitySelection> paritySelection;
226         private IFlagRegisterField transmissionCompleteInterruptEnabled;
227         private IFlagRegisterField transmitDataRegisterEmptyInterruptEnabled;
228         private IFlagRegisterField idleLineDetectedInterruptEnabled;
229         private IFlagRegisterField receiverNotEmptyInterruptEnabled;
230         private IFlagRegisterField receiverEnabled;
231         private IFlagRegisterField transmitterEnabled;
232         private IFlagRegisterField idleLineDetected;
233         private IFlagRegisterField readFifoNotEmpty;
234         private IFlagRegisterField transmissionComplete;
235         private IValueRegisterField dividerMantissa;
236         private IValueRegisterField dividerFraction;
237 
238         private readonly Queue<byte> receiveFifo = new Queue<byte>();
239 
240         private enum OversamplingMode
241         {
242             By16 = 0,
243             By8 = 1
244         }
245 
246         private enum StopBitsValues
247         {
248             One = 0,
249             Half = 1,
250             Two = 2,
251             OneAndAHalf = 3
252         }
253 
254         private enum ParitySelection
255         {
256             Even = 0,
257             Odd = 1
258         }
259 
260         private enum Register : long
261         {
262             Status = 0x00,
263             Data = 0x04,
264             BaudRate = 0x08,
265             Control1 = 0x0C,
266             Control2 = 0x10,
267             Control3 = 0x14,
268             GuardTimeAndPrescaler = 0x18
269         }
270     }
271 }
272