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 
8 using System.Linq;
9 using System.Collections.Generic;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Logging;
15 using Antmicro.Renode.Utilities;
16 
17 namespace Antmicro.Renode.Peripherals.SD
18 {
19     public class PULP_uDMA_SDIO : NullRegistrationPointPeripheralContainer<SDCard>, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize
20     {
PULP_uDMA_SDIO(IMachine machine)21         public PULP_uDMA_SDIO(IMachine machine) : base(machine)
22         {
23             sysbus = machine.GetSystemBus(this);
24             response = new IValueRegisterField[ResponseBytes / 4];
25             DefineRegisters();
26         }
27 
Reset()28         public override void Reset()
29         {
30             RegistersCollection.Reset();
31         }
32 
ReadDoubleWord(long offset)33         public uint ReadDoubleWord(long offset)
34         {
35             return RegistersCollection.Read(offset);
36         }
37 
WriteDoubleWord(long offset, uint value)38         public void WriteDoubleWord(long offset, uint value)
39         {
40             RegistersCollection.Write(offset, value);
41         }
42 
43         public DoubleWordRegisterCollection RegistersCollection { get; }
44 
45         public long Size => 0x80;
46 
DefineRegisters()47         private void DefineRegisters()
48         {
49             Registers.RxBufferAddress.Define(this)
50                 .WithValueField(0, 32, out rxBufferAddress, name: "SDIO_RX_SADDR")
51             ;
52 
53             Registers.RxBufferSize.Define(this)
54                 .WithValueField(0, 20, out rxBufferSize, name: "SDIO_RX_SIZE")
55                 .WithReservedBits(20, 12)
56             ;
57 
58             Registers.RxConfiguration.Define(this)
59                 .WithTag("CONTINOUS", 0, 1)
60                 .WithTag("DATASIZE", 1, 2)
61                 .WithFlag(4, out rxBufferEnabled, name: "EN")
62                 .WithTag("PENDING", 5, 1)
63                 .WithTag("CLR", 6, 1)
64                 .WithReservedBits(7, 25)
65             ;
66 
67             Registers.TxBufferAddress.Define(this)
68                 .WithValueField(0, 32, out txBufferAddress, name: "SDIO_TX_SADDR")
69             ;
70 
71             Registers.TxBufferSize.Define(this)
72                 .WithValueField(0, 20, out txBufferSize, name: "SDIO_TX_SIZE")
73                 .WithReservedBits(20, 12)
74             ;
75 
76             Registers.TxConfiguration.Define(this)
77                 .WithTag("CONTINOUS", 0, 1)
78                 .WithTag("DATASIZE", 1, 2)
79                 .WithFlag(4, out txBufferEnabled, name: "EN")
80                 .WithTag("PENDING", 5, 1)
81                 .WithTag("CLR", 6, 1)
82                 .WithReservedBits(7, 25)
83             ;
84 
85             Registers.Command.Define(this)
86                 .WithEnumField<DoubleWordRegister, ResponseType>(0, 3, out responseType, FieldMode.Write, name: "CMD_RSP_TYPE")
87                 .WithReservedBits(3, 5)
88                 .WithValueField(8, 6, out commandOperation, FieldMode.Write, name: "CMD_OP")
89                 .WithReservedBits(8, 24)
90             ;
91 
92             Registers.CommandArgument.Define(this)
93                 .WithValueField(0, 32, out commandArgument, FieldMode.Write, name: "CMD_ARG")
94             ;
95 
96             Registers.DataSetup.Define(this)
97                 .WithFlag(0, out dataEnabled, name: "DATA_EN")
98                 .WithFlag(1, out dataRead, name: "DATA_RWN")
99                 .WithTag("DATA_QUAD", 2, 1)
100                 .WithReservedBits(3, 5)
101                 .WithValueField(8, 8, out numberOfBlocks, name: "BLOCK_NUM")
102                 .WithValueField(16, 10, out blockSize, name: "BLOCK_SIZE")
103                 .WithReservedBits(26, 6)
104             ;
105 
106             Registers.Start.Define(this)
107                 .WithFlag(0, FieldMode.Write, name: "START", writeCallback: (_, val) =>
108                 {
109                     if(!val)
110                     {
111                         return;
112                     }
113 
114                     var device = RegisteredPeripheral;
115                     if(device == null)
116                     {
117                         this.Log(LogLevel.Warning, "Tried to start a communication, but no device is currently attached");
118                         return;
119                     }
120 
121                     SendCommand(device);
122 
123                     if(dataEnabled.Value)
124                     {
125                         if(dataRead.Value)
126                         {
127                             if(!rxBufferEnabled.Value)
128                             {
129                                 this.Log(LogLevel.Warning, "Tried to read data from the device, but the DMA RX channel is disabled");
130                                 return;
131                             }
132 
133                             ReadFromDevice(device);
134                         }
135                         else
136                         {
137                             if(!txBufferEnabled.Value)
138                             {
139                                 this.Log(LogLevel.Warning, "Tried to write data to the device, but the DMA TX channel is disabled");
140                                 return;
141                             }
142 
143                             WriteDataToDevice(device);
144                         }
145                     }
146 
147                     endOfTransfer.Value = true;
148                 })
149                 .WithReservedBits(1, 31)
150             ;
151 
152             Registers.Response0.Define(this)
153                 .WithValueField(0, 32, out response[0], FieldMode.Read, name: "SDIO_RSP0")
154             ;
155 
156             Registers.Response1.Define(this)
157                 .WithValueField(0, 32, out response[1], FieldMode.Read, name: "SDIO_RSP1")
158             ;
159 
160             Registers.Response2.Define(this)
161                 .WithValueField(0, 32, out response[2], FieldMode.Read, name: "SDIO_RSP2")
162             ;
163 
164             Registers.Response3.Define(this)
165                 .WithValueField(0, 32, out response[3], FieldMode.Read, name: "SDIO_RSP3")
166             ;
167 
168             Registers.Status.Define(this)
169                 .WithFlag(0, out endOfTransfer, name: "EOT")
170                 .WithTag("ERROR", 1, 1)
171                 .WithReservedBits(2, 14)
172                 .WithTag("CMD_ERR_STATUS", 16, 6)
173                 .WithReservedBits(22, 2)
174                 .WithTag("DATA_ERR_STATUS", 24, 6)
175                 .WithReservedBits(30, 2)
176             ;
177 
178             Registers.StopCommand.Define(this)
179                 .WithTag("STOPCMD_RSP_TYPE", 0, 3)
180                 .WithReservedBits(3, 5)
181                 .WithTag("STOPCMD_OP", 8, 6)
182                 .WithReservedBits(14, 18)
183             ;
184 
185             Registers.StopCommand.Define(this)
186                 .WithTag("STOPCMD_ARG", 0, 32)
187             ;
188 
189             Registers.ClockDivider.Define(this)
190                 .WithTag("CLK_DIV", 0, 9)
191                 .WithReservedBits(9, 23)
192             ;
193         }
194 
SendCommand(SDCard device)195         private void SendCommand(SDCard device)
196         {
197             response[0].Value = 0;
198             response[1].Value = 0;
199             response[2].Value = 0;
200             response[3].Value = 0;
201 
202             var commandResult = device.HandleCommand((uint)commandOperation.Value, (uint)commandArgument.Value);
203             switch(responseType.Value)
204             {
205                 case ResponseType.None:
206                     if(commandResult.Length != 0)
207                     {
208                         this.Log(LogLevel.Warning, "Expected no response, but {0} bits received", commandResult.Length);
209                         return;
210                     }
211                     break;
212                 case ResponseType.Bits136:
213                     // our response does not contain 8 bits:
214                     // * start bit
215                     // * transmission bit
216                     // * command index / reserved bits (6 bits)
217                     if(commandResult.Length != 128)
218                     {
219                         this.Log(LogLevel.Warning, "Unexpected response of length 128 bits (excluding control bits), but {0} received", commandResult.Length);
220                         return;
221                     }
222                     // the following bits are considered a part of returned register, but are not included in the response buffer:
223                     // * CRC7 (7 bits)
224                     // * end bit
225                     // that's why we are skipping the initial 8-bits
226                     response[0].Value = commandResult.AsUInt32(8);
227                     response[1].Value = commandResult.AsUInt32(40);
228                     response[2].Value = commandResult.AsUInt32(72);
229                     response[3].Value = commandResult.AsUInt32(104, 24);
230                     break;
231                 case ResponseType.Bits48WithCRC:
232                 case ResponseType.Bits48NoCRC:
233                 case ResponseType.Bits48WithBusyCheck:
234                     // our response does not contain 16 bits:
235                     // * start bit
236                     // * transmission bit
237                     // * command index / reserved bits (6 bits)
238                     // * CRC7 (7 bits)
239                     // * end bit
240                     if(commandResult.Length != 32)
241                     {
242                         this.Log(LogLevel.Warning, "Expected a response of length {0} bits (excluding control bits and CRC), but {1} received", 32, commandResult.Length);
243                         return;
244                     }
245                     response[0].Value = commandResult.AsUInt32();
246                     break;
247                 default:
248                     this.Log(LogLevel.Warning, "Unexpected response type selected: {0}. Ignoring the command response.", responseType.Value);
249                     return;
250             }
251         }
252 
ReadFromDevice(SDCard device)253         private void ReadFromDevice(SDCard device)
254         {
255             var bytesToReadFromDevice = numberOfBlocks.Value * blockSize.Value;
256             if(bytesToReadFromDevice != rxBufferSize.Value)
257             {
258                 this.Log(LogLevel.Warning, "There seems to be an inconsistency between the number of bytes to read from the device ({0}) and the number of bytes to copy to the memory ({1})", bytesToReadFromDevice, rxBufferSize.Value);
259             }
260 
261             var data = device.ReadData((uint)bytesToReadFromDevice);
262             if((int)rxBufferSize.Value < data.Length)
263             {
264                 data = data.Take((int)rxBufferSize.Value).ToArray();
265             }
266 
267             sysbus.WriteBytes(data, (ulong)rxBufferAddress.Value);
268             this.Log(LogLevel.Noisy, "Copied {0} bytes from the device to 0x{1:X}", data.Length, rxBufferAddress.Value);
269         }
270 
WriteDataToDevice(SDCard device)271         private void WriteDataToDevice(SDCard device)
272         {
273             var bytesToWriteToDevice = numberOfBlocks.Value * blockSize.Value;
274             if(bytesToWriteToDevice != txBufferSize.Value)
275             {
276                 this.Log(LogLevel.Warning, "There seems to be an inconsistency between the number of bytes to write to the device ({0}) and the number of bytes to copy from the memory ({1})", bytesToWriteToDevice, txBufferSize.Value);
277             }
278 
279             var data = sysbus.ReadBytes((ulong)txBufferAddress.Value, (int)txBufferSize.Value);
280             if((int)bytesToWriteToDevice < data.Length)
281             {
282                 data = data.Take((int)txBufferSize.Value).ToArray();
283             }
284             device.WriteData(data);
285 
286             this.Log(LogLevel.Noisy, "Copied {0} bytes from 0x{1:X} to the device", data.Length, txBufferAddress.Value);
287         }
288 
289         private IFlagRegisterField endOfTransfer;
290 
291         private IValueRegisterField numberOfBlocks;
292         private IValueRegisterField blockSize;
293 
294         private IFlagRegisterField dataEnabled;
295         private IFlagRegisterField dataRead;
296 
297         private IValueRegisterField rxBufferAddress;
298         private IValueRegisterField rxBufferSize;
299         private IFlagRegisterField rxBufferEnabled;
300 
301         private IValueRegisterField txBufferAddress;
302         private IValueRegisterField txBufferSize;
303         private IFlagRegisterField txBufferEnabled;
304 
305         private IEnumRegisterField<ResponseType> responseType;
306         private IValueRegisterField commandOperation;
307         private IValueRegisterField commandArgument;
308 
309         private readonly IBusController sysbus;
310         private readonly IValueRegisterField[] response;
311 
312         private const int ResponseBytes = 16;
313 
314         private enum ResponseType
315         {
316             None = 0x0,
317             Bits48WithCRC = 0x1,
318             Bits48NoCRC = 0x2,
319             Bits136 = 0x3,
320             Bits48WithBusyCheck = 0x4
321         }
322 
323         private enum Registers
324         {
325             RxBufferAddress = 0x0,
326             RxBufferSize = 0x4,
327             RxConfiguration = 0x8,
328             TxBufferAddress = 0x10,
329             TxBufferSize = 0x14,
330             TxConfiguration = 0x18,
331             Command = 0x20,
332             CommandArgument = 0x24,
333             DataSetup = 0x28,
334             Start = 0x2c,
335             Response0 = 0x30,
336             Response1 = 0x34,
337             Response2 = 0x38,
338             Response3 = 0x3c,
339             ClockDivider = 0x40,
340             Status = 0x44,
341             StopCommand = 0x48,
342             StopCommandArgument = 0x52
343         }
344     }
345 }
346 
347