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.Linq; 9 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.Bus; 14 15 namespace Antmicro.Renode.Peripherals.Memory 16 { 17 public class MSP430F261X_DMA : BasicWordPeripheral 18 { MSP430F261X_DMA(IMachine machine)19 public MSP430F261X_DMA(IMachine machine) : base(machine) 20 { 21 ChannelRegisterCollections = new WordRegisterCollection(this); 22 23 DefineRegisters(); 24 DefineChannelRegisters(); 25 } 26 27 [ConnectionRegionAttribute("channelRegisters")] WriteWordToChannelRegisters(long offset, ushort value)28 public void WriteWordToChannelRegisters(long offset, ushort value) 29 { 30 ChannelRegisterCollections.Write(offset, value); 31 } 32 33 [ConnectionRegionAttribute("channelRegisters")] ReadWordFromChannelRegisters(long offset)34 public ushort ReadWordFromChannelRegisters(long offset) 35 { 36 return ChannelRegisterCollections.Read(offset); 37 } 38 39 public WordRegisterCollection ChannelRegisterCollections { get; } 40 41 public GPIO IRQ { get; } = new GPIO(); 42 GetIncrement(IncrementMode incrementMode, bool byteTransfer)43 private int GetIncrement(IncrementMode incrementMode, bool byteTransfer) 44 { 45 int incrementSign; 46 switch(incrementMode) 47 { 48 case IncrementMode.Ignored0: 49 case IncrementMode.Ignored1: 50 incrementSign = 0; 51 break; 52 case IncrementMode.Increment: 53 incrementSign = 1; 54 break; 55 case IncrementMode.Decrement: 56 incrementSign = -1; 57 break; 58 default: 59 throw new Exception("unreachable"); 60 } 61 62 return incrementSign * (byteTransfer ? 1 : 2); 63 } 64 PerformTransfer(int channelIndex)65 private void PerformTransfer(int channelIndex) 66 { 67 var toTransfer = (transferMode[channelIndex].Value == TransferMode.Single || transferMode[channelIndex].Value == TransferMode.Repeated) ? 1 : transferSize[channelIndex].Value; 68 69 var sourceIncrement = GetIncrement(sourceIncrementMode[channelIndex].Value, sourceByteTransfer[channelIndex].Value); 70 var destinationIncrement = GetIncrement(destinationIncrementMode[channelIndex].Value, destinationByteTransfer[channelIndex].Value); 71 72 while(toTransfer-- > 0) 73 { 74 var sourceValue = sourceByteTransfer[channelIndex].Value ? machine.SystemBus.ReadByte((ulong)sourceAddress[channelIndex]) : machine.SystemBus.ReadWord((ulong)sourceAddress[channelIndex]); 75 if(destinationByteTransfer[channelIndex].Value) 76 { 77 machine.SystemBus.WriteByte((ulong)destinationAddress[channelIndex], (byte)sourceValue); 78 } 79 else 80 { 81 machine.SystemBus.WriteWord((ulong)destinationAddress[channelIndex], sourceValue); 82 } 83 84 sourceAddress[channelIndex] = sourceAddress[channelIndex] + sourceIncrement; 85 destinationAddress[channelIndex] = destinationAddress[channelIndex] + destinationIncrement; 86 transferSize[channelIndex].Value -= 1; 87 } 88 89 if(transferSize[channelIndex].Value == 0) 90 { 91 transferSize[channelIndex].Value = transferSizeCached[channelIndex]; 92 interruptPending[channelIndex].Value = true; 93 UpdateInterrupts(); 94 } 95 } 96 UpdateInterrupts()97 private void UpdateInterrupts() 98 { 99 var interrupt = interruptEnabled.Zip(interruptPending, (enabled, pending) => enabled.Value && pending.Value).Any(); 100 this.Log(LogLevel.Debug, "IRQ set to {0}", interrupt); 101 IRQ.Set(interrupt); 102 } 103 DefineRegisters()104 private void DefineRegisters() 105 { 106 Registers.Control0.Define(this) 107 .WithEnumFields(0, 4, 3, out triggerSelect, name: "DMA_TSELx", 108 changeCallback: (index, _, value) => 109 { 110 if(value != TriggerSelect.DMAREQ) 111 { 112 this.Log(LogLevel.Warning, "DMA_TSEL{0} changed to {1} but only DMAREQ trigger (software trigger) is supported", index, value); 113 } 114 }) 115 .WithReservedBits(12, 4) 116 ; 117 118 Registers.Control1.Define(this) 119 .WithTaggedFlag("ENNMI", 0) 120 .WithTaggedFlag("ROUNDROBIN", 1) 121 .WithTaggedFlag("DMAONFETCH", 2) 122 .WithReservedBits(3, 13) 123 ; 124 125 Registers.InterruptVector.Define(this) 126 .WithValueField(0, 16, name: "DMAIVx", 127 valueProviderCallback: _ => 128 interruptPending.Select((pending, index) => pending.Value ? (ulong)index + 1 : 0).FirstOrDefault(index => index > 0) << 1) 129 ; 130 } 131 DefineChannelRegisters()132 private void DefineChannelRegisters() 133 { 134 ChannelRegisters.ChannelControl0.DefineMany(ChannelRegisterCollections, ChannelsCount, (register, index) => 135 register 136 .WithFlag(0, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMAREQ", 137 writeCallback: (_, value) => { if(value) PerformTransfer(index); } ) 138 .WithTaggedFlag("DMAABORT", 1) 139 .WithFlag(2, out interruptEnabled[index], name: "DMAIE") 140 .WithFlag(3, out interruptPending[index], name: "DMAIFG") 141 .WithFlag(4, out channelEnabled[index], name: "DMAEN") 142 .WithTaggedFlag("DMALEVEL", 5) 143 .WithFlag(6, out sourceByteTransfer[index], name: "DMASRCBYTE") 144 .WithFlag(7, out destinationByteTransfer[index], name: "DMADSTBYTE") 145 .WithEnumField(8, 2, out sourceIncrementMode[index], name: "DMASRCINCRx") 146 .WithEnumField(10, 2, out destinationIncrementMode[index], name: "DMADSTINCRx") 147 .WithEnumField(12, 3, out transferMode[index], name: "DMADTx") 148 .WithReservedBits(15, 1) 149 .WithChangeCallback((_, __) => UpdateInterrupts()), 150 stepInBytes: ChannelStructureSize); 151 152 ChannelRegisters.ChannelSource0.DefineMany(ChannelRegisterCollections, ChannelsCount, (register, index) => 153 register 154 .WithValueField(0, 16, name: "DMAxSA", 155 valueProviderCallback: _ => (ulong)sourceAddress[index], 156 writeCallback: (_, value) => sourceAddress[index] = (long)value), 157 stepInBytes: ChannelStructureSize); 158 159 ChannelRegisters.ChannelDestination0.DefineMany(ChannelRegisterCollections, ChannelsCount, (register, index) => 160 register 161 .WithValueField(0, 16, name: "DMAxDA", 162 valueProviderCallback: _ => (ulong)destinationAddress[index], 163 writeCallback: (_, value) => destinationAddress[index] = (long)value), 164 stepInBytes: ChannelStructureSize); 165 166 ChannelRegisters.ChannelTransferSize0.DefineMany(ChannelRegisterCollections, ChannelsCount, (register, index) => 167 register 168 .WithValueField(0, 16, out transferSize[index], name: "DMAxSZ", 169 writeCallback: (_, value) => transferSizeCached[index] = value), 170 stepInBytes: ChannelStructureSize); 171 } 172 173 private long[] sourceAddress = new long[ChannelsCount]; 174 private long[] destinationAddress = new long[ChannelsCount]; 175 private ulong[] transferSizeCached = new ulong[ChannelsCount]; 176 177 private IEnumRegisterField<TriggerSelect>[] triggerSelect = new IEnumRegisterField<TriggerSelect>[ChannelsCount]; 178 private IEnumRegisterField<TransferMode>[] transferMode = new IEnumRegisterField<TransferMode>[ChannelsCount]; 179 private IEnumRegisterField<IncrementMode>[] sourceIncrementMode = new IEnumRegisterField<IncrementMode>[ChannelsCount]; 180 private IEnumRegisterField<IncrementMode>[] destinationIncrementMode = new IEnumRegisterField<IncrementMode>[ChannelsCount]; 181 182 private IValueRegisterField[] transferSize = new IValueRegisterField[ChannelsCount]; 183 184 private IFlagRegisterField[] interruptEnabled = new IFlagRegisterField[ChannelsCount]; 185 private IFlagRegisterField[] interruptPending = new IFlagRegisterField[ChannelsCount]; 186 private IFlagRegisterField[] channelEnabled = new IFlagRegisterField[ChannelsCount]; 187 private IFlagRegisterField[] sourceByteTransfer = new IFlagRegisterField[ChannelsCount]; 188 private IFlagRegisterField[] destinationByteTransfer = new IFlagRegisterField[ChannelsCount]; 189 190 private const int ChannelsCount = 3; 191 private const int ChannelStructureSize = 0xC; 192 193 private enum TransferMode 194 { 195 Single, 196 Block, 197 BurstBlock0, 198 BurstBlock1, 199 Repeated, 200 RepeatedBlock, 201 RepeatedBurstBlock0, 202 RepeatedBurstBlock1, 203 } 204 205 private enum IncrementMode 206 { 207 Ignored0, 208 Ignored1, 209 Decrement, 210 Increment, 211 } 212 213 private enum TriggerSelect 214 { 215 DMAREQ, 216 TACCR2, 217 TBCCR2, 218 UCA0RXIFG, 219 UCA0TXIFG, 220 DAC12, 221 ADC12, 222 TACCR0, 223 TBCCR0, 224 UCA1RXIFG, 225 UCA1TXIFG, 226 Multiplier, 227 UCB0RXIFG, 228 UCB0TXIFG, 229 Chained, 230 External, 231 } 232 233 private enum Registers 234 { 235 Control0 = 0x00, 236 Control1 = 0x02, 237 InterruptVector = 0x04, 238 } 239 240 private enum ChannelRegisters 241 { 242 ChannelControl0 = 0x00, 243 ChannelSource0 = 0x02, 244 ChannelDestination0 = 0x06, 245 ChannelTransferSize0 = 0x0A, 246 247 ChannelControl1 = 0x0C, 248 ChannelSource1 = 0x0E, 249 ChannelDestination1 = 0x12, 250 ChannelTransferSize1 = 0x16, 251 252 ChannelControl2 = 0x18, 253 ChannelSource2 = 0x1A, 254 ChannelDestination2 = 0x1E, 255 ChannelTransferSize2 = 0x22, 256 } 257 } 258 } 259