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 
8 using System;
9 using System.Collections.Generic;
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_USCIA : IUART, IBytePeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, IKnownSize
19     {
MSP430_USCIA()20         public MSP430_USCIA()
21         {
22             RegistersCollection = new ByteRegisterCollection(this);
23             InterruptEnableRegister = new ByteRegister(this);
24             InterruptStatusRegister = new ByteRegister(this, resetValue: 0x02);
25 
26             DefineRegisters();
27             Reset();
28         }
29 
Reset()30         public void Reset()
31         {
32             RegistersCollection.Reset();
33             InterruptEnableRegister.Reset();
34             InterruptStatusRegister.Reset();
35 
36             interruptTransmitPending.Value = true;
37             UpdateInterrupts();
38 
39             queue.Clear();
40         }
41 
WriteChar(byte character)42         public void WriteChar(byte character)
43         {
44             queue.Enqueue(character);
45             interruptReceivePending.Value = true;
46             UpdateInterrupts();
47         }
48 
49         [ConnectionRegionAttribute("interruptEnable")]
WriteByteToInterruptEnable(long offset, byte value)50         public void WriteByteToInterruptEnable(long offset, byte value)
51         {
52             InterruptEnableRegister.Write(offset, value);
53         }
54 
55         [ConnectionRegionAttribute("interruptEnable")]
ReadByteFromInterruptEnable(long offset)56         public byte ReadByteFromInterruptEnable(long offset)
57         {
58             return InterruptEnableRegister.Read();
59         }
60 
61         [ConnectionRegionAttribute("interruptStatus")]
WriteByteToInterruptStatus(long offset, byte value)62         public void WriteByteToInterruptStatus(long offset, byte value)
63         {
64             InterruptStatusRegister.Write(offset, value);
65             UpdateInterrupts();
66         }
67 
68         [ConnectionRegionAttribute("interruptStatus")]
ReadByteFromInterruptStatus(long offset)69         public byte ReadByteFromInterruptStatus(long offset)
70         {
71             return InterruptStatusRegister.Read();
72         }
73 
WriteByte(long offset, byte value)74         public void WriteByte(long offset, byte value)
75         {
76             RegistersCollection.Write(offset, value);
77         }
78 
ReadByte(long offset)79         public byte ReadByte(long offset)
80         {
81             return RegistersCollection.Read(offset);
82         }
83 
84         public ByteRegisterCollection RegistersCollection { get; }
85         public ByteRegister InterruptEnableRegister { get; }
86         public ByteRegister InterruptStatusRegister { get; }
87 
88         public long Size => 0xB;
89 
90         public GPIO RxInterrupt { get; } = new GPIO();
91         public GPIO TxInterrupt { get; } = new GPIO();
92 
93         public Bits StopBits { get; }
94         public Parity ParityBit { get; }
95         public uint BaudRate { get; }
96 
97         public event Action<byte> CharReceived;
98 
UpdateInterrupts()99         private void UpdateInterrupts()
100         {
101             var rxInterrupt = interruptReceivePending.Value && interruptReceiveEnabled.Value;
102             var txInterrupt = interruptTransmitPending.Value && interruptTransmitEnabled.Value;
103 
104             this.Log(LogLevel.Debug, "RxInterrupt={0} TxInterrupt={1}", rxInterrupt, txInterrupt);
105 
106             RxInterrupt.Set(rxInterrupt);
107             TxInterrupt.Set(txInterrupt);
108         }
109 
DefineRegisters()110         private void DefineRegisters()
111         {
112             Registers.Control0.Define(this)
113                 .WithFlag(0, out syncMode, name: "UCSYNC")
114                 .WithValueField(1, 2, out usciMode, name: "UCMODEx")
115                 .WithTaggedFlag("UCSPB", 3)
116                 .WithTaggedFlag("UC7BIT", 4)
117                 .WithTaggedFlag("UCMSB", 5)
118                 .WithTaggedFlag("UCPAR", 6)
119                 .WithTaggedFlag("UCPEN", 7)
120             ;
121 
122             Registers.Control1.Define(this)
123                 .WithTaggedFlag("UCSWRST", 0)
124                 .WithTaggedFlag("UCTXBRK", 1)
125                 .WithTaggedFlag("UCTXADDR", 2)
126                 .WithTaggedFlag("UCDORM", 3)
127                 .WithTaggedFlag("UCBRKIE", 4)
128                 .WithTaggedFlag("UCRXEIE", 5)
129                 .WithTag("UCSSEL", 6, 2)
130             ;
131 
132             Registers.Status.Define(this)
133                 .WithTaggedFlag("UCBUSY", 0)
134                 .WithFlag(1, FieldMode.Read, name: "UCADDR",
135                     valueProviderCallback: _ => false)
136                 .WithTaggedFlag("UCRXERR", 2)
137                 .WithTaggedFlag("UCBRK", 3)
138                 .WithTaggedFlag("UCPE", 4)
139                 .WithTaggedFlag("UCOE", 5)
140                 .WithTaggedFlag("UCFE", 6)
141                 .WithFlag(7, out loopbackEnabled, name: "UCLISTEN")
142             ;
143 
144             Registers.ReceiveBuffer.Define(this)
145                 .WithValueField(0, 8, name: "UCRXBUFx",
146                     valueProviderCallback: _ =>
147                     {
148                         var returnValue = queue.TryDequeue(out var character) ? (byte)character : (byte)0;
149 
150                         if(queue.Count > 0)
151                         {
152                             interruptReceivePending.Value = true;
153                             UpdateInterrupts();
154                         }
155 
156                         return returnValue;
157                     })
158             ;
159 
160             Registers.TransmitBuffer.Define(this)
161                 .WithValueField(0, 8, name: "UCTXBUFx",
162                     valueProviderCallback: _ => 0,
163                     writeCallback: (_, value) =>
164                     {
165                         CharReceived?.Invoke((byte)value);
166 
167                         interruptTransmitPending.Value = true;
168                         UpdateInterrupts();
169 
170                         if(loopbackEnabled.Value)
171                         {
172                             WriteChar((byte)value);
173                         }
174                     })
175             ;
176 
177             InterruptEnableRegister
178                 .WithFlag(0, out interruptReceiveEnabled, name: "UCAxRXIE")
179                 .WithFlag(1, out interruptTransmitEnabled, name: "UCAxTXIE")
180                 .WithValueField(2, 6, name: "RESERVED")
181                 .WithWriteCallback((_, __) => UpdateInterrupts())
182             ;
183 
184             InterruptStatusRegister
185                 .WithFlag(0, out interruptReceivePending, name: "UCAxRXIFG")
186                 .WithFlag(1, out interruptTransmitPending, name: "UCAxTXIFG")
187                 .WithValueField(2, 6, name: "RESERVED")
188                 .WithWriteCallback((_, __) => UpdateInterrupts())
189             ;
190         }
191 
192         private Mode CurrentMode =>
193             !syncMode.Value ? Mode.UART : usciMode.Value == 0x3 ? Mode.I2C : Mode.SPI;
194 
195         private IFlagRegisterField syncMode;
196         private IValueRegisterField usciMode;
197 
198         private IFlagRegisterField interruptTransmitEnabled;
199         private IFlagRegisterField interruptReceiveEnabled;
200 
201         private IFlagRegisterField interruptTransmitPending;
202         private IFlagRegisterField interruptReceivePending;
203 
204         private IFlagRegisterField loopbackEnabled;
205 
206         private readonly Queue<byte> queue = new Queue<byte>();
207 
208         public enum Mode
209         {
210             UART,
211             SPI,
212             I2C,
213         }
214 
215         public enum Registers
216         {
217             AutoBaud,
218             IrDATransmit,
219             IrDAReceive,
220             Control0,
221             Control1,
222             BaudRate0,
223             BaudRate1,
224             Modulation,
225             Status,
226             ReceiveBuffer,
227             TransmitBuffer,
228         }
229     }
230 }
231