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.Collections.Generic; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Peripherals.Bus; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Utilities; 15 16 namespace Antmicro.Renode.Peripherals.I2C 17 { 18 public class PULP_uDMA_I2C : SimpleContainer<II2CPeripheral>, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection> 19 { PULP_uDMA_I2C(IMachine machine)20 public PULP_uDMA_I2C(IMachine machine) : base(machine) 21 { 22 sysbus = machine.GetSystemBus(this); 23 outputBuffer = new Queue<byte>(); 24 RegistersCollection = new DoubleWordRegisterCollection(this); 25 26 TxEvent = new GPIO(); 27 RxEvent = new GPIO(); 28 29 DefineRegisters(); 30 31 Reset(); 32 } 33 Reset()34 public override void Reset() 35 { 36 RegistersCollection.Reset(); 37 outputBuffer.Clear(); 38 39 repeatCounter = 1; 40 dataBytesLeft = 0; 41 bytesToRead = 0; 42 43 state = State.WaitForCommand; 44 } 45 ReadDoubleWord(long offset)46 public uint ReadDoubleWord(long offset) 47 { 48 return RegistersCollection.Read(offset); 49 } 50 WriteDoubleWord(long offset, uint value)51 public void WriteDoubleWord(long offset, uint value) 52 { 53 RegistersCollection.Write(offset, value); 54 } 55 56 public DoubleWordRegisterCollection RegistersCollection { get; } 57 58 public GPIO TxEvent { get; } 59 public GPIO RxEvent { get; } 60 DefineRegisters()61 private void DefineRegisters() 62 { 63 Registers.RxBufferBaseAddress.Define(this) 64 // this is not consistent with the documentation 65 // that states that only 21 bits are used for the address, 66 // but otherwise the sample fails 67 .WithValueField(0, 32, out rxBufferAddress, name: "RX_SADDR") 68 ; 69 70 Registers.RxBufferSize.Define(this) 71 .WithValueField(0, 20, out rxBufferSize, name: "RX_SIZE") 72 .WithReservedBits(20, 12) 73 ; 74 75 Registers.RxStreamConfiguration.Define(this) 76 .WithTag("CONTINOUS", 0, 1) 77 .WithReservedBits(1, 3) 78 .WithFlag(4, out rxStreamEnabled, name: "EN") 79 .WithTag("PENDING", 5, 1) 80 .WithTag("CLR", 6, 1) 81 .WithReservedBits(7, 25) 82 ; 83 84 Registers.TxBufferBaseAddress.Define(this) 85 // this is not consistent with the documentation 86 // that states that only 21 bits are used for the address, 87 // but otherwise the sample fails 88 .WithValueField(0, 32, out txBufferAddress, name: "TX_SADDR") 89 ; 90 91 Registers.TxBufferSize.Define(this) 92 .WithValueField(0, 20, out txBufferSize, name: "TX_SIZE") 93 .WithReservedBits(20, 12) 94 ; 95 96 Registers.TxStreamConfiguration.Define(this) 97 .WithTag("CONTINOUS", 0, 1) 98 .WithReservedBits(1, 3) 99 .WithFlag(4, name: "EN", 100 valueProviderCallback: _ => false, // the transfer is instant + we don't support the continous mode, so we disable right away 101 writeCallback: (_, val) => 102 { 103 if(!val) 104 { 105 return; 106 } 107 108 this.Log(LogLevel.Debug, "Starting a DMA TX transaction"); 109 var data = sysbus.ReadBytes(txBufferAddress.Value, (int)txBufferSize.Value); 110 #if DEBUG_PACKETS 111 this.Log(LogLevel.Noisy, "Read data from the memory @ 0x{0:X}: {1}", txBufferAddress.Value, Misc.PrettyPrintCollectionHex(data)); 112 #endif 113 114 HandleIncomingBytes(data); 115 TxEvent.Blink(); 116 }) 117 .WithFlag(5, FieldMode.Read, name: "PENDING", valueProviderCallback: _ => false) 118 .WithTag("CLR", 6, 1) 119 .WithReservedBits(7, 25) 120 ; 121 122 Registers.CommandBufferBaseAddress.Define(this) 123 .WithValueField(0, 21, name: "CMD_SADDR") 124 .WithReservedBits(21, 11) 125 ; 126 127 Registers.CommandBufferSize.Define(this) 128 .WithValueField(0, 20, name: "CMD_SIZE") 129 .WithReservedBits(20, 12) 130 ; 131 132 Registers.CommandStreamConfiguration.Define(this) 133 .WithTag("CONTINOUS", 0, 1) 134 .WithReservedBits(1, 3) 135 .WithTag("EN", 4, 1) 136 .WithTag("PENDING", 5, 1) 137 .WithTag("CLR", 6, 1) 138 .WithReservedBits(7, 25) 139 ; 140 141 Registers.Status.Define(this) 142 .WithTag("BUSY", 0, 1) 143 .WithTag("ARB_LOST", 1, 1) 144 .WithReservedBits(2, 30) 145 ; 146 147 Registers.Setup.Define(this) 148 .WithTag("DO_RST", 0, 1) 149 .WithReservedBits(1, 31) 150 ; 151 } 152 HandleIncomingBytes(byte[] data)153 private bool HandleIncomingBytes(byte[] data) 154 { 155 var index = 0; 156 while(index < data.Length) 157 { 158 switch(state) 159 { 160 case State.WaitForCommand: 161 { 162 var result = HandleCommand((Command)(data[index] >> 4), data, index + 1); 163 if(result == -1) 164 { 165 // there was an error 166 return false; 167 } 168 index += result + 1; 169 } 170 break; 171 172 case State.WaitForData: 173 { 174 var result = HandleData(data, index); 175 if(result == -1) 176 { 177 // there was an error 178 return false; 179 } 180 index += result; 181 } 182 break; 183 184 default: 185 this.Log(LogLevel.Warning, "Received data in an unexpected state: {0}", state); 186 return false; 187 } 188 } 189 190 return true; 191 } 192 HandleCommand(Command command, byte[] data, int argumentOffset)193 private int HandleCommand(Command command, byte[] data, int argumentOffset) 194 { 195 // A command can be followed by argument bytes 196 // - their actual amount is dependent on: 197 // (a) the command type, 198 // (b) the preceeding RPT command. 199 // That's why we must dynamically return the number of bytes that were consumed in this particular execution. 200 201 this.Log(LogLevel.Debug, "Handling command {0} (0x{0:X}) at offset {1}", command, argumentOffset - 1); 202 203 int argumentBytesConsumed; 204 switch(command) 205 { 206 case Command.Start: 207 case Command.Stop: 208 argumentBytesConsumed = 0; 209 // it might be a repeated start 210 TrySendToDevice(finishTransmission: true, generateWarningOnEmptyBuffer: false); 211 break; 212 213 case Command.ReadAck: 214 argumentBytesConsumed = 0; 215 bytesToRead += repeatCounter; 216 break; 217 218 case Command.ReadNotAck: 219 argumentBytesConsumed = 0; 220 bytesToRead++; 221 TryReadFromDevice(); 222 break; 223 224 case Command.EndOfTransmission: 225 argumentBytesConsumed = 0; 226 break; 227 228 case Command.Write: 229 argumentBytesConsumed = 0; 230 dataBytesLeft = repeatCounter; 231 state = State.WaitForData; 232 this.Log(LogLevel.Debug, "Write command detected with {0} bytes of data", repeatCounter); 233 break; 234 235 case Command.Configure: 236 // skip the div value, we don't simulate it anyway 237 argumentBytesConsumed = 2; 238 break; 239 240 case Command.Repeat: 241 repeatCounter = data[argumentOffset]; 242 argumentBytesConsumed = 1; 243 break; 244 245 default: 246 this.Log(LogLevel.Warning, "Unhandled command: {0} (0x{0:X})", command); 247 argumentBytesConsumed = -1; 248 break; 249 } 250 251 if(command != Command.Repeat) 252 { 253 // reset the repeat counter 254 repeatCounter = 1; 255 } 256 257 return argumentBytesConsumed; 258 } 259 HandleData(byte[] data, int offset)260 private int HandleData(byte[] data, int offset) 261 { 262 var bytesConsumed = 0; 263 264 while(offset < data.Length && dataBytesLeft > 0) 265 { 266 outputBuffer.Enqueue(data[offset]); 267 268 bytesConsumed++; 269 offset++; 270 dataBytesLeft--; 271 } 272 273 if(dataBytesLeft == 0) 274 { 275 state = State.WaitForCommand; 276 } 277 278 this.Log(LogLevel.Debug, "Handled {0} bytes of incoming data, bytes left {1}", bytesConsumed, dataBytesLeft); 279 280 return bytesConsumed; 281 } 282 TrySendToDevice(bool finishTransmission, bool generateWarningOnEmptyBuffer = true)283 private bool TrySendToDevice(bool finishTransmission, bool generateWarningOnEmptyBuffer = true) 284 { 285 if(outputBuffer.Count == 0) 286 { 287 if(generateWarningOnEmptyBuffer) 288 { 289 this.Log(LogLevel.Warning, "Tried to send data to the device, but there is no device address in the buffer"); 290 } 291 return false; 292 } 293 294 var addressAndDirection = outputBuffer.Dequeue(); 295 var address = addressAndDirection >> 1; 296 var isRead = BitHelper.IsBitSet(addressAndDirection, 0); 297 298 if(isRead) 299 { 300 this.Log(LogLevel.Warning, "Read bit should not be set when sending data to the device, ignoring the transfer"); 301 return false; 302 } 303 304 if(outputBuffer.Count == 0) 305 { 306 this.Log(LogLevel.Warning, "Tried to send data to the device, but the output buffer is empty"); 307 return false; 308 } 309 310 if(!TryGetByAddress(address, out var device)) 311 { 312 this.Log(LogLevel.Warning, "Tried to send data to the device 0x{0:X}, but it's not connected", address); 313 return false; 314 } 315 316 var data = outputBuffer.DequeueAll(); 317 318 this.Log(LogLevel.Debug, "Sending data of size {0} to the device 0x{1:X}", data.Length, address); 319 device.Write(data); 320 321 if(finishTransmission) 322 { 323 device.FinishTransmission(); 324 } 325 return true; 326 } 327 TryReadFromDevice()328 private bool TryReadFromDevice() 329 { 330 if(outputBuffer.Count == 0) 331 { 332 this.Log(LogLevel.Warning, "Tried to read data from the device, but there is no device address in the buffer"); 333 return false; 334 } 335 336 var addressAndDirection = outputBuffer.Dequeue(); 337 var address = addressAndDirection >> 1; 338 var isRead = BitHelper.IsBitSet(addressAndDirection, 0); 339 340 if(!isRead) 341 { 342 this.Log(LogLevel.Warning, "Read bit should be set when reading data from the device, ignoring the transfer"); 343 return false; 344 } 345 346 if(!TryGetByAddress(address, out var device)) 347 { 348 this.Log(LogLevel.Warning, "Tried to read data from the device 0x{0:X}, but it's not connected", address); 349 return false; 350 } 351 352 var data = device.Read(bytesToRead); 353 if(data.Length != bytesToRead) 354 { 355 this.Log(LogLevel.Warning, "Tried to read {0} bytes from the device, but it returned {1} bytes", bytesToRead, data.Length); 356 } 357 358 if(!rxStreamEnabled.Value) 359 { 360 this.Log(LogLevel.Warning, "Received {0} bytes from the device, but RX DMA stream is not enabled. Dropping it", data.Length); 361 } 362 else 363 { 364 if(data.Length != (int)rxBufferSize.Value) 365 { 366 this.Log(LogLevel.Warning, "Received {0} bytes from the device, but RX DMA stream is configured for {1} bytes. This might indicate problems in the driver", data.Length, rxBufferSize.Value); 367 } 368 369 sysbus.WriteBytes(data, rxBufferAddress.Value); 370 RxEvent.Blink(); 371 372 rxStreamEnabled.Value = false; 373 } 374 375 bytesToRead = 0; 376 return true; 377 } 378 379 // holds information about 380 // how many times *the next* command 381 // should be repeated; 382 // set by the RPT command; 383 // reset to 1 after the first use 384 private int repeatCounter; 385 private int dataBytesLeft; 386 private int bytesToRead; 387 388 private State state; 389 390 private IValueRegisterField txBufferAddress; 391 private IValueRegisterField txBufferSize; 392 private IValueRegisterField rxBufferAddress; 393 private IValueRegisterField rxBufferSize; 394 private IFlagRegisterField rxStreamEnabled; 395 396 private readonly IBusController sysbus; 397 private readonly Queue<byte> outputBuffer; 398 399 private enum State 400 { 401 WaitForCommand, 402 WaitForData 403 } 404 405 private enum Command : byte 406 { 407 Start = 0x0, 408 WaitEvent = 0x1, 409 Stop = 0x2, 410 SetupStartAddress = 0x3, 411 ReadAck = 0x4, 412 SetupTransferSize = 0x5, 413 ReadNotAck = 0x6, 414 WriteByte = 0x7, 415 Write = 0x8, 416 EndOfTransmission = 0x9, 417 Wait = 0xA, 418 Repeat = 0xC, 419 Configure = 0xE 420 } 421 422 private enum Registers 423 { 424 RxBufferBaseAddress = 0x0, 425 RxBufferSize = 0x4, 426 RxStreamConfiguration = 0x8, 427 428 TxBufferBaseAddress = 0x10, 429 TxBufferSize = 0x14, 430 TxStreamConfiguration = 0x18, 431 432 CommandBufferBaseAddress = 0x20, 433 CommandBufferSize = 0x24, 434 CommandStreamConfiguration = 0x28, 435 436 Status = 0x30, 437 Setup = 0x34 438 } 439 } 440 } 441