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;
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     public class PULP_uDMA_UART : UARTBase, IDoubleWordPeripheral, IKnownSize
17     {
PULP_uDMA_UART(IMachine machine)18         public PULP_uDMA_UART(IMachine machine) : base(machine)
19         {
20             sysbus = machine.GetSystemBus(this);
21             TxIRQ = new GPIO();
22             RxIRQ = new GPIO();
23 
24             var registersMap = new Dictionary<long, DoubleWordRegister>
25             {
26                 {(long)Registers.RxBaseAddress, new DoubleWordRegister(this)
27                     .WithValueField(0, 32, out rxBufferAddress, name: "RX_SADDR / Rx buffer base address")
28                 },
29                 {(long)Registers.RxSize, new DoubleWordRegister(this)
30                     .WithValueField(0, 17, out rxBufferSize, name: "RX_SIZE / Rx buffer size")
31                 },
32                 {(long)Registers.RxConfig, new DoubleWordRegister(this)
33                     .WithFlag(4, name: "EN / RX channel enable and start",
34                               // Continuous mode is currently not supported
35                               valueProviderCallback: _ => rxStarted,
36                               writeCallback: (_, value) =>
37                               {
38                                   rxStarted = value;
39                                   if(value)
40                                   {
41                                       rxIdx = 0;
42                                       if(Count > 0)
43                                       {
44                                           // With the new round of reception we might still have some characters in the
45                                           // buffer.
46                                           CharWritten();
47                                       }
48                                   }
49                               })
50                 },
51                 {(long)Registers.TxBaseAddress, new DoubleWordRegister(this)
52                     .WithValueField(0, 32, out txBufferAddress, name: "TX_SADDR / Tx buffer base address")
53                 },
54                 {(long)Registers.TxSize, new DoubleWordRegister(this)
55                     .WithValueField(0, 17, out txBufferSize, name: "TX_SIZE / Tx buffer size")
56                 },
57                 {(long)Registers.TxConfig, new DoubleWordRegister(this)
58                     .WithFlag(4, name: "EN / TX channel enable and start",
59                               // Continuous mode is currently not supported
60                               valueProviderCallback: _ => false,
61                               writeCallback: (_, value) =>
62                               {
63                                   if(!value)
64                                   {
65                                       return;
66                                   }
67 
68                                   if(txBufferSize.Value == 0)
69                                   {
70                                       this.Log(LogLevel.Warning, "TX is being enabled, but the buffer size is not configured. Ignoring the operation");
71                                       return;
72                                   }
73 
74                                   var data = sysbus.ReadBytes(txBufferAddress.Value, (int)txBufferSize.Value);
75                                   foreach(var c in data)
76                                   {
77                                       TransmitCharacter(c);
78                                   }
79                                   TxIRQ.Blink();
80                               })
81                 },
82                 {(long)Registers.Status, new DoubleWordRegister(this)
83                     .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => false, name: "TX busy") // tx is never busy
84                     .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => false, name: "RX busy") // rx is never busy
85                 },
86                 {(long)Registers.Setup, new DoubleWordRegister(this)
87                     .WithFlag(0, out parityEnable, name: "PARITY_ENA / Parity Enable")
88                     .WithTag("BIT_LENGTH / Character length", 1, 2)
89                     .WithFlag(3, out stopBits, name: "STOP_BITS / Stop bits length")
90                     .WithTag("POLLING_EN / Polling Enabled", 4, 1)
91                     .WithTag("CLEAN_FIFO / Clean RX FIFO", 5, 1)
92                     .WithTag("TX_ENA / TX enabled", 8, 1)
93                     .WithTag("RX_ENA / RX enabled", 9, 1)
94                     .WithTag("CLKDIV / Clock divider", 16, 16)
95                 },
96             };
97 
98             registers = new DoubleWordRegisterCollection(this, registersMap);
99         }
100 
Reset()101         public override void Reset()
102         {
103             base.Reset();
104             registers.Reset();
105             rxIdx = 0;
106             rxStarted = false;
107         }
108 
ReadDoubleWord(long offset)109         public uint ReadDoubleWord(long offset)
110         {
111             return registers.Read(offset);
112         }
113 
WriteDoubleWord(long offset, uint value)114         public void WriteDoubleWord(long offset, uint value)
115         {
116             registers.Write(offset, value);
117         }
118 
119         public long Size => 0x80;
120 
121         public GPIO TxIRQ { get; }
122         public GPIO RxIRQ { get; }
123 
124         public override Bits StopBits => stopBits.Value ? Bits.Two : Bits.One;
125         public override Parity ParityBit => parityEnable.Value ? Parity.Even : Parity.None;
126 
127         public override uint BaudRate => 115200;
128 
CharWritten()129         protected override void CharWritten()
130         {
131             if(!rxStarted)
132             {
133                 this.Log(LogLevel.Warning, "Received byte, but the RX is not started - ignoring it");
134                 return;
135             }
136 
137             if(!TryGetCharacter(out var c))
138             {
139                 this.Log(LogLevel.Error, "CharWritten called, but there is no data in the buffer. This might indicate a bug in the model");
140                 return;
141             }
142 
143             if(rxIdx >= rxBufferSize.Value)
144             {
145                 // this might happen when rxBufferSize is not initiated properly
146                 this.Log(LogLevel.Warning, "Received byte {0} (0x{0:X}), but there is no more space in the buffer - ignoring it", (char)c, c);
147                 return;
148             }
149 
150             sysbus.WriteByte(rxBufferAddress.Value + rxIdx, c);
151             rxIdx++;
152 
153             if(rxIdx == rxBufferSize.Value)
154             {
155                 RxIRQ.Blink();
156                 rxStarted = false;
157             }
158         }
159 
QueueEmptied()160         protected override void QueueEmptied()
161         {
162             // Intentionally left blank
163         }
164 
165         private uint rxIdx;
166         private bool rxStarted;
167 
168         private readonly IBusController sysbus;
169         private readonly DoubleWordRegisterCollection registers;
170 
171         private IFlagRegisterField parityEnable;
172         private IFlagRegisterField stopBits;
173         private IValueRegisterField txBufferAddress;
174         private IValueRegisterField txBufferSize;
175         private IValueRegisterField rxBufferAddress;
176         private IValueRegisterField rxBufferSize;
177 
178         private enum Registers : long
179         {
180             RxBaseAddress = 0x00,
181             RxSize        = 0x04,
182             RxConfig      = 0x08,
183             TxBaseAddress = 0x10,
184             TxSize        = 0x14,
185             TxConfig      = 0x18,
186             Status        = 0x20,
187             Setup         = 0x24,
188             Error         = 0x28,
189             IrqEnable     = 0x2c,
190             Valid         = 0x30,
191             Data          = 0x34,
192         }
193     }
194 }
195