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; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.Bus; 14 15 namespace Antmicro.Renode.Peripherals.SPI 16 { 17 public class PULP_uDMA_SPI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize 18 { PULP_uDMA_SPI(IMachine machine)19 public PULP_uDMA_SPI(IMachine machine) : base(machine) 20 { 21 sysbus = machine.GetSystemBus(this); 22 RxIRQ = new GPIO(); 23 TxIRQ = new GPIO(); 24 CmdIRQ = new GPIO(); 25 26 var registers = new Dictionary<long, DoubleWordRegister> 27 { 28 {(long)Registers.RxTransferAddress, new DoubleWordRegister(this) 29 .WithValueField(0, 32, out rxTransferAddress, name: "SPIM_RX_SADDR") 30 }, 31 32 {(long)Registers.RxTransferBufferSize, new DoubleWordRegister(this) 33 .WithValueField(0, 20, out rxTransferBufferSize, name: "SPIM_RX_SIZE") 34 .WithReservedBits(20, 12) 35 }, 36 37 {(long)Registers.RxTransferConfiguration, new DoubleWordRegister(this) 38 .WithTag("CONTINOUS", 0, 1) 39 .WithTag("DATASIZE", 1, 2) 40 .WithReservedBits(3, 1) 41 .WithFlag(4, out rxEnable, name: "EN") 42 .WithTag("CLR/PENDING", 5, 1) 43 .WithReservedBits(6, 26) 44 .WithWriteCallback((_, __) => TryStartReception()) 45 }, 46 47 {(long)Registers.TxTransferAddress, new DoubleWordRegister(this) 48 .WithValueField(0, 32, out txTransferAddress, name: "SPIM_TX_SADDR") 49 }, 50 51 {(long)Registers.TxTransferBufferSize, new DoubleWordRegister(this) 52 .WithValueField(0, 20, out txTransferBufferSize, name: "SPIM_TX_SIZE") 53 .WithReservedBits(20, 12) 54 }, 55 56 {(long)Registers.TxTransferConfiguration, new DoubleWordRegister(this) 57 .WithTag("CONTINOUS", 0, 1) 58 .WithTag("DATASIZE", 1, 2) 59 .WithReservedBits(3, 1) 60 .WithFlag(4, out txEnable, name: "EN") 61 .WithTag("CLR/PENDING", 5, 1) 62 .WithReservedBits(6, 26) 63 .WithWriteCallback((_, __) => TryStartTransmission()) 64 }, 65 66 {(long)Registers.CommandTransferAddress, new DoubleWordRegister(this) 67 .WithValueField(0, 32, out commandTransferAddress, name: "SPIM_CMD_SADDR") 68 }, 69 70 {(long)Registers.CommandTransferBufferSize, new DoubleWordRegister(this) 71 .WithTag("CMD_SIZE", 0, 20) 72 .WithReservedBits(20, 12) 73 }, 74 75 {(long)Registers.CommandTransferConfiguration, new DoubleWordRegister(this) 76 .WithTag("CONTINOUS", 0, 1) 77 .WithTag("DATASIZE", 1, 2) 78 .WithReservedBits(3, 1) 79 .WithFlag(4, out commandEnable, name: "EN") 80 .WithTag("CLR/PENDING", 5, 1) 81 .WithReservedBits(6, 26) 82 .WithWriteCallback((_, __) => TryExecuteTransaction()) 83 }, 84 }; 85 86 registersCollection = new DoubleWordRegisterCollection(this, registers); 87 Reset(); 88 } 89 ReadDoubleWord(long offset)90 public uint ReadDoubleWord(long offset) 91 { 92 return registersCollection.Read(offset); 93 } 94 WriteDoubleWord(long offset, uint value)95 public void WriteDoubleWord(long offset, uint value) 96 { 97 registersCollection.Write(offset, value); 98 } 99 Reset()100 public override void Reset() 101 { 102 registersCollection.Reset(); 103 command = Commands.None; 104 // Updating interrupts here is not necessary as we blink with them 105 } 106 107 public long Size => 0x80; 108 109 public GPIO RxIRQ { get; } 110 public GPIO TxIRQ { get; } 111 public GPIO CmdIRQ { get; } 112 TryExecuteTransaction()113 private void TryExecuteTransaction() 114 { 115 if(command != Commands.None) 116 { 117 this.Log(LogLevel.Debug, "Tried to process a new command before finishing the previous one."); 118 return; 119 } 120 if(!commandEnable.Value) 121 { 122 this.Log(LogLevel.Debug, "Tried to issue a transaction without full configuration."); 123 return; 124 } 125 command = ReadCommand(); 126 switch(command) 127 { 128 case Commands.ReceiveData: 129 TryStartReception(); 130 break; 131 case Commands.TransferData: 132 TryStartTransmission(); 133 break; 134 default: 135 this.Log(LogLevel.Error, "Encountered unsupported command: 0x{0:X}", command); 136 command = Commands.None; 137 break; 138 } 139 CmdIRQ.Blink(); 140 } 141 TryStartReception()142 private void TryStartReception() 143 { 144 if(!rxEnable.Value || command != Commands.ReceiveData) 145 { 146 this.Log(LogLevel.Debug, "Tried to issue a transaction without full configuration."); 147 return; 148 } 149 if(RegisteredPeripheral == null) 150 { 151 this.Log(LogLevel.Warning, "Trying to issue a transaction to a slave peripheral, but nothing is connected"); 152 return; 153 } 154 var receiveQueue = new Queue<byte>(); 155 for(int i = 0; i < (int)rxTransferBufferSize.Value; i++) 156 { 157 receiveQueue.Enqueue(RegisteredPeripheral.Transmit(0)); 158 } 159 RegisteredPeripheral.FinishTransmission(); 160 sysbus.WriteBytes(receiveQueue.ToArray(), rxTransferAddress.Value); 161 receiveQueue.Clear(); 162 command = Commands.None; 163 RxIRQ.Blink(); 164 } 165 TryStartTransmission()166 private void TryStartTransmission() 167 { 168 if(!txEnable.Value || command != Commands.TransferData) 169 { 170 this.Log(LogLevel.Debug, "Tried to issue a transaction without full configuration."); 171 return; 172 } 173 if(RegisteredPeripheral == null) 174 { 175 this.Log(LogLevel.Warning, "Trying to issue a transaction to a slave peripheral, but nothing is connected"); 176 return; 177 } 178 foreach(var b in sysbus.ReadBytes(txTransferAddress.Value, (int)txTransferBufferSize.Value)) 179 { 180 RegisteredPeripheral.Transmit(b); 181 } 182 RegisteredPeripheral.FinishTransmission(); 183 command = Commands.None; 184 TxIRQ.Blink(); 185 } 186 ReadCommand()187 private Commands ReadCommand() 188 { 189 // The command is the third element of an array stored in memory, hence the addition 190 var cmd = (Commands)(sysbus.ReadDoubleWord(commandTransferAddress.Value + 8) >> 28); 191 if(!Enum.IsDefined(typeof(Commands), cmd)) 192 { 193 this.Log(LogLevel.Warning, "Invalid command has been issued: {0}", cmd); 194 } 195 return cmd; 196 } 197 198 private Commands command; 199 200 private readonly IBusController sysbus; 201 private readonly DoubleWordRegisterCollection registersCollection; 202 private readonly IFlagRegisterField commandEnable; 203 private readonly IFlagRegisterField rxEnable; 204 private readonly IFlagRegisterField txEnable; 205 private readonly IValueRegisterField rxTransferAddress; 206 private readonly IValueRegisterField rxTransferBufferSize; 207 private readonly IValueRegisterField txTransferAddress; 208 private readonly IValueRegisterField txTransferBufferSize; 209 private readonly IValueRegisterField commandTransferAddress; 210 211 private enum Commands : byte 212 { 213 Config = 0x0, 214 SetChipSelect = 0x1, 215 SendCommand = 0x2, 216 // no 0x3 command 217 ReceiveDummyBits = 0x4, 218 Wait = 0x5, 219 TransferData = 0x6, 220 ReceiveData = 0x7, 221 RepeatCommand = 0x8, 222 ClearChipSelect = 0x9, 223 RepeatCommandEnd = 0xA, 224 CheckAgainstExpectedValue = 0xB, 225 FullDuplex = 0xC, 226 SetAddressForDMA = 0xD, 227 SetSizeAndStartDMA = 0xE, 228 // not a standard command 229 None = 0xF 230 } 231 232 private enum Registers 233 { 234 RxTransferAddress = 0x0, 235 RxTransferBufferSize = 0x4, 236 RxTransferConfiguration = 0x8, 237 RxInitConfig = 0xC, 238 TxTransferAddress = 0x10, 239 TxTransferBufferSize = 0x14, 240 TxTransferConfiguration = 0x18, 241 TxInitConfig = 0x1C, 242 CommandTransferAddress = 0x20, 243 CommandTransferBufferSize = 0x24, 244 CommandTransferConfiguration = 0x28, 245 CommandInitConfig = 0x2C 246 } 247 } 248 } 249