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 Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Peripherals.Bus; 12 using Antmicro.Renode.Utilities; 13 using Antmicro.Renode.Logging; 14 15 namespace Antmicro.Renode.Peripherals.DMA 16 { 17 public abstract class RenesasDA_DMABase : IProvidesRegisterCollection<DoubleWordRegisterCollection>, IDoubleWordPeripheral, IGPIOReceiver 18 { RenesasDA_DMABase(IMachine machine, int channelCount, int peripheralSelectCount)19 public RenesasDA_DMABase(IMachine machine, int channelCount, int peripheralSelectCount) 20 { 21 this.machine = machine; 22 interruptsManager = new InterruptManager<Interrupt>(this, IRQ); 23 this.channelCount = channelCount; 24 channels = new Channel[channelCount]; 25 for(int i = 0; i < channelCount; i++) 26 { 27 channels[i] = new Channel(this, i); 28 } 29 RegistersCollection = new DoubleWordRegisterCollection(this, BuildRegisterMap()); 30 peripheralSelect = new IValueRegisterField[peripheralSelectCount]; 31 } 32 Reset()33 public void Reset() 34 { 35 RegistersCollection.Reset(); 36 interruptsManager.Reset(); 37 foreach(var channel in channels) 38 { 39 channel.Reset(); 40 } 41 } 42 ReadDoubleWord(long offset)43 public uint ReadDoubleWord(long offset) 44 { 45 return RegistersCollection.Read(offset); 46 } 47 WriteDoubleWord(long offset, uint mask)48 public void WriteDoubleWord(long offset, uint mask) 49 { 50 RegistersCollection.Write(offset, mask); 51 } 52 53 /* 54 GPIO number is used to identify a peripheral that requested DMA transfer and its direction (rx/tx). 55 */ OnGPIO(int number, bool value)56 public void OnGPIO(int number, bool value) 57 { 58 var peripheralSource = number / 2; 59 var oddChannel = number % 2; 60 61 // See Table 528: DMA_REQ_MUX_REG. 62 if(peripheralSource < 0 || peripheralSource >= 16) 63 { 64 this.ErrorLog("DMA request from unknown source: {0}. Allowed values are in the range 0-15", peripheralSource); 65 return; 66 } 67 68 if(!value) 69 { 70 return; 71 } 72 // Documentation 35.21 73 // DMA_REQ_MUX_REG - Select which combination of peripherals are mapped on the DMA channels. 74 var index = Array.FindIndex(peripheralSelect, val => (int)val.Value == peripheralSource); 75 if(index != -1) 76 { 77 var channelID = index * 2 + oddChannel; 78 if(!channels[channelID].dmaEnabled.Value) 79 { 80 this.Log(LogLevel.Warning, "Channel {0} isn't enabled. Ignoring request", channelID); 81 return; 82 } 83 channels[channelID].DoTransfer(); 84 } 85 else 86 { 87 this.Log(LogLevel.Warning, "No DMA channel is programmed to handle request 0x{0:X}. Ignoring request", number); 88 } 89 } 90 91 public DoubleWordRegisterCollection RegistersCollection { get; } 92 93 public GPIO IRQ { get; } = new GPIO(); 94 95 protected readonly Channel[] channels; 96 protected readonly InterruptManager<Interrupt> interruptsManager; 97 protected readonly IValueRegisterField[] peripheralSelect; 98 BuildRegisterMap()99 private Dictionary<long, DoubleWordRegister> BuildRegisterMap() 100 { 101 var registerMap = new Dictionary<long, DoubleWordRegister>(); 102 for(long i = 0; i < channelCount; i++) 103 { 104 long channelID = i; 105 long offset = 0x20 * i; 106 registerMap.Add(offset + (long)Registers.SourceAddress_0, new DoubleWordRegister(this) 107 .WithValueField(0, 32, 108 valueProviderCallback: (_) => channels[channelID].SourceAddress, 109 writeCallback: (_, val) => { channels[channelID].SourceAddress = val; }, 110 name: $"DMA{i}_A_START") 111 ); 112 registerMap.Add(offset + (long)Registers.DestinationAddress_0, new DoubleWordRegister(this) 113 .WithValueField(0, 32, 114 valueProviderCallback: (_) => channels[channelID].DestinationAddress, 115 writeCallback: (_, val) => {channels[channelID].DestinationAddress = val; }, 116 name: $"DMA{i}_B_START") 117 ); 118 registerMap.Add(offset + (long)Registers.InterruptLength_0, new DoubleWordRegister(this) 119 .WithValueField(0, 16, out channels[i].interruptLength, name: $"DMA{i}_INT") 120 .WithReservedBits(16, 16) 121 ); 122 registerMap.Add(offset + (long)Registers.TransferLength_0, new DoubleWordRegister(this) 123 .WithValueField(0, 16, out channels[i].transferLength, name: $"DMA{i}_LEN") 124 .WithReservedBits(16, 16) 125 ); 126 registerMap.Add(offset + (long)Registers.Control_0, new DoubleWordRegister(this, 0x00018000) 127 .WithFlag(0, out channels[i].dmaEnabled, name: $"DMA{i}_ON", writeCallback: (_, value) => 128 { 129 if(value && !channels[channelID].peripheralTriggered.Value) 130 { 131 channels[channelID].itemsAlreadyTransferred.Value = 0; 132 channels[channelID].DoTransfer(); 133 } 134 }) 135 .WithValueField(1, 2, name: "BW", writeCallback: (_, value) => 136 { 137 if(value == 3) 138 { 139 this.Log(LogLevel.Warning, "The bus width value cannot be set to {0}", value); 140 return; 141 } 142 channels[channelID].transferType = (TransferType)Math.Pow(2, value); 143 }) 144 .WithFlag(3, out channels[i].peripheralTriggered, name: "DREQ_MODE") 145 .WithFlag(4, out channels[i].incrementDestinationAddress, name: "BINC") 146 .WithFlag(5, out channels[i].incrementSourceAddress, name: "AINC") 147 .WithFlag(6, out channels[i].circularMode, name: "CIRCULAR") 148 .WithTag("DMA_PRIO", 7, 3) 149 .WithTaggedFlag("DMA_IDLE", 10) 150 .WithFlag(11, out channels[i].dmaInit, name: "DMA_INIT") 151 .WithTaggedFlag("REQ_SENSE", 12) 152 .WithEnumField(13, 2, out channels[i].burstMode, name: "BURST_MODE") 153 .WithTaggedFlag("BUS_ERROR_DETECT", 15) 154 .WithTaggedFlag("DMA_EXCLUSIVE_ACCESS", 16) 155 .WithReservedBits(17, 15) 156 ); 157 registerMap.Add(offset + (long)Registers.IndexPointer_0, new DoubleWordRegister(this) 158 .WithValueField(0, 16, out channels[i].itemsAlreadyTransferred, FieldMode.Read, name: $"DMA{i}_IDX") 159 .WithReservedBits(16, 16) 160 ); 161 } 162 163 return registerMap; 164 } 165 166 private readonly IMachine machine; 167 private readonly int channelCount; 168 169 protected class Channel 170 { Channel(RenesasDA_DMABase parent, int channelNumber)171 public Channel(RenesasDA_DMABase parent, int channelNumber) 172 { 173 this.parent = parent; 174 this.channelNumber = channelNumber; 175 engine = new DmaEngine(parent.machine.GetSystemBus(parent)); 176 } 177 Reset()178 public void Reset() 179 { 180 transferCompleted = false; 181 } 182 DoTransfer()183 public void DoTransfer() 184 { 185 // Table 497: DMA0_IDX_REG 186 // When the transfer is completed and circular mode is not set, 187 // itemsAlreadyTransferred is automatically reset to 0 upon starting a new transfer. 188 if(transferCompleted && !circularMode.Value) 189 { 190 transferCompleted = false; 191 itemsAlreadyTransferred.Value = 0; 192 } 193 194 var bytesToTransfer = (int)(transferLength.Value + 1) * (int)transferType; 195 if((ulong)bytesToTransfer == itemsAlreadyTransferred.Value) 196 { 197 parent.Log(LogLevel.Noisy, "All requested data are already transfered. Skipping this transfer."); 198 return; 199 } 200 201 // In normal mode, we are instantly copying requested data size, 202 // in peripheral trigger mode, peripheral should trigger DMA on each sample 203 // unless burst mode is selected which requests a few samples at once. 204 var transactionLength = peripheralTriggered.Value ? (int)transferType * GetBurstCount(burstMode.Value) : bytesToTransfer; 205 Request getDescriptorData = new Request(sourceAddress, destinationAddress, 206 transactionLength, transferType, transferType, incrementSourceAddress.Value && !dmaInit.Value, 207 incrementDestinationAddress.Value); 208 209 parent.Log(LogLevel.Debug, "[Channel {0}] Starting transfer from 0x{1:X} to 0x{2:X}. Copy length: 0x{3:X}; transferType = {4}, srcAddrIncr = {5} dstAddrIncr = {6}", 210 channelNumber, 211 sourceAddress, 212 destinationAddress, 213 transactionLength, 214 transferType, 215 (incrementSourceAddress.Value && !dmaInit.Value), 216 incrementDestinationAddress.Value); 217 218 var response = engine.IssueCopy(getDescriptorData); 219 itemsAlreadyTransferred.Value += (ulong)transactionLength; 220 sourceAddress = (ulong)response.ReadAddress; 221 destinationAddress = (ulong)response.WriteAddress; 222 if((interruptLength.Value * (ulong)transferType) < itemsAlreadyTransferred.Value) 223 { 224 parent.interruptsManager.SetInterrupt((Interrupt)channelNumber); 225 } 226 227 if((ulong)bytesToTransfer == itemsAlreadyTransferred.Value) 228 { 229 // Keep internal information about transfer state 230 // to know if transfer continues or starts from begin. 231 // Used for tracking peripheral triggered requests. 232 transferCompleted = true; 233 } 234 235 if(peripheralTriggered.Value && transferCompleted) 236 { 237 if(circularMode.Value) 238 { 239 CircularModeReset(); 240 return; 241 } 242 this.parent.Log(LogLevel.Noisy, "Disabling DMA channel because all items were transferred and circular mode is off"); 243 dmaEnabled.Value = false; 244 } 245 } 246 247 public ulong SourceAddress 248 { 249 get 250 { 251 return sourceAddress; 252 } 253 set 254 { 255 setSourceAddress = value; 256 sourceAddress = value; 257 } 258 } 259 260 public ulong DestinationAddress 261 { 262 get 263 { 264 return destinationAddress; 265 } 266 set 267 { 268 setDestinationAddress = value; 269 destinationAddress = value; 270 } 271 } 272 273 public ulong sourceAddress; 274 public ulong destinationAddress; 275 276 public IFlagRegisterField dmaEnabled; 277 public IValueRegisterField transferLength; 278 public IValueRegisterField interruptLength; 279 public IFlagRegisterField incrementSourceAddress; 280 public IFlagRegisterField incrementDestinationAddress; 281 public IValueRegisterField itemsAlreadyTransferred; 282 public IFlagRegisterField peripheralTriggered; 283 public IFlagRegisterField circularMode; 284 public IFlagRegisterField dmaInit; 285 public IEnumRegisterField<BurstMode> burstMode; 286 public TransferType transferType; 287 CircularModeReset()288 private void CircularModeReset() 289 { 290 sourceAddress = setSourceAddress; 291 destinationAddress = setDestinationAddress; 292 itemsAlreadyTransferred.Value = 0; 293 } 294 GetBurstCount(BurstMode mode)295 private int GetBurstCount(BurstMode mode) 296 { 297 switch(mode) 298 { 299 case BurstMode.Disabled: return 1; 300 case BurstMode.Four: return 4; 301 case BurstMode.Eight: return 8; 302 default: 303 parent.WarningLog("Invalid selection of burst mode: {0}", mode); 304 return 0; 305 } 306 } 307 308 // For use in circular mode 309 private ulong setSourceAddress; 310 private ulong setDestinationAddress; 311 312 private bool transferCompleted; 313 314 private readonly int channelNumber; 315 private readonly RenesasDA_DMABase parent; 316 private readonly DmaEngine engine; 317 318 public enum BurstMode 319 { 320 Disabled = 0, 321 Four = 1, 322 Eight = 2, 323 Reserved = 3 324 } 325 } 326 327 protected enum Interrupt 328 { 329 Channel0, 330 Channel1, 331 Channel2, 332 Channel3, 333 Channel4, 334 Channel5, 335 Channel6, 336 Channel7, 337 Channel8, 338 Channel9, 339 Channel10, 340 Channel11, 341 Channel12, 342 Channel13, 343 Channel14, 344 Channel15 345 } 346 347 private enum Registers 348 { 349 SourceAddress_0 = 0x0, 350 DestinationAddress_0 = 0x4, 351 InterruptLength_0 = 0x8, 352 TransferLength_0 = 0xC, 353 Control_0 = 0x10, 354 IndexPointer_0 = 0x14, 355 } 356 } 357 } 358 359