1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Logging; 11 using Antmicro.Renode.Peripherals.Bus; 12 using System.Collections.Generic; 13 using System.Collections.ObjectModel; 14 15 namespace Antmicro.Renode.Peripherals.DMA 16 { 17 public sealed class TegraDma : IDoubleWordPeripheral, IKnownSize, INumberedGPIOOutput 18 { TegraDma(IMachine machine)19 public TegraDma(IMachine machine) 20 { 21 dmaEngine = new DmaEngine(machine.GetSystemBus(this)); 22 channels = new Channel[ChannelNo]; 23 24 var innerConnections = new Dictionary<int, IGPIO>(); 25 for(var i = 0; i < channels.Length; i++) 26 { 27 channels[i] = new Channel(this, i); 28 innerConnections[i] = channels[i].IRQ; 29 } 30 31 Connections = new ReadOnlyDictionary<int, IGPIO>(innerConnections); 32 Reset(); 33 } 34 35 public long Size 36 { 37 get 38 { 39 return 0x2000; 40 } 41 } 42 ReadDoubleWord(long offset)43 public uint ReadDoubleWord(long offset) 44 { 45 if(offset >= 0x1000 && offset < 0x1000 + 32*ChannelNo) 46 { 47 return channels[(offset - 0x1000)/32].Read(offset % 32); 48 } 49 switch((Register)offset) 50 { 51 case Register.Counter: 52 return counterRegister; 53 case Register.Status: 54 // currently we're never busy 55 return activeChannelInterrupts; 56 default: 57 this.LogUnhandledRead(offset); 58 return 0; 59 } 60 } 61 WriteDoubleWord(long offset, uint value)62 public void WriteDoubleWord(long offset, uint value) 63 { 64 if(offset >= 0x1000 && offset < 0x1000 + 32*ChannelNo) 65 { 66 channels[(offset - 0x1000)/32].Write(offset % 32, value); 67 return; 68 } 69 switch((Register)offset) 70 { 71 case Register.Command: 72 enabled = ((1 << 31) & value) != 0; 73 break; 74 case Register.Counter: 75 // throttle down is ignored by us, we only keep the value for the reads 76 counterRegister = value; 77 break; 78 case Register.IrqMaskSet: 79 irqMask |= value; 80 break; 81 case Register.IrqMaskClear: 82 irqMask &= ~value; 83 break; 84 default: 85 this.LogUnhandledWrite(offset, value); 86 break; 87 } 88 } 89 Reset()90 public void Reset() 91 { 92 enabled = false; 93 counterRegister = 0; 94 irqMask = 0; 95 activeChannelInterrupts = 0; 96 for(var i = 0; i < channels.Length; i++) 97 { 98 channels[i].Reset(); 99 } 100 } 101 102 private enum Register 103 { 104 Command = 0x0, 105 Status = 0x4, 106 Counter = 0x10, 107 IrqMaskSet = 0x20, 108 IrqMaskClear = 0x24 109 } 110 111 private sealed class Channel 112 { Channel(TegraDma parent, int number)113 public Channel(TegraDma parent, int number) 114 { 115 this.parent = parent; 116 channelNumber = number; 117 IRQ = new GPIO(); 118 } 119 Reset()120 public void Reset() 121 { 122 apbStartingAddress = 0; 123 ahbStartingAddress =0; 124 interruptEnabled = false; 125 apbIsSource = false; 126 apbTransferType = TransferType.Byte; 127 ahbTransferType= TransferType.Byte; 128 } 129 130 public GPIO IRQ { get; private set; } 131 Read(long offset)132 public uint Read(long offset) 133 { 134 switch((ChannelRegister)offset) 135 { 136 case ChannelRegister.Status: 137 if((parent.activeChannelInterrupts & (1 << channelNumber)) != 0) 138 { 139 return 1 << 30; 140 } 141 return 0; 142 default: 143 parent.Log(LogLevel.Warning, "Unhandled read from 0x{1:X} (channel {0}).", offset, channelNumber); 144 return 0; 145 } 146 } 147 Write(long offset, uint value)148 public void Write(long offset, uint value) 149 { 150 switch((ChannelRegister)offset) 151 { 152 case ChannelRegister.Control: 153 HandleControlWrite(value); 154 break; 155 case ChannelRegister.Status: 156 if((value & (1 << 30)) == 0) 157 { 158 break; 159 } 160 parent.activeChannelInterrupts &= (ushort)~(1 << channelNumber); 161 IRQ.Unset(); 162 break; 163 case ChannelRegister.AhbStartingAddress: 164 ahbStartingAddress = value; 165 break; 166 case ChannelRegister.ApbStartingAddress: 167 apbStartingAddress = 0x70000000; 168 apbStartingAddress |= value & 0xFFFF; 169 break; 170 case ChannelRegister.ApbAddressSequencer: 171 // TODO: currently we ignore the send interrupt to COP option 172 apbTransferType = GetTransferType(value); 173 if((value & ~0xF0010000) != 0) 174 { 175 parent.Log(LogLevel.Warning, "Channel {0}: unsupported APB sequencer flags, value written is 0x{1:X}.", channelNumber, value); 176 } 177 break; 178 case ChannelRegister.AhbAddressSequencer: 179 // TODO: currently we ignore the send interrupt to COP option 180 ahbTransferType = GetTransferType(value); 181 if((value & ~0xF4000000) != 0) 182 { 183 parent.Log(LogLevel.Warning, "Channel {0}: unsupported AHB sequencer flags, value written is 0x{1:X}.", channelNumber, value); 184 } 185 break; 186 default: 187 parent.Log(LogLevel.Warning, "Unhandler write to offset 0x{1:X}, value 0x{2:X} (channel {0}).", channelNumber, offset, value); 188 break; 189 } 190 } 191 GetTransferType(uint value)192 private TransferType GetTransferType(uint value) 193 { 194 var busWidth = value >> 28; 195 switch(busWidth) 196 { 197 case 0: 198 return TransferType.Byte; 199 case 1: 200 return TransferType.Word; 201 default: 202 return TransferType.DoubleWord; 203 } 204 } 205 HandleControlWrite(uint value)206 private void HandleControlWrite(uint value) 207 { 208 interruptEnabled = ((1 << 30) & value) != 0; 209 var start = ((1 << 31) & value) != 0; 210 apbIsSource = ((1 << 28) & value) == 0; 211 if(((1 << 27) & value) == 0) 212 { 213 parent.Log(LogLevel.Warning, "Channel {0}: unsupported multi block transfer.", channelNumber); 214 } 215 // check whether we have some not supported flags 216 // note that we also ignore bit 21, i.e. flow control enable 217 // D020 = enabled bits 21, 27, 28, 30 and 31 218 if((value & (~0xD820FFFF)) != 0) 219 { 220 parent.Log(LogLevel.Warning, "Channel {0}: unsupported flags in channel control register (value written was 0x{1:X}.", channelNumber, value); 221 } 222 if(start && parent.enabled) 223 { 224 var sourceAddress = apbIsSource ? apbStartingAddress : ahbStartingAddress; 225 var destinationAddress = apbIsSource ? ahbStartingAddress : apbStartingAddress; 226 var sourceTransferType = apbIsSource ? apbTransferType : ahbTransferType; 227 var destinationTransferType = apbIsSource ? ahbTransferType : apbTransferType; 228 var size = ((((int)value & 0xFFFF) >> 2) + 1) << 2; 229 parent.DebugLog("Channel {0}: issuing copy: 0x{1:X} ({2}) ---> 0x{3:X} ({4}), size {5}B.", channelNumber, sourceAddress, sourceTransferType, 230 destinationAddress, destinationTransferType, size); 231 parent.dmaEngine.IssueCopy(new Request(sourceAddress, destinationAddress, size, sourceTransferType, destinationTransferType)); 232 parent.activeChannelInterrupts |= (ushort)(1 << channelNumber); 233 if(interruptEnabled) 234 { 235 IRQ.Set(); 236 } 237 } 238 } 239 240 private enum ChannelRegister 241 { 242 Control = 0x0, 243 Status = 0x4, 244 AhbStartingAddress = 0x10, 245 AhbAddressSequencer = 0x14, 246 ApbStartingAddress = 0x18, 247 ApbAddressSequencer = 0x1c, 248 } 249 250 private uint apbStartingAddress; 251 private uint ahbStartingAddress; 252 private readonly TegraDma parent; 253 private bool interruptEnabled; 254 private readonly int channelNumber; 255 private bool apbIsSource; 256 private TransferType apbTransferType; 257 private TransferType ahbTransferType; 258 } 259 260 private bool enabled; 261 private uint counterRegister; 262 private uint irqMask; 263 private readonly Channel[] channels; 264 private readonly DmaEngine dmaEngine; 265 private ushort activeChannelInterrupts; 266 267 public IReadOnlyDictionary<int, IGPIO> Connections { get; private set; } 268 269 private const int ChannelNo = 16; 270 } 271 } 272 273