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