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.Peripherals.Bus; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Logging; 13 14 namespace Antmicro.Renode.Peripherals.UART 15 { 16 public class PULP_uDMA_UART : UARTBase, IDoubleWordPeripheral, IKnownSize 17 { PULP_uDMA_UART(IMachine machine)18 public PULP_uDMA_UART(IMachine machine) : base(machine) 19 { 20 sysbus = machine.GetSystemBus(this); 21 TxIRQ = new GPIO(); 22 RxIRQ = new GPIO(); 23 24 var registersMap = new Dictionary<long, DoubleWordRegister> 25 { 26 {(long)Registers.RxBaseAddress, new DoubleWordRegister(this) 27 .WithValueField(0, 32, out rxBufferAddress, name: "RX_SADDR / Rx buffer base address") 28 }, 29 {(long)Registers.RxSize, new DoubleWordRegister(this) 30 .WithValueField(0, 17, out rxBufferSize, name: "RX_SIZE / Rx buffer size") 31 }, 32 {(long)Registers.RxConfig, new DoubleWordRegister(this) 33 .WithFlag(4, name: "EN / RX channel enable and start", 34 // Continuous mode is currently not supported 35 valueProviderCallback: _ => rxStarted, 36 writeCallback: (_, value) => 37 { 38 rxStarted = value; 39 if(value) 40 { 41 rxIdx = 0; 42 if(Count > 0) 43 { 44 // With the new round of reception we might still have some characters in the 45 // buffer. 46 CharWritten(); 47 } 48 } 49 }) 50 }, 51 {(long)Registers.TxBaseAddress, new DoubleWordRegister(this) 52 .WithValueField(0, 32, out txBufferAddress, name: "TX_SADDR / Tx buffer base address") 53 }, 54 {(long)Registers.TxSize, new DoubleWordRegister(this) 55 .WithValueField(0, 17, out txBufferSize, name: "TX_SIZE / Tx buffer size") 56 }, 57 {(long)Registers.TxConfig, new DoubleWordRegister(this) 58 .WithFlag(4, name: "EN / TX channel enable and start", 59 // Continuous mode is currently not supported 60 valueProviderCallback: _ => false, 61 writeCallback: (_, value) => 62 { 63 if(!value) 64 { 65 return; 66 } 67 68 if(txBufferSize.Value == 0) 69 { 70 this.Log(LogLevel.Warning, "TX is being enabled, but the buffer size is not configured. Ignoring the operation"); 71 return; 72 } 73 74 var data = sysbus.ReadBytes(txBufferAddress.Value, (int)txBufferSize.Value); 75 foreach(var c in data) 76 { 77 TransmitCharacter(c); 78 } 79 TxIRQ.Blink(); 80 }) 81 }, 82 {(long)Registers.Status, new DoubleWordRegister(this) 83 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => false, name: "TX busy") // tx is never busy 84 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => false, name: "RX busy") // rx is never busy 85 }, 86 {(long)Registers.Setup, new DoubleWordRegister(this) 87 .WithFlag(0, out parityEnable, name: "PARITY_ENA / Parity Enable") 88 .WithTag("BIT_LENGTH / Character length", 1, 2) 89 .WithFlag(3, out stopBits, name: "STOP_BITS / Stop bits length") 90 .WithTag("POLLING_EN / Polling Enabled", 4, 1) 91 .WithTag("CLEAN_FIFO / Clean RX FIFO", 5, 1) 92 .WithTag("TX_ENA / TX enabled", 8, 1) 93 .WithTag("RX_ENA / RX enabled", 9, 1) 94 .WithTag("CLKDIV / Clock divider", 16, 16) 95 }, 96 }; 97 98 registers = new DoubleWordRegisterCollection(this, registersMap); 99 } 100 Reset()101 public override void Reset() 102 { 103 base.Reset(); 104 registers.Reset(); 105 rxIdx = 0; 106 rxStarted = false; 107 } 108 ReadDoubleWord(long offset)109 public uint ReadDoubleWord(long offset) 110 { 111 return registers.Read(offset); 112 } 113 WriteDoubleWord(long offset, uint value)114 public void WriteDoubleWord(long offset, uint value) 115 { 116 registers.Write(offset, value); 117 } 118 119 public long Size => 0x80; 120 121 public GPIO TxIRQ { get; } 122 public GPIO RxIRQ { get; } 123 124 public override Bits StopBits => stopBits.Value ? Bits.Two : Bits.One; 125 public override Parity ParityBit => parityEnable.Value ? Parity.Even : Parity.None; 126 127 public override uint BaudRate => 115200; 128 CharWritten()129 protected override void CharWritten() 130 { 131 if(!rxStarted) 132 { 133 this.Log(LogLevel.Warning, "Received byte, but the RX is not started - ignoring it"); 134 return; 135 } 136 137 if(!TryGetCharacter(out var c)) 138 { 139 this.Log(LogLevel.Error, "CharWritten called, but there is no data in the buffer. This might indicate a bug in the model"); 140 return; 141 } 142 143 if(rxIdx >= rxBufferSize.Value) 144 { 145 // this might happen when rxBufferSize is not initiated properly 146 this.Log(LogLevel.Warning, "Received byte {0} (0x{0:X}), but there is no more space in the buffer - ignoring it", (char)c, c); 147 return; 148 } 149 150 sysbus.WriteByte(rxBufferAddress.Value + rxIdx, c); 151 rxIdx++; 152 153 if(rxIdx == rxBufferSize.Value) 154 { 155 RxIRQ.Blink(); 156 rxStarted = false; 157 } 158 } 159 QueueEmptied()160 protected override void QueueEmptied() 161 { 162 // Intentionally left blank 163 } 164 165 private uint rxIdx; 166 private bool rxStarted; 167 168 private readonly IBusController sysbus; 169 private readonly DoubleWordRegisterCollection registers; 170 171 private IFlagRegisterField parityEnable; 172 private IFlagRegisterField stopBits; 173 private IValueRegisterField txBufferAddress; 174 private IValueRegisterField txBufferSize; 175 private IValueRegisterField rxBufferAddress; 176 private IValueRegisterField rxBufferSize; 177 178 private enum Registers : long 179 { 180 RxBaseAddress = 0x00, 181 RxSize = 0x04, 182 RxConfig = 0x08, 183 TxBaseAddress = 0x10, 184 TxSize = 0x14, 185 TxConfig = 0x18, 186 Status = 0x20, 187 Setup = 0x24, 188 Error = 0x28, 189 IrqEnable = 0x2c, 190 Valid = 0x30, 191 Data = 0x34, 192 } 193 } 194 } 195