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