1 //
2 // Copyright (c) 2010-2024 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.Core.Structure;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Utilities;
14 using System.Collections.Generic;
15 using Antmicro.Renode.Network;
16 
17 namespace Antmicro.Renode.Peripherals.Network
18 {
19     //TODO: Might be Word/BytePeripheral as well
20     public sealed class SynopsysEthernetMAC : NetworkWithPHY, IDoubleWordPeripheral, IMACInterface, IKnownSize
21     {
SynopsysEthernetMAC(IMachine machine, SynopsysEthernetVersion version = SynopsysEthernetVersion.STM32F4)22         public SynopsysEthernetMAC(IMachine machine, SynopsysEthernetVersion version = SynopsysEthernetVersion.STM32F4) : base(machine)
23         {
24             sysbus = machine.GetSystemBus(this);
25             this.version = version;
26             MAC = EmulationManager.Instance.CurrentEmulation.MACRepository.GenerateUniqueMAC();
27             IRQ = new GPIO();
28             Reset();
29         }
30 
Reset()31         public override void Reset()
32         {
33             macConfiguration = 0x8000;
34             macFrameFilter = 0x0;
35             macMiiAddress = 0x0;
36             macMiiData = 0x0;
37             macFlowControl = 0x0;
38             dmaBusMode = 0x20100;
39             dmaReceiveDescriptorListAddress = 0x0;
40             dmaTransmitDescriptorListAddress = 0x0;
41             dmaOperationMode = 0x0;
42             dmaInterruptEnable = 0x0;
43         }
44 
ReadDoubleWord(long offset)45         public uint ReadDoubleWord(long offset)
46         {
47             this.NoisyLog("Read from {0}", (Registers)offset);
48             switch((Registers)offset)
49             {
50             case Registers.MACConfiguration:
51                 return macConfiguration;
52             case Registers.MACFrameFilter:
53                 return macFrameFilter;
54             case Registers.MACMIIAddress:
55                 return macMiiAddress;
56             case Registers.MACMIIData:
57                 return macMiiData;
58             case Registers.MACFlowControl:
59                 return macFlowControl;
60             case Registers.MACAddress0High:
61                 return (uint)((MAC.F << 8) | MAC.E);
62             case Registers.MACAddress0Low:
63                 return (uint)((MAC.D << 24) | (MAC.C << 16) | (MAC.B << 8) | MAC.A);
64             case Registers.DMABusMode:
65                 return dmaBusMode;
66             case Registers.DMAReceiveDescriptorListAddress:
67                 return dmaReceiveDescriptorListAddress;
68             case Registers.DMATransmitDescriptorListAddress:
69                 return dmaTransmitDescriptorListAddress;
70             case Registers.DMAStatusRegister:
71                 if((dmaStatus & ((1u << 14) | (1u << 6) | (1u << 2) | 1u)) != 0)
72                 {
73                     dmaStatus |= 1u << 16; //Normal interrupt summary
74                 }
75                 return dmaStatus;
76             case Registers.DMAOperationMode:
77                 return dmaOperationMode;
78             case Registers.DMAInterruptEnable:
79                 return dmaInterruptEnable;
80             default:
81                 this.LogUnhandledRead(offset);
82                 return 0;
83             }
84         }
85 
WriteDoubleWord(long offset, uint value)86         public void WriteDoubleWord(long offset, uint value)
87         {
88             this.NoisyLog("Write {0:X} to {1}", value, (Registers)offset);
89             switch((Registers)offset)
90             {
91             case Registers.MACConfiguration:
92                 macConfiguration = value;
93                 crcStrippingForTypeFrames = (macConfiguration & 1u << 25) != 0;
94                 automaticPadCRCStripping = (macConfiguration & 1u << 7) != 0;
95                 break;
96             case Registers.MACFrameFilter:
97                 macFrameFilter = value;
98                 break;
99             case Registers.MACMIIAddress:
100                 macMiiAddress = value;
101                 var busyClear = (value & 0x1) != 0;
102                 if(busyClear)
103                 {
104                     macMiiAddress = macMiiAddress & ~0x1u;
105                 }
106                 var phyId = (value >> 11) & 0x1F;
107                 var register = (ushort)((value >> 6) & 0x1F);
108                 var isRead = ((value >> 1) & 0x1) == 0;
109                 if(!TryGetPhy<ushort>(phyId, out var phy))
110                 {
111                     this.Log(LogLevel.Warning, "Access to unknown phy {0}", phyId);
112                     break;
113                 }
114                 if(isRead)
115                 {
116                     macMiiData = phy.Read(register);
117                 }
118                 else
119                 {
120                     phy.Write(register, macMiiData);
121                 }
122 
123                 break;
124             case Registers.MACMIIData:
125                 macMiiData = (ushort)value;
126                 break;
127             case Registers.MACFlowControl:
128                 macFlowControl = value;
129                 break;
130             case Registers.MACAddress0High:
131                 MAC = MAC.WithNewOctets(f: (byte)(value >> 8), e: (byte)value);
132                 break;
133             case Registers.MACAddress0Low:
134                 MAC = MAC.WithNewOctets(d: (byte)(value >> 24), c: (byte)(value >> 16), b: (byte)(value >> 8), a: (byte)value);
135                 break;
136             case Registers.DMABusMode:
137                 dmaBusMode = value & ~0x1u;
138                 if((value & 0x1) != 0)
139                 {
140                     Reset();
141                 }
142                 break;
143             case Registers.DMATransmitPollDemand:
144                 if((dmaStatus | StartStopTransmission) != 0)
145                 {
146                     SendFrames();
147                 }
148                 break;
149             case Registers.DMAReceiveDescriptorListAddress:
150                 this.Log(LogLevel.Info, "Setting RDLA to 0x{0:X}.", value);
151                 dmaReceiveDescriptorListAddress = value & ~3u;
152                 dmaReceiveDescriptorListAddressBegin = dmaReceiveDescriptorListAddress;
153                 break;
154             case Registers.DMATransmitDescriptorListAddress:
155                 dmaTransmitDescriptorListAddress = value & ~3u;
156                 dmaTransmitDescriptorListAddressBegin = dmaReceiveDescriptorListAddress;
157                 break;
158             case Registers.DMAStatusRegister:
159                 dmaStatus &= ~value; //write 1 to clear;
160                 if((value & 0x10000) > 0)
161                 {
162                     IRQ.Unset();
163                     TryDequeueFrame();
164                 }
165                 break;
166             case Registers.DMAOperationMode:
167                 dmaOperationMode = value;
168                 if((value & StartStopTransmission) != 0)
169                 {
170                     SendFrames();
171                 }
172                 break;
173             case Registers.DMAInterruptEnable:
174                 if(BitHelper.IsBitSet(value, 16)) //normal interrupt summary enable
175                 {
176                     value |= (1u << 14) | (1u << 6) | (1u << 2) | 1u;
177                 }
178                 dmaInterruptEnable = value;
179                 break;
180             default:
181                 this.LogUnhandledWrite(offset, value);
182                 break;
183             }
184         }
185 
ReceiveFrame(EthernetFrame frame)186         public void ReceiveFrame(EthernetFrame frame)
187         {
188             /*if(machine.ElapsedTime < TimeSpan.FromSeconds(30))
189             {
190                 return;
191             }*/
192             lock(receiveLock)
193             {
194                 if((dmaStatus & ReceiveStatus) != 0)
195                 {
196                     queue.Enqueue(frame);
197                     return;
198                 }
199                 if(frame.Bytes.Length < 14)
200                 {
201                     this.Log(LogLevel.Error, "DROPPING - packet too short.");
202                     return;
203                 }
204                 if(this.machine.IsPaused)
205                 {
206                     this.Log(LogLevel.Debug, "DROPPING - cpu is halted.");
207                     return;
208                 }
209                 var destinationMac = frame.DestinationMAC;
210                 if(!destinationMac.IsBroadcast && !destinationMac.Equals(MAC))
211                 {
212                     this.Log(LogLevel.Debug, "DROPPING - not for us.");
213                     return;
214                 }
215 		/*
216                 if((dmaInterruptEnable & (ReceiveStatus)) == 0)
217                 {
218                     this.Log(LogLevel.Debug, "DROPPING - rx irq is turned off.");
219                     return;
220                 }
221 		*/
222                 this.Log(LogLevel.Noisy, Misc.DumpPacket(frame, false, machine));
223                 if(dmaReceiveDescriptorListAddress < 0x20000000)
224                 {
225                     // TODO: not in ram
226                     this.Log(LogLevel.Error, "DROPPING - descriptor is not valid.");
227                     return;
228                 }
229                 var written = 0;
230                 var first = true;
231                 var bytes = frame.Bytes;
232 
233                 if(!EthernetFrame.CheckCRC(bytes))
234                 {
235                     if(!(crcStrippingForTypeFrames && bytes.Length > 1536) || !(automaticPadCRCStripping && bytes.Length < 1500))
236                     {
237                         this.Log(LogLevel.Info, "Invalid CRC, packet discarded");
238                         return;
239                     }
240                 }
241 
242                 var receiveDescriptor = new RxDescriptor(this, sysbus, version);
243                 receiveDescriptor.Fetch(dmaReceiveDescriptorListAddress);
244                 if(receiveDescriptor.IsOwnedByDMA)
245                 {
246                     this.Log(LogLevel.Error, "DROPPING  - descriptor is used.");
247                     return;
248                 }
249                 this.Log(LogLevel.Noisy, "DESCRIPTOR ADDR1={0:X}, ADDR2={1:X}", receiveDescriptor.Address1, receiveDescriptor.Address2);
250                 while(!receiveDescriptor.IsOwnedByDMA)
251                 {
252                     if(receiveDescriptor.Address1 < 0x20000000)
253                     {
254                         this.Log(LogLevel.Error, "Descriptor points outside of ram, aborting... This should not happen!");
255                         break;
256                     }
257                     receiveDescriptor.IsOwnedByDMA = true;
258                     receiveDescriptor.IsFirstSegment = first;
259                     first = false;
260                     var howManyBytes = Math.Min(receiveDescriptor.Buffer1Length, frame.Bytes.Length - written);
261                     var toWriteArray = new byte[howManyBytes];
262 
263                     Array.Copy(bytes, written, toWriteArray, 0, howManyBytes);
264                     sysbus.WriteBytes(toWriteArray, receiveDescriptor.Address1);
265                     written += howManyBytes;
266                     //write second buffer
267                     if(frame.Bytes.Length - written > 0 && !receiveDescriptor.IsNextDescriptorChained)
268                     {
269                         howManyBytes = Math.Min(receiveDescriptor.Buffer2Length, frame.Bytes.Length - written);
270                         toWriteArray = new byte[howManyBytes];
271                         Array.Copy(bytes, written, toWriteArray, 0, howManyBytes);
272                         sysbus.WriteBytes(toWriteArray, receiveDescriptor.Address2);
273                         written += howManyBytes;
274                     }
275                     if(frame.Bytes.Length - written <= 0)
276                     {
277                         receiveDescriptor.IsLastSegment = true;
278                         this.NoisyLog("Setting descriptor length to {0}", (uint)frame.Bytes.Length);
279                         receiveDescriptor.FrameLength = (uint)frame.Bytes.Length;
280                     }
281                     this.NoisyLog("Writing descriptor at 0x{6:X}, first={0}, last={1}, written {2} of {3}. next_chained={4}, endofring={5}", receiveDescriptor.IsFirstSegment, receiveDescriptor.IsLastSegment, written, frame.Bytes.Length, receiveDescriptor.IsNextDescriptorChained, receiveDescriptor.IsEndOfRing, dmaReceiveDescriptorListAddress);
282                     receiveDescriptor.WriteBack();
283                     if(!receiveDescriptor.IsNextDescriptorChained)
284                     {
285                         dmaReceiveDescriptorListAddress += 8;
286                     }
287                     else if(receiveDescriptor.IsEndOfRing)
288                     {
289                         dmaReceiveDescriptorListAddress = dmaReceiveDescriptorListAddressBegin;
290                     }
291                     else
292                     {
293                         dmaReceiveDescriptorListAddress = receiveDescriptor.Address2;
294                     }
295                     if(frame.Bytes.Length - written <= 0)
296                     {
297                         if((dmaInterruptEnable & (ReceiveStatus)) != 0)// receive interrupt
298                         {
299                             dmaStatus |= ReceiveStatus;
300                             IRQ.Set();
301                         }
302                         else
303                         {
304                             this.DebugLog("Exiting but not scheduling an interrupt!");
305                         }
306                         break;
307                     }
308                     receiveDescriptor.Fetch(dmaReceiveDescriptorListAddress);
309                 }
310                 this.DebugLog("Packet of length {0} delivered.", frame.Bytes.Length);
311                 if(written < frame.Bytes.Length)
312                 {
313                     this.Log(LogLevel.Error, "Delivered only {0} from {1} bytes!", written, frame.Bytes.Length);
314                 }
315             }
316         }
317 
318         public event Action<EthernetFrame> FrameReady;
319 
320         public MACAddress MAC { get; set; }
321 
322         public GPIO IRQ { get; private set; }
323 
324         public long Size
325         {
326             get
327             {
328                 return 0x1400;
329             }
330         }
331 
SendFrames()332         private void SendFrames()
333         {
334             this.Log(LogLevel.Noisy, "Sending frame");
335             var transmitDescriptor = new TxDescriptor(this, sysbus, version);
336             var packetData = new List<byte>();
337 
338             transmitDescriptor.Fetch(dmaTransmitDescriptorListAddress);
339             while(!transmitDescriptor.IsOwnedByDMA)
340             {
341                 transmitDescriptor.IsOwnedByDMA = true;
342                 this.Log(LogLevel.Noisy, "GOING TO READ FROM {0:X}, len={1}", transmitDescriptor.Address1, transmitDescriptor.Buffer1Length);
343                 packetData.AddRange(sysbus.ReadBytes(transmitDescriptor.Address1, transmitDescriptor.Buffer1Length));
344                 if(!transmitDescriptor.IsNextDescriptorChained)
345                 {
346                     packetData.AddRange(sysbus.ReadBytes(transmitDescriptor.Address2, transmitDescriptor.Buffer2Length));
347                 }
348 
349                 transmitDescriptor.WriteBack();
350 
351                 if(transmitDescriptor.IsEndOfRing)
352                 {
353                     dmaTransmitDescriptorListAddress = dmaTransmitDescriptorListAddressBegin;
354                 }
355                 else if(transmitDescriptor.IsNextDescriptorChained)
356                 {
357                     dmaTransmitDescriptorListAddress = transmitDescriptor.Address2;
358                 }
359                 else
360                 {
361                     dmaTransmitDescriptorListAddress += 8;
362                 }
363                 if(transmitDescriptor.IsLastSegment)
364                 {
365                     this.Log(LogLevel.Noisy, "Sending frame of {0} bytes.", packetData.Count);
366 
367                     if(!Misc.TryCreateFrameOrLogWarning(this, packetData.ToArray(), out var frame, addCrc: true))
368                     {
369                         continue;
370                     }
371                     if(transmitDescriptor.ChecksumInstertionControl > 0)
372                     {
373                         this.Log(LogLevel.Noisy, "Calculating checksum (mode {0}).", transmitDescriptor.ChecksumInstertionControl);
374                         if(transmitDescriptor.ChecksumInstertionControl == 1)
375                         {
376                             //IP only
377                             frame.FillWithChecksums(supportedEtherChecksums, new IPProtocolType[] {});
378                         }
379                         else
380                         {
381                             //IP and payload
382                             frame.FillWithChecksums(supportedEtherChecksums, supportedIPChecksums);
383                         }
384                     }
385                     this.Log(LogLevel.Debug, Misc.DumpPacket(frame, true, machine));
386 
387                     if((dmaInterruptEnable & (TransmitStatus)) != 0) // transmit interrupt
388                     {
389                         dmaStatus |= TransmitStatus;
390                         IRQ.Set();
391                     }
392 
393                     FrameReady?.Invoke(frame);
394                 }
395                 transmitDescriptor.Fetch(dmaTransmitDescriptorListAddress);
396             }
397 
398             //set TransmitBufferUnavailable
399             dmaStatus |= TransmitBufferUnavailableStatus;
400             dmaStatus |= TransmitStatus;
401             if((dmaInterruptEnable & (StartStopTransmission)) == 0)
402             {
403                 IRQ.Set();
404             }
405             this.Log(LogLevel.Noisy, "Frame sent.");
406         }
407 
TryDequeueFrame()408         private void TryDequeueFrame()
409         {
410             lock(receiveLock)
411             {
412                 if(queue.Count > 0 && ((dmaStatus & ReceiveStatus) == 0))
413                 {
414                     var frame = queue.Dequeue();
415                     ReceiveFrame(frame);
416                 }
417             }
418         }
419 
420         private bool automaticPadCRCStripping;
421         private bool crcStrippingForTypeFrames;
422         private uint macConfiguration;
423         private uint macFrameFilter;
424         private uint macMiiAddress;
425         private ushort macMiiData;
426         private uint macFlowControl;
427         private uint dmaBusMode;
428         private uint dmaReceiveDescriptorListAddress;
429         private uint dmaReceiveDescriptorListAddressBegin;
430         private uint dmaTransmitDescriptorListAddress;
431         private uint dmaTransmitDescriptorListAddressBegin;
432         private uint dmaStatus;
433         private uint dmaOperationMode;
434         private uint dmaInterruptEnable;
435         private readonly IBusController sysbus;
436         private readonly object receiveLock = new object();
437         private readonly Queue<EthernetFrame> queue = new Queue<EthernetFrame>();
438         private readonly EtherType[] supportedEtherChecksums = { EtherType.IpV4, EtherType.Arp };
439         private readonly IPProtocolType[] supportedIPChecksums = {
440             IPProtocolType.TCP,
441             IPProtocolType.UDP,
442             IPProtocolType.ICMP
443         };
444         private readonly SynopsysEthernetVersion version;
445         private const uint StartStopTransmission = 1 << 13;
446         private const uint TransmitBufferUnavailableStatus = 1 << 2;
447         private const uint ReceiveStatus = 1 << 6;
448         private const uint TransmitStatus = 1 << 0;
449 
450         public enum SynopsysEthernetVersion
451         {
452             STM32F4,
453             BeagleV,
454         }
455 
456         private class Descriptor
457         {
Descriptor(SynopsysEthernetMAC parent, IBusController sysbus, SynopsysEthernetVersion version)458             public Descriptor(SynopsysEthernetMAC parent, IBusController sysbus, SynopsysEthernetVersion version)
459             {
460                 this.sysbus = sysbus;
461                 this.version = version;
462                 this.parent = parent;
463             }
464 
Fetch(uint address)465             public void Fetch(uint address)
466             {
467                 this.address = address;
468 
469                 word0 = sysbus.ReadDoubleWord(address);
470                 word1 = sysbus.ReadDoubleWord(address + 4);
471                 word2 = sysbus.ReadDoubleWord(address + 8);
472                 word3 = sysbus.ReadDoubleWord(address + 12);
473             }
474 
WriteBack()475             public void WriteBack()
476             {
477                 sysbus.WriteDoubleWord(address, word0);
478                 sysbus.WriteDoubleWord(address + 4, word1);
479                 sysbus.WriteDoubleWord(address + 8, word2);
480                 sysbus.WriteDoubleWord(address + 12, word3);
481             }
482 
483             public bool IsOwnedByDMA
484             {
485                 get => !BitHelper.IsBitSet(word0, 31);
486                 set => BitHelper.SetBit(ref word0, 31, !value);
487             }
488 
489             public uint Address1
490             {
491                 get => word2;
492             }
493 
494             public uint Address2
495             {
496                 get => word3;
497             }
498 
499             public int Buffer1Length
500             {
501                 get
502                 {
503                     switch(version)
504                     {
505                     case SynopsysEthernetVersion.BeagleV:
506                         return (int)BitHelper.GetValue(word1, 0, 10);
507                     case SynopsysEthernetVersion.STM32F4:
508                         return (int)BitHelper.GetValue(word1, 0, 13);
509                     default:
510                         parent.ErrorLog("Unsupported {0}: {1}, returning 0", nameof(SynopsysEthernetVersion), version);
511                         return 0;
512                     }
513                 }
514             }
515 
516             public int Buffer2Length
517             {
518                 get
519                 {
520                     switch(version)
521                     {
522                     case SynopsysEthernetVersion.BeagleV:
523                         return (int)BitHelper.GetValue(word1, 11, 10);
524                     case SynopsysEthernetVersion.STM32F4:
525                         return (int)BitHelper.GetValue(word1, 16, 13);
526                     default:
527                         parent.ErrorLog("Unsupported {0}: {1}, returning 0", nameof(SynopsysEthernetVersion), version);
528                         return 0;
529                     }
530                 }
531             }
532 
533             protected const uint UsedField = 1u << 31;
534             protected uint address;
535             protected uint word0;
536             protected uint word1;
537             protected uint word2;
538             protected uint word3;
539             protected readonly SynopsysEthernetVersion version;
540             protected readonly SynopsysEthernetMAC parent;
541             private readonly IBusController sysbus;
542         }
543 
544         private class TxDescriptor : Descriptor
545         {
TxDescriptor(SynopsysEthernetMAC parent, IBusController sysbus, SynopsysEthernetVersion version)546             public TxDescriptor(SynopsysEthernetMAC parent, IBusController sysbus, SynopsysEthernetVersion version) : base(parent, sysbus, version)
547             {
548             }
549 
550             public uint ChecksumInstertionControl
551             {
552                 get
553                 {
554                     switch(version)
555                     {
556                     case SynopsysEthernetVersion.BeagleV:
557                         return BitHelper.GetValue(word1, 27, 2);
558                     case SynopsysEthernetVersion.STM32F4:
559                         return BitHelper.GetValue(word0, 22, 2);
560                     default:
561                         parent.ErrorLog("Unsupported {0}: {1}, returning 0", nameof(SynopsysEthernetVersion), version);
562                         return 0;
563                     }
564                 }
565             }
566 
567             public bool IsLastSegment
568             {
569                 get
570                 {
571                     switch(version)
572                     {
573                     case SynopsysEthernetVersion.BeagleV:
574                         return BitHelper.IsBitSet(word1, 30);
575                     case SynopsysEthernetVersion.STM32F4:
576                         return BitHelper.IsBitSet(word0, 29);
577                     default:
578                         parent.ErrorLog("Unsupported {0}: {1}, returning 0", nameof(SynopsysEthernetVersion), version);
579                         return false;
580                     }
581                 }
582             }
583 
584             public bool IsNextDescriptorChained
585             {
586                 get
587                 {
588                     switch(version)
589                     {
590                     case SynopsysEthernetVersion.BeagleV:
591                         return BitHelper.IsBitSet(word1, 24);
592                     case SynopsysEthernetVersion.STM32F4:
593                         return BitHelper.IsBitSet(word0, 20);
594                     default:
595                         parent.ErrorLog("Unsupported {0}: {1}, returning 0", nameof(SynopsysEthernetVersion), version);
596                         return false;
597                     }
598                 }
599             }
600 
601             public bool IsEndOfRing
602             {
603                 get
604                 {
605                     switch(version)
606                     {
607                     case SynopsysEthernetVersion.BeagleV:
608                         return BitHelper.IsBitSet(word1, 25);
609                     case SynopsysEthernetVersion.STM32F4:
610                         return BitHelper.IsBitSet(word0, 21);
611                     default:
612                         parent.ErrorLog("Unsupported {0}: {1}, returning 0", nameof(SynopsysEthernetVersion), version);
613                         return false;
614                     }
615                 }
616             }
617         }
618 
619         private class RxDescriptor : Descriptor
620         {
RxDescriptor(SynopsysEthernetMAC parent, IBusController sysbus, SynopsysEthernetVersion version)621             public RxDescriptor(SynopsysEthernetMAC parent, IBusController sysbus, SynopsysEthernetVersion version) : base(parent, sysbus, version)
622             {
623             }
624 
625             public bool IsNextDescriptorChained
626             {
627                 get
628                 {
629                     switch(version)
630                     {
631                     case SynopsysEthernetVersion.BeagleV:
632                         return BitHelper.IsBitSet(word1, 24);
633                     case SynopsysEthernetVersion.STM32F4:
634                         return BitHelper.IsBitSet(word1, 14);
635                     default:
636                         parent.ErrorLog("Unsupported {0}: {1}, returning 0", nameof(SynopsysEthernetVersion), version);
637                         return false;
638                     }
639 
640                 }
641             }
642 
643             public bool IsEndOfRing
644             {
645                 get
646                 {
647                     switch(version)
648                     {
649                     case SynopsysEthernetVersion.BeagleV:
650                         return BitHelper.IsBitSet(word1, 25);
651                     case SynopsysEthernetVersion.STM32F4:
652                         return BitHelper.IsBitSet(word1, 15);
653                     default:
654                         parent.ErrorLog("Unsupported {0}: {1}, returning 0", nameof(SynopsysEthernetVersion), version);
655                         return false;
656                     }
657                 }
658             }
659 
660             public bool IsLastSegment
661             {
662                 set
663                 {
664                     BitHelper.SetBit(ref word0, 8, value);
665                 }
666                 get
667                 {
668                     return BitHelper.IsBitSet(word0, 8);
669                 }
670             }
671 
672             public bool IsFirstSegment
673             {
674                 set
675                 {
676                     BitHelper.SetBit(ref word0, 9, value);
677                 }
678                 get
679                 {
680                     return BitHelper.IsBitSet(word0, 9);
681                 }
682             }
683 
684             public uint FrameLength
685             {
686                 set
687                 {
688                     BitHelper.ReplaceBits(ref word0, value, width: 14, destinationPosition: 16);
689                 }
690             }
691         }
692 
693         private enum Registers
694         {
695             MACConfiguration = 0x0000,
696             MACFrameFilter = 0x0004,
697             MACMIIAddress = 0x0010,
698             MACMIIData = 0x0014,
699             MACFlowControl = 0x0018,
700             MACAddress0High = 0x0040,
701             MACAddress0Low = 0x0044,
702             DMABusMode = 0x1000,
703             DMATransmitPollDemand = 0x1004,
704             DMAReceiveDescriptorListAddress = 0x100C,
705             DMATransmitDescriptorListAddress = 0x1010,
706             DMAStatusRegister = 0x1014,
707             DMAOperationMode = 0x1018,
708             DMAInterruptEnable = 0x101C
709         }
710     }
711 }
712