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