1 // 2 // Copyright (c) 2010-2024 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.Text; 9 using System.Linq; 10 using System.Collections.Generic; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Utilities; 13 14 namespace Antmicro.Renode.Peripherals.DMA 15 { 16 public partial class PL330_DMA 17 { RegisterInstructions()18 private void RegisterInstructions() 19 { 20 decoderRoot.AddOpcode(0b01010100, 8, () => new DMAADH(this, isDestinationAddressRegister: false)); 21 decoderRoot.AddOpcode(0b01010110, 8, () => new DMAADH(this, isDestinationAddressRegister: true)); 22 23 // DMAADNH should not be present in product revision r0p0 24 if(Revision > 0x0) 25 { 26 decoderRoot.AddOpcode(0b01011100, 8, () => new DMAADNH(this, isDestinationAddressRegister: false)); 27 decoderRoot.AddOpcode(0b01011110, 8, () => new DMAADNH(this, isDestinationAddressRegister: true)); 28 } 29 30 decoderRoot.AddOpcode(0b00000000, 8, () => new DMAEND(this)); 31 32 decoderRoot.AddOpcode(0b10100000, 8, () => new DMAGO(this, nonSecure: false)); 33 decoderRoot.AddOpcode(0b10100010, 8, () => new DMAGO(this, nonSecure: true)); 34 35 decoderRoot.AddOpcode(0b00000001, 8, () => new DMAKILL(this)); 36 decoderRoot.AddOpcode(0b00110100, 8, () => new DMASEV(this)); 37 decoderRoot.AddOpcode(0b00011000, 8, () => new DMANOP(this)); 38 decoderRoot.AddOpcode(0b00110110, 8, () => new DMAWFE(this)); 39 decoderRoot.AddOpcode(0b00010011, 8, () => new DMAWMB(this)); 40 decoderRoot.AddOpcode(0b00010010, 8, () => new DMARMB(this)); 41 42 decoderRoot.AddOpcode(0b00000100, 8, () => new DMALD(this, isConditional: false)); 43 decoderRoot.AddOpcode(0b00000101, 8, () => new DMALD(this, isConditional: true, transactionType: Channel.ChannelRequestType.Single)); 44 decoderRoot.AddOpcode(0b00000111, 8, () => new DMALD(this, isConditional: true, transactionType: Channel.ChannelRequestType.Burst)); 45 46 decoderRoot.AddOpcode(0b00001000, 8, () => new DMAST(this, isConditional: false)); 47 decoderRoot.AddOpcode(0b00001001, 8, () => new DMAST(this, isConditional: true, transactionType: Channel.ChannelRequestType.Single)); 48 decoderRoot.AddOpcode(0b00001011, 8, () => new DMAST(this, isConditional: true, transactionType: Channel.ChannelRequestType.Burst)); 49 decoderRoot.AddOpcode(0b00001100, 8, () => new DMASTZ(this)); 50 51 decoderRoot.AddOpcode(0b10111100, 8, () => new DMAMOV(this)); 52 53 decoderRoot.AddOpcode(0b00100000, 8, () => new DMALP(this, loopCounterIndex: 0)); 54 decoderRoot.AddOpcode(0b00100010, 8, () => new DMALP(this, loopCounterIndex: 1)); 55 56 decoderRoot.AddOpcode(0b00111000, 8, () => new DMALPEND(this, isConditional: false)); 57 decoderRoot.AddOpcode(0b00111100, 8, () => new DMALPEND(this, isConditional: false, loopCounterIndex: 1)); 58 decoderRoot.AddOpcode(0b00101100, 8, () => new DMALPEND(this, isConditional: false, loopCounterIndex: 1, isForever: true)); 59 decoderRoot.AddOpcode(0b00111001, 8, () => new DMALPEND(this, isConditional: true, transactionType: Channel.ChannelRequestType.Single)); 60 decoderRoot.AddOpcode(0b00111101, 8, () => new DMALPEND(this, isConditional: true, transactionType: Channel.ChannelRequestType.Single, loopCounterIndex: 1)); 61 decoderRoot.AddOpcode(0b00101101, 8, () => new DMALPEND(this, isConditional: true, transactionType: Channel.ChannelRequestType.Single, loopCounterIndex: 1, isForever: true)); 62 decoderRoot.AddOpcode(0b00111011, 8, () => new DMALPEND(this, isConditional: true, transactionType: Channel.ChannelRequestType.Burst)); 63 decoderRoot.AddOpcode(0b00111111, 8, () => new DMALPEND(this, isConditional: true, transactionType: Channel.ChannelRequestType.Burst, loopCounterIndex: 1)); 64 decoderRoot.AddOpcode(0b00101111, 8, () => new DMALPEND(this, isConditional: true, transactionType: Channel.ChannelRequestType.Burst, loopCounterIndex: 1 , isForever: true)); 65 66 decoderRoot.AddOpcode(0b00110101, 8, () => new DMAFLUSHP(this)); 67 decoderRoot.AddOpcode(0b00110001, 8, () => new DMAWFP(this, isPeripheralDriven: true)); 68 decoderRoot.AddOpcode(0b00110000, 8, () => new DMAWFP(this, isPeripheralDriven: false, transactionType: Channel.ChannelRequestType.Single)); 69 decoderRoot.AddOpcode(0b00110010, 8, () => new DMAWFP(this, isPeripheralDriven: false, transactionType: Channel.ChannelRequestType.Burst)); 70 71 decoderRoot.AddOpcode(0b00100101, 8, () => new DMALDP(this, transactionType: Channel.ChannelRequestType.Single)); 72 decoderRoot.AddOpcode(0b00100111, 8, () => new DMALDP(this, transactionType: Channel.ChannelRequestType.Burst)); 73 74 decoderRoot.AddOpcode(0b00101001, 8, () => new DMASTP(this, transactionType: Channel.ChannelRequestType.Single)); 75 decoderRoot.AddOpcode(0b00101011, 8, () => new DMASTP(this, transactionType: Channel.ChannelRequestType.Burst)); 76 } 77 78 private SimpleInstructionDecoder<Instruction> decoderRoot = new SimpleInstructionDecoder<Instruction>(); 79 80 private abstract class Instruction 81 { ParseAll(ulong value)82 public void ParseAll(ulong value) 83 { 84 if(Length > sizeof(ulong)) 85 { 86 // Quite unlikely, since the instructions have at most 48 bits total 87 throw new ArgumentException($"Expected instruction length: {Length} is greater than the size of provided value"); 88 } 89 90 while(!IsFinished) 91 { 92 Parse((byte)value); 93 value >>= 8; 94 } 95 } 96 Parse(byte value)97 public void Parse(byte value) 98 { 99 if(IsFinished) 100 { 101 return; 102 } 103 104 currentByteCount++; 105 instructionBytes.Add(value); 106 107 if(IsFinished) 108 { 109 ParseCompleteAction(); 110 } 111 } 112 Execute(DMAThreadType threadType, int? channelIndex = null, bool suppressAdvance = false)113 public void Execute(DMAThreadType threadType, int? channelIndex = null, bool suppressAdvance = false) 114 { 115 if(threadType == DMAThreadType.Manager && channelIndex != null) 116 { 117 throw new InvalidOperationException("Thread is a manager, but channel given"); 118 } 119 if(threadType == DMAThreadType.Channel && channelIndex == null) 120 { 121 throw new InvalidOperationException("Thread is a channel, but no channel given"); 122 } 123 124 ulong offset = ExecuteIfCorrectThread(threadType, channelIndex); 125 126 // Automatically advance PC by offset (usually instruction length) if offset > 0 127 // Don't do it by length automatically, since there exist instructions that change PC explicitly 128 if(!suppressAdvance && offset > 0) 129 { 130 if(threadType == DMAThreadType.Channel) 131 { 132 Parent.channels[channelIndex.Value].PC += offset; 133 } 134 else 135 { 136 Parent.Log(LogLevel.Error, "DMA Manager thread behavior is unimplemented."); 137 } 138 } 139 } 140 ToString()141 public override string ToString() 142 { 143 StringBuilder bits = new StringBuilder(""); 144 foreach(var b in instructionBytes.Reverse()) 145 { 146 bits.Append(Convert.ToString(b, 2).PadLeft(8, '0')); 147 } 148 return $"{Name}" + (bits.Length > 0 ? $" [{bits}]" : ""); 149 } 150 151 public string Name { get; } 152 public bool IsFinished 153 { 154 get => currentByteCount == Length; 155 } 156 Instruction(PL330_DMA parent, uint length = 1, bool usableByChannel = true, bool usableByManager = false)157 protected Instruction(PL330_DMA parent, uint length = 1, bool usableByChannel = true, bool usableByManager = false) 158 { 159 this.usableByManager = usableByManager; 160 this.usableByChannel = usableByChannel; 161 this.Length = length; 162 this.Parent = parent; 163 164 Name = GetType().Name; 165 } 166 ParseCompleteAction()167 protected virtual void ParseCompleteAction() {} 168 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)169 protected virtual ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 170 { 171 Parent.Log(LogLevel.Error, "Instruction \"{0}\" is not implemented. Skipping it!", Name); 172 return Length; 173 } 174 ExecuteIfCorrectThread(DMAThreadType threadType, int? channelIndex)175 private ulong ExecuteIfCorrectThread(DMAThreadType threadType, int? channelIndex) 176 { 177 if(threadType == DMAThreadType.Manager && !usableByManager) 178 { 179 Parent.Log(LogLevel.Error, "Thread is a manager, but instruction {0} not usable by manager", this.ToString()); 180 // TODO: We should abort manager thread here, but its logic is unimplemented now 181 return Length; 182 } 183 if(threadType == DMAThreadType.Channel && !usableByChannel) 184 { 185 Parent.Log(LogLevel.Error, "Thread is a channel, but instruction {0} not usable by channel", this.ToString()); 186 // The docs are not clear about what happens here, but UndefinedInstruction seems logical 187 Parent.channels[channelIndex.Value].SignalChannelAbort(Channel.ChannelFaultReason.UndefinedInstruction); 188 return Length; 189 } 190 return ExecuteInner(threadType, channelIndex); 191 } 192 193 protected readonly uint Length; 194 195 protected int currentByteCount; 196 protected IList<byte> instructionBytes = new List<byte>(); 197 198 protected readonly PL330_DMA Parent; 199 private readonly bool usableByManager; 200 private readonly bool usableByChannel; 201 } 202 203 private abstract class DMAADH_base : Instruction 204 { DMAADH_base(PL330_DMA parent, bool isDestinationAddressRegister, bool negative)205 public DMAADH_base(PL330_DMA parent, bool isDestinationAddressRegister, bool negative) : base(parent, length: 3) 206 { 207 this.negative = negative; 208 this.isDestinationAddressRegister = isDestinationAddressRegister; 209 } 210 ParseCompleteAction()211 protected override void ParseCompleteAction() 212 { 213 immediate = (ushort)((instructionBytes[1] << 8) | instructionBytes[0]); 214 } 215 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)216 protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 217 { 218 unchecked 219 { 220 if(isDestinationAddressRegister) 221 { 222 if(!negative) 223 { 224 Parent.channels[channelIndex.Value].DestinationAddress += immediate; 225 } 226 else 227 { 228 Parent.channels[channelIndex.Value].DestinationAddress -= immediate; 229 } 230 } 231 else 232 { 233 if(!negative) 234 { 235 Parent.channels[channelIndex.Value].SourceAddress += immediate; 236 } 237 else 238 { 239 Parent.channels[channelIndex.Value].SourceAddress -= immediate; 240 } 241 } 242 } 243 return Length; 244 } 245 246 private ushort immediate; 247 248 private readonly bool negative; 249 private readonly bool isDestinationAddressRegister; 250 } 251 252 private class DMAADH : DMAADH_base 253 { DMAADH(PL330_DMA parent, bool isDestinationAddressRegister)254 public DMAADH(PL330_DMA parent, bool isDestinationAddressRegister) : base(parent, isDestinationAddressRegister, negative: false) {} 255 } 256 257 private class DMAADNH : DMAADH_base 258 { DMAADNH(PL330_DMA parent, bool isDestinationAddressRegister)259 public DMAADNH(PL330_DMA parent, bool isDestinationAddressRegister) : base(parent, isDestinationAddressRegister, negative: true) {} 260 } 261 262 private class DMAEND : Instruction 263 { DMAEND(PL330_DMA parent)264 public DMAEND(PL330_DMA parent) : base(parent, usableByManager: true) {} 265 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)266 protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 267 { 268 if(threadType == DMAThreadType.Manager) 269 { 270 Parent.Log(LogLevel.Error, "DMAEND is currently not supported for manager"); 271 } 272 else 273 { 274 var selectedChannel = Parent.channels[channelIndex.Value]; 275 selectedChannel.Status = Channel.ChannelStatus.Stopped; 276 selectedChannel.localMFIFO.Clear(); 277 selectedChannel.Peripheral = null; 278 } 279 return Length; 280 } 281 } 282 283 private class DMAGO : Instruction 284 { DMAGO(PL330_DMA parent, bool nonSecure)285 public DMAGO(PL330_DMA parent, bool nonSecure) : base(parent, length: 6, usableByChannel: false, usableByManager: true) 286 { 287 this.nonSecure = nonSecure; 288 } 289 ParseCompleteAction()290 protected override void ParseCompleteAction() 291 { 292 channelNumber = instructionBytes[1] & 0b111; 293 294 foreach(var b in instructionBytes.Reverse().Take(4)) 295 { 296 programCounter <<= 8; 297 programCounter |= b; 298 } 299 } 300 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)301 protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 302 { 303 var channel = Parent.channels[channelNumber]; 304 305 if(channel.Status != Channel.ChannelStatus.Stopped) 306 { 307 Parent.Log(LogLevel.Debug, "This channel is in state: {0}, DMAGO treated as NOP.", channel.Status.ToString()); 308 return Length; 309 } 310 if(nonSecure) 311 { 312 Parent.Log(LogLevel.Warning, "Non-secure bit is ignored, value: {0}", nonSecure); 313 } 314 315 channel.PC = programCounter; 316 channel.Status = Channel.ChannelStatus.Executing; 317 318 return 0; 319 } 320 321 private int channelNumber; 322 // The immediate value, used to set nth's channel's PC 323 private uint programCounter; 324 // Used to force channel into Non-Secure mode. We don't support secure/non-secure mode, so it's ignored 325 private readonly bool nonSecure; 326 } 327 328 private class DMAKILL : Instruction 329 { DMAKILL(PL330_DMA parent)330 public DMAKILL(PL330_DMA parent) : base(parent, usableByManager: true) {} 331 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)332 protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 333 { 334 if(threadType == DMAThreadType.Manager) 335 { 336 Parent.Log(LogLevel.Error, "KILL on Manager thread is currently unsupported"); 337 } 338 else 339 { 340 var selectedChannel = Parent.channels[channelIndex.Value]; 341 selectedChannel.localMFIFO.Clear(); 342 selectedChannel.Status = Channel.ChannelStatus.Stopped; 343 selectedChannel.Peripheral = null; 344 } 345 return Length; 346 } 347 } 348 349 private abstract class DMA_LD_ST_base : Instruction 350 { DMA_LD_ST_base(PL330_DMA parent, bool IsConditional, Channel.ChannelRequestType TransactionType, uint length = 1)351 public DMA_LD_ST_base(PL330_DMA parent, bool IsConditional, Channel.ChannelRequestType TransactionType, uint length = 1) 352 : base(parent, length) 353 { 354 this.isConditional = IsConditional; 355 this.transactionType = TransactionType; 356 } 357 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)358 protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 359 { 360 if(!isConditional) 361 { 362 DoTransfer(channelIndex.Value, false); 363 return Length; 364 } 365 366 var requestType = Parent.channels[channelIndex.Value].RequestType; 367 if(requestType == transactionType) 368 { 369 DoTransfer(channelIndex.Value, requestType == Channel.ChannelRequestType.Single); 370 return Length; 371 } 372 373 // Treat as NOP 374 return Length; 375 } 376 DoEndianSwap(Channel selectedChannel, int dataLengthToSwap)377 protected void DoEndianSwap(Channel selectedChannel, int dataLengthToSwap) 378 { 379 byte[] bytesToSwap = selectedChannel.localMFIFO.DequeueRange(dataLengthToSwap); 380 byte[] bytesRemaining = selectedChannel.localMFIFO.DequeueAll(); 381 382 if(bytesToSwap.Length % selectedChannel.EndianSwapSize != 0) 383 { 384 // Not sure what happens here - the docs recommend to avoid this state 385 // but there is no mention if DMA should abort now 386 Parent.Log(LogLevel.Error, "Number of bytes requested for transfer: {0} is not a multiple of EndianSwapSize: {1}", bytesToSwap.Length, selectedChannel.EndianSwapSize); 387 } 388 389 for(int i = 0; i < bytesToSwap.Length; i += selectedChannel.EndianSwapSize) 390 { 391 Array.Reverse(bytesToSwap, i, Math.Min(selectedChannel.EndianSwapSize, bytesToSwap.Length - i)); 392 } 393 // Restore contents of the thread's buffer 394 selectedChannel.localMFIFO.EnqueueRange(bytesToSwap); 395 selectedChannel.localMFIFO.EnqueueRange(bytesRemaining); 396 } 397 DoTransfer(int channelIndex, bool ignoreBurst)398 protected abstract void DoTransfer(int channelIndex, bool ignoreBurst); 399 400 private readonly bool isConditional; 401 private readonly Channel.ChannelRequestType transactionType; 402 } 403 404 private class DMALD : DMA_LD_ST_base 405 { DMALD(PL330_DMA parent, bool isConditional, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)406 public DMALD(PL330_DMA parent, bool isConditional, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single) 407 : base(parent, isConditional, transactionType) {} 408 DoTransfer(int channelIndex, bool ignoreBurst)409 protected override void DoTransfer(int channelIndex, bool ignoreBurst) 410 { 411 var selectedChannel = Parent.channels[channelIndex]; 412 var readLength = selectedChannel.SourceReadSize; 413 414 for(var burst = 0; burst < (ignoreBurst ? 1 : selectedChannel.SourceBurstLength); ++burst) 415 { 416 byte[] byteArray = Parent.machine.GetSystemBus(Parent).ReadBytes(selectedChannel.SourceAddress, readLength, context: Parent.GetCurrentCPUOrNull()); 417 selectedChannel.localMFIFO.EnqueueRange(byteArray); 418 419 if(selectedChannel.SourceIncrementingAddress) 420 { 421 selectedChannel.SourceAddress += (uint)readLength; 422 } 423 } 424 } 425 } 426 427 private class DMAST : DMA_LD_ST_base 428 { DMAST(PL330_DMA parent, bool isConditional, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)429 public DMAST(PL330_DMA parent, bool isConditional, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single) 430 : base(parent, isConditional, transactionType) {} 431 DoTransfer(int channelIndex, bool ignoreBurst)432 protected override void DoTransfer(int channelIndex, bool ignoreBurst) 433 { 434 var selectedChannel = Parent.channels[channelIndex]; 435 var writeLength = selectedChannel.DestinationWriteSize; 436 var burstLength = ignoreBurst ? 1 : selectedChannel.DestinationBurstLength; 437 438 // If requested, swap endianness of data in buffer, just before the transmission 439 if(selectedChannel.EndianSwapSize > 1) 440 { 441 DoEndianSwap(selectedChannel, writeLength * burstLength); 442 } 443 444 for(var burst = 0; burst < burstLength; ++burst) 445 { 446 byte[] byteArray = selectedChannel.localMFIFO.DequeueRange(writeLength); 447 if(byteArray.Length != writeLength) 448 { 449 Parent.Log(LogLevel.Error, "Underflow in channel queue, {0} bytes remaining in FIFO, but requested to write {1}. Aborting thread.", byteArray.Length, writeLength); 450 selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.NotEnoughStoredDataInMFIFO); 451 return; 452 } 453 Parent.machine.GetSystemBus(Parent).WriteBytes(byteArray, selectedChannel.DestinationAddress, context: Parent.GetCurrentCPUOrNull()); 454 455 if(selectedChannel.DestinationIncrementingAddress) 456 { 457 selectedChannel.DestinationAddress += (uint)writeLength; 458 } 459 } 460 } 461 } 462 463 private class DMASTZ : DMA_LD_ST_base 464 { DMASTZ(PL330_DMA parent)465 public DMASTZ(PL330_DMA parent) : base(parent, IsConditional: false, TransactionType: Channel.ChannelRequestType.Single) {} 466 DoTransfer(int channelIndex, bool _)467 protected override void DoTransfer(int channelIndex, bool _) 468 { 469 var selectedChannel = Parent.channels[channelIndex]; 470 var writeLength = selectedChannel.DestinationWriteSize; 471 472 for(var burst = 0; burst < selectedChannel.DestinationBurstLength; ++burst) 473 { 474 Parent.sysbus.WriteBytes(Enumerable.Repeat((byte)0, writeLength).ToArray(), selectedChannel.DestinationAddress, context: Parent.GetCurrentCPUOrNull()); 475 476 if(selectedChannel.DestinationIncrementingAddress) 477 { 478 selectedChannel.DestinationAddress += (uint)writeLength; 479 } 480 } 481 } 482 } 483 484 485 private abstract class DMANOP_base : Instruction 486 { DMANOP_base(PL330_DMA parent, uint length, bool usableByChannel = true, bool usableByManager = false)487 public DMANOP_base(PL330_DMA parent, uint length, bool usableByChannel = true, bool usableByManager = false) 488 : base(parent, length, usableByChannel, usableByManager) 489 {} 490 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)491 protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 492 { 493 // NOP - intentionally no action 494 return Length; 495 } 496 } 497 498 private class DMANOP : DMANOP_base 499 { DMANOP(PL330_DMA parent)500 public DMANOP(PL330_DMA parent) : base(parent, length: 1, usableByManager: true) {} 501 } 502 503 private class DMAWMB : DMANOP_base 504 { 505 // Write memory barrier - treat as NOP 506 // for us each load/store instruction has immediate result 507 // so there should be no need for a barrier operation DMAWMB(PL330_DMA parent)508 public DMAWMB(PL330_DMA parent) : base(parent, length: 1) {} 509 } 510 511 private class DMARMB : DMANOP_base 512 { 513 // See: DMAWMB for explanation DMARMB(PL330_DMA parent)514 public DMARMB(PL330_DMA parent) : base(parent, length: 1) {} 515 } 516 517 518 private class DMASEV : Instruction 519 { DMASEV(PL330_DMA parent)520 public DMASEV(PL330_DMA parent) : base(parent, length: 2, usableByManager: true) {} 521 ParseCompleteAction()522 protected override void ParseCompleteAction() 523 { 524 eventNumber = (uint)instructionBytes[1] >> 3; 525 } 526 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)527 protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 528 { 529 if(threadType == DMAThreadType.Manager) 530 { 531 Parent.Log(LogLevel.Error, "SEV on Manager thread is currently unsupported"); 532 } 533 else 534 { 535 if(!Parent.SignalEventOrInterrupt(eventNumber)) 536 { 537 Parent.channels[channelIndex.Value].SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand); 538 } 539 } 540 return Length; 541 } 542 543 private uint eventNumber; 544 } 545 546 547 private class DMAWFE : Instruction 548 { DMAWFE(PL330_DMA parent)549 public DMAWFE(PL330_DMA parent) : base(parent, length: 2, usableByManager: true) {} 550 ParseCompleteAction()551 protected override void ParseCompleteAction() 552 { 553 eventNumber = (uint)instructionBytes[1] >> 3; 554 invalid = ((instructionBytes[1] >> 1) & 0x1) == 1; 555 } 556 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)557 protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 558 { 559 if(threadType == DMAThreadType.Manager) 560 { 561 Parent.Log(LogLevel.Error, "WFE on Manager thread is currently unsupported"); 562 } 563 else 564 { 565 var selectedChannel = Parent.channels[channelIndex.Value]; 566 567 if(!Parent.IsEventOrInterruptValid(eventNumber)) 568 { 569 selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand); 570 return Length; 571 } 572 573 if(Parent.eventActive[eventNumber]) 574 { 575 // If the event was pending before, let's deactivate it and continue execution normally 576 Parent.eventActive[eventNumber] = false; 577 } 578 else 579 { 580 // If the event is not active, then let's wait 581 selectedChannel.WaitingEventOrPeripheralNumber = eventNumber; 582 selectedChannel.Status = Channel.ChannelStatus.WaitingForEvent; 583 Parent.Log(LogLevel.Noisy, "DMAWFE: Channel {0} is waiting for event: {1}", selectedChannel.Id, eventNumber); 584 } 585 } 586 return Length; 587 } 588 589 private uint eventNumber; 590 // Invalid bit is used to force DMAC to invalidate its icache - we don't have any cache implemented 591 private bool invalid; 592 } 593 594 private class DMAMOV : Instruction 595 { DMAMOV(PL330_DMA parent)596 public DMAMOV(PL330_DMA parent) : base(parent, length: 6) {} 597 ParseCompleteAction()598 protected override void ParseCompleteAction() 599 { 600 registerNumber = instructionBytes[1] & 0b111; 601 foreach(var b in instructionBytes.Reverse().Take(4)) 602 { 603 immediate <<= 8; 604 immediate |= b; 605 } 606 } 607 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)608 protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 609 { 610 var selectedChannel = Parent.channels[channelIndex.Value]; 611 switch(registerNumber) 612 { 613 case 0b000: // SAR 614 selectedChannel.SourceAddress = immediate; 615 break; 616 case 0b001: // CCR 617 selectedChannel.ChannelControlRawValue = immediate; 618 break; 619 case 0b010: // DAR 620 selectedChannel.DestinationAddress = immediate; 621 break; 622 default: 623 Parent.Log(LogLevel.Error, "Invalid destination bits: {0}", registerNumber); 624 selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand); 625 break; 626 } 627 return Length; 628 } 629 630 private uint immediate; 631 private int registerNumber; 632 } 633 634 private class DMALP : Instruction 635 { DMALP(PL330_DMA parent, int loopCounterIndex)636 public DMALP(PL330_DMA parent, int loopCounterIndex) : base(parent, length: 2) 637 { 638 this.loopCounterIndex = loopCounterIndex; 639 } 640 ParseCompleteAction()641 protected override void ParseCompleteAction() 642 { 643 loopIterations = instructionBytes[1]; 644 } 645 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)646 protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 647 { 648 // There is no need to save loop start address here, it's done by assembler at code generation time in DMALPEND 649 var selectedChannel = Parent.channels[channelIndex.Value]; 650 selectedChannel.LoopCounter[loopCounterIndex] = loopIterations; 651 return Length; 652 } 653 654 private byte loopIterations; 655 656 private readonly int loopCounterIndex; 657 } 658 659 private class DMALPEND : Instruction 660 { DMALPEND(PL330_DMA parent, bool isConditional = false, bool isForever = false, int loopCounterIndex = 0, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)661 public DMALPEND(PL330_DMA parent, bool isConditional = false, bool isForever = false, int loopCounterIndex = 0, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single) : base(parent, length: 2) 662 { 663 this.isConditional = isConditional; 664 this.isForever = isForever; 665 this.loopCounterIndex = loopCounterIndex; 666 this.transactionType = transactionType; 667 } 668 ParseCompleteAction()669 protected override void ParseCompleteAction() 670 { 671 backwardsJump = instructionBytes[1]; 672 } 673 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)674 protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 675 { 676 if(!isConditional) 677 { 678 return DoJump(channelIndex.Value); 679 } 680 681 var requestType = Parent.channels[channelIndex.Value].RequestType; 682 if(requestType == transactionType) 683 { 684 return DoJump(channelIndex.Value); 685 } 686 687 // Treat as NOP 688 return Length; 689 } 690 DoJump(int channelIndex)691 private ulong DoJump(int channelIndex) 692 { 693 var channel = Parent.channels[channelIndex]; 694 695 bool shouldExitLoop = (!isForever && (channel.LoopCounter[loopCounterIndex] == 0)) 696 || (isForever && channel.RequestLast); 697 if(shouldExitLoop) 698 { 699 return Length; 700 } 701 702 channel.PC -= backwardsJump; 703 704 if(!isForever) 705 { 706 --channel.LoopCounter[loopCounterIndex]; 707 } 708 return 0; 709 } 710 711 private byte backwardsJump; 712 713 private readonly bool isConditional; 714 private readonly bool isForever; 715 private readonly int loopCounterIndex; 716 private readonly Channel.ChannelRequestType transactionType; 717 } 718 719 private class DMAFLUSHP : Instruction 720 { 721 // We don't model FLUSHP requests to the peripheral 722 // but let's use it to bind a peripheral to a channel 723 // this information will be cleared on END or KILL DMAFLUSHP(PL330_DMA parent)724 public DMAFLUSHP(PL330_DMA parent) : base(parent, length: 2) {} 725 ParseCompleteAction()726 protected override void ParseCompleteAction() 727 { 728 peripheral = (byte)(instructionBytes[1] >> 3); 729 } 730 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)731 protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 732 { 733 var selectedChannel = Parent.channels[channelIndex.Value]; 734 735 if(!Parent.IsPeripheralInterfaceValid(peripheral)) 736 { 737 selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand); 738 return Length; 739 } 740 selectedChannel.Peripheral = peripheral; 741 742 return Length; 743 } 744 745 private byte peripheral; 746 } 747 748 private class DMAWFP : Instruction 749 { DMAWFP(PL330_DMA parent, bool isPeripheralDriven = false, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)750 public DMAWFP(PL330_DMA parent, bool isPeripheralDriven = false, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single) : base(parent, length: 2) 751 { 752 this.isPeripheralDriven = isPeripheralDriven; 753 this.transactionType = transactionType; 754 } 755 ParseCompleteAction()756 protected override void ParseCompleteAction() 757 { 758 peripheral = (byte)(instructionBytes[1] >> 3); 759 } 760 ExecuteInner(DMAThreadType threadType, int? channelIndex = null)761 protected override ulong ExecuteInner(DMAThreadType threadType, int? channelIndex = null) 762 { 763 var selectedChannel = Parent.channels[channelIndex.Value]; 764 765 if(!Parent.IsPeripheralInterfaceValid(peripheral)) 766 { 767 selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand); 768 return Length; 769 } 770 selectedChannel.Peripheral = peripheral; 771 772 if(isPeripheralDriven) 773 { 774 // This feature is not yet implemented in this model 775 // treat it as invalid operand case, so a driver can kill the DMA thread and potentially recover 776 Parent.Log(LogLevel.Error, "DMAWFP: Channel {0}, waiting for peripheral: {1}, cannot be peripheral driven (periph bit set) - this is not yet supported. Aborting thread.", selectedChannel.Id, peripheral); 777 selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand); 778 return Length; 779 } 780 781 // Wait for peripheral here 782 selectedChannel.WaitingEventOrPeripheralNumber = peripheral; 783 selectedChannel.Status = Channel.ChannelStatus.WaitingForPeripheral; 784 785 // Since we don't support `periph` we operate under the simplified assumption 786 // that we will be woken up by the correct transfer type from the peripheral 787 // this might not always be correct 788 selectedChannel.RequestType = transactionType; 789 selectedChannel.RequestLast = false; 790 791 Parent.Log(LogLevel.Noisy, "DMAWFP: Channel {0} is waiting for peripheral: {1}", selectedChannel.Id, peripheral); 792 return Length; 793 } 794 795 private byte peripheral; 796 797 private readonly bool isPeripheralDriven; 798 private readonly Channel.ChannelRequestType transactionType; 799 } 800 801 private class DMALDP : DMA_LD_ST_base 802 { DMALDP(PL330_DMA parent, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)803 public DMALDP(PL330_DMA parent, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single) 804 : base(parent, true, transactionType, length: 2) 805 { 806 this.transactionType = transactionType; 807 } 808 ParseCompleteAction()809 protected override void ParseCompleteAction() 810 { 811 peripheral = (byte)(instructionBytes[1] >> 3); 812 } 813 DoTransfer(int channelIndex, bool ignoreBurst)814 protected override void DoTransfer(int channelIndex, bool ignoreBurst) 815 { 816 var selectedChannel = Parent.channels[channelIndex]; 817 if(!Parent.IsPeripheralInterfaceValid(peripheral)) 818 { 819 selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand); 820 return; 821 } 822 selectedChannel.Peripheral = peripheral; 823 824 var dmaEngine = new DmaEngine(Parent.machine.GetSystemBus(Parent)); 825 var readLengthInBytes = selectedChannel.SourceBurstLength * selectedChannel.SourceReadSize; 826 827 var bufferPlace = new Place(new byte[readLengthInBytes], 0); 828 829 var request = new Request( 830 selectedChannel.SourceAddress, 831 bufferPlace, 832 readLengthInBytes, 833 (TransferType)selectedChannel.SourceReadSize, 834 (TransferType)selectedChannel.DestinationWriteSize, 835 selectedChannel.SourceIncrementingAddress, 836 selectedChannel.DestinationIncrementingAddress 837 ); 838 var response = dmaEngine.IssueCopy(request, Parent.GetCurrentCPUOrNull()); 839 840 // Update address, if it was incrementing 841 selectedChannel.SourceAddress = (uint)response.ReadAddress.Value; 842 selectedChannel.localMFIFO.EnqueueRange(bufferPlace.Array); 843 } 844 845 private byte peripheral; 846 847 private readonly Channel.ChannelRequestType transactionType; 848 } 849 850 private class DMASTP : DMA_LD_ST_base 851 { DMASTP(PL330_DMA parent, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single)852 public DMASTP(PL330_DMA parent, Channel.ChannelRequestType transactionType = Channel.ChannelRequestType.Single) 853 : base(parent, true, transactionType, length: 2) 854 { 855 this.transactionType = transactionType; 856 } 857 ParseCompleteAction()858 protected override void ParseCompleteAction() 859 { 860 peripheral = (byte)(instructionBytes[1] >> 3); 861 } 862 DoTransfer(int channelIndex, bool ignoreBurst)863 protected override void DoTransfer(int channelIndex, bool ignoreBurst) 864 { 865 var selectedChannel = Parent.channels[channelIndex]; 866 if(!Parent.IsPeripheralInterfaceValid(peripheral)) 867 { 868 selectedChannel.SignalChannelAbort(Channel.ChannelFaultReason.InvalidOperand); 869 return; 870 } 871 selectedChannel.Peripheral = peripheral; 872 873 var dmaEngine = new DmaEngine(Parent.machine.GetSystemBus(Parent)); 874 var writeLengthInBytes = selectedChannel.DestinationBurstLength * selectedChannel.DestinationWriteSize; 875 876 // If requested, swap endianness of data in buffer, just before the transmission 877 if(selectedChannel.EndianSwapSize > 1) 878 { 879 DoEndianSwap(selectedChannel, writeLengthInBytes); 880 } 881 882 var bufferPlace = new Place(selectedChannel.localMFIFO.DequeueRange(writeLengthInBytes), 0); 883 884 var request = new Request( 885 bufferPlace, 886 selectedChannel.DestinationAddress, 887 writeLengthInBytes, 888 (TransferType)selectedChannel.SourceReadSize, 889 (TransferType)selectedChannel.DestinationWriteSize, 890 selectedChannel.SourceIncrementingAddress, 891 selectedChannel.DestinationIncrementingAddress 892 ); 893 var response = dmaEngine.IssueCopy(request, Parent.GetCurrentCPUOrNull()); 894 895 // Update address, if it was incrementing 896 selectedChannel.DestinationAddress = (uint)response.WriteAddress.Value; 897 } 898 899 private byte peripheral; 900 901 private readonly Channel.ChannelRequestType transactionType; 902 } 903 904 } 905 } 906