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 8 using System; 9 using System.Collections.Generic; 10 using System.Collections.ObjectModel; 11 using Antmicro.Renode.Peripherals.Bus; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Core.Structure; 14 using Antmicro.Renode.Logging; 15 using Antmicro.Renode.Utilities; 16 using Antmicro.Renode.Core.Structure.Registers; 17 18 namespace Antmicro.Renode.Peripherals.UART 19 { 20 public class NEORV32_UART : UARTBase, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize 21 { NEORV32_UART(IMachine machine)22 public NEORV32_UART(IMachine machine) : base(machine) 23 { 24 var registersMap = new Dictionary<long, DoubleWordRegister>(); 25 26 registersMap.Add((long)Registers.Control, new DoubleWordRegister(this) 27 .WithTaggedFlag("CTRL_EN", 0) // UART enable 28 .WithTaggedFlag("CTRL_SIM_MODE", 1) // Enable simulation mode 29 .WithTaggedFlag(name: "HWFC_EN", 2) // Enable RTS/CTS hardware flow-control 30 .WithTag("CTRL_PRSC2:CTRL_PRSC0", 3, 3) // Baud rate clock prescaler select 31 .WithTag("CTRL_BAUD9:CTRL_BAUD0", 6, 10) // 12-bit Baud value configuration value 32 .WithFlag(16, 33 FieldMode.Read, 34 valueProviderCallback: _ => rxWasNotEmptyOnLastRead, 35 name: "CTRL_RX_NEMPTY") // RX FIFO not empty 36 .WithTaggedFlag("CTRL_RX_HALF", 17) // RX FIFO at least half-full 37 .WithTaggedFlag("CTRL_RX_FULL", 18) // RX FIFO full 38 .WithTaggedFlag("CTRL_TX_EMPTY", 19) // TX FIFO empty 39 .WithTaggedFlag("CTRL_TX_NHALF", 20) // TX FIFO not at least half-full 40 .WithTaggedFlag("CTRL_TX_FULL", 21) // TX FIFO full 41 .WithTaggedFlag("CTRL_IRQ_RX_NEMPTY", 22) // fire IRQ if RX FIFO not empty 42 .WithTaggedFlag("CTRL_IRQ_RX_HALF", 23) // fire IRQ if RX FIFO at least half-full 43 .WithTaggedFlag("CTRL_IRQ_RX_FULL", 24) // fire IRQ if RX FIFO full 44 .WithTaggedFlag("CTRL_IRQ_TX_EMPTY", 25) // fire IRQ if TX FIFO empty 45 .WithTaggedFlag("CTRL_IRQ_TX_NHALF", 26) // fire IRQ if TX not at least half full 46 .WithReservedBits(27, 1) // Reserved read as zero 47 .WithTaggedFlag("CTRL_RX_CLR", 28) // Clear RX FIFO, flag auto-clears 48 .WithTaggedFlag("CTRL_TX_CLR", 29) // Clear TX FIFO, flag auto-clears 49 .WithTaggedFlag("CTRL_RX_OVER", 30) // RX FIFO overflow; leared by disabling the module 50 .WithTaggedFlag("CTRL_TX_BUSY", 31) // TX busy or TX FIFO not empty 51 .WithWriteCallback((_, __) => UpdateInterrupts()) 52 ); 53 54 registersMap.Add((long)Registers.Data, new DoubleWordRegister(this) 55 .WithValueField(0, 8, name: "RTX_MSB:RTX_LSB", 56 valueProviderCallback: _ => HandleReceiveData(), 57 writeCallback: (_, v) => HandleTransmitData(v)) 58 .WithValueField(8, 4, FieldMode.Read, name: "RX_FIFO_SIZE_MSB:RX_FIFO_SIZE_LSB", valueProviderCallback: _ => (ulong)System.Math.Log(FifoSize, 2)) 59 .WithValueField(12, 4, FieldMode.Read, name: "TX_FIFO_SIZE_MSB:TX_FIFO_SIZE_LSB", valueProviderCallback: _ => (ulong)System.Math.Log(FifoSize, 2)) 60 // Flag below shouldn't be defined in Data register, but it is in Zephyr. 61 // There's PR fixing this: https://github.com/zephyrproject-rtos/zephyr/pull/72385 62 .WithFlag(16, 63 FieldMode.Read, 64 valueProviderCallback: _ => rxWasNotEmptyOnLastRead, 65 name: "NEORV32_UART_CTRL_RX_NEMPTY") 66 .WithReservedBits(17, 15) 67 ); 68 69 TxInterrupt = new GPIO(); 70 RxInterrupt = new GPIO(); 71 72 RegistersCollection = new DoubleWordRegisterCollection(this, registersMap); 73 } 74 HandleReceiveData()75 private uint HandleReceiveData() 76 { 77 rxWasNotEmptyOnLastRead = false; 78 79 if(receiveQueue.TryDequeue(out var result)) 80 { 81 rxWasNotEmptyOnLastRead = true; 82 UpdateInterrupts(); 83 } 84 return result; 85 } 86 HandleTransmitData(ulong value)87 private void HandleTransmitData(ulong value) 88 { 89 TransmitCharacter((byte)value); 90 91 UpdateInterrupts(); 92 } 93 UpdateInterrupts()94 private void UpdateInterrupts() 95 { 96 bool rxInterrupt = receiveQueue.Count > 0; 97 // In Renode, the TX happens instantly, 98 // and the TX interrupt fires if the TX FIFO is empty. 99 bool txInterrupt = true; 100 this.Log(LogLevel.Noisy, "Updating interrupts, tx: {0}, rx: {1}", txInterrupt, rxInterrupt); 101 102 TxInterrupt.Set(txInterrupt); 103 RxInterrupt.Set(rxInterrupt); 104 } 105 Reset()106 public override void Reset() 107 { 108 base.Reset(); 109 RegistersCollection.Reset(); 110 receiveQueue.Clear(); 111 TxInterrupt.Set(false); 112 RxInterrupt.Set(false); 113 } 114 ReadDoubleWord(long offset)115 public uint ReadDoubleWord(long offset) 116 { 117 return RegistersCollection.Read(offset); 118 } 119 WriteDoubleWord(long offset, uint val)120 public void WriteDoubleWord(long offset, uint val) 121 { 122 RegistersCollection.Write(offset, val); 123 } 124 WriteChar(byte value)125 public override void WriteChar(byte value) 126 { 127 if(!IsReceiveEnabled) 128 { 129 this.Log(LogLevel.Warning, "Char was received, but the receiver (or the whole USART) is not enabled. Ignoring."); 130 return; 131 } 132 receiveQueue.Enqueue(value); 133 UpdateInterrupts(); 134 } 135 136 public GPIO TxInterrupt { get; } 137 public GPIO RxInterrupt { get; } 138 139 public DoubleWordRegisterCollection RegistersCollection { get; } 140 141 public override Bits StopBits => Bits.One; 142 143 public override Parity ParityBit => Parity.None; 144 145 public override uint BaudRate => 115200; 146 147 public long Size => 0x8; 148 149 private bool rxWasNotEmptyOnLastRead; 150 151 private readonly Queue<byte> receiveQueue = new Queue<byte>(); 152 153 private const int FifoSize = 8; 154 CharWritten()155 protected override void CharWritten() 156 { 157 // intentionally left blank 158 } 159 QueueEmptied()160 protected override void QueueEmptied() 161 { 162 // intentionally left blank 163 } 164 165 private enum Registers 166 { 167 Control = 0x00, 168 Data = 0x04, 169 } 170 } 171 } 172