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.Core; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Peripherals.Bus; 13 14 namespace Antmicro.Renode.Peripherals.UART 15 { 16 public class SiFive_UART : UARTBase, IDoubleWordPeripheral, IKnownSize 17 { SiFive_UART(IMachine machine, long inputClockFrequency = 16000000)18 public SiFive_UART(IMachine machine, long inputClockFrequency = 16000000) : base(machine) 19 { 20 this.inputClockFrequency = inputClockFrequency; 21 22 var registersMap = new Dictionary<long, DoubleWordRegister> 23 { 24 {(long)Registers.TransmitData, new DoubleWordRegister(this) 25 .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, b) => 26 { 27 if(transmitEnable.Value) 28 { 29 this.TransmitCharacter((byte)b); 30 UpdateInterrupts(); 31 } 32 else 33 { 34 var c = (char)b; 35 this.Log(LogLevel.Warning, "Trying to transmit '{1}' (0x{0}), but the transmitter is disabled", b, ' ' <= c ? c.ToString() : "<not a printable character>"); 36 } 37 }, name: "DATA") 38 .WithTag("RESERVED", 8, 23) 39 .WithFlag(31, valueProviderCallback: _ => false, name: "FULL") 40 }, 41 42 {(long)Registers.ReceiveData, new DoubleWordRegister(this) 43 // the "EMPTY" flag MUST be declared before "DATA" value field because 'Count' value 44 // might change as a result of dequeuing a character; otherwise if the queue was of 45 // length 1, the read of this register would return both the character and "EMPTY" flag 46 .WithFlag(31, valueProviderCallback: _ => Count == 0, name: "EMPTY") 47 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => 48 { 49 if(!TryGetCharacter(out var character)) 50 { 51 this.Log(LogLevel.Warning, "Trying to read data from empty receive fifo"); 52 } 53 return character; 54 }, name: "DATA") 55 .WithTag("RESERVED", 8, 23) 56 }, 57 58 {(long)Registers.TransmitControl, new DoubleWordRegister(this) 59 .WithFlag(0, out transmitEnable, name: "TXEN") 60 .WithFlag(1, out numberOfStopBits, name: "NSTOP") 61 .WithTag("RESERVED", 2, 14) 62 .WithValueField(16, 3, out transmitWatermarkLevel, changeCallback: (_, __) => UpdateInterrupts(), name: "TXCNT") 63 .WithTag("RESERVED", 19, 13) 64 }, 65 66 {(long)Registers.ReceiveControl, new DoubleWordRegister(this) 67 .WithFlag(0, out receiveEnable, changeCallback: (_, val) => 68 { 69 if(!val) 70 { 71 ClearBuffer(); 72 } 73 }, name: "RXEN") 74 .WithTag("RESERVED", 1, 15) 75 .WithValueField(16, 3, out receiveWatermarkLevel, changeCallback: (_, __) => UpdateInterrupts(), name: "RXCNT") 76 .WithTag("RESERVED", 19, 13) 77 }, 78 79 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 80 .WithFlag(0, out transmitWatermarkInterruptEnable, changeCallback: (_, __) => UpdateInterrupts(), name: "TXWM") 81 .WithFlag(1, out receiveWatermarkInterruptEnable, changeCallback: (_, __) => UpdateInterrupts(), name: "RXWM") 82 .WithTag("RESERVED", 2, 30) 83 }, 84 85 {(long)Registers.InterruptPending, new DoubleWordRegister(this) 86 .WithFlag(0, out transmitWatermarkInterruptPending, FieldMode.Read, name: "TXWM") 87 .WithFlag(1, out receiveWatermarkInterruptPending, FieldMode.Read, name: "RXWM") 88 .WithTag("RESERVED", 2, 30) 89 }, 90 91 {(long)Registers.BaudrateDivisor, new DoubleWordRegister(this, 0xFFFF) 92 .WithValueField(0, 16, out baudRateDivisor, name: "DIV") 93 .WithTag("RESERVED", 16, 16) 94 } 95 }; 96 97 registers = new DoubleWordRegisterCollection(this, registersMap); 98 IRQ = new GPIO(); 99 100 Reset(); 101 } 102 ReadDoubleWord(long offset)103 public uint ReadDoubleWord(long offset) 104 { 105 lock(innerLock) 106 { 107 return registers.Read(offset); 108 } 109 } 110 WriteDoubleWord(long offset, uint value)111 public void WriteDoubleWord(long offset, uint value) 112 { 113 lock(innerLock) 114 { 115 registers.Write(offset, value); 116 } 117 } 118 Reset()119 public override void Reset() 120 { 121 base.Reset(); 122 registers.Reset(); 123 UpdateInterrupts(); 124 } 125 126 public long Size => 0x100; 127 public GPIO IRQ { get; private set; } 128 public override Bits StopBits => numberOfStopBits.Value ? Bits.Two : Bits.One; 129 public override Parity ParityBit => Parity.None; 130 public override uint BaudRate => (uint)(inputClockFrequency / (uint)(1 + baudRateDivisor.Value)); 131 CharWritten()132 protected override void CharWritten() 133 { 134 if(!receiveEnable.Value) 135 { 136 ClearBuffer(); 137 } 138 else 139 { 140 UpdateInterrupts(); 141 } 142 } 143 QueueEmptied()144 protected override void QueueEmptied() 145 { 146 if(receiveEnable.Value) 147 { 148 UpdateInterrupts(); 149 } 150 } 151 UpdateInterrupts()152 private void UpdateInterrupts() 153 { 154 lock(innerLock) 155 { 156 transmitWatermarkInterruptPending.Value = (transmitWatermarkLevel.Value > 0); 157 receiveWatermarkInterruptPending.Value = (Count > (int)receiveWatermarkLevel.Value); 158 159 IRQ.Set(transmitWatermarkInterruptEnable.Value && transmitWatermarkInterruptPending.Value 160 || receiveWatermarkInterruptEnable.Value && receiveWatermarkInterruptPending.Value); 161 } 162 } 163 164 private readonly IFlagRegisterField transmitEnable; 165 private readonly IFlagRegisterField receiveEnable; 166 private readonly IFlagRegisterField numberOfStopBits; 167 private readonly IValueRegisterField baudRateDivisor; 168 private readonly IValueRegisterField transmitWatermarkLevel; 169 private readonly IValueRegisterField receiveWatermarkLevel; 170 private readonly IFlagRegisterField transmitWatermarkInterruptPending; 171 private readonly IFlagRegisterField receiveWatermarkInterruptPending; 172 private readonly IFlagRegisterField transmitWatermarkInterruptEnable; 173 private readonly IFlagRegisterField receiveWatermarkInterruptEnable; 174 175 private readonly long inputClockFrequency; 176 private readonly DoubleWordRegisterCollection registers; 177 178 private enum Registers : long 179 { 180 TransmitData = 0x0, 181 ReceiveData = 0x04, 182 TransmitControl = 0x08, 183 ReceiveControl = 0x0C, 184 InterruptEnable = 0x10, 185 InterruptPending = 0x14, 186 BaudrateDivisor = 0x18 187 } 188 } 189 } 190