1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // Copyright (c) 2024 Sean "xobs" Cross 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System.Collections.Generic; 9 using Antmicro.Renode.Peripherals.Bus; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Logging; 13 14 namespace Antmicro.Renode.Peripherals.UART 15 { 16 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 17 public class ESP32_UART : UARTBase, IDoubleWordPeripheral, IKnownSize 18 { ESP32_UART(IMachine machine)19 public ESP32_UART(IMachine machine) : base(machine) 20 { 21 IRQ = new GPIO(); 22 interruptRawStatuses = new bool[InterruptsCount]; 23 interruptMasks = new bool[InterruptsCount]; 24 25 var registersMap = new Dictionary<long, DoubleWordRegister> 26 { 27 {(long)Registers.Fifo, new DoubleWordRegister(this) 28 .WithValueField(0, 8, writeCallback: (_, value) => this.TransmitCharacter((byte)value), 29 valueProviderCallback: _ => { 30 if(!TryGetCharacter(out var character)) 31 { 32 this.Log(LogLevel.Warning, "Trying to read from an empty FIFO."); 33 } 34 return character; 35 }) 36 .WithReservedBits(8, 24) 37 }, 38 {(long)Registers.RxFilter, new DoubleWordRegister(this) 39 .WithTag("GLITCH_FILT", 0, 8) // when input pulse width is lower than this value, the pulse is ignored. 40 .WithTaggedFlag("GLITCH_FILT_EN", 8) // Set this bit to enable Rx signal filter. 41 .WithReservedBits(9, 23) 42 }, 43 {(long)Registers.RawInterruptStatus, new DoubleWordRegister(this) 44 .WithFlags(0, 20, FieldMode.Read, valueProviderCallback: (interrupt, _) => interruptRawStatuses[interrupt]) 45 .WithReservedBits(20, 12) 46 }, 47 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 48 .WithFlags(0, 20, changeCallback: (interrupt, _, newValue) => { interruptMasks[interrupt] = newValue; UpdateInterrupts(); }) 49 .WithReservedBits(20, 12) 50 }, 51 {(long)Registers.InterruptClear, new DoubleWordRegister(this) 52 .WithFlags(0, 20, FieldMode.Write, writeCallback: (interrupt, _, newValue) => { if(newValue) ClearInterrupt(interrupt); }) 53 .WithReservedBits(20, 12) 54 }, 55 {(long)Registers.Status, new DoubleWordRegister(this) 56 .WithTag("RXFIFO_CNT", 0, 10) 57 .WithTaggedFlag("UART_DSRN", 13) 58 .WithFlag(14, FieldMode.Read, valueProviderCallback: (_) => true, name: "UART_CTSN") 59 .WithFlag(15, FieldMode.Read, valueProviderCallback: (_) => true, name: "UART_RXD") 60 .WithTag("TXFIFO_CNT", 16, 10) 61 .WithFlag(29, FieldMode.Read, valueProviderCallback: (_) => true, name: "UART_DTRN") 62 .WithFlag(30, FieldMode.Read, valueProviderCallback: (_) => true, name: "UART_RTSN") 63 .WithFlag(31, FieldMode.Read, valueProviderCallback: (_) => true, name: "UART_TXD") 64 }, 65 {(long)Registers.Config0, new DoubleWordRegister(this) 66 .WithTaggedFlag("UART_PARITY", 0) 67 .WithTaggedFlag("UART_PARITY_EN", 1) 68 .WithTag("UART_BIT_NUM", 2, 2) 69 .WithTag("UART_STOPBIT_NUM", 4, 2) 70 .WithTaggedFlag("UART_RXFIFO_RST", 17) 71 .WithTaggedFlag("UART_TXFIFO_RST", 18) 72 .WithTaggedFlag("UART_ERR_WR_MASK", 26) 73 .WithTaggedFlag("UART_AUTOBAUD_EN", 27) 74 }, 75 {(long)Registers.Config1, new DoubleWordRegister(this) 76 .WithTag("RXFIFO_FULL_THRHD", 0, 10) // It will produce rxfifo_full_int interrupt when receiver receives more data than this register value. 77 .WithTag("TXFIFO_EMPTY_THRHD", 10, 10) // It will produce txfifo_empty_int interrupt when the data amount in Tx-FIFO is less than this register value. 78 .WithTaggedFlag("DIS_RX_DAT_OVF", 20) // Disable UART Rx data overflow detect. 79 .WithTaggedFlag("RX_TOUT_FLOW_DIS", 21) // Set this bit to stop accumulating idle_cnt when hardware flow control works. 80 .WithTaggedFlag("RX_FLOW_EN", 22) // This is the flow enable bit for UART receiver. 81 .WithTaggedFlag("RX_TOUT_EN", 23) // This is the enble bit for uart receiver's timeout function. 82 }, 83 {(long)Registers.EdgeChangeCount, new DoubleWordRegister(this) 84 .WithTag("RXD_EDGE_CNT", 0, 10) // This register stores the count of rxd edge change. It is used in baud rate-detect process. 85 .WithReservedBits(10, 22) 86 }, 87 {(long)Registers.ClockDivider, new DoubleWordRegister(this) 88 .WithTag("CLKDIV", 0, 12) // Clock divider configuration 89 .WithReservedBits(12, 8) 90 .WithTag("FRAG", 20, 4) // The decimal part of the frequency divider factor. 91 .WithReservedBits(24, 8) 92 }, 93 {(long)Registers.CoreClock, new DoubleWordRegister(this) 94 .WithTag("SCLK_DIV_B", 0, 6) // The denominator of the frequency divider factor. 95 .WithTag("SCLK_DIV_A", 6, 6) // The numerator of the frequency divider factor. 96 .WithTag("SCLK_DIV_NUM", 12, 8) // The integral part of the frequency divider factor. 97 .WithTag("SCLK_SEL", 20, 2) // UART clock source select. 1: 80Mhz, 2: 8Mhz, 3: XTAL. 98 .WithTaggedFlag("SCLK_EN", 22) // Set this bit to enable UART Tx/Rx clock. 99 .WithTaggedFlag("RST_CORE", 23) // Write 1 then write 0 to this bit, reset UART Tx/Rx. 100 .WithTaggedFlag("TX_SCLK_EN", 24) // Set this bit to enable UART Tx clock. 101 .WithTaggedFlag("RX_SCLK_EN", 25) // Set this bit to enable UART Rx clock. 102 .WithReservedBits(26, 6) 103 }, 104 }; 105 106 registers = new DoubleWordRegisterCollection(this, registersMap); 107 } 108 ReadDoubleWord(long offset)109 public uint ReadDoubleWord(long offset) 110 { 111 return registers.Read(offset); 112 } 113 Reset()114 public override void Reset() 115 { 116 base.Reset(); 117 registers.Reset(); 118 UpdateInterrupts(); 119 } 120 WriteDoubleWord(long offset, uint value)121 public void WriteDoubleWord(long offset, uint value) 122 { 123 registers.Write(offset, value); 124 } 125 126 public long Size => 0x100; 127 128 public GPIO IRQ { get; } 129 130 public override Bits StopBits => Bits.One; 131 132 public override Parity ParityBit => Parity.None; 133 134 public override uint BaudRate => 115200; 135 CharWritten()136 protected override void CharWritten() 137 { 138 UpdateInterrupts(); 139 } 140 QueueEmptied()141 protected override void QueueEmptied() 142 { 143 UpdateInterrupts(); 144 } 145 ClearInterrupt(int interrupt)146 private void ClearInterrupt(int interrupt) 147 { 148 this.Log(LogLevel.Noisy, "Clearing {0} interrupt.", (Interrupts)interrupt); 149 interruptRawStatuses[interrupt] = false; 150 UpdateInterrupts(); 151 } 152 UpdateInterrupts()153 private void UpdateInterrupts() 154 { 155 bool flag = MaskedInterruptStatus != 0; 156 this.Log(LogLevel.Debug, "Setting IRQ to {0}", flag); 157 IRQ.Set(flag); 158 } 159 160 private uint InterruptMask => Renode.Utilities.BitHelper.GetValueFromBitsArray(interruptMasks); 161 162 private uint RawInterruptStatus => Renode.Utilities.BitHelper.GetValueFromBitsArray(interruptRawStatuses); 163 164 private uint MaskedInterruptStatus => RawInterruptStatus & InterruptMask; 165 166 private readonly bool[] interruptMasks; 167 private readonly bool[] interruptRawStatuses; 168 private readonly DoubleWordRegisterCollection registers; 169 170 private const uint InterruptsCount = 20; 171 172 private enum Interrupts 173 { 174 RxFifoFull, 175 TxFifoEmpty, 176 ParityError, 177 DataFrameError, 178 RxFifoOverflow, 179 DSREdgeChange, 180 CTSEdgeChange, 181 BreakDetected, 182 ReceiverTimeout, 183 SWFlowXON, 184 SWFlowXOFF, 185 GlitchDetected, 186 TxDone, 187 TxIdleDone, 188 TxSentAllData, 189 RS485ParityError, 190 RS485DataFrameError, 191 RS485Clash, 192 CMDCharDetected, 193 Wakeup, 194 } 195 196 private enum Registers : long 197 { 198 // FIFO Configuration 199 Fifo = 0x00, 200 ThresholdAndAllocation = 0x60, 201 202 // UART interrupt register 203 RawInterruptStatus = 0x04, 204 MaskedInterruptStatus = 0x08, 205 InterruptEnable = 0x0c, 206 InterruptClear = 0x10, 207 208 // Configuration register 209 ClockDivider = 0x14, 210 RxFilter = 0x18, 211 Config0 = 0x20, 212 Config1 = 0x24, 213 SoftwareFlowControl = 0x34, 214 SleepMode = 0x38, 215 SoftwareFlowControlChar0 = 0x3c, 216 SoftwareFlowControlChar1 = 0x40, 217 TxBreakCharacter = 0x44, 218 IdleTime = 0x48, 219 RS485Mode = 0x4c, 220 CoreClock = 0x78, 221 222 // Status register 223 Status = 0x1c, 224 TxFifoOffsetAddress = 0x64, 225 RxFifoOffsetAddress = 0x68, 226 TxRxStatus = 0x6c, 227 228 // Autobaud register 229 MinLowPulseDuration = 0x28, 230 MinHighPulseDuration = 0x2c, 231 EdgeChangeCount = 0x30, 232 HighPulse = 0x70, 233 LowPulse = 0x74, 234 235 // Escape sequence selection configuration 236 PreSequenceTiming = 0x50, 237 PostSequenceTiming = 0x54, 238 Timeout = 0x58, 239 ATEscapeSequenceDetect = 0x5c, 240 241 // Version 242 Version = 0x7c, 243 ID = 0x80, 244 } 245 } 246 } 247