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.Peripherals.Bus;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Utilities;
13 using System.Threading;
14 using System.Threading.Tasks;
15 
16 
17 namespace Antmicro.Renode.Peripherals.DMA
18 {
19     public class VybridDma : IDoubleWordPeripheral, IWordPeripheral, IBytePeripheral
20     {
VybridDma(Machine mach)21         public VybridDma(Machine mach)
22         {
23             machine = mach;
24             engine = new DmaEngine(machine.GetSystemBus(this));
25 
26             channels = new Channel[32];
27             for (var i = 0; i < 32; i++)
28             {
29                channels[i] = new Channel(this, i);
30             }
31 
32             IRQ = new GPIO();
33         }
34 
ReadWord(long offset)35         public ushort ReadWord(long offset)
36         {
37             this.LogUnhandledRead(offset);
38             return 0;
39         }
40 
WriteWord(long offset, ushort value)41         public void WriteWord(long offset, ushort value)
42         {
43             if (offset < 0x1000) {
44                 this.LogUnhandledWrite(offset, value);
45                 return;
46             }
47             uint channel = (uint)((offset - 0x1000) / 0x20);
48             var operation = (offset - 0x1000) - (channel * 0x20);
49             if (channel > 31) {
50                 this.Log(LogLevel.Error, "Channel is greater than 31");
51                 return;
52             }
53             channels[channel].WriteWord(operation, value);
54             UpdateIRQ();
55         }
56 
ReadByte(long offset)57         public byte ReadByte(long offset)
58         {
59             this.LogUnhandledRead(offset);
60             return 0;
61         }
62 
WriteByte(long offset, byte value)63         public void WriteByte(long offset, byte value)
64         {
65             switch((Register)offset)
66             {
67             case Register.SetEnableRequest:
68                 EnableRequestRegister |= (uint)(1<<value);
69                 DoCopy();
70                 break;
71             case Register.ClearInterruptRequest:
72                 InterruptRequestRegister &= (uint)~(1<<value);
73                 break;
74             case Register.ClearEnableRequest:
75                 EnableRequestRegister &= (uint)~(1<<value);
76                 break;
77             default:
78                 this.LogUnhandledWrite(offset, value);
79                 break;
80             }
81             UpdateIRQ();
82         }
83 
ReadDoubleWord(long address)84         public uint ReadDoubleWord(long address)
85         {
86             if (address < 0x1000) {
87                     switch ((Register)address)
88                     {
89                         case Register.InterruptRequest:
90                                 return InterruptRequestRegister;
91                     }
92             }
93             this.LogUnhandledRead(address);
94             return 0;
95         }
96 
WriteDoubleWord(long address, uint value)97         public void WriteDoubleWord(long address, uint value)
98         {
99             if (address < 0x1000) {
100                     switch ((Register)address)
101                     {
102                         default:
103                                 this.LogUnhandledWrite(address, value);
104                                 break;
105                     }
106                     return;
107             }
108 
109             var channel = (address - 0x1000) / 0x20;
110             var operation = (address - 0x1000) - (channel * 0x20);
111 
112             if (channel > 31) {
113                 this.Log(LogLevel.Error, "Channel is greater than 31");
114                 return;
115             }
116 
117             channels[channel].WriteDoubleWord(operation, value);
118             UpdateIRQ();
119         }
120 
DoCopy()121         public bool DoCopy()
122         {
123             bool res = false;
124             if (EnableRequestRegister == 0) return false;
125             for (int i = 0; i < 32; i++) {
126                     if ((EnableRequestRegister & (1 << i)) == 0) continue;
127                     var channel = channels[i];
128                     if (!channel.DoCopy()) continue;
129                     res = true;
130 
131                     // TODO: also move those to channel.DoCopy() ?
132                     if ((channel.TCD_ControlAndStatus & (1u << 3)) != 0)
133                     {
134                         EnableRequestRegister &= ~(1u << channel.channelNumber);
135                     }
136 
137                     if ((channel.TCD_ControlAndStatus & (1u << 1)) != 0)
138                     {
139                         InterruptRequestRegister |= (1u << channel.channelNumber);
140                     }
141             }
142             if (res) UpdateIRQ();
143             return res;
144         }
145 
Reset()146         public void Reset()
147         {
148             InterruptRequestRegister = 0;
149             UpdateIRQ();
150         }
151 
UpdateIRQ()152         private void UpdateIRQ() {
153                 if (InterruptRequestRegister != 0) {
154                         this.NoisyLog("IRQ is on, val = {0:X},enable={1:X}", InterruptRequestRegister, EnableRequestRegister);
155                         IRQ.Set();
156                 } else {
157                         IRQ.Unset();
158                 }
159         }
160 
161         //private uint EnableErrorInterruptRegister;
162         private uint EnableRequestRegister;
163         private uint InterruptRequestRegister;
164         //private uint HardwareRequestStatusRegister;
165         //private uint ControlRegister;
166         //private uint ErrorRegister;
167 
168         private readonly IMachine machine;
169         private readonly DmaEngine engine;
170 
171         public GPIO IRQ { get; private set; }
172 
173         private enum Register : long
174         {
175             Control                     = 0x00,
176             EnableErrorInterrupt        = 0x14,
177             ClearEnableErrorInterupt    = 0x18,
178             SetEnableErrorInterrupt     = 0x19,
179             ClearEnableRequest          = 0x1A,
180             SetEnableRequest            = 0x1B,
181             ClearDONEStatusBit          = 0x1C,
182             ClearError                  = 0x1E,
183             ClearInterruptRequest       = 0x1F,
184             InterruptRequest            = 0x24,
185             Error                       = 0x2C,
186             HardwareRequestStatus       = 0x34,
187         }
188 
189         private sealed class Channel
190         {
Channel(VybridDma parent, int number)191                 public Channel(VybridDma parent, int number) {
192                         this.parent = parent;
193                         channelNumber = number;
194                 }
195 
WriteDoubleWord(long offset, uint value)196                 public void WriteDoubleWord(long offset, uint value) {
197                     switch ((ChannelRegister)offset) {
198                          case ChannelRegister.TCD_MinorByteCount:
199                             TCD_MinorByteCount = value;
200                             break;
201                          case ChannelRegister.TCD_SourceAddress:
202                             TCD_SourceAddress = value;
203                             break;
204                          case ChannelRegister.TCD_DestinationAddress:
205                             TCD_DestinationAddress = value;
206                             break;
207                          default:
208                             parent.Log(LogLevel.Noisy, "Unhandled DWORD write val=0x{2:X} offset 0x{0:X} on channel {1}",offset,channelNumber,value);
209                             break;
210                     }
211                 }
212 
WriteWord(long offset, ushort value)213                 public void WriteWord(long offset, ushort value) {
214                     switch((ChannelRegister)offset) {
215                         case ChannelRegister.TCD_CurrentMajorLoopCount:
216                             TCD_CurrentMajorLoopCount = value;
217                             break;
218                         case ChannelRegister.TCD_ControlAndStatus:
219                             TCD_ControlAndStatus = value;
220                             break;
221                         default:
222                             parent.Log(LogLevel.Noisy, "Unhandled WORD write val=0x{2:X} offset 0x{0:X} on channel {1}",offset,channelNumber,value);
223                             break;
224                     }
225                 }
226 
DoCopy()227                 public bool DoCopy() {
228 		    if ((TCD_ControlAndStatus & (1u << 7)) > 0) return false;
229                     uint size = (uint)(TCD_MinorByteCount * TCD_CurrentMajorLoopCount);
230                     parent.Log(LogLevel.Noisy, "Channel {0} : copying data size {1} from 0x{2:X} to 0x{3:X}", channelNumber, size, TCD_SourceAddress, TCD_DestinationAddress);
231                     if (size == 0) {
232                         parent.Log(LogLevel.Error, "Error: size is 0 - stopping copy request (minor={0},major={1})", TCD_MinorByteCount, TCD_CurrentMajorLoopCount);
233                         return false;
234                     }
235                     var request = new Request(TCD_SourceAddress, TCD_DestinationAddress, (int)size, TransferType.Byte, TransferType.Byte, incrementWriteAddress: false);
236                     parent.engine.IssueCopy(request);
237 
238                     TCD_ControlAndStatus |= (1u << 7);
239                     return true;
240                 }
241 
242                 public readonly int channelNumber;
243                 private readonly VybridDma parent;
244 
245                 uint TCD_SourceAddress;
246                 //uint TCD_SignedSourceAddressOffset;
247                 //uint TCD_TransferAttributes;
248                 uint TCD_MinorByteCount;
249                 //uint TCD_LastSourceAddressAdjustment;
250                 uint TCD_DestinationAddress;
251                 //uint TCD_SignedDestinationAddressOffset;
252                 uint TCD_CurrentMajorLoopCount;
253                 //uint TCD_LastDestinationAddressAdjustment;
254                 public uint TCD_ControlAndStatus;
255 
256                 private enum ChannelRegister : long
257                 {
258                     TCD_SourceAddress                       = 0x00,
259                     TCD_SignedSourceAddressOffset           = 0x04,
260                     TCD_TransferAttributes                  = 0x06,
261                     TCD_MinorByteCount                      = 0x08,
262                     TCD_LastSourceAddressAdjustment         = 0x0C,
263                     TCD_DestinationAddress                  = 0x10,
264                     TCD_SignedDestinationAddressOffset      = 0x14,
265                     TCD_CurrentMajorLoopCount               = 0x16,
266                     TCD_LastDestinationAddressAdjustment    = 0x18,
267                     TCD_ControlAndStatus                    = 0x1C,
268                 }
269         }
270 
271         private readonly Channel[] channels;
272     }
273 }
274 
275