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