1 // 2 // Copyright (c) 2010-2025 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.Collections.Generic; 9 using System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals; 14 using Antmicro.Renode.Peripherals.Bus; 15 using Antmicro.Renode.Utilities; 16 17 namespace Antmicro.Renode.Peripherals.DMA 18 { 19 public interface ISamPdcPeripheral : IProvidesRegisterCollection<DoubleWordRegisterCollection>, IBusPeripheral 20 { 21 TransferType DmaReadAccessWidth { get; } 22 TransferType DmaWriteAccessWidth { get; } 23 } 24 25 public interface ISamPdcBytePeripheral : ISamPdcPeripheral 26 { DmaByteRead()27 byte? DmaByteRead(); DmaByteWrite(byte data)28 void DmaByteWrite(byte data); 29 } 30 31 public interface ISamPdcBlockBytePeripheral : ISamPdcPeripheral 32 { DmaBlockByteRead(int count)33 byte[] DmaBlockByteRead(int count); DmaBlockByteWrite(byte[] data)34 void DmaBlockByteWrite(byte[] data); 35 } 36 37 public interface ISamPdcWordPeripheral : ISamPdcPeripheral 38 { DmaWordRead()39 ushort? DmaWordRead(); DmaWordWrite(ushort data)40 void DmaWordWrite(ushort data); 41 } 42 43 public interface ISamPdcDoubleWordPeripheral : ISamPdcPeripheral 44 { DmaDoubleWordRead()45 uint? DmaDoubleWordRead(); DmaDoubleWordWrite(uint data)46 void DmaDoubleWordWrite(uint data); 47 } 48 49 public interface ISamPdcQuadWordPeripheral : ISamPdcPeripheral 50 { DmaQuadWordRead()51 ulong? DmaQuadWordRead(); DmaQuadWordWrite(ulong data)52 void DmaQuadWordWrite(ulong data); 53 } 54 55 public class SAM_PDC 56 { SAM_PDC(IMachine machine, ISamPdcPeripheral parent, long registersOffset, Action flagsChangedCallback, bool receiverEnabled = true, bool transmitterEnabled = true)57 public SAM_PDC(IMachine machine, ISamPdcPeripheral parent, long registersOffset, Action flagsChangedCallback, bool receiverEnabled = true, bool transmitterEnabled = true) 58 { 59 this.machine = machine; 60 this.parent = parent; 61 this.receiverEnabled = receiverEnabled; 62 this.transmitterEnabled = transmitterEnabled; 63 FlagsChanged = flagsChangedCallback; 64 engine = new DmaEngine(machine.GetSystemBus(parent)); 65 receiverBuffer = new List<byte>(); 66 DefineRegisters(registersOffset); 67 Reset(); 68 } 69 Reset()70 public void Reset() 71 { 72 RxBufferFull = true; 73 TxBufferEmpty = true; 74 EndOfRxBuffer = false; 75 EndOfTxBuffer = false; 76 receiverBuffer.Clear(); 77 transmitterBuffer = null; 78 transmitterBufferOffset = 0; 79 InvokeFlagsChanged(); 80 } 81 DefineRegisters(long offset)82 public void DefineRegisters(long offset) 83 { 84 ((Registers)((long)Registers.ReceivePointer + offset)).Define(parent) 85 .WithValueField(0, 32, out receivePointer, name: "RXPTR") 86 ; 87 88 ((Registers)((long)Registers.ReceiveCounter + offset)).Define(parent) 89 .WithValueField(0, 16, out receiveCounter, name: "RXCTR") 90 .WithReservedBits(16, 16) 91 .WithWriteCallback((_, __) => 92 { 93 RxBufferFull = receiveCounter.Value == 0; 94 EndOfRxBuffer = false; 95 InvokeFlagsChanged(); 96 }) 97 ; 98 99 ((Registers)((long)Registers.TransmitPointer + offset)).Define(parent) 100 .WithValueField(0, 32, out transmitPointer, name: "TXPTR") 101 ; 102 103 ((Registers)((long)Registers.TransmitCounter + offset)).Define(parent) 104 .WithValueField(0, 16, out transmitCounter, name: "TXCTR") 105 .WithReservedBits(16, 16) 106 .WithWriteCallback((_, __) => 107 { 108 TxBufferEmpty = transmitCounter.Value == 0; 109 EndOfTxBuffer = false; 110 InvokeFlagsChanged(); 111 }) 112 ; 113 114 ((Registers)((long)Registers.ReceiveNextPointer + offset)).Define(parent) 115 .WithValueField(0, 32, out receiveNextPointer, name: "RXNPTR") 116 ; 117 118 ((Registers)((long)Registers.ReceiveNextCounter + offset)).Define(parent) 119 .WithValueField(0, 16, out receiveNextCounter, name: "RXNCTR") 120 .WithReservedBits(16, 16) 121 .WithWriteCallback((_, __) => 122 { 123 EndOfRxBuffer = false; 124 InvokeFlagsChanged(); 125 }) 126 ; 127 128 ((Registers)((long)Registers.TransmitNextPointer + offset)).Define(parent) 129 .WithValueField(0, 32, out transmitNextPointer, name: "TXNPTR") 130 ; 131 132 ((Registers)((long)Registers.TransmitNextCounter + offset)).Define(parent) 133 .WithValueField(0, 16, out transmitNextCounter, name: "TXNCTR") 134 .WithReservedBits(16, 16) 135 .WithWriteCallback((_, __) => 136 { 137 EndOfTxBuffer = false; 138 InvokeFlagsChanged(); 139 }) 140 ; 141 142 ((Registers)((long)Registers.TransferControl + offset)).Define(parent) 143 .If(receiverEnabled) 144 .Then(reg => reg 145 .WithFlag(0, out receiverTransferEnabled, FieldMode.Set, name: "RXTEN") 146 .WithFlag(1, FieldMode.WriteOneToClear, writeCallback: (_, value) => { if(value) receiverTransferEnabled.Value = false; }, name: "RXTDIS") 147 .WithWriteCallback((_, __) => TriggerReceiver()) 148 ) 149 .Else(reg => reg 150 .WithReservedBits(0, 2) 151 ) 152 .WithReservedBits(2, 6) 153 .If(transmitterEnabled) 154 .Then(reg => reg 155 .WithFlag(8, out transmitterTransferEnabled, FieldMode.Set, name: "TXTEN") 156 .WithFlag(9, FieldMode.WriteOneToClear, writeCallback: (_, value) => { if(value) transmitterTransferEnabled.Value = false; }, name: "TXTDIS") 157 .WithWriteCallback((_, __) => 158 { 159 TriggerTransmitter(); 160 InvokeFlagsChanged(); 161 }) 162 ) 163 .Else(reg => reg 164 .WithReservedBits(8, 2) 165 ) 166 .WithReservedBits(10, 22) 167 .WithWriteCallback((_, __) => InvokeFlagsChanged()) 168 ; 169 170 ((Registers)((long)Registers.TransferStatus + offset)).Define(parent) 171 .If(receiverEnabled) 172 .Then(reg => reg 173 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => receiverTransferEnabled.Value, name: "RXTEN") 174 ) 175 .Else(reg => reg 176 .WithReservedBits(0, 1) 177 ) 178 .WithReservedBits(1, 7) 179 .If(transmitterEnabled) 180 .Then(reg => reg 181 .WithFlag(8, FieldMode.Read, valueProviderCallback: _ => transmitterTransferEnabled.Value, name: "TXTEN") 182 ) 183 .Else(reg => reg 184 .WithReservedBits(8, 1) 185 ) 186 .WithReservedBits(9, 23) 187 ; 188 } 189 TriggerReceiver()190 public void TriggerReceiver() 191 { 192 if(!receiverEnabled) 193 { 194 return; 195 } 196 197 if(receiveCounter.Value == 0) 198 { 199 parent.NoisyLog("Receiver triggered, but buffer is not available"); 200 receiverTransferEnabled.Value = false; 201 EndOfRxBuffer = true; 202 InvokeFlagsChanged(); 203 return; 204 } 205 206 if(!receiverTransferEnabled.Value) 207 { 208 return; 209 } 210 parent.NoisyLog("Receiver triggered"); 211 212 TriggerReceiverInner(); 213 214 if(receiveCounter.Value == 0) 215 { 216 EndOfRxBuffer = true; 217 if(receiveNextCounter.Value == 0) 218 { 219 receiverTransferEnabled.Value = false; 220 RxBufferFull = true; 221 InvokeFlagsChanged(); 222 return; 223 } 224 225 receivePointer.Value = receiveNextPointer.Value; 226 receiveNextPointer.Value = 0x0; 227 receiveCounter.Value = receiveNextCounter.Value; 228 receiveNextCounter.Value = 0; 229 TriggerReceiverInner(); 230 } 231 InvokeFlagsChanged(); 232 } 233 234 public bool RxBufferFull 235 { 236 get => rxBufferFull; 237 private set 238 { 239 flagsChanged |= rxBufferFull != value; 240 rxBufferFull = value; 241 } 242 } 243 244 public bool TxBufferEmpty 245 { 246 get => txBufferEmpty; 247 private set 248 { 249 flagsChanged |= txBufferEmpty != value; 250 txBufferEmpty = value; 251 } 252 } 253 254 public bool EndOfRxBuffer 255 { 256 get => endOfRxBuffer; 257 private set 258 { 259 flagsChanged |= endOfRxBuffer != value; 260 endOfRxBuffer = value; 261 } 262 } 263 264 public bool EndOfTxBuffer 265 { 266 get => endOfTxBuffer; 267 private set 268 { 269 flagsChanged |= endOfTxBuffer != value; 270 endOfTxBuffer = value; 271 } 272 } 273 FinalizeReceiverTransfer()274 private void FinalizeReceiverTransfer() 275 { 276 if(receiverBuffer.Count == 0) 277 { 278 return; 279 } 280 var transferType = parent.DmaReadAccessWidth; 281 282 var request = new Request( 283 new Place(receiverBuffer.ToArray(), 0), 284 new Place(receivePointer.Value), 285 receiverBuffer.Count, 286 transferType, 287 transferType 288 ); 289 290 parent.DebugLog("Executing receiver transfer to 0x{0:X} of {1} bytes", receivePointer.Value, receiverBuffer.Count); 291 engine.IssueCopy(request); 292 receiverBuffer.Clear(); 293 } 294 TriggerTransmitter()295 private void TriggerTransmitter() 296 { 297 if(!transmitterEnabled) 298 { 299 return; 300 } 301 302 if(transmitCounter.Value == 0) 303 { 304 parent.NoisyLog("Transmitter triggered, but buffer is not available"); 305 transmitterTransferEnabled.Value = false; 306 EndOfTxBuffer = true; 307 return; 308 } 309 310 if(!transmitterTransferEnabled.Value) 311 { 312 return; 313 } 314 parent.NoisyLog("Transmitter triggered"); 315 316 TriggerTransmitterInner(); 317 318 if(transmitCounter.Value == 0) 319 { 320 EndOfTxBuffer = true; 321 if(transmitNextCounter.Value == 0) 322 { 323 transmitterTransferEnabled.Value = false; 324 TxBufferEmpty = true; 325 InvokeFlagsChanged(); 326 return; 327 } 328 329 transmitPointer.Value = transmitNextPointer.Value; 330 transmitNextPointer.Value = 0x0; 331 transmitCounter.Value = transmitNextCounter.Value; 332 transmitNextCounter.Value = 0; 333 TriggerTransmitterInner(); 334 } 335 } 336 StartTransmitterTransfer()337 private void StartTransmitterTransfer() 338 { 339 var transferType = parent.DmaWriteAccessWidth; 340 transmitterBuffer = new byte[(int)transmitCounter.Value * (int)transferType]; 341 transmitterBufferOffset = 0; 342 var request = new Request( 343 new Place(transmitPointer.Value), 344 new Place(transmitterBuffer, 0), 345 (int)transmitCounter.Value, 346 transferType, 347 transferType 348 ); 349 350 parent.DebugLog("Executing transmitter transfer from 0x{0:X} of {1} bytes", transmitPointer.Value, transmitterBuffer.Length); 351 engine.IssueCopy(request); 352 } 353 TriggerReceiverInner()354 private void TriggerReceiverInner() 355 { 356 // Block accesses are preferred, but are optional 357 if(TryBlockRead((int)receiveCounter.Value)) 358 { 359 receiveCounter.Value = 0; 360 } 361 362 for(; receiveCounter.Value > 0; receiveCounter.Value -= 1) 363 { 364 if(!TryRead()) 365 { 366 break; 367 } 368 } 369 370 if(receiveCounter.Value == 0) 371 { 372 FinalizeReceiverTransfer(); 373 } 374 } 375 TriggerTransmitterInner()376 private void TriggerTransmitterInner() 377 { 378 if(transmitterBuffer == null) 379 { 380 StartTransmitterTransfer(); 381 } 382 383 // Block accesses are preferred, but are optional 384 if(TryBlockWrite((int)transmitCounter.Value)) 385 { 386 transmitCounter.Value = 0; 387 return; 388 } 389 390 for(; transmitCounter.Value > 0; transmitCounter.Value -= 1) 391 { 392 Write(); 393 } 394 } 395 TryBlockWrite(int count)396 private bool TryBlockWrite(int count) 397 { 398 var buffer = transmitterBuffer.Skip(transmitterBufferOffset); 399 var transferType = parent.DmaWriteAccessWidth; 400 401 switch(transferType) 402 { 403 case TransferType.Byte: 404 if(parent is ISamPdcBlockBytePeripheral bytePeripheral) 405 { 406 bytePeripheral.DmaBlockByteWrite(buffer.Take(count).ToArray()); 407 break; 408 } 409 return false; 410 case TransferType.Word: 411 case TransferType.DoubleWord: 412 case TransferType.QuadWord: 413 // Not implemented 414 return false; 415 default: 416 throw new Exception("Unreachable"); 417 } 418 419 transmitterBufferOffset += (int)transferType * count; 420 if(transmitterBufferOffset == transmitterBuffer.Length) 421 { 422 transmitterBuffer = null; 423 } 424 return true; 425 } 426 Write()427 private void Write() 428 { 429 var transferType = parent.DmaWriteAccessWidth; 430 switch(transferType) 431 { 432 case TransferType.Byte: 433 if(parent is ISamPdcBytePeripheral bytePeripheral) 434 { 435 bytePeripheral.DmaByteWrite(transmitterBuffer[transmitterBufferOffset]); 436 break; 437 } 438 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcBytePeripheral, but Byte transfer was selected"); 439 return; 440 case TransferType.Word: 441 if(parent is ISamPdcWordPeripheral wordPeripheral) 442 { 443 wordPeripheral.DmaWordWrite(BitConverter.ToUInt16(transmitterBuffer, transmitterBufferOffset)); 444 break; 445 } 446 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcWordPeripheral, but Word transfer was selected"); 447 return; 448 case TransferType.DoubleWord: 449 if(parent is ISamPdcDoubleWordPeripheral doubleWordPeripheral) 450 { 451 doubleWordPeripheral.DmaDoubleWordWrite(BitConverter.ToUInt32(transmitterBuffer, transmitterBufferOffset)); 452 break; 453 } 454 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcDoubleWordPeripheral, but DoubleWord transfer was selected"); 455 return; 456 case TransferType.QuadWord: 457 if(parent is ISamPdcQuadWordPeripheral quadWordPeripheral) 458 { 459 quadWordPeripheral.DmaQuadWordWrite(BitConverter.ToUInt64(transmitterBuffer, transmitterBufferOffset)); 460 break; 461 } 462 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcQuadWordPeripheral, but QuadWord transfer was selected"); 463 return; 464 default: 465 throw new Exception("Unreachable"); 466 } 467 transmitterBufferOffset += (int)transferType; 468 if(transmitterBufferOffset == transmitterBuffer.Length) 469 { 470 transmitterBuffer = null; 471 } 472 } 473 TryBlockRead(int count)474 private bool TryBlockRead(int count) 475 { 476 byte[] data = null; 477 478 switch(parent.DmaReadAccessWidth) 479 { 480 case TransferType.Byte: 481 if(parent is ISamPdcBlockBytePeripheral bytePeripheral) 482 { 483 data = bytePeripheral.DmaBlockByteRead(count); 484 break; 485 } 486 return false; 487 case TransferType.Word: 488 case TransferType.DoubleWord: 489 case TransferType.QuadWord: 490 // Not implemented 491 return false; 492 default: 493 throw new Exception("Unreachable"); 494 } 495 496 if(data == null) 497 { 498 return false; 499 } 500 501 var paddedData = data.Concat(Enumerable.Repeat((byte)0x0, count)).Take(count); 502 receiverBuffer.AddRange(paddedData); 503 return true; 504 } 505 TryRead()506 private bool TryRead() 507 { 508 byte[] data = null; 509 510 switch(parent.DmaReadAccessWidth) 511 { 512 case TransferType.Byte: 513 if(parent is ISamPdcBytePeripheral bytePeripheral) 514 { 515 data = bytePeripheral.DmaByteRead()?.AsRawBytes(); 516 break; 517 } 518 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcBytePeripheral, but Byte transfer was selected"); 519 return false; 520 case TransferType.Word: 521 if(parent is ISamPdcWordPeripheral wordPeripheral) 522 { 523 data = wordPeripheral.DmaWordRead()?.AsRawBytes(); 524 break; 525 } 526 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcWordPeripheral, but Word transfer was selected"); 527 return false; 528 case TransferType.DoubleWord: 529 if(parent is ISamPdcDoubleWordPeripheral doubleWordPeripheral) 530 { 531 data = doubleWordPeripheral.DmaDoubleWordRead()?.AsRawBytes(); 532 break; 533 } 534 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcDoubleWordPeripheral, but DoubleWord transfer was selected"); 535 return false; 536 case TransferType.QuadWord: 537 if(parent is ISamPdcQuadWordPeripheral quadWordPeripheral) 538 { 539 data = quadWordPeripheral.DmaQuadWordRead()?.AsRawBytes(); 540 break; 541 } 542 parent.ErrorLog("Perent peripheral doesn't implement ISamPdcQuadWordPeripheral, but QuadWord transfer was selected"); 543 return false; 544 default: 545 throw new Exception("Unreachable"); 546 } 547 548 if(data == null) 549 { 550 return false; 551 } 552 553 receiverBuffer.AddRange(data); 554 return true; 555 } 556 InvokeFlagsChanged()557 private void InvokeFlagsChanged() 558 { 559 if(flagsChanged) 560 { 561 FlagsChanged?.Invoke(); 562 flagsChanged = false; 563 } 564 } 565 566 private Action FlagsChanged { get; } 567 568 private bool flagsChanged; 569 private bool rxBufferFull; 570 private bool txBufferEmpty; 571 private bool endOfRxBuffer; 572 private bool endOfTxBuffer; 573 private IValueRegisterField receivePointer; 574 private IValueRegisterField receiveCounter; 575 private IValueRegisterField transmitPointer; 576 private IValueRegisterField transmitCounter; 577 private IValueRegisterField receiveNextPointer; 578 private IValueRegisterField receiveNextCounter; 579 private IValueRegisterField transmitNextPointer; 580 private IValueRegisterField transmitNextCounter; 581 private IFlagRegisterField receiverTransferEnabled; 582 private IFlagRegisterField transmitterTransferEnabled; 583 584 private byte[] transmitterBuffer; 585 private int transmitterBufferOffset; 586 587 private readonly IMachine machine; 588 private readonly ISamPdcPeripheral parent; 589 private readonly DmaEngine engine; 590 private readonly List<byte> receiverBuffer; 591 private readonly bool receiverEnabled; 592 private readonly bool transmitterEnabled; 593 594 public enum Registers 595 { 596 ReceivePointer = 0x00, 597 ReceiveCounter = 0x04, 598 TransmitPointer = 0x08, 599 TransmitCounter = 0x0C, 600 ReceiveNextPointer = 0x10, 601 ReceiveNextCounter = 0x14, 602 TransmitNextPointer = 0x18, 603 TransmitNextCounter = 0x1C, 604 TransferControl = 0x20, 605 TransferStatus = 0x24, 606 } 607 } 608 }