1 //
2 // Copyright (c) 2010-2025 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 
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.Peripherals.UART
17 {
18     public class MSP430_eUSCI : IUART, IWordPeripheral, IProvidesRegisterCollection<WordRegisterCollection>, IKnownSize
19     {
MSP430_eUSCI()20         public MSP430_eUSCI()
21         {
22             RegistersCollection = new WordRegisterCollection(this);
23             DefineRegisters();
24         }
25 
Reset()26         public void Reset()
27         {
28             RegistersCollection.Reset();
29             rxQueue.Clear();
30 
31             UpdateInterrupts();
32         }
33 
ReadWord(long offset)34         public ushort ReadWord(long offset)
35         {
36             return RegistersCollection.Read(offset);
37         }
38 
WriteWord(long offset, ushort value)39         public void WriteWord(long offset, ushort value)
40         {
41             RegistersCollection.Write(offset, value);
42         }
43 
WriteChar(byte character)44         public void WriteChar(byte character)
45         {
46             rxQueue.Enqueue(character);
47             interruptReceivePending.Value = true;
48             UpdateInterrupts();
49         }
50 
51         public event Action<byte> CharReceived;
52 
53         public WordRegisterCollection RegistersCollection { get; }
54 
55         public long Size => 0x20;
56 
57         public GPIO IRQ { get; } = new GPIO();
58 
59         public Bits StopBits { get; }
60         public Parity ParityBit { get; }
61         public uint BaudRate { get; }
62 
UpdateInterrupts()63         private void UpdateInterrupts()
64         {
65             var interrupt = false;
66 
67             interrupt |= interruptReceiveEnabled.Value && interruptReceivePending.Value;
68             interrupt |= interruptTransmitEnabled.Value && interruptTransmitPending.Value;
69 
70             this.Log(LogLevel.Debug, "IRQ set to {0}", interrupt);
71 
72             IRQ.Set(interrupt);
73         }
74 
DefineRegisters()75         private void DefineRegisters()
76         {
77             Registers.Control.Define(this)
78                 .WithTaggedFlag("UCSWRST", 0)
79                 .WithTaggedFlag("UCSTEM", 1)
80                 .WithTaggedFlag("UCTXADDR", 2)
81                 .WithTaggedFlag("UCDORM", 3)
82                 .WithTaggedFlag("UCBRKIE", 4)
83                 .WithTaggedFlag("UCRXEIE", 5)
84                 .WithTag("UCSSEL", 6, 2)
85                 .WithTaggedFlag("UCSYNC", 8)
86                 .WithTag("UCMOD", 9, 2)
87                 .WithTaggedFlag("UCMST", 11)
88                 .WithTaggedFlag("UC7BIT", 12)
89                 .WithTaggedFlag("UCMSB", 13)
90                 .WithTaggedFlag("UCCKPL", 14)
91                 .WithTaggedFlag("UCCKPH", 15)
92             ;
93 
94             Registers.Status.Define(this)
95                 .WithTaggedFlag("UCBUSY", 0)
96                 .WithTaggedFlag("UCADDR", 1)
97                 .WithTaggedFlag("UCRXERR", 2)
98                 .WithTaggedFlag("UCBRK", 3)
99                 .WithTaggedFlag("UCPE", 4)
100                 .WithTaggedFlag("UCOE", 5)
101                 .WithTaggedFlag("UCFE", 6)
102                 .WithFlag(7, out loopbackEnabled, name: "UCLISTEN")
103                 .WithReservedBits(8, 8)
104             ;
105 
106             Registers.ReceiveBuffer.Define(this)
107                 .WithValueField(0, 8, name: "UCRXBUFx",
108                     valueProviderCallback: _ =>
109                     {
110                         var returnValue = rxQueue.TryDequeue(out var character) ? (byte)character : (byte)0;
111 
112                         if(rxQueue.Count > 0)
113                         {
114                             interruptReceivePending.Value = true;
115                             UpdateInterrupts();
116                         }
117 
118                         return returnValue;
119                     })
120             ;
121 
122             Registers.TransmitBuffer.Define(this)
123                 .WithValueField(0, 8, name: "UCTXBUFx",
124                     writeCallback: (_, value) =>
125                     {
126                         CharReceived?.Invoke((byte)value);
127 
128                         interruptTransmitPending.Value = true;
129                         UpdateInterrupts();
130 
131                         if(loopbackEnabled.Value)
132                         {
133                             WriteChar((byte)value);
134                         }
135                     })
136             ;
137 
138             Registers.InterruptEnable.Define(this)
139                 .WithFlag(0, out interruptReceiveEnabled, name: "UCRXIE")
140                 .WithFlag(1, out interruptTransmitEnabled, name: "UCTXIE")
141                 .WithTaggedFlag("UCSTTIE", 2)
142                 .WithTaggedFlag("UCTXCPTIE", 3)
143                 .WithReservedBits(4, 12)
144                 .WithWriteCallback((_, __) => UpdateInterrupts())
145             ;
146 
147             Registers.InterruptFlag.Define(this)
148                 .WithFlag(0, out interruptReceivePending, name: "UCRXIFG")
149                 .WithFlag(1, out interruptTransmitPending, name: "UCTXIFG")
150                 .WithTaggedFlag("UCSTTIFG", 2)
151                 .WithTaggedFlag("UCTXCPTIFG", 3)
152                 .WithReservedBits(4, 12)
153                 .WithWriteCallback((_, __) => UpdateInterrupts())
154             ;
155 
156             Registers.InterruptVector.Define(this)
157                 .WithValueField(0, 16, FieldMode.Read, name: "UCIVx",
158                     valueProviderCallback: _ =>
159                     {
160                         if(interruptReceivePending.Value)
161                         {
162                             return 0x2;
163                         }
164                         else if(interruptTransmitPending.Value)
165                         {
166                             return 0x4;
167                         }
168                         return 0x00;
169                     })
170             ;
171         }
172 
173         private IFlagRegisterField interruptTransmitEnabled;
174         private IFlagRegisterField interruptReceiveEnabled;
175 
176         private IFlagRegisterField interruptTransmitPending;
177         private IFlagRegisterField interruptReceivePending;
178 
179         private IFlagRegisterField loopbackEnabled;
180 
181         private readonly Queue<byte> rxQueue = new Queue<byte>();
182 
183         private enum Registers
184         {
185             Control = 0x00,
186             BaudRate = 0x06,
187             Modulation = 0x08,
188             Status = 0x0A,
189             ReceiveBuffer = 0x0C,
190             TransmitBuffer = 0x0E,
191             AutoBaudRate = 0x10,
192             IrDA = 0x12,
193             InterruptEnable = 0x1A,
194             InterruptFlag = 0x1C,
195             InterruptVector = 0x1E,
196         }
197     }
198 }
199