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.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Exceptions;
14 using Antmicro.Renode.Logging;
15 using Antmicro.Renode.Network;
16 using Antmicro.Renode.Peripherals.Bus;
17 using Antmicro.Renode.Peripherals.CPU;
18 using Antmicro.Renode.Peripherals.Timers;
19 using Antmicro.Renode.Time;
20 using Antmicro.Renode.Utilities;
21 using PacketDotNet;
22 
23 using IPProtocolType = PacketDotNet.IPProtocolType;
24 
25 namespace Antmicro.Renode.Peripherals.Network
26 {
27     public partial class SynopsysDWCEthernetQualityOfService
28     {
29         protected readonly DMAChannel[] dmaChannels;
30 
31         protected enum BusWidth
32         {
33             Bits32  = 4,
34             Bits64  = 8,
35             Bits128 = 16,
36         }
37 
38         protected enum DMAChannelInterruptMode
39         {
40             Pulse            = 0b00,
41             Level            = 0b01,
42             LevelAndReassert = 0b10,
43             Reserved         = 0b11,
44         }
45 
46         private enum DMAState
47         {
48             Stopped = 0,
49             Running = 1,
50             ProcessingIntermediate = 2,
51             ProcessingSecond = 4,
52             Suspended = 8,
53         }
54 
55         protected class DMAChannel
56         {
DMAChannel(SynopsysDWCEthernetQualityOfService parent, int channelNumber, long systemClockFrequency, bool hasInterrupts)57             public DMAChannel(SynopsysDWCEthernetQualityOfService parent, int channelNumber, long systemClockFrequency, bool hasInterrupts)
58             {
59                 this.parent = parent;
60                 this.channelNumber = channelNumber;
61                 this.hasInterrupts = hasInterrupts;
62 
63                 if(hasInterrupts)
64                 {
65                     TxIRQ = new GPIO();
66                     RxIRQ = new GPIO();
67                 }
68 
69                 incomingFrames = new Queue<EthernetFrame>();
70                 rxWatchdog = new LimitTimer(parent.machine.ClockSource, systemClockFrequency, parent, $"DMA Channel {channelNumber}: rx-watchdog", enabled: false, workMode: WorkMode.OneShot, eventEnabled: true, autoUpdate: true, divider: RxWatchdogDivider);
71                 rxWatchdog.LimitReached += delegate
72                 {
73                     this.Log(LogLevel.Noisy, "Receive: Watchdog reached limit.");
74                     rxInterrupt.Value = true;
75                     parent.UpdateInterrupts();
76                 };
77             }
78 
Reset()79             public void Reset()
80             {
81                 rxFinishedRing = true;
82                 txFinishedRing = true;
83                 txState = DMAState.Stopped;
84                 rxState = DMAState.Stopped;
85                 rxOffset = 0;
86                 latestTxContext = null;
87                 rxWatchdog.Reset();
88                 incomingFrames.Clear();
89                 rxQueueLength = 0;
90                 frameAssembler = null;
91             }
92 
DefineChannelRegisters(ref Dictionary<long, DoubleWordRegister> map)93             public void DefineChannelRegisters(ref Dictionary<long, DoubleWordRegister> map)
94             {
95                 var offset = parent.DMAChannelOffsets[channelNumber];
96                 map = map.Concat(new Dictionary<long, DoubleWordRegister>()
97                 {
98                     {(long)RegistersDMAChannel.Control + offset, new DoubleWordRegister(parent)
99                         .WithValueField(0, 14, out maximumSegmentSize, name: "DMACCR.MSS (Maximum Segment Size)")
100                         .WithReservedBits(14, 2)
101                         .WithFlag(16, out programmableBurstLengthTimes8, name: "DMACCR.PBLX8 (8xPBL mode)")
102                         .WithReservedBits(17, 1)
103                         .WithValueField(18, 3, out descriptorSkipLength, name: "DMACCR.DSL (Descriptor Skip Length)")
104                         .WithReservedBits(21, 11)
105                     },
106                     {(long)RegistersDMAChannel.TransmitControl + offset, new DoubleWordRegister(parent)
107                         .WithFlag(0, out startTx, changeCallback: (_, __) =>
108                         {
109                             if(startTx.Value)
110                             {
111                                 txFinishedRing = txDescriptorRingCurrent.Value == txDescriptorRingTail.Value;
112                                 StartTx();
113                             }
114                         },
115                         name: "DMACTxCR.ST (Start or Stop Transmission Command)")
116                         .WithReservedBits(1, 3)
117                         .WithFlag(4, out operateOnSecondPacket, name: "DMACTxCR.OSF (Operate on Second Packet)")
118                         .WithReservedBits(5, 7)
119                         .WithFlag(12, out tcpSegmentationEnable, name: "DMACTxCR.TSE (TCP Segmentation Enabled)")
120                         .WithReservedBits(13, 3)
121                         .WithValueField(16, 6, out txProgrammableBurstLength, name: "DMACTxCR.TXPBL (Transmit Programmable Burst Length)")
122                         .WithReservedBits(22, 10)
123                     },
124                     {(long)RegistersDMAChannel.ReceiveControl + offset, new DoubleWordRegister(parent)
125                         .WithFlag(0, out startRx, changeCallback: (_, __) =>
126                         {
127                             if(startRx.Value)
128                             {
129                                 rxFinishedRing = rxDescriptorRingCurrent.Value == rxDescriptorRingTail.Value;
130                                 StartRx();
131                             }
132                         },
133                         name: "DMACRxCR.SR (Start or Stop Receive Command)")
134                         .WithValueField(1, 14, out rxBufferSize, writeCallback: (_, __) =>
135                         {
136                             if(rxBufferSize.Value % 4 != 0)
137                             {
138                                 this.Log(LogLevel.Warning, "Receive buffer size must be a multiple of 4. Ignoring LSBs, but this behavior is undefined.");
139                                 rxBufferSize.Value &= ~0x3UL;
140                             }
141                         },
142                         name: "DMACRxCR.RBSZ (Receive Buffer size)")
143                         .WithReservedBits(15, 1)
144                         .WithValueField(16, 6, out rxProgrammableBurstLength, name: "DMACRxCR.RXPBL (RXPBL)")
145                         .WithReservedBits(22, 9)
146                         .WithTaggedFlag("DMACRxCR.RPF (DMA Rx Channel Packet Flush)", 31)
147                     },
148                     {(long)RegistersDMAChannel.TxDescriptorListAddress  + offset, new DoubleWordRegister(parent)
149                         .WithValueField(0, 32, out txDescriptorRingStart, writeCallback: (_, __) =>
150                         {
151                             txDescriptorRingCurrent.Value = txDescriptorRingStart.Value;
152                         },
153                         name: "DMACTxDLAR.TDESLA (Start of Transmit List)")
154                     },
155                     {(long)RegistersDMAChannel.RxDescriptorListAddress + offset, new DoubleWordRegister(parent)
156                         .WithValueField(0, 32, out rxDescriptorRingStart, writeCallback: (_, __) =>
157                         {
158                             rxDescriptorRingCurrent.Value = rxDescriptorRingStart.Value;
159                         },
160                         name: "DMACRxDLAR.RDESLA (Start of Receive List)")
161                     },
162                     {(long)RegistersDMAChannel.TxDescriptorTailPointer + offset, new DoubleWordRegister(parent)
163                         .WithValueField(0, 32, out txDescriptorRingTail, changeCallback: (previousValue, _) =>
164                         {
165                             var clearTxFinishedRing = txDescriptorRingTail.Value != txDescriptorRingCurrent.Value;
166                             if((txState & DMAState.Suspended) != 0 || clearTxFinishedRing)
167                             {
168                                 txFinishedRing &= !clearTxFinishedRing;
169                                 StartTx();
170                             }
171                             this.Log(LogLevel.Debug, "Transmit Tail register (DMACTxDTPR.TDT) set to: 0x{0:X}", txDescriptorRingTail.Value);
172                         }, name: "DMACTxDTPR.TDT (Transmit Descriptor Tail Pointer)")
173                     },
174                     {(long)RegistersDMAChannel.RxDescriptorTailPointer + offset, new DoubleWordRegister(parent)
175                         .WithValueField(0, 32, out rxDescriptorRingTail, changeCallback: (previousValue, _) =>
176                         {
177                             var clearRxFinishedRing = rxDescriptorRingTail.Value != rxDescriptorRingCurrent.Value;
178                             if((rxState & DMAState.Suspended) != 0 || clearRxFinishedRing)
179                             {
180                                 rxFinishedRing &= !clearRxFinishedRing;
181                                 StartRx();
182                             }
183                             this.Log(LogLevel.Debug, "Receive Tail register (DMACRxDTPR.RDT) set to: 0x{0:X}", rxDescriptorRingTail.Value);
184                         }, name: "DMACRxDTPR.RDT (Receive Descriptor Tail Pointer)")
185                     },
186                     {(long)RegistersDMAChannel.TxDescriptorRingLength + offset, new DoubleWordRegister(parent)
187                         .WithValueField(0, 10, out txDescriptorRingLength, name: "DMACTxRLR.TDRL (Transmit Descriptor Ring Length)")
188                         .WithReservedBits(10, 22)
189                     },
190                     {(long)RegistersDMAChannel.RxDescriptorRingLength + offset, new DoubleWordRegister(parent)
191                         .WithValueField(0, 10, out rxDescriptorRingLength, name: "DMACRxRLR.RDRL (Receive Descriptor Ring Length)")
192                         .WithReservedBits(10, 6)
193                         .WithValueField(16, 8, out alternateRxBufferSize, name: "DMACRxRLR.ARBS (Alternate Receive Buffer Size)")
194                         .WithReservedBits(24, 8)
195                     },
196                     {(long)RegistersDMAChannel.InterruptEnable + offset, new DoubleWordRegister(parent)
197                         .WithFlag(0, out txInterruptEnable, name: "DMACIER.TIE (Transmit Interrupt Enable)")
198                         .WithFlag(1, out txProcessStoppedEnable, name: "DMACIER.TXSE (Transmit Stopped Enable)")
199                         .WithFlag(2, out txBufferUnavailableEnable, name: "DMACIER.TBUE (Transmit Buffer Unavailable Enable)")
200                         .WithReservedBits(3, 3)
201                         .WithFlag(6, out rxInterruptEnable, name: "DMACIER.RIE (Receive Interrupt Enable)")
202                         .WithFlag(7, out rxBufferUnavailableEnable, name: "DMACIER.RBUE (Receive Buffer Unavailable Enable)")
203                         .WithFlag(8, out rxProcessStoppedEnable, name: "DMACIER.RSE (Receive Stopped Enable)")
204                         .WithFlag(9, out rxWatchdogTimeoutEnable, name: "DMACIER.RWTE (Receive Watchdog Timeout Enable)")
205                         .WithFlag(10, out earlyTxInterruptEnable, name: "DMACIER.ETIE (Early Transmit Interrupt Enable)")
206                         .WithFlag(11, out earlyRxInterruptEnable, name: "DMACIER.ERIE (Early Receive Interrupt Enable)")
207                         .WithFlag(12, out fatalBusErrorEnable, name: "DMACIER.FBEE (Fatal Bus Error Enable)")
208                         .WithFlag(13, out contextDescriptorErrorEnable, name: "DMACIER.CDEE (Context Descriptor Error Enable)")
209                         .WithFlag(14, out abnormalInterruptSummaryEnable, name: "DMACIER.AIE (Abnormal Interrupt Summary Enable)")
210                         .WithFlag(15, out normalInterruptSummaryEnable, name: "DMACIER.NIE (Normal Interrupt Summary Enable)")
211                         .WithReservedBits(16, 16)
212                         .WithChangeCallback((_, __) => parent.UpdateInterrupts())
213                     },
214                     {(long)RegistersDMAChannel.RxInterruptWatchdogTimer + offset, new DoubleWordRegister(parent)
215                         .WithValueField(0, 8, out rxWatchdogCounter, changeCallback: (_, __) =>
216                         {
217                             rxWatchdog.Limit = rxWatchdogCounter.Value;
218                         },
219                         name: "DMACRxIWTR.RWT (Receive Interrupt Watchdog Timer Count)")
220                         .WithReservedBits(8, 8)
221                         .WithValueField(16, 2, out rxWatchdogCounterUnit, changeCallback: (_, __) =>
222                         {
223                             rxWatchdog.Divider = RxWatchdogDivider << (byte)rxWatchdogCounterUnit.Value;
224                         },
225                         name: "DMACRxIWTR.RWTU (Receive Interrupt Watchdog Timer Count Units)")
226                         .WithReservedBits(18, 14)
227                     },
228                     {(long)RegistersDMAChannel.CurrentApplicationTransmitDescriptor + offset, new DoubleWordRegister(parent)
229                         .WithValueField(0, 32, out txDescriptorRingCurrent, FieldMode.Read, name: "DMACCATxDR.CURTDESAPTR (Application Transmit Descriptor Address Pointer)")
230                     },
231                     {(long)RegistersDMAChannel.CurrentApplicationReceiveDescriptor + offset, new DoubleWordRegister(parent)
232                         .WithValueField(0, 32, out rxDescriptorRingCurrent, FieldMode.Read, name: "DMACCARxDR.CURRDESAPTR (Application Receive Descriptor Address Pointer)")
233                     },
234                     {(long)RegistersDMAChannel.CurrentApplicationTransmitBuffer + offset, new DoubleWordRegister(parent)
235                         .WithValueField(0, 32, out txCurrentBuffer, FieldMode.Read, name: "DMACCATxBR.CURTBUFAPTR (Application Transmit Buffer Address Pointer)")
236                     },
237                     {(long)RegistersDMAChannel.CurrentApplicationReceiveBuffer + offset, new DoubleWordRegister(parent)
238                         .WithValueField(0, 32, out rxCurrentBuffer, FieldMode.Read, name: "DMACCARxBR.CURRBUFAPTR (Application Receive Buffer Address Pointer)")
239                     },
240                     {(long)RegistersDMAChannel.Status + offset, new DoubleWordRegister(parent)
241                         .WithFlag(0, out txInterrupt, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMACSR.TI (Transmit Interrupt)")
242                         .WithFlag(1, out txProcessStopped, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMACSR.TPS (Transmit Process Stopped)")
243                         .WithFlag(2, out txBufferUnavailable, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMACSR.TBU (Transmit Buffer Unavailable)")
244                         .WithReservedBits(3, 3)
245                         .WithFlag(6, out rxInterrupt, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMACSR.RI (Receive Interrupt)")
246                         .WithFlag(7, out rxBufferUnavailable, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMACSR.RBU (Receive Buffer Unavailable)")
247                         .WithFlag(8, out rxProcessStopped, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMACSR.RPS (Receive Process Stopped)")
248                         .WithFlag(9, out rxWatchdogTimeout, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMACSR.RWT (Receive Watchdog Timeout)")
249                         .WithFlag(10, out earlyTxInterrupt, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMACSR.ET (Early Transmit Interrupt)")
250                         .WithFlag(11, out earlyRxInterrupt, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMACSR.ER (Early Receive Interrupt)")
251                         .WithFlag(12, out fatalBusError, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMACSR.FBE (Fatal Bus Error)")
252                         .WithFlag(13, out contextDescriptorError, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMACSR.CDE (Context Descriptor Error)")
253                         .WithFlag(14, out abnormalInterruptSummary, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMACSR.AIS (Abnormal Interrupt Summary)")
254                         .WithFlag(15, out normalInterruptSummary, FieldMode.Read | FieldMode.WriteOneToClear, name: "DMACSR.NIS (Normal Interrupt Summary)")
255                         .WithTag("DMACSR.TEB (Tx DMA Error Bits)", 16, 3)
256                         .WithTag("DMACSR.REB (Rx DMA Error Bits)", 19, 3)
257                         .WithReservedBits(22, 10)
258                         .WithChangeCallback((_, __) => parent.UpdateInterrupts())
259                     },
260                     {(long)RegistersDMAChannel.MissedFrameCount + offset, new DoubleWordRegister(parent)
261                         .WithTag("DMACMFCR.MFC (Dropped Packet Counters)", 0, 11)
262                         .WithReservedBits(11, 4)
263                         .WithTaggedFlag("DMACMFCR.MFCO (Overflow status of the MFC Counter)", 15)
264                         .WithReservedBits(16, 16)
265                     }
266                 }).ToDictionary(x => x.Key, x => x.Value);
267             }
268 
ReceiveFrame(EthernetFrame frame)269             public void ReceiveFrame(EthernetFrame frame)
270             {
271                 if(rxQueueLength + frame.Length > parent.RxQueueSize)
272                 {
273                     parent.IncrementPacketCounter(parent.rxFifoPacketCounter, parent.rxFifoPacketCounterInterrupt);
274                     this.Log(LogLevel.Debug, "Receive: Dropping overflow frame {0}", frame);
275                     parent.UpdateInterrupts();
276                     return;
277                 }
278 
279                 this.Log(LogLevel.Debug, "Receive: Incoming frame {0}", frame);
280                 incomingFrames.Enqueue(frame);
281                 rxQueueLength += frame.Bytes.Length;
282                 StartRx();
283             }
284 
UpdateInterrupts()285             public bool UpdateInterrupts()
286             {
287                 // Depending on the DMA interrupt mode transmit/receive completed condition may not
288                 // contribute to the common interrupt. Those masks are used to block those conditions from
289                 // influencing the common interrupt state.
290                 var txMask = true;
291                 var rxMask = true;
292 
293                 if(hasInterrupts)
294                 {
295                     switch(parent.dmaInterruptMode.Value)
296                     {
297                         case DMAChannelInterruptMode.Pulse:
298                             if(txInterrupt.Value)
299                             {
300                                 TxIRQ.Blink();
301                                 this.Log(LogLevel.Debug, "Blinking TxIRQ");
302                             }
303                             if(rxInterrupt.Value)
304                             {
305                                 RxIRQ.Blink();
306                                 this.Log(LogLevel.Debug, "Blinking RxIRQ");
307                             }
308                             break;
309                         case DMAChannelInterruptMode.Level:
310                         case DMAChannelInterruptMode.LevelAndReassert:
311                         {
312                             var txState = txInterrupt.Value && txInterruptEnable.Value && normalInterruptSummaryEnable.Value;
313                             var rxState = rxInterrupt.Value && rxInterruptEnable.Value && normalInterruptSummaryEnable.Value;
314                             TxIRQ.Set(txState);
315                             RxIRQ.Set(rxState);
316                             this.Log(LogLevel.Debug, "TxIRQ: {0}, RxIRQ: {1}", txState ? "setting" : "unsetting", rxState ? "setting" : "unsetting");
317                             // In both Level and LevelAndReassert transmit/receive completed conditions are only used for channel specific interrupts
318                             // and don't contribute to the common interrupt. Mask their influence
319                             txMask = false;
320                             rxMask = false;
321                             break;
322                         }
323                         default:
324                             this.Log(LogLevel.Warning, "Invalid interrupt mode value {0}", parent.dmaInterruptMode.Value);
325                             break;
326                     }
327                 }
328 
329                 normalInterruptSummary.Value |= GetNormalInterruptSummary();
330                 abnormalInterruptSummary.Value |= (txProcessStopped.Value && txProcessStoppedEnable.Value) ||
331                                                   (rxBufferUnavailable.Value && rxBufferUnavailableEnable.Value) ||
332                                                   (rxProcessStopped.Value && rxProcessStoppedEnable.Value) ||
333                                                   (earlyTxInterrupt.Value && earlyTxInterruptEnable.Value) ||
334                                                   (fatalBusError.Value && fatalBusErrorEnable.Value) ||
335                                                   (contextDescriptorError.Value && contextDescriptorErrorEnable.Value);
336 
337                 return (rxWatchdogTimeout.Value && rxWatchdogTimeoutEnable.Value)               ||
338                        (abnormalInterruptSummary.Value && abnormalInterruptSummaryEnable.Value) ||
339                        (GetNormalInterruptSummary(txMask, rxMask) && normalInterruptSummaryEnable.Value);
340             }
341 
342             public DMATxProcessState DmaTxState => (txState == DMAState.Stopped) ? DMATxProcessState.Stopped : DMATxProcessState.Suspended;
343             public DMARxProcessState DmaRxState => (rxState == DMAState.Stopped) ? DMARxProcessState.Stopped :
344                                             ((incomingFrames.Count == 0) ? DMARxProcessState.WaitingForPacket : DMARxProcessState.Suspended);
345 
346             public bool Interrupts =>
347                 txInterrupt.Value ||
348                 txProcessStopped.Value ||
349                 txBufferUnavailable.Value ||
350                 rxInterrupt.Value ||
351                 rxBufferUnavailable.Value ||
352                 rxProcessStopped.Value ||
353                 rxWatchdogTimeout.Value ||
354                 earlyTxInterrupt.Value ||
355                 earlyRxInterrupt.Value ||
356                 fatalBusError.Value ||
357                 abnormalInterruptSummary.Value ||
358                 normalInterruptSummary.Value;
359 
360             public GPIO TxIRQ { get; }
361             public GPIO RxIRQ { get; }
362 
GetTxDescriptor(ulong index = 0)363             private TxDescriptor GetTxDescriptor(ulong index = 0)
364             {
365                 var descriptor = new TxDescriptor(parent.Bus, txDescriptorRingCurrent.Value, parent.CpuContext);
366                 descriptor.Fetch();
367                 return descriptor;
368             }
369 
GetRxDescriptor()370             private RxDescriptor GetRxDescriptor()
371             {
372                 var descriptor = new RxDescriptor(parent.Bus, rxDescriptorRingCurrent.Value, parent.CpuContext);
373                 descriptor.Fetch();
374                 return descriptor;
375             }
376 
IncreaseTxDescriptorPointer()377             private void IncreaseTxDescriptorPointer()
378             {
379                 IncreaseDescriptorPointer(txDescriptorRingCurrent, txDescriptorRingStart, txDescriptorRingLength, "TX");
380                 txFinishedRing = txDescriptorRingCurrent.Value == txDescriptorRingTail.Value;
381             }
382 
IncreaseRxDescriptorPointer()383             private void IncreaseRxDescriptorPointer()
384             {
385                 IncreaseDescriptorPointer(rxDescriptorRingCurrent, rxDescriptorRingStart, rxDescriptorRingLength, "RX");
386                 rxFinishedRing = rxDescriptorRingCurrent.Value == rxDescriptorRingTail.Value;
387             }
388 
IncreaseDescriptorPointer(IValueRegisterField current, IValueRegisterField start, IValueRegisterField length, string name)389             private void IncreaseDescriptorPointer(IValueRegisterField current, IValueRegisterField start, IValueRegisterField length, string name)
390             {
391                 var size = descriptorSkipLength.Value * (ulong)parent.DMABusWidth + Descriptor.Size;
392                 var offset = current.Value - start.Value;
393                 offset += size;
394                 // The docs state that: "If you want to have 10 descriptors, program it to a value of 0x9" - so it always should be +1 descriptor than obtained from the register
395                 offset %= (length.Value + 1) * size;
396                 this.Log(LogLevel.Noisy, "{0} Descriptor pointer was 0x{1:X}, now is 0x{2:X}, size 0x{3:X}, ring length 0x{4:x}", name, current.Value, start.Value + offset, size, length.Value);
397                 current.Value = start.Value + offset;
398             }
399 
TriggerRxWatchdog()400             private void TriggerRxWatchdog()
401             {
402                 rxWatchdog.Value = rxWatchdogCounter.Value;
403                 rxWatchdog.Enabled = rxWatchdogCounter.Value != 0 || rxWatchdogCounterUnit.Value != 0;
404             }
405 
StartRx()406             private void StartRx()
407             {
408                 if(!parent.rxEnable.Value)
409                 {
410                     rxState = DMAState.Stopped;
411                     this.Log(LogLevel.Noisy, "Receive: Rx DMA is not enabled.");
412                     return;
413                 }
414                 if(!startRx.Value)
415                 {
416                     rxState = DMAState.Stopped;
417                     this.Log(LogLevel.Noisy, "Receive: Rx DMA is not started.");
418                     return;
419                 }
420                 if(rxState == DMAState.Stopped)
421                 {
422                     rxState = DMAState.Running;
423                     rxDescriptorRingCurrent.Value = rxDescriptorRingStart.Value;
424                     this.Log(LogLevel.Debug, "Receive: Starting DMA at 0x{0:X}.", rxDescriptorRingCurrent.Value);
425                 }
426                 else
427                 {
428                     this.Log(LogLevel.Debug, "Receive: Resuming DMA at 0x{0:X}.", rxDescriptorRingCurrent.Value);
429                 }
430 
431                 if(incomingFrames.Count == 0)
432                 {
433                     this.Log(LogLevel.Noisy, "Receive: No frames to process.");
434                     rxState |= DMAState.Suspended;
435                     return;
436                 }
437                 var frame = incomingFrames.Peek();
438                 var bytes = frame.Bytes;
439                 var isFirst = true;
440                 while(!rxFinishedRing && parent.rxEnable.Value && startRx.Value)
441                 {
442                     var descriptor = GetRxDescriptor();
443 
444                     if(!descriptor.IsOwnedByDMA.Value)
445                     {
446                         this.Log(LogLevel.Debug, "Receive: Loaded descriptor is not owned by DMA.");
447                         rxBufferUnavailable.Value = true;
448                         rxState |= DMAState.Suspended;
449                         break;
450                     }
451                     rxState &= ~DMAState.Suspended;
452                     var structure = descriptor.GetNormalReadDescriptor();
453     #if DEBUG
454                     this.Log(LogLevel.Noisy, "Receive: Loaded {0} from 0x{1:X}.", structure, descriptor.Address);
455     #endif
456 
457                     var bufferAddress = 0UL;
458                     var bufferSize = 0UL;
459                     var invalidDescriptor = structure.buffer1Address == UInt32.MaxValue || structure.buffer2Address == UInt32.MaxValue;
460                     if(!invalidDescriptor && structure.buffer1Address != 0 && structure.buffer1AddressValid)
461                     {
462                         bufferAddress = structure.buffer1Address;
463                         bufferSize = RxBuffer1Size;
464                     }
465                     else if(!invalidDescriptor && structure.buffer2Address != 0 && structure.buffer2AddressValid)
466                     {
467                         bufferAddress = structure.buffer2Address;
468                         bufferSize = RxBuffer2Size;
469                     }
470                     else
471                     {
472                         contextDescriptorError.Value |= invalidDescriptor;
473                         this.Log(LogLevel.Debug, "Receive: Loaded descriptor doesn't provide a valid buffer.");
474                         structure.owner = DescriptorOwner.Application;
475     #if DEBUG
476                         this.Log(LogLevel.Noisy, "Receive: Writing {0} to 0x{1:X}.", structure, descriptor.Address);
477     #endif
478                         descriptor.SetDescriptor(structure);
479                         descriptor.Write();
480                         IncreaseRxDescriptorPointer();
481                         continue;
482                     }
483                     rxCurrentBuffer.Value = bufferAddress;
484 
485                     if(isFirst)
486                     {
487                         earlyRxInterrupt.Value = true;
488                         parent.UpdateInterrupts();
489                     }
490 
491                     if(rxOffset >= (ulong)bytes.Length)
492                     {
493                         if(parent.enableTimestamp.Value && (parent.enableTimestampForAll.Value /* || is PTP */))
494                         {
495                             this.Log(LogLevel.Error, "Receive: Timestamping is not supported.");
496                             var contextStructure = descriptor.GetAsContextDescriptor();
497                             contextStructure.contextType = true;
498                             contextStructure.owner = DescriptorOwner.Application;
499     #if DEBUG
500                             this.Log(LogLevel.Noisy, "Receive: Writing {0} to 0x{1:X}.", contextStructure, descriptor.Address);
501     #endif
502                             descriptor.SetDescriptor(contextStructure);
503                             descriptor.Write();
504                             IncreaseRxDescriptorPointer();
505                         }
506                         rxOffset = 0;
507                         incomingFrames.Dequeue();
508                         rxQueueLength -= bytes.Length;
509 
510                         if(incomingFrames.Count == 0)
511                         {
512                             this.Log(LogLevel.Noisy, "Receive: Finished handling frame, no more frames to process.");
513                             break;
514                         }
515                         this.Log(LogLevel.Noisy, "Receive: Finished handling frame, processing next frame.");
516                         frame = incomingFrames.Peek();
517                         isFirst = true;
518                         bytes = frame.Bytes;
519                         continue;
520                     }
521 
522                     var bytesWritten = Math.Min((ulong)bytes.Length - rxOffset, bufferSize);
523                     parent.Bus.WriteBytes(bytes, bufferAddress, (int)rxOffset, (long)bytesWritten, true, parent.CpuContext);
524                     this.Log(LogLevel.Noisy, "Receive: Writing frame[0x{0:X}, 0x{1:X}) at 0x{2:X}.", rxOffset, rxOffset + bytesWritten, bufferAddress);
525                     rxOffset += bytesWritten;
526 
527                     var writeBackStructure = descriptor.GetAsNormalWriteBackDescriptor();
528                     writeBackStructure.owner = DescriptorOwner.Application;
529                     writeBackStructure.firstDescriptor = isFirst;
530                     writeBackStructure.lastDescriptor = rxOffset == (ulong)bytes.Length;
531                     writeBackStructure.contextType = false;;
532                     writeBackStructure.receiveStatusSegment0Valid = true;
533                     writeBackStructure.receiveStatusSegment1Valid = true;
534                     writeBackStructure.receiveStatusSegment2Valid = true;
535                     isFirst = false;
536 
537                     writeBackStructure.packetLength = (uint)bytes.Length;
538                     writeBackStructure.outerVlanTag = 0x0;
539                     writeBackStructure.innerVlanTag = 0x0;
540                     writeBackStructure.oamSubtypeCodeOrMACControlPacketOpcode = (uint)frame.UnderlyingPacket.Type;
541                     writeBackStructure.ipHeaderError = false;
542                     writeBackStructure.ipv4HeaderPresent = frame.UnderlyingPacket.Type == EthernetPacketType.IpV4;
543                     writeBackStructure.ipv6HeaderPresent = frame.UnderlyingPacket.Type == EthernetPacketType.IpV6;
544                     if(writeBackStructure.ipv4HeaderPresent || writeBackStructure.ipv6HeaderPresent)
545                     {
546                         switch(((IpPacket)frame.UnderlyingPacket.PayloadPacket).NextHeader)
547                         {
548                             case IPProtocolType.UDP:
549                                 writeBackStructure.payloadType = PayloadType.UDP;
550                                 break;
551                             case IPProtocolType.TCP:
552                                 writeBackStructure.payloadType = PayloadType.TCP;
553                                 break;
554                             case IPProtocolType.ICMP:
555                             case IPProtocolType.ICMPV6:
556                                 writeBackStructure.payloadType = PayloadType.ICMP;
557                                 break;
558                             case IPProtocolType.IGMP:
559                                 if(!writeBackStructure.ipv4HeaderPresent)
560                                 {
561                                     goto default;
562                                 }
563                                 writeBackStructure.payloadType = PayloadType.IGMPIPV4;
564                                 break;
565                             default:
566                                 writeBackStructure.payloadType = PayloadType.Unknown;
567                                 break;
568                         }
569                     }
570 
571                     // NOTE: VLAN tagging is not supported by PacketDotNet, the `Type` may contain a VLAN tag
572                     switch(frame.UnderlyingPacket.Type)
573                     {
574                         case EthernetPacketType.Arp:
575                             writeBackStructure.lengthTypeField = PacketKind.ARPRequest;
576                             break;
577                         case EthernetPacketType.MacControl:
578                             writeBackStructure.lengthTypeField = PacketKind.MACControlPacket;
579                             break;
580                         case EthernetPacketType.VLanTaggedFrame:
581                             writeBackStructure.lengthTypeField = PacketKind.TypePacketWithVLANTag;
582                             break;
583                         case EthernetPacketType.ProviderBridging:
584                             writeBackStructure.lengthTypeField = PacketKind.TypePacketWithDoubleVLANTag;
585                             break;
586                         case EthernetPacketType.ConnectivityFaultManagementOrOperationsAdministrationManagement:
587                             writeBackStructure.lengthTypeField = PacketKind.OAMPacket;
588                             break;
589                         default:
590                             writeBackStructure.lengthTypeField = (uint)frame.UnderlyingPacket.Type < EtherTypeMinimalValue ? PacketKind.LengthPacket : PacketKind.TypePacket;
591                             break;
592                     }
593 
594                     writeBackStructure.timestampAvailable = parent.enableTimestamp.Value;
595                     writeBackStructure.timestampDropped = false;
596                     writeBackStructure.dribbleBitError = false;
597                     writeBackStructure.receiveError = false;
598                     writeBackStructure.overflowError = false;
599                     writeBackStructure.receiveWatchdogTimeout = false;
600                     writeBackStructure.giantPacket = false;
601                     writeBackStructure.crcError = parent.crcCheckDisable.Value ? false : !EthernetFrame.CheckCRC(bytes);
602                     writeBackStructure.errorSummary = new bool[]
603                     {
604                         writeBackStructure.dribbleBitError,
605                         writeBackStructure.receiveError,
606                         writeBackStructure.overflowError,
607                         writeBackStructure.receiveWatchdogTimeout,
608                         writeBackStructure.giantPacket,
609                         writeBackStructure.crcError,
610                     }.Any(x => x);
611     #if DEBUG
612                     this.Log(LogLevel.Noisy, "Receive: Writing {0} to 0x{1:X}.", writeBackStructure, descriptor.Address);
613     #endif
614                     descriptor.SetDescriptor(writeBackStructure);
615                     descriptor.Write();
616                     IncreaseRxDescriptorPointer();
617 
618                     if(!writeBackStructure.lastDescriptor)
619                     {
620                         continue;
621                     }
622 
623                     if(structure.interruptOnCompletion)
624                     {
625                         rxInterrupt.Value = true;
626                         rxWatchdog.Enabled = false;
627                     }
628                     else
629                     {
630                         TriggerRxWatchdog();
631                     }
632                     earlyRxInterrupt.Value = false;
633                     parent.UpdateRxCounters(frame, writeBackStructure);
634                     this.Log(LogLevel.Noisy, "Receive: Frame fully processed.");
635                 }
636                 if(!parent.rxEnable.Value || !startRx.Value)
637                 {
638                     rxProcessStopped.Value = true;
639                     rxState = DMAState.Stopped;
640                     this.Log(LogLevel.Debug, "Receive: Stopping Rx DMA at 0x{0:X}.", rxDescriptorRingCurrent.Value);
641                 }
642                 else
643                 {
644                     if(rxFinishedRing)
645                     {
646                         this.Log(LogLevel.Noisy, "Receive: Descriptor ring is empty.");
647                     }
648                     rxBufferUnavailable.Value |= rxFinishedRing || incomingFrames.Count != 0;
649                     rxState |= DMAState.Suspended;
650                     this.Log(LogLevel.Debug, "Receive: Suspending Rx DMA at 0x{0:X}.", rxDescriptorRingCurrent.Value);
651                 }
652                 parent.UpdateInterrupts();
653             }
654 
StartTx()655             private void StartTx()
656             {
657                 if(!parent.txEnable.Value)
658                 {
659                     txState = DMAState.Stopped;
660                     this.Log(LogLevel.Noisy, "Transmission: Tx DMA is not enabled.");
661                     return;
662                 }
663                 if(!startTx.Value)
664                 {
665                     txState = DMAState.Stopped;
666                     this.Log(LogLevel.Noisy, "Transmission: Tx DMA is not started.");
667                     return;
668                 }
669                 if(txState == DMAState.Stopped)
670                 {
671                     txState |= DMAState.Running;
672                     txDescriptorRingCurrent.Value = txDescriptorRingStart.Value;
673                     this.Log(LogLevel.Debug, "Transmission: Starting Tx DMA at 0x{0:X}.", txDescriptorRingCurrent.Value);
674                 }
675                 else
676                 {
677                     this.Log(LogLevel.Debug, "Transmission: Resuming Tx DMA at 0x{0:X}.", txDescriptorRingCurrent.Value);
678                 }
679 
680                 while(!txFinishedRing && parent.txEnable.Value && startTx.Value)
681                 {
682                     var descriptor = GetTxDescriptor();
683 
684                     if(!descriptor.IsOwnedByDMA.Value)
685                     {
686                         this.Log(LogLevel.Debug, "Transmission: Loaded descriptor is not owned by DMA.");
687                         txProcessStopped.Value = true;
688                         txBufferUnavailable.Value = true;
689                         txState |= DMAState.Suspended;
690                         this.Log(LogLevel.Debug, "Transmission: Suspending Tx DMA at 0x{0:X}.", txDescriptorRingCurrent.Value);
691                         break;
692                     }
693                     txState &= ~DMAState.Suspended;
694                     if(descriptor.Type.Is<TxDescriptor.NormalReadDescriptor>())
695                     {
696                         var structure = descriptor.GetNormalReadDescriptor();
697     #if DEBUG
698                         this.Log(LogLevel.Noisy, "Transmission: Loaded {0} from 0x{1:X}.", structure, descriptor.Address);
699     #endif
700                         if(frameAssembler == null && !structure.firstDescriptor)
701                         {
702                             this.Log(LogLevel.Warning, "Transmission: Building frame without first descriptor.");
703                             break;
704                         }
705                         else if(frameAssembler != null && structure.firstDescriptor)
706                         {
707                             this.Log(LogLevel.Warning, "Transmission: Building new frame without clearing last frame.");
708                         }
709 
710                         var buffer = structure.FetchBuffer1OrHeader(parent.Bus, parent.CpuContext);
711                         txCurrentBuffer.Value = structure.buffer1OrHeaderAddress;
712                         var tsoEnabled = structure.tcpSegmentationEnable && tcpSegmentationEnable.Value;
713 
714                         MACAddress? sourceAddress = null;
715                         switch(structure.sourceAddressControl)
716                         {
717                             case DescriptorSourceAddressOperation.MACAddressRegister0Insert:
718                             case DescriptorSourceAddressOperation.MACAddressRegister0Replace:
719                                 sourceAddress = parent.MAC0;
720                                 break;
721                             case DescriptorSourceAddressOperation.MACAddressRegister1Insert:
722                             case DescriptorSourceAddressOperation.MACAddressRegister1Replace:
723                                 sourceAddress = parent.MAC1;
724                                 break;
725                             default:
726                                 sourceAddress = null;
727                                 break;
728                         }
729 
730                         if(!sourceAddress.HasValue)
731                         {
732                             switch(parent.sourceAddressOperation.Value)
733                             {
734                                 case RegisterSourceAddressOperation.MACAddressRegister0Insert:
735                                 case RegisterSourceAddressOperation.MACAddressRegister0Replace:
736                                     sourceAddress = parent.MAC0;
737                                     break;
738                                 case RegisterSourceAddressOperation.MACAddressRegister1Insert:
739                                 case RegisterSourceAddressOperation.MACAddressRegister1Replace:
740                                     sourceAddress = parent.MAC1;
741                                     break;
742                                 default:
743                                     this.Log(LogLevel.Error, "Using a reserved value in ETH_MACCR.SARC register.");
744                                     break;
745                             }
746                         }
747 
748                         if(structure.firstDescriptor)
749                         {
750                             if(tsoEnabled)
751                             {
752                                 frameAssembler = new FrameAssembler(
753                                     parent,
754                                     buffer,
755                                     (uint)maximumSegmentSize.Value,
756                                     latestTxContext,
757                                     parent.checksumOffloadEnable.Value,
758                                     parent.SendFrame,
759                                     sourceAddress
760                                 );
761                                 buffer = structure.FetchBuffer2OrBuffer1(parent.Bus, parent.CpuContext);
762                                 txCurrentBuffer.Value = structure.buffer2orBuffer1Address;
763                             }
764                             else
765                             {
766                                 frameAssembler = new FrameAssembler(
767                                     parent,
768                                     structure.crcPadControl,
769                                     parent.checksumOffloadEnable.Value ? structure.checksumControl : ChecksumOperation.None,
770                                     parent.SendFrame
771                                 );
772                             }
773                         }
774                         if(structure.buffer2Length != 0 && !tsoEnabled)
775                         {
776                             // Though it's not clearly stated in the documentation, the STM driver
777                             // expects buffer2 to be valid even with TSO disabled. In this case,
778                             // concatenate two buffers when assembling frame to be sent.
779                             frameAssembler.PushPayload(buffer);
780                             buffer = structure.FetchBuffer2OrBuffer1(parent.Bus, parent.CpuContext);
781                             txCurrentBuffer.Value = structure.buffer2orBuffer1Address;
782                         }
783                         frameAssembler.PushPayload(buffer);
784                         earlyTxInterrupt.Value = true;
785 
786                         if(!structure.lastDescriptor)
787                         {
788                             txState |= DMAState.ProcessingIntermediate;
789                             var writeBackIntermediateStructure = new TxDescriptor.NormalWriteBackDescriptor();
790                             writeBackIntermediateStructure.owner = DescriptorOwner.Application;
791     #if DEBUG
792                             this.Log(LogLevel.Noisy, "Transmission: Writing intermediate {0} to 0x{1:X}.", writeBackIntermediateStructure, descriptor.Address);
793     #endif
794                             descriptor.SetDescriptor(writeBackIntermediateStructure);
795                             descriptor.Write();
796                             IncreaseTxDescriptorPointer();
797                             continue;
798                         }
799 
800                         frameAssembler.FinalizeAssembly();
801                         frameAssembler = null;
802 
803                         if((txState & DMAState.ProcessingSecond) == 0 && operateOnSecondPacket.Value)
804                         {
805                             txState |= DMAState.ProcessingSecond;
806                             continue;
807                         }
808 
809                         var writeBackStructure = new TxDescriptor.NormalWriteBackDescriptor();
810                         writeBackStructure.ipHeaderError = false;
811                         writeBackStructure.deferredBit = false;
812                         writeBackStructure.underflowError = structure.buffer1OrHeaderAddress == 0x0 || structure.headerOrBuffer1Length == 0x0;
813                         writeBackStructure.excessiveDeferral = false;
814                         writeBackStructure.collisionCount = false;
815                         writeBackStructure.excessiveCollision = false;
816                         writeBackStructure.lateCollision = false;
817                         writeBackStructure.noCarrier = false;
818                         writeBackStructure.lossOfCarrier = false;
819                         writeBackStructure.payloadChecksumError = false;
820                         writeBackStructure.packetFlushed = false;
821                         writeBackStructure.jabberTimeout = false;
822                         writeBackStructure.errorSummary = new bool[]
823                         {
824                             writeBackStructure.ipHeaderError,
825                             writeBackStructure.jabberTimeout,
826                             writeBackStructure.packetFlushed,
827                             writeBackStructure.payloadChecksumError,
828                             writeBackStructure.lossOfCarrier,
829                             writeBackStructure.noCarrier,
830                             writeBackStructure.lateCollision,
831                             writeBackStructure.excessiveCollision,
832                             writeBackStructure.excessiveDeferral,
833                             writeBackStructure.underflowError,
834                         }.Any(x => x);
835                         writeBackStructure.txTimestampCaptured = false;
836                         writeBackStructure.owner = DescriptorOwner.Application;
837                         writeBackStructure.firstDescriptor = structure.firstDescriptor;
838                         writeBackStructure.lastDescriptor = structure.lastDescriptor;
839 
840                         if(structure.transmitTimestampEnable && parent.enableTimestamp.Value)
841                         {
842                             this.Log(LogLevel.Error, "Transmission: Timestamping is not supported.");
843                         }
844     #if DEBUG
845                         this.Log(LogLevel.Noisy, "Transmission: Writing {0} to 0x{1:X}.", writeBackStructure, descriptor.Address);
846     #endif
847                         descriptor.SetDescriptor(writeBackStructure);
848 
849                         if(structure.interruptOnCompletion)
850                         {
851                             txInterrupt.Value = true;
852                         }
853                     }
854                     else if(descriptor.Type.Is<TxDescriptor.ContextDescriptor>())
855                     {
856                         var structure = descriptor.GetContextDescriptor();
857                         latestTxContext = structure;
858     #if DEBUG
859                         this.Log(LogLevel.Noisy, "Transmission: Loaded {0} from 0x{1:X}.", structure, descriptor.Address);
860     #endif
861                         if(structure.oneStepTimestampCorrectionEnable && structure.oneStepTimestampCorrectionInputOrMaximumSegmentSizeValid)
862                         {
863                             this.Log(LogLevel.Error, "Transmission: Timestamping is not supported. One Step Timestamp Correction failed.");
864                         }
865                         structure.owner = DescriptorOwner.Application;
866     #if DEBUG
867                         this.Log(LogLevel.Noisy, "Transmission: Writing {0} to 0x{1:X}.", structure, descriptor.Address);
868     #endif
869                         descriptor.SetDescriptor(structure);
870                     }
871                     else
872                     {
873                         throw new RecoverableException("Unreachable");
874                     }
875                     descriptor.Write();
876                     IncreaseTxDescriptorPointer();
877                 }
878 
879                 if(txFinishedRing)
880                 {
881                     txBufferUnavailable.Value = true;
882                     txState |= DMAState.Suspended;
883                     this.Log(LogLevel.Debug, "Transmission: Descriptor ring is empty.");
884                 }
885                 if(!parent.txEnable.Value || !startTx.Value)
886                 {
887                     txState = DMAState.Stopped;
888                     txProcessStopped.Value = true;
889                     this.Log(LogLevel.Debug, "Transmission: Stopping Tx DMA at 0x{0:X}.", txDescriptorRingCurrent.Value);
890                 }
891                 parent.UpdateInterrupts();
892             }
893 
Log(LogLevel level, string format, params object[] args)894             private void Log(LogLevel level, string format, params object[] args)
895             {
896                 parent.Log(level, $"DMA Channel {channelNumber}: {format}", args);
897             }
898 
GetNormalInterruptSummary(bool txMask = true, bool rxMask = true)899             private bool GetNormalInterruptSummary(bool txMask = true, bool rxMask = true)
900             {
901                 return (txMask && txInterrupt.Value && txInterruptEnable.Value) ||
902                        (txBufferUnavailable.Value && txBufferUnavailableEnable.Value) ||
903                        (rxMask && rxInterrupt.Value && rxInterruptEnable.Value) ||
904                        (earlyRxInterrupt.Value && earlyRxInterruptEnable.Value);
905             }
906 
907             private ulong RxBuffer1Size => alternateRxBufferSize.Value == 0 ? rxBufferSize.Value : alternateRxBufferSize.Value;
908             private ulong RxBuffer2Size => rxBufferSize.Value;
909 
910             // TODO: Maybe remove those:
911             private ulong ProgrammableBurstLengthMultiplier => programmableBurstLengthTimes8.Value ? 8UL : 1UL;
912             private ulong TxProgrammableBurstLength => txProgrammableBurstLength.Value * ProgrammableBurstLengthMultiplier;
913             private ulong RxProgrammableBurstLength => rxProgrammableBurstLength.Value * ProgrammableBurstLengthMultiplier;
914 
915             private IValueRegisterField maximumSegmentSize;
916             private IFlagRegisterField programmableBurstLengthTimes8;
917             private IValueRegisterField descriptorSkipLength;
918             private IFlagRegisterField startTx;
919             private IFlagRegisterField operateOnSecondPacket;
920             private IFlagRegisterField tcpSegmentationEnable;
921             private IValueRegisterField txProgrammableBurstLength;
922             private IFlagRegisterField startRx;
923             private IValueRegisterField rxBufferSize;
924             private IValueRegisterField rxProgrammableBurstLength;
925             private IValueRegisterField txDescriptorRingStart;
926             private IValueRegisterField rxDescriptorRingStart;
927             private IValueRegisterField txDescriptorRingTail;
928             private IValueRegisterField rxDescriptorRingTail;
929             private IValueRegisterField txDescriptorRingLength;
930             private IValueRegisterField rxDescriptorRingLength;
931             private IValueRegisterField alternateRxBufferSize;
932             private IValueRegisterField txDescriptorRingCurrent;
933             private IValueRegisterField rxDescriptorRingCurrent;
934             private IValueRegisterField txCurrentBuffer;
935             private IValueRegisterField rxCurrentBuffer;
936 
937             private IFlagRegisterField txInterruptEnable;
938             private IFlagRegisterField txProcessStoppedEnable;
939             private IFlagRegisterField txBufferUnavailableEnable;
940             private IFlagRegisterField rxInterruptEnable;
941             private IFlagRegisterField rxBufferUnavailableEnable;
942             private IFlagRegisterField rxProcessStoppedEnable;
943             private IFlagRegisterField rxWatchdogTimeoutEnable;
944             private IFlagRegisterField earlyTxInterruptEnable;
945             private IFlagRegisterField earlyRxInterruptEnable;
946             private IFlagRegisterField fatalBusErrorEnable;
947             private IFlagRegisterField contextDescriptorErrorEnable;
948             private IFlagRegisterField abnormalInterruptSummaryEnable;
949             private IValueRegisterField rxWatchdogCounter;
950             private IValueRegisterField rxWatchdogCounterUnit;
951             private IFlagRegisterField normalInterruptSummaryEnable;
952             private IFlagRegisterField txInterrupt;
953             private IFlagRegisterField txProcessStopped;
954             private IFlagRegisterField txBufferUnavailable;
955             private IFlagRegisterField rxInterrupt;
956             private IFlagRegisterField rxBufferUnavailable;
957             private IFlagRegisterField rxProcessStopped;
958             private IFlagRegisterField rxWatchdogTimeout;
959             private IFlagRegisterField earlyTxInterrupt;
960             private IFlagRegisterField earlyRxInterrupt;
961             private IFlagRegisterField fatalBusError;
962             private IFlagRegisterField contextDescriptorError;
963             private IFlagRegisterField abnormalInterruptSummary;
964             private IFlagRegisterField normalInterruptSummary;
965 
966             private FrameAssembler frameAssembler;
967 
968             private bool rxFinishedRing = true;
969             private bool txFinishedRing = true;
970             private DMAState txState = DMAState.Stopped;
971             private DMAState rxState = DMAState.Stopped;
972             private ulong rxOffset;
973             private TxDescriptor.ContextDescriptor? latestTxContext;
974             private int rxQueueLength;
975 
976             private readonly Queue<EthernetFrame> incomingFrames;
977             private readonly LimitTimer rxWatchdog;
978 
979             private readonly SynopsysDWCEthernetQualityOfService parent;
980             private readonly int channelNumber;
981             private readonly bool hasInterrupts;
982         }
983     }
984 }