1 // 2 // Copyright (c) 2010-2023 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.Collections.Generic; 8 using Antmicro.Renode.Core; 9 using Antmicro.Renode.Core.Structure.Registers; 10 using Antmicro.Renode.Logging; 11 using Antmicro.Renode.Peripherals.Bus; 12 using Antmicro.Renode.Peripherals.Miscellaneous; 13 14 namespace Antmicro.Renode.Peripherals.UART 15 { 16 public class MAX32650_UART : UARTBase, IDoubleWordPeripheral, IKnownSize 17 { MAX32650_UART(IMachine machine, MAX32650_GCR gcr)18 public MAX32650_UART(IMachine machine, MAX32650_GCR gcr) : base(machine) 19 { 20 registers = new DoubleWordRegisterCollection(this, BuildRegisterMap()); 21 IRQ = new GPIO(); 22 GCR = gcr; 23 } 24 ReadDoubleWord(long offset)25 public uint ReadDoubleWord(long offset) 26 { 27 return registers.Read(offset); 28 } 29 WriteDoubleWord(long offset, uint value)30 public void WriteDoubleWord(long offset, uint value) 31 { 32 registers.Write(offset, value); 33 } 34 WriteChar(byte value)35 public override void WriteChar(byte value) 36 { 37 if(Count < FIFOBufferSize) 38 { 39 base.WriteChar(value); 40 } 41 else 42 { 43 interruptRxOverrunPending.Value |= true; 44 } 45 interruptRxFIFOLevelPending.Value |= Count >= (int)rxFIFOLevel.Value; 46 UpdateInterrupts(); 47 } 48 Reset()49 public override void Reset() 50 { 51 base.Reset(); 52 registers.Reset(); 53 IRQ.Unset(); 54 } 55 56 public override Bits StopBits => stopBits; 57 58 public override Parity ParityBit => parityEnabled.Value ? parityBit : Parity.None; 59 60 public override uint BaudRate 61 { 62 get 63 { 64 var clockFreq = clockSelect.Value ? InternalBitRateClockFrequency : GCR.SysClk / 2; 65 var divider = (float)baudDividerInteger.Value + ((float)baudDividerDecimal.Value / 128.0); 66 return (uint)((float)clockFreq / (divider * (1 << (int)baudClockDivider.Value))); 67 } 68 } 69 70 public GPIO IRQ { get; } 71 public long Size => 0x1000; 72 CharWritten()73 protected override void CharWritten() 74 { 75 // intentionally left empty 76 } 77 QueueEmptied()78 protected override void QueueEmptied() 79 { 80 // intentionally left empty 81 } 82 BuildRegisterMap()83 private Dictionary<long, DoubleWordRegister> BuildRegisterMap() 84 { 85 var registersMap = new Dictionary<long, DoubleWordRegister> 86 { 87 {(long)Registers.Control0, new DoubleWordRegister(this, 0x00) 88 .WithFlag(0, out isEnabled, name: "CTRL1.ENABLE") 89 .WithFlag(1, out parityEnabled, name: "CTRL1.PARITY_EN") 90 .WithEnumField<DoubleWordRegister, ParityMode>(2, 2, name: "CTRL1.PARITY_MODE", 91 writeCallback: (_, value) => 92 { 93 switch(value) 94 { 95 case ParityMode.Even: 96 parityBit = Parity.Even; 97 break; 98 case ParityMode.Odd: 99 parityBit = Parity.Odd; 100 break; 101 default: 102 this.Log(LogLevel.Warning, "Unsupported parity has been set"); 103 break; 104 } 105 }) 106 .WithTaggedFlag("CTRL1.PARITY_LVL", 4) 107 .WithFlag(5, name: "CTRL1.TX_FLUSH", valueProviderCallback: _ => false) 108 .WithFlag(6, name: "CTRL1.RX_FLUSH", 109 valueProviderCallback: _ => false, 110 writeCallback: (_, value) => 111 { 112 if(value) 113 { 114 ClearBuffer(); 115 } 116 }) 117 .WithTaggedFlag("CTRL1.BITACC", 7) 118 .WithEnumField<DoubleWordRegister, CharacterSize>(8, 2, out characterSize, name: "CTRL1.SIZE", 119 writeCallback: (_, value) => 120 { 121 if(value != CharacterSize.EightBits) 122 { 123 this.Log(LogLevel.Warning, "Character size set to {0}, but only {1} characters are supported", value, CharacterSize.EightBits); 124 } 125 }) 126 .WithFlag(10, name: "CTRL1.STOP", 127 writeCallback: (_, value) => 128 { 129 if(!value) 130 { 131 stopBits = Bits.One; 132 } 133 else if(value && characterSize.Value == CharacterSize.FiveBits) 134 { 135 stopBits = Bits.OneAndAHalf; 136 } 137 else 138 { 139 stopBits = Bits.Two; 140 } 141 }) 142 .WithTaggedFlag("CTRL1.FLOW", 11) 143 .WithTaggedFlag("CTRL1.FLOWPOL", 12) 144 .WithTaggedFlag("CTRL1.NULLMOD", 13) 145 .WithTaggedFlag("CTRL1.BREAK", 14) 146 .WithFlag(15, out clockSelect, name: "CTRL1.CLK_SEL") 147 .WithTag("CTRL1.TIMEOUT_CNT", 16, 8) 148 .WithReservedBits(25, 7) 149 }, 150 {(long)Registers.Control1, new DoubleWordRegister(this, 0x00) 151 .WithValueField(0, 6, out rxFIFOLevel, name: "CTRL2.RX_FIFO_LVL") 152 .WithReservedBits(6, 2) 153 .WithTag("CTRL2.TX_FIFO_LVL", 8, 6) 154 .WithReservedBits(14, 2) 155 .WithTag("CTRL2.RTS_FIFO_LVL", 16, 6) 156 .WithReservedBits(22, 10) 157 }, 158 {(long)Registers.Status, new DoubleWordRegister(this, 0x50) 159 .WithFlag(0, FieldMode.Read, name: "STAT.TX_BUSY", valueProviderCallback: _ => false) 160 .WithFlag(1, FieldMode.Read, name: "STAT.RX_BUSY", valueProviderCallback: _ => false) 161 .WithTaggedFlag("STAT.PARITY", 2) 162 .WithTaggedFlag("STAT.BREAK", 3) 163 .WithFlag(4, FieldMode.Read, name: "STAT.RX_EMPTY", valueProviderCallback: _ => Count == 0) 164 .WithFlag(5, FieldMode.Read, name: "STAT.RX_FULL", valueProviderCallback: _ => Count > FIFOBufferSize) 165 .WithFlag(6, FieldMode.Read, name: "STAT.TX_EMPTY", valueProviderCallback: _ => true) 166 .WithFlag(7, FieldMode.Read, name: "STAT.TX_FULL", valueProviderCallback: _ => false) 167 .WithValueField(8, 6, FieldMode.Read, name: "STAT.RX_NUM", valueProviderCallback: _ => (uint)Count) 168 .WithReservedBits(14, 2) 169 .WithValueField(16, 6, FieldMode.Read, name: "STAT.TX_NUM", valueProviderCallback: _ => 0) 170 .WithReservedBits(22, 2) 171 .WithTaggedFlag("STAT.RX_TIMEOUT", 24) 172 .WithReservedBits(25, 7) 173 }, 174 {(long)Registers.InterruptEnable, new DoubleWordRegister(this, 0x00) 175 // marked as a flag to limit the amount of log messages 176 .WithFlag(0, name: "INT_EN.RX_FRAME_ERROR") 177 // marked as a flag to limit the amount of log messages 178 .WithFlag(1, name: "INT_EN.RX_PARITY_ERROR") 179 .WithTaggedFlag("INT_EN.CTS", 2) 180 .WithFlag(3, out interruptRxOverrunEnabled, name: "INT_EN.RX_OVERRUN") 181 .WithFlag(4, out interruptRxFIFOLevelEnabled, name: "INT_EN.RX_FIFO_LVL") 182 // marked as a flag to limit the amount of log messages 183 .WithFlag(5, name: "INT_EN.TX_FIFO_AE") 184 // marked as a flag to limit the amount of log messages 185 .WithFlag(6, name: "INT_EN.TX_FIFO_LVL") 186 .WithTaggedFlag("INT_EN.BREAK", 7) 187 .WithTaggedFlag("INT_EN.RX_TIMEOUT", 8) 188 .WithTaggedFlag("INT_EN.LASTBREAK", 9) 189 .WithReservedBits(10, 22) 190 .WithWriteCallback((_, __) => UpdateInterrupts()) 191 }, 192 {(long)Registers.InterruptFlags, new DoubleWordRegister(this, 0x00) 193 .WithTaggedFlag("INT_FL.FRAME", 0) 194 .WithTaggedFlag("INT_FL.PARITY", 1) 195 .WithTaggedFlag("INT_FL.CTS", 2) 196 .WithFlag(3, out interruptRxOverrunPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL.RX_OVERRUN") 197 .WithFlag(4, out interruptRxFIFOLevelPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL.RX_FIFO_LVL") 198 .WithTaggedFlag("INT_FL.TX_FIFO_AE", 5) 199 .WithTaggedFlag("INT_FL.TX_FIFO_LVL", 6) 200 .WithTaggedFlag("INT_FL.BREAK", 7) 201 .WithTaggedFlag("INT_FL.RX_TIMEOUT", 8) 202 .WithTaggedFlag("INT_FL.LASTBREAK", 9) 203 .WithReservedBits(10, 22) 204 .WithWriteCallback((_, __) => UpdateInterrupts()) 205 }, 206 {(long)Registers.BaudInteger, new DoubleWordRegister(this, 0x00) 207 .WithValueField(0, 12, out baudDividerInteger, name: "BAUD0.IBAUD") 208 .WithReservedBits(12, 4) 209 .WithValueField(16, 3, out baudClockDivider, name: "BAUD0.CLKDIV") 210 .WithReservedBits(19, 13) 211 }, 212 {(long)Registers.BaudDecimal, new DoubleWordRegister(this, 0x00) 213 .WithValueField(0, 7, out baudDividerDecimal, name: "BAUD1.DBAUD") 214 .WithReservedBits(7, 25) 215 }, 216 {(long)Registers.FIFO, new DoubleWordRegister(this, 0x00) 217 .WithValueField(0, 8, name: "FIFO.FIFO", 218 valueProviderCallback: _ => 219 { 220 if(!TryGetCharacter(out var character)) 221 { 222 this.Log(LogLevel.Warning, "Trying to read from empty buffer"); 223 } 224 return character; 225 }, 226 writeCallback: (_, value) => 227 { 228 if(isEnabled.Value) 229 { 230 TransmitCharacter((byte)value); 231 } 232 }) 233 .WithReservedBits(8, 24) 234 }, 235 {(long)Registers.DMA, new DoubleWordRegister(this, 0x00) 236 .WithTaggedFlag("DMA.TXDMA_EN", 0) 237 .WithTaggedFlag("DMA.RXDMA_EN", 1) 238 .WithReservedBits(2, 6) 239 .WithTag("DMA.TXDMA_LVL", 8, 6) 240 .WithReservedBits(14, 2) 241 .WithTag("DMA.RXDMA_LVL", 16, 6) 242 .WithReservedBits(22, 10) 243 }, 244 {(long)Registers.TxFIFO, new DoubleWordRegister(this, 0x00) 245 .WithValueField(0, 8, FieldMode.Read, name: "TXFIFO.DATA", 246 valueProviderCallback: _ => (byte)0x00) 247 .WithReservedBits(8, 24) 248 }, 249 }; 250 251 return registersMap; 252 } 253 UpdateInterrupts()254 private void UpdateInterrupts() 255 { 256 var interruptPending = false; 257 258 interruptPending |= interruptRxOverrunEnabled.Value && interruptRxOverrunPending.Value; 259 interruptPending |= interruptRxFIFOLevelEnabled.Value && interruptRxFIFOLevelPending.Value; 260 261 IRQ.Set(interruptPending); 262 } 263 264 private Bits stopBits; 265 private Parity parityBit; 266 267 private IFlagRegisterField isEnabled; 268 private IFlagRegisterField parityEnabled; 269 private IFlagRegisterField clockSelect; 270 private IEnumRegisterField<CharacterSize> characterSize; 271 272 private IValueRegisterField rxFIFOLevel; 273 274 private IValueRegisterField baudClockDivider; 275 private IValueRegisterField baudDividerInteger; 276 private IValueRegisterField baudDividerDecimal; 277 278 private IFlagRegisterField interruptRxOverrunEnabled; 279 private IFlagRegisterField interruptRxFIFOLevelEnabled; 280 281 private IFlagRegisterField interruptRxOverrunPending; 282 private IFlagRegisterField interruptRxFIFOLevelPending; 283 284 private const long FIFOBufferSize = 32; 285 private const long InternalBitRateClockFrequency = 7372800; // Only used to calculate baudrate 286 287 private readonly DoubleWordRegisterCollection registers; 288 private readonly MAX32650_GCR GCR; 289 290 private enum ParityMode : byte 291 { 292 Even, 293 Odd, 294 Mark, 295 Space 296 } 297 298 private enum CharacterSize : byte 299 { 300 FiveBits, 301 SixBits, 302 SevenBits, 303 EightBits 304 } 305 306 private enum Registers : long 307 { 308 Control0 = 0x00, 309 Control1 = 0x04, 310 Status = 0x08, 311 InterruptEnable = 0x0C, 312 InterruptFlags = 0x10, 313 BaudInteger = 0x14, 314 BaudDecimal = 0x18, 315 FIFO = 0x1C, 316 DMA = 0x20, 317 TxFIFO = 0x24 318 } 319 } 320 } 321