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 System.Linq; 10 using Antmicro.Renode.Utilities; 11 using Antmicro.Renode.Logging; 12 using static Antmicro.Renode.Peripherals.SPI.Cadence_xSPI; 13 14 namespace Antmicro.Renode.Peripherals.SPI.Cadence_xSPICommands 15 { 16 internal abstract class STIGCommand : Command 17 { CreateSTIGCommand(Cadence_xSPI controller, CommandPayload payload)18 static public STIGCommand CreateSTIGCommand(Cadence_xSPI controller, CommandPayload payload) 19 { 20 var commandType = DecodeCommandType(payload); 21 switch(commandType) 22 { 23 case CommandType.SendOperation: 24 case CommandType.SendOperationWithoutFinish: 25 return new SendOperationCommand(controller, payload); 26 case CommandType.DataSequence: 27 return new DataSequenceCommand(controller, payload); 28 default: 29 controller.Log(LogLevel.Warning, "Unable to create a STIG command, unknown command type 0x{0:x}", commandType); 30 return null; 31 } 32 } 33 STIGCommand(Cadence_xSPI controller, CommandPayload payload)34 public STIGCommand(Cadence_xSPI controller, CommandPayload payload) : base(controller) 35 { 36 Type = DecodeCommandType(payload); 37 ChipSelect = BitHelper.GetValue(payload[4], 12, 3); 38 } 39 ToString()40 public override string ToString() 41 { 42 return $"{base.ToString()}, commandType = {Type}"; 43 } 44 45 public override uint ChipSelect { get; } 46 47 protected CommandType Type { get; } 48 DecodeCommandType(CommandPayload payload)49 static private CommandType DecodeCommandType(CommandPayload payload) 50 { 51 return (CommandType)BitHelper.GetValue(payload[1], 0, 7); 52 } 53 54 protected enum CommandType 55 { 56 SendOperation = 0x0, 57 SendOperationWithoutFinish = 0x1, 58 DataSequence = 0x7f 59 } 60 } 61 62 internal class SendOperationCommand : STIGCommand 63 { SendOperationCommand(Cadence_xSPI controller, CommandPayload payload)64 public SendOperationCommand(Cadence_xSPI controller, CommandPayload payload) 65 : base(controller, payload) 66 { 67 operationCode = BitHelper.GetValue(payload[3], 16, 8); 68 addressRaw = (((ulong)payload[3] & 0xff) << 40) | ((ulong)payload[2] << 8) | (payload[1] >> 24); 69 addressValidBytes = (int)BitHelper.GetValue(payload[3], 28, 3); 70 addressBytes = BitHelper.GetBytesFromValue(addressRaw, addressValidBytes); 71 } 72 Transmit()73 public override void Transmit() 74 { 75 if(Peripheral != null) 76 { 77 Peripheral.Transmit((byte)operationCode); 78 foreach(var addressByte in addressBytes) 79 { 80 Peripheral.Transmit(addressByte); 81 } 82 } 83 84 Completed = true; 85 if(Type != CommandType.SendOperationWithoutFinish) 86 { 87 FinishTransmission(); 88 } 89 } 90 ToString()91 public override string ToString() 92 { 93 return $"{base.ToString()}, operationCode = 0x{operationCode:x}, addressBytes = [{string.Join(", ", addressBytes.Select(x => $"0x{x:x2}"))}]"; 94 } 95 96 private readonly uint operationCode; 97 private readonly ulong addressRaw; 98 private readonly int addressValidBytes; 99 private readonly byte[] addressBytes; 100 } 101 102 internal class DataSequenceCommand : STIGCommand, IDMACommand 103 { DataSequenceCommand(Cadence_xSPI controller, CommandPayload payload)104 public DataSequenceCommand(Cadence_xSPI controller, CommandPayload payload) 105 : base(controller, payload) 106 { 107 DMADirection = BitHelper.GetValue(payload[4], 4, 1) == 0 ? TransmissionDirection.Read : TransmissionDirection.Write; 108 doneTransmission = (payload[0] & 1) == 1; 109 DMADataCount = (payload[3] & 0xffff) | payload[2] >> 16; 110 var dummyBitsCount = BitHelper.GetValue(payload[3], 20, 6); 111 if(dummyBitsCount % 8 != 0) 112 { 113 Log(LogLevel.Warning, "The dummy bit count equals to {0} isn't multiplication of 8. Data sequence command doesn't support that."); 114 } 115 dummyBytesCount = dummyBitsCount / 8; 116 } 117 Transmit()118 public override void Transmit() 119 { 120 if(Peripheral == null) 121 { 122 DMAError = true; 123 Finish(); 124 return; 125 } 126 DMATriggered = true; 127 } 128 WriteData(IReadOnlyList<byte> data)129 public void WriteData(IReadOnlyList<byte> data) 130 { 131 if(DMADirection != TransmissionDirection.Write) 132 { 133 throw new InvalidOperationException($"Trying to write data using the command with wrong direction ({DMADirection})."); 134 } 135 136 TransmitDummyIfFirst(); 137 if(Peripheral != null) 138 { 139 foreach(var dataByte in data) 140 { 141 Peripheral.Transmit(dataByte); 142 dataTransmittedCount++; 143 } 144 } 145 FinishIfDone(); 146 } 147 ReadData(int length)148 public IList<byte> ReadData(int length) 149 { 150 if(DMADirection != TransmissionDirection.Read) 151 { 152 throw new InvalidOperationException($"Trying to read data using the command with wrong direction ({DMADirection})."); 153 } 154 155 TransmitDummyIfFirst(); 156 var data = Enumerable.Repeat(default(byte), length); 157 if(Peripheral != null) 158 { 159 data = data.Select( 160 dataByte => Peripheral.Transmit(dataByte) 161 ); 162 } 163 var dataList = data.ToList(); 164 dataTransmittedCount += (uint)dataList.Count; 165 FinishIfDone(); 166 167 return dataList; 168 } 169 ToString()170 public override string ToString() 171 { 172 return $"{base.ToString()}, dataCount = {DMADataCount}, dummyBytesCount = {dummyBytesCount}, doneTransmission = {doneTransmission}"; 173 } 174 175 public TransmissionDirection DMADirection { get; } 176 public uint DMADataCount { get; } 177 public bool DMATriggered { get; private set; } 178 public bool DMAError { get; private set; } 179 TransmitDummyIfFirst()180 private void TransmitDummyIfFirst() 181 { 182 if(dataTransmittedCount == 0 && Peripheral != null) 183 { 184 for(var i = 0; i < dummyBytesCount; i++) 185 { 186 Peripheral.Transmit(default(Byte)); 187 } 188 } 189 } 190 FinishIfDone()191 private void FinishIfDone() 192 { 193 if(dataTransmittedCount >= DMADataCount) 194 { 195 if(dataTransmittedCount > DMADataCount) 196 { 197 Log(LogLevel.Warning, "Accessed more data than command data count."); 198 } 199 Finish(); 200 } 201 } 202 Finish()203 private void Finish() 204 { 205 Completed = true; 206 if(doneTransmission) 207 { 208 FinishTransmission(); 209 } 210 } 211 212 private uint dataTransmittedCount; 213 private readonly bool doneTransmission; 214 private readonly uint dummyBytesCount; 215 } 216 } 217