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; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.Bus; 14 using Antmicro.Renode.Utilities; 15 16 namespace Antmicro.Renode.Peripherals.DMA 17 { 18 public class NPCX_MDMA : BasicDoubleWordPeripheral, IKnownSize, IGPIOReceiver 19 { NPCX_MDMA(IMachine machine, uint sourceAddress, uint destinationAddress)20 public NPCX_MDMA(IMachine machine, uint sourceAddress, uint destinationAddress) : base(machine) 21 { 22 IRQ = new GPIO(); 23 channelContexts = new DMAChannelContext[ChannelCount]; 24 25 DefineRegisters(sourceAddress, destinationAddress); 26 Reset(); 27 } 28 OnGPIO(int number, bool value)29 public void OnGPIO(int number, bool value) 30 { 31 if(number < 0 && number > ChannelCount) 32 { 33 this.ErrorLog("Invalid channel number: {0}. Expected value between 0 and {1}", number, ChannelCount - 1); 34 } 35 36 if(value) 37 { 38 channelContexts[number].TryPerformCopy(); 39 } 40 } 41 Reset()42 public override void Reset() 43 { 44 base.Reset(); 45 IRQ.Unset(); 46 foreach(var context in channelContexts) 47 { 48 context.Reset(); 49 } 50 } 51 52 public long Size => 0x100; 53 54 public GPIO IRQ { get; } 55 DefineRegisters(uint sourceAddress, uint destinationAddress)56 private void DefineRegisters(uint sourceAddress, uint destinationAddress) 57 { 58 for(var i = 0; i < ChannelCount; i++) 59 { 60 var channelIdx = i; 61 62 DMAChannelContext context; 63 switch(channelIdx) 64 { 65 case 0: 66 context = DefineChannel0Registers(sourceAddress); 67 break; 68 case 1: 69 context = DefineChannel1Registers(destinationAddress); 70 break; 71 default: 72 throw new Exception($"Invalid channel count value of {channelIdx}. Expected value between 0 and {ChannelCount - 1}"); 73 } 74 75 channelContexts[channelIdx] = context; 76 var channelOffset = channelIdx * 0x20; 77 78 (Registers.Channel0Control + channelOffset).Define(this) 79 .WithFlag(0, out context.Enabled, name: "MDMAEN (MDMA Enable)", 80 writeCallback: (previous, value) => 81 { 82 if(!value) 83 { 84 if(previous) 85 { 86 this.WarningLog("Trying to disable the DMA channel {0} while a transaction is in progress. Ignoring", channelIdx); 87 context.Enabled.Value = true; 88 } 89 return; 90 } 91 92 this.DebugLog("DMA Channel {0} {1}", channelIdx, value ? "enabled" : "disabled"); 93 if(context.Direction == DMAChannelContext.TransferDirection.ToPeripheral) 94 { 95 // Only process the DMA transfer request for ToPeripheral channels 96 // as FromPeripheral transfers have to be explicitly requested by the peripheral 97 context.TryPerformCopy(); 98 } 99 }) 100 .WithTag("MPD (MDMA Power-Down)", 1, 1) 101 .WithReservedBits(2, 6) 102 .WithFlag(8, out context.InterruptEnable, name: "SIEN (Stop Interrupt Enable)", 103 writeCallback: (_, __) => UpdateInterrupt()) 104 .WithReservedBits(9, 5) 105 .WithTag("MPS (MDMA Power Save)", 14, 1) 106 .WithReservedBits(15, 3) 107 .WithFlag(18, out context.IsFinished, FieldMode.Read | FieldMode.WriteZeroToClear, 108 name: "TC (Terminal Count)", writeCallback: (_, __) => UpdateInterrupt()) 109 .WithReservedBits(19, 13); 110 111 (Registers.Channel0TransferCount + channelOffset).Define(this) 112 .WithValueField(0, 13, name: "TFR_CNT (13-bit Transfer Count)", 113 valueProviderCallback: _ => context.TransferCount, 114 writeCallback: (_, value) => 115 { 116 if(value > MaxTransferCount) 117 { 118 this.WarningLog("Maximum transfer count is {0} bytes. Got {1}", MaxTransferCount, value); 119 value = MaxTransferCount; 120 } 121 context.TransferCount = (ushort)value; 122 context.CurrentTransferCount = (ushort)value; 123 }) 124 .WithReservedBits(13, 19); 125 126 (Registers.Channel0CurrentTransferCount + channelOffset).Define(this) 127 .WithValueField(0, 13, FieldMode.Read, name: "CURENT_TFR_CNT (13-bit Current Transfer Count)", 128 valueProviderCallback: _ => context.CurrentTransferCount) 129 .WithReservedBits(13, 19); 130 } 131 } 132 DefineChannel0Registers(uint sourceAddress)133 private DMAChannelContext DefineChannel0Registers(uint sourceAddress) 134 { 135 var context = new DMAChannelContext(this, sourceAddress, DefaultMemoryAddress, DMAChannelContext.TransferDirection.FromPeripheral); 136 137 Registers.Channel0SourceBaseAddress.Define(this) 138 .WithValueField(0, 32, FieldMode.Read, name: "SRC_BASE_ADDR (32-bit Source Base Address)", 139 valueProviderCallback: _ => context.SourceAddress); 140 141 Registers.Channel0DestinationBaseAddress.Define(this) 142 .WithValueField(0, 20, name: "DST_BASE_ADDR19-0 (20-bit Destination Base Address)", 143 valueProviderCallback: _ => BitHelper.GetValue(context.DestinationAddress, 0, 20), 144 writeCallback: (_, value) => BitHelper.SetMaskedValue(ref context.DestinationAddress, (uint)value, 0, 20)) 145 .WithValueField(20, 12, FieldMode.Read, name: "DST_BASE_ADDR31-20 (32-bit Destination Base Address)", 146 valueProviderCallback: _ => BitHelper.GetValue(context.DestinationAddress, 20, 12)); 147 148 Registers.Channel0CurrentDestination.Define(this) 149 .WithValueField(0, 32, FieldMode.Read, name: "CURRENT_DST_ADDR (32-bit Current Destination Address)", 150 valueProviderCallback: _ => context.DestinationAddress + context.CurrentTransferCount); 151 152 return context; 153 } 154 DefineChannel1Registers(uint destinationAddress)155 private DMAChannelContext DefineChannel1Registers(uint destinationAddress) 156 { 157 var context = new DMAChannelContext(this, DefaultMemoryAddress, destinationAddress, DMAChannelContext.TransferDirection.ToPeripheral); 158 159 Registers.Channel1SourceBaseAddress.Define(this) 160 .WithValueField(0, 20, name: "SRC_BASE_ADDR19-0 (20-bit Source Base Address)", 161 valueProviderCallback: _ => BitHelper.GetValue(context.SourceAddress, 0, 20), 162 writeCallback: (_, value) => BitHelper.SetMaskedValue(ref context.SourceAddress, (uint)value, 0, 20)) 163 .WithValueField(20, 12, FieldMode.Read, name: "SRC_BASE_ADDR31-20 (12-bit Source Base Address)", 164 valueProviderCallback: _ => BitHelper.GetValue(context.SourceAddress, 20, 12)); 165 166 Registers.Channel1DestinationBaseAddress.Define(this) 167 .WithValueField(0, 32, FieldMode.Read, name: "DST_BASE_ADDR (32-bit Destination Base Address)", 168 valueProviderCallback: _ => context.DestinationAddress); 169 170 Registers.Channel1CurrentSource.Define(this) 171 .WithValueField(0, 32, FieldMode.Read, name: "CURRENT_SRC_ADDR (32-bit Current Source Address)", 172 // Since ToPeripheral transfers happen instantly current source address will always be equal to 173 // to base source address 174 valueProviderCallback: _ => context.SourceAddress); 175 176 return context; 177 } 178 UpdateInterrupt()179 private void UpdateInterrupt() 180 { 181 var state = false; 182 foreach(var context in channelContexts) 183 { 184 state |= context.IsFinished.Value && context.InterruptEnable.Value; 185 } 186 this.DebugLog("{0} interrupt", state ? "Setting" : "Unsetting"); 187 IRQ.Set(state); 188 } 189 190 private enum Registers 191 { 192 Channel0Control = 0x0, 193 Channel0SourceBaseAddress = 0x4, 194 Channel0DestinationBaseAddress = 0x8, 195 Channel0TransferCount = 0xC, 196 Channel0CurrentDestination = 0x14, 197 Channel0CurrentTransferCount = 0x18, 198 Channel1Control = 0x20, 199 Channel1SourceBaseAddress = 0x24, 200 Channel1DestinationBaseAddress = 0x28, 201 Channel1TransferCount = 0x2C, 202 Channel1CurrentSource = 0x30, 203 Channel1CurrentTransferCount = 0x38 204 } 205 206 private readonly DMAChannelContext[] channelContexts; 207 208 private const int ChannelCount = 2; 209 private const int MaxTransferCount = 4096; 210 private const int DefaultMemoryAddress = 0x10000000; 211 212 private class DMAChannelContext 213 { DMAChannelContext(NPCX_MDMA owner, uint sourceAddress, uint destinationAddress, TransferDirection direction)214 public DMAChannelContext(NPCX_MDMA owner, uint sourceAddress, uint destinationAddress, TransferDirection direction) 215 { 216 this.owner = owner; 217 defaultSourceAddress = sourceAddress; 218 defaultDestinationAddress = destinationAddress; 219 this.Direction = direction; 220 221 engine = new DmaEngine(owner.machine.GetSystemBus(owner)); 222 223 Reset(); 224 } 225 TryPerformCopy()226 public void TryPerformCopy() 227 { 228 if(!Enabled.Value) 229 { 230 owner.ErrorLog("Attempted to perform a DMA transaction without enabling the channel"); 231 return; 232 } 233 234 Request request; 235 236 switch(Direction) 237 { 238 case TransferDirection.FromPeripheral: 239 // FromPeripheral transfers are performed 1 byte at a time, since from the perspective of the MDMA 240 // device there is no way to tell if the peripheral's receive buffer contains valid data. 241 // The peripheral should notify the MDMA that data has been placed into the receive buffer via Renode's 242 // GPIO mechanism 243 request = new Request( 244 new Place(SourceAddress), 245 new Place(DestinationAddress + (uint)(TransferCount - CurrentTransferCount)), 246 1, TransferType.Byte, TransferType.Byte, 247 incrementReadAddress: false, 248 incrementWriteAddress: false 249 ); 250 251 CurrentTransferCount--; 252 break; 253 case TransferDirection.ToPeripheral: 254 request = new Request( 255 new Place(SourceAddress), 256 new Place(DestinationAddress), 257 TransferCount, TransferType.Byte, TransferType.Byte, 258 incrementReadAddress: true, 259 incrementWriteAddress: false 260 ); 261 262 CurrentTransferCount = 0; 263 break; 264 default: 265 throw new Exception($"Invalid {nameof(TransferDirection)} value: {Direction}"); 266 } 267 268 engine.IssueCopy(request); 269 if(CurrentTransferCount == 0) 270 { 271 Enabled.Value = false; 272 IsFinished.Value = true; 273 owner.UpdateInterrupt(); 274 } 275 } 276 Reset()277 public void Reset() 278 { 279 TransferCount = 0; 280 CurrentTransferCount = 0; 281 SourceAddress = defaultSourceAddress; 282 DestinationAddress = defaultDestinationAddress; 283 } 284 285 public TransferDirection Direction { get; } 286 public ushort TransferCount { get; set; } 287 public ushort CurrentTransferCount { get; set; } 288 289 public IFlagRegisterField Enabled; 290 public IFlagRegisterField IsFinished; 291 public IFlagRegisterField InterruptEnable; 292 293 public uint SourceAddress; 294 public uint DestinationAddress; 295 296 private readonly uint defaultSourceAddress; 297 private readonly uint defaultDestinationAddress; 298 299 public enum TransferDirection 300 { 301 FromPeripheral, 302 ToPeripheral, 303 } 304 305 private readonly NPCX_MDMA owner; 306 private readonly DmaEngine engine; 307 } 308 } 309 } 310