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.Linq; 9 using System.Collections.Generic; 10 using System.Collections.ObjectModel; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Core.Structure.Registers; 14 using Antmicro.Renode.Peripherals.CPU; 15 using Antmicro.Renode.Exceptions; 16 using Antmicro.Renode.Utilities; 17 18 namespace Antmicro.Renode.Peripherals.DMA 19 { 20 public partial class PL330_DMA : BasicDoubleWordPeripheral, IKnownSize, IDMA, INumberedGPIOOutput, IGPIOReceiver 21 { 22 // This model doesn't take into account differences in AXI bus width, 23 // which could have impact on unaligned transfers in real HW 24 // this is a know limitation at this moment PL330_DMA(IMachine machine, uint numberOfSupportedEventsAndInterrupts = MaximumSupportedEventsOrInterrupts, uint numberOfSupportedPeripheralRequestInterfaces = MaximumSupportedPeripheralRequestInterfaces, byte revision = 0x3)25 public PL330_DMA(IMachine machine, uint numberOfSupportedEventsAndInterrupts = MaximumSupportedEventsOrInterrupts, uint numberOfSupportedPeripheralRequestInterfaces = MaximumSupportedPeripheralRequestInterfaces, byte revision = 0x3) : base(machine) 26 { 27 this.Revision = revision; 28 if(numberOfSupportedEventsAndInterrupts > MaximumSupportedEventsOrInterrupts) 29 { 30 throw new ConstructionException($"No more than {MaximumSupportedEventsOrInterrupts} Events or Interrupts are supported by this peripheral"); 31 } 32 this.NumberOfSupportedEventsAndInterrupts = (int)numberOfSupportedEventsAndInterrupts; 33 if(numberOfSupportedPeripheralRequestInterfaces > MaximumSupportedPeripheralRequestInterfaces) 34 { 35 throw new ConstructionException($"No more than {MaximumSupportedPeripheralRequestInterfaces} Peripheral Request Interfaces are supported by this peripheral"); 36 } 37 this.NumberOfSupportedPeripheralRequestInterfaces = (int)numberOfSupportedPeripheralRequestInterfaces; 38 39 channels = new Channel[NumberOfChannels]; 40 for(int i = 0; i < channels.Length; ++i) 41 { 42 channels[i] = new Channel(this, i); 43 } 44 45 RegisterInstructions(); 46 DefineRegisters(); 47 48 eventActive = new bool[NumberOfSupportedEventsAndInterrupts]; 49 50 var gpios = new Dictionary<int, IGPIO>(); 51 for(var i = 0; i < NumberOfSupportedEventsAndInterrupts; ++i) 52 { 53 gpios.Add(i, new GPIO()); 54 } 55 Connections = new ReadOnlyDictionary<int, IGPIO>(gpios); 56 57 Reset(); 58 } 59 Reset()60 public override void Reset() 61 { 62 base.Reset(); 63 debugStatus = false; 64 65 for(int i = 0; i < channels.Length; ++i) 66 { 67 channels[i].Reset(); 68 } 69 70 foreach(var connection in Connections.Values) 71 { 72 connection.Unset(); 73 } 74 for(int i = 0; i < NumberOfSupportedEventsAndInterrupts; ++i) 75 { 76 eventActive[i] = false; 77 } 78 AbortIRQ.Unset(); 79 } 80 OnGPIO(int number, bool value)81 public void OnGPIO(int number, bool value) 82 { 83 if(!IsPeripheralInterfaceValid((uint)number)) 84 { 85 return; 86 } 87 lock(executeLock) 88 { 89 if(!value) 90 { 91 // To simulate requestLast, hold IRQ line high during the entire transmission sequence, 92 // and bring it down, when all data is sent 93 // If this feature is not used at all in DMA microcode (program) this doesn't matter 94 foreach(var channel in channels.Where(c => c?.Peripheral == number)) 95 { 96 channel.RequestLast = true; 97 } 98 return; 99 } 100 101 bool anyChangedState = false; 102 foreach(var channel in channels.Where(c => c.Status == Channel.ChannelStatus.WaitingForPeripheral).Where(c => c.WaitingEventOrPeripheralNumber == number)) 103 { 104 // A peripheral has signaled that it's ready for DMA operations to start 105 channel.Status = Channel.ChannelStatus.Executing; 106 anyChangedState = true; 107 } 108 if(anyChangedState) 109 { 110 // Rerun ExecuteLoop if any channel woke up from sleep 111 ExecuteLoop(); 112 } 113 } 114 } 115 RequestTransfer(int channel)116 public void RequestTransfer(int channel) 117 { 118 throw new RecoverableException("This DMA requires an in-memory program to transfer data"); 119 } 120 121 // This method should be called from Monitor to decode instruction at given address. 122 // It is intended as a helper to investigate in-memory program. 123 // Uses QuadWord accesses, so the target must support them. TryDecodeInstructionAtAddress(ulong address, bool fullDecode = false)124 public string TryDecodeInstructionAtAddress(ulong address, bool fullDecode = false) 125 { 126 ulong bytes = machine.GetSystemBus(this).ReadQuadWord(address); 127 if(!decoderRoot.TryParseOpcode((byte)bytes, out var instruction)) 128 { 129 throw new RecoverableException("Unrecognized instruction"); 130 } 131 if(fullDecode) 132 { 133 instruction.ParseAll(bytes); 134 } 135 return instruction.ToString(); 136 } 137 138 public long Size => 0x1000; 139 public int NumberOfChannels => 8; 140 141 public IReadOnlyDictionary<int, IGPIO> Connections { get; } 142 public GPIO AbortIRQ { get; } = new GPIO(); 143 144 public byte Revision { get; } 145 public int NumberOfSupportedEventsAndInterrupts { get; } 146 public int NumberOfSupportedPeripheralRequestInterfaces { get; } 147 148 // The values below are only used in Configuration and DmaConfiguration registers - they have no impact on the model's operation 149 public int InstructionCacheLineLength { get; set; } = 16; 150 public int InstructionCacheLinesNumber { get; set; } = 32; 151 152 public ulong WriteIssuingCapability { get; set; } = 8; 153 public ulong ReadIssuingCapability { get; set; } = 8; 154 155 public ulong ReadQueueDepth { get; set; } = 16; 156 public ulong WriteQueueDepth { get; set; } = 16; 157 158 public ulong DataBufferDepth { get; set; } = 1024; 159 public ulong AXIBusWidth { get; set; } = 32; 160 DefineRegisters()161 private void DefineRegisters() 162 { 163 Registers.DmaInterruptEnable.Define(this) 164 .WithFlags(0, NumberOfSupportedEventsAndInterrupts, out interruptEnabled, 165 writeCallback: (idx, _, val) => 166 { 167 if(!val) 168 { 169 Connections[idx].Unset(); 170 } 171 }, 172 name: "DMA Interrupt Enable") 173 .WithReservedBits(NumberOfSupportedEventsAndInterrupts, 32 - NumberOfSupportedEventsAndInterrupts); 174 175 Registers.DmaEventInterruptRawStatus.Define(this) 176 .WithFlags(0, NumberOfSupportedEventsAndInterrupts, FieldMode.Read, 177 valueProviderCallback: (idx, _) => eventActive[idx] || Connections[idx].IsSet, 178 name: "DMA Event or Interrupt Status") 179 .WithReservedBits(NumberOfSupportedEventsAndInterrupts, 32 - NumberOfSupportedEventsAndInterrupts); 180 181 Registers.DmaInterruptStatus.Define(this) 182 .WithFlags(0, NumberOfSupportedEventsAndInterrupts, FieldMode.Read, 183 valueProviderCallback: (idx, _) => Connections[idx].IsSet, 184 name: "DMA Interrupt Status") 185 .WithReservedBits(NumberOfSupportedEventsAndInterrupts, 32 - NumberOfSupportedEventsAndInterrupts); 186 187 Registers.DmaInterruptClear.Define(this) 188 .WithFlags(0, NumberOfSupportedEventsAndInterrupts, FieldMode.Write, 189 writeCallback: (idx, _, val) => 190 { 191 if(val) 192 { 193 Connections[idx].Unset(); 194 } 195 }, 196 name: "DMA Interrupt Clear") 197 .WithReservedBits(NumberOfSupportedEventsAndInterrupts, 32 - NumberOfSupportedEventsAndInterrupts); 198 199 // This register is RO. To bring channel out of fault, issue KILL to Debug Registers 200 Registers.FaultStatusDmaChannel.Define(this) 201 .WithFlags(0, NumberOfChannels, FieldMode.Read, 202 valueProviderCallback: (idx, _) => channels[idx].Status == Channel.ChannelStatus.Faulting, 203 name: "Faulting channels"); 204 205 Registers.DebugStatus.Define(this) 206 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => debugStatus, name: "Debug status (dbgstatus)") 207 .WithReservedBits(1, 31); 208 209 Registers.DebugCommand.Define(this) 210 .WithValueField(0, 2, FieldMode.Write, 211 writeCallback: (_, val) => 212 { 213 if(val == 0b00) 214 { 215 ExecuteDebugStart(); 216 } 217 else 218 { 219 this.Log(LogLevel.Error, "Undefined DMA Debug Command: {0}", val); 220 } 221 }, 222 name: "Debug Command") 223 .WithReservedBits(2, 30); 224 225 Registers.DebugInstruction0.Define(this) 226 .WithEnumField(0, 1, out debugThreadType, name: "Debug thread select") 227 .WithReservedBits(1, 7) 228 .WithValueField(8, 3, out debugChannelNumber, name: "Debug channel select") 229 .WithReservedBits(11, 5) 230 .WithValueField(16, 8, out debugInstructionByte0, name: "Instruction byte 0") 231 .WithValueField(24, 8, out debugInstructionByte1, name: "Instruction byte 1"); 232 233 Registers.DebugInstruction1.Define(this) 234 .WithValueField(0, 32, out debugInstructionByte2_3_4_5, name: "Instruction byte 2,3,4,5"); 235 236 Registers.Configuration0.Define(this) 237 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => NumberOfSupportedPeripheralRequestInterfaces > 0, name: "Supports Peripheral Request Interface") 238 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => false, name: "Boot from PC") 239 .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => false, name: "Boot security state for Manager Thread") 240 .WithReservedBits(3, 1) 241 .WithValueField(4, 3, FieldMode.Read, valueProviderCallback: _ => (ulong)NumberOfChannels - 1, name: "Number of DMA channels") 242 .WithReservedBits(7, 5) 243 .WithValueField(12, 5, FieldMode.Read, valueProviderCallback: _ => (ulong)NumberOfSupportedPeripheralRequestInterfaces - 1, name: "Number of Peripheral Request Interfaces") 244 .WithValueField(17, 5, FieldMode.Read, valueProviderCallback: _ => (ulong)NumberOfSupportedEventsAndInterrupts - 1, name: "Number of Events/Interrupts") 245 .WithReservedBits(22, 10); 246 247 Registers.Configuration1.Define(this) 248 .WithValueField(0, 3, FieldMode.Read, valueProviderCallback: _ => (ulong)Math.Floor(Math.Log(InstructionCacheLineLength, 2)), name: "Length of icache line") 249 .WithReservedBits(3, 1) 250 .WithValueField(4, 4, FieldMode.Read, valueProviderCallback: _ => (ulong)InstructionCacheLinesNumber - 1, name: "Number of icache lines") 251 .WithReservedBits(8, 24); 252 253 // Following Configuration registers are tagged only, and currently have no functionality implemented 254 // this is the model's limitation at this moment 255 Registers.Configuration2.Define(this) 256 .WithTag("Boot PC Address", 0, 32); 257 258 Registers.Configuration3.Define(this) 259 .WithTag("Event/Interrupt is Non-secure", 0, NumberOfSupportedEventsAndInterrupts) 260 .WithReservedBits(NumberOfSupportedEventsAndInterrupts, 32 - NumberOfSupportedEventsAndInterrupts); 261 262 Registers.Configuration4.Define(this) 263 .WithTag("Peripheral Request Interface is Non-secure", 0, NumberOfSupportedEventsAndInterrupts) 264 .WithReservedBits(NumberOfSupportedEventsAndInterrupts, 32 - NumberOfSupportedEventsAndInterrupts); 265 266 // These are configurable, but have currently no effect on DMA operation 267 Registers.DmaConfiguration.Define(this) 268 .WithValueField(0, 3, valueProviderCallback: _ => (ulong)Math.Floor(Math.Log(AXIBusWidth, 2)) - 3, name: "AXI bus width") // formula is: width_bytes = log2 (width / 8) ==> log2 width - 3 269 .WithReservedBits(3, 1) 270 .WithValueField(4, 3, valueProviderCallback: _ => WriteIssuingCapability - 1, name: "Write issuing capability (number of outstanding write transactions)") 271 .WithReservedBits(7, 1) 272 .WithValueField(8, 4, valueProviderCallback: _ => WriteQueueDepth - 1, name: "Write queue depth") 273 .WithValueField(12, 3, valueProviderCallback: _ => ReadIssuingCapability - 1, name: "Read issuing capability (number of outstanding read transactions)") 274 .WithReservedBits(15, 1) 275 .WithValueField(16, 4, valueProviderCallback: _ => ReadQueueDepth - 1, name: "Read queue depth") 276 .WithValueField(20, 10, valueProviderCallback: _ => DataBufferDepth - 1, name: "Data buffer lines") 277 .WithReservedBits(30, 2); 278 279 Registers.PeripheralIdentification0.Define(this) 280 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => 0x30, name: "Part number 0") 281 .WithReservedBits(8, 24); 282 283 Registers.PeripheralIdentification1.Define(this) 284 .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: _ => 0x3, name: "Part number 1") 285 .WithValueField(4, 4, FieldMode.Read, valueProviderCallback: _ => 0x1, name: "Designer 0") 286 .WithReservedBits(8, 24); 287 288 Registers.PeripheralIdentification2.Define(this) 289 .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: _ => 0x4, name: "Designer 1") 290 .WithValueField(4, 4, FieldMode.Read, valueProviderCallback: _ => Revision, name: "Revision") 291 .WithReservedBits(8, 24); 292 293 Registers.PeripheralIdentification3.Define(this) 294 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => false, name: "Integration test logic") 295 .WithReservedBits(1, 31); 296 297 Registers.ComponentIdentification0.Define(this) 298 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => 0x0D, name: "Component ID 0") 299 .WithReservedBits(8, 24); 300 301 Registers.ComponentIdentification1.Define(this) 302 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => 0xF0, name: "Component ID 1") 303 .WithReservedBits(8, 24); 304 305 Registers.ComponentIdentification2.Define(this) 306 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => 0x05, name: "Component ID 2") 307 .WithReservedBits(8, 24); 308 309 Registers.ComponentIdentification3.Define(this) 310 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => 0xB1, name: "Component ID 3") 311 .WithReservedBits(8, 24); 312 } 313 IsEventOrInterruptValid(uint eventNumber)314 private bool IsEventOrInterruptValid(uint eventNumber) 315 { 316 if(eventNumber >= NumberOfSupportedEventsAndInterrupts) 317 { 318 this.Log(LogLevel.Error, "Trying to signal event: {0}, greater than number of supported events {1}", eventNumber, NumberOfSupportedEventsAndInterrupts); 319 return false; 320 } 321 return true; 322 } 323 IsPeripheralInterfaceValid(uint peripheral)324 private bool IsPeripheralInterfaceValid(uint peripheral) 325 { 326 if(peripheral >= NumberOfSupportedPeripheralRequestInterfaces) 327 { 328 this.Log(LogLevel.Error, "Peripheral Request Interface {0} is not supported. {1} Request Interfaces are enabled.", 329 peripheral, NumberOfSupportedPeripheralRequestInterfaces); 330 return false; 331 } 332 return true; 333 } 334 SignalEventOrInterrupt(uint eventNumber)335 private bool SignalEventOrInterrupt(uint eventNumber) 336 { 337 if(!IsEventOrInterruptValid(eventNumber)) 338 { 339 return false; 340 } 341 342 // Depending on the value of DmaInterruptEnable register, we either signal an IRQ on a line 343 // or raise an internal event to wake up a thread that might be waiting for this event 344 // either now, or in the future. 345 if(interruptEnabled[eventNumber].Value) 346 { 347 // Signal an IRQ - use DmaInterruptClear to clear it. 348 Connections[(int)eventNumber].Set(); 349 } 350 else 351 { 352 // Raise an event 353 eventActive[eventNumber] = true; 354 bool anyResumed = false; 355 foreach(var channel in channels.Where(c => c.Status == Channel.ChannelStatus.WaitingForEvent).Where(c => c.WaitingEventOrPeripheralNumber == eventNumber)) 356 { 357 anyResumed = true; 358 // The channel will execute in next iteration of `ExecuteLoop` 359 channel.Status = Channel.ChannelStatus.Executing; 360 } 361 362 // Only clear the event if there already existed a channel waiting for this event - see section 2.7.1 of the Reference Manual 363 if(anyResumed) 364 { 365 eventActive[eventNumber] = false; 366 } 367 } 368 return true; 369 } 370 UpdateAbortInterrupt()371 private void UpdateAbortInterrupt() 372 { 373 if(channels.Any(c => c.Status == Channel.ChannelStatus.Faulting)) 374 { 375 AbortIRQ.Set(); 376 } 377 else 378 { 379 AbortIRQ.Unset(); 380 } 381 } 382 ExecuteDebugStart()383 private void ExecuteDebugStart() 384 { 385 debugStatus = true; 386 ulong debugInstructionBytes = debugInstructionByte2_3_4_5.Value << 16 | debugInstructionByte1.Value << 8 | debugInstructionByte0.Value; 387 388 string binaryInstructionString = Convert.ToString((long)debugInstructionBytes, 2); 389 this.Log(LogLevel.Debug, "Inserted debug instruction: {0}", binaryInstructionString); 390 391 if(!decoderRoot.TryParseOpcode((byte)debugInstructionBytes, out var debugInstruction)) 392 { 393 this.Log(LogLevel.Error, "Debug instruction \"{0}\" is not supported or invalid. DMA will not execute.", binaryInstructionString); 394 return; 395 } 396 397 debugInstruction.ParseAll(debugInstructionBytes); 398 ExecuteDebugInstruction(debugInstruction, (int)debugChannelNumber.Value, debugThreadType.Value); 399 debugStatus = false; 400 } 401 ExecuteDebugInstruction(Instruction firstInstruction, int channelIndex, DMAThreadType threadType)402 private void ExecuteDebugInstruction(Instruction firstInstruction, int channelIndex, DMAThreadType threadType) 403 { 404 if(!(firstInstruction is DMAGO 405 || firstInstruction is DMAKILL 406 || firstInstruction is DMASEV)) 407 { 408 this.Log(LogLevel.Error, "Debug instruction \"{0}\" is not DMAGO, DMAKILL, DMASEV. It cannot be the first instruction.", firstInstruction.ToString()); 409 return; 410 } 411 412 LogInstructionExecuted(firstInstruction, threadType, channelIndex); 413 // This is an instruction provided by the debug registers - it can't advance PC 414 firstInstruction.Execute(threadType, threadType != DMAThreadType.Manager ? (int?)channelIndex : null, suppressAdvance: true); 415 debugStatus = false; 416 ExecuteLoop(); 417 } 418 ExecuteLoop()419 private void ExecuteLoop() 420 { 421 // lock uses Monitor calls, and is re-entrant, so there will be no problems when executing on the same thread 422 lock(executeLock) 423 { 424 // TODO: in case of infinite loop, this will hang the emulation. 425 // It's not ideal - separate thread will be good, but what about time flow? 426 // Still, it's enough in the beginning, for simple use cases 427 do 428 { 429 foreach(var channelThread in channels.Where(c => c.Status == Channel.ChannelStatus.Executing)) 430 { 431 this.Log(LogLevel.Debug, "Executing channel thread: {0}", channelThread.Id); 432 433 while(channelThread.Status == Channel.ChannelStatus.Executing) 434 { 435 var address = channelThread.PC; 436 var insn = sysbus.ReadByte(address, context: GetCurrentCPUOrNull()); 437 if(!decoderRoot.TryParseOpcode(insn, out var instruction)) 438 { 439 this.Log(LogLevel.Error, "Invalid instruction with opcode 0x{0:X} at address: 0x{1:X}. Aborting thread {2}.", insn, address, channelThread.Id); 440 channelThread.SignalChannelAbort(Channel.ChannelFaultReason.UndefinedInstruction); 441 continue; 442 } 443 444 while(!instruction.IsFinished) 445 { 446 instruction.Parse(sysbus.ReadByte(address, context: GetCurrentCPUOrNull())); 447 address += sizeof(byte); 448 } 449 450 LogInstructionExecuted(instruction, DMAThreadType.Channel, channelThread.Id, channelThread.PC); 451 instruction.Execute(DMAThreadType.Channel, channelThread.Id); 452 } 453 } 454 455 // A channel might have become unpaused as a result of an event generated by another channel 456 // As long as there are any channels in executing state, we have to retry 457 } while(channels.Any(c => c.Status == Channel.ChannelStatus.Executing)); 458 } 459 } 460 LogInstructionExecuted(Instruction insn, DMAThreadType threadType, int threadId, ulong? address = null)461 private void LogInstructionExecuted(Instruction insn, DMAThreadType threadType, int threadId, ulong? address = null) 462 { 463 this.Log(LogLevel.Noisy, "[{0}] Executing: {1} {2}", threadType == DMAThreadType.Manager ? "M" : threadId.ToString(), 464 insn.ToString(), address != null ? $"@ 0x{address:X}" : "" ); 465 } 466 GetCurrentCPUOrNull()467 private ICPU GetCurrentCPUOrNull() 468 { 469 if(!machine.SystemBus.TryGetCurrentCPU(out var cpu)) 470 { 471 return null; 472 } 473 return cpu; 474 } 475 476 private IEnumRegisterField<DMAThreadType> debugThreadType; 477 private IValueRegisterField debugChannelNumber; 478 private IValueRegisterField debugInstructionByte0; 479 private IValueRegisterField debugInstructionByte1; 480 private IValueRegisterField debugInstructionByte2_3_4_5; 481 private IFlagRegisterField[] interruptEnabled; 482 // It's volatile, so this status is visible if several CPU threads will try to drive the DMA 483 private volatile bool debugStatus; 484 private readonly bool[] eventActive; 485 // Driving DMA from several cores at once has little sense 486 // but it's still possible that another core will drive a client peripheral requesting transfers 487 private readonly object executeLock = new object(); 488 489 private readonly Channel[] channels; 490 491 private const int MaximumSupportedEventsOrInterrupts = 32; 492 private const int MaximumSupportedPeripheralRequestInterfaces = 32; 493 494 private class Channel 495 { Channel(PL330_DMA parent, int id)496 public Channel(PL330_DMA parent, int id) 497 { 498 this.Parent = parent; 499 this.Id = id; 500 501 Reset(); 502 DefineRegisters(); 503 } 504 Reset()505 public void Reset() 506 { 507 ChannelControlRawValue = 0x00800200; 508 PC = 0; 509 SourceAddress = 0; 510 DestinationAddress = 0; 511 512 status = ChannelStatus.Stopped; 513 faultReason = ChannelFaultReason.NoFault; 514 RequestType = ChannelRequestType.Single; 515 RequestLast = false; 516 WaitingEventOrPeripheralNumber = 0; 517 Peripheral = null; 518 519 LoopCounter[0] = 0; 520 LoopCounter[1] = 0; 521 localMFIFO.Clear(); 522 } 523 DefineRegisters()524 private void DefineRegisters() 525 { 526 (Registers.Channel0FaultType + Id * 4).Define(Parent) 527 .WithEnumField<DoubleWordRegister, ChannelFaultReason>(0, 32, FieldMode.Read, valueProviderCallback: _ => faultReason, name: $"Channel {Id} Fault Reason"); 528 529 (Registers.Channel0Status + Id * 8).Define(Parent) 530 .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: _ => (ulong)Status, name: $"Channel {Id} Status") 531 .WithValueField(4, 5, FieldMode.Read, valueProviderCallback: _ => WaitingEventOrPeripheralNumber, name: $"Channel {Id} Wakeup Number") 532 .WithReservedBits(9, 5) 533 .WithFlag(14, FieldMode.Read, valueProviderCallback: _ => RequestType == ChannelRequestType.Burst, name: "DMAWFP is burst set") // dmawfp_b_ns 534 .WithTaggedFlag("DMAWFP is periph bit set", 15) // It the transfer type is driven by the peripheral 535 .WithReservedBits(16, 5) 536 .WithTaggedFlag("Channel Non Secure", 21) 537 .WithReservedBits(22, 10); 538 539 (Registers.Channel0ProgramCounter + Id * 8).Define(Parent) 540 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => PC, name: $"Channel {Id} Program Counter"); 541 542 (Registers.Channel0SourceAddress + Id * 0x20).Define(Parent) 543 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => SourceAddress, name: $"Channel {Id} Source Address"); 544 545 (Registers.Channel0DestinationAddress + Id * 0x20).Define(Parent) 546 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => DestinationAddress, name: $"Channel {Id} Destination Address"); 547 548 (Registers.Channel0Control + Id * 0x20).Define(Parent) 549 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => ChannelControlRawValue, name: $"Channel {Id} Control"); 550 551 (Registers.Channel0LoopCounter0 + Id * 0x20).Define(Parent) 552 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => LoopCounter[0], name: $"Channel {Id} Loop Counter 0"); 553 554 (Registers.Channel0LoopCounter1 + Id * 0x20).Define(Parent) 555 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => LoopCounter[1], name: $"Channel {Id} Loop Counter 1"); 556 } 557 558 public ulong PC { get; set; } 559 560 public uint SourceAddress { get; set; } 561 public uint DestinationAddress { get; set; } 562 public uint ChannelControlRawValue 563 { 564 get => channelControlRawValue; 565 set 566 { 567 channelControlRawValue = value; 568 569 SourceIncrementingAddress = BitHelper.GetValue(value, 0, 1) == 1; 570 SourceReadSize = 1 << (int)BitHelper.GetValue(value, 1, 3); 571 SourceBurstLength = (int)BitHelper.GetValue(value, 4, 4) + 1; 572 573 DestinationIncrementingAddress = BitHelper.GetValue(value, 14, 1) == 1; 574 DestinationWriteSize = 1 << (int)BitHelper.GetValue(value, 15, 3); 575 DestinationBurstLength = (int)BitHelper.GetValue(value, 18, 4) + 1; 576 577 EndianSwapSize = 1 << (int)BitHelper.GetValue(value, 28, 3); 578 } 579 } 580 SignalChannelAbort(Channel.ChannelFaultReason reason)581 public void SignalChannelAbort(Channel.ChannelFaultReason reason) 582 { 583 Parent.Log(LogLevel.Error, "Channel {0} is aborting because of {1}", Id, reason.ToString()); 584 // Changing status will automatically set Abort IRQ 585 this.Status = Channel.ChannelStatus.Faulting; 586 this.faultReason = reason; 587 } 588 589 public ChannelStatus Status 590 { 591 get => status; 592 set 593 { 594 status = value; 595 if(status != Channel.ChannelStatus.Faulting) 596 { 597 // If we are not Faulting then clear Fault Reason 598 // since it could be set previously, when the channel aborted 599 this.faultReason = ChannelFaultReason.NoFault; 600 } 601 Parent.UpdateAbortInterrupt(); 602 } 603 } 604 605 // RequestType is part of Peripheral Request Interface (is set by `DMAWFP`) 606 public ChannelRequestType RequestType { get; set; } 607 // Whether it's a last request - this is set in peripheral transfers only, in infinite loop transfers 608 public bool RequestLast { get; set; } 609 610 // Sizes are specified in bytes 611 public int SourceReadSize { get; private set; } 612 public int DestinationWriteSize { get; private set; } 613 614 public int SourceBurstLength { get; private set; } 615 public int DestinationBurstLength { get; private set; } 616 617 public bool SourceIncrementingAddress { get; private set; } 618 public bool DestinationIncrementingAddress { get; private set; } 619 620 // In bytes 621 public int EndianSwapSize { get; private set; } 622 623 // What event is the channel waiting for (after calling DMAWFE) 624 // We don't care about clearing this, since it only has meaning when the channel is in WaitingForEvent state 625 public uint WaitingEventOrPeripheralNumber { get; set; } 626 // Peripheral bound to the channel 627 public int? Peripheral { get; set; } 628 629 public readonly int Id; 630 public readonly byte[] LoopCounter = new byte[2]; 631 public readonly Queue<byte> localMFIFO = new Queue<byte>(); 632 633 private ChannelStatus status; 634 private ChannelFaultReason faultReason; 635 private uint channelControlRawValue; 636 private readonly PL330_DMA Parent; 637 638 public enum ChannelStatus 639 { 640 // The documentation enumerates more states, but let's reduce this number for now 641 // for the implementation to be more manageable. 642 // Also, some states have no meaning for us, as they are related to the operation of the bus 643 Stopped = 0b0000, 644 Executing = 0b0001, 645 WaitingForEvent = 0b0100, 646 WaitingForPeripheral = 0b0111, 647 Faulting = 0b1111, 648 } 649 650 [Flags] 651 public enum ChannelFaultReason 652 { 653 NoFault = 0, 654 UndefinedInstruction = 1 << 0, // undef_instr 655 InvalidOperand = 1 << 1, // operand_invalid 656 EventInvalidSecurityState = 1 << 5, // ch_evnt_err 657 PeripheralInvalidSecurityState = 1 << 6, // ch_periph_err 658 ChannelControlRegisterManipulationInvalidSecurityState = 1 << 7, // ch_rdwr_err 659 OutOfSpaceInMFIFO = 1 << 12, // mfifo_err 660 NotEnoughStoredDataInMFIFO = 1 << 13, // st_data_unavailable 661 InstructionFetchError = 1 << 16, // instr_fetch_err 662 DataWriteError = 1 << 17, // data_write_err 663 DataReadError = 1 << 18, // data_read_err 664 OriginatesFromDebugInstruction = 1 << 30, // dbg_instr 665 LockupError = 1 << 31, // lockup_err 666 } 667 668 public enum ChannelRequestType 669 { 670 Single = 0, 671 Burst = 1 672 } 673 } 674 675 private enum DMAThreadType 676 { 677 Manager = 0, 678 Channel = 1 679 } 680 681 private enum Registers : long 682 { 683 DmaManagerStatus = 0x0, 684 DmaProgramCounter = 0x4, 685 DmaInterruptEnable = 0x20, 686 DmaEventInterruptRawStatus = 0x24, 687 DmaInterruptStatus = 0x28, 688 DmaInterruptClear = 0x2C, 689 FaultStatusDmaManager = 0x30, 690 FaultStatusDmaChannel = 0x34, 691 FaultTypeDmaManager = 0x38, 692 693 Channel0FaultType = 0x40, 694 Channel1FaultType = 0x44, 695 Channel2FaultType = 0x48, 696 Channel3FaultType = 0x4C, 697 Channel4FaultType = 0x50, 698 Channel5FaultType = 0x54, 699 Channel6FaultType = 0x58, 700 Channel7FaultType = 0x5C, 701 702 Channel0Status = 0x100, 703 Channel1Status = 0x108, 704 Channel2Status = 0x110, 705 Channel3Status = 0x118, 706 Channel4Status = 0x120, 707 Channel5Status = 0x128, 708 Channel6Status = 0x130, 709 Channel7Status = 0x138, 710 711 Channel0ProgramCounter = 0x104, 712 Channel1ProgramCounter = 0x10C, 713 Channel2ProgramCounter = 0x114, 714 Channel3ProgramCounter = 0x11C, 715 Channel4ProgramCounter = 0x124, 716 Channel5ProgramCounter = 0x12C, 717 Channel6ProgramCounter = 0x134, 718 Channel7ProgramCounter = 0x13C, 719 720 Channel0SourceAddress = 0x400, 721 Channel1SourceAddress = 0x420, 722 Channel2SourceAddress = 0x440, 723 Channel3SourceAddress = 0x460, 724 Channel4SourceAddress = 0x480, 725 Channel5SourceAddress = 0x4A0, 726 Channel6SourceAddress = 0x4C0, 727 Channel7SourceAddress = 0x4E0, 728 729 Channel0DestinationAddress = 0x404, 730 Channel1DestinationAddress = 0x424, 731 Channel2DestinationAddress = 0x444, 732 Channel3DestinationAddress = 0x464, 733 Channel4DestinationAddress = 0x484, 734 Channel5DestinationAddress = 0x4A4, 735 Channel6DestinationAddress = 0x4C4, 736 Channel7DestinationAddress = 0x4E4, 737 738 Channel0Control = 0x408, 739 Channel1Control = 0x428, 740 Channel2Control = 0x448, 741 Channel3Control = 0x468, 742 Channel4Control = 0x488, 743 Channel5Control = 0x4A8, 744 Channel6Control = 0x4C8, 745 Channel7Control = 0x4E8, 746 747 Channel0LoopCounter0 = 0x40C, 748 Channel1LoopCounter0 = 0x42C, 749 Channel2LoopCounter0 = 0x44C, 750 Channel3LoopCounter0 = 0x46C, 751 Channel4LoopCounter0 = 0x48C, 752 Channel5LoopCounter0 = 0x4AC, 753 Channel6LoopCounter0 = 0x4CC, 754 Channel7LoopCounter0 = 0x4EC, 755 756 Channel0LoopCounter1 = 0x410, 757 Channel1LoopCounter1 = 0x430, 758 Channel2LoopCounter1 = 0x450, 759 Channel3LoopCounter1 = 0x470, 760 Channel4LoopCounter1 = 0x490, 761 Channel5LoopCounter1 = 0x4B0, 762 Channel6LoopCounter1 = 0x4D0, 763 Channel7LoopCounter1 = 0x4F0, 764 765 DebugStatus = 0xD00, 766 DebugCommand = 0xD04, 767 DebugInstruction0 = 0xD08, 768 DebugInstruction1 = 0xD0C, 769 770 Configuration0 = 0xE00, 771 Configuration1 = 0xE04, 772 Configuration2 = 0xE08, 773 Configuration3 = 0xE0C, 774 Configuration4 = 0xE10, 775 DmaConfiguration = 0xE14, 776 // The watchdog is used to detect lock-ups - when there are not enough resources (MFIFO space) to complete a transfer. 777 // It can be a source of abort IRQ. For now, let's leave it unimplemented (2.8.3 Watchdog abort) 778 Watchdog = 0xE80, 779 780 PeripheralIdentification0 = 0xFE0, 781 PeripheralIdentification1 = 0xFE4, 782 PeripheralIdentification2 = 0xFE8, 783 PeripheralIdentification3 = 0xFEC, 784 ComponentIdentification0 = 0xFF0, 785 ComponentIdentification1 = 0xFF4, 786 ComponentIdentification2 = 0xFF8, 787 ComponentIdentification3 = 0xFFC, 788 } 789 } 790 } 791