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.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.Bus; 14 using Antmicro.Renode.Peripherals.CPU; 15 16 namespace Antmicro.Renode.Peripherals.DMA 17 { 18 // Currently only memory to memory transfers are supported. 19 public class RenesasRZG_DMAC : IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize, INumberedGPIOOutput 20 { RenesasRZG_DMAC(IMachine machine)21 public RenesasRZG_DMAC(IMachine machine) 22 { 23 sysbus = machine.GetSystemBus(this); 24 ErrorIRQ = new GPIO(); 25 channels = new Channel[ChannelCount]; 26 RegistersCollection = new DoubleWordRegisterCollection(this, DefineRegisters()); 27 Connections = channels 28 .Select((channel, i) => new { Key = i, Value = (IGPIO)channel.IRQ }) 29 .ToDictionary(x => x.Key, x => x.Value); 30 } 31 Reset()32 public void Reset() 33 { 34 RegistersCollection.Reset(); 35 ErrorIRQ.Unset(); 36 foreach(var channel in channels) 37 { 38 channel.Reset(); 39 } 40 } 41 ReadDoubleWord(long offset)42 public uint ReadDoubleWord(long offset) 43 { 44 return RegistersCollection.Read(offset); 45 } 46 WriteDoubleWord(long offset, uint value)47 public void WriteDoubleWord(long offset, uint value) 48 { 49 RegistersCollection.Write(offset, value); 50 } 51 52 public IReadOnlyDictionary<int, IGPIO> Connections { get; } 53 public DoubleWordRegisterCollection RegistersCollection { get; } 54 public long Size => 0x1000; 55 public GPIO ErrorIRQ { get; } 56 DefineRegisters()57 private Dictionary<long, DoubleWordRegister> DefineRegisters() 58 { 59 var registerMap = new Dictionary<long, DoubleWordRegister>(); 60 for(var block = 0; block < BlockCount; block++) 61 { 62 var blockIdx = block; 63 for(var channelIdx = 0; channelIdx < ChannelsPerBlock; channelIdx++) 64 { 65 var absoluteIdx = ChannelsPerBlock * block + channelIdx; 66 var channel = new Channel(this, absoluteIdx); 67 channels[absoluteIdx] = channel; 68 69 var registersOffset = 0x400 * block + 0x40 * channelIdx; 70 foreach(var registerEntry in channel.DefineRegisters()) 71 { 72 registerMap.Add(registersOffset + registerEntry.Key, registerEntry.Value); 73 } 74 } 75 76 var baseOffset = 0x400 * block + 0x300; 77 registerMap.Add(baseOffset + (long)ControlRegisters.Control, new DoubleWordRegister(this) 78 .WithTaggedFlag("PR (Transfer Priority)", 0) 79 .WithTaggedFlag("LVINT (Interrupt Level)", 1) 80 .WithReservedBits(2, 14) 81 .WithTag("LDPR (Link Descriptor Protection)", 16, 3) 82 .WithReservedBits(19, 1) 83 .WithTag("LDCA (Link Descriptor Cache)", 20, 4) 84 .WithTag("LWPR (Link WriteBack Protection)", 24, 3) 85 .WithReservedBits(27, 1) 86 .WithTag("LWCA (Link WriteBack Cache)", 28, 4)); 87 88 registerMap.Add(baseOffset + (long)ControlRegisters.StatusEnabled, new DoubleWordRegister(this) 89 .WithFlags(0, ChannelsPerBlock, FieldMode.Read, name: "EN", 90 valueProviderCallback: (i, _) => channels[ChannelsPerBlock * blockIdx + i].Enabled) 91 .WithReservedBits(ChannelsPerBlock, 32 - ChannelsPerBlock)); 92 93 registerMap.Add(baseOffset + (long)ControlRegisters.StatusError, new DoubleWordRegister(this) 94 .WithFlags(0, ChannelsPerBlock, FieldMode.Read, name: "ER", 95 valueProviderCallback: (i, _) => channels[ChannelsPerBlock * blockIdx + i].Error) 96 .WithReservedBits(ChannelsPerBlock, 32 - ChannelsPerBlock)); 97 98 registerMap.Add(baseOffset + (long)ControlRegisters.StatusInterrupted, new DoubleWordRegister(this) 99 .WithFlags(0, ChannelsPerBlock, FieldMode.Read, name: "END", 100 valueProviderCallback: (i, _) => channels[ChannelsPerBlock * blockIdx + i].EndInterrupt) 101 .WithReservedBits(ChannelsPerBlock, 32 - ChannelsPerBlock)); 102 103 registerMap.Add(baseOffset + (long)ControlRegisters.StatusTerminalCount, new DoubleWordRegister(this) 104 .WithFlags(0, ChannelsPerBlock, FieldMode.Read, name: "TC", 105 valueProviderCallback: (i, _) => channels[ChannelsPerBlock * blockIdx + i].TerminalCount) 106 .WithReservedBits(ChannelsPerBlock, 32 - ChannelsPerBlock)); 107 108 registerMap.Add(baseOffset + (long)ControlRegisters.StatusSuspend, new DoubleWordRegister(this) 109 .WithTaggedFlags("SUS", 0, ChannelsPerBlock) 110 .WithReservedBits(ChannelsPerBlock, 32 - ChannelsPerBlock)); 111 } 112 return registerMap; 113 } 114 UpdateErrorInterrupt()115 private void UpdateErrorInterrupt() 116 { 117 var status = channels.Where(x => x.Error).Any(); 118 this.DebugLog("ErrorIRQ: {0}", status ? "Set" : "Unset"); 119 ErrorIRQ.Set(status); 120 } 121 122 private readonly IBusController sysbus; 123 private readonly Channel[] channels; 124 125 private const int BlockCount = 2; 126 private const int ChannelsPerBlock = 8; 127 private const int ChannelCount = BlockCount * ChannelsPerBlock; 128 129 private enum ControlRegisters 130 { 131 Control = 0x00, // DCTRL_n_m/n_mS 132 StatusEnabled = 0x10, // DCTRL_n_m/n_mS 133 StatusError = 0x14, // DSTAT_ER_n_m/n_mS 134 StatusInterrupted = 0x18, // DSTAT_END_n_m/n_mS 135 StatusTerminalCount = 0x1C, // DSTAT_TC_n_m/n_mS 136 StatusSuspend = 0x20, // DSTAT_SUS_n_m/n_mS 137 } 138 139 private class Channel 140 { Channel(RenesasRZG_DMAC parent, int channelId)141 public Channel(RenesasRZG_DMAC parent, int channelId) 142 { 143 this.parent = parent; 144 IRQ = new GPIO(); 145 logPrefix = $"DMA Channel {channelId}"; 146 dma = new DmaEngine(parent.sysbus); 147 nextSourceAddresses = new IValueRegisterField[AddressRegisterBanks]; 148 nextDestinationAddresses = new IValueRegisterField[AddressRegisterBanks]; 149 nextTransactionBytes = new IValueRegisterField[AddressRegisterBanks]; 150 151 Reset(); 152 } 153 Reset()154 public void Reset() 155 { 156 IRQ.Unset(); 157 currentSourceAddress = 0; 158 currentDestinationAddress = 0; 159 currentTransactionByte = 0; 160 sourceTransferType = TransferType.Byte; 161 destinationTransferType = TransferType.Byte; 162 requestingCpu = null; 163 } 164 DefineRegisters()165 public Dictionary<long, DoubleWordRegister> DefineRegisters() 166 { 167 var registers = new Dictionary<long, DoubleWordRegister>(); 168 169 registers.Add((long)Registers.Next0SourceAddress, new DoubleWordRegister(parent) 170 .WithValueField(0, 32, out nextSourceAddresses[0], name: "SA (Next Source Address 0)")); 171 172 registers.Add((long)Registers.Next0DestinationAddress, new DoubleWordRegister(parent) 173 .WithValueField(0, 32, out nextDestinationAddresses[0], name: "DA (Next Destination Address 0)")); 174 175 registers.Add((long)Registers.Next0TransactionByte, new DoubleWordRegister(parent) 176 .WithValueField(0, 32, out nextTransactionBytes[0], name: "TB (Next Transaction Byte 0)")); 177 178 registers.Add((long)Registers.Next1SourceAddress, new DoubleWordRegister(parent) 179 .WithValueField(0, 32, out nextSourceAddresses[1], name: "SA (Next Source Address 1)")); 180 181 registers.Add((long)Registers.Next1DestinationAddress, new DoubleWordRegister(parent) 182 .WithValueField(0, 32, out nextDestinationAddresses[1], name: "DA (Next Destination Address 1)")); 183 184 registers.Add((long)Registers.Next1TransactionByte, new DoubleWordRegister(parent) 185 .WithValueField(0, 32, out nextTransactionBytes[1], name: "TB (Next Transaction Byte 1)")); 186 187 registers.Add((long)Registers.CurrentSourceAddress, new DoubleWordRegister(parent) 188 .WithValueField(0, 32, FieldMode.Read, name: "CRSA (Current Source Address)", 189 valueProviderCallback: _ => currentSourceAddress)); 190 191 registers.Add((long)Registers.CurrentDestinationAddress, new DoubleWordRegister(parent) 192 .WithValueField(0, 32, FieldMode.Read, name: "CRDA (Current Destination Address)", 193 valueProviderCallback: _ => currentDestinationAddress)); 194 195 registers.Add((long)Registers.CurrentTransactionByte, new DoubleWordRegister(parent) 196 .WithValueField(0, 32, FieldMode.Read, name: "CRTB (Current Transaction Byte)", 197 valueProviderCallback: _ => currentTransactionByte)); 198 199 statusRegister = new DoubleWordRegister(parent) 200 .WithFlag(0, out dmaEnabled, FieldMode.Read, name: "EN (Enable)") 201 .WithTaggedFlag("RQST (Request)", 1) 202 .WithTaggedFlag("TACT (Transaction Active)", 2) 203 .WithTaggedFlag("SUS (Suspended)", 3) 204 .WithFlag(4, out dmaError, FieldMode.Read, name: "ER (Error Bit)") 205 .WithFlag(5, out endInterrupt, FieldMode.Read, name: "END (End Interrupted)") 206 .WithFlag(6, out terminalCount, FieldMode.Read, name: "TC (Terminal Count)") 207 .WithFlag(7, FieldMode.Read, name: "SR (Selected Register Set)", 208 valueProviderCallback: _ => registerSetSelect.Value) 209 .WithTaggedFlag("DL (Descriptor Load)", 8) 210 .WithTaggedFlag("DW (Descriptor WriteBack)", 9) 211 .WithTaggedFlag("DER (Descriptor Error)", 10) 212 .WithEnumField<DoubleWordRegister, DMAMode>(11, 1, FieldMode.Read, name: "MODE (DMA Mode)", 213 valueProviderCallback: _ => dmaMode.Value) 214 .WithReservedBits(12, 4) 215 .WithTaggedFlag("INTMSK (Temporary Interrupt Mask)", 16) 216 .WithReservedBits(17, 15); 217 218 registers.Add((long)Registers.ChannelStatus, statusRegister); 219 220 registers.Add((long)Registers.ChannelControl, new DoubleWordRegister(parent) 221 .WithFlag(0, FieldMode.Write, name: "SETEN (Set Enable)", 222 writeCallback: (_, value) => 223 { 224 if(value) 225 { 226 LoadTransferParameters(); 227 } 228 }) 229 .WithFlag(1, FieldMode.Write, name: "CLREN (Clear Enable)", 230 writeCallback: (_, value) => 231 { 232 if(value) 233 { 234 dmaEnabled.Value = false; 235 } 236 }) 237 .WithFlag(2, FieldMode.Write, name: "STG (Software Trigger)", 238 writeCallback: (_, value) => 239 { 240 if(value) 241 { 242 if(!PerformTransfer()) 243 { 244 return; 245 } 246 247 // Continue the register transfer 248 LoadTransferParameters(setRequestingCpu: false); 249 if(performFullTransfer.Value) 250 { 251 // Perform the transfer again, with new parameters, if full transfer was requested 252 PerformTransfer(); 253 } 254 } 255 }) 256 .WithFlag(3, FieldMode.Write, name: "SWRST (Software Reset)", 257 writeCallback: (_, value) => 258 { 259 if(value) 260 { 261 statusRegister.Reset(); 262 } 263 }) 264 .WithTaggedFlag("CLRRQ (Clear Request bit)", 4) 265 .WithFlag(5, FieldMode.Write, name: "CLREND (Clear End bit)", 266 writeCallback: (_, value) => 267 { 268 if(value) 269 { 270 endInterrupt.Value = false; 271 } 272 }) 273 .WithFlag(6, FieldMode.Write, name: "CLRTC (Clear Terminal Count)", 274 writeCallback: (_, value) => 275 { 276 if(value) 277 { 278 terminalCount.Value = false; 279 } 280 }) 281 .WithReservedBits(7, 1) 282 .WithTaggedFlag("SETSUS (Set Suspend)", 8) 283 .WithTaggedFlag("CLRSUS (Clear Suspend)", 9) 284 .WithReservedBits(10, 6) 285 .WithTaggedFlag("SETINTMSK (Set Temporary Interrupt Mask)", 16) 286 .WithTaggedFlag("CLRINTMSK (Clear Temporary Interrupt Mask)", 17) 287 .WithReservedBits(18, 14) 288 .WithWriteCallback((_, __) => UpdateInterrupts())); 289 290 registers.Add((long)Registers.ChannelConfiguration, new DoubleWordRegister(parent) 291 .WithTag("SEL (DMAC Channel Select)", 0, 3) 292 .WithTaggedFlag("REQD (Request Direction)", 3) 293 .WithTaggedFlag("LOEN (Low Enable)", 4) 294 .WithTaggedFlag("HIEN (High Enable)", 5) 295 .WithTaggedFlag("LVL (Level)", 6) 296 .WithReservedBits(7, 1) 297 .WithTag("AM (ACK Mode)", 8, 3) 298 .WithReservedBits(11, 1) 299 .WithValueField(12, 4, name: "SDS (Source Data Size)", 300 changeCallback: (_, value) => sourceTransferType = ConvertToTransferType(value)) 301 .WithValueField(16, 4, name: "DDS (Destination Data Size)", 302 changeCallback: (_, value) => destinationTransferType = ConvertToTransferType(value)) 303 .WithFlag(20, out sourceAddressCounting, name: "SAD (Source Address Counting Direction)") 304 .WithFlag(21, out destinationAddressCounting, name: "DAD (Destination Address Counting Direction)") 305 .WithFlag(22, out performFullTransfer, name: "TM (Transfer Mode)") 306 .WithReservedBits(23, 1) 307 .WithFlag(24, out endInterruptMask, name: "DEM (DMA Transfer End Interrupt Mask)") 308 .WithReservedBits(25, 2) 309 .WithTaggedFlag("SBE (Sweep Buffer Enable)", 27) 310 .WithFlag(28, out registerSetSelect, name: "RSEL (Register Set Select)") 311 .WithFlag(29, out registerSetSwitch, name: "RSW (Register Select Switch)") 312 .WithFlag(30, out registerSetEnable, name: "REN (Register Set Enable)") 313 .WithEnumField(31, 1, out dmaMode, name: "DMS (DMA Mode Select)")); 314 315 registers.Add((long)Registers.ChannelInterval, new DoubleWordRegister(parent) 316 .WithTag("ITVL (Channel Transfer Interval)", 0, 16) 317 .WithReservedBits(16, 16)); 318 319 registers.Add((long)Registers.ChannelExtension, new DoubleWordRegister(parent) 320 .WithTag("SPR (Source Protection)", 0, 3) 321 .WithReservedBits(3, 1) 322 .WithTag("SCA (Source Cache)", 4, 4) 323 .WithTag("DPR (Destination Protection)", 8, 3) 324 .WithReservedBits(11, 1) 325 .WithTag("DCA (Destination Cache)", 12, 4) 326 .WithReservedBits(16, 16)); 327 328 registers.Add((long)Registers.NextLinkAddress, new DoubleWordRegister(parent) 329 .WithTag("NXLA (Next Link Address)", 0, 32)); 330 331 registers.Add((long)Registers.CurrentLinkAddress, new DoubleWordRegister(parent) 332 .WithTag("CRLA (Current Link Address)", 0, 32)); 333 334 return registers; 335 } 336 PerformTransfer()337 public bool PerformTransfer() 338 { 339 if(!dmaEnabled.Value) 340 { 341 parent.WarningLog("{0}: Attempted to perform trigger a transaction on a disabled DMA channel, ignoring", logPrefix); 342 return false; 343 } 344 345 Request request; 346 if(performFullTransfer.Value) 347 { 348 request = new Request( 349 new Place(currentSourceAddress), 350 new Place(currentDestinationAddress), 351 (int)currentTransactionByte, 352 sourceTransferType, destinationTransferType, 353 !sourceAddressCounting.Value, 354 !destinationAddressCounting.Value 355 ); 356 currentTransactionByte = 0; 357 } 358 else 359 { 360 var transferSize = (int)sourceTransferType; 361 request = new Request( 362 new Place(currentSourceAddress), 363 new Place(currentDestinationAddress), 364 transferSize, 365 sourceTransferType, destinationTransferType, 366 !sourceAddressCounting.Value, 367 !destinationAddressCounting.Value 368 ); 369 currentTransactionByte -= (ulong)transferSize; 370 } 371 372 var response = dma.IssueCopy(request, requestingCpu); 373 currentSourceAddress = response.ReadAddress.Value; 374 currentDestinationAddress = response.WriteAddress.Value; 375 376 if(currentTransactionByte == 0) 377 { 378 if(registerSetSwitch.Value) 379 { 380 registerSetSelect.Value = !registerSetSelect.Value; 381 } 382 383 if(registerSetEnable.Value && dmaMode.Value == DMAMode.Register) 384 { 385 registerSetEnable.Value = false; 386 // Transfer hasn't completed it will be continued from the next register set 387 return true; 388 } 389 390 dmaEnabled.Value = false; 391 terminalCount.Value = true; 392 endInterrupt.Value = terminalCount.Value && !endInterruptMask.Value; 393 endInterruptMask.Value = false; 394 UpdateInterrupts(); 395 } 396 397 return false; 398 } 399 400 public GPIO IRQ { get; } 401 public bool Enabled => dmaEnabled.Value; 402 public bool Error => dmaError.Value; 403 public bool EndInterrupt => endInterrupt.Value; 404 public bool TerminalCount => terminalCount.Value; 405 ConvertToTransferType(ulong value)406 private TransferType ConvertToTransferType(ulong value) 407 { 408 const ulong MaxTransferSize = (ulong)TransferType.QuadWord; 409 ulong transferWordSize = value + 1; 410 if(transferWordSize > MaxTransferSize) 411 { 412 parent.ErrorLog("{0}: Transfer size set to {1}, but the maximum supported transfer size is {2}. Clamping to {2}", 413 logPrefix, transferWordSize, MaxTransferSize); 414 transferWordSize = MaxTransferSize; 415 } 416 417 return (TransferType)transferWordSize; 418 } 419 LoadTransferParameters(bool setRequestingCpu = true)420 private void LoadTransferParameters(bool setRequestingCpu = true) 421 { 422 if(setRequestingCpu) 423 { 424 if(!parent.sysbus.TryGetCurrentCPU(out requestingCpu)) 425 { 426 parent.WarningLog("{0}: Could not obtain a CPU context when starting a transaction", logPrefix); 427 } 428 } 429 430 switch(dmaMode.Value) 431 { 432 case DMAMode.Register: 433 { 434 dmaEnabled.Value = true; 435 var bank = registerSetSelect.Value ? 1 : 0; 436 currentSourceAddress = nextSourceAddresses[bank].Value; 437 currentDestinationAddress = nextDestinationAddresses[bank].Value; 438 currentTransactionByte = nextTransactionBytes[bank].Value; 439 break; 440 } 441 case DMAMode.Link: 442 parent.ErrorLog("{0}: {1} DMA mode is currently not supported, triggering an error", logPrefix, nameof(DMAMode.Link)); 443 dmaEnabled.Value = false; 444 dmaError.Value = true; 445 UpdateInterrupts(); 446 return; 447 default: 448 throw new Exception("unreachable"); 449 } 450 } 451 UpdateInterrupts()452 private void UpdateInterrupts() 453 { 454 parent.DebugLog("{0}: IRQ: {1}", logPrefix, endInterrupt.Value ? "Set" : "Unset"); 455 IRQ.Set(endInterrupt.Value); 456 parent.UpdateErrorInterrupt(); 457 } 458 459 private readonly RenesasRZG_DMAC parent; 460 private readonly DmaEngine dma; 461 private readonly string logPrefix; 462 463 private readonly IValueRegisterField[] nextSourceAddresses; 464 private readonly IValueRegisterField[] nextDestinationAddresses; 465 private readonly IValueRegisterField[] nextTransactionBytes; 466 467 private DoubleWordRegister statusRegister; 468 private ulong currentSourceAddress; 469 private ulong currentDestinationAddress; 470 private ulong currentTransactionByte; 471 private ICPU requestingCpu; 472 private TransferType sourceTransferType; 473 private TransferType destinationTransferType; 474 private IFlagRegisterField sourceAddressCounting; 475 private IFlagRegisterField destinationAddressCounting; 476 private IFlagRegisterField registerSetSelect; 477 private IFlagRegisterField registerSetSwitch; 478 private IFlagRegisterField registerSetEnable; 479 private IFlagRegisterField dmaEnabled; 480 private IFlagRegisterField performFullTransfer; 481 private IEnumRegisterField<DMAMode> dmaMode; 482 483 private IFlagRegisterField terminalCount; 484 private IFlagRegisterField endInterrupt; 485 private IFlagRegisterField endInterruptMask; 486 private IFlagRegisterField dmaError; 487 488 private const int AddressRegisterBanks = 2; 489 490 private enum DMAMode 491 { 492 Register = 0, 493 Link = 1, 494 } 495 496 private enum Registers 497 { 498 Next0SourceAddress = 0x00, // N0SA_n/nS 499 Next0DestinationAddress = 0x04, // N0DA_n/nS 500 Next0TransactionByte = 0x08, // N0TB_n/nS 501 Next1SourceAddress = 0x0C, // N1SA_n/nS 502 Next1DestinationAddress = 0x10, // N1DA_n/nS 503 Next1TransactionByte = 0x14, // N1TB_n/nS 504 CurrentSourceAddress = 0x18, // CRSA_n/nS 505 CurrentDestinationAddress = 0x1C, // CRDA_n/nS 506 CurrentTransactionByte = 0x20, // CRTB_n/nS 507 ChannelStatus = 0x24, // CHSTAT_n/nS 508 ChannelControl = 0x28, // CHCTRL_n/nS 509 ChannelConfiguration = 0x2C, // CHCFG_n/nS 510 ChannelInterval = 0x30, // CHITVL_n/nS 511 ChannelExtension = 0x34, // CHEXT_n/nS 512 NextLinkAddress = 0x38, // NXLA_n/nS 513 CurrentLinkAddress = 0x3C, // CRLA_n/nS 514 } 515 } 516 } 517 } 518