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