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