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