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